# Anti-debug.Timing & assembly instruction (p3) Khi process được trace trong khi debug, thời gian delay giữa các câu lệnh rất lớn so với thời gian thực thi bình thương. ## 1. RDPMC/RDTSC Là 2 câu lệnh sử dụng cờ PCE trong thanh ghi CR4. >RDPMC chỉ được dùng trong Kernel mode >RDTSC được sử dụng trong user mode. (IDA, x96dbg) >NOTE: time counting bắt đầu count từ thời điểm gặp rdpmc ```cpp= bool IsDebugged(DWORD64 qwNativeElapsed) { ULARGE_INTEGER Start, End; __asm { xor ecx, ecx rdpmc mov Start.LowPart, eax mov Start.HighPart, edx } // ... some work, functions... // Nếu debbuger step trace hoặc set breakpoint trong đoạn này -> large delay time -> debugger bẹhavior detected. __asm { xor ecx, ecx rdpmc mov End.LowPart, eax mov End.HighPart, edx } return (End.QuadPart - Start.QuadPart) > qwNativeElapsed; } ``` ### detect yara ```yaml= strings: $rdtsc = { 0F 31 } $rdpmc = { 0F 33 } condition: uint16(0) == 0x5A4D and any of them ``` ## 2. GetLocalTime, GetSystemTime Tương tự như cách trên nhưng sử dụng Windows API GetLocalTime, GetSystemTime, GetTickCount, timeGetTime, QueryPerformanceCounter ```cpp= SYSTEMTIME stStart, stEnd; GetLocalTime(&stStart); // ... some work functions... // Nếu debbuger step trace hoặc set breakpoint trong đoạn này -> large delay time -> debugger bẹhavior detected. GetLocalTime(&stEnd); ``` ```cpp= GetSystemTime(&stStart); // your code. GetSystemTime(&stEnd); ``` ```cpp= DWORD dwStart = GetTickCount(); // your code return (GetTickCount() - dwStart) > dwNativeElapsed; ``` ```cpp= DWORD dwStart = timeGetTime(); // some work return (timeGetTime() - dwStart) > dwNativeElapsed; ``` ```cpp= LARGE_INTEGER liStart, liEnd; QueryPerformanceCounter(&liStart); // your code QueryPerformanceCounter(&liEnd); return (liEnd.QuadPart - liStart.QuadPart) > qwNativeElapsed; ``` ## 3. ZwGetTickCout/KiGetTickCount Cả 2 hàm đều sử dụng từ [kernel mode](https://fragglet.github.io/dos-help-files/alang.hlp/x_at_L829d.html). ```cpp= bool IsDebugged(DWORD64 qwNativeElapsed) { ULARGE_INTEGER Start, End; __asm { int 2ah mov Start.LowPart, eax mov Start.HighPart, edx } // ... some work __asm { int 2ah mov End.LowPart, eax mov End.HighPart, edx } return (End.QuadPart - Start.QuadPart) > qwNativeElapsed; } ``` ### detect yara ```yaml= strings: $int_2a = { CD 2A } condition: uint16(0) == 0x5A4D and $int_2a ``` ## bypass patch các hàm check time = NOPs hoặc set lại giá trị trả về. # Assembly instructions ## 1. INT 3 Là một interuption được sử dụng như một software breakpoint. Trong trường hợp không có debugger, câu lệnh này sẽ raise EXCEPTION_BREAKPOINT (0x80000003) exception và một exception handler sẽ được gọi. Nếu có debugger, luồng điều khiển sẽ **không** được đưa tới exception handler để xử lí. > ```cpp= __try{ __asm int 3; return true; }__except(EXCEPTION_EXECUTE_HANDLER){ return false; } ``` ## 2. INT 2D Tương tự như INT3, INT 2D cũng sẽ raise EXCEPTION_BREAKPOINT exception. > Nhưng với INT 2D, Windows sử dụng thanh ghi IP (instruction pointer) như một địa chỉ exception.Nếu EAX = 1, 3, 4; thanh gi IP sẽ tăng thêm 1. > ## 3. DebugBreak raise breakpoint exception trên tiến trình hiện tại. Nếu không có debugger, luồng điểu khiển sẽ được xử lí bởi exception handler. ```cpp __try{ DebugBreak(); } __except(EXCEPTION_BREAKPOINT){ return false; } return true; ``` ## 4. ICE Its opcode is 0xF1, được dùng để phát hiện nếu chương trình bị traced. ## 5. Stack Segment Register Cũng được dùng để phát hiện single-stepping > Không có 64bit code version vì thanh ghi SS không được hỗ trợ. ```cpp bool bTraced = false; __asm{ push ss pop ss pushf // push trap flag on stack test byte ptr [esp+1], 1 // examine the trap flag jz movss_not_being_debugged } bTraced = true; movss_not_being_debugged: // restore stack __asm popf; return bTraced; ``` ## 6. Instruction counting Sử dụng debugger handle cho EXCEPTION_SINGLE_STEP exception. Ta sẽ chèn các hardware breakpoint vào chuỗi code thực thi. Exception từ hardware breakpoint sẽ bị bắt bởi vectored exception handler, ta sẽ sử dụng biến đếm tăng lên 1 mỗi lần bắt được exception. khi chạy đến cuối chuỗi code thực thi, ta sẽ so sánh biến đếm để xác định có bị debug không. Nếu khác -> bị debug. ## 7. POPF và Trap Flag (TF) | Bit | Mask | Abbreviation | category | -------- | -------- | -------- | -------- | | 8 | 0x0100 | TF | Trap flag (single step) | control| Trap Flag trong thanh ghi Flags, khi TF được set, sẽ raise SINGLE_STEP exception. Tuy nhiên, khi ta trace code, TF sẽ bị clear bởi debugger, nên sẽ không nhảy vào exception hander. ```cpp __try{ __asm{ pushfd // push Flags register into stack mov dword ptr [esp], 0x100 // change stack value popfd // set TF nop } return true; }__except(GetExceptionCode() == EXCEPTION_SINGE_STEP? EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_EXCUTION) return false; ``` ## 8. Instruction prefixes Lợi dụng cách hoạt động của debugger khi gặp các lệnh prefixes. Nếu thực thi cùng debugger, sau khi đến byte F3; Debugger skip prefix và nhảy tới câu lệnh INT1. Nếu chạy mà không có debugger, exeption sẽ raise và luông thực thi sẽ vào exception handler. ```cpp __try { // 0xF3 0x64 disassembles as PREFIX REP: __asm __emit 0xF3 __asm __emit 0x64 // One byte INT 1 __asm __emit 0xF1 return true; } __except(EXCEPTION_EXECUTE_HANDLER) { return false; } ``` ## bypass: - Patch với lênh NOP. ## detect yara ```yaml strings: $int3 = { CC } $intCD = { CD } $int03 = { 03 } $int2D = { 2D } $ICE = { F1 } $Prefix = { F3 } $pushss_popss = { 16 17 } $pushf = { 9C } condition: uint16(0) == 0x5A4D and any of them ``` ###### tags: `windows-internal` `anti-debug` `rev`