动静态分析之去弹窗重打包

篇幅有限

完整内容及源码关注公众号:ReverseCode,发送

重打包

带壳应用重打包失败,需要先脱壳

objection的patchapk需要aapt、adb、jarsigner、apktool,其中apktool需要官网下载下,其他前仨在android-studio中就有,注意AndroidStudio版本号:

tree -NCfhl|grep aapt 全局搜索aapt

tree -NCfhl|grep jarsigner 全局搜索 jarsigner

apktool下载jar和编译文件后mv apktool* /usr/local/bin && chmod 777 /usr/local/bin/apktool*

1
2
3
4
5
ln -s /root/Desktop/android-studio/jre/bin/jarsigner /usr/bin
ln -s /root/Android/Sdk/build-tools/30.0.3/aapt /usr/bin
ln -s /root/Android/Sdk/build-tools/30.0.3/aapt2 /usr/bin
ln -s /root/Android/Sdk/platform-tools/adb /usr/bin
ln -s /root/Android/Sdk/build-tools/30.0.3/zipalign /usr/bin

获取到重打包Fulao2_2021-02-09.objection.apk,卸载fulao2后重新安装adb install -r Fulao2_2021-02-09.objection.apk

1
2
3
pyenv local 3.8.5
apt install aapt zipalign
proxychains objection patchapk --architecture armeabi-v7a --source Fulao2_2021-02-09.apk

虚拟机中没有native层无法安装

内存信息搜索与执行

1
2
3
4
5
cd /data/local/tmp && ./fs1428arm64  启动frida server
objection -g com.ilulutv.fulao2 explore
memory list modules 查看app加载的so模块
memory list exports libcipher-lib.so --json /root/Desktop/fulao.libcipher.json 导出库所有函数
android heap search instances android.graphics.Bitmap 获取内存中图片对象

获取内存中图片对象

1
2
3
4
android heap execute 257601546 getHeight --return-string  数字串是HashCode,获取图片高度
android heap execute 257601546 getWidth --return-string
android heap evaluate 257601546 打印带参数的方法返回值
console.log(clazz.getPixel(10,20)) 在控制台中输入js脚本,调用有参函数

启动activityservice

1
2
3
4
5
6
7
8
9
10
11
android hooking list activities  查看当前可用的activities
android intent launch_activity com.ilulutv.fulao2.me.news.NewsActivity 启动指定Activity
android hooking list receivers 查看当前可用广播
android hooking list services
android hooking watch class android.graphics.Bitmap hook图片所有方法
jobs list 查看hook了116个函数,ID为oj90asdc
jobs kill oj90asdc 取消hook
android hooking watch class_method android.graphics.Bitmap.nativeRowBytes --dump-args --dump-backtrace --dump-return hook指定方法
android hooking watch class android.graphics.BitmapFactory hook图片所有函数
android hooking watch class_method android.graphics.BitmapFactory.decodeByteArray --dump-args --dump-backtrace --dump-return 直接得到图片数据流
android hooking watch class_method android.graphics.BitmapFactory.decodeByteArray --dump-backtrace 获取显示图片前解密的函数,去jadx中查看该方法逻辑

trace中获取解密函数

objection自带集成flask api,可以通过rpc跑预置的命令,也可以自己传脚本到手机上跑

1
2
3
4
5
6
objection -g com.ilulutv.fulao2 explore --enable-api
curl -s "http://127.0.0.1:8888/rpc/invoke/androidFileCwd"
curl -s "http://127.0.0.1:8888/rpc/invoke/androidHookingListActivities"
curl -s "http://127.0.0.1:8888/rpc/invoke/androidHookingGetClasses"
url -s -X POST "http://127.0.0.1:8888/rpc/invoke/androidHookingGetClassMethods" -H "Content-Type: application/json" -d '{"clazz": "android.graphics.BitmapFactory"}'
curl -s -X POST "http://127.0.0.1:8888/rpc/invoke/androidHookingWatchClass" -H "Content-Type: application/json" -d '{"clazz": "android.graphics.BitmapFactory"}' 启动hook BitmapFactory,job lisk可以查看hook

frida&flask

脱壳

objection patchapk –architecture arm64-v8a –source movetv.apk 重打包出错,加壳(腾讯)应用无法objection patchapk

objection -g com.cz.babySister explore -P ~/.objection/plugins/dexdump/frida_dexdump

1
2
3
4
5
6
7
8
9
10
11
pyenv local 3.8.5
proxychains git clone https://github.com/hluwa/FRIDA-DEXDump ~/Downloads/FRIDA-DEXDump
mv ~/Downloads/FRIDA-DEXDump/frida_dexdump ~/.objection/plugins/dexdump
adb install movetv.apk
objection -g com.cz.babySister explore -P ~/.objection/plugins 加载全部插件启动app多运行一会
plugin dexdump dump 脱壳
cd com.cz.babySister/
grep -ril "MainActivity" * 查看主入口dex
objection -g com.cz.babySister explore
android hooking list activities
android intent launch_activity com.cz.babySister.activity.MainActivity 不登录进Activity,判断是否越权bug

无壳去更新重打包

环境

/root/Android/Sdk/build-tools/30.0.3/d8 MainActivity 直接生成dex文件,老版的是d8->dx

adb install zhibo.apk 安装apk,启动新版本更新弹窗

分析

1
2
3
4
5
6
7
8
9
pyenv local 3.8.5
cd /data/local/tmp/ && ./fs1428arm64
objection -g com.hd.zhibo explore
android hooking list activities 只有一个主界面com.zhibo.media.channel_main,在AndroidManifest.xml中只有一个activity
android heap search instances android.app.AlertDialog 获取弹窗实例hashcode,在弹窗出现前hook
plugin load /root/.objection/plugins/Wallbreaker
plugin wallbreaker objectsearch android.app.AlertDialog 找到实例的地址
plugin wallbreaker objectdump 0x2956 打印类结构,其中AlertController 指向地址是0x2b26
plugin wallbreaker objectdump 0x2b26 拿出该类中的具体信息

打印类结构

重新hook 类AlertDialog,并打印出调用栈

1
2
3
4
5
adb shell
ps -e| grep zhibo
kill 18149 干掉进程
objection -g com.hd.zhibo explore 启动超级直播
android hooking watch class android.app.AlertDialog 在弹窗弹出之前开始hook

hook弹窗

android hooking watch class_method android.app.AlertDialog.onCreate --dump-args --dump-backtrace --dump-return 再次在弹窗弹出前hook OnCreate方法

hook弹窗Oncreate

通过jadx搜索com.zhibo.media.channel_receiver.onReceivecom.zhibo.media.channel_main.update_show定位到核心弹窗判断逻辑

1
2
3
4
5
public void update_show(Bundle bundle) {
if (bundle != null && bundle.containsKey("ver") && bundle.containsKey("info") && bundle.containsKey("path")) {
new AlertDialog.Builder(this).setTitle("发现新版本 " + bundle.getString("ver") + " 是否升级").setMessage(bundle.getString("info")).setPositiveButton("立刻升级", new o(this, bundle)).show();
}
}

或者objection -g com.hd.zhibo explore --startup-command "android hooking watch class_method android.app.AlertDialog.onCreate --dump-args --dump-backtrace --dump-return" 启动时则运行命令即可避免在弹窗前跟不上执行hook

去弹窗

apktool d zhibo.apk && vim zhibo/smali/com/zhibo/media/channel_main.smali 解包并搜索info,修改if-eqz改为if-nez

1
2
3
4
5
6
7
8
apktool b zhibo/  重打包
cd zhibo/dist && keytool -genkey -alias abc.keystore -keyalg RSA -validity 20000 -keystore abc.keystore
Is CN=Unknown, OU=Unknown, O=Unknown, L=Unknown, ST=Unknown, C=Unknown correct?
[no]: yes
jarsigner -verbose -keystore abc.keystore -signedjar textx.apk zhibo.apk abc.keystore
pm uninstall com.hd.zhibo 卸载包
adb install textx.apk 安装包
am start-activity com.hd.zhibo 启动包

加壳去更新重打包

adb install 青青草视频.apk 启动弹窗是强制升级

jadx打开包,com.SecShell.SecShell 梆梆加固

环境

1
2
3
4
5
6
7
8
pyenv local 3.8.0 && ./data/local/tmp/fs128arm64
top 查看进程最高是com.hello.qqc
┌──(root💀kali)-[~/Desktop]
└─# frida-ps -U|grep hello 双线程
23169 com.hello.qqc
23190 com.hello.qqc
23247 com.hello.qqc:pushcore
23277 com.hello.qqc:pushcore

脱壳

  • objection plugin
1
2
objection -g com.hello.qqc explore -P ~/.objection/plugins  加载所有插件,关闭所有qqc进程,使用objection启动hook
plugin dexdump dump 多进程的话,用objection先占用一个,再frida dexdump

双进程脱壳

  • python main.py

通过cd ~/.objection/plugins/dexdump && python main.py 由于双进程,需要用objection先占住启动进程,再判断前台进程进行脱壳

  • frida-dexdump

pip install frida-dexdump 通过直接运行frida-dexdump进行脱壳

越权

1
2
android hooking list activities
android intent launch_activity cn.net.tokyo.ccg.ui.activity.AboutActivity 进入关于我们页面,绕过弹窗,再进入其他页面发现依旧存在升级弹窗

弹窗分析

基于objection进程动态hook弹窗

Window

查看类是否可以hook

1
2
3
android hooking list classes   
cat ~/.objection/objection.log |grep -i window 如果有则可以hook
objection -g com.hello.qqc explore --startup-command "android hooking watch class android.view.Window" 对所有该Window类进行hook

点击立即升级,objection显示新增agent如下

windows弹窗升级

Dialog

1
2
cat ~/.objection/objection.log |grep -i dialog
objection -g com.hello.qqc explore --startup-command "android hooking watch class android.app.Dialog"

点击立即升级,objection显示新增agent如下

dialog弹窗升级

点击弹窗也有agentlog打印出调用Dialog方法

1
2
3
4
5
objection -g com.hello.qqc explore --startup-command "android hooking watch class android.widget.PopupWindow"
或者
objection -g com.hello.qqc explore
android hooking watch class android.widget.PopupWindow
android hooking watch class android.app.AlertDialog

点击立即升级发现无新agentlog新增


主要可能是Dialog弹窗嫌疑最大,其中agent的log(agent) [5384329357348] Called android.app.Dialog.setCancelable(boolean)

1
android hooking watch class_method android.app.Dialog.setCancelable --dump-args --dump-backtrace --dump-return

hook弹窗出现

通过jadx搜索脱壳下的dex文件cn.net.tokyo.ccg.ui.fragment.dialog.UpdateDialogFragment的onCreateDialog方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public Dialog onCreateDialog(@Nullable Bundle bundle) {
FragmentActivity activity = getActivity();
activity.getClass();
Dialog dialog = new Dialog(activity, 2131755208);
dialog.requestWindowFeature(1);
dialog.setContentView(2131427414);
VersionBean.Version version = this.f2134a;
if (version != null) {
dialog.setCanceledOnTouchOutside(!WakedResultReceiver.CONTEXT_KEY.equals(version.status));
dialog.setCancelable(true ^ WakedResultReceiver.CONTEXT_KEY.equals(this.f2134a.status));
Window window = dialog.getWindow();
if (window != null) {
window.setBackgroundDrawable(new ColorDrawable(0));
WindowManager.LayoutParams attributes = window.getAttributes();
attributes.width = -1;
attributes.height = -1;
window.setAttributes(attributes);
}
a(dialog);
}
return dialog;
}

调用了a(dialog);

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
private void a(Dialog dialog) {
Button button = (Button) dialog.findViewById(b.btn_force_update);
Button button2 = (Button) dialog.findViewById(b.btn_update);
Button button3 = (Button) dialog.findViewById(b.btn_cancel);
TextView textView = (TextView) dialog.findViewById(b.tv_file_size);
LinearLayout linearLayout = (LinearLayout) dialog.findViewById(b.ll_button);
((TextView) dialog.findViewById(b.tv_version)).setText(String.format(Locale.getDefault(), "版本号:%s", this.f2134a.version));
((TextView) dialog.findViewById(b.tv_content)).setText(this.f2134a.desc);
if (!TextUtils.isEmpty(this.f2134a.filesize)) {
textView.setText(String.format(Locale.getDefault(), "文件大小: %s", this.f2134a.filesize));
}
ImageView imageView = (ImageView) dialog.findViewById(b.im_close);
if (WakedResultReceiver.CONTEXT_KEY.equals(this.f2134a.status)) {
linearLayout.setVisibility(8);
button.setVisibility(0);
button.setOnClickListener(new f(this));
return;
}
linearLayout.setVisibility(0);
button.setVisibility(8);
button3.setOnClickListener(new g(this));
imageView.setVisibility(0);
imageView.setOnClickListener(new d(this));
button2.setOnClickListener(new e(this));
}

中文搜索,可能被字符串混淆加密,Android 字符串及字典混淆开源实现,或通过wallbreak查看类结构,避免字符串加密。

plugin wallbreaker objectsearch cn.net.tokyo.ccg.ui.fragment.dialog.UpdateDialogFragment 搜索类地址

plugin wallbreaker objectdump –fullname 0x2452 打印类结构

wallbreaker查看类结构

通过点击立即升级按钮,打印调用栈如下,jadx搜索cn.net.tokyo.ccg.ui.activity.MainActivity.a

hook弹窗取消

@Override // b.a.a.a.d.b.z
public void a(VersionBean.Version version, boolean z) {
    this.f1696a = version.url;
    if (version != null) {
        UpdateDialogFragment.b(version, z).show(getSupportFragmentManager(), UpdateDialogFragment.class.getSimpleName());
    }
}

smali源码

去壳

1
2
3
apktool -s d 青青草视频.apk && cd 青青草视频/   解包指定不反编译源码并保留dex,并扔掉壳
cp ~/Desktop/com.hello.qqc/*.dex . 将之前去壳后的dex放入解包文件夹
thunar . 打开所在文件夹,ctrl+2以详细信息查看文件,根据大小进行重命名dex,classes.dex,classes2.dex...

重命名dex

在jadx中搜索extends Application 找到主入口文件cn.net.tokyo.ccg.base.App,vim AndroidManifest.xml 搜索application标签,修改android:name="com.SecShell.SecShell.ApplicationWrapper"android:name="cn.net.tokyo.ccg.base.App"修改加壳入口点为原app的入口点。

1
2
3
4
5
apktool b 青青草视频    回编译
cd 青青草视频/dist 编译完成的apk
keytool -genkey -alias abc.keystore -keyalg RSA -validity 20000 -keystore abc.keystore
jarsigner -verbose -keystore abc.keystore -signedjar qqc_signed.apk 青青草视频.apk abc.keystore 签名
adb install -r qqc_signed.apk 重新安装编译后的apk,即脱壳后的重打包dex

去弹窗

1
2
3
4
5
6
7
8
9
10
apktool d 青青草视频.apk
cd 青青草视频/
tree -NCfhl | grep MainActivity
vim ./青青草视频/smali/cn/net/tokyo/ccg/ui/activity/MainActivity.smali 搜索UpdateDialogFragment
修改if-eqz p1, :cond_0为if-nez p1, :cond_0
apktool b 青青草视频/ 回编译
cd 青青草视频/dist 编译完成的apk
keytool -genkey -alias abc.keystore -keyalg RSA -validity 20000 -keystore abc.keystore
jarsigner -verbose -keystore abc.keystore -signedjar qqc2_signed.apk 青青草视频.apk abc.keystore 重签名
adb install -r qqc2_signed.apk 重新安装编译后的apk
文章作者: J
文章链接: http://onejane.github.io/2021/02/09/动静态分析之去弹窗重打包/
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 万物皆可逆向
支付宝打赏
微信打赏