前言
在前面的篇章中做好了Android程序分析的准备工作,编写了一个可以用来破解的入门程序,配置了右键命令行的快捷键以及逆向分析需要用到的工具Apktool和AndroidKiller。本篇就来用这些工具对Android程序进行一次简单的破解。
破解入手
我们之前编写的程序是一个简单的注册的页面程序,没有后台的,可以专门用来破解上手。也算是比较基础的破解类型。在正式破解之前,我们先捋一捋破解思路。如果有过Windows平台逆向破解经验的小伙伴就会这样想:
- 将程序拖进OllyDbg
- 注册程序 -> 注册失败,并留意注册失败时弹出的字符串
- 在OllyDbg中搜索相关字符串
- 找到分支执行语句并修改执行流程
- 完成暴破
这的确是一个非常连贯且合理的思路,但是Android程序并不像Windows上的exe程序一样,可以直接拖进调试器进行调试并修改。所以在对Android程序破解时步骤会有所变化:
- 对apk文件进行反编译
- 分析apk文件
- 修改Smali文件
- 将修改后的程序重新编译,并签名
下面,我们就按照这些步骤,对程序进行逆向破解吧。
反编译
首先找到我们之前的apk文件,在我们编写好的Android程序项目中的地址位于:
Code1
项目名\app\build\outputs\apk\debug
右键在当前目录打开命令行,输入如下指令
Code1
apktool d app-debug.apk -o outdir_dbg
这样,我们反编译部分的工作就完成了
程序分析
这里首先我们需要下载一个工具,git bash,这部分在前一篇忘记提了,不过安装过程也很简单,进入官网下载后自行安装。一般情况下安装时它会要求添加到右键快捷键的,如果没有的话,就按照添加右键命令行的方法添加到注册表即可。
根据编写程序时的运行结果来看 我们需要搜索Register Failed字符串,然后找到其在程序中的所在位置,接着向上回溯,找到条件分支语句的地方,进行修改,便可完成暴破
有了思路后,开始实践,根据Android程序的特性,通常情况下,字符串硬编码到源码中,也可能引用自res/values目录下的string.xml文件中,所以我们决定对整个目录进行搜索,查找”Register Failed”字符串出现的位置,在对apk进行反汇编的目录中,右键Git Bash Here,键入如下指令:
可以发现,仅有一处,并且的确位于res/values目录下的string.xml文件中Code1
grep -r "Register Failed" outdir_dbg/
根据结果我们可以发现“Register Failed”这个字符串的值对应的Id是unsuccess,可以推测unsuccess有可能作为引用出现在反汇编代码中的某一处,所以接下来输入指令:
结果显示有3处出现unsuccess,其中一处是第三步出现的;另外两处可以看出unsuccess只不过是个变量名,其值为0x7f0b002b。所以这个值,就有可能会是代码中引用代表"Register Failed"的值。Code1
grep -r "unsuccess" outdir_dbg/
根据上一步的推测,继续搜索0x7f0b002b的值,输入如下指令:
根据结果,发现“0x7f0b002b”这个值出现了3次,值得关注的一处位于MainActivity$MyListener.smali这个文件中,有点Android正向开发经验都知道,一般来说MainActivity.java用于一个Android程序的主体功能的实现,而smali文件对应的就是Java文件编译完再反编译回来的文件。下面一个部分,将主要关注该文件。Code1
grep -r "0x7f0b002b" outdir_dbg/
修改Smali文件
找到MainActivity$MyListener.smali文件,路径位于:
Code1
项目名\app\build\outputs\apk\debug\outdir_dbg\smali\com\droider\crackme_0x1
用AndroidKiller打开该文件(也可以用别的文本编译器,例如010Editor),并定位到指令
smali1
const v7, 0x7f0b002b
的位置,观察前后程序 我们可以发现,0x7f0b002b这个代表着“Register Failed”这个字符串的语句位于cond_1这个执行分支中,并且该执行分支,会将该参数传入Toast.makeText函数中执行,可以猜测,这部分就属于执行失败时要执行的地方。向上观察,有一个条件判断语句
smali1
if-ne v6, v1, :cond_1
该语句当条件判断成立时会跳转到cond_1执行,也就是执行失败的分支。这就是突破口,我们可以修改这行条件判断语句,改为
smali1
if-eq v6, v1, :cond_1
这样语句判断的条件相反,也就不那么容易跳转到执行失败的分支了,修改后代码如下:
修改完Smali文件后,这时就只剩下最后一步了
重新编译并签名
修改完smali文件后,回退到最初反编译的那个文件夹,右键进入命令行,输入如下指令:
Code1
apktool b outdir_dbg
这里是通过apktool将我们修改后的文件夹重新编译成apk文件,编译后生成的文件位于目录:
Code1
outdir_dbg\dist
这里有一个很大的坑,我之前最早进行实验的时候,就是重新编译时总是失败,主要原因是apktool的版本不对,我的电脑Java的版本是13.0.1,apktool版本是2.4.1,在这个条件下是可以重新编译成功的,其它情况就不一定了。
在重新编译完后,生成了app-debug.apk文件,此时该文件是不能拖进Android虚拟机运行的,原因是我们修改了原本的apk文件,需要重新签名。这时我们利用AndroidKiller进行签名就行了。
步骤是:打开AndroidKiller -> 工具 -> APK签名 -> 将app-debug拖入 -> 选择签名AndroidKiller -> 执行
执行结束后效果如下: 此时在刚刚的文件夹中会生成一个新的文件apk-debug_sign.apk,这是已经签名过的程序,可以拖入虚拟机中运行
我们将apk-debug_sign.apk文件拖进虚拟机安装并运行,这时可以发现,无论我们输入什么用户名和校验码时,都会 弹出”Register Succeed”,原因是我们修改了程序的执行流程,使得在原本执行打印”Register Failed”情况变成了相反的情况。
至此,便完成了此次程序的整个的破解流程
小结
本篇结束,Android逆向算是开了一个头,然而接下来的更新却可能遥遥无期。首先Windows内核部分,在驱动篇结束后,将和Android逆向部分穿插更新,接下来要到APC机制了,APC机制非常繁琐,而且需要逆向大量函数,这一篇章花费一个月都更不完也是有可能的。另一个,最近公司有Python开发的需求,加上最近使用Frida进行hook时,需要用到Python和JavaScript,虽说以前用过一段时间,也很久没用了,最近需要花点时间复习下,也会顺便更新几期Python以及JavaScript相关的内容。再之后,可能才轮到Android的Smali语法部分的更新,总的来说,5月接下来会更加忙碌了。
参考资料
《Android软件安全权威指南》 —— 丰生强