李恒道 发表于 2023-9-20 15:26:56

ast解密of及Container is falsy解决

观察结构发现有多层字符串解密赋值
![图片.png](data/attachment/forum/202309/20/151919iqiy039o00zqj03n.png)
其本质多层调用函数,我们可以对CallExpression做解析
判断为字符串和数字的结构下,就通过scope找到原函数
判断函数是否是一个return,然后调用了函数继续传入
如果符合条件就进行拍平
直接上代码
```js
traverse(ast, {
    CallExpression(path) {
      if (path.node.arguments.length === 2) {
            const type0 = path.node.arguments.type
            const type1 = path.node.arguments.type
            const isLikelyNumber=(type)=>{
                return type==='UnaryExpression'||type==='NumericLiteral'
            }
            if ((type0 === 'StringLiteral' && isLikelyNumber(type1)) || (type1 === 'StringLiteral' && isLikelyNumber(type0))) {
                const funcBinding = path.scope.getBinding(path.node.callee.name)
                const funcNode = funcBinding.path.node
                if (funcNode.params.length !== 2) {
                  return
                }
                if (funcNode.body.body.length !== 1) {
                  return
                }
                if (funcNode.body.body.type !== 'ReturnStatement') {
                  return
                }
                const funcArgs0 = funcNode.params.name
                const funcArgs1 = funcNode.params.name
                const bodyCallArgs = funcNode.body.body.argument.arguments
                let isSwap = false
                for (let index = 0; index < bodyCallArgs.length; index++) {
                  const item = bodyCallArgs;
                  if (item.type === 'Identifier') {

                        if (item.name === funcArgs0 && index === 1) {
                            isSwap = true
                        } else if (item.name === funcArgs1 && index === 0) {
                            isSwap = true
                        }
                        break;
                  }
                }
                const handleExpression = (bodyExpress, argsIdentifier) => {
                  if (bodyExpress.type !== 'BinaryExpression') {
                        return argsIdentifier
                  }
                  const handleIdentifier = (item) => {
                        if (item.type !== 'Identifier') {
                            return item
                        } else {
                            return argsIdentifier
                        }
                  }
                  const numAst = types.binaryExpression(bodyExpress.operator, handleIdentifier(bodyExpress.left), handleIdentifier(bodyExpress.right))
                  const numResult = eval(generator(numAst).code)
                  return types.numericLiteral(numResult)
                }
                const firstIdentifier = path.node.arguments
                const secondIdentifier = path.node.arguments
                let newCalleeArgs = , isSwap ? secondIdentifier : firstIdentifier), handleExpression(bodyCallArgs, isSwap ? firstIdentifier : secondIdentifier)]
                let newNode = types.callExpression(funcNode.body.body.argument.callee, newCalleeArgs);
                path.replaceInline(newNode)
            }
      }
    },
});
```
这样变成了对字符串解密函数的直接引用
![图片.png](data/attachment/forum/202309/20/152108b0j67jbpbkakbzzc.png)
我们直接对字符串解密进行处理
crackCharFunc直接对抄网页中的解密函数做抽取即可
```js
traverse(ast, {
    CallExpression(path) {
      if (path.node.arguments.length === 2) {
            if (path.node.callee.name !== 'e') {
                return
            }
            if (path.node.arguments.type !== 'NumericLiteral') {
                return;
            }
            if (path.node.arguments.type !== 'StringLiteral') {
                return;
            }
            const nodeResult = crackCharFunc(path.node.arguments.value, path.node.arguments.value)
            path.replaceInline(types.stringLiteral(nodeResult))
      }
    },
});
```
这个时候还存在花指令混淆
![图片.png](data/attachment/forum/202309/20/152206ugchtujlg2lgzngg.png)
判断CallExpression和MemberExpression函数,在符合的条件下重写花指令,去除花指令的函数调用
```js
const handleObfs = {
    CallExpression: {
      exit(outerPath) {
            const node = outerPath.node.callee
            const parentPath = outerPath
            if (node?.object?.type === 'Identifier' && node?.property?.type === 'StringLiteral') {
                const objBinding = outerPath.scope.getBinding(node.object.name)
                if (objBinding === undefined) {
                  return;
                }
                const objNode = objBinding.path.node
                const funcList = objNode.init?.properties ?? []
                const funcInstance = funcList.find((item) => {
                  const keyName = item.key.name
                  return keyName === node.property.value
                })
                if (funcInstance) {
                  const parentNode = parentPath.node

                  let replaceAst = null
                  if (funcInstance.value.type === 'FunctionExpression') {
                        const originNode = funcInstance.value.body.body.argument
                        //函数
                        if (originNode.type === 'CallExpression') {
                            replaceAst = types.callExpression(parentNode.arguments, [...parentNode.arguments].splice(1))
                        } else if (originNode.type === 'BinaryExpression') {
                            replaceAst = types.binaryExpression(originNode.operator, parentNode.arguments, parentNode.arguments)
                        }
                  } else {
                        //字符串
                        debugger
                        replaceAst = types.stringLiteral(funcInstance.value.value)
                  }
                  if (replaceAst) {
                        parentPath.replaceWith(replaceAst)


                  }

                }
            }
      }
    },
    MemberExpression: {
      enter(path) {
            const node = path.node
            if (node?.object?.type === 'Identifier' && node?.property?.type === 'StringLiteral') {
                const objBinding = path.scope.getBinding(node.object.name)
                if (objBinding === undefined) {
                  return;
                }
                const objNode = objBinding.path.node
                const funcList = objNode.init?.properties ?? []
                const funcInstance = funcList.find((item) => {
                  const keyName = item.key.name
                  return keyName === node.property.value
                })
                if (funcInstance) {
                  let replaceAst = null
                  if (funcInstance.value.type === 'StringLiteral') {
                        replaceAst = types.stringLiteral(funcInstance.value.value)
                  }
                  if (replaceAst) {
                        path.replaceWith(replaceAst)
                  }

                }
            }
      }
    }
}

traverse(ast, handleObfs);
```
一开始我遍历MemberExpress对象触发了Container is falsy
因为`n["Pvaqs"](n["Pvaqs"])`需要先遍历里层后遍历外层
所以需要exit,但是如果使用MemberExpress,此时里层不存在层级
所以会直接替换,然后内层的再被遍历时已经丧失了上下文
所以一定要注意遍历的节点,确保需要遍历的节点一定保持正确的前后顺序
为了让里面的节点被先遍历
我将MemberExpress改成了CallExpression来进行递归函数的解析,往上挪了一层
此时因为有内层节点,可以触发exit先遍历内层后遍历外层

这时就得到了正确解密的内容
![图片.png](data/attachment/forum/202309/20/152633de2mlmfne2o02pla.png)

王一之 发表于 2023-9-20 16:29:11

太强了,看不懂,根本看不懂

steven026 发表于 2023-9-20 18:16:31

太强了,看不懂,根本看不懂

李恒道 发表于 2023-9-20 21:29:15

王一之 发表于 2023-9-20 16:29
太强了,看不懂,根本看不懂

哥哥快学babel!

李恒道 发表于 2023-9-20 21:29:22

steven026 发表于 2023-9-20 18:16
太强了,看不懂,根本看不懂

禁止复制粘贴!
页: [1]
查看完整版本: ast解密of及Container is falsy解决