背景
多年前学过龙书,一来当时本身也没看懂,二来时间也长也差不多都忘记了。直到最近有 deobfuscate
问题才看了下AST。
说实话,一旦稍微了解AST和熟悉了 Babel 接口,deobfuscate 实在不是啥难事。
反混淆总结放前面。
注意事项
最重要的就2条:
- 开源/简单的混淆方案,现有的基本上都能解决;如:https://deobfuscator.kuizuo.cn/ 和 https://github.com/Tsaiboss/decodeObfuscator
- 其它混淆方案,特别是商用的或者是变形后的 deobfuscate 需要注意以下几点:
- 务必要处理掉反逆向逻辑,如:死循环这类;
- 使用 astexplorer 务必分析好特征,一般来讲开源的处理不了基本上是因为特征变了,其核心是一样的。一旦你补上了对应的特征基本上使用前面的开源项目就达到目的了。
- 必要的动态调试(浏览器debug)少不了,因为可以非常方便进行对比验证;
JS混淆总的来讲是提升 deobfuscate 难度,和其它安全类产品类似。JS毕竟是运行在客户端,想要完全的避免是不可能的。甚至是Native APP VM加壳这种都不能完全避免。
实战
证书问题
官网(https://inv-veri.chinatax.gov.cn/)使用自签证书,如果没有安装跟证书会出现:
浏览器解决方案有下面几种:
- 第一种是不管,直接点击继续访问。但是各省市的网站需要都点击一次。比较麻烦。
- 第二种是安装跟证书,参考:https://inv-veri.chinatax.gov.cn/zdaz.html ,Windows平台建议选择自动安装。
- 第三种浏览器里面设置忽略证书验证,设置浏览器启动参数
-ignore-certificate-errors
,参考:https://www.likecs.com/show-304407.html
接口分析
整个功能就2个接口,一个验证码接口一个发票查验接口,对应如下:
- yzmQuery:获取验证码。 eb23.js 文件。
- vatQuery:获取发票数据。被 eval 隐藏,请求在 90a1c.js 文件。
我们分别对这2个接口进行分析。
代码分析见后面的 源码整理。
验证码接口 yzmQuery
先在浏览器中抓个包,接口如下:
https://fpcy.guangdong.chinatax.gov.cn/NWebQuery/yzmQuery?callback=jQuery36108117432652732104_1665738094937&fpdm=224420000000&fphm=00234125&r=0.8586908933148021&v=2.0.11_060&nowtime=1665738219455&publickey=1665738219455&key9=6ed669ba8da29e71cd3f582907f3840a&_=1665738094939&flwq39=eoyleUZchlfg6YBgVcLURuRzQ3Z8GRI148s3JKhei5SSK%2BfibNFq7YxxZT6NO2aXSzrimz7GoKWos%2BVh2bdBiAT4tMlYqyQB%2BQY1%2BD5AmUv1nfu2stTeQCJVCHzhRcmHFoV2VPTq%2BBegLJwOatgO4yYY59uStSsc1nT6qvBcaf4%3D
具体参数如下:
callback: jQuery36108117432652732104_1665738094937
fpdm: 224420000000
fphm: 00234125
r: 0.8586908933148021
v: 2.0.11_060
nowtime: 1665738219455
publickey: 1665738219455
key9: 6ed669ba8da29e71cd3f582907f3840a
_: 1665738094939
flwq39: eoyleUZchlfg6YBgVcLURuRzQ3Z8GRI148s3JKhei5SSK+fibNFq7YxxZT6NO2aXSzrimz7GoKWos+Vh2bdBiAT4tMlYqyQB+QY1+D5AmUv1nfu2stTeQCJVCHzhRcmHFoV2VPTq+BegLJwOatgO4yYY59uStSsc1nT6qvBcaf4=
此接口返回内容下:
jQuery36108117432652732104_1665738094937({
"key1": "xxx", // 验证码的base64编码
"key2": "2022-10-14 16:44:47", // 时间,发票数据接口要使用
"key3": "dda81561f442e25c978cb7d5b2139928", // key,发票数据接口要使用
"key4": "01", // 颜色
"key5": "2",
"key6": "8ae8f8fe838974270183d5bc260e5ae3" // key,发票数据接口要使用
})
我们对上面参数进行分类:
第一类是jsoup请求参数:callback
和 _
callback
和 _
, 前面是随机字符串,可要可不要。不要的化服务器会直接返回json格式数据。_
参数是当前时间(参考后面!)。
发票参数: fpdm
、fphm
fpdm
、fphm
发票代码和发票号码。注意新的电子发票是 20位发票号码,需要拆分。
获取当前时间参考
环境参数: nowtime
,r
, v
, publickey
这4个参数中只有 v是版本号的意思,在 validate.js 中,自己可以打开 https://inv-veri.chinatax.gov.cn/js/validate.js?0.3320348734641909 看。 r 参数是随机值,可以自己取。 nowtime
和 publickey
和 都是当前时间,参考前面的 showTime()
接口。
加密参数:key9
、flwq39
这重点。
参数 key9 是 算出来的,来源参考:
这个参数的三个参数分别是 发票代码、发票号码、时间。我们自己可以验证。
跟踪$.nnyd.yzm
函数可以发现对应位置,方式如下:
- 控制台输入函数名称
$.nnyd.yzm
。 - 点击呈现的代码。
在新窗口中格式化代码并搜索关键字。
flwq39
参数:这个参数是放到 ajaxSetup
中,如下所示:
$.ajaxSetup({
"beforeSend": function (_0xc6fbc4, _0x5a4363) {
if (_0x5a4363.url.indexOf("127.0.0.1") == -1) {
_0x5a4363.url += _0x5a4363.url.match(/\?/) ? "&" : "?";
if (_0x5a4363.url.indexOf("yzmQuery") >= 0) {
_0x5a4363.url += "flwq39=" + _0x3ed54a(_0x5a4363);
dlqee = new Date().getTime() + 2000;
}
if (_0x5a4363.url.indexOf("vatQuery") >= 0) {
var _0x61b5fc = new Date().getTime() < dlqee;
_0x5a4363.url += "flwq39=" + _0x51824e(_0x5a4363, _0x61b5fc);
}
}
}
});
查验接口 vatQuery
如果你看前面的验证码接口,那么发现此接口和之前基本上是一样的。
看实际地址:
https://fpcy.guangdong.chinatax.gov.cn/NWebQuery/vatQuery?callback=jQuery36108117432652732104_1665738094937&key1=224420000000&key2=00234125&key3=20221013&key4=52200&fplx=09&yzm=zsj&yzmSj=2022-10-14%2016%3A45%3A04&index=e4c1e5acc4313342aa2e62cbb6dffdbd&key6=8ae8f8fe8389744a0183d5bc662d6b1e&publickey=2022-10-14%2016%3A45%3A04&key9=1065daa3d1a4b8a8b85e8ec531d13bac&_=1665738094940&flwq39=lYNyPq6FOGowj1WkJsBztPN8Gq%2FS4tfy9STQYrQoDbV7ac1KrlFeU8MopI9BGNo7Xf7VaCRDKeE6UNpps2UlMcC1IlYabqVzRog0VFh%2FNDrXPgylod3E4UjLDE3l6mk5A%2B57SMzNe%2BV7gSH7a9%2BhYTJ42B5wRTPT2ph2hNJoff4%3D
对应参数:
callback: jQuery36108117432652732104_1665738094937
key1: 224420000000
key2: 00234125
key3: 20221013
key4: 52200
fplx: 09
yzm: zsj
yzmSj: 2022-10-14 16:45:04
index: e4c1e5acc4313342aa2e62cbb6dffdbd
key6: 8ae8f8fe8389744a0183d5bc662d6b1e
publickey: 2022-10-14 16:45:04
key9: 1065daa3d1a4b8a8b85e8ec531d13bac
_: 1665738094940
flwq39:
lYNyPq6FOGowj1WkJsBztPN8Gq/S4tfy9STQYrQoDbV7ac1KrlFeU8MopI9BGNo7Xf7VaCRDKeE6UNpps2UlMcC1IlYabqVzRog0VFh/NDrXPgylod3E4UjLDE3l6mk5A+57SMzNe+V7gSH7a9+hYTJ42B5wRTPT2ph2hNJoff4=
第一类是jsoup请求参数:callback
和 _
见验证码接口
发票参数: key1
、key2
、key3
、key4
key1
:发票代码key2
发票号码。注意新的电子发票是 20位发票号码,需要拆分。key3
开票日期key4
发票金额,注意不同类型的发票这个值不一样,可能是校验码,也可能是 不含税进金额,也可能是含税金额。yzm
验证码fplx
发票类型,由 alxd 函数获取。如下所示:
alxd 代码获取请参考前面的$.nnyd.yzm
。
上下文参数: yzmSj
、index
、publickey
、key6
yzmSj
验证码接口返回的index
验证码接口返回的 key3publickey
验证码接口返回的时间。key6
验证码接口返回的key6
加密参数:key9
、flwq39
擦看前面的方式可以发现, key9
是 $.nnyd.cy
接口返回的,如下所示。
flwq39
参数见前面。
部分结果源码整理
// 获取发票的接口。
function getYzmXx() {
$.pricode.clearA();
show_yzm = "1";
var _0x436381 = $("#fpdm").val().trim();
if ("" === _0x436381) {
_0x436381 = $("#fphm").val().trim();
}
var _0x58c97a = getSwjg(_0x436381, 0);
if (!_0x58c97a[1]) {
jAlert("<div id='popup_message'>请输入正确的代码号码!</div>", "提示");
return;
}
var _0x3bab81 = _0x58c97a[1] + "/yzmQuery";
var _0x469bdb = showTime().toString();
var _0x4c4952 = $("#fpdm").val().trim();
var _0x5699c9 = $("#fphm").val().trim();
var _0x43c245 = $("#kjje").val().trim();
var _0x4b069d = Math.random();
var _0x1d0e71 = _0x58c97a[2];
if (_0x5699c9.length === 20) {
// 处理20位发票号码,前面12位分割成发票代码,后面8位分割位发票号码。
_0x4c4952 = _0x5699c9.substring(0, 12);
_0x5699c9 = _0x5699c9.substring(12);
}
var _0x28b87b = {
"fpdm": _0x4c4952,
"fphm": _0x5699c9,
"r": _0x4b069d,
"v": VVV,
"nowtime": _0x469bdb,
"area": _0x1d0e71,
"publickey": _0x469bdb,
"key9": $.nnyd.yzm(_0x4c4952, _0x5699c9, _0x469bdb)
};
$.ajaxSetup({
"cache": false
});
yzmFlag = 1;
$.ajax({
"type": "post",
"url": _0x3bab81,
"data": _0x28b87b,
"dataType": "jsonp",
"jsonp": "callback",
"success": function (_0x165bcc) {
// 省略....
},
"timeout": 5000,
"error": function (_0x283219, _0x4d7b1f, _0xb6705a) {
if (retrycount == 9) {
jAlert("系统繁忙,请稍后重试!", "提示");
} else {
// 省略....
}
});
function showTime() {
const _0x2969c0 = new Date();
return _0x2969c0.getTime();
}
发票查验接口所在代码:
$("#checkfp").hide();
$("#uncheckfp").show();
var _0x2e8cad = 5;
var _0x320d7e = $("#yzm").val().trim();
_0x2e8cad = 6;
var _0x53ae8a;
var _0x2730af = '';
_0x53ae8a = 8;
var _0x23d188 = null;
var _0x49fc66 = '';
if (avai(fplx)) {
if (fplx == '01' || fplx == '02' || fplx == '03' || fplx == '08' || fplx == '09' || fplx == '83' || fplx == '61') {
var _0x472fbd;
var _0x312fb7 = _0x57615f.indexOf('.');
_0x472fbd = 11;
if (_0x312fb7 > 0) {
var _0x1d99cd = _0x57615f.split('.');
if (_0x1d99cd[1] == '00' || _0x1d99cd[1] == '0') {
_0x57615f = _0x1d99cd[0];
} else {
if (_0x1d99cd[1].charAt(1) == '0') {
_0x57615f = _0x1d99cd[0] + '.' + _0x1d99cd[1].charAt(0);
}
}
}
}
if (_0x277a13.length === 20) {
_0x3e827f = _0x277a13.substring(0, 12);
_0x277a13 = _0x277a13.substring(12);
}
var _0x181fe9;
var _0x1220aa = _0x5f273a[1];
_0x181fe9 = 11;
_0x49fc66 = _0x1220aa + "/vatQuery";
_0x23d188 = {
'key1': _0x3e827f,
'key2': _0x277a13,
'key3': _0x57f456,
'key4': _0x57615f,
'fplx': fplx,
'yzm': _0x320d7e,
'yzmSj': yzmSj,
'index': jmmy,
'area': _0x3fb76c,
'key6': key6,
'publickey': yzmSj,
'key9': $.nnyd.cy(_0x3e827f, _0x277a13, yzmSj)
};
if (oldweb == 1) {
_0x49fc66 = _0x1220aa + "/invQuery";
_0x23d188 = {
'fpdm': _0x3e827f,
'fphm': _0x277a13,
'kprq': _0x57f456,
'fpje': _0x57615f,
'fplx': fplx,
'yzm': _0x320d7e,
'yzmSj': yzmSj,
'index': jmmy,
'area': _0x3fb76c,
'key6': key6,
'publickey': yzmSj,
'key9': $.nnyd.cy(_0x3e827f, _0x277a13, yzmSj)
};
}
delayMessage = "发票查验请求失败!";
showTime();
var _0x201fbe = 'N';
$.ajax({
'type': "post",
'url': _0x49fc66,
'dataType': "jsonp",
'data': _0x23d188,
'jsonp': "callback",
// 省略....
});
重点算法分析
涉及的接口 $.nnyd.yzm
、 $.nnyd.cy
、flw39
。
涉及的算法有:md5、RSA、Base64。
流程大体如下:
- 对发票代码、发票号号码、当前时间分别组合进行 Base64 ;
- 分别组合算MD5;
- 对MD5进行RSA加密; 92da1b9c13d7432c8eae5aa66e641262.js 文件
- 对RSA结果进行URL编码;
除此之外还有其它的东西:
- 统计识别失败次数;
- 检查环境;常见爬虫的特征检查。
参考及工具
- https://juejin.cn/post/7045604250126123015
- https://github.com/jamiebuilds/babel-handbook/blob/master/translations/zh-Hans/plugin-handbook.md#builders
- https://astexplorer.net/
- https://lzc6244.github.io/2021/07/27/Babel-AST%E5%85%A5%E9%97%A8.html
- https://steakenthusiast.github.io/