Table of Contents
Dynamic Anti-Reversing
1. Exception
2. Software Breakpoint Detection
3. Hardware Breakpoint Detection
4. Timing Check
5. Single Step Execution Detection
6. Patching Detection
7. Anti-Disassembly
8. PE Image Switching
9. Self-Execution
10. Self-Debugging
11. Nanomite
12. Code Obfuscation
13. Encryption/Decryption
14. Stolen Bytes
15. Multi-Threaded Packers
16. Code Virtualization
17. API Redirection
18. API Wrapping
Static Anti-Reversing
1. PEB
2. TEB
3. Static Anti-Debugging with Native API (NT)
7. TLS Callback Function
8. Static Anti-Debugging with Normal API (WinAPI)
Attacking Debugger
1. NtSetInformationThread()
2. BlockInput()
3. Disable Breakpoint
4. UnhandledExceptionFilter()
5. Exploit Debugger with Format String Bug
6. ??
Dynamic Anti-Reversing Techniques
1. Using Exceptions
๋๋ฒ๊น ์ค ๋๋ฒ๊ธฐ์์ ์์ธ๊ฐ ๋ฐ์ํ๋ฉด ๋๋ฒ๊ฑฐ๊ฐ ๊ธฐ๋ณธ์ ์ผ๋ก ์ด๋ฅผ ์ฒ๋ฆฌํ๋ค. ๋๋ฒ๊ธฐ์ ์ ์๋ Exception Handler๊ฐ ์์ด๋ ๋ง์ด๋ค. ์ด ํน์ฑ์ ์ด์ฉํ์ฌ ์์ธ๋ฅผ ๋ฐ์์ํค๊ณ , ์ด๋ฅผ ์ฒ๋ฆฌํ๋ ์ฃผ์ฒด๋ฅผ ์๋ณํ์ฌ anti-debugging ๊ธฐ๋ฒ์ ๊ตฌํํ ์ ์๋ค. ์ฃผ๋ก SEH Chain์ ์ด์ฉํ๊ฑฐ๋, kernel32.dll์ `SetUnhandledExceptionFilter()` ํจ์๋ฅผ ์ฌ์ฉํด ์์ธ๋ฅผ ์ฒ๋ฆฌํ ์ฃผ์ฒด๋ฅผ ๋ฑ๋กํ๋ค.
1) SEH (Structured Exception Handler)
SEH๋ `_EXCEPTION_REGISTRATION_RECORD` ๊ตฌ์กฐ์ฒด์ ์ฐ๊ฒฐ ๋ฆฌ์คํธ๋ก ๊ตฌ์ฑ๋๋ฉฐ, ์ด๋ ์คํ์ ์ ์ฅ๋๋ค. (๋ฐ๋ผ์ ๋ฒ์ ์ข ์์ ์ด๋ค. -> ์ฝ๊ฐ์ ํ์ธ ํ์, ๋ฒ์๋ฅผ ๋์ด๊ฐ SEH top ์์๊ฐ ๋ฎ์ด์ฐ์ด๋ฉด fs:\[0]์ ์ด๋ป๊ฒ ๋๋๊ฐ?) ์ด๋ฅผ ๊ฐ๋ฆฌ์ผ SEH Chain์ด๋ผ ์นญํ๋ค. ์ฒซ ์์๋ถํฐ ๋ง์ง๋ง ์์๊น์ง, ์์ธ๊ฐ ์ฒ๋ฆฌ๋ ๋๊น์ง ์์ฐจ์ ์ผ๋ก ์คํ๋๋ค.
(1) `_EXCEPTION_REGISTRATION_RECORD` ๊ตฌ์กฐ์ฒด
`_EXCEPTION_REGISTRASTION_RECORD` ๊ตฌ์กฐ์ฒด์ ์ ์๋ ์๋์ ๊ฐ๋ค.
typedef struct _EXCEPTION_REGISTRATION_RECORD{
PEXCEPTION_REGISTRATION_RECORD Next;
PEXCEPTION_DISPOSITION Handler;
} EXCEPTION_REGISTRATION_RECORD, *PEXCEPTION_REGISTRATION_RECORD;
`Next` ๋ฉค๋ฒ๋ ์คํ์ ์๋ ๋ค์ ์์ธ ์ฒ๋ฆฌ๊ธฐ๋ฅผ ๊ฐ๋ฆฌํจ๋ค. ์ด ๊ฐ์ด 0xFFFFFFFF์ด๋ผ๋ฉด ์ด ๋
ธ๋๊ฐ SEH Chain์ ๋ง์ง๋ง ์์ธ ์ฒ๋ฆฌ๊ธฐ๋ผ๋ ๊ฒ์ ๋ปํ๋ค.
`Handler` ๋ฉค๋ฒ๋ ์์ธ ์ฒ๋ฆฌ์ ์ฌ์ฉ๋ ํจ์์ ์ฃผ์๋ฅผ ์ ์ฅํ๋ค. `Handler`๋ ์์คํ
์ ์ํด ํธ์ถ๋๋ callback ํจ์๋ก, ์ ์๋ ์๋์ ๊ฐ๋ค.
(a) `Handler` calllback function
func `_except_handler()` (winnt.h)
EXCEPTION_DISPOSITION _except_handler(
EXCEPTION_RECORD *pRecord,
EXCEPTION_REGISTRATION_RECORD *pFrame,
CONTEXT *pContext,
PVOID DispatcherContext // ์์คํ
๋ด๋ถ์์ ์ฌ์ฉ๋๋ ๊ฐ. ์ ๊ฒฝ ์ธ ํ์ x
)
์ด ํจ์๋ SEH Chain์ ์ผ๋ถ๋ก, ๋ฐ์ํ ์์ธ๋ฅผ ์ฒ๋ฆฌํ๊ธฐ ์ํด ์กด์ฌํ๋ค. ์์ธ ์ฒ๋ฆฌ๊ธฐ(`handler`)๊ฐ ์ธ ๋ฒ์งธ ์ธ์ `CONTEXT` ๊ตฌ์กฐ์ฒด์ ์์์ ์ผ๋ก๋ถํฐ 0xB8๋งํผ ๋จ์ด์ง `Eip` ๋ฉค๋ฒ๋ฅผ ์ ์ ํ ๋ณ๊ฒฝํจ์ผ๋ก์จ ์์ธ ๋ฐ์ ์ง์ ์ผ๋ก๋ถํฐ ์คํ ํ๋ฆ์ ์ฎ๊ธฐ๋ ๋ฐฉ์์ผ๋ก ์์ธ๋ฅผ ์ฒ๋ฆฌํ ์ ์๋ค. (bytecode์ ๋ํ ์ดํด๊ฐ ๊น๋ค๋ฉด ์์ธ๊ฐ ๋ฐ์ํ `pRecord->ExceptionAddress`์ Opcode๋ฅผ ์ ์ ํ ๋ณ๊ฒฝํ๋ ๋ฐฉ์์ผ๋ก๋ ์ฒ๋ฆฌํ ์ ์์ ๊ฒ์ด๋ค. -> ์ด๊ฑฐ ์ฌ๋ฏธ์์ด ๋ณด์ด๋๋ฐ ๊ตฌํ ใฑใฑ)
์๋๋ `CONTEXT.Eip`๋ฅผ 2๋งํผ ์ฆ๊ฐ์์ผ ์์ธ๋ฅผ ์ฒ๋ฆฌํ๋ ๊ฐ๋จํ `handler`์ ์ฝ๋์ด๋ค.
mov eax, dword ptr ss:[esp + 0x04]
// pContext์ ์ฃผ์ ์ฎ๊น. [esp] = pValue, [esp+8] = pFrame, [esp+C] = pRecord
mov ecx, dword ptr ds:[eax + 0xB8] // pContext.Eip ๊ฐ์ eax์ ์ฎ๊น
add ecx, 2
mov dword ptr ds:[eax+0xB8], ecx
xor eax, eax
retn 4 // handler์ ์ธ์๊ฐ 4๊ฐ์ด๋ฏ๋ก, ์ด๋ฅผ ๋ชจ๋ ์ ๋ฆฌํ ๋ค return.
// ์๋ง __stdcall, win32์์๋ ๊ฐ๋ณ์ธ์ํจ์๋ฅผ ์ ์ธํ ๋ชจ๋ ํจ์๋ __stdcall ์ฌ์ฉ
(a-1) `Handler`'s 1st param: `_EXCEPTION_RECORD *` (winnt.h)
typedef struct _EXCEPTION_RECORD {
DWORD ExceptionCode; // ์์คํ
์ด exception์ ํ ๋นํ ์ซ์
DWORD ExceptionFlags; //
struct _EXCEPTION_RECORD *ExceptionRecord;
PVOID ExceptionAddress; // exception์ด ๋ฐ์ํ ์ฃผ์
DWORD NumberParameters;
ULONG_PTR ExceptionInformation[EXCEPTION_MAXIMUM_PARAMETERS];
} EXCEPTION_RECORD;
[Microsoft: EXCEPTION_RECORD structure (winnt.h)](https://learn.microsoft.com/en-us/windows/win32/api/winnt/ns-winnt-exception_record)
`ExceptionCode`๋ ์์ธ ๋ฒํธ์ธ๋ฐ, winnt.h์์ `#define STATUS_` ๋ฅผ ๊ฒ์ํด ์ฐพ์ ์ ์๋ค. ๋ ๋ง์ exception code๋ Windows NT DDK์ ntstatus.h์์ ์ฐพ์๋ณผ ์ ์๋ค.
`ExceptionFlags`๋ winnt.h์ ๋ค์๊ณผ ๊ฐ์ด ์ ์๋์ด ์๋ค.
#define EXCEPTION_NONCONTINUABLE 0x1 // Noncontinuable exception
#define EXCEPTION_UNWINDING 0x2 // Unwind is in progress
#define EXCEPTION_EXIT_UNWIND 0x4 // Exit unwind is in progress
#define EXCEPTION_STACK_INVALID 0x8 // Stack out of limits or unaligned
#define EXCEPTION_NESTED_CALL 0x10 // Nested exception handler call
#define EXCEPTION_TARGET_UNWIND 0x20 // Target unwind in progress
#define EXCEPTION_COLLIDED_UNWIND 0x40 // Collided exception handler call
(a-2) `Handler`'s 2nd param: `EXCEPTION_REGISTRATION_RECORD *` (winnt.h)
์ด ํ๋ผ๋ฏธํฐ๋ establisher frame structure๋ฅผ ๋ํ๋ธ๋ค. ์ด๋ ํธ์ถ๋ SEH handler๊ฐ ์ํ `_EXCEPTION_REGISTRATION_RECORD` ๊ตฌ์กฐ์ฒด์ ์์ ์ฃผ์๋ฅผ ๋ํ๋ธ๋ค. (`*pFrame` = `Next`์ ์ ์ฅ๋ ๊ฐ, `**pFrame` = ๋ค์ SEH ๋
ธ๋์ Next์ ์ ์ฅ๋ ์ฃผ์)
์๋ ์ฝ๋๋ฅผ ํตํด ์ด๋ฅผ ํ์ธํ ์ ์๋ค.
// x86 debug, VS19
#include <Windows.h>
#include <stdio.h>
EXCEPTION_DISPOSITION
_cdecl
handler(
EXCEPTION_RECORD* pRecord,
EXCEPTION_REGISTRATION_RECORD* pFrame,
CONTEXT* pContext,
PVOID pValue
) {
MessageBox(NULL,
L"Exception Occurred",
L"Exception Handler 1",
MB_ICONERROR);
printf("Exception Occurred at: %p\n", pRecord->ExceptionAddress);
printf("Exception filter 1 called at: %p\n", pFrame);
return ExceptionContinueSearch;
}
EXCEPTION_DISPOSITION
_cdecl
handler2(
EXCEPTION_RECORD* pRecord,
EXCEPTION_REGISTRATION_RECORD* pFrame,
CONTEXT* pContext,
PVOID pValue
) {
MessageBox(NULL,
L"Exception Occurred",
L"Exception Handler 2",
MB_ICONERROR);
pContext->Eip += 7;
printf("Exception Occurred at: %p\n", pRecord->ExceptionAddress);
printf("Exception filter 2 called at: %p\n", pFrame);
return ExceptionContinueExecution;
}
int main() {
DWORD handlerAddr = 0;
DWORD temp = 0;
__asm {
push handler2
push dword ptr fs:[0x00]
push handler
lea eax, [esp+4]
push eax
mov dword ptr fs:[0x00], esp
lea eax, [esp]
mov handlerAddr, eax
}
printf("SEH[0] is now set at %p\n", (void*)handlerAddr);
printf("Handler 1 at %p\n", handler);
printf("Handler 2 at %p\n", handler2);
__asm {
xor eax, eax
mov dword ptr ds:[eax], 1 // Exception: Access Violation
mov eax, dword ptr fs:[0x00]
mov temp, eax
}
printf("Exception Successfully Handled\nContinue Execution...\n");
__asm {
xor eax, eax
mov dword ptr ds : [eax] , 1 // Exception: Access Violation
}
printf("Exception 2 Successfully Handled\nContinue Execution...\n");
__asm {
add esp, 0x10 // SEH ์ค์นํ๋ฉด์ push ํ๋ ๊ฒ ์ ๋ฆฌ.
}
}
`handler()`๋ ์์ ์ `pFrame`์ ์ถ๋ ฅํ๊ณ , ์์ธ๋ฅผ ๋ค์ SEH ๋
ธ๋๋ก ๋๊ธด๋ค. ๋ค์ SEH ๋
ธ๋์๋ `handler2()`๊ฐ ์กด์ฌํ๋ฉฐ, ์ฌ๊ธฐ์ ์์ธ๋ฅผ ์ฒ๋ฆฌํ ๋ค ์์ ์ `pFrame`์ ์ถ๋ ฅ, ์คํ์ ์ฌ๊ฐํ๋ค.
์ ์ฝ๋์ ์คํ ๊ฒฐ๊ณผ๋ ์๋์ ๊ฐ๋ค.
SEH[0] is now set at 00CDFAC4
Handler 1 at 00D713CA
Handler 2 at 00D713CF
Exception Occurred at: 00D753DD
Exception filter 1 called at: 00CDFAC4
Exception Occurred at: 00D753DD
Exception filter 2 called at: 00CDFACC
Exception Successfully Handled
Continue Execution...
Exception Occurred at: 00D753FC
Exception filter 1 called at: 00CDFAC4
Exception Occurred at: 00D753FC
Exception filter 2 called at: 00CDFACC
Exception 2 Successfully Handled
Continue Execution...
๋ญ.. ์ฃผ์์ผ ์คํ ์๋ง๋ค ๋ฌ๋ผ์ง๊ฒ ์ง๋ง, `FS:[0x00]`์ ๋ด๊ธด ๊ฐ์ด ์ฒซ SEH ๋
ธ๋์ ์์ ์ฃผ์, ์ฆ `handler()`์ ์ฃผ์๊ฐ ํฌํจ๋ `_EXCEPTION_REGISTRATION_RECORD` ๊ตฌ์กฐ์ฒด์ ์์ ์ฃผ์์ด๊ณ (์คํ ์์ญ) ๋ํ `handler()`์ `pFrame`์ ๋ด๊ธด ์ฃผ์์ด๋ค. ๋ง์ฐฌ๊ฐ์ง๋ก `handler2()`์ `pFrame`์ผ๋ก๋ `handler2()`์ ์ฃผ์๊ฐ ํฌํจ๋ `_EXCEPTION_REGISTRATION_RECORD` ๊ตฌ์กฐ์ฒด์ ์ฃผ์๊ฐ ๋ค์ด๊ฐ๋ค.
์ ์ฝ๋์์๋ Stack์ `handler2()`๋ฅผ ์ํ `_EXCEPTION_REGISTRATION_RECORD` ๊ตฌ์กฐ์ฒด๋ฅผ ๋ง๋ ํ `handler()`๋ฅผ ์ํ ๊ตฌ์กฐ์ฒด๋ฅผ ๋ง๋ค๊ณ ์๊ธฐ ๋๋ฌธ์ ๋ `pFrame`์ ์ ์ฅ๋ ์ฃผ์๊ฐ์ ์ฐจ์ด๊ฐ `_EXCEPTION_REGISTRATION_RECORD` ๊ตฌ์กฐ์ฒด์ ํฌ๊ธฐ์ธ 8byte๋งํผ ๋ฐ์ํ๋ค.
์ด๋ ์ด๋๊น์ง๋ ์ถ๊ฐ์ ์ธ ์ฆ๋ช
์ผ ๋ฟ์ด๊ณ , ์๋ SEH ๊ตฌํ ๋ถ๋ถ์ ์ฝ์ด์ผ ์ดํด๊ฐ ๋ ๊ฒ์ด๋ค. ์ง๊ธ์ผ๋ก์๋ ๋์ด๊ฐ๋ ์ข๋ค.
(a-3) `Handler`'s 3rd param: struct `_CONTEXT` (winnt.h)
`CONTEXT` ๊ตฌ์กฐ์ฒด๋ multi-threading ํ๊ฒฝ์ ์ํด ํน์ thread์ ๋ ์ง์คํฐ ๊ฐ์ ๋ฐฑ์
ํด ๋๋ ์ฉ๋์ ๊ตฌ์กฐ์ฒด์ด๋ค. ๋ ์ง์คํฐ๋ฅผ ์ ์ฅํ๋ค ๋ณด๋ x86 ๋ฒ์ ๊ณผ x64 ๋ฒ์ , amd๋ arm ๋ฒ์ ๋ฑ ์ํคํ
์ฒ์ ๋ฐ๋ผ ์ ์๊ฐ ์กฐ๊ธ์ฉ ๋ค๋ฅธ๋ฐ, ์ด๋ค ๋ชจ๋ winnt.h์์ ํ์ธํ ์ ์์๋ค. ์๋๋ intel x86 (IA-32) ์ ์ฉ `_CONTEXT` ๊ตฌ์กฐ์ฒด์ ์ ์. (MSDN์ `CONTEXT` ๊ตฌ์กฐ์ฒด๋ฅผ ๊ฒ์ํ๋ฉด ๋์ค๋ ์ฝ๋๋ x64 ๋ฒ์ ์ด๋ค.)
typedef struct _CONTEXT {
DWORD ContextFlags;
DWORD Dr0;
DWORD Dr1;
DWORD Dr2;
DWORD Dr3;
DWORD Dr6;
DWORD Dr7;
FLOATING_SAVE_AREA FloatSave;
DWORD SegGs;
DWORD SegFs;
DWORD SegEs;
DWORD SegDs;
DWORD Edi;
DWORD Esi;
DWORD Ebx;
DWORD Edx;
DWORD Ecx;
DWORD Eax;
DWORD Ebp;
DWORD Eip; // B8h
DWORD SegCs;
DWORD EFlags;
DWORD Esp;
DWORD SegSs;
BYTE ExtendedRegisters[MAXIMUM_SUPPORTED_EXTENSION];
};
64-bit Windows์์ 32-bit ํ๋ก๊ทธ๋จ์ ๋๋ฆฐ๋ค๋ฉด ๋์ผํ ๊ตฌ์ฑ์ ๊ฐ์ง `_WOW64_CONTEXT`์ ์ฐพ์๋ด๋ ๋๋ค. ์๋ง..
ํ ์ค๋ ๋๋ฅผ ์คํํ๊ณ ๋ค๋ฅธ ์ค๋ ๋๋ก ์คํ ํ๋ฆ์ ๋ฐ๊ฟ ๋ ๋ ์ง์คํฐ๋ค์ ์ํ(context)๋ฅผ ์ด ๊ตฌ์กฐ์ฒด์ ๋ฐฑ์
ํด๋๊ณ ๋ค๋ฅธ ์ค๋ ๋๋ฅผ ์คํ, ๋ค์ ๋ณธ ์ค๋ ๋๋ก ๋์์ฌ ๋์๋ ๋ ์ง์คํฐ๋ค์ ์ด ๊ตฌ์กฐ์ฒด์ ์ ์ฅ๋ ๊ฐ์ผ๋ก ๋ฎ์ด ์ฐ๊ณ ์คํ์ ์ฌ๊ฐํ๋ค.
winnt.h์ ์์ค ์ฝ๋๋ ์๋ ๋งํฌ์์ ํ์ธํ ์ ์๋ค.
[winnt.h](https://codemachine.com/downloads/win80/winnt.h)
(a-4) `Handler`'s 3rd param: PVOID `DispatcherContext`
(a-5) `Handler`'s return value: enum `EXCEPTION_DISPOSITION`
typedef enum _EXCEPTION_DISPOSITION {
ExceptionContinueExecution = 0, // ์์ธ ์ฒ๋ฆฌ ์๋ฃ, ์ฝ๋ ์คํ
ExceptionContinueSearch = 1, // ์ฒ๋ฆฌ ์คํจ, SEH Chain์ ๋ค์ ์์ธ ์ฒ๋ฆฌ๊ธฐ ์คํ
ExceptionNestedException = 2, // OS์์ ์ฌ์ฉ๋จ
ExceptionCollidedUnwind = 3 // OS์์ ์ฌ์ฉ๋จ
} EXCEPTION_DISPOSITION;
`Handler`์ ์์ธ ์ฒ๋ฆฌ ํ, ์์คํ
์ ๋์์ ์ง์ ํ๊ธฐ ์ํด ์ฌ์ฉ๋๋ค. ๊ทผ๋ฐ ์์ธ์ ๋ํ ์ฒ๋ฆฌ๋ฅผ ์ ํ๊ณ ๋ฌด์์ 0์ ๋ฐํํ๋ฉด, ์์คํ
์ด ์คํ์ ์ฌ๊ฐํ์ ๋ ๋๊ฐ์ ๋ถ๋ถ์์ ๋ค์ ์์ธ๊ฐ ๋ฐ์ํด ์ด ํธ๋ค๋ฌ๊ฐ ๋ฌดํ์ ์ฌํธ์ถ๋๋ ์ผ์ด ์๊ธด๋ค. ์๋์ ์ฝ๋๊ฐ ๊ทธ ์์์ด๋ค.
//x86 debug, VS19
#include <stdio.h>
int handler(){
printf("Exception Occurred; And I, exception handler, will do nothing.\n");
return 0;
}
int main(){
__asm{
push handler
push dword ptr FS:[0]
mov dword ptr FS:[0], esp
xor eax, eax
mov dword ptr [eax], 1 // Exception: Access Violation
}
}
์ ์ฝ๋๋ฅผ ์คํํ๋ฉด `handler()` ํจ์๊ฐ ๊ณ์ํด์ ํธ์ถ๋๋ค. Access Violation ์์ธ๊ฐ ํด๊ฒฐ๋์ง ์์ ์ฑ๋ก `ExceptionContinueExecution` ์ ํธ๋ฅผ ๋ณด๋ด์, ์ฒ๋ฆฌ๊ฐ ์๋ฃ๋ ์ค ์ ์์คํ
์ด ์์ธ๊ฐ ๋ฐ์ํ ์ฝ๋ ๋ถ๋ถ๋ถํฐ ๋ค์ ์คํํ์ฌ ๊ณ์ ์์ธ๊ฐ ๋ฐ์ํ๊ณ , ๋ ๋๊ฐ์ `handler()`๊ฐ ๊ทธ๊ฑธ ์ฒ๋ฆฌํ๊ธฐ ๋๋ฌธ์ด๋ค. `handler()`๊ฐ 1์ ๋ฐํํ๊ฒ ์ฝ๋๋ฅผ ์์ ํ๊ณ ์คํํ๋ฉด, `handler()`๊ฐ ํ ๋ฒ ํธ์ถ๋ ๋ค ์์คํ
์ด ์ค์ ํ ๊ธฐ๋ณธ ์์ธ ์ฒ๋ฆฌ๊ธฐ๊ฐ ์์ธ๋ฅผ ๋๊ฒจ๋ฐ์ ํ๋ก์ธ์ค๋ฅผ ์ข
๋ฃํ๋ค.
๋ณดํต์ ์ด๋ฌํ ์ผ์ด ์ผ์ด๋์ง ์๊ฒ๋, `handler()`์์ ์ ๋ฌ๋ `CONTEXT` ๊ตฌ์กฐ์ฒด์ `Eip` ๋ฉค๋ฒ(`&CONTEXT + 0xB8`)๋ฅผ ์ ์ ํ ๋ณ๊ฒฝํ ๋ค ๊ฐ์ ๋ฐํํ๋๋ก ๊ตฌํํ๋ค. (์๋ ์ฌ์ฉ ์์ ๋ฅผ ์ฐธ๊ณ .)
(2) SEH ์ค์นํ๊ธฐ
SEH์ ์์ ์ฃผ์๋ `TEB.NtTib.ExceptionList`์ ์ ์ฅ๋์ด ์๋ค.
์๋๋ TEB์ TEB.NtTib์ ๊ตฌ์กฐ์ด๋ค. WinDbg๋ฅผ ์ฌ์ฉํ๋ค.
(a) TEB (Thread Environment Block / TIB) ๊ตฌ์กฐ
Windbg์์ `dt _TEB` ํน์ `dt TEB` ๋ช
๋ น์ด๋ฅผ ํตํด ๊ตฌ์กฐ๋ฅผ ํ์ธํ ์ ์๋ค. (x86 on x86, x64 on x64)
x86 debugee on x64 Windbg์ ๊ฒฝ์ฐ, `dt _TEB32` ๋ช
๋ น์ด๋ฅผ ์ฌ์ฉํ์ฌ์ผ ํ๋ค.
0:000> dt _TEB
ntdll!_TEB
+0x000 NtTib : _NT_TIB
+0x01c EnvironmentPointer : Ptr32 Void
+0x020 ClientId : _CLIENT_ID
+0x028 ActiveRpcHandle : Ptr32 Void
+0x02c ThreadLocalStoragePointer : Ptr32 Void
+0x030 ProcessEnvironmentBlock : Ptr32 _PEB
+0x034 LastErrorValue : Uint4B
+0x038 CountOfOwnedCriticalSections : Uint4B
+0x03c CsrClientThread : Ptr32 Void
+0x040 Win32ThreadInfo : Ptr32 Void
+0x044 User32Reserved : [26] Uint4B
+0x0ac UserReserved : [5] Uint4B
+0x0c0 WOW32Reserved : Ptr32 Void
+0x0c4 CurrentLocale : Uint4B
+0x0c8 FpSoftwareStatusRegister : Uint4B
+0x0cc ReservedForDebuggerInstrumentation : [16] Ptr32 Void
+0x10c SystemReserved1 : [26] Ptr32 Void
+0x174 PlaceholderCompatibilityMode : Char
+0x175 PlaceholderHydrationAlwaysExplicit : UChar
+0x176 PlaceholderReserved : [10] Char
+0x180 ProxiedProcessId : Uint4B
+0x184 _ActivationStack : _ACTIVATION_CONTEXT_STACK
+0x19c WorkingOnBehalfTicket : [8] UChar
+0x1a4 ExceptionCode : Int4B
+0x1a8 ActivationContextStackPointer : Ptr32 _ACTIVATION_CONTEXT_STACK
+0x1ac InstrumentationCallbackSp : Uint4B
+0x1b0 InstrumentationCallbackPreviousPc : Uint4B
+0x1b4 InstrumentationCallbackPreviousSp : Uint4B
+0x1b8 InstrumentationCallbackDisabled : UChar
+0x1b9 SpareBytes : [23] UChar
+0x1d0 TxFsContext : Uint4B
+0x1d4 GdiTebBatch : _GDI_TEB_BATCH
+0x6b4 RealClientId : _CLIENT_ID
+0x6bc GdiCachedProcessHandle : Ptr32 Void
+0x6c0 GdiClientPID : Uint4B
+0x6c4 GdiClientTID : Uint4B
+0x6c8 GdiThreadLocalInfo : Ptr32 Void
+0x6cc Win32ClientInfo : [62] Uint4B
+0x7c4 glDispatchTable : [233] Ptr32 Void
+0xb68 glReserved1 : [29] Uint4B
+0xbdc glReserved2 : Ptr32 Void
+0xbe0 glSectionInfo : Ptr32 Void
+0xbe4 glSection : Ptr32 Void
+0xbe8 glTable : Ptr32 Void
+0xbec glCurrentRC : Ptr32 Void
+0xbf0 glContext : Ptr32 Void
+0xbf4 LastStatusValue : Uint4B
+0xbf8 StaticUnicodeString : _UNICODE_STRING
+0xc00 StaticUnicodeBuffer : [261] Wchar
+0xe0c DeallocationStack : Ptr32 Void
+0xe10 TlsSlots : [64] Ptr32 Void
+0xf10 TlsLinks : _LIST_ENTRY
+0xf18 Vdm : Ptr32 Void
+0xf1c ReservedForNtRpc : Ptr32 Void
+0xf20 DbgSsReserved : [2] Ptr32 Void
+0xf28 HardErrorMode : Uint4B
+0xf2c Instrumentation : [9] Ptr32 Void
+0xf50 ActivityId : _GUID
+0xf60 SubProcessTag : Ptr32 Void
+0xf64 PerflibData : Ptr32 Void
+0xf68 EtwTraceData : Ptr32 Void
+0xf6c WinSockData : Ptr32 Void
+0xf70 GdiBatchCount : Uint4B
+0xf74 CurrentIdealProcessor : _PROCESSOR_NUMBER
+0xf74 IdealProcessorValue : Uint4B
+0xf74 ReservedPad0 : UChar
+0xf75 ReservedPad1 : UChar
+0xf76 ReservedPad2 : UChar
+0xf77 IdealProcessor : UChar
+0xf78 GuaranteedStackBytes : Uint4B
+0xf7c ReservedForPerf : Ptr32 Void
+0xf80 ReservedForOle : Ptr32 Void
+0xf84 WaitingOnLoaderLock : Uint4B
+0xf88 SavedPriorityState : Ptr32 Void
+0xf8c ReservedForCodeCoverage : Uint4B
+0xf90 ThreadPoolData : Ptr32 Void
+0xf94 TlsExpansionSlots : Ptr32 Ptr32 Void
+0xf98 MuiGeneration : Uint4B
+0xf9c IsImpersonating : Uint4B
+0xfa0 NlsCache : Ptr32 Void
+0xfa4 pShimData : Ptr32 Void
+0xfa8 HeapData : Uint4B
+0xfac CurrentTransactionHandle : Ptr32 Void
+0xfb0 ActiveFrame : Ptr32 _TEB_ACTIVE_FRAME
+0xfb4 FlsData : Ptr32 Void
+0xfb8 PreferredLanguages : Ptr32 Void
+0xfbc UserPrefLanguages : Ptr32 Void
+0xfc0 MergedPrefLanguages : Ptr32 Void
+0xfc4 MuiImpersonation : Uint4B
+0xfc8 CrossTebFlags : Uint2B
+0xfc8 SpareCrossTebBits : Pos 0, 16 Bits
+0xfca SameTebFlags : Uint2B
+0xfca SafeThunkCall : Pos 0, 1 Bit
+0xfca InDebugPrint : Pos 1, 1 Bit
+0xfca HasFiberData : Pos 2, 1 Bit
+0xfca SkipThreadAttach : Pos 3, 1 Bit
+0xfca WerInShipAssertCode : Pos 4, 1 Bit
+0xfca RanProcessInit : Pos 5, 1 Bit
+0xfca ClonedThread : Pos 6, 1 Bit
+0xfca SuppressDebugMsg : Pos 7, 1 Bit
+0xfca DisableUserStackWalk : Pos 8, 1 Bit
+0xfca RtlExceptionAttached : Pos 9, 1 Bit
+0xfca InitialThread : Pos 10, 1 Bit
+0xfca SessionAware : Pos 11, 1 Bit
+0xfca LoadOwner : Pos 12, 1 Bit
+0xfca LoaderWorker : Pos 13, 1 Bit
+0xfca SkipLoaderInit : Pos 14, 1 Bit
+0xfca SkipFileAPIBrokering : Pos 15, 1 Bit
+0xfcc TxnScopeEnterCallback : Ptr32 Void
+0xfd0 TxnScopeExitCallback : Ptr32 Void
+0xfd4 TxnScopeContext : Ptr32 Void
+0xfd8 LockCount : Uint4B
+0xfdc WowTebOffset : Int4B
+0xfe0 ResourceRetValue : Ptr32 Void
+0xfe4 ReservedForWdf : Ptr32 Void
+0xfe8 ReservedForCrt : Uint8B
+0xff0 EffectiveContainerId : _GUID
+0x1000 LastSleepCounter : Uint8B
+0x1008 SpinCallCount : Uint4B
+0x1010 ExtendedFeatureDisableMask : Uint8B
(b) PEB.NtTib ๊ตฌ์กฐ
TEB์ ์ฒซ ๋ฒ์งธ ๋ฉค๋ฒ์ธ `NtTib`์ ๊ตฌ์กฐ์ด๋ค. ๋ฌผ๋ก 32bit PE์ ์ฐ์ธ๋ค.
0:000> dt _NT_TIB
ntdll!_NT_TIB
+0x000 ExceptionList : Ptr32 _EXCEPTION_REGISTRATION_RECORD
+0x004 StackBase : Ptr32 Void
+0x008 StackLimit : Ptr32 Void
+0x00c SubSystemTib : Ptr32 Void
+0x010 FiberData : Ptr32 Void
+0x010 Version : Uint4B
+0x014 ArbitraryUserPointer : Ptr32 Void
+0x018 Self : Ptr32 _NT_TIB
`ExceptionList` ๋ฉค๋ฒ์ SEH Chain์ ์์์ ์ด ์ ์ฅ๋๋ค.
(c) FS ๋ ์ง์คํฐ๋ก SEH ์ ๊ทผํ๊ธฐ
x86 usermode์ FS ๋ ์ง์คํฐ๋ `TEB` ์ ์์ ์ฃผ์๋ฅผ ๊ฐ๋ฆฌํค๊ณ ์๋ค. `NtTib`๋ `TEB`์ ์ฒซ ๋ฉค๋ฒ, ๊ทธ๋ฆฌ๊ณ `ExceptionList`๋ `NtTib`์ ์ฒซ ๋ฉค๋ฒ์ด๋ฏ๋ก `FS:[0]`์ ํตํด SEH Chain์ ์์์ ์ ์ ๊ทผํ ์ ์๋ค.
FS:[0] == TEB.NtTib.ExceptionList
mov eax, dword ptr FS:[0x00] // eax: SEH Chain ์์ ์ฃผ์
x64 usermode์์๋ `GS:[0x30]`์ `TEB`์ ์ฃผ์๊ฐ ๋ด๊ฒจ์๊ณ , ๋ง์ฐฌ๊ฐ์ง๋ก `GS:[0x60]`์ ํตํด `PEB`์ ์ฃผ์๊ฐ์ ์ป์ ์ ์๋ค๋ง, ์ด๋ฐ ๋ฐฉ์์ ์์ธ ํธ๋ค๋ง์ ์ง์ํ์ง ์๋ ๋ฏํ๋ค. (์ถ๊ฐ ์กฐ์ฌ ํ์)
(d) SEH Chain์ Handler ์ถ๊ฐํ๊ธฐ
์๋์ ๊ฐ์ ์ผ๋ จ์ ๋ช
๋ น์ด๋ฅผ ํตํด SEH Chain์ Top์ ์ ํธ๋ค๋ฌ๋ฅผ ์ถ๊ฐํ ์ ์๋ค.
push handler // Handler
push dword ptr FS:[0x00] // Next
mov dword ptr FS:[0x00], esp
`push handler`๋ก ์คํ์ handler ํจ์์ ์ฃผ์๊ฐ ์ ์ฅ๋๋ฉฐ, ๋ค์ด์ `push dword ptr FS:[0x00]`์ผ๋ก ์ด์ SEH Chain์ ์์์ ์ด ์คํ์ ๋ค์ด๊ฐ๋ค. ์ด๋ก์จ ์คํ์ `_EXCEPTION_REGISTRATION_RECORD` ๊ตฌ์กฐ์ฒด๊ฐ ์๋ฆฌ์ก๋๋ค. ๋ง์ง๋ง์ผ๋ก `mov dword ptr FS:[0x00], esp` ๋ช
๋ น์ด๋ก `TEB.NtTib.ExceptionList`์ ์๋ก์ด `_EXCEPTION_REGISTRATION_RECORD` ๊ตฌ์กฐ์ฒด ์ฃผ์๋ฅผ ์ ์ฅํ๋ค. (์ด๋ esp๊ฐ ๊ฐ๋ฆฌํค๋ ์ฃผ์์๋ ์ด์ ์ SEH Chain์ Top, ์ฆ, ์ด์ ์ `FS:[0x00]` ๊ฐ์ด ๋ค์ด ์๋ค.)
SEH Chain์ ์ฒซ `Handler` ์ฝ๋ฐฑ ํจ์์ ์ฃผ์๋ ์๋์ ๊ฐ์ด ๊ตฌํ ์ ์๋ค.
mov eax, fs:[0x00] // stack ์์ SEH Chain ์์ ์ฃผ์
mov eax, [eax + 4] // SEH Chain์ ์์์ ์ผ๋ก๋ถํฐ 4 ๋จ์ด์ง ์ฃผ์์ ์ ์ฅ๋ ๊ฐ -> ํธ๋ค๋ฌ ์ฃผ์
(e) SEH ์ฌ์ฉ ์์
์๋ ์ฝ๋๋ ์ง์ SEH Chain์ `handler()` ํจ์๋ฅผ ๋ฑ๋กํ๊ณ , Access Violation ์์ธ๋ฅผ ๋ฐ์์์ผ `handler()`๋ฅผ ํธ์ถํ๊ฒ๋ ํ๋ค.
// x86 debug, VS19
#include <Windows.h>
#include <stdio.h>
EXCEPTION_DISPOSITION
_cdecl
handler(
EXCEPTION_RECORD* pRecord,
EXCEPTION_REGISTRATION_RECORD* pFrame,
CONTEXT* pContext,
PVOID pValue
) {
MessageBox(NULL,
L"Exception Occurred",
L"Exception Handler",
MB_ICONERROR);
pContext->Eip += 7;
printf("Exception Occurred at: %p\n", pRecord->ExceptionAddress);
return ExceptionContinueExecution;
}
int main() {
DWORD handlerAddr = 0;
__asm {
push handler
push dword ptr fs:[0x00]
mov dword ptr fs:[0x00], esp
mov eax, dword ptr fs:[0x00]
mov eax, dword ptr ds:[eax+4]
mov handlerAddr, eax
// mov dword ptr [handlerAddr], eax ๊ฐ์ ํ๊ธฐ๋ ๋์ผํ๊ฒ ๋์
}
printf("SEH handler is now set at %p\n", (void*)handlerAddr);
__asm {
xor eax, eax
mov dword ptr ds:[eax], 1 // Exception 1: Access Violation
}
printf("Exception Successfully Handled\nContinue Execution...\n");
__asm {
xor eax, eax
mov dword ptr ds : [eax] , 1 // Exception 2: Access Violation
}
printf("Exception 2 Successfully Handled\nContinue Execution...\n");
__asm {
add esp, 8
// SEH ์ค์นํ๋ฉด์ ๋ ๋ฒ push ํ๋ ๊ฒ ๋๋ฌธ์ esp์ 4*2 ๋ํด์ค์ผ ํจ.
// ์ ๊ทธ๋ฌ๋ฉด ์๋ชป๋ ESP ๊ฐ ๋๋ฌธ์ ์ค๋ฅ ๋ฐ์. (__RTC_CheckEsp())
}
}
`handler()` ํจ์ ๋ด์์๋ ์ธ์๋ก ๋ฐ์ `pContext->Eip`์ ๊ฐ์ 7 ์ฆ๊ฐ์์ผ ์์ธ ๋ฐ์ ์ฝ๋๋ฅผ ๊ฑด๋ ๋์ผ๋ก์จ ์์ธ๋ฅผ ์ฒ๋ฆฌํ๋ค.
์ ์ฝ๋๋ฅผ x86 release mode๋ก ์ปดํ์ผํ๋ฉด `mov dword ptr ds:[eax], 1` ๋ถ๋ถ์์ ๋ฐ์ํ ์์ธ๋ฅผ ์ฒ๋ฆฌํ๊ธฐ ์ํด SEH Chain์ ๋ฑ๋ก๋ `handler()` ํจ์๊ฐ ํธ์ถ๋๋๋ฐ, ์ด ํธ์ถ ๋ถ๋ถ์์ ๋ ํ ๋ฒ ์์ธ๊ฐ ๋ฐ์ํ๋ค. (STATUS_INVALID_EXCEPTION_HANDLER) ์ด๋ Visual Studio์ release mode์์๋ `/SAFESEH` ๋ผ๋ ๋ง์ปค ์ต์
์ ๊ธฐ๋ณธ์ ์ผ๋ก ํ์ฑํํด๋๊ธฐ ๋๋ฌธ์ด๋ค. ํ๋ก์ ํธ์ ๋ง์ปค ์ต์
์์ `Image Has Safe Exception Handlers` ์ต์
์ ๋๊ณ ์ปดํ์ผํ๋ค๋ฉด ์๋ํ ๋์์ ํ๊ฒ ์ง๋ง, SEH Overwrite ๊ณต๊ฒฉ ๋ฑ์ด ๊ฐ๋ฅํด์ง๋ค. (๋ฌผ๋ก ์์ด๋ ๊ฐ๋ฅํ๋ค. ์ข ๋ณต์กํด์ง ๋ฟ.)
์์ธํ ๊ฑด [[WinPwn - SEH Overwrite]] ์ฐธ๊ณ .
SafeSEH๋, `Handler` ๋ณ์์ ๋ด๊ธด `__except_handler()`์ ์ฃผ์๊ฐ ์คํ์ ์ฃผ์ ๋ฒ์์ ํฌํจ๋ ๋, ํน์ PE ํค๋์ `IMAGE_LOAD_CONFIG_DIRECTORY`์ `SEHandlerTable` ๊ฐ๊ณผ ๋ค๋ฅผ ๋ ์ด ํธ๋ค๋ฌ๋ฅผ ํธ์ถํ์ง ์๋ ๊ธฐ๋ฒ์ด๋ค. ์ ์ฝ๋๋ฅผ release ๋ชจ๋๋ก ์ปดํ์ผํ ๊ฒฝ์ฐ SafeSEH๊ฐ ๊ธฐ๋ณธ ์ ์ฉ๋๋ฉฐ, `handler()`์ ์ฃผ์๊ฐ `SEHandlerTable` ๊ฐ๊ณผ ๋ฌ๋ผ ์์ธ๊ฐ ๋ฐ์ํ ๊ฒ์ด๋ค.
(f) ntdll์ ์์ธ ํธ๋ค๋ง ์์ (x86)
์์ธ๊ฐ ๋ฐ์ํ๋ฉด ๋ค์๊ณผ ๊ฐ์ ์์ผ๋ก ํจ์๋ค์ด ์คํ๋๋ค.
ntdll!KiUserExceptionDispatcher()
ntdll!RtlDispatchException()
ntdll!RtlpExecuteHandlerForException()
ntdll!ExceptionHandler2()
SEH called here!
`ExceptionHandler2()` ํจ์์ ์ด์
๋ธ๋ฆฌ ์ฝ๋๋ ์๋์ ๊ฐ๋ค.
77229CDC | push ebp |
77229CDD | mov ebp,esp |
77229CDF | push dword ptr ss:[ebp+C] |
77229CE2 | push edx | edx: 77229D20
77229CE3 | push dword ptr fs:[0] |
77229CEA | mov dword ptr fs:[0],esp |
77229CF1 | push dword ptr ss:[ebp+14] |
77229CF4 | push dword ptr ss:[ebp+10] |
77229CF7 | push dword ptr ss:[ebp+C] |
77229CFA | push dword ptr ss:[ebp+8] |
77229CFD | mov ecx,dword ptr ss:[ebp+18] |
77229D00 | call ecx | call _except_handler
77229D02 | mov esp,dword ptr fs:[0] |
77229D09 | pop dword ptr fs:[0] |
77229D10 | mov esp,ebp |
77229D12 | pop ebp |
77229D13 | ret 14 |
77229D16 | lea esp,dword ptr ss:[esp] |
77229D1D | lea ecx,dword ptr ds:[ecx] |
77229D20 | mov ecx,dword ptr ss:[esp+4] | SEH chain์ ์ฒซ ๋
ธ๋๋ก ์ฝ์
๋จ
77229D24 | test dword ptr ds:[ecx+4],6 |
77229D2B | mov eax,1 |
77229D30 | jne ntdll.77229D44 |
77229D32 | mov ecx,dword ptr ss:[esp+8] |
77229D36 | mov edx,dword ptr ss:[esp+10] |
77229D3A | 8mov eax,dword ptr ds:[ecx+8] |
77229D3D | mov dword ptr ds:[edx],eax |
77229D3F | mov eax,2 |
77229D44 | ret 10 |
77229D47 | lea esp,dword ptr ss:[esp] |
77229D4E | mov edi,edi |
77229D50 | mov ecx,dword ptr ss:[esp+4] |
77229D54 | test dword ptr ds:[ecx+4],6 |
77229D5B | mov eax,1 |
77229D60 | je ntdll.77229D74 |
77229D62 | mov ecx,dword ptr ss:[esp+8] |
77229D66 | mov edx,dword ptr ss:[esp+10] |
77229D6A | mov eax,dword ptr ds:[ecx+8] |
77229D6D | mov dword ptr ds:[edx],eax |
77229D6F | mov eax,3 |
77229D74 | ret 10 |
์ด ํจ์์ ์์ ๋ถ๋ถ์์ `push ebp -> push fs:[0] -> mov fs:[0], esp`๋ฅผ ํตํด SEH Chain์ ์์ ์ง์ ์ ntdll ์์ญ์ ์ฃผ์๋ฅผ ์์ธ ํธ๋ค๋ฌ์ ์ฃผ์๋ก ํฌํจํ ์ ๋
ธ๋๋ฅผ ์ฐ๊ฒฐํ๋ค. ์์ธ ์ฒ๋ฆฌ๊ธฐ์์ ์์ธ๊ฐ ๋ฐ์ํ ๊ฒฝ์ฐ ์ฒ๋ฆฌ๋ฅผ ์ํ ๊ฒ์ด๊ณ , ์๋ก ๋ฑ๋ก๋ ํธ๋ค๋ฌ์ ์ฝ๋๋ ๋ค์๊ณผ ๊ฐ๋ค. ์ด ์ญ์ handler callback function์ ๊ตฌ์กฐ๋ฅผ ๋ฐ๋ฅธ๋ค.
77229D20 | mov ecx,dword ptr ss:[esp+4] | pRecord
77229D24 | test dword ptr ds:[ecx+4],6 | pRecord->ExceptionFlag์ 6์ ๋น๊ต
77229D2B | mov eax,1 | ExceptionContinueExecution
77229D30 | jne ntdll.77229D44 | return
77229D32 | mov ecx,dword ptr ss:[esp+8] | pFrame
77229D36 | mov edx,dword ptr ss:[esp+10] | 4th param, DispatcherContext
77229D3A | mov eax,dword ptr ds:[ecx+8] | ์ด๊ฑฐ ํธ์ถ ์ SEH ๋
ธ๋์ Frame ์ฃผ์
77229D3D | mov dword ptr ds:[edx],eax |
77229D3F | mov eax,2 | ExceptionNestedException
77229D44 | ret 10 |
์ด ์ฝ๋ฐฑ ํจ์๋ ์์ธ ์ฒ๋ฆฌ ์ค ์์ธ๊ฐ ๋ฐ์ํ ๊ฒฝ์ฐ ํธ์ถ๋๋ค. ์ด ํจ์๊ฐ ํธ์ถ๋์์ ๋์ `pRecord->ExceptionFlags` ๊ฐ๊ณผ 6์ and ์ฐ์ฐํ ๊ฐ์ด 0์ด ์๋๋ผ๋ฉด `ExceptionNestedException`์ ๋ฐํํ๋ค. (์ฌ์ค์ `ExceptionFlags`๊ฐ `EXCEPTION_UNWINDING(0x02)`, `EXCEPTION_EXIT_UNWIND(0x04)` ๊ฐ ์๋๋ผ๋ฉด ๋ชจ๋ `ExceptionNestedException`์ ๋ฐํํ๊ฒ ๋๋ค.)
์ด ์์ธ ์ฒ๋ฆฌ ํจ์๊ฐ ๋ฐํํ ๊ฐ์ `RtlDispatchException()`์ผ๋ก ๊ทธ๋๋ก ์ ๋ฌ๋๋ค. ์ด ํจ์๋ ๋ด๋ถ์ ์ผ๋ก ํธ์ถํ๋ `RtlpExecuteHandlerForException()` ํจ์์ ๋ฐํ ๊ฐ์ ๋ฐ๋ผ์ ์์ธ๋ฅผ ํด์ ํ๋ ๊ฒฝ์ฐ์๋ `DISPOSION_DISMISS`๋ฅผ, ์์ธ ์บก์ํ ์ฒ๋ฆฌ๊ธฐ ์์ค๊น์ง ์์ธ๋ฅผ ์ ๋ฌํด์ผ ํ๋ ๊ฒฝ์ฐ์๋ `DISPOSION_CONTINUE_SEARCH`์ ๊ฐ์ ๊ฒฐ๊ณผ๋ฅผ ๋ฐํํ๋ค. [__except_handler3](https://learn.microsoft.com/ko-kr/cpp/c-runtime-library/except-handler3?view=msvc-170)
์ ์ ๋ชจ๋ ์์ธ ์ฒ๋ฆฌ์ ๊ฐ์ฅ ๋ฐ๊นฅ์ชฝ์์ ํธ์ถ๋๋ ํจ์๋ `KiUserExceptionDispatcher()`์ด๋ค. ์์ธ๊ฐ ๋ฐ์ํ๊ณ ์ปค๋์์ ์ด๋ฅผ ์ธ์งํ ๋ค ์ ์ ๋ชจ๋๋ก ๋ณต๊ทํ ๋ ์ด ํจ์๊ฐ ์คํ๋๋ค. ์ด ํจ์๋ ์ปค๋์์ ํธ์ถ๋ ๋ `ExceptionRecord`์ `Context`๋ฅผ ์ธ์๋ก ๋ฐ๋๋ค. `KiUserExceptionDispatcher()`๋ ๋ด๋ถ์ ์ผ๋ก `RtlDispatchException()` ํจ์๋ฅผ ์คํํ๋๋ฐ, ์ด์ ๋ฐํ ๊ฐ์ ๋ฐ๋ผ `NtContinue()`๋ก ์์ธ ๋ฐ์ ์ฝ๋๋ถํฐ ์คํ๋๊ฑฐ๋(`DISPOSITION_DISMISS`์ ๊ฒฝ์ฐ) `NtRaiseException()`์ผ๋ก ์์ธ๋ฅผ ๋ค์ ๋ฐ์์์ผ(`DISPOSITION_CONTINUE_SEARCH`์ ๊ฒฝ์ฐ) ๋๋ฒ๊ฑฐ/์๋์ฐ์ ์์ธ ํธ๋ค๋ฌ๋ฅผ ์ฐพ๊ธฐ๋ ํ๋ค. `NtContinue()`๋ฅผ ํธ์ถํ๋ ๊ฒฝ์ฐ ์ธ์๋ก `Context`๋ฅผ ๋๊ฒจ์ฃผ๋๋ฐ, `NtContinue()`๋ `Context` ๊ตฌ์กฐ์ฒด์ ์์ ์ง์ ์ผ๋ก๋ถํฐ 0xB8 ์คํ์
๋งํผ ๋จ์ด์ง ๊ณณ์ ์์นํ ๋ฉค๋ฒ์ธ `pContext->Eip`๋ฅผ ๋ณด๊ณ ์คํ์ ์ฌ๊ฐํ ์์น๋ฅผ ํ๋จํ๋ค.
์ฌ๊ธฐ๊น์ง๋ ์ด์
๋ธ๋ฆฌ์ด ์์ค์์ SEH๋ฅผ ๋ง๋ค์์ ๋์ ์ด์ผ๊ธฐ๊ณ , Windows์์ ์ ๊ณตํ๋ `__try{}__except(){}` ๋ฌธ์ ์ด์ฉํ SEH๋ custom handler ํจ์๊ฐ ์๋๋ผ `__exception_handler3()`๊ฐ ํธ์ถ๋๋ค. (๊ทธ๋ ์ง๋ง ์ญ์ `FS:[0]`์ ์ด๋ฅผ ๋ฑ๋กํด๋๊ณ ์ฌ์ฉํ๋ค.) ์ด ๋์๋ Scope Table๊ณผ TryLevel์ด ์กด์ฌํ๋ค. (์ด๊ฑด ์ ๋ชจ๋ฅด๊ฒ ์ผ๋ ๊ฒ์ ใฑใฑ)
VC++์์ /GS ์ต์
์ ํ์ฑํํ๊ณ ์ปดํ์ผํ๋ฉด `__except_handler3()` ๋์ `__except_handler4()`๊ฐ ํธ์ถ๋๋๋ฐ, ์ด๋ TryLevel ์ด๊น๊ฐ์ด `0xFFFFFFFE`๋ผ๋ ๊ฒ๊ณผ Stack cookie ๊ฒ์ฌ๋ฅผ ํ๋ค๋ ๊ฒ ์ธ์๋ `__except_handler3()`๊ณผ ๋ฑํ ๋ค๋ฅธ ์ ์ด ์๋ค.
2) kernel32!SetUnhandledExceptionFilter()
SEH์์ ์์ธ๊ฐ ์ฒ๋ฆฌ๋์ง ์์๊ฑฐ๋ ๋ฑ๋ก๋ SEH๊ฐ ์์ ๊ฒฝ์ฐ kernel32!UnhandledExceptionFilter() API๊ฐ ํธ์ถ๋๋ค. kernel32!UnhandledExceptionFilter() ํจ์์์๋ Top Level Exception Filter๋ผ๋ ์์คํ
์ ๋ง์ง๋ง ์์ธ ์ฒ๋ฆฌ๊ธฐ๋ฅผ ์คํํด, '์๋์ด ์ค์ง๋์์ต๋๋ค' ๊ฒฝ๊ณ ์ฐฝ์ ๋์ฐ๊ณ ํ๋ก์ธ์ค๋ฅผ ์ข
๋ฃํ๋ค. ์ด ํจ์์์๋ ๋ด๋ถ์ ์ผ๋ก ntdll!NtQueryInformationProcess(ProcessDebugPort) API๋ฅผ ํธ์ถํ์ฌ ๋๋ฒ๊น
์ฌ๋ถ๋ฅผ ํ๋จ, ๋๋ฒ๊น
์ค์ด๋ผ๋ฉด ๋๋ฒ๊ฑฐ์ ์ ์ด๊ถ์ ๋๊ธฐ๊ณ , ๋๋ฒ๊น
์ค์ด ์๋๋ผ๋ฉด ์์คํ
์์ธ ์ฒ๋ฆฌ๊ธฐ์ ์ ๋ฌํ๋ค.
kernel32!SetUnhandledExceptionFilter() ํจ์๋ฅผ ํตํด ์์คํ
์ Top Level Exception Filter๋ฅผ ๋ณ๊ฒฝํ ์ ์๋ค. ์ด ํจ์์ ์ํ์ ์๋์ ๊ฐ๋ค.
LPTOP_LEVEL_EXCEPTION_FILTER SetUnhandledExceptionFilter(
[in] LPTOP_LEVEL_EXCEPTION_FILTER lpTopLevelExceptionFilter
);
[Microsoft: SetUnhandledExceptionFilter function (errhandlingapi.h)](https://learn.microsoft.com/en-us/windows/win32/api/errhandlingapi/nf-errhandlingapi-setunhandledexceptionfilter)
`lpTopLevelExceptionFilter` ์ธ์์ ์๋ก ๋ฑ๋กํ Top Level Exception Filter์ ํจ์ ์ฃผ์๋ฅผ ๋๊ฒจ์ฃผ๋ฉด ๋๋ค. ๋ฐํ ๊ฐ์ ์ด์ ์ Top Level Exception Filter ํจ์ ์ฃผ์. (์๋ค๋ฉด NULL.)
ํ๋ก์ธ์ค์์ ๊ณ ์๋ก ์์ธ๋ฅผ ๋ฐ์์ํค๊ณ ์ฌ์ฉ์ ์ ์ Top Level Exception Filter๋ฅผ ํตํด EIP ๊ฐ์ ์ฌ๋ฐ๋ฅด๊ฒ ์์ ํ๋ฉด, ๋๋ฒ๊น
์คํ ์์๋ ์์ธ๊ฐ ๋๋ฒ๊ฑฐ์์ ์ฒ๋ฆฌ๋ ์ ์์ผ๋ฏ๋ก ์ข
๋ฃ๋์ง๋ง, ๋๋ฒ๊น
์ค์ด ์๋ ๋์๋ ์์ธ๊ฐ Top Level Exception Filter์ ์ ๋ฌ๋์ด ์ฒ๋ฆฌ๋๋ฏ๋ก ์ ์ ์คํ์ด ๊ฐ๋ฅํ๋ค.
#### (1) `SetUnhandledExceptionFilter()` ์ฌ์ฉ ์์
์๋์ ๊ฐ์ด ์ฝ๋๋ฅผ ์์ฑํ ์ ์๋ค.
// x86 debug, VS19
#include <stdio.h>
#include <Windows.h>
#include <errhandlingapi.h>
LONG WINAPI handler(PEXCEPTION_POINTERS ExceptionInfo) {
MessageBox(NULL,
L"Handler called!",
L"Exception Handler",
MB_ICONERROR);
ExceptionInfo->ContextRecord->Eip += 7;
printf("Exception at %p\n", ExceptionInfo->ExceptionRecord->ExceptionAddress);
return EXCEPTION_CONTINUE_EXECUTION;
}
int main() {
SetUnhandledExceptionFilter(handler);
__asm {
xor eax, eax
mov dword ptr ds:[eax], 1 // Exception: Access Violation
}
printf("Exception Successfully Handled!\nContinue Execution... \n");
}
์ธ์๋ก ๋ฐ์ `EXCEPTION_POINTERS` ๊ตฌ์กฐ์ฒด ํฌ์ธํฐ์ `->ContextRecord->Eip`์ ๊ฐ์ ์ง์ ์์ ํด ์์ธ ๋ฐ์ ์ฝ๋๋ฅผ ๊ฑด๋๋ฐ๋ ๋ฐฉ์์ผ๋ก ์์ธ๋ฅผ ์ฒ๋ฆฌํ๋ `handler()` ํจ์๋ฅผ ๊ตฌํํ์๋ค.
์ ์ฝ๋๋ฅผ ๋๋ฒ๊ฑฐ์์ ์คํํ๋ฉด `mov dword ptr ds:[eax], 1` ๋ช
๋ น์์ ์์ธ๊ฐ ๋ฐ์ํ๊ณ , ๋๋ฒ๊ฑฐ๊ฐ ์์ธ๋ฅผ ์ฒ๋ฆฌํ์ง ๋ชปํ์ฌ ๋๋ฒ๊น
์ด ์ค์ง๋๋ค.
x64 ํ๊ฒฝ์์๋ ์๋์ ๊ฐ์ด, `Eip` ๋์ `Rip` ๋ฉค๋ฒ๋ฅผ ์์ ํด์ผ ํ๋ค.
//x64 debug, VS19
#include <stdio.h>
#include <Windows.h>
#include <errhandlingapi.h>
LONG WINAPI handler(PEXCEPTION_POINTERS ExceptionInfo) {
MessageBox(NULL,
L"Handler called!",
L"Exception Handler",
MB_ICONERROR);
ExceptionInfo->ContextRecord->Rip += 6;
printf("Exception at %p\n", ExceptionInfo->ExceptionRecord->ExceptionAddress);
return EXCEPTION_CONTINUE_EXECUTION;
}
int main() {
SetUnhandledExceptionFilter(handler);
int zero = 0;
int value = 3;
value = value / zero; // Exception: Division by 0
printf("Exception Successfully Handled!\nContinue Execution... \n");
}
### 3) ์ฐํ
๊ทธ๋ฅ ์คํ์ํจ ๋ค ์์ธ ๋ฐ์ ์ง์ ์ ์ง๋ debugger๋ฅผ attachํ๊ฑฐ๋, `Shift+F7/F8/F9` ๋ฑ์ ํค๋ฅผ ํตํด `StepIn/StepOver/Run` ์ ์์ธ ์ฒ๋ฆฌ๋ฅผ debugee์๊ฒ ๋๊ธฐ๊ฑฐ๋ ํ๋ฉด ๋๋ค. ์์ธ ์ฒ๋ฆฌ๊ธฐ๋ฅผ ๋๋ฒ๊น
ํ๊ณ ์ถ์ผ๋ฉด SEH Chain ๋ฑ๋ก ๋ถ๋ถ์์ pushํ๋ ์ฃผ์ (์ผ๋ฐํํ์๋ฉด x86๊ธฐ์ค `fs:[0]`์ ์ฎ๊ฒจ์ง๋ esp์ 4๋ฅผ ๋ํ ์ฃผ์์ ์ ์ฅ๋ ๊ฐ)๋ฅผ ์ดํด๋ณด๊ณ ๊ทธ ์ฃผ์์ ์ฝ๋์ breakpoint๋ฅผ ๊ฑธ๋ฉด ๋๊ณ , `SetUnhandledExceptionFilter()`๋ฅผ ์ด์ฉํ ๊ฒฝ์ฐ์๋ ์ธ์๋ก ๋ค์ด๊ฐ๋ `handler` ํจ์์ ์์ ์ง์ ์ bp๋ฅผ ์ค์นํ๋ฉด ๋๋ค. (๋ฌผ๋ก `Shift+F7/F8/F9`๋ก ์คํํด์ผ ์ด ํธ๋ค๋ฌ๊ฐ ์ ๋๋ก ํธ์ถ๋๋ค.)
(x64dbg์์๋ ์ด๋ฅผ erun/ego ๋ฑ์ผ๋ก ๋ถ๋ฅด๋ฉฐ, first change exception์ debuggee์ ๋๊ฒจ ์ฒ๋ฆฌํ๋ค. Swallow exception and run์ `Ctrl+Alt+Shift+F7/F8/F9`์ด๋ค.)
## 2. Software Breakpoint Detection
ํ๋ก์ธ์ค๋ ๋ฉ๋ชจ๋ฆฌ๋ฅผ ๊ฒ์ฌํจ์ผ๋ก์จ debugger์ ์กด์ฌ๋ฅผ ์ ์ ์๋ค. Debugger๋ ์คํ์ ๋ฉ์ถ๊ณ ์ ํ๋ instruction์ ์ฒซ 1๋ฐ์ดํธ๋ฅผ 0xCC (INT3)์ผ๋ก ํจ์นํ์ฌ software breakpoint๋ฅผ ๊ตฌํํ๋ค. ์ด ์ ์ ์ด์ฉํ์ฌ ์ฝ๋ ์์ญ์ ๋ฉ๋ชจ๋ฆฌ๋ฅผ ๊ฒ์ฌํจ์ผ๋ก์จ software bp๊ฐ ์ ์ฉ๋์๋์ง ํ๋จํ๊ณ ์ํฐ๋๋ฒ๊น
๋ฃจํด์ ์ ์ฉํ ์ ์๋ค.
###### +TODO!("undefined instruction(0f 0b)๋ฅผ ์ฌ์ฉํด software bp๋ฅผ ๊ตฌํํ๋ ๊ฒฝ์ฐ๋ ์์ผ๋ฏ๋ก ์ด๊ฒ์ coverํ ๋งํ ๊ตฌํ์ฒด๋ ํ์")
์๋๋ software bp๋ฅผ ํ์งํ๋ C++ ์ฝ๋์ด๋ค.
#include <Windows.h>
#include <wtypes.h>
#include <WinDNS.h>
#include <stdio.h>
BOOL checkByte(BYTE, PVOID, SIZE_T);
BOOL checkBP(PVOID, SIZE_T);
BOOL isJmpTable(PVOID);
int critical() {
printf("This function is critical!\n");
return 0;
}
int main() {
BOOL flag = checkBP((PVOID)critical, 0x50);
if (flag) {
printf("Software BP detected.\n");
}
else {
printf("Software BP not detected.\n");
}
}
BOOL checkByte(BYTE bByte, PVOID pMem, SIZE_T nSize = 0) {
PBYTE bMem = (PBYTE)pMem;
for (SIZE_T i = 0; i < nSize; i++) {
if (*(bMem + i) == 0xC3) {
break;
}
if (*(bMem + i) == bByte) {
return 1;
}
}
return 0;
}
BOOL checkBP(PVOID pMem, SIZE_T n) {
BOOL result = 0;
if (isJmpTable(pMem)) {
DWORD dwOffset = *(DWORD*)((PBYTE)pMem + 1) + 5; // 5: size of 1 complete jmp instruction (with operand)
PVOID pFunc = (PBYTE)pMem + dwOffset;
result = checkByte(0xCC, pFunc, n);
} else {
result = checkByte(0xCC, pMem, n); // when using real addr of function
}
return result;
/* [false positives]
- EncStackInitStart :
moves 0xCCCCCCCC to [edi] <- mov eax, 0xCCCCCCCC; rep stosd; (only x86)
- 0xCC as data :
some offset/data can include 0xCC
(how to detect them, then? -> needs to disassemble the function (WTF))
[false negatives]
- ret (0xC3) in the middle of the function
[shortcomings]
- Detecting function scope with just using machine code
Debugger inserts 0xCC to the start of the instruction to stop at. -> Then, increase index by the size of instruction (WTF2)
*/
}
BOOL isJmpTable(PVOID pMem) {
int i = 0, j = 0;
while (*((PBYTE)pMem + 5 * i) == 0xE9) { // if pattern is `jmp rel32`
i++;
}
while (*((PBYTE)pMem - 5 * j) == 0xE9) {
j++;
}
if (i + j > 0x10) { // false negative: small jmp tables
return 1;
} else {
return 0;
}
}
ํจ์ ์์ฒด๊ฐ high level์ ๊ฐ๋
์ด๊ธฐ ๋๋ฌธ์ ๊ธฐ๊ณ์ด ์ฝ๋๋ง ๊ฐ๊ณ ํ๋จํ๊ธฐ์๋ ํ๊ณ๊ฐ ์๋ค. ํจ์์ ์ฃผ์๋ก๋ถํฐ ์ ํด์ง ๋ฐ์ดํธ ์๋งํผ ์ฝ๊ณ ๊ฒ์ฌํ๋๋ก ๊ตฌํํ ์๋, `ret`๊น์ง๋ง ์ฝ๊ณ ๊ฒ์ฌํ๋๋ก ๊ตฌํํ ์๋ ์์ง๋ง (์ญ์ ๋ฆฌ๋ฒ์ ๋ง์๋๋ก) ์ฌ๊ธฐ์๋ ์ต๋ ์ฃผ์ด์ง ๊ธธ์ด๊น์ง ์ฝ๋ `ret` ๋ช
๋ น์ ๋ง๋๋ฉด ๊ฒ์ฌ๋ฅผ ์ข
๋ฃํ๋๋ก ๋ง๋ค์๋ค. (์ฌ์ฉ์๊ฐ ํจ์์ ์ค์ ํฌ๊ธฐ๋ฅผ ๋ชจ๋ฅด๋ฉฐ, ํจ์์ ํฌ๊ธฐ๋ณด๋ค ๋ ํฐ ๊ฐ์ ์ธ์๋ก ๋ฃ์ ๊ฒ์ ๊ฐ์ .)
msvc๋ก ์ปดํ์ผํ๋ ํจ์ ํธ์ถ ์ jmp table์ ๋ง๋ค์ด ์ฃผ๊ณ , ํจ์(์ฃผ์)๋ฅผ ์ธ์๋ก ์ ๋ฌํ ๋์๋ ์ด jmp table์ ์ฃผ์๋ฅผ ๋ฃ์ด์ค๋ค. ์ด ๋๋ฌธ์ ํจ์์ ๋ฉ๋ชจ๋ฆฌ ์์ญ์์ software bp์ ์ ๋ฌด๋ฅผ ๊ฒ์ฌํ๊ณ ์ถ๋ค๋ฉด jmp table์ ์์นํ๋ jmp instruction์ ์ธ์ (offset)์ jmp instruction ํ๋์ ํฌ๊ธฐ์ธ 5byte๋ฅผ ์ธ์๋ก ์ฃผ์ด์ง ์ฃผ์์ ๋ํ๋ฉด ๋๋ค. ์ด์ ๊ฐ์ ๊ธฐ๋ฅ์ ์ํํ๋ ์ฝ๋๋ `checkBP()` ์์ ์์นํ๋ค.
DWORD dwOffset = *(DWORD*)((PBYTE)pMem + 1) + 5;
PVOID pFunc = (PBYTE)pMem + dwOffset;
๋ฌผ๋ก ์ด ๊ฒฝ์ฐ๋ ๋ด ํ๊ฒฝ์ specificํ ๊ตฌํ์ด๊ธฐ ๋๋ฌธ์ ์ฐ์ค ๋์๋ ๋ณธ์ธ ํ๊ฒฝ์ ๋ง์ถฐ ์ ๊ตฌ์ฑํด ์ฐ์๋ฉด ๋๊ฒ ๋ค. ์ธ์๋ก ์ ๋ฌ๋ ํจ์์ ์ฃผ์๊ฐ jmp table์ ์ฃผ์์ธ์ง๋ฅผ ํ๋จํ๊ธฐ ์ํด `isJmpTable()`์ด๋ผ๋ ํจ์๋ฅผ ์์ฑํด ๋์๋๋ฐ, ์ ๋ค๋ก jmp ๋ช
๋ น์ด์ ๊ฐ์๋ฅผ ์ธ์ ์ด ๊ฐ์ด ํน์ ๊ฐ ์ด์์ด๋ผ๋ฉด jmp table๋ก ํ๋จํ๋ค. (ํด๋ฆฌ์คํฑ์ ์์กด)
๊ตฌํ์ด ๋จ์ํ ์๋ก false positive๊ฐ ๋ง์์ง๋ ๋ฐฉ๋ฒ์ด๋ฏ๋ก ๊ธฐ๊ณ์ด ์ฝ๋๋ฅผ instruction ๋จ์๋ก ์ฒ๋ฆฌํ ๊ฒ์ด ์๋๋ผ๋ฉด ์ฌ์ฉํ๋ ๊ฒ์ ์ถ์ฒํ์ง ์๋๋ค. false positive์ ํ ์์๋ก, msvc x86 debug mode๋ก ์ปดํ์ผํ๋ฉด ๊ธฐ๋ณธ `/RTC` ์ต์
์ด ์ถ๊ฐ๋์ด ์๋์ ๋น์ทํ ์ด์
๋ธ๋ฆฌ์ด ์ฝ๋๊ฐ ์ถ๊ฐ๋๋ค. (uninitialized stack memory๋ฅผ ๊ตฌ๋ณํ๊ธฐ ์ํจ)
```asm
mov edi, ebp
mov eax, 0xCCCCCCCC
rep stosd
```
`checkByte()`๋ ์ ์ฝ๋์ ๋ฐ์ดํฐ๋ก ๋ค์ด๊ฐ 0xCC ๋ฐ์ดํธ๋ฅผ ์ฝ๊ณ software bp๋ผ ํ๋จํ์ฌ true๋ฅผ ๋ฐํํ๋ค. ๋ฐ์ดํฐ์ ์ฝ๋๋ฅผ ์ ๊ตํ๊ฒ ๊ตฌ๋ถํ๋ ค๋ฉด ์ธ์๋ก ๋ฐ์ ํจ์์ ์ฝ๋๋ฅผ instruction ๋จ์๋ก ์ฒ๋ฆฌํ์ฌ์ผ ํ๋ฉฐ, ์ด ๋ฐฉ์์ ํจ์ ๋ด์์ ๋ช
๋ น์ด์ ์ค๊ฐ์ผ๋ก jmpํ๋ ๊ฒฝ์ฐ ๋ถ์์ ํ ๊ฒฐ๊ณผ๋ฅผ ๋ผ ์ ์๋ค.
###### + TODO!("x86 release, x86-64 debug-release ๋ชจ๋์ ๋ํ software bp detection code ์์ฑ for msvc & gcc"), ๊ฐ๋จํ๊ฒ ์ค๋ช
์ด๋ผ๋..
## 3. Hardware Breakpoint Detection
Hardware breakpoint๋ thread์ debug register์ ์คํ์ ๋ฉ์ถ๊ณ ์ ํ๋ ์ฃผ์๋ฅผ ๋ฃ๋ ๋ฐฉ์์ผ๋ก ์ฌ์ฉํ ์ ์๋ค. Hardware bp๋ thread ๋ด์์ Dr0~Dr3์ ์ ์ฅ๋ ์ฃผ์์ ์ ๊ทผ/์ฐ๊ธฐ/์คํ ๋ฑ์ ์ด๋ฒคํธ๊ฐ ๋ฐ์ํ ๋ single step interrupt (INT1)๋ฅผ ํธ๋ฆฌ๊ฑฐํ๋ค. (Hardware bp mechanism์ด ํ์ฌ ์คํ๋๋ ๋ช
๋ น์ ์ฃผ์๋ฅผ ๊ณ์ํด์ ํ์ธํจ) Hardware bp์ ์ํด INT1 ์ด๋ฒคํธ๊ฐ ๋ฐ์ํ๋ฉด, JTAG module์ด ํ๋ก์ธ์์ ์ ์ด๊ถ์ ๊ฐ์ ธ๊ฐ๋ค. Software breakpoint์ ๋ค๋ฅด๊ฒ hardware breakpoint๋ ์ํคํ
์ฒ์ ์์กด์ ์ด๋ฏ๋ก, cpu ์ค๊ณ์ ๋ฐ๋ผ ์๋ ๋ฐฉ์์ด ๋ค๋ฅผ ์ ์๋ค. ๋ณธ ๋ฌธ์์์๋ Intel x86์ ๊ธฐ์ค์ผ๋ก ์ค๋ช
ํ ๊ฒ์ด๋ค.
### 1) Debug Registers
Intel x86์๋ ์ด 7๊ฐ์ debug register๊ฐ ์กด์ฌํ๋ค. (Dr0 ~ Dr7). winnt.h ํค๋ ํ์ผ์ ์ ์๋์ด ์๋ `CONTEXT` ๊ตฌ์กฐ์ฒด์ Dr0 ~ Dr7์ด ๋ฐ๋ก debug register์ด๋ค. ์๋๋ `CONTEXT` ๊ตฌ์กฐ์ฒด์ ์ํ. winnt.h์ ์ฃผ์์ผ๋ก ์ค๋ช
์ด ์ ๋์ด์๋ค.
typedef struct DECLSPEC_NOINITALL _CONTEXT {
DWORD ContextFlags;
DWORD Dr0;
DWORD Dr1;
DWORD Dr2;
DWORD Dr3;
DWORD Dr6;
DWORD Dr7;
FLOATING_SAVE_AREA FloatSave;
DWORD SegGs;
DWORD SegFs;
DWORD SegEs;
DWORD SegDs;
DWORD Edi;
DWORD Esi;
DWORD Ebx;
DWORD Edx;
DWORD Ecx;
DWORD Eax;
DWORD Ebp;
DWORD Eip;
DWORD SegCs;
DWORD EFlags;
DWORD Esp;
DWORD SegSs;
BYTE ExtendedRegisters[MAXIMUM_SUPPORTED_EXTENSION];
} CONTEXT;
Dr0~Dr3 ๋ ์ง์คํฐ์๋ hardware bp๋ฅผ ์ค์ ํ ์ ํ ์ฃผ์(linear address)๊ฐ ์ ์ฅ๋๋ค. Dr6์๋ ๋นํธ ๋จ์๋ก debug status๊ฐ ์ ์ฅ๋๋๋ฐ, ์ด๋ debug exception handler๋ก ์ ์ด๊ถ์ด ๋์ด๊ฐ๊ธฐ ์ ์ ์ค์ ๋๋ค. Dr6์ ๊ฐ ๋นํธ๊ฐ ์๋ฏธํ๋ ๋ฐ๋ ์๋์ ๊ฐ๋ค.
###### +TODO!("์๋ ํ ์ ๋ฆฌ")
| Bits | Abbreviation | Description |
| ----- | ------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| 0 | B0 | Breakpoint #0 (Dr0) Condition Detected |
| 1 | B1 | Breakpoint #1 (Dr1) Condition Detected |
| 2 | B2 | Breakpoint #2 (Dr2) Condition Detected |
| 3 | B3 | Breakpoint #3 (Dr3) Condition Detected |
| 10:4 | — | Reserved. <br>Read as all-0s on 386/486 processors, all-1s on later processors. |
| 11 | BLD | Cleared to 0 by the processor for Bus Lock Trap exceptions.<br>On processors that don't support Bus Lock Trap exceptions, bit 11 of DR6 is a read-only bit, acting in the same way as bits 10:4. |
| 12 | BK, <br>SMMS | (386/486 only) SMM or ICE mode entered (see also DR7, bit 12). <br>Reserved and read as 0 on all later processors. |
| 13 | BD | Debug Register Access Detected (see also DR7, bit 13). (Never cleared by hardware - manually cleaning this bits in debug handler is recommended) |
| 14 | BS | Single-Step execution (enabled by EFLAGS.TF) (Never cleared by hardware - manually cleaning this bits in debug handler is recommended)(Never cleared by hardware - manually cleaning this bits in debug handler is recommended) |
| 15 | BT | Task Switch breakpoint.<br>Occurs when a task switch is done with a [TSS](https://en.wikipedia.org/wiki/Task_state_segment "Task state segment") that has the T (debug trap flag) bit set. (Never cleared by hardware - manually cleaning this bits in debug handler is recommended) |
| 16 | RTM | (Processors with [Intel TSX](https://en.wikipedia.org/wiki/Intel_TSX "Intel TSX") only) <br>Cleared to 0 by the processor for debug exceptions inside RTM transactions, set to 1 for all debug exceptions outside transactions. <br>On processors without TSX, bit 16 of DR6 is a read-only bit, acting in the same way as bits 31:17. |
| 31:17 | — | Reserved. <br>Read as all-0s on 386/486/6x86 processors, all-1s on later processors. |
| 63:32 | — | (x86-64 only) Reserved. <br>Read as all-0s. Must be written as all-0s. |
(์ถ์ฒ: [Wikipedia - x86 debug register](https://en.wikipedia.org/wiki/X86_debug_register))
Dr7์ debug control register๋ก, ๊ฐ ๋นํธ๋ ๊ฐ hardware bp์ ๋ํ ์ค๋จ ์กฐ๊ฑด์ ํํํ๋ ๋ฐ์ ์ฌ์ฉ๋๋ค. ๊ฐ ๋นํธ ํ๋์ ์๋ฏธ๋ ์๋์ ๊ฐ๋ค.
| Bits | Abbreviation | Description |
| ----- | ------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| 0 | L0 | Local enable for breakpoint #0. |
| 1 | G0 | Global enable for breakpoint #0. |
| 2 | L1 | Local enable for breakpoint #1. |
| 3 | G1 | Global enable for breakpoint #1. |
| 4 | L2 | Local enable for breakpoint #2. |
| 5 | G2 | Global enable for breakpoint #2. |
| 6 | L3 | Local enable for breakpoint #3. |
| 7 | G3 | Global enable for breakpoint #3. |
| 8 | LE | (386 only) Local Exact Breakpoint Enable. |
| 9 | GE | (386 only) Global Exact Breakpoint Enable. |
| 10 | — | Reserved, read-only, read as 1 and should be written as 1. |
| 11 | RTM | (Processors with [Intel TSX](https://en.wikipedia.org/wiki/Intel_TSX "Intel TSX") only) <br>Enable advanced debugging of RTM transactions (only if `DEBUGCTL` bit 15 is also set) <br>On other processors: reserved, read-only, read as 0 and should be written as 0. |
| 12 | IR, <br>SMIE | (386/486 processors only) Action on breakpoint match: <br>0 = INT 1 (#DB exception, default) <br>1 = Break to ICE/SMM<br>On other processors: Reserved, read-only, read as 0 and should be written as 0. |
| 13 | GD | General Detect Enable. If set, will cause a debug exception on any attempt at accessing the DR0-DR7 registers. |
| 15:14 | — | Reserved, should be written as all-0s. |
| 17:16 | R/W0 | Breakpoint condition for breakpoint #0. |
| 19:18 | LEN0 | Breakpoint length for breakpoint #0. |
| 21:20 | R/W1 | Breakpoint condition for breakpoint #1. |
| 23:22 | LEN1 | Breakpoint length for breakpoint #1. |
| 25:24 | R/W2 | Breakpoint condition for breakpoint #2. |
| 27:26 | LEN2 | Breakpoint length for breakpoint #2. |
| 29:28 | R/W3 | Breakpoint condition for breakpoint #3. |
| 31:30 | LEN3 | Breakpoint length for breakpoint #3. |
| 32 | DR0_PT_LOG | Enable DR0/1/2/3 breakpoint match as a trigger input for PTTT (Processor Trace Trigger Tracing).<br><br>Read as 0 and must be written as all-0s on processors that don't support PTTT. |
| 33 | DR1_PT_LOG | " |
| 34 | DR2_PT_LOG | " |
| 35 | DR3_PT_LOG | " |
| 63:36 | — | (x86-64 only) Reserved. <br>Read as all-0s. Must be written as all-0s. |
Breakpoint condition representation in bits:
|Value|Break on|
|---|---|
|`00b`|Instruction execution only|
|`01b`|Data writes only|
|`10b`|I/O reads and writes <br>(only defined if [CR4.DE](https://en.wikipedia.org/wiki/Control_register#CR4 "Control register")=1)|
|`11b`|Data reads and writes|
Breakpoint length representation in bits:
|Value|Breakpoint length|
|---|---|
|`00b`|1 byte|
|`01b`|2 bytes|
|`10b`|8 bytes <br>(only defined in 64-bit mode)|
|`11b`|4 bytes|
Dr4์ Dr5๋ ์ค์ ๋ ์ง์คํฐ๊ฐ ์๋๋ค. CR4.DE bit์ ์ง์ํ๋ CPU (Intel Pentium ์ดํ)์์ Dr4/5์ ํ๋์ CR4.DE์ ์ํด ๋ค์๊ณผ ๊ฐ์ด ์ ์๋๋ค.
CR4.DE = 0: Dr4์ Dr5๋ Dr6, Dr7์ alias๋ค.
CR4.DE = 1: Dr4/5์ ์ ๊ทผํ๋ ๊ฒ์ UD (undefined instruction) exception์ ๋ฐ์์ํจ๋ค.
Debug register๋ ํ์ ๋ ์์์ด๊ธฐ ๋๋ฌธ์ debug register์ ๊ฐ์ ์ฐ๋ ๊ฒฝ์ฐ, ring 0 (kernel mode)์ ๊ถํ์ด ํ์ํ๋ค. (๋ฎ์ ๊ถํ์์ debug register์ ์ ๊ทผ ์ general protection fault ๋ฐ์)
#### (1) GetThreadContext()
##### (a) GetThreadContext() Example
Windows๊ฐ ์ ๊ณตํ๋ `GetThreadContext()` API๋ฅผ ์ฌ์ฉํด์ thread์ context๋ฅผ ์ป์ด์ฌ ์ ์๋ค. Context์ ํฌํจ๋ debug register๋ฅผ ํ์ธํ์ฌ hardware breakpoint๊ฐ ์ค์ ๋์๋์ง ํ์
ํ ์ ์๋ค.
์๋๋ thread์ context๋ฅผ ๋ฐ์์ ์ฝ๋ C ์ฝ๋์ด๋ค.
// x86 debug, VS19
#include <stdio.h>
#include <Windows.h>
#include <errhandlingapi.h>
int main() {
CONTEXT ctx;
DWORD threadId = GetCurrentThreadId();
HANDLE hThread = OpenThread(THREAD_GET_CONTEXT, FALSE, threadId);
DWORD e = GetLastError();
if(e) {
printf("error info: 0x%x\n", e);
}
printf("ThreadId: %x\n", threadId);
printf("hThread: %p\n", hThread);
// ContextFlags๋ฅผ ๋จผ์ ์ค์ ์ ํด์ฃผ๋ฉด 0x3eb ์ค๋ฅ ๋ฐ์
ctx.ContextFlags = CONTEXT_ALL;
if (GetThreadContext(hThread, &ctx) != 0) {
printf("\n");
printf("[registers]\n");
printf("Eip: 0x%08x\n", ctx.Eip);
printf("Ebp: 0x%08x\n", ctx.Ebp);
printf("Esp: 0x%08x\n", ctx.Esp);
printf("Eax: 0x%08x\n\n", ctx.Eax);
printf("[debug registers]\n");
printf("Dr0: 0x%08x\n", ctx.Dr0);
printf("Dr1: 0x%08x\n", ctx.Dr1);
printf("Dr2: 0x%08x\n", ctx.Dr2);
printf("Dr3: 0x%08x\n", ctx.Dr3);
printf("Dr6: 0x%08x\n", ctx.Dr6);
printf("Dr7: 0x%08x\n", ctx.Dr7);
} else {
printf("failed to get the context\n");
e = GetLastError();
printf("error info: 0x%x\n", e);
}
}
`GetThreadId()`๋ก ํ์ฌ thread์ ID๋ฅผ ์ป์ด์จ ๋ค์, `OpenThread()`๋ก ์ด thread์ ๋ํด `THREAD_GET_CONTEXT` ๊ถํ์ ๊ฐ์ง ํธ๋ค์ ๋ฐ์์จ๋ค. `GetThreadContext()`๋ฅผ ํธ์ถํ๊ธฐ ์ ์, `CONTEXT` ๊ตฌ์กฐ์ฒด์ `ContextFlags` ๋ฉค๋ฒ๋ฅผ ์์ ํด์ผ ํ๋ค. (`GetThreadContext()`๋ `Context.ContextFlags` ๋ฉค๋ฒ๋ฅผ ์ฝ๊ณ context์ ์ ์ฅํด์ผ ํ ๋ถ๋ถ์ ํ๋จํ๊ธฐ ๋๋ฌธ.) ์ด ์์ ์์๋ ๋จ์ํ context ์ ๋ณด ์ ์ฒด๋ฅผ ๋ฐ์์ค๊ธฐ ์ํด `CONTEXT_ALL`์ ์ฌ์ฉํ์์ผ๋, debug register๋ง ์ป์ด์ค๊ธฐ ์ํด์๋ `CONTEXT_DEBUG_REGISTERS`๋ฅผ ์ฌ์ฉํ ์ ์๋ค.
###### (a-1) CONTEXT.ContextFlags ๊ฐ
ContextFlags์๋ ๋ค์๊ณผ ๊ฐ์ ์์ ๊ฐ๋ค์ด ์ฌ ์ ์๋ค. (winnt.h ์ฐธ๊ณ )
#define CONTEXT_CONTROL (CONTEXT_i386 | 0x00000001L) // SS:SP, CS:IP, FLAGS, BP
#define CONTEXT_INTEGER (CONTEXT_i386 | 0x00000002L) // AX, BX, CX, DX, SI, DI
#define CONTEXT_SEGMENTS (CONTEXT_i386 | 0x00000004L) // DS, ES, FS, GS
#define CONTEXT_FLOATING_POINT (CONTEXT_i386 | 0x00000008L) // 387 state
#define CONTEXT_DEBUG_REGISTERS (CONTEXT_i386 | 0x00000010L) // DB 0-3,6,7
#define CONTEXT_EXTENDED_REGISTERS (CONTEXT_i386 | 0x00000020L) // cpu specific extensions
#define CONTEXT_FULL (CONTEXT_CONTROL | CONTEXT_INTEGER | CONTEXT_SEGMENTS)
#define CONTEXT_ALL (CONTEXT_CONTROL | CONTEXT_INTEGER | CONTEXT_SEGMENTS | CONTEXT_FLOATING_POINT | CONTEXT_DEBUG_REGISTERS | CONTEXT_EXTENDED_REGISTERS)
#define CONTEXT_XSTATE (CONTEXT_i386 | 0x00000040L)
#define CONTEXT_EXCEPTION_ACTIVE 0x08000000L
#define CONTEXT_SERVICE_ACTIVE 0x10000000L
#define CONTEXT_EXCEPTION_REQUEST 0x40000000L
#define CONTEXT_EXCEPTION_REPORTING 0x80000000L
`Context.ContextFlags`๋ฅผ ์ค์ ํ์ผ๋ฉด, `GetThreadContext()`๋ฅผ ํธ์ถํ์ฌ context๋ฅผ ๋ฐ์์จ๋ค. ๊ทธ ๋ค์ context์ ๊ฐ ๋ฉค๋ฒ์ ์ ๊ทผํ์ฌ ์ด๋ค ๊ฐ์ด ์ ์ฅ๋์๋์ง ํ์ธํ ์ ์๋ค.
debugger๋ฅผ ๋ถ์ฌ hardware bp๋ฅผ ์ค์ ํ๊ณ ํ๋ก๊ทธ๋จ์ ์คํํ์ฌ ๊ฒฐ๊ณผ๋ฅผ ํ์ธํด๋ณด๊ธธ ๋ฐ๋๋ค.
ThreadId: 54a0
hThread: 00000140
[registers]
Eip: 0x77637b0c
Ebp: 0x0133f71c
Esp: 0x0133f710
Eax: 0x00000000
[debug registers]
Dr0: 0x00191994
Dr1: 0x00195607
Dr2: 0x00000000
Dr3: 0x00000000
Dr6: 0xffff0ff0
Dr7: 0x00000005
๋๋ฒ๊ฑฐ๋ฅผ ๋ถ์ฌ ์คํํ๋ฉด ์์ ๊ฐ์ด ์ค์ ๋ hardware bp๋ฅผ ํ์ธํ ์ ์๋ค.
์ด์ ์ด ์ ๋ณด๋ฅผ ์ฝ์ด์ anti-debugging์ ์ ์ฉํด๋ณด์.
##### (b) Anti-debugging with GetThreadContext()
๊ฐ๋จํ Dr0 ~ Dr3์ 0์ด ์๋ ๊ฐ์ด ๋ค์ด์๋์ง๋ฅผ ํ์ธํ์ฌ ํ๋ก๊ทธ๋จ์ ์ข
๋ฃ์ํค๋ ๋ฑ์ ์์
์ ํ ์ ์๋ค. ์๋์ ์ฝ๋๋ hardware breakpoint check๋ฅผ ์ํํ๊ณ ๊ทธ ๊ฒฐ๊ณผ๋ฅผ ์ถ๋ ฅํ๋ค.
// x86 debug, VS19
#include <stdio.h>
#include <Windows.h>
#include <errhandlingapi.h>
int main() {
CONTEXT ctx;
DWORD threadId = GetCurrentThreadId();
HANDLE hThread = OpenThread(THREAD_GET_CONTEXT, FALSE, threadId);
ctx.ContextFlags = CONTEXT_DEBUG_REGISTERS;
if (GetThreadContext(hThread, &ctx) != 0) {
if ((ctx.Dr0 | ctx.Dr1 | ctx.Dr2 | ctx.Dr3) != 0) {
printf("Hardware breakpoint detected.\n");
} else {
printf("Hardware breakpoint not detected.\n");
}
}
else {
printf("failed to get the context\n");
DWORD e = GetLastError();
printf("error info: 0x%x\n", e);
}
}
์ง์ ๋๋ฒ๊น
ํด๋ณด๋ฉฐ ํ๋จํ๊ธธ ๋ฐ๋๋ค.
#### (2) Set SEH to check debug registers
## 4. Single Step Check
### 1) Trap Flag (TF)
### 2) PUSHFD/POPFD
### 3) INT 2D
## 5. Timing Check
### 1) RDTSC
### 2) QueryPerformanceCount()
### 3) GetTickCount()
### 4) timeGetTime()
#### (1) \_ftime()
## 6. Patching Detection
### 1) Scanning 0xCC
### 2) Checksum
## 7. Anti-Disassembly
### 1) Jump Over Invalid Bytes
### 2) Instruction Nesting
## 8. PE Image Switching
## 9. Self-Execution
## 10. Self-Debugging
## 11. Nanomite
## 12. Code Obfuscation
## 13. Encryption/Decryption
## 14. Stolen Bytes
## 15. Multi-Threaded Packers
## 16. Code Virtualization
## 17. API Redirection
## 18. API Wrapping
## 19. ptrace (linux)
## 20. Platform-Independent Program (PIP)
## 21. Execution-based Steganography
# Static Anti-Reversing Techniques
## 1. PEB
### 1) PEB.BeingDebugged (PEB + 0x02)
### 2) PEB.Ldr (PEB + 0x0C)
### 3) PEB.ProcessHeap (PEB + 0x18)
#### (1) SegmentFlags (\_Heap + 0x0C)
#### (2) SegmentListEntry (\_Heap + 0x10)
#### (3) Flags (\_Heap + 0x40)
#### (4) ForceFlags (\_Heap + 0x44)
### 4) NtGlobalFlag (PEB + 0x68)
## 2. TEB
## 3. Native APIs
### 1) ntdll!NtQueryInformationProcess(), ntdll!ZwQueryInformationProcess()
### 2) ntdll!NtQuerySystemInformation()
### 3) ntdll!NtQueryObject()
### 4) SeDebugPrivilege()
## 4. TLS callback function
## 5. Using Normal APIs
### 1) FindWindow(), FindWindowEx() (Window Name)
### 2) Process32First(), Process32Next() (Parent Process)
# Attacking Debugger
## 1. ntdll!NtSetInformationThread(), ntdll!ZwSetInformationThread()
## 2. user32!BlockInput()
## 3. Disable breakpoints
## 4. kernel32!UnhandledExceptionFilter()
## 5. Exploit Format String Vulns in debugger
## 6. Set
๊ท์ฐฎ์์ ์์ง ์ ๋ฐ์ดํธ ์ ํ ๊ธ๋ค์ด ์ฐ๋๋ฏธ์ ๋๋ค. ์ํฐ ๋ฆฌ๋ฒ์ฑ ๊ธฐ๋ฒ ์ค์๋ ์ฌ๋ฏธ์๋ ๊ฒ์ด ์์ฃผ ๋ง์ผ๋ ํ ๋ฒ์ฉ ์คํํด๋ณด์ธ์.
๊ฐ์ธ ์ต์๋์ธ์ ๋งํฌ๋ค์ด ํ์์ผ๋ก ์ ๋ฆฌํ๋ ๊ฒ๋ค์ด๋ผ html ํ๊ฒฝ์์๋ ๊ฐ๋ ์ฑ์ด ์ข ๋จ์ด์ง๋๋ค.
์๋ง 2025๋ ์ด ๋๋ ์ฆ์์๋ ์์ฑ๋์ด ์์ง ์์๊น... ๋ผ๊ณ ์๊ฐ๋ฉ๋๋ค.