[태그:] 인젝션

  • 🧩 Windows Thread Hiding 기반 스텔스형 인젝션

    이번 내용은 스텔스 인젝션 기법 중 비교적 고급 형태인
    “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 ... ]
    

    분석 포인트:

    1. ReturnAddress가 항상 KiUserApcDispatcher 근처
    2. 정상 스레드의 Call Stack 패턴과 불일치
    3. 페이로드 진입 전에 호출 규약이 명확하지 않아
      stdcall/cdecl 구분이 모호한 형태로 나타남
    4. 스택 복구(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

  • 📘 Windows Thread Hijacking 기반 프로세스 인젝션 분석 개요

    스레드 하이재킹(Thread Hijacking) 동작 원리 분석

    Windows 악성코드 분석 시 자주 관찰되는 기법 중 하나가 Thread Hijacking 기반 프로세스 인젝션임.
    이는 기존의 정지된 스레드를 일시적으로 중단한 뒤, 그 스레드 컨텍스트를 수정해 특정 페이로드를 실행시키는 구조로 동작.
    이번 분석은 《리버싱 핵심원리》의 흐름에 따라 스택 프레임, 제어 흐름, 레지스터 컨텍스트 변조,
    PE 구조 기반 페이로드 위치 산정, 동적 API 조회 등을 중심으로 기록식으로 정리함.

    리버싱 기록


    1. 개념적 C 스타일 구조(비실행·고위험 요소 제거)

    아래 코드는 일반적인 Thread Hijacking의 “구조”를 관찰 용도로만 반영한 예시.

    // 구조 예시
    typedef void* HTHREAD;
    typedef struct _CTX { unsigned long regs[8]; } CTX;
    
    void conceptual_thread_hijack(HTHREAD t, unsigned char* payloadBase) {
        CTX ctx = {0};
    
        // 1) 스레드 정지
        suspend_thread(t);
    
        // 2) 컨텍스트 조회
        get_thread_context(t, &ctx);
    
        // 3) 명령 포인터(EIP/RIP) 변경
        //    실제 주소 계산은 생략된 개념적 표현
        ctx.regs[0] = (unsigned long)(payloadBase);
    
        // 4) 수정된 컨텍스트 주입
        set_thread_context(t, &ctx);
    
        // 5) 스레드 재개
        resume_thread(t);
    }
    

    이 함수는 실제 실행이 불가능하도록 설계되었지만,
    실제 분석 시 “관찰 포인트”가 되는 흐름을 그대로 나타냄.


    2. Assembly 기반의 개념적 컨텍스트 변조 흐름(비실행)

    아래 코드는 일부 세부사항은 제거된 구조 예시.

    PSEUDO_HIJACK:
        push ebp
        mov  ebp, esp
    
        ; --- 스레드 일시 정지 ---
        ; (구체적 API 호출 생략)
    
        ; --- 컨텍스트 조회 ---
        ; 컨텍스트 구조체가 EAX에 저장된다고 가정
        mov  eax, [ebp-0x20]
    
        ; --- 명령 포인터 변경 ---
        ; 원래 EIP를 읽어온 뒤 페이로드 기점으로 가정한 값으로 치환
        mov  edx, [eax + CTX_EIP]      ; 원래 EIP
        mov  [eax + CTX_EIP], 0xDEADBEEF  ; 더미 주소
    
        ; --- 수정된 컨텍스트 쓰기 ---
        ; (구체적 API 호출 생략)
    
        leave
        ret
    

    이 흐름에서 중요한 건 다음입니다:

    • **컨텍스트(스레드 레지스터 집합)**는 구조체이며
      내부 필드에 EIP/RIP, ESP/RSP, EFLAGS, general-purpose register가 포함됨
    • 페이로드 실행을 위해 **명령 포인터(RIP/EIP)**가 수정됨
    • 해당 스레드가 재개되면 원래 코드가 아닌 새로운 주소로 흐름이 이동함
    • 이는 제어 흐름 교란(Control Flow Subversion)의 대표 패턴

    3. 제어 흐름 관찰 포인트

    Thread Hijacking 분석 시 포렌식·리버싱 관점에서 주로 확인하는 지점은 다음과 같음:

    1. SuspendThread / GetThreadContext / SetThreadContext / ResumeThread 호출 흔적
      • API 이름이 난독화되어 있을 수 있음
      • PE의 Import Table이 비어 있고, Export Directory 파싱으로 직접 API 주소를 가져오는 경우 빈번
    2. 레지스터 컨텍스트 구조 변형 여부
      • EIP/RIP가 갑작스럽게 비정상 주소로 이동
      • 스택 프레임이 전환되며 ESP/RSP가 자연스럽지 않게 변경
    3. PE 페이로드 구조¶
      • 패킹된 PE의 .text 외부에 존재하는 임의 메모리 공간
      • RWX 보호 속성의 세션 기반 메모리 영역(mmap된 구간 등)
    4. 실행 재개 후 흐름
      • 재개 직후 디스어셈블리 패턴이 정상 함수 프롤로그가 아닌
        NOP, JMP, 단순 MOV 시퀀스 등으로 시작하는지 관찰

    4. 동적 API 해결(PEB 기반) 개념 흐름

    실제 악성코드는 보통 Import Table을 제거하고 PEB 기반으로 API를 직접 파싱.
    아래는 책임 회피를 위해 개념적 형태만 제시한 예시.

    // 비실행 예시
    void* resolve_api_conceptually(const char* dllName, const char* funcName) {
        // 1) PEB → LDR 접근
        // 2) InMemoryOrderModuleList 순회
        // 3) dllName 비교
        // 4) Export Directory 파싱
        // 5) funcName 해시 또는 복호화 후 비교
        // 6) 주소 반환
        return 0;
    }
    

    포인트:

    • 해시 기반 문자열 비교
    • Export Directory의 AddressOfNames / AddressOfFunctions 사용
    • 악성코드 포렌식에서는 이 부분에서 암호화 루틴이 가장 자주 관찰됨

    5. 분석자가 확인해야 할 Windows Internals 포인트

    ✔ TEB / PEB 구조

    • PEB에서 LDR 위치
    • BeingDebugged flag
    • ImageBaseAddress 관찰

    ✔ 스레드 컨텍스트 구조

    • CONTEXT.Eip / Rip
    • SegGs 기반 접근(TLS 등)
    • 스택 기반 주소 전환 여부 확인

    ✔ 메모리 맵

    • RWX 섹션 추적
    • Unbacked 메모리(파일 매핑되지 않은 공간)

    ✔ 패커 흔적

    • OEP 복원 전 stub
    • API name 암호화
    • 호출 규약 교란(calling convention confusion)

    6. 조사 요약

    항목분석 포인트
    제어 흐름 분석EIP/RIP 변조, OEP 전환, JMP 패턴
    스택 프레임Thread Context 변경 전·후 비교
    PE 구조Import Table 비어 있음, Export 재파싱
    디스어셈블리 패턴비정상 프롤로그, 인위적 MOV/JMP
    암호화 루틴해시·XOR 기반 스트링 복호화 흔적
    API 후킹Thread Hijack 뒤 CreateRemoteThread와 구별 필요

    ✨ 마무리 한 줄

    Thread Hijacking은 프로세스 인젝션 기법 중에서도 제어 흐름 교란의 핵심 패턴을 가장 잘 드러내며,
    스레드 컨텍스트와 PEB 기반 동적 API 해석 흐름의 상호작용은 리버스 엔지니어링에서 반드시 짚어야 할 분석 축입니다.


    📍 Written by Code & Compass