先找到判断方法,显然是一个native
ida加载so,导出表中没有这个函数,所以是动态注册的,找到jni_onload
找到函数地址
修改3个参数的类型,便于分析
总得来看,最终要执行的不是a就是ao了
bool __fastcall sub_BE440(JNIEnv *env, jobject object, jstring inputKey)
{int v5; // r4const char *key; // r0const char *v7; // r9int v8; // r4int v9; // r6int v10; // r1unsigned int v11; // r6char *v12; // r4_BOOL4 v13; // r8int v14; // r0void (__fastcall *v15)(_BYTE *, const char *, int, void *); // r8void *v16; // r5int v17; // r4const std::nothrow_t *v18; // r1unsigned __int64 v20; // [sp+0h] [bp-58h]_BYTE v21[16]; // [sp+18h] [bp-40h] BYREF_QWORD v22[2]; // [sp+28h] [bp-30h] BYREFv5 = 0;key = (*env)->GetStringUTFChars(env, inputKey, 0);if ( key ){v7 = key;HIDWORD(v20) = inputKey;v8 = A();v9 = CNJAK();if ( !byte_134E49 ){afdm::decrypt_buffer((afdm *)byte_134D7E, &byte_4, 0xA8FC3415, v20);byte_134E49 = 1;}v10 = -1;if ( v8 )v10 = 1;v11 = v9 + v10;v12 = getenv(byte_134D7E); // 反调?v13 = v12 == 0 || v11 < 3;v14 = jgbjkb(); // 反调?if ( v11 <= 2 && v12 ){v13 = 1;dword_134D90 = -559038669;}v22[0] = *(_QWORD *)&off_12FCE8; // 下面的v15是为了获得一个函数,不是a就是aov22[1] = *(_QWORD *)&off_12FCF0;v15 = (void (__fastcall *)(_BYTE *, const char *, int, void *))nullsub_9(*(_DWORD *)((unsigned int)v22 | (4 * ((v14 | v13) ^ (unsigned int)sub_BE6CC & 1 ^ (((unsigned int)ao ^ (unsigned int)a) >> 24) & 1))));dword_134D90 = -559038669;memset(v21, 0, sizeof(v21));v16 = (void *)operator new[](0x13u);v15(v21, v7, 19, v16); // v15是一个函数,这边v7就是输入的keyv17 = memcmp(v16, &unk_3A0FC, 0x13u); // 比较结果operator delete[](v16, v18);(*env)->ReleaseStringUTFChars(env, (jstring)HIDWORD(v20), v7);return v17 == 0;}return v5;
}
a
和ao
的差异很小,但总归是要执行其中的一个的,所以反调可以直接忽略掉,两个函数都看一下
改一下参数的类型,发现这两个函数唯一的差别就是ao没有去动态修改sub_BED58生成的值,因此解密函数应该是a
int __fastcall a(_BYTE *a1, char *key, int a3, void *a4)
{__int64 v5; // d17int i; // r6int v9; // r5char v10; // r0_QWORD v12[2]; // [sp+0h] [bp-30h] BYREFint v13; // [sp+14h] [bp-1Ch]v5 = *((_QWORD *)a1 + 1);v12[0] = *(_QWORD *)a1;v12[1] = v5;if ( a3 ) // a3=19{for ( i = 0; i != a3; ++i ){v9 = i & 0xF;if ( (i & 0xF) == 0 )sub_BED58((unsigned __int8 *)v12); // 初始化v12的值,需要注意v12的长度是16,但一共有19次循环,第17次时这个函数又会被调用一次v10 = key[i] ^ *((_BYTE *)v12 + v9); // 异或*((_BYTE *)a4 + i) = v10;*((_BYTE *)v12 + v9) = v10;}}return v13;
}
int __fastcall ao(_BYTE *a1, char *key, int a3, void *a4)
{__int64 v5; // d17int i; // r4_QWORD v10[2]; // [sp+0h] [bp-30h] BYREFint v11; // [sp+14h] [bp-1Ch]v5 = *((_QWORD *)a1 + 1);v10[0] = *(_QWORD *)a1;v10[1] = v5;if ( a3 ){for ( i = 0; i != a3; ++i ){if ( (i & 0xF) == 0 )sub_BED58((unsigned __int8 *)v10);*((_BYTE *)a4 + i) = key[i] ^ *((_BYTE *)v10 + (i & 0xF));}}return v11;
}
最后比较结果,比较的值是0x48,0x27,0x8f,0xaf,0x9b,0xf8,0xec,0x72,0x98,0x07,0x72,0x0c,0x6b,0xe2,0x3a,0xb6,0x42,0x59,0xf7
最后根据手机的实际情况选择对应架构的so进行hook或者调试,分析时用的是armeabi-v7a
,我的手机是arm64,应该用arm64-v8a
(重打包apk,把其他架构的删掉也可以)
Java.perform(function(){var soAddr = Process.getModuleByName("libwuaipojie2025_game.so");var func_addr = soAddr.base.add(0xE9954);Interceptor.attach(func_addr, {onEnter: function(args){console.log("hook到函数");},onLeave: function(retval){console.log(retval.readByteArray(16));}});
});
得到该函数两次执行的结果,第二次是要依据第一次的输入来做的,所以要先解一下前16位
0x2e,0x4b,0xee,0xc8,0xe0,0x95,0x88,0x47,0xb0,0x72,0x1b,0x68,0x40,0xd0,0x0a,0x84
target = [0x48,0x27,0x8f,0xaf,0x9b,0xf8,0xec,0x72,0x98,0x07,0x72,0x0c,0x6b,0xe2,0x3a,0xb6,0x42,0x59,0xf7]
result = [0x2e,0x4b,0xee,0xc8,0xe0,0x95,0x88,0x47,0xb0,0x72,0x1b,0x68,0x40,0xd0,0x0a,0x84]for i in range(0,len(result)):print(chr(target[i]^result[i]),end='')
#flag{md5(uid+202
再次输入密钥时输入flag{md5(uid+202
(发现反调试时需要getenv返回非0,hook了一下),得到后3位0x77,0x70,0x8a
Java.perform(function(){var soAddr = Process.getModuleByName("libwuaipojie2025_game.so");var jgbjkb_addr = Module.findExportByName("libwuaipojie2025_game.so","_Z6jgbjkbv");Interceptor.attach(jgbjkb_addr, {onEnter: function(args){console.log("hook到jgbjkb");},onLeave: function(retval){console.log("jgbjkb返回值为"+retval);}}); var getenv_addr = Module.findExportByName("libc.so","getenv");Interceptor.attach(getenv_addr, {onEnter: function(args){console.log("hook到getenv");},onLeave: function(retval){console.log("修改getenv返回值为1");retval.replace(1);}}); var func_addr = soAddr.base.add(0xE9954);Interceptor.attach(func_addr, {onEnter: function(args){console.log("hook到函数");},onLeave: function(retval){console.log(retval.readByteArray(16));}});
});
target = [0x48,0x27,0x8f,0xaf,0x9b,0xf8,0xec,0x72,0x98,0x07,0x72,0x0c,0x6b,0xe2,0x3a,0xb6,0x42,0x59,0xf7]
result = [0x2e,0x4b,0xee,0xc8,0xe0,0x95,0x88,0x47,0xb0,0x72,0x1b,0x68,0x40,0xd0,0x0a,0x84,0x77,0x70,0x8a]for i in range(0,len(result)):print(chr(target[i]^result[i]),end='')
#flag{md5(uid+2025)}
得到flagflag{md5(uid+2025)}