AST 技巧:还在手动扣代码?AST技术帮你自动扣代码
大家好,今天来分享一种借助 AST 自动扣代码的方案。
扣代码
扣代码是什么我就不介绍了吧,大家应该都知道了。扣代码这种方法比较繁琐,而且像那种强混淆的代码,函数超级多,一个一个的复制要花费很多时间,属实是有点坐不住。
在晚上睡觉的时候,突然就想到一种方法,最近不是一直在写 AST 的文章吗,就想到了是不是可以使用 AST 来扣代码呢?想了一下发现还真可以,今天就给大家来分享一下操作方法。
AST自动扣代码思路
为了使用 AST 来自动扣代码,要先知道手动的流程是什么样子的。
- 先复制好需要扣的函数
- 然后找到函数定义,复制进来
- 查找未定义的函数,或者运行看报错,缺什么补什么
- 重复第二步
现在有了手动扣代码的流程,要使上面的流程自动化,就要知道每一步应该如何使用代码来实现。
- 第一步直接手动实现,就一行代码直接复制
- 找到函数定义,这个操作可以使用
scope
的getBinding
方法,根据函数名获取binding
,然后获取path
属性即可获取函数定义 - 查找未定义的函数,一样是使用
getBinding
方法,如果binding
是undefined
,则说明该函数没有找到函数定义 - 使用循环重复上述步骤
代码实现
思路已经有了,现在就差代码实现了。为了能使用 getBinding
方法获取函数定义,就要先解析一下原始的文件,因为它里面有所有的函数的定义,还要解析一下需要扣代码的文件,然后使用 getBinding
方法获取所有的函数调用,并且判断当前的函数是否有对应的函数定义。
1 | let astAll = parser.parse(fs.readFileSync('encode.js', {encoding: "utf-8"})); |
首先是获取所有的函数定义,并且将函数名和对应的函数定义放到 map
中,供后续使用。这里需要注意,如果有重复的函数名要记得处理,要看同名函数的定义是不是一样的,如果是一样的可以直接忽略,如果不一样那就要看具体函数的作用域了,可以先考虑使用 AST 重命名一下不同作用域的函数,尽量防止重名。
1 | //用于保存找到的函数定义,添加到输出结果中 |
然后就是获取所有没有函数定义的函数名,使用 getBinding
方法判断是否有函数定义,如果没有定义则判断是否可以在上述 map
中找到对应的函数名,如果可以找到的,删除 map
中对应的函数名,每个函数只能被扣一次,并且将函数定义添加到 list
中,后续将其添加到输出文件中。
为什么这里使用 list
来保存所有的函数定义呢?因为函数定义要添加到程序的最外层才可以,要遍历 Program
节点,如果每次都遍历会很低效,所以先保存起来最后再统一添加。
还有为什么在 push
的时候要解析代码呢?这里是我卡了一会的地方,刚开始想要使用 template
的方式解析字符串形式的函数定义,后来发现一直报错不能用,因为这里涉及到要在两个 AST 语法树之间搜索并添加代码,重新解析代码才不会报错。
1 | //将找到的依赖函数添加到文件中 |
将上面找到的所有函数定义添加到 Program
节点中,添加到节点的最开始的位置。别忘了最后一行,将 AST 重新解析一下,因为添加函数定义后,有的函数就可以找出对应的函数定义了,重新解析相当于重新生成 AST 语法树,更新 getBinding
的返回值。
完整代码
1 | let sourceCode = fs.readFileSync('encode_ok.js', {encoding: "utf-8"}); |
完整代码如上,一些导入函数和文件写入方法大家自行补充哈。
来看效果:
这是需要扣代码的文件,名字为 encode_ok.js
,看到里面有很多函数是找不到函数定义的状态,pycharm 有波浪线提示。
这是包含所有函数定义的 encode.js
。
最终效果如上图所示,现在代码的行数来到了 6000 多行,可见扣了很多函数定义过来,并且刚刚的波浪线提示也没有了,说明对应的函数现在都可以找到函数定义了。
有待完善
好了,现在演示完了,我实测了一下效果还可以,基本上补充一下变量的定义就可以正常的跑了,不用自己一个一个函数自己扣了。
不过现在的方案还是相对比较简单,不能扣变量定义,只能扣函数定义,并且只是简单的扣过来的函数放到全局,还是需要人工介入将变量定义补充完整才可以正常跑。有的时候遇到内部函数或者需要按顺序初始化的变量或者函数,会有问题,上述代码还不能自动处理。
如果有特殊需求或者有能力的小伙伴可以根据上述代码自行更改,让它可以变得更通用或者更适合你。
当然了,没有什么方案是可以处理所有情况的,越通用的方案越复杂,越容易出 bug,简单的方案虽然不能处理所有情况,但是可以自动扣几千行的函数也可以节省大量的时间了,有这时间可以多睡会哈哈哈。
总结
我再次强调,现在的方案还仅仅是初级阶段,代码很简单,处理方式也很简单,还有很多需要完善的地方,不推荐直接用到生产环境,仅供参考测试。
本文章首发于个人博客 LLLibra146’s blog
本文作者:LLLibra146
更多文章请关注公众号 (LLLibra146):
版权声明:本博客所有文章除特别声明外,均采用 © BY-NC-ND 许可协议。非商用转载请注明出处!严禁商业转载!