兜兜零元 发表于 2025-2-14 16:32:32

52pj2025春节红包解题-安卓中级

https://img2024.cnblogs.com/blog/2817142/202502/2817142-20250214151655821-1912692806.png
先找到判断方法,显然是一个native
https://img2024.cnblogs.com/blog/2817142/202502/2817142-20250214151655833-960358666.png
https://img2024.cnblogs.com/blog/2817142/202502/2817142-20250214151655790-486309514.png
ida加载so,导出表中没有这个函数,所以是动态注册的,找到jni_onload
https://img2024.cnblogs.com/blog/2817142/202502/2817142-20250214151655795-244462900.png
找到函数地址
https://img2024.cnblogs.com/blog/2817142/202502/2817142-20250214151655789-2102848477.png
修改3个参数的范例,便于分析
https://img2024.cnblogs.com/blog/2817142/202502/2817142-20250214151655797-16400752.png
总得来看,最终要执行的不是a就是ao了
bool __fastcall sub_BE440(JNIEnv *env, jobject object, jstring inputKey)
{
int v5; // r4
const char *key; // r0
const char *v7; // r9
int v8; // r4
int v9; // r6
int v10; // r1
unsigned int v11; // r6
char *v12; // r4
_BOOL4 v13; // r8
int v14; // r0
void (__fastcall *v15)(_BYTE *, const char *, int, void *); // r8
void *v16; // r5
int v17; // r4
const std::nothrow_t *v18; // r1
unsigned __int64 v20; //
_BYTE v21; // BYREF
_QWORD v22; // BYREF

v5 = 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 = *(_QWORD *)&off_12FCE8;            // 下面的v15是为了获得一个函数,不是a就是ao
    v22 = *(_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就是输入的key
    v17 = memcmp(v16, &unk_3A0FC, 0x13u);       // 比较结果
    operator delete[](v16, v18);
    (*env)->ReleaseStringUTFChars(env, (jstring)HIDWORD(v20), v7);
    return v17 == 0;
}
return v5;
}a和ao的差异很小,但总归是要执行此中的一个的,所以反调可以直接忽略掉,两个函数都看一下
https://img2024.cnblogs.com/blog/2817142/202502/2817142-20250214151656115-1204421302.png
https://img2024.cnblogs.com/blog/2817142/202502/2817142-20250214151656031-1075522495.png
改一下参数的范例,发现这两个函数唯一的差别就是ao没有去动态修改sub_BED58天生的值,因此解密函数应该是a
int __fastcall a(_BYTE *a1, char *key, int a3, void *a4)
{
__int64 v5; // d17
int i; // r6
int v9; // r5
char v10; // r0
_QWORD v12; // BYREF
int v13; //

v5 = *((_QWORD *)a1 + 1);
v12 = *(_QWORD *)a1;
v12 = 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 ^ *((_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; // d17
int i; // r4
_QWORD v10; // BYREF
int v11; //

v5 = *((_QWORD *)a1 + 1);
v10 = *(_QWORD *)a1;
v10 = v5;
if ( a3 )
{
    for ( i = 0; i != a3; ++i )
    {
      if ( (i & 0xF) == 0 )
      sub_BED58((unsigned __int8 *)v10);
      *((_BYTE *)a4 + i) = key ^ *((_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
https://img2024.cnblogs.com/blog/2817142/202502/2817142-20250214151655993-646543849.png
最后根据手机的现实情况选择对应架构的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));
    }
});
});https://img2024.cnblogs.com/blog/2817142/202502/2817142-20250214151656003-330616714.png
得到该函数两次执行的结果,第二次是要依据第一次的输入来做的,所以要先解一下前16位
0x2e,0x4b,0xee,0xc8,0xe0,0x95,0x88,0x47,0xb0,0x72,0x1b,0x68,0x40,0xd0,0x0a,0x84
target =
result =

for i in range(0,len(result)):
    print(chr(target^result),end='')
#flag{md5(uid+202再次输入密钥时输入flag{md5(uid+202(发现反调试时需要getenv返回非0,hook了一下,至于另一个函数,大概是由于我用frida的原因?它没有检测到),得到后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));
    }
});
});https://img2024.cnblogs.com/blog/2817142/202502/2817142-20250214151655993-1382268499.png
target =
result =

for i in range(0,len(result)):
    print(chr(target^result),end='')
#flag{md5(uid+2025)}得到flagflag{md5(uid+2025)}
https://img2024.cnblogs.com/blog/2817142/202502/2817142-20250214151656013-1867754946.png

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
页: [1]
查看完整版本: 52pj2025春节红包解题-安卓中级