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