news 2026/9/2 23:14:54

JS逆向实战:从定位到复现decode__1174混淆解密函数

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
JS逆向实战:从定位到复现decode__1174混淆解密函数

简介:面向JS逆向学习者,针对盼之decode__1174版本提供补环境项目代码,系统覆盖无限debugger绕过、动态后缀添加分析、加密环境数组明文还原等逆向基础流程,适用于分析加密JS逻辑与调试反调试机制的场景。压缩包内共5个文件,含3个HTML页面、1个inscode配置及1个gitignore文件,整体仅17KB,轻量而完整,目录结构简洁。已有201人学习下载,适合掌握一定JavaScript基础、希望进阶补环境技术的开发者研读;配合浏览器开发者工具,可逐步跟踪代码执行过程。代码紧扣实际检测点,实现localStorage触发后缀、Math.random.constructor构造检测、debugger冲突Hook以及Object.defineProperty描述符重写等关键手段;逐段对照可理清程序运行时的环境校验逻辑,掌握从环境伪造、断点绕过到数据还原的完整思路。HTML页面可在浏览器中直接运行,便于结合具体项目逐行验证补环境效果,是实践JS逆向的轻量参考。 JS逆向里最磨人的往往不是算法本身,而是像 decode__1174 这种编号随机、逻辑绕来绕去的混淆函数。这周我和朋友处理一个游戏交易平台的展示数据采集,目标站就叫盼之吧。接口返回的订单名、角色名全是密文,页面却能正常显示,说明前端必然有一个 decode 函数在做解密。全局搜了一轮,锁定的就是 decode__1174。整个排查过程走下来,从定位、断点、Hook 到用 Python 复现,思路其实完全通用。本文就按这个过程拆开讲,适合正在啃混淆 JS、做参数逆向或者被动态解密接口折磨的朋友参考,核心不是让你背代码,而是教你抓住输入输出,用动态调试反推静态逻辑。

1. 项目背景与总体逆向思路

1.1 decode__1174 在链路中到底干了什么

先交代背景。盼之这个站是比较典型的 SPA 结构,列表数据通过 XHR 拉取,返回 JSON 里的关键字段不是明文,而是类似kY8...的长字符串。页面展示走的是自研组件,你在 Network 面板里看到的响应是密文,屏幕上显示的却是明文。这中间隔着的就是一层 JS 解密。全局搜“decode”能捞出一堆候选,最后定位到的是 decode__1174。1174 这种编号说明它来自构建工具级别的混淆,函数名被统一打平,不是业务手写的命名。也就是说,这类函数大概率不是独立设计的高强度算法,而是一个把 A 转成 B 的工具函数,我们只要能拿到输入和输出,就能把它从黑盒变成白盒。

为什么说先弄清楚角色很重要?因为方向一错,时间全浪费。比如你把焦点放在分析某个自研加密算法上,结果发现它只是对服务端返回做了一层 base64 转码;反过来,你以为只是一个简单解码,里面却嵌套了 AES 和动态 key。所以第一步不是写代码,而是先确认这个函数被谁调用、传进去什么、吐出来什么。

1.2 动态调试优先,静态分析为辅

遇到 decode__1174 这种函数名,已经能说明它是被自动化混淆过的。你去静态读那一大坨_0x3f2a_0x5b9c,人脑容易被绕晕。我的习惯是优先动态调试,让浏览器自己把答案算出来。具体做法就是下断点、打印调用栈、Hook 返回值。这就好比你想知道自动售货机怎么把硬币换成饮料,与其拆开机箱研究电路,不如投一次币,在取货口接住结果,再在投币口装一个记录仪。

动态调试的最大优势是不用理解每一行代码。只要能在函数入口和出口拿到 input/output,就能确认算法效果。然后你再用静态阅读去还原其中关键几步,效率会高很多。这套流程适用于 decode__1174,也适用于大多数 sign、token、param 类的混淆函数。

1.3 手上要有的工具清单

环境上不需要特别重型的东西,我实际用到的就这些:

  • Chrome DevTools:核心工具。Sources 里可以格式化压缩 JS,Network 里能看请求发起者 Initiator,Console 里能随时执行表达式,Overrides 功能可以直接覆写线上 JS 文件。
  • Charles 或 Fiddler:用来做请求断点、重写返回,必要时直接替换某个 JS 文件为本地版本。没有的话,用 DevTools 本地替换也能实现类似效果。
  • VS Code + Prettier / js-beautify:把大段混淆代码格式化,便于搜索和阅读。注意格式化后的文件要另存,不要直接在线上改。
  • Node.js + npm:后面用 Python 复刻算法之前,至少要能本地运行一段 JS 验证。推荐装 pyexecjs 或 jsdom,用来在 Python 里临时调用 JS。
  • Python + pycryptodome / requests:批量解密验证用。

先别急着写代码,把这几样准备好。

2. 定位目标函数:从搜索到断点

2.1 在几百个 JS 文件里捞关键字

正式开始。打开 DevTools 的 Sources,找到项目主域名的文件目录,按 Ctrl+Shift+F 全局搜索。搜索词不要一上来就搜 decode__1174,因为函数名可能动态拼接,或者实际压缩后出现在别的分包里。先把范围扩大:搜decodeatobbtoafromCharCodecharCodeAtCryptoJSAESRSAbase64。这些是解密类函数的常见特征,命中概率高。

我这里运气不算差,搜decode__直接跳到了定义位置:一个约 300KB 的主业务 JS 文件里,第几万行的位置。压缩后的代码长这样:

function decode__1174(e){var t={}...

格式化之后,函数体仍然有一堆_0x...,说明字符串被抽到了一个数组里。但不用怕,先在函数开头、中间、结尾分别打上断点,然后去页面上操作一次触发接口。断点一停,右键当前函数,看 Scope、Call Stack,然后 Console 里打印arguments[0]decode__1174(arguments[0]),输入输出立刻明确。

如果全局搜索 decode__1174 搜不到,通常意味着函数名是在运行时动态生成的。别慌,改用搜索Function(eval(new Function,或者直接看Function.prototype.constructor相关调用。动态生成函数一般会先拼接字符串再执行,在网络请求返回的 JS 里搜1174也常常能捞到蛛丝马迹。

2.2 顺着调用栈锁定真正的入口

光找到函数定义还不够,要搞清楚它是被谁调的、什么时候被调用的。这里有个很实用的功能:在 Network 面板里找到返回密文的 XHR 请求,往下拉看 Initiator 那一列的调用栈。点击栈帧会自动跳转到发起请求或处理响应的源码位置。在响应处理的回调那句下断点,比如JSON.parse(xhr.responseText),然后刷新页面。断点触发后,Console 里输入console.trace()或者直接看 Call Stack 面板,一路点进调用链,直到某一帧出现 decode__1174。

我这次就是在某一帧里看到:一个命名类似renderList的函数拿到服务端返回后,对每个 item 的 name 字段调用了decode__1174(item.name)。此时疑问消失了:它不是生成请求参数的加密函数,而是响应数据的解包函数。它把服务端给的 code 字符串解成可读文本。这一步决定了后面复刻算法的方向。

注意,如果调用链非常深、回调很多,你可以在 decode__1174 的入口断点处直接看 caller。旧版浏览器可以用arguments.callee.caller,新版直接右键 Call Stack 面板里的上层函数跳转。不要试图手动往上翻几万行代码找调用方,那是低效的。

2.3 静态阅读:看懂关键步骤而非全部

定位之后就是静态阅读,目标不是把整段混淆代码逐行翻译,而是挑出影响结果的几个关键动作。我习惯先把函数体复制到本地,用 VS Code 格式化,再逐块标记。

第一块,预处理。看入参 e 是否被取子串、替换某些字符。例如e.substr(4)或者e.replace(/^..../,''),说明密文有一个固定前缀需要跳过去。

第二块,核心算法。看有没有调用atobCryptoJS.AES.decryptnew BlobTextDecoderdecodeURIComponent这类高特征 API。有的话基本就能确定算法族。

第三块,后处理。看解密结果是否有reverse()replaceJSON.parsesplit之类的操作。这一步很容易被忽略,但会导致你用 Python 解出来一坨正确 base64 却得不到正常字符串。

我这里看到的一个细节是:函数最开始用字符串的charCodeAt(0)减去某个偏移量得到前缀长度,再截掉前缀;中间调用了一个从字符串数组里取出来的函数,核心是 atob 加移位;最后把结果逆序。整个下来,本质就是把服务端返回的字符串做了一层自定义编码,再包了一层 base64,而不是真正的加密。到这里,算法轮廓已经清楚。

3. 还原解密逻辑与代码复现

3.1 逐行还原出可读的 JS 逻辑

按照前面的静态分析,我把 decode__1174 还原成了一个可读版本。这里要注意,真实版本里第 3 步不是直接用浏览器原生 atob,而是从一个字符串数组_0x2f1a['...']里取出来的等价函数,底层还是一样。这里直接写成 atob 是为了方便理解:

// 原混淆函数: decode__1174 function decode__1174(input) { // 1. 计算前缀长度:首字符的 charCode - 48 var prefixLen = input.charCodeAt(0) - 48; if (prefixLen < 0 || prefixLen > 8) return ''; // 2. 去掉前缀 var body = input.slice(prefixLen); // 3. base64 解码 var decoded = atob(body); // 4. 逐字符逆序 var result = decoded.split('').reverse().join(''); return result; }

如果你遇到的是 AES 这类对称加密,会多出 key 和 iv 两个问题。key 和 iv 往往不是写死的字符串,而是由其它函数动态算出来的。我的建议是:别在 Python 里硬编码 key,先用 Hook 把 key 的真实值打出来,或者干脆在 Python 里用 subprocess 调用 Node 执行一段 JS,把 key 计算出来再喂给解密函数。这样目标站一旦更新 key 生成逻辑,你只需要更新 JS,不用动解密主流程。

3.2 用 Hook 验证你的理解

算法还原得对不对,最好的验证方式是 Hook。如果 decode__1174 是挂在全局对象上的,直接在 Console 执行:

const originDecode = window.decode__1174; window.decode__1174 = function (...args) { const result = originDecode.apply(this, args); console.log('[hook] in:', args[0]); console.log('[hook] out:', result); return result; };

这样页面再调用的时候,Console 会打印真实入参和出参。把输出和你自己的还原脚本结果对比,一致就说明算法吃透了。

针对不在全局对象上的函数,有一个更通用的办法:用 DevTools 的 Local Overrides,在源码里临时加一行window.__decode1174 = decode__1174;,刷新后就可以在 Console 操作它。如果你不想改文件,也可以右键行号设置 Logpoint,只打日志不改逻辑。把 Logpoint 放在函数 return 那一行,能直接看到返回值;放在第一行能看到入参。这个技巧在压缩代码里尤其好用。

3.3 用 Python 实现批量解密

算法确认后,接下来就是把这套逻辑搬进自己的采集脚本。我这里用 Python 实现一个简化版本:

import base64 import requests from Crypto.Cipher import AES from Crypto.Util.Padding import unpad # 示例1: 如果是“前缀长度+base64+逆序” def decode_1174_simple(cipher_text: str) -> str: prefix_len = ord(cipher_text[0]) - 48 body = cipher_text[prefix_len:] decoded = base64.b64decode(body).decode('utf-8') return decoded[::-1] # 示例2: 如果是“4字节随机前缀 + AES-CBC” def decode_1174_aes(cipher_text: str, key: bytes, iv: bytes) -> str: payload = cipher_text[4:] enc = base64.b64decode(payload) cipher = AES.new(key, AES.MODE_CBC, iv) plain = unpad(cipher.decrypt(enc), AES.block_size) return plain.decode('utf-8') # 批量验证 resp = requests.get("https://target.example.com/api/list", headers={...}).json() for item in resp["data"]: name = decode_1174_simple(item["name"]) print(name)

实战中我强烈建议先跑一个包含 10 条左右记录的小样本,把脚本输出和页面上显示的内容逐条比对。确认完全一致后,再放开全量跑。因为一旦有 1% 的边界情况没覆盖,比如空字符串、null、异常前缀,全量跑会哗啦啦报错。批量处理的代码要增加异常捕获,遇到非法密文直接记录日志而不是中断。

另外,如果出于某种原因你不想在 Python 里重写算法,还可以用 pyexecjs 或者 Node 子进程直接执行原 JS。这是最保真的方式,代价是会慢一点。常见的做法是:把整个加解密模块剪出来,在 Node 环境里挂一个最小化的 window/document mock,然后暴露成 module.exports。注意依赖的浏览器 API 越少,越容易跑起来。

4. 实战中的常见问题与排查技巧

4.1 断点失效、无限 debugger 和反调试

做这种带混淆的站点,必然会碰到反调试。最常见的两个现象:

第一,页面无限进 debugger。点继续之后立刻又停,这种多半是代码里有一段setInterval(function(){debugger;}, 100)或者Function('debugger')()。解决办法是 Local Overrides 把包含 debugger 的 JS 文件替换成本地副本,搜索debugger关键词批量删掉。

第二,调试一开就检测。比较粗暴的站点会用 console 内容差异检测、窗口大小检测、时间差检测。遇到这种,优先考虑用 Puppeteer/Playwright 这类无头浏览器跑,它们天然就是一个真实环境,反调试代码一般不会触发。要是反调试太强,再考虑在 JS 里 HookconsoleDateperformance这些会被探测的 API。

不要一碰到反调试就退缩。大多数网站的所谓“防护”只是用现成混淆工具开了一层壳,核心还是那几类算法,把壳去掉之后,前面分析的解密流程完全不变。

4.2 混淆变量、闭包和调用时机

另一个常见坑是找不到函数引用。函数被定义在一个立即执行函数(IIFE)里,外部根本访问不到。你按照我上面的做法去window.decode__1174取,返回 undefined,这很正常。解决办法有两个:

  • 改源码导出。在函数定义后面加window.__decode1174 = decode__1174;,然后用 Overrides 刷新页面试试。
  • 在函数内部打断点,等断点停住后,在 Console 的 Scope 区域找到this、decode__1174,手动执行。这也能验证函数。

再就是调用时机。有些 key、iv、盐值是页面加载后通过某个接口动态获取的,如果你在页面还没加载完就手动调用 decode__1174,会得到错误结果。遇到这种情况,先重复触发一次正常流程,确保依赖的数据已经生成,再调用解密函数。

4.3 常见问题速查表

把这次和以往项目中常遇到的问题整理成表,方便你复盘时对照:

现象常见原因排查手段
全局搜不到 decode__1174函数名被动态拼接或写在 eval/Function 中Function(eval(1174,或用断点停到调用后看作用域
函数在闭包内无法访问定义在 IIFE 里改源码导出到 window,或函数内断点手动调用
断点一直被跳过检测调试、使用了 release 模式压缩本地替换 JS 去掉 debugger/检测
解密结果乱码key/iv/编码/模式不一致和页面输出比对,试 ECB/CBC、Pkcs7/ZeroPadding
前缀长度算法看错charCodeAt 取的是首字符而不是 ASCII 数字Console 打印input.charCodeAt(0)验证
Python 调用 JS 报 window 未定义原 JS 依赖浏览器全局对象用 jsdom/VM 模拟 window/document,或只剪出纯函数
解密成功但列表个别字段为空服务端返回了空串或非标准编码增加异常捕获,记录并跳过

表格里第 4、5 条是真实踩过的。尤其是前缀长度那里,我最初以为首字符就是前缀长度,结果发现它把字符的 ASCII 码减了 48 得到数字,如果用int(input[0])去解析,一个字母开头就直接报错。这种细节非常容易让批量脚本崩掉。

4.4 别忘了留底和对比

最后分享一个习惯。定位到 decode__1174 后,我会第一时间在 Console 里执行copy(decode__1174.toString()),把函数源码存成文件,再记下调用它的入口 URL 和参数示例。这样过几天网站升级,函数逻辑变了,我拿新旧代码一 diff,几秒钟就知道它改了哪一行。这个习惯帮我省掉了大量重复逆向时间,也方便把算法变化同步到自己的脚本里。

整套流程走下来,你会发现 decode__1174 这类函数并没有想象中神秘。它大概率就是混淆版的 base64、AES、自研位移,难点不在算法,而在定位。定位的核心思路永远是:先找到输入输出,再用动态调试反推静态逻辑,最后用脚本验证。只要你抓到一条真实的入参和出参,这个黑盒就已经开了 80%。另一个想提醒的是,所有分析和测试我都在自己有权访问的测试环境里做,大家动手练习时也尽量用自己搭的靶场或者官方允许的站点,别拿线上核心接口硬来。

本文还有配套的精品资源,点击获取

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/2 23:13:06

Web前端期末大作业高分指南:选题、开发与答辩全攻略

简介&#xff1a;面向高校学生的 web 前端期末作业资源包&#xff0c;内含多套大学生网页设计作品&#xff0c;可任选其一&#xff1a;既有 Dreamweaver 制作的基础作业&#xff0c;也有包含 6 个页面的个人主页完整站点&#xff0c;集成了视频、脚本等交互元素&#xff0c;适合…

作者头像 李华
网站建设 2026/9/2 23:12:29

免费进销存软件onlyit实战:从初始化到库存管理闭环

简介&#xff1a;这是一套面向小型企业和个体经营者的免费进销存管理软件&#xff0c;集成进货、销售、库存、财务等核心模块&#xff0c;并提供OA源码便于二次开发与功能定制。软件以窗体程序形式运行&#xff0c;界面直观&#xff0c;适合需要快速上手、低成本实现业务数字化…

作者头像 李华
网站建设 2026/9/2 23:09:49

WX Backup实测:把iPhone微信聊天记录完整导出到Windows电脑

简介&#xff1a;一份面向苹果手机用户的 Windows 版微信聊天记录备份导出工具 WX Backup&#xff0c;主要帮 iPhone 用户解决微信聊天记录因长期累积导致手机存储不足、却又不能随意删除的难题。它通过解析 iTunes 备份文件&#xff0c;将指定联系人或群聊记录完整导出到电脑端…

作者头像 李华
网站建设 2026/9/2 23:07:59

电路基础核心:电荷、电流、电压的定义与方向解析

电荷、电流、电压是电路基础里最容易被低估的一讲。拉扎维在 UCLA 的电路基础课程里&#xff0c;并没有一上来就讲欧姆定律&#xff0c;而是先把这三个概念之间的先后关系理清楚。我的直观感受是&#xff0c;很多人学完模拟电路之后会套公式&#xff0c;但遇到“为什么电阻两端…

作者头像 李华
网站建设 2026/9/2 23:06:46

OpenAI为Astra拉满预热,网络安全能力超强但安全问题成关键

突发&#xff01;OpenAI为Astra&#xff08;GPT - 6&#xff09;拉满预热&#xff0c;网络安全能力超强但安全问题成关键就在刚刚&#xff0c;OpenAI多线齐发&#xff0c;为下一代模型Astra&#xff08;传说中的GPT - 6&#xff09;拉满预热。官方突然放出技术长文&#xff0c;…

作者头像 李华
网站建设 2026/9/2 23:03:49

鸿蒙电脑部署OpenClaw开源Agent:源码直跑与踩坑实战

简介&#xff1a;面向在鸿蒙电脑上落地AI代理自动化的开发者&#xff0c;这份OpenClaw部署项目源码包聚焦开源智能代理框架的鸿蒙适配。OpenClaw支持自然语言指令、本地优先与跨设备协同&#xff0c;资源围绕环境准备、本地化适配及Gateway设置等环节提供可运行代码。压缩包仅3…

作者头像 李华