领域 发表于 2020-7-6 13:34

17track接口逆向分析

没有放修改后的完整代码,纯粹的学习,谁有更好方法找到_0x4f3f里内存暴破,请指教啊!
## 调试
17track的快递查询接口,可以通过调试工具看到请求,但是直接通过POST工具是没有结果的,最后测试需要加上Last-Event-ID这个cookie
![在这里插入图片描述]()
看了网页加载的js文件,最终只有一个加密的track.min.js,而且发现非常可疑的setCookie,还有Last-Event-ID字符。第一行是个数组,接下一共两个自执行的匿名函数。

```javascript
(function(_0x2297ee, _0x5f41f3) {
......
}(_0x50c7, 0x9f));
......
(function() {
    var _0x10f375
    ......
    _0x4cec2b();
}());
```
对这两个函数下断点,clear storage刷新然后一步一步调试。这里只要调用了函数的我们都对函数下断点。当跟到_0x4f3f这个函数的时候,发现很多地方都调用了这个函数,对返回下断点发现是返回的字符串。就把断点取消了继续跟,不然手要按软。第二个自执行函数最后调用的_0x4cec2b,_0x4cec2b最后又调用_0x3966d3,当断到_0x3966d3的时候已经能看到请求信息了,_0x164a16就是post的json,_0x152eb7就是一个ajax请求
![在这里插入图片描述]()
跟到_0x3966d3的时候,可以看到前两个参数是函数,最后是一个数字。继续一步一步走,注意_0x126b58 这个值是在变化的。
![在这里插入图片描述]()
最后在这里找到了Last-Event-ID,拿去post试一试,成功。

## 分析代码
我们还原设置Last-Event-ID的代码。
```javascript
document = _0x2ec18e(_0x316f08, '=') + _0x472ff2('') + _0x2ec18e + _0x1720d7();
```

直接在控制台调用_0x4f3f函数得到
```javascript
document["cookie"] = _0x2ec18e["GAOJi"]('Last-Event-ID', '=') + _0x472ff2["join"]('') + _0x2ec18e["FPaVy"] + _0x1720d7["toGMTString"]();

_0x472ff2["join"]('')//就是我们要的Last-Event-ID
```
_0x472ff2是重要变量,复制所有加密代码,格式化,放到新网页执行。哈哈哈,崩溃了。
![在这里插入图片描述]()
看来是有检测代码。这里我不知道怎么才能下断点,只有一步一步故意写错代码,看是先崩溃还是先报错确定了检测代码位置。有大佬有好方法没?

```javascript
_0x3184d0['prototype']['yzecjS'] = function() {
                var _0x5e1a47 = new RegExp(this['IbesCh'] + this['kRbSMK']);
                var _0x44235e = _0x5e1a47['test'](this['ZDEFCw']['toString']()) ? --this['LoqXko'] : --this['LoqXko'];//这里是一个正则匹配,把_0x44235e 设为-1就跳过了
                return this['NUSOYG'](_0x44235e);
            }
```
刷新执行,又报新的错误,这个错误直接显示了位置,下断点跟原网页对比
```javascript
var _0xd2c455 = function(_0x570d92) {
                var _0x6d545d = ~-0x4 >> 0x1 + 0xff % 0x0;
                if (_0x570d92['\x69\x6e\x64\x65\x78\x4f\x66']((!![] + '')) !== _0x6d545d) { //这个判断改为不成立即可跳过
                  _0x10cf6b(_0x570d92);
                }
            };
```
到这里,继续,还是报错,这个错误简单明了缺少jquery,在网页加上。搜索_0x3966d3 找到调用它的地方。
```javascript
      _0x2ec18e($, document)(function(_0x2c3354, _0x28d1d7, _0x152eb7) {
            if (_0x152eb7(_0x4f3f('0x8a', 'L*ZS')) && _0x152eb7['data']) {
                _0x567d6b = _0x152eb7;
                var _0x164a16;
                try {
                  _0x164a16 = JSON['parse'](_0x567d6b);
                } catch (_0x198faf) {
                  if (_0x2ec18e(_0x2ec18e, _0x4f3f('0x8e', 'PGF('))) {
                        var _0x574153 = _0x56b4ca(_0x3df0c1, str, seed);
                        if (tag) {
                            _0x472ff2 = _0x56b4ca(_0x2c724a, _0x574153(0x10));
                            return;
                        }
                        _0x472ff2 = _0x2c724a(_0x574153['toString'](0x10));
                  } else {
                        _0x164a16 = null;
                  }
                }
                if (_0x164a16 && _0x2ec18e(_0x164a16, '')) {
                  try {
                        _0x2ec18e(_0x9ff66b, _0x567d6b, _0x567d6b, !![]);
                        var _0x3fd2d0 = _0x4f3f('0x96', '7la!') + _0x4f3f('0x97', 'ttkq') + ']';
                        var _0x3a6885 = _0x2ec18e($, _0x3fd2d0);
                        _0x3966d3(_0x28d1d7, _0x3a6885, _0x1a395d(_0x3a6885));
                  } catch (_0x1739bd) {}
                }
            }
      });
```
还原后

```javascript
      $(document).ajaxSend(function (_0x2c3354, _0x28d1d7, _0x152eb7) {
            if (_0x152eb7['url']['match']('(//+.17track.net/restapi/track)') && _0x152eb7['data']) {
                _0x567d6b = _0x152eb7['data']; //post的数据 {"data":[{"num":"***","fc":0,"sc":0}],"guid":"","timeZoneOffset":-480}
                var _0x164a16;
                try {
                  _0x164a16 = JSON['parse'](_0x567d6b);//执行JSON.parse获得对象
                } catch (_0x198faf) {
                  //这里省略掉
                }
                if (_0x164a16 && _0x2ec18e['ZIKlo'](_0x164a16['guid'], '')) { //guid参数为空
                  try {
                        //_0x2ec18e['WaYmY'](_0x9ff66b, _0x567d6b, _0x567d6b.length, true);
                        _0x9ff66b(_0x567d6b, _0x567d6b.length, true);//这句跟上面句相等,调用了_0x9ff66b传了data数据和长度,去看_0x9ff66b函数实际对_0x472ff2关键变量作了设置
                        var _0x3fd2d0 = _0x4f3f('0x96', '7la!') + _0x4f3f('0x97', 'ttkq') + ']';//_0x3fd2d0 = "";
                        var _0x3a6885 = _0x2ec18e($, _0x3fd2d0);//_0x3a6885 = $("");
                        _0x3966d3(_0x28d1d7, _0x3a6885, _0x1a395d(_0x3a6885));//调用_0x3966d3传入的参数,_0x28d1d7是XMLHttpRequest,_0x3a6885 = $(""),_0x3a6885 = _0x3a6885['length'] = $("").length,实际等于21,接下来是_0x1a395d函数传入了$("").length,
                  } catch (_0x1739bd) { }
                }
            }
      });
```
_0x1a395d函数

```javascript
    function _0x1a395d(_0x3e48ff) { //传过来的21
      var _0x402f82 = _0x3e48ff;
      if (window && document && window['innerHeight'] && window > 0x0) {//这里判断了innerHeight,我们要把这个判断去掉
            var _0x5027d7 = window['random']();//_0x5027d7 = Math.random();
            _0x402f82 = window['round'](_0x2ec18e(_0x5027d7, _0x3e48ff));//Math.round(_0x5027d7 * _0x3e48ff);//生成了一个随机数_0x402f82
      }
      //设置了关键变量_0x472ff2
      _0x472ff2 = _0x402f82(0x10);//_0x402f82.toString(16);
      _0x472ff2 = _0x402f82(0x10)['length'];//_0x402f82.toString(16).length;
      return _0x402f82;//返回了这个数
    }
```
_0x3966d3函数

```javascript
    function _0x3966d3(_0x8517d0, _0xae9512, _0x1e1c09) { //_0x1e1c09 = _0x1a395d生成的数字,
      var _0x126b58 = _0x4f3f('0x56', '#C9L');//_0x126b58= "yq-";
      var _0x316f08 = _0x4f3f('0x57', 'tG@(');//_0x316f08 = 'Last-Event-ID';
      if (_0xae9512 > _0x1e1c09 && document && document) {//$("").length > _0x1e1c09 && document && document['removeEventListener'],_0xae9512['length'] = $("").length;
            _0x126b58 = _0xae9512['className'];//这里修改了_0x126b58值,最后我把_0x1a395d生成的数字设置为固定值,_0x126b58也设置成对应的className,可以去掉这个判断了。
      }
      _0x126b58 = _0x126b58 + '/' + window['now']()(0x10) + '/' + window(0x10) + '/' + _0x4ba351(); //_0x126b58 + '/' + Date.now().toString(16) + '/' + window.innerHeight.toString(16)+ '/' + _0x4ba351(); 最后咱是要脱离浏览器运行的,window.innerHeight.toString(16)可以设置为一个固定值,最后是_0x4ba351这个函数
      _0x9ff66b(_0x126b58, _0x1e1c09);//_0x9ff66调用了_0x3df0c1,_0x3df0c1调用了_0x5e7be4,_0x5e7be4又有检测函数
      _0x126b58 = _0x2ec18e(_0x3c4889, _0xd74c71(_0x126b58));
      _0x472ff2 = _0x126b58;
      //后面的代码已经没有用了,目的只为得到Last-Event-ID
      var eid = _0x472ff2.join("");
      return eid;
      ......
    }
```
_0x4ba351

```javascript
    function _0x4ba351() {//这个密密麻麻一大片,主要目的是生成XMLHttpRequest返回一个true,咱又不需要XMLHttpRequest。直接return true就好
      if (_0x2ec18e === _0x4f3f('0x3a', 'VuqV')) {
            var _0x2371bc = _0x4f3f('0x3b', 'f*#]');
            var _0x135745 = _0x2ec18e;
            if (objs > rndNo && document && document) {
                _0x2371bc = objs;
            }
            _0x2371bc = _0x2ec18e(_0x2ec18e(_0x2371bc + '/' + window['now']()['toString'](0x10) + '/', window(0x10)), '/') + _0x2ec18e')](_0x4ba351);
            _0x2ec18e(_0x9ff66b, _0x2371bc, rndNo);
            _0x2371bc = _0x2ec18e(_0x3c4889, _0xd74c71(_0x2371bc));
            _0x472ff2 = _0x2371bc;
            if (navigator) {
                var _0x55d807 = new Date();
                _0x55d807(_0x2ec18e(_0x55d807(), 0x12c * 0x3e8));
                document = _0x135745 + '=' + _0x472ff2('') + ';path=/;domain=17track.net;expires=' + _0x55d807();
                _0x55d807 = new Date();
                _0x55d807(_0x55d807['getTime']() + 0x12c * 0x3e8);
                document['cookie'] = _0x135745 + '=' + _0x472ff2('') + ';path=/;domain=17track.net;expires=' + _0x55d807();
            }
            if (!_0x411c47(_0x135745)) {
                xhr(_0x135745, _0x472ff2(''));
            }
      } else {
            var _0x47b984;
            try {
                _0x47b984 = new XMLHttpRequest();
            } catch (_0x3ef69c) {
                try {
                  _0x47b984 = new ActiveXObject(_0x4f3f('0x54', 'VMDb'));
                } catch (_0x494a23) {
                  try {
                        _0x47b984 = new ActiveXObject(_0x4f3f('0x55', 'jc9G'));
                  } catch (_0x1f73b5) {
                        return ![];
                  }
                }
            }
            _0x47b984 = null;
            return !![];
      }
    }
```

_0x5e7be4

```javascript
    function _0x5e7be4(_0x14393c) {
      if (_0x2ec18e(_0x4f3f('0x28', '7la!'), _0x4f3f('0x29', 'y*Cp'))) {
            if (window && window && window > 0x0 && document && document['hostname'](_0x4f3f('0x2e', 'DYwS'))) { //if (window && window['innerWidth'] && window['innerWidth'] > 0 && document && document['location']['hostname']['match']('.17track.net')) { 让这个判断永远为true吧
                if (!_0x14393c) {
                  _0x472ff2 = 0x1;
                } else { //下面可以看到对_0x472ff2做了设置
                  if (_0x4f3f('0x2f', '!nz(') !== _0x4f3f('0x30', 'klod')) {
                        _0x472ff2 = 0x1;
                  } else {
                        _0x472ff2 = _0x14393c;
                  }
                }
            } else {
                if (_0x4f3f('0x31', 'm^Gz') !== _0x2ec18e) {
                  that['console'] = function(_0x114f36) {
                        var _0x49a662 = {};
                        _0x49a662')] = _0x114f36;
                        _0x49a662 = _0x114f36;
                        _0x49a662')] = _0x114f36;
                        _0x49a662['info'] = _0x114f36;
                        _0x49a662 = _0x114f36;
                        _0x49a662 = _0x114f36;
                        _0x49a662 = _0x114f36;
                        return _0x49a662;
                  }(func);
                } else {
                  _0x472ff2 = 0x0;
                }
            }
      } else {
            _0x472ff2 = _0x14393c;
      }
    }
```
最后我们删掉调用_0x3966d3的代码去掉,对所有使用$的地方已经设置为固定值,去掉第二个自执行函数,然后给_0x4cec2b加一个参数

```javascript
function _0x4cec2b(_0x567d6b) {
    .....
    _0x8f6309();
    var _0x164a16 = JSON['parse'](_0x567d6b);
    _0x9ff66b(_0x567d6b, _0x567d6b.length, true);
    var eid = _0x3966d3(0, 0, _0x1a395d(21));
    return eid;
}
```
最后直接调用就可以获得Last-Event-ID拉,拿着python或者nodejs去浪吧!
```javascript
var eid= _0x4cec2b('{"data":[{"num":"***","fc":0,"sc":0}],"guid":"","timeZoneOffset":-480}'));
```

涛之雨 发表于 2020-7-7 08:52

此外就是楼主问的格式化的问题
var _0x2333a0=_0x10f375(this,function(){var _0x4d2180=function(){return'\x64\x65\x76';},_0x326747=function(){return'\x77\x69\x6e\x64\x6f\x77';};var _0x2b03ab=function(){var _0x48fa7a=new RegExp('\x5c\x77\x2b\x20\x2a\x5c\x28\x5c\x29\x20\x2a\x7b\x5c\x77\x2b\x20\x2a\x5b\x27\x7c\x22\x5d\x2e\x2b\x5b\x27\x7c\x22\x5d\x3b\x3f\x20\x2a\x7d');return!_0x48fa7a['\x74\x65\x73\x74'](_0x4d2180['\x74\x6f\x53\x74\x72\x69\x6e\x67']());};var _0x209b4b=function(){var _0x1542e8=new RegExp('\x28\x5c\x5c\x5b\x78\x7c\x75\x5d\x28\x5c\x77\x29\x7b\x32\x2c\x34\x7d\x29\x2b');return _0x1542e8['\x74\x65\x73\x74'](_0x326747['\x74\x6f\x53\x74\x72\x69\x6e\x67']());};var _0x10cf6b=function(_0x10f4d5){var _0x1679be=~-0x1>>0x1+0xff%0x0;if(_0x10f4d5['\x69\x6e\x64\x65\x78\x4f\x66']('\x69'===_0x1679be)){_0xd2c455(_0x10f4d5);}};var _0xd2c455=function(_0x570d92){var _0x6d545d=~-0x4>>0x1+0xff%0x0;if(_0x570d92['\x69\x6e\x64\x65\x78\x4f\x66']((!![]+''))!==_0x6d545d){_0x10cf6b(_0x570d92);}};if(!_0x2b03ab()){if(!_0x209b4b()){_0x10cf6b('\x69\x6e\x64\u0435\x78\x4f\x66');}else{_0x10cf6b('\x69\x6e\x64\x65\x78\x4f\x66');}}else{_0x10cf6b('\x69\x6e\x64\u0435\x78\x4f\x66');}});
_0x2333a0();
开fd劫持一下页面的那个js,删掉上述正则防格式化部分就行(当然可以格式化后再保存到本地)

涛之雨 发表于 2020-7-7 06:58

本帖最后由 涛之雨 于 2020-7-7 08:48 编辑

额。这是加密了,可以解密后再分析的。。。
我最近在研究这个{:301_1008:},等完善了会把源码扔出来。。。
现在有时候会出各种莫名其妙的bug(代码的不规范,版本的不同,抽取规则的不同都是问题)


此外他的代码应该是有禁止格式化,所以你格式化会出错(原理是正则表达式)
唔,貌似是买的高级版,方法有点复杂,看一下
有些地方定义的方法不一样。
半自动半手动吧,(变量没有办法还原,而且我也没看流程。。。)


截图是加密部分的(仔细看那个代码,前面一半全都是_0x变量,后一半都是没有处理过的。姑且称前面一半为加密部分吧)

变量建议分析后批量重命名。不然很难看懂

geniusxiao 发表于 2020-7-6 17:36

可以的,赞一个

ambo 发表于 2020-7-6 19:45

萌新看不懂哦

魔道书生 发表于 2020-7-6 22:07

这算不算简单点?狗头保命

pwp 发表于 2020-7-6 22:19

{:1_936:}根本看不懂你所云。

bags 发表于 2020-7-7 00:17

太强了看不懂。。。

allennt 发表于 2020-7-7 00:34

有点厉害>_>

lifz888 发表于 2020-7-7 07:45

非常好的分析,支持原创,支持发帖
页: [1] 2
查看完整版本: 17track接口逆向分析