0x0 零
做APK分析的时候难免会遇到一些非原生的开发模式,比如H5、Mono等等,要破解这些开发模式的APK原理其实和破解加了壳的APK一样,无非就是找到它的核心代码(或者前者比后者要简单的多)。这次遇到了一个Xamarin开发的APK,初步了解了一下这个Xamarin,不外乎就是基于 Mono 的一个 APP 开发框架,里面包装了很多Android Framework的API给你用。
0x1 壹
把APK装到手机上跑了跑,了解大致功能。按部就班的把APK脱到反编译工具里。震惊!!
几乎所有的类都如上图所示,有static代码块 + 字符串格式的代码 + Native方法 。 找了一圈发现没啥有用的代码,不过感觉告诉我,要么加壳了,要么是我没遇见过的情况。
0x2 贰
确定了一点,DEX文件里的东西是没什么用的,那么SO文件里呢?无论是加壳还是别的,都会有相应的SO去处理,所以说看一下lib目录下有什么熟悉的文件,能帮组我进一步了解这个APK。
lib目录下没有啥libdexhelper.so这些看着就很碍眼的文件,不过有两比较有意思的文件,libmonodroid.so和libmonosgen.so,这两个文件就是Xamarin的引擎文件了。
知道了APK是Xamarin框架之后,就去assemblies目录下找它的核心代码,发现这里很多DLL文件,它们都是由C#源码文件编译而成。反编译这些文件的工具很多,比如ILSpy、.NET Reflector以及dotpeek等(个人比较推荐ILSpy)。
接下来就是去分析反编译代码,可以看到C#的反编译程度已经几乎和源码相同了,很容易分析。寻找你想要修改了的地方(修改IL代码只需要给你的反编译工具加一个插件就好-Reflexil),很多小游戏破解,改改金币、血量什么的都很容易。我这里为了更方便的研究下它通信协议方面的东西,需要让流量走过我的代理服务器,所以说我需要修改它的证书校验的代码。
找到校验的地方,然后将返回的false改成true即可,通过reflexil把ldc.i4.0改成ldc.i4.1。接下来就是保存,替换源文件,打包,签名。
0x3 叁
在打包的时候要注意几个方面的问题
第一个问题是打包方式,一般重打包我习惯性的会选择apktool b去重打包,但是这里因为某些bug导致apktool打包不了,后面想了想其他方式,反正只替换DLL文件,而且这个资源又不用aapt去编译,所以我可以选择解压APK文件,然后替换DLL文件,最后在压缩回来就ok了,其实所有添加或删除不需要编译的文件都可以用这种方式,比如assets目录下的资源、编译好的DEX、编译好的SO;
第二个问题是文件压缩问题,当我替换了DLL之后,压缩文件夹时,需要选择某些资源不进行压缩,否者后期运行会报错,针对这个APK,assemblies目录下的所有文件都要选择不压缩;
第三个问题是APK的字节对齐问题,这个问题主要会出现在google store上的应用打包时,我需要通过zipalign工具裁剪一下压缩好了的APK
Android SDK中包含一个"zipalign"的工具,它能够对打包的应用程序进行优化。在你的应用程序上运行zipalign,使得在运行时Android与应用程序间的交互更加有效率。因此,这种方式能够让应用程序和整个系统运行得更快。
这个工具在sdk目录下有,命令是
Android/sdk/build-tools/25.0.0/zipalign -v -p 4 in.apk out.apk
第四个问题是签名,接下来就用sdk里的apksigner工具签名就行了,这里需要一个签名文件,可以在Android Studio里面生成一个,签名命令是:
Android/sdk/build-tools/25.0.0/apksigner sign --ks releasekeystore.jks --out out.apk in.apk
0x04 肆
最后安装到手机,设置系统代理,发现其流量任然没有走到我的代理服务器。为什么呢?明明证书校验是改了的呀。。
猜想那么几种可能,第一种可能是它协议不是http/https,不过分析C#代码,发现里面很多url都是https协议的,break。第二种可能是它流量不走系统代理,通过观察我代理服务器上的流量,确实是没有抓到该APP的流量,但是这个APP能正常运行,continue。
那么为啥不走系统代理呢?第一种可能是它把代理设置成了NO_PROXY,这样就强制让代理失效了。
回头去跟C#代码,发现它用的HTTP库是flurl,发现它并没有设置flurl的proxy属性,这样一来,讲道理,它就不会不走代理。继续分析,flurl是由C#实现,作用于.NET程序,所以猜想这个库在设计之初应该不会考虑到Android手机上有系统代理的情况(我也没去深挖Xamarin对代理的处理),所以更不会去主动获取Android上的系统代理。而在Android平台上获取系统代理地址一般是通过这种方式(图中hostKey为“http.proxyHost”,portKey为“http.proxyPort”):
那么怎么才能让flurl的proxy去自动配置成Android的系统代理呢?可以通过修改flurl的代码,编译后去替换APK里的flurl.dll文件。当时想到这个办法的时候,我就已经放弃了。几经波折,最后还是用ProxyDroid这个Android上的工具强制让http流量走到了我的代理服务器上。
0x05 伍
回头再看一次,要实现上面那个功能,还有很多方法,比如说HOOK。Mono是一个JIT运行模式,那么我们可以Hook Mono加载DLL文件的地方,去替换特定的DLL(DLL改动比较大时采用),或者通过文件偏移去patch DLL中某个字节(DLL改动小时采用)。
Mono是一个开源的工程,网上也有很多对它的分析。我这里简单的再总结一下一个DLL加载过程中涉及到的一些函数
1.mono_assembly_open 打开DLL文件
2.mono_assembly_load_from_full 将DLL文件加载进内存
3.mono_compile_method 编译某个函数
4.mono_runtime_invoke 执行函数