# Anti-debug.Debug Flags (p1)
# 1. Using Win32 API:
## 1.1 kernel32!IsDebuggerPresent():
Xác định xem tiến trình hiện tại có bị debugging bởi trình debugger (ollyDbg, x64dbg) không? Bằng cách kiểm tra cờ `BeingDebugged` trong [PEB](https://www.nirsoft.net/kernel_struct/vista/PEB.html). (Tiến trình bị debug sẽ được spawn từ tiến trình debugger).
> API cũng hoạt động khi process đang bị attached
>
| isDebuggerPresent()| 32bit | 64bit |
| -------| -------- | -------- |
| Get **PEB** struct| mov eax, dword ptr fs:[30h] | mov rax, qword ptr gs:[60h] |
| Get Being Debuged flag | movzx eax, byte ptr [eax+2h] | movzx eax, byte ptr [rax+2h] |
|Return | ret | ret |
```cpp!
BOOL IsDebuggerPresentPEB ()
{
char IsDbgPresent = 0;
__asm {
mov eax, fs:[30h] // PEB struct, gs:[60h] in x64
mov al, [eax + 2h] // BeingDebugged flag
mov IsDbgPresent, al
}
return IsDbgPresent;
}
```
> Bypass: set BeingDebugged flag trong PEB thành 0
## 1.2. [kernel32!CheckRemoteDebuggerPresent()](https://learn.microsoft.com/en-us/windows/win32/api/debugapi/nf-debugapi-checkremotedebuggerpresent):
Kiểm tra xem có trình debugger nào (tiến trình khác trên cùng máy) attach tới tiến trình hiện tại (Check**Remote**DebuggerPresent).
> cũng hoạt động khi được spawn từ trình debugger.
```c++=
Bool bDebuggerPresent;
if(true == CheckRemoteDebuggerPresent(GetCurrentProcess(), &bDebuggerPresent) && bDebuggerPresent == true)
ExitProcess(-1);
```
```asm=
; x64
lea rdx, [bDebuggerPresent]
mov rcx, -1 ; current PID
call CheckRemoteDebuggerPresent
cmp [bDebuggerPresent], 1
jz _being_debugged
; ...
_being_debugged:
mov ecx, -1
call ExitProcess
```
> bypass: bypass được NtQueryInformationProcess
## 1.3. [ntdll!NtQueryInformationProcess()](https://learn.microsoft.com/en-us/windows/win32/api/winternl/nf-winternl-ntqueryinformationprocess):
- Được sử dụng để lấy nhiều loại thông tin từ một tiến trình tùy thuộc vào tham số `ProcessInformationClass`
> Hàm này được gọi bên trong của `CheckRemoteDebuggerPresent`
```cpp=
NTSTATUS WINAPI NtQueryInformationProcess(
[in] HANDLE ProcessHandle,
[in] PROCESSINFOCLASS ProcessInformationClass,
[out] PVOID ProcessInformation,
[in] ULONG ProcessInformationLength,
[out, optional] PULONG ReturnLength
);
```
### A. ProcessDebugPort (0x7h):
- Object được sử dụng để giao tiếp giữa debugger và debuggee.
- Khi tham số thứ 2 là cấu trúc enum [`ProcessDebugPort`](https://learn.microsoft.com/en-us/windows/win32/api/winternl/nf-winternl-ntqueryinformationprocess#PROCESSDEBUGPORT) bằng **7**. Giá trị trả về sẽ được lưu vào tham số thứ 3.
- Do thông tin được lấy từ kernel, nên khó sử dụng user-mode code để ngăn hàm này.
> Retrieves a DWORD_PTR value that is the port number of the debugger for the process. A nonzero value indicates that the process is being run under the control of a ring 3 debugger.
> hoạt động cả khi spawn từ debbuger hoặc bị attached
> **NOTE: chỉ hoạt động với build x86**
```cpp=
DWORD dwProcDbgPort = 0;
NTSTATUS Status = NtQueryInformationProcess(
GetCurrentProcess(),
7, // ProcessDbgPort
&dwProcDbgPort,
sizeof(DWORD),
NULL);
if (dwProcDbgPort == -1) { // 0xffffffff
exit(-1);
```
> bypass: hook NtQueryInformationProcess và set giá trị trả về khác -1
### B. ProcessDebugFlags:
Tương tự với undocumented **ProcessDebugFlags** (= **0x1F**) class sẽ trả về giá trị trường **NoDebugInherit** trong cấu trúc **EPROCESS**.
Cấu trúc kernel [EPROCESS](https://www.nirsoft.net/kernel_struct/vista/EPROCESS.html), bao gồm trường **NoDebugInherit**. Nếu giá trị trả về là **0** -> có sự tồn tại của trình debugger. (= **1** Nếu không bị attached/debugging).
```c++=
DWORD dwProcDbgFlags = 0;
NTSTATUS Status = NtQueryInformationProcess(
GetCurrentProcess(),
0x1f, // ProcessDebugFlags
&dwProcDbgFlags,
sizeof(DWORD),
NULL);
if (Status == 0 && dwProcDbgFlags == 0)
exit(-1);
```
> bypass: hook NtQueryInformationProcess và set giá trị trả về khác 0.
### C. ProcessDebugObjectHandle:
Khi quá trình debug được bắt đầu, một đối tượng kernel được gọi là `debug object` được tạo. Có thể truy vấn tới **handle** của đối tượng này bởi undocument class **ProcessDebugObjectHandle** = **0x1E**.
> **NOTE: chỉ hoạt động với build x86**
```cpp=
DWORD dwReturnLength;
HANDLE hProcessDebugObject = 0;
const DWORD ProcessDebugObjectHandle = 0x1e;
NTSTATUS status = NtQueryInformationProcess(
GetCurrentProcess(),
ProcessDebugObjectHandle,
&hProcessDebugObject,
sizeof(HANDLE),
&dwReturnLength)
if(status==0 && (hProcessDebugObjectHandle != 0))
ExitProcess(-1);
```
> bypass: hook NtQueryInformationProcess và set giá trị trả về = 0.
## 1.4. ntdll!RtlQueryProcessHeapInformation():
Có thể sử dụng để đọc các cờ heap của bộ nhớ tiến trình hiện tại.
> build x86:
> spawn từ VisualStudio -> HeapFlags = 2 -> not detect.
> spawn từ debugger (IDA) -> HeapFlags = 0x40000062 -> detect.
> attach từ debugger (IDA) -> HeapFlags = 2 -> not detect.
```cpp=
#define HEAP_FLAGS 0x40000062
bool isDebug(){
ntdll:PDEBUG_BUFFER pDebugBuffer = ntdll::RtlCreateQueryDebugBuffer(0, FALSE);
if (!SUCCEEDED(ntdll::RtlQueryProcessHeapInformation((ntdll::PRTL_DEBUG_INFORMATION)pDebugBuffer)))
return false;
ULONG dwFlags = ((ntdll::PRTL_PROCESS_HEAPS)pDebugBuffer->HeapInformation)->Heaps[0].Flags;
return dwFlags & ~HEAP_FLAGS;
}
```
```asm=
; x32
push 0
push 0 ; FALSE
call RtlCreateQueryDebugBuffer
mov [ebp+pDebugBuffer], eax
mov eax, [ebp+pDebugBuffer]
push eax
call RtlQueryProcessHeapInformation
mov eax, [ebp+pDebugBuffer]
mov ecx, [eax+38h] ; HeapInformation 70h in x64
mov edx, 40h
imul eax, edx, 0 ; Heaps[0]
mov ecx, [ecx+eax+8] ; Flags
; Flags field in window 64 bit:
; #define HEAP_GROWABLE 0x2
; #define HEAP_TAIL_CHECKING_ENABLED 0x20
; #define HEAP_FREE_CHECKING_ENABLED 0x40
; #define HEAP_VALIDATE_PARAMETERS_ENABLED 0x40000000
cmp ecx, 0x40000062h
je _being_debugged
```
> bypass: hook RtlQueryProcessHeapInformation và set giá trị trả về thành HEAP_GROWABLE (0x2h).
## 1.5. ntdll!RtlQueryProcessDebugInformation
Đọc một số trường từ bộ nhớ tiến trình, bao gồm cả HeapFlags.
> build x86:
> spawn từ VisualStudio -> HeapFlags = 2 -> not detect.
> spawn từ debugger (IDA) -> HeapFlags = 0x40000062 -> detect
> attach từ debugger (IDA) -> HeapFlags = 2 -> not detect.
```cpp=
bool isDebug(){
ntdll::PDEBUG_BUFFER pDebugBuffer = ntdll::RtlCreateQueryDebugBuffer(0, FALSE);
if(!SUCCESS(ntdll::RtlQueryProcessDebugInformation(GetCurrentProcessId(), ntdll::PDI_HEAPS | ntdll::PDI_HEAP_BLOCKS, pDebugBuffer)))
return false;
ULONG dwFlags = ((ntdll::PRTL_PROCESS_HEAPS)pDebugBuffer->HeapInformation)->Heaps[0].Flags;
return dwFlags & ~HEAP_GROWABLE;
}
```
```asm=
; x86
push 0
push 0
call RtlCreateQueryDebugBuffer
mov [ebp+pDebugBuffer], eax
push eax
push 14h; PDI_HEAPS + PDI_HEAP_BLOCKS
call GetCurrentProcessId
push eax
call RtlQueryProcessDebugInformation
mov eax, [ebp+pDebugBuffer]
mov ecx, [eax+38h] ; HeapInformation, 0x70 in x64
mov edx, 40h
imul eax, edx, 0
mov ecx, [ecx+eax+8] ; Flags, 10h in x64
; Flags
; HEAP_TAIL_CHECKING_ENABLED
; HEAP_FREE_CHECKING_ENABLED
; HEAP_VALIDATE_PARAMETERS_ENABLE
cmp ecx, 40000062h
je _being_debugged
```
> bypass: hook RtlQueryProcessHeapInformation và set giá trị trả về thành HEAP_GROWABLE (0x2h).
## 1.6 ntdll! [NtQuerySystemInformation](https://learn.microsoft.com/en-us/windows/win32/api/winternl/nf-winternl-ntquerysysteminformation):
```cpp
__kernel_entry NTSTATUS NtQuerySystemInformation(
[in] SYSTEM_INFORMATION_CLASS SystemInformationClass,
[in, out] PVOID SystemInformation,
[in] ULONG SystemInformationLength,
[out, optional] PULONG ReturnLength
);
```
Tham số đầu tiên của hàm là `SYSTEM_INFORMATION_CLASS`, từ enumerate class [`SYSTEM_INFORMATION_CLASS`](https://www.geoffchappell.com/studies/windows/km/ntoskrnl/inc/api/ntexapi/system_information_class.htm) sẽ quyết định thông tin được truy vấn. Nếu thành công, kết quả sẽ được lưu tại biến được khai báo thủ công `SystemInformation`.
Cái ta sử dụng sẽ là `0x23` (SystemKernelDebuggerInformation class). Class này sẽ trả về 2 flags:
- `KdDebuggerEnabled` là thanh ghi`AH`
- `KdDebuggerNotPresennt` là thanh ghi `AL`.
-> Do đó, nếu thanh ghi `AH` = 0 -> có sự tồn tại của trình debugger.
> Phương pháp này chỉ hoạt động trên phiên bản WinXP
>
```cpp!
enum {SystemKernelDebuggerInformation = 0x23};
typedef struct _SYSTEM_KERNEL_DEBUGGER_INFORMATION{
BOOLEAN DebuggerEnabled;
BOOLEAN DebuggerNotPresent;
} SYSTEM_KERNEL_DEBUGGER_INFORMATION, *PSYSTEM_KERNEL_DEBUGGER_INFORMATION;
bool Check(){
NTSTATUS status;
SYSTEM_KERNEL_DEBUGGER_INFORMATION SystemInfo;
status = NtQuerySystemInformation(
(SYSTEM_INFORMATION_CLASS) SystemKernelDebuggerInformation,
&SystemInfo,
sizeof(SystemInfo),
NULL);
return SUCCEEDED(status)?(SystemInfo.DebuggerEnabled && ! SystemInfo.DebuggerNotPresent): false;
}
```
> bypass: hook RtlQueryProcessHeapInformation và set giá trị trả về DebuggerEnabled = 0 và DebuggerNotPresent = 1.
# 2. Manual checks:
## 2.1. NtGlobalFlag
- là một trường trong PEB (giá trị mặc định = 0)
- offset = 0x68 on 32 bit
- offset = 0xBC on 64 bit
- Attach vào process không làm giá trị này thay đổi, nhưng process spawn từ debugger, các flags sẽ được set:
- FLG_HEAP_ENABLE_TAIL_CHECK (0x10)
- FLG_HEAP_ENABLE_FREE_CHECK (0x20)
- FLG_HEAP_VALIDATE_PARAMETERS (0x40)
> bypass: set flag này thành 0, bằng cách DLL injection. Hay sử dụng ScyllaHide plugin.
## 2.3. Heap Flags
- 2 trường **Flags** và **ForceFlags** bị thay đổi phụ thuộc vào phiên bản Windows. Hai trường này có giá trị mặc định là 0 hoặc `HEAP_GROWABLE` (0x2).
> Không hoạt động khi attach process.
```cpp=
bool heap_flags() {
BOOL bIsWow64;
if (ERROR_SUCCESS != IsWow64Process(GetCurrentProcess(), &bIsWow64))
bIsWow64 = FALSE;
#ifndef _WIN64
PPEB pPeb = (PPEB)__readfsdword(0x30);
PVOID pHeapBase = !bIsWow64
? (PVOID)(*(PDWORD_PTR)((PBYTE)pPeb + 0x18))
: (PVOID)(*(PDWORD_PTR)((PBYTE)pPeb + 0x1030));
DWORD dwHeapFlagsOffset = IsWindowsVistaOrGreater()
? 0x40
: 0x0C;
DWORD dwHeapForceFlagsOffset = IsWindowsVistaOrGreater()
? 0x44
: 0x10;
#else
PPEB pPeb = (PPEB)__readgsqword(0x60);
PVOID pHeapBase = (PVOID)(*(PDWORD_PTR)((PBYTE)pPeb + 0x30));
DWORD dwHeapFlagsOffset = IsWindowsVistaOrGreater()
? 0x70
: 0x14;
DWORD dwHeapForceFlagsOffset = IsWindowsVistaOrGreater()
? 0x74
: 0x18;
#endif // _WIN64
PDWORD pdwHeapFlags = (PDWORD)((PBYTE)pHeapBase + dwHeapFlagsOffset);
PDWORD pdwHeapForceFlags = (PDWORD)((PBYTE)pHeapBase + dwHeapForceFlagsOffset);
return (*pdwHeapFlags & ~HEAP_GROWABLE) || (*pdwHeapForceFlags != 0);
}
```
> bypass: set Flags thành HEAP_GROWABLE (0x2) và ForgeFlags thành 0. Sử dụng DLL Injection hay ScyllaHide plugin.
## 2.4 Heap Protection
Khi vùng nhớ heap được khởi tạo,
Nếu cờ `HEAP_TAIL_CHECKING_ENABLED`(0x20) được set trong **NtGlobalFlag**, **0xABABABAB** (32bit)/ **0xBABABABBABABAB** (64 bit) sẽ được set tại cuối khối heap được khởi tạo.
Nếu cờ `HEAP_FREE_CHECKING_ENABLED` được set trong **NtGlobalFlag**, 0xFEEEFEEE sẽ được thêm nếu chuỗi bytes bổ sung được yêu cầu để fill vào không gian trống đến khối nhớ tiếp theo.
> Không hoạt động khi attach process.
```cpp=
PROCESS_HEAP_ENTRY HeapEntry = { 0 };
do
{
if (!HeapWalk(GetProcessHeap(), &HeapEntry))
return false;
} while (HeapEntry.wFlags != PROCESS_HEAP_ENTRY_BUSY);
PVOID pOverlapped = (PBYTE)HeapEntry.lpData + HeapEntry.cbData;
return (DWORD)(*(PDWORD)pOverlapped) == 0xABABABAB;
```
# refs:
- https://www.apriorit.com/dev-blog/367-anti-reverse-engineering-protection-techniques-to-use-before-releasing-software
- http://www.cs.utah.edu/~aburtsev/malw-sem/slides/02-anti-debugging.pdf
- https://anti-reversing.com/Downloads/Anti-Reversing/The_Ultimate_Anti-Reversing_Reference.pdf
- https://anti-debug.checkpoint.com
###### tags: `windows-internal` `anti-debug`