文章总结: 本文详细分析了Akamai的字符串混淆机制,包括入口函数初始化过程、6个解混淆函数的工作原理以及YO数组作为关键解密元素的作用。作者指出akm采用首次调用解密并缓存的方式,一旦解密失败会导致整个系统崩溃。文章提供了通过AST快速提取特征函数的思路,特别强调了Cp8等于11的切入点以及YO.push操作的重要性,为实现自动化解密提供了技术路径。 综合评分: 85 文章分类: 逆向分析,WEB安全,漏洞分析,代码审计,安全工具
Akamai本地字符串解密
原创
OOO
Frida and So
2025年12月8日 16:06 上海
声明
本文仅供学习交流,如有侵权联系删除!!
目标
实现akm本地解字符串混淆。
预处理
Akm的混淆文件除了变量名,逻辑基本不变。
先对常量进行赋值还原,然后对入口函数控制流还原,再将RS函数内常量赋值还原,完成以上操作后便可以正式跟踪分析了。
初步分析
入口函数其实就是在进行解混淆的初始化,RS前声明的函数基本都要在下面的call内重写为真正的解混淆方法,下面6个混淆字符串数组意味着存在6个解混淆函数,sl8是一个100多位的函数名字符串数组,YO贯穿全文解混淆的关键数组,call调用给每个解混淆迭代器绑定sl8内函数名,并且为每一个解混淆函数生成一个独特的字符串,异或的key。现在看不懂,跟着走一遍就懂了。
以hS的解混淆函数初始化为例,首先进入case 45分支,在vB中完成解混淆函数的初始化,然后遍历sl8()数组完成解混淆函数绑定。
vB调用进入Yc的case 331分支,给YO放入关键数,重写vB,进入kD函数。
kD调用进入了Yc的case 43分支,MK函数的case 6分支也只是做了些判断进行String.fromCharCode,最后将生成的字符串传入f2。
重写kD方法,取出字符串,传入重写后的vB,也就是进入dl的case 46分支。
对字符串进行了重排序操作,将其赋值给了kD函数的Tk字段,完成后,一路返回,来到CP8循环处。
如下代码段,首次调用时解密字符串并缓存,之后直接返回缓存结果。这就是为什么akm一旦解密失败一次,整个就完蛋了。
var Cp8 = 0;
while (Cp8 < Dp8.length) {
if(Cp8 == 11){
Q6()[Dp8[Cp8]]= function () {
return SZ.apply(this, [15, arguments]);
}
}else{
Q6()[Dp8[Cp8]] = function () {
var kv8 = Dp8[Cp8];
returnfunction (WQ8, Y98) {
//重点,每次解混淆函数,如Q6()[YB(27)].call(null, 129, 1255),YB(27)返回索引,只有第一次 调用该索引触发使用kD函数进行解密
var hr8 = kD(WQ8, Y98);
Q6()[kv8] = function () {
//缓存结果
return hr8;
};
return hr8;
};
}();
}
++Cp8;
}
//Q6用闭包创建并返回一个全局唯一的空对象(缓存容器),之后再次调用 Q6 时直接返回该同一个对象。
function Q6() {
var SR1 = []['keys']();
Q6 = function () {
return SR1;
};
return SR1;
}
解混淆分析
kD在初始化的时候被重写了,直接跟入L78的case 40分支,用到了前面的kD.Tk,也用到了YO数组的最后一位。
思路
到这里,akm怎么解混淆的已经被解读完了,通过AST快速提取这些特征函数,前面的Cp8 == 11就是重新初始化kD.Tk这种字符串的,是一个不错的切入点,点进去后,第一句kD.Tk,而且你直接搜xor刚好6个,每一个都是xx.xx = 混淆数组[xx],除此之外,还有不同解密函数%的值是不同的,最恶心其实是解密前一定会YO.push(值),毕竟YO起名都是global_subkey。
Q6()[Dp8[Cp8]]= function () {
return SZ.apply(this, [15, arguments]);
}
比如下面这段,我演示一下YO.push与不push的情况,很明显不去搞这个YO一定解密失败,要实现AST自动解,就得完成上面的操作,剩下的就是如何去编写AST了,额外注意一下索引 11 这个根本不会调用,思路有了,编写还不简单吗 ?。
查看原文:《Akamai本地字符串解密》
版权声明
本站仅做备份收录,仅供研究与教学参考之用。
读者将信息用于其他用途的,全部法律及连带责任由读者自行承担,本站不承担任何责任。










评论