---
tags: [Beginner]
---
# Subtask
## mixture.exe
* Đối với bài này, sử dụng 1 kỹ thuật có thể nói là mới với mình, đó là **Process Hollowing**.
* Một vài blog mình kiếm được để đọc về kỹ thuật này là https://sec.vnpt.vn/2019/01/process-hollowing
https://viblo.asia/p/tim-hieu-ve-process-hollowing-V3m5WRmxlO7
Ngoài ra còn rất nhiều nguồn khác.
### Kỹ thuật Process Injection
* Bài này tập trung ở kỹ thuật **Process hollowing**, nên đương nhiên phần này mình trình bày ở mức đơn giản thui nhe. 😶🌫️
#### Khái niệm kỹ thuật
* **Process Injection** là tập hợp các kỹ thuật cho phép kẻ tấn công chèn mã độc vào không gian bộ nhớ của tiến trình khác và thực thi mã đó trong ngữ cảnh của tiến trình mục tiêu.
#### Mục tiêu kỹ thuật
* Che giấu hành vi tấn công (ẩn sau tiến trình hợp pháp).
* Bypass AV/EDR.
* Duy trì persistence hoặc nâng quyền.
#### Một số kỹ thuật phổ biến thuộc nhóm này
* DLL Injection
* **Process Hollowing**
* Thread Hijacking
* Reflective DLL Injection
#### Refs
https://github.com/plackyhacker/Shellcode-Injection-Techniques/blob/master/README.md
https://sec.vnpt.vn/2025/04/process-injection-ky-thuat-moi-su-dung-thread-name
### Kỹ thuật Process hollowing
#### Khái niệm
* **Process hollowing** là một kỹ thuật phổ biến trong lĩnh vực **malware** và **red team**, thuộc nhóm kỹ thuật **Process Injection**, nhằm che giấu mã độc bên trong một tiến trình hợp pháp của hệ điều hành. Đây là một tactic thường thấy trong fileless malware, giúp mã độc né tránh sự phát hiện của các phần mềm antivirus hay EDR.
* Cụ thể hơn thì kẻ tấn công tạo một tiến trình hợp pháp ở trạng thái treo (`suspended`), rỗng hóa bộ nhớ chứa mã gốc của tiến trình đó, sau đó ghi mã độc vào và cho tiến trình tiếp tục chạy như bình thường. Mã độc lúc này sẽ chạy dưới vỏ bọc của một tiến trình hợp pháp.
#### Mục đích
* Ẩn mã độc dưới danh nghĩa tiến trình hệ thống hoặc tiến trình phổ biến (như `svchost.exe`, `explorer.exe`, `cmd.exe`, v.v.).
* Tránh bị phát hiện bởi các công cụ bảo mật (`antivirus`, `EDR`).
* Bypass `sandbox`, kiểm tra tĩnh, và `signature detection`.
#### Cơ chế hoạt động
* 1. Tạo tiến trình hợp pháp với trạng thái `CREATE_SUSPENDED`.
* 2. Xóa (unmap) vùng bộ nhớ image gốc bằng `NtUnmapViewOfSection`.
* 3. Cấp phát vùng nhớ mới, ghi payload (mã độc) vào.
* 4. Chỉnh sửa **Entry Point** (`EIP/RIP`) để chuyển hướng thực thi về mã độc.
* 5. Resume tiến trình, lúc này tiến trình sẽ chạy mã độc đã chèn vào.
* Cơ bản thì chỉ có như thế, vậy thì vào bài xem có gì nào.
### Phân tích
* Đây là 1 bài **flag checker**.
* Load file vào **IDA**, hàm `main()` trông như sau:

:::spoiler Code
```py
int __cdecl main(int argc, const char **argv, const char **envp)
{
unsigned int v3; // ebx
unsigned int i; // esi
int v5; // edi
unsigned int j; // ecx
int v7; // eax
unsigned int k; // esi
int v9; // edi
unsigned int m; // ecx
int v11; // eax
unsigned int n; // ecx
unsigned int ii; // ecx
unsigned int jj; // esi
void (__stdcall *v15)(int, int); // edi
int v16; // esi
char v18; // [esp+0h] [ebp-448h]
_BYTE v19[4]; // [esp+Ch] [ebp-43Ch] BYREF
void (__stdcall *v20)(int); // [esp+10h] [ebp-438h]
int v21; // [esp+14h] [ebp-434h] BYREF
void (__stdcall *v22)(int, char *, unsigned int, _BYTE *, _DWORD); // [esp+18h] [ebp-430h]
int v23; // [esp+1Ch] [ebp-42Ch]
char Format[4]; // [esp+20h] [ebp-428h] BYREF
char v25; // [esp+24h] [ebp-424h]
char v26; // [esp+25h] [ebp-423h] BYREF
char v27; // [esp+26h] [ebp-422h]
char v28; // [esp+27h] [ebp-421h]
_BYTE v29[5]; // [esp+28h] [ebp-420h] BYREF
char v30; // [esp+2Dh] [ebp-41Bh]
char v31; // [esp+2Eh] [ebp-41Ah]
char v32; // [esp+30h] [ebp-418h]
char v33; // [esp+31h] [ebp-417h] BYREF
_BYTE v34[18]; // [esp+32h] [ebp-416h] BYREF
char Arglist[1024]; // [esp+44h] [ebp-404h] BYREF
v3 = 0;
v34[2] = 0;
v34[3] = 7;
v34[4] = 3;
v34[5] = 54;
v34[6] = 9;
v34[7] = 3;
v34[8] = 50;
v34[9] = 118;
v34[10] = 75;
v34[11] = 30;
qmemcpy(&v34[12], "W22T", 4);
for ( i = 0; i < 0xD; ++i )
v34[i + 3] = (62 * (84 - (unsigned __int8)v34[i + 3]) % 127 + 127) % 127;
v5 = sub_1F1123(&v34[3]);
*(_DWORD *)Format = v5;
v25 = 0;
v26 = 9;
v27 = 88;
v28 = 104;
v29[0] = 28;
v29[1] = 97;
v29[2] = 11;
v29[3] = 104;
v29[4] = 14;
v30 = 97;
v31 = 79;
for ( j = 0; j < 0xA; ++j )
*(&v26 + j) = (55 * ((unsigned __int8)*(&v26 + j) - 79) % 127 + 127) % 127;
v7 = sub_1F11B3(v5, &v26);
v25 = 0;
v26 = 110;
v27 = 26;
v28 = 37;
v29[0] = 124;
v29[1] = 16;
v29[2] = 15;
v29[3] = 102;
v29[4] = 26;
v30 = 18;
v22 = (void (__stdcall *)(int, char *, unsigned int, _BYTE *, _DWORD))v7;
for ( k = 0; k < 9; ++k )
*(&v26 + k) = (35 * (18 - (unsigned __int8)*(&v26 + k)) % 127 + 127) % 127;
v9 = *(_DWORD *)Format;
*(_DWORD *)v29 = sub_1F11B3(*(_DWORD *)Format, &v26);
v34[2] = 0;
v34[3] = 114;
v34[4] = 115;
v34[5] = 81;
v34[6] = 78;
v34[7] = 25;
v34[8] = 15;
v34[9] = 28;
v34[10] = 50;
v34[11] = 121;
v34[12] = 115;
v34[13] = 25;
v34[14] = 69;
for ( m = 0; m < 0xC; ++m )
v34[m + 3] = (41 * ((unsigned __int8)v34[m + 3] - 69) % 127 + 127) % 127;
v11 = sub_1F11B3(v9, &v34[3]);
v32 = 0;
v33 = 29;
qmemcpy(v34, ")_T~\nSJ)G_?>)", 13);
v34[13] = 21;
v34[14] = 21;
v34[15] = 22;
v20 = (void (__stdcall *)(int))v11;
for ( n = 0; n < 0x11; ++n )
v34[n - 1] = (12 * ((unsigned __int8)v34[n - 1] - 22) % 127 + 127) % 127;
v23 = sub_1F11B3(v9, &v33);
memset(Arglist, 0, sizeof(Arglist));
v32 = 0;
v33 = 68;
v34[0] = 14;
qmemcpy(&v34[1], "fQK\npoNQ\\g+", 11);
v34[12] = 18;
v34[13] = 15;
qmemcpy(&v34[14], "Q-", 2);
for ( ii = 0; ii < 0x11; ++ii )
v34[ii - 1] = (15 * ((unsigned __int8)v34[ii - 1] - 45) % 127 + 127) % 127;
sub_1F32E6(&v33, v18);
Format[0] = 0;
Format[1] = 38;
Format[2] = 18;
Format[3] = 54;
for ( jj = 0; jj < 3; ++jj )
Format[jj + 1] = (42 * (54 - (unsigned __int8)Format[jj + 1]) % 127 + 127) % 127;
v15 = (void (__stdcall *)(int, int))v23;
sub_1F32AE(&Format[1], (char)Arglist);
v22(dword_1F6438, Arglist, strlen(Arglist), v19, 0);
v16 = unknown_libname_1(100);
(*(void (__stdcall **)(int, int, int, int *, _DWORD))v29)(dword_1F6438, v16, 100, &v21, 0);
*(_BYTE *)(v21 + v16) = 0;
v29[0] = 0;
qmemcpy(&v29[1], "Tkx[", 4);
do
{
v29[v3 + 1] = (31 * ((unsigned __int8)v29[v3 + 1] - 91) % 127 + 127) % 127;
++v3;
}
while ( v3 < 4 );
sub_1F32E6(&v29[1], v16);
v20(dword_1F6438);
v15(dword_1F643C, -1);
return 0;
}
```
:::
* Để detect kỹ thuật **Process Hollowing**, có thể sử dụng:
* **[Process Explorer](https://learn.microsoft.com/en-us/sysinternals/downloads/process-explorer):**

* **[Process Hacker](https://systeminformer.sourceforge.io/downloads):**

* Ta thấy trong `mixture.exe` đang có `cmd.exe`.
* Tuy hơi rời rạc, nhưng mình sẽ thử phân tích tiếp `mixture.exe` xem có gì không. 😶
* Sau 1 hồi ~~vật vã~~ debug, thì mình thấy chương trình có tương đối nhiều hàm thực hiện **resolve API**, mình sẽ tóm tắt dưới đây.
#### Hàm main()
* `Kernel32.dll`

* [kernel32_WriteFile](https://learn.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-writefile)

* [kernel32_ReadFile](https://learn.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-readfile)

* [kernel32_CloseHandle](https://learn.microsoft.com/en-us/windows/win32/api/handleapi/nf-handleapi-closehandle)

* Ngoài ra thì hàm `sub_4A32E6(&v33, v18);` để `printf([+] Enter Flag:)` và
`(*(void (__stdcall **)(int, int, int, int *, _DWORD))v29)(dword_4A6438, v16, 100, &v21, 0);` để `scanf(input)`.
#### Hàm sub_4A2082()
:::spoiler Code
```py
int sub_4A2082()
{
unsigned int i; // esi
int v1; // eax
int v2; // esi
unsigned int j; // ecx
void (__stdcall *v4)(int); // ebx
unsigned int k; // ecx
unsigned int m; // esi
char *v7; // eax
_BYTE v9[17]; // [esp+Dh] [ebp-47h] BYREF
_BYTE v10[12]; // [esp+1Eh] [ebp-36h] BYREF
_BYTE v11[13]; // [esp+2Ah] [ebp-2Ah] BYREF
_BYTE v12[8]; // [esp+38h] [ebp-1Ch] BYREF
_BYTE v13[8]; // [esp+40h] [ebp-14h] BYREF
int v14; // [esp+48h] [ebp-Ch]
char v15; // [esp+4Ch] [ebp-8h]
char v16; // [esp+4Dh] [ebp-7h]
v13[0] = 0;
qmemcpy(&v13[1], "B6PH6D", 6);
v13[7] = 18;
v14 = 1144260624;
v15 = 68;
v16 = 43;
for ( i = 0; i < 0xD; ++i )
v13[i + 1] = (63 * (43 - (unsigned __int8)v13[i + 1]) % 127 + 127) % 127;
v1 = sub_4A1123(&v13[1]);
v13[4] = 0;
v2 = v1;
strcpy(&v13[5], ">u");
LOBYTE(v14) = 0;
*(_WORD *)((char *)&v14 + 1) = 27723;
for ( j = 0; j < 6; ++j )
v13[j + 5] = (12 * ((unsigned __int8)v13[j + 5] - 108) % 127 + 127) % 127;
v13[0] = 0;
v4 = (void (__stdcall *)(int))sub_4A11B3(v1, (int)&v13[5]);
v13[1] = 49;
v13[2] = 28;
v13[3] = 23;
v13[4] = 41;
v13[5] = 19;
v13[6] = 23;
v13[7] = 99;
v14 = 1444361989;
v15 = 33;
for ( k = 0; k < 0xC; ++k )
v13[k + 1] = (28 * ((unsigned __int8)v13[k + 1] - 33) % 127 + 127) % 127;
v14 = sub_4A11B3(v2, (int)&v13[1]);
sub_4A154D();
v4(104);
v9[0] = 14;
v9[1] = 112;
v9[2] = 14;
v9[3] = 112;
v9[4] = 63;
v9[5] = 112;
v9[6] = 14;
v9[7] = 112;
v9[8] = 70;
v9[9] = 112;
v9[10] = 25;
v9[11] = 112;
v9[12] = 70;
v9[13] = 112;
v9[14] = 90;
v9[15] = 112;
v9[16] = 14;
qmemcpy(v10, "pDpGpApGpGp", 11);
v10[11] = 17;
qmemcpy(v11, "p6p}p^p}p}ppp", sizeof(v11));
for ( m = 0; m < 0x2A; ++m )
v9[m] = (45 * (112 - (unsigned __int8)v9[m]) % 127 + 127) % 127;
dword_4A6438 = ((int (__stdcall *)(_BYTE *, int, _DWORD, _DWORD, int, _DWORD, _DWORD))v14)(
v9,
-536870912,
0,
0,
3,
0,
0);
if ( dword_4A6438 == -1 )
{
v12[0] = 0;
v12[1] = 44;
v12[2] = 111;
v12[3] = 111;
v12[4] = 115;
v12[5] = 111;
v12[6] = 51;
v12[7] = 4;
qmemcpy(v13, "oV1B{J)3", sizeof(v13));
v14 = 1444772637;
v15 = 38;
v16 = 9;
v7 = (char *)sub_4A3273(v12);
sub_4A32E6(v7);
exit(0);
}
return 0;
}
```
:::
* `Kernel32.dll`

* [kernel32_Sleep](https://learn.microsoft.com/en-us/windows/win32/api/synchapi/nf-synchapi-sleep)

* [kernel32_CreateFileW](https://learn.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-createfilea)

* Còn 1,2 hàm nữa gì đấy, cũng **resolve API** như này, mình nghĩ thay vì liệt kê tiếp thì mình sẽ tóm tắt lại flow của chương trình, cũng như mục đích cụ thể của kỹ thuật **Process Hollowing** đối với bài này như sau:
* **main()**:
* Resolve API, hiển thị các prompt.
* Gọi `WriteFile`, `ReadFile` để giao tiếp với một tiến trình khác (qua named pipe).
* **sub_402082()**:
* Resolve các API: `kernel32.dll`, `Sleep`, `CreateFileW`.
* Gọi đến `sub_40154D()` → hàm thực hiện kỹ thuật **Process Hollowing**.
* Gọi `Sleep` (mục đích: delay, tránh detection).
* Tạo **Named Pipe** với tên `\\.\pipe\KCSCCTF2022`.
* Hàm **sub_40154D()** — trọng tâm chính
* Resolve các API phục vụ **Hollowing**: `CreateProcessW`,`VirtualAllocEx`,`WriteProcessMemory`,`NtUnmapViewOfSection`,`ResumeThread`.
* Tạo tiến trình **cmd.exe** dưới dạng `CREATE_SUSPENDED`. Sau đó Unmap phân vùng code gốc trong tiến trình bằng `NtUnmapViewOfSection`.
* Cấp phát lại vùng nhớ mới bằng **VirtualAllocEx** với quyền `PAGE_EXECUTE_READWRITE`.
* Lấy **payload** từ resource (qua `sub_401233()`), dùng `FindResourceW`, `LoadResource`, `SizeofResource`.
* **Payload** ban đầu bị mã hóa, cần giải mã trước khi inject (chắc để tránh detect). Sau đó inject **payload** vào tiến trình **cmd.exe** bằng `WriteProcessMemory`.
* Resume thực thi tiến trình **cmd.exe** bằng `ResumeThread`.
* Hiểu được ý đồ như vậy, mình nghĩ sẽ phải dump được con **payload** này - gọi là **malware** cũng được, phân tích thử xem nó có gì không.
* Như ở trên mình có phân tích, là tiến trình `mixture.exe` đã tạo ra một tiến trình `cmd.exe`. Kết hợp với phần tóm tắt thì thứ mình cần dump chính là con `cmd.exe`.
* Ở đây, sử dụng **[Process Explorer](https://learn.microsoft.com/en-us/sysinternals/downloads/process-explorer)** hoặc **[Process Hacker](https://systeminformer.sourceforge.io/downloads)** có lẽ được. Công nhân hơn dump tay :)). Nhưng mình thấy con **[Hollows_Hunter](https://github.com/hasherezade/hollows_hunter)** khá ngon khi nó tự scan và detect, đồng thời hỗ trợ dump. Hẹ hẹ hẹ 😂
* Nếu chỉ scan thì ` .\hollows_hunter64.exe /pname cmd.exe`

* Nếu xuất file dump thì `.\hollows_hunter64.exe /pname cmd.exe /dir .`

* Như vậy thì file dump nằm cùng thư mục hiện tại, tiến hành phân tích bằng **IDA**.
* Hàm `main()`:
:::spoiler Code
```py
int __cdecl main(int argc, const char **argv, const char **envp)
{
unsigned int i; // ecx
int v4; // eax
unsigned int v5; // ecx
int (__stdcall *v6)(_BYTE *); // edi
unsigned int j; // esi
int v8; // eax
unsigned int k; // esi
int v10; // ebx
int v11; // eax
unsigned int m; // ecx
int v13; // eax
unsigned int n; // ecx
int v15; // eax
unsigned int ii; // ecx
int v17; // eax
unsigned int jj; // esi
unsigned int kk; // esi
int v20; // eax
int v21; // edi
unsigned int mm; // ecx
int v23; // eax
unsigned int nn; // esi
int v25; // eax
unsigned int i1; // ecx
unsigned int i2; // esi
int v28; // eax
unsigned int i3; // esi
int v30; // ebx
int v31; // eax
unsigned int i4; // ecx
int v33; // eax
unsigned int i5; // esi
int v35; // eax
unsigned int i6; // ecx
int v37; // eax
unsigned int i7; // esi
int v39; // eax
unsigned int i8; // ecx
unsigned int i9; // ecx
int v42; // eax
unsigned int i10; // ecx
int v44; // eax
unsigned int i11; // ecx
int v46; // eax
unsigned int i12; // esi
int v48; // eax
unsigned int i13; // esi
int v50; // ebx
int v51; // eax
unsigned int i14; // ecx
int v53; // eax
unsigned int i15; // esi
unsigned int i16; // esi
int v56; // eax
int v57; // ebx
unsigned int i17; // esi
const char *v59; // edx
unsigned int i18; // ecx
char v62[96]; // [esp-60h] [ebp-520h] BYREF
_DWORD v63[24]; // [esp+Ch] [ebp-4B4h] BYREF
char v64; // [esp+6Ch] [ebp-454h]
_BYTE v65[43]; // [esp+6Dh] [ebp-453h] BYREF
int v66; // [esp+98h] [ebp-428h]
char v67; // [esp+9Ch] [ebp-424h]
_BYTE v68[23]; // [esp+9Dh] [ebp-423h] BYREF
char v69[4]; // [esp+B4h] [ebp-40Ch] BYREF
char v70[4]; // [esp+B8h] [ebp-408h] BYREF
_BYTE v71[1024]; // [esp+BCh] [ebp-404h] BYREF
memset(v71, 0, sizeof(v71));
v68[3] = 0;
v68[4] = 86;
v68[5] = 8;
v68[6] = 50;
v68[7] = 125;
v68[8] = 8;
v68[9] = 99;
v68[10] = 28;
v68[11] = 15;
strcpy(&v68[12], "Zzcc");
for ( i = 0; i < 0xD; ++i )
v68[i + 4] = (-39 * (unsigned __int8)v68[i + 4] % 127 + 127) % 127;
v4 = sub_B9102A(&v68[4]);
v5 = 0;
v68[3] = 0;
v68[4] = 22;
strcpy(&v68[5], "F");
v68[7] = 15;
v68[8] = 22;
v68[9] = 40;
v68[10] = 5;
strcpy(&v68[11], "U");
v68[13] = 85;
v68[14] = 120;
v68[15] = 94;
v68[16] = 23;
v66 = v4;
do
{
v68[v5 + 4] = (51 * ((unsigned __int8)v68[v5 + 4] - 23) % 127 + 127) % 127;
++v5;
}
while ( v5 < 0xD );
v67 = 0;
v6 = (int (__stdcall *)(_BYTE *))sub_B910BA(v4, &v68[4]);
v68[0] = 118;
v68[1] = 21;
qmemcpy(&v68[2], "\nK4\nbK\a\n:", 9);
v68[11] = 2;
qmemcpy(&v68[12], "Hu\n/ ", 5);
v63[0] = v6;
for ( j = 0; j < 0x11; ++j )
v68[j] = (45 * (32 - (unsigned __int8)v68[j]) % 127 + 127) % 127;
v8 = sub_B910BA(v66, v68);
v67 = 0;
qmemcpy(v68, "VR;;j<FU", 8);
v68[8] = 14;
v68[9] = 36;
v68[10] = 106;
v68[11] = 83;
v68[12] = 4;
qmemcpy(&v68[13], "GijE", 4);
v63[2] = v8;
for ( k = 0; k < 0x11; ++k )
v68[k] = (11 * (69 - (unsigned __int8)v68[k]) % 127 + 127) % 127;
v10 = v66;
v11 = sub_B910BA(v66, v68);
v67 = 0;
v68[0] = 7;
v68[1] = 105;
v68[2] = 80;
v68[3] = 120;
v68[4] = 90;
v68[5] = 29;
v68[6] = 29;
v68[7] = 115;
v68[8] = 120;
v68[9] = 14;
v68[10] = 109;
v68[11] = 125;
v68[12] = 95;
v68[13] = 115;
v68[14] = 54;
v68[15] = 104;
v68[16] = 105;
v68[17] = 24;
qmemcpy(&v68[18], "s2", 2);
v63[3] = v11;
for ( m = 0; m < 0x14; ++m )
v68[m] = (25 * ((unsigned __int8)v68[m] - 50) % 127 + 127) % 127;
v13 = sub_B910BA(v10, v68);
v68[7] = 0;
v68[8] = 97;
v68[9] = 35;
v68[10] = 28;
v68[11] = 65;
v68[12] = 76;
v68[13] = 42;
v68[14] = 79;
v68[15] = 35;
v68[16] = 17;
v63[4] = v13;
for ( n = 0; n < 9; ++n )
v68[n + 8] = (55 * ((unsigned __int8)v68[n + 8] - 17) % 127 + 127) % 127;
v15 = sub_B910BA(v10, &v68[8]);
v68[7] = 0;
v68[8] = 8;
v68[9] = 125;
v68[10] = 86;
v68[11] = 49;
v68[12] = 111;
v68[13] = 19;
v68[14] = 86;
v68[15] = 99;
v68[16] = 111;
v68[17] = 12;
v63[5] = v15;
for ( ii = 0; ii < 0xA; ++ii )
v68[ii + 8] = (10 * ((unsigned __int8)v68[ii + 8] - 12) % 127 + 127) % 127;
v17 = sub_B910BA(v10, &v68[8]);
v68[7] = 0;
v68[8] = 15;
qmemcpy(&v68[9], "W4ZT~.j\vWTM", 11);
v63[6] = v17;
for ( jj = 0; jj < 0xC; ++jj )
v68[jj + 8] = (40 * (77 - (unsigned __int8)v68[jj + 8]) % 127 + 127) % 127;
v68[3] = 0;
qmemcpy(&v68[4], "mESm$WT7BE//", 12);
v68[16] = 2;
for ( kk = 0; kk < 0xD; ++kk )
v68[kk + 4] = (35 * (2 - (unsigned __int8)v68[kk + 4]) % 127 + 127) % 127;
v20 = v6(&v68[4]);
v68[3] = 0;
v21 = v20;
v68[4] = 18;
v68[5] = 70;
v68[6] = 22;
v68[7] = 124;
v68[8] = 12;
qmemcpy(&v68[9], "F'[F;FbLz-Q", 11);
for ( mm = 0; mm < 0x10; ++mm )
v68[mm + 4] = (37 * ((unsigned __int8)v68[mm + 4] - 81) % 127 + 127) % 127;
v23 = sub_B910BA(v20, &v68[4]);
v68[3] = 0;
v68[4] = 27;
v68[5] = 20;
v68[6] = 46;
v68[7] = 40;
v68[8] = 20;
v68[9] = 88;
v68[10] = 79;
v68[11] = 95;
v68[12] = 111;
v68[13] = 101;
v68[14] = 20;
v68[15] = 112;
v68[16] = 13;
qmemcpy(&v68[17], "<h", 2);
v63[8] = v23;
for ( nn = 0; nn < 0xF; ++nn )
v68[nn + 4] = (39 * (104 - (unsigned __int8)v68[nn + 4]) % 127 + 127) % 127;
v25 = sub_B910BA(v21, &v68[4]);
v68[7] = 0;
v68[8] = 46;
v68[9] = 63;
v68[10] = 18;
v68[11] = 66;
v68[12] = 96;
v68[13] = 92;
v68[14] = 2;
v68[15] = 63;
v68[16] = 13;
qmemcpy(&v68[17], "?yq", 3);
v63[14] = v25;
for ( i1 = 0; i1 < 0xC; ++i1 )
v68[i1 + 8] = (31 * ((unsigned __int8)v68[i1 + 8] - 113) % 127 + 127) % 127;
v68[7] = 0;
v68[8] = 90;
v68[9] = 83;
v68[10] = 67;
v68[11] = 88;
v68[12] = 78;
v68[13] = 21;
v68[14] = 57;
v68[15] = 30;
v68[16] = 41;
v68[17] = 83;
v68[18] = 78;
v68[19] = 24;
for ( i2 = 0; i2 < 0xC; ++i2 )
v68[i2 + 8] = (24 * (24 - (unsigned __int8)v68[i2 + 8]) % 127 + 127) % 127;
v28 = sub_B910BA(v66, &v68[8]);
v68[3] = 0;
v68[4] = 22;
v68[5] = 87;
v68[6] = 42;
v68[7] = 77;
v68[8] = 6;
v68[9] = 42;
v68[10] = 32;
v68[11] = 111;
v68[12] = 87;
v68[13] = 42;
v68[14] = 77;
v68[15] = 19;
v68[16] = 5;
v63[7] = v28;
for ( i3 = 0; i3 < 0xD; ++i3 )
v68[i3 + 4] = (11 * (5 - (unsigned __int8)v68[i3 + 4]) % 127 + 127) % 127;
v30 = v66;
v31 = sub_B910BA(v66, &v68[4]);
v67 = 0;
qmemcpy(v68, "\t?", 2);
v68[2] = 30;
qmemcpy(&v68[3], "@_%hY", 5);
v68[8] = 30;
v68[9] = 57;
v68[10] = 70;
v68[11] = 97;
v68[12] = 110;
v68[13] = 42;
v68[14] = 43;
v68[15] = 10;
v68[16] = 110;
v68[17] = 23;
qmemcpy(&v68[18], "@b", 2);
v63[12] = v31;
for ( i4 = 0; i4 < 0x14; ++i4 )
v68[i4] = (19 * ((unsigned __int8)v68[i4] - 98) % 127 + 127) % 127;
v33 = sub_B910BA(v30, v68);
v67 = 0;
v68[0] = 1;
v68[1] = 14;
v68[2] = 84;
v68[3] = 34;
v68[4] = 18;
v68[5] = 75;
v68[6] = 84;
v68[7] = 67;
v68[8] = 103;
v68[9] = 94;
v68[10] = 14;
v68[11] = 104;
v68[12] = 28;
v68[13] = 117;
v68[14] = 14;
qmemcpy(&v68[15], "P^]", 3);
v63[13] = v33;
for ( i5 = 0; i5 < 0x12; ++i5 )
v68[i5] = (27 * (93 - (unsigned __int8)v68[i5]) % 127 + 127) % 127;
v35 = sub_B910BA(v66, v68);
v68[3] = 0;
v68[4] = 91;
v68[5] = 4;
v68[6] = 55;
v68[7] = 78;
v68[8] = 94;
v68[9] = 4;
v68[10] = 43;
v68[11] = 103;
v68[12] = 4;
v68[13] = 6;
qmemcpy(&v68[14], "M,f ", 4);
v63[15] = v35;
for ( i6 = 0; i6 < 0xE; ++i6 )
v68[i6 + 4] = (10 * ((unsigned __int8)v68[i6 + 4] - 32) % 127 + 127) % 127;
v37 = sub_B910BA(v21, &v68[4]);
v67 = 0;
v68[0] = 120;
v68[1] = 31;
v68[2] = 35;
v68[3] = 118;
v68[4] = 63;
v68[5] = 31;
v68[6] = 57;
v68[7] = 71;
v68[8] = 1;
v68[9] = 23;
v68[10] = 45;
v68[11] = 63;
v68[12] = 31;
qmemcpy(&v68[13], "^EVS", 4);
v63[16] = v37;
for ( i7 = 0; i7 < 0x11; ++i7 )
v68[i7] = (63 * (83 - (unsigned __int8)v68[i7]) % 127 + 127) % 127;
v39 = sub_B910BA(v21, v68);
v68[7] = 0;
v68[8] = 31;
v68[9] = 19;
v68[10] = 118;
v68[11] = 114;
v68[12] = 48;
v68[13] = 6;
v68[14] = 77;
v68[15] = 19;
v68[16] = 2;
v68[17] = 19;
qmemcpy(&v68[18], "x$", 2);
v63[17] = v39;
for ( i8 = 0; i8 < 0xC; ++i8 )
v68[i8 + 8] = (9 * ((unsigned __int8)v68[i8 + 8] - 36) % 127 + 127) % 127;
v65[19] = 0;
v65[20] = 102;
v65[21] = 117;
v65[22] = 76;
v65[23] = 38;
v65[24] = 69;
v65[25] = 23;
v65[26] = 96;
v65[27] = 14;
qmemcpy(&v65[28], "-Ou0f>VE0dE", 11);
v65[39] = 23;
v65[40] = 59;
v63[10] = sub_B910BA(v21, &v68[8]);
for ( i9 = 0; i9 < 0x15; ++i9 )
v65[i9 + 20] = (37 * ((unsigned __int8)v65[i9 + 20] - 59) % 127 + 127) % 127;
v42 = sub_B910BA(v21, &v65[20]);
v68[3] = 0;
v68[4] = 63;
v68[5] = 14;
v68[6] = 4;
strcpy(&v68[7], "#x");
v68[10] = 3;
v68[11] = 35;
v68[12] = 109;
v68[13] = 14;
v68[14] = 120;
v68[15] = 106;
v68[16] = 87;
v68[17] = 4;
v68[18] = 68;
v63[18] = v42;
for ( i10 = 0; i10 < 0xF; ++i10 )
v68[i10 + 4] = (12 * ((unsigned __int8)v68[i10 + 4] - 68) % 127 + 127) % 127;
v44 = sub_B910BA(v21, &v68[4]);
v67 = 0;
qmemcpy(v68, ";(K", 3);
v68[3] = 30;
v68[4] = 50;
v68[5] = 12;
v68[6] = 102;
v68[7] = 50;
v68[8] = 99;
v68[9] = 102;
v68[10] = 75;
v68[11] = 124;
v68[12] = 82;
v68[13] = 40;
v68[14] = 82;
v68[15] = 15;
v68[16] = 105;
v63[19] = v44;
for ( i11 = 0; i11 < 0x11; ++i11 )
v68[i11] = (51 * ((unsigned __int8)v68[i11] - 105) % 127 + 127) % 127;
v46 = sub_B910BA(v21, v68);
v68[3] = 0;
v68[4] = 25;
qmemcpy(&v68[5], "qv'<c\\Bqv'<V", 12);
v63[20] = v46;
for ( i12 = 0; i12 < 0xD; ++i12 )
v68[i12 + 4] = (24 * (86 - (unsigned __int8)v68[i12 + 4]) % 127 + 127) % 127;
v48 = sub_B910BA(v21, &v68[4]);
v68[3] = 0;
qmemcpy(&v68[4], "aG @N%Y\vNG| }Y 5", 16);
v63[21] = v48;
for ( i13 = 0; i13 < 0x10; ++i13 )
v68[i13 + 4] = (36 * (53 - (unsigned __int8)v68[i13 + 4]) % 127 + 127) % 127;
v50 = v66;
v51 = sub_B910BA(v21, &v68[4]);
v67 = 0;
v68[0] = 71;
v68[1] = 17;
v68[2] = 63;
v68[3] = 22;
v68[4] = 12;
v68[5] = 97;
v68[6] = 113;
v68[7] = 32;
v68[8] = 113;
v68[9] = 123;
v68[10] = 78;
v68[11] = 113;
v68[12] = 71;
v68[13] = 88;
v68[14] = 27;
v68[15] = 12;
v68[16] = 113;
v68[17] = 2;
v68[18] = 12;
v68[19] = 48;
v63[22] = v51;
for ( i14 = 0; i14 < 0x14; ++i14 )
v68[i14] = (25 * ((unsigned __int8)v68[i14] - 48) % 127 + 127) % 127;
v53 = sub_B910BA(v21, v68);
v68[7] = 0;
v68[8] = 109;
v68[9] = 57;
v68[10] = 104;
v68[11] = 10;
v68[12] = 67;
v68[13] = 29;
qmemcpy(&v68[14], "+xq", 3);
v63[23] = v53;
for ( i15 = 0; i15 < 9; ++i15 )
v68[i15 + 8] = (27 * (113 - (unsigned __int8)v68[i15 + 8]) % 127 + 127) % 127;
v63[11] = sub_B910BA(v50, &v68[8]);
v64 = 0;
qmemcpy(v65, "/&/&j&/&<&\v&<&n&/&7&~&o&~&~&v&", 30);
v65[30] = 20;
v65[31] = 38;
v65[32] = 7;
v65[33] = 38;
v65[34] = 120;
v65[35] = 38;
v65[36] = 7;
v65[37] = 38;
v65[38] = 7;
memset(&v65[39], 38, 3);
for ( i16 = 0; i16 < 0x2A; ++i16 )
v65[i16] = (18 * (38 - (unsigned __int8)v65[i16]) % 127 + 127) % 127;
v56 = ((int (__stdcall *)(_BYTE *, int, int, int, int, int, _DWORD, _DWORD))v63[2])(
v65,
3,
6,
1,
0x4000,
0x4000,
0,
0);
v57 = v56;
v66 = v56;
if ( v56 == -1 || !((int (__stdcall *)(int, _DWORD))v63[3])(v56, 0) )
return -1;
((void (__stdcall *)(int, _BYTE *, int, char *, _DWORD))v63[5])(v57, v71, 1024, v70, 0);
qmemcpy(v62, v63, sizeof(v62));
if ( sub_B9155A(v62[0]) == 1 )
{
v67 = 0;
v68[0] = 20;
v68[1] = 0;
v68[2] = 86;
v68[3] = 16;
qmemcpy(&v68[4], "(#", 2);
v68[6] = 3;
qmemcpy(&v68[7], "(i\\99|iLquu", 11);
for ( i17 = 0; i17 < 0x12; ++i17 )
v68[i17] = (40 * (117 - (unsigned __int8)v68[i17]) % 127 + 127) % 127;
v57 = v66;
v59 = v68;
}
else
{
v65[19] = 0;
v65[20] = 50;
v65[21] = 26;
v65[22] = 53;
v65[23] = 96;
v65[24] = 74;
v65[25] = 68;
v65[26] = 55;
v65[27] = 74;
v65[28] = 68;
v65[29] = 125;
v65[30] = 25;
v65[31] = 111;
v65[32] = 69;
v65[33] = 69;
v65[34] = 124;
v65[35] = 25;
v65[36] = 41;
v65[37] = 60;
v65[38] = 14;
v65[39] = 14;
for ( i18 = 0; i18 < 0x14; ++i18 )
v65[i18 + 20] = (9 * ((unsigned __int8)v65[i18 + 20] - 14) % 127 + 127) % 127;
v59 = &v65[20];
}
((void (__stdcall *)(int, const char *, unsigned int, char *, _DWORD))v63[6])(v57, v59, strlen(v59), v69, 0);
((void (__stdcall *)(int))v63[4])(v57);
((void (__stdcall *)(int))v63[7])(v57);
return 0;
}
```
:::
* Để tránh lan man, mình sẽ debug tới đoạn check flag:

* `main()` ----> `sub_B9155A()` ----> `sub_B9113A()`:
:::spoiler Code
```py
int __stdcall sub_B9113A(int a1)
{
const char *v1; // ecx
unsigned int i; // esi
unsigned int j; // ecx
unsigned int k; // ecx
_BYTE v6[8]; // [esp+Dh] [ebp-877h] BYREF
_BYTE v7[14]; // [esp+15h] [ebp-86Fh] BYREF
_BYTE v8[6]; // [esp+23h] [ebp-861h] BYREF
_BYTE v9[4]; // [esp+29h] [ebp-85Bh] BYREF
_BYTE v10[3]; // [esp+2Dh] [ebp-857h] BYREF
int v11; // [esp+30h] [ebp-854h] BYREF
unsigned int v12; // [esp+34h] [ebp-850h] BYREF
_BYTE v13[4]; // [esp+38h] [ebp-84Ch] BYREF
int v14; // [esp+3Ch] [ebp-848h] BYREF
int v15; // [esp+40h] [ebp-844h] BYREF
int v16; // [esp+44h] [ebp-840h] BYREF
int v17; // [esp+48h] [ebp-83Ch] BYREF
_BYTE v18[1024]; // [esp+4Ch] [ebp-838h] BYREF
_DWORD v19[256]; // [esp+44Ch] [ebp-438h] BYREF
_BYTE v20[32]; // [esp+84Ch] [ebp-38h] BYREF
_BYTE v21[20]; // [esp+86Ch] [ebp-18h] BYREF
v16 = 0;
v17 = 0;
memset(v20, 0, sizeof(v20));
v1 = *(const char **)(a1 + 4);
memset(v21, 0, 17);
v19[0] = 1214720153;
v19[1] = 1707300528;
v19[2] = -1726911327;
v12 = strlen(v1) + 1;
v19[3] = -1537570562;
v19[4] = -1486946960;
v19[5] = -870368618;
v19[6] = 667672498;
v19[7] = -1765573493;
v19[8] = -1168597336;
v19[9] = 325425054;
v19[10] = 1783112709;
v19[11] = 689536444;
memset(&v19[12], 0, 0x3D0u);
dword_B95434 = 520;
dword_B95438 = 26128;
dword_B9543C = 32;
qmemcpy(v6, "qGz", 3);
v6[3] = 20;
v6[4] = 122;
v6[5] = 42;
v6[6] = 70;
v6[7] = 29;
qmemcpy(v7, "@CC* i@V*- \r\r", 13);
v7[13] = 26;
qmemcpy(v8, "Vv9", 3);
for ( i = 0; i < 0x19; ++i )
v6[i] = (40 * (57 - (unsigned __int8)v6[i]) % 127 + 127) % 127;
v9[3] = 0;
qmemcpy(v10, "#jz", sizeof(v10));
for ( j = 0; j < 3; ++j )
v10[j] = (9 * ((unsigned __int8)v10[j] - 122) % 127 + 127) % 127;
v8[5] = 0;
v9[0] = 79;
v9[1] = 12;
v9[2] = 47;
for ( k = 0; k < 3; ++k )
v9[k] = (15 * ((unsigned __int8)v9[k] - 47) % 127 + 127) % 127;
v15 = 33;
v14 = 16;
if ( !(*(int (__stdcall **)(unsigned int, _BYTE *, _DWORD, int, int *))(a1 + 72))(0x80000000, v6, 0, 131097, &v11) )
{
(*(void (__stdcall **)(int, _BYTE *, _DWORD, _BYTE *, _BYTE *, int *))(a1 + 76))(v11, v10, 0, v13, v20, &v15);
(*(void (__stdcall **)(int, _BYTE *, _DWORD, _BYTE *, _BYTE *, int *))(a1 + 76))(v11, v9, 0, v13, v21, &v14);
(*(void (__stdcall **)(int))(a1 + 48))(v11);
}
qmemcpy(&unk_B95440, v20, 0x20u);
if ( (*(int (__stdcall **)(int *, _DWORD, _DWORD, int, int))(a1 + 80))(&v16, 0, 0, 24, -268435456) )
{
if ( (*(int (__stdcall **)(int, int *, int, _DWORD, _DWORD, int *))(a1 + 84))(v16, &dword_B95434, 44, 0, 0, &v17) )
{
if ( (*(int (__stdcall **)(int, int, _BYTE *, _DWORD))(a1 + 88))(v17, 1, v21, 0) )
{
memset(v18, 0, sizeof(v18));
memcpy(v18, *(const void **)(a1 + 4), strlen(*(const char **)(a1 + 4)));
if ( (*(int (__stdcall **)(int, _DWORD, int, _DWORD, _BYTE *, unsigned int *, int))(a1 + 92))(
v17,
0,
1,
0,
v18,
&v12,
1024) )
{
if ( !(*(int (__stdcall **)(_DWORD *, _BYTE *))(a1 + 52))(v19, v18) )
return 1;
}
}
}
}
if ( v17 )
(*(void (__stdcall **)(int))(a1 + 96))(v17);
if ( v16 )
(*(void (__stdcall **)(int, _DWORD))(a1 + 100))(v16, 0);
return 0;
}
```
:::
* Đoạn này:

* ``(a1 + 80)...`` là `advapi32_CryptAcquireContextA`
* `(a1 + 84)...` là `advapi32_CryptImportKey` tạo `key`
* `(a1 + 88)...` là `advapi32_CryptSetKeyParam` tạo `IV`
* `(a1 + 92)...` là `advapi32_CryptEncrypt`
* `(a1 + 52)...` là `kernel32_lstrcmp` copy **flag_encrypt**.
* Tóm lại đoạn này thực hiện mã hóa bằng **AES-256-CBC**, sử dụng **CryptoAPI** (`CryptImportKey`, `CryptSetKeyParam`, `CryptDecrypt`) của **Windows API**.
* Vậy nhặt data rồi decrypt thôi nào. Hẹ hẹ 😶🌫️
### Script
```py
from Crypto.Util.number import *
from pwn import *
from tqdm import tqdm
from hashlib import sha256
from Crypto.Cipher import AES
import string
from gmpy2 import *
import string
from Crypto.Cipher import AES
enc = bytes([
153, 40, 103, 72, 176, 86, 195, 101, 161, 108, 17, 153, 254, 136, 90, 164,
112, 253, 94, 167, 150, 58, 31, 204, 178, 223, 203, 39, 139, 124, 195, 150,
168, 158, 88, 186, 158, 151, 101, 19, 5, 36, 72, 106, 188, 125, 25, 41
])
key = b"1st't_7l@g_u_2h0uldn't_s5bm1t_1t"
iv = bytes([
0x05, 0x17, 0x2B, 0x0C, 0x22, 0x41, 0x0C, 0x2D,
0x0C, 0x22, 0x20, 0x5E, 0x1C, 0x22, 0x2A, 0x1C
])
cipher = AES.new(key, AES.MODE_CBC, iv)
flag = cipher.decrypt(enc)
print(flag)
```
* Lúc đầu thì là `KCSC{C0n9r@tulT/10n2_0n_mak1ng_1t(^.^)}`, nhập thì đúng, nhưng mình thấy nó cứ cấn cấn sao á. `C0n9r@tulT/10n2` trông không giống `Congratulation`. Sau đó mình check lại và ~~thúc đít~~ **chatGPT** thì biết rằng hai số cuối của **IV** là lấy từ **serial number volume** của máy, mà của máy mỗi người thì giá trị này khác nhau. Tức là 2 bytes cuối của `flag` phải ~~bút phọt~~ rùi.
* Mình đoán `flag` là `KCSC{C0n9r@tulat10n2_0n_mak1ng_1t(^.^)}`
hoặc đại loại vậy.
## VEH.exe
### Phân tích
* Đây là 1 bài **flag checker**.
* Load file vào **IDA**, hàm `main()` trông như sau:

* Trông tởm quá, có lẽ chương trình đã obfucate ~~nhè nhẹ~~ 1 chút.
* Mình lướt tiếp xuống xem như nào, thì thấy có vùng đỏ tiếp 
* Mình thử `Disassembly` của `48 89 C6` trên [defuse](https://defuse.ca/online-x86-assembler.htm#disassembly2)

* Sau 1 hồi ~~rất lâu~~ mình mò thì nhận ra 1 điều là có rất nhiều `div rax ~~ div 0` gây `exception` sau đó luôn là opcode `E9`. Có lẽ đây là bytes được dùng để obfucate khiến **IDA** không xác định được luồng để `jmp` chuẩn.
* Vậy mình sẽ viết 1 đoạn code python ~~nho nhỏ~~ để replace bytes `E9` này đi. Tuy nhiên chỉ những bytes mà nằm sau opcode của `div rax` thôi nha, chứ những vị trí khác chưa gây ảnh hưởng.
```py
from Crypto.Util.number import *
from pwn import *
from tqdm import tqdm
from hashlib import sha256
from Crypto.Cipher import AES
import string
from gmpy2 import *
import string
with open("VEH.exe", "rb") as f:
data = f.read()
data_tmp = data.replace(long_to_bytes(0x48F7F0E9),long_to_bytes(0x48F7F090))
with open("VEH.exe", "wb") as f:
f.write(data_tmp)
```

* Đó trông đỡ khiếp ngay. 😶🌫️
* Mình biết được chương trình có khá nhiều `div 0` để raise exception. Vậy mình thử debug để tới từng lệnh `div` đó xem như nào.
* `exception` đầu tiên:

* Hàm `sub_7FF7BD9F2540`:
```py
__int64 sub_7FF7BD9F2540()
{
void (__fastcall *v0)(__int64, __int64 (__fastcall *)()); // rax
__int64 v1; // rdx
unsigned __int128 v2; // rtt
__int64 (__fastcall *v3)(__int64, unsigned __int64, __int64, __int64); // rsi
__int64 result; // rax
sub_7FF7BD9F11F0();
dword_7FF7BD9F5108 = 0;
v0 = (void (__fastcall *)(__int64, __int64 (__fastcall *)()))sub_7FF7BD9F1000(3239761349LL, 341012667LL);
v0(1LL, sub_7FF7BD9F11A0);
dword_7FF7BD9F510C = 0;
*(_QWORD *)&v2 = 0LL;
*((_QWORD *)&v2 + 1) = v1;
v3 = (__int64 (__fastcall *)(__int64, unsigned __int64, __int64, __int64))(v2 / 0);
qword_7FF7BD9F50F8 = v3(4294967286LL, v2 % 0, -474380438LL, 701355107LL);
result = ((__int64 (__fastcall *)(__int64))v3)(4294967285LL);
qword_7FF7BD9F5100 = result;
dword_7FF7BD9F5110 = 0;
return result;
}
```
* Tại hàm này thì mình thấy chỉ có hàm `sub_7FF7BD9F11F0()` là quan trọng:
```py
__int64 sub_7FF7BD9F11F0()
{
void (__fastcall *v0)(_BYTE *); // r14
__int64 i; // rax
unsigned int v2; // edx
__int64 j; // rsi
__int16 v4; // dx
char v5; // di
unsigned int v6; // edx
__int64 k; // rsi
__int64 m; // rsi
__int16 v9; // dx
char v11; // [rsp+28h] [rbp-40h]
_BYTE v12[63]; // [rsp+29h] [rbp-3Fh] BYREF
v0 = (void (__fastcall *)(_BYTE *))sub_7FF7BD9F1000(1102179001LL, 701355107LL);
v11 = 0;
v12[0] = 24;
v12[1] = 1;
v12[2] = 16;
v12[3] = 1;
v12[4] = 122;
v12[5] = 1;
v12[6] = 69;
v12[7] = 1;
v12[8] = 69;
v12[9] = 1;
v12[10] = 109;
v12[11] = 1;
v12[12] = 37;
v12[13] = 1;
v12[14] = 111;
v12[15] = 1;
v12[16] = 111;
v12[17] = 1;
v12[18] = 1;
v12[19] = 1;
for ( i = 1LL; i != 21; ++i )
{
v2 = (unsigned __int16)(31 * (unsigned __int8)v12[i - 1]
+ ((unsigned int)(-32509 * (__int16)(31 * (unsigned __int8)v12[i - 1] - 31)) >> 16)
- 31);
v12[i - 1] = (unsigned __int8)(31 * v12[i - 1] - 127 * ((v2 >> 15) + (v2 >> 6)) - 31 + 127) % 0x7Fu;
}
v0(v12);
v11 = 0;
qmemcpy(v12, ";lcl}lwlhl|lMl\rlklklll", 22);
for ( j = 1LL; j != 23; ++j )
{
v4 = (unsigned __int8)v12[j - 1];
v5 = 19 * v4;
v6 = (unsigned __int16)(19 * v4 + ((unsigned int)(-32509 * (__int16)(19 * v4 - 2052)) >> 16) - 2052);
v12[j - 1] = (unsigned __int8)(v5 - 127 * ((v6 >> 15) + (v6 >> 6)) - 4 + 127) % 0x7Fu;
}
v0(v12);
v11 = 0;
v12[0] = 12;
qmemcpy(&v12[1], "K{K|K KWK.K@K\tKyKhKhKKK", 23);
for ( k = 1LL; k != 25; ++k )
v12[k - 1] = (unsigned __int8)(7 * v12[k - 1]
- 127
* (((unsigned __int16)(7 * (unsigned __int8)v12[k - 1]
+ ((unsigned int)(-32509
* (__int16)(7 * (unsigned __int8)v12[k - 1] - 525)) >> 16)
- 525) >> 15)
+ ((unsigned __int16)(7 * (unsigned __int8)v12[k - 1]
+ ((unsigned int)(-32509
* (__int16)(7 * (unsigned __int8)v12[k - 1] - 525)) >> 16)
- 525) >> 6))
- 13
+ 127)
% 0x7Fu;
v0(v12);
v11 = 0;
v12[0] = 17;
v12[1] = 70;
v12[2] = 8;
v12[3] = 70;
v12[4] = 7;
qmemcpy(&v12[5], "FrF\\F+F.F'F\vF\bF@F@FFF", 21);
for ( m = 1LL; m != 27; ++m )
{
v9 = 18 * (unsigned __int8)v12[m - 1];
v12[m - 1] = (unsigned __int8)(((unsigned __int16)((unsigned int)(33027 * (__int16)(1260 - v9)) >> 16) >> 15)
+ ((unsigned __int16)((unsigned int)(33027 * (__int16)(1260 - v9)) >> 16) >> 6)
- (v9
+ ((((unsigned __int16)((unsigned int)(33027 * (__int16)(1260 - v9)) >> 16) >> 15)
+ (unsigned __int8)((unsigned __int16)((unsigned int)(33027 * (__int16)(1260 - v9)) >> 16) >> 6)) << 7))
- 20
+ 127)
% 0x7Fu;
}
v0(v12);
return 0LL;
}
```
* Trông ~~tởm điên~~, mình thử debug thêm thì phát hiện hàm này có tác dụng resolve khá nhiều. Cụ thể hơn thì:
* `ntdll_RtlAddVectoredExceptionHandler`:

* Theo [tài liệu](https://learn.microsoft.com/en-us/windows/win32/api/errhandlingapi/nf-errhandlingapi-addvectoredexceptionhandler)

* `AddVectoredExceptionHandler` là **Windows API** cho phép đăng ký một hàm xử lý ngoại lệ (`exception handler`) ở cấp hệ thống, được gọi khi xảy ra lỗi như:
* Lỗi truy cập bộ nhớ (Access Violation)
* Lỗi chia cho 0
* Lỗi phần cứng (divide overflow, etc.)
* Nó là một phần của cơ chế xử lý ngoại lệ **vectored** — khác với **SEH** (`__try/__except`), **vectored handler** có phạm vi toàn bộ quá trình.
* Tham số:
* `First`: Nếu là **TRUE (1)**, `handler` được thêm vào đầu danh sách (ưu tiên cao). Nếu **FALSE (0)**, thêm vào cuối.
* `Handler`: Con trỏ tới hàm xử lý kiểu **LONG CALLBACK Handler**(`EXCEPTION_POINTERS *ExceptionInfo`)
* `ntdll.dll`:

* `USER32.dll`:

* `CRYPT32.DLL`:

* `Advapi32.dll`:

* Tiếp sau đó, thì khi mà chương trình xảy ra `exception`, chương trình sẽ redirect sang 1 handle để xử lý, ở đây thì là hàm `sub_7FF7BD9F11A0()`:

* À còn hàm `sub_7FF7BD9F1000()` thì:
```py
__int64 __fastcall sub_7FF7BD9F1000(int a1, int a2)
{
struct _PEB_LDR_DATA *Ldr; // r8
struct _LIST_ENTRY *Flink; // r9
struct _LIST_ENTRY *p_InMemoryOrderModuleList; // r8
__int64 result; // rax
struct _LIST_ENTRY *v6; // r10
__int16 v7; // di
unsigned __int64 v8; // rsi
char v9; // bp
unsigned __int64 v10; // rbp
unsigned __int8 v12; // bl
int v13; // ebp
unsigned __int8 *v14; // rdi
struct _LIST_ENTRY *v15; // r11
__int64 v16; // rbp
unsigned __int64 v17; // r12
__int64 v18; // r14
__int64 v19; // r13
__int64 v20; // r15
bool v21; // bp
__int64 v22; // rdi
__int64 v23; // rsi
unsigned __int8 v24; // bl
unsigned __int8 *v25; // rsi
int v26; // r10d
unsigned __int8 v27; // [rsp+0h] [rbp-88h]
char v28; // [rsp+1h] [rbp-87h] BYREF
Ldr = NtCurrentTeb()->ProcessEnvironmentBlock->Ldr;
Flink = Ldr->InMemoryOrderModuleList.Flink;
p_InMemoryOrderModuleList = &Ldr->InMemoryOrderModuleList;
result = 0LL;
while ( 1 )
{
if ( Flink == p_InMemoryOrderModuleList )
return result;
if ( LOWORD(Flink[4].Blink) <= 0x3Eu )
{
v6 = Flink[5].Flink;
v7 = (__int16)v6->Flink;
if ( LOWORD(v6->Flink) )
{
v8 = 0LL;
do
{
v9 = v7 - 32;
if ( (unsigned __int8)(v7 - 97) >= 0x1Au )
v9 = v7;
*(&v27 + v8) = v9;
v10 = v8 + 1;
v7 = *((_WORD *)&v6->Flink + v8 + 1);
if ( !v7 )
break;
}
while ( v8++ < 0x3E );
}
else
{
v10 = 0LL;
}
*(&v27 + v10) = 0;
v12 = v27;
v13 = -2128831035;
if ( v27 )
{
v14 = (unsigned __int8 *)&v28;
do
{
v13 = 16777619 * (v13 ^ v12);
v12 = *v14++;
}
while ( v12 );
}
if ( v13 == a2 )
{
v15 = Flink[2].Flink;
v16 = *(int *)((char *)&v15[8].Blink + SHIDWORD(v15[3].Blink));
if ( *(_DWORD *)((char *)&v15[2].Flink + v16) )
{
v17 = *(unsigned int *)((char *)&v15[1].Blink + v16);
if ( *(_DWORD *)((char *)&v15[1].Blink + v16) )
break;
}
}
}
LABEL_2:
Flink = Flink->Flink;
}
v18 = *(int *)((char *)&v15[2].Flink + v16 + 4);
v19 = (__int64)v15 + *(int *)((char *)&v15[2].Flink + v16);
v20 = *(int *)((char *)&v15[1].Blink + v16 + 4);
v21 = 1;
v22 = 0LL;
while ( 1 )
{
v23 = *(int *)(v19 + 4 * v22);
v24 = *((_BYTE *)&v15->Flink + v23);
if ( v24 )
break;
if ( a1 == -2128831035 )
return (__int64)v15
+ *(int *)((char *)&v15->Flink
+ 4 * *(unsigned __int16 *)((char *)&v15->Flink + 2 * (unsigned int)v22 + v18)
+ v20);
LABEL_21:
v21 = ++v22 < v17;
if ( v22 == v17 )
goto LABEL_2;
}
v25 = (unsigned __int8 *)&v15->Flink + v23 + 1;
v26 = -2128831035;
do
{
v26 = 16777619 * (v26 ^ v24);
v24 = *v25++;
}
while ( v24 );
if ( v26 != a1 )
goto LABEL_21;
if ( !v21 )
goto LABEL_2;
return (__int64)v15
+ *(int *)((char *)&v15->Flink
+ 4 * *(unsigned __int16 *)((char *)&v15->Flink + 2 * (unsigned int)v22 + v18)
+ v20);
}
```
* Nhìn thì cũng thấy rằng hàm này tác dụng là **resolve API**, kỹ hơn nữa thì nó là resolve function address bằng **FNV-1a** hash. Hay là kỹ thuật **resolve API** bằng **API hashing** sử dụng **FNV-1a** hash.
* Vậy mình đổi tên hàm đó là `sub_7FF7BD9F1000()` ----> `ResolveAPI_FNV1a()`.
* Quay lại với hàm `sub_7FF7BD9F11A0()`. Trông thì chưa rõ nó làm gì, mình stuck khá lâu ở đoạn này.
* Nhớ ra là `AddVectoredExceptionHandler` khi xảy ra exception sẽ gọi `handler` trỏ tới hàm xử lý kiểu **LONG CALLBACK Handler**(`EXCEPTION_POINTERS *ExceptionInfo`). `Handler` đó chính là [PVECTORED_EXCEPTION_HANDLER](https://learn.microsoft.com/en-us/windows/win32/api/winnt/nc-winnt-pvectored_exception_handler)

* Sau 1 hồi ~~thúc đít~~ **chatGPT** thì mình hiểu như này: **IDA** không biết hàm `sub_7FF7BD9F11A0()` là một **vectored exception handler** (`VEH`). Nên khi thấy truy cập kiểu ``*(_QWORD *)(a1 + 8)`` → **IDA** chỉ biết "à, offset $8$ khỏi `a1`, không biết là **struct** gì". Đại loại vậy. Nên các `field` khá mù mờ mà tối nghĩa. Và **chatGPT** recommend mình convert biến `a1` sang kiểu struct `_EXCEPTION_POINTERS`. 😶🌫️
* Cách convert xem ở [đây](https://hshrzd.wordpress.com/2022/02/09/ida-tips-how-to-use-a-custom-structure/#:~:text=Adding%20the%20structure%20into%20IDA&text=1%20%E2%80%93%20First%20we%20need%20to,paste%20there%20our%20custom%20structure.) nha.
* Hoặc không thì như mình: `View` --> `Opensubviews` --> `Local Type`. Sau đó chuột phải chọn `Add Type`.


* Do kiến trúc file là 64 bit, nên 2 con trỏ 64-bit → mỗi con trỏ chiếm 8 bytes --> Tổng struct là 16 bytes hay là `0x10` nha.
* Sau đó nhấn `Y` tại tên hàm `sub_7FF7BD9F11A0()`, 
* Thay prototype
`__int64 __fastcall sub_7FF7BD9F11A0(struct _EXCEPTION_POINTERS* a1)
`
* Còn nếu dùng `typedef`:
`__int64 __fastcall sub_7FF7BD9F11A0(PEXCEPTION_POINTERS a1)
`

* Ú òa, đẹp chưa này:

* Như tại [PVECTORED_EXCEPTION_HANDLER](https://learn.microsoft.com/en-us/windows/win32/api/winnt/nc-winnt-pvectored_exception_handler) nói:

* Tại trường hợp của mình là `return 0xFFFFFFFFLL;`, là tiếp tục thực thi từ vị trí xảy ra `exception (RIP)`.
* Thông thường, việc tăng `RIP/EIP` sau khi xử lý `exception` là điều bắt buộc nếu muốn tiếp tục thực thi, vì mặc định **OS** sẽ giữ nguyên `RIP` tại vị trí gây lỗi. Nếu không thay đổi nó, chương trình sẽ rơi vào `infinite exception loop`.
* Và việc tăng bao nhiêu thì phụ thuộc vào độ dài lệnh máy (`instruction`) gây ra `exception`. Nếu tăng lung tung thì vẫn nhảy vào giữa opcode ----> crash tiếp.
* Cụ thể với trường hợp của mình, thì là ~~div 0~~, ví dụ mình lấy tại hàm `sub_7FF7C8FA2540()` nhé:

* Lệnh `nop` này thực chất chính là `E9` mà lúc đầu mình đã thực hiện patch nó. Tại `div 0` (opcode là `48 F7 F0`), thì sau khi raise exception, `rip + 4` sẽ làm cho chương trình nhảy vào lệnh ngay sau lệnh `nop`. Vậy rõ ràng mục đích ở đây là bypass/skip qua exception và lệnh `nop` - đúng hơn là lệnh rác cũ `E9`.
* Được rồi, đến đây thì khá là clean và hiểu được chắc là gần hết ý đồ của chương trình, mình công nhân trace theo từng lệnh `div 0` thì `input` - có thể là `flag` luôn, mã hóa **AES** mode **CBC** và so sánh với 1 mảng trong chương trình.
* Hàm `sub_7FF7BD9F1640()`: Tạo key.
:::spoiler Code
```py
__int64 __fastcall sub_7FF7BD9F1640(__int64 a1, __int64 a2)
{
unsigned __int128 v2; // rtt
__int64 v3; // rdx
unsigned __int64 v4; // rax
__int64 i; // r8
__int64 v6; // rdx
unsigned __int64 v7; // rdx
unsigned __int128 v8; // rtt
unsigned __int64 v9; // r14
unsigned __int64 v10; // rdx
void (__fastcall *v11)(__int64); // rdi
unsigned __int64 v12; // rax
unsigned __int64 v13; // rdx
__int64 v14; // rbx
unsigned __int64 v15; // rax
unsigned __int64 v16; // rdx
unsigned __int128 v17; // rtt
unsigned __int64 v18; // rax
unsigned __int64 v19; // rdx
unsigned __int128 v20; // rtt
unsigned int v21; // ebx
unsigned __int64 v22; // rax
unsigned __int64 v23; // rdx
unsigned __int128 v24; // rtt
unsigned __int128 v25; // rtt
unsigned __int64 v26; // rsi
__int64 v27; // rax
__int64 v28; // rcx
char genkey[8]; // [rsp+30h] [rbp-C8h] BYREF
char genkey_12; // [rsp+3Ch] [rbp-BCh]
char genkey_13; // [rsp+3Dh] [rbp-BBh]
char genkey_14; // [rsp+3Eh] [rbp-BAh]
char genkey_15; // [rsp+3Fh] [rbp-B9h]
char genkey_16; // [rsp+40h] [rbp-B8h]
char genkey_17; // [rsp+41h] [rbp-B7h]
char genkey_18; // [rsp+42h] [rbp-B6h]
char genkey_19; // [rsp+43h] [rbp-B5h]
char genkey_20; // [rsp+44h] [rbp-B4h]
char genkey_21; // [rsp+45h] [rbp-B3h]
char genkey_22; // [rsp+46h] [rbp-B2h]
char genkey_23; // [rsp+47h] [rbp-B1h]
char genkey_24; // [rsp+48h] [rbp-B0h]
char genkey_25; // [rsp+49h] [rbp-AFh]
char genkey_26; // [rsp+4Ah] [rbp-AEh]
char genkey_27; // [rsp+4Bh] [rbp-ADh]
char genkey_28; // [rsp+4Ch] [rbp-ACh]
char genkey_29; // [rsp+4Dh] [rbp-ABh]
char genkey_30; // [rsp+4Eh] [rbp-AAh]
char genkey_31; // [rsp+4Fh] [rbp-A9h]
_BYTE genkey_32[14]; // [rsp+50h] [rbp-A8h] BYREF
__int64 v52; // [rsp+60h] [rbp-98h] BYREF
int v53; // [rsp+6Ch] [rbp-8Ch] BYREF
_BYTE v54[136]; // [rsp+70h] [rbp-88h] BYREF
*(_QWORD *)&v2 = 0LL;
*((_QWORD *)&v2 + 1) = a2;
((void (__fastcall *)(_BYTE *, _QWORD, __int64, __int64))(v2 / 0))(v54, 0LL, 100LL, 341012667LL);
*(_QWORD *)&v2 = 0LL;
*((_QWORD *)&v2 + 1) = v3;
v4 = v2 / 0;
genkey[0] = 0;
qmemcpy(&genkey[1], "@UUNs/{{zzz", 11);
genkey_12 = 26;
genkey_13 = 62;
genkey_14 = 108;
genkey_15 = 55;
genkey_16 = 85;
genkey_17 = 55;
genkey_18 = 117;
genkey_19 = 27;
genkey_20 = 26;
genkey_21 = 87;
genkey_22 = 108;
genkey_23 = 41;
genkey_24 = 123;
genkey_25 = 122;
genkey_26 = 20;
genkey_27 = 85;
genkey_28 = 87;
genkey_29 = 64;
genkey_30 = 24;
genkey_31 = 25;
qmemcpy(genkey_32, "T9wzdzMB^$Ww\t\t", sizeof(genkey_32));
for ( i = 1LL; i != 46; ++i )
genkey[i] = (unsigned __int8)(-127
* (((unsigned __int16)(55 * (unsigned __int8)genkey[i]
+ ((unsigned int)(-32509
* (__int16)(55 * (unsigned __int8)genkey[i] - 495)) >> 16)
- 495) >> 15)
+ ((unsigned __int16)(55 * (unsigned __int8)genkey[i]
+ ((unsigned int)(-32509
* (__int16)(55 * (unsigned __int8)genkey[i] - 495)) >> 16)
- 495) >> 6))
+ 55 * genkey[i]
- 112)
% 0x7Fu;
((void (__fastcall *)(_BYTE *, char *, __int64, __int64))v4)(v54, &genkey[1], 44LL, 341012667LL);
*(_QWORD *)genkey = 0LL;
v52 = 0LL;
*(_QWORD *)&v8 = 0LL;
*((_QWORD *)&v8 + 1) = v6;
v7 = v8 % 0;
v9 = v8 / 0;
*(_QWORD *)&v8 = 0LL;
*((_QWORD *)&v8 + 1) = v7;
v10 = v8 % 0;
v11 = (void (__fastcall *)(__int64))(v8 / 0);
*(_QWORD *)&v8 = 0LL;
*((_QWORD *)&v8 + 1) = v10;
v12 = v8 / 0;
v13 = v8 % 0;
if ( !v12
|| (v14 = 0LL,
((unsigned int (__fastcall *)(char *, _QWORD, _QWORD, __int64, int))v12)(genkey, 0LL, 0LL, 24LL, -268435456)) )
{
*(_QWORD *)&v17 = 0LL;
*((_QWORD *)&v17 + 1) = v13;
v15 = v17 / 0;
v16 = v17 % 0;
if ( !v15
|| ((unsigned int (__fastcall *)(_QWORD, __int64, _QWORD, _QWORD, __int64 *))v15)(
*(_QWORD *)genkey,
32780LL,
0LL,
0LL,
&v52) )
{
*(_QWORD *)&v20 = 0LL;
*((_QWORD *)&v20 + 1) = v16;
v18 = v20 / 0;
v19 = v20 % 0;
if ( v18 )
v21 = ((__int64 (__fastcall *)(_BYTE *, unsigned __int64, __int64, __int64))v18)(
v54,
v19,
-1724985304LL,
701355107LL);
else
v21 = 0;
*(_QWORD *)&v24 = 0LL;
*((_QWORD *)&v24 + 1) = v19;
v22 = v24 / 0;
v23 = v24 % 0;
if ( !v22 || ((unsigned int (__fastcall *)(__int64, _BYTE *, _QWORD, _QWORD))v22)(v52, v54, v21, 0LL) )
{
*(_QWORD *)&v25 = 0LL;
*((_QWORD *)&v25 + 1) = v23;
v26 = v25 / 0;
v27 = sub_7FF7BD9F25C8(0x21uLL);
v14 = v27;
v53 = 32;
if ( v26
&& !((unsigned int (__fastcall *)(__int64, __int64, __int64, int *, _DWORD))v26)(v52, 2LL, v27, &v53, 0) )
{
v14 = 0LL;
}
v11(v52);
v28 = *(_QWORD *)genkey;
goto LABEL_18;
}
v11(v52);
}
v28 = *(_QWORD *)genkey;
v14 = 0LL;
LABEL_18:
((void (__fastcall *)(__int64, _QWORD))v9)(v28, 0LL);
}
return v14;
}
```
:::
* Hàm sẽ sử dụng **CryptoAPI** của **Windows** tạo `key` nha.
* Cụ thể hơn thì gán data = `https://www.youtube.com/watch?v=dQw4w9WgXcQ` qua hàm [RtlCopyMemory](https://learn.microsoft.com/en-us/windows-hardware/drivers/ddi/wdm/nf-wdm-rtlcopymemory), tạo context qua [CryptAcquireContext](https://learn.microsoft.com/en-us/windows/win32/api/wincrypt/nf-wincrypt-cryptacquirecontexta), với `dwProvType = 0x18` thì là `PROV_RSA_AES` từ [Cryptographic Provider Types](https://learn.microsoft.com/en-us/windows/win32/SecCrypto/cryptographic-provider-types). Sau đó khởi tạo đối tượng băm qua hàm [CryptCreateHash](https://learn.microsoft.com/en-us/windows/win32/api/wincrypt/nf-wincrypt-cryptcreatehash), với `ALG_ID = 0x800c` là `CALG_SHA_256` theo [ALG_ID](https://learn.microsoft.com/en-us/windows/win32/seccrypto/alg-id), hoàn thành xong thì gọi [CryptHashData](https://learn.microsoft.com/en-us/windows/win32/api/wincrypt/nf-wincrypt-crypthashdata) để băm data. Lấy giá trị băm vừa rồi qua hàm [CryptGetHashParam](https://learn.microsoft.com/en-us/windows/win32/api/wincrypt/nf-wincrypt-cryptgethashparam) và cuối cùng là giải phóng tài nguyên bằng [CryptDestroyHash](https://learn.microsoft.com/en-us/windows/win32/api/wincrypt/nf-wincrypt-cryptdestroyhash) và [CryptReleaseContext](https://learn.microsoft.com/en-us/windows/win32/api/wincrypt/nf-wincrypt-cryptreleasecontext).
* **IV**:

* **Flag encrypted**:

* Có các tham số của **AES-CBC** rồi, tiến hành decrypt thôi.
### Script
```py
from Crypto.Util.number import *
from pwn import *
from tqdm import tqdm
from hashlib import sha256
from Crypto.Cipher import AES
import string
from gmpy2 import *
import string
flag_enc = long_to_bytes(0xe560440942c4bbdef6a12d93d91d1372af8d4cf7a79f1fb999689cb8c24c4f85)
iv = long_to_bytes(0x0102030405060708090a0b0c0d0e0f10)
key = long_to_bytes(0x0424974c68530290458c8d58674e2637f65abc127057957d7b3acbd24c208f93)
cipher = AES.new(key, AES.MODE_CBC, iv)
flag = cipher.decrypt(flag_enc)
print(flag)
# b'KMACTF{3Xc3pTI0n_3v3rYwh3R3@_@}\x01'
```
* **Flag: ~~KMACTF{3Xc3pTI0n_3v3rYwh3R3@_@}~~**

## FlagChecker.exe
### Phân tích
* Cho vào **DIE**:

* Từ đây ta có thể thấy file đã bị pack bằng **UPX**.
* Tải **UPX** trên **WSL**:`sudo apt install upx-ucl`
* Chạy lệnh để unpack: `upx -d <file>.exe`
* Unpack xong thì file trông như này:

* Trông bài khá lạ, mình chưa có ý tưởng gì, chính xác hơn thì mình chưa từng gặp thể loại bài như này bao giờ. Nên không còn cách nào khác, mình phải tìm xem có gì có thể hint được không.
* Thực ra lúc đầu mình tính xem hết các function, tuy nhiên có tận 2500+ function :)). Cũng chả dại dột gì mà mất công vậy, do bài này có **GUI**:

Nên mình sẽ thử tìm các **API** như `MessageBoxA()` rồi **xref** xem, thì thấy có 1 cái này lạ lạ:

* Kết hợp với thông tin mà **DIE** đưa ra ở phía trên, thì có lẽ bài này được code bằng **AutoIT**.
#### AutoIT
##### Khái niệm
* **AutoIt** là một ngôn ngữ scripting dùng để tự động hóa **GUI** và tác vụ trong **Windows**, tương tự như macro.
* Tự động nhấn chuột, nhập phím.
* Tự động thao tác GUI (như mở cửa sổ, bấm nút, v.v).
* Dễ dàng tạo script giả lập hành vi người dùng.
* Tập tin **AutoIt** script thường có đuôi `.au3` và có thể được biên dịch thành file thực thi (`.exe`).
* Đặc điểm nhận biết file **AutoIt**: Trong file thực thi `.exe`, thường thấy chuỗi như `AU3!EA06`, `AUTOIT`, hay `This is a compiled AutoIt script`. Hoặc sử dụng công cụ như **Detect It Easy (DIE)** hoặc **PEiD**.
##### Tại sao AutoIt lại quan trọng trong reverse?
* Nhiều phần mềm (hoặc malware) được viết bằng **AutoIt** để tạo **GUI** đơn giản, tự động hóa các hành động như nhập `key`, kiểm tra môi trường,...
* **AutoIt** dễ bị dùng cho **obfuscation** hoặc **dropper** trong malware (tức là shell chính được viết bằng **AutoIt**, và tải `payload` thật khi chạy).
* Khi reverse, bạn sẽ thấy file **PE** có kích thước nhỏ, nhưng thực chất là runtime interpreter cho mã **AutoIt** đã bị `packed/encrypted` bên trong.
* Nhiều malware dùng **AutoIt** để:
* Kiểm tra hệ thống ảo hóa.
* Thực hiện `persistence`.
* Decrypt payload `.NET` hoặc `shellcode`. ----> (`shellcode loader`)
* Viết dropper nhỏ, dễ vượt `AV`. ----> (`malware dropper`)
* Đó, sơ qua về **AutoIT** mình tìm hiểu thì có vậy, thế thì có lẽ phải đi kiếm tools để decompile nó. Mình có tìm kiếm 1 chút thì thấy [Exe2Aut](https://exe2aut.com/exe2aut-converter/) có vẻ tín. Nhưng có vẻ `Exe2Aut` chỉ decompile được file 32 bit, còn 64 bit thì mình thấy có **MyAutToExe**, à cái này cũng hỗ trợ 32 bit nữa.😶🌫️
* Sau khi decompile ra được file `.au3` thì mình có code 16380 dòng.
* Đầu tiên ta có hàm `check()`, có thể dùng để check `input` với `flag`:
```py
FUNC CHECKER()
LOCAL $INPUT = GUICTRLREAD($IFLAG)
LOCAL $LEN_INPUT = STRINGLEN($INPUT)
LOCAL $OPCODE = "0x558bec83ec6c8d45d850e8aa05000083c4048d4d94518d55d8" & "52e8cb03000083c408e80c0000007573657233322e646c6c00" & "00ff55d88945f8837df8007505e9fb000000e80c0000004d65" & "7373616765426f7841008b45f850e8b306000083c4088945f0" & "8b4d0851ff55e883f81c740b8b550cc60200e9c4000000c645" & "bcf8c645bd50c645beccc645bfefc645c0e6c645c13cc645c2" & "35c645c396c645c41dc645c561c645c6aec645c7c0c645c8c5" & "c645c931c645cacec645cbb0c645cce7c645cd1dc645ceedc6" & "45cfbcc645d05dc645d181c645d269c645d38ac645d435c645" & "d574c645d657c645d7b68b4508508d4d94518d55d852e84700" & "000083c40c8945f4c745fc00000000eb098b45fc83c0018945" & "fc837dfc1c7d1f8b4df4034dfc0fb6118b45fc0fb64c05bc3b" & "d174088b550cc60200eb08ebd28b450cc600018be55dc3558b" & "ec83ec445657b90b000000e82c00000068747470733a2f2f77" & "77772e796f75747562652e636f6d2f77617463683f763d6451" & "773477395767586351005e8d7dbcf3a5c745f800000000c745" & "f400000000c745fc000000008b4510508b4d088b5110ffd289" & "45ec6a006a016a006a008d45f8508b4d0c8b11ffd285c07507" & "33c0e91b0200008d45f4506a006a0068048000008b4df8518b" & "550c8b420cffd085c075156a008b4df8518b550c8b4224ffd0" & "33c0e9e9010000837df40075156a008b4df8518b550c8b4224" & "ffd033c0e9ce0100006a008d4dbc518b55088b4210ffd0508d" & "4dbc518b55f4528b450c8b4808ffd185c075218b55f4528b45" & "0c8b481cffd16a008b55f8528b450c8b4824ffd133c0e98a01" & "00008d55fc526a008b45f45068016800008b4df8518b550c8b" & "4214ffd085c075218b4df4518b550c8b421cffd06a008b4df8" & "518b550c8b4224ffd033c0e94a0100008b4df4518b550c8b42" & "1cffd06a008d4dec516a006a006a016a008b55fc528b450c8b" & "4810ffd185c07527837dfc0074218b55fc528b450c8b4820ff" & "d16a008b55f8528b450c8b4824ffd133c0e9f90000006a0468" & "001000008b55ec83c201526a008b45088b4808ffd18945e883" & "7de8007527837dfc0074218b55fc528b450c8b4820ffd16a00" & "8b55f8528b450c8b4824ffd133c0e9b10000008b55ec83c201" & "528b45e850e8cc06000083c408c745f000000000eb098b4df0" & "83c101894df08b55f03b55ec73128b45e80345f08b4d10034d" & "f08a118810ebdd8b4510508b4d088b5110ffd2508d45ec508b" & "4de8516a006a016a008b55fc528b450c8b4810ffd185c07524" & "837dfc00741e8b55fc528b450c8b4820ffd16a008b55f8528b" & "450c8b4824ffd133c0eb23837dfc00741a8b55fc528b450c8b" & "4820ffd16a008b55f8528b450c8b4824ffd18b45e85f5e8be5" & "5dc3558bec51e81000000061647661706933322e646c6c0000" & "00008b45088b08ffd18945fc837dfc00750732c0e99b010000" & "e818000000437279707441637175697265436f6e7465787441" & "000000008b55fc52e8d102000083c4088b4d0c8901e8100000" & "00437279707443726561746548617368008b55fc52e8ab0200" & "0083c4088b4d0c89410ce8100000004372797074496d706f72" & "744b657900008b55fc52e88402000083c4088b4d0c894104e8" & "1000000043727970744465726976654b657900008b55fc52e8" & "5d02000083c4088b4d0c894114e81000000043727970744861" & "7368446174610000008b55fc52e83602000083c4088b4d0c89" & "4108e8100000004372797074456e6372797074000000008b55" & "fc52e80f02000083c4088b4d0c894110e81400000043727970" & "7447657448617368506172616d0000008b55fc52e8e4010000" & "83c4088b4d0c894118e814000000437279707444657374726f" & "7948617368000000008b55fc52e8b901000083c4088b4d0c89" & "411ce810000000437279707444657374726f794b6579008b55" & "fc52e89201000083c4088b4d0c894120e81400000043727970" & "7452656c65617365436f6e74657874008b55fc52e867010000" & "83c4088b4d0c894124b0018be55dc3558bec83ec18e81c0000" & "006b00650072006e0065006c00330032002e0064006c006c00" & "00000000e88602000083c4048945fc837dfc00750732c0e915" & "010000e8100000004c6f61644c69627261727941000000008b" & "45fc50e8fb00000083c4088945f8837df800750732c0e9e400" & "0000e81000000047657450726f634164647265737300008b4d" & "fc51e8ca00000083c4088945f4837df400750732c0e9b30000" & "00e8100000005669727475616c416c6c6f63000000008b55fc" & "52e89900000083c4088945f0837df000750732c0e982000000" & "e80c0000005669727475616c46726565008b45fc50e86c0000" & "0083c4088945ec837dec00750432c0eb58e80c0000006c7374" & "726c656e41000000008b4dfc51e84200000083c4088945e883" & "7de800750432c0eb2e8b55088b45f889028b4d088b55f48951" & "048b45088b4df08948088b55088b45ec89420c8b4d088b55e8" & "895110b0018be55dc3558bec83ec3c8b45088945ec8b4dec0f" & "b71181fa4d5a0000740733c0e9350100008b45ec8b4d080348" & "3c894de4ba080000006bc2008b4de48d5401788955e88b45e8" & "833800750733c0e9080100008b4de88b118955e08b45e00345" & "088945f48b4df48b51188955dc8b45f48b481c894dd08b55f4" & "8b42208945d88b4df48b51248955d4c745f800000000eb098b" & "45f883c0018945f88b4df83b4ddc0f83b30000008b55080355" & "d88b45f88d0c82894dc88b55080355d48b45f88d0c42894dcc" & "8b55080355d08b45cc0fb7088d148a8955c48b45c88b4d0803" & "08894df0c745fc00000000c745fc00000000eb098b55fc83c2" & "018955fc8b450c0345fc0fbe0885c974278b55f00355fc0fbe" & "0285c0741a8b4d0c034dfc0fbe118b45f00345fc0fbe083bd1" & "7402eb02ebc38b550c0355fc0fbe0285c075198b4df0034dfc" & "0fbe1185d2750c8b45c48b4d0803088bc1eb07e938ffffff33" & "c08be55dc3558bec83ec34c745e40000000064a13000000089" & "45e48b4de48b510c8955d88b45d88b480c8b5010894dcc8955" & "d08b45cc8945d48b4dd4894de8837de8000f845a0100008b55" & "e8837a18000f844d0100008b45e8837830007502ebde8b4de8" & "8b51308955ecc745f000000000c745f000000000eb098b45f0" & "83c0018945f08b4df08b55080fb7044a85c00f84dd0000008b" & "4df08b55ec0fb7044a85c00f84cb0000008b4df08b55080fb7" & "044a83f85a7f378b4df08b55080fb7044a83f8417c288b4df0" & "8b55080fb7044a83c0208945e08b4df08b5508668b45e06689" & "044a668b4de066894dfeeb0e8b55f08b4508668b0c5066894d" & "fe668b55fe668955f88b45f08b4dec0fb7144183fa5a7f378b" & "45f08b4dec0fb7144183fa417c288b45f08b4dec0fb7144183" & "c2208955dc8b45f08b4dec668b55dc66891441668b45dc6689" & "45fceb0e8b4df08b55ec668b044a668945fc668b4dfc66894d" & "f40fb755f80fb745f43bd07402eb05e908ffffff8b4df08b55" & "080fb7044a85c075168b4df08b55ec0fb7044a85c075088b4d" & "e88b4118eb0f8b55e88b028945e8e99cfeffff33c08be55dc3" & "558bec518b45088945fc837d0c00741a8b4dfcc601008b55fc" & "83c2018955fc8b450c83e80189450cebe08b45088be55dc300" & "00000000000000000000000000000000000000000000000000" & "00000000000000000000000000000000000000000000000000" & "00000000000000000000000000000000000000000000000000" & "00000000000000000000000000000000000000000000000000" & "00000000000000000000000000000000000000000000000000" & "00000000000000000000000000000000000000000000000000" & "00000000000000000000000000000000000000000000000000" & "00000000000000000000000000000000000000000000000000" & "00000000000000000000000000000000000000000000000000" & "00000000000000000000000000000000000000000000000000" & "00000000000000000000000000000000000000000000000000" & "00000000000000000000000000000000000000000000000000" & "00000000000000000000000000000000000000000000000000" & "00000000000000000000000000000000000000000000000000" & "00000000000000000000000000000000000000000000000000" & "00000000000000000000000000000000000000000000000000" & "00000000000000000000000000000000000000000000000000" & "00000000000000000000000000000000000000000000"
LOCAL $OPCODE_BUF = DLLSTRUCTCREATE("byte[" & BINARYLEN($OPCODE) & "]")
DLLSTRUCTSETDATA($OPCODE_BUF, 1, BINARY($OPCODE))
LOCAL $INPUT_BUF = DLLSTRUCTCREATE("byte[" & BINARYLEN($INPUT) + 1 & "]")
DLLSTRUCTSETDATA($INPUT_BUF, 1, BINARY($INPUT))
LOCAL $IS_FLAG = DLLSTRUCTCREATE("byte[1]")
DLLSTRUCTSETDATA($IS_FLAG, 1, BINARY("0x00"))
DLLCALL("user32.dll", "none", "CallWindowProcA", "ptr", DLLSTRUCTGETPTR($OPCODE_BUF), "ptr", DLLSTRUCTGETPTR($INPUT_BUF), "ptr", DLLSTRUCTGETPTR($IS_FLAG), "int", 0, "int", 0)
IF DLLSTRUCTGETDATA($IS_FLAG, 1) == "0x01" THEN
MSGBOX(0, "", "Correct!")
ELSE
MSGBOX($MB_ICONERROR, "", "Incorrect!")
ENDIF
ENDFUNC
```
* Đây có thể là một kỹ thuật thường gặp trong `obfuscation` hoặc `crackme`:
* **Shellcode** chứa trong biến `$OPCODE` (gần 20.000 byte).
* Nó được nạp vào bộ nhớ, rồi gọi thực thi bằng `CallWindowProcA` như một đoạn mã máy.
* Dữ liệu truyền vào **shellcode** là:
* `$INPUT_BUF`: chuỗi người dùng nhập
* `$IS_FLAG`: 1 byte dùng làm kết quả đầu ra (`0x01` = đúng flag, `0x00` = sai)
* Sau khi **shellcode** chạy xong, nếu `$IS_FLAG = 0x01` thì **"Correct!"** được hiện ra.
* Do mình mới chỉ xem mỗi hàm này, và đoán rằng con **shellcode** này sẽ thực hiện việc chính là check `input`. Và `DllCall` gọi **API** `CallWindowProcA` để thực thi **shellcode** check flag. Ý mình là như mình giới thiệu thì code có tận `16380` dòng, chưa phân tích thêm xem còn gì liên quan nữa không, nhưng mà tác dụng của con **shellcode** này thì chắc chắn 99% là quan trọng. Vậy mình ưu tiên phân tích nó, nếu cụt đường thì quay lại thui 🤣
* Lúc đầu mình có lấy thử 1,2 giá trị của mảng **OPCODE**, đem lên [defuse](https://defuse.ca/online-x86-assembler.htm#disassembly2) thì nó là mã máy thật. À không cái này mình kiểm tra cho chắc thôi :)) Nhưng mà **defuse** thì nó không nhận được **opcode** khá dài, nên nhặt từng cái 1 thì công nhân quá. Tiếp theo thì mình ~~thúc đít~~ **chatGPT** nó viết code chuyển từ **OPCODE** sang **shellcode**, nhưng mà trông không khả khi vì cho vào **IDA** trông nó bùi nhùi hỗn độn với lỗi địa chỉ tùm lum. Stuck quá nhưng mình nhận ra là code gọi **API** `CallWindowProcA()` để thực thi **shell**, vậy debug lại file `.exe` biết đâu nhặt được **shell** đỡ tốn sức hơn.
* Được rồi, vào debug thôi. Lỗi đầu tiên mình gặp:

* Tìm tới thì mình thấy có cơ chế anti debug:

* Vậy chắc phải bypass cái này rồi. Patch opcode `0F 85 61 F2 03 00` thành `E9 01 00 00 00 90` nha.
* Sau đó tìm tới **API** `CallWindowProcA()`:

* Phần này phải vào debug chứ không đọc tĩnh mà tìm được, vì có thể **AutoIT** compile khác bình thường. ~~Thực ra mình đọc tĩnh không tìm được `CallWindowProcA()` nên có khả năng là như vậy~~
* Nhìn phần **Modules**:


* Đến đoạn này, mình không biết là do máy mình hay do **IDA**, nhưng việc load các **API** để phục vụ debug nó cứ dở chứng, mình loay hoay 1 lúc (~~1 tiếng~~) nhưng không ổn định được, nên mình quyết định viết code python dump còn **shellcode** này ra, chấp nhận rủi ro khó đọc hoặc không phân tích được.
```py
from Crypto.Util.number import *
from pwn import *
from tqdm import tqdm
from hashlib import sha256
from Crypto.Cipher import AES
import string
from gmpy2 import *
import string
import re
OPCODE = (
"""0x558bec83ec6c8d45d850e8aa05000083c4048d4d94518d55d8" & "52e8cb03000083c408e80c0000007573657233322e646c6c00" & "00ff55d88945f8837df8007505e9fb000000e80c0000004d65" & "7373616765426f7841008b45f850e8b306000083c4088945f0" & "8b4d0851ff55e883f81c740b8b550cc60200e9c4000000c645" & "bcf8c645bd50c645beccc645bfefc645c0e6c645c13cc645c2" & "35c645c396c645c41dc645c561c645c6aec645c7c0c645c8c5" & "c645c931c645cacec645cbb0c645cce7c645cd1dc645ceedc6" & "45cfbcc645d05dc645d181c645d269c645d38ac645d435c645" & "d574c645d657c645d7b68b4508508d4d94518d55d852e84700" & "000083c40c8945f4c745fc00000000eb098b45fc83c0018945" & "fc837dfc1c7d1f8b4df4034dfc0fb6118b45fc0fb64c05bc3b" & "d174088b550cc60200eb08ebd28b450cc600018be55dc3558b" & "ec83ec445657b90b000000e82c00000068747470733a2f2f77" & "77772e796f75747562652e636f6d2f77617463683f763d6451" & "773477395767586351005e8d7dbcf3a5c745f800000000c745" & "f400000000c745fc000000008b4510508b4d088b5110ffd289" & "45ec6a006a016a006a008d45f8508b4d0c8b11ffd285c07507" & "33c0e91b0200008d45f4506a006a0068048000008b4df8518b" & "550c8b420cffd085c075156a008b4df8518b550c8b4224ffd0" & "33c0e9e9010000837df40075156a008b4df8518b550c8b4224" & "ffd033c0e9ce0100006a008d4dbc518b55088b4210ffd0508d" & "4dbc518b55f4528b450c8b4808ffd185c075218b55f4528b45" & "0c8b481cffd16a008b55f8528b450c8b4824ffd133c0e98a01" & "00008d55fc526a008b45f45068016800008b4df8518b550c8b" & "4214ffd085c075218b4df4518b550c8b421cffd06a008b4df8" & "518b550c8b4224ffd033c0e94a0100008b4df4518b550c8b42" & "1cffd06a008d4dec516a006a006a016a008b55fc528b450c8b" & "4810ffd185c07527837dfc0074218b55fc528b450c8b4820ff" & "d16a008b55f8528b450c8b4824ffd133c0e9f90000006a0468" & "001000008b55ec83c201526a008b45088b4808ffd18945e883" & "7de8007527837dfc0074218b55fc528b450c8b4820ffd16a00" & "8b55f8528b450c8b4824ffd133c0e9b10000008b55ec83c201" & "528b45e850e8cc06000083c408c745f000000000eb098b4df0" & "83c101894df08b55f03b55ec73128b45e80345f08b4d10034d" & "f08a118810ebdd8b4510508b4d088b5110ffd2508d45ec508b" & "4de8516a006a016a008b55fc528b450c8b4810ffd185c07524" & "837dfc00741e8b55fc528b450c8b4820ffd16a008b55f8528b" & "450c8b4824ffd133c0eb23837dfc00741a8b55fc528b450c8b" & "4820ffd16a008b55f8528b450c8b4824ffd18b45e85f5e8be5" & "5dc3558bec51e81000000061647661706933322e646c6c0000" & "00008b45088b08ffd18945fc837dfc00750732c0e99b010000" & "e818000000437279707441637175697265436f6e7465787441" & "000000008b55fc52e8d102000083c4088b4d0c8901e8100000" & "00437279707443726561746548617368008b55fc52e8ab0200" & "0083c4088b4d0c89410ce8100000004372797074496d706f72" & "744b657900008b55fc52e88402000083c4088b4d0c894104e8" & "1000000043727970744465726976654b657900008b55fc52e8" & "5d02000083c4088b4d0c894114e81000000043727970744861" & "7368446174610000008b55fc52e83602000083c4088b4d0c89" & "4108e8100000004372797074456e6372797074000000008b55" & "fc52e80f02000083c4088b4d0c894110e81400000043727970" & "7447657448617368506172616d0000008b55fc52e8e4010000" & "83c4088b4d0c894118e814000000437279707444657374726f" & "7948617368000000008b55fc52e8b901000083c4088b4d0c89" & "411ce810000000437279707444657374726f794b6579008b55" & "fc52e89201000083c4088b4d0c894120e81400000043727970" & "7452656c65617365436f6e74657874008b55fc52e867010000" & "83c4088b4d0c894124b0018be55dc3558bec83ec18e81c0000" & "006b00650072006e0065006c00330032002e0064006c006c00" & "00000000e88602000083c4048945fc837dfc00750732c0e915" & "010000e8100000004c6f61644c69627261727941000000008b" & "45fc50e8fb00000083c4088945f8837df800750732c0e9e400" & "0000e81000000047657450726f634164647265737300008b4d" & "fc51e8ca00000083c4088945f4837df400750732c0e9b30000" & "00e8100000005669727475616c416c6c6f63000000008b55fc" & "52e89900000083c4088945f0837df000750732c0e982000000" & "e80c0000005669727475616c46726565008b45fc50e86c0000" & "0083c4088945ec837dec00750432c0eb58e80c0000006c7374" & "726c656e41000000008b4dfc51e84200000083c4088945e883" & "7de800750432c0eb2e8b55088b45f889028b4d088b55f48951" & "048b45088b4df08948088b55088b45ec89420c8b4d088b55e8" & "895110b0018be55dc3558bec83ec3c8b45088945ec8b4dec0f" & "b71181fa4d5a0000740733c0e9350100008b45ec8b4d080348" & "3c894de4ba080000006bc2008b4de48d5401788955e88b45e8" & "833800750733c0e9080100008b4de88b118955e08b45e00345" & "088945f48b4df48b51188955dc8b45f48b481c894dd08b55f4" & "8b42208945d88b4df48b51248955d4c745f800000000eb098b" & "45f883c0018945f88b4df83b4ddc0f83b30000008b55080355" & "d88b45f88d0c82894dc88b55080355d48b45f88d0c42894dcc" & "8b55080355d08b45cc0fb7088d148a8955c48b45c88b4d0803" & "08894df0c745fc00000000c745fc00000000eb098b55fc83c2" & "018955fc8b450c0345fc0fbe0885c974278b55f00355fc0fbe" & "0285c0741a8b4d0c034dfc0fbe118b45f00345fc0fbe083bd1" & "7402eb02ebc38b550c0355fc0fbe0285c075198b4df0034dfc" & "0fbe1185d2750c8b45c48b4d0803088bc1eb07e938ffffff33" & "c08be55dc3558bec83ec34c745e40000000064a13000000089" & "45e48b4de48b510c8955d88b45d88b480c8b5010894dcc8955" & "d08b45cc8945d48b4dd4894de8837de8000f845a0100008b55" & "e8837a18000f844d0100008b45e8837830007502ebde8b4de8" & "8b51308955ecc745f000000000c745f000000000eb098b45f0" & "83c0018945f08b4df08b55080fb7044a85c00f84dd0000008b" & "4df08b55ec0fb7044a85c00f84cb0000008b4df08b55080fb7" & "044a83f85a7f378b4df08b55080fb7044a83f8417c288b4df0" & "8b55080fb7044a83c0208945e08b4df08b5508668b45e06689" & "044a668b4de066894dfeeb0e8b55f08b4508668b0c5066894d" & "fe668b55fe668955f88b45f08b4dec0fb7144183fa5a7f378b" & "45f08b4dec0fb7144183fa417c288b45f08b4dec0fb7144183" & "c2208955dc8b45f08b4dec668b55dc66891441668b45dc6689" & "45fceb0e8b4df08b55ec668b044a668945fc668b4dfc66894d" & "f40fb755f80fb745f43bd07402eb05e908ffffff8b4df08b55" & "080fb7044a85c075168b4df08b55ec0fb7044a85c075088b4d" & "e88b4118eb0f8b55e88b028945e8e99cfeffff33c08be55dc3" & "558bec518b45088945fc837d0c00741a8b4dfcc601008b55fc" & "83c2018955fc8b450c83e80189450cebe08b45088be55dc300" & "00000000000000000000000000000000000000000000000000" & "00000000000000000000000000000000000000000000000000" & "00000000000000000000000000000000000000000000000000" & "00000000000000000000000000000000000000000000000000" & "00000000000000000000000000000000000000000000000000" & "00000000000000000000000000000000000000000000000000" & "00000000000000000000000000000000000000000000000000" & "00000000000000000000000000000000000000000000000000" & "00000000000000000000000000000000000000000000000000" & "00000000000000000000000000000000000000000000000000" & "00000000000000000000000000000000000000000000000000" & "00000000000000000000000000000000000000000000000000" & "00000000000000000000000000000000000000000000000000" & "00000000000000000000000000000000000000000000000000" & "00000000000000000000000000000000000000000000000000" & "00000000000000000000000000000000000000000000000000" & "00000000000000000000000000000000000000000000000000" & "00000000000000000000000000000000000000000000"""
)
hex_string = OPCODE.replace("0x", "").replace("\"", "").replace("&", "").replace("\n", "").replace(" ", "")
try:
shellcode_bytes = bytes.fromhex(hex_string)
except ValueError as e:
print(f"[!] Lỗi khi chuyển hex sang bytes: {e}")
exit(1)
# Bước 3: Ghi ra file nhị phân
output_file = "shellcode.bin"
with open(output_file, "wb") as f:
f.write(shellcode_bytes)
print(f"[+] Đã ghi shellcode vào file: {output_file}")
```
* Đúng như mình nghĩ, địa chỉ lung tung, không có entrypoint, đỏ tùm lum, ... Thôi thì mình cố gắng ngồi đọc, xem có thể tìm ra được gì nữa không.

* Mình thấy hàm `sub_181()` đây ~~có khả năng cao~~ là hàm mã hóa chính rồi. Tóm tắt hành vi nó thì nó sử dụng cấu trúc giống **CryptoAPI**:
* Tạo context (`CryptAcquireContext`)
* Tạo hash (`CryptCreateHash`)
* Derive key (`CryptDeriveKey`)
* Giải mã (`CryptDecrypt`)
* Vậy mình phân tích kỹ hơn hàm `sub_181()` nha. Flow như sau:
* 1. **CryptAcquireContext**

Đây là dòng gọi hàm tại offset +0 trong bảng hàm tại `a2 + 12`, kết quả lưu vào `a2 - 8` → đây là nơi **CryptAcquireContext** lưu **HCRYPTPROV**.
Dòng này tương đương với:
`CryptAcquireContext(&hProv, NULL, NULL, PROV_RSA_AES, 0);
`
* 2. **CryptCreateHash**

Hàm tại offset +12 của `vtable` (tức `a2 + 12 + 12`).
Truyền vào `hProv`, `ALG_ID = 0x800C` (= `CALG_SHA1` = `32772`) → đây là `SHA-1`.
Tương đương với:
`CryptCreateHash(hProv, CALG_SHA1, 0, 0, &hHash);`
* 3. **CryptHashData**

Gọi hàm từ `vtable[a2 + 8 + 16]` để lấy độ dài dữ liệu hash (có thể là độ dài IV hoặc block dữ liệu).
Sau đó gọi `vtable[a2 + 12 + 8]` với `hHash`, `buffer`, `length`.
Tương ứng với
`CryptHashData(hHash, pbData, dwDataLen, 0);
`
* 4. **CryptDeriveKey**

Gọi hàm từ offset +20 trong `vtable[a2 + 12]`
`ALG_ID = 26625 = 0x6801 = CALG_RC4`
Tương đương với `CryptDeriveKey(hProv, CALG_RC4, hHash, 0, &hKey);`
* 5. **CryptDecrypt**

Gọi từ `a2 + 12 + 16` → offset +16 trong `vtable`
Truyền `hKey, Final = TRUE, buffer, length`.
Tương đương với `CryptDecrypt(hKey, 0, TRUE, 0, pbData, &dwDataLen);`
* Các hàm khác, như `sub_86B()` là `GetModuleHandleW`, `sub_711()` là `GetProcAddress(hModule, functionName);`.

* Kiểu kiểu đó, vậy mình hiểu cái **shellcode** làm gì rồi (~~nếu như không hiểu nhầm~~), đơn giản nó chỉ nhận `input`, mã hóa **RC4** `input` với `key` được đem đi `hash`, rồi so sánh với `data` nào đó có sẵn thui à.
* **flag_encrypted** ở đây nha.

* Đầy đủ data rồi thì giải mã kiếm lại flag thui.
### Script
```py
from Crypto.Util.number import *
from pwn import *
from tqdm import tqdm
from hashlib import sha256
from Crypto.Cipher import AES
import string
from gmpy2 import *
import string
from Crypto.Cipher import ARC4
from Crypto.Hash import SHA1
cipher = bytes([
0xF8, 0x50, 0xCC, 0xEF, 0xE6, 0x3C, 0x35, 0x96,
0x1D, 0x61, 0xAE, 0xC0, 0xC5, 0x31, 0xCE, 0xB0,
0xE7, 0x1D, 0xED, 0xBC, 0x5D, 0x81, 0x69, 0x8A,
0x35, 0x74, 0x57, 0xB6
])
password = b"https://www.youtube.com/watch?v=dQw4w9WgXcQ"
sha1 = SHA1.new()
sha1.update(password)
key = sha1.digest()
rc4_key = key[:16]
cipher_rc4 = ARC4.new(rc4_key)
plaintext = cipher_rc4.decrypt(cipher)
print(plaintext)
# b'KCSC{rC4_8uT_1T_L00k2_W31Rd}'
```
* **Flag: ~~KCSC{rC4_8uT_1T_L00k2_W31Rd}~~**

## 1 vài lưu ý/ chỉnh sửa
* Khi làm xong 3 bài này, thì mình có 1 thắc mắc trong đầu. Có lẽ nếu các bạn để ý kỹ thì cũng thấy rằng 3 bài **resolve API** khá nhiều, chi tiết hơn thì là các **Crypt API**. Vậy câu hỏi đặt ra là như này. Ừ thì nếu chỉ **resolve** có ~~in ít~~ **API** thì không sao, nhưng trong trường hợp hàng trăm hàng nghìn cái được gọi ra, thì làm sao để biết nó gọi **API** nào? Debug chay tới từng chỗ đó để xem ư?
* Đương nhiên là không ai công nhân như vậy cho khổ. Ở đây mình có tìm hiểu được 1 cái khá là ngon, đó là **Appcall**, đây chính là câu trả lời cho thắc mắc ở trên. Mình sẽ lấy bài **VEH.exe** để làm ví dụ thực hiện nha.
### Bài **VEH.exe**
* Bây giờ ý tưởng là duyệt qua toàn bộ `.text section` của binary, tìm lệnh `div rax`, truy ngược 17 byte trước đó để phân tích giá trị `r8` và `r9` sau đó gọi `Appcall.ResolveAPI_FNV1a(r8, r9)` để tới luôn handler xử lý. Mình code 1 đoạn ~~tạm bợ~~ như sau:
```python
import ida_bytes
import ida_segment
import idc
import idautils
seg = ida_segment.get_segm_by_name(".text")
if not seg:
print("Không tìm thấy đoạn .text")
exit()
start = seg.start_ea
end = seg.end_ea
for ea in range(start, end):
b = ida_bytes.get_bytes(ea, 3)
if b == b'\x48\xf7\xf0': # div rax
resolveStart = ea - 17
data = ida_bytes.get_bytes(resolveStart, 17)
if data.startswith(b'\x49\xc7') and data.endswith(b'\x31\xc0'):
r8 = int.from_bytes(data[3:7], byteorder='little')
r9 = int.from_bytes(data[10:14], byteorder='little')
try:
offset = Appcall.ResolveAPI_FNV1a(r8, r9)
name = idc.get_name(offset)
print(f"[+] DIV 0 at {hex(ea)} → resolveAPI({hex(r8)}, {hex(r9)}) = {hex(offset)}: {name}")
except Exception as e:
print(f"[!] Failed resolveAPI at {hex(ea)}: {e}")
```
* Kết quả:

* Dựa vào đây thì hoàn toàn khiến việc debug dễ dàng hơn rất nhiều thay vì debug từng lệnh, xong dính exception, đúng không nào? 😶🌫️
### Bài mixture.exe
* Đối với bài này thì không có gì lấn cấn cả, chỉ là mình thấy rằng dùng **tools** dump con **cmd.exe** rất tiện lợi. Nhưng nếu thử dump tay thì sao? Liệu có trường hợp tools nó sai hoặc thiếu sót gì không?
* Khi thực hiện dump tay, mình lựa chọn sử dụng **Process Hacker**.
* Các bước như sau: Chuột phải vào `cmd.exe` trong `mixture.exe` ----> Chọn **Create dump file** ----> **Full** ----> Chọn vị trí lưu file `.dmp`.
* Tuy nhiên file `.dmp` này chưa phải thứ mình muốn, phải extract ra file `.exe`. Để chắc chắn file `.dmp` của mình là đúng, thì thử string grep xem có header PE không bằng lệnh `strings <ten_file>.dmp | grep -a MZ`. Còn nếu có nhiều `MZ` thì `grep -aob 'MZ' <ten_file>.dmp`.
* Sau đó thì mình viết tạm bợ 1 đoạn code python, tạo 1 folder để lưu các file `exe` extract được, cũng là để phòng trường hợp có nhiều chuỗi `MZ`.
```py
from Crypto.Util.number import *
from pwn import *
from tqdm import tqdm
from hashlib import sha256
from Crypto.Cipher import AES
import string
from gmpy2 import *
import string
import os
import struct
def is_valid_pe(data):
if data[0:2] != b'MZ':
return False
if len(data) < 0x40:
return False
e_lfanew = struct.unpack_from("<I", data, 0x3C)[0]
if e_lfanew >= len(data):
return False
if data[e_lfanew:e_lfanew+4] != b'PE\x00\x00':
return False
return True
def extract_pes(dmp_path, output_dir, max_pe_size=2*1024*1024):
with open(dmp_path, 'rb') as f:
dmp_data = f.read()
mz_offsets = []
for i in range(len(dmp_data) - 2):
if dmp_data[i:i+2] == b'MZ':
mz_offsets.append(i)
print(f"[+] Found {len(mz_offsets)} potential MZ headers")
if not os.path.exists(output_dir):
os.makedirs(output_dir)
count = 0
for offset in mz_offsets:
chunk = dmp_data[offset:offset + max_pe_size]
if is_valid_pe(chunk):
out_path = os.path.join(output_dir, f'pe_extracted_{count:03}.exe')
with open(out_path, 'wb') as out:
out.write(chunk)
print(f"[+] Valid PE found at offset {offset}, saved to {out_path}")
count += 1
if count == 0:
print("[-] No valid PE headers found.")
else:
print(f"[✓] Extracted {count} PE file(s).")
# ======== Customize this ===============
DMP_PATH = "cmd.exe_2025-07-02_14-58-36.dmp"
OUTPUT_DIR = "extracted_pe"
# ======================================
if __name__ == "__main__":
extract_pes(DMP_PATH, OUTPUT_DIR)
```
* Ví dụ kết quả:

* Được rồi, việc cuối cùng là xem 12 file `.exe` con nào là `cmd.exe` được hollowing. Do mình đã có file đó từ bên trên rồi, nên compare cho nhanh. Và có 1 vài chỗ vẫn phải make code lại.
* Ngoài ra, nếu công nhân hơn, tức là dump trực tiếp từ **IDA**, còn cách trên dump từ **Process Hacker**, thì mình làm như này. Nhưng đây là cách làm cụ thể ứng với bài này thôi, tức là với bài khác, hay nay mai gặp **malware** thật thì cách nó inject vào không biết đâu mà lần. ( ~~Thực ra lúc đầu mình tính viết 1 script dump trực tiếp trên RAM, nhưng mà nó cứ lệch offset liên tục khi trước và sau debug, không fix được nên mình bỏ lun~~ :))
* Ở đây thì mình dùng do biết payload nó inject từ section **resource**, nên mình dùng **CFF Explorer** tìm địa chỉ của nó.
* 
`VA = Base + VirtualAddress = 0xCF0000 + 0x7000 = 0xCF7000`
* Cắm breakpoint tại `WriteProcessMemory()`:

* Thông tin [WriteProcessMemory](https://learn.microsoft.com/en-us/windows/win32/api/memoryapi/nf-memoryapi-writeprocessmemory):
```py
BOOL WriteProcessMemory(
HANDLE hProcess, // [esp+4]
LPVOID lpBaseAddress, // [esp+8]
LPCVOID lpBuffer, // [esp+C]
SIZE_T nSize, // [esp+10]
SIZE_T *lpNumberOfBytesWritten // [esp+14]
);
```
* Dựa vào đó thì mình viết code python tìm `address` của `payload`:
```py
import idaapi, idc
esp = idc.get_reg_value("esp")
proc_handle = idaapi.get_wide_dword(esp + 4)
dest_addr = idaapi.get_wide_dword(esp + 8)
src_buffer = idaapi.get_wide_dword(esp + 12)
size = idaapi.get_wide_dword(esp + 16)
print(f"[+] hProcess = 0x{proc_handle:X}")
print(f"[+] dest_addr = 0x{dest_addr:X}")
print(f"[+] src_buffer = 0x{src_buffer:X}")
print(f"[+] size = 0x{size:X}")
```

* **Jump** tới:

* Ngon luôn, `MZ` chính là header của PE file, tiến hành dump con này ra nào.
```py
import ida_bytes
import struct
import idaapi, idc
esp = idc.get_reg_value("esp")
proc_handle = idaapi.get_wide_dword(esp + 4)
dest_addr = idaapi.get_wide_dword(esp + 8)
src_buffer = idaapi.get_wide_dword(esp + 12)
size = idaapi.get_wide_dword(esp + 16)
print(f"[+] hProcess = 0x{proc_handle:X}")
print(f"[+] dest_addr = 0x{dest_addr:X}")
print(f"[+] src_buffer = 0x{src_buffer:X}")
print(f"[+] size = 0x{size:X}")
src = src_buffer
header = ida_bytes.get_bytes(src, 0x1000)
e_lfanew = struct.unpack_from("<I", header, 0x3C)[0]
size_of_image = struct.unpack_from("<I", header, e_lfanew + 0x50)[0]
buf = ida_bytes.get_bytes(src, size_of_image)
with open("dump.exe", "wb") as f:
f.write(buf)
print(f"[✓] Dumped PE image ({size_of_image} bytes) from 0x{src:X} → dump.exe")
```
* Mút luôn, con `dump.exe` này cũng là con mà chúng ta cần dump từ nãy tới giờ nha.