์ด๋ฒ ๋ด์ฉ์ ์คํ
์ค ์ธ์ ์
๊ธฐ๋ฒ ์ค ๋น๊ต์ ๊ณ ๊ธ ํํ์ธ
โThread Hiding + APC ๊ธฐ๋ฐ ์ฝ๋ ์คํ ํ๋ฆ ๊ฐ์ถ๊ธฐโ ํจํด์ ๋ถ์ํ ๋ด์ฉ์.
๋ฆฌ๋ฒ์ฑ ๊ธฐ๋ก
1. ๊ฐ์ โ Thread Hiding ๊ธฐ๋ฒ ๊ฐ๋
์ผ๋ถ ์ํ์ ๊ธฐ๋ณธ์ ์ธ CreateRemoteThread ์ฌ์ฉ์ ํผํ๊ณ ,
์ด๋ฏธ ์กด์ฌํ๋ ํ๊น ํ๋ก์ธ์ค์ ์ค๋ ๋์
**APC(Asynchronous Procedure Call)**๋ฅผ ํ์ํ๊ฑฐ๋
Thread State๋ฅผ Suspended โ Running์ผ๋ก ๋ฏธ์ธ ์กฐ์ํ์ฌ
์ฝ๋ ์คํ ํ๋ฆ์ ๊ฐ์ถ๋ค.
ํต์ฌ ๋ชฉํ:
- ์ค๋ ๋ ์์ ์ฃผ์(Start Address)๋ก ๋๋ฌ๋๋ ์ธ์ ์ ํ์ ์ต์ํ
- ๋๋ฒ๊ฑฐยทEDR์ ์ค๋ ๋ ์์ฑ ์ด๋ฒคํธ ํ์ง ํํผ
- APC ์ค์ผ์ค ํ์ด๋ฐ์ ์ด์ฉํด ํธ์ถ ์คํ์ ์ํ
2. ๊ฐ๋ ์ ์ธ์ ์ ํ๋ฆ๋
[1] ํ๊น ํ๋ก์ธ์ค ์ด๊ธฐ
โ
[2] ๋ฉ๋ชจ๋ฆฌ ๊ณต๊ฐ ํ๋ณด(VirtualAllocEx)
โ
[3] Payload ์ฐ๊ธฐ
โ
[4] ํ๊น์ ๊ธฐ์กด ์ค๋ ๋ ํธ๋ค ํ๋(Thread32First/Next)
โ
[5] APC ํ์(QueueUserAPC)
โ
[6] ์ค๋ ๋๊ฐ Alertable ์ํ๋ก ์ง์
ํ ๋ ์คํ
3. PE ๊ตฌ์กฐ ๋ฐ API ๋์ ํด์(Export Directory ์ค์บ ๊ธฐ๋ฐ)
์
์ฑ์ฝ๋๋ ๋ณดํต API ๋ฌธ์์ด์ ์ง์ ํฌํจํ์ง ์๋๋ค.
๋ค์์ C๋ก ์์ฑ๋ ์์๋ก, Export Table์ ์ํํ์ฌ
ํจ์ ํด์๋ฅผ ๋น๊ตํด ์ฃผ์๋ฅผ ์ฐพ๋ ๋ฃจํด์ ํํ๋ฅผ ๋ณด์ฌ์ค๋ค.
// ์์ โ Export ์ค์บ ๊ธฐ๋ฐ ๋์ API ๊ฒ์
void* ResolveAPI(void* moduleBase, unsigned int targetHash) {
IMAGE_DOS_HEADER* dos = (IMAGE_DOS_HEADER*)moduleBase;
IMAGE_NT_HEADERS* nt = (IMAGE_NT_HEADERS*)((BYTE*)moduleBase + dos->e_lfanew);
IMAGE_EXPORT_DIRECTORY* exp =
(IMAGE_EXPORT_DIRECTORY*)((BYTE*)moduleBase +
nt->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress);
DWORD* nameRVA = (DWORD*)((BYTE*)moduleBase + exp->AddressOfNames);
WORD* ordinal = (WORD*)((BYTE*)moduleBase + exp->AddressOfNameOrdinals);
DWORD* funcRVA = (DWORD*)((BYTE*)moduleBase + exp->AddressOfFunctions);
for (DWORD i = 0; i < exp->NumberOfNames; i++) {
char* name = (char*)moduleBase + nameRVA[i];
unsigned int h = HashFunctionConcept(name); // ํด์
if (h == targetHash) {
void* fn = (BYTE*)moduleBase + funcRVA[ordinal[i]];
return fn;
}
}
return NULL;
}
4. APC ๊ธฐ๋ฐ ์ฝ๋ ์คํ ๋์ค์ด์ ๋ธ๋ฆฌ ํจํด ๋ถ์
๋ค์์ APC๋ฅผ ํตํด ์คํ๋๋ ํจ์์
x86 ๋์ค์ด์
๋ธ๋ฆฌ ๋ถ์.
; ์์ โ APC๋ก ํธ์ถ๋ ํจ์์ ํ๋กค๋ก๊ทธ ํจํด
APC_PAYLOAD:
push ebp
mov ebp, esp
sub esp, 0x20 ; ๋ก์ปฌ ๋ฒํผ ํ๋ณด
mov eax, [ebp+0x08] ; APC ์ ๋ฌ ํ๋ผ๋ฏธํฐ
xor ecx, ecx ; ๋ ์ง์คํฐ ์ด๊ธฐํ
mov [ebp-0x04], eax ; Stack frame ๋ด๋ถ์ ์ ์ฅ
; --- ๋ฉ๋ชจ๋ฆฌ ์กฐ์ ๋๋ ์คํ
์ค ๋ก์ง (๊ฐ๋
) ---
; ์: ํน์ ๋ฉ๋ชจ๋ฆฌ ํ์ด์ง์ ํ๋๊ทธ ํ์ธ
mov edx, [eax] ; (๋ฉ๋ชจ๋ฆฌ ์ฐธ์กฐ)
test edx, edx
jz SHORT .exit_path
; ์ถ๊ฐ ์ฐ์ฐ ๋๋ ๋ถ๊ธฐ
add edx, 0x10
mov [ebp-0x08], edx
.exit_path:
mov esp, ebp
pop ebp
ret 4
โถ ์คํ ํ๋ ์ ๊ตฌ์ฑ ํด์
| ์์น | ์ค๋ช |
|---|---|
[ebp+8] | APC ํจ์์ ์ฒซ ๋ฒ์งธ ํ๋ผ๋ฏธํฐ |
[ebp-4] | ์์ ์ ์ฅ์ฉ ๋ก์ปฌ ๋ณ์ |
[ebp-8] | ๋ถ๊ธฐ ๊ณ์ฐ์ฉ ๋ก์ปฌ ๋ฒํผ |
โถ ์ ์ด ํ๋ฆ ๋ถ์ ํฌ์ธํธ
jz SHORT .exit_path๋ ์กฐ๊ฑด๋ถ๋ก ์คํ ์ค ๋ก์ง์ ๊ฑด๋๋ฐ์ด ๋ถ์์ ๋ฐฉํดํ ์ ์์- ๋ถ์ ์ ์กฐ๊ฑด์ ์ญ๋ฐฉํฅ์ผ๋ก ์ถ์ ํด ์ค์ ์คํ ๊ฒฝ๋ก๋ฅผ ํ์ธํด์ผ ํจ
- EAX/EDX์ ์ค์ ์ฌ์ฉ ํจํด์ด payload ์ฑ๊ฒฉ์ ๊ฒฐ์
5. Anti-Debugging ์ฐ๊ณ ํจํด
APC ๊ธฐ๋ฐ ์ธ์ ์ ์ ์ข ์ข ๋๋ฒ๊ฑฐ ํ์ง์ ํจ๊ป ์ฌ์ฉ๋๋ค.
x86 ์ฝ๋:
mov eax, fs:[0x30] ; PEB
mov al, [eax+0x02] ; BeingDebugged
test al, al
jnz SHORT DEBUG_EXIT ; ๋๋ฒ๊ฑฐ ์์ผ๋ฉด ๋ค๋ฅธ ๊ฒฝ๋ก
; ํ์ด๋ฐ ์ฒดํฌ ๊ธฐ๋ฐ ์ํฐ ๋๋ฒ๊น
rdtsc
mov ebx, eax
rdtsc
sub eax, ebx
cmp eax, 0x00001000 ; ์๊ณ๊ฐ ๊ธฐ์ค
jg SHORT DEBUG_EXIT
; ์ ์์ด๋ฉด ๊ณ์
jmp CONTINUE_EXECUTION
ํน์ง
- PEB ํ๋๊ทธ ํ์ธ
RDTSC์ฌ์ดํด ์ฐจ์ด๋ก single-step, breakpoints, VM ์๋ ์ ํ ๊ฐ์ง- APC ํ์ด๋ก๋๊ฐ ๋๋ฒ๊ฑฐ ์ฐํ ๊ฒฝ๋ก๋ก๋ง ์คํ๋๋๋ก ์กฐ๊ฑด ๋ถ๊ธฐ ๊ตฌ์ฑ ๊ฐ๋ฅ
6. Thread Hiding ๊ตฌํ ๊ฐ๋
์๋๋ C ์ธ์ด ์์์ด๋ค.
// ๊ฐ๋
์์ โ Thread Hide
void HideThreadContext(HANDLE hThread) {
// 1) ์ค๋ ๋ ์ปจํ
์คํธ ๊ฐ์ ธ์ค๊ธฐ
CONTEXT ctx = {0};
ctx.ContextFlags = CONTEXT_FULL;
GetThreadContext(hThread, &ctx);
// 2) ์คํ ํฌ์ธํฐ ๋ฐ ๋ช
๋ น ํฌ์ธํฐ ์กฐ์ (๊ฐ๋
์ )
ctx.Esp -= 0x200; // ์๋ก์ด ์คํ ์์ญ
ctx.Eip = (DWORD)APC_PAYLOAD; // ํ์ด๋ก๋ ์ํธ๋ฆฌ
// 3) ์กฐ์๋ ์ปจํ
์คํธ ๋ฐ์
SetThreadContext(hThread, &ctx);
}
์ด ๊ณผ์ ์ด ์ค์ ๋ก๋ ๋ค์๊ณผ ๊ฐ์ ์ ์์ ํ์ง ํํผ์ ๊ธฐ์ฌํ๋ค:
- ์ค๋ ๋ ์์ ์ฃผ์(Start Address)์ ์คํ ์์น๊ฐ ๋ถ์ผ์น
- ์คํ ํ๋ ์์ด ์ ์ API ํธ์ถ ์คํ ํํ๋ฅผ ๊ฐ์ถ์ง ์์
- ์ค๋ ๋๊ฐ Alertable ์ํ ์ ํ ์์๋ง ๋น์ ์ ์ฝ๋๊ฐ ์คํ๋จ
7. Call Stack ํ๋ฆ ๋ถ์ ๊ธฐ๋ฒ
APC ๊ธฐ๋ฐ ํ์ด๋ก๋๋ Call Stack์ด ๋ค์์ฒ๋ผ ๊ตฌ์ฑ๋๋ค:
[ ReturnAddress to ntdll!KiUserApcDispatcher ]
[ APC_PAYLOAD arguments ]
[ ... local stack frame ... ]
๋ถ์ ํฌ์ธํธ:
- ReturnAddress๊ฐ ํญ์
KiUserApcDispatcher๊ทผ์ฒ - ์ ์ ์ค๋ ๋์ Call Stack ํจํด๊ณผ ๋ถ์ผ์น
- ํ์ด๋ก๋ ์ง์
์ ์ ํธ์ถ ๊ท์ฝ์ด ๋ช
ํํ์ง ์์
stdcall/cdecl ๊ตฌ๋ถ์ด ๋ชจํธํ ํํ๋ก ๋ํ๋จ - ์คํ ๋ณต๊ตฌ(
mov esp, ebp; pop ebp)๊ฐ ์ ๋๋ก ๋ง์ง ์์ผ๋ฉด
๋ค๋ฅธ APC์ ์ถฉ๋ ์ํ
8. ์ํธํ๋ ํ์ด๋ก๋ ๋ณต์(๊ฐ๋ )
๋๋ถ๋ถ์ APC ํ์ด๋ก๋๋ ์์ถยท์ํธํ๋์ด ์์ผ๋ฉฐ,
๋ณต์ ๋ฃจํด์ ๋ค์๊ณผ ๊ฐ์ ํจํด์ ๊ฐ๋๋ค.
// ์์
void DecryptConcept(BYTE* buf, size_t len, BYTE key) {
for (size_t i = 0; i < len; i++) {
buf[i] ^= key; // ๋จ์ XOR ๊ฐ๋
buf[i] = rol(buf[i], 3); // ROL ๊ธฐ๋ฐ ๋ณํ
}
}
๋์ค์ด์ ๋ธ๋ฆฌ ํน์ง:
movzx eax, byte ptr [ecx]
xor al, dl
rol al, 3
mov [ecx], al
inc ecx
cmp ecx, edi
jb SHORT LOOP
โจ ๋ง๋ฌด๋ฆฌ ํ ์ค
APC ๊ธฐ๋ฐ ์ค๋ ๋ ํ์ด๋ฉ์ โ์ค๋ ๋ ์์ฑ ์ด๋ฒคํธ ์์ด ์ฝ๋ ์คํ ํ๋ฆ์ ์ ํํ๋โ ํน์ฑ ๋๋ถ์
์ ์ ยท๋์ ๋ถ์ ๋ชจ๋์์ ํ์ง ๋๋๊ฐ ๋๋ค.
๋ถ์๊ฐ๋ ์ค๋ ๋ ์ปจํ
์คํธ, ์ฝ์คํ ํจํด, Export ๊ธฐ๋ฐ API ํด์ ๋ฃจํด์
์ ๋ฐํ๊ฒ ์ถ์ ํด์ผ ์ ์ฒด ํ๋ฆ์ ๋ณต์ํ ์ ์๋ค.
๐ Written by Code & Compass