资讯详情

JS逆向实战:变种MD5与OB混淆破解全流程解析

📅 2026/10/22 19:22:28 | 华诺云谱 👁 阅读
JS逆向实战:变种MD5与OB混淆破解全流程解析
老规矩先看题。这道“猿人学内部练习平台——第3题”的核心考察点写得很直白变种md5加密和OB混淆练习。这两个词组合在一起基本就是在告诉你——这一题不是让你直接抄参数、改个cookie就能过的而是需要你把前端加密逻辑完整拆开看懂它到底怎么算的再把这套算法拿到自己代码里重新实现一遍。先说下这题适合谁。如果你已经会用抓包工具、会看网络请求里的参数但对“前端JS加密怎么定位、混淆代码怎么跟、加密逻辑怎么还原”还处于一知半解的状态这题就是为这个阶段准备的。它不算特别难但把JS逆向里最常碰到的两件事——加密函数定位和混淆代码分析——都覆盖了。把这道题的思路捋顺了后面遇到复杂的webpack、sojson、vmp类题目你至少知道从哪下刀。我自己刷这题的时候前前后后踩了不少坑尤其是被OB混淆的大数组移位函数恶心过一阵。这篇文章就把我当时分析的完整过程、踩过的坑、总结的套路都写出来给正在死磕这题的朋友一个参考。1. 这是一道什么样的题先看懂考察点再动手1.1 从标题拆解题目的核心考点先拆标题。“变种md5加密”前半段说明加密算法的主体是MD5后半段“变种”两个字才是重点——它意味着你不能直接拿hashlib.md5()去套拼接顺序、大小写、加盐方式、多次摘要等细节一定被改过。“OB混淆练习”则指的是JavaScript代码经过Obfuscator类型的混淆处理典型的特征是代码里有一个巨大的字符串数组、自执行函数、移位函数正常的函数名、变量名全部被替换成无意义的乱码有些版本还会做控制流扁平化把简单的if/else变成一堆switch/case堆叠。这个组合在真实业务里很常见。很多网站不会用标准MD5直接出参而是会做一层自定义包装比如“把时间戳倒过来拼上固定值再算MD5、取结果第8到24位转大写”。这种包装本身不复杂但配合混淆之后你光看代码根本看不出业务逻辑藏在哪里必须先绕过混淆这一层。所以这题本质上是两道题的缝合体先费劲定位加密函数再去还原变种MD5的算法。1.2 我的解题路线规划我当时拿到这题先没有急着看JS代码而是按平时做逆向的固定流程走打开开发者工具切到Network面板清空日志触发一次页面请求先看提交了哪些参数。在服务器返回的数据里找出哪些字段的值是“我控制不了的”——比如一个看起来像签名、token、密文的东西。对这个值做全局搜索看它出现在哪些JS文件里找到加密函数的位置。在加密函数附近下断点单步执行观察入参和返回值把整个加密流程拆出来。最后用Python或者Node把算法复现一遍对比结果。这套流程对绝大多数前端加密都适用区别只是每一步的难易程度。本题的难点主要集中在第4步——OB混淆的代码跟起来非常痛苦。后面我重点讲这一块我用的几个技巧。2. 定位加密入口断点与分析思路2.1 通过接口数据先锁定加密参数我实际打开页面后第一次提交请求发现提交的参数里面有这么几个page页码m: 一个32位的字符串大小写混合t: 一个10位的数字像时间戳m这个参数一看就是MD5的特征——32位十六进制基本跑不掉。t则很可能是时间戳也可能是参与加密的另一个原始字段。这时候我们需要确认的是m到底是用哪些原始数据算出来的以及它和t是什么关系。直接在Network面板里点开这个请求看它的“发起者”或“调用堆栈”浏览器有时候能直接帮你指到发起请求的JS代码位置。这一题里请求是由XMLHttpRequest发起的点击进去定位到一个被混淆过的匿名函数。到这里我们就知道加密逻辑大概率在某个混淆后的JS文件里。注意如果发起者面板看不到有用信息别死磕。直接切到Sources面板全文件搜索md5、hex_md5、MD5这些特征字符串几乎能在几秒钟内找到加密函数所在地。这是最快的定位方式。2.2 事件监听与全局搜索的组合定位法这个题如果用事件监听来定位也是可行的——在页面里找到触发请求的按钮或分页控件通过Chrome的“Event Listener Breakpoints”在click事件上打断点。但实际试下来由于代码经过混淆事件绑定函数往往也在那个超级大的匿名函数里打断点后单步进去会直接面对一堆变量名是_0xabc1、_0xdef2的代码很容易把人劝退。所以我个人更推荐“搜索特征字符串 断点回调”的组合方式。具体做法是切换到Sources面板按CtrlShiftF打开全局搜索。输入md5或t的赋值语句特征比如t 这种方式容易误搜建议直接搜md5。一般在搜索结果里会看到一个_0x...function(){...}这样的加密函数在它return语句那一行下断点。重新触发请求代码会自动停在这个断点上。然后看右侧Call Stack从下往上找到业务调用层逐步查看参数是怎么传进来的。这样绕过直接跟混淆代码的困难从“加密函数本身”作为切入口效率会高很多。2.3 分清混淆代码与业务代码OB混淆后整个JS文件看起来就像一个巨大的乱码块但不要被吓到。混淆只是换了个样子程序逻辑还在关键在于区分“基础设施”和“业务逻辑”。OB混淆通常包含三块内容一个大的字符串数组里面存着加密后的字符串有的用base64编码有的直接是Unicode转义这是整个脚本的“字符串表”。一个移位/解码函数用来把字符串表里的内容解密成真正能读的字符。实际业务函数它们通过调用移位函数来获取字符串而不是直接把字符串写在代码里。所以当你看到某个函数里全是_0x1234这样的调用时不要试图逐行读懂重点看两个地方入参是什么、返回值是什么。把这两个搞清楚业务逻辑基本就出来了。这也是这套题的核心训练目标学会在“看不懂代码”的表象下用断点和日志把关键信息抽出来。提示有些混淆版本会把整个业务函数塞进一个超大的try/catch里或者在函数尾部套一层while(1){switch(_0x...){...}}的控制流扁平化结构。看到这种结构不要慌本质上就是一个条件分支树只是把所有if/else换成了switch-case。后面第四章我会专门讲怎么处理这种结构。3. 变种MD5破解从函数还原到代码实现3.1 识别MD5的“变种”在变什么搞懂加密函数长什么样之后接下来最关键的一步把标准MD5和变种MD5的差异找出来。在我分析的这个版本里加密流程大概是这样的// 伪代码用于说明结构并非原混淆代码 function get_m(t, val) { var s val t; // 注意拼接顺序是 val 在前t 在后 var b base64_encode(s); // 先做了一次base64编码 return hex_md5(b).toUpperCase(); // 再做MD5结果转大写 }这里就有三个变种点拼接顺序不是常见的时间戳固定串而是固定串时间戳。原始字符串不是直接做MD5而是先做了一次Base64编码。输出结果统一转为大写。很多新手拿到题直接拿默认拼接顺序去算MD5算出来怎么都对不上原因就在这种细节上。所以还原MD5时顺序和大写这两个点一定要优先检查。3.2 参数拼接顺序与大小写规则还原为了确定到底是不是这样拼接的我当时用了两个方法交叉验证。第一个方法在加密函数的return语句上打断点然后手动往控制台里塞一些测试值看看在给定入参的情况下返回值是什么样的。给定一组已知的t和val再用Python侧尝试不同拼接方式算出MD5对比浏览器里的返回值能非常快地确认规则。第二个方法直接在控制台里修改Date.now()的返回或者骗过时间戳参数用固定的时间戳去测试这样Python侧同样用固定时间戳去计算排除时间误差。这一步很重要——如果不固定时间戳前后两次请求的t不同你没法验证。拼出来之后还发现一个容易忽略的细节t本身在参与拼串之前被一个函数处理过比如做了个“转成字符串然后倒序”的操作。这个在浏览器控制台里一眼就能看出来但如果不打断点光看混淆代码是绝对发现不了的。3.3 Python侧加密函数完整实现确认规则之后用Python实现就很直接了import time import hashlib import base64 FIXED_KEY 某种固定值从JS中提取 def get_m(t): raw FIXED_KEY str(t) b64 base64.b64encode(raw.encode(utf-8)).decode(utf-8) m hashlib.md5(b64.encode(utf-8)).hexdigest().upper() return m t str(int(time.time())) m get_m(t) print(t, m)这里有一个要点base64编码的对象是FIXED_KEY str(t)而不是str(t) FIXED_KEY顺序反了结果就完全不对。这种“细节差异”正是变种MD5考察的核心。我记得当时卡了快半天最后发现是拼接顺序的问题气得直拍桌子。注意不同部署版本的题的变种规则不一定相同。有的可能是“先MD5再Base64”有的可能是“MD5后取中间16位”有的可能是“两次MD5”。不要把我这里的具体规则当成标准答案重点是掌握“通过断点和对比实验还原算法”的方法。3.4 为什么很多人算出的MD5总是对不上这个问题我见得太多了几乎每个做这题的人都会问。归纳下来最大概率是以下三个原因拼接顺序错误先入为主认为一定是“时间戳”开头的格式。忽略中间编码步骤题目在MD5之前可能套了base64或者去空格、大小写转换之类的预处理。时间戳类型没对齐JS里的时间戳可能是数字类型Python里你却用了字符串类型拼出来的原始串不一样。另外还有一个坑如果JS代码里取了Date.now()时间戳是自增的毫秒级数字13位但很多接口要求传秒级10位这中间可能有一个除以1000并取整的操作。这个操作也要在Python里复现不然每次加密结果都会差在最后几位数字上。4. OB混淆逆向大数组与控制流摊平4.1 Obfuscator混淆的典型特征这一章专门聊聊OB混淆。它和你平时见到的简单压缩混淆完全不同我拿到的JS文件里开篇就是一个几十行的大数组var _0x4b8a [hello, world, 1337, ...];然后紧跟一个解包函数(function(_0x4b8a, _0x1f2d) { var _0x3e9c function(_0x2b8f) { while (--_0x2b8f) { _0x4b8a[push](_0x4b8a[shift]()); } }; _0x3e9c(_0x1f2d); }(_0x4b8a, 0x1f2d));这个叫数组移位它的作用是每次运行时把数组里的元素重新排列然后通过一个解码函数去访问数组。你看到的字符串都是通过_0x3e9c[_0x...]这种方式取出来的直接用搜索工具搜一个中文字符串在代码里根本搜不到。还有一个更恶心的特征是控制流扁平化比如原本简单的一个函数function getToken() { if (isValid) return createToken(); else return null; }会被改写成function _0xabc() { while (true) { switch (_0x1f2d[_0x3e9c]) { case 0: var result; case 1: if (isValid) { ... } case 2: ... } } }这种代码人类看着头大但实际执行逻辑和原来的if/else是等价的只是把分支拆散了。4.2 手工还原与批量还原的取舍我处理OB混淆的时候试过两条路线。路线一纯手工跟栈在断点处一步一步F11观察变量变化。优点是信息是最准确的因为所有解密函数在运行时都已经执行过了你看到的字符串是明文缺点是被大数组一搞每走几步就要跳到解包函数里多跳几次脑子就乱了。适合加密逻辑比较短、分支比较少的情况。路线二先整体脱壳再静态分析把混淆后的大数组和解包函数在浏览器控制台里先执行一遍然后把代码里的变量引用批量替换成明文。这个操作听起来很酷但实际做起来需要写脚本来处理AST语法树。如果你对AST不熟更建议用已经做好的反混淆工具把混淆代码丢进去让工具自动把数组还原成明文字符串。我当时试过之后代码可读性提升了一大截至少能看清主干逻辑了。我个人的经验是不一定非要追求完全还原。很多时候你只需要找出加密函数是怎么写的那就在浏览器里把混淆代码执行完直接调用那个加密函数观察它的行为和输出就够了。比纯逆向更省事。4.3 用“黑盒调用”绕开大部分混淆这题有个很务实的做法既然混淆后的代码本质还是在浏览器JavaScript引擎里跑那我完全可以不把它完全还原成干净代码而是把这个加密函数当做一个黑盒通过“调它”来“理解它”。我当时的具体操作是在控制台里把混淆JS文件里的所有代码复制粘贴执行一遍这一步让大数组和解包函数都初始化好。找到加密函数赋值给的变量直接调用它传入测试参数。用console.log在函数内部加日志打印每一步的中间结果。这样做的好处是它让你在“不读代码”的情况下直接观察输入输出。缺点是需要你自己手动把混淆代码里的业务函数找出来有时要通过打断点的方式在调用栈里找到它。但比起在一堆_0x4b8a[0x13]里猜业务逻辑这个思路已经轻松太多了。4.4 实用小技巧控制台直接运行混淆代码段这里分享一个我后来经常用的小技巧。如果你定位到的加密函数在一个(function() { ... })()的匿名函数内部但你不想把整个文件都执行一遍可以直接在Sources面板里找到那一行的代码右键选择“Evaluate in console”把你关心的那一段代码粘贴到控制台执行函数定义就会挂到全局作用域。再配合window.变量名 函数名把它显式挂载出来就可以随时随地调用了。这个方法对OB混淆尤其有效因为它能绕过整个大数组的初始化问题直接获得一个可运行环境的快照。注意如果那段代码里依赖了某些在页面初始化时才生成的状态脱离页面环境执行可能会报错。遇到报错不要慌回到Sources面板在它被调用之前下断点再在断点处打开控制台执行通常就顺了。5. 完整请求流程把分析结果落成可用代码5.1 会话保持与请求参数构造分析完加密算法剩下的就是把所有东西拼成一个能跑通的爬虫脚本。这题本身是一个练习环境和真实站点一样请求过程中需要注意几个点携带Cookie/Session有的版本第一次访问页面会种一个Cookie后续请求需要带上才能返回正确数据。我建议先用requests.Session()统一管理。请求头伪造有的反爬会校验User-Agent、Referer和X-Requested-With我直接把浏览器里抓到的那套请求头原样抄过来基本能过。参数过期问题t是动态时间戳m必须基于同一个t实时计算不能写死。我写脚本的时候把这两个参数放在同一个函数里生成避免不匹配。5.2 完整请求示例与说明下面给一个简化版的参考代码基于我上面假设的变种规则注意实际题目的拼接方式要以你自己分析为准import time import hashlib import base64 import requests FIXED_KEY xxxxx # 从JS里提取的固定字符串 URL https://练习环境的接口地址/ def gen_m(t): raw FIXED_KEY t b64 base64.b64encode(raw.encode()).decode() return hashlib.md5(b64.encode()).hexdigest().upper() def main(): session requests.Session() session.headers.update({ User-Agent: Mozilla/5.0 ..., Referer: 练习环境页面地址, }) # 先访问一次首页拿到Cookie session.get(练习环境首页地址) for page in range(1, 6): t str(int(time.time())) m gen_m(t) resp session.get(URL, params{page: page, t: t, m: m}) data resp.json() # 处理data里的业务数据 print(page, data.get(msg)) time.sleep(1) # 控制频率别打太快 if __name__ __main__: main()这段代码有几个细节值得说明我用time.sleep(1)控制请求频率不光是礼貌问题也是避免触发风控。gen_m和t的生成必须同步如果你先生成t再去调gen_m中间间隔过长导致t变化就会造成签名不一致。如果你发现返回数据里有验证码跳转之类的内容大概率是Cookie没处理好检查一下Session里有没有拿到关键的Cookie。5.3 验证加密逻辑是否正确的快捷方法跑通之前先做一步静态验证在Python里用固定的t算一遍m然后在浏览器控制台用同样的t重新调一次加密函数对比两个m是否一致。如果不一致说明你Python侧的算法和JS侧还有差异就别急着发请求先把差异找出来。这个验证方法比直接跑脚本高效得多因为请求失败的原因太多了可能是加密问题、可能是Cookie问题、也可能是IP封禁问题。先隔离变量从最简单的算签名开始能帮你省下大量排查时间。6. 常见问题与排查技巧实录6.1 加密结果对不上的排查步骤这是这题出现频率最高的问题。我整理了一个排查顺序照着做基本能定位排查顺序检查点操作方式1拼接顺序换一下固定值和时间戳的位置对比结果2中间编码确认是否先做了base64或URL编码再MD53大小写统一转大写或小写看哪个能match4时间戳格式确认是10位还是13位是否是字符串拼入5额外处理检查是否把非字母数字字符过滤或替换掉了我遇到过最刁钻的一次是JS里拼接前先把固定值里的替换成了空字符串这种细节不打断点根本猜不到。6.2 OB混淆分析卡住的备选方案如果你在分析OB混淆代码时长时间卡住觉得脑子都要炸了我的建议是把代码贴到格式化工具里先整体看一下结构找到大数组和解包函数的位置。只在可疑的业务函数上打断点别从头到尾手跟一遍。如果实在跟不下去就退而求其次把整个加密过程当黑盒处理只验证输入输出。实在不行就换调试工具有的环境用火狐的调试器比Chrome顺手有的混淆代码在火狐里的作用域表现更友好。6.3 关于“刷题”这件事的一点个人体会最后说点题外话。这种练习平台的题本质上是给你一个安全、合法的环境去练逆向基本功它有别于真实站点的对抗不会搞什么极验滑块、设备指纹重点就是考察你“能不能读懂一段被加密和混淆的代码”。我个人建议刷这种题不要只追求“跑通就完事”。每做完一道题你都应该回头梳理一下自己是怎么定位加密函数的、用了哪些调试技巧、哪些地方浪费时间了。把这些经验沉淀下来比多刷十道题都有用。而我实际体验下来像这种“变种MD5 OB混淆”的组合在真实的业务里就是最常见的配置——很多公司不会用多高深的算法就是标准算法套一个自己改过的壳再加一层混淆延长你的分析时间。把这套基本功练扎实了后面遇到再复杂的混淆也只是“耗时更长”而不是“无从下手”的问题。这道题我做完之后最大的收获是养成了“先固定时间戳再对比签名最后才发请求”的调试习惯。这个习惯在后面做其他加密题的时候帮我绕开了无数个“看似是签名错误、实则是时间不同步”的坑。也希望这篇文章能帮你少走一些弯路。
📝

华诺云谱内容团队

资深建站顾问 · 行业研究员

10年+企业数字化服务经验,专注智能建站、SEO优化与品牌营销,持续输出建站技巧、行业洞察与营销干货,已帮助5000+企业实现数字化增长。

你可能需要的服务

订阅华诺云谱资讯周报

每周一封,精选建站技巧、SEO与营销干货,直达邮箱。已有 8,000+ 企业主订阅,助你少走弯路。

↑