package mobi.acpm.inspeckage.hooks;
import org.apache.http.conn.scheme.HostNameResolver;
import org.apache.http.conn.ssl.SSLSocketFactory;
import java.net.Socket;
import java.security.KeyStore;
import java.security.SecureRandom;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.KeyManager;
import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;
import de.robv.android.xposed.XC_MethodHook;
import de.robv.android.xposed.XSharedPreferences;
import de.robv.android.xposed.callbacks.XC_LoadPackage;
import mobi.acpm.inspeckage.Module;
import static de.robv.android.xposed.XposedHelpers.callMethod;
import static de.robv.android.xposed.XposedHelpers.callStaticMethod;
import static de.robv.android.xposed.XposedHelpers.findAndHookConstructor;
import static de.robv.android.xposed.XposedHelpers.findAndHookMethod;
import static de.robv.android.xposed.XposedHelpers.findClass;
import static de.robv.android.xposed.XposedHelpers.getObjectField;
import static de.robv.android.xposed.XposedHelpers.newInstance;
import static de.robv.android.xposed.XposedHelpers.setObjectField;
/**
* Created by acpm on 25/11/15.
*
* Code from SSLUnpinning project https://github.com/ac-pm/SSLUnpinning_Xposed
*/
public class SSLPinningHook extends XC_MethodHook {
public static final String TAG = "Inspeckage_SSLPinning:";
private static XSharedPreferences sPrefs;
public static void loadPrefs() {
sPrefs = new XSharedPreferences(Module.class.getPackage().getName(), Module.PREFS);
sPrefs.makeWorldReadable();
}
public static void initAllHooks(final XC_LoadPackage.LoadPackageParam loadPackageParam) {
// --- Java Secure Socket Extension (JSSE) ---
//TrustManagerFactory.getTrustManagers >> EmptyTrustManager
findAndHookMethod("javax.net.ssl.TrustManagerFactory", loadPackageParam.classLoader, "getTrustManagers", new XC_MethodHook() {
@Override
protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
loadPrefs();
if (sPrefs.getBoolean("sslunpinning", false)) {
TrustManager[] tms = EmptyTrustManager.getInstance();
param.setResult(tms);
}
}
});
//SSLContext.init >> (null,EmptyTrustManager,null)
findAndHookMethod("javax.net.ssl.SSLContext", loadPackageParam.classLoader, "init", KeyManager[].class, TrustManager[].class, SecureRandom.class, new XC_MethodHook() {
@Override
protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
loadPrefs();
if (sPrefs.getBoolean("sslunpinning", false)) {
param.args[0] = null;
param.args[1] = EmptyTrustManager.getInstance();
param.args[2] = null;
}
}
});
//HttpsURLConnection.setSSLSocketFactory >> new SSLSocketFactory
findAndHookMethod("javax.net.ssl.HttpsURLConnection", loadPackageParam.classLoader, "setSSLSocketFactory", javax.net.ssl.SSLSocketFactory.class, new XC_MethodHook() {
@Override
protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
loadPrefs();
if (sPrefs.getBoolean("sslunpinning", false)) {
param.args[0] = newInstance(javax.net.ssl.SSLSocketFactory.class);
}
}
});
// --- APACHE ---
try {
final Class<?> httpsURLConnection = findClass("org.apache.http.conn.ssl.HttpsURLConnection", loadPackageParam.classLoader);
if (httpsURLConnection != null) {
//HttpsURLConnection.setDefaultHostnameVerifier >> SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER
try {
findAndHookMethod(httpsURLConnection, "setDefaultHostnameVerifier",
HostnameVerifier.class, new XC_MethodHook() {
@Override
protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
loadPrefs();
if (sPrefs.getBoolean("sslunpinning", false)) {
param.args[0] = SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER;
}
}
});
} catch (Error e) {
Module.logError(e);
}
//HttpsURLConnection.setHostnameVerifier >> SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER
try {
findAndHookMethod("org.apache.http.conn.ssl.HttpsURLConnection", loadPackageParam.classLoader, "setHostnameVerifier", HostnameVerifier.class,
new XC_MethodHook() {
@Override
protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
loadPrefs();
if (sPrefs.getBoolean("sslunpinning", false)) {
param.args[0] = SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER;
}
}
});
} catch (Error e) {
Module.logError(e);
}
//SSLSocketFactory.getSocketFactory >> new SSLSocketFactory
try {
findAndHookMethod("org.apache.http.conn.ssl.SSLSocketFactory", loadPackageParam.classLoader, "getSocketFactory", new XC_MethodHook() {
@Override
protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
loadPrefs();
if (sPrefs.getBoolean("sslunpinning", false)) {
param.setResult((SSLSocketFactory) newInstance(SSLSocketFactory.class));
}
}
});
} catch (Error e) {
Module.logError(e);
}
//SSLSocketFactory(...) >> SSLSocketFactory(...){ new EmptyTrustManager()}
try {
Class<?> sslSocketFactory = findClass("org.apache.http.conn.ssl.SSLSocketFactory", loadPackageParam.classLoader);
findAndHookConstructor(sslSocketFactory, String.class, KeyStore.class, String.class, KeyStore.class,
SecureRandom.class, HostNameResolver.class, new XC_MethodHook() {
@Override
protected void afterHookedMethod(MethodHookParam param) throws Throwable {
loadPrefs();
if (sPrefs.getBoolean("sslunpinning", false)) {
String algorithm = (String) param.args[0];
KeyStore keystore = (KeyStore) param.args[1];
String keystorePassword = (String) param.args[2];
SecureRandom random = (SecureRandom) param.args[4];
KeyManager[] keymanagers = null;
TrustManager[] trustmanagers;
if (keystore != null) {
keymanagers = (KeyManager[]) callStaticMethod(SSLSocketFactory.class, "createKeyManagers", keystore, keystorePassword);
}
trustmanagers = new TrustManager[]{new EmptyTrustManager()};
setObjectField(param.thisObject, "sslcontext", SSLContext.getInstance(algorithm));
callMethod(getObjectField(param.thisObject, "sslcontext"), "init", keymanagers, trustmanagers, random);
setObjectField(param.thisObject, "socketfactory", callMethod(getObjectField(param.thisObject, "sslcontext"), "getSocketFactory"));
}
}
});
} catch (Error e) {
Module.logError(e);
}
//SSLSocketFactory.isSecure >> true
try {
findAndHookMethod("org.apache.http.conn.ssl.SSLSocketFactory", loadPackageParam.classLoader, "isSecure", Socket.class, new XC_MethodHook() {
@Override
protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
loadPrefs();
if (sPrefs.getBoolean("sslunpinning", false)) {
param.setResult(true);
}
}
});
} catch (Error e) {
Module.logError(e);
}
}
} catch (Error e) {
Module.logError(e);
}
///OKHTTP
try {
findAndHookMethod("okhttp3.CertificatePinner", loadPackageParam.classLoader, "findMatchingPins", String.class, new XC_MethodHook() {
protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
loadPrefs();
if (sPrefs.getBoolean("sslunpinning", false)) {
param.args[0] = "";
}
}
});
} catch (Error e) {
Module.logError(e);
}
}
}
class EmptyTrustManager implements X509TrustManager {
private static TrustManager[] emptyTM = null;
public static TrustManager[] getInstance() {
if (emptyTM == null) {
emptyTM = new TrustManager[1];
emptyTM[0] = new EmptyTrustManager();
}
return emptyTM;
}
@Override
public void checkClientTrusted(X509Certificate[] chain, String authType)
throws CertificateException {
}
@Override
public void checkServerTrusted(X509Certificate[] chain, String authType)
throws CertificateException {
}
@Override
public X509Certificate[] getAcceptedIssuers() {
return new X509Certificate[0];
}
}