native层 安卓_记一次APP的so层算法逆向(二)

 前言:初学逆向 请多多指教

学习到的内容

1、对JNI接口提供的常用方法的了解和学习

2、在so层加密的未必不能用java层来进行hook

3、对IDA工具的了解和学习

4、RSA 1024位加密,如何加密的长度超过了117个字节会如何处理

01

APP登陆界面:

07b4fa71-6f15-eb11-8da9-e4434bdf6706.png

HTTP请求抓取的数据包:

POST http://mobile.ximalaya.com/passport-sign-mobile/v2/signin/password HTTP/1.1Cookie: 1&_device=android&00000000-765b-f767-ffff-ffffb1f6f2ee&6.3.90;channel=and-f5;impl=com.ximalaya.ting.android;osversion=19;XUM=CAAnCf6e;XIM=31508d6f5a927;c-oper=%E6%9C%AA%E7%9F%A5;net-mode=WIFI;res=720%2C1280;NSUP=;AID=MGEERkcGWHQ=;manufacturer=OPPO;xm_grade=1;domain=.ximalaya.com;path=/;x_xmly_resource=xm_source%3Ahomepage%26xm_medium%3Ahomepage;x_xmly_tid=3854071131308217476;x_xmly_ts=1603115956979;Cookie2: $version=1Accept: */*user-agent: ting_6.3.90(OPPO+R11+Plus,Android19)Content-Type: application/json; charset=utf-8Content-Length: 349Host: mobile.ximalaya.comConnection: Keep-AliveAccept-Encoding: gzipcaZsssMPghps7hYeuO/h3lG2ujXN6dzCSKwRJiOLGp/Dl1L22kRHxCXwpwaCsk8Blmlit2n7wXUpF9rfsgPKNiHQZEVLuDMpCzdKBk/8HTOg5SoI5Ee9iqN/x3EZLjgyKRzvw6rcKbN8CykAgXYJmvSZrXnROCAX7lDmdq53kNU AlqzqooeenWw2hUK+LtCdJvL8j2S6SYpG4eOU64C/6wGdlvdG0nR/1Sbv9AHBY4VyMm/z/3ENPhu6+ygJiGp8HCOjvaveMFqiW4+KrLusph1CV2Z2nPVwRYxDz7xGo0GYVuJB7Lg9ZYYyMSU2Tcjwer0if1u6UIIeQlx/+3dkbQ==

登陆hook日志如下:

10-19 21:59:16.976 3383-3383/com.ximalaya.ting.android D/MyHook:  = = = = = = = = = =javax.crypto.Cipher's doFinal hooking was Starting = = = = = = = =  = = =10-19 21:59:16.976 3383-3383/com.ximalaya.ting.android E/MyHook: The Stack:    java.lang.Throwable: Stack Dump        at com.example.xposedtest.Test$6.afterHookedMethod(Test.java:225)        at de.robv.android.xposed.XposedBridge.handleHookedMethod(XposedBridge.java:645)        at javax.crypto.Cipher.doFinal(Native Method)        at com.ximalaya.ting.android.host.util.common.EncryptUtil.encryptByPublicKeyNative(Native Method)        at com.ximalaya.ting.android.host.manager.i.a$1.a(LoginRequest.java:82)        at com.ximalaya.ting.android.host.manager.i.a$1.onSuccess(LoginRequest.java:54)        at com.ximalaya.ting.android.host.manager.request.a$a.run(ExecutorDeliveryM.java:92)        at android.os.Handler.handleCallback(Handler.java:733)        at android.os.Handler.dispatchMessage(Handler.java:95)        at android.os.Looper.loop(Looper.java:136)        at android.app.ActivityThread.main(ActivityThread.java:5001)        at java.lang.reflect.Method.invokeNative(Native Method)        at java.lang.reflect.Method.invoke(Method.java:515)        at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:793)        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:609)        at de.robv.android.xposed.XposedBridge.main(XposedBridge.java:132)        at dalvik.system.NativeStart.main(Native Method)10-19 21:59:16.976 3383-3383/com.ximalaya.ting.android D/MyHook: Algorithm: RSA/ECB/PKCS1Padding Normal doFinal function's args[0]: {"account":"19817353426","nonce":"4786336759083248576","password":"e9bc0e13a8a16cbb07b175d92a113126","signature":"d8610-19 21:59:16.976 3383-3383/com.ximalaya.ting.android D/MyHook: Algorithm: RSA/ECB/PKCS1Padding Base64 Result Data: caZsssMPghps7hYeuO/h3lG2ujXN6dzCSKwRJiOLGp/Dl1L22kRHxCXwpwaCsk8Blmlit2n7wXUp    F9rfsgPKNiHQZEVLuDMpCzdKBk/8HTOg5SoI5Ee9iqN/x3EZLjgyKRzvw6rcKbN8CykAgXYJmvSZ    rXnROCAX7lDmdq53kNU=10-19 21:59:16.976 3383-3383/com.ximalaya.ting.android D/MyHook:  = = = = = = = = = =javax.crypto.Cipher's doFinal hooking was Ending = = = = = = = = = = = = =10-19 21:59:16.976 3383-3383/com.ximalaya.ting.android D/MyHook: Algorithm: RSA/ECB/PKCS1Padding Normal doFinal function's args[0]: cf1da2325f7191630b265fefcb384"}10-19 21:59:16.976 3383-3383/com.ximalaya.ting.android D/MyHook: Algorithm: RSA/ECB/PKCS1Padding Base64 Result Data: AJas6qKHnp1sNoVCvi7QnSby/I9kukmKRuHjlOuAv+sBnZb3RtJ0f9Um7/QBwWOFcjJv8/9xDT4b    uvsoCYhqfBwjo72r3jBaoluPiqy7rKYdQldmdpz1cEWMQ8+8RqNBmFbiQey4PWWGMjElNk3I8Hq9    In9bulCCHkJcf/t3ZG0=

这里会发现RSA分为两段进行加密了,原因是这里的RSA加密长度是1024bit,最大的加密长度是128-11,也就是117个字节,因为要加密的长度大于所以就分为两段加密!

HOOK日志中的RSA密钥:

MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCVhaR3Or7suUlwHUl2Ly36uVmboZ3+HhovogDjLgRE9CbaUokS2eqGaVFfbxAUxFThNDuXq/fBD+SdUgppmcZrIw4HMMP4AtE2qJJQH/KxPWmbXH7Lv+9CisNtPYOlvWJ/GHRqf9x3TBKjjeJ2CjuVxlPBDX63+Ecil2JR9klVawIDAQAB

加密的明文:account=>手机号 password=>md5加密密码 nonce未知(其实这个是随机数 知不知道都可以 直接固定就好了) signature签名值

{"account":"19817353426","nonce":"4786336759083248576","password":"e9bc0e13a8a16cbb07b175d92a113126","signature":"d86cf1da2325f7191630b265fefcb384"}

JEB反编译 根据函数调用栈开始分析

重点java层关注如下内容:

LoginParameterModel v1 = new LoginParameterModel();v1.setAccount(this.b.get("account"));v1.setPassword(MD5.md5(this.b.get("password")));v1.setNonce(arg7);HashMap v0 = new HashMap();((Map)v0).put("account", v1.getAccount());((Map)v0).put("password", v1.getPassword());((Map)v0).put("nonce", arg7);v1.setSignature(d.a(((Map)v0)));v0_1 = EncryptUtil.getInstance(MainApplication.getMyApplicationContext()).encryptByPublicKeyNative(new Gson().toJson(v1, LoginParameterModel.class));

0cb4fa71-6f15-eb11-8da9-e4434bdf6706.png

公钥的加密是在so层 public native String encryptByPublicKeyNative(String arg1)

0eb4fa71-6f15-eb11-8da9-e4434bdf6706.png

12b4fa71-6f15-eb11-8da9-e4434bdf6706.png

重要的加密函数如下,自己就不说啥了,因为把注释全写在里面了,主要的作用就是将json的数组放进去然后进行RSA加密 最后Base64编码返回,这里大家不知道会不会想到怪不得hook能hook到RSA的公钥呢,是的,因为终究调用的还是java的RSA加密库!

然会看下面的数据,因为RSA加密的时候是分段加密了,所以就将hook的数据两个(这两个是分别进行base64编码的)进行拼接了起来 但是你会发现数据不一样!

15b4fa71-6f15-eb11-8da9-e4434bdf6706.png

前面部分还一样,但是后面部分就不一样了

caZsssMPghps7hYeuO/h3lG2ujXN6dzCSKwRJiOLGp/Dl1L22kRHxCXwpwaCsk8Blmlit2n7wXUpF9rfsgPKNiHQZEVLuDMpCzdKBk/8HTOg5SoI5Ee9iqN/x3EZLjgyKRzvw6rcKbN8CykAgXYJmvSZrXnROCAX7lDmdq53kNU AlqzqooeenWw2hUK+LtCdJvL8j2S6SYpG4eOU64C/6wGdlvdG0nR/1Sbv9AHBY4VyMm/z/3ENPhu6+ygJiGp8HCOjvaveMFqiW4+KrLusph1CV2Z2nPVwRYxDz7xGo0GYVuJB7Lg9ZYYyMSU2Tcjwer0if1u6UIIeQlx/+3dkbQ==上面是HTTP数据包中的数据=============================================下面是hook数据日志中的数据的两段base64编码的rsacaZsssMPghps7hYeuO/h3lG2ujXN6dzCSKwRJiOLGp/Dl1L22kRHxCXwpwaCsk8Blmlit2n7wXUpF9rfsgPKNiHQZEVLuDMpCzdKBk/8HTOg5SoI5Ee9iqN/x3EZLjgyKRzvw6rcKbN8CykAgXYJmvSZrXnROCAX7lDmdq53kNU=AJas6qKHnp1sNoVCvi7QnSby/I9kukmKRuHjlOuAv+sBnZb3RtJ0f9Um7/QBwWOFcjJv8/9xDT4buvsoCYhqfBwjo72r3jBaoluPiqy7rKYdQldmdpz1cEWMQ8+8RqNBmFbiQey4PWWGMjElNk3I8Hq9In9bulCCHkJcf/t3ZG0=拼接完的字符串,发现如下的和上面的不一样caZsssMPghps7hYeuO/h3lG2ujXN6dzCSKwRJiOLGp/Dl1L22kRHxCXwpwaCsk8Blmlit2n7wXUpF9rfsgPKNiHQZEVLuDMpCzdKBk/8HTOg5SoI5Ee9iqN/x3EZLjgyKRzvw6rcKbN8CykAgXYJmvSZrXnROCAX7lDmdq53kNU =AJas6qKHnp1sNoVCvi7QnSby/I9kukmKRuHjlOuAv+sBnZb3RtJ0f9Um7/QBwWOFcjJv8/9xDT4buvsoCYhqfBwjo72r3jBaoluPiqy7rKYdQldmdpz1cEWMQ8+8RqNBmFbiQey4PWWGMjElNk3I8Hq9In9bulCCHkJcf/t3ZG0=

所以还是得去看SO文件中的函数对json数据是如何进行处理的,RSA加密的函数分析如下,注释都写好了,其实这里最坑的就是下面循环的地方,导致RSA数据不一样的原因就是:RSA分成两段数据进行加密完了,之后就对这字节数组进行一次base64编码然后整体返回

int __fastcall rsa_encode(_JNIEnv *env, int a2, int a3, int a4){  _JNIEnv *env_3; // r4  int KeyFactory_class; // r0  int v6; // r5  int v7; // r0  int v8; // r0  const struct JNINativeInterface *v9; // r1  int result; // r0  int v11; // r0  int v12; // r5  int v13; // r0  int v14; // r6  int v15; // r0  int v16; // r5  int v17; // r0  int v18; // ST38_4  int v19; // r0  int v20; // ST34_4  struct _jmethodID *v21; // r0  int v22; // r0  const struct JNINativeInterface *v23; // r1  int v24; // r0  int v25; // ST38_4  int v26; // r0  int v27; // r5  int v28; // r0  int v29; // r6  int v30; // r0  int v31; // r4  int v32; // r6  int v33; // r5  jobject (*v34)(JNIEnv *, jclass, jmethodID, ...); // r4  int v35; // r0  int v36; // r0  void *v37; // r5  int v38; // r0  int v39; // r6  int v40; // r0  int v41; // r0  int v42; // r0  int v43; // r5  int v44; // r0  int v45; // ST4C_4  int getBytes_function; // r4  jobject (*v47)(JNIEnv *, jobject, jmethodID, ...); // ST20_4  int v48; // r0  int src_data_bytes; // r4  int src_data_bytes_length; // r4  _JNIEnv *env_1; // r6  int v52; // r3  int v53; // r5  int v54; // r2  int v55; // ST4C_4  jobject v56; // ST48_4  int v57; // ST58_4  jobject (*v58)(JNIEnv *, jclass, jmethodID, ...); // ST54_4  int v59; // r0  int v60; // r5  int v61; // r0  int OutputStreamObject_toByteArray; // [sp+Ch] [bp-60h]  int OutputStreamObject_close; // [sp+10h] [bp-5Ch]  int need_encode_bit_num; // [sp+14h] [bp-58h]  struct _jmethodID *doFinal_function; // [sp+18h] [bp-54h]  int src_data_bytes_array; // [sp+1Ch] [bp-50h]  int OutputStreamObject; // [sp+20h] [bp-4Ch]  int v68; // [sp+24h] [bp-48h]  int v69; // [sp+28h] [bp-44h]  int b64_2; // [sp+2Ch] [bp-40h]  void *v71; // [sp+30h] [bp-3Ch]  int default_1024_bit_rsa_max_size; // [sp+34h] [bp-38h]  int OutputStreamObject_wriTe; // [sp+38h] [bp-34h]  int src_data; // [sp+40h] [bp-2Ch]  int src_data_bytes_length_1; // [sp+40h] [bp-2Ch]  int v76; // [sp+48h] [bp-24h]  jobject v77; // [sp+48h] [bp-24h]  void *v78; // [sp+54h] [bp-18h]  int v79; // [sp+58h] [bp-14h]  _JNIEnv *env_2; // [sp+5Ch] [bp-10h]  v76 = a4;  src_data = a3;  env_3 = env;  v79 = (env->field_0->NewStringUTF)();  KeyFactory_class = (env_3->field_0->FindClass)(env_3, "java/security/KeyFactory");  v6 = KeyFactory_class;  v7 = (env_3->field_0->GetStaticMethodID)(         env_3,         KeyFactory_class,         "getInstance",         "(Ljava/lang/String;)Ljava/security/KeyFactory;");  v78 = (env_3->field_0->CallStaticObjectMethod)(env_3, v6, v7, v79);// 调用getInstance获得KeyFactory实例  v8 = (env_3->field_0->ExceptionCheck)(env_3);  v9 = env_3->field_0;  if ( v8 )  {    (v9->ExceptionClear)(env_3);    result = 0;  }  else  {    v11 = (v9->FindClass)(env_3, "android/util/Base64");// 拿到Base64类    v12 = v11;    env_2 = env_3;    v13 = (env_3->field_0->GetStaticMethodID)(env_3, v11, "decode", "(Ljava/lang/String;I)[B");    b64_2 = v12;    v14 = (env_2->field_0->CallStaticObjectMethod)(env_2, v12, v13, v76, 0);    v15 = (env_2->field_0->FindClass)(env_2, "java/security/spec/X509EncodedKeySpec");    v16 = v15;    v17 = (env_2->field_0->GetMethodID)(env_2, v15, "", "([B)V");    v69 = v14;    v18 = (env_2->field_0->NewObject)(env_2, v16, v17, v14);    (env_2->field_0->DeleteLocalRef)(env_2, v16);    v19 = (env_2->field_0->GetObjectClass)(env_2, v78);    v20 = v19;    v21 = (env_2->field_0->GetMethodID)(            env_2,            v19,            "generatePublic",            "(Ljava/security/spec/KeySpec;)Ljava/security/PublicKey;");    v77 = env_2->field_0->CallObjectMethod(&env_2->field_0, v78, v21, v18);// 获取PublicKey RSA的公钥 下面开始进行RSA加密    (env_2->field_0->DeleteLocalRef)(env_2, v20);    (env_3->field_0->DeleteLocalRef)(env_3, v78);    (env_3->field_0->DeleteLocalRef)(env_3, v18);    v22 = (env_3->field_0->ExceptionCheck)(env_3);    v23 = env_3->field_0;    if ( v22 )      goto LABEL_19;    v24 = (v23->GetObjectClass)(env_3, v77);    v25 = v24;    v26 = (env_3->field_0->GetMethodID)(env_3, v24, "getModulus", "()Ljava/math/BigInteger;");    v27 = (env_3->field_0->CallObjectMethod)(env_3, v77, v26);// 调用 getModulus    (env_3->field_0->DeleteLocalRef)(env_3, v25);    v28 = (env_3->field_0->GetObjectClass)(env_3, v27);    v29 = v28;    v30 = (env_3->field_0->GetMethodID)(env_3, v28, "bitLength", "()I");    v31 = (env_3->field_0->CallIntMethod)(env_3, v27, v30);// 调用 bitLength    (env_2->field_0->DeleteLocalRef)(env_2, v27);    (env_2->field_0->DeleteLocalRef)(env_2, v29);    need_encode_bit_num = v31 / 8;              // 计算要加密的位数    default_1024_bit_rsa_max_size = v31 / 8 - 11;// /8 - 11 是RSA1024 能加密的最大的位数    v32 = (env_2->field_0->FindClass)(env_2, "javax/crypto/Cipher");    v33 = (env_2->field_0->GetStaticMethodID)(env_2, v32, "getInstance", "(Ljava/lang/String;)Ljavax/crypto/Cipher;");    v34 = env_2->field_0->CallStaticObjectMethod;    v35 = (env_2->field_0->NewStringUTF)(env_2, "RSA/ECB/PKCS1Padding");    v68 = v32;    v36 = (v34)(env_2, v32, v33, v35);    env_3 = env_2;    v37 = v36;    v38 = (env_2->field_0->ExceptionCheck)(env_2);    v23 = env_2->field_0;    if ( v38 )      goto LABEL_19;    v71 = v37;    env_3 = env_2;    v39 = (v23->GetMethodID)(env_2, v32, "init", "(ILjava/security/Key;)V");    v40 = (env_2->field_0->ExceptionCheck)(env_2);    v23 = env_2->field_0;    if ( v40 )      goto LABEL_19;    (v23->CallVoidMethod)(env_2, v37, v39, 1, v77);    env_3 = env_2;    (env_2->field_0->DeleteLocalRef)(env_2, v77);    v41 = (env_2->field_0->ExceptionCheck)(env_2);    v23 = env_2->field_0;    if ( v41 )      goto LABEL_19;    v42 = (v23->FindClass)(env_2, "java/io/ByteArrayOutputStream");    v43 = v42;    v44 = (env_2->field_0->GetMethodID)(env_2, v42, "", "()V");    OutputStreamObject = (env_2->field_0->NewObject)(env_2, v43, v44);    OutputStreamObject_wriTe = (env_2->field_0->GetMethodID)(env_2, v43, "write", "([B)V");    OutputStreamObject_toByteArray = (env_2->field_0->GetMethodID)(env_2, v43, "toByteArray", "()[B");    OutputStreamObject_close = (env_2->field_0->GetMethodID)(env_2, v43, "close", "()V");    (env_2->field_0->DeleteLocalRef)(env_2, v43);    v45 = (env_2->field_0->GetObjectClass)(env_2, src_data);    getBytes_function = (env_2->field_0->GetMethodID)(env_2, v45, "getBytes", "(Ljava/lang/String;)[B");    v47 = env_2->field_0->CallObjectMethod;    v48 = (env_2->field_0->NewStringUTF)(env_2, "UTF-8");    src_data_bytes = (v47)(env_2, src_data, getBytes_function, v48);    (env_2->field_0->DeleteLocalRef)(env_2, v45);    src_data_bytes_array = src_data_bytes;    src_data_bytes_length = (env_2->field_0->GetArrayLength)(env_2, src_data_bytes);    env_1 = env_2;    doFinal_function = (env_2->field_0->GetMethodID)(env_2, v68, "doFinal", "([BII)[B");    (env_2->field_0->DeleteLocalRef)(env_2, v68);    src_data_bytes_length_1 = src_data_bytes_length;    if ( src_data_bytes_length >= 1 )           // 开始循环遍历字节,将字节写入到OutPutStream中去    {      v52 = default_1024_bit_rsa_max_size;      if ( src_data_bytes_length <= default_1024_bit_rsa_max_size )// 如果长度小于默认的 则设置加密的长度为需要加密的长度 否则需要加密的长度就是最大的1024        v52 = src_data_bytes_length;            // 第一次不走这里      v53 = 0;      do      {        v54 = src_data_bytes_length;        if ( v52 <= src_data_bytes_length )     // 第一次的时候 v52是默认的最大长度 1024位的长度,此时的if成立,因为src_data_bytes_length超出了1024位的长度          v54 = v52;                            // 所以此时加密的长度就先加密最大的1024位 后面多出来的先不加密        v55 = v54;                              // 这里的v55 就是 1024位的长度        v56 = env_1->field_0->CallObjectMethod(&env_1->field_0, v71, doFinal_function, src_data_bytes_array);        (env_2->field_0->CallVoidMethod)(env_2, OutputStreamObject, OutputStreamObject_wriTe);        env_1 = env_2;        (env_2->field_0->DeleteLocalRef)(env_2, v56);// 释放资源        src_data_bytes_length += 11 - need_encode_bit_num;// 要被加密的长度 减去第一次加密过的长度1024位        v53 += default_1024_bit_rsa_max_size;   // v53是一个计数器,记录着已经加密过的字节的长度        v52 = v55;      }      while ( src_data_bytes_length_1 > v53 );  // 需要加密的长度是大于的v53(默认的长度),所以还是第二次循环还是继续执行    }    env_3 = env_2;    v57 = (env_1->field_0->GetStaticMethodID)(env_2, b64_2, "encodeToString", "([BI)Ljava/lang/String;");// 最后调用静态方法encodeToString进行加密RSA加密过的数据    v58 = env_2->field_0->CallStaticObjectMethod;    v59 = (env_2->field_0->CallObjectMethod)(env_2, OutputStreamObject, OutputStreamObject_toByteArray);    v60 = (v58)(env_2, b64_2, v57, v59, 0);    (env_3->field_0->CallVoidMethod)(env_3, OutputStreamObject, OutputStreamObject_close);    v61 = (env_3->field_0->ExceptionCheck)(env_3);    v23 = env_2->field_0;    if ( v61 )    {LABEL_19:      (v23->ExceptionClear)(env_3);      result = 0;    }    else    {      (v23->DeleteLocalRef)(env_2, b64_2);      (env_2->field_0->DeleteLocalRef)(env_2, v69);      (env_2->field_0->DeleteLocalRef)(env_2, v71);      (env_2->field_0->DeleteLocalRef)(env_2, src_data_bytes_array);      (env_2->field_0->DeleteLocalRef)(env_2, OutputStreamObject);      (env_2->field_0->DeleteLocalRef)(env_2, v79);      result = v60;    }  }  return result;}

所以解决方法,模拟Base64编码生成的RSA的密文的结果:

1fb4fa71-6f15-eb11-8da9-e4434bdf6706.png

RSA弄完了可以继续看signature是如何生成的,来到如下的函数地方:

26b4fa71-6f15-eb11-8da9-e4434bdf6706.png

int __fastcall Java_com_ximalaya_ting_android_host_util_common_EncryptUtil_getPlaySignatureNative(_JNIEnv *env, JNINativeInterface *a2, int a3, int data_key, int data_value, int data_size){  _BYTE *v6; // r0  _BYTE *v7; // r4  int v8; // ST28_4  _BYTE *v9; // r4  size_t v10; // r5  size_t v11; // r0  void *a5; // r5  _JNIEnv *env_2; // r6  int v14; // r4  int data_key_1; // [sp+14h] [bp-20h]  JNINativeInterface *v17; // [sp+18h] [bp-1Ch]  _JNIEnv *env_1; // [sp+1Ch] [bp-18h]  int v19; // [sp+20h] [bp-14h]  data_key_1 = data_key;  v17 = a2;  env_1 = env;  v19 = a3;  if ( sub_1D00(env, a3) < 0 )                  // 判断...    j_exit(0);  v6 = j_malloc(29);  v7 = v6;  v8 = v6;  j___aeabi_memcpy(v6, "23627d1451047b8d257a96af5db32", 28);  v7[28] = 0;  v9 = j_malloc(37);  j___aeabi_memcpy(v9, "59538f081d651df75b4aa1695085472081594", 36);  v9[36] = 0;  v10 = j_strlen(v8);  v11 = v10 + j_strlen(v9);  a5 = j_malloc(v11 + 1);                       // 接收上面来个字符串的拼接  j___aeabi_memclr(a5);                         // zeroMemory  j_strcat(a5, v8);                             // 拼接 23627d1451047b8d257a96af5db32  j_strcat(a5, v9);                             // 拼接 59538f081d651df75b4aa1695085472081594  j_free(v8);  j_free(v9);  env_2 = env_1;  v14 = i_find_encode_function(env_1, data_key_1, data_value, data_size, a5);// 加密函数  j_free(a5);  (env_2->field_0->DeleteLocalRef)(env_2, v17);  (env_2->field_0->DeleteLocalRef)(env_2, v19);  return v14;}

重点函数i_find_encode_function(这里改过名了),默认是一个sub_xxxx的函数名,跟进去,注释都写好了,重点是sub_B814这个函数,它是最终加密MD5的结果,在这里进行断点然年后查看R0寄存器的内存值就可以知道最终的加密结果了,MD5类是C写的,有什么办法识别MD5的特征呢?初始化MD5加密的时候 肯定会有4个常数的出现,比如如下sub_B814跟进会发现:

2fb4fa71-6f15-eb11-8da9-e4434bdf6706.png

38b4fa71-6f15-eb11-8da9-e4434bdf6706.png

int __fastcall i_find_encode_function(_JNIEnv *env, int data_key, int data_value, int data_size, int a5){  char *v5; // r5  int data_size_1; // r4  unsigned int v7; // r3  _JNIEnv *env_2; // r4  int data_key_a; // r0  _BYTE *v10; // r0  int v11; // r0  _BYTE *v12; // r0  signed int v13; // r5  int v14; // r4  char *v15; // r0  int *v16; // r4  int v17; // r0  size_t v18; // r0  int v19; // r4  bool v20; // nf  unsigned __int8 v21; // vf  int v22; // r5  char *v23; // r1  int *v24; // r4  int v25; // r5  size_t v26; // r0  size_t v27; // r0  char *v28; // r1  int v29; // r0  int *v30; // r4  int v31; // r1  int *v32; // r2  int v33; // r0  int *v34; // r4  _DWORD *v35; // r5  _JNIEnv *v36; // r4  char *v37; // r5  int v38; // r4  int v39; // r1  int result; // r0  _DWORD *v41; // [sp+4h] [bp-60h]  int v42; // [sp+8h] [bp-5Ch]  int *v43; // [sp+Ch] [bp-58h]  _DWORD *data_value_array; // [sp+10h] [bp-54h]  int data_key_1; // [sp+14h] [bp-50h]  int v46; // [sp+18h] [bp-4Ch]  int *v47; // [sp+1Ch] [bp-48h]  int *v48; // [sp+20h] [bp-44h]  char *data_key_a_1; // [sp+24h] [bp-40h]  int *v50; // [sp+28h] [bp-3Ch]  int *data_key_array; // [sp+2Ch] [bp-38h]  int v52; // [sp+30h] [bp-34h]  char *v53; // [sp+34h] [bp-30h]  _JNIEnv *env_1; // [sp+38h] [bp-2Ch]  int v55; // [sp+40h] [bp-24h]  int v56; // [sp+44h] [bp-20h]  int v57; // [sp+48h] [bp-1Ch]  int v58; // [sp+4Ch] [bp-18h]  int v59; // [sp+50h] [bp-14h]  v46 = data_value;  env_1 = env;  v5 = 0;  v58 = 0;  v43 = &v58;  v59 = 0;  v57 = 0;  v56 = 0;  v50 = &v55;  v55 = 0;  data_size_1 = data_size;  v7 = (4 * data_size + 7) & 0xFFFFFFF8;  v41 = &v41;  data_value_array = (&v41 - v7);  v52 = -1;  v42 = a5;                                     // 23627d1451047b8d257a96af5db3259538f081d651df75b4aa1695085472081594  v53 = data_size_1;  data_key_1 = data_key;  data_key_array = (&v41 - v7);  if ( data_size_1 <= 0 )  {    v13 = data_size_1;    j_qsort(&v41 - v7, data_size_1, 4u, sub_4D40);// 排序    v14 = 1;  }  else  {    v47 = &v59;    env_2 = env_1;    do    {      v48 = (&stru_2A8 + 12);      data_key_a = (env_2->field_0->GetObjectArrayElement)(env_2, data_key_1, v5);// 遍历获取data_key中的每个元素      data_key_a_1 = data_key_a;      v10 = sub_4F48(env_2, data_key_a);        // getBytes获取每个元素data_key的字节      data_key_array[v5] = v10;                 // 存储data_key_a      v11 = (*(&env_1->field_0->reserved0 + v48))(// // 遍历获取data_value中的每个元素              env_1,              v46,              v5);      v48 = v11;      v12 = sub_4F48(env_1, v11);               // getBytes获取每个元素data_value的字节      data_value_array[v5] = v12;      v50[4] = v12;                             // 存储      env_2 = env_1;      sub_AAF0(&v55, data_key_array[v5], v47, 4);      (env_2->field_0->DeleteLocalRef)(env_2, data_key_a_1);      (env_2->field_0->DeleteLocalRef)(env_2, v48);      ++v5;    }    while ( v53 != v5 );    v13 = v53;    j_qsort(data_key_array, v53, 4u, sub_4D40);    v14 = 1;    if ( v13 >= 1 )    {      v15 = 0;      v16 = data_key_array;      do      {        data_key_a_1 = v15;        v17 = sub_AA68(&v55, *v16);        v47 = v17;        *v43 = v17;        v48 = j_strlen(*v16);        v18 = j_strlen(*v47);        v15 = v48 + data_key_a_1 + v18 + 2;        v13 += v52;        ++v16;      }      while ( v13 );      v14 = (v15 + 1);      v13 = v53;    }  }  v19 = v14 + j_strlen(v42);  v43 = j_malloc(v19);  j___aeabi_memclr(v43);  v21 = __OFSUB__(v13, 1);  v20 = v13 - 1 < 0;  v22 = v50;  if ( !(v20 ^ v21) )  {    v23 = v53;    v24 = data_key_array;    do    {      data_key_a_1 = v23;      v48 = sub_AA68(&v55, *v24);      *(v22 + 12) = v48;      v25 = v43;      j_strcat(v43, *v24);      v26 = j_strlen(v25);      *(v25 + v26) = 61;      v47 = 0;      *(v25 + v26 + 1) = 0;      j_strcat(v25, *v48);      v27 = j_strlen(v25);      *(v25 + v27) = 38;      v28 = data_key_a_1;      v29 = v25 + v27;      v22 = v50;      *(v29 + 1) = v47;      v23 = &v28[v52];      ++v24;    }    while ( v23 );  }  v30 = v43;  j_strcat(v43, v42);  LOBYTE(v31) = *v30;  if ( *v30 )  {    v32 = v30;    do    {      *v32 = *(tolower_tab_ + 2 * v31 + 2);      v31 = *(v32 + 1);      v32 = (v32 + 1);    }    while ( v31 );  }  data_key_a_1 = sub_B814(v30); // C实现的MD5算法 加密完MD5然后作为sign值返回  j_free(v30);  sub_AA30(&v55);  v33 = v53;  v34 = data_key_array;  v35 = data_value_array;  if ( v53 >= 1 )  {    do    {      v53 = v33;      j_free(*v34);      j_free(*v35);      v33 = &v53[v52];      ++v35;      ++v34;    }    while ( &v53[v52] );  }  v36 = env_1;  (env_1->field_0->DeleteLocalRef)(env_1, data_key_1);  (v36->field_0->DeleteLocalRef)(v36, v46);  v37 = data_key_a_1;  v38 = (v36->field_0->NewStringUTF)(v36, data_key_a_1);// 转换为java字符串  j_free(v37);  v39 = v50[5];  result = _stack_chk_guard - v39;  if ( _stack_chk_guard == v39 )    result = v38;  return result;}

所以这里直接动态调试sub_B814这个函数

39b4fa71-6f15-eb11-8da9-e4434bdf6706.png

第一个函数执行过后返回值将会存储到R0中,所以内存窗口中就同步R0寄存器,R0寄存器中存储的是MD5,其值如下:

7219323f66f61ea591e0e733f8f835f3

3cb4fa71-6f15-eb11-8da9-e4434bdf6706.png

被加密的值,如下两部分组成

构建的HTTP参数 + 23627d1451047b8d257a96af5db359538f081d651df75b4aa169508547208159
account=19817353426&channel=and-f5&device=android&deviceid=00000000-6415-8c0c-f875-98ae0037d7ef&impl=com.ximalaya.ting.android&nonce=5402247414770639301&password=e9bc0e13a8a16cbb07b175d92a113126&version=6.3.90&23627d1451047b8d257a96af5db359538f081d651df75b4aa169508547208159

43b4fa71-6f15-eb11-8da9-e4434bdf6706.png

如下验证:

4ab4fa71-6f15-eb11-8da9-e4434bdf6706.png

所以最终的过程就是如下:

因为nonce是随机的,所以这里可以不必追究,直接让它为一个定值就好了,最终变动的就是account 和 password 这些也都是自己可控的!

Base64_encode(RSA_ENCODE({"account":"登陆账号","nonce":"4786336759083248576","password":"MD5(登陆密码)","signature":"MD5(account=登陆账号&channel=and-f5&device=android&deviceid=00000000-6415-8c0c-f875-98ae0037d7ef&impl=com.ximalaya.ting.android&nonce=4786336759083248576&password=MD5(登陆密码)&version=6.3.90&23627d1451047b8d257a96af5db359538f081d651df75b4aa169508547208159)"}))

53b4fa71-6f15-eb11-8da9-e4434bdf6706.png

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值