添加链接
link管理
链接快照平台
  • 输入网页链接,自动生成快照
  • 标签化管理网页链接

最终找到加密位置

1
2
3
4
5
6
7
8
9
10
11
12
13
try {
String signature = JDHttpTookit.getEngine().getSignatureHandlerImpl().signature(JDHttpTookit.getEngine().getApplicationContext(), queryParameter, str , deviceUUID, property , versionName);
if (OKLog.D) {
OKLog.d( "Signature" , "native signature sucess " + signature);
}
if (TextUtils.isEmpty(signature) || (urlParams = getUrlParams(signature)) = = null || urlParams.isEmpty()) {
return ;
}
for (String str8 : urlParams.keySet()) {
builder.addQueryParameter(str8, urlParams.get(str8));
}
} catch (Exception unused) {
}

发现加密的是一个接口

我们要找实现这个类型的方法

implements ISignatureHandler

所以我们直接搜索 ISignatureHandler

发现在这个类里有一定线索

在初始化时传入了

查找调用此函数的位置的方法

在initapp中传入了,我们往上跟入

进入方法查看

原来是使用new创建的 所以下次搜索可以使用

1
new ISignatureHandler

来搜索定位

定位到关键函数,接下来进行hook 并主动调用:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
function call(){
Java.perform(function () {
let BitmapkitUtils = Java.use( "com.jingdong.common.utils.BitmapkitUtils" );
let context = Java.use( "android.app.ActivityThread" ).currentApplication().getApplicationContext();
let str = "wareBusiness" ;
let str2 = '{"abTest800":true,"acceptPrivacy":true,"avoidLive":false,"bbtf":"","brand":"Redmi","businessType":"","bybt":"","cityCode":72,"cityId":0,"cpsNoTuan":null,"darkModelEnum":3,"districtId":0,"euaf":false,"eventId":"MyHistory_Product","fromType":0,"isDesCbc":true,"isFromOpenApp":true,"latitude":"0.0","lego":true,"longitude":"0.0","model":"Redmi Note 11T Pro","ocrFlag":false,"oneboxChannel":false,"oneboxKeyword":"","oneboxSource":"","openSimilarFlag":"","overseas":0,"pdVersion":"1","personas":null,"pluginVersion":101050,"plusClickCount":0,"plusLandedFatigue":0,"popBusinessType":"","poplayer":false,"productJdv":"-1|kong|t_1000210271_502774|zssc|d36d13b9-61c4-4fdf-b7f2-11dbc28d14dd-p_1999-pr_100746-at_502774-tg_ext_0-00-0-tgx-5050508-3935-20231110|1699610674","provinceId":"0","prstate":"0","refreshMe":null,"searchWareflag":"","selfDelivery":"0","skuId":"48905840961","source_type":"wojing_history","source_value":"","townId":0,"uAddrId":"0","utmMedium":null,"wareInnerSource":"extra.inner.source.init","yrqNew":"1"}'
let str3 = "789e43b8e08521ee" ;
let str4 = "android" ;
let str5 = "12.2.2" ;
let result = BitmapkitUtils.getSignFromJni(context, str , str2, str3, str4, str5);
console.log( "BitmapkitUtils.getSignFromJni result = " + result);
});
}

发现疑似是hash函数

hook dlsym函数,得知函数加载的so

libjdbitmapkit.so

定位到要分析的函数,由于位数疑似md5,所以使用龙哥的findhash插件,进行寻找

熟悉的朋友应该认出了,这个就是md5运算部分,我们寻找上层引用

再次寻找上层引用

发现调用位置就在我们目标分析的函数里(如果没跟到的小伙伴可以打印堆栈)

我们进行hook来分析入参

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
(function () {
/ / @ts - ignore
function print_arg(addr) {
try {
var module = Process.findRangeByAddress(addr);
if (module ! = null) return "\n" + hexdump(addr) + "\n" ;
return ptr(addr) + "\n" ;
} catch (e) {
return addr + "\n" ;
}
}
/ / @ts - ignore
function hook_native_addr(funcPtr, paramsNum) {
var module = Process.findModuleByAddress(funcPtr);
try {
Interceptor.attach(funcPtr, {
onEnter: function (args) {
this.logs = "";
this.params = [];
/ / @ts - ignore
this.logs = this.logs.concat( "So: " + module.name + "  Method: sub_25E0 offset: " + ptr(funcPtr).sub(module.base) + "\n" );
for (let i = 0 ; i < paramsNum; i + + ) {
this.params.push(args[i]);
this.logs = this.logs.concat( "this.args" + i + " onEnter: " + print_arg(args[i]));
}
}, onLeave: function (retval) {
for (let i = 0 ; i < paramsNum; i + + ) {
this.logs = this.logs.concat( "this.args" + i + " onLeave: " + print_arg(this.params[i]));
}
this.logs = this.logs.concat( "retval onLeave: " + print_arg(retval) + "\n" );
console.log(this.logs);
}
});
} catch (e) {
console.log(e);
}
}
/ / @ts - ignore
hook_native_addr(Module.findBaseAddress( "libjdbitmapkit.so" ).add( 0x25e0 ), 0x3 );
})();

发现入参是一段base64,解密后是乱码

在md5前,明文进行了额外处理:

v39是我们要分析的密文,

继续往上跟踪v39生成的位置

经过hook可知

密文由sub_18c9c计算而来

其中两个参数是rand随机生成的

在这里决定了sign由哪个函数进行加密

今天我们分析case2里面的加密函数,也是最简单的,后面我会分析剩下两个算法的计算方法

算法肉眼可见的可以复现,所以我们进行hook入参和出参进行分析

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
(function () {
/ / @ts - ignore
function print_arg(addr) {
try {
var module = Process.findRangeByAddress(addr);
if (module ! = null) return "\n" + hexdump(addr) + "\n" ;
return ptr(addr) + "\n" ;
} catch (e) {
return addr + "\n" ;
}
}
/ / @ts - ignore
function hook_native_addr(funcPtr, paramsNum) {
var module = Process.findModuleByAddress(funcPtr);
try {
Interceptor.attach(funcPtr, {
onEnter: function (args) {
this.logs = "";
this.params = [];
/ / @ts - ignore
this.logs = this.logs.concat( "So: " + module.name + "  Method: sub_6858 offset: " + ptr(funcPtr).sub(module.base) + "\n" );
for (let i = 0 ; i < paramsNum; i + + ) {
this.params.push(args[i]);
if (i = = 0 ){
/ / this.logs = this.logs.concat( "this.args" + i + " onEnter: " + args[i].readCString());
}
else if (i = = 3 ){
this.logs = this.logs.concat( "this.args" + i + " onEnter: " + hexdump(args[ 3 ]));
}
else {
this.logs = this.logs.concat( "this.args" + i + " onEnter: " + print_arg(args[i]));
}
}
}, onLeave: function (retval) {
for (let i = 0 ; i < paramsNum; i + + ) {
this.logs = this.logs.concat( "this.args" + i + " onLeave: " + print_arg(this.params[i]));
}
this.logs = this.logs.concat( "retval onLeave: " + print_arg(retval) + "\n" );
console.log(this.logs);
}
});
} catch (e) {
console.log(e);
}
}
/ / @ts - ignore
hook_native_addr(Module.findBaseAddress( "libjdbitmapkit.so" ).add( 0x1882C ), 0x4 );
})();

不是所有的call都能触发这个分支,得多call几次

我们可以看到明文数据了

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
(function () {
/ / @ts - ignore
function print_arg(addr) {
try {
var module = Process.findRangeByAddress(addr);
if (module ! = null) return "\n" + hexdump(addr) + "\n" ;
return ptr(addr) + "\n" ;
} catch (e) {
return addr + "\n" ;
}
}
/ / @ts - ignore
function hook_native_addr(funcPtr, paramsNum) {
var module = Process.findModuleByAddress(funcPtr);
try {
Interceptor.attach(funcPtr, {
onEnter: function (args) {
this.logs = "";
this.params = [];
/ / @ts - ignore
this.logs = this.logs.concat( "So: " + module.name + "  Method: sub_6858 offset: " + ptr(funcPtr).sub(module.base) + "\n" );
for (let i = 0 ; i < paramsNum; i + + ) {
this.params.push(args[i]);
if (i = = 1 ){
this.logs = this.logs.concat( "this.args" + i + " onEnter: " + args[i].readCString());
}
else if (i = = 3 ){
this.logs = this.logs.concat( "this.args" + i + " onEnter: " + hexdump(args[ 3 ]));
}
else {
this.logs = this.logs.concat( "this.args" + i + " onEnter: " + print_arg(args[i]));
}
}
}, onLeave: function (retval) {
for (let i = 0 ; i < paramsNum; i + + ) {
this.logs = this.logs.concat( "this.args" + i + " onLeave: " + print_arg(this.params[i]));
}
this.logs = this.logs.concat( "retval onLeave: " + print_arg(retval) + "\n" );
console.log(this.logs);
}
});
} catch (e) {
console.log(e);
}
}
/ / @ts - ignore
hook_native_addr(Module.findBaseAddress( "libjdbitmapkit.so" ).add( 0x1882C ), 0x4 );
})();

修改脚本 拿到arg1的key CleanShot 2024-05-12 at 19.11.47

afcb2afb1f349bed06555aef7fd47cecbec115c6083eafa51f608df10bdd350bab2a21cb1ffb6cb7df2b9dea43cf07ccaaf12ffd1b378cf126678edb57840d1ebbcb2184c52ca2ee265595e144cf35c4aff728cb08ef5ee11f658f9a088435f66bf03ef931275eb9df4382dc7bd335f66bf031cb0c2991f2284586e873840dcc6b82eefa1c2da0a1f7134ba430c5401ea2d12bfc08ed76b6ef1d4bdb47de5013adb0e688f7ed9ff728bfb4cc43cb41cc63fc31c437ef5eeb1e63b0c57dce7c368efc31c5c5c56f93df55b6eb42d4400dbddf20baddf358a122668ede309c790b95c12184c520a2e42b6596dc309c3517a2de15cb1f2aaef814419be772df7a1e....省略

使用c语言进行复现,发现结果一致,接下来把结果from hex 再base64

把base64进行md5加密 即可得到京东的sign

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
void TenSeattosEncrypt( char * input, int input_len)
{
int v4, v5;
char v6;
const char * TenSeattos_key = "80306f4370b39fd5630ad0529f77adb6" ;
unsigned char table[0x10] = { 0x37, 0x92, 0x44, 0x68, 0xA5, 0x3D, 0xCC, 0x7F, 0xBB, 0xF, 0xD9, 0x88, 0xEE, 0x9A, 0xE9, 0x5A };
for ( int i = 0; i != input_len; ++i) {
v4 = i & 7;
v5 = table[i & 0xF];
v6 = (v5 + (*(unsigned char *)(input + i) ^ *(unsigned char *)(TenSeattos_key + v4) ^ table[i & 0xF])) ^ table[i & 0xF];
*(unsigned char *)(input + i) = v6;
*(unsigned char *)(input + i) = *(unsigned char *)(TenSeattos_key + v4) ^ v6;
}
}

至此,我们已经完成了最容易的一个分支的京东sign的计算

下一篇文章将分析另外的两个加密过程,涉及到unidbg/unicorn的使用,记录算法还原的过程

[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课