
先聊几句题外话。很多刚开始接触爬虫的同学常常会卡在同一个地方请求头明明照着浏览器填了Cookie 也带了可服务器还是返回403或者data: [...]这种看不懂的密文。这个时候大概率是遇到了JS 逆向问题——数据在浏览器端被 JavaScript 加密服务器也只认加密后的结果。网上关于 JS 逆向的资料虽然多但大多零散要么只讲一个算法要么直接扔一段扣好的代码让人知其然不知其所以然。所以我整理了这一篇以“标准算法 扣代码思路 完整实战”为主线的文章把 JS 逆向里最常用的加密算法、定位方法、断点技巧、Python 还原实现都串起来。文章不会只堆代码而是会解释每个案例的思考过程方便你遇到新网站时能自己分析。内容偏基础也适合刚入门想系统学习爬虫逆向的朋友。1. JS 逆向到底在做什么1.1 什么是 JS 逆向JS 逆向简单来说就是分析网站前端 JavaScript 代码弄清楚它对请求参数、请求头、响应数据做了哪些处理然后把这些处理逻辑用 Python 或其他语言重新实现一遍从而让脚本能够模拟浏览器行为拿到服务器返回的真实数据。举个例子。某个网站登录时浏览器地址栏里看到的是POST https://example.com/api/loginPayload 是{ username: admin, password: a1b2c3... }但你直接写 Python 代码提交明文密码服务器会提示“密码错误”。打开浏览器开发者工具在 Sources 里搜一下就会发现页面里有一段 JS做了类似这样的操作password md5(password salt);所以真实请求里提交的 password 并不是明文而是“明文 盐值”后的 MD5 值。JS 逆向要做的就是找到这个salt和加密函数然后在 Python 里复现。1.2 JS 逆向的三大应用方向概括起来目前 Web 端的 JS 逆向主要分为三类方向说明常见场景请求参数加密对提交的参数做签名、加密登录密码、搜索参数、分页参数请求头加密对请求头做签名或动态生成反爬 Token、时间戳签名、Headers 校验响应数据加密服务器返回密文前端解密后渲染价格接口、订单接口、搜索列表这三个方向并不互斥很多网站会同时使用。例如先对参数做签名MD5/SHA256再对整个请求体做 AES 加密响应数据再用 RSA 解密。这也是为什么我建议新手先把标准算法掌握熟练再去研究魔改算法。1.3 需要掌握的基础工具正式进入案例之前建议先把工具链准备好Python 3.8推荐 3.9 或 3.10新项目尽量不用 2.x。Node.js用于运行、调试 JavaScript 代码尤其是扣下来的加密函数。浏览器开发者工具Chrome 或 Edge 的 F12 面板。Charles / Fiddler抓包工具用于移动端或桌面端应用。Burp Suite用于分析 HTTP 请求、重放请求尤其在自测接口时很有用。Postman / Apifox用于接口调试。Python 环境中常用到的库包括pip install requests pip install pycryptodome pip install execjs pip install nodejs说明execjs可以在 Python 中直接执行 JavaScript 代码但性能一般。实际生产环境中更推荐“用 Python 重写算法”或“直接调用 Node.js 子进程”。2. 爬虫入门先会写一个标准 requests 请求很多同学一上来就想搞复杂的 RSA、AES但真正到项目里你会发现 60% 的网站其实只做了最简单的参数校验甚至连加密都没有。所以先掌握标准爬虫请求的写法非常重要。2.1 环境准备操作环境以 Windows / macOS 为例Python 版本建议 3.9 以上。先确认环境python --version pip --version如果还没有安装 requests执行pip install requests2.2 最小请求示例我们以常用的requests库为例写一个最基本的 GET 请求import requests url https://httpbin.org/get headers { User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 } resp requests.get(url, headersheaders, timeout10) print(resp.status_code) print(resp.text)这是一个非常简单的请求但已经包含了两个核心点设置User-Agent避免被一些基础反爬拦截。设置timeout防止请求卡死。2.3 带参数的 POST 请求实际爬虫中更多请求是 POST 类型需要传表单或 JSON 数据。import requests url https://httpbin.org/post data { username: admin, password: 123456 } resp requests.post(url, datadata, timeout10) print(resp.json())如果网站要求提交 JSON使用json参数resp requests.post(url, jsondata, timeout10)需要注意的是data与json的编码方式不同服务器解析方式也不同具体要看抓包工具里原始请求的Content-Type。2.4 Session 保持会话涉及登录的网站通常需要携带 Cookie。用requests.Session()可以自动管理 Cookieimport requests session requests.Session() login_url https://example.com/api/login login_data { username: admin, password: 123456 } session.post(login_url, datalogin_data) profile_url https://example.com/api/profile resp session.get(profile_url) print(resp.text)Session对象会自动保存服务端返回的 Cookie并在后续请求中携带这能减少很多手动处理 Cookie 的麻烦。这一步做完后你会发现大部分“无需加密”的网站已经能正常爬取了。接下来我们再进入逆向的核心场景。3. 逆向前的准备定位加密位置3.1 通过抓包确认加密参数假设你遇到了一个需要加密参数的接口。第一步永远是打开浏览器开发者工具——Network 面板重新触发一次请求找到对应的 XHR 请求。例如某网站搜索接口Request URL: https://example.com/api/search Request Method: POST Payload: { keyword: 手机, sign: b2f0c9d1e8a74b6f9d3e... }肉眼观察keyword是明文但sign是一串十六进制字符。这个sign很可能就是加密签名。接下来要做的就是找到sign是怎么生成的。3.2 全局搜索关键词按CtrlShiftF在开发者工具的 Search 面板里搜索sign关键字通常能找到相关函数。如果网站过了混淆搜索不到还可以尝试搜索加密特征值md5、sha1、sha256、AES、RSA、encrypt算法特征库CryptoJS、jsencrypt、bcrypt参数名特征sign、token、key、secret以 CryptoJS 为例很多网站会直接在 HTML 里引入script srchttps://cdn.jsdelivr.net/npm/crypto-js4.1.1/crypto-js.js/script这就意味着你能直接在全局对象上找到CryptoJS。3.3 通过 XHR 断点定位调用栈如果全局搜索找不到可以用 XHR 断点在 Sources 面板右侧找到XHR/fetch Breakpoints。点击加号输入目标接口的 URL 片段比如api/search。刷新页面或重新触发请求浏览器会在发起请求的那一行 JS 自动暂停。在 Call Stack 面板中层层向上查看调用栈找到参数生成的位置。这是目前最常用、也最不容易误判的定位方式。很多复杂的混淆网站靠搜索关键词是搜不出结果的但断点一定能触发。3.4 分清“算法加密”和“环境校验”在定位到加密函数后不要急着扣代码。先判断一下加密函数是纯粹的标准算法还是依赖了浏览器环境。标准算法如 MD5、SHA256、AES、RSA这些是纯数学运算不依赖浏览器环境可以直接用 Python 或 Node.js 重写。环境校验如navigator.userAgent、window、document、canvas指纹这些值在 Python 里并不存在需要特殊处理例如用execjs注入 mock 环境或者扣取完整代码后手动补环境。新手建议先从“标准算法”入手这类案例占比很高也最容易获得成就感。4. 标准算法实战Base64 与 MD5 签名这一章开始进入实战。每个案例我会按照“接口特征 - 定位方式 - Python 实现 - 运行结果”的顺序来写。4.1 案例一Base64 编码型参数现在很多网站的搜索参数或翻页参数并没有做真正的加密只是用 Base64 把 JSON 编码了一下。虽然 Base64 严格来说不算加密算法但它却是逆向入门最常见的“低垂果实”。假设某网站搜索请求的参数如下https://example.com/api/list?dataeyJwYWdlIjoxLCJzaXplIjoyMH0看到这种eyJ开头的数据几乎可以立刻判断是 Base64。因为 Base64 编码后的 JSON 字符串开头通常是eyJ对应{。定位方式在开发者工具中搜索btoa或Base64就能找到类似代码function encodeParam(obj) { return btoa(JSON.stringify(obj)); }Python 实现import base64 import json def encode_param(page, size): payload { page: page, size: size } # 先转为 JSON 字符串再 Base64 编码 json_str json.dumps(payload, separators(,, :)) encoded base64.b64encode(json_str.encode(utf-8)).decode(utf-8) return encoded print(encode_param(1, 20))输出结果eyJwYWdlIjoxLCJzaXplIjoyMH0这样我们在 Python 端就还原了前端逻辑。4.2 案例二MD5 加盐签名MD5 是目前最常见的签名算法之一。很多网站在提交表单时会先把密码做一次 MD5然后再加盐拼接或者把请求参数按字典序排序后拼接成一个字符串整体做 MD5。假设某登录接口的参数是{ username: admin, password: e10adc3949ba59abbe56e057f20f883e, timestamp: 1731234567890 }其中password的明文是123456加密逻辑看起来是password md5(password timestamp);这里额外加了一个时间戳所以每次密码的 MD5 值都不同也算是动态签名的一种常见形式。Python 实现import hashlib import time def md5_encrypt(text): md5 hashlib.md5() md5.update(text.encode(utf-8)) return md5.hexdigest() password 123456 timestamp str(int(time.time() * 1000)) sign md5_encrypt(password timestamp) print(timestamp:, timestamp) print(sign:, sign)输出结果类似timestamp: 1731234567890 sign: 8a4b7f2c9e6d3f1a...需要特别注意的是MD5 加盐时盐值放在前面、后面、还是中间结果完全不同。实际逆向时一定要去 JS 代码里确认拼接顺序。4.3 案例三SHA256 请求体签名部分网站会对整个请求体做哈希签名。比如请求体是{ keyword: 手机, page: 1 }前端会把 JSON 字符串按特定规则排序后做 SHA256得到sign再把sign放入 Header 或请求体。伪装前的 JavaScript 可能如下const crypto require(crypto); function signRequest(params) { const keys Object.keys(params).sort(); let raw ; for (let key of keys) { raw ${key}${params[key]}; } raw raw.slice(0, -1); return crypto.createHash(sha256).update(raw).digest(hex); }Python 实现import hashlib def sha256_sign(params): # 按 key 排序拼接成 keyvaluekeyvalue 形式 sorted_keys sorted(params.keys()) raw .join(f{key}{params[key]} for key in sorted_keys) print(待签名串:, raw) sign hashlib.sha256(raw.encode(utf-8)).hexdigest() return sign params { keyword: 手机, page: 1 } print(签名:, sha256_sign(params))输出结果待签名串: keyword手机page1 签名: 0e3a5c8f8b8c9e1d2f3a4b5c6d7e8f9a0b1c2d3e4f5a6b7c8d9e0f1a2b3c4d5eSHA256 是一种不可逆哈希算法应用非常广。尤其是在一些大厂的开放平台 API 中签名逻辑基本都是这种思路参数排序 - 拼接 - 哈希 - 放入请求头或参数中。5. 对称加密实战AES 与 DESMD5 和 SHA256 属于哈希算法主要用来做完整性校验和签名。而真正意义上的“加密”是能在加密后还原明文的算法。Web 端最常用的对称加密就是 AES。5.1 AES 的核心知识点AES 对称加密需要掌握几个关键概念密钥长度AES-128、AES-192、AES-256。填充模式PKCS7、PKCS5、ZeroPadding、NoPadding。工作模式ECB、CBC、CFB、OFB、GCM。偏移量CBC 模式下需要 IVECB 模式不需要。网站里最常见的是 AES-128-CBC 和 AES-256-CBC配合 PKCS7 填充。如果遇到 GCM 模式还需要处理额外的 AuthTag复杂度会高一些。5.2 定位 AES 加密代码AES 加密代码在浏览器里有几个明显特征引入了 crypto-jsCryptoJS.AES.encrypt(...)引入了 WebCrypto APIcrypto.subtle.encrypt(...)自研了 S 盒替换逻辑少见在开发者工具里搜索AES或CryptoJS通常都能直接定位到加密函数。一个典型的加密逻辑const key CryptoJS.enc.Utf8.parse(0123456789abcdef); const iv CryptoJS.enc.Utf8.parse(abcdef9876543210); function encryptByAES(content) { const encrypted CryptoJS.AES.encrypt(content, key, { iv: iv, mode: CryptoJS.mode.CBC, padding: CryptoJS.pad.Pkcs7 }); return encrypted.toString(); }这段代码的意思是密钥key是一个 16 字节字符串。偏移量iv是另一个 16 字节字符串。使用 CBC 模式PKCS7 填充。5.3 Python 还原 AES-CBCPython 中推荐使用pycryptodome库。安装pip install pycryptodome下面是上述 JavaScript 逻辑的完整 Python 实现from Crypto.Cipher import AES from Crypto.Util.Padding import pad, unpad import base64 def aes_cbc_encrypt(content, key, iv): # 将明文转为字节 data content.encode(utf-8) key key.encode(utf-8) iv iv.encode(utf-8) cipher AES.new(key, AES.MODE_CBC, iv) # PKCS7 填充 padded_data pad(data, AES.block_size, stylepkcs7) encrypted cipher.encrypt(padded_data) # 转 Base64 输出 return base64.b64encode(encrypted).decode(utf-8) def aes_cbc_decrypt(encrypted_b64, key, iv): encrypted base64.b64decode(encrypted_b64) key key.encode(utf-8) iv iv.encode(utf-8) cipher AES.new(key, AES.MODE_CBC, iv) decrypted cipher.decrypt(encrypted) # 去掉填充 return unpad(decrypted, AES.block_size, stylepkcs7).decode(utf-8) key 0123456789abcdef iv abcdef9876543210 encrypted_text aes_cbc_encrypt(hello js spider, key, iv) print(加密结果:, encrypted_text) decrypted_text aes_cbc_decrypt(encrypted_text, key, iv) print(解密结果:, decrypted_text)运行结果加密结果: 3b7dceb0e1ba8ea4ca8491d33a1a0b76 解密结果: hello js spider这里有几个坑需要提醒key 和 iv 的字节长度必须符合要求。AES-128 需要 16 字节AES-256 需要 32 字节。CBC 模式的 IV 长度必须等于块大小即 16 字节。填充方式不一致会导致解密失败。JS 端如果使用的是Pkcs7Python 端也要对应选择pkcs7。5.4 案例登录密码 AES 加密某网站在登录时对密码做了 AES 加密。定位后发现加密逻辑是let encryptedPwd CryptoJS.AES.encrypt(plainPwd, key, { iv: iv, mode: CryptoJS.mode.CBC, padding: CryptoJS.pad.Pkcs7 }).toString();此时我们不需要知道密钥是怎么来的只需要在 JS 代码中找到固定的key和iv。比如var key CryptoJS.enc.Utf8.parse(www.xxx.com.key); var iv CryptoJS.enc.Utf8.parse(www.xxx.com.iv!);那么 Python 就可以直接复用上面的aes_cbc_encrypt函数key www.xxx.com.key iv www.xxx.com.iv! pwd mypassword123 encrypted_pwd aes_cbc_encrypt(pwd, key, iv) print(encrypted_pwd)这里只是示例网上很多网站的 key 和 iv 长度实际上并不标准比如某个字符串长度不是 16 字节前端在 parse 后会自动截断或补零。遇到这种情况Python 端也需要做同样的字节截断或补零处理。6. 非对称加密实战RSA6.1 RSA 加密特征RSA 是非对称加密公钥加密、私钥解密。在浏览器端服务器会把公钥下发给前端前端用公钥加密敏感数据后端再用私钥解密。RSA 加密在 JS 中的典型识别特征出现setPublicKey、encrypt等字眼。引入jsencrypt库。出现MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC...这样的公钥字符串。一个典型的 JS 加密逻辑const encryptor new JSEncrypt(); encryptor.setPublicKey(publicKey); const encrypted encryptor.encrypt(password);6.2 Python 实现 RSA 加密Python 中常用rsa或Crypto.PublicKey.RSA来实现。这里以pycryptodome为例from Crypto.PublicKey import RSA from Crypto.Cipher import PKCS1_v1_5 import base64 def rsa_encrypt(public_key_str, content): # 加载公钥 pub_key RSA.import_key(public_key_str) cipher PKCS1_v1_5.new(pub_key) # 加密 encrypted cipher.encrypt(content.encode(utf-8)) return base64.b64encode(encrypted).decode(utf-8) public_key -----BEGIN PUBLIC KEY----- MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC... -----END PUBLIC KEY----- result rsa_encrypt(public_key, 123456) print(result)有几点要注意RSA 加密有长度限制。使用 1024 位密钥时PKCS1_v1_5 最多只能加密 117 字节2048 位密钥最多 245 字节。如果明文太长需要分段加密。如果 JS 端使用了setPublicKey传入的是不带BEGIN PUBLIC KEY头的裸 Base64 字符串Python 端需要先拼装成标准 PEM 格式再导入。JSEncrypt 默认使用 PKCS1 v1.5 填充。如果遇到 OAEP 填充Python 端要改用PKCS1_OAEP。6.3 RSA 分段加密示例长文本分段加密是实际项目中的常见需求。比如密码字段可能不长但如果加密的是手机号验证码时间戳拼接的长字符串就必须分段。def rsa_long_encrypt(public_key_str, content, chunk_size100): pub_key RSA.import_key(public_key_str) cipher PKCS1_v1_5.new(pub_key) content_bytes content.encode(utf-8) encrypted_chunks [] for i in range(0, len(content_bytes), chunk_size): chunk content_bytes[i:i chunk_size] encrypted_chunk cipher.encrypt(chunk) encrypted_chunks.append(encrypted_chunk) return base64.b64encode(b.join(encrypted_chunks)).decode(utf-8)分段大小需要根据密钥位数计算1024 位密钥时chunk_size最大 1172048 位时最大 245。这个值写得太小不会出错但会多一些冗余写太大则会直接报错。7. 进阶实战从 JS 中扣取加密函数前面的案例我们都是根据算法逻辑在 Python 中重新实现。但有些网站的加密逻辑比较复杂比如魔改的 Base64、自定的字符映射表、多层嵌套的混淆算法这时手动重写往往比较费劲。更高效的做法是直接把浏览器里的 JS 加密函数扣下来用 Python 调用。7.1 使用 Node.js 运行扣下的函数假设我们从某网站扣下了这样一段加密函数const CryptoJS require(crypto-js); function encryptContent(content, secretKey) { const key CryptoJS.enc.Utf8.parse(secretKey); const iv CryptoJS.enc.Utf8.parse(1234567890abcdef); const encrypted CryptoJS.AES.encrypt(content, key, { iv: iv, mode: CryptoJS.mode.CBC, padding: CryptoJS.pad.Pkcs7 }); return encrypted.toString(); } // 导出供其他模块调用 module.exports { encryptContent };保存为encrypt.js在终端里执行npm init -y npm install crypto-js node -e const { encryptContent } require(./encrypt.js); console.log(encryptContent(hello, abcdef0123456789));输出U2FsdGVkX14n9Z4cH8XpYxUO...这样我们就验证了扣下来的 JS 函数能独立运行。接下来在 Python 中调用它。7.2 Python 调用 JSexecjs 方式安装pip install PyExecJSPython 调用代码import execjs with open(encrypt.js, r, encodingutf-8) as f: js_code f.read() ctx execjs.compile(js_code) result ctx.call(encryptContent, hello, abcdef0123456789) print(result)7.3 Python 调用 JSNode.js 子进程方式execjs虽然方便但偶尔会遭遇编码问题或环境依赖问题。更稳的方式是直接调用 Node.js 子进程。Python 端代码import subprocess import json def call_js_function(js_file, func_name, *args): # 将参数通过 stdin 传给 Node.js payload json.dumps({ func: func_name, args: args }) result subprocess.run( [node, js_file], inputpayload, textTrue, capture_outputTrue, encodingutf-8 ) if result.returncode ! 0: raise RuntimeError(fNode.js 执行失败: {result.stderr}) return result.stdout.strip() # 使用方式 encrypted call_js_function(encrypt_cli.js, encryptContent, hello, abcdef0123456789) print(encrypted)encrypt_cli.js内容const { encryptContent } require(./encrypt.js); process.stdin.on(data, (data) { const input JSON.parse(data.toString()); const func global[input.func] || decryptContent || encryptContent; const result func(...input.args); process.stdout.write(result); });这种方式的优势是Python 与 Node.js 完全隔离不会因为 JS 库的兼容性问题影响 Python 主流程。如果加密逻辑复杂推荐优先用这种方式。7.4 扣代码时的环境补齐有些 JS 加密函数会依赖window、document、navigator等浏览器对象。扣出来后直接用 Node.js 运行会报错window is not defined。解决方法有两种在 Node.js 脚本顶部手动 mock 环境变量。在 Python 中用execjs配合外部 JS 运行时先注入 mock 对象。比如const window { userAgent: Mozilla/5.0 ..., navigator: {}, document: {} }; const navigator { userAgent: Mozilla/5.0 ..., platform: Win32 }; // 然后引入加密函数这种方法属于“补环境”是逆向里比较进阶的操作。平时练习时可以刻意选择一些加密不依赖环境变量的网站先练好基本扣函数能力再慢慢接触复杂的补环境场景。8. 常见问题与排查思路逆向过程最让人头疼的不是算法复杂而是报错后不知道从哪里入手。我把新手最常碰到的问题整理成一张表方便你对照排查。问题现象常见原因解决思路请求返回 403缺少请求头或 Cookie 校验失败检查 User-Agent、Referer、Origin、Cookie返回内容是一串密文响应数据被加密前端解密后渲染搜索decrypt、decode、JSON.parse定位解密函数Python 加密结果与浏览器不一致算法顺序、填充模式、编码方式不同对比多次加密结果优先检查字符串拼接和 Base64 编码控制台报xxx is not defined扣代码时缺少依赖函数或环境对象补全依赖 JS 文件或在 Node.js 中 mock 环境变量AES 解密报错key/iv 长度不对或填充模式不一致确认密钥、IV 字节长度填充模式统一为 PKCS7RSA 加密报错明文过长超出单次加密上限使用分段加密浏览器能请求Python 请求失败HTTP 版本、TLS 指纹或 headers 顺序不同尝试使用curl_cffi或调整 headers 顺序这里特别说一个新手容易犯的错误很多人拿到加密函数后直接在浏览器控制台复制执行结果然后在 Python 里设定固定的值去请求。这样做虽然能跑通一次但一旦加密参数携带时间戳或随机数下一次请求就会失效。所以正确的做法是在 Python 端动态计算加密参数或者动态调用 JS 函数而不是粘贴一次性结果。9. 标准算法识别的快速判断口诀实际逆向过程中我们可以通过密文特征快速判断算法类型减少试错时间。整理了几个常见特征特征判断以eyJ开头大概率是 Base64 编码的 JSON直接解码查看32 位小写十六进制大概率是 MD540 位小写十六进制可能是 SHA164 位小写十六进制大概率是 SHA256有、/、的密文大概率是 Base64 编码后的加密结果密文长度远大于明文长度可能是 RSA 或 AES 再做了 Base64 输出密文开头有U2FsdGVkX1这是 CryptoJS AES 加密的默认格式特征这些特征只能作为初步判断不能直接当成定论。比如 MD5 也可以是 32 位大写AES 加密后也可以转成 Hex 而非 Base64。最终还是要以 JS 代码为准。10. 逆向的原则与合规提醒爬虫和 JS 逆向技术本身是中性工具但在实际使用中需要注意边界。我从工程实践角度给几点建议只对你有权访问的网站进行逆向分析比如自己公司的系统、已授权的接口或公开的练习靶场。不要将逆向技术用于绕过登录态、破解付费内容、抓取用户隐私数据这些行为可能违反相关法律法规。练习时优先选择公开的模拟站点或使用本地搭建的靶场环境。在生产环境中控制请求频率合理设置延时避免给目标服务器造成压力。如果目标网站有明确的 robots 协议或服务条款限制应当遵守。涉及账号密码、支付信息等敏感数据时不要在代码中硬编码密钥和口令。这里也顺便解释一下很多大厂网站上看到的复杂加密并不是为了“防爬”而是为了保护用户数据和接口安全。我们学习 JS 逆向更重要的是理解常见的加密算法和前端安全设计思路而不是把它当作绕过一切限制的万能钥匙。11. 总结与下一步学习路线这篇文章从 JS 逆向的基础概念讲起介绍了抓包定位、算法识别、Python 还原以及扣取 JS 函数的完整流程。核心内容包括Base64、MD5、SHA256 等常见摘要与编码算法的识别和还原。AES 对称加密的密钥、IV、填充模式概念以及 Python 端实现。RSA 非对称加密的公钥加载与分段加密。从浏览器中扣取 JS 加密函数并在 Python 中通过execjs或 Node.js 子进程调用的方法。常见报错的排查思路和算法特征判断口诀。下一步你可以按照下面的路线继续深入先熟悉 Node.js 的基本用法能独立运行一个 JS 文件。练习从简单网站中定位加密参数用 Python 重写。尝试扣取完整的 JS 文件并补全依赖。学习 AST 抽象语法树用于处理混淆代码。了解接口签名、时间戳防重放、行为验证码等进阶反爬手段。最后想说的是逆向这条路没有捷径唯有多练。每遇到一个新网站把它当作一道新的算法题先观察再定位最后动手实现。你会发现很多所谓的“魔改加密”底层依然是标准算法。只要掌握了底层逻辑剩下的都是耐心活。如果你觉得这篇文章对你有帮助可以收藏备用也欢迎在评论区交流你在逆向过程中遇到的坑。下一篇我会继续整理更复杂的混淆对抗与代码补环境实战感兴趣的话可以关注。