TLS Callback 기반 은밀한 초기 실행 분석

악성코드가 Thread Local Storage(TLS) Callback을 이용해
PE 진입점(EntryPoint) 이전 단계에서 실행 흐름을 선점하는 기법을
분석 관점에서 정리한 기록이다.

TLS Callback은 PE 로더가 모듈을 로딩할 때 자동으로 호출되는 함수로,
공격자는 이를 악용하여 Anti-Debugging, 환경 점검,
언패킹 루틴의 사전 실행, 동적 API 해석 등을
EntryPoint 이전 초기화 구역에 은밀히 삽입한다.

리버싱 기록


1. 개요 — 왜 TLS Callback을 사용하는가

TLS Callback 기반 초기 실행 흐름 전이는 다음과 같은 목적에서 빈번히 활용된다:

  • EntryPoint 은닉: 정적 분석에서 확인되는 EntryPoint는 무해한 코드로 덮어두고, 실제 로직은 TLS에서 실행
  • Anti-Debugging 사전 수행: 디버거 부착 이전 단계에서 환경 검사
  • 언패킹 루틴 선실행: 압축·암호화된 페이로드를 EntryPoint보다 먼저 복구
  • Dynamic API Resolution 초기화
  • PE Loader 내부 동작을 악용한 선제 실행

Windows PE 로더는 TLS Directory를 확인하고 Callback 목록을 순서대로 호출하므로,
이는 PE 구조 관점에서 자연스러운 “합법적 진입점”으로 인식된다.


2. TLS Directory 구조 관찰

TLS 관련 PE Directory는 다음과 같이 구성된다:

typedef struct _IMAGE_TLS_DIRECTORY32 {
    DWORD StartAddressOfRawData;
    DWORD EndAddressOfRawData;
    DWORD AddressOfIndex;
    DWORD AddressOfCallbacks;   // TLS Callback 배열
    DWORD SizeOfZeroFill;
    DWORD Characteristics;
} IMAGE_TLS_DIRECTORY32;

분석에서 주목해야 할 지점:

  • AddressOfCallbacks가 가리키는 함수 배열
  • 함수 주소들이 .text 범위를 벗어나는지
  • RWX 메모리 또는 Private Memory 영역을 가리키는지 여부
  • 정적 분석에서 EntryPoint보다 먼저 실행되는 경우

3. TLS Callback 등록 패턴 (C 예제)

// 패턴 — TLS Callback 선언
void __stdcall DemoTLSCallback(PVOID DllHandle, DWORD Reason, PVOID Reserved) {
    // (예시) Anti-Debugging, Dynamic API Resolution, Unpacking 등
    // 실제 악성 행위 없음
}

// TLS Callback 배열 정의
#pragma const_seg(".CRT$XLB")
PIMAGE_TLS_CALLBACK pCallback = DemoTLSCallback;
#pragma const_seg()

특징

  • .CRT$XL* 섹션을 이용해 TLS 배열을 구성
  • 링커가 이를 TLS Directory에 자동으로 삽입
  • 실행 시 EntryPoint 이전 단계에서 호출됨

4. 실행 흐름 디스어셈블리 분석

다음은 PE 로더가 TLS Callback을 호출하는 단순화된 패턴이다.

; --- LdrpCallTlsInitializers 내부 흐름 단순화 버전 ---
mov     esi, [TlsDirectory.AddressOfCallbacks]
test    esi, esi
jz      no_tls

tls_loop:
    mov     eax, [esi]
    test    eax, eax
    jz      end_tls

    push    0                ; Reserved
    push    1                ; DLL_PROCESS_ATTACH
    push    dword ptr [ModuleHandle]
    call    eax              ; → TLS Callback 호출

    add     esi, 4
    jmp     tls_loop

레지스터 변화 분석

레지스터의미
ESITLS Callback 배열 순회 포인터
EAX현재 Callback 함수 주소
ESP호출 규약에 따라 인자 3개 푸시 후 Call

분석 포인트

  • Callback 주소가 PE 이미지 내 정적 함수인지 확인
  • Private Memory / 언패킹된 메모리로 흐름이 전이되는지
  • Return 시 스택 프레임 왜곡 여부 추적

5. TLS 내부에서 수행되는 Anti-Debugging 패턴

초기화 구간에서 자주 발견되는 패턴:

  1. PEB.BeingDebugged 플래그 확인
mov     eax, fs:[30h]
cmp     byte ptr [eax+2], 0     ; BeingDebugged
jne     debug_found
  1. NtQueryInformationProcess(ProcessDebugFlags)
// 패턴
NTSTATUS status;
DWORD flags = 0;
NtQueryInformationProcess(GetCurrentProcess(),
                          ProcessDebugFlags,
                          &flags, sizeof(flags), NULL);
// flags == 0 → 디버깅 중
  1. Timing Check (RDTSC / QueryPerformanceCounter)
    언패킹 루틴 전 디버깅 지연 여부 검사.

6. TLS 기반 언패킹 초기화 패턴

패커/로더는 TLS Callback에서 다음 작업을 수행한다:

  • 압축 블록 해제
  • XOR/RC4 기반 암호화 루틴 복호화
  • Shellcode를 RWX 메모리에 풀어 배치
  • IAT 재구성 또는 동적 API 로더 초기화
  • Process Injection 준비 (WriteProcessMemory 버퍼 생성 등)

단순화된 언패킹 패턴 예시

void __stdcall DemoTLSCallback(...) {
    for (int i = 0; i < payloadSize; i++)
        unpackBuf[i] ^= 0x5A;

    // EntryPoint 이전에 Memory Patch 완료
}

7. 메모리 포렌식에서의 IOC

TLS 기반 공격은 다음 지표로 식별 가능:

(1) TLS Directory 존재 + Callbacks 배열이 비정상적 주소 사용

(2) Callback 함수가 Private Memory / RWX 메모리에 위치

(3) EntryPoint 실행 이전 단계에서 언패킹 흔적

(4) PEB 검사, Timing Check 등 Anti-Debugging 로직이 TLS 내부에 존재

(5) Call Stack Reconstruction 시 EntryPoint보다 앞선 실행 기록 확인


8. 결론

TLS Callback 기반 실행 흐름 전이는
EntryPoint 이전의 은닉 실행 엔진을 구성하기 위한
대표적인 고급 로딩 기법이다.

PE 구조·메모리 보호 속성·레지스터 전이·디버깅 플래그 관찰을 종합하면
초기 흐름 왜곡을 충분히 추적할 수 있으며,
포렌식 단계에서도 명확한 IOC가 남는다.


📍 Written by Code & Compass

MalClown에서 더 알아보기

지금 구독하여 계속 읽고 전체 아카이브에 액세스하세요.

계속 읽기