因一纸设计稿,我把竞品APP扒得裤衩不剩(下)

8,953 阅读15分钟

严正声明

  • 1、相关破解技术仅限于技术研究使用,不得用于非法目的,否则后果自负!
  • 2、笔者仅出于对逆向技术的好奇,无恶意破坏APP,尊重原开发者的劳动成果,未用于商业用途。

0x5、狩猎的时候到了 => 调试定位

  • 经过上一章的「淬炼」,相信大部分「简单APP的源码」你都信手拈来了;
  • 但古语有云“弱水三千,只取一瓢”,在品尝别人代码时,可能会遇到很多美味的“奇淫巧技”
  • 但有限精力和时间,使得我们无法一一品尝,抓住需要的一样就够了;
  • 美人乡英雄冢,时刻保持清醒「知道自己要的是什么」,以免自己深陷其中(钻牛角尖);
  • 快速定位到目标代码,弄清逻辑,完成核心代码的抽取

接着说下笔者常用的一些「调试定位技法」,没有优劣之分,混合使用快速找到 G点(关键代码) 即可~


① 资源定位法


最简单,却最高效的方法,可以通过「一行文字」「一张图片」「一个音视频」完成定位,「无脑全局搜索!

记得先把apktool反编译后的res和asserts文件夹和jadx反编译后的代码丢到一起! (也可以使用我的批处理反编译脚本自动搞定)

举个例子:突然觉得竞品的对话框有点好看,但是好懒,不想自己写:

Ctrl+Shift+F 全局搜「快去登录吧」或「登录后会解锁更精彩的内容哦

打开DialogUtils类,6个参数,不难定位到第三个a方法:

跟下CommonDialog类:

直接定位到了布局文件:

复制粘贴,改点东西,精美对话框get√


② 获取到当前Activity


ActivityFragment 也是非常好的切入点,通过简单的adb命令即可获得栈顶(当前) Activity的相关信息:

# 获取当前栈顶Activity的名称
adb shell dumpsys activity | grep "mFocusedActivity"

# 你可以通过alias为上述命名设置一个别名:
alias ada="adb shell dumpsys activity | grep 'mFocusedActivity'"

效果如下图所示:

你可以可以用:adb shell dumpsys activity top > info.txt 把Activity详细信息打印到文件中。
也可以安装「开发者助手」直接获取到(还可以看到可能涉及的Fragment):

还可以通过「反射大师」获取到:


③ Android Device Monitor 跟踪方法调用


旧版的as是有这个东西,新版把入口给隐藏了,直接来到「android-sdk/tools」目录下,找到monitor,运行即可:

左侧选择待调试进程:

觉得跟踪得差不多了,就点击②那个按钮停止,右侧会出现如下所述的方法调用流程:

然后你就可以一步步跟了~


④ Smali动态调试


Apktool反编译apk后,生成的是「Smali」文件,而非「Java」文件,Smali 是一种虚拟机指令语言。
Jadx反编译dex后虽然生成了Java代码,看看可以,是不支持调试的,而Smali是可调试的,在AS中想调试Smali的话,还需要安装「smalidea」插件,插件的下载,可移步至:bitbucket.org/JesusFreke/…,下载后,AS安装一波插件,安装完毕重启即可。

注意:现在是你想调试别人的APP,那首先,要先把别人的APP变成「可调教状态(debugable)

一个看似简单但却很繁琐的方法:

把目标APP的 AndroidManifest.xml 中的 debuggable属性设置为true,签名重打包。

如果逆向的APP有多个,这应该显得好繁冗,不如直接把「手机上所有应用都变成可调试状态」,方法如下:

  • Android 7.0以下安装了Xposed的
    下载安装「BDOpener模块」:github.com/riusksk/BDO…,启用插件重启即可。
     
  • Android 7.0及以上安装了Magisk的
    Magisk里下载安装:「MagiskHide Props Config」和「Busybox for Android NDK」然后重启。
    接着终端 adb shell,输入 props 进行设置,找到:Edit MagiskHide props的选项,比如这里是4:

回车,会提示是否设置为0,输入y,接着设置完,提示是否重启,输入y重启,接着打开AS 的 Logcat 就可以看到所有的进程了:

设置完可调教,就可以把smail导入AS中,导入过程需要注意下哦,不是直接open!打开AS在导入脚本反编译后的apktool文件夹:

导入后,点击顶部菜单栏的这个位置,「Edit Configurations」,点「+」选择「Remote

随便填个名字,然后设置一个端口号 (自己定,auto.sh脚本那里要配合着改哦!)

命令行启动进程调试等待模式」比较繁琐且重复,我直接写到一个auto.sh文件中:

#!/bin/sh
if [[ $# == 0 ]]; then
    echo "./debugTool [package] [activity] "
    exit 1
fi
package=$1
activity=$2
adb shell am start -D -n ${package}/${activity}
varId=`adb shell ps | grep package| awk '{print $2}'`
adb forward tcp:4567 jdwp:$varId

接着命令行键入:./auto.sh 包名 入口Activity,示例如下:

./auto.sh com.xxx.xxx .MainActivity

APP进入待调试等待模式后,点击顶部「Attach Debugger To Android Process」,下好断点,选择调试进程:

接着等待程序运行至断点,Debugger就可以看到相关的变量了:


⑤ Smali语法的学习姿势分享


Smali的语法比较繁杂(感觉比我以前学汇编还麻烦),网上的教程不是一般的凌乱,有机会我整理一份吧…

你真的想学Smali得话,建议边「实操边学,即用即插(查)」,idea有个插件「intellij-java2smali
Github地址github.com/ollide/inte…,支持把「正确项目中的Java」转换为smali文件。
注意:正确!和 项目!项目有错的话,转换直接报错,单个独立的java文件也是不能转换的!用法很简单:

打开需要转换的java文件,依次点击顶部菜单栏的 run -> Compile to smali

说个bug:

官方说是支持kotlin转的,但是新版的AS会报错:Error:Kotlin: Unsupported plugin option: org.jetbrains.kotlin.android:enabled=true,估计是插件的用到的kotlin相关依赖没升级,目前无解(等官方维护,或者自己改插件源码);

你可以选择创建一个纯Java的项目,程序没报错,可以跑,但是转换的时候,又会出现一个bug:

java: 程序包R不存在 ,R文件找不到,尽管你可以在build目录下找到它,猜测是没有适配新版本AS的R文件路径,一个治标不治本的方法,把setContentView()一行的代码注释掉。

另外,个人感觉AS上看 smali并不是很爽 !既然觉得不爽,那就换个体位,是吧?举几个例子:

  • 男的觉得不够刺激,就用「推磨式」;
  • 女的觉得不够刺激,就用「猫式」;
  • 男的想快感加倍,就用「后入式」;
  • 女的想自由把控,就用「传统女上位式」;
  • 想丰富彼此情感交流,就用「传教士体位」;
  • 想延长时间,就用「面对面侧卧」;
  • em…体位有很多,就不一一列举了,只是想告诉大家:哪种方式适合自己就用哪种~

如果你用「VS Code」的话,可以安装一个「Smalise」插件:Smali语法高亮 支持变量或者方法名的定位:

除此之外,还可以直接看「Unicode转换后的中文」、「变量信息」、「定位到定义处


⑤ 动态调试so

目前不会,以后有兴趣或者需要学再去学吧,学完了再来补全~


0x6、是时候表演真正的技术了 => 破解


学完调试定位技巧,我们已经能快速找到G点(核心代码),再来玩点「刺激」的吧:

看到,有人在上一篇《因一纸设计稿,我把竞品APP扒得裤衩不剩(中)》有人在评论区留言对「邻居的故事」感兴趣,我用简单的几句诗来概括下后续剧情吧:

男子加班回家晚,草草了事交公粮
妹子觉得略失望,欲求不满暗自叹
邻居首捷心暗爽,唯美画面反思量
妹子软萌待开发,制服跳蛋震动棒

觉得妹子过于乖巧,邻居想二次开发妹子(APK),于是用上了三个道具,这些道具怎么用等等再说。说回这一节,在使用别人APP的时候,你可能会想:如果是我的做的APP,我要…!建议别想,直接动手吧!加点什么(自定义UI) 或者 改点什么(付费变免费,开隐藏关卡),当然自己暗爽就好了,可别到处传播,「破坏计算机信息系统罪警告」。以竞品APP关卡付费为例,讲解一波:

问题描述如下:

  • 1、首页课程有锁,点击:弹出需要开通完整课程的对话框;
  • 2、点击下一关,无法切换到下一关,提示:敬请期待;
  • 3、50-100节的课程带锁,点击提示:努力建造中…;
  • 4、课程等级除了L1外,其他级别带锁,点击提示:敬请期待;

① 制服——调试下断点直接改标志位


怎么动态调试Smali上面已经介绍过了,具体调试技巧可见《逮虾户!Android程序调试竟简单如斯》,来到APP中的如下页面:

点击50-100 会出现「努力建造中」的提示语,打开开发者助手:

直接定位到 CourseChangeFragment

看下this.n是啥,以及里面的d是什么:

行吧,这个d就是图片是否高亮,1为高亮,接着看下this.n在哪里完成赋值:

而关卡选择,直接全局搜「选择等级对应的课程地图」定位到ChangeLevelDialog,全局搜,定位回CourseChangeFragment

同样,看下this.k是啥,在哪里完成了赋值:

跟到ChangeLevelDialog,可以看到,提取的是里面的CourseLevelInfo

跟过去看看:

行吧,这个c就是关卡的状态1为可用状态,行吧,找下断点的时机,搜下控件:

控件id为0x7f0f0363,回到CourseChangeFragment.smali搜:

直接定位到sswitch_2:

这里圈住的地方,其实是中文的Unicode码,不信,转换下你就知道了:

定位到下面这段代码:

接着讲解一波这段代码:

iget-object v0, p0, .../CourseChangeFragment;->n:.../$CourseLevelMapInfo;
# 引用对象,p0为该变量所在类的实例,即this,把n的值放到v0寄存器中

if-eqz v0, :cond_1
# 判断v0是否为0(null),是的话跳到cond_1

iget-object v0, p0, .../CourseChangeFragment;->n:.../$CourseLevelMapInfo
# 同上上一行代码把n的值放到v0寄存器中

iget v0, v0, ...$CourseLevelMapInfo;->d:I
# 引用v0寄存器中保存的n对象,把n.d的值放到v0寄存器中

if-ne v0, v1, :cond_1
# 判断v0寄存器和v1寄存器中的值是否相等,不等跳到cond_1

如图下完断点,开始调试smail,来到关切页面,点击后进入调试模式:

右键d,选择Set Value,把值设置为1,接着往下执行断点,可以看到「隐藏关卡」开启成功:

啧啧,有点意思,其他几个破解也是「如法炮制」,不过每次都要这样调试显得太麻烦了,有没有一劳永逸的方法?当然有,写个Xposed插件耍耍吧~


② 跳蛋——编写Xposed插件


关于Xposed,之前写过一个系列,就不再详细介绍了:

简单说通过它,你可以:

修改变量的值,Hook方法(执行前后做一些修改),显式去执行一些方法等

我们需要先定位到「标志位的源头处」(一般是方法),在方法执行前或后,进行一些修改,过程比较琐碎,直接给出插件代码吧!先是3、破解课程选择的锁

接着是:4、破解关卡选择对话框

再接着是:2、破解首页下一关切换

最后是:1、破解首页所有关卡

把写好的Xposed插件安装到手机上,Xposed Installer勾选,重启,接着打开APP,看下效果:

行吧,插件编写完毕,但是毕竟不是每个人的手机都会Root装Xposed,索性破解后二次打包吧。


③ 振动棒——apktool二次打包


使用apktool二次打包的流程很简单难的是改Samli代码

改smali => apktool b 重打包 => jarsigner/apksigner 二次签名

行吧,动手撸smali,定位到 CourseChangeFragment.smalionClick函数 的判断处代码:

前面这个数字是「十六进制的资源id」,可以直接把jadx反编译出来的id转换下:

分别对应:7f0f03607f0f0363,对应跳=> sswitch_3sswitch_2,先去sswitch_2处,可以看到源码中做了判断,如果不满足return,满足的话继续走,这里可以钻个空子,「直接把判断相关的代码删掉」,只保留下图圈着的代码:

接着拖过如下命令把项目二次打包回apk:

apktool b xxx -o xxx.apk

接着用「jadx-gui」打开apk看看代码:

啧啧,可以,接着需要对APK进行二次签名才能安装到手机上,可以通过「jarsigner」或「apksigner」完成签名。玩法如下:

# 如果你没有签名,可通过下述命令创建,回车后会让你输入密码,输完无脑回车即可
keytool -genkey -v -keystore 文件名.jks -keyalg RSA -keysize 2048 -validity 10000 -alias 签名别名

# ====== jarsigner签名 ======

#  1、输入下述指令,回车后输入密码
jarsigner -verbose -keystore 证书 -signedjar 签名后.apk 签名前.apk 证书别名

# 2、报错的话可能需要最在-keystore前添加下述参数:
-digestalg SHA1 -sigalg MD5withRSA

# 3、Tips:可以使用下述命令查看APK是否已签名
jarsigner -verify xxx.apk

# ====== 使用谷歌提供的签名及验证的专用工具:apksigner ======
# 位于:Android SDK/build-tools/SDK版本/apksigner.bat

# 1、对齐
zipalign -v -p 4 xxx.apk xxx_aligned.apk    

# 2、签名
sign --ks 证书 --out release.apk origin_aligned.apk

# 3、验证是否签名成功
apksigner verify release.apk

把签名后的APK安装到手机上,打开看下效果:

虽然还有锁的那个图标,但是却可以点击切换关卡了,可以,继续折腾,选择关卡部分:

点击时判断 courseLevelInfo.c == 1,那么我们找到初始化courseLevelInfo对象的地方,在这里直接把c设置为1即可,不难定位到:

在箭头所指的代码后,添加一句:courseLevelInfo.c = 1;,就好了,但是你需要把这个java语句转换成smali!这个转换可没你想象中那么简单哦!打开ChangeLevelDialog.smali,定位a方法:

不难定位到如下位置,我们需要在.line 添加赋值代码:

赋值操作需要用一个寄存器保存1(0x1),然后用iput进行赋值:

接着apktool b打包,用jadx-gui打开看看:

行吧,转换后除了行号,没问题,直接jarsigner二次签名,adb安装到手机上验证下:

可以,nice,把剩下的首页下一关和所有关卡也折腾下吧:

删除赋值语句,对应Smali中:

重打包,jadx-gui打开查看:

每个关卡锁定位到:MainHomeworkAdapter$CommonViewHolder类 (美元符号代表内部类)

在如图所示的地方插入赋值语句,

重打包,jadx-gui打开查看:

运行下看下效果,结果和xposed大同小异,但是没装Xposed的手机也能直接使用~

然后说一点:

不是所有的APK都能直接二次打包签名!有些会做签名校验,打开签名不对,直接闪退,Java层可以Hook掉检验的方法,Native层的只能去改So;还有些加固的APK,尽管可以导出dex,但是二次打包的话还需要修复onCreate函数(如360加固宝)。所以大部分时候,我们都只是能看看代码而已~所以Xposed还是香,Smali写起来是真的繁琐,复杂逻辑的代码会写到想哭,每次看效果都要重打包…


④ 移动端逆向利器——MT管理器2


PC端逆向,需要下载一堆反编译工具(apktool, jadx等),一些更好用的工具(AndroidKillersmali2java)等还不支持mac,
所以它来了—— MT管理器2:移动端「双窗口文件管理」和「强大的APK编辑功能」,可在手机上高效地修改安卓软件,界面如下:

官方文档中已有详细的讲解,就不哔哔了,读者自行查看文档把:MT管理器,另外还有个相关的逆向论坛:MT论坛

「Tips:基本上可以说是付费的,部分功能需要会员才能使用~」

笔者有「记账」的习惯,用的是「X记账」,界面粉粉的,挺Gay的,就是每次打开弹出的「广告闪屏页」令我很是反感。行吧,就拿它来开刀吧,顺带给大家演示下「MT管理器2」怎么耍。

点击左侧菜单栏的「跟踪Activity」,开始跟踪,接着打开软件,然后回到MT管理器,可以看到Activity的启动记录:

先进LogoActivity,再进MainActivity,点击左侧菜单「安装包提取」:

提取后定位到/MT2/apks目录下,点apk,「查看」,打开AndroidManifest.xml也看到:

行吧,入口是就是LogoActivity,点击classex.dex打开,两个dex都选择:

接着按照包名,打开LogoActivity,点击右上角,「转成Java

转换后的Java代码和Jadx的一样:

见名知意,圈着的就是显示广告的View(@BindView祖传黄油刀),想把广告禁掉,把相关加载的代码都删掉就好啦:

和PC一样改smali,不过比起PC,改完,直接就可以转Java,方便好多,修改完后保存,返回会弹出如下对话框:

但是,打开二次打包后的APP:「广告内容没了,但是却卡在了图标页,要点击右上角才能关闭
我擦,估计是AdsView里还有什么逻辑(比如广告加载完才跳转),一不做二不休,直接把这个AdsView给去掉吧,
删布局文件里的,再删LogoActivity相关的代码,正当我准备开始这样做的时候,我看到了这些:

2333,为了避免「空指针异常」,调用adsView相关方法前都做了「判空」可以,很好的开发习惯!

但也方便了我(心里乐开了花

我直接在页面初始化的地方把这个「adsView设置为null」不就好了!

定位到如图所示位置,在初始化View的地方把View置空,没毛病

打开initView函数对应的smali代码,编写把adsView置空的smali代码:

二次打包,覆盖安装看看效果:

对比下没去广告的效果:

当真是日了狗,点击跳过的按钮很小很容易误触,而且点了还不是立马关闭!改完后瞬间舒服了~
对了,学完这里你可以出去吹牛逼了:《两行代码去掉APP闪屏广告页》


0x7 、死亡如风,常伴吾身 => 爬取APP数据


其实,本来没有这一节的,毕竟,APP源码都被你扒得裤衩不剩「任意蹂躏」了,但是有一点:

她并不属于你!

如果哪一天,这个APP下架了,服务器撤掉了,你所做的一切就「付之东流」了。

与其在以后的某一天说暗自感叹

祝你岁月无波澜,敬我余生不悲欢

不如想办法让她真的属于你:

说到底:APP只是一个资源展示工具,而后台则是一个资源调度工具,关键的还是「资源」!

把资源搞到手,APP和后台随便你折腾,接着抓包分析一波,然后写个爬虫把每一关的数据保存起来~
直接通过 Fiddler,Charles等 抓包工具抓取一波数据,移动端如何抓包,可参见我之前写过的文章:《忘了他吧!我偷别人APP的代码养你》,此处就不再复述了,直接开抓:

好几个接口,筛选出我们需要的接口:

规则比较简单,接口没做用户校验,付费资源链接也有返回,只是在APP端做了限制。
直接顶一个一个存储数据的类,把爬取到的数据塞到MongoDB中:

出于反爬虫的习惯,使用「代理ip」访问,关于代理IP如何获取,以前说过,就不哔哔了,这里用阿布云的代理隧道,1块钱1小时,美滋滋,相关配置如下:

除了使用代理ip,「设备名设备id版本」都随机生成:

接着编写请求和解析数据的方法:

最后开启一波多进程访问:

多进程+可用性比较高的代理,一分钟不到就爬完,美滋滋:

lessonDay的顺序有些不对,排序查询一波:

行吧,数据爬取完毕,剩下的接口就不一一演示了,对了,你还可以写个脚本把所有的音视频资源,图片爬到自己电脑,或者同步到七牛云等CDN站点,接着遍历替换一波基地址。另外,如果抓包分析不出规律,你也可以从APP入手~


0x8、小结


历经坎坷,终于把最后一章给肝出来了,写这个系列的初衷是想扒一些竞品APP的好看效果,结果活生生写成了逆向,也算是自己一个阶段性学习的总结了,逆向的水挺深的,有机会和闲情再耍耍把,就这样,接下来,准备写个「面试准备周报」系列,以督查自己好好准备面试,敬请期待。


本系列最后一次送书,意思意思送「一本自己写的Python爬虫入门书」,评论区留言抽,包邮,下周五抽~ (可能延迟,但一定抽,无暗箱~)

谢谢各位一直以来的支持和厚爱,爱你们~

Tips:本你想系列用到的工具,都有给出比较官方的下载链接!!!你也可以到公号
抠腚男孩」输入000,回复对应序号下载,谢谢~


恭喜「weihuawei」童鞋中奖~抽奖录屏稍后给出~