业余时间写的,纯兴趣更新! 做个记录。
雷电模拟器 Android 7.1.2/pixexl2 Android 8.1
首先在apk中进行登录账户的时候进行抓包,获取登录凭证。
请求报文如下
1
2
3
4
5
6
7
|
GET
/
store
/
has_unfinished_game
/
?heybox_id
=
???&imei
=
???&os_type
=
Android&os_version
=
7.1
.
2
&version
=
1.1
.
50
&_time
=
1666165262
&hkey
=
5656c95245635812219460113c5a14eb
HTTP
/
1.1
Referer: http:
/
/
api.maxjia.com
/
User
-
Agent: Mozilla
/
5.0
AppleWebKit
/
537.36
(KHTML, like Gecko) Chrome
/
41.0
.
2272.118
Safari
/
537.36
ApiMaxJia
/
1.0
Cookie: pkey
=
???
Host: api.xiaoheihe.cn
Connection: Keep
-
Alive
Accept
-
Encoding: gzip
|
在get请求中发现有传递以下参数
1
2
3
4
5
6
7
|
heybox_id 固定且唯一(对于每一个用户来说)
imei 移动通信国际识别码(每个移动设备具有唯一性)
os_type 系统类型
os_version 系统版本
version apk的版本
_time 时间
hkey 时间key
|
以及cookie中的pkey
1
|
Cookie: pkey
=
??? 固定且唯一(对于每一个用户登录后)
|
以上参数我们需要知道hkey是怎么来的
因为是请求报文,所以算法肯定在apk内部实现的。要么在应用层要么在so层
将apk拖入到GDA中进字符串搜索"hkey"
发现在intercept方法中有存在这个字符串。双击进入
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
|
package com.
max
.xiaoheihe.network.b$
1
;
import
okhttp3.w;
import
com.
max
.xiaoheihe.network.b;
import
java.lang.
Object
;
import
okhttp3.w$a;
import
okhttp3.ad;
import
okhttp3.ab;
import
com.
max
.xiaoheihe.app.HeyBoxApplication;
import
com.
max
.xiaoheihe.bean.account.User;
import
okhttp3.v;
import
okhttp3.v$a;
import
java.lang.StringBuilder;
import
java.lang.System;
import
java.lang.String;
import
com.
max
.xiaoheihe.b.d;
import
com.
max
.xiaoheihe.b.af;
import
com.
max
.xiaoheihe.b.c;
import
com.
max
.xiaoheihe.bean.account.AccountDetailObj;
import
android.os.Build$VERSION;
import
okhttp3.ab$a;
class
b$
1
implements w
/
/
class
@
000e5c
{
final b a;
void b$
1
(b p0){
this.a
=
p0;
super
();
}
public ad intercept(w$a p0){
ab uoab
=
p0.request();
User user
=
HeyBoxApplication.b();
v$a uoa
=
uoab.a().v();
StringBuilder
str
=
(System.currentTimeMillis()
/
1000
)
+
"";
/
/
获取当前时间
/
1000
String str1
=
d.h(((d.h(
"xiaoheihe/_time="
+
str
)).replaceAll(
"a"
,
"app"
)).replaceAll(
"0"
,
"app"
));
/
/
这里是关键代码
if
(c.b(af.b(uoab.a().toString(),
"heybox_id"
))) {
String str2
=
"heybox_id"
;
String userid
=
(user.isLoginFlag())? user.getAccount_detail().getUserid():
"-1"
;
uoa.b(str2, userid);
}
uoa.b(
"imei"
, d.g()).b(
"os_type"
,
"Android"
).b(
"os_version"
, (Build$VERSION.RELEASE).trim()).b(
"version"
, d.h()).b(
"_time"
,
str
).b(
"hkey"
, str1);
if
(!c.b("")) {
uoa.b(
"game_type"
, "");
}
v ov
=
uoa.c();
str
=
"";
String str3
=
(c.b(user.getPkey()))? "
": "
pkey
=
"
+
user.getPkey();
str
=
str
+
str3;
str3
=
(c.b(uoab.a(
"Cookie"
)))? "
": "
;
"+uoab.a("
Cookie");
return
p0.proceed(uoab.f().b(
"Referer"
,
"http://api.maxjia.com/"
).b(
"User-Agent"
,
"Mozilla/5.0 AppleWebKit/537.36 \(KHTML, like Gecko\) Chrome/41.0.2272.118 Safari/537.36 ApiMaxJia/1.0"
).a(
"Cookie"
,
str
+
str3).a(ov).d());
}
}
|
str是当前时间/1000
分析下str1是怎么赋值的
1
|
String str1
=
d.h(((d.h(
"xiaoheihe/_time="
+
str
)).replaceAll(
"a"
,
"app"
)).replaceAll(
"0"
,
"app"
));
|
首先调用类d的h方法,参数为特定字符串"xiaoheihe/_time="和当前时间str进行拼接的结果
然后再次调用类d的h方法,参数为 将第一次调用的的返回值中的“a”和"0"给替换为"app"的结果
最后就是对str1进行赋值了,将第二次调用d的h方法的返回值.
后面其实就是一个拼接请求头的操作,不需要关注。
所以这里我们看下类d的h方法的怎么实现的
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
public static String h(String p0){
if
(TextUtils.isEmpty(p0)) {
return
"";
}
try
{
byte[] uobyteArray
=
MessageDigest.getInstance(
"MD5"
).digest(p0.getBytes());
String
str
=
"";
int
len
=
uobyteArray.length;
for
(
int
i
=
0
; i <
len
; i
=
i
+
1
) {
int
i1
=
uobyteArray[i] &
0x00ff
;
String str1
=
Integer.toHexString(i1);
if
(str1.length()
=
=
1
) {
str1
=
"0"
+
str1;
}
str
=
str
+
str1;
}
return
str
;
}catch(java.security.NoSuchAlgorithmException e6){
e6.printStackTrace();
return
"";
}
}
|
发现就是个md5.
我们可以得知请求报文中的_time和hkey字段是关联的。
1
2
3
4
5
6
7
|
GET
/
store
/
has_unfinished_game
/
?heybox_id
=
46447531
&imei
=
863342023667829
&os_type
=
Android&os_version
=
7.1
.
2
&version
=
1.1
.
50
&_time
=
1666165262
&hkey
=
5656c95245635812219460113c5a14eb
HTTP
/
1.1
Referer: http:
/
/
api.maxjia.com
/
User
-
Agent: Mozilla
/
5.0
AppleWebKit
/
537.36
(KHTML, like Gecko) Chrome
/
41.0
.
2272.118
Safari
/
537.36
ApiMaxJia
/
1.0
Cookie: pkey
=
MTY2NjE2NTIyNC45M180NjQ0NzUzMWhyd2JubG9ud2NndHFubmI__
Host: api.xiaoheihe.cn
Connection: Keep
-
Alive
Accept
-
Encoding: gzip
|
用脚本去验证下
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
|
#coding:utf8
import
hashlib
import
datetime
def
md5(test_str:
str
, key
=
None
):
print
(
'MD5加密前为 :'
+
test_str)
if
key
is
None
:
m
=
hashlib.md5()
else
:
m
=
hashlib.md5(key.encode(
'utf-8'
))
m.update(test_str.encode())
res
=
m.hexdigest()
print
(
'MD5加密后为 :'
+
res)
return
res
def
gethkey():
_time
=
"1666165262"
str
=
md5(_time)
str1
=
md5(
str
.replace(
"a"
,
"app"
).replace(
"0"
,
"app"
))
if
__name__
=
=
'__main__'
:
gethkey()
'''
MD5加密前为 :xiaoheihe/_time=1666165262
MD5加密后为 :49d640ad07515bc4c9ec4a9f893c24a7
MD5加密前为 :49d64appappdapp7515bc4c9ec4app9f893c24app7
MD5加密后为 :5656c95245635812219460113c5a14eb 和请求报文中的hkey是一致的
'''
|
下载了小黑盒 v.1.3.92版本。
同样在登录的时候进行抓包。
1
2
3
4
5
6
7
|
GET
/
bbs
/
app
/
api
/
user
/
permission?heybox_id
=
???&imei
=
???&os_type
=
Android&os_version
=
8.1
.
0
&version
=
1.3
.
92
&_time
=
1666259837
&hkey
=
0cf9be6f88
HTTP
/
1.1
Referer: http:
/
/
api.maxjia.com
/
User
-
Agent: Mozilla
/
5.0
AppleWebKit
/
537.36
(KHTML, like Gecko) Chrome
/
41.0
.
2272.118
Safari
/
537.36
ApiMaxJia
/
1.0
Cookie: pkey
=
???
Host: api.xiaoheihe.cn
Connection: Keep
-
Alive
Accept
-
Encoding: gzip
|
这里imei变化了是因为换了台手机。
我们先不逆向
用上面的gethkey.py跑一下看看hkey和请求报文中的是否一致。如果不一致就说明,加密算法变化了。
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
|
#coding:utf8
import
hashlib
import
datetime
def
md5(test_str:
str
, key
=
None
):
print
(
'MD5加密前为 :'
+
test_str)
if
key
is
None
:
m
=
hashlib.md5()
else
:
m
=
hashlib.md5(key.encode(
'utf-8'
))
m.update(test_str.encode())
res
=
m.hexdigest()
print
(
'MD5加密后为 :'
+
res)
return
res
def
gethkey():
_time
=
"xiaoheihe/_time="
+
"1666165262"
#v1.1.50
_time
=
"xiaoheihe/_time="
+
"1666259837"
#v.1.3.92
str
=
md5(_time)
str1
=
md5(
str
.replace(
"a"
,
"app"
).replace(
"0"
,
"app"
))
if
__name__
=
=
'__main__'
:
gethkey()
'''
MD5加密前为 :xiaoheihe/_time=1666259837
MD5加密后为 :5ff217a9966ad4c64a17fd4df122f03b
MD5加密前为 :5ff217app9966appd4c64app17fd4df122fapp3b
MD5加密后为 :2ba491715784e9606d289bde1f7890b0
'''
|
发现不一致,所以这个版本的getkey的算法发生了变化。
额,脑子坏掉了,请求报文中的hkey的长度只有10位的长度,肯定加密算法变了。
同样的操作定位到hkey的位置
发现新版本有三处地址含有该字符串。
先看第一个
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
private void i(){
L l
=
new L$a().c(
0
, TimeUnit.MILLISECONDS).b(
0
, TimeUnit.MILLISECONDS).a();
String
str
=
this.h();
T.a(
"zzzzconntest"
,
"url=="
+
str
);
HashMap hashMap
=
new HashMap(
16
);
User user
=
HeyBoxApplication.k();
String userid
=
(user.isLoginFlag())? user.getAccount_detail().getUserid():
"-1"
;
hashMap.put(
"userid"
, userid);
hashMap.put(
"appid"
,
"heybox"
);
hashMap.put(
"pkey"
, user.getPkey());
hashMap.put(
"imei"
, Q.d());
hashMap.put(
"os_type"
,
"Android"
);
hashMap.put(
"os_version"
, (Build$VERSION.RELEASE).trim());
hashMap.put(
"version"
, Q.g());
hashMap.put(
"hkey"
, Q.b(((Q.b(
"xiaoheihe/_time="
+
(System.currentTimeMillis()
/
1000
)
+
"
")).replaceAll("
a
", "
app
")).replaceAll("
0
", "
app")));
/
/
这里其实就是v1.
1.50
版本gethkey是一样的
l.a(new N$a().b(Ab.a(
str
, hashMap)).a(), new l$c(this));
l.h().b().shutdown();
return
;
}
|
类Q下的b方法,就是md5加密
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
public static String b(String p0){
if
(TextUtils.isEmpty(p0)) {
return
"";
}
try
{
byte[] uobyteArray
=
MessageDigest.getInstance(
"MD5"
).digest(p0.getBytes());
int
len
=
uobyteArray.length;
String
str
=
"";
for
(
int
i
=
0
; i <
len
; i
=
i
+
1
) {
int
i1
=
uobyteArray[i] &
0x00ff
;
String str1
=
Integer.toHexString(i1);
if
(str1.length()
=
=
1
) {
str1
=
"0"
+
str1;
}
str
=
str
+
str1;
}
return
str
;
}catch(java.security.NoSuchAlgorithmException e7){
e7.printStackTrace();
return
"";
}
}
|
第二个是就是这个版本使用的接口
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
|
package com.
max
.xiaoheihe.network.c;
import
okhttp3.H;
import
com.
max
.xiaoheihe.network.d;
import
java.lang.
Object
;
import
okhttp3.H$a;
import
okhttp3.T;
import
okhttp3.N;
import
com.
max
.xiaoheihe.app.HeyBoxApplication;
import
com.
max
.xiaoheihe.bean.account.User;
import
okhttp3.G;
import
okhttp3.G$a;
import
java.lang.StringBuilder;
import
java.lang.System;
import
java.lang.String;
import
com.
max
.xiaoheihe.utils.NDKTools;
import
com.
max
.xiaoheihe.utils.Q;
import
com.
max
.xiaoheihe.utils.Ab;
import
com.
max
.xiaoheihe.utils.M;
import
com.
max
.xiaoheihe.bean.account.AccountDetailObj;
import
android.os.Build$VERSION;
import
okhttp3.N$a;
class
c implements H
/
/
class
@
00135d
{
final d a;
void c(d p0){
this.a
=
p0;
super
();
}
public T intercept(H$a p0){
N n
=
p0.request();
User user
=
HeyBoxApplication.k();
G$a uoa
=
n.h().j();
String
str
=
"";
StringBuilder str1
=
(System.currentTimeMillis()
/
1000
)
+
str
;
String str2
=
n.h().c();
int
i
=
0
;
if
(str2.endsWith(
"/"
)) {
str2
=
str2.substring(i, (str2.length()
-
1
));
}
String str3
=
"app"
;
str2
=
(Q.b(((NDKTools.encode(HeyBoxApplication.g(), str2, str1)).replaceAll(
"a"
, str3)).replaceAll(
"0"
, str3))).substring(i,
10
);
String str4
=
"heybox_id"
;
if
(M.d(Ab.b(n.h().toString(), str4))) {
String userid
=
(user.isLoginFlag())? user.getAccount_detail().getUserid():
"-1"
;
uoa.a(str4, userid);
}
uoa.a(
"imei"
, Q.d()).a(
"os_type"
,
"Android"
).a(
"os_version"
, (Build$VERSION.RELEASE).trim()).a(
"version"
, Q.g()).a(
"_time"
, str1).a(
"hkey"
, str2);
if
(Q.h()) {
uoa.a(str3,
"concept"
);
}
G g
=
uoa.a();
str1
=
"";
String str5
=
(M.d(user.getPkey()))?
str
:
"pkey="
+
user.getPkey();
str1
=
str1
+
str5;
if
(!M.d(n.a(
"Cookie"
))) {
str
=
";"
+
n.a(
"Cookie"
);
}
return
p0.proceed(n.f().a(
"Referer"
,
"http://api.maxjia.com/"
).a(
"User-Agent"
,
"Mozilla/5.0 AppleWebKit/537.36 \(KHTML, like Gecko\) Chrome/41.0.2272.118 Safari/537.36 ApiMaxJia/1.0"
).b(
"Cookie"
, str1
+
str
).a(g).a());
}
}
|
关键代码
1
|
uoa.a(
"imei"
, Q.d()).a(
"os_type"
,
"Android"
).a(
"os_version"
, (Build$VERSION.RELEASE).trim()).a(
"version"
, Q.g()).a(
"_time"
, str1).a(
"hkey"
, str2);
|
看下str2是怎么获得的
1
2
3
4
5
6
7
8
9
|
String
str
=
"";
StringBuilder str1
=
(System.currentTimeMillis()
/
1000
)
+
str
;
String str2
=
n.h().c();
int
i
=
0
;
if
(str2.endsWith(
"/"
)) {
str2
=
str2.substring(i, (str2.length()
-
1
));
}
String str3
=
"app"
;
str2
=
(Q.b(((NDKTools.encode(HeyBoxApplication.g(), str2, str1)).replaceAll(
"a"
, str3)).replaceAll(
"0"
, str3))).substring(i,
10
);
|
发现调用了类NDKTools下的encode方法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
package com.
max
.xiaoheihe.utils.NDKTools;
import
java.lang.System;
import
java.lang.String;
import
java.lang.
Object
;
public
class
NDKTools
/
/
class
@
00138f
{
static {
System.loadLibrary(
"native-lib"
);
}
public void NDKTools(){
super
();
}
public static native
int
checkSignature(
Object
p0);
public static native synchronized String encode(
Object
p0,String p1,String p2);
public static native String getrsakey(
Object
p0,String p1);
}
|
encode方法在native-lib的so中进行实现的。
本地环境
1
2
|
frida
-
-
version
15.2
.
2
|
手机端运行frida的服务端
1
|
.
/
frida
-
server
-
15.2
.
2
-
android
-
arm64 &
|
电脑端查看进程
1
|
Frida
-
ps
-
U
|
run.py
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
|
#coding:utf8
import
sys
import
frida
process_name
=
'com.max.xiaoheihe'
# 发送信息回调函数
def
on_message(message, data):
if
message[
'type'
]
=
=
'send'
:
print
(f
"[*] {message['payload']}"
)
else
:
print
(message)
if
__name__
=
=
'__main__'
:
try
:
device
=
frida.get_usb_device(timeout
=
1000
)
print
(
"* get usb device成功"
)
except
:
device
=
frida.get_remote_device(timeout
=
1000
)
print
(
"* get remote device成功"
)
if
not
device:
print
(
"* 连接到Frida Server失败"
)
else
:
process
=
device.attach(process_name)
# 加载JS脚本
js
=
open
(
'hook.js'
, encoding
=
'utf-8'
).read()
print
(js)
script
=
process.create_script(js)
script.on(
'message'
, on_message)
script.load()
# 读取返回输入
input
()
script.unload()
|
hook.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
|
console.log(
"脚本载入成功"
);
Java.perform(function () {
var encodeAddr
=
Module.findExportByName(
"libnative-lib.so"
,
"encode"
);
console.log(encodeAddr);
if
(encodeAddr !
=
null) {
Interceptor.attach(encodeAddr, {
onEnter: function (args) {
/
/
args参数数组
console.log(
'encode-Enter'
)
console.log(args[
0
], Memory.readCString(args[
0
]));
console.log(args[
1
], Memory.readCString(args[
1
]));
console.log(args[
2
], Memory.readCString(args[
2
]));
console.log(args[
3
], Memory.readCString(args[
3
]));
console.log(args[
4
], Memory.readCString(args[
4
]));
},
onLeave: function (retval) {
/
/
retval函数返回值
console.log(
'encode-Leave'
);
console.log(retval.toString());
console.log(
'======'
);
}
});
}
})
|
发现可以成功运行起来
0xc9e90b01是encode函数的地址,当打开app后,这个地址是不变的。
这里我运行这个脚本只输出这些内容,但并没有打印出来参数和返回地址。
先静态分析吧。解包获得libnative-lib.so并拖入ida中
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
|
int
__fastcall encode(_JNIEnv
*
env,
int
a2,
int
a3,
int
can_str2,
int
can_str1)
{
const char
*
v5;
/
/
r10
_JNIEnv
*
v6;
/
/
r4
int
v7;
/
/
r6
int
v8;
/
/
r8
const char
*
str2;
/
/
r11
int
str1;
/
/
r0
bool
v11;
/
/
zf
size_t v12;
/
/
r5
char
*
v13;
/
/
r5
char
*
v14;
/
/
r0
size_t v15;
/
/
r0
char
*
v16;
/
/
r0
int
v17;
/
/
r5
int
result;
/
/
r0
int
v19;
/
/
[sp
+
0h
] [bp
-
30h
]
int
*
v20;
/
/
[sp
+
4h
] [bp
-
2Ch
]
int
v21;
/
/
[sp
+
8h
] [bp
-
28h
]
int
v22;
/
/
[sp
+
Ch] [bp
-
24h
]
v19
=
can_str2;
v6
=
env;
v7
=
can_str2;
if
( j_check_signature(env)
=
=
1
)
{
v8
=
0
;
str2
=
(const char
*
)((
int
(__fastcall
*
)(_JNIEnv
*
,
int
, _DWORD))v6
-
>functions
-
>GetStringUTFChars)(v6, v7,
0
);
str1
=
((
int
(__fastcall
*
)(_JNIEnv
*
,
int
, _DWORD))v6
-
>functions
-
>GetStringUTFChars)(v6, can_str1,
0
);
/
/
时间戳
v11
=
str2
=
=
0
;
if
( str2 )
{
v5
=
(const char
*
)str1;
v11
=
str1
=
=
0
;
}
if
( !v11 )
/
/
str2不为空进入
if
{
v21
=
can_str1;
v12
=
strlen(str2);
v20
=
&v19;
v13
=
(char
*
)&v19
-
((strlen(v5)
+
v12
+
21
) &
0xFFFFFFF8
);
v14
=
strcpy(v13, str2);
v15
=
strlen(v14);
_aeabi_memcpy(&v13[v15],
"/bfhdkud_time="
,
15
);
/
/
v13为str2后面拼接上
"/bfhdkud_time="
v16
=
strcat(v13, v5);
/
/
v16为v13后面拼接str1 即时间戳
v17
=
j_MDString(v16);
/
/
对v16进行md5加密赋值给v17
((void (__fastcall
*
)(_JNIEnv
*
,
int
, const char
*
))v6
-
>functions
-
>ReleaseStringUTFChars)(v6, v7, str2);
((void (__fastcall
*
)(_JNIEnv
*
,
int
, const char
*
))v6
-
>functions
-
>ReleaseStringUTFChars)(v6, v21, v5);
v8
=
j_charToJstring(v6, v17);
/
/
md5加密的结果赋值给v8
}
if
( _stack_chk_guard
=
=
v22 )
return
v8;
/
/
返回
}
result
=
_stack_chk_guard
-
v22;
if
( _stack_chk_guard
=
=
v22 )
result
=
j_j_charToJstring(v6, UNSIGNATURE[
0
]);
return
result;
}
|
分析得知encode函数是将(str2+"/bfhdkud_time="+str1)进行md5加密,将加密的结果吗作为返回值
回到接口类中
1
2
|
String str3
=
"app"
;
str2
=
(Q.b(((NDKTools.encode(HeyBoxApplication.g(), str2, str1)).replaceAll(
"a"
, str3)).replaceAll(
"0"
, str3))).substring(i,
10
);
|
发现只取md5返回值然后将其中的"a"和"0"都替换成str3即"app"
然后又调用了Q类下的b(String)方法,发现同样是md5算法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
public static String b(String p0){
if
(TextUtils.isEmpty(p0)) {
return
"";
}
try
{
byte[] uobyteArray
=
MessageDigest.getInstance(
"MD5"
).digest(p0.getBytes());
int
len
=
uobyteArray.length;
String
str
=
"";
for
(
int
i
=
0
; i <
len
; i
=
i
+
1
) {
int
i1
=
uobyteArray[i] &
0x00ff
;
String str1
=
Integer.toHexString(i1);
if
(str1.length()
=
=
1
) {
str1
=
"0"
+
str1;
}
str
=
str
+
str1;
}
return
str
;
}catch(java.security.NoSuchAlgorithmException e7){
e7.printStackTrace();
return
"";
}
}
|
然后再将其返回值取前10位赋值给str2,即后面的hkey
写成python实现
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
|
#coding:utf8
import
hashlib
import
datetime
def
md5(test_str:
str
, key
=
None
):
print
(
'MD5加密前为 :'
+
test_str)
if
key
is
None
:
m
=
hashlib.md5()
else
:
m
=
hashlib.md5(key.encode(
'utf-8'
))
m.update(test_str.encode())
res
=
m.hexdigest()
print
(
'MD5加密后为 :'
+
res)
return
res
def
gethkey():
str1
=
"1666285158"
str1
=
"1666259837"
str2
=
"/bbs/app/api/user/permission/"
#str2="/account/get_ads_info/"
if
str2[
-
1
]
=
=
"/"
:
str2
=
str2[:
-
1
]
zuhe_str
=
str2
+
"/bfhdkud_time="
+
str1
#print(zuhe_str)
ret
=
md5(zuhe_str).replace(
"a"
,
"app"
).replace(
"0"
,
"app"
)
hkey
=
md5(ret)[
0
:
10
]
print
(hkey)
if
__name__
=
=
'__main__'
:
gethkey()
'''
MD5加密前为 :/bbs/app/api/user/permission/bfhdkud_time=1666259837
MD5加密后为 :5e3fa112b55e9b8a087a5a1196f4645f
MD5加密前为 :5e3fapp112b55e9b8appapp87app5app1196f4645f
MD5加密后为 :0cf9be6f884cbf25ffd97e9fa61778d4
0cf9be6f88
'''
|
对比下请求报文,发现key值是一样的。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
GET
/
bbs
/
app
/
api
/
user
/
permission?heybox_id
=
???&imei
=
???&os_type
=
Android&os_version
=
8.1
.
0
&version
=
1.3
.
92
&_time
=
1666259837
&hkey
=
0cf9be6f88
HTTP
/
1.1
Referer: http:
/
/
api.maxjia.com
/
User
-
Agent: Mozilla
/
5.0
AppleWebKit
/
537.36
(KHTML, like Gecko) Chrome
/
41.0
.
2272.118
Safari
/
537.36
ApiMaxJia
/
1.0
Cookie: ???
Host: api.xiaoheihe.cn
Connection: Keep
-
Alive
Accept
-
Encoding: gzip
#####################################################
GET
/
account
/
get_ads_info
/
?heybox_id
=
???&imei
=
???&os_type
=
Android&os_version
=
8.1
.
0
&version
=
1.3
.
92
&_time
=
1666285158
&hkey
=
d630c904c2 HTTP
/
1.1
Referer: http:
/
/
api.maxjia.com
/
User
-
Agent: Mozilla
/
5.0
AppleWebKit
/
537.36
(KHTML, like Gecko) Chrome
/
41.0
.
2272.118
Safari
/
537.36
ApiMaxJia
/
1.0
Cookie: ????
Host: api.xiaoheihe.cn
Connection: Keep
-
Alive
Accept
-
Encoding: gzip
|
更多【某黑盒某请求参数分析】相关视频教程:www.yxfzedu.com