Loading... # 京麒CTF挑战赛第三届 复现 ## drillbeam java层没东西,直接看native层找到了控制流平坦化的几处函数,用IDA d810就可以恢复 ~~~c __int64 __fastcall sub_3550(__int64 result, __int64 a2) { __int64 v2; // x5 char v3; // w3 unsigned int v4; // w4 int i; // [xsp+8h] [xbp-18h] if ( dword_6518 != 1 ) { for ( i = 0; ; ++i ) { v2 = i; v3 = *(_BYTE *)(a2 + 20 + i); v4 = i % 20u; if ( i == 6 ) break; *(_BYTE *)(result + v2) = v3 ^ *(_BYTE *)(a2 + v4); } *(_BYTE *)(result + i) = v3 ^ *(_BYTE *)(a2 + v4); dword_6518 = 1; } return result; } void *__fastcall sub_184C(const void *a1, size_t n, __int128 *a3, _QWORD *a4) { // [COLLAPSED LOCAL DECLARATIONS. PRESS NUMPAD "+" TO EXPAND] v31 = *(_QWORD *)(_ReadStatusReg(ARM64_SYSREG(3, 3, 13, 0, 2)) + 40); v30 = *a3; v20 = 0LL; if ( !(_BYTE)v30 ) goto LABEL_3; if ( !BYTE1(v30) ) { v20 = 1LL; goto LABEL_3; } if ( !BYTE2(v30) ) { v20 = 2LL; goto LABEL_3; } if ( !BYTE3(v30) ) { v20 = 3LL; goto LABEL_3; } if ( !BYTE4(v30) ) { v20 = 4LL; goto LABEL_3; } if ( !BYTE5(v30) ) { v20 = 5LL; goto LABEL_3; } if ( !BYTE6(v30) ) { v20 = 6LL; goto LABEL_3; } if ( !BYTE7(v30) ) { v20 = 7LL; goto LABEL_3; } if ( !BYTE8(v30) ) { v20 = 8LL; goto LABEL_3; } if ( !BYTE9(v30) ) { v20 = 9LL; goto LABEL_3; } if ( !BYTE10(v30) ) { v20 = 10LL; goto LABEL_3; } if ( !BYTE11(v30) ) { v20 = 11LL; goto LABEL_3; } if ( !BYTE12(v30) ) { v20 = 12LL; goto LABEL_3; } if ( !BYTE13(v30) ) { v20 = 13LL; goto LABEL_3; } if ( !BYTE14(v30) ) { v20 = 14LL; LABEL_3: memset((char *)&v30 + v20 + 1, 0, v20 ^ 0xF); } v12 = 0LL; if ( n ) { v7 = n >> 2; if ( (n & 3) != 0 ) ++v7; v21 = v7; nmemb = v7 + 1; v23 = (int *)calloc(v7 + 1, 4uLL); v12 = 0LL; if ( v23 ) { v23[v21] = n; memcpy(v23, a1, n); v24 = calloc(4uLL, 4uLL); if ( v24 ) { *v24 = v30; v25 = &v23[(unsigned int)(nmemb - 1)]; if ( (_DWORD)nmemb != 1 ) { v26 = qword_64F0; v18 = (unsigned int)*v25; for ( i = 0x34 / (unsigned int)nmemb + 5; ; --i ) { v27 = HIDWORD(v18) + v26; v28 = ((unsigned int)(HIDWORD(v18) + v26) >> 2) & 3; v16 = 0LL; v15 = v18; v17 = *v23; while ( 1 ) { v8 = v23[v16 + 1]; v29 = ((((4 * v8) ^ (v15 >> 5)) + ((v8 >> 3) ^ (16 * v15))) ^ ((*((_DWORD *)v24 + (v16 & 3 ^ v28)) ^ v15) + (v8 ^ v27))) + v17; v23[v16] = v29; if ( v16 + 1 == (_DWORD)nmemb - 1 ) break; v17 = v8; ++v16; v15 = v29; } v5 = ((((4 * *v23) ^ (v29 >> 5)) + (((unsigned int)*v23 >> 3) ^ (16 * v29))) ^ ((*((_DWORD *)v24 + (v28 ^ ((_BYTE)nmemb - 1) & 3)) ^ v29) + (*v23 ^ v27))) + *v25; *v25 = v5; if ( !i ) break; LODWORD(v18) = v5; HIDWORD(v18) += v26; } } v6 = malloc((4 * nmemb) | 1); memcpy(v6, v23, 4 * nmemb); *((_BYTE *)v6 + 4 * nmemb) = 0; *a4 = 4 * nmemb; free(v23); v13 = v6; v14 = (int *)v24; } else { v13 = 0LL; v14 = v23; } free(v14); return v13; } } return (void *)v12; }void __fastcall sub_2F5C(JNIEnv *a1, __int64 a2, __int64 a3) { int v5; // w0 size_t v6; // x21 const void *v8; // [xsp+40h] [xbp-30h] _QWORD v9[2]; // [xsp+60h] [xbp-10h] BYREF v9[1] = *(_QWORD *)(_ReadStatusReg(ARM64_SYSREG(3, 3, 13, 0, 2)) + 40); v5 = ((__int64 (__fastcall *)(JNIEnv *, __int64))(*a1)->GetStringUTFLength)(a1, a3); v6 = v5; v9[0] = v5; v8 = (const void *)((__int64 (__fastcall *)(JNIEnv *, __int64, _QWORD))(*a1)->GetStringUTFChars)(a1, a3, 0LL); sub_3550((__int64)&unk_6510, (__int64)&unk_13BF); sub_184C(v8, v6, (__int128 *)&unk_6510, v9); malloc((2LL * v9[0]) | 1); __asm { BR X9 } } __int64 __fastcall sub_3550(__int64 result, __int64 a2) { __int64 v2; // x5 char v3; // w3 unsigned int v4; // w4 int i; // [xsp+8h] [xbp-18h] if ( dword_6518 != 1 ) { for ( i = 0; ; ++i ) { v2 = i; v3 = *(_BYTE *)(a2 + 20 + i); v4 = i % 20u; if ( i == 6 ) break; *(_BYTE *)(result + v2) = v3 ^ *(_BYTE *)(a2 + v4); } *(_BYTE *)(result + i) = v3 ^ *(_BYTE *)(a2 + v4); dword_6518 = 1; } return result; } void *__fastcall sub_184C(const void *a1, size_t n, __int128 *a3, _QWORD *a4) { // [COLLAPSED LOCAL DECLARATIONS. PRESS NUMPAD "+" TO EXPAND] v31 = *(_QWORD *)(_ReadStatusReg(ARM64_SYSREG(3, 3, 13, 0, 2)) + 40); v30 = *a3; v20 = 0LL; if ( !(_BYTE)v30 ) goto LABEL_3; if ( !BYTE1(v30) ) { v20 = 1LL; goto LABEL_3; } if ( !BYTE2(v30) ) { v20 = 2LL; goto LABEL_3; } if ( !BYTE3(v30) ) { v20 = 3LL; goto LABEL_3; } if ( !BYTE4(v30) ) { v20 = 4LL; goto LABEL_3; } if ( !BYTE5(v30) ) { v20 = 5LL; goto LABEL_3; } if ( !BYTE6(v30) ) { v20 = 6LL; goto LABEL_3; } if ( !BYTE7(v30) ) { v20 = 7LL; goto LABEL_3; } if ( !BYTE8(v30) ) { v20 = 8LL; goto LABEL_3; } if ( !BYTE9(v30) ) { v20 = 9LL; goto LABEL_3; } if ( !BYTE10(v30) ) { v20 = 10LL; goto LABEL_3; } if ( !BYTE11(v30) ) { v20 = 11LL; goto LABEL_3; } if ( !BYTE12(v30) ) { v20 = 12LL; goto LABEL_3; } if ( !BYTE13(v30) ) { v20 = 13LL; goto LABEL_3; } if ( !BYTE14(v30) ) { v20 = 14LL; LABEL_3: memset((char *)&v30 + v20 + 1, 0, v20 ^ 0xF); } v12 = 0LL; if ( n ) { v7 = n >> 2; if ( (n & 3) != 0 ) ++v7; v21 = v7; nmemb = v7 + 1; v23 = (int *)calloc(v7 + 1, 4uLL); v12 = 0LL; if ( v23 ) { v23[v21] = n; memcpy(v23, a1, n); v24 = calloc(4uLL, 4uLL); if ( v24 ) { *v24 = v30; v25 = &v23[(unsigned int)(nmemb - 1)]; if ( (_DWORD)nmemb != 1 ) { v26 = qword_64F0; v18 = (unsigned int)*v25; for ( i = 0x34 / (unsigned int)nmemb + 5; ; --i ) { v27 = HIDWORD(v18) + v26; v28 = ((unsigned int)(HIDWORD(v18) + v26) >> 2) & 3; v16 = 0LL; v15 = v18; v17 = *v23; while ( 1 ) { v8 = v23[v16 + 1]; v29 = ((((4 * v8) ^ (v15 >> 5)) + ((v8 >> 3) ^ (16 * v15))) ^ ((*((_DWORD *)v24 + (v16 & 3 ^ v28)) ^ v15) + (v8 ^ v27))) + v17; v23[v16] = v29; if ( v16 + 1 == (_DWORD)nmemb - 1 ) break; v17 = v8; ++v16; v15 = v29; } v5 = ((((4 * *v23) ^ (v29 >> 5)) + (((unsigned int)*v23 >> 3) ^ (16 * v29))) ^ ((*((_DWORD *)v24 + (v28 ^ ((_BYTE)nmemb - 1) & 3)) ^ v29) + (*v23 ^ v27))) + *v25; *v25 = v5; if ( !i ) break; LODWORD(v18) = v5; HIDWORD(v18) += v26; } } v6 = malloc((4 * nmemb) | 1); memcpy(v6, v23, 4 * nmemb); *((_BYTE *)v6 + 4 * nmemb) = 0; *a4 = 4 * nmemb; free(v23); v13 = v6; v14 = (int *)v24; } else { v13 = 0LL; v14 = v23; } free(v14); return v13; } } return (void *)v12; } ~~~ 很明显,先是解密得到密钥,然后xxtea加密,尝试解密xxtea发现报错,怀疑delta值有问题 直接尝试hook发现值是0x7c1ca759 ~~~js Java.perform(function() { const moduleName = Process.findModuleByName("libre0.so"); const functionOffset = 0x1B5C; // 函数的偏移地址 const baseAddr = moduleName.base; console.log("libre0.so的基地址是: " + baseAddr); // 3. 计算函数的绝对地址 const functionAddr = baseAddr.add(functionOffset); console.log("要Hook的函数绝对地址是: " + functionAddr); // 4. 使用 Interceptor.attach 来拦截 Interceptor.attach(functionAddr, { // 当代码执行到 hookAddr 时,onEnter 会被调用 // 此时,0x1B58 处的指令已经执行完毕 onEnter: function(args) { console.log("\n[+] 成功 Hook 到地址: " + functionAddr); const x10_value = this.context.x10; console.log("指令 0x1B58 (LDR X10, ...) 执行完毕后, X10 的值是: " + x10_value); } }); }); ~~~ 去尝试同构加密发现对的上(16字节后面还有个额外的字节,所以加密完是20字节),但是去解密就不对了,不知道什么原因,八成是反hook了,哭死,但是看不懂啊 看wp说是得爆破delta值了,让ai写了个,注意只需要检查前16字符是否为hex字符即可 ~~~c #include <stdio.h> #include <stdint.h> #include <stdbool.h> #include <string.h> #include <pthread.h> #include <unistd.h> // For sysconf #include <ctype.h> // For isprint // --- XXTEA 解密核心函数 --- void xxtea_decrypt(uint32_t* v, uint32_t n, const uint32_t* key, uint32_t delta) { uint32_t r = 6 + 52 / n; uint32_t total = delta * r; uint32_t v0, v1, e; v1 = v[0]; for (unsigned int i = 0; i < r; ++i) { e = (total >> 2) & 3; // 从 v[n-1] 到 v[1] for (uint32_t j = n - 1; j > 0; --j) { v0 = v[j - 1]; v[j] -= (((v0 >> 5) ^ (v1 << 2)) + ((v1 >> 3) ^ (v0 << 4))) ^ ((total ^ v1) + (key[(j & 3) ^ e] ^ v0)); v1 = v[j]; } // 处理 v[0] v0 = v[n - 1]; v[0] -= (((v0 >> 5) ^ (v1 << 2)) + ((v1 >> 3) ^ (v0 << 4))) ^ ((total ^ v1) + (key[(0 & 3) ^ e] ^ v0)); v1 = v[0]; total -= delta; } } // --- 多线程相关 --- // 传递给每个线程的参数 typedef struct { int thread_id; uint64_t start_delta; uint64_t end_delta; const uint32_t* v_data; const uint32_t* k_data; uint32_t n; } thread_args_t; // 全局共享状态 pthread_mutex_t result_mutex = PTHREAD_MUTEX_INITIALIZER; volatile bool found_flag = false; uint32_t final_delta = 0; char final_plaintext[128] = {0}; // 线程工作函数 void* worker(void* args) { thread_args_t* t_args = (thread_args_t*)args; uint32_t v_copy[t_args->n]; // 创建密文的本地副本,因为解密是原地操作 printf("\[Thread-%d\] 开始搜索 Delta... 范围: 0x%llx -> 0x%llx\n", t_args->thread_id, t_args->start_delta, t_args->end_delta); for (uint64_t delta = t_args->start_delta; delta < t_args->end_delta; ++delta) { // 检查是否应该停止 pthread_mutex_lock(&result_mutex); if (found_flag) { pthread_mutex_unlock(&result_mutex); printf("[Thread-%d] 收到停止信号, 退出。\n", t_args->thread_id); return NULL; } pthread_mutex_unlock(&result_mutex); // 每次解密前都必须重置为原始密文 memcpy(v_copy, t_args->v_data, t_args->n * sizeof(uint32_t)); xxtea_decrypt(v_copy, t_args->n, t_args->k_data, (uint32_t)delta); bool is_hex_string = true; // 检查整个解密后的字符串是否可打印 for (uint32_t i = 0; i < 16; ++i) { char c = ((char*)v_copy)[i]; if (!((c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F'))) { is_hex_string = false; break; } } if (is_hex_string) { pthread_mutex_lock(&result_mutex); if (!found_flag) { // 双重检查,防止多个线程同时发现 found_flag = true; final_delta = (uint32_t)delta; // 拷贝结果,去除可能的尾部空字节 strncpy(final_plaintext, (const char*)v_copy, sizeof(final_plaintext) - 1); printf("\n============================================================\n"); printf(" ~~~ 最后修改:2025 年 10 月 08 日 © 允许规范转载 打赏 赞赏作者 支付宝微信 赞 如果觉得我的文章对你有用,请随意赞赏