# Anti-debug.Proces Memory(p4) > NOTE: > Các cách phát hiện software breakpoint trong file thực thi sẽ vô dụng khi gặp hardware breakpoint (trong thanh ghi). ## 1. Tìm Breakpoints ### 1.1. Software breakpoint (INT3): - Tìm byte 0xCC hay (INT 3) ```cpp= bool checkForSpecificByte(BYTE cByte, PVOID pMem, SIZE_T nMemSize=0) { PBYTE pBytes = (PBYTE)pMem; for (SIZE_T i = 0; ; i++) { // Break on RET instruction when we scan the function // and when we dont know the function's size if ((nMemSize != 0 && nMemSize <= i) || ((nMemSize == 0) & pBytes[i] == 0xC3)) { break; } if (pBytes[i] == cByte) { return true; } return false; } } bool ScanFucnt() { // funct list to scan PVOID fucnt_list[] = { &fucnt1, &fucnt2, &fucnt3, }; for (auto funcAddr : fucnt_list) { if (checkForSpecificByte(0xCC, funcAddr)) return true; return false; } } ``` ## 1.2 Anti-Step-Over Debugger cho phép step-over function call (button F8). Thông thường debugger sẽ set software breakpoint (0xCC). Để phát hiện khi step over qua hàm, ta có thể xem xét byte đầu tiên của câu lệnh ngay lệnh call một function, xem có bị set breakpoint không. Hoặc trap tại entrypoint của file. ### 1.2.1 Direct Memory Modification Check bên trong hàm, sử dụng _ReturnAddress để lấy địa chỉ trả về ngay sau của hàm đang được thực thi. Nếu byte đó có giá trị băng với software breakpoint (0xCC), ta có thể sửa lại byte đó. ```cpp= #include <intrin.h> void foo(){ PVOID pRetAddr = _ReturnAddress(); if(*(PBYTE)pRetAddress == 0xCC) // int 3 { DWORD dwOldProtect; if (VirtualProtect(pRetAddress, 1, PAGE_EXECUTE_READWRITE, &dwOldProtect)) { *(PBYTE)pRetAddress = 0x90; // nop VirtualProtect(pRetAddress, 1, dwOldProtect, &dwOldProtect); } } } ``` #### kernel32!WriteProcessMemory Ý tưởng tương tự, nhưng sử dụng Windows API ``` BOOL WriteProcessMemory( [in] HANDLE hProcess, // GetCurrentProcess() [in] LPVOID lpBaseAddress, // pRetAddress [in] LPCVOID lpBuffer, // NOP byte [in] SIZE_T nSize, // 1 [out] SIZE_T *lpNumberOfBytesWritten // NULL ); ``` #### kernel32!Toolhelp32ReadProcessMemory Cho phép đọc bộ nhớ của các tiến trình khác ```cpp= #include <TLHelp32.h> bool foo(){ PVOID pRetAddr = _ReturnAddress(); BYTE uByte; if (FALSE != Toolhelp32ReadProcessMemory(GetCurrentProcessId(), _ReturnAddress(), &uByte, sizeof(BYTE), NULL)){ if (uByte == 0xCC) ExitProcess(0); } } ``` ### 1.2.2 kernel32!ReadFile Cũng là để patch lại file, nhưng đọc file thực thi ### 1.3. Memory Breakpoints: Sử dụng guard page trong bộ nhớ (page này được bảo vệ bởi OS). *Nếu page này bị truy cập lần đầu, nó sẽ raise STATUSGUARDVIOLATION exception.* Guard page được tạo bằng cách sử dụng **PAGE_GUARD** memory protection option khi sử dụng API VirtualProtect. ```cpp= DWORD dwOldProtect = 0; SYSTEM_INFO sysinfo = {0}; GetSystemInfo(&SysInfo); PVOID pPage = VirtualAlloc(NULL, sysinfo.dwPageSize, MEM_COMMIT|MEM_REVERSE, PAGE_EXECUTE_READWRIRE); // in new allocated mem PBYTE pMem = (PBYTE)pPage; *pMem = 0xC3; // ret instruction // make the page a guard page VirtualProtect(pPage, sysinfo.dwPageSize, PAGE_EXECUTE_READWRIRE| PAGE_GUARD, &dwOldProtect); // flNewProtect = 140h __try{ __asm{ mov eax, pPage push mem_bp_being_debugged jmp eax // after jmp into new allocated mem, it will ret mem_bp_being_debugged } }__except(EXCEPTION_EXECUTE_HANDLER){ VirtualFree(pPage, NULL, MEM_RELEASE); return false; } mem_bp_being_debugged: VirtualFree(pPage, NULL, MEM_RELEASE); return true; ``` ### 1.4 hardware breakpoints hardware breapoint trong các thanh ghi DR0 -> DR3, có thể được lấy từ thread context. Nếu các giá trị này khác không, tức là hardware bp đã được set. ```cpp= CONTEXT ctx; ZeroMemory(&ctx, sizeof(CONTEXT)); ctx.ContextFlags = CONTEXT_DEBUG_REGISTERS; if(!GetThreadContext(GetCurrentThread(), &ctx)){ return false; } return ctx.DR0 || ctx.DR1 || ctx.DR2 || ctx.DR3; ``` #### bypass Hook GetCurrentThread và chỉnh giá trị của dr0->dr3 ## 2.1 NtQueryVirtualMemory Memory page của bộ nhớ tiến trình, code section là vùng nhớ chia sẻ giữa các processes cho đến khi page này được ghi đè. Ngược lại, khi OS tạo một bản **copy** của page nhớ này và map vào vùng nhớ ảo (process virtual mem) thì mem page này không còn được chia sẻ nữa. Quá trình [Copy-on-write](https://www.studytonight.com/operating-system/copyonwrite-in-operating-system) diễn ra khi debugger set software breakpoint vào code section. Do đó, ta có thể truy vấn [Working Set](https://docs.microsoft.com/en-us/windows/win32/memory/working-set?redirectedfrom=MSDN) của tiến trình hiện tại và kiểm tra trường **Shared** và **ShareCount**. Nếu software breakpoint được set trong code section, 2 trường này sẽ không được set. ## 2.2 patch DbgBreakPoint Hàm này được gọi khi một debugger attach vào môt tiến trình đang chạy. Ta có thể ngăn debugger attach vào bằng cách xóa breakpoint bên trong ntdll!DbgBreakPoint(). Do đó, debugger không thể can thiệp và thread sẽ bị thoát. ![](https://i.imgur.com/7jrJRup.png) ```cpp= HMODULE hNtDll = GetModuleHandleA("ntdll.dll"); if (hNtDll) { FARPROC pDbgBp = GetProcAddress(hNtDll, "DbgBreakPoint"); if (pDbgBp) { DWORD dwOldProtect; VirtualProtect(pDbgBp, 1, PAGE_EXECUTE_READWRITE, &dwOldProtect); *(PBYTE)pDbgBp = (BYTE)0xC3;// ret } } ``` ## 2.3 patch ntdll!DbgUiRemoteBreakin Khi debugger gọi kernel32!DebugActiveProcess, ntdll!DbgUiRemoteBreakin cũng được gọi tương ứng. Để ngăn debugger attach tới tiến trình, ta sẽ patch DbgUiRemoteBreakin gọi tới kernel32!TerminateProcess. ![](https://i.imgur.com/7L94wKO.png) ```cpp= void patch_DbgUiRemoteBreakin() { HMODULE hNtDll = GetModuleHandleA("ntdll.dll"); HMODULE hkernel32 = GetModuleHandleA("kernel32.dll"); if (hNtDll || hkernel32) { FARPROC pDbgUiRemoteBreakin = GetProcAddress(hNtDll, "DbgUiRemoteBreakin"); FARPROC pTerminateProcess = GetProcAddress(hkernel32, "TerminateProcess"); // patch instruction in DbgUiRemoteBreakin unsigned char patch[] = "\x6A\x00\x68\xFF\xFF\xFF\xFF\xB8\xCA\xFE\xBA\xBE\xFF\xD0"; memcpy(patch + 8, &pTerminateProcess, sizeof(DWORD)); DWORD dwOldProtect; if (!VirtualProtect(pDbgUiRemoteBreakin, sizeof(patch), PAGE_READWRITE, &dwOldProtect)) return; ::memcpy_s(pDbgUiRemoteBreakin, sizeof(patch), &patch, sizeof(patch)); VirtualProtect(pDbgUiRemoteBreakin, sizeof(patch), dwOldProtect, &dwOldProtect); } } ``` ## 2.4. checksum functions: - Sử dụng các thuật toán hash để check tính toàn vẹn của hàm, khi bị software bp chèn vào code. ### bypass: - phát hiện các thuật toán hash trong file. ## Bypass: - Xác định được đoạn check và patch với NOPs. ###### tags: `windows-internal` `anti-debug` `rev`