-
先charles抓包,api.xxxxxx.com域名的包
-
分析包 看到路径参数如下
-
appkey=1d8b6e7d45233436&build=5531000&channel=dw056&mobi_app=android&mode=0&oid=326052200&plat=2&platform=android&ps=20&statistics=%7B%22appId%22%3A1%2C%22platform%22%3A3%2C%22version%22%3A%225.53.1%22%2C%22abtest%22%3A%22%22%7D&ts=1705305495&type=1&sign=2c9086d4acc853a017ec087699902634
可以看到一个sign,是个32字符,128位,直觉告诉我可能是个md5签名 -
然后用jadx打开xxx.apk包,全局搜索sign字符串,在众多函数方法中,找到一个SignedQuery函数,跟进去看,发现又个loadLibrary(xxx),那么直接告诉我是在一个so里
-
用apktool把xxx.apk decode出来,拿到libxxx.so
-
用ida打开该libxxx.so,直接看JNI_OnLoad函数,找到这一行
if ( (*v5)->RegisterNatives(v5, v4, (const JNINativeMethod *)®ister_native_struct, 5) < 0
-
register_native_struct跟进去
-
每个函数进去看下,有惊喜,确实是个md5算法,找到 md5_impl,跟进去看看算法参数
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
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
int
__fastcall md5_impl(JNIEnv
*
a1, jobject (
*
a2)(JNIEnv
*
, jclass, jmethodID, ...))
{
JNIEnv
*
v2;
/
/
r11
jobject (
*
v3)(JNIEnv
*
, jclass, jmethodID, ...);
/
/
r6
const char
*
v4;
/
/
r2
const char
*
v5;
/
/
r1
int
result;
/
/
r0
int
v7;
/
/
r0
JNIEnv v8;
/
/
r1
int
v9;
/
/
r0
int
v10;
/
/
r4
int
v11;
/
/
r0
int
v12;
/
/
r10
signed
int
v13;
/
/
r9
int
v14;
/
/
r5
int
v15;
/
/
r8
time_t v16;
/
/
r0
int
v17;
/
/
r6
int
v18;
/
/
r5
const char
*
input_str;
/
/
r8
_DWORD
*
v20;
/
/
r10
int
v21;
/
/
r4
_DWORD
*
v22;
/
/
r0
int
v23;
/
/
r1
int
v24;
/
/
r2
int
v25;
/
/
r0
unsigned
int
input_len;
/
/
r5
signed
int
v27;
/
/
r4
char
*
v28;
/
/
r5
int
v29;
/
/
r4
const char
*
v30;
/
/
[sp
+
8h
] [bp
-
C0h]
int
v31;
/
/
[sp
+
8h
] [bp
-
C0h]
int
v32;
/
/
[sp
+
Ch] [bp
-
BCh]
char v33;
/
/
[sp
+
10h
] [bp
-
B8h]
int
s;
/
/
[sp
+
38h
] [bp
-
90h
]
int
v35;
/
/
[sp
+
3Ch
] [bp
-
8Ch
]
int
v36;
/
/
[sp
+
40h
] [bp
-
88h
]
int
v37;
/
/
[sp
+
44h
] [bp
-
84h
]
char v38[
24
];
/
/
[sp
+
90h
] [bp
-
38h
]
int
v39;
/
/
[sp
+
A8h] [bp
-
20h
]
v2
=
a1;
v3
=
a2;
if
( !sub_2D20((
int
)a1) )
goto LABEL_5;
v4
=
"com.xxxxxxxx.nativelibrary.SignedQuery"
;
v5
=
"java/lang/ClassNotFoundException"
;
LABEL_3:
sub_401C((
int
)v2, (
int
)v5, (
int
)v4);
result
=
0
;
while
( _stack_chk_guard !
=
v39 )
{
LABEL_5:
if
( !v3 )
{
v5
=
"java/lang/NullPointerException"
;
v4
=
"Null params!"
;
goto LABEL_3;
}
v7
=
is_empty(v2, (
int
)v3);
v8
=
*
v2;
if
( v7 )
{
v3
=
v8
-
>NewObject;
result
=
((
int
(__fastcall
*
)(JNIEnv
*
,
int
,
int
, _DWORD, _DWORD))v3)(
v2,
cls_signed_query,
signed_query_init,
0
,
0
);
}
else
{
v9
=
((
int
(__fastcall
*
)(JNIEnv
*
, const char
*
))v8
-
>NewStringUTF)(v2,
"appkey"
);
v10
=
v9;
v11
=
sub_60CC(v2, (
int
)v3, v9);
/
/
/
/
校验appkey
v12
=
v11;
if
( v11 )
{
v30
=
(const char
*
)((
int
(__fastcall
*
)(JNIEnv
*
,
int
, _DWORD))(
*
v2)
-
>GetStringUTFChars)(v2, v11,
0
);
v13
=
check_app_key_get_type(v30);
}
else
{
v13
=
-
1
;
v30
=
0
;
}
v14
=
((
int
(__fastcall
*
)(JNIEnv
*
, const char
*
))(
*
v2)
-
>NewStringUTF)(v2,
"ts"
);
v15
=
sub_60CC(v2, (
int
)v3, v14);
if
( !v15 )
{
v36
=
0
;
v37
=
0
;
s
=
0
;
v35
=
0
;
v16
=
time(
0
);
sprintf((char
*
)&s,
"%ld"
, v16);
((void (__fastcall
*
)(JNIEnv
*
,
int
*
))(
*
v2)
-
>NewStringUTF)(v2, &s);
sub_6188((
int
)v2);
}
sub_41D0(v2, v14);
sub_41D0(v2, v15);
v17
=
((
int
(__fastcall
*
)(JNIEnv
*
,
int
,
int
, jobject (
*
)(JNIEnv
*
, jclass, jmethodID, ...)))(
*
v2)
-
>CallStaticObjectMethod)(
v2,
cls_signed_query,
method_signed_query_r,
v3);
v18
=
0
;
if
( sub_3F70((
int
)v2) )
v17
=
0
;
v32
=
v17;
input_str
=
(const char
*
)((
int
(__fastcall
*
)(JNIEnv
*
,
int
, _DWORD))(
*
v2)
-
>GetStringUTFChars)(v2, v17,
0
);
if
( v13 !
=
-
1
)
{
((void (__fastcall
*
)(JNIEnv
*
,
int
, const char
*
))(
*
v2)
-
>ReleaseStringUTFChars)(v2, v12, v30);
v20
=
malloc(
0x10u
);
if
( v20 )
{
v31
=
v10;
v21
=
global_key[v13];
v22
=
&global_key[v13];
v23
=
v22[
5
];
v24
=
v22[
10
];
v25
=
v22[
15
];
*
v20
=
v21;
v20[
1
]
=
v23;
v20[
2
]
=
v24;
v20[
3
]
=
v25;
_aeabi_memclr8(&v33,
33
);
input_len
=
strlen(input_str);
_aeabi_memclr8(v38,
24
);
_aeabi_memclr8(&s,
88
);
md5_init(&s);
md5_update((
int
)&s, (
int
)input_str, input_len);
sprintf(v38,
"%08x"
, v21);
md5_update((
int
)&s, (
int
)v38,
8u
);
v27
=
1
;
do
{
sprintf(v38,
"%08x"
, v20[v27]);
md5_update((
int
)&s, (
int
)v38,
8u
);
+
+
v27;
}
while
( v27 !
=
4
);
md5_final((
int
)v38, (
int
)&s);
v28
=
&v33;
v29
=
0
;
do
{
sprintf(v28,
"%02x"
, (unsigned __int8)v38[v29
+
+
]);
v28
+
=
2
;
}
while
( v29 !
=
16
);
free(v20);
v10
=
v31;
v18
=
((
int
(__fastcall
*
)(JNIEnv
*
, char
*
))(
*
v2)
-
>NewStringUTF)(v2, &v33);
}
else
{
v18
=
0
;
}
}
sub_41D0(v2, v10);
v3
=
(
*
v2)
-
>NewObject;
result
=
((
int
(__fastcall
*
)(JNIEnv
*
,
int
,
int
,
int
,
int
))v3)(v2, cls_signed_query, signed_query_init, v32, v18);
}
}
return
result;}
-
分析代码,发现关键在 check_app_key_get_type和global_key,一个是app key获取type,还有一个存着类似于secret key的 global key
-
整体看下来,结论就是 md5加密,按参数字典序排序,然后拼接SecretKey,再md5得到sign,取其中一个type来验证下:
-
和app请求生成的sign一样,secret key打码,如果想要,可以按步骤自己去试试,实测成功,关键地方都写清楚了
最后,写个python脚本试试,确实都能访问,这里脚本就不放了,求赞求加精,求账号升级,感谢