抓包

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
POST /mobile/login/pwd/v3 HTTP/1.1
Cookie: 1&_device=android&33a5bf7e-3a1f-304d-88c0-2aa86f82e18d&6.6.99;channel=and-f5;impl=com.ximalaya.ting.android;osversion=29;fp=009527657x2022q22564v0500000000000000000000000000000000000000;device_model=Pixel;XUM=rDdDR+ue;XIM=;c-oper=%E6%9C%AA%E7%9F%A5;net-mode=WIFI;freeFlowType=0;res=1080%2C1794;NSUP=;AID=kp/0rc69Gq0=;manufacturer=Google;XD=sHnwclBO3jtu+l5DxGR4bIiZ6g6lwvKYqCbncl15SZsccW+GrFjdNFhWy8JireKMBd6MGYXSAkZ2LuQsYS4YPStl4EV4/tQ4xdmhmEAQ44oYB3XNwL88kFoOPn3AHnfpGqZtWURwn089pZG2kC7vrA==;umid=ai14e79e149756f4d94008f52d88b4a7af;xm_grade=0;minorProtectionStatus=0;domain=.ximalaya.com;path=/;
Cookie2: $version=1
Accept: */*
user-agent: ting_6.6.99(Pixel,Android29)
Content-Type: application/json; charset=utf-8
Content-Length: 555
Host: passport.ximalaya.com
Connection: Keep-Alive
Accept-Encoding: gzip

{"password":"RoygPt8w2Q8ounzx5L7OWK4/zkRBmlVM1hiQ1h1mn4saT4aFBa1uUVwUXoHnMUN/vwaC6VTACZmh\nsKTFJp9TKhbfI8P/zhq1oe43YtcP+TSd5fHSCtHizJ7bi765rRaXAKST4bmfbEjKw6mZoV1C8OLV\nv9WYwHZ8SrA9RlqKhZc\u003d\n",
"fdsOtp":"8486561300151916644",
"signature":"5773c1d238eaa98cc326ef75791330f46f0d25dd",
"nonce":"0-80EF46FB85EA8ae05778056ad5a50649c4c697ba21529151a1e80b0a5f6436",
"account":"kh08DatDyfB+dTAph6A+BPft+u279VZUGetmWtvWct16ksFpz2su+KdYK39+FqZz655TITrQfAys\nTe4IKHcPU54Rjw5YBzAIvk6mB4kD4+oZNx+no3GhIk5wcBb+FGhdP79+VsUoFR52gYsQP/FQCLtj\nohCX5Vn1f6Qh0mHMkfg\u003d\n"}

nonce不用管,就看下password accountsignature

用Java层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
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
RSA/ECB/PKCS1Padding init Key Utf8:  0��0
 *�H��

RSA/ECB/PKCS1Padding init Key Hex: 30819f300d06092a864886f70d010101050003818d00308189028181009585a4773abeecb949701d49762f2dfab9599ba19dfe1e1a2fa200e32e0444f426da528912d9ea8669515f6f1014c454e1343b97abf7c10fe49d520a6999c66b230e0730c3f802d136a892501ff2b13d699b5c7ecbbfef428ac36d3d83a5bd627f18746a7fdc774c12a38de2760a3b95c653c10d7eb7f84722976251f649556b0203010001
RSA/ECB/PKCS1Padding init Key Base64: MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCVhaR3Or7suUlwHUl2Ly36uVmboZ3+HhovogDjLgRE9CbaUokS2eqGaVFfbxAUxFThNDuXq/fBD+SdUgppmcZrIw4HMMP4AtE2qJJQH/KxPWmbXH7Lv+9CisNtPYOlvWJ/GHRqf9x3TBKjjeJ2CjuVxlPBDX63+Ecil2JR9klVawIDAQAB
=======================================================
Cipher.init('int', 'java.security.Key', 'java.security.SecureRandom') is called!
java.lang.Throwable
at javax.crypto.Cipher.init(Native Method)
at javax.crypto.Cipher.init(Cipher.java:1084)
at javax.crypto.Cipher.init(Native Method)
at com.ximalaya.ting.android.loginservice.LoginEncryptUtil.PDuxkguhSq(Native Method)
at com.ximalaya.ting.android.loginservice.LoginEncryptUtil.encryptByPublicKeyNative(SourceFile:43)
at com.ximalaya.ting.android.loginservice.LoginHelper.encryPsw(SourceFile:12)
at com.ximalaya.ting.android.loginservice.LoginRequest.loginByPsw(SourceFile:304)
at com.ximalaya.ting.android.loginservice.LoginService.login(SourceFile:92)
at com.ximalaya.ting.android.loginservice.LoginService.loginWithPswd(SourceFile:52)
at com.ximalaya.ting.android.login.fragment.BaseLoginFragment.doLoginWithXMLY(SourceFile:347)
at com.ximalaya.ting.android.login.fragment.LoginFragment.a(SourceFile:697)
at com.ximalaya.ting.android.login.fragment.n.run(SourceFile:1)
at org.aspectj.a.b.h.a(SourceFile:229)
at com.ximalaya.commonaspectj.f.b(SourceFile:85)
at com.ximalaya.commonaspectj.f.a(SourceFile:1)
at com.ximalaya.commonaspectj.f.a(SourceFile:24)
at com.ximalaya.ting.android.login.fragment.LoginFragment.onClick(SourceFile:587)
at android.view.View.performClick(View.java:7140)
at android.view.View.performClickInternal(View.java:7117)
at android.view.View.access$3500(View.java:801)
at android.view.View$PerformClick.run(View.java:27351)
at android.os.Handler.handleCallback(Handler.java:883)
at android.os.Handler.dispatchMessage(Handler.java:100)
at android.os.Looper.loop(Looper.java:214)
at com.ximalaya.ting.android.framework.c$1.run(SourceFile:107)
at android.os.Handler.handleCallback(Handler.java:883)
at android.os.Handler.dispatchMessage(Handler.java:100)
at android.os.Looper.loop(Looper.java:214)
at android.app.ActivityThread.main(ActivityThread.java:7356)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:492)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:930)

Cipher.doFinal('[B', 'int', 'int') is called!
java.lang.Throwable
at javax.crypto.Cipher.doFinal(Native Method)
at com.ximalaya.ting.android.loginservice.LoginEncryptUtil.PDuxkguhSq(Native Method)
at com.ximalaya.ting.android.loginservice.LoginEncryptUtil.encryptByPublicKeyNative(SourceFile:43)
at com.ximalaya.ting.android.loginservice.LoginHelper.encryPsw(SourceFile:12)
at com.ximalaya.ting.android.loginservice.LoginRequest.loginByPsw(SourceFile:304)
at com.ximalaya.ting.android.loginservice.LoginService.login(SourceFile:92)
at com.ximalaya.ting.android.loginservice.LoginService.loginWithPswd(SourceFile:52)
at com.ximalaya.ting.android.login.fragment.BaseLoginFragment.doLoginWithXMLY(SourceFile:347)
at com.ximalaya.ting.android.login.fragment.LoginFragment.a(SourceFile:697)
at com.ximalaya.ting.android.login.fragment.n.run(SourceFile:1)
at org.aspectj.a.b.h.a(SourceFile:229)
at com.ximalaya.commonaspectj.f.b(SourceFile:85)
at com.ximalaya.commonaspectj.f.a(SourceFile:1)
at com.ximalaya.commonaspectj.f.a(SourceFile:24)
at com.ximalaya.ting.android.login.fragment.LoginFragment.onClick(SourceFile:587)
at android.view.View.performClick(View.java:7140)
at android.view.View.performClickInternal(View.java:7117)
at android.view.View.access$3500(View.java:801)
at android.view.View$PerformClick.run(View.java:27351)
at android.os.Handler.handleCallback(Handler.java:883)
at android.os.Handler.dispatchMessage(Handler.java:100)
at android.os.Looper.loop(Looper.java:214)
at com.ximalaya.ting.android.framework.c$1.run(SourceFile:107)
at android.os.Handler.handleCallback(Handler.java:883)
at android.os.Handler.dispatchMessage(Handler.java:100)
at android.os.Looper.loop(Looper.java:214)
at android.app.ActivityThread.main(ActivityThread.java:7356)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:492)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:930)

RSA/ECB/PKCS1Padding doFinal data Utf8: 123456
RSA/ECB/PKCS1Padding doFinal data Hex: 313233343536
RSA/ECB/PKCS1Padding doFinal data Base64: MTIzNDU2
RSA/ECB/PKCS1Padding doFinal result Hex: 468ca03edf30d90f28ba7cf1e4bece58ae3fce44419a554cd61890d61d669f8b1a4f868505ad6e515c145e81e731437fbf0682e954c00999a1b0a4c5269f532a16df23c3ffce1ab5a1ee3762d70ff9349de5f1d20ad1e2cc9edb8bbeb9ad169700a493e1b99f6c48cac3a999a15d42f0e2d5bfd598c0767c4ab03d465a8a8597
RSA/ECB/PKCS1Padding doFinal result Base64: RoygPt8w2Q8ounzx5L7OWK4/zkRBmlVM1hiQ1h1mn4saT4aFBa1uUVwUXoHnMUN/vwaC6VTACZmhsKTFJp9TKhbfI8P/zhq1oe43YtcP+TSd5fHSCtHizJ7bi765rRaXAKST4bmfbEjKw6mZoV1C8OLVv9WYwHZ8SrA9RlqKhZc=

算法这里不是做这个app的重点

先看函数栈

at javax.crypto.Cipher.doFinal(Native Method)
at com.ximalaya.ting.android.loginservice.LoginEncryptUtil.PDuxkguhSq(Native Method)

第一个函数的native是frida自己改的,实际上不是native,第二个是native函数

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
package com.ximalaya.ting.android.loginservice;

import android.content.Context;
import com.alipay.sdk.sys.a;
import com.ximalaya.ting.android.xmuimonitorbase.core.AppMethodBeat;
import java.util.Map;
import java.util.TreeMap;

/* loaded from: classes8.dex */
public class LoginEncryptUtil {
private native String AQqfJzIZgx(Context context, boolean z, String str);

private native String BBVNXvcLGU(Context context, Map<String, String> map);

private native String PDuxkguhSq(String str);

static {
AppMethodBeat.i(22458);
System.loadLibrary("login_encrypt");
AppMethodBeat.o(22458);
}

/* loaded from: classes8.dex */
private static class Holder {
private static final LoginEncryptUtil instance = new LoginEncryptUtil();

private Holder() {
}

static {
AppMethodBeat.i(22585);
AppMethodBeat.o(22585);
}
}

public static LoginEncryptUtil getInstance() {
AppMethodBeat.i(22454);
LoginEncryptUtil loginEncryptUtil = Holder.instance;
AppMethodBeat.o(22454);
return loginEncryptUtil;
}

public String createLoginParamSign(Context context, boolean z, Map<String, String> map) {
AppMethodBeat.i(22455);
if (map == null || map.isEmpty()) {
AppMethodBeat.o(22455);
return null;
}
TreeMap treeMap = new TreeMap(map);
StringBuilder sb = new StringBuilder();
for (Map.Entry entry : treeMap.entrySet()) {
sb.append((String) entry.getKey());
sb.append("=");
sb.append((String) entry.getValue());
sb.append(a.f2464b);
}
String AQqfJzIZgx = AQqfJzIZgx(context, z, sb.toString());
AppMethodBeat.o(22455);
return AQqfJzIZgx;
}

public String encryptByPublicKeyNative(String str) {
AppMethodBeat.i(22456);
String PDuxkguhSq = PDuxkguhSq(str);
AppMethodBeat.o(22456);
return PDuxkguhSq;
}

public String getSignatureNative(Context context, Map<String, String> map) {
AppMethodBeat.i(22457);
String BBVNXvcLGU = BBVNXvcLGU(context, map);
AppMethodBeat.o(22457);
return BBVNXvcLGU;
}
}

找到对应的so文件,查看exports导出表,这里导出函数其实都是有混淆的

这里PDuxkguhSq里的sub_B70也就是RSA算法被混淆成了如下

1
2
3
4
5
6
7
8
9
10
11
12
int __fastcall Java_com_ximalaya_ting_android_loginservice_LoginEncryptUtil_PDuxkguhSq(int a1, int a2, int a3)
{
__int64 v5; // r0
int v6; // r6
int v7; // r4

v5 = ((__int64 (__fastcall *)(int, void *))*(_DWORD *)(*(_DWORD *)a1 + 668))(a1, &unk_D870);
v6 = v5;
v7 = sub_B70(a1, HIDWORD(v5), a3, v5);
(*(void (__fastcall **)(int, int))(*(_DWORD *)a1 + 92))(a1, v6);
return v7;
}

反编译这个函数,可以看到虽然解析除了jni函数,但是参数是乱七八糟的

1
2
3
4
5
6
7
8
9
int *__fastcall sub_B70(JNIEnv *a1, int a2, int a3, int a4)
{
...

v90 = (*a1)->NewStringUTF(a1, aUtf);
v5 = (*a1)->FindClass(a1, asc_D060);
v6 = (*a1)->GetStaticMethodID(a1, v5, &byte_D079, &unk_D090);
v91 = (*a1)->CallStaticObjectMethod(a1, v5, v6, v90);
v92 = (*a1)->ExceptionCheck(a1);

比如这里的GetStaticMethodID,第三个参数应该是函数名,第四个参数是函数签名,但是跳转过去之后都是其他字符

1
2
.data:0000D079 byte_D079       DCB 0x93                ; DATA XREF: sub_B70+3A↑o
.data:0000D079 ; sub_B70+48↑o ...

这就涉及到ollvm字符串的加解密

一种方式是直接hook函数的地址,比如GetStaticMethodID函数,因为so在运行的时候字符串是解密状态的,此时再打印解密之后的字符串的地址

第二种方式是用jnitrace,什么是jnitrace?so中会应用很多的jni函数,比如Java的字符串到so,需要先使用GetStringUtfChars来转成C语言字符串,而且加密后的结果,如果需要转成jstring,又需要用到NewStringUtf ,通过hook这些jni函数,可以定位关键代码,也可以大体上了解函数的代码逻辑,简单来说,jnitrace就是hook一系列jni函数,把执行这些jni函数的堆栈给弄清楚

1
2
3
4
5
6
7
8
9
10
github地址 https://github.com/chame1eon/jnitrace
安装 pip install jnitrace
// -l指定so文件名 然后指定进程名也就是包名
// 不加attach参数的话,jnitrace会自行启动app进行hook,一般要加attach参数,不然hook太久
jnitrace -m attach -l liblogin_encrypt.so com.ximalaya.ting.android

jnitrace对版本有时候比较玄学,这里使用的是
Android 10.0
frida 14.2.8
jnitrace 3.3.0

输出如下

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
C:\Users\Administrator>jnitrace -m attach -l liblogin_encrypt.so com.ximalaya.ting.android
Tracing. Press any key to quit...
Traced library "liblogin_encrypt.so" loaded from path "/data/app/com.ximalaya.ting.android-Q_vQ9K708see9mNcQzYjCw==/lib/arm".

/* TID 13380 */
1746 ms [+] JNIEnv->NewStringUTF
1746 ms |- JNIEnv* : 0xe8519140
1746 ms |- char* : 0xae42c870
1746 ms |: MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCVhaR3Or7suUlwHUl2Ly36uVmboZ3+HhovogDjLgRE9CbaUokS2eqGaVFfbxAUxFThNDuXq/fBD+SdUgppmcZrIw4HMMP4AtE2qJJQH/KxPWmbXH7Lv+9CisNtPYOlvWJ/GHRqf9x3TBKjjeJ2CjuVxlPBDX63+Ecil2JR9klVawIDAQAB
1746 ms |= jstring : 0x71 { MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCVhaR3Or7suUlwHUl2Ly36uVmboZ3+HhovogDjLgRE9CbaUokS2eqGaVFfbxAUxFThNDuXq/fBD+SdUgppmcZrIw4HMMP4AtE2qJJQH/KxPWmbXH7Lv+9CisNtPYOlvWJ/GHRqf9x3TBKjjeJ2CjuVxlPBDX63+Ecil2JR9klVawIDAQAB }

1746 ms ----------------------------------------------------------Backtrace----------------------------------------------------------
1746 ms |-> 0xae4230df: Java_com_ximalaya_ting_android_loginservice_LoginEncryptUtil_PDuxkguhSq+0x1a (liblogin_encrypt.so:0xae41f000)


/* TID 13380 */
2183 ms [+] JNIEnv->NewStringUTF
2183 ms |- JNIEnv* : 0xe8519140
2183 ms |- char* : 0xae42c055
2183 ms |: RSA
2183 ms |= jstring : 0x81 { RSA }

2183 ms ---------------------------------Backtrace---------------------------------
2183 ms |-> 0xae41fb93: liblogin_encrypt.so!0xb93 (liblogin_encrypt.so:0xae41f000)


/* TID 13380 */
2196 ms [+] JNIEnv->FindClass
2196 ms |- JNIEnv* : 0xe8519140
2196 ms |- char* : 0xae42c060
2196 ms |: java/security/KeyFactory
2196 ms |= jclass : 0x91 { java/security/KeyFactory }

2196 ms ---------------------------------Backtrace---------------------------------
2196 ms |-> 0xae41fba5: liblogin_encrypt.so!0xba5 (liblogin_encrypt.so:0xae41f000)


/* TID 13380 */
2208 ms [+] JNIEnv->GetStaticMethodID
2208 ms |- JNIEnv* : 0xe8519140
2208 ms |- jclass : 0x91 { java/security/KeyFactory }
2208 ms |- char* : 0xae42c079
2208 ms |: getInstance
2208 ms |- char* : 0xae42c090
2208 ms |: (Ljava/lang/String;)Ljava/security/KeyFactory;
2208 ms |= jmethodID : 0x7084aae8 { getInstance(Ljava/lang/String;)Ljava/security/KeyFactory; }

2208 ms ---------------------------------Backtrace---------------------------------
2208 ms |-> 0xae41fbc1: liblogin_encrypt.so!0xbc1 (liblogin_encrypt.so:0xae41f000)

可以看到首先调用了JNIEnv->NewStringUTF,参数是MIGf...AQAB,然后依次调用了JNIEnv->NewStringUTFJNIEnv->FindClassJNIEnv->GetStaticMethodID等等

这样有啥用呢?比如有时候逆向的时候,定位不到Java的代码,也找不到so层的代码,就可以尝试用jnitrace找函数,因为如果加密是在so层,那么大概率会调用jni函数去返回值或者传参

而且,如果字符串是被加密了,使用jnitrace也可以hook到jni函数的同时,获取函数的参数,而这里hook到的参数都是解密状态的

ollvm解密

接上文,比如v5 = (*a1)->FindClass(a1, asc_D060);的参数应该是类名,但是我们查看asc_D060处却发现是其他的字符

1
2
.data:0000D060 asc_D060        DCB 0xB,0               ; DATA XREF: sub_B70+28↑o
.data:0000D060 ; sub_B70+2E↑o ...

那么可以在so加载之后再去打印该地址处的内存,有时候会报错,因为so还未加载进内存,需要点击某些app的功能才会让so被加载

1
2
3
//打印内存中的明文字符串
var soAddr = Module.findBaseAddress("liblogin_encrypt.so");
console.log(hexdump(soAddr.add(0xD060)));
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
ae42c060  6a 61 76 61 2f 73 65 63 75 72 69 74 79 2f 4b 65  java/security/Ke
ae42c070 79 46 61 63 74 6f 72 79 00 67 65 74 49 6e 73 74 yFactory.getInst
ae42c080 61 6e 63 65 00 00 00 00 00 00 00 00 00 00 00 00 ance............
ae42c090 28 4c 6a 61 76 61 2f 6c 61 6e 67 2f 53 74 72 69 (Ljava/lang/Stri
ae42c0a0 6e 67 3b 29 4c 6a 61 76 61 2f 73 65 63 75 72 69 ng;)Ljava/securi
ae42c0b0 74 79 2f 4b 65 79 46 61 63 74 6f 72 79 3b 00 00 ty/KeyFactory;..
ae42c0c0 61 6e 64 72 6f 69 64 2f 75 74 69 6c 2f 42 61 73 android/util/Bas
ae42c0d0 65 36 34 00 64 65 63 6f 64 65 00 00 00 00 00 00 e64.decode......
ae42c0e0 28 4c 6a 61 76 61 2f 6c 61 6e 67 2f 53 74 72 69 (Ljava/lang/Stri
ae42c0f0 6e 67 3b 49 29 5b 42 00 00 00 00 00 00 00 00 00 ng;I)[B.........
ae42c100 6a 61 76 61 2f 73 65 63 75 72 69 74 79 2f 73 70 java/security/sp
ae42c110 65 63 2f 58 35 30 39 45 6e 63 6f 64 65 64 4b 65 ec/X509EncodedKe
ae42c120 79 53 70 65 63 00 3c 69 6e 69 74 3e 00 28 5b 42 ySpec.<init>.([B
ae42c130 29 56 00 67 65 6e 65 72 61 74 65 50 75 62 6c 69 )V.generatePubli
ae42c140 63 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 c...............
ae42c150 28 4c 6a 61 76 61 2f 73 65 63 75 72 69 74 79 2f (Ljava/security/