좋은자료가 있어 참고합니다.
본 문서는 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, 0x06의 2 byte를 제외한 그 다음 4 Byte를 vTable 변수에 저장하라는 의미다.
**현재 위와 같이 패턴을 이용해서 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로 유도한다던지 하는 방법등 여러가지 방법이 있을 수 있겠다.
'Reversing' 카테고리의 다른 글
Windows8 OS MajorVersion , MinorVersion (0) | 2012.11.27 |
---|---|
PE구조파일 Image Base Address 쉽게 얻자 (0) | 2012.06.11 |
[ANTI-Reversing] ANTI_DLL Injection (0) | 2012.04.24 |
[ANTI Reversing] BegingDebuged 설명 (0) | 2012.04.24 |
PEB구조체 시작 주소 MS Windows 버전별 (0) | 2012.04.24 |