
搞过验证类SDK分析的朋友一看“某盾blackBox逆向——纯算”这个说法基本能秒懂说的是哪个方向。blackBox就是黑盒意思是把整个SDK当成一个不透明的盒子不去管内部怎么Hook、怎么反调试只看输入什么、输出什么纯算则是把盒子里的算法一层层还原成可独立运行的代码脱离SDK环境也能算出同样的结果。这篇文章就围绕这条“黑盒到纯算”的技术路线说说它到底能解决什么问题、适合什么场景以及我在实操里踩过的坑和摸索出来的方法。如果你正准备对一个带验证、风控、签名能力的SDK做算法分析又没有足够条件做动态调试那这篇文章应该能帮你省不少时间。我会尽量把思路讲透也会给一些可以照着做的步骤但请记住所有分析仅限你自己有授权的目标别拿去打真实业务。1. 纯算逆向到底在逆什么1.1 某盾类SDK为什么适合黑盒分析某盾这类验证和风控SDK对外表现其实特别收敛要么启动时采集设备信息要么在业务请求里塞进一坨加密参数最后统一提交到一个验证接口。你拆开App看到的请求体翻来覆去就是那几个字段deviceId、profiles、riskData、sign、token再藏一点随机数和时间戳。这种形态天然适合黑盒分析因为它的核心价值集中在“几个请求参数是怎么生成的”这件事上。它不像游戏引擎需要还原渲染管线也不像协议栈需要关心每个包的状态机。你要关心的只是给定一组输入算法到底怎么加工才能得到输出参数。这种问题本质上可以在不透过程序内部的情况下靠输入输出对来反推。举个我常用的类比你不需要拆开闹钟的齿轮只要反复观察“拨动指针到几点闹钟在几点响”多试几次就能总结出定时逻辑。如果闹钟还带贪睡功能你就加一个变量继续试。SDK的签名算法也是一样无外乎是“几个参数进去一组字符串出来”黑盒分析的底层逻辑完全成立。1.2 纯算与Hook、动态调试怎么选很多新手一上来就想着Frida Hook把目标函数直接改成打印参数。这个思路没错但在某盾这类高对抗场景下往往死得很难看反调试检测、root检测、模拟器检测稍微没打点好进程就主动退出或者返回一个假数据把你带偏。纯算路线不一样它把博弈点从“进程内对抗”转移到了“数据分析”上。你在黑盒外面目标进程甚至感觉不到你在分析。这样有几个非常现实的好处不依赖特定设备和系统版本不用守着那台唯一能Hook的旧手机。不需要和反调试正面硬刚省掉大量“加固对抗”的体力活。还原出来的算法可以直接集成到服务端脚本、自动化流程、跨语言工具里可移植性极强。我用一张表把常见技术路线做了对比方便你按场景选方案上手速度对抗强度环境依赖结果可移植性典型问题Frida Hook快弱设备/系统强相关低还得靠设备跑反调试检测、环境兼容主动调用/模拟器中中需要可用的调用环境较低每次要先起环境调用栈不完整、参数难满足unidbg模拟执行较快中能跑so即可中算法在so里仍待还原环境差异、反调试检测纯算法还原慢强一台电脑抓包工具高任何语言可复现费脑、需要耐心如果你追求的是长期稳定、不绑设备纯算几乎是必经之路。但我也得说实话纯算是慢功夫尤其是刚开始的时候一个参数差异能折腾你一晚上。所以我的建议是“以纯算为主、动态辅助为辅”遇到黑盒实在无法判断的关键分支再用Hook或unidbg定点打一下两边互相验证。1.3 算法是怎样被“逼”出来的有人会问别人的算法是编译在so里的怎么可能只靠输入输出就还原出来这话对一半。算法本身确实是不可见的但它必须遵守程序的基本规律确定性计算加上可变输入产生可观察的输出。只要我们能控制输入并且拿到足够多的输入输出对就能把这个“确定性函数”的外形摸清楚。比如某次分析我发现把deviceId改一个字符输出里有一段32位hex完全改变了而另一段48位hex只变了几个字节于是就能推断出32位那段大概率由deviceId直接参与计算48位那段则含有与deviceId无关的固定分量。真实场景会比这个复杂因为里面混着时间戳、随机数、nonce这些“会动的变量”。但换个角度看这反而是好事它们等于暴露了算法的输入维度。如果你把时间戳设成固定值、随机数固定掉输出的变化范围就会一下子缩小很多。所谓黑盒还原本质就是一个不断“固定变量、控制变量、对比输出”的过程。2. 黑盒分析前的准备工作2.1 先画清楚目标的数据流拿到一个带某盾SDK的App先别急着抓包和跑脚本。我习惯先画一张简单的数据流图搞清楚数据从哪里来、到哪里去、哪些字段是算法生成的、哪些是服务端下发的。大致步骤是安装目标App启动触发一次完整的验证请求然后用抓包工具记录所有网络报文。要特别关注三类参数身份标识类deviceId、uuid、seq这类字段一般由设备信息加工而来。时间/随机类timestamp、nonce、sign这类字段往往是算法输入的一部分。签名/摘要类sign、sig、token名字里带sign和token的基本都是重点分析对象。画数据流图的时候不用画得很标准自己能看懂就行。关键是标出“哪些字段在请求里出现、在响应里返回、在本地生成”。比如某个riskData请求前出现请求后服务端会返回一个验证结果那它大概率是客户端生成后提交给服务端校验的这就是纯算的切入点。2.2 工具选型和环境准备纯算路线对设备要求很低一台电脑加抓包工具就够。但工欲善其事必先利其器我把自己常用的工具列一下都是很常见的不用刻意追求高端抓包与报文分析Burp Suite或者Charles重点看HTTPS请求。如果目标是自有App没有证书校验问题直接装证书即可。字节与文件分析HxD或者010 Editor用来查看so文件里的明文字符串和常量。编码与加密辅助CyberChef非常推荐Base64、Hex、MD5、AES这些转换拉一拉就出来能省很多手工代码。脚本计算Python配合hashlib、Crypto、requests这几个库几乎所有算法验证都能搞定。模拟执行辅助unidbg后面讲验证还原结果时会用到它不是必须的但在纯算卡壳时能帮你定位so里某个函数的输入输出。这里有个小建议所有抓包数据都导出成har或者结构化文本建一个样本目录。不要只在终端里看否则后面做差分对比时你会很痛苦。2.3 授权边界和心态准备安全研究的前提是授权。别拿这套方法去打自己无权测试的业务更不要试图绕过真实的风控系统牟利。很多安全圈的前辈都强调过能把算法算出来是技术能力管住手是职业底线。这篇文章里的方法只建议用在你自有应用、CTF赛题、授权的众测目标或者学习研究上。心态方面也要调整好。纯算是一个“低手气、高回报”的活大部分时间是在试探和排除真正写算法代码的时间可能只占两成。如果抱着“半小时出结果”的心态很容易半途而废。我自己的经验是分段设置小目标今天先判断sign是不是HMAC明天再确定入参顺序后天再验证AES的key。这样每步都有收获心态就不容易崩。3. 从报文中抓算法指纹3.1 一眼认出常见编码和摘要算法拿到报文后最难的一关其实是“猜算法类型”。但很多SDK都没有强大到自研加密算法他们大部分在标准算法上套了一层组合逻辑。所以识别算法指纹非常关键。最直观的指纹是输出长度和字符集特征输出样例初步判断固定32位hexa5c3f1...32位MD5或HmacMD5固定40位hex32位多一点40位SHA-1固定64位hexe3b0c442...64位SHA-256结尾是的可见字符YWJjZABase64或AES后Base64密文长度是16倍数32字节/48字节大概率AES-CBC/ECB定长但每次不同长度固定内容全变哈希或HMAC且含随机盐比如某次我分析某盾类SDK看到一个字段叫sign值是64位hex第一反应是SHA-256。但把同一组参数重新触发一次sign还是64位hex内容却完全变了。这就不像纯SHA-256更像HMAC-SHA256因为里面很可能混入了nonce或时间戳作为key或盐。如果你发现某段密文在明文基本相同的情况下一次和一次结果都不同那还要多考虑一个可能性算法里用了随机填充或者使用了带随机iv的加密模式。RSA也有这个特点因为PKCS1填充会引入随机字节导致同一个明文每次加密结果不同。3.2 黑盒敏感性测试找到哪些字段进了算法这一步是纯算的核心基本功我管它叫“黑盒敏感性测试”。具体做法非常朴实控制变量一次只改一个字段然后观察输出参数有没有变化。举个例子我抓到一组正常请求里面包含deviceId、timestamp、nonce、sign四个字段。第一次只改deviceId重新触发请求发现sign变了第二次只改timestampsign也变了第三次只改noncesign还是变了。这就说明三个字段全部参与了sign的生成。如果改一个字段对sign毫无影响那它大概率只是业务参数和签名算法无关。实际操作中建议写个简单脚本批量触发这几种改动把结果记录下来。不要手动一次一次点那样效率太低也容易看漏。还有一点要注意某些字段对签名的影响是间接的比如改动nonce可能影响服务端下发的一个key再由这个key影响签名。遇到这种情况就要往上追一层看看nonce是不是先参与了另一个参数的生成。我在做了几次敏感性测试后会习惯画一张“关系表”把字段间的相互影响都记下来。这张表在后续还原算法时会变成最重要的地图。3.3 从组合结构推断构造顺序很多签名算法不是简单的哈希而是“拼串再哈希”。具体怎么拼、用什么分隔符是黑盒还原时最头疼的问题之一。好在这种拼串套路是有规律可循的。常见的构造模式无非是这几种字段按字典序拼接比如a1b2c3这种URL query格式。字段直接首尾相连比如abcd中间没有任何分隔符。字段之间用特定字符分隔比如竖线“|”、换行“\n”、逗号“,”。把所有字段放进一个JSON对象再序列化然后对序列化后的字符串做摘要。怎么判断是哪种还是靠差分法。比如你怀疑是“a1b2”这种格式就分别构造出候选字符串算一次哈希和真实sign对比。如果对不上就换分隔符再试。这个过程很机械但一旦试中了你会瞬间看懂整个签名逻辑。我遇到过一种情况直接拼接和JSON序列化都能算出相同结果原因是字段顺序恰好一致、JSON里没有多余空格。这种巧合会造成误判所以建议用特殊字符或空值来区分。比如让某个字段为空字符串看输出是否包含连续分隔符就能反推拼接细节。4. 纯算法还原的实操套路4.1 先把“组装逻辑”锁定纯算的第一步不是碰加密算法而是先把参数组装逻辑锁死。因为只要组装逻辑对了后面无论MD5还是SHA都能快速验证组装逻辑不对你就算把HMAC猜对了算出来也是错的。我的实操流程是这样拿到一组完整样本把所有可能入参的字段列出来包括设备信息、时间戳、随机数、固定盐值。用前面的敏感性测试确定哪些字段真正进入了最终输出。针对这些字段生成一批候选构造字符串覆盖常见分隔符和拼接顺序。用Python批量计算MD5、SHA-1、SHA-256、HMAC变种逐一和真实sign比对。等找到完全一致的那个构造就先定下来后面再根据情况调整。这一步看起来笨但非常可靠我在多个目标上都靠这个方法一步步锁定了签名逻辑。示意伪代码大概长这样仅供参考不是某盾的真实算法import hashlib, hmac def gen_sign(device_id, timestamp, nonce, secret_key): # 第一步按固定顺序拼接参数 raw |.join([device_id, timestamp, nonce, secret_key]) # 第二步做一次摘要 return hashlib.sha256(raw.encode()).hexdigest()真实SDK可能会先做一次Base64再做一次HMAC甚至中间插入一个长度字段。但无论包多少层黑盒阶段能用“构造后比对”的方法一层层剥开。4.2 摘要类与加密类分头处理拿到一个输出参数先判断它是摘要类还是加密类处理思路完全不同。摘要类MD5、SHA、HMAC是不可逆的你没法从输出倒推出输入只能靠“给定输入看输出是否一致”来验证。这类算法的好处是逻辑简单坏处是只要入参有一点不对加密结果就完全对不上排查难度高。加密类AES、RSA、RC4是可逆的如果你能定位到key和iv甚至可以解出中间的明文从而看清SDK到底加工了哪些内容。处理方式也很明确先在报文或so的静态字符串里找key、iv的线索再尝试把密文解出来看结构。某盾类SDK经常是两者混用传输业务数据用AES防止篡改用HMAC或RSA。我遇到过一种典型组合riskData字段是AES-128-CBC加密后的Base64sign字段是对riskData原文做的HMAC-SHA256。这种情况下先解AES能看到业务数据本体再看HMAC就能明白完整性校验范围。还有个关键问题key从哪里来黑盒阶段能确定的key来源主要有三种写死在客户端常量里、由某个固定算法根据设备信息生成、由服务端下发且随会话变化。前两种比较好处理第三种就需要把“下发key和解密/验签”联动起来分析复杂度会高一些。如果发现每次新会话key都变那就别急着还原算法先追key的生成或下发流程。4.3 用unidbg辅助验证还原结果纯算还原到一定程度一定会遇到瓶颈你猜了一个算法的中间输入但没法在真机上复现也没法确认so内部是否加了混淆。这时候unidbg就派上用场了。unidbg是一个可以在PC上模拟执行安卓so的框架适合用来定点验证“我猜的这个函数输入某某参数输出是不是某某值”。操作上大致是定位到so里负责生成的导出函数用unidbg加载so构造参数跑一遍然后把返回值和你黑盒推演的结果对比。它不算纯算路线本身但能帮纯算大大提高效率。一个典型的场景是你怀疑某段逻辑是把时间戳和随机数拼起来算HMAC但不确定用的是HmacSHA256还是HmacSHA1。用unidbg调一下真实so里的导出函数多传两组参数看输出长度就能快速区分。unidbg也不是万能的它经常遭遇so里的反调试、环境检测导致跑出来的结果和真机不一致。遇到这种情况要么先去patch掉检测点要么退回黑盒方式继续猜。我的原则是unidbg能验证就验证验证不了不强求别让它耽误主线分析。4.4 正确性验证三板斧算法还原出来不代表结束。真正让我心里踏实的是一套完整验证流程我称之为“三板斧”。第一板斧是历史样本回溯。把抓包记录里的几十条历史请求作为测试集用还原出来的算法重算sign比对原请求里的sign。如果每条都能对上说明算法至少在“这几十个输入空间内”是成立的。第二板斧是新样本预测。重新触发一次新请求在SDK发出请求之前先用你的算法算出sign等SDK真正发出来时再比对。这个过程能把“过拟合于历史样本”的假算法筛掉因为新样本是模型没遇到过的。第三板斧是服务端行为验证。把用你的算法生成的完整参数提交到测试环境或自有同名服务观察返回结果是否符合预期。这里要注意服务端可能还有频率、设备指纹、User-Agent等一系列校验返回失败未必是算法错。我踩过最大的坑是算法明明完全正确但就因为Base64在URL传输时把“”替换成了“%20”服务端接受不了。所以验证时不要只看算法的值对不对还要确认传输编码环节是否兼容。5. 常见问题与排查技巧实录5.1 玄学问题速查表纯算过程里“算出来差一点但不知道差在哪”是常态。我把常见症状和可能原因整理成了速查表症状大概率原因算出来的值总是差一位或几个字符哈希前的字符串末尾混入了换行符或空格Base64有padding差异同样的输入两次计算输出不同未固定nonce、随机数时间戳精度不同秒级/毫秒级本地算法和真机输出完全不一致入参中有隐藏字段没找到算法可能还叠加了一次压缩或序列化换台机器跑就结果不同设备指纹相关字段进了签名比如deviceId、AndroidId、安装时间签名算对了但服务端仍拒绝服务端还校验了User-Agent、设备信息、请求频率等其他维度unidbg和真机输出不一致so内部有环境检测或依赖系统调用架构ABI不匹配如果你遇到的是后几种不要死死盯住哈希算法先回头看看是不是有隐藏的入参没被发现。很多时候签名算法本身很简单真正复杂的是参数体系。5.2 桌面排查流程当“差一位”这样的诡异问题出现时我建议按下面的流程走别瞎猜把两段输出做hex dump肉眼对比第一个不同的字节在哪。如果前面一小段相同、后面全不同说明算法主体一致可能是拼接末尾多了内容。检查输入字符串的编码。同一个字符串UTF-8和UTF-16算出来的哈希是完全不同的。尤其注意中文、特殊符号和数字字符串的编码方式。检查大小写规则。很多SDK对hex输出统一转小写或者统一转大写但中间某一段可能保留了大写导致整体对不上。用二分法缩小差异范围。把输入字符串分成多段分别做哈希看哪一段改变后和真实输出对得上。这个办法能帮你快速定位是前半段的问题还是后半段的问题。有次我算一个HMAC-SHA256结果总是差最后四位。后来发现key不是我以为的固定字符串而是“固定字符串时间戳”时间戳精度是毫秒我一直用秒级所以前面大部分对得上最后因为key尾部不同导致整体不同。所以遇到“大部分对不对上”的情况优先怀疑参与计算的key或盐里有动态分量。5.3 几条避坑心得这里分享几条我用真金白银换来的经验不要第一步就逆so。很多关键参数在Java层或者JS层就已经拼好了so里只是一个标准哈希调用。先黑盒分析性价比最高。不要把未经验证的算法写进项目。我在一次自动化流程里为了图快把只验证过一条样本的算法直接上线结果第二天目标SDK升级算法全变线上直接崩了。给自己设时间盒。纯算很容易陷入死胡同我一般设定两小时没突破就切到Hook或unidbg辅助一下而不是死磕到底。每次还原步骤都要文档化。SDK升级后你可以对照旧文档快速定位变化的字段而不是从头再来。保持怀疑精神。即使“99%匹配”也有可能是服务端校验比较宽松、接受了一部分旧算法。真正要确保稳定还是要靠新样本预测和服务端行为验证都通过。6. 写在最后的一点体会纯算逆向的门槛其实不算高一台电脑加抓包工具就够了但它非常考验“系统化拆解问题”的能力。很多朋友一接触逆向就想找“万能脚本”可某盾这类SDK恰恰是反自动化设计的只有彻底理解数据流、参数体系和算法逻辑才能在环境变化时快速迁移。这条路线练出来的分析能力比单纯会Hook一个函数值钱得多。如果你正准备走这条路我的建议是从小目标开始先找一个自己写的或者授权的目标尝试还原其中一个签名参数。这个过程会让你体会到“输入输出对反推算法”的乐趣也会让你对加密、摘要、编码之间的组合关系有更深的理解。别把时间花在和反调试无限对抗上纯粹地分析数据反而是一条更踏实、更持久的路。最后再分享一个小技巧做黑盒纯算最值钱的往往不是最后那几行算法代码而是你一路积累的样本库。每次抓包、每次改动、每次猜测都记录在案维护好这些历史数据。SDK一旦升级很多字段的演化规律还能让你快速锁定新算法。这个习惯能让你在逆向这条路上走得更远。