# Antidebug
## **Overview**
- Binary được viết theo kiểu PE32

## **Reverse**
- Tiến hành ném vào IDA và code từ entrypoint chương trình như sau :

- `push` vào stack Exception Function, EAX và EDX lần lượt chứa chuỗi byte theo dạng hex là `0x6E72654B`(`b"nreK"`),`0x32336C65`(`b"23le"`)
- Ở bên dưới có rất nhiều lệnh `nop` và tiếp theo là lệnh `stosd` nên chúng ta có thể phỏng đoán chương trình sẽ tự sửa chính nó. Để xác nhận mình bật thông tin các segment :

- Vậy là `segment text` có quyền thực thi và có thêm cả quyền đọc và ghi `(R,W,X)`, vậy nên nó chắc chắn sẽ có khả năng là tự sửa chính nó và chạy.
- Tiến hành debug và đặt breakpoint tại lệnh `stosd`, và khi chạy đến chúng ta được :

- Có thế thấy chương trình đã tự sửa chính nó và patch đoạn **b"Kernel32"** vào đoạn toàn lệnh `NOP` kia:

- Để hiện được chuỗi như trên thì chỉ cần bôi đen chỗ lệnh asm mà ida hiểu nhầm rồi nhấn phím `A`.
- Ngay bên dưới là chương trình đã sử dụng `SetUnhandledExceptionFilter` hàm này nhận đầu vào là một function xử lý lúc nhận được `exception`.

## Integer divide by zero
- Sau khi `SetUnhandledExceptionFilter` chúng ta tiếp tục debug đến dòng lệnh `div eax` thì lập tức IDA hiện lên như sau :

- Là excetion không thể chia cho 0, vì ngay dòng lệnh `0x401037 đã có lệnh xor al,al` làm cho thanh ghi `eax` bị xóa đi và có giá trị là 0. Nên khi chia cho 0 sẽ xảy ra lỗi.
- Giải thích qua dễ hiểu nhất về hàm `SetUnhandledExceptionFilter(my_functions)` thì khi có bất cứ lỗi nào thì chương trình sẽ lập tức nhảy vào và chạy `my_functions`, và trong trường hợp của chúng ta thì `my_functions` chính là hàm `TopLevelExceptionFilter`.
- Vậy tóm tắt lại là tác giả cố tình để cho chương trình chia cho 0 rồi sau đó sẽ nhảy vào hàm `TopLevelExceptionFilter` để thực thi tiếp.Tiếp tục công việc phân tích hàm `TopLevelExceptionFilter`.
## TopLevelExceptionFilter
- Vì khi debug IDA không thể tự xử lý exceptions (Nhảy vào hàm `TopLevelExceptionFilter` để chạy được) nên công đoạn này chúng ta sẽ phải làm bằng tay ở IDA.
- Ý tưởng ở đây sẽ là mình sẽ không để ``exeptions`` xảy ra, mình sẽ đặt break point trước lệnh `div eax`. Khi chương trình chạy đến lệnh `div eax` thì mình sẽ `set EIP` thành địa chỉ của functions `TopLevelExceptionFilter`:
* Đặt Breakpoint :

* Cho chương trình chạy tới, và lấy địa chỉ của hàm `TopLevelExceptionFilter` (**0x040105C**):
* Sửa **EIP** :

- Phần việc tiếp theo là chúng ta sẽ bắt đầu phân tích hàm `TopLevelExceptionFilter`:
```assembly=
.text:0040105C TopLevelExceptionFilter: ; DATA XREF: start+F↑o
.text:0040105C push 0 ; lpModuleName
.text:0040105E call GetModuleHandleA
.text:0040105E
.text:00401063 push 0 ; dwInitParam
.text:00401065 push offset DialogFunc ; lpDialogFunc
.text:0040106A push 0 ; hWndParent
.text:0040106C push 64h ; 'd' ; lpTemplateName
.text:0040106E push eax ; hInstance
.text:0040106F call DialogBoxParamA
.text:0040106F
.text:00401074 push 0 ; uExitCode
.text:00401076 call ExitProcess
```
- Chương trình gọi function `DialogBoxParamA` để tạo một box UI tương tác. Với function thực hiện sau khi tạo là `DialogFunc`. Nên chúng ta lập tức chuyển đến hàm `DialogFunc` để phân tích luôn:
```C=
// positive sp value has been detected, the output may be wrong!
void __stdcall DialogFunc(HWND a1, UINT a2)
{
// [COLLAPSED LOCAL DECLARATIONS. PRESS KEYPAD CTRL-"+" TO EXPAND]
if ( a1 == (HWND)16 )
JUMPOUT(0x401509);
if ( a1 == (HWND)272 )
{
sub_401757(&loc_4011AC, 782);
hWnd = (HWND)retaddr[1];
ModuleHandleA = GetModuleHandleA(0);
IconA = LoadIconA(ModuleHandleA, (LPCSTR)0x2711);
SendMessageA(hWnd, 0x80u, 1u, (LPARAM)IconA);
if ( !IsDebuggerPresent() )
{
v4 = NtCurrentPeb();
if ( !v4->NtGlobalFlag )
....
```
- Có những đoạn `JUMPOUT(..)` nhìn khá mù mắt nên mình quyết định sẽ đọc ưu tiên code assembly hơn. Khi đọc API hàm `DialogBoxParamA` set up cho `DialogFunc` chạy như thế nào thì mình thấy là đầu tiên chương trình sẽ chạy các bước như set up các font chữ, set màu .... sau đó thì sẽ chạy vào các bước init ( Túm lại là hàm ``DialogFunc`` này được chạy đi chạy lại khá nhiều lần và tác giả sẽ control nó để thực hiện luồng chương trình theo ý của tác giả):
```!
<=====https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-dialogboxparama#remarks=====>
The DialogBoxParam function uses the CreateWindowEx function to create the dialog box. DialogBoxParam then sends a WM_INITDIALOG message (and a WM_SETFONT message if the template specifies the DS_SETFONT or DS_SHELLFONT style) to the dialog box procedure. The function displays the dialog box (regardless of whether the template specifies the WS_VISIBLE style), disables the owner window, and starts its own message loop to retrieve and dispatch messages for the dialog box.
```
- Như vậy mình sẽ **defined** lại các giá trị để dễ đọc hơn :

- Vậy là khi`` message window`` là **0x110** thì chương trình sẽ bắt đầu thực hiện bước `INITDIALOG`. Ở bước INIT này thoạt qua đã thấy có rất nhiều những đoạn check debug:
## CASE **WM_INITDIALOG**
### Sofware breakpoint

- Trước khi phân tích hàm này thì mình sẽ nói qua về kĩ thuật này, nếu như bạn đã quen với việc code ` assembly ` hay sử dụng các trình debug thông dụng như `x64dbg,ida,...` thì các bạn có thể đã biết đến câu lệnh `int 3` lệnh này là lệnh ngắt để dừng chương trình lại tại điểm các bạn muốn. Và `OPCODE` của lệnh này là `0xCC`. Tức là, khi chúng ta set breakpoint ở bất kì đâu trên chương trình thì lúc này trình debugger đã chèn lệnh `int 3`( hay nói cách khác là thay đổi `OPCODE` của lệnh này thành `0xCC`) vào vị trí mà chúng ta set breakpoint.
- Vậy để chống chúng ta debug được thì tác giả sẽ tạo một hàm để kiểm tra chương trình của chúng ta có chỗ nào đó `opcode` đã bị biến đổi thành `0xCC` hay không, nếu có thì sẽ thoát luôn chương trình.
- Đó chính là những gì mà hàm **sub_401757** sẽ làm điều tương tự như lí thuyết mình nói bên trên :

- Hàm **sub_401757** :
```C=
void __stdcall __spoils<ecx> sub_401757(__int16 *a1, int a2)
{
int v3; // ecx
__int16 v4; // bx
__int16 v5; // ax
v3 = a2;
LOBYTE(v3) = a2 & 0xFE;
v4 = 0;
do
{
v5 = *a1++;
v4 += __CFADD__(v5, v4) + v5;
--v3;
}
while ( v3 );
if ( ~v4 != word_40320F ) // 0xA8A6
ExitProcess(0);
}
```
- Hàm **sub_401757** thực hiện tính toán với `782` byte bắt đầu từ địa chỉ `loc_4011AC` , sau khi tính toán xong mà khác với kết quả của tác giả đặt ra thì chứng tỏ trong `782` byte đó đã có opcode bị thay đổi (Bị đặt breakpoint), và kết luận là đang bị debug nên sẽ thoát chương trình ngay lúc đó.
- Thế nên sau khi biết hàm này sẽ check debug thì mình sẽ tiến hành xóa bỏ nó đi và không cho nó thực thi nữa :

### IsDebuggerPresent
- Đọc Tiếp ngay bên dưới thì lại thấy hàm **IsDebuggerPresent**, tên hàm đã nói lên tất cả rồi nên chúng ta tiếp tục `nop` nó và không cho nó thực thi nữa thui :

- Và chúng ta không muốn chương trình thoát ngay lúc lệnh `test eax,eax` nên chúng ta lại tiếp tục sửa cho chương trình chạy tiếp tục chứ không dừng lại :

### NtGlobalFlag
- Các bạn có thể tham khảo thêm tại [link](https://anti-debug.checkpoint.com/techniques/debug-flags.html#manual-checks-ntglobalflag)

- Tiếp tục patch :

### HeapFlag
- [link](https://anti-debug.checkpoint.com/techniques/debug-flags.html#manual-checks-heap-flags)

- Khi trình debug được khởi tạo thì những cờ như cờ **NtGlobalFlag** sẽ được set một giá trị cố định, tương tự như vậy với vùng nhớ heap cũng vậy, sẽ có những **heapflag** được set mặc định khi debug :

- Để Bypass chúng ta tiếp tục `nop` những lệnh nhảy tới exit chương trình :

- **NOP** những lệnh nhảy đến **exit** :

- Vậy là ở case InitDlg kia thì chương trình đã set icon rồi font chữ cho box ui của chúng ta. Đồng thời cũng kiểm tra xem chúng ta có đang debug hay không. Lúc đầu mình không chạy được chương trình, mình cũng không rõ tại sao lại vậy, nhưng khi patch xong những đoạn check debug kia thì mình lại mở được chương trình :

- Vậy là chúng ta sẽ phải nhập key và chương trình sẽ check key của chúng ta. Chúng ta đã phân tích xong tương đối case **WM_INITDIALOG** , tiếp theo chúng ta sẽ đến với case **WM_COMMAND** :

## CASE **WM_COMMAND**

- Đến đây thì mình đoán là 2 nút **Check** và nút **Clear** sẽ tương ứng với 2 đoạn **cmp** mình bôi màu đỏ và xanh như dưới đây :

- Mình tạm thời bỏ qua và phân tích tiếp phần bên dưới, mình define lại các giá trị, trước tiên là **loc_4011C4** :

- Như vậy đến đây chúng ta có thể phỏng đoán được `key` mà chúng ta nhập vào đang được đi theo luồng này vì sau khi define lại giá trị thì thấy có sử dụng `SendDlgItemMessageA` với hằng số là **WM_GETTEXTLENGTH**.
- Hai nhánh bên dưới :

- Đến đây thì chúng ta có thể khẳng định được nhánh bên trái đang check `key` của chúng ta rồi.
### NtQueryInformationProcess
- [link](https://anti-debug.checkpoint.com/techniques/debug-flags.html#using-win32-api-ntqueryinformationprocess)
- Ngay bên dưới sẽ tiếp tục là hàm check debug :

- Các bạn có thể tham khảo link bên trên, căn bản hàm này sẽ lấy thông tin về process đang chạy và cụ thể ở đây là chính nó, nếu như chương trình đang bị debug thì **dwProcessDebugPort** hay ở trong chương trình chúng ta thì **dword_403714** sẽ bị biến đổi thành giá trị ``-1``, còn không thì nó vẫn sẽ là giá trị cũ:

- Để Bypass chúng ta lại tiếp tục patch, bỏ qua đoạn exit và đồng thời không để nó chạy hàm `NtQueryInformationProcess`:
- **Patch_1 : Bỏ NtQueryInformationProcess**

- **Patch_2 : Force JUMP**

## Main Flow
- Đi tiếp theo luồng flow :

- Theo luồng flow chúng ta phân tích tiếp hàm **sub_401284** , hàm này nhận vào những 3 đầu vào : `sub_401284(0x19,0x403001,0x19)`, khi kiểm tra vùng nhớ **0x403001** thì mình thấy nó trống không và không có gì cả, nên rất có thể chương trình sẽ lưu `key` hoặc gì đó vào đây:

- Mình thử tìm những vùng nhớ nào đang sử dụng đến data tại `0x403001`:

- Và mình tìm thấy một chỗ rất hay:

- Vậy là chương trình sẽ check **input** của chúng ta và data tại **0x403001** . Vậy là trong lúc chạy chương trình sẽ lưu kết quả đúng vào **0x403001** nên chúng ta sẽ phải luôn để ý đến vùng nhớ quan trọng này.
---
- Tiếp tục :

- Quay lại luồng của chương trình đang phân tích, chúng ta sẽ đi sâu vào hàm `sub_401284(0x19,0x403001,0x19)` :

- `CALL sub_401290` ? Tiếp tục vào sâu hơn :

- Tận dụng chức năng make code của ida :

- Vào tiếp hàm **loc_40129C**:

### OutputDebugStringA
- Lại là một check debug tiếp :

- Ý tưởng của kĩ thuật này rất đơn giản, nếu như chúng ta đang bật debug thì nó sẽ thông báo được cho chúng ta `message` thông qua hàm `OutputDebugStringA`, Còn nếu chúng ta không bật debug thì lúc thực thi `OutputDebugStringA` chương trình sẽ có lỗi. Vậy để bypass đoạn này, chúng ta sẽ cố tình để bị lỗi, tức là sau khi **GetLastError** thì sẽ có trả về lỗi, hay nói cách khác là `EAX != 0`.
- Mình sẽ sửa đoạn trên như sau :

### NtSetInformationThread
- Vậy là chúng ta đã bypass được thành công, Tiếp tục phân tích hàm **sub_401525** :
```C=
void __stdcall __spoils<ecx> sub_401525(LPCSTR lpString, char *a2, unsigned int a3, _BYTE *a4, unsigned int a5)
{
unsigned int v5; // kr00_4
unsigned int v6; // ecx
char v9; // al
unsigned int v10; // edx
v5 = __readeflags();
NtSetInformationThread((HANDLE)0xFFFFFFFE, ThreadHideFromDebugger, 0, 0);
dword_403710 = lstrlenA(lpString);
v6 = a3;
if ( a5 >= a3 )
{
do
{
v9 = *a2++;
v10 = 0;
do
{
v9 ^= *lpString;
++v10;
}
while ( v10 < dword_403710 );
*a4++ = -v9;
--v6;
}
while ( v6 );
}
byte_40320A = sub_401789(start, 2086) != 0;
__writeeflags(v5);
}
```
- Hàm `NtSetInformationThread` set up với hằng số là `ThreadHideFromDebugger` sẽ làm cho tiến trình chúng ta đang debug bị tách ra và khiến chúng ta không thẻ tương tác được với tiến trình đang bị debug nữa. Tiếp tục bypass bằng cách bỏ hàm này đi :
- Before:

- After:

- Lúc này ở mã giả chúng ta sẽ có :
```C=
void __stdcall sub_401525(LPCSTR lpString, char *a2, unsigned int a3, _BYTE *a4, unsigned int a5)
{
unsigned int v5; // kr00_4
unsigned int v6; // ecx
char v9; // al
unsigned int v10; // edx
v5 = __readeflags();
dword_403710 = lstrlenA(lpString);
v6 = a3;
if ( a5 >= a3 )
{
do
{
v9 = *a2++;
v10 = 0;
do
{
v9 ^= *lpString;
++v10;
}
while ( v10 < dword_403710 );
*a4++ = -v9;
--v6;
}
while ( v6 );
}
byte_40320A = sub_401789(start, 2086) != 0;
__writeeflags(v5);
}
```
- ở cuối chúng ta lại thấy hàm **sub_401789** nhận vào là địa chỉ của hàm `start` và giá trị `2086`:
```C=
BOOL __stdcall sub_401789(_BYTE *a1, int a2)
{
bool v4; // zf
do
{
if ( !a2 )
break;
v4 = *a1++ == 0xCC;
--a2;
}
while ( !v4 );
return a2 != 0;
}
```
- Chúng ta lại một lần nữa thấy anti sofware breakpoint dựa vào dòng `v4 = *a1++ == 0xCC` (``0xCC`` tương đương với ``INT 3``).
- Vậy là nếu như có sofware breakpoint thì ``sub_401789`` sẽ return về kết quả là 1.
- Thế nên chúng ta sẽ sửa sao cho hàm này cho nó luôn trả về kết quả là 1 là được.Có một điều đặc biệt là sau khi mình chạy hết hàm **sub_401525** mình để ý ở vùng nhớ chứa **key** mà mình nói trước đó :

- Đã dần dần xuất hiện `key` nên mình sẽ đặt tên của hàm vừa rồi (**sub_401525**) là hàm **decrypt_key**.
- Tiếp tục phân tích sau khi đã decrypt_key được 1 phần:

- Có thể các bạn sẽ bận tâm đến hàm **GetTickCount** là nó sẽ antidebug, nhưng ở đây nó sử dụng **GetTickCount** để làm **seed** cho hàm **random** ( `sub_401819` ).
- Sau khi `random` nó sẽ check ngẫu nhiên 1 kiểu antidebug nào đó theo các case dưới đây :

### Integer divide by zero

- Cố tình chia cho 0 để xuất hiện **exceptions** ở dòng **0x04012E3**
### INT 3

- Tương tự như trên nhưng exceptions sofware breakpoint mình đã giải thích bên trên
### Unknown Instructions

- Tại dòng **0x4012F7** là những `OPCODE (0xFFFF,FF...)` không thể hiểu được nên lúc debug chương trình sẽ có **exception** và thoát luôn chương trình còn nếu ở trong điều kiện không debug thì nó vẫn có thể chạy tiếp.
### Invalid Address

- Trường hợp này sau khi `xor eax,eax` , thì eax = 0, sau đó lại lấy giá trị tại địa chỉ 0 thì sẽ gây ra lỗi và **exception** lại tiếp tục được raise
### Invalid Address 2

- lệnh **pushfw** sẽ push **eflags** vào **stack** và dòng lệnh tiếp theo lại truy vấn đến **esp** hay nói cách khác là esp lúc này đang là giá trị của eflags nên cũng sẽ gây ra lỗi.
### INT 2D

- [link](https://anti-debug.checkpoint.com/techniques/assembly.html#int2d)
- Khi bị debug nó sẽ chạy theo luồng thoát chương trình, còn không nó sẽ chạy theo luồng từ **loc_401323**
- Để bypass thì mình cũng sẽ `nop` toàn bộ những đoạn check trên là xong :

- Sau khi đã `NOP` xong chúng ta lại thấy vùng nhớ **đặc biệt** được gọi đến ở hàm **sub_40134F** :

- Đi sâu vào hàm **sub_40134F** -> **sub_40135B**:

- Tiếp tục là **decrypt_key** , trace qua chỗ này chúng ta được chuỗi key dài hơn:

- Luồng tiếp theo chương trình sẽ tìm các trình debugger thông dụng, nếu đang có sẽ tắt đi ngay :

- Tiếp theo là check tickCount:

- Chúng ta sẽ né nhánh exit process và đến một hàm tiếp tục decrypt key :

- Chạy qua lệnh **popa** :

**key** :`NtQu3ry1nf0rm@t10nPr0(355R@!s33xc3pt!onD3bugPr1v1l3g3St@ckT1m3CCS3lf-P3BF1ndW1nd0wH1d1ng@nt1-R3v3rs3`