提交人:太刀
superguesser | 动态调试
静态分析发现函数全部被混淆 在入口下断点步过函数直到进入被还原的函数 找到核心加密逻辑:
// positive sp value has been detected, the output may be wrong! __int64 __fastcall sub_401530() { __int64 stack; // rbp char v1; // zf __int64 (*v2)(void); // rax void *v3; // rsp __int64 v4; // rcx char v5; // of (loc_46EB33)(); _disable(); (loc_46EB77)(); (loc_46EBB4)(); if ( v1 ) JUMPOUT(0x401548i64); (loc_46EBF7)(); *(stack - 96) = 81; *(stack - 95) = 81; *(stack - 94) = 82; *(stack - 93) = 95; *(stack - 92) = 89; *(stack - 91) = 67; *(stack - 90) = 93; *(stack - 89) = 95; *(stack - 88) = 89; *(stack - 87) = 73; *(stack - 86) = 90; *(stack - 85) = 89; *(stack - 84) = 86; *(stack - 83) = 46; *(stack - 82) = 38; *(stack - 81) = 29; *(stack - 80) = 42; *(stack - 79) = 55; *(stack - 78) = 26; *(stack - 77) = 39; *(stack - 76) = 41; *(stack - 75) = 23; *(stack - 74) = 40; *(stack - 73) = 36; *(stack - 72) = 42; *(stack - 71) = 56; *(stack - 70) = 37; *(stack - 69) = 33; *(stack - 68) = 61; *(stack - 67) = 15; *(stack - 66) = 50; *(stack - 65) = 58; *(stack - 64) = 60; *(stack - 63) = 61; *(stack - 62) = 54; *(stack - 61) = 51; *(stack - 60) = 42; *(stack - 59) = 0; *(stack - 24) = (loc_46EC76)(stack - 96); *(stack - 25) = 51; v2 = (loc_46ECB3)(); __outbyte(0x2Fu, v2); *(stack - 26) = v2(); *(stack - 40) = *(stack - 24) - 1i64; (loc_46ECF6)(); (loc_46ED38)(); v3 = alloca((loc_46ED75)()); *(stack - 48) = alloc(); (scanf_0)(v4, *(stack - 48)); (loc_46EE32)(); (loc_46EEB1)(); if ( !v5 ) JUMPOUT(0x401651i64); while ( *(stack - 20) < *(stack - 24) ) *(*(stack - 48) + (*(stack - 20))++) ^= *(stack - 20) + *(stack - 25) + 17 * *(stack - 26);// i + ? + 17 * ?? if ( !(cmp)(*(stack - 48), stack - 96, *(stack - 24)) ) { (loc_46F030)(); JUMPOUT(0x4016E8i64); } (loc_46EF73)(); return (loc_46EFF3)(); }
动调发现stack-48
处存放的就是输入的flag 而只有这一处加密 由于不确定*(stack - 26)
的值是否因为反调试手段被修改 故用爆破方法解flag:
enc = [81, 81, 82, 95, 89, 67, 93, 95, 89, 73, 90, 89, 86, 46, 38, 29, 42, 55, 26, 39, 41, 23, 40, 36, 42, 56, 37, 33, 61, 15, 50, 58, 60, 61, 54, 51, 42] for ran in range(128): flag = "" for i in range(len(enc)): flag += chr((enc[i] ^ (i + 0x33 + 17 * ran)) & 0xff) if flag[:5] == "begin": print(flag) # begin{debugging_is_an_anathor_choice}
ezpython | 注入解法
用pyinstxtractor解包出的key和enc无法通过sm4解密得到flag 故使用原程序的环境进行解密 原程序反编译后主要逻辑:
from gmssl import sm4 from secrets import key, enc import base64 def pad_pkcs7(data): """PKCS#7填充""" padding_len = 16 - len(data) % 16 padding = bytes([padding_len] * padding_len) return data + padding def unpad_pkcs7(padded_data): """PKCS#7去填充""" padding_len = padded_data[-1] return padded_data[:-padding_len] class SM4: def __init__(self): self.gmsm4 = sm4.CryptSM4() def encryptSM4(self, encrypt_key, value): gmsm4 = self.gmsm4 gmsm4.set_key(encrypt_key.encode(), sm4.SM4_ENCRYPT) padded_value = pad_pkcs7(value.encode()) encrypt_value = gmsm4.crypt_ecb(padded_value) return base64.b64encode(encrypt_value) if __name__ == "__main__": flag = input("...") sm4_instance = SM4() flag_1 = sm4_instance.encryptSM4(key, flag) if flag_1 == enc: print("Success") else: print("Failed")
在powershell启动题目程序(用cmd或者直接启动貌似不行) 找到该程序的pid 使用de4py的PyShell功能将以下解密代码注入到程序:
def decryptSM4(self, encrypt_key, enc): gmsm4 = self.gmsm4 gmsm4.set_key(encrypt_key.encode(), sm4.SM4_DECRYPT) encrypt_value = base64.b64decode(enc) decrypt_value = gmsm4.crypt_ecb(encrypt_value) return unpad_pkcs7(decrypt_value).decode() SM4.decrypt = decryptSM4 sm4_new = SM4() print(sm4_new.decrypt(key, enc))
效果:
goforfun | go语言逆向
第一个加密的核心特征:
for ( i = 255; i >= 0; --i ) { if ( i >= 0x100 ) runtime_panicIndex(); *(box + i) = -1 - i; } count = 0; v7 = 0; while ( count < 256 ) { v8 = *(box + count); v9 = count; v10 = count - 12 * (((2863311531LL * count) >> 32) >> 3); if ( v10 >= 0xC ) runtime_panicIndex(); v7 += v35[v10] + v8; *(box + v9) = *(box + v7); *(box + v7) = v8; count = v9 + 1; } sub_462C00(&PRGA_result, box); flag_context = v4; lenth_of_context = v34; v26 = v11; main_PRGA(PRGA_result);
Sbox初始化 PRGA 但是不是普通的RC4加密 进入PRGA函数:
int __usercall main_PRGA@<eax>(char table) { int result; // eax signed int v2; // ecx unsigned int v3; // edx signed int v4; // ebx char v5; // bp char v6; // si char v7; // cl char v8; // [esp+12h] [ebp-2h] void *retaddr; // [esp+14h] [ebp+0h] BYREF if ( &retaddr <= *(*__readfsdword(runtime_tls_g) + 8) ) runtime_morestack_noctxt(); result = runtime_makeslice(&uint8, STACK[0x11C], STACK[0x11C]); v2 = STACK[0x11C]; v3 = STACK[0x118]; v4 = 0; v5 = 0; v6 = 0; while ( v2 > v4 ) { v7 = *(&table + (v5 + 1)); v8 = v7 + v6; *(&table + (v5 + 1)) = *(&table + (v7 + v6)); *(&table + (v7 + v6)) = v7; *(result + v4) = *(&table + (v7 + *(&table + (v5 + 1)))) ^ *(v3 + v4) ^ 0x2F; ++v4; v2 = STACK[0x11C]; ++v5; v6 = v8; } STACK[0x124] = result; STACK[0x128] = v2; STACK[0x12C] = v2; return result; }
动调发现v3
存放的就是输入的flag去除begin{}包裹的内容 所以加密只关心和其异或的东西 在最后异或步骤下状况断点 输出存放异或的数字的寄存器:
得到异或内容:
xor_key = [i ^ 0x2f for i in [132, 14, 121, 193, 41, 61, 231, 134, 147, 244, 180, 102, 100, 175, 25, 151, 133, 7, 230, 74, 200, 3, 55]]
最后一处加密的核心特征:
从64长度的表中按第二处加密得到的结果为下标取元素 最后和密文比较 猜测是换表base64
第二处加密:
v20 = main_byteArrayToBigInt(result, lenth_2); v22 = main_bigIntModToArray(str_head);
动调发现第一个函数的作用就是将第一处加密得到的结果按小端序储存为Go中的一种大整型数据(没有位数上限 能储存多大取决于计算机)
第二个函数:
__int128 __golang main_bigIntModToArray(int *a1) { int *mod_result_instanc; // eax int v2; // ecx int v3; // edx unsigned int i; // ebx int v5; // ebp int now_result; // ecx int origin_data; // eax unsigned int count_1; // ecx int add_; // edx int *final_procees; // ebx int *result_; // [esp+Ch] [ebp-6Ch] int *v12; // [esp+14h] [ebp-64h] unsigned int v13; // [esp+18h] [ebp-60h] int v14; // [esp+1Ch] [ebp-5Ch] int v15; // [esp+20h] [ebp-58h] unsigned int v16; // [esp+24h] [ebp-54h] unsigned int v17; // [esp+28h] [ebp-50h] int v18; // [esp+28h] [ebp-50h] int mod_num_2; // [esp+2Ch] [ebp-4Ch] BYREF int mod_num; // [esp+30h] [ebp-48h] BYREF int v21; // [esp+44h] [ebp-34h] int v22[4]; // [esp+48h] [ebp-30h] BYREF int mod_n[4]; // [esp+58h] [ebp-20h] BYREF int be_moded_instanc[4]; // [esp+68h] [ebp-10h] BYREF void *retaddr; // [esp+78h] [ebp+0h] BYREF __int128 result; // [esp+80h] [ebp+8h] if ( &retaddr <= *(*__readfsdword(runtime_tls_g) + 8) ) runtime_morestack_noctxt(); mod_result_instanc = a1; v2 = 0; v3 = 0; for ( i = 0; ; i = v16 ) { if ( mod_result_instanc[2] ) v5 = *mod_result_instanc ? -1 : 1; else v5 = 0; if ( v5 <= 0 ) break; v17 = v2; v21 = v3; LOBYTE(be_moded_instanc[0]) = 0; memset(&be_moded_instanc[1], 0, 12); mod_num = 64; LOBYTE(mod_n[0]) = 0; mod_n[1] = &mod_num; mod_n[2] = 1; mod_n[3] = 1; result_ = math_big__Int_Mod(be_moded_instanc, mod_result_instanc, mod_n); if ( result_[2] ) now_result = *result_[1]; else now_result = 0; if ( *result_ ) origin_data = -now_result; else origin_data = now_result; count_1 = i + 1; add_ = v17; if ( v17 < i + 1 ) { v15 = origin_data; v12 = runtime_growslice(v21, i + 1, v17, 1, &int); count_1 = v13; final_procees = v12; add_ = v14; origin_data = v15; } else { final_procees = v21; } v21 = final_procees; v16 = count_1; v18 = add_; final_procees[count_1 - 1] = origin_data; mod_num_2 = 0x40; LOBYTE(v22[0]) = 0; v22[1] = &mod_num_2; v22[2] = 1; v22[3] = 1; math_big__Int_Div(a1, a1, v22); mod_result_instanc = a1; v3 = v21; v2 = v18; } *&result = __PAIR64__(i, v3); DWORD2(result) = v2; return result; }
核心是其中的大整型取模和大整型除法 动调发现取模和除法都是对第一个函数得到的大整型进行 另一个操作数是0x40
也就是result = big_int & 0xFFFFFF, big_int >> 6
符合之前base64的猜测(但是直接用base64解出来不对 所以自己写解密脚本):
xor_key = [i ^ 0x2f for i in [132, 14, 121, 193, 41, 61, 231, 134, 147, 244, 180, 102, 100, 175, 25, 151, 133, 7, 230, 74, 200, 3, 55]] enc = ["8G+cazk2jqb7w01CtoKH4FsrgR3vVmQ9pPhXLAleOd/nB6DfIxMWYiUZ5SEJyNuT".index(i) for i in"HZ0sMJXqxHgUb2b9RNg+1xw"] enc = [bin(i).replace("0b", "").zfill(6) for i in enc][::-1] big_int = int("".join(enc), 2) flag = [] while big_int > 0: flag.append(big_int & 0xff) big_int >>= 8 flag = flag[::-1] for i in range(len(flag)): print(chr(flag[i] ^ xor_key[i]), end="") # go_a_nice_journey
not main | 反调试
IDA识别出的主函数:
int __cdecl main(int argc, const char **argv, const char **envp) { unsigned int v3; // ecx __int32 *pt_input1; // edi unsigned __int32 now; // edx int sum; // ebx unsigned __int32 next; // esi int v8; // edi __int32 *pt_input3; // ecx __int32 *v10; // edx unsigned int v11; // esi bool v12; // cf unsigned int v13; // edx int v14; // eax int v16; // [esp-4h] [ebp-2Ch] __int32 *pt_input2; // [esp+Ch] [ebp-1Ch] __int128 v18; // [esp+10h] [ebp-18h] BYREF int v19; // [esp+20h] [ebp-8h] sub_141930(std::cin); v3 = strlen(enc); if ( v3 == 0x1A ) { pt_input1 = enc; pt_input2 = enc; do { now = *pt_input1; sum = 0; // TEA next = pt_input1[1]; v8 = 32; do { sum -= 0x61C88647; now += ((next >> 5) + 97) ^ (16 * next + 102) ^ (sum + next); next += ((now >> 5) + 101) ^ (16 * now + 107) ^ (sum + now); --v8; } while ( v8 ); *pt_input2 = now; pt_input2[1] = next; pt_input1 = pt_input2 + 2; pt_input2 = pt_input1; } while ( pt_input1 < &debug_flag_1 ); pt_input3 = enc; v10 = to_cmp; v11 = 28; while ( *pt_input3 == *v10 ) { ++pt_input3; ++v10; v12 = v11 < 4; v11 -= 4; if ( v12 ) { v18 = xmmword_143220; LOBYTE(v19) = 19; v13 = 0; v3 = strlen(&v18); if ( v3 ) { do *(&v18 + v13++) ^= 0x13u; while ( v13 < &v18 + strlen(&v18) + 1 - (&v18 + 1) ); } goto LABEL_11; } } } else { LABEL_11: // fail v14 = sub_141600(v3, sub_141830); std::ostream::operator<<(v14, v16); } return 0; }
包含一个TEA 但是解出来是假flag while ( pt_input1 < &debug_flag_1 );
这一行对flag交叉引用到达一个异常处理函数:
int sub_141010() { uint8_t BeingDebugged; // al BeingDebugged = NtCurrentPeb()->BeingDebugged; debug_flag_1 = BeingDebugged != 0; if ( BeingDebugged ) AddVectoredExceptionHandler(1u, Handler); return atexit(sub_142AE0); }
Handler
应该就是程序正常运行过程中主函数触发异常后执行的处理函数:
LONG __userpurge Handler@<eax>(int a1@<edi>, struct _EXCEPTION_POINTERS *ExceptionInfo) { DWORD ExceptionCode; // eax PCONTEXT ContextRecord; // eax int sum; // ecx unsigned __int32 temp; // eax unsigned int v7; // ebx unsigned int i; // edi bool v9; // zf _DWORD *real_enc; // edx __int32 *v11; // ecx unsigned int v12; // esi bool v13; // cf unsigned int j; // edx int v15; // eax int v16; // [esp-10h] [ebp-44h] unsigned int addr_13503C; // [esp+4h] [ebp-30h] int v19; // [esp+8h] [ebp-2Ch] int v20; // [esp+Ch] [ebp-28h] int temp_2; // [esp+10h] [ebp-24h] int key[4]; // [esp+14h] [ebp-20h] int v23[3]; // [esp+24h] [ebp-10h] BYREF ExceptionCode = ExceptionInfo->ExceptionRecord->ExceptionCode; if ( ExceptionCode == -2147483645 ) { ContextRecord = ExceptionInfo->ContextRecord; dword_145038 = 0; ++ContextRecord->Eip; return -1; } else if ( ExceptionCode == 0xC0000094 ) { return 0; } else { key[0] = 116; addr_13503C = to_cmp ^ ::addr_13503C; sum = 0; ::addr_13503C ^= to_cmp; temp = enc_7_; key[1] = 114; key[2] = 117; key[3] = 101; v19 = 12; temp_2 = enc_7_; do { v20 = sum - 0x61C88647; v7 = ((sum - 0x61C88647) >> 2) & 3; for ( i = 0; i < 7; ++i ) { enc[i] += ((v20 ^ enc_1_[i]) + (temp_2 ^ key[v7 ^ i & 3])) ^ (((16 * te