?前言
? ? ? ? ? 最近正式工作了,这段时间比较忙,上篇android脱壳机可能要等一段时间,不过看雪已经有人公布android7.1的脱壳机的源码了。之前验证了android8.1的脱壳,感觉android8.1增加了参数解引用的问题,会有一些坑点导致程序会crash。所以暂时先放弃android8.1,过段时间先复现android7.1的脱壳机。然后工作这段时间,有遇到js混淆,打算学习一下。
js混淆比较出名的工具就有Obfuscator,但是Obfuscator缺点在于平坦化做的一般,有些企业会自己做一下平坦化,再用Obfuscator来混淆。所以先学习Obfuscator很有必要性。
js混淆学习前置知识
? ? ? ? 建议先学习了解AST树和解析AST树的框架,可以先看下面的参考链接,补充前置知识。
Obfuscator混淆解析
? ? ? ? ?Obfuscator 在线混淆网站https://obfuscator.io/?底下有介绍相关混淆内容,这里直接看提供例子来解析的代码。
var _0x3ed0 = ['1241023ikpdYM', 'Hello\x20World!', '291190xIUkft', '1251274vQVPdI', '124952hgHyOi', '1983KQSSIW', '247DipWFn', '7354VgseoG', '49680CQWPxl', '1ZTWTUo', '648lISKkF'];
function _0x4ed9(_0x475ec5, _0x372034) {
return _0x4ed9 = function (_0x3ed0df, _0x4ed9c4) {
_0x3ed0df = _0x3ed0df - 0x96;
var _0x5a22f3 = _0x3ed0[_0x3ed0df];
return _0x5a22f3;
}, _0x4ed9(_0x475ec5, _0x372034);
}
(function (_0xa942b4, _0x57410c) {
var _0x4e4980 = _0x4ed9;
while (!![]) {
try {
var _0x1e86fa = parseInt(_0x4e4980(0x9b)) + parseInt(_0x4e4980(0x9e)) + -parseInt(_0x4e4980(0x97)) + -parseInt(_0x4e4980(0x9c)) * -parseInt(_0x4e4980(0xa0)) + -parseInt(_0x4e4980(0x98)) * parseInt(_0x4e4980(0x9d)) + -parseInt(_0x4e4980(0x96)) + parseInt(_0x4e4980(0x99)) * parseInt(_0x4e4980(0x9a));
if (_0x1e86fa === _0x57410c) break;else _0xa942b4['push'](_0xa942b4['shift']());
} catch (_0x178fbf) {
_0xa942b4['push'](_0xa942b4['shift']());
}
}
})(_0x3ed0, 0xb3f61);
function hi() {
var _0x81b55a = _0x4ed9;
console['log'](_0x81b55a(0x9f));
}
hi();
/*
source code
function hi() {
console.log("Hello World!");
}
hi();
*/
暂时可以看出Obfuscator有如下特点:
1.大数组变量
2.位移大数组函数
3.自解密函数
由于代码较短,还未看出平坦化,不过不着急,先尝试把这个还原。
解析ast推荐在线网站:?https://astexplorer.net/
通过分析ast和混淆代码的分析,现在去混淆思路:
1.抠出自解密函数和解密函数的name
2.遍历调用函数和初始化赋值操作,获取解密函数的结果并且替代节点
3.修复函数调用方式,比如console['log']修复成console.log
修复办法:
1.使用ast来解析出body,转化成相关代码,获取到解密函数
2.遍历并判断调用解密函数的点,来进行获取最后结果
3.使用ast在线解析网站,对比二者不同,来进行修复
去混淆脚本如下
const fs=require('fs');
const parser=require('@babel/parser');
const traverse=require('@babel/traverse').default;
const types =require('@babel/types');
const generator=require('@babel/generator').default;
const { type } = require('os');
const { exit } = require('process');
var arguments=process.argv;
if(arguments.length<4)
{
console.log("need input file and output file path");
exit();
}
const inputFilePath = arguments[2];
const outputFilePath=arguments[3];
const jscode=fs.readFileSync(inputFilePath,
{
encoding:"utf-8"
});
var globalArrayName;
var globalDecode;
var DecodeFunName;
var globalDecodeList=new Array();
/*
function getVariableName(path)
{
var node = path.node;
if(!types.isArrayExpression(node.init))
{
return;
}
//console.log(node.id.name);
globalArrayName=node.id.name;
}
*/
function parserAst(ast)
{
globalArrayName=ast.program.body[0].declarations[0].id.name;
DecodeFunName=ast.program.body[1].id.name;
globalDecodeList.push(DecodeFunName);
var runCode=ast.program.body.slice(3,);
ast.program.body=ast.program.body.slice(0,3);
globalDecode=generator(ast).code;
ast.program.body=runCode;
return ast;
}
function getDecodeList(path) {
var node=path.node;
if(!types.isIdentifier(node.id)||!types.isIdentifier(node.init)||node.init.name!=DecodeFunName)
{
return;
}
globalDecodeList.push(node.id.name);
path.remove();
}
function funToStr(path)
{
var node =path.node;
if (!types.isIdentifier(node.callee)||globalDecodeList.indexOf(node.callee.name)==-1)
{
return;
}
//console.log(path.toString());
node.callee.name=DecodeFunName;
let value = eval(globalDecode+path.toString());
//console.log(value);
path.replaceWith(types.valueToNode(value));
}
function fixFunCall(path)
{
var node =path.node;
if(!types.isIdentifier(node.property))
{
return;
}
let name =node.property.name;
path.node.property=types.stringLiteral(name);
path.node.computed=true;
}
function solveOb(ast)
{
//eval(globalDecode);
traverse(ast,{
VariableDeclarator:getDecodeList,
CallExpression:funToStr,
MemberExpression:fixFunCall
});
return ast;
}
let ast=parser.parse(jscode);
ast=parserAst(ast);
ast=solveOb(ast);
let code=generator(ast).code;
console.log(code);
总结
? ? ? ? 多在ast在线网站查看相关数据,多加对比。后续会写长的代码来去混淆,使其体现出平坦化,并且让脚本尽量通用化。
参考链接
Js Ast一部曲:高完整度还原某V5的加密
https://bbs.nightteam.cn/thread-417.htm
??
????????
? ?
|