
Reverse 1 (Lawer)

- Đầu tiên mình sẽ sử dụng DiE để xem các thông tin về file.

- Xem qua thì file này cũng không có gì đặc biệt cả nên mình sẽ chạy thử để xem liệu chương trình sẽ có những gì.
![]()
- Sau khi chạy thì file không in ra hoặc mở gì cả.
- Thế nên mình sẽ dùng IDA để xem liệu file đang ẩn chứa những gì.
int __fastcall main(int argc, const char **argv, const char **envp){ char v3; // si char *v4; // rbx unsigned int v5; // ecx char v6; // al int wShowWindow; // r9d _STARTUPINFOA StartupInfo; // [rsp+20h] [rbp-88h] BYREF
v3 = 0; _main(argc, argv, envp); v4 = *_p__acmdln(); if ( v4 ) { while ( 1 ) { v5 = *v4; if ( *v4 <= 32 ) { if ( !(_BYTE)v5 ) goto LABEL_15; if ( (v3 & 1) == 0 ) { do v6 = *++v4; while ( v6 && v6 <= 32 ); goto LABEL_15; } v3 = 1; } else if ( (_BYTE)v5 == 34 ) { v3 ^= 1u; } if ( ismbblead(v5) ) v4 += -(v4[1] == 0) + 1; ++v4; } } v4 = (char *)&unk_1400040A0;LABEL_15: memset(&StartupInfo, 0, sizeof(StartupInfo)); GetStartupInfoA(&StartupInfo); wShowWindow = 10; if ( (StartupInfo.dwFlags & 1) != 0 ) wShowWindow = StartupInfo.wShowWindow; return WinMain((HINSTANCE)refptr___image_base__, 0LL, v4, wShowWindow);}- Đầu tiên ta sẽ thấy một hàm
main, nhưng thực chất hàm này chỉ có nhiệm vụ chuẩn bị môi trường để sau đó gọi đến hàmWinMain. - Ta cùng xem hàm
WinMainhoạt động như nào.
int __stdcall WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd){ BOOL v4; // ebx BOOL v5; // eax int v6; // ebx HANDLE CurrentProcess; // rax int v8; // ebx HMODULE ModuleHandleA; // rax HMODULE v10; // rsi FARPROC ProcAddress; // rdi HANDLE v12; // rsi unsigned int v13; // r12d HANDLE ProcessHeap; // rax char *v15; // rbp int v16; // r10d unsigned __int64 i; // r9 char v18; // al int v19; // eax char *v20; // rdx char *v21; // rcx HANDLE v22; // rax unsigned __int64 v24; // rdx unsigned __int64 j; // rax char *v26; // rcx HANDLE v27; // rax int v28; // [rsp+3Ch] [rbp-4Ch] BYREF __int64 v29; // [rsp+40h] [rbp-48h] BYREF _QWORD v30[8]; // [rsp+48h] [rbp-40h] BYREF
hide_from_debugger(); v4 = NtCurrentPeb()->BeingDebugged != 0; v5 = IsDebuggerPresent(); LODWORD(v30[0]) = 0; v6 = v4 - (!v5 - 1); CurrentProcess = GetCurrentProcess(); _IAT_start__(CurrentProcess, v30); v8 = v6 - ((LODWORD(v30[0]) == 0) - 1); ModuleHandleA = GetModuleHandleA("ntdll.dll"); v10 = ModuleHandleA; if ( ModuleHandleA ) { ProcAddress = GetProcAddress(ModuleHandleA, "NtQueryInformationProcess"); GetProcAddress(v10, "NtSetInformationThread"); if ( ProcAddress ) { v29 = 0LL; v12 = GetCurrentProcess(); if ( ((int (__fastcall *)(HANDLE, __int64, __int64 *, __int64, _QWORD))ProcAddress)(v12, 7LL, &v29, 8LL, 0LL) >= 0 ) v8 -= (v29 == 0) - 1; v30[0] = 0LL; if ( ((int (__fastcall *)(HANDLE, __int64, _QWORD *, __int64, _QWORD))ProcAddress)(v12, 30LL, v30, 8LL, 0LL) >= 0 ) v8 -= (v30[0] == 0LL) - 1; v28 = 0; if ( ((int (__fastcall *)(HANDLE, __int64, int *, __int64, _QWORD))ProcAddress)(v12, 31LL, &v28, 4LL, 0LL) >= 0 ) v8 += v28 == 0; } } v13 = 1590204330; if ( v8 >= 2 ) v13 = -2140842124; ProcessHeap = GetProcessHeap(); v15 = (char *)HeapAlloc(ProcessHeap, 0, 0x28uLL); if ( !v15 ) goto LABEL_20; v16 = 85; for ( i = 0LL; i != 39; ++i ) { v18 = v16 ^ __ROR1__(g_flag_enc[i], i - (i / 5 + (((0xCCCCCCCCCCCCCCCDuLL * (unsigned __int128)i) >> 64) & 0xFC))); v16 += 7; v15[i] = (v13 >> (8 * (i & 3))) ^ v18; } v15[39] = 0; if ( v8 <= 1 || lstrlenA(v15) <= 8 || *v15 != 80 || v15[1] != 84 || v15[2] != 73 || v15[3] != 84 || v15[4] != 67 || v15[5] != 84 || v15[6] != 70 || v15[7] != 123 ) { v19 = lstrlenA(v15); v20 = v15; v21 = &v15[v19]; if ( v19 ) { if ( (v19 & 1) == 0 || (v20 = v15 + 1, *v15 = 0, v21 != v15 + 1) ) { do { *v20 = 0; v20 += 2; *(v20 - 1) = 0; } while ( v21 != v20 ); } } v22 = GetProcessHeap(); HeapFree(v22, 0, v15);LABEL_20: Sleep(0xC8u); return v8 >= 2; } OutputDebugStringA("[CTF] FLAG: "); OutputDebugStringA(v15); DebugBreak(); v24 = lstrlenA(v15); for ( j = 0LL; j < v24; ++j ) { v26 = &v15[j]; *v26 = 0; } v27 = GetProcessHeap(); HeapFree(v27, 0, v15); return v8 >= 2;}- Chuẩn ở đây rồi, hàm
WinMainchính là hàm xử lý chính của chương trình này. - Ta hãy phân tích xem nó đang làm gì.
hide_from_debugger();v4 = NtCurrentPeb()->BeingDebugged != 0;v5 = IsDebuggerPresent();LODWORD(v30[0]) = 0;v6 = v4 - (!v5 - 1);CurrentProcess = GetCurrentProcess();_IAT_start__(CurrentProcess, v30);v8 = v6 - ((LODWORD(v30[0]) == 0) - 1);ModuleHandleA = GetModuleHandleA("ntdll.dll");v10 = ModuleHandleA;if ( ModuleHandleA ){ ProcAddress = GetProcAddress(ModuleHandleA, "NtQueryInformationProcess"); GetProcAddress(v10, "NtSetInformationThread"); if ( ProcAddress ) { v29 = 0LL; v12 = GetCurrentProcess(); if ( ((int (__fastcall *)(HANDLE, __int64, __int64 *, __int64, _QWORD))ProcAddress)(v12, 7LL, &v29, 8LL, 0LL) >= 0 ) v8 -= (v29 == 0) - 1; v30[0] = 0LL; if ( ((int (__fastcall *)(HANDLE, __int64, _QWORD *, __int64, _QWORD))ProcAddress)(v12, 30LL, v30, 8LL, 0LL) >= 0 ) v8 -= (v30[0] == 0LL) - 1; v28 = 0; if ( ((int (__fastcall *)(HANDLE, __int64, int *, __int64, _QWORD))ProcAddress)(v12, 31LL, &v28, 4LL, 0LL) >= 0 ) v8 += v28 == 0; }}v4 = NtCurrentPeb()->BeingDebugged != 0;- v4 = 1 nếu PEB->BeingDebugged = true, ngược lại 0.
v5 = IsDebuggerPresent();- v5 = 1 nếu Windows API IsDebuggerPresent() cho biết có debugger.
LODWORD(v30[0]) = 0;- khởi tạo vùng v30[0] = 0 (dùng sau này để chứa kết quả kiểm tra).
v6 = v4 - (!v5 - 1);- Nếu BeingDebugged=0, IsDebuggerPresent=0 thì v6 = 0.
- Nếu một trong hai là 1 (một trong hai báo debug) thì v6 = 1.
- Nếu cả hai là 1 thì v6 = 2.
=> v6 là số “điểm” ban đầu cho các dấu hiệu debug từ PEB & IsDebuggerPresent.
CurrentProcess = GetCurrentProcess();_IAT_start__(CurrentProcess, v30);v8 = v6 - ((LODWORD(v30[0]) == 0) - 1);- Ở đoạn này sẽ tăng v8 lên 1 đơn vị nếu như phát hiện có can thiệp của debugger.
v29 = 0LL;v12 = GetCurrentProcess();if ( ((int (__fastcall *)(HANDLE, __int64, __int64 *, __int64, _QWORD))ProcAddress)(v12, 7LL, &v29, 8LL, 0LL) >= 0 ) v8 -= (v29 == 0) - 1;- ProcessInformationClass = 7 → ProcessDebugPort.
- Nếu process không bị debug thì v29 = 0.
- Nếu có debugger attach thì v29 != 0.
- Nếu có
debug portthì v8 + 1.
v30[0] = 0LL;if ( NtQueryInformationProcess(v12, 30, v30, 8, 0) >= 0 ) v8 -= (v30[0] == 0LL) - 1;- ProcessInformationClass = 30 → ProcessDebugObjectHandle.
- Nếu không có debugger thì v30[0] = 0.
- Nếu bị debug thì v30[0] != 0.
- Nếu có
debug objectthì v8 + 1.
v28 = 0;if ( NtQueryInformationProcess(v12, 31, &v28, 4, 0) >= 0 ) v8 += v28 == 0;- ProcessInformationClass = 31 → ProcessDebugFlags.
- Nếu không debug thì v28 = 1.
- Nếu bị debug thì v28 = 0.
- Nếu
DebugFlags = 0thì tăng 1 điểm nghi ngờ.
v13 = 0x5EC897AA;if ( v8 >= 2 ) v13 = 0x80655774;- Nếu phát hiện nhiều hơn 1 dấu hiệu debug thì
v13sẽ đổi sang một giá trị khác.
v16 = 85;for ( i = 0LL; i != 39; ++i ){ v18 = v16 ^ __ROR1__(g_flag_enc[i], i - (i / 5 + (((0xCCCCCCCCCCCCCCCDuLL * (unsigned __int128)i) >> 64) & 0xFC))); v16 += 7; v15[i] = (v13 >> (8 * (i & 3))) ^ v18;}v15[39] = 0;- Tiếp đến là một hàm giải mã dữ liệu bên trong mảng
g_flag_encdựa theo giá trị của v13 vì thế ta có thể sẽ có 2 kết quả tùy theo việc có đang sử dụng debug hay không.
if ( v8 <= 1 || lstrlenA(v15) <= 8 || *v15 != 80 || v15[1] != 84 || v15[2] != 73 || v15[3] != 84 || v15[4] != 67 || v15[5] != 84 || v15[6] != 70 || v15[7] != 123 ){v19 = lstrlenA(v15);v20 = v15;v21 = &v15[v19];if ( v19 ){ if ( (v19 & 1) == 0 || (v20 = v15 + 1, *v15 = 0, v21 != v15 + 1) ) { do { *v20 = 0; v20 += 2; *(v20 - 1) = 0; } while ( v21 != v20 ); }}v22 = GetProcessHeap();HeapFree(v22, 0, v15);LABEL_20:Sleep(0xC8u);return v8 >= 2;}OutputDebugStringA("[CTF] FLAG: ");OutputDebugStringA(v15);DebugBreak();v24 = lstrlenA(v15);for ( j = 0LL; j < v24; ++j ){ v26 = &v15[j]; *v26 = 0;}v27 = GetProcessHeap();HeapFree(v27, 0, v15);return v8 >= 2;- Tiếp đó là đoạn kiểm tra và in flag ra nếu flag đúng.
- Dựa vào phần giải mã mảng
g_flag_enc, mình viết một script python để thực hiện giải mã với cả 2 giá trị củav8để tìm ra được giá trị đúng cho flag.
def ror(val, rbits): rbits &= 7 return ((val >> rbits) | (val << (8 - rbits))) & 0xFF
def decode_flag(g_flag_enc, v13): v16 = 85 result = [] for i in range(39): shift = i - (i // 5 + (((0xCCCCCCCCCCCCCCCD * i) >> 64) & 0xFC))
v18 = v16 ^ ror(g_flag_enc[i], shift) v16 = (v16 + 7) & 0xFF decoded = ((v13 >> (8 * (i & 3))) & 0xFF) ^ v18 result.append(decoded) return bytes(result).decode(errors="ignore")
g_flag_enc = [ 0x71, 0xBE, 0x3D, 0xF5, 0x64, 0x7B, 0xB8, 0xF5, 0x6D, 0xBA, 0x97, 0xA2, 0x0A, 0xB6, 0x1A, 0x50, 0xBD, 0xBF, 0x4F, 0x73, 0xA1, 0xA7, 0xF7, 0xB8, 0xBF, 0x36, 0x62, 0xDB, 0x10, 0x91, 0x65, 0xB5, 0x78, 0xE0, 0x94, 0xB8, 0xAE, 0xD9, 0x3A, 0x00]
print(decode_flag(g_flag_enc, 0x5EC897AA))print(decode_flag(g_flag_enc, 0x80655774))- Sau khi chạy đoạn code python này thì mình đã có được flag cho bài này.
Flag
PTITCTF{This_1snot_m4lware_don't_worry}Reverse 2 (Carnival Show)

- Trước hết mình sử dụng DiE để xem các thông tin của file này.

- File này cũng không có gì đặc biệt, mình tiến hành chạy thử để xem file này sẽ in ra những gì.

- Có vẻ là chương trình đang check debug hay gì đó.
- Ta cùng dùng IDA để xem liệu cụ thế nó đang làm gì.
void __fastcall __noreturn start(__int64 a1, __int64 a2, void (*a3)(void)){ __int64 v3; // rax int v4; // esi __int64 v5; // [rsp-8h] [rbp-8h] BYREF char *retaddr; // [rsp+0h] [rbp+0h] BYREF
v4 = v5; v5 = v3; _libc_start_main(main, v4, &retaddr, 0LL, 0LL, a3, &v5); __halt();}
__int64 __fastcall main(int a1, char **a2, char **a3){ printf("flag (maybe?): %s\n", "PTITCTF{W3lcome_T0_My_Chall!!!}"); return 0LL;}- Đầu tiên ta sẽ được dùng lại ở hàm
startvà nó sẽ dẫn đến hàmmainnhưng có lẽ đây sẽ không phải logic chính xác của chương trình và flag ở trong main cũng là fake flag. - Ta cùng tìm kỹ hơn trong các hàm lân cận xem có gì khai thác được không.
- Sau một lúc tìm kiếm thì mình nhận thấy có hàm
sub_1300có xử lý yêu cầu nhập vào của người dùng và mã hóa input đó và mình nghĩ nó chính là logic chính để xử lý flag. - Ta cùng phân tích xem nó liệu đang làm những gì.
- Các dòng đầu của hàm
sub_1300thật sự chúng chỉ là những phần để check debug nên mình nghĩ chúng ta có thể bỏ qua phần đó và phân tích những phần cụ thể hơn ở bên dưới.
sub_12B0(1); // "Enter flag: " while ( read(0, rlimits, 1uLL) > 0 && LOBYTE(rlimits[0].sa_handler) != 10 ) { s[v14++] = (char)rlimits[0].sa_handler; v15 = s; if ( v14 == 255 ) { s[255] = 0; goto LABEL_26; } } s[v14] = 0; v15 = s; if ( !v14 ) { sub_12B0(1); exit(2); }LABEL_26: prctl(22, 1LL); v16 = strlen(s); if ( 4 * ((v16 + 2) / 3) == 60 ) { if ( v16 ) { v18 = rlimits; v19 = 0LL; do { v27 = v19 + 1; v28 = (unsigned __int8)s[v19]; if ( v19 + 1 < v16 ) { v20 = v19 + 2; v21 = (unsigned __int8)s[v27] << 8; if ( v19 + 2 >= v16 ) { v19 += 2LL; v22 = 0; } else { v22 = (unsigned __int8)s[v20]; v19 += 3LL; } } else { v20 = ++v19; v22 = 0; v21 = 0; } v23 = v28 << 16; v24 = v23 | v22 | v21; LOBYTE(v23) = aQwertyuiopasdf[v23 >> 18]; BYTE1(v23) = aQwertyuiopasdf[(v24 >> 12) & 0x3F]; LOWORD(v18->sa_handler) = v23; v25 = 46; if ( v27 < v16 ) v25 = aQwertyuiopasdf[(v24 >> 6) & 0x3F]; BYTE2(v18->sa_sigaction) = v25; v26 = 46; if ( v20 < v16 ) v26 = aQwertyuiopasdf[v24 & 0x3F]; BYTE3(v18->sa_sigaction) = v26; v18 = (struct sigaction *)((char *)v18 + 4); } while ( v19 < v16 ); } v29 = 0LL; v30 = 1; do { v31 = v30; v30 += 3; v32 = (char *)&v43 + v29 + 800; *(_DWORD *)((char *)&rlimits[0].sa_handler + v29) = (unsigned __int8)v32[(v31 & 3) - 512] | (((unsigned __int8)v32[(((v31 & 3) + 1) & 3) - 512] | (((unsigned __int8)v32[(((v31 & 3) + 2) & 3) - 512] | ((unsigned __int8)v32[(((v31 & 3) + 3) & 3) - 512] << 8)) << 8)) << 8); v29 += 4LL; } while ( v30 != 46 ); v33 = -2128831035; v34 = "n0_dbg^_^"; do { v35 = (unsigned __int8)*v34++; v33 = 16777619 * (v35 ^ v33); } while ( v34 != "" ); v36 = v33 ^ 0x9E377985; v37 = 0; for ( j = 0LL; j != 60; ++j ) { v36 ^= ((v36 ^ (v36 << 13)) >> 17) ^ (v36 << 13) ^ (32 * (((v36 ^ (v36 << 13)) >> 17) ^ v36 ^ (v36 << 13))); v39 = v36 + aN0Dbg[j - (j / 9 + (((0xE38E38E38E38E38FLL * (unsigned __int128)j) >> 64) & 0xFFFFFFFFFFFFFFF8LL))]; v40 = byte_2220[j] ^ *((_BYTE *)&rlimits[0].sa_handler + j); v37 |= v40 ^ v39; } v41 = rlimits; do { v42 = v41; v41 = (struct sigaction *)((char *)v41 + 1); LOBYTE(v42->sa_handler) = 0; } while ( v41 != (struct sigaction *)&v49 ); v0 = v37 == 0; } do { v17 = v15++; *v17 = 0; } while ( v15 != (char *)rlimits ); if ( v0 ) { sub_12B0(1); // "Correct! GG.\n" exit(0); } sub_12B0(1); // "Nope.\n" exit(1);- Những gì ta cần để ý chính là đoạn này.
while ( read(0, rlimits, 1uLL) > 0 && LOBYTE(rlimits[0].sa_handler) != 10 ){ s[v14++] = (char)rlimits[0].sa_handler; v15 = s; if ( v14 == 255 ) { s[255] = 0; goto LABEL_26; }}- Đầu tiên chương trình cho ta nhập flag vào và gán vào biến
s.
v16 = strlen(s);if ( 4 * ((v16 + 2) / 3) == 60 )- Sau đó là kiểm tra độ dài input khi đi qua một phép toán và có thể nó sẽ là độ dài của input ngay sau khi được mã hóa base64 và ở dưới mình thấy có một đoạn base64.
v18 = rlimits;v19 = 0LL;do{ v27 = v19 + 1; v28 = (unsigned __int8)s[v19]; if ( v19 + 1 < v16 ) { v20 = v19 + 2; v21 = (unsigned __int8)s[v27] << 8; if ( v19 + 2 >= v16 ) { v19 += 2LL; v22 = 0; } else { v22 = (unsigned __int8)s[v20]; v19 += 3LL; } } else { v20 = ++v19; v22 = 0; v21 = 0; } v23 = v28 << 16; v24 = v23 | v22 | v21; LOBYTE(v23) = aQwertyuiopasdf[v23 >> 18]; BYTE1(v23) = aQwertyuiopasdf[(v24 >> 12) & 0x3F]; LOWORD(v18->sa_handler) = v23; v25 = '.'; if ( v27 < v16 ) v25 = aQwertyuiopasdf[(v24 >> 6) & 0x3F]; BYTE2(v18->sa_sigaction) = v25; v26 = '.'; if ( v20 < v16 ) v26 = aQwertyuiopasdf[v24 & 0x3F]; BYTE3(v18->sa_sigaction) = v26; v18 = (struct sigaction *)((char *)v18 + 4);}while ( v19 < v16 );- Tiếp đến ở đây chương trình thực hiện một đoạn mã hóa base64 giá trị input với bảng
aQwertyuiopasdfvà pad byte sẽ được thay thành..
v29 = 0LL;v30 = 1;do{ v31 = v30; v30 += 3; v32 = (char *)&v43 + v29 + 800; *(_DWORD *)((char *)&rlimits[0].sa_handler + v29) = ( unsigned __int8)v32[(v31 & 3) - 512] | (((unsigned __int8)v32[(((v31 & 3) + 1) & 3) - 512] | (((unsigned __int8)v32[(((v31 & 3) + 2) & 3) - 512] | ((unsigned __int8)v32[(((v31 & 3) + 3) & 3) - 512] << 8)) << 8)) << 8 ); v29 += 4LL;}while ( v30 != 46 );- Sau đó lấy chuỗi encode ở trong
rlimitsvà biến đổi thêm dựa trên vùng nhớv32 = (char *)&v43 + v29 + 800;.
v33 = -2128831035;v34 = "n0_dbg^_^";do{ v35 = (unsigned __int8)*v34++; v33 = 16777619 * (v35 ^ v33);}while ( v34 != "" );v36 = v33 ^ 0x9E377985;- Tiếp ngay dưới đó là một đoạn hash bằng FNV-1a 64-bit với chuỗi
n0_dbg^_^. Sau đó XOR kết quả với 0x9E377985.
v37 = 0;for ( j = 0LL; j != 60; ++j ){ v36 ^= ((v36 ^ (v36 << 13)) >> 17) ^ (v36 << 13) ^ (32 * (((v36 ^ (v36 << 13)) >> 17) ^ v36 ^ (v36 << 13))); v39 = v36 + aN0Dbg[j - (j / 9 + (((0xE38E38E38E38E38FLL * (unsigned __int128)j) >> 64) & 0xFFFFFFFFFFFFFFF8LL))]; v40 = byte_2220[j] ^ *((_BYTE *)&rlimits[0].sa_handler + j); v37 |= v40 ^ v39;}- Loop qua 60 ký tự của chuỗi encode.
- Ở mỗi bước cập nhật giá trị của biến
v36. - v39 = v36 + ký tự trong
aN0Dbg(n0_dbg^_^). - Xor các giá trị trong
byte_2220với giá trị encode ở trongrlimits. v37sẽ so sánh kết quả củav40vàv39, nếu 2 biến này có kết quả giống nhau thì v37 sẽ = 0.
- Ở mỗi bước cập nhật giá trị của biến
v0 = v37 == 0;if ( v0 ){ sub_12B0(1); // "Correct! GG.\n" exit(0);}- Nếu như
v37có kết quả = 0 thìv0= 1 thì in raCorrect! GG.\n. - Từ logic đó mình tiến hành viết một đoạn script python để reverse lại flag.
aQwertyuiopasdf = b"QWERTYUIOPASDFGHJKLZXCVBNMqwertyuiopasdfghjklzxcvbnm0123456789-_"aN0Dbg = b"n0_dbg^_^"byte_2220 = [ 0x87,0xA4,0x55,0x21,0xAC,0x4B,0x57,0xAE,0x13,0xAB, 0x5D,0x97,0x5C,0xFD,0xF0,0xB5,0xCA,0x5D,0x22,0xCF, 0xE7,0xE0,0x3F,0x98,0x49,0x58,0x06,0xAF,0x87,0x90, 0x50,0xBC,0xE3,0xA9,0x30,0xFC,0xE0,0xB3,0x8F,0xAE, 0x4C,0x04,0x56,0x39,0x76,0xC0,0x39,0x93,0xDC,0x08, 0x21,0xF7,0xC2,0xE2,0x56,0xFC,0xFE,0x16,0xDE,0x43]
def fnv(): h = 0x811C9DC5 for b in aN0Dbg: h = ((h ^ b) * 16777619) & 0xFFFFFFFF return h
def last(): v = (fnv() ^ 0x9E377985) & 0xFFFFFFFF result = [] for j in range(60): a = ((v ^ ((v << 13) & 0xFFFFFFFF)) >> 17) & 0xFFFFFFFF b = (v << 13) & 0xFFFFFFFF u = (a ^ v ^ b) & 0xFFFFFFFF v = (v ^ a ^ b ^ ((32 * u) & 0xFFFFFFFF)) & 0xFFFFFFFF
idx = j - (j // 9 + (((0xE38E38E38E38E38F * j) >> 64) & 0xFFFFFFFFFFFFFFF8)) result.append((v + aN0Dbg[idx % len(aN0Dbg)]) & 0xFF) return result
def xorr(c_bytes, prng): return [c ^ p for c, p in zip(c_bytes, prng)]
def rotation(eperm): enc = [] v31 = 1 for i in range(0, len(eperm), 4): block = eperm[i:i+4] s = v31 & 3 if s: rotated = block[-s:] + block[:-s] else: rotated = block[:] enc.extend(rotated) v31 += 3 return bytes(enc)
def base64_decode(enc_bytes): pad_byte = ord('.') inv = {aQwertyuiopasdf[i]: i for i in range(len(aQwertyuiopasdf))} out = bytearray() for i in range(0, len(enc_bytes), 4): chunk = enc_bytes[i:i + 4] if len(chunk) < 4: chunk = chunk.ljust(4, bytes([pad_byte])) pad_count = chunk.count(pad_byte) vals = [(0 if b == pad_byte else inv.get(b, 0)) for b in chunk] v = (vals[0] << 18) | (vals[1] << 12) | (vals[2] << 6) | vals[3] b1 = (v >> 16) & 0xFF b2 = (v >> 8) & 0xFF b3 = v & 0xFF if pad_count == 0: out += bytes([b1, b2, b3]) elif pad_count == 1: out += bytes([b1, b2]) elif pad_count == 2: out += bytes([b1]) return bytes(out)
def decode(): xorr_result = xorr(byte_2220, last()) encoded_bytes = rotation(xorr_result) decoded = base64_decode(encoded_bytes) return decoded
flag = decode()print(flag.decode())- Sau khi chạy script python này thì mình đã có được flag cho chall.
Flag
PTITCTF{Y0u_c4n_bypass_4ll_types_0f_4nt1!!!}Forensic 2 (Candle)

- Đầu tiên mình mở file MP3 tiếng cười bằng HXD để xem hex của file.

- Khá bất ngờ khi kéo xuống cuối file thì mình thấy rất nhiều các hex
PNGcủa file ảnh. - Vậy nên mình sử dụng binwalk để lấy các file png này ra.

- Sau khi binwalk ra thì mình nhận được khá nhiều ảnh và có vẻ như nó sẽ ghép thành một mã QR.

- Sau đó mình ghép lại các mảnh này thành một mã QR, từ đó mình quét ra được một link X.

- Ồ có vẻ có một đoạn
base64ở đây, mình thử decode nó ra xem được gì.

- Tuy đây là một flag chuẩn form nhưng tiếc nó lại là một fake flag.
- Mình tiếp tục tìm kiếm trong bài viết trên X để xem có được thêm thông tin gì không.

- Cũng ở chính bình luận đó mình thấy một đoạn có vẻ như là
ROT-13, mình sẽ decode nó ra để xem có thông tin gì không.

- Tuyệt vời, ta có một link github ở đây.
- Truy cập vào link github đó và tải về file exe được cung cấp.
- Trước hết như bao bài reverse mình sẽ dùng DiE để xem các thông tin của file exe này.

- Có vẻ file này cũng khá bình thường và không có gì đặc biệt.
- Mình chạy thử xem chương trình này như nào.

- Chúng ta được cung cấp một chương trình chứng khoán ở đây, mình có chơi thử và khi bị khá sản sẽ hiện ra một hàm hình màu đen như bị hack.
- Vì cũng không biết phải làm gì tiếp theo tôi mình sẽ dùng IDA để có thể check code của nó.
__int64 sub_140001180(){ ... *(_QWORD *)off_140191658 = qword_14020E018; result = sub_140189EE0(v14, qword_14020E020); dword_14020E010 = result; ...}- Men theo flow của chương trình mình đến được hàm này.
- Và ở đây cái chúng ta cần quan tâm chính là hàm
sub_140189EE0được gọi ở đoạnresult = sub_140189EE0(v14, qword_14020E020);. - Ta cùng phân tích xem hàm đó đang làm gì.
- Như khi chạy chương trình giao dịch chứng khoán thì sẽ có một phần
Bankroll: xxx$nên mình nghĩ chương trình cũng sẽ lấy giá trị ở đây để so sánh điều kiện thắng và phá sản.
sub_140004720(v325, v295);v319 = 10LL;strcpy(v320, "Bankroll: ");v265 = &Block;Block = v320;v262 = &Size;sub_140187050(&Size, &Block, v325); // In ra "Bankroll: ...$"- Sau một hồi phân tích thì mình tìm đến được vị trí gán
Bankroll: + Tiền$. - Và ở đây biến
v295chính là biến chứa số tiền của còn lại trong tài khoản, nó được khai báo mặc định làv295 = 1000;. - Và chương trình này sẽ kiểm tra số dư của tài khoản, nếu thỏa mãn thì sẽ in ra flag.
- Ở đây mình sẽ sửa giá trị mặc định của
v295lên một số lớn ví dụ như 0x7FFFFFFF cho cực lớn, sau đó trade thử.

- Ta chỉ cần sửa lại hex mặc định là được.
- Giờ thì mình sẽ chạy file và trade thử.

- Và với số tiền lớn đó thì mình đã có được flag cho bài này.
Flag
PTITCTF{PTIT_Futures_is_a_crypt0currency_futures_trading_platf0rm_and_sh0rt_BTC_set_up_n0w!!!}