AST自动化还原JS

The man fears losing already has lost

0x01 Common Strategies

还原数值常量

上一节混淆中对于数值常量的加密,是先随机生成一个key,再和value异或得到cipher,最后把原节点改成cipher异或key

还原思路:遍历BinaryExpression节点,取出left和right,判断其是否为NumericLiteral,接着调用path.evaluate方法来计算节点的值并替换原节点。path.evaluate返回的confident是一个布尔值,表示节点是否可以计算。

let visitor = {
    BinaryExpression(path){
        let left = path.node.left;
        let right = path.node.right;
        if(t.isNumericLiteral(left) && t.isNumericLiteral(right)){
            let {confident, value} = path.evaluate();
            confident && path.replaceWith(t.valueToNode(value));
        }
    }
}

还原eval加密

遍历CallExpression节点,判断callee.name是否为eval

eval的参数就是一个字符串,直接使用types组件的identifier让参数变成代码

eval的参数是个表达式,提取其arguments[0]并通过eval计算出来

最后替换整个CallExpression节点

上面的解密函数是js内置的atob,若解密函数自定义的,得扣进代码中

还原unicode和hex字符串加密

JS中字符串可以是unicode或hex形式,标识符可以用unicode表示

Unicode字符串加密

generator中传入一些配置即可还原

第二次parse和generator是为了把代码格式化

还原逗号表达式混淆

逗号表达式的还原要比混淆简单

逗号表达式在AST中为SequenceExpressionexpressions节点就是逗号运算符连接的每一个表达式,expressions数组中最后一个成员就是逗号表达式整体的返回结果

还原前

还原后(只贴test函数)

  • 遍历SequenceExpression节点,提取expressions数组

  • expressions数组最后一个成员,即逗号表达式整体的返回结果给finalExpression

  • path.getStatementParent获取最近的父节点,然后把expressions数组的其他成员插入到父节点前面

  • 最后用finalExpression替换原来的SequenceExpression

还原控制流平坦化混淆

  • 首先得找到分发器的赋值语句,这里简单地提取函数体中第一条语句,判断其是否为VariableDeclaration节点,若是则提取其第一个变量声明的init属性

  • 继续遍历SwitchStatements节点,获取其cases数组,根据上面获取的分发器数组dispenser的顺序去索引cases,将SwitchCase的内容放入states,最后一条continue语句记得去掉

  • 最后把FunctionDeclarationbody属性替换掉

Last updated