赞
踩
本文教程针对的是2021年7月2日时国税查验平台的js分析,其中版本号为V2.0.06_009。主要分析内容为key9和flwq39以及fplx这3个参数的算法,其中key9分为获取验证码阶段和查验阶段,算法有所区别,flwq39同理。
教程开始:
一、官方网址
二、请求分析
国税查验平台请求共分为2个,第一个请求获取验证码,第二个请求为输入验证码后查验数据并返回发票详细信息。
第一步:安装证书
基础:谷歌浏览器
首先需要安装证书,建议选择自动安装,在某些教程中可能选择手动安装,以便于使用ast工具挂代理找内存数据,但是这里我们采用一种新的方法来解决js逆向问题,因此那种方便用那种。
第二步:抓包
基础:谷歌浏览器+开发者工具+js基础知识
我们使用开发者工具或按F12打开谷歌浏览器的开发者工具,请注意尽量在网页加载完成后打开开发者工具,界面如下:
官方很给力,直接debug了,至于如何去掉断点,可以参考百度上的其他教程,这里给出一个办法,上图中点击数据1所处位置,使其颜色为蓝色,点击2或者点击上方的debug恢复按钮。此时,我们成功绕过了debug,但是特别注意尽量关闭其他无用软件或者网页,因为在后面的操作中,会感到特别卡顿。
我们在发票代码输入框和发票号码中输入合法的数据,并在开发者工具模块,观察所发送的请求包:
在这个请求中,分为url的网址前缀部分和尾部参数,其中尾部参数如下:
- callback=固定值
- fpdm=发票代码
- fphm=发票号码
- r=随机数
- V=版本号
- nowtime=当前时间减去1分钟
- publickey=当前时间减去1分钟
- key9=加密参数
- _=可为固定值
- flwq39=加密参数
如果你观察过会发现,不同地区的发票,请求的地址不同,如这里是anhui。该请求的前缀部分在一个js文件中,如图:
但是,很不幸,当你打开这个文件时,你会发现混淆了,不过很容易发现是sojson的样子,这并不影响我们逆向,我们使用http://tool.yuanrenxue.com/decode_obfuscator这个网站可以解密这些js代码,尽管解密的代码看着很头疼,但是已经比原始的好太多了。
解密后可以得到各个地区的ip和网址以及对应的地区代码:
- 0: {code: "1100", sfmc: "北京", Ip: "https://fpcy.beijing.chinatax.gov.cn:443/NWebQuery", address: "https://fpcy.beijing.chinatax.gov.cn:443"}
- 1: {code: "1200", sfmc: "天津", Ip: "https://fpcy.tjsat.gov.cn:443/NWebQuery", address: "https://fpcy.tjsat.gov.cn:443"}
- 2: {code: "1300", sfmc: "河北", Ip: "https://fpcy.hebei.chinatax.gov.cn/NWebQuery", address: "https://fpcy.hebei.chinatax.gov.cn"}
- 3: {code: "1400", sfmc: "山西", Ip: "https://fpcy.shanxi.chinatax.gov.cn:443/NWebQuery", address: "https://fpcy.shanxi.chinatax.gov.cn:443"}
- 4: {code: "1500", sfmc: "内蒙古", Ip: "https://fpcy.neimenggu.chinatax.gov.cn:443/NWebQuery", address: "https://fpcy.neimenggu.chinatax.gov.cn:443"}
- 5: {code: "2100", sfmc: "辽宁", Ip: "https://fpcy.liaoning.chinatax.gov.cn:443/NWebQuery", address: "https://fpcy.liaoning.chinatax.gov.cn:443"}
- 6: {code: "2102", sfmc: "大连", Ip: "https://sbf.dalian.chinatax.gov.cn:8402/NWebQuery", address: "https://sbf.dalian.chinatax.gov.cn:8402"}
- 7: {code: "2200", sfmc: "吉林", Ip: "https://fpcy.jilin.chinatax.gov.cn:4432/NWebQuery", address: "https://fpcy.jilin.chinatax.gov.cn:4432"}
- 8: {code: "2300", sfmc: "黑龙江", Ip: "https://fpcy.heilongjiang.chinatax.gov.cn:443/NWebQuery", address: "https://fpcy.heilongjiang.chinatax.gov.cn:443"}
- 9: {code: "3100", sfmc: "上海", Ip: "https://fpcy.shanghai.chinatax.gov.cn:1001/NWebQuery", address: "https://fpcy.shanghai.chinatax.gov.cn:1001"}
- 10: {code: "3200", sfmc: "江苏", Ip: "https://fpcy.jiangsu.chinatax.gov.cn:80/NWebQuery", address: "https://fpcy.jiangsu.chinatax.gov.cn:80"}
- 11: {code: "3300", sfmc: "浙江", Ip: "https://fpcy.zhejiang.chinatax.gov.cn:443/NWebQuery", address: "https://fpcy.zhejiang.chinatax.gov.cn:443"}
- 12: {code: "3302", sfmc: "宁波", Ip: "https://fpcy.ningbo.chinatax.gov.cn:443/NWebQuery", address: "https://fpcy.ningbo.chinatax.gov.cn:443"}
- 13: {code: "3400", sfmc: "安徽", Ip: "https://fpcy.anhui.chinatax.gov.cn:443/NWebQuery", address: "https://fpcy.anhui.chinatax.gov .cn:443"}
- 14: {code: "3500", sfmc: "福建", Ip: "https://fpcy.fujian.chinatax.gov.cn:443/NWebQuery", address: "https://fpcy.fujian.chinatax.gov.cn:443"}
- 15: {code: "3502", sfmc: "厦门", Ip: "https://fpcy.xiamen.chinatax.gov.cn/NWebQuery", address: "https://fpcy.xiamen.chinatax.gov.cn"}
- 16: {code: "3600", sfmc: "江西", Ip: "https://fpcy.jiangxi.chinatax.gov.cn:82/NWebQuery", address: "https://fpcy.jiangxi.chinatax.gov.cn:82"}
- 17: {code: "3700", sfmc: "山东", Ip: "https://fpcy.shandong.chinatax.gov.cn:443/NWebQuery", address: "https://fpcy.shandong.chinatax.gov.cn:443"}
- 18: {code: "3702", sfmc: "青岛", Ip: "https://fpcy.qingdao.chinatax.gov.cn:443/NWebQuery", address: "https://fpcy.qingdao.chinatax.gov.cn:443"}
- 19: {code: "4100", sfmc: "河南", Ip: "https://fpcy.henan.chinatax.gov.cn/NWebQuery", address: "https://fpcy.henan.chinatax.gov.cn"}
- 20: {code: "4200", sfmc: "湖北", Ip: "https://fpcy.hubei.chinatax.gov.cn:443/NWebQuery", address: "https://fpcy.hubei.chinatax.gov.cn:443"}
- 21: {code: "4300", sfmc: "湖南", Ip: "https://fpcy.hunan.chinatax.gov.cn:8083/NWebQuery", address: "https://fpcy.hunan.chinatax.gov.cn:8083"}
- 22: {code: "4400", sfmc: "广东", Ip: "https://fpcy.guangdong.chinatax.gov.cn:443/NWebQuery", address: "https://fpcy.guangdong.chinatax.gov.cn:443"}
- 23: {code: "4403", sfmc: "深圳", Ip: "https://fpcy.shenzhen.chinatax.gov.cn:443/NWebQuery", address: "https://fpcy.shenzhen.chinatax.gov.cn:443"}
- 24: {code: "4500", sfmc: "广西", Ip: "https://fpcy.guangxi.chinatax.gov.cn:8200/NWebQuery", address: "https://fpcy.guangxi.chinatax.gov.cn:8200"}
- 25: {code: "4600", sfmc: "海南", Ip: "https://fpcy.hainan.chinatax.gov.cn:443/NWebQuery", address: "https://fpcy.hainan.chinatax.gov.cn:443"}
- 26: {code: "5000", sfmc: "重庆", Ip: "https://fpcy.chongqing.chinatax.gov.cn:80/NWebQuery", address: "https://fpcy.chongqing.chinatax.gov.cn:80"}
- 27: {code: "5100", sfmc: "四川", Ip: "https://fpcy.sichuan.chinatax.gov.cn:443/NWebQuery", address: "https://fpcy.sichuan.chinatax.gov.cn:443"}
- 28: {code: "5200", sfmc: "贵州", Ip: "https://fpcy.guizhou.chinatax.gov.cn:80/NWebQuery", address: "https://fpcy.guizhou.chinatax.gov.cn:80"}
- 29: {code: "5300", sfmc: "云南", Ip: "https://fpcy.yunnan.chinatax.gov.cn:443/NWebQuery", address: "https://fpcy.yunnan.chinatax.gov.cn:443"}
- 30: {code: "5400", sfmc: "西藏", Ip: "https://fpcy.xztax.gov.cn:81/NWebQuery", address: "https://fpcy.xztax.gov.cn:81"}
- 31: {code: "6100", sfmc: "陕西", Ip: "https://fpcy.shaanxi.chinatax.gov.cn:443/NWebQuery", address: "https://fpcy.shaanxi.chinatax.gov.cn:443"}
- 32: {code: "6200", sfmc: "甘肃", Ip: "https://fpcy.gansu.chinatax.gov.cn:443/NWebQuery", address: "https://fpcy.gansu.chinatax.gov.cn:443"}
- 33: {code: "6300", sfmc: "青海", Ip: "https://fpcy.qinghai.chinatax.gov.cn:443/NWebQuery", address: "https://fpcy.qinghai.chinatax.gov.cn:443"}
- 34: {code: "6400", sfmc: "宁夏", Ip: "https://fpcy.ningxia.chinatax.gov.cn:443/NWebQuery", address: "https://fpcy.ningxia.chinatax.gov.cn:443"}
- 35: {code: "6500", sfmc: "新疆", Ip: "https://fpcy.xj-n-tax.gov.cn:443/NWebQuery", address: "https://fpcy.xj-n-tax.gov.cn:443"}
回到抓包中,我们观察到这时发送了一个获取验证码的请求:
注意到通过分析这个url,我们发现请求来自如下图,我们注意到getyzmxx这个函数,来自eab23.js这个文件里,我们提取这个文件使用ob解密。
ob解密后的数据如下:
- var _0x14658c = function () {
- var _0x2253f1 = true;
- return function (_0x41c5d5, _0x508a27) {
- var _0x47fbda = _0x2253f1 ? function () {
- if (_0x508a27) {
- var _0x6b4e27 = _0x508a27["apply"](_0x41c5d5, arguments);
-
- _0x508a27 = null;
- return _0x6b4e27;
- }
- } : function () {};
-
- _0x2253f1 = false;
- return _0x47fbda;
- };
- }();
-
- var _0x59e564 = _0x14658c(this, function () {
- var _0x595419 = function () {
- return "dev";
- },
- _0x4c5231 = function () {
- return "window";
- };
-
- var _0x1842a8 = function () {
- var _0x21d015 = new RegExp("\\w+ *\\(\\) *{\\w+ *['|\"].+['|\"];? *}");
-
- return !_0x21d015["test"](_0x595419["toString"]());
- };
-
- var _0x113bb3 = function () {
- var _0x326224 = new RegExp("(\\\\[x|u](\\w){2,4})+");
-
- return _0x326224["test"](_0x4c5231["toString"]());
- };
-
- var _0x185d70 = function (_0x4ac461) {
- var _0x557ade = 0;
-
- if (_0x4ac461["indexOf"](false)) {
- _0x3cbd92(_0x4ac461);
- }
- };
-
- var _0x3cbd92 = function (_0x2bb9d8) {
- var _0x5f4806 = 3;
-
- if (_0x2bb9d8["indexOf"]("true"[3]) !== _0x5f4806) {
- _0x185d70(_0x2bb9d8);
- }
- };
-
- if (!_0x1842a8()) {
- if (!_0x113bb3()) {
- _0x185d70("ind\u0435xOf");
- } else {
- _0x185d70("indexOf");
- }
- } else {
- _0x185d70("ind\u0435xOf");
- }
- });
-
- _0x59e564();
-
- var yzmWait = 2;
- var retrycount = 0;
- $(document)["ready"](function () {
- $("#fpdm")["blur"](function () {
- retrycount = 0;
- });
- });
-
- function yzmTime(_0x5c47fb) {
- if (yzmWait == 0) {
- $("#yzm_unuse_img")["hide"]();
- $("#yzm_img")["show"]();
- yzmWait = 60;
- } else {
- if (yzmWait == 2) {
- $("#yzm_unuse_img")["show"]();
- $("#yzm_img")["hide"]();
- }
-
- yzmWait--;
- setTimeout(function () {
- yzmTime(_0x5c47fb);
- }, 1000);
- }
- }
-
- function getYzmXx() {
- $["pricode"]["clearA"]();
- show_yzm = "1";
-
- var _0x31767b = $("#fpdm")["val"]()["trim"]();
-
- var _0x4dfb9 = getSwjg(_0x31767b, 0);
-
- var _0x3297c1 = _0x4dfb9[1] + "/yzmQuery";
-
- var _0x41afd2 = showTime()["toString"]();
-
- var _0x1912a2 = $("#fpdm")["val"]()["trim"]();
-
- var _0x478041 = $("#fphm")["val"]()["trim"]();
-
- var _0x1a5014 = $("#kjje")["val"]()["trim"]();
-
- var _0x70138f = Math["random"]();
-
- var _0x1d5629 = _0x4dfb9[2];
- var _0x18c20a = {
- "fpdm": _0x1912a2,
- "fphm": _0x478041,
- "r": _0x70138f,
- "v": VVV,
- "nowtime": _0x41afd2,
- "area": _0x1d5629,
- "publickey": _0x41afd2,
- "key9": $["nnyd"]["yzm"](_0x1912a2, _0x478041, _0x41afd2)
- };
- $["ajaxSetup"]({
- "cache": false
- });
- yzmFlag = 1;
- $["ajax"]({
- "type": "post",
- "url": _0x3297c1,
- "data": _0x18c20a,
- "dataType": "jsonp",
- "jsonp": "callback",
- "success": function (_0x135eb2) {
- if (_0x135eb2["hasOwnProperty"]("data")) {
- _0x135eb2 = _0x135eb2["data"];
- _0x135eb2 = replaceStr(_0x135eb2, _0x41afd2);
- _0x135eb2 = Base64["decode"](_0x135eb2);
- _0x135eb2 = eval("(" + _0x135eb2 + ")");
- }
-
- delayFlag = "1";
- var _0x2651d3 = _0x135eb2["key1"];
- var _0x1a96a9 = _0x135eb2["key2"];
- var _0x2b0c56 = _0x135eb2["key3"];
- var _0x1769c2 = _0x135eb2["key4"];
- var _0x483079 = _0x135eb2["key5"];
- var _0x2e8ee0 = _0x135eb2["key6"];
-
- if (_0x483079 == "2") {
- oldweb = 2;
- } else {
- if (_0x483079 == "1") {
- oldweb = 1;
- }
- }
-
- if (_0x2651d3 == "003") {
- jAlert("\u9A8C\u8BC1\u7801\u8BF7\u6C42\u6B21\u6570\u8FC7\u4E8E\u9891\u7E41\uFF0C\u8BF71\u5206\u949F\u540E\u518D\u8BD5\uFF01", "\u8B66\u544A");
- $("#yzm_img")["hide"]();
- } else {
- if (_0x2651d3 == "005") {
- jAlert("\u975E\u6CD5\u8BF7\u6C42!", "\u8B66\u544A");
- } else {
- if (_0x2651d3 == "010") {
- jAlert("\u7F51\u7EDC\u8D85\u65F6\uFF0C\u8BF7\u91CD\u8BD5\uFF01(01)", "\u8B66\u544A");
- } else {
- if (_0x2651d3 == "fpdmerr") {
- jAlert("\u8BF7\u8F93\u5165\u5408\u6CD5\u53D1\u7968\u4EE3\u7801!", "\u8B66\u544A");
- } else {
- if (_0x2651d3 == "024") {
- jAlert("24\u5C0F\u65F6\u5185\u9A8C\u8BC1\u7801\u8BF7\u6C42\u592A\u9891\u7E41\uFF0C\u8BF7\u7A0D\u540E\u518D\u8BD5\uFF01", "\u8B66\u544A");
- $("#yzm_img")["hide"]();
- } else {
- if (_0x2651d3 == "016") {
- jAlert("\u670D\u52A1\u5668\u63A5\u6536\u7684\u8BF7\u6C42\u592A\u9891\u7E41\uFF0C\u8BF7\u7A0D\u540E\u518D\u8BD5\uFF01", "\u8B66\u544A");
- $("#yzm_img")["hide"]();
- } else {
- if (_0x2651d3 == "020") {
- jAlert("\u7531\u4E8E\u67E5\u9A8C\u884C\u4E3A\u5F02\u5E38\uFF0C\u6D89\u5ACC\u8FDD\u89C4\uFF0C\u5F53\u524D\u65E0\u6CD5\u4F7F\u7528\u67E5\u9A8C\u670D\u52A1\uFF01", "\u63D0\u793A");
- } else {
- if (_0x2651d3 == "errv") {
- jAlert("\u5F53\u524D\u9875\u9762\u7248\u672C\u8F83\u4F4E\uFF0C\u8BF7\u6309CTRL-F5\u5237\u65B0\u9875\u9762\uFF01", "\u63D0\u793A");
- } else {
- if (_0x2651d3 != "") {
- setTimeout(function () {
- $("#yzm_img")["attr"]("src", "data:image/png;base64," + _0x2651d3);
- $("#yzm_unuse_img")["attr"]("src", "data:image/png;base64," + _0x2651d3);
-
- if (_0x1769c2 == "00") {
- $("#yzminfo")["text"]("\u8BF7\u8F93\u5165\u9A8C\u8BC1\u7801\u6587\u5B57");
- } else {
- if (_0x1769c2 == "01") {
- $("#yzminfo")["html"]("\u8BF7\u8F93\u5165\u9A8C\u8BC1\u7801\u56FE\u7247\u4E2D<font color=\"red\" size=\"4\" style=\"background:#C0C0C0\">\u7EA2\u8272</font>\u6587\u5B57");
- } else {
- if (_0x1769c2 == "02") {
- $("#yzminfo")["html"]("\u8BF7\u8F93\u5165\u9A8C\u8BC1\u7801\u56FE\u7247\u4E2D<font color=\"yellow\" size=\"4\" style=\"background:#C0C0C0\">\u9EC4\u8272</font>\u6587\u5B57");
- } else {
- if (_0x1769c2 == "03") {
- $("#yzminfo")["html"]("\u8BF7\u8F93\u5165\u9A8C\u8BC1\u7801\u56FE\u7247\u4E2D<font color=\"blue\" size=\"4\" style=\"background:#C0C0C0\">\u84DD\u8272</font>\u6587\u5B57");
- }
- }
- }
- }
-
- yzmSj = _0x1a96a9;
- jmmy = _0x2b0c56;
- key6 = _0x2e8ee0;
- }, 500);
- }
- }
- }
- }
- }
- }
- }
- }
- }
- },
- "timeout": 5000,
- "error": function (_0x11ade1, _0x2c42b8, _0x3a7f30) {
- if (retrycount == 9) {
- jAlert("\u7CFB\u7EDF\u7E41\u5FD9\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5!", "\u63D0\u793A");
- } else {
- retrycount = retrycount + 1;
- getYzmXx();
- }
- }
- });
- yzmWait = 2;
- yzmTime($("#yzm_img"));
- }
-
- function showTime() {
- var _0x229b47 = new Date();
-
- var _0x2be691 = _0x229b47["getTime"]();
-
- return _0x2be691;
- }
我们注意到key9值的位置,
$["nnyd"]["yzm"](_0x1912a2, _0x478041, _0x41afd2)
这里需要知道$["nnyd"]["yzm"]的具体函数,分析后如下:
-
- _0x4abed0["prototype"]["yzm"] = function (_0x4a04f9, _0x1fcc0a, _0x4473d7) {
- var _0x3a01ad = new Array(3);
-
- _0x3a01ad[0] = _0x4a04f9 + _0x1fcc0a;
- _0x3a01ad[1] = _0x4a04f9 + _0x1fcc0a + _0x1fcc0a + _0x4a04f9;
- _0x3a01ad[2] = _0x4a04f9 + _0x4a04f9 + _0x1fcc0a + _0x4a04f9 + _0x1fcc0a + _0x4a04f9;
-
- var _0x155fec = _0x3a01ad[0]["substring"](0, 8);
-
- var _0x32d768 = _0x3a01ad[0]["substring"](8);
-
- var _0x559889 = _0x1e3a12(_0x155fec);
-
- var _0x5a173e = _0x559889["length"];
-
- for (var _0x2e3884 = 0; _0x2e3884 < _0x5a173e; _0x2e3884++) {
- for (var _0x39a07a = _0x2e3884 + 1; _0x39a07a < _0x5a173e; _0x39a07a++) {
- if (_0x559889[_0x2e3884] > _0x559889[_0x39a07a]) {
- _0x3f015b(_0x559889, _0x2e3884, _0x39a07a);
- }
- }
- }
-
- _0x155fec = _0x328421(_0x559889);
-
- var _0x3cf5d7 = _0x1e3a12(_0x32d768);
-
- var _0x5a173e = _0x3cf5d7["length"];
- var _0x38743f = 0;
-
- for (var _0x2e3884 = 0; _0x2e3884 < _0x5a173e; _0x2e3884++) {
- _0x38743f = _0x2e3884;
-
- for (var _0x39a07a = _0x2e3884 + 1; _0x39a07a < _0x5a173e; _0x39a07a++) {
- if (_0x3cf5d7[_0x38743f] > _0x3cf5d7[_0x39a07a]) {
- _0x38743f = _0x39a07a;
- }
- }
-
- _0x3f015b(_0x3cf5d7, _0x2e3884, _0x38743f);
- }
-
- _0x32d768 = _0x328421(_0x3cf5d7);
-
- var _0x8300db = _0x205ff1["encrypt"](_0x155fec) + _0x32d768;
-
- var _0x15339a = _0x3a01ad[1]["substring"](0, 9);
-
- var _0x3c7f1d = _0x3a01ad[1]["substring"](9, 17);
-
- var _0x1dd08a = _0x3a01ad[1]["substring"](17);
-
- var _0x591ade = _0x1e3a12(_0x15339a);
-
- var _0x5a173e = _0x591ade["length"];
- var _0x41c5e3 = 0;
-
- for (var _0x2e3884 = 0; _0x2e3884 < _0x5a173e; _0x2e3884++) {
- _0x41c5e3 = _0x2e3884;
-
- for (var _0x39a07a = _0x2e3884 + 1; _0x39a07a < _0x5a173e; _0x39a07a++) {
- if (_0x591ade[_0x41c5e3] > _0x591ade[_0x39a07a]) {
- _0x41c5e3 = _0x39a07a;
- }
- }
-
- _0x3f015b(_0x591ade, _0x2e3884, _0x41c5e3);
- }
-
- _0x15339a = _0x328421(_0x591ade);
-
- var _0x185cf9 = _0x1e3a12(_0x3c7f1d);
-
- var _0x2fa209 = _0x185cf9["length"];
-
- _0x3ad848["yzmdelay"](_0x4a04f9, _0x1fcc0a, _0x4473d7);
-
- console["log"]("\u9A8C\u8BC1\u7801\u9ED8\u8BA4\u5EF6\u65F6:312");
-
- for (var _0x2e3884 = 0; _0x2e3884 < _0x2fa209; _0x2e3884++) {
- for (var _0x39a07a = _0x2e3884 + 1; _0x39a07a < _0x2fa209; _0x39a07a++) {
- if (_0x185cf9[_0x2e3884] > _0x185cf9[_0x39a07a]) {
- _0x3f015b(_0x185cf9, _0x2e3884, _0x39a07a);
- }
- }
- }
-
- _0x3c7f1d = _0x328421(_0x185cf9);
-
- var _0x30d382 = _0x1e3a12(_0x1dd08a);
-
- var _0x3a8185 = _0x30d382["length"];
-
- for (var _0x2e3884 = 1; _0x2e3884 < _0x3a8185; _0x2e3884++) {
- for (var _0x39a07a = _0x2e3884; _0x39a07a > 0; _0x39a07a--) {
- if (_0x30d382[_0x39a07a] < _0x30d382[_0x39a07a - 1]) {
- _0x3f015b(_0x30d382, _0x39a07a, _0x39a07a - 1);
- } else {
- break;
- }
- }
- }
-
- _
这里只展示了部分数据,同时注意到一个参数,后面用得到:
0x5ecc18["setPublicKey"]("MIGdMA0GCSqGSIb3DQEBAQUAA4GLADCBhwKBgQCjrcnzPya+QS6i5QND2EsIcrsP3/NRwgcYoaNyowpuIzQGDIySfhifm/+j41vJqwKd0D5Otjn6lF5mpUz0zvgMlVco5YytXIbBR8n7WfJ/1W4kTWYr9PM/sT3P23fS3xt13NHln7XgEjP7juv6z52OQOKxYKL/LFxoLkhQpUydWQIBEQ==");
如果有经验,你会知道可能和RSA加密相关,事实也是如此,上面已经对key9的算法位置分析清楚了,下面对flwq39进行分析,实际上,flwq39也是在这个文件中,我使用vscode对解密后的js代码进行了格式化,并查找了flwq39这个关键字,发现如图:
因此,我们继续分析会发现该参数是在请求前拼接完成的,并且关键字本身被替换了:
反向找到调用的函数就会发现最终是调用了RSA加密算法,其中PublicKey就是
MIGdMA0GCSqGSIb3DQEBAQUAA4GLADCBhwKBgQCjrcnzPya+QS6i5QND2EsIcrsP3/NRwgcYoaNyowpuIzQGDIySfhifm/+j41vJqwKd0D5Otjn6lF5mpUz0zvgMlVco5YytXIbBR8n7WfJ/1W4kTWYr9PM/sT3P23fS3xt13NHln7XgEjP7juv6z52OQOKxYKL/LFxoLkhQpUydWQIBEQ==
在验证码获取后返回包如下:
jQuery11020854931324645166_1625223145135({"key1":"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","key2":"2021-07-02 18:34:48","key3":"43017c78ec891a4b9406501694f0b479","key4":"01","key5":"2","key6":"402887f17a53027c017a66d4d62d72d0"})
其中key1是经过base64编码的图片数据,key2是验证码的时间,另外注意到key3和key4及key6。
其中key4分为4种情况分别为输入全部、黄色、红色、蓝色,对应00,01,02,03。余下的参数在验证阶段用到。
同理,在查验阶段,请求如下:
这里注意到
- key1=发票代码
- key2=发票号码
- key3=发票日期
- key4=校验码或者不含税的金额
- fplx=发票类型,一个函数
- yzm=验证码
- yzmsj=获取验证码请求的返回包里的key2
- index=获取验证码请求的返回包里的Key3
- key6=获取验证码请求的返回包里的Key6
- publickey=验证码时间
- key9=加密参数
- flwq39=加密参数
这里的key9和flwq39在
$["nnyd"]["cy"]
这个对应的函数中,key9和flwq39算法和上面的类似。
此外注意到fplx这种参数,也在这几个js文件中,这里给出具体的函数名:
- var code=["144031539110", "131001570151", "133011501118", "111001571071"];
- function alxd(a) {
- var b;
- var c = "99";
- if (a.length == 12) {
- b = a.substring(7, 8);
- for (var i = 0; i < code.length; i++) {
- if (a == code[i]) {
- c = "10";
- break
- }
- }
- if (c == "99") {
- if (a.charAt(0) == '0' && a.substring(10, 12) == '11') {
- c = "10"
- }
- if (a.charAt(0) == '0' && (a.substring(10, 12) == '04' || a.substring(10, 12) == '05')) {
- c = "04"
- }
- if (a.charAt(0) == '0' && (a.substring(10, 12) == '06' || a.substring(10, 12) == '07')) {
- c = "11"
- }
- if (a.charAt(0) == '0' && a.substring(10, 12) == '12') {
- c = "14"
- }
- }
- if (c == "99") {
- if (a.substring(10, 12) == '17' && a.charAt(0) == '0') {
- c = "15"
- }
- if (c == "99" && b == 2 && a.charAt(0) != '0') {
- c = "03"
- }
- }
- } else if (a.length == 10) {
- b = a.substring(7, 8);
- if (b == 1 || b == 5) {
- c = "01"
- } else if (b == 6 || b == 3) {
- c = "04"
- } else if (b == 7 || b == 2) {
- c = "02"
- }
- }
- return c
- }
通过使用php的v8js拓展,我们可以实现接口编写:
二者结果一致。
至此,全部参数已经解密部分完毕。
第三步,算法的完全还原,脱离js环境,这里我使用C#实现算法还原和脱离
首先,我们需要知道的是在核心的算法中,源代码使用了ob进行了混淆,我们使用第三方软件进行初次还原时,也只能还原一部分,核心的内容基本会被还原,但是生成的代码还是无法完全被理解阅读,基于此,需要二次还原,这里注意到Ob混淆的核心思想,是一个大数组和若干个解密函数,后续的代码里不断的调用解密函数进行解密。
设计如下:通过分析可以注意到解密函数的前缀是_开头的,后续是)]或其他的组合,因此,我们需要使用提取函数名,在调用时,才能知道那些函数可以还原,同时在提取时也随同获取调用的参数,如果参数中包含变量,则直接忽略,并在调用成功后进行替换。
- string[] ret = getBValueT(djm, "_", "\\)");
- if (ret != null && ret.Count() > 0)
- {
- foreach (string s in ret)
- {
- if (s.IndexOf("=") == -1 && s.IndexOf(":") == -1 && s.IndexOf(",") != -1)
- {
- listBox1.Items.Add("_" + s + ")");
- listBox1.Items.Add("_" + s + "))");
- File.AppendAllText("function.txt", "_0x" + s + ")" + ";\r\n");
- }
- }
-
- }
- //第二种情况
- ret = getBValueT(djm, "_", "\\]");
- if (ret != null && ret.Count() > 0)
- {
- foreach (string s in ret)
- {
- if (s.IndexOf(",") != -1)
- {
- listBox1.Items.Add("_" + s + "]");
- listBox1.Items.Add("_" + s + "");
- File.AppendAllText("function.txt", "_" + s + ")" + ";\r\n");
- }
- }
-
- }
- //第三种情况
- ret = getBValueT(djm, "\\[_", "\\]");
- if (ret != null && ret.Count() > 0)
- {
- foreach (string s in ret)
- {
- if (s.IndexOf(",") != -1)
- {
- listBox1.Items.Add("[_" + s + "]");
- listBox1.Items.Add("_" + s + "");
- File.AppendAllText("function.txt", "_" + s + ")" + ";\r\n");
- }
- }
-
- }
- //第四种情况
- ret = getBValueT(djm, "\\(_", "\\)");
- if (ret != null && ret.Count() > 0)
- {
- foreach (string s in ret)
- {
- if (s.IndexOf(",") != -1)
- {
- listBox1.Items.Add("(_" + s + ")");
- listBox1.Items.Add("(_" + s + "))");
- File.AppendAllText("function.txt", "_" + s + ")" + ";\r\n");
- }
- }
- // label3.Text = "匹配到" + listBox1.Items.Count + "个函数";
- }
- try
- {
- List<string> ret = new List<string>();
- Regex regex = new Regex("(?<=(" + sStr + "))0x[0-9a-z]{4,6}[\\[|\\(][.\\s\\S]*?(?=(" + eStr + "))", RegexOptions.Singleline|RegexOptions.Multiline);
-
- MatchCollection mc = regex.Matches(str);
- for (int i = 0; i < mc.Count; i++)
- {
- ret.Add(mc[i].Value);
- }
- return ret.ToArray();
- }
- catch
- {
- return new string[] { };
- }
通过上述操作,我们就能最大程度还原出原始的代码段。
接着,彻底逆向还原使用其他语言改写,如果你看过本文,并对相关js有分析,你大概会知道,核心部分就是涉及几个加密算法,如md5和移位的一些操作。但其核心的算法如下:
第一个是Gen函数
第二个是key9
其他的类似。部分代码如下图:
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。