早先在研究对移动网络传输举行功耗优化,,,在一次意外的监听网络传输包中截获了微信小程序的请求包,,,借此来窥探当下前端代码清静。。。
0x01 小程序剖析
小程序包结构
SegmentNameLengthRemark
HeaderFirstMark1 byte0xBE 牢靠值
Edition4 bytes0 ->微信分发到客户端 1 ->开发者上传到微信后台
IndexInfoLength4 bytes索引段的长度
BodyInfoLength4 bytes数据段的长度
LastMark1 byte0xED 牢靠值
FileCount4 bytes文件数目
IndexNameLength4 bytes文件名长度
NameNameLength bytes文件名,,,长度为NameLength
FileOffset4 bytes文件在数据段位置
FileSize4 bytes文件巨细
LOOP......
DataFiles Package......
包结构很是清晰,,,分为三个部分:
头信息,,,包括一些包的标识,,,版本界说等,,,包括了三个冗余字段 --- 索引段和数据段的长度应该是用于做校验,,,但实质上没有用(设计者以为需要设计一些冗余字段来确保设计的完整性,,,防止剖析的时间溢出,,,但现实工程实践中并没有起到响应的作用),,,文件数目应该是用于简化包剖析历程,,,现实上知道了索引段长度或数据段长度中任何一个皆可推算出文件数目。。。
索引段,,,包括文件的元信息 --- 文件名以及文件位置(通过FileOffset和FileSize定位数据段中的文件)。。。若是从精简包的巨细的角度来看,,,FileOffset和FileSize只需一个保存即可,,,可是这样剖析包的难度就大大增添了,,,照旧以工程实践为主。。。
数据段,,,将所有的文件枚举在一起。。。
由此可见,,,数据完全没有经由压缩或者加密,,,连包的署名信息也没有。。。这导致只能在制品流程上举行严酷控制,,,例如在开发者上传代码历程中需要授信,,,必需经由审查,,,也一定得由授信平台举行代码分发等。。。这些都无关风月,,,事实App Store就是这种模式,,,可是......
怎样拆解这种自界说文件名堂呢??????
对多个相同名堂的文件举行比照,,,对概略结构有宏观的感受,,,很容易发明一些牢靠的字段以及一些结构的长度。。。关于像小程序这种有软件本体的例子,,,还可以通过微量修改来视察文件的转变来找到文件结构和意义。。。
视察特殊形式,,,首先英文的字符串是很显着的,,,一般hex编辑器都自带字符串化窗口,,,若是发明常见的字符串,,,就可以继续去寻找字符串的界线,,,字符串在二进制文件里有两种贮存方式:一种是不纪录字符串的长度,,,读取字符串到0x00位置,,,另一种一定在某一个地方贮存了这个字符串的长度,,,因此一旦得知了该字符串的内容,,,搜索该长度字段即可获取更多的信息。。。其次一些文件头也很是显眼,,,例如PNG、ZIP等通用标准文件名堂都有牢靠的文件头,,,在小程序的自界说名堂中很容易发明一些png、jpg等资源的文件头,,,因此可以定位数据区的位置。。。
对特定区域的二进制举行推理推测,,,一般来说二进制文件里需要贮存大宗的offset和size的数据作为数据段的索引。。。offset相当于一个指针 - 索引文件在数据段的位置,,,工程实践中,,,大部分贮存了offset的地方也会贮存size字段,,,事实在剖析文件的时间会利便许多,,,也可以防止校验数据泛起指针越界。。。因此,,,一旦确认了文件中的数据段,,,就可以通过它的位置(offset)和大。。。╯ize)的现实数据举行搜索,,,逆向找到指向它的数据位置,,,并且继续逆向直到剖析完整的文件。。。另外,,,若是要思量设计的完整性,,,需要在二进制文件中加入一些冗余字段举行校验或者纠错,,,例如CheckSum、CRC32、Alder32、MD5、ECC等,,,这些通过hex编辑器很容易盘算并发明。。。小程序中FileCount的字段,,,这完全是为了工程实践思量的,,,在小程序中并没有泛起这类的盘算值,,,这是可能是由于小程序为了简朴设计思量,,,一旦发明包体被改动或损坏就直接扬弃。。。
着实拆解小程序这种名堂并不需要破费特殊多的精神,,,由于其名堂较量简朴,,,并且从下图流程上来说,,,后缀为wx的二进制名堂很可能与wxapkg名堂是同源的。。。
从开发者工具的代码中的 pack 很容易发明一些对wx名堂封装的痕迹,,,只不过其中 unpack 的代码被隐去了。。。通过现实的剖析发明(wxapkg文件可以通过截获网络包请求获得或者在外地的微信appbrand目录下可以发明),,,wxapkg名堂就是将wx名堂举行了转化:Wxml -> Html、 Wxml -> JS、Wxss -> Css,,,其二进制名堂跟后缀名为wx二进制名堂完全一致。。。我写了两个版本的剖析二进制包的代码( Java版本传送门,,, python版本传送门),,,着实很是简朴,,,凭证小程序包结构一步一步剖析,,,基本上没啥难度。。。但若是要将Html -> Wxml, JS -> Wxml, Css -> Wxss举行还原,,,其中JS -> Wxml的历程中需要将if语句转酿成wx-if标签、for语句转化成wx-for标签有点贫困,,,需要对剖析包后的 page-frame.html 中 JS 代码举行修改,,,修改细节太多就不再详说了,,,总之微信小程序的代码没有经由特另外保;;げ椒,,,较量容易举行还原。。。
(PS:袒露一下微信小程序未果真的API,,, openUrl - 在小程序中翻开外部网页;;; getGroupInfo - 获得群的名称,,,群内成员的昵称等数据;;; getOpenidToken - 获得用户openid;;;这些权限微信应该是没有准备开放的。。。每次在进入小程序时,,,客户端都需要先去请求该小程序的元数据,,,例如应用名、版本号、一些权限列表、代码包下载地点等形貌信息,,,修改这些元数据可以获得响应的权限,,,小程序的要害信息完全由后台控制举行设置,,,另外小程序的外地文件存储接纳HASH映射机制举行文件定位,,,文件存储在外部存储,,,自己通过自界说算法实现完整性校验 - 首先,,,小程序最终存储的文件名是:对称加密(文件流内容Alder32校验和 | 原始文件名)天生的,,,最终文件名和文件内容会通过自校验判断完整性;;;其次,,,外地缓存是通过HASH映射查找文件。。。以是纵然能破解文件名和文件内容,,,绕过文件自身签学校验,,,改动为攻击者的伪造文件,,,小程序APP也无法映射到该伪造文件举行使用。。。)
0x02 前端代码清静
由上可见,,,微信并没有在代码清静上举行过多的思量。。。这导致需要在应用审核历程中破费较量多的功夫,,,否则作品太容易被复制改动,,,以至于会失去渠道先机,,,这对流量是致命攻击。。。由于历史原因,,,前端的代码清静手艺生长的较量缓慢,,,相比其他被编译成二进制的应用,,,前端这种纯文本应用,,,太容易被辨识与改动。。。
对前端代码举行保;;さ哪康脑谟谌没等菀资侗鹣喙氐闹噶,,,而使人难以明确代码的逻辑,,,但往往在对前端代码举行保;;だ讨,,,很难既兼顾指令效率又能使可读性降低。。。因此,,,经常需要在现有的代码中增添一些特另外验证逻辑,,,例如一些增添无效的代码举行混淆、接纳守护代码保;;び荡氩豢稍谄渌挠蛎抡T诵小⒃鎏硪恍┓乐沟魇愿俚亩系愕,,,这些步伐都是使得破解代码时人工本钱增添,,,从而增添代码的清静性。。。
下面提供一些能够增添前端代码清静性的战略:
1. 精简(minify)
这是最简朴且无害的要领,,,精简代码能镌汰代码体积,,,从而减小数据传输的负荷,,,同时也能降低代码的可读性。。。在小程序开发者工具中也提供该选项。。。对Java代码举行精简大致可以从以下几个方面入手:
删除注释,,,删除无意义或者多余的空缺,,,删除可以省略的符号
删除一些没有挪用的代码(Dead code),,,对函数举行精简(三元运算符?:、字符串操作、工具函数、工具继承、函数引用、无名函数、递归函数)
将变量名举行简化,,,将零星的变量声明合并,,,缩短语句
......
常用的工具有许多: YUI Compressor 、 UglifyJS 、 Google Closure Compiler 、 JS Packer 、 JS Min ...
使用工具对代码举行精简时需要注重:1. 最好备份原始代码,,,利便调试与后期修改。。。 2. 用于调试精简代码时生涯的sourcemap,,,在线上应该删除。。。 3.编写代码的时间应该严酷凭证规范,,,最好使用lint工具对代码举行检查,,,精简代码后导致代码不可用时,,,调试很是难题。。。
这种简朴的要领虽然很适用,,,可是也很容易被还原出源代码,,,使用一些代码名堂化工具可以补齐被删除的空格、换行、符号等,,,例如 jsbeautifier 。。。另外2015年就有相关的研究,,,从大宗的代码中推测出被精简的代码,,,由于人写代码总有牢靠的范式,,,所写的代码相似性都很是的高,,,若是用统计方式就很容易反推源代码,,,苏黎世联邦理工大学Martin Vechev教授领带下开发的工具 JSNice 就是一款运用条件随机。。。–onditional Random Fields)机械学习和程序剖析要领来还原Java代码利器,,,使用大宗的开源代码,,,去学习命名和类型的纪律。。。JSNice可以用于以下差别的方面:反精简的Java代码、对目今的代码提供更多的更有意义的变量名、自动化程序的注释等。。。相关论文传送门后台代码传送门
2. 混淆(obfuscation)
混淆可以减低代码的可读性,,,防止被容易追踪出程序逻辑。。。常见的混淆要领有如下几种:
通过编码混淆代码,,,这篇文章《Java常用混淆要领》内里介绍了许多不错的编码加密要领。。。可是这些要领有个显着弱点,,,增添代码体积,,,并且编码加密都是可逆的。。。
将标识符混淆和控制逻辑混淆(疏散静态资源、打乱控制流、增添无义的代码等),,,例如 aaencode 和 jjencode 。。。
标识符混淆的要领有多种,,,有些与编码混淆代码要领有些重叠,,,常用要领有哈希函数命名、标识符交流和重载归纳等。。。哈希函数命名是简朴地将原来标识符的字符串替换成该字符串的哈希值,,,这样标识符的字符串就与软件代码不相关了;;;标识符交流是指先网络软件代码中所有的标识符字符串,,,然后再随机地分配给差别的标识符,,,该要领不易被攻击者察觉;;;重载归纳是指使用高级编程语言命名规则中的一些特点,,,例如在差别的命名空间中变量名可以相同,,,使代码中差别的标识符只管使用相同的字符串,,,增添攻击者对软件源代码的明确难度。。。
控制混淆是改变程序的执行流程,,,从而打断逆向剖析职员的跟踪思绪,,,抵达保;;と砑的目的。。。一般接纳的手艺有插入指令、伪装条件语句、断点等。。。伪装条件语句是当程序顺序执行从A到B,,,混淆后在A和B之间加入条件判断,,,使A执行完后输出TRUE或FALSE,,,但岂论怎么输出,,,B一定会执行。。??????刂苹煜幽山狭慷嗟纳杏心:酱省⒛谇锻饬⑼黄扑承虻纫。。。模糊谓词是使用新闻差池称的原理,,,在加入模糊谓词时其值对混淆者是已知的,,,而对反混淆者却很难推知。。。以是加入后将滋扰反汇编者对值的剖析。。。模糊谓词的使用一般是插入一些死的或不相关的代码(bogus code),,,或者是插入在循环或分支语句中,,,打断程序执行流程。。。内嵌(in-line)是将一小段程序嵌入到被挪用的每一个程序点,,,外联(out-line)是将没有任何逻辑联系的一段代码笼统成一段可被多次挪用的程序。。。突破顺序是指突破程序的局部相关性。。。由于程序员往往倾向于把相关代码放在一起,,,通过突破顺序改变程序空间结构,,,将加大破解者的头脑跳跃。。。
另外尚有些混淆方式是专门针关于反混淆工具设计的,,,这就需要去仔细剖析反混淆工具的原理,,,在一些特定的地方插入代码使反混淆器进入死循环或者异常跳出。。。
一般来说,,,提供代码精简的工具都会提供一些混淆的要领,,,除此之外,,,较量着名的商业工具有 jasob 、 jscrambler ,,,一般越商业的越难被反混淆,,,然而这些高级的代码混淆也常;;岜挥糜谝藏应用中的恶意代码。。。对恶意代码举行混淆是为了逃避杀毒软件的检测,,,这些代码在被混淆扩充后会难以被识别为恶意软件。。。响应的也有一些反混淆的工具泛起,,,例如上面提到的 JSNice 工具能够对混淆的代码举行推理,,,另外反混淆工具 JSDetox 专门针对一些混淆要领做过专门的支持。。。反混淆一直是一项体力活,,,凭证差别的混淆战略需要举行反推演算,,,这就是一场攻与防的游戏罢了。。。
3. 加密(encryption)
加密的要害头脑在于将需要执行的代码举行一次编码,,,在执行的时间还原出浏览器可执行的正当的剧本,,,在某个角度也可以以为是一种混淆的形式,,,看上去和可执行文件的加壳有点类似。。。Java提供了将字符串当做代码执行(evaluate)的能力,,,可以通过 constructor 、 eval 、 、 setInterval 、 Worker 、 DOM event 等将字符勾转达给JS引擎举行剖析执行,,,由于有些需要用到 eval 函数,,,会导致代码性能会减低。。。以Worker 执行举例:
var URL = window.URL || window.webkitURL; var Blob = window.Blob || window.webkitBlob; var blobURL = URL.createObjectURL( new Blob(['console.log("Hello World!")'], {type: 'application/java'})
); new Worker(blobURL);
URL.revokeObjectURL(blobURL);

有以下常见的几种加密要领:
base64编码,,,一种简朴的要领就是将代码转化成base64编码,,,然后通过 atob 以及 eval 举行解码然后运行,,,另外一种接纳 base62编码手艺更为常见,,,其最显着的特征是天生的代码以(p,a,c,k,e,r)) 开头。。。无论代码怎样举行变形,,,其最终都要挪用一次 eval等函数。。。解密的要领不需要对其算法做任何剖析,,,只需要简朴地找到这个最终的挪用,,,改为 console.log 或者其他方式,,,将程序解码后的效果凭证字符串输出即可。。。
(PS: 从算法上看,,, packer 是一种base64编码字典压缩战略,,,packer的base64编码的压缩率很高,,,精简子女码依然可以镌汰50%体积以上,,,由于带有解压器和字符表,,,越长的代码理论上压缩率更高,,,想要相识详情可以看看这篇文章《Packer,,,你对我的JS做了什么!》 )
使用重大化表达式,,,在Java中可以把原本简朴的字面量(Literal)、成员会见(Member)、函数挪用(Call)等代码片断变得难以阅读。。。例如这个要领仅用+!等符号就足以实现险些恣意Java代码。。。在 JS 代码中可以找到许多这样互逆的运算,,,通过使用随机天生的方式将其组合使用,,,可以把简朴的表达式无限重大化。。。
隐写术,,,将 JS 代码隐藏到了特定的介质当中。。。如通过最低有用位(LSB)算法嵌入到图片的 RGB 通道、隐藏在图片 EXIF 元数据、隐藏在 HTML 空缺字符、放到css文件中(使用content样式能存放字符串的特征)等。。。好比一张图片黑掉你:在图片中嵌入恶意程序,,,这个正是使用了最低有用位平面算法,,,连系HTML5的canvas或者处理二进制数据的TypeArray,,,抽取出载体中隐藏的数据(如代码)。。。隐写的方式同样需要解码程序和动态执行,,,以是破解的方式和前者相同,,,在浏览器上下文中挟制替换要害函数挪用的行为,,,改为文本输出即可获得载体中隐藏的代码。。。
混淆加密,,,单个要领容易被破解,,,但组合起来就不会那么容易了,,,破解本钱也会指数增添,,,例如 jdists 接纳组合加密和嵌套加密的方式。。。
这些加密的方式都很容易通过对源代码举行词法剖析、语法剖析举行还原,,,首先将代码的字符串转换为笼统语法树(Abstract Syntax Tree, AST)的数据形式,,,然后从语法树的根节点最先,,,使用深度优先遍历整棵树的所有节点,,,凭证节点上剖析出来的指令逐个执行,,,直到剧本竣事返回效果。。。这种要领大大都用于对代码举行优化,,,例如最近Facebook开源了代码优化工具 Prepack,,,可以自动消除冗余代码,,,降低打包体积和执行时间,,,基本上就可以用来将这些加密的字符串举行还原,,,事实编码这些字符串都是可以通过词法语法推测出来的。。。
4. 编译(compile)
Github上有一份清单纪录了所有Java扩展语言,,,这些语言都可以通过编译器转化为Java语言,,,这也是前端生长的一个趋势,,,原来写的html,,,css,,,Java已经最先酿成了一个“中心语言”,,,并且越来越多的团队也有了自己的一套前端编译系统。。。Java越来越像Web中的汇编语言,,,特殊是近些年Node的普及,,,让前端变得越来越重大,,,大宗前端框架的泛起,,,使得Java代码可以通过手工编写,,,也可以从另一种语言编译而来,,,详情参考几年前Brendan Eich(Java之父)、Douglas Crockford(JSON之父),,,尚有Mike Shaver(Mozilla手艺副总裁)的邮件。。。通过编译后的Java代码越利便机械的明确,,,降低可读性,,,在某一定角度上讲,,,这也不愧为一种代码保;;げ椒。。。听说几大科技巨头正在酝酿给浏览器应用设计一款通用的字节码标准——WebAssembly ,,,一旦这个设想得以实现,,,代码保;;そ梢砸胝嬲庖迳系“加壳”或者虚拟机保;;,,,反抗手艺又将提升到一个新的台阶。。。现在在桌面端,,,使用 NW 框架可以Java应用程序的源代码可以被编译为外地代码,,,在运行时通过NW动态还原出源代码,,,可是这种要领现在会比正常的JS代码慢30%左右。。。
5. 防止被调试
对代码举行破解剖析无非分为静态剖析和动态剖析,,,若是对代码举行混淆加密等形式操作,,,那么静态剖析就很贫困了,,,对代码调试跟踪剖析可以对代码整体逻辑有一个宏观的把控。。。例如首先判断浏览器是否开启了开发者工具控制台(现在最完善的解决方案传送门),,,若是检测出控制台开启则梗塞Java执行或让代码异常跳出。。。另外Android 4.4及以上和iOS是支持webkit remote debug的,,,因此应该在debug模式下,,,设置代码可以被debug,,,release模式下,,,榨取debug以保;;な萸寰。。。
6. 前后端协作
首先得强调的事情是不要在前端放敏感数据,,,前端容易破解,,,因此需要配合后端举行清静防护,,,例如微信小程序的登录,,,必需使用授信的后端配合才华完成此项功效,,,另外在小程序的网络请求中的referer是不可以设置的,,,其中 {appid} 为小程序的appid,,,通过验证appid字段可以抵御一些直接的山寨,,,其次就是加速迭代速率更改代码保;;ふ铰,,,这样可以让之前的剖析失效,,,增添破解的本钱。。。
以上就是对目今前端代码清静举行的探索。。。
更多关于微信小程序开发内容,,,可以多关注。。。
KESION 金狮贵宾会软件
KESION 金狮贵宾会软件是海内领先的在线教育软件及私域社交电商软件服务提供商,,,恒久专注于为企业提供在线教育软件及社交电商SaaS平台解决方案。。。
公司焦点产品云开店SaaS社交电商服务平台、在线教育SaaS服务平台、教育企业数字化SaaS云平台、企微营销助手、私有化自力安排品牌网校和在线教育咨询等。。。KESION 一直通过手艺立异,,,提供产品和服务,,,助力企业向数字化转型,,,通过科技驱动商业刷新,,,让商业变得更智慧!
console console.log要领用于在 console 窗口输出信息。。。它可以接受多个参数,,,将它们的效果毗连起来输出。。。 Math 属性 E LN10 LN2 LOG2E LOG10E PI S
setData setData 是小程序开发中使用最频仍的接口,,,也是最容易引发性能问题的接口。。。在介绍常见的过失用法前,,,先简朴介绍一下 setData 背后的事情原理。。。 事情原理 小程序的视图层现在使.