# Reverse **Repository Path**: lztkdr/reverse ## Basic Information - **Project Name**: Reverse - **Description**: 逆向破解相关笔记。 - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 1 - **Forks**: 0 - **Created**: 2020-11-10 - **Last Updated**: 2022-10-10 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README ## APP逆向 ### 安卓基础与逆向工具 #### 预备工具 - JAVA环境 - apktool - 解压 `E:\>apktool d E:\Soft\Reverse\App\shuqi.apk` , 会解压到 E盘根目录。 解压到指定目录: ``` shell # shuqi 目录会自动创建 apktool d E:\Soft\Reverse\App\shuqi.apk -o E:\Soft\Reverse\App\shuqi ``` - 打包 ``` shell apktool.bat b E:\Soft\Reverse\App\shuqi ``` 执行完成后,在资源文件夹下,会产生 `build ` ` dist ` 文件夹,`dist` 文件夹下会生成二次打包的apk文件。 - jadx - keytool (jdk自带)生成证书 ``` shell keytool -genkey -keystore shuqi.keystore -alias my_alias -keyalg RSA -keysize 4096 -validity 500000 ### keystore 转换为 pkcs12 keytool -importkeystore -srckeystore shuqi.keystore -destkeystore shuqi.keystore -deststoretype pkcs12 ### =============================================================== ### 不用交互输入签名信息的方式(推荐) keytool -genkey -v -alias my_alias -keyalg RSA -storetype PKCS12 -keystore shuqi.keystore -dname "CN=abc,OU=abc.com,O=abc.com,L=abc.com,ST=abc.com,C=CN" -storepass 123456 -keypass 123456 -validity 500000 ``` - jarsigner (jdk自带)用证书给apk签名 ``` shell jarsigner -sigalg MD5withRSA -digestalg SHA1 -keypass 123456 -storepass 123456 -keystore shuqi.keystore -signedjar shuqi_sign.apk shuqi.apk my_alias ## (推荐) ## 防止出现 ## 警告: 未提供 -tsa 或 -tsacert, 此 jar 没有时间戳。如果没有时间戳, 则在签名者证书的到期... ## 以下 2个 ,任意1个(只是tsa网址不一同) jarsigner -sigalg MD5withRSA -digestalg SHA1 -tsa http://sha256timestamp.ws.symantec.com/sha256/timestamp -keystore shuqi.keystore -keypass 123456 -storepass 123456 -signedjar shuqi_sign.apk shuqi.apk my_alias jarsigner -sigalg MD5withRSA -digestalg SHA1 -tsa http://timestamp.digicert.com -keystore shuqi.keystore -keypass 123456 -storepass 123456 -signedjar shuqi_sign.apk shuqi.apk my_alias ``` #### 安卓软件包组成 APK是一个压缩文件,用zip压缩解压 APK包的目录结构 - assets:资源文件(图片,网页,视频),不会被编译。 - res:资源文件(静态文本,图片,关键资源) 汉化,要被编译。 举例汉化 - lib:.so库,系统库,自己打包的库。有的把加密/token生成方式放在.so文件里 - META-INF:签名信息。 - AndroidManifest.xml:配置信息(关键),举例修改权限 - classes.dex:android dalvik虚拟机可执行文件 - resources.arsc:资源索引/对应文件 #### AndroidManifest.xml - package="包的名称" - uses-permission - application - android:name - activity - activity 通过 AndroidManifest.xml 可以了解到 - 应用要使用的权限 - 启动界面activity - 启动界面的包路径位置。 #### .smali & .dex & .java ​ .dex -> .smali -> .java ​ 三种文件格式,是可以相互转换的。 #### 破解安装签名校验 1. 修改smali文件 2. hook技术 #### APK启动加载 - Dex加载流程:(安卓源代码) Davlivk虚拟机加载dex文件 Java层Dex加载流程: BootClassLoader ---> PathClassLoader ----> DexClassLoader Native层Dex加载流程: libdvm.so (重编译 system.img) OpenDexFileNative - 一个APK只有一个Application 每个Apk运行时都需要有一个Application对象 Application对象执行onCreate方法是时 APP就开始运行 代码演示 #### 练习 1. 反编译apk 2. 修改AndroidManifest.xml权限 3. 重新打包APK 4. 重新签名 5. 安装 ### 破解安装签名 #### adb 命令的使用 https://www.yeshen.com/faqs/H15tDZ6YW ``` shell # 安卓设备列表 adb devices adb devices 显示device unauthorized adb kill-server adb start-server # 进入安卓shell adb shell # 安卓日志 adb logcat adb logcat -s keyword #把文件推送进安卓设备 adb push #打印AndroidManifest.xml #adb shell dumpsys activity top #adb shell dumpsys package packagename adb install XXX.apk # 查找已安装的应用 包名 adb shell pm list package -f | findstr shuqi # package:/data/app/com.shuqi.controller-1/base.apk=com.shuqi.controller # adb uninstall com.shuqi.controller adb uninstall packagename #拉取安卓文件到本地 adb pull android-path local-path #推送本地文件到安卓 adb push local-path android-path adb shell pm clear package_name ``` #### adb 连接出错 ``` shell adb devices 或 adb connect 127.0.0.1:5037 # 出现错误 adb devices adb server version (31) doesn't match this client (41); killing... could not read ok from ADB Server * failed to start daemon adb.exe: failed to check server version: cannot connect to daemon # 查询使用 5037 端口进程 任务管理器中结束进程 netstat -ano|findstr "5037" # 再次执行 adb devices ``` #### Smali语法 Dex文件逆向后的一种语法,一种反汇编语言 smali代码 和 .java代码对应看 更容易 .line 跟 .java的代码行数对应 #### 练习 shuqi.apk 1. apktool d shuqi.apk 2. apktool b shuqi 3. cd shuqi/dist 4. keytool -genkey -v -alias my_alias -keyalg RSA -storetype PKCS12 -keystore shuqi.keystore -dname "CN=abc,OU=abc.com,O=abc.com,L=abc.com,ST=abc.com,C=CN" -storepass 123456 -keypass 123456 -validity 99999999 5. jarsigner -sigalg MD5withRSA -digestalg SHA1 -keystore shuqi.keystore -signedjar shuqi_sign.apk shuqi.apk my_alias 6. 安装 shuqi_sign.apk 7. 手机端打开 `书旗小说` 图标。 闪退,如果代码没问题话,就是存在签名校验,签名比对不一样,导致闪退。 8. 卸载 `书旗小说` 9. jadx 打开 shqi.apk 10. 全文搜索 `Application`, 找到 ShuqiApplication 11. 通过在 `onCreate` 方法中,发现 `checkSigAsync(getApplication());` ![image-20201110165734161](media/image-20201110165734161.png) 12. 对 `checkSigAsync` 查找用例,发现 校验签名代码。 13. 要做什么呢?就是不让 apk 执行 `checkSigAsync` 或者`checkSigAsync` 返回 ` true`。 14. 查找 该类文件 对应 的 smali 文件,shuqi\smali\com\shuqi\app\ShuqiApplication.smali 15. 查找文件 `.line 138` , java 代码中 `checkSigAsync(getApplication());` 在 138 行 。 ![image-20201110165948073](media/image-20201110165948073.png) 16. 删除 `checkSigAsync(getApplication());` 对应在 smali 的 代码。 17. 删除 shuqi 目录下的 build 和 dist 文件夹 18. 重新 执行 apktool b shuqi 打包,生成证书,用证书给apk签名。 19. 安装 shuqi_sign.apk 20. 手机端打开 `书旗小说` 图标 ,没有闪退。 ### 加固脱壳破解Token #### 加固后的APK包 1. 加固apk的特征(各家.so文件名特征 使用软件检测)(腾讯乐加固 360加固 梆梆 爱加密 等等) 2. 第三方加固/自己加固(一般在哪家应用市场发布 就要用哪家的加固方案) 3. 为什么要加固(一定程度保护源代码) 4. 加固方式(.dex加固 .so加固) #### 不同厂商的加固特征 - 爱加密:libexec.so,libexecmain.so,ijiami.dat - 梆梆: libsecexe.so,libsecmain.so , libDexHelper.so libSecShell.so - 360:libprotectClass.so,libjiagu.so,libjiagu_art.so,libjiagu_x86.so - 百度:libbaiduprotect.so - 腾讯:libshellx-2.10.6.0.so,libBugly.so,libtup.so, libexec.so,libshell.so stub_tengxun - 网易易盾:libnesec.so 查看app有没有加固,可以更改app扩展名为zip,解压观察文件夹以及文件名(assets下一些文件名、lib 下so文件名称、根目录下个别文件名称)来了解使用的哪种厂商加固方法。 #### 加固原理 壳dex 读取源dex文件,加密后,写进一个新的dex文件。 新APK运行 - 先加载壳APP ---> 壳APP读Dex文件末尾的源APKD大小 ----> 在内存中壳APP解密出源APP ---> 运行源APP - 壳APK有自己的Application对象 源APK有自己的Application对象 - 壳APK启动时 在AndroidMenifest.xml里找源APK的Application 执行它的oncreate方法 启动源APK Dex文件格式: 任何类型的文件都有文件格式,对应的软件按照文件格式来就能解析出类型,.xml .json .jpeg ##### 逆向/脱壳方法 反编译/Hook技术和动态调试 Hook:先取得要Hook函数/方法的控制权,不用破坏程序 动态调试:反调试,汇编,计算内存地址 ##### Hook技术 改变程序执行流程的一种技术 在函数被调用前,通过HOOK技术,先得到该函数的控制权,实现该函数的逻辑改写。 ###### Hook层面 - Hook Java层 - Hook Native层(.so库) 全是汇编代码,不太好hook,通常是获取返回值,或者去改返回值,改传入的变量值,改内部逻辑是比较难的。 ###### Hook前提 - 在代码层 寻找要Hook的地方 查看源代码,脱壳后查看源代码。 - 进行Hook 改下代码逻辑 #### 脱壳 ##### 脱壳目的 脱壳之后可以更近异步,更能看懂源代码、了解app数据交互过程、破解签名或token的生成方法。 ##### 脱壳原理 在壳APK解密源APK后,源APK被加载前,拦截这个过程中的系统函数 把内存种Dex dump出来 ##### 手动脱壳 通过动态调试,跟踪计算Dex源文件的内存偏移地址,从内存中Dump出Dex文件 难度大,寄存器,汇编,反调试,反读写 IDA ##### 工具脱壳 HOOK技术/内存特征寻找 简单易操作 - 基于xposed 脱壳工具(基于java层脱壳) Fdex2:Hook ClassLoader loadClass方法 通用脱壳 dumpDex:https://github.com/WrBug/dumpDex - 重写底层函数(基于native层脱壳) DexExtractor:重写libdvm.so dexFileParse函数。可以脱 脱梆梆 爱加密 ,没有办法脱 libart.so 安卓7.0。 - 逆向框架: 筑好底层 提供开发接口 xposed(Java 编译) frida(Python Javascript 代码注入) 主要系统函数都已HOOK/基于xposed frida开发脱壳工具/有大神已开发上层应用模块 开发工具要了解 APP启动 加载过程的原理,细节 使用已有工具,技术晚半代至一代 脱老版本APP com.iCitySuzhou.suzhou001_8.2_乐加固_54.apk 脱壳 ,signature实现 Java在线执行 http://www.dooccn.com/java/ ##### Java编译运行 ``` shell Java jdk环境 sudo apt install default-jdk 编译Java成class javac MySig.java 把class做成jar jar cvf MySig.jar * # 要有main函数 java -cp MySig.jar MySig jpype python第三方库 调用jar 对windows不太兼容 ``` ##### xposed + Fdex2 脱壳步骤 推荐用真机,模拟器会有各种问题。 1. 安装Xposed(app) 2. 安装Fdex2(app) 3. xposed 模块中点击勾选 Fdex2 ,并且点击 Fdex2 4. 选择要脱壳的 app名称 点击,记录路径。 5. 启动 app 。 6. 打开 文件管理器,从记录的路径中,根目录下拷贝出所有 dex 文件 进行分析。 ##### 夜神模拟器文件共享 1. 在文件管理器中,勾选 共享的文件或文件夹,切换到 ` \mnt\shared\Other ` ,` 粘贴选择项 `。 2. 点击模拟器右上角,文件助手,`打开电脑文件夹` 。 #### 反混淆 - 混淆工具: proguard 自己混淆 - Jadx - Jeb2 - 日志打印 经验 站在程序设计的角度上思考, 程序要健壮,要安全,要性能 isNetworkAvailable #### 注意 ##### wifi 感叹号 出现 connected, no internet , 需要 usb 连接 手机,执行以下命令: ``` adb shell settings delete global captive_portal_server adb shell settings put global captive_portal_detection_enabled 0 ``` ##### /data/user/0 使用nexus 6p ,通过 adb shell ,fdex2 hook 后 提示的 /data/user/0 文件夹,其实是个快捷方式!!!对应路径 /data/data/ 。 ![image-20201117133847901](media/image-20201117133847901.png) ##### /data/data/ [通过shell 移动移动app系统目录让app支持系统权限 - 简书 (jianshu.com)](https://www.jianshu.com/p/34e48f440ca7) [Android安装系统App(adb push实现)_HeXinGen的博客-CSDN博客](https://blog.csdn.net/hexingen/article/details/76736708) 默认 /data 权限级别很高,360手机助手或者一些手机应用统统都看不到 /data/data 中的内容。需要执行: ``` shell adb shell su chmod 777 system mount -o rw,remount -t yaffs2 /dev/block/mtdblock3 /system mount -o remount,rw /system mount -o rw,remount -t auto /system chmod 777 system ``` 手机端,USB调试,USB用途 需选择`文件传输` ,然后下载安装 `WiFi FTP` 软件,设置默认根目录 `/`, 启动服务。 电脑端使用FTP,连接`WiFi FTP`提供的IP地址,就可以 浏览 手机上的 `/data/` 目录了。 ![1605617773755](media/1605617773755.png) ##### Fiddler - 配置代理 本机电脑设置了wifi热点,手机连接了电脑的wifi热点。 如果属于同一网段,则直接 在手机上 修改wifi网络 `代理`改为 `手动`,填写电脑的IP地址,端口`8888`。 如果不同网段,手机上修改网络的代理IP为无线wifi的IP地址,然后修改 C:\Users\用户名\Documents\Fiddler2\Scripts 中 `BrowserPAC.js`,原 `PROXY 127.0.0.1:8888` 修改 为本机电脑的 无线局域网适配器 本地连接 的 IP ,如:PROXY 192.168.137.1:8888,然后重启启动 Fiddler。 - 配置https 用手机浏览器,访问 代理IP地址,下载Fiddler证书并安装即可。 ![image-20201119152412439](media/image-20201119152412439.png) ### Frida开发 - 学习资料 https://www.frida.re/docs/javascript-api https://www.sohu.com/a/246175537_557054. https://www.cnblogs.com/bladecheng/p/13746755.html #### Hook框架 - xposed Java语言开发的,只能hook应用层(Java),开发经常需要重启设备,开发效率低。与安卓能无缝对接 - frida C语言开发的, 能Hook 安卓 iOS windows 应用层 native层,可以使用JS和Python代码完成Hook,不需要重启设备,开发效率高。与安卓的数据类型转换麻烦 #### 关于Root 最好设备或模拟器要root, 也提供可以不用root的方式,不过防止出现不知道怎么回事的问题,要去root。 #### 准备条件 有一台root的安卓手机。最好是安卓6的手机,不要使用安卓5的 #### Hook层面 - Hook安卓应用层:classes.dex java代码 - Hook安卓Native层:.so文件 c/c++代码 #### Hook先决条件 要知道Hook哪个方法,哪个函数,要有源代码(方法定义原型) #### Frida 组成部分 - Frida-server: 运行在设备上 - Frida :Python模块 - Frida-tools:提供cli工具命令 跟Frida-server交互。 - frida CLI :是一个交互式解释器 ![img](media/6638756445df4b02af400bc118443e15.jpeg) - frida-ps :用于列出进程的一个命令行工具,当我们需要跟远程系统进行交互的时候,这个是非常有用的。 ![img](media/32e7cfa176d34ca6b8ee18d6e9ee592d.jpeg) - frida-trace 不常用。。。 - frida-discover 不常用。。。 - frida-ls-devices 列出所有设备。 - frida-kill 不常用。。。 #### Frida 官方文档 https://www.frida.re/docs/javascript-api #### Frida安装配置 ##### 安装 https://pypi.org/project/frida/#files - pip install frida - pip install frida-tools ##### 下载 frida server 放在手机上执行 要注意手机CPU型号 https://github.com/frida/frida/releases ##### 启动frida服务器 ``` shell # 将文件推送到 手机的指定目录下 adb push frida-server-14.0.8-android-x86 /data/local/tmp/ # 进入手机shell adb shell # 切换到root su # 进入目录 cd /data/local/tmp # 确定手机当前用户是root用户或拥有root权限 chmod 777 frida-server-14.0.8-android-x86 # Nexus 6P # chmod 777 frida-server-14.0.8-android-arm64 # 关闭 selinux setenforce 0 # 确认是否关闭 getenforce # 执行,启动 frida服务器 ./frida-server-14.0.8-android-x86 # Nexus 6P # ./frida-server-14.0.8-android-arm64 ``` ##### 查看所有进程 ``` shell frida-ps -U ``` 显示 android.process.acore 字样表示成功 ##### 端口转发 windows运行 端口转发到PC ``` shell adb forward tcp:27043 tcp:27043 adb forward tcp:27042 tcp:27042 ``` 如果使用夜神模拟器,在夜神安装目录下找到 nox_adb.exe。 ```shell nox_adb forward tcp:27043 tcp:27043 nox_adb forward tcp:27042 tcp:27042 ``` 如果使用mumu模拟器,使用adb连接: ```shell # mac 下 adb connect 127.0.0.1:5555 # windows 下 adb connect 127.0.0.1:7555 adb devices ``` #### Hook Java层 - Python + Javascript ​ Python代码作用是控制,写法固定,负责跟frida-server通信,把JS代码传递给frida-server - Javascript代码作用 ​ Hook操作 ##### 示例代码 https://frida.re/docs/examples/android/ **注意:** - 应用要处于打开状态/已加载状态才能Hook。 - 闪退可能是app hook 的有问题了,需要换`app本身的原因`或者 `js脚本写的有问题`。 ``` python # -*- coding: utf-8 -*- __author__ = 'lztkdr' __date__ = '2020/11/25 11:24' import frida import sys import os # 包名 package = 'com.shuqi.controller' # get_usb_device获取设备 dev = frida.get_usb_device() # 获取给定包名的app进程 process = dev.attach(package) # 打印 def on_message(message, data): if message['type'] == 'send': print("[*] {0}".format(message['payload'])) else: print(message) # 运行脚本 # 获取js脚本内容 hookjs = os.path.join(os.getcwd(), 'hook.js') with open(hookjs, mode='r', encoding='utf-8') as fr: js_text = fr.read() # 这里是把你的js脚本给塞进了process script = process.create_script(js_text) script.on("message", on_message) print('[*] Running CTF') # 加载脚本 script.load() sys.stdin.read() ``` ``` js // hook.js Java.perform(function () { send('Version:' + Java.androidVersion); var classnames = Java.enumerateLoadedClassesSync(); for (var index = 0; index < classnames.length; index++) { var clsname = classnames[index]; send("class name : " + clsname); } }) ``` ``` js java.perform(function () { var class_name = java.user('你反编译需要拿到的类名'); var methods=class_name.getDeclaredMethods(); for(var j =0;j