정보공간_1

[6기 대구 허정욱] Detours 3.0 Express #2.Class Member Hooking 본문

IT 놀이터/Elite Member Tech & Talk

[6기 대구 허정욱] Detours 3.0 Express #2.Class Member Hooking

알 수 없는 사용자 2014. 9. 13. 03:25

#2. Class Member Hooking


Detours 오픈소스를 사용해서의 이로운 점은,

사용자는 내부의 프로세스 전체를 이해하지 않아도, 원하는 서비스를 자신의 프로젝트에 주입 시킬 수 있다는 것입니다.

이전에 사용했던 방식은 API 후킹 방법입니다.

즉  Function후킹방식을 이용하여, 함수의 주소를 가로채는 방식이였습니다.

이번에는 Class Member Function 후킹방법을 알아보겠습니다.


Class Member Function Hooking 하기

지난번의 함수 후킹방식처럼, 클래스도 클래스 멤버함수를 똑같은 방식으로 후킹할 수 있을까요?

먼저 아래와 같은 소스를 Visual Studio에 타이핑합니다.




위의 소스는 아래의 파일을 받으시면 됩니다.

 detours_class_hooking.cpp


위의 소스는 아래의 그림과 같습니다.



하나의 HookingTest 클래스를 만들었으며,

test1, test2라는 객체를 생성하는 동시에, 각 객체에 있는 변수 number에 숫자 5와 6을 초기화해줍니다.

여기서 저희는 C++ 기초적인 지식중의 하나인 의문점을 생각해볼 수 있습니다.

test1에 있는 함수와 test2의 함수의 메모리 주소는 과연 같을까요?

만약 같다면, 하나의 Hooking함수로 여러 개의 객체의 멤버함수를 후킹할 수 있을 것입니다.

만약 다르다면 각 객체의 메모리 주소를 컨트롤하여 멤버 함수를 후킹해야 합니다.


먼저다음부터 보여지는 것은 어셈블리 코드가 많이 보일 것입니다.

아래 정도의 어셈블리 명령어를 인지하고 있다면 어셈블리 코드를 쉽게 보실 수 있을 것입니다.

 명령어

 설명

 PUSH

 (Data Transfer) Push

오퍼랜드의 내용을 스택에 쌓는다. 

 POP

 (Data Transfer) Pop 

스택으로부터 값을 뽑아낸다. 

LEA 

  (Data Transfer)  Load Effective Address to Register

메모리의 오프셋값을 레지스터로 로드

 CALL

(Control Transfer) Call 

프로시저 호출 

MOV 

 (Data Transfer)  Move

데이터 이동(전송) 

 EAX

범용 레지스터

함수 리턴값 저장 및 계산에 이용 

EBX 

범용 레지스터

계산속도높이기, 일반적인 값 저장 

ECX 

 범용 레지스터

루프 카운터 및 함수의 파라미터로 사용 

 ESP, EBP

범용 레지스터

함수 호출과 스택 연산에 사용

EDI 

범용 레지스터 

데이터 연산의 결과가 저장되는 위치 


좀 더 많은 명령어를 알고 싶으시면

어셈블리 명령어 정리
범용레지스터 정리

위의 두 개의 블로그에서 확인하시면 됩니다.


Visual Studio에서 어셈블리 코드를 보는 방법 중 가장 많이 쓰는 두  가지의 방법을 소개하겠습니다.

한 가지는 출력 파일을 .asm파일로도 출력하는 것입니다.


[1] 현재 프로젝트의 속성을 누릅니다.

[2] 구성속성을 선택

[3] C/C++ 선택

[4] 출력파일선택

[5] 어셈블러 출력 에 디렉토리를 정해 줍니다.

[6] 그리고 가장 오른쪽의 선택 옵션에서 소스코드로된 어셈블리를 선택합니다.


그리고 빌드를 하면 됩니다.


빌드를 하게 되면, 위와 같은 asm파일이 Debug 폴더 안에 있는 것을 볼 수 있습니다.





위와 같이 .asm 파일을 통해서 어셈블리를 확인할 수 있습니다.


다른 한 가지는 디버그 탭에 있는 [디스어셈블리]를 이용하는 것입니다.


[1] 디버그 클릭

[2] 창 클릭

[3] 디스어셈블리 클릭



두 번째 방법을 이용해서 어셈블리를 확인하면, 첫 번째 방법과 달리 메모리 주소값도 나오는 것을 볼 수 있습니다.

우리는 두 번째 방법을 통해 확인한 어셈블리 코드를 통해서 확인할 수 있는 한 가지가 있습니다.

test1 객체에서 부르는 HookingTest 클래스의 주소와 

test2 객체에서 부르는 HookingTest 클래스의 주소가 0F9143Dh로 같다는 것을 알 수 있습니다.

이것은 동일한 클래스의 인스턴스가 여러 개가 존재하더라도 해당 클래스의 멤버 함수는 같은 메모리의 주소를 부른다는 것을 의미합니다.




이제 클래스의 멤버함수를 후킹하려면, 색칠한 부분을 이해하셔야 합니다.

위 색칠한 부분은 클래스의 인스턴스의 주소를 ecx에 넣고, 해당 함수를 콜한다는 규칙을 의미합니다.

ecx는 휘발성 메모리이므로 일반 후킹 함수에서 HookingTestFunction함수의 주소값을 알 수가 없습니다.

만약 ecx값이 보존이 된다면, 일반 후킹함수처럼 HookingTestFunction의 주소값을 가지고 우리가 만든 후킹함수로 jmp할 수 있을 것입니다.

이렇게 이해하시면 이제 우리는 일반 함수로 후킹하는 것이 아니라, 클래스의 멤버함수로 후킹을 해야한다는 방법을 생각해볼 수 있습니다.


[위의 어셈블리는 illeft의 이야기 블로그를 참조하였습니다.]


위와 같은 소스코드를 보면, 어셈블리코드로 __asm mov ecs, [this];를 통해서 멤버함수의 주소를 가져오는 것을 알 수 있습니다. 이러한 방식을 통해서 후킹 클래스 멤버함수로 내가 후킹하려고 하는 멤버 함수를 찾을 수 있습니다.

이렇게만 만들어준다면, 이전 #1.API Hooking과 같은 방식으로 후킹을 진행하면 됩니다.


이외에도 Detours에서는 멤버함수 후킹을 할 수 있는 예제를 제공하고 있습니다.


member.cpp


이번에는 클래스 멤버 함수 후킹에 대해 알아보았습니다.

MyHookingJMP는 위의 member.cpp의 방식을 좀 더 이해하기 쉽도록 만든 것입니다.

Detours API의 예제들은 대부분이 위와 같은 방식이며, 어셈블리 코드로 디버깅을 하며 분석해보는 것이 더 빨리 이해가 될 것입니다.