createGraphNode
- 图节点创建核心javascriptfunction createGraphNode(nodeGraph, astNode, fromPc, type) {
const node = {
astNode: astNode, // 关联的AST语法节点
fromPc, // 对应的程序计数器位置
postNodes: [], // 后继节点索引数组
index: nodeGraph.length, // 当前节点在nodeGraph中的索引
type // 节点类型("direct"顺序/"jump"跳转等)
};
nodeGraph.push(node);
return node;
}
目的:为每条语句或表达式创建图节点,承载控制流信息
getBlockAst
- 构建入口及整体流程javascriptfunction getBlockAst(startPc, noProxystack, blockEndPc, stackTop) {
const nodeGraph = []; // 存储语句节点
const pcNodeMap = new Map(); // 记录PC对应的图节点
const enterNode = createGraphNode(nodeGraph, t.emptyStatement(), startPc);
// 递归构建控制流图
traverseLinkNode(startPc, noProxystack, enterNode, stackTop);
//后续进行代码结构的还原
const blockAst = convertBlockAst(nodeGraph, enterNode);
return blockAst;
}
参数说明:
startPc
:字节码起始地址noProxystack
:操作栈blockEndPc
:代码块结束地址stackTop
:当前栈顶指针位置traverseLinkNode
- 递归构建代码:
javascript function traverseLinkNode(pc, noProxystack, preNode, stackTop) {
//每个pc的访问都需要创建一个空节点,用做节点的连接。
const firstNode = createGraphNode(nodeGraph, t.emptyStatement(), pc);
//把空语句节点连接前节点的后驱,并让前节点引用指向当前节点
preNode.postNodes.push(firstNode.index);
preNode = firstNode;
if (pc >= blockEndPc) {
return;
}
var startPc = pc;
var breakFlag = false;
function handlerForAstList() {
}
function createProxyStack(noProxystack) {
return new Proxy(noProxystack, handlerForStack());
}
function handlerForStack() {
}
const proxyStack = createProxyStack(noProxystack);
var astList = [];
const proxyAstList = new Proxy(astList, handlerForAstList());
const visNode = pcNodeMap.get(pc);
if (visNode) {
preNode.postNodes.push(visNode.index);
return;
}
pc = excuteInstruction(pc, proxyStack, proxyAstList);
pcNodeMap.set(startPc, firstNode);
if (breakFlag || pc >= blockEndPc) {
return;
}
traverseLinkNode(pc, noProxystack, preNode, blockEndPc, stackTop);
function excuteInstruction(pc, valueStack, astList) {
}
const blockAst = convertBlockAst(nodeGraph, enterNode, fucCount);//后续进行控制流还原
return blockAst;
}
javascriptconst firstNode = createGraphNode(nodeGraph, t.emptyStatement(), pc);
preNode.postNodes.push(firstNode.index);
preNode = firstNode;
作用:每次递归入口创建空节点
javascriptif (pc >= blockEndPc) {
return;
}
作用:超过块范围时停止递归
javascriptconst proxyAstList = new Proxy(astList, {
get(target, prop, receiver) {
if (prop === 'push') {
return (astNode, type) => {
const cloneAstNode = t.cloneNode(astNode);
const node = createGraphNode(nodeGraph, cloneAstNode, startPc, type || "direct");
preNode.postNodes.push(node.index);
preNode = node;
if (t.isReturnStatement(astNode) || t.isThrowStatement(astNode)) {
breakFlag = true; // 遇到终止语句停止递归
}
return node;
};
}
return Reflect.get(target, prop, receiver);
}
});
功能:
javascriptconst proxyStack = new Proxy(noProxystack, {
set(target, prop, value, receiver) {
if (!isNaN(prop)) {
if (t.isNode(value)) {
const id = t.identifier(`temp${prop}`);
const assignStmt = t.variableDeclaration("let", [
t.variableDeclarator(id, value)
]);
const node = proxyAstList.push(assignStmt);
value = id;
}
return Reflect.set(target, prop, value, receiver);
}
}
});
功能:执行栈写入AST节点时自动生成临时变量声明语句
javascriptconst visNode = pcNodeMap.get(pc);
if (visNode) {
preNode.postNodes.push(visNode.index);
return;
}
作用:访问已处理节点时直接连接,防止重复生成和死循环
excuteInstruction
- 指令执行模拟,每个网站的vmp不同的部分javascriptfunction excuteInstruction(pc, valueStack, astList) {
const instructionCode = instruction[pc++];
switch (instructionCode) {
// 算术指令示例
case 0:
// 原:
// valueStack[stackTop] = valueStack[stackTop] + valueStack[stackTop - 1];
// 实际AST构造
valueStack[stackTop] = t.binaryExpression(
"+",
valueStack[stackTop],
valueStack[stackTop - 1]
);
break;
// 条件跳转指令示例
case 1:
//原:
// const gotoValue = instruction[pc++];
// valueStack[stackTop] && pc + gotoValue;
// break;
//实际处理:
const gotoValue = instruction[pc++];
const test = valueStack[stackTop];
proxyAstList.push(test); // 自动创建条件节点
// 克隆当前栈状态
const ifTrueStack = shallowCloneArray(noProxystack);
const ifFalseStack = shallowCloneArray(noProxystack);
// 递归处理两个分支
traverseLinkNode(pc + gotoValue, ifTrueStack, preNode, stackTop);
traverseLinkNode(pc, ifFalseStack, preNode, stackTop);
breakFlag = true;
break;
}
return pc;
}
功能:
javascriptconst blockAst = convertBlockAst(nodeGraph, enterNode);
作用:将语句图交给后续的还原模块,生成结构化代码块。
可以看到每一个代码语句或表达式都是图中的一个节点,并且正确构建了连接关系
本文作者:韦峰
本文链接:
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!