某德地图矢量瓦片逆向(快速wasm逆向),c/c++/c#可调用,执行wasm2c翻译出来的c代码一
本文仅供学习交流使用,如有侵权,联系我删除。
由于篇幅过长,本篇分析wasm的网页加载流程,下一篇分析如何执行wasm2c翻译出来的c代码
一般来说对于webassmebly的逆向,有两种选择,一个是扣js在nodejs等含有wasm运行时的环境中运行(缺点需要wasm运行时),另一种就是分析算法,自己写算法(缺点是耗时时间长)。那么有没有一种不用分析算法且可以脱离wasm运行时的快速逆向方法呢?当然是有的。即执行wasm2c翻译出来的c代码,快速wasm逆向,c/c++/c#/python可调用,
我们打开高德地图,
看到如图所示的url,即矢量瓦片:https://vdata04.amap.com/nebula/v3?key=bfe31f4e0fb231d29e1d3ce951e2c780&msg=7188355d0c44c5dabfd17b014f888bbf2321f2116e4354556cf52a9d7782368fadbd71091671a8ef05353af85941de664fc42793f4791a5029cb4a2342f3f7a3dc4b4432685cb09c6504dd1bd958c96d8f61f1098710c56cea2a50cac26c1a58&p=2
经过跟踪可以看到关键点:
b.prototype.bw = function(g, A, I, t, B, i, Q, C, e, o, E, h, a) {
a = A.join(";"),
a = this.parent.PW.transform('["' + a + '","' + E + '"]'),
g = g + ("?key=" + this.parent.key) + "&msg=" + a,
return g += "&p=2",
makeFetchRequest(this.parent.Uh, g, function(A, i) {
A ? h(A) : setTimeout(function() {
l.pQ(i, B, I, C, e, Q, t, o, h, g)
}, 10)
})
}
关键的句子是his.parent.PW.transform('["' + a + '","' + E + '"]'),即把经纬度坐标经过加密,然后访问url。跟进去
就可以发现他是如何初始化wasm以及调用wasm的。
首先把那一大串base64编码的的字符串解码成buffer,然后调用
function initSync(A) {
var i = {
wbg: {}
};
return i.wbg.__wbg_new_59cb74e423758ede = function() {
return addHeapObject(new Error)
}
,
i.wbg.__wbg_stack_558ba5917b466edd = function(A, i) {
var g = passStringToWasm0(getObject(i).stack, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc)
, i = WASM_VECTOR_LEN;
getInt32Memory0() = i,
getInt32Memory0() = g
}
,
i.wbg.__wbg_error_4bb6c2a97407129a = function(A, i) {
try {
console.error(getStringFromWasm0(A, i))
} finally {
wasm.__wbindgen_free(A, i)
}
}
,
i.wbg.__wbindgen_object_drop_ref = function(A) {
takeObject(A)
}
,
i.wbg.__wbindgen_throw = function(A, i) {
throw new Error(getStringFromWasm0(A, i))
}
,
i = loadSync(A, i).instance,
wasm = i.exports
}来加载wasm。其中i是wasm的导入函数,直接扣即可。
加载wasm之后调用了RSAPublicKeyPair函数,跟进去可以发现调用了wasm里面的rsapublickeypair_new函数
紧接着执行了this.instance.init(),即调用了wasm里面的$rsapublickeypair_init函数
这上面均是RSA算法初始化的过程,即-加载wasm同时加载导入函数-初始化-加载RSA公钥
分析过RSA算法初始化的过程之后。我们来看看他是如何加密的‘
function passStringToWasm0(A, i, g) {//A是待加密字符串,i是wasm里面的函数,g是前面rsapublickeypair_new函数返回的内存地址
if (void 0 === g) {
var I = cachedTextEncoder.encode(A)
, t = i(I.length);
return getUint8Memory0().subarray(t, t + I.length).set(I),
WASM_VECTOR_LEN = I.length,
t
}
for (var B = A.length, Q = i(B), C = getUint8Memory0(), e = 0; e < B; e++) {
var o = A.charCodeAt(e);
if (127 < o)
break;
C = o
}
首先他把字符串变成了ascll格式,然后调用了i函数__wbindgen_malloc:
从函数名字__wbindgen_malloc就可以看出,为待加密的字符串分配内存空间
然后C = o直接把加密的字符串写入内存。
之后调用了wasm.rsapublickeypair_encode(8, this.UW, i, g)函数进行加密。参数 this.UW是前面rsapublickeypair_new函数返回的内存地址,i是待加密的字符串分配内存空间的地址,g是加密的字符串长度
up.prototype.encode = function(A) {
try {
var i = passStringToWasm0(A, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc)
, g = WASM_VECTOR_LEN;
return wasm.rsapublickeypair_encode(8, this.UW, i, g),
getStringFromWasm0(I = getInt32Memory0(), t = getInt32Memory0())
} finally {
var I = getInt32Memory0()
, t = getInt32Memory0();
wasm.__wbindgen_free(I, t)
}
}
加密之后取了uint8array内存地址为8和12处(对应 I=getInt32Memory0(), t = getInt32Memory0()))的数,然后跟踪可以发现I,也就是内存地址8处的数即为加密后字符串的内存地址,内存12处即为加密后字符串的长度,有地址和长度,加密的字符串就出来了。。
接下来的流程可以不要,如果你是扣代码或者分析算法可以走,便于分析,如果执行wasm2c翻译出来的c代码,就不需要。
没有什么好说的,直接扣就行了。
如上图,扣完之后写了一个html,输入待价密字符串,点击按钮,就会在页面上写出加密的内容,为了便于调试,我们在wasm加载完成之后直接注入一些脚本
这样在wasm的调试页面就直接可以搜索了,非常方便(这里感谢@jixun66)大神在我上个帖子评论区https://www.52pojie.cn/forum.php ... 379690&pid=37592798指出
写的挺详细 就是看不懂 写的太专业了,看不太懂 看不太懂 挺详细 能换个交流方法不 写得这么好,评分走起来 给大牛鼓掌👏👏 {:301_992:}腻害,写得忒详细了 写的真详细,but看不懂{:301_1009:} 好专业、感谢分享