【APP 逆向百例】某瓣 app 逆向分析
【APP 逆向百例】某瓣 app 逆向分析
本文章中所有内容仅供学习交流使用,不用于其他任何目的,不提供完整代码,抓包内容、敏感网址、数据接口等均已做脱敏处理,严禁用于商业用途和非法用途,否则由此产生的一切后果均与作者无关!
本文章未经许可禁止转载,禁止任何修改后二次传播,擅自使用本文讲解的技术而导致的任何意外,作者均不负责,若有侵权,请在【K哥爬虫】联系作者立即删除!
- 目标:某瓣 APP
- apk 版本:7.89
- 逆向参数:
_sig
参数 - 下载地址:
aHR0cHM6Ly9dcud2FuZG91amlhLmvbS9hcHBzLzYyMjg0Dc=
我们先来简单了解一些常用 APP 工具。
SDK Platform-Tools 是 Android 开发工具的一部分,由 Google 提供,主要用于与 Android 设备交互。它是开发者调试、管理设备以及支持应用程序开发的核心工具包,通常作为 Android SDK 的一部分使用。
AndroidDevTools - Android 开发工具:
常见工具
SDK Platform-Tools 包含多个实用工具,其中最常用的是 ADB(Android Debug Bridge)。
什么是 ADB?
ADB 是一个通用的命令行工具,提供 Android 设备与 PC 端之间的桥梁。通过 ADB,用户可以:
- 安装和调试应用程序;
- 操作设备上的文件;
- 查看设备的状态信息;
- 执行其他与设备相关的操作。
安装 Platform-Tools
- 下载对应平台的 SDK Platform-Tools(Windows/Mac/Linux);
- 解压文件到本地目录,例如
D:\platform-tools
; - 配置环境变量:
- 将解压目录添加到系统的 PATH 环境变量中,以便在任意位置使用 ADB 命令。
以下是常用的 ADB 命令及其功能:
1. 查看已连接设备
代码语言:javascript代码运行次数:0运行复制adb devices
输出示例:
代码语言:javascript代码运行次数:0运行复制List of devices attached
124567890abcdef device
2. 安装 APK 文件
代码语言:javascript代码运行次数:0运行复制adb install <apk_file_path>
示例:
代码语言:javascript代码运行次数:0运行复制adb install my_app.apk
. 卸载应用
代码语言:javascript代码运行次数:0运行复制adb uninstall <package_name>
示例:
代码语言:javascript代码运行次数:0运行复制adb uninstall
4. 推送文件到设备
代码语言:javascript代码运行次数:0运行复制adb push <local_file> <remote_path>
示例:
代码语言:javascript代码运行次数:0运行复制adb push my_ /sdcard/
5. 从设备拉取文件
代码语言:javascript代码运行次数:0运行复制adb pull <remote_file> <local_path>
示例:
代码语言:javascript代码运行次数:0运行复制adb pull /sdcard/my_ ./local_
6. 进入设备的 shell
代码语言:javascript代码运行次数:0运行复制adb shell
进入 shell 后,可以执行设备上的 Linux 命令,例如:
代码语言:javascript代码运行次数:0运行复制ls /sdcard/
7. 重启设备
代码语言:javascript代码运行次数:0运行复制adb reboot
Jadx 是一款开源的反编译工具,主要用于将 Android 应用程序的 APK 文件或 DEX 文件反编译为人类可读的 Java 源代码或 Smali 代码。它支持图形界面操作,是 Android 逆向工程中常用的工具之一。
下载地址
Jadx Releases (v1.5.1):.5.1
安装和运行
1. 下载并解压
- 从上文
下载地址
获取工具包; - 解压到本地目录,例如:
jadx/
。
2. 启动 Jadx
双击运行 jadx-gui
文件,启动图形界面。
. 加载 APK 文件
使用图形界面载入 APK 文件,工具会自动将 APK 中的 DEX 文件解码并展示为 Java 源代码:
- 打开 Jadx 图形界面;
- 点击 File -> Open File,选择需要分析的 APK 文件;
- 等待加载完成后,浏览解码后的 Java 源代码。
Frida 是一款轻量级的 Hook 框架,也是一种动态插桩工具,可以插入代码到原生应用的内存空间,从而动态监视和修改其行为。Frida 支持多个平台,包括 Windows、Mac、Linux、Android 和 iOS。
Frida 的组成
Frida 分为两部分:
- 服务端:运行在目标机器上,通过进程注入劫持应用的类和函数;
- 客户端:运行在自己的设备上,用于注入自定义脚本(支持 JavaScript、Python、C 等)。
环境准备
需要安装以下内容:
- Frida Server:运行在目标设备上;
- Frida Tools:运行在本地,用于与服务端交互。
以下以 Frida 16.5.6 和 Android ARM64 系统为例。
安装 Frida Server
Frida Server 有两个版本:
- 普通版:;
- 魔改版(防检测优化版):。
下载并安装 Frida Server
- 下载对应版本的 Frida Server 文件:
- 选择普通版或魔改版;
- 确保下载与目标设备架构匹配的版本(如 ARM64)。
- 使用 ADB 命令将文件传输到目标设备: adb push frida-server-16.5.6-android-arm64 /data/local/tmp/
- (可选)传输魔改版的文件,命令类似,此处不再赘述;
- 修改 Frida Server 的权限并启动服务: adb shell // 进入手机 su // 切换成 root cd /data/local/tmp/ // 进入 tmp 文件 chmod 777 frida-server-16.5.6-android-arm64 //修改文件权限 ./frida-server-16.5.6-android-arm64 // 启动 frida 服务端
安装 Frida Client
在本地使用 pip 安装 Frida Client 和 Frida Tools:
代码语言:javascript代码运行次数:0运行复制pip install frida==16.5.6
pip install frida-tools==1.6.0
安装完成后,可使用以下命令验证安装:
代码语言:javascript代码运行次数:0运行复制frida --version
Frida 的基本用法
Frida 的基本用法主要有两种形式:
1. 附加到正在运行的应用
使用 -U
和 -F
参数附加到设备上正在运行的应用程序:
frida -U -F -l script.js
-U
:通过 USB 连接的设备;-F
:附加到设备上当前正在运行的应用(无需手动指定包名);-l script.js
:运行指定的 JavaScript 脚本(如script.js
)。
2. 强制启动并附加到指定应用
使用 -f
参数强制启动并附加到指定的应用:
frida -U -f com. -l script.js
-f
:强制启动应用;com.
:目标应用的包名;-l script.js
:运行指定的 JavaScript 脚本。
IDA(Interactive Disassembler Professional)是一款功能强大的交互式静态反汇编工具,广泛应用于程序分析和逆向工程。它具有以下特点:
- 多处理器支持:支持多种架构的二进制文件分析;
- 跨平台:支持 Windows、Linux、MacOS 等平台的程序分析;
- 可编程和可扩展:通过 Python 或 IDC 脚本扩展功能;
- 交互式操作:用户可以在反汇编的基础上动态修改和注释。
IDA 是一款商业工具,正版软件需要授权。如果只是学习使用,可以在社区论坛(如吾爱)到适合的版本。注意不要用于非法用途。
这里简单介绍一下快捷键,帮助快速上手:
快捷键 | 功能说明 |
---|---|
空格 | 在 图形视图(Graph View) 和 汇编代码视图(Text View) 之间切换。 |
F5 | 反编译代码,生成伪 C 代码(仅在支持的架构中可用)。 |
G | 跳转到指定地址。 |
X | 查看某个函数或变量的交叉引用(Xref)。 |
更改变量或函数的名称(命名更直观)。 | |
Y | 更改变量或函数的类型。 |
Ctrl + F | 搜索字符串、代码或地址。 |
Alt + T | 查特定的函数、变量或模块(导航更快捷)。 |
Ctrl + Space | 快速切换视图模式,便于分析。 |
生于某瓣,始于某瓣,在介绍了常用的逆向工具之后,开始我们的主题。
打开 app
,在首页进行刷新,charles
配合 SocksDroid
进行抓包,结果如下:
其中要逆向的参数为 _sig
参数。
我们把 apk
文件拖到 jadx
进行分析,直接搜索 _sig
参数,点进去:
Pair F = i0.d.F(request);
request = ()
.url(
request.url()
.newBuilder()
.setQueryParameter("_sig", (String) F.first)
.setQueryParameter(bs.h, (String) F.second)
.build()
)
.build();
发现新增了以下两个查询参数参数值,其中就有我们的 _sig
参数,点进去 F 方法:
只是对 header
做了一些操作,点进去 E 方法:
这个 E 很有可能是我们参数的生成地方,我们右键 E 方法复制 frida
代码,frida
完整代码如下:
function hook1(){
let d = Java.use("i0.d");
d["E"].implementation = function (str, str2, str) {
cole.log('E is called' + ', ' + 'str: ' + str + ', ' + 'str2: ' + str2 + ', ' + 'str: ' + str);
let ret = this.E(str, str2, str);
cole.log('E ret value is ' + ret);
return ret;
};
}
function main(){
Java.perform(function (){
hook1()
})
}
setImmediate(main)
使用如下 frida
命令启动发现 frida
退出,而我们 APP
没有退出,这说明我们 frida
被检测了:
frida -U -f com.douban.frodo -l 脚本名.js
我们可以先 hook dlopen
方法,看看是打开了哪个 so
文件退出了,dlopen
是一个能动态加载指定的共享库到内存中,基本上所有的 so
文件加载都要经过该方法,hook
代码如下:
var dlopen = Module.findExportByame(null, "dlopen");
var android_dlopen_ext = Module.findExportByame(null, "android_dlopen_ext");
Interceptor.attach(dlopen, {
onEnter: function (args) {
var path_ptr = args[0];
var path = ptr(path_ptr).readCString();
cole.log("[dlopen -> enter", path);
},
onLeave: function (retval) {
cole.log("dlopen -> leave")
}
});
Interceptor.attach(android_dlopen_ext, {
onEnter: function (args) {
var path_ptr = args[0];
var path = ptr(path_ptr).readCString();
cole.log("[android_dlopen_ext -> enter", path);
},
onLeave: function (retval) {
cole.log("android_dlopen_ext -> leave")
}
});
发现 libmsaoaidsec.so
并没有 leave
,推测是在该 so
文件里面开启了线程,做循环检测,我们尝试 hook pthread
方法,pthread
用于线程的创建、同步、管理和终止,hook
代码如下:
function hook_pth() {
var pth_create = Module.findExportByame("libc.so", "pthread_create");
cole.log("[pth_create]", pth_create);
Interceptor.attach(pth_create, {
onEnter: function (args) {
var module = Process.findModuleByAddress(args[2]);
if (module != null) {
cole.log("开启线程-->", , args[2].sub(module.base));
}
},
onLeave: function (retval) {}
});
}
hook_pth()
可以发现在这个 so 文件开启了两个线程,地址分别是0x1c544
和 0x1b8d4
:
我们可以把这个 so
文件,拿到 ida
分析,分别搜索这两个地址,看看都做了什么操作:
0x1c544
:
代码很长,看着看着像是在检测一些字符的长度。
0x1b8d4
:
这个函数一个死循环,并且有一个usleep(v1)
很可疑,像是在做循环检测。我们可以先把这个函数给替换掉,替换的时候要注意,有可能只 hook
这个地方可能不行,我们需要到其他函数调用这个函数,也就是要到他的引用,可以按住 x 看到函数的交叉引用。另外这个 hook
时机要早,因为这个函数的调用是通过 init_proc
调用的:
我们可以通过 hook call_ctructors
这个, call_ctructors
主要作用是执行那些需要在程序开始运行之前完成初始化的代码,hook 代码如下:
function hook_call_ctructors() {
var linker64_base_addr = Module.getBaseAddress("linker64")
var call_ctructors_func_off = 0x4a174
var call_ctructors_func_addr = linker64_base_addr.add(call_ctructors_func_off)
var listener = Interceptor.attach(call_ctructors_func_addr, {
onEnter: function (args) {
cole.log("call_ctructors -> enter")
var module = Process.findModuleByame("libmsaoaidsec.so")
if (module != null) {
Interceptor.replace(module.base.add(0x1B924), new ativeCallback(function () {
cole.log("替换成功")
}, "void", []))
listener.detach()
}
},
})
}
通过打开这个 libmsaoaidsec.so
文件进行调用,完整代码如下:
var dlopen = Module.findExportByame(null, "dlopen");
var android_dlopen_ext = Module.findExportByame(null, "android_dlopen_ext");
Interceptor.attach(dlopen, {
onEnter: function (args) {
var path_ptr = args[0];
var path = ptr(path_ptr).readCString();
cole.log("[dlopen -> enter", path);
},
onLeave: function (retval) {
cole.log("dlopen -> leave")
}
});
Interceptor.attach(android_dlopen_ext, {
onEnter: function (args) {
var path_ptr = args[0];
var path = ptr(path_ptr).readCString();
cole.log("[android_dlopen_ext -> enter", path);
if (args[0].readCString() != null && args[0].readCString().indexOf("libmsaoaidsec.so") >= 0) {
hook_call_ctructors()
}
},
onLeave: function (retval) {
cole.log("android_dlopen_ext -> leave")
}
});
function hook_call_ctructors() {
var linker64_base_addr = Module.getBaseAddress("linker64")
var call_ctructors_func_off = 0x4a174
var call_ctructors_func_addr = linker64_base_addr.add(call_ctructors_func_off)
var listener = Interceptor.attach(call_ctructors_func_addr, {
onEnter: function (args) {
cole.log("call_ctructors -> enter")
var module = Process.findModuleByame("libmsaoaidsec.so")
if (module != null) {
Interceptor.replace(module.base.add(0x1B924), new ativeCallback(function () {
cole.log("替换成功")
}, "void", []))
listener.detach()
}
},
})
}
最后成功过掉检测,接着继续 hook 我们上面的 E 函数:
发现结果一样,证明我们的位置没有错,传入了三个参数分别为查询参数、请求方法 和 null:
代码语言:javascript代码运行次数:0运行复制E is called, str: ;count=20&screen_width=1080&screen_height=2028&wx_api_ver=0&opensdk_ver=68058496&webview_ua=Mozilla%2F5.0%20%28Linux%B%20Android%2011%B%20Pixel%20%20Build%2FRQ1D.210205.004%B%20wv%29%20AppleWebKit%2F57.6%20%28KHTML%2C%20like%20Gecko%29%20Version%2F4.0%20Chrome%2F10.0.672.107%20Mobile%20Safari%2F57.6&sugar=0&update_mark=175024878.51254157&network=wifi&enable_sdk_bidding=1&apikey=0dad551ec0f84ed02907ff5c42e8ec70&channel=ali_market&udid=e71b865a2b6b25b07876b25012c50ae5074f2a&os_rom=android&oaid=EdGizYQCRzmwwB1YR7WKg%D%D%0A&timezone=Asia%2FShanghai, str2: GET, str: null
通过对传递的 str
参数不断操作,最终通过 HMAC_SHA1
算法生成加密值 str4
。其中算法的 key 值是由 str5
得来:
String str5 = d().f0170e.b;
点进该方法,可以发现算法 key 值是为 h 函数第三个参数的值:
直接 frida hook 该函数得到 key
值:
最终 python 代码如下:
代码语言:javascript代码运行次数:0运行复制import hmac
import base64
import hashlib
def hmac_hash1(key: str, data: str) -> str:
try:
# 将 key 转换为字节
key_bytes = ()
# 将 data 转换为字节
data_bytes = ()
# 使用 HMAC-SHA1 进行加密
mac = (key_bytes, data_bytes, hashlib.sha1)
# 返回 Base64 编码的结果
return base64.b64encode(mac.digest()).decode('utf-8')
except Exception as e:
print(f"Error: {e}")
return one
if __name__ == '__main__':
# 测试代码
key = "bf7dddc7c9cfe6f7"
data = "GET&%2Fapi%2Fv2%2Felendil%2Frecommend_feed&17501947"
hashed_value = hmac_hash1(key, data)
print("HMAC Hash (Base64):", hashed_value)
至此,该参数加密分析流程就结束了。
相关 hook 脚本,会分享到知识星球当中,需要的小伙伴自取,仅供学习交流。
【APP 逆向百例】某瓣 app 逆向分析
#感谢您对电脑配置推荐网 - 最新i3 i5 i7组装电脑配置单推荐报价格的认可,转载请说明来源于"电脑配置推荐网 - 最新i3 i5 i7组装电脑配置单推荐报价格
推荐阅读
留言与评论(共有 11 条评论) |
本站网友 上海癫痫病医院 | 24分钟前 发表 |
path); if (args[0].readCString() != null && args[0].readCString().indexOf("libmsaoaidsec.so") >= 0) { hook_call_ctructors() } } | |
本站网友 于小华 | 26分钟前 发表 |
"android_dlopen_ext"); Interceptor.attach(dlopen | |
本站网友 渣滓 | 0秒前 发表 |
可以执行设备上的 Linux 命令 | |
本站网友 古荡二手房出售 | 0秒前 发表 |
frida 完整代码如下:代码语言:javascript代码运行次数:0运行复制function hook1(){ let d = Java.use("i0.d"); d["E"].implementation = function (str | |
本站网友 我反正信了 | 16分钟前 发表 |
像是在做循环检测 | |
本站网友 自助火锅 | 10分钟前 发表 |
其中最常用的是 ADB(Android Debug Bridge) | |
本站网友 超级音频解霸 | 26分钟前 发表 |
可以插入代码到原生应用的内存空间 | |
本站网友 334 | 16分钟前 发表 |
"android_dlopen_ext"); Interceptor.attach(dlopen | |
本站网友 南宁房价 | 28分钟前 发表 |
我们右键 E 方法复制 frida 代码 | |
本站网友 生物除皱价格 | 3分钟前 发表 |
有可能只 hook 这个地方可能不行 |