JavaScript-Obfuscator4.0.0字符串阵列化Bug及修复方法
Javascript-obfuscator是全球知名的开源JavaScript代码混淆加密工具,由俄罗斯程序员Timofey Kachalov开发维护。
在2022年2月15日发布的4.0.0版本中,其存在一个字符串阵列化Bug,某些情况下会导致混淆结果异常。本文对Bug情况进行说明并提供修复方法。
注:此bug由JShaman团队发现,并已提交作者修复。
JShaman是国内专业的javascript源代码安全研究组织,与Javascript-obfuscator保持着友好联络与技术交流。
Bug描述:
Javascript-obfuscator4.0.0,其字符串阵列功能,对async函数中的成员对象进行阵列化处理,会导致代码异常。
例如,一段NodeJS代码:
async function waitPage(page) {
await page.evaluateHandle(function(){
return window.renderdone;
});
}
(async () => {
const puppeteer = require('puppeteer');
puppeteer.launch().then(
async browser => {
console.log('...');
const [page] = await browser.pages();
await page.goto('https://www.baidu.com');
await waitPage(page);
}
)
})();
使用Javascript-obfuscator进行混淆加密,保护选项只选择了字符串阵列化这一个功能:
混淆加密后的代码,运行时发生异常,提示有变量未定义:
注意图中命令行中所显示,第一次执行是在未加密前,可正常使用。第二次是执行加密后的代码,出现错误。
Bug原因:
上述JS代码混淆加密后出现错误的原因,是由于进行字符串的阵列化处理时,未考虑是否处于async函数中。导致阵列化时MemberExpression字面量放置到了函数不可访问的外部区域中。如下图所示:
注:绿线上方是原始代码,做对比用。参考上面图中的错误提示变量,可以看出错误原因。
Bug修复方案:
阵列化功能,在JavaScript-Obfuscator目录下的StringArrayTransformer.ts文件中。
以下为临时修复代码:
//原始: return this.transformNode(node, parentNode);
//修复bug后的代码:
//是否是异步函数的标识
var in_async_function = false;
//递归函数,检测节点所有上级节点,判断是否处于异步函数中
var point = "-";
function detect_async_function(node:ESTree.Node){
console.log(point, node.type);
if(node.type == "FunctionDeclaration"){
console.log(point, node.id?.name);
}
//是函数定义,并且是异步函数
if((node.type == "ArrowFunctionExpression" || node.type == "FunctionDeclaration" || node.type == "FunctionExpression") && node.async == true){
in_async_function = true;
return;
}
//是否达到节点顶部。测试中发现node.parentNode永远存在,达顶点后上级顶点依然是Program
if(node.type == "Program"){
return;
}
if(node.parentNode){
point = point + "-";
detect_async_function(node.parentNode)
}else{
//不能获得父节点,是异常的代码,跳过
in_async_function = true;
return;
}
}
detect_async_function(node);
console.log(node.value,node.loc);
//没有检测出异步函数,正常处理
if(in_async_function == false){
return this.transformNode(node, parentNode);
}
即:在处理字面量时,判断是否处于async函数体中,如是,则跳过。
用此方法修复后,运行混淆加密后的代码正常,如下图所示:
|