自己动手修改Android程序 - twicca改造
仅以此文悼念为了使用twicca上twitter与GFW搏斗浪费掉的20多个小时
apktool
首先介绍一个软件 apktool , 利用它可以反汇编Android的二进制软件包. 利用apktool反汇编Android程序的结果与程序源代码目录结构相同, 唯一区别是包含Java代码的src目录变成了包含Dalvik汇编码的smali目录.
|
修改代码后使用apktool可以重新编译修改过的反汇编结果, 生成新的Android程序.
Dalvik
smali目录里面是Dalvik虚拟机的汇编码. Dalvik 是android使用的Java虚拟机, 类似sun的JVM, 使用与Java bytecode类似的指令集. 与Java bytecode最大的区别是Dalvik是寄存器机, Java bytecode是堆栈机. Dalvik的效率更高, 因为现有大部分硬件体系都是寄存器机, Dalvik能够更加充分得利用寄存器资源; 而且Dalvik的汇编代码也更加容易理解, 寄存器变量比堆栈变量更加清晰明了.
比如说这样一段代码
public void HelloWorld(String p1, String p2) { System.out.println(p1 + p2); } |
编译成JVM和Dalvik分别是
JVM byte code
|
Dalvik
|
很明显, 下面Dalvik的汇编代码容易理解, 即使没有接触过Dalvik也能轻松理解这段代码. 上面的JVM bytecode就不是那么容易理解了, 函数调用的参数与结果都隐含在堆栈中, 必须分析堆栈才能知道程序意义.
调试
使用apktool的-d参数解开/打包的程序会拥有调试信息, 在Android上运行的时候可以通过JPDA调试, 在手机上使用Dev Tools打开调试, 然后在PC上用Android SDK里面的ddms连上去, 启动程序, 用任意一个JPDA客户端连上去就可以调试了. apktool的作者测试, Eclipse无法设置断点, 其他功能正常, 笔者使用jswat所有功能都正常. apktool作者的调试教程在 这里
有了apktool, 并且熟悉了Dalvik bytecode, 我们就可以对所有的Android程序动手术了.
twicca的防篡改机制
twicca这个程序会在运行的时候把自己的signature发给服务器验证本身的没有被篡改, 改过程序的signature肯定会变化, 无法通过服务器验证, 不过得到自己signature的程序也是程序写的, 我们把这段改掉, 直接返回正确的signature就可以了. 得到一个程序signature的方法有很多种, 通过调试器在原程序得到signature的地方设置断点, 直接从程序内存中取出来; 或者写一个程序在手机中运行计算原程序的signature. 这里笔者使用的是另外一个黑客研究出来的方法, 可以 直接在PC上获得程序的signature . 获取到程序正确的signature后, 要想办法替换到程序中得到修改过的signature.
下面这段代码是twicca获取自己signature的代码
const-string v1, "jp.r246.twicca" const/16 v2, 0x40 invoke-virtual {v0, v1, v2}, Landroid/content/pm/PackageManager;->getPackageInfo(Ljava/lang/String;I)Landroid/content/pm/PackageInfo; move-result-object v0 iget-object v0, v0, Landroid/content/pm/PackageInfo;->signatures:[Landroid/content/pm/Signature; const/4 v2, 0x0 aget-object v0, v0, v2 invoke-virtual {v0}, Landroid/content/pm/Signature;->toCharsString()Ljava/lang/String; move-result-object v0 |
翻译成Java就是
PackageManager v0a = ...; PackageInfo v0b = v0a.getPackageInfo("jp.r246.twicca", 0x40); Signature[] v0c = v0b.signatures; Signature v0d = v0c[0]; String v0e = v0d.toCharsString(); |
我们只需要把最后的结果改成正确的signature, 而不是从PackageInfo中得到的signature, 所以只需要改最后一句, 直接给v0e赋值就可以了, 修改后就是这样(注意只有最后一句有变化).
const-string v1, "jp.r246.twicca" const/16 v2, 0x40 invoke-virtual {v0, v1, v2}, Landroid/content/pm/PackageManager;->getPackageInfo(Ljava/lang/String;I)Landroid/content/pm/PackageInfo; move-result-object v0 iget-object v0, v0, Landroid/content/pm/PackageInfo;->signatures:[Landroid/content/pm/Signature; const/4 v2, 0x0 aget-object v0, v0, v2 invoke-virtual {v0}, Landroid/content/pm/Signature;->toCharsString()Ljava/lang/String; const-string v0, "30820269308201d2......27a2ee" |
这样的话无论我们如何篡改程序, 它在自检验的时候都会得到我们硬编码到这段代码中的signature, 这样就能跳过它的自检验了.
下面就可以充分发挥想象力随意修改程序了, 比如把所有指向twitter.com的请求改成指向某代理的请求…
这是笔者修改的用来翻墙的twicca 0.8.24c的patch