云裂变营销网

标题: 小程序不让用 JS 解释器?那我再肛一次鹅厂 [打印本页]

作者: 匿名    时间: 2022-7-15 21:42
标题: 小程序不让用 JS 解释器?那我再肛一次鹅厂
前言

6月23号的时候,微信团队发了如下通知将禁止小程序使用 JavaScript 解释来动态更新代码。消息一出,小程序开发者们哀嚎哀嚎遍野,更有人声称要开始加班改代码了。
为进一步提升小程序的安全性和用户体验,目前平台对提审的小程序均需进行安全检测,在检测过程中,发现有小程序采用内置 JavaScript 解释器(如eval5、estime、evil-eval等)的方式,动态执行JS代码、对小程序wxml代码进行热更新。对于使用解释器的小程序,平台将自2022年7月6日开始在代码审核环节进行驳回,请各位开发者于7月6日前完成自查、修复。
—— 摘自 关于禁止小程序JavaScript解释器使用规范要求 | 微信开放社区
自 2018年1月,我写下《brambles:微信小程序也要强行热更代码,鹅厂不服你来肛我呀》这篇文章开始,就带起来在小程序里面用 JavaScript 解释器的潮流。然而四年过去了,微信小程序终于明文规定不在让用 JavaScript 解释器了,那小程序热更的时代是不是就过去了?

小程序不让用 JS 解释器?那我再肛一次鹅厂-1.jpg

大人,时代变了?不!没变!

当然不是,如果就这样过去了也就没我这篇文章了,其实早在四年前写前一篇文章的时候我就已经想好解决方案,只是我是没想到的一年以后微信小程序才开始封 JavaScript 解释器让我之前设想的方案一直拖到四年后的今天。n那么今天我们这篇文章主要就讨论两个点:
基本步骤 & 最终效果

示例代码 Github 仓库:https://github.com/bramblex/jsjs-vm-demo

小程序不让用 JS 解释器?那我再肛一次鹅厂-2.jpg

在小程序中加载一张藏着字节码的图片,并执行图片上隐藏的代码

写个编译器将 JavaScript 编译成字节码 & 实现一个字节码虚拟机

为什么我们要将 JavaScript 编译成字节码呢?我们的目的是为了绕过微信小程序的代码审核限制,所以我们要想尽办法隐藏两样东西。第一个是要想办法隐藏解释器看,因为一个完整的 JavaScript 解释器代码量非常庞大,并且往往都需要引入别人写的库没办法自己维护,这样的解释往都不需要用什么高深的技术手,字符串一匹配就能查出来个七七八八。
比如在小程序一个完整可用的 JavaScript 解释器引入代码,起码需要引入一个至少 100k 以上的代码,这个目标实在是太大了,几乎很难隐藏。但是在小程序里面引入一个可以执行字节码的虚拟机实现,可以做到只引入 10k 左右,压缩前总代码量不超过千行的代码,这样就更容易隐藏动态代码的实现。比如我目前实现的字节码虚拟机,除了 trycatch 和 with 以外,能实现 es5 所有能力的虚拟机总共才 7k 大小,就这都是还可以再压缩的。
第二点是我们需要隐藏热更的 JavaScript 代码不被微信发现,比如你把热更的大量 JavaScript 代码通过接口明文传输,只要微信稍微拦截一下你的网络,这不就全露馅了吗?所以将代码编译成了二进制的字节码以后,微信就没有办法通过简单的拦截你的接口请求来确定里面有没有 JavaScript 代码来判断你是是否热更代码了。二进制的文件在你能够明确清楚它的个格式之前是没办法准确接出来他到底是个什么东西的1,更何况二进制的加密混淆的算法满大街都是,而且还都没有几行……
下面是我实现字节码的指令集,总共只有 50 多个指令:
  1. export enum OpCode {
  2.   NOP = 0x00,
  3.   UNDEF = 0x01, NULL = 0x02, OBJ = 0x03, ARR = 0x04, TRUE = 0x05,
  4.   FALSE = 0x06, NUM = 0x07, ADDR = 0x08, STR = 0x09, POP = 0x0A,
  5.   TOP = 0x0D, TOP2 = 0x0E, VAR = 0x10, LOAD = 0x11, OUT = 0x12,
  6.   JUMP = 0x20, JUMPIF = 0x21, JUMPNOT = 0x22, FUNC = 0x30, CALL = 0x31,
  7.   NEW = 0x32, RET = 0x33, GET = 0x40, SET = 0x41, IN = 0x43,
  8.   DELETE = 0x44, EQ = 0x50, NEQ = 0x51, SEQ = 0x52, SNEQ = 0x53,
  9.   LT = 0x54, LTE = 0x55, GT = 0x56, GTE = 0x57, ADD = 0x60,
  10.   SUB = 0x61, MUL = 0x62, EXP = 0x63, DIV = 0x64, MOD = 0x65,
  11.   BNOT = 0x70, BOR = 0x71, BXOR = 0x72, BAND = 0x73, LSHIFT = 0x73,
  12.   RSHIFT = 0x75, URSHIFT = 0x76, OR = 0x80, AND = 0x81, NOT = 0x82,
  13.   INSOF = 0x90, TYPEOF = 0x91,
  14. }
复制代码
以下是将一段示例代码以及其编译后的字节码:

小程序不让用 JS 解释器?那我再肛一次鹅厂-3.jpg

JavaScript 代码与编译后的字节码,这个字节码中还能看到 wx showModal 等字样

上面字节码是以下指令(节选)的二进制表示:
  1. .main_1:
  2.         STR(09)
  3.         "wx" (00 77 00 78 00 00)
  4.         LOAD(11)
  5.         TOP(0d)
  6.         STR(09)
  7.         "showModal" (00 73 00 68 00 6f 00 77 00 4d 00 6f 00 64 00 61 00 6c 00 00)
  8.         GET(40)
  9.         ARR(04)
  10.         TOP(0d)
  11.         NUM(07)
  12.         0 (00 00 00 00 00 00 00 00)
  13.         OBJ(03)
  14.         TOP(0d)
  15.         STR(09)
  16.         "title" (00 74 00 69 00 74 00 6c 00 65 00 00)
  17.         STR(09)
  18.         "这是一段隐藏在图片中的代码" (8f d9 66 2f 4e 00 6b b5 96 90 85 cf 57 28 56 fe 72 47 4e 2d 76 84 4e e3 78 01 00 00)
  19.         SET(41)
  20.         POP(0a)
  21.         TOP(0d)
  22.         STR(09)
  23.         "content" (00 63 00 6f 00 6e 00 74 00 65 00 6e 00 74 00 00)
  24.         STR(09)
  25.         "这是一段隐藏在图片中的代码" (8f d9 66 2f 4e 00 6b b5 96 90 85 cf 57 28 56 fe 72 47 4e 2d 76 84 4e e3 78 01 00 00)
  26.         SET(41)
  27.         POP(0a)
  28.         TOP(0d)
  29.         STR(09)
  30.         "success" (00 73 00 75 00 63 00 63 00 65 00 73 00 73 00 00)
  31.         NULL(02)
  32.         NUM(07)
  33.         1 (3f f0 00 00 00 00 00 00)
  34.         ADDR(08)
  35.         .anonymous_2
  36.         FUNC(30)
  37.         SET(41)
  38.         POP(0a)
  39.         SET(41)
  40.         POP(0a)
  41.         CALL(31)
  42.         POP(0a)
  43.         RET(33)
复制代码
毕竟是做个 Demo,如果真的需要实用的话,还有大量的优化空间。比如字节码字面量现在都是非常简单粗暴直接内联,如果将数据和代码部分区分可以得到一个更好的性能。比如字符串的编码使用的是 utf16 编码,如果转换成 utf8 编码可以节省空间占用等等,这些以后有心情再做。

将字节码藏在图片里

上一章我们说需要隐藏虚拟机和热更的代码,但是我们思考一下,一个普通的小程序整天需要加在二进制文件,这一个行为是不是非常的怪异?没错,这件事情非常非常的奇怪,因为一个正常小程序使根本没有什么读写二进制文件的需求。但是如果我告诉一个小程序,需要做一张有小程序二维码的分享图给用户保存,而且这张分享图还经常需要更新,这不是就非常符合逻辑了?
所以我们要将热更的字节码藏在图片里面,伪装成一个正常小程序的行为,并且要保证这场图片看起来也是正常的。以下就是我们开头示例中图片,作图是原图片,而右图是藏了我们上面示例代码的图,只有非常仔细看才能看到细微的差别。

小程序不让用 JS 解释器?那我再肛一次鹅厂-4.jpg

仔细看隐藏了字节码的区域,跟原图片有细微的差别

图片一个像素点有 RGBA 一共四个 byte,为了最少影响图片看上去的效果,我们选择只将字节码编码隐藏在图片的 Alpha 通道,这里用了最简单的编码方式,将 RGBA 中的 A 当成一个 bit 来进行编码。A 高于 0xF8 则为 1,否则则为 0。编码和解码算法如下:

小程序不让用 JS 解释器?那我再肛一次鹅厂-5.jpg

在编译器中的编码算法(左)在小程序中执行的解码算法(右)

在小程序中只需要把图片画在 Canvas 上面,并且逐个读取 Alpha 通道上的数据就能隐藏在图片中的字节码接解码出来。最后通过我们上一小节实现的字节码虚拟机,就能执行我们想要热更的代码了。

为什么无法从根本上禁止小程序代码的热更

先说结论,只要满足以下两个条件,那么从根本上禁止热更都是无稽之谈:
第一,宿主语言如果图灵完备的话,那么宿主语言就可以实现任何其他图灵完备的编程语言。比如 JavaScript 图灵完备,那么你就能用 JavaScript 实现 JavaScript 解释器、Python 解释器、PHP 解释器等等只要你能想得到的编程语言解释器,甚至你还可以设计一个自己的比如本文的字节码虚拟机。所以当公告一出来的时候,楼底下第一个回复的朋友就一语道破封 JavaScript 解释器是一件多么可笑的事情。

小程序不让用 JS 解释器?那我再肛一次鹅厂-6.jpg

公告发出来的第一天,就有朋友在评论区中抖机灵

第二,你可以把一切能够从得到不同输入,并且产生不同结果的程序都称之为解释器,无非就是它表达能力的强与弱、是通用的还是专用的区别而已,所以这个界限是非常模糊的。比如我们业务中,可能需要程序去服务器上拉一份配置,这份配置可能是某些功能的开关显示与否等等,那么这时候我拉的一份配置文件和拉了一份 JavaScript 代码动态执行有本质上的区别吗?其实也你可以理解代码不过是一份解释器/编译器的配置文件而已,没有那么特殊,唯一的区别仅仅是代码设计通用且复杂。所以才有那么一句话,代码既数据,数据既代码。

写在最后

在文章的最后,要向两位科学家致敬。第一位是艾伦·图灵,提出了图灵机奠定了计算理论的基础。第二位是香农,奠定了现代信息论的基础。感谢巨人们给我们提供的肩膀。

小程序不让用 JS 解释器?那我再肛一次鹅厂-7.jpg

艾伦·图灵(左) 克劳德·香农(右)
作者: liuhenry    时间: 2022-7-15 21:43
我们选择lisp ,直接下发一段json[思考]坐等json违规。
作者: 公子无咎    时间: 2022-7-15 21:43
[酷]其实差不多,hhhh
作者: QGSbDJqi    时间: 2022-7-15 21:44
JSON 表达程序,利好 Cirru!
作者: 香客斯    时间: 2022-7-15 21:45
看到企鹅新规时就想到你之前那篇文章,没想到这么快跟进了[惊喜]
作者: 帝国    时间: 2022-7-15 21:46
我代码其实在新规出来的那个周末就已经写完了,只是文章拖到了现在[捂脸]
作者: zhangjjqb    时间: 2022-7-15 21:47
别做做题家了,对着干只会让规矩越加越多,审核越来越严,开发越来越扭曲。别人不让用看不惯就减少在小程序上的投入,让市场去做选择,做做题家还是太单纯
作者: Kaoyao123    时间: 2022-7-15 21:47
[吃瓜]我就做个玩具炫技,隔壁上纲上线呢
作者: 永恒永恒    时间: 2022-7-15 21:47
怪不得小程序这么卡[大笑]
作者: 安安安安安    时间: 2022-7-15 21:49
这个就不是技术能解决的问题,跑得了和尚跑不了庙,企鹅要下重手不用通过技术,发现搞热更的公司统统关停小程序拉黑名单就行,公司在物理上又跑不掉。对于这种行为只能说要么忍了,要么道不同不相为谋,搞技术对抗只会升级惩处力度。
作者: lqmy_23    时间: 2022-7-15 21:49
[笑哭]那关我什么事,我又不写小程序,我就是纯粹好玩……
作者: tcyjy    时间: 2022-7-15 21:50
狠人[赞]
作者: tdgxxl    时间: 2022-7-15 21:51
ast树也是json,是不是就不违规了[看看你]
作者: yalpi    时间: 2022-7-15 21:52
法务部摸着网线来抓你! 给你整个什么破坏计算机罪
作者: cjomGlea    时间: 2022-7-15 21:53
“对抗”
作者: yuabhui    时间: 2022-7-15 21:55
你说,有没有一种可能。可以监控内存和线程异动[思考],
作者: 尐sHōw;`z.du’    时间: 2022-7-15 21:56
冻住不许走[耶]
作者: 520711    时间: 2022-7-15 21:57
摊牌了,我脑残粉[惊喜]直接三连
作者: xiarui270    时间: 2022-7-15 21:57
nice!
作者: 25868    时间: 2022-7-15 21:59
环境限制不了,就用法务限制你[尴尬]
作者: zuojia    时间: 2022-7-15 21:59
真真是没想到微信居然做到这种程度




欢迎光临 云裂变营销网 (https://www.yunliebian.com/yingxiao/) Powered by Discuz! X3.4