๐Ÿงฉ ๋ฏธ๋‹ˆ๋ฉ€ ๋กœ๋”

๋™์  API ํ•ด์„(dynamic API resolution) ํŒจํ„ด๊ณผ, ๊ทธ ๊ณผ์ •์—์„œ ๋‚˜ํƒ€๋‚˜๋Š” PE ๊ตฌ์กฐ ๊ธฐ๋ฐ˜ ํƒ์ƒ‰, ํ˜ธ์ถœ ๊ทœ์•ฝ, ๋ ˆ์ง€์Šคํ„ฐ ์‚ฌ์šฉ ๋ฐฉ์‹, ๊ทธ๋ฆฌ๊ณ  ๊ธฐ๋ณธ์ ์ธ ์•ˆํ‹ฐ๋””๋ฒ„๊น… ํ๋ฆ„์„ ํŒŒ์•…ํ•˜๋Š” ๋‚ด์šฉ.

๋ฆฌ๋ฒ„์‹ฑ ๊ธฐ๋ก


1. ๊ฐœ์š”

์ผ๋ถ€ ์•…์„ฑ ๋กœ๋”๋Š” Import Table ์„ ์ด์šฉํ•˜์ง€ ์•Š๊ณ  ์ปค์Šคํ…€ PE ํŒŒ์„œ๋ฅผ ํ†ตํ•ด ํ•„์š”ํ•œ API ์ฃผ์†Œ๋ฅผ ์ง์ ‘ ํš๋“ํ•ฉ๋‹ˆ๋‹ค.
์ด ํŒจํ„ด์€ ์ •์  ๋ถ„์„์„ ๋ฐฉํ•ดํ•˜๊ณ , ๋‹จ์ˆœํ•œ ๋ฌธ์ž์—ด ๊ธฐ๋ฐ˜ ํƒ์ง€๋ฅผ ์šฐํšŒํ•˜๊ธฐ ์œ„ํ•ด ํ”ํžˆ ์‚ฌ์šฉ๋ฉ๋‹ˆ๋‹ค.

  • ๊ณ ์ •๋œ DLL ์ด๋ฆ„ ๋ฌธ์ž์—ด
  • API ์ด๋ฆ„ Hashing
  • ์ปค์Šคํ…€ ์ฃผ์†Œ ํƒ์ƒ‰ ๋ฃจํ‹ด
  • ๋ฌดํ•ดํ•œ โ€œplaceholderโ€ ํ˜ธ์ถœ

2. C ๊ธฐ๋ฐ˜ ๋กœ๋” ๊ตฌ์กฐ(๋น„ํ™œ์„ฑ/๋ฌดํ•ด ์˜ˆ์‹œ)

์•„๋ž˜ ์ฝ”๋“œ๋Š” ์‹ค์ œ ์•…์„ฑ ๊ธฐ๋Šฅ์ด ์ œ๊ฑฐ๋œ ์•ˆ์ „ํ•œ ๊ตฌ์กฐ ์˜ˆ์‹œ์ž…๋‹ˆ๋‹ค.
๋ถ„์„ ํŒจํ„ด ์—ฐ๊ตฌ ๋ชฉ์ ์ด๋ฉฐ, ์‹คํ–‰ํ•ด๋„ ์•„๋ฌด ์ž‘์—…๋„ ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

#include <stdint.h>
#include <windows.h>

typedef FARPROC(WINAPI* ResolveFn)(HMODULE, const char*);

// ๋‹จ์ˆœ ๋ฌธ์ž์—ด hash (๋ฌดํ•ด)
uint32_t simple_hash(const char* s) {
    uint32_t h = 0;
    while (*s) {
        h = (h << 5) - h + (unsigned char)*s++;
    }
    return h;
}

// ์˜ˆ์‹œ: Export Table์„ ์ˆœํšŒํ•ด API ์ฃผ์†Œ๋ฅผ ๊ฐ€์ ธ์˜ค๋Š” ํŒจํ„ด
FARPROC resolve_api(HMODULE mod, uint32_t target_hash) {
    uint8_t* base = (uint8_t*)mod;
    IMAGE_DOS_HEADER* dos = (IMAGE_DOS_HEADER*)base;
    IMAGE_NT_HEADERS* nt = (IMAGE_NT_HEADERS*)(base + dos->e_lfanew);

    IMAGE_EXPORT_DIRECTORY* exp = (IMAGE_EXPORT_DIRECTORY*)(
        base + nt->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress
    );

    uint32_t* names = (uint32_t*)(base + exp->AddressOfNames);
    uint16_t* ords = (uint16_t*)(base + exp->AddressOfNameOrdinals);
    uint32_t* funcs = (uint32_t*)(base + exp->AddressOfFunctions);

    for (uint32_t i = 0; i < exp->NumberOfNames; i++) {
        const char* api_name = (const char*)(base + names[i]);
        uint32_t h = simple_hash(api_name);

        if (h == target_hash) {
            uint16_t ord = ords[i];
            return (FARPROC)(base + funcs[ord]);
        }
    }

    return NULL;
}

int main() {
    HMODULE k32 = LoadLibraryA("kernel32.dll");
    if (!k32) return 0;

    // ์˜ˆ: โ€œGetTickCountโ€ ํ•ด์‹œ๊ฐ’
    uint32_t hash = simple_hash("GetTickCount");

    FARPROC api = resolve_api(k32, hash);
    if (api) {
        // ์‹ค์ œ ํ˜ธ์ถœํ•˜์ง€ ์•Š๊ณ  ์ฃผ์†Œ๋งŒ ํ™•์ธ
        // ((DWORD(WINAPI*)())api)();  // ๋น„ํ™œ์„ฑํ™”
    }
    return 0;
}

3. ์–ด์…ˆ๋ธ”๋ฆฌ ๋ ˆ๋ฒจ ์ œ์–ด ํ๋ฆ„ ๋ถ„์„

์•„๋ž˜๋Š” ํ•ต์‹ฌ ๋ฃจํ‹ด์ธ resolve_api ๋‚ด๋ถ€ ๋ฐ˜๋ณต ๊ตฌ์กฐ๋ฅผ ๋””์Šค์–ด์…ˆ๋ธ”ํ•œ ํ˜•ํƒœ์ด๋ฉฐ,
๋ ˆ์ง€์Šคํ„ฐ์˜ ์—ญํ• ๊ณผ ์Šคํƒ ํ”„๋ ˆ์ž„์˜ ๋ณ€ํ™”๋ฅผ ์ƒ์„ธํ•˜๊ฒŒ ์„ค๋ช…ํ•ฉ๋‹ˆ๋‹ค.

loc_export_loop:
    mov     eax, [esi]            ; EAX = current RVA of API name
    add     eax, ebx              ; EBX = base, so EAX = VA(name)
    push    eax                   ; push(api_name)
    call    simple_hash
    add     esp, 4                ; clean args
    cmp     eax, edi              ; target_hash in EDI?
    jne     short next_api

    ; If matched:
    mov     cx, [eds + ecx*2]     ; fetch ordinal
    mov     edx, [funcs + ecx*4]  ; fetch RVA
    add     edx, ebx              ; convert RVA โ†’ VA
    mov     eax, edx              ; return address
    jmp     end_resolve

next_api:
    inc     ecx
    cmp     ecx, [number_of_names]
    jb      loc_export_loop

end_resolve:
    ret

๐Ÿงท ๋ ˆ์ง€์Šคํ„ฐ ๋™์ž‘ ํ•ด์„ค

  • EBX: ๋ชจ๋“ˆ ๋ฒ ์ด์Šค ์ฃผ์†Œ
  • ECX: ๋ฐ˜๋ณต ์ธ๋ฑ์Šค(i)
  • EDI: ๋ชฉํ‘œ ํ•ด์‹œ ๊ฐ’
  • EDI == EAX ๋น„๊ต: API ์ด๋ฆ„ hash ๋น„๊ต
  • EAX: ํ˜„์žฌ ๊ณ„์‚ฐ๋œ hash ๋˜๋Š” ๋ฐ˜ํ™˜ ์ฃผ์†Œ

๐Ÿ“ฆ ์Šคํƒ ํ”„๋ ˆ์ž„ ๊ด€์ฐฐ

  • ํ˜ธ์ถœ ๊ทœ์•ฝ์€ ๋‹จ์ˆœ C ์„ ์–ธ์ด๋ฏ€๋กœ caller-cleaned, call simple_hash ํ›„ add esp, 4 ์ˆ˜ํ–‰
  • recursion์ด๋‚˜ frame pointer ์‚ฌ์šฉ ์—†์Œ
  • ์ง€์—ญ ๋ณ€์ˆ˜๋Š” ๋ ˆ์ง€์Šคํ„ฐ ๊ธฐ๋ฐ˜์œผ๋กœ ์ฒ˜๋ฆฌ๋จ

4. ๊ฐ„๋‹จํ•œ ์•ˆํ‹ฐ๋””๋ฒ„๊น… ๊ด€์ฐฐ(๋น„ํ™œ์„ฑยท๋ฌดํ•ด ์˜ˆ์‹œ)

์ผ๋ถ€ ๋กœ๋”๋Š” API ๋™์  ํƒ์ƒ‰๊ณผ ํ•จ๊ป˜ ๋””๋ฒ„๊ฑฐ ์กด์žฌ ์—ฌ๋ถ€๋ฅผ ๊ฒ€์‚ฌํ•ฉ๋‹ˆ๋‹ค.

BOOL anti_debug_stub() {
    __asm {
        mov     eax, fs:[0x30]          ; PEB
        mov     al, [eax+2]             ; BeingDebugged
        ; al == 0์ด๋ฉด ๋ฏธ๋””๋ฒ„๊น… ์ƒํƒœ
        ; ์‹ค์ œ ๋™์ž‘ ์ œ๊ฑฐ
    }
    return FALSE;
}

์ด ํŒจํ„ด์€ PEB ์ ‘๊ทผ โ†’ ๋””๋ฒ„๊น… ์—ฌ๋ถ€ ํ™•์ธ โ†’ ์กฐ๊ฑด ๋ถ„๊ธฐ ํ๋ฆ„


5. ํฌ๋ Œ์‹ ๊ด€์ฐฐ ํฌ์ธํŠธ

๋ฐ์ดํ„ฐ ํ๋ฆ„(Data Flow)์„ ๊ธฐ์ค€์œผ๋กœ ๋ถ„์„ํ•˜๋ฉด ๋‹ค์Œ ๋‹จ๊ณ„๊ฐ€ ๋ช…ํ™•ํ•˜๊ฒŒ ๋“œ๋Ÿฌ๋‚ฉ๋‹ˆ๋‹ค.

  1. LoadLibraryA ํ˜ธ์ถœ
  2. PE Export Table ํŒŒ์‹ฑ
  3. ์ด๋ฆ„ ๋ฌธ์ž์—ด ๋ฐ˜๋ณต โ†’ hashing
  4. ํ•ด์‹œ ๋งค์นญ ์‹œ Ordinal โ†’ Function RVA โ†’ VA ๋ณ€ํ™˜
  5. ๋ฐ˜ํ™˜๋œ ์ฃผ์†Œ ๊ธฐ๋ฐ˜์˜ ํ›„์† ๋กœ์ง ์กด์žฌ ์—ฌ๋ถ€ ๊ฒ€์‚ฌ
  6. ์•ˆํ‹ฐ๋””๋ฒ„๊น… ์Šคํ…๊ณผ ๊ฒฐํ•ฉ ์—ฌ๋ถ€ ํ™•์ธ
  7. ์ด์ƒ ๋™์ž‘ ํƒ์ง€ ์‹œ ์ƒŒ๋“œ๋ฐ•์Šค/๋ฉ”๋ชจ๋ฆฌ ๋คํ”„ ํ›„ ์‹คํ–‰ ํ๋ฆ„ ์žฌ๊ตฌ์„ฑ

6. ์ •๋ฆฌ

  • PE ๊ตฌ์กฐ: Export Directory Table, Name/Ordinal/Function ๋ฐฐ์—ด ๊ด€๊ณ„
  • ์Šคํƒ ํ”„๋ ˆ์ž„: Caller-cleaned ํ˜ธ์ถœ ๊ทœ์•ฝ์˜ ESP ์กฐ์ •
  • ๋””์Šค์–ด์…ˆ๋ธ” ํŒจํ„ด: ์ด๋ฆ„ ๋ฐ˜๋ณต ๋ฃจํ”„, RVAโ†’VA ๋ณ€ํ™˜
  • ์ œ์–ด ํ๋ฆ„ ๋ถ„์„: ํ•ด์‹œ ๋น„๊ต โ†’ ๋ถ„๊ธฐ โ†’ ์กฐ๊ธฐ ์ข…๋ฃŒ
  • API ํ›„ํ‚น ๋Œ€์‘: IAT ๋ฏธ์‚ฌ์šฉ์œผ๋กœ ํ›„ํ‚น ์šฐํšŒ
  • ์•”ํ˜ธํ™” ๋ณต์› ํŒจํ„ด: Hash ๊ธฐ๋ฐ˜ ๋ฌธ์ž์—ด ๋งคํ•‘
  • ๋™์  ๋กœ๋”ฉ ์„œ๋ช…: LoadLibraryA + ์ปค์Šคํ…€ resolver

7. ๋งˆ๋ฌด๋ฆฌ

๋™์  API ํŒจํ„ด์€ ๋‹จ์ˆœํ•ด ๋ณด์ด์ง€๋งŒ, PE ๊ตฌ์กฐ์™€ ์ œ์–ด ํ๋ฆ„์„ ๊นŠ์ด ์ดํ•ดํ• ์ˆ˜๋ก ๊ทธ ์•ˆ์—์„œ ๋“œ๋Ÿฌ๋‚˜๋Š” โ€˜๋กœ๋”์˜ ์˜๋„โ€™๋ฅผ ๋”์šฑ ๋ช…ํ™•ํžˆ ํฌ์ฐฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.


๐Ÿ“ Written by Code & Compass

MalClown์—์„œ ๋” ์•Œ์•„๋ณด๊ธฐ

์ง€๊ธˆ ๊ตฌ๋…ํ•˜์—ฌ ๊ณ„์† ์ฝ๊ณ  ์ „์ฒด ์•„์นด์ด๋ธŒ์— ์•ก์„ธ์Šคํ•˜์„ธ์š”.

๊ณ„์† ์ฝ๊ธฐ