최상단

컨텐츠

3. Direct3D Hooking

글 정보

Category
● 프로그래밍/다이렉트 후킹[D3D]
2014.05.14 00:35

본문

3. Direct3D Hooking


 3-1. What is Direct3D Hooking?


 말 그대로 D3D에 대해 Hooking 하는 것이다. 이 문서에서 다루는 D3D Hooking은 원래


프로세스에서 D3D 관련 함수를 호출할 때 D3D 함수 대신 내가 제작한 함수가 호출되게 만드는
것이다.


 이 강의에서 할 D3D Hooking의 원리를 설명해드리면 타겟 프로세스가 그려주는 함수를 호출


할 때 내가 만든 함수를 호출 한 후 그리고 싶은 것을 그리고 원래의 그려주는 함수를 호출하면


프로세스는 정상적으로 작동하면서 내가 원하는 부분이 추가된 것이다. 이 문서에서는 d3dx의


vTable을 참고하여 타겟 함수를 설정하여 Detours 방식의 Hooking을 시도할 것이다.


 D3D Hooking이 쓰이는 대표적인 것으로는 아직까지는 악용되는 것밖에 보질 못했다. FPS


게임에서 Wall Hack, RPG 게임에서 Map Hack 등등 그래픽을 가진 게임에서는 무궁무진하게


D3D Hooking 악용될 수 있다. 이 문서를 통해 FPS 게임이나 기타 프로그램에서 D3D에 대한


후킹을 연구하여 방어 방법에 대해 생각 할 수 있는 기회가 되었으면 좋겠다.


이 강의에서 D3D Hooking 하는데 사용될 방법은 Detours 라는 후킹 기법이다.


Detours에 대한 자세한 내용은 뒤에서 설명하겠다.


악용되는 예 - FPS Game Wall Hack

 


출처 : 네이버 이미지 ( 본 문서와 해당 그림은 상관없습니다.)

 

3-2. D3D Hooking 원리


 D3D Hooking 원리는 함수를 후킹 할 때 사용하는 방법인 "Detours Hook"와 후킹 할 함수의

 
주소를 찾고자 할 때 참고하는 “vTable”를 사용하여 D3D에서 사용하는 함수를 후킹 한다.


"Detours Hook" 와 "vTable“ 에 대한 설명을 간단하게 하고 넘어가겠다.

 


 3-2-1. Detours Hook 이란? (Detours hook 원리에 대해 알고 있다면 넘어가도 됩니다.)


 Detour Patching 또는 inline Function Hooking 이라고도 불리는 이 후킹 방법은 여러 방법들


중에 하나이며 그 중에서도 매우 강력한 기능을 가진 후킹 방법이라고 할 수 있습니다.


Target Function에 Detour Function 으로 점프하는 코드를 심어 Detour Function 이 실행되고


실행이 끝나면 Trampoline Function에서 다시 Target Function을 실행시키고 다시 Detour


Function으로 return할 수 있도록 하는 후킹 기술입니다.

 

 

 


출처 : http://research.microsoft.com/apps/pubs/default.aspx?id=68568


위의 그림처럼 Target Function 보다 Detour Function 이 먼저 실행되고 또 Detour


Function으로 return되기 때문에 어떤 작업을 하거나 결과 값을 수정 할 수 있습니다.


실제 함수 전, 후로 전부 후킹이 되기 때문에 매우 효과적인 조작이 가능합니다.

 

 

 

후킹 방식은 위의 그림과 같이 Trampoline Function에서 Target Function에서 jmp 코드가

 
덮어쓴 Function Instruction을 수행하고 Target Function에서 해당 Function Instruction이

실행된 이후 번지로 점프하여 Target Function 함수가 정상적으로 호출 된 것처럼 실행되도록

 

합니다. 이 문서에서는 D3D Hooking에서 함수 후킹 할 때 Detours Hook을 사용합니다.

 

Detours Hook 에 관련된 내용은 다음 강의를 통해 자세히 다루겠습니다.

 

 

void *DetourFunc(BYTE *src, const BYTE *dst, const int len) 
{
        BYTE *jmp = (BYTE*)malloc(len+5);
        DWORD dwback;
        VirtualProtect(src, len, PAGE_READWRITE, &dwback);

         memcpy(jmp, src, len); jmp += len;
        jmp[0] = 0xE9;         *(DWORD*)(jmp+1) = (DWORD)(src+len - jmp) - 5;
        src[0] = 0xE9;         *(DWORD*)(src+1) = (DWORD)(dst - src) - 5;
        VirtualProtect(src, len, dwback, &dwback);    

        return (jmp-len);
}      

 - 이 강의에서 사용하는 Detours function 소스 (자세한 설명은 뒤에서 하겠습니다.)

 

3-2-1. vTable 이란? (vTable에 대해 알고 있다면 넘어가도 됩니다.)

 

vTable(virtual table)이란 가상 함수의 번지 목록을 가지는 일종의 포인터 배열이다.


즉, 이 클래스에 소속된 가상 함수들이 어떤 번지에 저장되어 있는지를 표 형태로 저장해 놓은


목록이다. 컴파일러는 가상 함수를 단 하나라도 가진 클래스에 대해 vTable을 작성하는데


이 테이블에는 클래스에 소속된 가상 함수들의 실제 번지들이 선언된 순서대로 기록되어 있다.


그리고 이 테이블 타입의 객체가 생성될 때, 각 객체의 선두에 vTable의 번지인 vptr을 기록


한다. 간단한 소스와 그림을 통해 vTable의 구조와 원리를 간단하게 살펴보고 넘어가도록 하자.

 

 

 #include <iostream.h>
class B{
private:         int memB;
public:
        b() : memB(0x11111111){}
        virtual void f1() { puts("B::f1");}
        virtual void f2() { puts("B::f2");}
        virtual void f3() { puts("B::f3");}
        void normal() { puts("non virtual");}
};  class D : public B
{
private:         int memD;
public:
        D() : memD(0x22222222){}
        virtual void f1() { puts("D::f1");}
        virtual void f2() { puts("D::f2");}
};  void main()
{
        B *pB;
        B b;
        D d;
       pB=&b;
        pB->f2();
        pB=&d;
        pB->f2();
        pB->f3();
}

 

실행 결과는 다음과 같다.
B::f2
D::f2
B::f3

 

B가 세 개의 가상 함수와 하나의 비가상 함수를 정의하고 있으며 이를 상속 받는 D는 그 중


f1, f2를 재정의하고 있다. 테스트의 편의를 위해 멤버 변수도 하나씩 선언했다. B와 D의 객체


b와 d가 생성되었을 때 이 객체들이 메모리에 구현된 모양을 그려 보면 다음과 같다.

 

 

컴파일러는 B 클래스를 위해 B 클래스에 속한 가상 함수의 번지를 vTable로 작성한다.


vTable은 가상 함수들의 포인터 배열이라고 할 수 있는데 비가상 함수의 번지는 목록에서


제외된다. 그래서 vTable에 normal 함수의 번지는 없는데 이 함수는 정적으로 결합되므로


테이블에 있을 필요가 없다. B 타입의 b객체에는 자신의 멤버 변수 memB앞에 B클래스의


vTable에 대한 포인터 vptr이 먼저 배치되고 이 포인터가 가리키는 vTable에는 자신이 호출


할 수 있는 가상 함수들에 대한 실제 번지들이 기록되어 있다. B 클래스는 모든 가상 함수의


코드를 정의하고 있으므로 vTable에는 자신의 멤버 함수들에 대한 번지만 있다.


 D클래스도 가상 함수를 가지고 있으므로 컴파일러는 D에 대해서도 vTable을 작성한다. 이


테이블의 f1, f2는 D가 재 정의한 함수를 가리키고 있으며 f3은 B로부터 상속받은 B::f3을


가리키고 있다. 이 표에 의해 D타입의 객체가 f1, f2를 호출하면 D::f1, D::f2가 호출되지만 f3에


대해서는 상속받은 B::f3이 호출되어야 한다는 것을 알 수 있다. D타입의 객체 d에는 상속받은


memB와 memD 앞에 D클래스의 vTable을 가리키는 포인터 vptr이 배치되어 있다.


즉, vTable의 주소를 알면 선언된 순서대로 위치하기 때문에 vTable의 주소 + 함수 offset을


통해 Target Function 위치를 알 수 있다.


참고 : http://winapi.co.kr/clec/cpp3/30-1-4.png

 

 

 

 

 

저작자 표시 비영리 변경 금지
신고

트랙백과 댓글 여닫기

트랙백 0 댓글 1

페이징

PREV NEXT

1 2 3 4 5 6 7 8 ··· 14

TOP

티스토리 툴바