安卓逆向学习2-APP攻防01
本帖最后由 Juana111 于 2024-8-8 10:42 编辑2.1APK结构
apk本质是一个zip压缩文件,可以通过打开压缩包的形式打开apk文件。
.dex文件
除了源文件与资源文件之外,可以发现.dex文件,是逆向分析的核心文件。
Tip: .dex文件是Android系统虚拟机的可执行文件,是Java文件夹中所有Java类编译的二进制文件,存储apk整体业务逻辑的核心。一般是将.dex文件反编译为smali语言【Android虚拟机的汇编语言】的文件。
其他文件
其他文件一把梭!
assets文件夹:配置文件,包含非xml的静态资源文件,音频、字体、html这些文件;
lib文件夹:应用程序的本地链接库目录(.so文件),存放与特定硬件架构相关的本地库文件,下图文件包括armeabi-v7a【通用所有Android设备】和x86【Android模拟器】,一般会存在,下图lib文件在assets文件夹中;
kotlin文件夹:kotlin编程语言开发的源文件,Android官方开发语言,与Java兼容;
META-INF文件夹:存储apk程序的签名信息,用做校验计算存储计算结果,保证包的文件不被替换;
okhttps3文件夹:处理网络请求的框架,使用Get\Post请求,上传下载文件等功能;
org文件夹:不了解,查看文件有关于Antlr的,百度了下是一个语法分析器生成器。嗯,暂时不重要;
res/raw文件夹:xml、json、txt、图片文件这些文件的存储。
AndroidManifest.xml文件:配置清单文件,编译自动生成的文件,在安装时会读取该文件确定应用的基本信息和权限要求。文件中包括包名、应用名、权限、安卓四大组件、版本等信息的声明。学到的方法是通过资源文件中的AndroidManifest.xml文件中的四大组件标签【activity/service/receiver/provider】标识来找到app包名(进程的唯一标志)。通过<intent-filter>标签内容定位app入口类。
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
resources.arsc文件:资源索引表,用来描述具有ID值的资源的配置信息。再使用反编译工具查看,主要是源代码与资源文件两种。
2.2安卓加固
预告:安卓安全相关手段
代码混淆手段:更改类名、函数名、变量名,实现两种结果。一是符号混淆:将有意义的类名或者函数名改为a,b,c这种没有意义的名称,破解人员看不懂,增加破解难度;二是压缩文件大小。代码混淆中又包括了字符串加密、花指令和资源加密等。
动态加载方案:将保护的代码单独编译,加密后保存在外部二进制文件中,常见的是将程序的业务逻辑放置在.so文件中。使用动态分析的手段破解程序,在加载后的函数中设置断点dump内存,找到真实的dex文件,具体是通过注入将指令代码和数据注入到目标进程,对目标进程进行调试和内存监控。
正片:安卓加固手段
[*]DEX加固:对dex整体加固,将dex整体加密后动态加载。-->先解密文件并在解密完成后写入到另一个文件,解密完调用加载函数来加载解密后文件。-->加密的dex文件在内存中解密并直接在内存中加载。
[*]代码抽取保护:利用私有函数,通过对进程的hook拦截函数调用路径,在函数真实调用前使用nop代码数据填充。-->存在兼容性和性能损耗问题。
[*]VMP和DEX2C:VMP-->java代码变成Native层代码,找到与系统解释器的映射关系;DEX2C-->java代码转为Native层代码,native的二进制编码(C/C++)比Java字节码更难。
[*]OLLVM保护native层:编写PASS控制中间代码混淆,实现平坦化。
2.3app分析与破解
2.3.1未加固app进行分析破解
目的:使用Objection对未加固应用包中的弹窗进行快速定位操作,并去掉弹窗
实验工具:
adb - 35.0.1
Frida - 16.3.0
objection - 1.11.0
雷电模拟器 安卓4.0(64位)
jadx-gui
具体操作:第一步:先在adb中安装apk文件:
adb install XXX.apk这里遇到问题有两个
[*]adb.exe: more than one device/emulator
Xxxx offline 检查是不是开了多个设备或者模拟器使用命令:
adb devices
##解决方式
##adb -s emulator-5554 shell
##adb kill-server
##adb start-server[*]adb: failed to install zhibo.apk: Failure
安装apk之前,设备上已经安装过了一个版本,再次安装会安装失败。
##解决方式 -r覆盖安装
adb install -r XXX.apk第二步:在adb启动frida-server,执行命令如下
D:\CTF_Tools\APK_Tools\adb>adb shell
aosp:/ # su
aosp:/ # cd /data/local/tmp/
aosp:/data/local/tmp # ./fs
另外起一个cmd,objection注入
D:\CTF_Tools\APK_Tools\adb>objection -g com.hd.zhibo explore
成功之后就像图中这样,这里尝试了用不同机型来启动,发现只有xiaomi不报错,牛批!这里遇到的问题比较多
[*]电脑重新安装过,之前的frida版本乱乱的,总体还是遗留了之前和朋友打比赛复现的环境,重新将frida-server push到adb中
adb.exe push frida-server /data/local/tmp
##push 文件名 文件位置[*]objection安装
直接在python中安装frida和objection的包,直接使用cmd会发现报错,就直接在pycharm中进行了package的安装,一种蛮快捷的方式!对反编译之后的apk进行文本的搜索,找到弹窗调用的类名,使用命令
android heap search instances android.app.AlertDialog
跑出来的效果太亏贼了,需要进一步了解弹窗类的类型,大概先了解操作,笔者又使用了ubuntu和真机测试,发现又出现了一系列问题,等到后面重新解决。这里还推荐了一个插件Wallbreaker,在github可以搜到,等后面换设备再重新搭建。
使用命令对AlterDialog类进行hook来去欸的那个调用弹窗的函数。
objection -g com.hd.zhibo explore -s "android hooking watch class android.app.AlertDialog"
确定被调用函数后,选取其中一个再进行hook来确定提示弹窗中在app下的函数,这里选定activity启动调用的第一个函数OnCreat()进行hook并打印调用栈。
手动关闭app之后输入命令:
objection -g com.hd.zhibo explore -s "android hooking watch class_method android.app.AlertDialog.OnCreate --dump-args --dump-backtrace --dump-return"
确定app弹窗的创建函数com.zhibo.media.channel_main.update_show(),用jadx具体查看函数的内容。
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 DialogInterface$OnClickListenerC0971o(this, bundle)).show();
}
}
根据外面的if条件判断,不再弹出弹窗就需要修改对应的判断语句重新打包app。使用apktool命令对app进行反编译
apktool_2.8.0.jar d D:\CTF_Tools\APK_Tools\adb\zhibo.apk
新获得一个同名的文件夹,使用android stdio打开文件夹。找到channel_main类中对应的smali文件以及update_show()函数在的文件中的位置。【对代码进行注释解释】
.method public update_show(Landroid/os/Bundle;)V
##.method定义updata_show这个方法
.locals 3
##本地寄存器的个数
if-eqz p1, :cond_0
##p1寄存器 p开头--->参数寄存器
##equals zero,p1寄存器等于0则跳转到:cond_0处
##nez not equals zero
const-string v0, "ver"
##v0寄存器 v开头--->本地寄存器
invoke-virtual {p1, v0}, Landroid/os/Bundle;->containsKey(Ljava/lang/String;)Z
##方法调用 {参数},方法所属类的包路径->方法名(参数类型)方法返回值类型【Z:boolean】
move-result v0
##将上一个方法调用的结果值移到v0
if-eqz v0, :cond_0
const-string v0, "info"
invoke-virtual {p1, v0}, Landroid/os/Bundle;->containsKey(Ljava/lang/String;)Z
move-result v0
if-eqz v0, :cond_0
const-string v0, "path"
invoke-virtual {p1, v0}, Landroid/os/Bundle;->containsKey(Ljava/lang/String;)Z
move-result v0
if-eqz v0, :cond_0
new-instance v0, Landroid/app/AlertDialog$Builder;
invoke-direct {v0, p0}, Landroid/app/AlertDialog$Builder;-><init>(Landroid/content/Context;)V
##方法构造【V:void】
new-instance v1, Ljava/lang/StringBuilder;
const-string v2, "\u53d1\u73b0\u65b0\u7248\u672c "
invoke-direct {v1, v2}, Ljava/lang/StringBuilder;-><init>(Ljava/lang/String;)V
const-string v2, "ver"
invoke-virtual {p1, v2}, Landroid/os/Bundle;->getString(Ljava/lang/String;)Ljava/lang/String;
##【Lpackage/name/ObjectName格式-->Ljava/lang/String:String-->对象数组】
move-result-object v2
invoke-virtual {v1, v2}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;
move-result-object v1
const-string v2, " \u662f\u5426\u5347\u7ea7"
invoke-virtual {v1, v2}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;
move-result-object v1
invoke-virtual {v1}, Ljava/lang/StringBuilder;->toString()Ljava/lang/String;
move-result-object v1
invoke-virtual {v0, v1}, Landroid/app/AlertDialog$Builder;->setTitle(Ljava/lang/CharSequence;)Landroid/app/AlertDialog$Builder;
move-result-object v0
const-string v1, "info"
invoke-virtual {p1, v1}, Landroid/os/Bundle;->getString(Ljava/lang/String;)Ljava/lang/String;
move-result-object v1
invoke-virtual {v0, v1}, Landroid/app/AlertDialog$Builder;->setMessage(Ljava/lang/CharSequence;)Landroid/app/AlertDialog$Builder;
move-result-object v0
const-string v1, "\u7acb\u523b\u5347\u7ea7"
new-instance v2, Lcom/zhibo/media/o;
invoke-direct {v2, p0, p1}, Lcom/zhibo/media/o;-><init>(Lcom/zhibo/media/channel_main;Landroid/os/Bundle;)V
invoke-virtual {v0, v1, v2}, Landroid/app/AlertDialog$Builder;->setPositiveButton(Ljava/lang/CharSequence;Landroid/content/DialogInterface$OnClickListener;)Landroid/app/AlertDialog$Builder;
move-result-object v0
invoke-virtual {v0}, Landroid/app/AlertDialog$Builder;->show()Landroid/app/AlertDialog;
:cond_0
return-void
.end method
##定义方法结束
这里是直接在第一个if判断那里的eqz(等于0跳转),改为nez(不等于0跳转)。使用打包命令对修改后的app重新打包,在dist文件下生成一个未作签名的out.apk文件。
apktool_2.8.0.jar b zhibo
最后生成的apk是在目录的dist文件下,由于修改后缺少对应的app签名,这里需要使用签名工具生成签名文件对新打包的apk进行签名。这里使用的是自带的签名工具,也可以选择其他的工具。使用的两条命令:keytool -genkey -alias new_zhibo.keystore -keyalg RSA -validity 2000 -keystore /home/juana-2u/Software/zhibo/dist/new_zhibo.key
##生成签名文件
##-genkey 生成证书
##-alias 证书别名,包名
##-keyalg RSA 签名文件使用的加密类型
##-validity 签名有效期2000days
##-keystore 签名文件生成的路径
jarsigner -verbose -keystore new_zhibo.key -signedjar zhibo_patch.apk out.apk new_zhibo.keystore
##生成patch之后的apk文件
##jarsigner -verbose -keystore [签名秘钥文件路径] -signerjar [签名后apk的文件路径] [未签名apk的文件路径] [证书别名]
2.3.2对加固app进行分析破解
目的:使用Objection对加固后应用包中的弹窗进行快速定位操作,并去掉弹窗
实验工具:adb - 35.0.1
Frida - 16.3.0
objection - 1.11.0
雷电模拟器 安卓4.0(64位)
jadx-gui具体操作:
apk启动不了,直接反编译来看了,与上面的未加固app的分析步骤类似。对弹窗类进行hook判断具体实现的弹窗类名,使用watch class的方式hook弹窗类,根据函数的被调用记录判断类是否被使用。前面都是差不多的,点击对应的弹窗位置或者操作,objection都会返回被调用的函数,与下图类似。
确定样本中升级提示弹窗的实现类对setCancelate(boolean)函数进行hook,根据调用栈发现app中发起弹窗的函数这样。
重点是加固的部分的处理。总结如下两点
[*]对加固的dex进行脱壳处理,app的dex要替换壳的dex
[*]修改app的入口点:加固后的入口点只是壳的入口点,即在重打包时要在配置AndroidManifest.xml文件中修改入口类。
第一步:对dex脱壳
用jadx打开apk文件,与上面的非加固的apk不一样,加固之后的apk只显示了外部壳的类信息。
使用frida-dexdump,github地址:https://github.com/hluwa/frida-dexdump,需要在frida-server调试的同时进行脱壳。
这里的插件安装存在问题,windows上直接安装不了,使用pip安装出现一排? 安装失败ubuntu安装好了,看资料需要在apk运行的条件下进行dexdump【?】准备再研究一下继续复现这一块。
彩蛋:来填上期留下的Objection的坑!
2.4Objection简单介绍
1. objection是什么
Objection是基于Firda的一个工具。利用Frida(安卓逆向的一种Hook框架)提供各种api实现安卓逆向分析的具体功能,Objection是集合体,同时支持android和IOS两大平台。完成任务:内存搜索、类和模块搜索、方法Hook、打印参数、打印返回值、调用栈等功能(内存漫游+逆向分析),依托Frida完成对应用注入和对函数的hook模板使用:填充具体的类完成Hook测试
2.objection三大组成部分:
[*]Objection重打包的组件:完成Frida无root调试,重打包Frida运行需要的firda-gadget.so文件
[*]Objection本身的pypi包:和包含firda-gadget.so文件的app交互,运行hook脚本,分析hook结果
[*]Objection编译的agent.js文件:【TypeScript】项目,在app运行中插入了Frida的运行库
参考资料:
《安卓Frida逆向与抓包实战》陈佳林
附件地址:https://github.com/r0ysue/AndroidFridaBeginnersBook
笔者复现附件地址:https://github.com/Juana-2u/Android_Study_demo xiaomi?牛批! h1989 发表于 2024-8-15 11:13
请问大佬,某软件查题,不论mt什么反混淆,都无法查到兑换码、会员、vip等字眼,这是什么原因呢?
会不会是进行了类似加密的方式将文本格式转成了其他格式 试试触发带有这些字眼的操作进行抓包呢 好有用,学习了 感谢分享,学习了 介绍详细,回头学学 感谢 学习了 非常感谢详细内容的文章 刚从ios切换到安卓,正好对这块内容感兴趣,感谢分享 感谢学习了 详细,感谢分享!