作用域与闭包——压缩代码阅读基础
本节目标
- 搞懂「作用域」为什么决定你能不能读到某个变量
- 理解「闭包」在压缩代码里长什么样
- 能识别压缩后的
_0xabc变量与闭包结构
压缩工具常把变量名改成 _0x1a2b 这种短名,但作用域规则不变。读懂作用域,才能在乱码里找到「加密函数到底用了哪些外部变量」。
// 运行环境:Node.js 16+(无需依赖)
// 运行:node jsr-l4.cjs
// 演示闭包:函数「记住」了它定义时所在的外部变量
function makeCounter() {
let count = 0 // 这个变量被「包」在返回的函数里
return function () {
count += 1
return count
}
}
const next = makeCounter()
console.log(next()) // 1
console.log(next()) // 2 (count 被闭包保留,没被回收)
// 压缩后可能长这样(等价,只是名字短了):
function a(){let b=0;return function(){return b+=1}}闭包在逆向里的意义:很多混淆会把「密钥」「配置」藏在闭包里,函数外部读不到,只能通过「调用这个返回的函数」间接使用。你下断点看闭包变量的值,就能拿到藏起来的密钥。
名词解释
作用域(Scope):「变量在哪些地方能被访问」的规则。JS 有全局作用域、函数作用域、块级作用域。压缩代码名字虽乱,但「哪个变量在哪个花括号里可见」这套规则不变。读懂作用域,你才知道某个 _0xabc 是从哪来的。
闭包(Closure):函数「记住了它诞生时周围的环境(变量)」,即使那个环境已经「执行完」了,函数仍能访问那些变量。它像函数随身带的一个小背包,里面装着定义时周边的变量。逆向里常用来「藏密钥」——你只能调函数,看不到包里的密钥,但断点能让你偷看背包。
课后练习
练习 1:下面 getKey 为什么能一直返回同一个 _key 的值?
function box(){ const _key='SECRET'; return { getKey(){ return _key } } }答案:
getKey是定义在box内部的闭包,它「记住」了外部的_key。_key外部访问不到,但通过getKey能间接读出来——这就是闭包「私有变量」的效果。
练习 2:压缩代码里 var _0x1a=require('crypto') 之后,为什么还能在别的函数里用 _0x1a?
答案:如果
_0x1a定义在「这些函数共同的外层作用域」,它们就能共享它(闭包/外层作用域可见)。反之若定义在某函数内部,则只有该函数内可见。判断是否共享,看声明语句的「花括号层级」。
总结
作用域和闭包是「读得懂压缩代码」的地基。我的观点:新手看到 _0x1a2b 满屏飞就慌,其实名字丑不影响结构美——作用域怎么嵌套、闭包包了什么,压缩工具一概不动。我读压缩代码时根本不关心变量叫什么,只在乎「这个函数在哪个作用域、它闭包捕获了哪些外部变量」。一个小技巧:在 DevTools 里把压缩文件「美化格式化(Pretty Print)」一下,花括号层级立刻清晰,再配合断点看闭包里的真实值,乱码瞬间变 readable。别和变量名较劲,和结构较劲。