a_bogus纯算(V1.0.1.19-fix.01)逆向全流程:从日志插桩到算法复现

张开发
2026/4/21 13:30:09 15 分钟阅读

分享文章

a_bogus纯算(V1.0.1.19-fix.01)逆向全流程:从日志插桩到算法复现
1. 逆向分析a_bogus算法的完整技术流程最近在研究某平台的接口加密时遇到了一个叫a_bogus的参数。这个参数看起来像是一串随机字符但实际上是通过复杂算法生成的。经过一周的逆向分析我终于搞清楚了它的生成逻辑。下面就把这个完整的逆向过程分享给大家希望能帮助到有类似需求的开发者。这个a_bogus参数在V1.0.1.19-fix.01版本中的生成过程相当复杂涉及到多层加密和转换。整个过程可以分为几个关键步骤环境准备、日志插桩、数据收集、日志分析和算法复现。我会按照这个顺序详细讲解每个环节的具体操作。2. 环境准备与日志插桩2.1 开发环境搭建首先需要准备好开发环境我使用的是以下工具组合带有DevTools的Edge浏览器VS Code用于查看和分析日志PyCharm用于编写处理脚本在实际操作中我发现直接使用浏览器控制台输出的日志量太大很容易导致浏览器卡死。于是写了一个日志收集和下载的工具函数这个函数可以将日志分块保存到本地。// 将对象转为字符串便于存储 get_obj_str function(one_obj) { try { return JSON.stringify(one_obj, function(k, v) { if (v window) return window; else if (typeof v function) return function ${v.name}; else if (v v[Symbol.toStringTag]) return Symbol ${v[Symbol.toStringTag]}; else return v; }) } catch (e) {}; } // 添加日志并自动分块下载 add_json_str function(param_name, param) { var add_log function(log_str) { var save_log function(textContent, file_name) { const blob new Blob([textContent], { type: text/plain }); const url URL.createObjectURL(blob); const a document.createElement(a); a.style.display none; a.href url; a.download file_name; document.body.appendChild(a); a.click(); setTimeout(() { document.body.removeChild(a); window.URL.revokeObjectURL(url); }, 0); } window.my_code log_str; if (window.my_code.length 1024 * 1024 * 5) { // 每5MB分块 save_log(window.my_code, logs${window.my_offset}.txt) window.my_offset 1; window.my_code ; } } add_log(param_name get_obj_str(param) \n\n); };2.2 定位关键函数通过分析网络请求我发现a_bogus参数是在bdms模块中生成的。具体定位方法是打开开发者工具找到包含a_bogus的请求在Fetch/XHR筛选器中找到对应的数据包查看发起程序的调用栈发现a_bogus在bdms模块中生成多次实验后发现bdms的入口是5704堆栈出口是index堆栈。a_bogus就是在入口和出口之间生成的。虽然入口和出口有时会变化但分析方法是一样的。2.3 关键函数插桩找到入口后我们需要在关键函数内部插入日志点。a_bogus的生成主要在d函数内部所以重点在这里插桩。插桩的技巧是首先在运算操作符加赋值的地方插桩观察d函数发现使用了v和s两个内存栈设置条件断点记录关键变量的值// 在d函数内部设置的条件断点示例 if (t 2) if (0 t) { var r o[a]; p - r; var e v.slice(p 1, p r 1) , n v[p--] , d v[p--]; if (function ! typeof n) return f 3, void (l new TypeError(typeof n is not a function)); var y V.get(n); if (y) h.push([o, i, u, s, c, a, f, l]), g(y[0], d, e, y[1]); else { var m n.apply(d, e); v[p] m // 插桩记录apply调用结果 add_json_str(v_apply,v),add_json_str(s_apply,s),add_json_str(${get_obj_str(n)}.apply(${get_obj_str(d)}, ${get_obj_str(e)}),m),false } }3. 日志分析与算法推导3.1 日志预处理收集到的日志文件通常很大我这次收集的日志超过了1GB。处理这种大文件VS Code表现很不错。为了减少日志体积可以进行以下优化替换重复的长字符串为短变量名删除不必要的中间数据合并多个日志文件我写了一个Python脚本来处理日志合并和替换import os def replace_log(tup_list, log_file_path): data_lines [] with open(log_file_path, r, encodingutf-8, errorsreplace) as file: data_lines [line.strip() for line in file.readlines()] for i in range(len(data_lines)): if data_lines[i]: for old_str, new_str in tup_list: data_lines[i] data_lines[i].replace(old_str, new_str) for old_str, new_str in tup_list: data_lines.append(f{new_str}{old_str}) os.remove(log_file_path) with open(log_file_path, w, encodingutf-8) as file: file.write(\n.join(data_lines)) # 替换用户代理字符串等长文本 replace_list [ (Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/133.0.0.0 Safari/537.36 Edg/133.0.0.0, ua) ] replace_log(replace_list, /path/log.txt)3.2 逆向分析a_bogus生成逻辑分析日志要从a_bogus完全生成的位置开始然后倒推它的生成过程。我发现a_bogus的生成主要分为几个阶段基础字符串拼接多层加密转换最终编码处理具体来说a_bogus是通过以下步骤生成的// a_bogus生成的核心逻辑 function get_raw_ab(lm_get_ab_n, key_str) { var s , bw 0; for (var i 0; i lm_get_ab_n.length; i 3) { var cl 16; var tcz 0; var sof 16515072; for (var j i; j i 3; j) { if (j lm_get_ab_n.length) { var tlcy lm_get_ab_n.charCodeAt(j) 255; tcz tcz | (tlcy cl); cl - 8; } else { bw 1; // 计算补位 } } for (var h 18; h (6 * bw); h - 6) { var tsz tcz sof; s key_str[tsz h]; sof sof / 64; } s .repeat(bw); } return s; }这个函数的核心是将输入字符串每3个字符一组进行处理通过位运算和查表生成最终的a_bogus值。其中key_str是一个特殊的编码表info_dic { s4: Dkdpgh2ZmsQB80/MfvV36XI1R45-WUAlEixNLwoqYTOPuzKFjJnry79HbGcaStCe }3.3 关键算法解析通过分析日志我发现a_bogus的生成用到了类似Base64的编码方式但有几点关键区别使用自定义的编码表而非标准Base64表输入字符串先经过多层转换包含特殊的补位逻辑具体编码过程如下将输入字符串按3字节分组每组24位拆分为4个6位片段每个6位片段作为索引查自定义编码表根据剩余字节数添加补位字符逆向这个算法的难点在于理解中间的转换过程。我通过日志发现在进入编码函数前原始输入已经经过了多次转换原始输入 - 加密转换1 - 加密转换2 - 最终编码 - a_bogus4. 算法复现与验证4.1 复现加密流程基于日志分析我逐步复现了整个加密流程。下面是关键步骤的代码实现// 生成中间字符串 function get_lm_g_ab(lm_g_lm_n) { var fixed_sz256_n [194, 249, 255, 165, 114, 67, 251, 187, 174, 231, 164, 237, 124, 235, 68, 83, 206, 79, 142, 167, 30, 77, 0, 93, 118, 29, 32, 161, 2, 171, 243, 179, 42, 170, 223, 119, 98, 222, 219, 57, 245, 135, 197, 13, 186, 202, 88, 184, 214, 12, 76, 185, 116, 74, 54, 53, 104, 208, 158, 163, 82, 173, 253, 240, 172, 63, 191, 207, 25, 15, 201, 203, 215, 236, 183, 233, 145, 127, 72, 6, 16, 10, 228, 35, 232, 159, 66, 168, 108, 71, 217, 75, 33, 155, 112, 128, 36, 24, 138, 50, 211, 23, 107, 14, 247, 137, 175, 242, 234, 157, 199, 49, 139, 85, 81, 17, 180, 86, 120, 78, 51, 205, 169, 148, 181, 3, 94, 106, 252, 220, 150, 47, 151, 84, 212, 18, 149, 182, 100, 123, 121, 156, 154, 152, 126, 204, 60, 133, 132, 248, 7, 91, 58, 59, 20, 97, 113, 117, 131, 46, 250, 224, 21, 73, 146, 31, 193, 69, 140, 125, 9, 39, 89, 5, 65, 141, 218, 80, 1, 70, 64, 166, 87, 189, 55, 147, 22, 26, 143, 61, 144, 99, 92, 44, 129, 130, 227, 103, 90, 192, 198, 244, 136, 101, 246, 153, 56, 38, 4, 178, 221, 162, 134, 37, 111, 28, 216, 96, 102, 210, 254, 196, 195, 230, 241, 62, 11, 122, 52, 40, 41, 229, 226, 225, 48, 45, 160, 105, 8, 115, 34, 43, 209, 95, 239, 190, 188, 109, 27, 19, 176, 213, 200, 238, 177, 110]; var z 0; var st ; for (var i 0; i lm_g_lm_n.length; i) { var a (i 1) % 256; var c fixed_sz256_n[a]; z (z c) % 256; var e fixed_sz256_n[z]; fixed_sz256_n[a] e; fixed_sz256_n[z] c; var g (e c) % 256; var h lm_g_lm_n.charCodeAt(i); var j fixed_sz256_n[g]; var k h ^ j; var l String.fromCharCode(k); st l; } return st; }4.2 完整算法实现将各个步骤组合起来就得到了完整的a_bogus生成算法function generate_a_bogus(dpf, ua) { // 1. 初始化变量和编码表 var info_dic { s4: Dkdpgh2ZmsQB80/MfvV36XI1R45-WUAlEixNLwoqYTOPuzKFjJnry79HbGcaStCe }; // 2. 生成随机数种子 Math.random function() { return 0.5 }; // 固定随机数便于调试 // 3. 多层加密转换 var lm_get_lm generate_lm(dpf, ua); var lm_get_ab_tail get_lm_g_ab(lm_get_lm); var lm_get_ab_head4 generate_head4(); var lm_get_ab lm_get_ab_head4 lm_get_ab_tail; // 4. 最终编码 var a_bogus get_raw_ab(lm_get_ab, info_dic.s4); return a_bogus; }4.3 验证算法准确性为了验证算法的正确性我对比了生成的a_bogus和实际请求中的值// 测试用例 var test_ab generate_a_bogus(dpf, ua); console.log(test_ab O7UfhzU7dNmfCdMbuKpByXrUzhVMrPSyaiTobOMTSDTGT1tbUSN2EcGaJooLiiLyRbB0ie3H0ETMGDVcKsUkZK9pKmZDuPsS5G2nVg0L0qwXYzhsLqRiCh0Fww0C8RtL-5cRi1WRIssx1EAl9qIDABIaH5Pq-O8pRrZVp/YycDcWpBgTVx/CenzWfwj);5. 关键技术与难点解析5.1 自定义编码算法a_bogus使用的编码算法与Base64类似但有重要区别编码表完全不同输入预处理更复杂包含额外的加密步骤核心编码函数的关键部分for (var h 18; h (6 * bw); h - 6) { var tsz tcz sof; s key_str[tsz h]; sof sof / 64; }这段代码将24位数据分成4个6位片段然后查表编码。sof初始值为16515072(0xFC0000)每次右移6位。5.2 多层加密转换在进入最终编码前原始数据经过了多层转换用户代理(UA)字符串加密请求参数(dpf)加密时间戳混合运算随机数生成这些转换使得直接猜测a_bogus的生成逻辑变得非常困难。5.3 动态密钥数组算法中使用了一个256字节的固定数组作为加密密钥var fixed_sz256_n [194, 249, 255, 165, ... , 177, 110];这个数组虽然固定但它的生成过程相当复杂通过多步数学运算和位操作得到。6. 实际应用中的注意事项6.1 时间戳的影响在逆向过程中我发现a_bogus的生成与时间戳密切相关。即使其他参数完全相同不同时间生成的a_bogus也会不同。这增加了逆向的难度也说明服务端可能在校验时间有效性。6.2 随机数的处理算法中使用了Math.random()生成随机数但在实际请求中这些随机数被固定了。这意味着服务端可能不校验随机性可以使用固定值代替随机数但随机数的存在增加了逆向难度6.3 参数更新机制在测试中发现即使使用完全相同的参数生成的a_bogus在一段时间后也会失效。这表明服务端可能定期更换加密参数需要动态更新算法中的某些固定值完全逆向的算法可能需要定期维护7. 完整实现代码以下是完整的a_bogus生成算法实现包含了所有关键步骤function generate_full_a_bogus(dpf, ua) { // 1. 初始化编码表 const info_dic { s0: ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789/, s1: Dkdpgh4ZKsQB80/Mfvw36XI1R25WUAlEi7NLboqYTOPuzmFjJnryx9HVGcaStCe, s2: Dkdpgh4ZKsQB80/Mfvw36XI1R25-WUAlEi7NLboqYTOPuzmFjJnryx9HVGcaStCe, s3: ckdp1h4ZKsUB80/Mfvw36XIgR25WQAlEi7NLboqYTOPuzmFjJnryx9HVGDaStCe, s4: Dkdpgh2ZmsQB80/MfvV36XI1R45-WUAlEixNLwoqYTOPuzKFjJnry79HbGcaStCe }; // 2. 固定随机数便于调试 Math.random function() { return 0.5 }; // 3. 生成加密所需的中间数据 const t1 Date.now(); const t2 t1 - 1 Math.floor(Math.random() * 3); const t3 t1 Math.floor(Math.random() * 12) 4; const t4 t1 Math.floor(Math.random() * 900) 100; // 4. 多层加密转换 const lm_get_EP generate_lm_g_EP(ua); const EP get_raw_ab(lm_get_EP, info_dic.s3); const eEP enc_sum(EP); const eedp enc_sum(enc_sum(dpf dhzx)); // 5. 生成最终a_bogus const szenc_o95 generate_szenc_o95(t1, t2, t3, t4, eedp, eEP); const szenc_tail get_szenc_tail(szenc_o95); const szenc generate_szenc_head8().concat(szenc_tail); const lm_get_lm get_list_str(szenc); const lm_get_ab_tail get_lm_g_ab(lm_get_lm); const lm_get_ab generate_lm_g_ab_head4() lm_get_ab_tail; const a_bogus get_raw_ab(lm_get_ab, info_dic.s4); return a_bogus; }8. 逆向经验总结通过这次对a_bogus算法的完整逆向我总结了几点重要经验系统性分析从结果倒推逐步分析每个中间步骤日志优化合理处理日志减少干扰信息模块化验证将复杂算法拆分为多个小模块分别验证动态调试结合断点调试和日志分析理解执行流程耐心细致复杂算法逆向需要极大的耐心和细心这个案例也展示了现代Web应用如何通过多层加密和混淆来保护接口安全。作为开发者理解这些机制不仅能帮助我们解决爬虫问题也能提升自身的安全开发能力。最后需要强调的是本文的技术内容仅供学习研究之用。在实际应用中请遵守相关法律法规和网站的使用条款。逆向工程应该以学习和提升技术为目的而不是用于非法用途。

更多文章