篇幅有限
完整内容及源码关注公众号: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脚本,调用有参函数
|
启动activity
或service
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中查看该方法逻辑
|
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
|
脱壳
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
|
android hooking watch class_method android.app.AlertDialog.onCreate --dump-args --dump-backtrace --dump-return
再次在弹窗弹出前hook OnCreate方法
通过jadx搜索com.zhibo.media.channel_receiver.onReceive
和com.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
|
脱壳
1 2
| objection -g com.hello.qqc explore -P ~/.objection/plugins 加载所有插件,关闭所有qqc进程,使用objection启动hook plugin dexdump dump 多进程的话,用objection先占用一个,再frida dexdump
|
通过cd ~/.objection/plugins/dexdump && python main.py
由于双进程,需要用objection先占住启动进程,再判断前台进程进行脱壳
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如下
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如下
点击弹窗也有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
|
通过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 打印类结构
通过点击立即升级按钮,打印调用栈如下,jadx搜索cn.net.tokyo.ccg.ui.activity.MainActivity.a
@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());
}
}
去壳
1 2 3
| apktool -s d 青青草视频.apk && cd 青青草视频/ 解包指定不反编译源码并保留dex,并扔掉壳 cp ~/Desktop/com.hello.qqc/*.dex . 将之前去壳后的dex放入解包文件夹 thunar . 打开所在文件夹,ctrl+2以详细信息查看文件,根据大小进行重命名dex,classes.dex,classes2.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
|