目录
插件说明
在 JS 逆向中,想要找到某个参数是怎么生成的,一般的套路就是,比如要找的参数是 w
,那么我们直接使用 F12
打开 DevTools,然后使用 search 功能来搜索 w
,但是往往有时候会一无所获,这是因为混淆的代码中,把字符串字面量都进行了编码,比如 Unicode编码(\u0077)
,hex编码(\x77)
,上面的编码例子都表示为 w
。
本文主要介绍以下三种常见情况:
- 字符串的 Unicode 编码
\u0031\u0030\u0030\u0038\u0036
- 字符串的 Hex 编码
\x31\x30\x30\x38\x36
- 整型的 Hex 编码
0x2766
以上的 \u0031\u0030\u0030\u0038\u0036
、\x31\x30\x30\x38\x36
都表示字符串 "10086"
,而 0x2766
则表示的是 整数 10086
。
注意:文中出现以 A
结尾的即为混淆插件,以 B
结尾的即为还原插件
插件名称
- UnicodeEscapeSequence
- UnicodeEscapeSequenceA(混淆)
把字符串进行 Unicode 编码
- UnicodeEscapeSequenceB(还原)
把 Unicode编码进行解码
- UnicodeEscapeSequenceA(混淆)
- HexEscapeSequence
- HexEscapeSequenceA(混淆)
把字符串或整数进行 Hex 编码
- HexEscapeSequenceB (还原)
把 Hex 编码进行解码
- HexEscapeSequenceA(混淆)
还原前后对比
UnicodeEscapeSequenceA,对正常内容进行 Unicode 编码混淆
Date.prototype.format = function (formatStr) {
let str = formatStr;
let Week = ["\u65e5", "\u4e00", "\u4e8c", "\u4e09", "\u56db", "\u4e94", "\u516d"];
str = str.replace(/yyyy|YYYY/, this.getFullYear());
str = str.replace(/MM/, this.getMonth() + 1 > 9 ? (this.getMonth() + 1).toString() : "\u0030" + (this.getMonth() + 1));
str = str.replace(/dd|DD/, this.getDate() + 1 > 9 ? (this.getDate() + 1).toString() : "\u0030" + (this.getDate() + 1));
return str;
};
console.log(new Date().format("\u0079\u0079\u0079\u0079\u002d\u004d\u004d\u002d\u0064\u0064"));
UnicodeEscapeSequenceB,对 Unicode 混淆过的代码进行还原
Date.prototype.format = function (formatStr) {
let str = formatStr;
let Week = ["日", "一", "二", "三", "四", "五", "六"];
str = str.replace(/yyyy|YYYY/, this.getFullYear());
str = str.replace(/MM/, this.getMonth() + 1 > 9 ? (this.getMonth() + 1).toString() : "0" + (this.getMonth() + 1));
str = str.replace(/dd|DD/, this.getDate() + 1 > 9 ? (this.getDate() + 1).toString() : "0" + (this.getDate() + 1));
return str;
};
console.log(new Date().format("yyyy-MM-dd"));
HexEscapeSequenceA,对正常内容进行 Hex 编码混淆
Date.prototype.format = function (formatStr) {
let str = formatStr;
let Week = ["\u65e5", "\u4e00", "\u4e8c", "\u4e09", "\u56db", "\u4e94", "\u516d"];
str = str.replace(/yyyy|YYYY/, this.getFullYear());
str = str.replace(/MM/, this.getMonth() + 0x1 > 0x9 ? (this.getMonth() + 0x1).toString() : "\x30" + (this.getMonth() + 0x1));
str = str.replace(/dd|DD/, this.getDate() + 0x1 > 0x9 ? (this.getDate() + 0x1).toString() : "\x30" + (this.getDate() + 0x1));
return str;
};
console.log(new Date().format("\x79\x79\x79\x79\x2d\x4d\x4d\x2d\x64\x64"));
HexEscapeSequenceB,对 Hex 混淆后的代码进行还原
Date.prototype.format = function (formatStr) {
let str = formatStr;
let Week = ["日", "一", "二", "三", "四", "五", "六"];
str = str.replace(/yyyy|YYYY/, this.getFullYear());
str = str.replace(/MM/, this.getMonth() + 1 > 9 ? (this.getMonth() + 1).toString() : "0" + (this.getMonth() + 1));
str = str.replace(/dd|DD/, this.getDate() + 1 > 9 ? (this.getDate() + 1).toString() : "0" + (this.getDate() + 1));
return str;
};
console.log(new Date().format("yyyy-MM-dd"));
混淆与还原思路
UnicodeEscapeSequence 插件的混淆与还原思路,我们先准备两行代码
var a = "yyyy-MM-dd"
var b = "\u0079\u0079\u0079\u0079\u002d\u004d\u004d\u002d\u0064\u0064"
把上述代码复制到 https://astexplorer.net/ AST 结构解析网站中,观察两个变量的初始化节点,分别如下
发现两个变量的 extra
中的 raw
不同,那么混淆以及还原的思路如下:
- 遍历
StringLiteral
节点 - 判断当前节点是否包含
extra
属性- 如果是混淆就把编码内容赋值给
extra
- 如果要还原直接删除
extra
属性即可(官网手册查询得知,NumericLiteral、StringLiteral类型的extra节点并非必需,这样在将其删除时,不会影响原节点。
)
- 如果是混淆就把编码内容赋值给
HexEscapeSequence 插件原理和 UnicodeEscapeSequence 插件的思路是一模一样的。
插件部分代码
UnicodeEscapeSequenceA.js
function fix(path) {
const node = path.node;
let value = node.value;
if (node.extra && (/\\[ux]/gi.test(node.extra.raw) === false)) {
value = str2unicode(value);
node.extra.raw = '"' + value + '"';
}
}
function str2unicode(str) {
let unicodeStr = "", tmp;
for (const val of str) {
tmp = val.codePointAt(0).toString(16);
while (tmp.length < 4) {
tmp = '0' + tmp;
}
unicodeStr += "\\u" + tmp;
}
return unicodeStr;
}
UnicodeEscapeSequenceB.js
function fix(path) {
const node = path.node;
if (node.extra && /\\[ux]/gi.test(node.extra.raw)) {
node.extra = undefined;
}
}
完整代码
关注公众号【趣码周
】回复 AST
即可获得。
关注我们
微信公众号:【趣码周
】
关注公众快速了解最新文章。