AST实战|遇到混淆的代码怎么下手(初级混淆)

举报
悦来客栈的老板 发表于 2021/12/11 23:46:03 2021/12/11
【摘要】 看到群里的徐大哥被 webpack 折磨许久,写下此混淆还原入门篇,仅供学习参考。 实战地址: https://match.yuanrenxue.com/match/16 怎么分析就不讲了,直接将 732 这个函数抠下来,去头去尾,形成一个AST可解析的完整代码。 格式化后的代码如下: 通过分析代码,可以发现,有很多...

看到群里的徐大哥被 webpack 折磨许久,写下此混淆还原入门篇,仅供学习参考。

实战地址:

https://match.yuanrenxue.com/match/16
  

怎么分析就不讲了,直接将 732 这个函数抠下来,去头去尾,形成一个AST可解析的完整代码。

格式化后的代码如下:

1b3baa59de232a19fee347344fc9ab40.png

通过分析代码,可以发现,有很多函数调用的地方:

1a732d5be84c85fc3e8b5ca5c70e0d6a.png

以及:

fdc3ffc53e5363f32553730aa24bd468.png

它们有一个很明显的特征,就是实参都是字面量。那就好办啊,直接把它们全部都计算出来,然后再把计算出来的值全部替换这些函数调用表达式,不就更直观了么。

混淆还原的通用法则一:实参全部为字面量的函数可还原

下面就是写插件了,仔细观察后发现,这些 e, t, o,u 函数名,都是重复赋值,其实都是 l 函数:

b7a296451ca55648d1a4bd766d955977.png

因此,我们把 l 函数 及其环境都扣取出来,浏览器运行后发现卡死,肯定有代码展开检测,那就压缩代码吧。使用之前文章 工具推荐|一款爬虫界的神兵利器,值得拥有   介绍的压缩工具,效果还挺好:

var r,o,a,s;_0x34e7=["AqLWq","0zyxwvutsr","TKgNw","eMnqD","thjIz","btoa","MNPQRSTWXY","oPsqh","niIlq","evetF","LVZVH","fYWEX","kmnprstwxy","aYkvo","tsrqpomnlk","HfLqY","aQCDK","lGBLj","test","3210zyxwvu","QWK2Fi",'return /" ',"hsJtK","jdwcO","SlFsj","OWUOc","LCaAn","[^ ]+)+)+[","FAVYf","2Fi+987654","floor","join","EuwBW","OXYrZ","charCodeAt","SkkHG","iYuJr","GwoYF","kPdGe","cVCcp","INQRH","INVALID_CH","charAt","push","apply","lalCJ","kTcRS",'+ this + "',"ykpOn","gLnjm","gmBaq","kukBH","dvEWE","SFKLi","^([^ ]+( +","qpomnlkjih","^ ]}","pHtmC","length","split","ABHICESQWK","FKByN","U987654321","lmHcG","dICfr","Szksx","Bgrij","iwnNJ","jihgfdecba","GfTek","gfdecbaZXY","constructo","QIoXW","jLRMs"],a=_0x34e7,s=function(e){for(;--e;){a.push(a.shift())}},(o=(r={data:{key:"cookie",value:"timeout"},setCookie:function(e,t,n,r){r=r||{};for(var i=t+"="+n,o=0,a=e.length;o<a;o++){var s=e[o];i+="; "+s;var l=e[s];e.push(l),a=e.length,!0!==l&&(i+="="+l)}r.cookie=i},removeCookie:function(){return"dev"},getCookie:function(e,t){var n,r=(e=e||function(e){return e})(new RegExp("(?:^|; )"+t.replace(/([.$?*|{}()[]\/+^])/g,"$1")+"=([^;]*)"));return n=133,s(++n),r?decodeURIComponent(r[1]):void 0},updateCookie:function(){return new RegExp("\\w+ *\\(\\) *{\\w+ *['|\"].+['|\"];? *}").test(r.removeCookie.toString())}}).updateCookie())?o?r.getCookie(null,"counter"):r.removeCookie():r.setCookie(["*"],"counter",1);var l=function(e,t){return _0x34e7[e-=188]};
  

浏览器再次运行,可以得到正确的结果:

3d0265436f88a8ab798eeaec2d3e5733.png

再就是编写插件了,这里我们遍历函数表达式,代码如下:


   
  1. const callToLiteral=
  2. {
  3. CallExpression(path)
  4. {
  5. }
  6. }

然后把上面的环境代码补到插件上面,使它可以进行调用。

再就是在线解析(有个小坑,自己解决),对照分析:

a412b203f7097522f8ecc986e1e4c6b1.png

写出判断规则:


   
  1. const callToLiteral=
  2. {
  3. CallExpression(path)
  4. {
  5. let {callee,arguments} = path.node;
  6. if (!types.isIdentifier(callee) || arguments.length != 1)
  7. {
  8. return;
  9. }
  10. let name = callee.name;
  11. if (!['e','t','o','u'].includes(name) || !types.isNumericLiteral(arguments[0]))
  12. {
  13. return 0;
  14. }
  15. }
  16. }

判断后,计算出结果,并进行替换:


   
  1. const callToLiteral=
  2. {
  3. CallExpression(path)
  4. {
  5. let {callee,arguments} = path.node;
  6. if (!types.isIdentifier(callee) || arguments.length != 1)
  7. {
  8. return;
  9. }
  10. let name = callee.name;
  11. if (!['e','t','o','u'].includes(name) || !types.isNumericLiteral(arguments[0]))
  12. {
  13. return 0;
  14. }
  15. let value = l(arguments[0].value);
  16. path.replaceWith(types.valueToNode(value));
  17. }
  18. }

这样,一个简单的替换插件就完成了,这只是插件,还要记得调用哈。

traverse(ast, callToLiteral);
  

还原后,在混淆代码中可以把 l函数 及其环境删除,因为代码中没有地方再用到它了。

部分还原后的代码截图,效果如下:

daaabf18501961c22c3a1d593cd9c686.png

可以看到,这比之前的代码清爽多了。代码当然还是可以继续还原的,在这里不多讲。感谢阅读。

插件代码已发在星球,需要学习的自取:

https://t.zsxq.com/jiMb23J
  

文章来源: blog.csdn.net,作者:悦来客栈的老板,版权归原作者所有,如需转载,请联系作者。

原文链接:blog.csdn.net/qq523176585/article/details/121847865

【版权声明】本文为华为云社区用户转载文章,如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。