news 2026/4/17 17:59:11

逆向工程实战:Hook 某大厂 App 协议,抓包看到加密参数的那一刻,我悟了

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
逆向工程实战:Hook 某大厂 App 协议,抓包看到加密参数的那一刻,我悟了

0. 引言:那串令人绝望的sign

作为一名爬虫工程师或者协议分析爱好者,你一定经历过这样的绝望时刻:

你挂上 Charles,配置好 SSL Unpinning,成功抓到了包。你兴奋地打开请求体,准备复现 API。
然而,映入眼帘的不仅有常规的userIdtimestamp,还有一个让你透心凉的参数:

"sign":"a1b2c3d4e5f6..."

你尝试修改任何一个参数,服务器都会冷冷地回你一句:{"code": 403, "msg": "Invalid Signature"}

这个sign是怎么来的?是 MD5?是 SHA256?还是加了盐?盐是在本地生成的,还是服务器下发的?

面对大厂 App 动辄几百 MB 的混淆代码(Obfuscated Code),静态分析就像在大海捞针。但是,动态 Hook技术的出现,让这场游戏变得公平了。

今天,我不谈枯燥的汇编,只讲实战。我将带你用Frida这把手术刀,直接切入 App 的内存血管,在加密函数执行的前一毫秒,截获它的“核心机密”。

⚠️ 免责声明
本文技术仅用于移动安全研究与教学。请勿用于通过非法手段获取数据、攻击服务器或进行任何商业黑产行为。逆向工程应严格遵守《网络安全法》,白帽子需自律。


1. 战前准备:工欲善其事

在开始“手术”之前,我们需要准备好全套工具。

1.1 核心武器库
  • 测试设备:一台已 Root 的 Android 手机(推荐 Pixel 或 Nexus,系统 Android 8-10 最佳)。
  • 抓包工具:Charles 或 Fiddler(配合 Postern/Drony 解决代理检测)。
  • 静态分析:Jadx-GUI(看 Java 层代码)、IDA Pro(看 So 层代码,本篇主要聚焦 Java 层)。
  • 动态注入:Frida (Client + Server)。
  • 开发环境:Python 3 + VS Code。
1.2 目标确认

为了避嫌,我们将目标 App 称为“TargetApp”
目标:破解其核心搜索接口的sign生成逻辑,并实现 Python 自动化调用。


2. 第一阶段:侦察(抓包与定位)

2.1 抓包分析

打开 TargetApp,进行一次搜索操作。Charles 抓到的请求如下:

POST /api/v2/search HTTP/1.1 Content-Type: application/json { "keyword": "逆向手机", "page": 1, "timestamp": 1678888888, "nonce": "Ax9871hz", "sign": "3f8a0b9c7d6e5f4a3b2c1d0e9f8a7b6c" }

分析

  1. timestampnonce(随机数)通常用于防重放。
  2. sign长度为 32 位,且由 0-9, a-f 组成。初步推测是 MD5
  3. 现在的任务是:找到生成这个 MD5 之前的原始内容(Plaintext)
2.2 静态分析定位 (Jadx)

将 APK 拖入 Jadx,等待反编译完成。
面对成千上万个类,不要慌。我们要利用关键词搜索

搜索策略

  1. 搜索"sign"(双引号包裹,精确匹配字符串)。
  2. 搜索 URL 路径"/api/v2/search"
  3. 搜索常见的加密类名,如SecurityUtil,SignUtil,Encrypt

经过一番苦搜,我发现了一个可疑的类com.targetapp.utils.SecurityUtils,里面有一个静态方法:

// 混淆后的代码片段publicstaticStringa(Contextcontext,Map<String,String>map){// ... 省略部分代码 ...StringsortedString=b(map);// 看起来像是在对参数排序Stringsalt=AppConfig.getSalt();// 获取盐值returnMD5.encode(sortedString+salt);// 疑似最终加密点}

虽然代码被混淆成了a,b,c,但逻辑结构出卖了它:排序 -> 加盐 -> MD5


3. 第二阶段:手术(Frida Hook)

静态分析只能通过猜测。万一AppConfig.getSalt()是在 Native 层(.so)生成的动态盐呢?万一排序规则很特殊呢?

这时候,Frida登场了。

3.1 Frida 原理图解

Frida 就像是一个植入 App 大脑的“寄生虫”。它将 V8 引擎注入到目标进程中,允许我们使用 JavaScript 代码动态地修改 Java 方法的行为。

3.2 编写 Hook 脚本

我们要做的就是 Hook 上面找到的com.targetapp.utils.SecurityUtils.a方法。我想看看,传入这个方法的map到底长什么样?返回的String又是什么?

创建一个hook_sign.js

Java.perform(function(){console.log("[*] Frida script started...");// 1. 定位目标类varSecurityUtils=Java.use("com.targetapp.utils.SecurityUtils");// 2. Hook 目标方法 'a'// 注意:如果方法有重载,需要用 .overload(...) 指明参数类型SecurityUtils.a.overload('android.content.Context','java.util.Map').implementation=function(context,map){console.log("\n================= HOOK DETECTED =================");// 3. 打印入参// 将 Map 转换为 JSON 字符串方便查看varmapStr=Java.cast(map,Java.use("java.util.HashMap")).toString();console.log("[+] Input Map: "+mapStr);// 4. 执行原方法,获取结果varresult=this.a(context,map);// 5. 打印结果console.log("[+] Output Sign: "+result);// 6. 打印堆栈信息(可选,用于追踪是谁调用了它)// console.log(Java.use("android.util.Log").getStackTraceString(Java.use("java.lang.Throwable").$new()));console.log("=================================================\n");returnresult;};});
3.3 注入并见证奇迹

在 PC 终端运行 Frida 命令:

frida -U -f com.targetapp.android -l hook_sign.js

手机上 App 自动启动。我随意在搜索框输入“Python”,点击搜索。

那一刻,终端屏幕疯狂滚动,直到这一行日志出现,我悟了:

================= HOOK DETECTED ================= [+] Input Map: {keyword=Python, page=1, timestamp=1678888888, nonce=Ax9871hz} [+] Output Sign: 3f8a0b9c7d6e5f4a3b2c1d0e9f8a7b6c =================================================

等等,这还不够。我还需要知道salt是什么!
我们可以进一步 Hook 那个 MD5 的入口,或者直接在刚才的代码里,Hook 那个b(map)方法(疑似排序拼接字符串的方法)。

修改脚本,HookSecurityUtils.b

SecurityUtils.b.implementation=function(map){varresult=this.b(map);console.log("[*] Serialized String (Pre-Salt): "+result);returnresult;}

再次运行,输出:

[*] Serialized String (Pre-Salt): keyword=Python&nonce=Ax9871hz&page=1&timestamp=1678888888

看来是标准的 ASCII 排序拼接。
那么盐呢?通常盐是直接拼在字符串后面的。如果我 Hook MD5 函数的入参,就能看到完整体了。

Hookcom.targetapp.utils.MD5.encode(String str):

varMD5=Java.use("com.targetapp.utils.MD5");MD5.encode.implementation=function(str){console.log("[!!!] MD5 Input (THE SECRET): "+str);returnthis.encode(str);}

最终的真相:

[!!!] MD5 Input (THE SECRET): keyword=Python&nonce=Ax9871hz&page=1&timestamp=1678888888&secret=Unicorn_2024_@!#

我悟了!原来所谓的“高强度加密”,就是在参数屁股后面拼了一个固定的盐值&secret=Unicorn_2024_@!#,然后做了一次 MD5。

所谓的“大厂防线”,在内存可视化的那一刻,就像没穿衣服一样。


4. 第三阶段:武器化(Python RPC)

知道了原理,我们就可以用 Python 复现算法。但有些时候,算法可能极其复杂(例如魔改的 AES,或者逻辑在 .so 层很难还原)。

这时候,最好的办法不是还原,而是借用。我们要利用Frida-RPC,让 Python 脚本远程调用手机里的加密函数。

4.1 修改 JS 脚本为 RPC 模式
// rpc_sign.jsrpc.exports={// 导出这个函数给 Python 调用getSign:function(keyword,page,timestamp,nonce){varresult="";// 必须在 Java 线程中执行Java.perform(function(){// 获取当前 Context (通常用 ActivityThread 或者 Application)varcurrentApplication=Java.use('android.app.ActivityThread').currentApplication();varcontext=currentApplication.getApplicationContext();// 构造 HashMapvarHashMap=Java.use("java.util.HashMap");varmap=HashMap.$new();map.put("keyword",keyword);map.put("page",page.toString());// 注意类型转换map.put("timestamp",timestamp.toString());map.put("nonce",nonce);// 主动调用加密函数varSecurityUtils=Java.use("com.targetapp.utils.SecurityUtils");result=SecurityUtils.a(context,map);});returnresult;}};
4.2 编写 Python 脚本
importfridaimporttime# 连接 USB 设备device=frida.get_usb_device()# 启动或附加到进程session=device.attach("com.targetapp.android")# 加载 JSwithopen("rpc_sign.js")asf:script=session.create_script(f.read())script.load()# 调用导出函数# 这里的 script.exports 对应 js 里的 rpc.exportsprint("[*] Calling App function via RPC...")keyword="CSDN"page=1ts=int(time.time())nonce="random123"# 就像调用本地函数一样调用手机里的函数!sign=script.exports.get_sign(keyword,page,ts,nonce)print(f"[-] Keyword:{keyword}")print(f"[-] Timestamp:{ts}")print(f"[-] Generated Sign:{sign}")# 接下来就可以拿着这个 sign 去发 requests 请求了

效果
你运行 Python 脚本,手机后台默默地计算出了sign并返回给了你。你不需要扣算法细节,不需要关心盐值变没变。只要 App 不更新,你的爬虫就永远有效。

这就是RPC (Remote Procedure Call)的降维打击。


5. 进阶思考:如果逻辑在 Native 层 (.so) 怎么办?

如果SecurityUtils.anative方法:

publicstaticnativeStringa(Contextcontext,Map<String,String>map);

Jadx 就看不到了。这时候我们需要:

  1. 解压 APK,提取.so文件。
  2. 使用IDA Pro进行反汇编。
  3. 利用 Frida HookInterceptor.attach(Module.findExportByName("libnative.so", "Java_com_..."), ...)
  4. 分析汇编指令(ARM64),查看寄存器(x0, x1…)中的值。

虽然难度升级,但核心思路不变:Input -> BlackBox -> Output。只要我们能在 BlackBox 执行前截获数据,我们就赢了。


6. 总结与反思

回顾整个过程,为什么我说“我悟了”?

  1. 加密不是魔法:无论 App 吹嘘多么安全的算法,在客户端层面,它必须包含“加密逻辑”和“密钥”。只要它在本地运行,就有被逆向的可能。
  2. Hook 的上帝视角:静态分析是读天书,动态分析是看电影。Frida 让我们跳过了复杂的逻辑推导,直接看到了结果。
  3. 攻防的不对称性:开发者为了保护一个参数,可能写了上千行混淆代码;而逆向者只需要几十行 Frida 脚本就能绕过。

给开发者的建议(防御)

  • 不要把核心盐值硬编码在 Java 层字符串里。
  • 关键逻辑放入 Native 层(C++),并进行虚假控制流混淆(OLLVM)。
  • 增加 Frida 检测、Root 检测、模拟器检测。
  • 最重要的:风控要放在服务端!不要轻信客户端传来的任何sign,要结合 IP、行为轨迹、设备指纹做综合判定。

互动环节 (Hook)

👀你在逆向过程中遇到过最奇葩的加密算法是什么?

  1. 把参数转成 Base64 再倒序?
  2. 把时间戳藏在 User-Agent 里?
  3. 或者是把密钥写死在图片 Exif 信息里?

在评论区晒出你的“脱坑”经历,点赞最高的评论,我下期专门出一篇针对 Native 层 .so 逆向的保姆级教程!别忘了关注我,带你硬核玩转 Android 安全!👇

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/4/16 12:52:14

【收藏不迷路】大模型学习进阶篇:避开误区,从实战到落地全攻略

随着大模型技术的普及&#xff0c;越来越多程序员和AI小白投身其中&#xff0c;但不少人陷入“学了就忘”“实战卡壳”“落地无门”的困境。相较于基础入门&#xff0c;掌握科学的学习方法、精准避开技术坑点、聚焦场景落地&#xff0c;才是快速吃透大模型的关键。本文基于实战…

作者头像 李华
网站建设 2026/4/17 1:41:28

飞机起落架减震系统

1.2飞机起落架的布置形式 起落架的布置形式是指飞机起落架支柱(支点)的数目和其相对于飞机重心的布置特点。目前&#xff0c;飞机上通常采用四种起落架形式&#xff1a; 图1.1 后三点式起落架 后三点式起落架&#xff1a;这种起落架有一个尾支柱和两个主起落架。并且飞机的重…

作者头像 李华
网站建设 2026/4/16 9:21:38

普通人想入网络安全,到底该走哪条路?

对普通人来说&#xff0c;网络安全这条路到底该怎么走&#xff1f; 由于我之前写了不少网络安全技术相关的故事文章&#xff0c;不少读者朋友知道我是从事网络安全相关的工作&#xff0c;于是经常有人在微信里问我&#xff1a; 我刚入门网络安全&#xff0c;该怎么学&#xff…

作者头像 李华
网站建设 2026/4/17 17:41:45

性能测试必看 | 8个方法让图片加载更快一点

看到这个标题&#xff0c;相信你都会怀疑是否看错了吧&#xff0c;觉得这应该是前端开发会去关注的事&#xff0c;测试人员管这个干嘛&#xff0c;是这样&#xff0c;有一次&#xff0c;我在做一个web网站的压测时&#xff0c;总感觉并发上不去&#xff0c;后台&#xff0c;中间…

作者头像 李华
网站建设 2026/4/12 20:12:05

Jmeter 工具如何做接口测试和接口自动化测试

Jmeter工具作为测试业界的最常用的工具之一&#xff0c;无疑功能是非常强大的。它可以做接口功能测试&#xff0c;也可以做接口自动化测试&#xff0c;还可以做接口性能测试。你针对这个工具的使用程度不一样&#xff0c;也都代表着你测试级别是不一样的&#xff0c;自然你的薪…

作者头像 李华