这是一款带壳的APP,打开之后要求强制升级最新版,否则无法使用,针对此APP可以进行脱壳后定位关键代码,然后重打包进行消除强制升级弹窗。
DEXDump三种使用模式脱壳
1、使用objection
加载frida_dexdump
objection -g com.xxx explore
plugin load /root/.objection/plugins/dexdump/frida_dexdump
plugin dexdump dump
2、直接运行
objection -g com.xxx explore
python3 main.py
会自动判断前台运行的App
3、利用pip安装后运行frida-dexdump
objection -g com.xxx explore
pip3 install frida-dexdump
frida-dexdump
脱壳成功后,发现生成了4个dex文件,然后搜索带有MainActivity
的dex文件
grep -ril "MainActivity"
Objection快速自动化定位
正常方式首先以开发者的角度来思考是如何实现窗口弹出功能
https://www.jianshu.com/p/18e1f518c625
一 activity以窗口形式呈现
二 Android:将activity设置为弹出式的并设置为透明的
三 Dialog
1、如果是弹出的activity,可以hook所有的activities进行启动查看
android hooking list activities
启动可疑的Activity,尝试能不能绕过,然后发现界面到了app信息页面,但是返回后还是要求升级的界面,说明此路不通。
android intent launch_activity xxx.ui.activity.About Activity
查看一个函数能不能hook,可以将它所有的类打印出来,然后过滤,如果有则可以hook
android hooking list classes
cat .objection/objection.log |grep -i window
发现有android.view.Window
,然后尝试hook
objection -g com.xxx explore --startup-command "android hooking watch class android.view.Window"
注:
也可以不需要
--startup-command
,进去app之后再hook也来得及
当点击“立即升级”发现会立即跳出下图内容,说明与升级框相关android.view.Window.getWindowManager()
2、尝试下dialog
cat .objection/objection.log |grep -i dialog
先hook看一下android.app.AlertDialog
android hooking watch class android.app.AlertDialog
发现点升级没有任何反应,故判断此API与升级框没关系
然后再尝试hook下android.app.Dialog
看有没有反应
objection -g com.xxx explore --startup-command "android hooking watch class android.app.Dialog"
点击新版本框空白地方会出现
点击“立即升级”出现
看到存在android.app.Dialog.setCancelable
(用返回键无法取消)
然后hook该方法
android hooking watch class_method android.app.Dialog.setCancelab le --dump-args --dump-backtrace --dump-return
找到对应位置,发现与界面上的版本号、文件升级相关联,从而定位到了代码的关键位置。
这里是明文,所以很明显就可以判断定位的对错。如果关键字符串做了加密混淆,搜索大法也就无效了,可以使用wallbreaker内存可视化漫游,所见即所得。
Wallbreaker内存可视漫游
wallbreaker四种模式:
classdump 查看一个类的结构
classsearch 根据类名来找类
objectsearch 查看一个类的实例内容
objectdump 查看对象的属性
使用objection加载Wallbreaker搜索值得怀疑的地方
plugin load /root/.objection/plugins/Wallbreaker
plugin wallbreaker objectsearch xxx.ui.fragment.dialog.UpdateDialogFragment
找到之后,打印该对象的属性
plugin wallbreaker objectdump --fullname 0x276a
看到xxx.bean.VersionBean$Version _a; => [0x2266]:
然后将其打印出来
plugin wallbreaker objectdump --fullname 0x2266
可以看到打印出的内容,与界面所展示的一致,验证了所见即所得原理。
所见即所得的代码定位思路
定位完之后,我们继hookxxx.ui.fragment.dialog.UpdateDialogFragment
android hooking watch class xxx.ui.fragment.dialog.UpdateDialogFragment
打印调用栈
xxx.ui.fragment.dialog.UpdateDialogFragment.b
android hooking watch class_method xxx.ui.fragment.d
ialog.UpdateDialogFragment.b --dump-args --dump-backtrace --dump-return
根据打印的结果看到
xxx.ui.fragment.dialog.UpdateDialogFragment.b
是从xxx.ui.activity.MainActivity.a
该类过来,然后定位该类,发现也是做了个条件判断。
修改源码重打包去强制升级
接着我们进行修改代码去掉升级框并重打包,首先因为是带壳的APP,无法直接使用apktool进行反编译,不然壳也会被反编译为smali。所以我们使用apktool保留classes.dex文件进行解包,然后删除apk原有的classes.dex文件,并将脱壳后的classes.dex放入
apktool -s d xxx.apk
rm classes.dex
按照文件大小重命名后放入该文件夹中
搜索extends Application
查找AndroidManifest.xml
内容,将android:name
的内容替换为xxx.base.App
将入口点改为脱壳后的入口点
然后回编译、首次使用需先生成keytool、签名
apktool b xxx
keytool -genkey -alias abc.keystore -keyalg RSA -validity 20000 -keystore abc.keystore # 生成keytool
jarsigner -verbose -keystore abc.keystore -signedjar xxxsigned.apk xxx.apk abc.keystore
测试可以成功运行后,我们接着反编译,搜索之前定位的类名含MainActivity
的smali文件,编辑查找UpdateDialogFragment
找到之后修改判断语句
apktool d xxxsigned.apk
tree -NCfhl |grep -i MainActivity
nano smali/cn/net/tokyo/ccg/ui/activity/MainActivity.smali
改完之后回编译、签名、运行
apktool b xxxsigned/
jarsigner -verbose -keystore abc.keystore -signedjar xxx2signed.apk xxx2.apk abc.keystore
发现已经没有了强制升级的弹窗。