JS逆向 - 滴滴(dd03、dd05)WSGSIG
文章目录
- 概要
- 整体架构流程
- 技术名词解释
- 小结
概要
提示:仅供学习,不得用做商业交易,如有侵权请及时联系
逆向:JS逆向 - 滴滴(dd03、dd05)WSGSIG
URL(dd03): aHR0cHM6Ly9wYWdlLnVkYWNoZS5jb20vdXQtd2VieC91dC1yZWNoYXJnZS1waG9uZS9vdGhlcnMuaHRtbD9mdXNpb25fZW5hYmxlX29mZl9tb2RlPTEmaGlkZU5hdmlnYXRpb249MiZ4ZW52PWg1JmNmdD1jYXImY3N0PSZkY2huPWRkV3Bqa3gmb3BlbmlkPXVuZGVmaW5lZCZjaXR5SWQ9MCZsYXQ9MCZsbmc9MCZlbnRyYW5jZV9jaGFubmVsPTcyMjc4ODA1Mzcmd2VieF9jbHVzdGVyX2lkPTQzNiZ4X2FjdF9rZXk9dXQtcmVjaGFyZ2UtcGhvbmUtZGVmYXVsdCZ4Yml6PSZwcm9kX2tleT11dC1yZWNoYXJnZS1waG9uZSZ4cHNpZD0yNGFmODNkMTcxMmU0NjcxOTYwMTg2MDUwMzY3N2M4NSZ4b2lkPWU1YmQxMGExLTRmYTMtNGU1Yy04NWIyLTUzYmZmNGRjYzQ5OSZ4c3BtX2Zyb209Jnhwc2lkX3Jvb3Q9MjRhZjgzZDE3MTJlNDY3MTk2MDE4NjA1MDM2NzdjODUmeHBzaWRfZnJvbT0meHBzaWRfc2hhcmU9JmZfeHBzaWQ9MjRhZjgzZDE3MTJlNDY3MTk2MDE4NjA1MDM2NzdjODUmcm9vdF94cHNpZD0yNGFmODNkMTcxMmU0NjcxOTYwMTg2MDUwMzY3N2M4NSZjaGFubmVsX2lkPTcyJTJDMjc4JTJDODA1MzcjL2luZGV4P2hhc2hfcGFzc3BvcnRfbG9naW4=
URL(dd05):aHR0cHM6Ly9wYXNzcG9ydC5kaWRpY2h1eGluZy5jb20vY29tbW9uL3BjLWxvZ2luL3YzL2luZGV4Lmh0bWwjLw==
整体架构流程
提示:分析流程
一、dd03、关键字搜索或者堆栈断点都可以
- 搜索wsgsig
- 观察发现加密函数是一个s.getSign
- 单步进入发现是一个l()方法加密
- 最后发现它是一个拼接的处理的字符串,有进行md5和base64操作
- 比较简单,直接扣下来就可以了
源码:
const CrytpJs = require('crypto-js');
function s(t) {for (var e = t.length, n = t.length - 1; n >= 0; n--) {var r = t.charCodeAt(n);r > 127 && r <= 2047 ? e++ : r > 2047 && r <= 65535 && (e += 2),r >= 56320 && r <= 57343 && n--}return e
}var b = Math.floor(new Date / 1e3);
var E = 'q{自己去网页拿}';
E += Math.random();var k = CrytpJs.MD5("R4doMFFeMNlliIWM"+E).toString()
, T = [{
k: "ts",
v: b
}, {
k: "v",
v: "1"
}, {
k: "os",
v: "web"
}, {
k: "av",
v: "02"
}, {
k: "kv",
v: "0000010001"
}, {
k: "vl",
v: s(E)
}, {
k: "sig",
v: k
}].map(function(t) {
return t.k + "=" + t.v
}).join("&");function i(t, e) {for (var n = [], r = 0; r < e.length; r++)n[r] = t[r % 4] ^ e.charCodeAt(r);return n = Array.prototype.slice.apply(t).concat(n),String.fromCharCode.apply(null, n)
}
function r(t) {for (var e = "ABCDEFG0123456789abcdefgHIJKLMN+/hijklmnOPQRSTopqrstUVWXYZuvwxyz", n = "" + t, r = void 0, i = void 0, o = 0, a = ""; n.charAt(0 | o) || (e = "=",o % 1); a += e.charAt(63 & r >> 8 - o % 1 * 8)) {if ((i = n.charCodeAt(o += .75)) > 255)throw new Error("'base64' failed: The string to be encoded contains characters outside of the Latin1 range.");r = r << 8 | i}return a
}
dd03 = "dd03-" + r(i(new Uint8Array(new Uint32Array([Math.floor(4294967296 * Math.random())]).buffer), T)).replace(/=*$/, "")
console.log(dd03)
二、dd05、一样的关键词搜索或者堆栈断点
- 搜索wsgsig断点
- 发现它是通过Object(it[‘a’])函数加密的,i是一个表单参数、r是表示sign值是否更新
Object(it["a"])({body: i,noDomainCheck: !0,signUpgrade: r,contentType: "application/x-www-form-urlencoded"})
3. 那我们进入先进入到这个it[‘a’]方法中去观察它是怎么实现的,发现是一个webpack
4. 往上翻一点点,你会发现它其实是一个jsvmp,那么我们有俩种选择:1、插装纯算。2、扣webpack补环境
5. 这里我选择补环境吧,纯算有点废时间,大佬可以去纯一下,会到刚刚那个**it[‘a’]**的位置,我们需要知道他是通过那个函数去加载的it,直接搜索it =会发现就在上面
- 到这里,我们就得去重新去刷新网页了,让浏览器去加载到这个地方
var it = a("4bf1");wsgsign = Object(it["a"])({body: i,noDomainCheck: !0,signUpgrade: r,contentType: "application/x-www-form-urlencoded"})
- 进入到,a函数里面,发现导出加载函数是i函数
这里说一个webpack的形式:
!function (所有加载对象) {// 导出函数function r() {var n = []; // 本地存储,加载一次就不需要加载if (n[r])return n[r].exports;var t = n[r] = {i: r,l: !1,exports: {}};// 加载器return 所有加载对象[r].call(t.exports, t, t.exports, i),t.l = !0,t.exports}// 导出函数自定义的属性或者方法r.e = ...;r.m = ....;r.c = ....;
}({// 所有加载对象"键名": "值函数",.......
});
- 所以我们首页需要把r这个导出函数定义到全局,然后去浏览器中拿到,‘4bf1’键名对应的函数
window = globalThis;
window.导出;
}({// 所有加载对象"键名": "值函数",.......
});这个上面加上 window.导出 = r这个导出函数
-
然后执行window.导出(‘4bf1’),注意导出的时候不只是导出了这一个对象,因为他里面会去加载其他的对象,所以还得去拿其他的对象,不然会报错e[r].call这个加载不起来
-
我这里直接就使用自吐了,把4bf1加载的对象全部收集起来,大概有个600多的对象函数
-
如果还是不太懂,网上有一堆的webpack自吐教程,自己可以看看,研究研究
-
当我们全部扣下来,补完环境发现它的值的位数与浏览器不一致
-
继续观察浏览器,你会发现,它回去初始化加载其他包signinit
请求载荷:
data = { "diuu": "xxx", "bizId": "xxx", "sdkVer": "5.1.14", "os": "4", "appVer": "v3" }
请求头:
Sign
14. 最后你会发现它是从这里进行初始化的
Object(rt["a"])(Object(y["a"])(Object(y["a"])({}, pt[ot] || pt.default), {}, {os: "4",appVer: "v3",domainList: [ct],byQuery: !0,httpEnable: ["development", "rdtest", "wycdev", "wycdev01"].includes(ot)}))
- 还是一样的扣webpack
技术名词解释
提示:补环境
- 挂代理,导包(webpack)
//set不需要打印的属性或者函数
set_print = [];
//get不需要打印的属性或者函数
no_print = [];
setProxyArr = function setProxyArr(proxyObjArr) {for (let i = 0; i < proxyObjArr.length; i++) {const handler = `{get: function(target, property, receiver) {if(no_print.includes(property)){return target[property];}dtavm.log("方法:", "get ", "对象:", "${proxyObjArr[i]}", " 属性:", property, " 属性类型:", typeof property, ", 属性值:", target[property], ", 属性值类型:", typeof target[property]);return target[property];},set: function(target, property, value, receiver) {if(set_print.includes(property)){return Reflect.set(...arguments);}dtavm.log("方法:", "set ", "对象:", "${proxyObjArr[i]}", " 属性:", property, " 属性类型:", typeof property, ", 属性值:", value, ", 属性值类型:", typeof target[property]);return Reflect.set(...arguments);}}`;eval(`try {${proxyObjArr[i]};${proxyObjArr[i]} = new Proxy(${proxyObjArr[i]}, ${handler});} catch (e) {${proxyObjArr[i]} = {};${proxyObjArr[i]} = new Proxy(${proxyObjArr[i]}, ${handler});}`);}
}
- 保护函数,tostring检测
!(function () {"use strict";const $toString = Function.toString;const myFunction_toString_symbol = Symbol('('.concat('', ')_', (Math.random() + '').toString(36)));const mytoString = function () {return typeof this == 'function' && this[myFunction_toString_symbol] || $toString.call(this);};function set_native(func, key, value) {Object.defineProperty(func, key, {"enumerable": false,"configurable": true,"writable": true,"value": value})};delete Function.prototype['toString'];set_native(Function.prototype, "toString", mytoString);set_native(Function.prototype.toString, myFunction_toString_symbol, "function toString() { [native code] }");this.func_set_native = function (func) {set_native(func, myFunction_toString_symbol, `function ${myFunction_toString_symbol, func.name || ''}() { [native code] }`)}
}).call(globalThis);
- 检测点document.all、navigator.plugins原型链和window函数
createTagProto('HTMLAllCollection');
const v8 = require_('v8');
const vm=require_('vm');
v8.setFlagsFromString('--allow-natives-syntax');
let undetectable = vm.runInThisContext("%GetUndetectable()");
v8.setFlagsFromString('--no-allow-natives-syntax');
all = undetectable;
all.__proto__ = HTMLAllCollection.prototype;
all.length = xx;createTagProto('PromiseRejectionEvent');
createTagProto('MutationObserver');
createTagProto('CSSStyleDeclaration');
createTagProto('CSSRuleList');
createTagProto('DOMRectList');
createTagProto('DOMStringList');
createTagProto('DataTransferItemList');
createTagProto('FileList');
createTagProto('HTMLCollection');
createTagProto('HTMLFormElement');
createTagProto('HTMLSelectElement');
createTagProto('MediaList');
createTagProto('MimeTypeArray');
createTagProto('NamedNodeMap');
createTagProto('NodeList');
createTagProto('SVGLengthList');
createTagProto('SVGNumberList');
createTagProto('SVGPointList');
createTagProto('SVGStringList');
createTagProto('SVGTransformList');
createTagProto('SourceBufferList');
createTagProto('StyleSheetList');
createTagProto('TextTrackCueList');
createTagProto('TextTrackList');
createTagProto('TouchList');
createTagProto('DOMTokenList');DOMTokenList.prototype[Symbol.iterator] = function values() {}
DOMTokenList.prototype[Symbol.iterator].toString = function toString() {}
DOMTokenList.prototype.values = function values() {}
DOMTokenList.prototype.keys = function keys() {}
DOMTokenList.prototype.entries = function entries() {}
DOMTokenList.prototype.forEach = function forEach() {}
window.func_set_native(DOMTokenList.prototype.values);
window.func_set_native(DOMTokenList.prototype.keys);
window.func_set_native(DOMTokenList.prototype.entries);
window.func_set_native(DOMTokenList.prototype.forEach);
小结
提示:学习交流主页,星球持续更新中:链接https://t.zsxq.com/AJTw2(+星球主页+v)