前言
因为某个机缘,我拿到一个赛车app,玩了一会想买个装备,居然要我掏钱包,作为一名cracker,我觉得我的尊严受到了严重的蔑视(无奈钱包空空),我觉得要捍卫我那脆弱的玻璃心(钱包),所以,开干吧。我搜索了网上相关的帖子,发现这个apk的破解都是讲了关键点都在哪里,没有具体的关键点查找思路,所以我重新自己破解了一次,中间多次误入歧途,我把自己的详细思路发在这里,与所有的菜鸟共勉。
工具
Android killer v1.3.1.0 -- 用于搜索字符串和重打包,下文使用缩写AK
Apktool Box v1.6.4 – 用于获取调试启动命令,下文使用缩写AB
小米5 MIUI9.5.1.0 android7.0 – 用于运行apk
Android Studio 2.3.3(安装 smalidea-0.0.5插件) – 用于调试smali源码,下文使用缩写AS
Jadx-gui 0.7.1 – 用于查看smali反编译源码,其实AK也有反编译功能,但是AK的反编译有的地方没有jadx好看,我发现switchcase结构AK总是翻译成ifelse结构。
搭建调试环境
重打包生成可调式APK
使用AS调试smali需要apk设置调试标识(不然AS会显示错误:Unable to open debugger port (localhost:7800): java.net.SocketException "connection reset"),用AK打开apk,从工程管理器栏打开文件androidmanifest.xml,在application项添加如下代码:
1
|
android:debuggable="true" |
如下图:
然后选择菜单android->编译,重打包APK。并安装到米5里面。
调试启动apk
使用AB打开apk,点击启动命令按钮生成调试启动命令,
将上图中下面的命令拷贝到cmd中,启动apk,手机中apk会停在调试启动界面。然后查看apk的进程PID,
然后使用adb命令转发调试到tcp端口8700,如下:
接下来,使用AS建立smali源码的工程,并配置调试端口,这部分内容网上有现成的教程,这里就不多说了,建立完了之后,点击调试按钮,AS下面显示日志如下表示启动调试成功:
到此,调试环境搭建成功,下面开始破解。
破解过程
思路一:错误信息提示
老思路,先是试用一下。点击游戏的购买,会弹出一个购买的框,需要输入手机号和验证码,这里随便输入一个手机号和验证码,点击确认支付,不出意外会失败,并弹出一个提示框:
好了,这是我的第一个第一个失败点。我的思路是,既然它给了这个错误提示,那么显示这个错误的地方肯定有支付成功和失败的逻辑,那么我修改了这个逻辑,就可以了。OK,下面就是如何找到这个判断的逻辑。没错,字符串查找,最传统的方法,在android里面搜字符串,不能直接搜汉字,需要将它转为unicode码,然后将“短信验证码验证失败”转换为unicode码,则为“u77edu4fe1u9a8cu8bc1u7801u9a8cu8bc1u5931u8d25 ”,结果没有找到,好吧,是不是字符串拼接出来的呢,短一点,搜“短信验证码”,然后有了搜索结果
不好意思,我是小菜看不懂smali,将其转换为java源代码,
原函数太大了,这里只贴了一部分,看着逻辑真不像,那么到底是不是这个逻辑呢?验证的办法很简单,在这里下个断点,然后,没有断下来。那么在函数入口下个断点,还是没有来。好吧,这个思路以失败告终。后来跟踪代码我才知道这个支付框是从so里面弹出来的。呜呜。。
思路二:网络帖子借鉴
之前搜过类似的帖子,帖子说是搜字符串“支付成功”,所以我将字符串转化为unicode码,到AK里面去搜索,然后搜索到三个结果:
同样,验证这里是不是判断是否支付成功的逻辑代码,在每个字符串所在的函数头部设置断点,这里没有在字符串所在的位置设置断点是因为我是不会掏钱包的,所有的操作都是支付失败的操作,支付成功的代码逻辑肯定是不会来的,所以在函数头部设置断点,然后,点击确认支付没有断下来,但是当我点击关闭按钮时,却成功的断在了第一个函数中。这是意外的惊喜。然后使用jadx查看这个函数的逻辑:
代码的逻辑很清晰,检查参数resultCode的值,然后判断是否支付成功,所以这里很有可能就是我要的关键代码逻辑。验证一下,把所有的switch结构都改成到case1分支。Smali代码的语法是把所有的case放到一块,如下
上面代码的意思是将switch里面的值与1比较,结果为0则跳转到pswitch_0标号,结果为1则跳转到pswitch_1执行,所以这里把所有的标号都改为pwtich_0,并在下面添加一行,这样默认情况也会跳转到pswtich_0,即三种不管resultCode为何值,都会跳转到成功的分支,如下:
最后,编译重打包,安装测试,成功,OK。至此,成功破解了这个app的内功功能。
其实,当初我并没有按下支付框的关闭按钮,所以我走的是另一条思路,如果看到这里你还有耐心,可以看下我的弯路,哈哈。
思路三:我的弯路
当在三个“支付成功”字符串所在的函数设置断点,点击手机上确认支付按钮时,三个断点都没有来。然后我换了搜所的字符串为“支付失败”,然后搜到6个结果
我在6个结果的字符串所在的函数头部设置断点,点击确认支付按钮,结果所有的断点都没有来。后来我就去网上搜帖子,见到有帖子说“支付失败”所在的一个函数checkPayResultTest的调用者在用户点击购买时被调用。所以我转变了思路,放弃了直接查找判断是否支付成功的代码,而是查找能在支付过程中会被执行到的函数,然后我可以从这个函数跟踪到判断支付的代码。使用jadx打开apk,定位到函数checkPayResultTest,右键find usage,
然后查看其调用函数,
在此函数的头部设置断点,当我点击购买时,程序断在了这里。这是个喜讯。此时,我发现手机上的支付框还没有弹出来,所以我想,我可以从这里跟踪下去知道支付框弹出,然后找到确认支付按钮的回调。于是我一路F7单步步入跟进去,当调试跟踪到下图中的红线标识部分时我跟不进去了:
通过查资料我才知道,这是jni调用,我这才知道了,为什么我之前设置的失败断点都没有来,原因最后掉用到so库里了。那怎么办,难道我要算调试so文件么?然后我仔细观察了这个函数调用,发现这里有个回调,于是我找到回调定义的地方,
哈哈,是不是很眼熟,在回调里面设置断点,然后这次我鬼使神差的点击了支付框的关闭按钮,然后断下来了。哈哈。再然后的操作就如同上个思路一样了。这就是我的弯路了。
思路四:程序的“漏洞”
这里怎么说呢,其实也不算是程序的漏洞,而是程序员的一种编程习惯。我称之为“漏洞”,通过这个“漏洞”可以定位到支付功能必须执行的某些代码。思路是这样的,我发现在程序的代码中有很多调试日志输出,我如果能修改程序输出日志,那么当我使用支付功能的时候,相关代码会输出相关调试信息,然后我搜索输出的字符串,不就可以定位到支付功能一定会调用的代码吗!
首先,随便找一段代码,看看它是如何输出的,
从上图可以看出,调试信息的输出是通过一个叫LogUil.iT函数实现的,找到这个函数:
这里会看到,代码中有个DEBUG标志,如果此标志为真则输出调试信息,然后找到这个标志赋值的位置:
本来以为这个标志是会在构造里面赋值的,没想到居然是判断sdcard路径下有没有alg,哈哈,那就更简单了,我在手机的sdcard下面放了一个alg文件,
然后打开DDMS,启动程序,点击购买,DDMS日志视图里面发现如下信息:
到AK里面去搜索字符串“支付接口被游戏调用”,哈哈,有一条结果:
转到java源代码:
在AS中对应位置下断点,程序成功断下来啦。哈哈。
总结
虽然走了很多的弯路,但是也发现了很多有趣的东西。例如通过日志来定位代码,so中弹出对话框是无法在java中找到关键点的,要多试几种别的操作。
其它
附件太大,这里给个链接吧:https://pan.baidu.com/s/1P40TZLvJXV727-mlJLNIdA
本帖子如果引起任何其它负面的影响,请版主直接删帖即可。
如果转载,请声明来自看雪。