网页逆向实战:MD5、SHA、HMAC加密算法识别与破解指南
1. 项目概述从加密算法到网页逆向的实战桥梁在网页逆向分析这个领域里你经常会遇到一堆看似杂乱无章的字符串它们可能是登录时的密码参数也可能是请求数据包里的一个神秘签名。很多时候这些字符串就是由MD5、SHA、HMAC这类加密算法处理后的结果。不理解它们逆向工作就像在黑暗中摸索寸步难行。今天我们不谈高深的理论就从一名逆向工程师的实战视角拆解这些最常见的加密算法看看它们是如何在网页中“兴风作浪”而我们又该如何“见招拆招”。简单来说MD5、SHA、HMAC都属于密码学中的哈希函数或消息认证码范畴。它们不是用来“加密”后还能“解密”的那是AES、RSA的活儿而是将任意长度的输入通过一个不可逆的数学变换映射成一个固定长度的、看起来像乱码的输出我们称之为“摘要”或“哈希值”。在网页中它们常被用于密码存储、数据完整性校验、生成API签名或请求令牌。逆向分析的核心任务之一就是识别出前端JavaScript代码中使用了哪种算法、密钥是什么、对哪些数据进行了运算从而在本地复现这个计算过程模拟出合法的请求。2. 核心加密算法原理与逆向特征拆解要逆向先得正向理解。我们得知道这些算法在“正常”情况下是怎么工作的才能在被混淆、压缩的代码中找到它们的蛛丝马迹。2.1 MD5算法曾经的王者与如今的“指纹”MD5Message-Digest Algorithm 5生成一个128位16字节的哈希值通常表示为32个十六进制字符。它的计算过程包括填充、分块、循环压缩等。虽然因其抗碰撞性已被攻破即能找到两个不同的输入产生相同的MD5值不再推荐用于密码等安全场景但在网页逆向中它依然无处不在。逆向识别特征常量特征MD5算法内部使用4个固定的32位幻数A0x67452301 B0xEFCDAB89 C0x98BADCFE D0x10325476。在JavaScript代码中如果看到这四个常量的定义极大概率是MD5。函数名特征常见的JavaScript MD5库会暴露函数如md5()、hex_md5()、b64_md5()等。输出长度32位十六进制字符串是最明显的标志。实战注意事项MD5是单向哈希理论上不能解密。网上所谓的“MD5解密”网站实际上是庞大的“彩虹表”查询即预先计算好海量明文和其MD5的对应关系通过查询哈希值反推明文。对于加了“盐”salt的MD5这种查询就失效了。在逆向时我们的目标不是“解密”而是找到生成这个MD5值的原始数据和算法代码。2.2 SHA家族算法更安全的哈希标准SHASecure Hash Algorithm系列比MD5更安全常见的有SHA-1160位输出、SHA-256256位输出、SHA-512512位输出等。数字越长安全性一般越高计算也稍慢。逆向识别特征常量特征SHA-256使用8个初始化哈希值如0x6a09e667SHA-512使用8个64位初始化哈希值。这些固定的魔数是代码中的路标。函数名如sha1(),sha256(),sha512(),CryptoJS.SHA256()如果使用CryptoJS库。输出长度SHA-1是40位十六进制SHA-256是64位SHA-512是128位。这是快速区分的利器。实操心得在逆向中看到长度40位的哈希先怀疑SHA-1看到64位的优先考虑SHA-256。很多网站的API签名已经从MD5升级到了SHA-256因为它更抗碰撞。如果代码中引用了crypto-js或forge这类库搜索SHA256关键词往往能快速定位加密点。2.3 HMAC算法带密钥的哈希验证HMACHash-based Message Authentication Code是关键所在。它不是独立的算法而是利用MD5、SHA-1、SHA-256等哈希函数结合一个密钥来生成消息认证码。它可以确保数据在传输过程中未被篡改并且发送方拥有正确的密钥。公式可以简化为HMAC Hash( (Key XOR opad) Hash( (Key XOR ipad) Message ) )。你不用记这个但要知道核心是密钥Key。逆向识别特征与核心挑战函数调用常见形式如CryptoJS.HmacSHA256(message, key)、hmac-sha256库的调用。寻找密钥这是逆向HMAC最困难也最关键的一步。密钥可能被硬编码在JavaScript文件里经过混淆也可能由前端通过其他接口动态获取甚至是由用户密码等派生而来。你需要仔细追踪生成HMAC的函数的第二个参数来源。流程固定HMAC一定是先有原始数据消息和密钥然后调用特定哈希函数生成结果。在代码中寻找这种“二元输入”的哈希计算点。一个关键技巧在Chrome DevTools的Sources面板中对疑似加密函数如encrypt、sign、getToken设置断点然后触发网络请求。当断点命中时观察调用栈Call Stack和该函数的参数Scope密钥和待加密的原始数据很可能就在眼前。这是动态调试优于静态分析的地方。3. 网页逆向中的加密算法实战定位与分析理论懂了我们进入实战环节。假设我们要对一个登录请求进行逆向发现其密码或一个sign参数被加密了。3.1 第一步网络抓包与初步判断打开浏览器的开发者工具F12切换到Network网络选项卡勾选Preserve log保留日志。进行登录操作观察发出的POST请求。查看Form Data或Payload寻找可疑参数。例如password可能是一串32位十六进制数MD5signature可能是一串64位十六进制数SHA-256/HMAC-SHA256。特别注意像_t时间戳、nonce随机数这类参数它们经常是生成签名的一部分。3.2 第二步全局搜索与静态分析在Sources面板中对整个网页的JavaScript代码进行搜索CtrlShiftF。搜索关键词从网络热词和算法特征入手。例如md5、sha256、hmac、encrypt、sign、CryptoJS一个非常流行的前端加密库、hex、digest。也可以直接搜索你抓包到的那个加密字符串看它是否作为常量出现在代码中。分析代码结构找到相关函数后不要只看那几行。向上追溯看这个函数的参数从哪里来密钥是如何定义的。向下查看看生成的哈希值被用在了哪里通常是赋值给某个请求参数。3.3 第三步动态调试与参数追踪静态分析遇到混淆代码时动态调试是唯一出路。XHR/Fetch断点在Network面板找到那个加密的请求右键点击选择“Break on” - “XHR/fetch Breakpoints”。这样当JavaScript发起这个特定请求时会自动暂停。事件监听器断点在Sources面板的右侧展开“Event Listener Breakpoints”勾选“Script”下的“Script First Statement”或者“Mouse”下的“click”事件如果加密由点击触发。这有助于从用户交互点开始跟踪。手动断点在静态分析找到的疑似加密函数入口处打上断点点击行号。 当代码暂停后利用Step OverF10、Step IntoF11逐行执行同时在Scope面板和Console面板观察变量值的变化。重点关注传递给加密函数的明文和密钥。3.4 第四步本地复现与验证一旦找到了加密算法、密钥和明文组合方式就需要在本地比如用Python的hashlib、hmac库或Node.js环境复现这个计算过程。import hashlib import hmac # 示例复现一个可能的HMAC-SHA256签名 def generate_sign(api_key, secret, timestamp, request_body): message f{api_key}{timestamp}{request_body} sign hmac.new(secret.encode(utf-8), message.encode(utf-8), hashlib.sha256).hexdigest() return sign # 使用抓包到的参数进行测试 my_sign generate_sign(your_api_key, your_secret_key, 1648886400000, {page:1}) print(my_sign) # 对比抓包到的sign值如果本地计算的结果与抓包到的值一致恭喜你逆向成功。你可以用这个函数生成任意请求所需的签名了。4. 复杂场景与组合加密策略的应对网页开发者为了增加逆向难度不会只使用单一的MD5。他们会组合使用多种技术这正是开头搜索片段中提到的“几种加密方式配合次序”的实战体现。4.1 常见组合模式解析哈希加盐Saltmd5(password salt)。盐是一个随机字符串与密码拼接后再哈希。这彻底杜绝了彩虹表攻击。逆向时你需要找到这个salt它可能藏在另一个接口的响应里或者是一个前端固定的值。多次哈希与迭代sha256(sha256(password))或者进行上千次的迭代哈希以对抗暴力破解。这在密码存储中常见。逆向登录时你需要模拟同样的迭代次数。HMAC与动态参数sign hmac-sha256(排序后的所有参数 密钥)。这是API签名的经典模式。难点在于参数排序规则通常按字典序和拼接格式如key1value1key2value2。非对称加密管理对称密钥正如搜索片段所言这是更高级的模式。例如前端用RSA公钥加密一个随机生成的AES密钥然后用这个AES密钥加密请求体。服务器用私钥解密出AES密钥再解密请求。逆向这种模式关键在于找到RSA公钥和AES密钥的生成逻辑。4.2 逆向组合加密的思维导图面对复杂加密需要一个清晰的排查思路确定最终输出抓包确定需要破解的那个加密参数如encryptedData,sign。识别算法家族通过输出长度、特征初步判断是MD5、SHA还是HMAC。定位加密函数通过搜索关键词、断点追踪找到生成该参数的JavaScript函数入口。逆向输入流从加密函数出发反向追踪每一个参数的来源。明文数据从哪里来表单输入、页面元素、其他接口返回值密钥从哪里来硬编码、接口获取、本地计算派生盐Salt或初始向量IV从哪里来是否有额外的处理步骤Base64编码、十六进制转换、字符串排序、截取部分结果绘制加密流程图在纸上或脑图中画出数据流动和变换的完整路径。分步复现验证按照流程图从第一步开始在本地逐步实现每一步都与调试时看到的中途结果对比确保无误。5. 常用工具链与调试技巧实录工欲善其事必先利其器。除了浏览器自带的DevTools还有一些工具能极大提升逆向效率。5.1 浏览器插件ReRes可以拦截浏览器请求将线上JavaScript文件替换为本地的修改版方便你插入调试代码或直接修改函数逻辑进行测试。EditThisCookie直接查看和编辑Cookie有时签名或令牌会放在Cookie里。JavaScript Source Tracker或类似概念有些插件能帮你可视化JavaScript文件的依赖关系和函数调用链对理解大型项目结构有帮助。5.2 本地模拟环境Node.js对于复杂的、依赖Node环境或npm包的前端代码直接在Node里运行调试是最准确的。你可以把关键的加密函数片段提取出来在Node中运行并打印中间变量。Python ExecJS/PyExecJS如果你更熟悉Python可以用这个库来执行JavaScript代码片段。这样你的爬虫或自动化脚本可以直接调用前端的加密逻辑无需完全用Python重写。import execjs # 假设你提取出了关键的js加密函数保存为 sign.js with open(sign.js, r, encodingutf-8) as f: js_code f.read() ctx execjs.compile(js_code) result ctx.call(generateSign, your_data, your_key) print(result)5.3 高级调试技巧Hook函数在控制台直接重写关键函数让其输出调用参数和结果。这是“暴力”但有效的方法。// 假设原加密函数是 window.encrypt var old_encrypt window.encrypt; window.encrypt function(data, key) { console.log(encrypt被调用数据:, data, 密钥:, key); var result old_encrypt(data, key); console.log(加密结果:, result); return result; };内存断点Watchpoint如果你知道密钥存储在某个变量或对象属性中可以在DevTools的Memory面板或通过代码对其设置“访问/修改”断点当代码读取或修改这个密钥时就会暂停。格式化混淆代码面对压缩成一行的代码点击DevTools源代码面板左下角的{}美化按钮是第一步。对于更复杂的混淆变量名替换、控制流平坦化可能需要使用专门的去混淆工具或耐心分析。6. 常见问题排查与避坑指南在实际操作中你肯定会遇到各种问题。这里记录一些典型的坑和解决方案。6.1 问题本地复现的签名和服务器的总是对不上排查思路编码问题最常见确保你本地和前端JavaScript处理字符串的编码一致。前端通常使用UTF-8。在Python中在哈希前明确使用.encode(utf-8)。特别注意中文等非ASCII字符。参数顺序或格式检查拼接参数的顺序、中间是否有连接符如、|、是否对键值对进行了URL编码。服务器端的拼接逻辑必须和你本地完全一致。密钥或盐错误再次确认你找到的密钥是否正确、完整。是否在传输前经过了Base64解码或其他变换动态获取的密钥是否已过期算法或迭代次数错误确认哈希算法是否准确是SHA-256还是SHA-256-224。确认哈希迭代了 exactly 多少次。时间戳或随机数的同步如果签名包含时间戳检查本地生成的时间戳和服务器时间是否在允许的误差范围内。随机数nonce是否每次请求都重新生成。6.2 问题断点打上了但代码不暂停可能原因与解决代码被动态加载或eval执行代码可能通过eval()、setTimeout或动态创建script标签加载导致源映射丢失。尝试在“Script First Statement”事件上设置断点或者搜索代码片段在Network中对应的JS文件直接在该文件上打点。断点被反调试技巧绕过有些网站会检测开发者工具并跳入无限debugger循环或使用异步函数扰乱调试。可以尝试使用“停用断点”功能先跳过无限循环或者使用插件禁用反调试。更高级的方法是修改JavaScript文件直接注释掉反调试代码。断点位置不对你可能在压缩后的代码行上打了断点但实际执行的是另一份源文件。确保在正确的文件通常是经过美化后的主文件上操作。6.3 问题密钥被严重混淆难以追踪应对策略动态拦截如果密钥是在运行时通过Ajax请求获取的在Network面板找到那个请求直接查看响应体。这比从混淆的JS代码里找容易得多。内存搜索如果密钥最终会被加载到内存中的一个变量里可以在代码运行到某个阶段后在Console中使用Object.keys(window)查看全局对象或者对已知的容器对象进行递归搜索寻找符合密钥特征长度、字符集的字符串。日志大法如果能在关键位置插入代码通过ReRes替换文件就在所有可能的密钥赋值语句前后加入console.log进行“地毯式”输出。逆向分析是一场与开发者斗智斗勇的过程。MD5、SHA、HMAC这些算法是工具理解它们是基础。真正的功夫在于耐心、细致的代码追踪和逻辑推理。每一次成功的逆向都是对网页应用安全机制的一次深刻理解。记住我们的目的不是破坏而是学习和实现合法的自动化交互。随着经验的积累你会逐渐形成自己的直觉和工具箱面对新的加密挑战时也能更加从容。