安卓逆向-一次未完全完成的游戏破解【修正】

i春秋2020-02-20 06:42:13

作者:SisconElang

i春秋社区

前言

前不久在i春秋看了几帖安卓逆向的帖子,这次也用到了几个那里面提到的函数,就在这里附上链接https://bbs.ichunqiu.com/thread-23303-1-1.html)吧,碰巧的是最近我一朋友在拉着我玩一比较,额 “垃圾”的游戏吧,于是就有了这次为时三天的逆向分析过程(最近比较忙,所以...)

用到的工具:

AndroidKiller (需安装JDK)
夜神模拟器
最新版apkTool:apktool_2.2.3,这里顺便附上链接吧2.2.3版本的apkTool(戳阅读原文下载)

首先把app给下载下来,百度找到 “*族应用商店”,然后搜索**骑士(不要问我为什么在*族下游戏,因为我手机是这个牌子...)。
当然,其他端的我也不知道能不能,反正我只用这个端的做了。接下来,在我们的虚拟机中打开这个app,点击新游戏,然后等待初始化画面的加载完毕。

初始化之后 我们就可以看到这略显“可爱”的游戏界面加载进来了,跳过引导的步骤,我们点击右上角的暂停按钮,然后点小房子回到主页,再点击新游戏就进入到了真正的页面了,这里我就不截图了
跟着拇指走(这里也是引导...),在拇指消失不见后再重复上面的步骤一次,重新进入到这个界面

这个时候,点那些灰色的人,就会出现这样的画面

熟悉的¥,点解锁

这个东西就是这个游戏最变态的东西了,也是我们的目标

然后我们关掉这个订单的弹窗,会出现一个提示

关键字:订单取消
好了 逆向的主题开始了


华丽丽的手动分割

祭出神器AndroidKiller
先查壳

选到下好的安装包

好了,既然没壳,那就直接上了!
点击 打开,选到下载好的安装包,当然,建议先备份一个

打开后,等待反编译的完成

这就是反编译后的目录了,箭头所指的就是入口,也就是程序中的main函数了,我们先不管,切换到搜索tab

将我们的关键字转成Unicode编码后搜索

可以看出来是在main里面,我们点进去,往上翻,找到方法名,也就是方法的开始

找到了这个方法,onPayResult,我们可以看到第757行有一行的Unicode编码的提示文字,鼠标放上去可以知道是“支付成功”的样式
那么,我们用Java源码来分析

点击通过Java源码查看,找到那个onPayResult的方法

我们可以看到关键的提示和对应的调用的方法,我们熟悉的Success和Fail,那么,用最直接的方法,把Success替换成Fail
画面转回我们的反编译工具,找到onPayFail,当然,可以直接搜索,全都替换成onPaySuccess

并且,为了能直观的看到效果,我在提示上面加上了行号,不能debug就将就着用吧
那么 编译一下,并且把虚拟机上的应用删掉后重新安装
顺便提一下,在使用AndroidKiller编译的时候,可能会遇到编译报错,提示AndroidManifest.xml错误的问题
这个时候,我们最新版的apkTool就有用了

把最新版的apkTool添加进来,并且修改默认的为自己添加的apkTool版本
好了 重新编译签名后,我们打开修改过后的app
再重复分割线前面的步骤
这个时候会发现,在关闭了订单的时候,会进行订单状态查询,并且充值的工作并没有完成,最后返回的依然是失败
那么,我们来分析源码
首先,找到onPaySuccess方法的声明的地方

可以看到搜索结果只有两个smali,第一个就是我们改的入口类,那么,肯定是第二个类了,我们点进去来看看

果然,我们用Java来看看源码

可以看到,成功、失败、取消、未知,这些支付后的方法都在这里,我们看到onPaySuccess下的方法体
两行代码。按照方法命名来看,第一个调用的应该是检查订单,第二个是清除了,那么,转到checkOrder这个方法

可以看到有三个重载了的checkOrder方法,onPaySuccess调用的是两个参数的方法,继续跟进,发现到了第二个checkOrder

前面是日志和提示,不管,继续跟进,到了OrderCheckTask类的StartTaskOnMainThread方法,跟进分析

发现两个方法,但是两个方法都是找不到关键信息的,这一段跟进中断
后来我仔细看了看main里面的代码,因为毕竟是入口,从上往下看,我找到了doPay函数,分析

可以看到这是在各种赋值,在这里面我们能看到有两个特殊的字段赋值,并且还有一个类型转换的步骤,程序猿的直觉告诉我,这可能可以利用
我们尝试一下修改paramString4的值,转到反编译代码,找到doPay函数的入口

从这里开始,我们来分析,很多代码可能看不懂,不重要,找敏感有用的东西
继续往下找,可以看到

100.0 valueOf 似曾相识?没错,类型转换!

那么,既然找到了地方,继续查看函数
现在这里还不能确定到底改哪一个,我们继续往下走,找到字符串的拼接

熟悉的字眼,好,找对地方了,那么,一步一步来分析

[/align][align=left]const-string v18, "total_price="

[/align][align=left]invoke-virtual/range {v17 .. v18}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;

[/align][align=left]    move-result-object v17

[/align][align=left]    move-object/from16 v0, v17

[/align][align=left]    move-object/from16 v1, v16

[/align][align=left]    invoke-virtual {v0, v1}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;

[/align][align=left]    move-result-object v17

[/align][align=left]    const-string v18, "&"

[/align][align=left]    invoke-virtual/range {v17 .. v18}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;

move-result-object v17

[/align][align=left]    invoke-virtual/range {v17 .. v17}, Ljava/lang/StringBuilder;->toString()Ljava/lang/String;

[/align][align=left]    move-result-object v17

    move-object/from16 v0, v17

    invoke-virtual {v5, v0}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;

    .line 77

    new-instance v17, Ljava/lang/StringBuilder;

[/align][align=left]    invoke-direct/range {v17 .. v17}, Ljava/lang/StringBuilder;-><init>()V

第一个append可以看出来是StringBuffer对象添加字符串total_price(不要问我怎么知道的,把鼠标放在头调用上会有操作提示)
那么,第一个append就可以不管了,我们来看第二个
可以看到,将v16的值赋给v1然后传参,我们找到v16(怕太乱可以从方法头开始找)

可以看到,熟悉的valueOf,分析函数执行,将100.0赋值给v20,再将v20的值转换结果成double,然后传入v2,也就是D,最后用valueOf取D的值
取到值之后,将结果返回给v16,好 v16的赋值找到了,可以看出这里的v16的值就是计算后的total_price了
那我们尝试一下给v16赋值

给v16赋值字符串,值为0.01,好 我们编译运行一下 看有没有效果
可以看到,订单金额已经被改成了我们修改过的值0.01
当然,我的目的肯定不止如此了,因为终究还是要进行支付,我们继续从doPay方法跟进,找到下面

可以看到有一个MZ什么什么游戏中心的接口,我们跟进找到调用的singlePay方法

跟进找到sWorkQueue的对象

就是这个了,我们跟进,到g这个类
但是最后,并没有发现什么有价值的东西,尴尬,线索中断,但是不放弃
突然看到上面有个IGameCenterPlatform的引用,怀着查探的心态点进去看看,被我发现了有用的东西!

可以看到几个方法,这是接口,那么我们尝试着找实现这个接口的类,在同一个目录下发现了一个叫GameCenterPlatformProxy的类,跟进

的确是实现那个接口的类,看到这里加载了一个类,我们来找找,然而我并没有找到,那么猜测是远程调用的,这样就看不到核心代码了,尴尬
这个时候换个思路,既然我没办法修改订单进行时的运行代码(至少我现在不知道怎么做到),那我就改本地的数据文件呗...

==================走起==================
我们都知道,单机游戏的数据是以文件保存在本地方便读取的,那么我们从虚拟机进入到游戏的数据目录

打开夜神模拟器的文件管理,可以看到获得了root权限后的真正的手机文件目录,有个data,我们进去

还有个data,我们再点进去

翻到最后,可以看到这里有一个叫yuanqiqishi.game**的文件夹

可以看到有这么多目录,那么,数据文件到底在哪个文件夹下呢?其实很简单,可以用死方法,运行一下游戏,看哪个文件夹显示的修改日期是最新的就是哪个了

进入到这个目录下,以编辑器的形式打开这个xml

更改这个的值,我这里已经是更改过的了,改了之后保存,重新进入游戏

修改成功
就此,这个游戏的破解就告一段落了


点击阅读原文,一起玩耍

为了满足各位安全小伙伴日渐丰富的阅读需求,我们开始推出白帽日报系列~看完每天的文章后,可以继续聊聊热点趣事,学学安全技术新姿势,岂不快哉!话不多说,一起来看吧~

生活有别于小说,修仙不能长生,知识却能永存!

1.【今日话题】

嘿!春秋,你好

【在千万人之中遇见你所遇见的人,于千万年之中,时间的无涯的荒野里,没有早一步,也没有晚一步,刚巧赶上了,那也没有别的话可说,惟有轻轻地问一声:“噢,你也在这里吗?”对于春秋,正像我与此的缘分。】

https://bbs.ichunqiu.com/thread-24895-1-1.html



2.【思路分享】

记一次简单而又详细的项目渗透测试记录

【这篇文章主要教学如何从注入到服务器在到全身而退(适合小白,不要以为渗透了就完了,保护自身才是第一要务。有些事,一次就够了】

https://bbs.ichunqiu.com/thread-24833-1-1.html



3.【思路分享】

从编写知乎粉丝监控到漏洞挖掘再到盗号

【今天突然有一个想法,就是去写一个知乎的粉丝量监控。现在脑里有一个基本思维模型:找一个接口或者直接匹配页面的粉丝量->(自定义时间)刷新一次再次获取粉丝量对比上一次->输出增减粉丝量】

https://bbs.ichunqiu.com/thread-24853-1-1.html



4.【翻译】

一次对SNMP服务的渗透测试

【在本篇文章中,作者通过对其漏洞的分析和实践直观的让读者对其相关内容进行了解。对入门学员可能会有所帮助。】

https://bbs.ichunqiu.com/thread-24848-1-1.html