逆向分析:基于 JS 字节码的保护技术

更新日期: 2022-09-13阅读: 697标签: 技术

现在流行使用 JS 字节码对 JavaScript 源码进行保护。我怎么感觉技术也是轮回发展的呢?字节码与 JavaScript 源码的关系,就像汇编与 PE 或 ELF 一样,感觉又回到了 N 年前用 IDA+OD 做逆向的时代。但时代不同了,逆向的程序越来越大,工作量越来越大,需要更有效的自动化手段来降低逆向的工作量。当然,再自动的方法也只能是辅助工具,因为逆向就是个体力活。

我的思路:既然 V8 是 JavaScript 运行的宿主机,那么在 V8 中能看到 JavaScript 的一切行为,那我应该在 V8 中加入必要的监控手段来调试 JS 和字节码,我在 V8 中加入的监控分为以下两类:

(1) 监控每个 JS api,准确地说是所有的 ECMAScript API。

(2) V8中内置 API,这些 API 主要用于为 ECMAScript API 提供基础库,例如,读取本地时间等一些加解密常用的操作。

这 (2) 点非常用重要,如果不做这一点,我们只能看到字节码表面的信息,数据在字节码内部的流转或操作就捕捉不到了。

这和我一直在研究的 Chromium-powered Taint Tracking 是同样的技术,只是应用场景不同而已。我的最终目标是以 V8 为基础实现一个可监控的、有字节码断点功能的调试器,简单地说就是一个可以动态调试字节码的 OD。

这个目标的原理很简单,但工程量很大,过去的大半年里,我做了不少的逆向工作,在实际中逐渐完善,目前算基本完工了。通过字节码保护 JavaScript,再稍微多加一些无用的字节码(花指令)就可以达到人工难以分析的工作量。我觉得一定要用动态的方法,这样可以跳过大量的干扰指令,在字节码执行过程中去观察一些关键信息,这是我为什么要在 V8 中开发调试器的原因。

两个小例子

下面的代码很简单,hello world,明文代码,没加密。

000001D800293976 @    0 : 13 00             LdaConstant [0]
         000001D800293978 @    2 : c3                Star1 
         000001D800293979 @    3 : 19 fe f8          Mov <closure>, r2
         000001D80029397C @    6 : 65 59 01 f9 02    CallRuntime [DeclareGlobals], r1-r2
         000001D800293981 @   11 : 13 01             LdaConstant [1]
         000001D800293983 @   13 : 23 02 00          StaGlobal [2], [0]
         000001D800293986 @   16 : 13 03             LdaConstant [3]
         000001D800293988 @   18 : 23 04 02          StaGlobal [4], [2]
         000001D80029398B @   21 : 21 05 04          LdaGlobal [5], [4]
         000001D80029398E @   24 : c2                Star2 
         000001D80029398F @   25 : 2d f8 06 06       GetNamedProperty r2, [6], [6]
         000001D800293993 @   29 : c3                Star1 
         000001D800293994 @   30 : 21 02 09          LdaGlobal [2], [9]
         000001D800293997 @   33 : c1                Star3 
         000001D800293998 @   34 : 21 04 0b          LdaGlobal [4], [11]
         000001D80029399B @   37 : 39 f7 08          Add r3, [8]
         000001D80029399E @   40 : c1                Star3 
         000001D80029399F @   41 : 5e f9 f8 f7 0d    CallProperty1 r1, r2, r3, [13]
         000001D8002939A4 @   46 : c4                Star0 
         000001D8002939A5 @   47 : a9                Return 
Constant pool (size = 7)
000001D800293931: [FixedArray] in OldSpace
 - map: 0x01d800002229 <Map(FIXED_ARRAY_TYPE)>
 - length: 7
           0: 0x01d800293921 <FixedArray[2]>
           1: 0x01d8002938a5 <String[6]: #Hello >
           2: 0x01d800003fd5 <String[1]: #a>
           3: 0x01d8002938b9 <String[6]: #world!>
           4: 0x01d800003fe5 <String[1]: #b>
           5: 0x01d8000059b5 <String[7]: #console>
           6: 0x01d8002027a9 <String[3]: #log>
Handler Table (size = 0)
Source Position Table (size = 0)

代码末尾的常量池给我们提供了重要的参考信息,再配合字节码,我们可以确认这个程序是把两个字符串拼接并输出。

下面的代码是 JSFuck,也是 hello world,但没有任何信息可以帮助我们分析。

[generated bytecode for function:  (0x0269002938b1 <SharedFunctionInfo>)]
Bytecode length: 28229
Parameter count 1
Register count 22
Frame size 176
Bytecode age: 0
         00000269002947CA @    0 : 7b 00             CreateEmptyArrayLiteral [0]
         00000269002947CC @    2 : c0                Star4 
         00000269002947CD @    3 : 7b 02             CreateEmptyArrayLiteral [2]
         00000269002947CF @    5 : 55                ToBooleanLogicalNot 
         00000269002947D0 @    6 : bf                Star5 
         00000269002947D1 @    7 : 7b 03             CreateEmptyArrayLiteral [3]
         00000269002947D3 @    9 : 39 f5 01          Add r5, [1]
         00000269002947D6 @   12 : bf                Star5 
         00000269002947D7 @   13 : 7b 04             CreateEmptyArrayLiteral [4]
//......省略.............
Constant pool (size = 134)
0000026900294589: [FixedArray] in OldSpace
 - map: 0x026900002229 <Map(FIXED_ARRAY_TYPE)>
 - length: 134
           0: 0x026900293905 <ArrayBoilerplateDescription PACKED_SMI_ELEMENTS, 0x0269002938f9 <FixedArray[1]>>
           1: 0x02690029391d <ArrayBoilerplateDescription PACKED_SMI_ELEMENTS, 0x026900293911 <FixedArray[1]>>
           2: 0x026900293935 <ArrayBoilerplateDescription PACKED_SMI_ELEMENTS, 0x026900293929 <FixedArray[1]>>
           3: 0x02690029394d <ArrayBoilerplateDescription PACKED_SMI_ELEMENTS, 0x026900293941 <FixedArray[1]>>
           4: 0x026900293965 <ArrayBoilerplateDescription PACKED_SMI_ELEMENTS, 0x026900293959 <FixedArray[1]>>
           5: 0x02690029397d <ArrayBoilerplateDescription PACKED_SMI_ELEMENTS, 0x026900293971 <FixedArray[1]>>
//......省略.........

上面的代码中,我看不到常量池的内容,也看不到的 JS API,面对这样的逆向,我认为用 V8 做动态调试是一劳永逸的方法。

最后

JavaScript 会越来越复杂,这样的工具使逆向过程变得轻松多了,工欲善其事,必先利其器。但逆向依旧就是体力,该干的事是一点也没有少。

又是好几个月,我天天在啃字节码,做逆向,做 V8 逆向工具。

原文 https://zhuanlan.zhihu.com/p/563705199

链接: https://www.fly63.com/article/detial/12125

技术开发,如何与领导谈涨薪

归根结底,涨薪其实是达到自己价值与薪资的最佳匹配. 好比你就是一只股票,公司当然会选择那些估值远高于股指的股票. 所以唯有不断增长自己的价值,才会成为你在涨薪谈判中的重要筹码.

bt种子简介与magnet磁力介绍

BT下载相信老司机们都接触过,为什么BT种子会慢慢被磁链取而代之?它们都可以用于BT下载,除了文件和字符串这表面上的区别,背后的技术上又有何不同?

WebService的两种方式SOAP和REST,之间的区别与优缺点

SOAP用于在Web Service中把远程调用和返回封装成机器可读的格式化数据。REST形式上应该表述为客户端通过申请资源来实现状态的转换,在这个角度系统可以看成一台虚拟的状态机。

工作了四五年,感觉技术上依旧长进不大

技术精进是一个持续增长的过程,而非一朝一夕,即便你在最短时间的掌握了大量的技术点,如何不及时应用到实际问题中,也很容易被遗忘。有朋友会说,我平时也挺努力的,一直不间断的学习

在阿里做了五年技术主管,我有话想说

今天的文章,他将继续深入探讨这一话题,从管理的角度分享技术TL的核心职责,主要包括团队建设、团队管理、团队文化、沟通与辅导、招聘与解雇等,希望与大家共同探讨、交流。

你和阿里员工的技术水平到底差几个等级

根据近年数据,中国现有程序员500万左右,其中P1、P2数量占据了近100万,P8及以下程序员约有490万,P9及以上仅有10万。80后是企业的技术支柱,90后已开始逐步成为企业的中坚力量

程序员常逛的技术社区

技术的成长路上,少不了跟一些志同道合的人交流,阅读一些技术前辈们的经验分享。这一路走来,还是要感谢有技术社区的陪伴,让码字之余,在技术、以及技术以外,都有不少收获。

未来,哪些技术在前端开发的地位会越来越高?

过去的这段时间里,不论是互联网巨头还是初创企业,都纷纷进行了一波优化。渐趋理智的资本淘汰了一批不能适应市场的业务,而业务的紧缩也淘汰了一批不能适应市场的程序员。

合格PHP程序员应该掌握哪些技术?

除了能够完成基本的PHP业务开发,还能够解决大部分深入复杂的技术问题,并且可以独立设计完成中大型的系统设计和开发工作;自己能够独立hold深入某个技术方向,在这块比较专业

技术追求的误区[观点与思考]

认识的一个 10 人左右的团队,本来是用 PHP 的,这些年看到网上很多用 / 转 Go 的消息,于是团队里有不少人就焦虑了,希望找一个合适的切入时间,能够试一把 Go

点击更多...

内容以共享、参考、研究为目的,不存在任何商业目的。其版权属原作者所有,如有侵权或违规,请与小编联系!情况属实本人将予以删除!