본문 바로가기

카테고리 없음

2024.9.4 리버싱 공부(나뭇잎 책)

TLS(Thread Local Storage) 콜백 함수

EP 코드보다 먼저 실행되는 코드

 

PE헤더의 Data_Directory[9]에 TLS_DIRECTORY가 있음.

 

구조체에 Address of Callbacks이 TLS callback임.

 

(linux에도 이런게 있나 검색해봤는데 Thread마다 local 전역변수는 있는데 callback은 없음. windows의 특성)

 

TLS는 스레드별로 독립된 데이터 저장 공간임.

스레드 내에서 프로세스의 global, static data를 마치 local데이터 처럼 취급할때 쓰임.

 

TLS callback은 프로세스의 스레드가 생성/종료될 때마다 호출되는 함수. (메인 스레드가 생성/종료될 때도 호출됨. EP코드 보다 더 먼저)

 

보통 debugging을 EP코드부터 진행해서 이 함수에서 안티디버깅 루틴 타서 감지함.

 

 

#include <windows.h>


void print_console(const char* szMsg)
{
    HANDLE hStdout = GetStdHandle(STD_OUTPUT_HANDLE);

    WriteConsoleA(hStdout, szMsg, strlen(szMsg), NULL, NULL);
}

void NTAPI TLS_CALLBACK1(PVOID DllHandle, DWORD Reason, PVOID Reserved)
{
    char szMsg[80] = { 0, };
    wsprintfA(szMsg, "TLS_CALLBACK1() : DllHandle = %X, Reason = %d\n", DllHandle, Reason);
    print_console(szMsg);
}

void NTAPI TLS_CALLBACK2(PVOID DllHandle, DWORD Reason, PVOID Reserved)
{
    char szMsg[80] = { 0, };
    wsprintfA(szMsg, "TLS_CALLBACK2() : DllHandle = %X, Reason = %d\n", DllHandle, Reason);
    print_console(szMsg);
}

#ifdef _WIN64
#pragma comment (linker, "/INCLUDE:_tls_used")  // See p. 1 below
#pragma comment (linker, "/INCLUDE:tls_callback_func")  // See p. 3 below
#else
#pragma comment (linker, "/INCLUDE:__tls_used")  // See p. 1 below
#pragma comment (linker, "/INCLUDE:_tls_callback_func")  // See p. 3 below
#endif

// Explained in p. 3 below
#ifdef _WIN64
#pragma const_seg(".CRT$XLF")
EXTERN_C const
#else
#pragma data_seg(".CRT$XLF")
EXTERN_C
#endif
PIMAGE_TLS_CALLBACK tls_callback_func[] = { TLS_CALLBACK1,TLS_CALLBACK2 };
#ifdef _WIN64
#pragma const_seg()
#else
#pragma data_seg()
#endif //_WIN64

DWORD WINAPI ThreadProc(LPVOID lParam)
{
    print_console("ThreadProc() start\n");

    print_console("ThreadProc() end\n");

    return 0;
}

int main(void)
{
    HANDLE hThread = NULL;

    print_console("main() start\n");

    hThread = CreateThread(NULL, 0, ThreadProc, NULL, 0, NULL);
    WaitForSingleObject(hThread, 60 * 1000);
    CloseHandle(hThread);

    print_console("main() end\n");

    return 0;
}

 

 

main보다 tls 함수가 먼저 불린다.

 

TLS 디버깅 방법 - system startup breakpoint부터 디버깅 하면 된다.

 

 

system breakpoint부터 디버깅하면 찾을 수 있다.

 

수작업으로 TLS 콜백함수 추가하기

 

64bit는 TLS directory 크기가 28

 

 

 

 

 

빈공간을 찾아서

 

그 공간 권한 설정 하고

 

 

 

TLS DIR에 적고

 

 

 

Address of Callbacks에 함수주소를 넣고 그 주소에 함수를 구현한다.

 

 

안된다 TLS DIR을 추가하면 이런 오류가 나온다. 

 

다시 돌아오겠다.

 

 

다시 돌아왔다.

 

디버거로 분석해보니

 

잘되는 파일은 주소가 base에 맞게 재배치되어 올라오는데

수정한건 재배치 안돼서 그대로 올라온다. 이게 문제인듯 하다.

 

잘 되는 파일으

relocation table을 보면

 

 

들어있는걸 볼수있다. 안되는거 relocation table에 추가해봐야겠다.

 

 

RELOCATION TABLE 추가 방법

 4바이트(RVA OFFSET) + 4바이트(크기) + 2바이트(## A# 위치(64bit 인경우 A))

0x5328에 relocation 해야하는게 있으면

 

00 50 00 00  //  0c 00 00 00 // 28 A3 // 00 00 //

 

총 길이(크기) 12 - 0C

 

 

추가함

 

 

 

RELOCATION 돼서 PE가 로딩됨.

 

 

 

 

TLS CALLBACK으로 집어넣은 RET C 잘 됨^^

 

원인 : ASLR때문에 RELOCATION TABLE에 TLS에서 사용하는 변수들이 들어있어야 정상 작동한다! 해결법은 ASLR을 꺼도 되지만 RELOCATION TABLE에 넣으면 된다.