好友
阅读权限10
听众
最后登录1970-1-1
|
本文仅作为个人技术学习与研究的记录,旨在分享逆向分析的思路和方法,不涉及也不鼓励任何形式的商业破解、盗版传播或非法用途。文中所有示例与操作均出于技术探讨目的,且涉及的关键信息已进行脱敏处理,不会影响或泄露实际软件的商业利益。
如有内容存在侵权或不当之处,请联系我,我将第一时间删除或修正。环境及版本:- 手机: Pixel 4xl android12
- frIDA-server 魔改16.6.6
- App版本:17.24.1
工具:jadx-gui 1.5.2 ida9.2 Pro Charles+Postern+JustTrustMe++抓包: 用的 Charles+Postern+JustTrustMe++ 抓
通过观察,logininfo被加密了分析Java层:将apk放到桌面,打开jadk,把apk拖进去,搜索"logininfo"
只有这一个,点进去观察能看到doEncrypt传入了 jSONObject.toString() + com.wuba.loginsdk.network.l.a.a()是账号密码组成的json格式的数据转成字符串和另外一个字符串拼接而成.跟进a方法继续分析public static java.lang.String a() {
java.lang.String r2;
android.net.NetworkInfo r2;
//省略不是那么重要的代码
java.lang.String r3 = !android.text.TextUtils.isEmpty(com.wuba.loginsdk.internal.l.a.H().L()) ? com.wuba.loginsdk.internal.l.a.H().L() : "";
if (!com.wuba.loginsdk.b.b.v(com.wuba.loginsdk.b.d.L)) {
r3 = "";
}
java.lang.String r1 = com.wuba.loginsdk.network.l.a.c.replace("%nettype", r2).replace("%deviceid", com.wuba.loginsdk.data.b.K()).replace("%thirdinfo", com.wuba.loginsdk.thirdapi.ThirdUtils.generateInjectThirdInfo()).replace("%preMobileMaskMd5", android.text.TextUtils.isEmpty(r3) ? "" : com.wuba.loginsdk.utils.DeviceUtils.encryptionMD5(r3.getBytes())).replace("%preMobileMaskFormat", com.wuba.loginsdk.utils.o.e(r3)).replace("currentTimeMillis", java.lang.String.valueOf(java.lang.System.currentTimeMillis())).replace("randomUUID", com.wuba.loginsdk.utils.DeviceUtils.getRandomUUID());
com.wuba.loginsdk.log.LOGGER.d("NetWorkFactory", r1);
return r1;
}
​这里能看到返回的r1是由com.wuba.loginsdk.network.l.a.c这个字符串通过replace方法处理过后得到,定位com.wuba.loginsdk.network.l.a.c的位置
继续跟进b方法public static String b(Context context) throws Throwable {
String imei = DeviceUtils.getImei(context);
String model = DeviceUtils.getModel();
String osVersion = DeviceUtils.getOsVersion();
String model2 = DeviceUtils.getModel();
String deivceLanguage = DeviceUtils.getDeivceLanguage();
String deviceCountry = DeviceUtils.getDeviceCountry();
String brand = DeviceUtils.getBrand();
String displayHxW = DeviceUtils.getDisplayHxW(context);
String applicationName = DeviceUtils.getApplicationName(context);
String versionName = DeviceUtils.getVersionName(context);
String timeZone = DeviceUtils.getTimeZone();
String simOperatorType = DeviceUtils.getSimOperatorType(context);
String cPUSerial = DeviceUtils.getCPUSerial();
String deviceMemory = DeviceUtils.getDeviceMemory(context);
String androidID = DeviceUtils.getAndroidID(context);
String applicationId = DeviceUtils.getApplicationId(context);
String wifiSSID = DeviceUtils.getWifiSSID(context);
String wifiIPAddress = DeviceUtils.getWifiIPAddress(context);
String emulatorMsg = DeviceUtils.getEmulatorMsg(context);
boolean zIsDeviceRoot = DeviceUtils.isDeviceRoot();
int i = !TextUtils.isEmpty(emulatorMsg) ? 1 : 0;
StringBuilder sb = new StringBuilder();
sb.append("\u0001");
sb.append(imei);
sb.append(f34988b);
sb.append("android");
sb.append(f34988b);
sb.append(model);
sb.append(f34988b);
sb.append(osVersion);
sb.append(f34988b);
sb.append("%nettype");
sb.append(f34988b);
sb.append(model2);
sb.append(f34988b);
sb.append(deivceLanguage);
sb.append(f34988b);
sb.append(deviceCountry);
sb.append(f34988b);
sb.append(brand);
sb.append(f34988b);
sb.append(displayHxW);
sb.append(f34988b);
sb.append(applicationName);
sb.append(f34988b);
sb.append(versionName);
sb.append(f34988b);
sb.append(timeZone);
sb.append(f34988b);
sb.append(simOperatorType);
sb.append(f34988b);
sb.append("");
sb.append(f34988b);
sb.append(cPUSerial);
sb.append(f34988b);
sb.append(deviceMemory);
sb.append(f34988b);
sb.append(f34988b);
sb.append(f34988b);
sb.append(wifiSSID);
sb.append(f34988b);
sb.append(wifiIPAddress);
sb.append(f34988b);
sb.append("");
sb.append(f34988b);
sb.append("");
sb.append(f34988b);
sb.append(zIsDeviceRoot ? 1 : 0);
sb.append(f34988b);
sb.append("%deviceid");
sb.append(f34988b);
sb.append(i);
sb.append(f34988b);
sb.append(emulatorMsg);
sb.append(f34988b);
sb.append("%thirdinfo");
sb.append(f34988b);
sb.append(androidID);
sb.append(f34988b);
sb.append(applicationId);
sb.append(f34988b);
sb.append("%preMobileMaskMd5");
sb.append(f34988b);
sb.append("%preMobileMaskFormat");
sb.append(f34988b);
sb.append("currentTimeMillis");
sb.append(f34988b);
sb.append("randomUUID");
LOGGER.d("NetWorkFactory", sb.toString());
return sb.toString();
}这个方法就是把一系列系统参数拼接到一起,f34988b是#需要注意的一点是这个字符串最前面有一个控制字符\u0001(我最开始没看这个方法,直接hook的doEncrypt拿的明文,这个控制字符看不到,hook这hook那,搞了好久才发现),返回doEncrpt继续分析
很清楚地看到了doEncrypt方法内部逻辑,把明文转成byte数组传入native方法encrypt
分析so层:解压apk,拿到lib\armeabi-v7a\下面的libcom_wuba_uc_rsa.so,打开ida分析是个动态注册的,直接进入Jni_Onload
点击off_C004( JNINativeMethod 数组的地址)
一眼就看到了加密方法,继续跟进
进入j_encrypt_defaultint __fastcall encrypt_default(int byte_len, int byte, int out)
{
int result; // r0
int v6; // r0
_BYTE *v7; // r4
int v8; // r8
int i; // r6
int v10; // r0
int v11; // [sp+Ch] [bp-434h]
_DWORD v13[3]; // [sp+14h] [bp-42Ch] BYREF
_BYTE v14[128]; // [sp+20h] [bp-420h] BYREF
_BYTE v15[128]; // [sp+A0h] [bp-3A0h] BYREF
_BYTE v16[768]; // [sp+120h] [bp-320h] BYREF
​
result = -1;
if ( byte_len <= 585 && byte )
{
v6 = sub_4C4C(byte_len, 117);
v7 = v16;
if ( byte_len - 117 * v6 > 0 )
++v6;
v16[128 * v6] = 1;
v11 = v6 << 7;
j_apply(v13, 32, v15);
j_hexsToBigInt(
"bc587820f3ad01608b64af88af1ee883bed3e4954457da8f7b5af76403c40329bc4f5e66e46f83eacfba28b2a4d3a61553ee237db6c22f49f5"
"377e4d6ac3b63b3cd36a9746f9dc6bdd7c2aae26020a6b6fc1ac6399b4da58f8ed3944686cfaca328d1dd581ef86d1dbe87c12af0130e8ce72"
"8d00814e968bee602752d25ac691",
v13);
v8 = 0;
for ( i = byte_len; ; i -= 117 )
{
if ( v8 >= byte_len )
{
j_base64_encode_uc(v16, v11 | 1, out); //base64
return 0;
}
v10 = i < 118 ? i : 117;
if ( j_encrypt_one_group(v10, byte + v8, 65537, v13[0], v13[1], v13[2], v14) ) //加密
break;
qmemcpy(v7, v14, 0x80u);
v7 += 128;
v8 += 117;
}
return -1;
}
return result;
}大概看一下,像rsa加密,分析一下逻辑整个代码是一个 分块加密,每一块是117个字节,加密逻辑在j_encrypt_one_group这个函数里面,每次加密117个字节,再拼接在一起,最后在末尾拼接了一个字节0x01,在进行base64编码。分析j_encrypt_one_group:int __fastcall encrypt_one_group(char *byte_len, int byte, int a3, int a4, int a5, int a6, int out)
{
_BYTE v8[132]; // [sp+10h] [bp-98h] BYREF
​
if ( j_encrypt(byte_len, byte, a3, a4, a5, a6, out) )
return -1;
memset(v8, 0, 0x80u);
j_ch_init(v8, "1V:8(Nqa,U&Ll.RW2 @slN>Vp$qT#T=phf MA<0GO/6V+rW-A!");
j_ch_crypt(v8, out, 128);
return 0;
}这里先进行了一个RSA加密,再把加密结果进行了RC4加密进入j_encrypt
填充
用时间戳当作随机数种子,生成随机数填充回到rc4,先看看rc4加密逻辑
通过j_ch_init初始化,返回一个128长度的数组,再传入j_ch_crypt进行一些列运算,得到真正的S盒,然后进行运算拿到结果,再进行rc4加密(非标准rc4,标准的S盒长度是256)
sq是hook j_ch_init的Onleave的第一个参数
代码是ai写的,自己简单改改就好了,就不贴代码了新手小白第一次尝试,难免有疏漏或不足,欢迎各位批评指正。总结加密流程: 分块加密,每一块最长117,这一块先rsa再rc4,再把每一块加密结果拼接在一起,再在字节数组最后面拼接上0x01,再进行base64加密(+替换成-,/替换成_.=替换成B) |
免费评分
-
查看全部评分
|