0%

嘟嘟牛v4.54

抓包

打开手机端小黄鸟 / 进入嘟嘟牛登录页面 / 输入账号和密码 /点击登录

抓取到一个数据包,分析得到一个 “Encrypt”

0

查壳

1743070291861.jpg

pkid.jar

脱壳

拖入Jadx 分析代码

搜索 “Encrypt” 出现 两条 可疑结果

使用 Frida 定位 确定点击登录时会走哪条方法

Hook第一个可疑函数

paraMap

1
2
3
4
5
6
7
8
9
10
11
Java.perform(function(){
// 使用app的类
var jsonRequest = Java.use("com.dodonew.online.http.JsonRequest");
// 确认以下看是否找到
console.log("jsonRequest:",jsonRequest);

jsonRequest.paraMap.implementation = function(a){
console.log("param1:",a);
this.paraMap();
}
});

成功找到该类 / 进入嘟嘟牛 ,点击登录 / 没有反应

0

App 可能没有走这个函数,也可能是代码写错了或者Frida版本问题

Hook第二个可疑函数

addRequestMap

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
Java.perform(function(){
// 使用app的类
var jsonRequest = Java.use("com.dodonew.online.http.JsonRequest");
// 确认看是否找到
console.log("jsonRequest:",jsonRequest);

jsonRequest.paraMap.implementation = function(a){
console.log("param1:",a);
this.paraMap();
}
jsonRequest.addRequestMap.overload('java.util.Map', 'int').implementation = function(a,b){
console.log("addRequestMap param:",a,b);
this.addRequestMap(a,b);
}
});

进入App 点击登录,会出现参数

970565.jpg

确定该App在登陆时走的是第二个可疑函数

打印可疑函数的参数

addRequestMap

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
Java.perform(function(){
// 使用app的类
var jsonRequest = Java.use("com.dodonew.online.http.JsonRequest");
// 确认看是否找到
console.log("jsonRequest:",jsonRequest);

jsonRequest.paraMap.implementation = function(a){
console.log("param1:",a);
this.paraMap();
}
jsonRequest.addRequestMap.overload('java.util.Map', 'int').implementation = function(a,b){
console.log("addRequestMap param:",a,b);
var v = Java.cast(a,Java.use("java.util.HashMap"));
console.log("addRequestMap param:",v.toString());
this.addRequestMap(a,b);
}
});

4.jpg

app协议分析

找到第一个加密点

分析确定的可疑方法 addRequestMap

找到第一个加密点

“sign” 值加密

57.jpg

709.jpg

分析RequestUtil.encodeDesMap 方法

1743079108144.jpg

Hook Utils.md5 方法查看传入的参数

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
Java.perform(function(){
// 使用app的类
var jsonRequest = Java.use("com.dodonew.online.http.JsonRequest");
// 确认看是否找到
console.log("jsonRequest:",jsonRequest);

jsonRequest.paraMap.implementation = function(a){
console.log("param1:",a);
this.paraMap();
}
jsonRequest.addRequestMap.overload('java.util.Map', 'int').implementation = function(a,b){
console.log("addRequestMap param:",a,b);
var v = Java.cast(a,Java.use("java.util.HashMap"));
console.log("addRequestMap param:",v.toString());

this.addRequestMap(a,b);
}
var utils = Java.use("com.dodonew.online.util.Utils");
utils.md5.implementation = function(a){
console.log("md5 param:",a);
var retval = this.md5(a);
console.log("md5 retval:",retval);
return retval;
}
});

1743079145050.jpg

测试下看是不是标准的md5

1743079160873.jpg

是标准的md5

找第二个加密点

返回 可疑方法 addRequestMap

1743079181739.jpg

Hook RequestUtil.encodeDesMap函数查看传入的参数

1
2
3
4
5
6
7
8
var requestUtil = Java.use("com.dodonew.online.http.RequestUtil");
requestUtil.encodeDesMap.overload('java.lang.String', 'java.lang.String', 'java.lang.String').implementation = function(a,b,c){
console.log("encodeDesMap params:",a);
console.log("encodeDesMap key:",b);
console.log("encodeDesMap iv:",c);
var retval = this.encodeDesMap(a,b,c);
console.log("encodeDesMap retval:",retval);
return retval;

这里将第一个加密点的 sign 值换成大写后放入 data 参数中

1743079197955.jpg

进入 encodeDesMap 方法分析

分析该方法对参数的操作

1743079212821.jpg

该方法操作了 data, key, iv 参数

涉及 DesSecurity 方法 和 encrypt64 方法

进入 DesSecurity 方法分析

1743079225922.jpg

对密钥 key 进行了 md5 加密

利用所得密钥 对data 进行了 DES 加密

Hook DESKeySpec 方式

取 md5 加密后的前八位

1
2
3
4
5
var base64 = Java.use("android.util.Base64");
var dESkeySpec = Java.use("javax.crypto.spec.DESKeySpec");
dESkeySpec.$init.overload('[B').implementation = function(a){
console.log("DESKeySpec param:",base64.encodeToString(a,0));
this.$init(a);

1743079249013.jpg

算法复现

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
// md5 param: equtype=ANDROID&loginImei=Androidd1c9f85c8bbeb3f4&timeStamp=1742895425805&userPwd=12345678&username=15639245016&key=sdlkjsdljf0j2fsjk
// md5 retval: 174d17095a6c843775597826b40dacc2
import CryptoJS from "crypto-js";
function getSign(user,pwd,time){
var data = "equtype=ANDROID&loginImei=Androidd1c9f85c8bbeb3f4&timeStamp="+time +"&userPwd="+pwd +"&username=" + user+"&key=sdlkjsdljf0j2fsjk";

return CryptoJS.MD5(data).toString();
}
// encodeDesMap params: {"equtype":"ANDROID","loginImei":"Androidd1c9f85c8bbeb3f4","sign":"174D17095A6C843775597826B40DACC2","timeStamp":"1742895425805","userPwd":"12345678","username":"15639245016"}
// encodeDesMap key: 65102933
// encodeDesMap iv: 32028092
function getData(user,pwd){
var time = "1742904583925";
var sign = getSign(user,pwd,time).toUpperCase();
console.log(sign);
var _plainText = '{"equtype":"ANDROID","loginImei":"Androidd1c9f85c8bbeb3f4","sign":"'+ sign +'","timeStamp":"'+time +'","userPwd":"'+pwd +'","username":"'+user +'"}';

var keyMD5 = CryptoJS.MD5("65102933").toString();
var _key= CryptoJS.enc.Hex.parse(keyMD5);
var _iv = CryptoJS.enc.Utf8.parse("32028092");

return CryptoJS.DES.encrypt(_plainText,_key,{
iv:_iv,
mode:CryptoJS.mode.CBC,
padding:CryptoJS.pad.Pkcs7
}).toString();

}
console.log(getData("15639245016","12345678"));

1743079291541.jpg

协议复现

app 与服务器的交互 是依靠数据(抓包抓取到的)

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
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
// md5 param: equtype=ANDROID&loginImei=Androidd1c9f85c8bbeb3f4&timeStamp=1742895425805&userPwd=12345678&username=15639245016&key=sdlkjsdljf0j2fsjk
// md5 retval: 174d17095a6c843775597826b40dacc2
var CryptoJS = require('crypto-js');
function getSign(user,pwd,time){
var data = "equtype=ANDROID&loginImei=Androidd1c9f85c8bbeb3f4&timeStamp="+time +"&userPwd="+pwd +"&username=" + user+"&key=sdlkjsdljf0j2fsjk";

return CryptoJS.MD5(data).toString();
}
// encodeDesMap params: {"equtype":"ANDROID","loginImei":"Androidd1c9f85c8bbeb3f4","sign":"174D17095A6C843775597826B40DACC2","timeStamp":"1742895425805","userPwd":"12345678","username":"15639245016"}
// encodeDesMap key: 65102933
// encodeDesMap iv: 32028092
function encrypt(user,pwd){
var time = new Date().getTime();
var sign = getSign(user,pwd,time).toUpperCase();
console.log(sign);
var _plainText = '{"equtype":"ANDROID","loginImei":"Androidd1c9f85c8bbeb3f4","sign":"'+ sign +'","timeStamp":"'+time +'","userPwd":"'+pwd +'","username":"'+user +'"}';

var keyMD5 = CryptoJS.MD5("65102933").toString();
var _key= CryptoJS.enc.Hex.parse(keyMD5);
var _iv = CryptoJS.enc.Utf8.parse("32028092");

return CryptoJS.DES.encrypt(_plainText,_key,{
iv:_iv,
mode:CryptoJS.mode.CBC,
padding:CryptoJS.pad.Pkcs7
}).toString();

}
function decrypt(cipherText){
var key = CryptoJS.enc.Hex.parse(CryptoJS.MD5("65102933").toString());
var iv = CryptoJS.enc.Utf8.parse("32028092");
return CryptoJS.DES.decrypt(cipherText,key,{
iv: iv,
mode:CryptoJS.mode.CBC,
padding:CryptoJS.pad.Pkcs7
}).toString(CryptoJS.enc.Utf8);
}
// var a = decrypt("2v+DC2gq7RuAC8PE5GZz5wH3/y9ZVcWhFwhDY9L19g9iEd075+Q7xwewvfIN0g0ec/NaaF43/S0=")
// console.log(a)

使用 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
import base64
import requests
import json
import execjs
import _locale
_locale._getdefaultlocale = (lambda *args: ['zh_CN', 'utf8'])
f = open("demo.js", "r")
demo_list = f.readlines()
print(list(range(0, len(demo_list))))
jscode = ""
for i in range(0,len(demo_list)):
jscode += demo_list[i]
f.close()
js = execjs.compile(jscode)
plainText = js.call('encrypt', '15639245016', '12345678')
print(plainText)
url = 'http://api.dodovip.com/api/user/login'
data = json.dumps({"Encrypt": plainText})
headers = {
"Content-Type": "application/json;charset=utf-8",
"User-Agent": "Dalvik/2.1.0 (Linux; U; Android 9; Pixel 3 Build/PD1A.180720.030)"
}
r = requests.post(url=url, data=data, headers=headers)
print(r)
print(r.text)
print(type(r.text))
print(r.content)
cipherText = js.call('decrypt', 'r.text')
print(cipherText)

1743079307691.jpg

1743079317970.jpg

伪造协议成功