颜文字混淆工具aaencode解析

背景

很多年前了解到有一个将js代码转换为颜文字的混淆工具,混淆也就意味着输出的一段颜文字可以直接执行并且得到与原js相同的结果。今天想分析一下他的执行原理,记录如下。

混淆工具地址:https://utf-8.jp/public/aaencode.html

示例:

输入:

console.log("rock")

输出:

゚ω゚ノ= /`m´)ノ ~┻━┻ //*´∇`*/ ['_']; o=(゚ー゚) =_=3; c=(゚Θ゚) =(゚ー゚)-(゚ー゚); (゚Д゚) =(゚Θ゚)= (o^_^o)/ (o^_^o);(゚Д゚)={゚Θ゚: '_' ,゚ω゚ノ : ((゚ω゚ノ==3) +'_') [゚Θ゚] ,゚ー゚ノ :(゚ω゚ノ+ '_')[o^_^o -(゚Θ゚)] ,゚Д゚ノ:((゚ー゚==3) +'_')[゚ー゚] }; (゚Д゚) [゚Θ゚] =((゚ω゚ノ==3) +'_') [c^_^o];(゚Д゚) ['c'] = ((゚Д゚)+'_') [ (゚ー゚)+(゚ー゚)-(゚Θ゚) ];(゚Д゚) ['o'] = ((゚Д゚)+'_') [゚Θ゚];(゚o゚)=(゚Д゚) ['c']+(゚Д゚) ['o']+(゚ω゚ノ +'_')[゚Θ゚]+ ((゚ω゚ノ==3) +'_') [゚ー゚] + ((゚Д゚) +'_') [(゚ー゚)+(゚ー゚)]+ ((゚ー゚==3) +'_') [゚Θ゚]+((゚ー゚==3) +'_') [(゚ー゚) - (゚Θ゚)]+(゚Д゚) ['c']+((゚Д゚)+'_') [(゚ー゚)+(゚ー゚)]+ (゚Д゚) ['o']+((゚ー゚==3) +'_') [゚Θ゚];(゚Д゚) ['_'] =(o^_^o) [゚o゚] [゚o゚];(゚ε゚)=((゚ー゚==3) +'_') [゚Θ゚]+ (゚Д゚) .゚Д゚ノ+((゚Д゚)+'_') [(゚ー゚) + (゚ー゚)]+((゚ー゚==3) +'_') [o^_^o -゚Θ゚]+((゚ー゚==3) +'_') [゚Θ゚]+ (゚ω゚ノ +'_') [゚Θ゚]; (゚ー゚)+=(゚Θ゚); (゚Д゚)[゚ε゚]='\\'; (゚Д゚).゚Θ゚ノ=(゚Д゚+ ゚ー゚)[o^_^o -(゚Θ゚)];(o゚ー゚o)=(゚ω゚ノ +'_')[c^_^o];(゚Д゚) [゚o゚]='\"';(゚Д゚) ['_'] ( (゚Д゚) ['_'] (゚ε゚+(゚Д゚)[゚o゚]+ (゚Д゚)[゚ε゚]+(゚Θ゚)+ (゚ー゚)+ (o^_^o)+ (゚Д゚)[゚ε゚]+(゚Θ゚)+ ((゚ー゚) + (゚Θ゚))+ ((゚ー゚) + (o^_^o))+ (゚Д゚)[゚ε゚]+(゚Θ゚)+ ((゚ー゚) + (゚Θ゚))+ ((o^_^o) +(o^_^o))+ (゚Д゚)[゚ε゚]+(゚Θ゚)+ ((o^_^o) +(o^_^o))+ (o^_^o)+ (゚Д゚)[゚ε゚]+(゚Θ゚)+ ((゚ー゚) + (゚Θ゚))+ ((゚ー゚) + (o^_^o))+ (゚Д゚)[゚ε゚]+(゚Θ゚)+ ((゚ー゚) + (゚Θ゚))+ (゚ー゚)+ (゚Д゚)[゚ε゚]+(゚Θ゚)+ (゚ー゚)+ ((゚ー゚) + (゚Θ゚))+ (゚Д゚)[゚ε゚]+((゚ー゚) + (゚Θ゚))+ ((o^_^o) +(o^_^o))+ (゚Д゚)[゚ε゚]+(゚Θ゚)+ ((゚ー゚) + (゚Θ゚))+ (゚ー゚)+ (゚Д゚)[゚ε゚]+(゚Θ゚)+ ((゚ー゚) + (゚Θ゚))+ ((゚ー゚) + (o^_^o))+ (゚Д゚)[゚ε゚]+(゚Θ゚)+ (゚ー゚)+ ((゚ー゚) + (o^_^o))+ (゚Д゚)[゚ε゚]+((゚ー゚) + (゚Θ゚))+ (c^_^o)+ (゚Д゚)[゚ε゚]+(゚ー゚)+ ((o^_^o) - (゚Θ゚))+ (゚Д゚)[゚ε゚]+(゚Θ゚)+ ((o^_^o) +(o^_^o))+ ((o^_^o) - (゚Θ゚))+ (゚Д゚)[゚ε゚]+(゚Θ゚)+ ((゚ー゚) + (゚Θ゚))+ ((゚ー゚) + (o^_^o))+ (゚Д゚)[゚ε゚]+(゚Θ゚)+ (゚ー゚)+ (o^_^o)+ (゚Д゚)[゚ε゚]+(゚Θ゚)+ ((゚ー゚) + (゚Θ゚))+ (o^_^o)+ (゚Д゚)[゚ε゚]+(゚ー゚)+ ((o^_^o) - (゚Θ゚))+ (゚Д゚)[゚ε゚]+((゚ー゚) + (゚Θ゚))+ (゚Θ゚)+ (゚Д゚)[゚o゚]) (゚Θ゚)) ('_');

解析过程很简单:

1. 把奇怪的变量名替换成正常的字母便于识别;

2. 把确定的常量带入,常量表达式直接求值(基本上就是几种:算数计算,字符串拼接(非string类型变量可以拼出一些'undefined','true','false','object'之类的值用来提供字符,下文中的constructor就是从这些字符里拼出来的),从字符串取字符(用[]操作))。

很快就可以化简到如下形式:

X=3['constructor']['constructor'];
X ( X ('return\"\\143\\157\\156\\163\\157\\154\\145\\56\\154\\157\\147\\50\\42\\162\\157\\143\\153\\42\\51\"') (1)) ('_');

里面的8进制作为ascii转出来看一下:

> "\143\157\156\163\157\154\145\56\154\157\147\50\42\162\157\143\153\42\51"
输出:'console.log("rock")'

原理就很明白了:

通过一个数值对象,取其构造方法,得到一个函数。再取该函数的构造方法,得到一个可以构造函数实例的方法X。

X的参数是函数代码的字符串标识,返回一个函数实例。

于是乎我们就可以将任意一个输入的字符串当做代码执行了。

 

所以上面的例子中第一行的3不重要,换成其他对象也可以;第二行最后的两个参数1和'_'也不中要,因为两层X实例化出来的函数都不消费参数。

另外上面的例子中套了一层 'return <转义后的代码>' 的结构,所以用了两层X来执行最终的结果。这个例子中不添加这一层嵌套也能达到效果,可能是为了增加混淆度。

所以我再简化一下来示例代码原理,如下:

> ''['constructor']['constructor']("\143\157\156\163\157\154\145\56\154\157\147\50\42\162\157\143\153\42\51")()
输出:rock

 

PS:

其他小技巧:将每一个小表情解释成变量或简短的算数表达式,来表示一个数字,然后通过加减法来凑八进制标识的每一位数字,只有0-7,比较好凑,也保证了表情密度,让人不容易注意到表情之外的冗余加减号和括号。

posted @ 2021-10-01 00:03  rainforwind  阅读(967)  评论(0编辑  收藏  举报