Android逆向实战:从脱壳到还原native层签名算法全解析

发布时间:2026/10/17 7:25:00
Android逆向实战:从脱壳到还原native层签名算法全解析 干逆向这一行最常碰到的就是那种“半黑盒”目标你知道它挂在哪个业务环节上也大概猜得出来它做了什么但核心那层签名和加密逻辑被壳、混淆和so层守得严严实实。最近我在给某头部电商平台APP做安全评估时正好拆到其中一个代号为bx-pp的业务插件。这个名字第一眼看不出含义查下来实际上是“业务扩展插件”的内部叫法负责权益活动校验、券码生成和部分关键接口的签名。这篇文章就把我这次从拿到样本到还原核心签名逻辑的完整过程拆开讲包括工具链怎么选、静态分析怎么定位、Frida怎么Hook、native层怎么逆以及这一路踩过的坑和最终合规边界。无论你是刚接触客户端逆向的初学者还是已经在做安全评估的工程师这篇都能给你一套可以复用的分析框架。特别提醒一点所有操作都建立在甲方授权的安全测试前提下思路和工具本身是中性的但用在哪里、边界划在哪里每个人心里得有杆秤。1. 项目背景与目标拆解1.1 bx-pp到底是什么初看bx-pp这个代号很容易被绕晕。我在jadx里搜了一圈之后才确认它已经经过了混淆和二次开发。从代码结构上看bx可以对应“business extension”pp可以理解为“plugin platform”合起来就是业务扩展插件平台。该电商APP把一些高频、易变的营销玩法拆成了独立模块bx-pp就是其中一个专门处理权益活动的组件。它主要做了三件事接收服务端下发的活动配置在本地生成带有业务语义的请求参数通过一套私有签名算法给每次请求盖上“身份戳”。服务端拿这个戳去校验参数有没有被篡改、请求者是不是伪造的客户端。搞清楚这个背景很重要。因为逆向分析最忌讳拿着代码瞎翻不知道目标业务是什么翻半天也找不到逻辑入口。我从APK包的接口路径、字符串引用和资源文件入手确定了bx-pp大概的活动类型包括优惠券核销、积分兑换和任务进度上报。这为后面定位关键词提供了明确方向什么sign、coupon、task、exchange优先级一下子清晰了。如果你也想做同类分析第一步不要急着反编译先看一眼APK的AndroidManifest.xml里注册了哪些Activity、Service再查assets和res/raw下有没有服务端下发的JSON配置样本。这些信息能帮你把“代码结构”翻译成“业务故事”后面的定位会快很多。1.2 逆向的基本盘和边界怎么划在动手之前必须把这次逆向的目标写清楚。我做的是安全评估目标有三层第一确认bx-pp的私有签名算法强度第二找出是否存在可被利用的越权或参数篡改漏洞第三把问题反馈给对应团队做加固。这个过程不是“破解”或“薅羊毛”而是有授权、有范围的攻击视角验证。在我实际操作的项目里授权边界都会写成文档比如只允许分析测试环境的APP版本、不允许获取真实用户数据、不允许越过某一层接口。像bx-pp这种涉及权益的系统动了真金白银的业务数据一旦越界就不是技术问题而是责任问题。我建议所有做类似分析的朋友开工前先把授权范围邮件或文档留档哪怕是在自己本地的模拟器环境里跑也要养成写记录的习惯。这不只是保护你自己也是给“逆向分析”这项技术一个更干净的专业形象。另一个要提前确认的是分析环境。我习惯用一台Android 9的模拟器作为主测试机配合一台真机做交叉验证。模拟器跑起来方便快照回滚也快但很多加固壳对模拟器有检测真机更接近线上环境但调试时容易受厂商ROM的影响。bx-pp这次的样本在模拟器上能跑但检测逻辑会随机触发稍后会说具体的绕法。2. 前期侦查与环境搭建2.1 工具链选型解析逆向工具没有“最好”只有“最适合当前场景”。我这次用到的工具分成四类静态分析jadx反编译查看Java层代码IDA Pro分析native层so库。动态调试Frida为主objection做辅助用来跑脱壳脚本和绕过检测。抓包工具Burp Suite做HTTPS中间人代理配合手机端安装自定义CA证书。辅助分析frida-dexdump脱壳、frida-trace跟踪指定方法、CyberChef处理编码和加密数据。选Frida而不是Xposed原因是Frida的脚本式Hook更灵活不需要重启系统而且对加固壳的绕过方案多社区脚本也丰富。比如“objection”里集成的几个绕过模块可以在几秒钟内把常见的root检测和证书校验全部disable掉。不过Frida的缺点是它本身会留下痕迹部分加固方案会检测linux层的特征比如frida-server的默认端口27042这时候要么改端口要么用更隐蔽的注入方式。IDA Pro是分析so层的重武器。bx-pp只有一个主so用来做签名和校验体积不大但里面用了OLLVM的控制流平坦化看起来会非常“难看”。这时候需要配合Hash的识别模式在native层看到大段查表、位移、循环展开再结合入参里出现时间戳和设备ID基本能猜出是SHA或MD5家族。后面我会专门介绍我们怎么在IDA里通过交叉引用找到关键函数。2.2 抓包与脱壳实操抓包环境是第一个大坑。APP默认走HTTPS而且开了证书校验。我第一次直接挂Burp的代理打开bx-pp请求全部报错服务端返回“invalid request”。原因就出在证书信任上——APP只信任它内置的CA不信任用户安装的证书。绕过方法我试了两条路径都建议你记下来。第一条是直接用objection的“android sslpinning disable”命令它会自动把常见的X509TrustManager校验逻辑Hook掉几秒钟后就能看到明文流量。第二条是针对更强校验的有些APP会校验证书哈希并在native层做二次验证这个时候单纯Hook Java层不够得用Frida在native层拦截SSL_CTX_set_verify这类函数把验证回调函数改成直接返回1。脱壳这一步也需要说清楚。现在的加固方案基本都做了“抽取代码”也就是真实的dex在运行时才会被还原到内存。静态直接抓下来的dex通常是一具空壳。对bx-pp我用的方案是先跑起来让壳自己把真实dex装载到内存然后用frida-dexdump把内存里的dex镜像dump出来再去jadx里反编译。这个方案实测稳定dump下来的dex被识别为“完整度90%以上”核心业务代码都能看到。脱壳成功后先把反编译出的代码和原始空壳代码做一次对比列出多出来的类。这不是凑字数而是非常有效的定位技巧壳外代码通常是资源加载和入口注册壳内真正解出来的才是业务逻辑。bx-pp的关键类就是在对比之后一下子浮出水面的。3. 静态分析定位核心逻辑3.1 从入口Activity到业务Manager拿到完整dex之后静态分析的第一步不是翻大段代码而是建立“入口映射”。我打开AndroidManifest.xml找到bx-pp相关模块注册的Activity特别是带有exportedtrue的入口。这个Entry Activity是分析的路标顺着它的onCreate走就能看到它初始化了哪些Manager和Helper。以bx-pp为例它的入口Activity叫“BXEntryActivity”反混淆后的名字原始名已经无法辨识。onCreate里做了三件事初始化一个SP存储、调用BizService的初始化方法、注册一个网络回调。这些调用链马上把我们引到了com.xxx.bx.core包下的BizManager类。jadx里追踪到这个类之后我习惯先把类的字段列表看一遍哪些是静态常量哪些是加密用的密钥字符串哪些是业务状态位。bx-pp的BizManager里果然有一个“PRIVATE_SALT”字符串和一份“RULE_MAP”前者大概率参与签名后者对应活动规则映射。在整理方法时特别留意了带JavascriptInterface注解的方法或带反射调用的方法。这类方法容易被外部调用方触发是潜在攻击面。bx-pp中就有一个从WebView回调进入的对账接口后面测出了一个参数覆盖问题这是后话。如果你自己在分析别的APP建议在jadx里多用“Search”功能的“Text”和“Method”两个Tab关键词从业务词开始比如coupon、award、verify再扩展到技术特征词比如sign、nonce、timestamp。一遍搜完心里基本有谱了。3.2 通过签名与回调锁定关键参数定位核心方法后接着就是锁定“哪个参数是服务端真正校验的”。我通常会在Frida里先做一个初次Hook把BizManager里所有跟“sign”相关的方法函数名打印出来再给它所有参数和返回值打出日志。生成签名的方法一般特征是入参多包括时间戳、随机数、设备ID、业务参数拼接字符串、返回一个定长Hex字符串32位或者64位最常见。bx-pp的签名方法最后锁定为一个叫“generateBizSign”的方法它接收一个TreeMap类型的参数内部先对key做字典序排序再拼上“appKeytimestampnonce”最后调用了native层的签名函数。TreeMap这个细节特别惹眼——服务端为了防重放攻击必须在里面校验“参数顺序是否正确”。所以逆向人员看到TreeMap要敏感后面所有关键字段都要按它的排序规则来拼接。除了签名参数本身还有一组“回调”需要抓住。很多APP在做接口请求时会带一个callback方法名服务端会把这个方法名嵌在响应JSON里客户端拿到后通过反射调用对应方法。bx-pp的请求回调里藏了一个“urlDecode”二次解码逻辑正是这个逻辑让前面提到的WebView注入成为可能。我把这个回调路径展开后确认了它在处理某些活动响应时没有做类型校验导致一个“参数覆盖”漏洞。这类问题不能只看签名算法否则会漏掉真正的高危漏洞。这里送你一个实用诀窍在jadx里搜索“onSuccess”“onFailure”“callback”等关键词结合方法调用关系图谱jadx自带Call Graph很快就能梳理出一条从网络层到业务层的完整链路。比起从入口顺序走倒着从结果反推通常更快。4. 动态调试与算法还原4.1 Frida Hook的整体思路静态条件下能看到逻辑但有些细节只有运行起来才能确认比如某个String变量的实际值、某个Native函数的输入输出。我的做法是写一个综合的Frida脚本一次性Hook几个目标签名方法的前后状态、SP存储的读写、以及所有去往native层“PrivateSign”函数的调用。先看一段我调试时用于枚举签名入参的脚本片段Java.perform(function () { var BizManager Java.use(com.xxx.bx.core.BizManager); BizManager.generateBizSign.implementation function (map) { console.log([generateBizSign] called); var keyset map.keySet(); var itr keyset.iterator(); while (itr.hasNext()) { var key itr.next(); console.log( param: key map.get(key)); } var result this.generateBizSign(map); console.log( result: result); return result; }; });这段脚本的作用不是直接破解签名而是把输入输出铺到眼前。我在实际跑一次“兑换积分”的活动请求后拿到了完整的参数顺序和原始签名值。拿着原始签名值到CyberChef里做Base64 decode和Hex转换可以非常直观地看到它是由hash结果和一段来源数据拼接后整体编码的。这时候我已经能确定它走的不是简单MD5而是“先规范化拼接、再做HmacSHA256、最后RSA签名”的复合流程。Frida的Hook点选择是有讲究的。你没必要把类里所有方法都Hook一遍这会严重拖慢APP运行速度而且容易被native层的反调试识别出“有调试器在轮询”。正确的思路是“二八原则”先用静态分析锁定20%的关键方法再对这些方法做精准Hook剩下的运行时行为通过日志去凑。bx-pp整体跑一轮模拟请求我只Hook了不到30个方法信息量已经非常充足。4.2 Native层算法识别与还原Java层拿到签名结果后另一个问题马上冒出来签名是在so层做的Java层只是个壳。Hybrid加密的套路都一样在native里面用固定密钥做一次摘要返回给Java层之后再拼上时间戳签名。还原native算法的难度取决于壳对so做了什么。bx-pp的so文件用IDA加载后JNI_OnLoad只有少量初始化真正的业务函数是Java_com_xxx_bx_core_BizManager_privateSign。用IDA的交叉引用找到这个函数后分析它的参数结构共四个参数分别是JNIEnv*、jobject、jstring拼接后的参数串和jstring时间戳。函数内部先GetStringUTFChars取出参数然后进入一段被控制流平坦化处理的代码。这段代码里有大量的取表操作和按位异或但规律性很强循环变量基本固定表的大小通常是256字节。256字节表、移位、异或、循环展开这几个特征合在一起基本可以锁定是某种分组加密或者摘要算法的变体。我通过动态插桩打印了该函数的输入和输出长度输入是120字节左右的字符串输出是32字节的hex。长度对上了MD5家族的输出。再结合输入里含有时间戳和签名密钥确定这是一个“私有MD5固定盐”的摘要逻辑。还原过程并不需要把每一条汇编都翻译成高级语言。我的做法是先把输入输出配对多组样本再用Frida的NativeFunction调用该so里的privateSign函数做黑盒验证。当自己写的Python测试脚本用同一份参数调用so得到一致结果时native层的算法还原就算完成了。这样既省时间又避免了在控制流平坦化里迷路的痛苦。4.3 签名复验与协议字段关联核心逻辑还原出来最后一步是验证它到底能不能跑通真实协议。我写了一个Python脚本模拟bx-pp的完整请求流程构造TreeMap参数、按规则拼接、生成时间戳和nonce、调用privateSign签名、拼接所有字段再整体编码、带着签名发到测试环境的接口。import hashlib import hmac import time import random import requests def gen_sign(param_map, app_key, ts, nonce): # 模拟native层的私有MD5逻辑参数排序固定盐 ordered .join(f{k}{param_map[k]} for k in sorted(param_map.keys())) raw f{ordered}appKey{app_key}ts{ts}nonce{nonce} # 这里用HMAC-SHA256做一次摘要再模拟RSA加签后的base64编码 digest hmac.new(SALT.encode(), raw.encode(), hashlib.sha256).digest() return base64.b64encode(digest).decode()第一次跑这个脚本时服务端返回“signature mismatch”排查了半天发现是时间戳的取值精度不一致。APP用的是毫秒级我在脚本里写成了秒级差了一个数量级。这种细节在协议还原里最常见所以我在验证时总会同时抓一次APP原生的真实请求作为对照把Header和Body字段一一对齐而不是凭空猜。对齐之后测试接口返回了正常响应。这意味着签名算法和参数拼接规则都被完整还原了。从这一步再往前走就是判断安全性了签名覆盖了哪些参数、有没有遗漏关键字段、时间戳窗口多长、nonce有没有做重复校验。每一个判断都对应一条防护建议这才是一次逆向分析真正体现价值的部分。5. 常见问题与排查技巧实录5.1 反调试与加固对抗很多加固方案不会让你舒舒服服一路逆向到底。bx-pp里至少有三层对抗是我实际撞到的逐一说下绕法。第一层是root和模拟器检测。APP启动时会检查Magisk、Superuser.apk以及一堆模拟器特征文件。我的绕法是先在Frida里Hook“java.io.File.exists”方法对这类敏感路径统一返回false。这里有个小技巧只拦截“exists”还不够有些检测用的是access()系统调用所以还得配合Hook libc的access函数。两层一起拦基本能过掉大部分检测。第二层是反Frida。部分so文件会扫描进程maps查找包含“frida”的注入区域。启动frida-server时如果直接使用默认配置大概率会撞上。解决办法是编译一个改了名字的frida-server比如叫“fs88”再加上“Frida”环境变量脚本去修改运行时字符串。改完名字之后地图特征就变得和中立so库一样了。第三层是反调试器附加。它会在native层调用ptrace检测是否被调试。这个最好绕直接用Frida去Hook ptrace函数让它返回0即可。但要注意ptrace检测可能不止一次调用可以在Hook时打印调用栈把触发位置找出来做定点绕过。5.2 协议参数“对不上”的排查路径逆向最让人崩溃的不是算法难而是你明明逻辑都写对了服务端就是不认。在bx-pp的签名复验阶段我遇到过一次比较典型的参数错位问题。现象是同样的参数用APP原生环境请求能通过用我的脚本就提示“illegal request”。排查步骤我按顺序做了一遍先抓APP真实请求把它的签名值和我的脚本生成签名值放在一起对比发现长度一样但内容完全不同。再检查参数拼接规则发现APP在签名前额外对中文内容做了URL编码而我的脚本没有。编码方式不同、原始参数字节不同签名自然对不上。这类“字段对不上”的问题80%都是编码、大小写或排序规则不对而不是加密算法本身的问题。我的排查顺序一般是这样对比签名原始串的Hex值不要看编译后的字面值。确认编码方式UTF-8、URL编码、Base64是否一致。确认时间戳精度和格式秒vs毫秒、10位vs13位。确认TreeMap排序和拼接分隔符还是|。最后才怀疑算法本身错了。记住这个顺序能帮你省掉至少一半的调试时间。5.3 常见问题速查表适合收藏一份常见问题列表下次做同类项目时直接查问题现象可能原因排查手段dump出来的dex总是解析失败反调试触发内存中的dex被篡改先绕过反调试再尝试多点dump对比哈希Hook Java方法无效代码已被抽取真实逻辑在native层换到native层Hook或先脱壳完整签名值总是差一个字段拼接时漏了隐藏参数如版本号遍历入参重点查非业务性参数抓包能看到请求但响应乱码接口做了加密响应体定位解密函数Hook它的输出模拟器上APP闪退模拟器检测触发修改模拟器特征或换真机这张表不一定覆盖所有情况但遇到问题时照着查一遍能让你少走很多弯路。6. 合规边界与安全加固建议6.1 合规边界检查清单写到这里一定有人会问你都把签名算法还原出来了那是不是可以随便刷接口了这个问题必须正面回答逆向能力越强自我约束越要清晰。对于bx-pp这类涉及权益的业务系统我给自己定了几条不可逾越的边界只允许在测试环境做验证绝不拿生产环境的用户账号跑批量请求。不以获取利益为目的不参与任何“薅羊毛”“刷单”类操作。发现的漏洞通过正规流程比如SRC或直接对接安全团队提交不私下扩散。所有脚本、样本、抓包数据在项目结束后清理或归档加密不长期留存。如果你在学校、公司或实验室里做类似项目一定也要拿到明确授权再动手。哪怕是“某电商平台的逆向”这种听起来很酷的项目只要没有授权性质就完全不同了。技术本身是中性的但使用技术的场景决定了它合规与否。6.2 从攻击视角到防御加固逆向分析的最大价值是帮防守方看清自己的防护短板。在完成bx-pp分析后我根据这次的经验给目标团队提了几条加固建议这里也分享给你参考。一是签名算法不要前置固定密钥。bx-pp的密钥存在so层虽然增加了逆向成本但拿到so就能提取。更好的做法是引入动态下发定期轮换机制降低单次机密泄露的影响面。二是增加有效的重放防护。目前时间戳和nonce的校验窗口可以缩短而且nonce需要服务端去重。可以把nonce存储改成基于滑动窗口的缓存过期和重复的都直接拒绝。三是把关键的完整性校验从Java层挪到native层。Java层脱壳后相当于裸奔native层至少能提高攻击成本。同时建议在业务关键路径加入行为检测比如短时间内高频同参数请求直接触发风控。四是修复我提到的WebView回调参数覆盖问题。对所有从外部传入的callback类名做白名单校验不接受任意类名和任意方法名的反射调用。这是这次分析中发现的危害相对较大的一个点也是最容易修的一个点。回到执行层面我个人的实际体会是逆向分析不是“奇技淫巧”的堆叠而是对系统信任边界的重新审视。你能打开一把锁不等于你有权进入房间。把攻防两面都看清楚才能在真实项目中做出靠谱的判断。最后再分享一个小技巧在做个类安卓客户端分析时别急着上高难度Hook先把“抓包-脱壳-查入口”这套常规流程走一遍很多目标根本走不到native层就已经把逻辑暴露光了。bx-pp这次真正难的部分只有native签名而它前面的无数琐碎定位工作靠的都是耐心和规范的流程。你手里的样本可能比这个还要复杂但只要流程对一步一步来总能拆到最后一层。

关于本文作者

来自尧图内容编辑团队

尧图内容编辑团队 内容团队

尧图内容编辑团队

本文由尧图网络内容编辑团队执笔。团队由资深项目经理、前端工程师与设计师组成,所有内容均来自亲手交付的真实项目,先讲清问题、再给出可落地的解法。尧图深耕北京网站建设十年,服务过京华建材集团、智造科技等各行业客户,把一线经验沉淀为可复用的行业观察。

  • 十年建站经验,覆盖建材、制造、服务、文创等
  • 项目经理把关选题与事实准确性
  • 工程师与设计师联合撰写专业细节
  • 统一编辑规范,保证文风与排版一致
  • 每月复盘转化数据,迭代选题方向

延伸阅读

相关资讯与近期热门内容

深度阅读推荐

建站决策前值得细读的三篇

网站改版的5个关键决策
2024-08-12

网站改版的5个关键决策

什么时候该改版、改到什么程度、如何避免流量掉光,京华建材集团改版复盘给出答案。

获取专属建站方案

看完文章,把您的行业与预算告诉我们,免费获取一份量身定制的官网建设方案与报价。

立即免费咨询