mov eax,dword ptr fs:[18h] //! TEB -> TIB

mov ecx,dword ptr ds:[eax+30h] //! self

movzx eax,dword ptr ds:[ecx+0A4h] //! OSMajorVersino 

shl eax,8

or eax,dword ptr ds:[ecx+0A8h] //! OSMinorVersion




  O/S     VERSION dwMajorVersion dwMinorVersion

          Windows 8                          6.2            6                             2

  Windows 7 6.1 6 1

  Windows Server 2008 R2 6.1 6 1

  Windows Server 2008 6.0 6 0

  Windows Vista 6.0 6 0

  Windows Server 2003 R2 5.2 5 2

  Windows Server 2003 5.2 5 2

  Windows XP  5.1 5 1

  Windows 2000 5.0 5 0

  Windows Me  4.90 4 90

  Windows 98  4.10 4 10



AND

by. 유창훈


정석으로 얻는 방법은 따로 정리도록 하겠다. 


해당방법은 좀 쉽게 얻는 방법인데, 별거없다. 

빠르게 구현해야 되는 상황이면 쓸만한것 같아 올려둔다. 



mov eax, PE_CODE_SECTION    //  PE_CODE_SECTION =  0x5A4D = MZ

add eax, 3ch //IMAGE_NT_HEADERS 의 위치의 값을 가지고 있는 곳으로  이동. MZ헤더에서 e_lfanew 값

mov edx, PE_CODE_SECTION //IMAGE_NT_HEADERS 의 시작 위치 로 이동

add edx, dword ptr [eax]

add edx, 34h //Base of Image 의 값의 위치로 이동



'Reversing' 카테고리의 다른 글

Windows8 OS MajorVersion , MinorVersion  (0) 2012.11.27
ANTI D3D9 hooking  (0) 2012.05.17
[ANTI-Reversing] ANTI_DLL Injection  (0) 2012.04.24
[ANTI Reversing] BegingDebuged 설명  (0) 2012.04.24
PEB구조체 시작 주소 MS Windows 버전별  (0) 2012.04.24
AND

ANTI D3D9 hooking

Reversing 2012. 5. 17. 15:35




좋은자료가 있어 참고합니다. 

출처 : http://ajlab.tistory.com/entry/Direct3D-Hooking-%EB%B6%84%EC%84%9D-%ED%95%B4%EB%B4%A4%EC%8A%B5%EB%8B%88%EB%8B%A4




본 문서는 DirectX 9 버전을 기반으로 동작하는 게임을 대상으로 d3d9.dll  모듈이 포함하고 있는 함수들을 보호하는데 그 목적이 있다. 


우리가 보호하려는 d3d9.dll 에 포함된 함수들은 가상함수 형태로 DirectX에서 정의하여 사용하고있다. 가상함수를 사용하기 위해서 운영체제는 가상함수가 순차적으로 정의된 가상함수테이블(vTable)을 메모리상에 유지하게 된다현재 가장많이 사용되며 일반적으로 후킹이 이루어지는 부분이 바로 vTable로드된 가상함수들의 시작번지를 후킹하는 것이다


따라서 Anti D3Dx9에서는 첫번째로 vTable의 시작번지수를 구하고, 두번째로 후킹에 주된 대상이 되는 함수들의 첫번째 byte를 검사하는 과정을 수행하게 된다.


가상함수테이블에 포함된 가상함수 목록 및 배열은 d3d9.h 파일에 순차적으로 정의되어있다.




1. vTable 시작주소 구하기 


bool bDataCompare(const BYTE* pData, const BYTE* bMask, const char* szMask)

{

for(;*szMask;++szMask,++pData,++bMask)

if(*szMask=='x' && *pData!=*bMask )

return false;

return (*szMask) == NULL;

}


DWORD FindPattern(DWORD dwAddress,DWORD dwLen,BYTE *bMask,char * szMask)

{

for(DWORD i=0; i < dwLen; i++)

if( bDataCompare( (BYTE*)( dwAddress+i ),bMask,szMask) )

return (DWORD)(dwAddress+i);

return 0;

}


DWORD D3DPattern=NULL,*vTable, DXBase=NULL;

DXBase = (DWORD)LoadLibraryA("d3d9.dll");

if(DXBase == NULL) return -4001;

while(!DXBase);

{

D3DPattern = FindPattern(DXBase, 0x128000,

(PBYTE)"\xC7\x06\x00\x00\x00\x00\x89\x86\x00\x00\x00\x00\x89\x86", "xx????xx????xx");

}


if(D3DPattern != NULL) memcpy(&vTable,(void *)(D3DPattern+2),4);




FindPattern()

: 가장 핵심이 되는 부분이며, vTable의 시작 주소를 메모리상에서 찾아내는 부분

이다. 시작주소만 찾으면 vTable에 로드된 함수들은 d3d9.h의 정의된 순서대로

(배열 인덱스 값으로) 찾을 수 있다


위에서 첫번째인자는 시작 주소 DXBase, 두번째인자는 범위 0x128000, 세번째인 자는 검색패턴, 네번째인자는 마스크이다.

여기서 마스크 검색이란, 예를 들어 검색하고자 하는 값이 12 34 56 78 이고 마스 크가 xx??xx?? 이면 12 ?? 56 ?? 형태를 가진 모든 값을 검색하는 것이다. 여기서 ?? 부분은 무슨 값이 들어가도 12 ?? 56 ?? 형태라면 해당 부분을 찾아낸다. 12 FF 56 FF 라든지,12 3C 56 A1 이라든지 말이다.


소스를 다시 보면 DXBase(d3d9.dll 모듈의 시작 주소)부터 0x128000 범위안에서

"\xC7\x06\x00\x00\x00\x00\x89\x86\x00\x00\x00\x00\x89 \x86" 패턴에 "xx????xx????xx" 의 마스크를 적용하여 해당 하는 값을 찾아 내라 라는 뜻이다


맨 마지막에 D3Dpattern에서 +2를 한 이유는 0xC7 0x06 이후에 나오는 4 Byte vTable의 주소 값이 들어가 있기 때문에 맨 앞에 0xC7, 0x062 byte를 제외한 그 다음 4 BytevTable 변수에 저장하라는 의미다.


**현재 위와 같이 패턴을 이용해서 vTable의 시작주소를 찾아내는 방식은 지금도 월핵과같은 핵툴에서 사용하고 있는 방식이며, 이를 보호하고자 하는 방법의 하나로 다음과 같은 방법을 소개한다. 



2. 주요 후킹대상 함수의 시작 바이트 검사


BYTE *TTT;

TTT=((PBYTE)vTable[41] ); // BeginScene

if(TTT[0] == 0xE9 || TTT[0] == 0xEB) return -1;


TTT=((PBYTE)vTable[42] ); //EndScene

if(TTT[0] == 0xE9 || TTT[0] == 0xEB) return -1:


TTT=((PBYTE)vTable[47] ); //SetViewport

if(TTT[0] == 0xE9 || TTT[0] == 0xEB) return -1:


TTT=((PBYTE)vTable[82] ); //DrawIndexedPrimitive

if(TTT[0] == 0xE9 || TTT[0] == 0xEB) return -1:


TTT=((PBYTE)vTable[100] ); //SetStreamSource

if(TTT[0] == 0xE9 || TTT[0] == 0xEB) return -1:



: vTable의 시작 주소를 찾았으면, 위와 같이 후킹 대상 함수들의 시작 바이트를 검사한다. 

Byte형 포인터 변수 TTT에 각 가상함수의 시작주소를 저장한다.

여기서 사용한 인덱스값 41,42,47,82,100은 각각 BeginScene(), EndScene(), SetViewport(), DrawIndexedPrimitive(), SetStreamSource() 함수이며, 이 인덱 스 값은 d3d9.h에 차례로 정의된 가상함수들을 순서 매겨 찾은 번호이다.

BeginScene()함수의 시작주소를 *TTT 변수에 받아왔다면 TTT[0] 과 같이 시작 바이트를 0xE9(lomg jmp)또는 0xEB(short jmp)와 비교한다.

정상적인 함수의 시작바이트에는 jmp코드가 없는 것을 사전에 확인하였다.

, jmp 코드가 들어있으면 정상적인 함수가 불법행위에 의해 후킹당한것으로 판단 하여 특정 값을 리턴하여 검출한다. 


위에서 필자가 사용한 가상함수 첫바이트를 검사하여 후킹여부를 판단하는 방법은 검출방법의 하나일 뿐이다. 

첫바이트가 아닌 다른 바이트를 후킹한다던지, vTable 통째로 복사하여 해커가 만들어놓은 vTable로 유도한다던지  하는 방법등 여러가지 방법이 있을 수 있겠다. 









AND




1. SetThreadContext를 이용한 Dll Injection 검사


: SetThreadContext를 이용하여 Dll Injection을 하기 위해서는 Code Injection을 해야 하므로

Code Injection시에는 메모리 속성을 MEM_PRIVATE로 변경하는 것을 이용하여 검출하는 방식.


MEMORY_BASIC_INFORMATION mbi;

VirtualQuery(Caller, &mbi, sizeof(MEMORY_BASIC_INFORMATION));


if(mbi.Type == MEM_PRIVATE)  return false;





2. CreateRemoteThread를 이용한 Dll Injection 검사


CreateRemoteThread() API함수를 이용한 Dll Inejction을 검출하는 방법은 두가지 이다.


첫째로 , 결국LoadLibraryA() 함수를 호출하는 함수 LdrLoadDll() 함수를 후킹하는 것이다


왜냐하면 어차피 유저레벨에서 LoadLlibrary함수류가 최종적으로 호출하는 함수가 바로 LdrLoadDll()함수이기 때문에 해당 LdrLoadDll() 함수가 호출되면 쓰레드의 stack base point를 얻어서 쓰레드로 호출되는 코드의 주소를 얻어올 수 있다. (해당 주소는 항상 stack의 가장 위에서 두번째 혹은 세 번째에 존재한다) . 


PDWORD psb;

__asm  

{

push eax  

mov eax, fs:[0x18]  

mov eax, [eax+0x4]  

mov psb, eax  

pop eax  

}  

이렇게 스택베이스포인터 구하고, 


PVOID pvAddr = *(psb-3) ? (PVOID)*(psb-3) : (PVOID)*(psb-2);

  if(pvAddr ==LoadLibraryA || pvAddr==LoadLibraryW)   return false;


이렇게 해서 LoadLibraryA/W() 의 주소와 비교하여, 맞는지 판단한다. 

    쓰레드로 호출할 함수는 인자를 하나만 받을 수 있기 때문에 LoadLibraryA() 함수와 LoadLibraryW() 함수 두 함수만 체크하면 된다.   





    두번째 방법 또한 첫번째 방법과 비슷한 방법인데, CreateRemoteThread()함수를 호출해주는 함수인 BaseThreadStartThunk()의 호출 여부를 확인하는 것이다.   


    BaseThreadStartThunk()ThreadProc호출시에 caller Address의 호출되는 코드 위치push eax(0x50)인것을 이용하여 검출하는 방식이다.

    LPBYTE pbCaller = (LPBYTE) Caller;

    if(*pbCaller == (BYTE)0x50 ) return false;




    3. SetWindowsHookEx를 이용한 Dll Injection 검사


    SetWindowsHookEx()GUI Thread를 가진 프로세스에 DLL을 삽입하는함수이다.


    SetWindowsHookEx()함수를 호출하면 User32.dll ClientLoadLibrary()함수가 호출된다. ClientLoadLibrary()함수는 LoadLibraryExW()함수를 최종적으로 호출하게 된다

    따라서 LoadLibraryExW()함수를 후킹하여 Caller의 주소가 User32.dll의 주소범위 안에 있는지(user32.dll 에서 호출되는 지 )확인하여 다음과 같이 검출하게 된다.    


    VirtualQuery(Caller, &mbi, sizeof(MEMORY_BASIC_INFORMATION));

    LPBYTE pbCaller = (LPBYTE) Caller;


    if(((DWORD)pbCaller >= dwUser32Start && (DWORD)pbCaller < dwUser32End) ||

    *(pbCaller-7) == (BYTE)0x1C)

    {

    AntiCodeError += 24;

    return false;

    }


    위에서 사용된 User32.dll 의 주소 범위를 구하는 것은 아래 와 같다. 

    DWORD dwUser32Start, dwUser32End;

    HMODULE hDllUser32;

    hDllUser32 = GetModuleHandle( TEXT("user32") );


    dwUser32Start = (DWORD)hDllUser32;


    PIMAGE_DOS_HEADER idh = (PIMAGE_DOS_HEADER)hDllUser32;

    PIMAGE_NT_HEADERS inh = (PIMAGE_NT_HEADERS)(dwUser32Start + idh->e_lfanew);


    dwUser32End ^= (dwUser32Start ^ dwUser32End);

    dwUser32End += inh->OptionalHeader.SizeOfImage;




    'Reversing' 카테고리의 다른 글

    PE구조파일 Image Base Address 쉽게 얻자  (0) 2012.06.11
    ANTI D3D9 hooking  (0) 2012.05.17
    [ANTI Reversing] BegingDebuged 설명  (0) 2012.04.24
    PEB구조체 시작 주소 MS Windows 버전별  (0) 2012.04.24
    PEB 구조체  (0) 2012.04.24
    AND

    자료는 많은데 다들 설명이없어.


    mov         eax,dword ptr fs:[18h]

    mov         eax,dword ptr ds:[eax+30h]

    movzx eax,byte ptr ds:[eax+2h]




    위 코드에서 fs:[18h]는 _TEB자체를 의미하는데 그 이유는 다음과 같다.
    kd> dt _TEB
    nt!_TEB
    +0x000 NtTib : _NT_TIB
    +0x01c EnvironmentPointer : Ptr32 Void
    +0x020 ClientId : _CLIENT_ID
    +0x028 ActiveRpcHandle : Ptr32 Void
    +0x02c ThreadLocalStoragePointer : Ptr32 Void
    +0x030 ProcessEnvironmentBlock : Ptr32 _PEB
    .
    .
    .
    kd> dt _NT_TIB
    nt!_NT_TIB
    +0x000 ExceptionList : Ptr32 _EXCEPTION_REGISTRATION_RECORD
    +0x004 StackBase : Ptr32 Void
    +0x008 StackLimit : Ptr32 Void
    +0x00c SubSystemTib : Ptr32 Void
    +0x010 FiberData : Ptr32 Void
    +0x010 Version : Uint4B
    +0x014 ArbitraryUserPointer : Ptr32 Void
    +0x018 Self : Ptr32 _NT_TIB



    fs:[18h] => _TEB._NT_TIB.Self를 의미하므로 결국 fs:[0]의 의미가 된다.

    그래서 다음 라인
    mov eax,dword ptr [eax+30h]

    는 _TEB + 30h 인 process environment block, 즉 _PEB를 의미한다.





    그냥 이렇게 쓰자. 


                  mov eax, fs:[30h]
                  mov eax, [eax+2]

    이렇게 해서 eax가 1인지 아닌지만 보면될걸 가지고 

    'Reversing' 카테고리의 다른 글

    ANTI D3D9 hooking  (0) 2012.05.17
    [ANTI-Reversing] ANTI_DLL Injection  (0) 2012.04.24
    PEB구조체 시작 주소 MS Windows 버전별  (0) 2012.04.24
    PEB 구조체  (0) 2012.04.24
    TEB 구조체  (0) 2012.04.24
    AND



    1. SP2 이전 시스템 :  0x7ffdf000


    2. sp2부터 : fs:[0x30] 


    3. win7 유저영역 : fs:[0x30]           


    4. win7 커널영역 : fs[0x18]


    'Reversing' 카테고리의 다른 글

    [ANTI-Reversing] ANTI_DLL Injection  (0) 2012.04.24
    [ANTI Reversing] BegingDebuged 설명  (0) 2012.04.24
    PEB 구조체  (0) 2012.04.24
    TEB 구조체  (0) 2012.04.24
    [Anti Revering] PEB NtGlobalFlags 값 변경 타이티밍.  (0) 2012.04.16
    AND

    PEB 구조체

    Reversing 2012. 4. 24. 10:12

    저번 TEB 편에 이어 PEB 편을 작성하겠습니다. 이 강좌는 끝내놔야 불만/불평이 없을듯 해서 :)

    PEB는 TEB와 달리 프로세스의 정보를 저장하는 구조체입니다. (이름에서 알 수 있죠 :p)

    PEB아래와 같이 생긴 녀석입니다.

    nt!_PEB
    +0x000 InheritedAddressSpace : UChar
    +0x001 ReadImageFileExecOptions : UChar
    +0x002 BeingDebugged : UChar
    +0x003 SpareBool : UChar
    +0x004 Mutant : Ptr32 Void
    +0x008 ImageBaseAddress : Ptr32 Void
    +0x00c Ldr : Ptr32 _PEB_LDR_DATA
    +0x010 ProcessParameters : Ptr32 _RTL_USER_PROCESS_PARAMETERS
    +0x014 SubSystemData : Ptr32 Void
    +0x018 ProcessHeap : Ptr32 Void
    +0x01c FastPebLock : Ptr32 _RTL_CRITICAL_SECTION
    +0x020 FastPebLockRoutine : Ptr32 Void
    +0x024 FastPebUnlockRoutine : Ptr32 Void
    +0x028 EnvironmentUpdateCount : Uint4B
    +0x02c KernelCallbackTable : Ptr32 Void
    +0x030 SystemReserved : [1] Uint4B
    +0x034 AtlThunkSListPtr32 : Uint4B
    +0x038 FreeList : Ptr32 _PEB_FREE_BLOCK
    +0x03c TlsExpansionCounter : Uint4B
    +0x040 TlsBitmap : Ptr32 Void
    +0x044 TlsBitmapBits : [2] Uint4B
    +0x04c ReadOnlySharedMemoryBase : Ptr32 Void
    +0x050 ReadOnlySharedMemoryHeap : Ptr32 Void
    +0x054 ReadOnlyStaticServerData : Ptr32 Ptr32 Void
    +0x058 AnsiCodePageData : Ptr32 Void
    +0x05c OemCodePageData : Ptr32 Void
    +0x060 UnicodeCaseTableData : Ptr32 Void
    +0x064 NumberOfProcessors : Uint4B
    +0x068 NtGlobalFlag : Uint4B
    +0x070 CriticalSectionTimeout : _LARGE_INTEGER
    +0x078 HeapSegmentReserve : Uint4B
    +0x07c HeapSegmentCommit : Uint4B
    +0x080 HeapDeCommitTotalFreeThreshold : Uint4B
    +0x084 HeapDeCommitFreeBlockThreshold : Uint4B
    +0x088 NumberOfHeaps : Uint4B
    +0x08c MaximumNumberOfHeaps : Uint4B
    +0x090 ProcessHeaps : Ptr32 Ptr32 Void
    +0x094 GdiSharedHandleTable : Ptr32 Void
    +0x098 ProcessStarterHelper : Ptr32 Void
    +0x09c GdiDCAttributeList : Uint4B
    +0x0a0 LoaderLock : Ptr32 Void
    +0x0a4 OSMajorVersion : Uint4B
    +0x0a8 OSMinorVersion : Uint4B
    +0x0ac OSBuildNumber : Uint2B
    +0x0ae OSCSDVersion : Uint2B
    +0x0b0 OSPlatformId : Uint4B
    +0x0b4 ImageSubsystem : Uint4B
    +0x0b8 ImageSubsystemMajorVersion : Uint4B
    +0x0bc ImageSubsystemMinorVersion : Uint4B
    +0x0c0 ImageProcessAffinityMask : Uint4B
    +0x0c4 GdiHandleBuffer : [34] Uint4B
    +0x14c PostProcessInitRoutine : Ptr32 void
    +0x150 TlsExpansionBitmap : Ptr32 Void
    +0x154 TlsExpansionBitmapBits : [32] Uint4B
    +0x1d4 SessionId : Uint4B
    +0x1d8 AppCompatFlags : _ULARGE_INTEGER
    +0x1e0 AppCompatFlagsUser : _ULARGE_INTEGER
    +0x1e8 pShimData : Ptr32 Void
    +0x1ec AppCompatInfo : Ptr32 Void
    +0x1f0 CSDVersion : _UNICODE_STRING
    +0x1f8 ActivationContextData : Ptr32 Void
    +0x1fc ProcessAssemblyStorageMap : Ptr32 Void
    +0x200 SystemDefaultActivationContextData : Ptr32 Void
    +0x204 SystemAssemblyStorageMap : Ptr32 Void
    +0x208 MinimumStackCommit : Uint4B

    ( ps: 일부 필드의 Offset은 NT version에 따라 조금씩 달랐습니다. )

    PEB는 TEB에서 point됩니다. 따라서 아래 명령을 통해서 그 주소 값을 획득할 수 있습니다.

    PPEB peb;
    __asm
    {
    MOV peb, fs:[0x30] // +0x030 ProcessEnvironmentBlock : Ptr32 _PEB
    }

    혹은 깔끔하게 C언어만으로 구현해야한다면, 메모리에 직접 접근해서 구할 수도 있을것 입니다.

    // 0x7FFDF000는 NT OS의 TEB address
    PPEB PEB = *(PPEB *)(0x7FFDF030L);

    그러나, 메모리를 직접 접근하는 것은 위험 부담이 크므로, (NT Version에 따라 동작하지 않을수도 있으므로 :p)

    별도의 구조체를 통해서 구할 수도 있습니다.

    PEB의 주소값은 PROCESS_BASIC_INFORMATION 이라는 구조체에 잘 나와있습니다.

    typedef struct _PROCESS_BASIC_INFORMATION {
    NTSTATUS ExitStatus;
    PPEB PebBaseAddress;
    ULONG AffinityMask;
    LONG BasePriority;
    ULONG UniqueProcessId;
    ULONG InheritedFromUniqueProcessId;
    } PROCESS_BASIC_INFORMATION;

    위 구조체는 ZwQueryInformationProcess() Native API에 의해 구해질 수 있으므로...

    typedef enum _PROCESS_INFORMATION_CLASS
    {
    ProcessBasicInformation = 0L, // PROCESS_BASIC_INFORMATION
    /* ... omitted ... */
    } PROCESS_INFORMATION_CLASS;

    NTSTATUS
    NTAPI
    ZwQueryInformationProcess(
    IN HANDLE ProcessHandle,
    IN PROCESS_INFORMATION_CLASS ProcessInformationClass,
    OUT PVOID ProcessInformation,
    IN ULONG ProcessInformationLength,
    OUT PULONG ReturnLength ); // exported by NTDLL.DLL

    아래와 같이 coding하면 PEB의 주소값을 안정적으로 구할 수 있습니다. (ASM을 쓰는것보단 느리겠지만요 :p)(참고로 ProcessHandle인자만 바꾸어주면 다른 프로세스의 PEB 주소값을 구할 수 있습니다.)

    typedef void *PPEB;
    typedef LONG NTSTATUS;
    #define NtCurrentProcess() ((HANDLE) -1L)
    typedef struct _PROCESS_BASIC_INFORMATION {
    NTSTATUS ExitStatus;
    PPEB PebBaseAddress;
    ULONG AffinityMask;
    LONG BasePriority;
    ULONG UniqueProcessId;
    ULONG InheritedFromUniqueProcessId;
    } PROCESS_BASIC_INFORMATION;
    typedef enum _PROCESS_INFORMATION_CLASS
    {
    ProcessBasicInformation = 0L, // PROCESS_BASIC_INFORMATION
    /* ... omitted ... */
    } PROCESS_INFORMATION_CLASS;
    typedef
    NTSTATUS
    (NTAPI *
    ZQIP)(
    IN HANDLE ProcessHandle,
    IN PROCESS_INFORMATION_CLASS ProcessInformationClass,
    OUT PVOID ProcessInformation,
    IN ULONG ProcessInformationLength,
    OUT PULONG ReturnLength ); // exported by NTDLL.DLL
    PPEB GetPEBAddress(HANDLE hProcess)
    {
    PROCESS_BASIC_INFORMATION pbi;
    ZQIP ZwQueryInformationProcess = NULL;

    HMODULE hNtDLL = GetModuleHandle("NTDLL.DLL");
    if(!hNtDLL)
    {
    hNtDLL = LoadLibrary("NTDLL.DLL");
    if(!hNtDLL) return NULL;
    }
    ZwQueryInformationProcess = (ZQIP)GetProcAddress(hNtDLL, "ZwQueryInformationProcess");
    if(!ZwQueryInformationProcess) return NULL;
    ULONG ret;
    if(ZwQueryInformationProcess( hProcess, ProcessBasicInformation, &pbi, sizeof(pbi), &ret) >= 0)
    {
    return pbi.PebBaseAddress;
    }
    else
    {
    return NULL;
    }
    }
    /*
    Usage:
    PPEB peb = GetPEBAddress(NtCurrentProcess());
    */

    PEB에서는 쓸만한 필드는 아래 세 녀석인듯 합니다.

    +0x002 BeingDebugged : UChar
    - 디버거 존재 여부를 반환. (IsDebuggerPresent() API에서 사용)

    +0x008 ImageBaseAddress : Ptr32 Void
    - 프로세스의 메인 module Base Address저장 (GetModuleHandle/LdrGetDllHandle()에서 사용)

    +0x00c Ldr : Ptr32 _PEB_LDR_DATA
    - 프로세스의 로드된 DLL 목록 저장 (GetModuleHandle/LdrGetDllHandle()에서 사용)

    p.s: 일부 user-land rootkit은 Dll Injection 여부를 숨기기 위해 Ldr 필드를 조작해서 DLL을 숨기기도 합니다.

    AND

    TEB 구조체

    Reversing 2012. 4. 24. 10:11
     윈도우에는 PEB와 TEB라 불리는 특수 구조체가 프로세스별로 존재합니다. PEB는 유저 모드 프로세스당 하나, TEB는 유저 모드 스레드당 하나씩 존재합니다.

    주의! 커널 모드(Kernel Mode)에서는 PEB/TEB가 존재하지 않습니다.

    PEB는 다음 논제로 남겨두기로 하고, TEB 부터 살펴보겠습니다.

    Windows XP SP3를 기준으로 TEB의 구조체는 아래와 같이 표현됩니다.

    nt!_TEB
    +0x000 NtTib : _NT_TIB
    +0x01c EnvironmentPointer : Ptr32 Void
    +0x020 ClientId : _CLIENT_ID
    +0x028 ActiveRpcHandle : Ptr32 Void
    +0x02c ThreadLocalStoragePointer : Ptr32 Void
    +0x030 ProcessEnvironmentBlock : Ptr32 _PEB
    +0x034 LastErrorValue : Uint4B
    +0x038 CountOfOwnedCriticalSections : Uint4B
    +0x03c CsrClientThread : Ptr32 Void
    +0x040 Win32ThreadInfo : Ptr32 Void
    +0x044 User32Reserved : [26] Uint4B
    +0x0ac UserReserved : [5] Uint4B
    +0x0c0 WOW32Reserved : Ptr32 Void
    +0x0c4 CurrentLocale : Uint4B
    +0x0c8 FpSoftwareStatusRegister : Uint4B
    +0x0cc SystemReserved1 : [54] Ptr32 Void
    +0x1a4 ExceptionCode : Int4B
    +0x1a8 ActivationContextStack : _ACTIVATION_CONTEXT_STACK
    +0x1bc SpareBytes1 : [24] UChar
    +0x1d4 GdiTebBatch : _GDI_TEB_BATCH
    +0x6b4 RealClientId : _CLIENT_ID
    +0x6bc GdiCachedProcessHandle : Ptr32 Void
    +0x6c0 GdiClientPID : Uint4B
    +0x6c4 GdiClientTID : Uint4B
    +0x6c8 GdiThreadLocalInfo : Ptr32 Void
    +0x6cc Win32ClientInfo : [62] Uint4B
    +0x7c4 glDispatchTable : [233] Ptr32 Void
    +0xb68 glReserved1 : [29] Uint4B
    +0xbdc glReserved2 : Ptr32 Void
    +0xbe0 glSectionInfo : Ptr32 Void
    +0xbe4 glSection : Ptr32 Void
    +0xbe8 glTable : Ptr32 Void
    +0xbec glCurrentRC : Ptr32 Void
    +0xbf0 glContext : Ptr32 Void
    +0xbf4 LastStatusValue : Uint4B
    +0xbf8 StaticUnicodeString : _UNICODE_STRING
    +0xc00 StaticUnicodeBuffer : [261] Uint2B
    +0xe0c DeallocationStack : Ptr32 Void
    +0xe10 TlsSlots : [64] Ptr32 Void
    +0xf10 TlsLinks : _LIST_ENTRY
    +0xf18 Vdm : Ptr32 Void
    +0xf1c ReservedForNtRpc : Ptr32 Void
    +0xf20 DbgSsReserved : [2] Ptr32 Void
    +0xf28 HardErrorsAreDisabled : Uint4B
    +0xf2c Instrumentation : [16] Ptr32 Void
    +0xf6c WinSockData : Ptr32 Void
    +0xf70 GdiBatchCount : Uint4B
    +0xf74 InDbgPrint : UChar
    +0xf75 FreeStackOnTermination : UChar
    +0xf76 HasFiberData : UChar
    +0xf77 IdealProcessor : UChar
    +0xf78 Spare3 : Uint4B
    +0xf7c ReservedForPerf : Ptr32 Void
    +0xf80 ReservedForOle : Ptr32 Void
    +0xf84 WaitingOnLoaderLock : Uint4B
    +0xf88 Wx86Thread : _Wx86ThreadState
    +0xf94 TlsExpansionSlots : Ptr32 Ptr32 Void
    +0xf98 ImpersonationLocale : Uint4B
    +0xf9c IsImpersonating : Uint4B
    +0xfa0 NlsCache : Ptr32 Void
    +0xfa4 pShimData : Ptr32 Void
    +0xfa8 HeapVirtualAffinity : Uint4B
    +0xfac CurrentTransactionHandle : Ptr32 Void
    +0xfb0 ActiveFrame : Ptr32 _TEB_ACTIVE_FRAME
    +0xfb4 SafeThunkCall : UChar
    +0xfb5 BooleanSpare : [3] UChar


    TEB(Thread Environment Block)에서 몇가지 재미난 필드를 살펴봅시다.

    +0x000 NtTib : _NT_TIB
    SEH(Structured Exception Handler) 체인 중 최상위 엔트리를 말합니다.

    +0x01c EnvironmentPointer : Ptr32 Void
    CreateProcess()의 Environment 필드에 의해 지시된 포인터 값입니다.

    +0x020 ClientId : _CLIENT_ID
    프로세스 아이디와 스레드 아이디를 가지고 있는 필드(CLIENT_ID structure)입니다.

    +0x02c ThreadLocalStoragePointer : Ptr32 Void
    TlsAlloc/TlsGetValue/TlsSetValue 등의 Win32 API에 의해 참조되는 값으로, Thread Local Storage(TLS) pointer를 저장합니다.

    +0x030 ProcessEnvironmentBlock : Ptr32 _PEB
    PEB(Process Environment Block) 포인터를 저장합니다.

    +0x040 Win32ThreadInfo : Ptr32 Void
    스레드 정보를 담고 있는 커널 모드 포인터입니다.


    그렇다면 Application에서 TEB를 어떻게 사용하고 참조하는 것일까요?

    그 해답은 API를 디어셈블하면 얻을 수 있을겁니다. GetCurrentProcessId()를 Disassemble해보았습니다.

    kernel32!GetCurrentProcessId:
    7C8099B0 64 A1 18 00 00 00 MOV EAX,DWORD PTR FS:[18]
    7C8099B6 8B 40 20 MOV EAX,DWORD PTR DS:[EAX+20]
    7C8099B9 C3 RETN

    생각 외로 코드가 별로 길지 않죠? :p

    ClientId: CLIENT_ID 필드 오프셋이 0x20 이였던가요? 왠지 'EAX+20' 이 눈에 띄는군요~

    그러면 EAX에는 TEB pointer가 있다는 것인데...

    그 전에는 MOV EAX, DWORD PTR FS:[18h] 로 FS segment block을 참조하고 있는것을 볼 수 있습니다.

    저 코드를 임의로 실행시켜보니 EAX 값은 0x7FFDF000 이였습니다. (물론 Thread 마다 주소는 다를겁니다. :p)

    Thread 마다 주소가 다를 것 같으니, FS segment에 대한 비밀을 파헤쳐 보아야 될 것 같은데요... 아쉽게도 유저 모드(Ring3)에서는 세그먼트 정보를 볼 수 없습니다. ( 유저 모드에서 얻을 수 있는 것은 단지 Segment Index 뿐이죠. )

    Debugger에서 Segment Index를 확인해보니 'FS=003B' 였습니다. (이 값은 버전, 환경에 따라 다를 수 있습니다.)

    003B는 2진수로 '0011 1011' 이고, 하위 3비트는 RPL(Request Privilege Level), 그리고 그 다음 1비트는 TI(LDT 사용 여부)를 의미합니다. 실제 세그먼트 인덱스는 (하위 4비트를 버린 값)+8 의 값을 가집니다.

    다음은 WinDbg에서 dg명령어를 이용하여 세그먼트 정보를 출력한 결과입니다.:

    P Si Gr Pr Lo
    Sel Base Limit Type l ze an es ng Flags
    ---- -------- -------- ---------- - -- -- -- -- --------
    0000 00000000 00000000 <Reserved> 0 Nb By Np Nl 00000000
    0008 00000000 ffffffff Code RE Ac 0 Bg Pg P Nl 00000c9b
    0010 00000000 ffffffff Data RW Ac 0 Bg Pg P Nl 00000c93
    0018 00000000 ffffffff Code RE Ac 3 Bg Pg P Nl 00000cfb
    0020 00000000 ffffffff Data RW Ac 3 Bg Pg P Nl 00000cf3
    0028 bab50d70 000020ab TSS32 Busy 0 Nb By P Nl 0000008b
    0030 bab50000 00001fff Data RW Ac 0 Bg Pg P Nl 00000c93
    0038 7ffdd000 00000fff Data RW Ac 3 Bg By P Nl 000004f3
    0040 00000400 0000ffff Data RW 3 Nb By P Nl 000000f2
    0048 00000000 00000000 <Reserved> 0 Nb By Np Nl 00000000

    7FFDD000 이군요. 아무래도 FS:[] 자체가 TEB인것 같습니다.

    그렇다면 FS:[18h] 는 무엇이였던걸까요? 오프셋 0x18은 NT_TIB 안입니다. 그렇다면 NT_TIB 구조체를 조사해보면...

    nt!_NT_TIB
    +0x000 ExceptionList : Ptr32 _EXCEPTION_REGISTRATION_RECORD
    +0x004 StackBase : Ptr32 Void
    +0x008 StackLimit : Ptr32 Void
    +0x00c SubSystemTib : Ptr32 Void
    +0x010 FiberData : Ptr32 Void
    +0x010 Version : Uint4B
    +0x014 ArbitraryUserPointer : Ptr32 Void
    +0x018 Self : Ptr32 _NT_TIB

    결국 FS:[0] 자기 자신을 가리키는 것이였습니다. Microsoft는 왜 Self 필드를 참조하는 것일까요? :p

    필자 생각으로는, 아마 미래를 위해서 그리 구현해놓은 것 같습니다. TEB가 아닌 다른 구조체가 FS:[]로 바뀌어도, 0x18 오프셋만은 TEB의 포인터를 가리키면 후방 호환성이 유지되니까요.
    (뭐, 그 덕분에 해커가 Self 필드를 수정하면 모든 Windows API에서 TEB에 접근할 때 다른 메모리를 참조할 수 있게 만들 수 있는 틈이 생긴 셈이지만요 :p)

    결론적으로, GetCurrentProcessId() API는 CLIENT_ID를 참조하는 것을 볼 수 있습니다.

    마지막으로, CLIENT_ID 구조체는 다음과 같습니다. 읽어주셔서 감사합니다.

    typedef struct {
    LPVOID UniqueProcess; // Process ID (Offset: 0x0)
    LPVOID UniqueThread; // Thread ID (Offset: 0x4)
    } CLIENT_ID, *PCLIENT_ID;

    ps: Visual Basic이나 Delphi같이 Assembly Language에 접근하기 힘든 언어는, TEB의 주소를 구해야 할 필요가 있습니다.

    Windows NT 이상 버전의 OS에서는 ntdll.dll에 NtCurrentTeb()을 export하고 있고, 그 함수는 인자가 없고, 반환값은 32비트 정수의 TEB 주소값입니다.

    그 함수를 이용해서 TEB의 주소를 구해주고 메모리 API(RtlMoveMemory 등)를 이용해서 읽거나 써주면, FS:[]와 마찬가지의 효과로 TEB에 접근할 수 있습니다.


    AND

    요즘 몇가지 안티리버싱을 해보고있는데, 그중한가지.


    PEB구조에서 NtGlobalFlags 값이 70h이면 디버깅이라고 판단한다.


    찾는 방법은 인터넷에 많이 있는데,


    검출추틴을 만들었는데, 의문점은 일단 아무프로세스하나 실행시키고 WinDBG에서 attach a process 로 붙이면 NtGlobalFlags로 걸리지가 않는다는점.


    아무프로그램을  WinDBG에서 open executable ... 이걸로 실행해서  열면 검출이되고....


    이거뭐야! 내가 잘못짠거야? ㅠ


    언제 값이 변경되는지 알아봐야겠다. 

    'Reversing' 카테고리의 다른 글

    [ANTI-Reversing] ANTI_DLL Injection  (0) 2012.04.24
    [ANTI Reversing] BegingDebuged 설명  (0) 2012.04.24
    PEB구조체 시작 주소 MS Windows 버전별  (0) 2012.04.24
    PEB 구조체  (0) 2012.04.24
    TEB 구조체  (0) 2012.04.24
    AND