API Hooking x86 x64
API Hooking 은 리버싱과 윈도우 프로그래밍을 조금이라도 하였으면 쉽게 접근할 수 있을 거라고 생각합니다.
DLL Injection을 통하여 API Hooking을 하시는 경우 아래 글을 참고하셔도 좋을거 같습니다.
API 란 무엇일까요?
API는 시스템 자원에 접근하기 위한 OS가 지원하는 인터페이스 입니다.
사용자가 시스템 유지에 중요한 자원을 함부로 사용하는 것을 막아 놓기 위해서 만들었습니다.
Hook 이란건 정보를 가로채거나 실행 흐름을 변경하는 의미입니다.
즉, API Hooking은 API 호출하는 중간에 가로채어 원하는 행위를 하는 기술입니다.
API Hooking을 하기 전에 API 가 어떻게 작동하는지에 대해서 간단하게 알아보고 가겠습니다.
후킹된 API의 작동 방식은 정상적인 API처럼 사용자가 입력한 인자가 전달되지만, 보통 API 첫부분을 임의의 함수로 점프시킨 뒤 함수가 종료하고 정상적인 API로 돌아와 실행된다.
API 후킹은 사용자 함수를 Hook할 수도 있고 API를 Hook할 수도 있다. 여기서는 기본적인 API 후킹만 다루도록 하겠다.
후킹하기 위해서 해당 함수의 주소를 알아서 첫부분을 코드패치해주는 것이 가장 기초적인 방법이다.
그럼 어떻게 API의 주소를 얻어 낼것인가??
그건 GetModuleHandle API와 GetProcAddress API를 이용하여 알아 낼 수 있다.
GetModuleHandle 로 DLL 의 핸들 값을 가져옵니다.
그리고 GetProcAddress 로 DLL 에서 원하는 함수를 불러와 주소를 받습니다.
이렇게 API의 주소를 받아왔으니 이제 해당 API에서 여러분이 만든 함수로 점프시키기 위해서 상대 주소를 구하는 방식과 코드 패치를 해야합니다.
우선 코드 패치전에 패치하기 위한 코드를 먼저 짜보겠습니다.
x86은 최대 4GB 의 메모리를 가지기 때문에 JUMP 0xFFFFFFFF 으로 커버가 됩니다.
즉, JUMP의 opcode인 0xe9 그리고 0x00, 0x00, 0x00, 0x00 으로 x86 내에 있는 모든 영역에 접근할 수 있습니다.
1 | BYTE code[] = {0xE9, 0x00, 0x00, 0x00, 0x00}; | cs |
점프를 하기 위한 opcode를 삽입하였으니 0x00 부분에 상대 주소를 넣으면 됩니다.
상대 주소 구하는건 아래 공식을 이용하면 됩니다.
Relatvie_Offset = JumpFunction - GetProcAddress_ret - 5
JumpFunction 은 여러분이 만든 함수이며, GetProcAddress_ret는 API의 주소입니다. 그리고 - 5 를 해줌으로서 상대 주소를 구할 수 있게 됩니다.
그러면 왜 -5를 하는가?
CPU가 명령어 포인터를 계산할 때 아래처럼 계산을 합니다.
Instruction_pointer = instruction_pointer + relative_offset + 5
여기서 Instruction_pointer은 JumpFunction이라고 생각하시면 됩니다.
더 자세한건,,,
공부해서 알려주세요...
상대 주소를 구하였으니 이제 코드 패치를 해보자.
코드 패치에서 사용하는 API는 VirtualProtect 입니다.
이건 lpAddress 부분을 dwSize 만큼 flNewProtect 로 페이지 권한을 변경하는 API입니다.
그리고 lpflOldProtect는 페이지 권한 바꾸기 이전의 권한을 넣어두는 임시 버퍼 같은 역할을 합니다.
이 API를 아래처럼 2번 호출하여 처음엔 상대 주소를 넣고 권한을 바꾸며, 두번째는 JUMP opcode와 상대 주소가 들어간 코드로 패치해 줍니다.
[여기에 있는 '/' 는 나누는게 아니라 분리용 입니다]
이렇게 하면 아래 사진과 같이 코드 패치가 완료 됩니다.
이렇게 x86 후킹이 완료 되었습니다.
x64 는 x86과 차이가 거의 없습니다.
우선 JUMP 방식이 다릅니다.
x86에서 사용했던 0xE9를 사용하면 제한적인 범위에서 밖에 점프를 하지 못합니다.
±2GB 밖에 하지를 못하죠.
하지만 x64에서는 메모리 범위가 엄청나죠...
그러면 0xE9를 사용해서 계속해서 점프를 하면 되는 것이 아니냐? 라고 생각하시겠지만 제가 0xE9로 점프를 계속 했더니 레지스터가 바뀌어 정상적인 작동을 하지 않는 경우가 생겼습니다.
또 다른 방법은
mov rax, address
jmp rax
를 하면 됩니다.
하지만 이 방식도 레지스터가 깨집니다.
push 4byte_low_address
mov dword[rsp+4], 4byte_high_address
ret
위와 같은 방식으로 하면 깨지지 않고 보낼 수 있습니다.
'My Study > System' 카테고리의 다른 글
Process Handle (0) | 2016.06.30 |
---|---|
Kernel Object (0) | 2016.06.29 |
DLL Injection (3) - Injector 사용 (0) | 2016.05.23 |
DLL Injection (2) - EAT 를 이용한 DLL Injection (0) | 2016.05.23 |
DLL Injection (1) - 간단 기본 개념 (0) | 2016.05.23 |