编辑
2025-05-30
jsVmp反编译
00
请注意,本文编写于 35 天前,最后修改于 10 天前,其中某些信息可能已经过时。

目录

🧱 控制流图节点结构转化
🔁 SSA 构建完整流程
代码控制流结构还原

🧱 控制流图节点结构转化

首先将原始的控制流图节点结构进行转换,方便后续进行分析:

原始节点格式如下:

js
const node = { astNode: astNode, // AST 语法节点 postNodes: [], // 后继节点在 nodeGraph 中的索引列表 index: nodeGraph.length, // 当前节点在 nodeGraph 中的位置 };

转换后的目标块结构如下:

js
{ index: 0, // 当前块编号(唯一) type: "direct" | "condBranch", // 块类型:顺序执行 or 条件分支 astNodes: [], // 所包含的 AST 节点(通常是一组语句) entryCond: null, // 如果是条件块,记录条件表达式(Babel AST 节点) directNext: null, // 顺序块的下一跳 index trueBranch: null, // 条件为 true 跳转的块 index(条件块专用) falseBranch: null, // 条件为 false 跳转的块 index(条件块专用) predecessors: [], // 所有前驱块 index 列表 successors: [], // 所有后继块 index 列表 }

使用如下步骤完成转化:

js
updatePreNodes(nodeGraph, enterNode.index); // 更新每个节点的前驱信息 processEmptyStatement(nodeGraph); // 去除无效或空语句节点 const { blocks, enterIndex } = buildControlFlowBlocks(nodeGraph, enterNode.index);
  • buildControlFlowBlocks 的核心作用是将一组顺序执行的语句合并到一个逻辑块(基本块),便于后续分析。

🔁 SSA 构建完整流程

主函数如下:

js
/** * 构建并拆解 SSA 的主函数 * @param {Array} blocks - 控制流图块列表(基本块) * @param {number} entry - 入口块索引(默认 0) */ function buildAndDestructSSA(blocks, entry = 0) { // Step 1: 计算支配关系 const Dom = computeDominators(blocks, entry); // 所有块的支配集合 const idoms = computeImmediateDoms(Dom, blocks); // 每个块的最近支配者(iDom) const domChildren = buildDomChildren(idoms); // 支配树子节点映射 // Step 2: 计算支配前沿(dominance frontier) const DF = computeDomFrontier(blocks, Dom); // Step 3: 收集变量的定义位置 const defSites = collectDefSites(blocks); // defSites: 变量 -> 块索引数组 // Step 4: 活跃性分析 computeFullLiveness(blocks); // 分析每个块中变量的 live-in/live-out // Step 5: 插入 φ 函数 insertPhi(blocks, DF, defSites); // 在 DF 上插入 φ 节点 // Step 6: 进行 SSA 重命名(核心:唯一化变量版本) renameSSA(blocks, entry, domChildren); // Step 7: 拆解 φ 函数为真实赋值 const idomsForDestruct = idoms; destructPhiAndInsertBlocks({ blocks }, idomsForDestruct); }

代码控制流结构还原

js
/** * 从 CFG 结构化生成 AST,支持 while + break/continue */ function decompileCFGWithLoops(blocks, enter, loops) { const postDom = computePostDominators(blocks); function findNearestCommonPostDominator(n, m) { const sN = postDom[n], sM = postDom[m]; const common = [...sN].filter(x => sM.has(x)); if (!common.length) return null; common.sort((a, b) => postDom[b].size - postDom[a].size); return common[0]; } const loopsByHead = new Map(loops.map(l => [l.head, l])); function region(cur, stop, currentLoop) { const stmts = []; while (cur != null && cur !== stop) { const blk = blocks[cur]; // 循环头 if (loopsByHead.has(cur) && (!currentLoop || currentLoop.head !== cur)) { const loop = loopsByHead.get(cur); const bodyStmts = region(loop.head, loop.exit, loop); stmts.push(t.whileStatement(t.booleanLiteral(true), wrapNodesInBlockStatement(bodyStmts))); cur = loop.exit; continue; } // 条件分支 if (blk.type === 'condBranch') { const [t0, f0] = blk.successors; const merge = findNearestCommonPostDominator(t0, f0); const thenStmts = []; const elseStmts = []; // 处理 then 分支 if (currentLoop) { if (t0 === currentLoop.exit) { thenStmts.push(t.breakStatement()); } else if (t0 === currentLoop.head) { thenStmts.push(t.continueStatement()); } else { thenStmts.push(...region(t0, merge, currentLoop)); } } else { thenStmts.push(...region(t0, merge, currentLoop)); } // 处理 else 分支 if (currentLoop) { if (f0 === currentLoop.exit) { elseStmts.push(t.breakStatement()); } else if (f0 === currentLoop.head) { elseStmts.push(t.continueStatement()); } else { elseStmts.push(...region(f0, merge, currentLoop)); } } else { elseStmts.push(...region(f0, merge, currentLoop)); } // 生成 if stmts.push(...blk.astNodes); stmts.push( t.ifStatement( blk.entryCond, wrapNodesInBlockStatement(thenStmts), elseStmts.length ? wrapNodesInBlockStatement(elseStmts) : null ) ); cur = merge; continue; } // 直接跳转 stmts.push(...blk.astNodes); const nxt = blk.directNext; if (currentLoop) { if (nxt === currentLoop.exit) { stmts.push(t.breakStatement()); return stmts; } if (nxt === currentLoop.head) { stmts.push(t.continueStatement()); return stmts; } } cur = nxt; continue; } return stmts; } return region(enter, null, null); }

可以看到反编译的代码都变成了静态单赋值形式,接下来要进行中间变量的合并,以消除代码行数,以及可以对代码进行的各种优化

image.png

后续更新啦

本文作者:韦峰

本文链接:

版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!