我们团队在开发过程中,测试接口时,常使用 fiddler 抓包查看请求报文和响应报文快速定位问题所在,这可比在代码中打断点看数据高效一万倍......
然而 fiddler 只能抓 http 包,如果是 https,有可能因为证书问题抓不到包.
而这种场景一般都出现在,需要黑别家 app,查看一些小秘密.
没错,这次我们由于业务需要,得分析某竞品,自然不能倒在这抓包的第一步,所以下面记录下使用 Xpose 绕过自定义证书验证的全过程.
反编译 APK
常见的套路,apk 后缀改 zip 解压,拿到 dex 文件,这里一般会有多个 dex,全部拷到 dex2jar 目录下.
接下来我们要使用 d2j-dex2jar.bat 把 dex 反编译成 jar.
最简单的方式,把 d2j-dex2jar.bat 和 dex 文件都拖到终端命令框,回车.
把反编译的 jar 丢到 gui,然后:
得到 java 源文件,到此为止我们就拿到了混淆后的 java 文件.
接下来就要考验大家对代码的敏感度了,因为核心代码都是 abc 这样的字母,我们要靠它们找到代码中疑似设置 httpClinet 的地方.
定位 https
这里推荐使用 EditPlus,可以全局搜索字符串,快捷键是 Ctrl + Shift + F5.
我们直接全局搜 httpClinet,找到对应代码段:
private static DefaultHttpClient a(boolean paramBoolean)
{
......
KeyStore ks= KeyStore.getInstance(KeyStore.getDefaultType());
ks.load(null, null);
b localb = new b(ks, paramBoolean);
localb.setHostnameVerifier(b.ALLOW_ALL_HOSTNAME_VERIFIER);
BasicHttpParams localBasicHttpParams = new BasicHttpParams();
localBasicHttpParams.setParameter("http.protocol.cookie-policy", "netscape");
HttpConnectionParams.setSocketBufferSize(localBasicHttpParams, 8192);
HttpConnectionParams.setStaleCheckingEnabled(localBasicHttpParams, false);
HttpConnectionParams.setConnectionTimeout(localBasicHttpParams, 20000);
HttpConnectionParams.setSoTimeout(localBasicHttpParams, 20000);
HttpProtocolParams.setUseExpectContinue(localBasicHttpParams, false);
SchemeRegistry localSchemeRegistry = new SchemeRegistry();
//注册http协议
localSchemeRegistry.register(new Scheme("http", PlainSocketFactory.getSocketFactory(), 80));
//注册https协议
localSchemeRegistry.register(new Scheme("https", localb, 443));
DefaultHttpClient localDefaultHttpClient = new DefaultHttpClient(new ThreadSafeClientConnManager(localBasicHttpParams, localSchemeRegistry), localBasicHttpParams);
localDefaultHttpClient.setRedirectHandler(new f());
......
}
可以看到 https 协议用了自己的证书,却信任全部证书...... 那就很简单了,只要勾住 SchemeRegistry 中的 register 方法,把其第二个参数(证书)替换成 http 证书即可.
Hook
直接上代码:
public void handleLoadPackage(final XC_LoadPackage.LoadPackageParam lpparam) throws Throwable {
if (!lpparam.packageName.equals("目标包名"))
return;
classLoader = lpparam.classLoader;
//勾住android入口函数
XposedHelpers.findAndHookMethod("android.app.Instrumentation", lpparam.classLoader, "newActivity", ClassLoader.class, String.class, Intent.class, new XC_MethodHook() {
@Override
protected void beforeHookedMethod(XC_MethodHook.MethodHookParam param) throws Throwable {
}
@Override
protected void afterHookedMethod(MethodHookParam param) throws Throwable {
ClassLoader realClassLoader = lpparam.classLoader;
final Class<?> acpv = XposedHelpers.findClass("org.apache.http.conn.scheme.SchemeRegistry", realClassLoader);
XposedBridge.hookAllMethods(acpv, "register", new XC_MethodHook() {
@Override
protected void afterHookedMethod(MethodHookParam param) throws Throwable {
super.afterHookedMethod(param);
}
@Override
protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
Scheme s = (Scheme) param.args[0];
if (s.getDefaultPort() == 443) {
//这里把证书替换成http证书
param.args[0] = new Scheme("https", PlainSocketFactory.getSocketFactory(), 443);
}
super.beforeHookedMethod(param);
}
});
super.afterHookedMethod(param);
}
});
}
由于目标代码信任全部证书,所以这里只是简单的替换证书.
对于更严格的,验证了自定义证书的目标,可以使用现成的轮子: JustTrustMe
轮子将 APK 中所有用于校验 SSL 证书的 API 都进行了 Hook,真狠呐......
虽然有现成的轮子,不过对于简单的东西,能自己动手实现一番,何乐而不为呢?
来源: http://blog.csdn.net/qq_27258799/article/details/79094399