후킹
"오늘의AI위키"의 AI를 통해 더욱 풍부하고 폭넓은 지식 경험을 누리세요.
1. 개요
후킹은 소프트웨어의 실행 흐름을 변경하는 기술로, 소스 코드 수정이나 런타임 수정을 통해 구현된다. 소스 코드 수정 방식은 리버스 엔지니어링을 통해 실행 파일이나 라이브러리의 코드를 직접 수정하거나, 래퍼 라이브러리를 사용하여 함수 호출을 가로채는 방식으로 이루어진다. 런타임 수정 방식은 운영체제나 소프트웨어가 제공하는 훅 기능을 활용하거나, 메모리를 직접 수정하여 함수 호출을 가로채는 방식으로 이루어진다. 후킹은 윈도우, 리눅스, Emacs, OS X 및 iOS 등 다양한 환경에서 사용되며, API 후킹, 가상 메서드 테이블 후킹, 내부 IAT 후킹 등 다양한 구현 방식이 존재한다.
더 읽어볼만한 페이지
- 제어 흐름 - 프로그램 카운터
프로그램 카운터는 CPU 내에서 다음에 실행될 명령어의 주소를 저장하는 레지스터로, 명령어 사이클의 fetch 단계에서 사용되어 명령어를 가져오고 실행 후 갱신되며, CPU 성능 향상 기술과 현대 프로그래밍 모델에 영향을 미친다. - 제어 흐름 - 예외 처리
예외 처리는 프로그램 실행 중 예외 발생 시 정상적인 실행 흐름을 유지하거나 안전하게 종료하기 위한 메커니즘으로, 많은 프로그래밍 언어에서 제공하며 예외 안전성을 목표로 한다. - 컴퓨터 보안 - 얼굴 인식 시스템
얼굴 인식 시스템은 디지털 이미지나 비디오에서 사람 얼굴을 감지하고 식별하는 기술로, 다양한 알고리즘 발전을 거쳐 보안, 신원 확인 등에 활용되지만, 편향성, 개인 정보 침해, 기술적 한계와 같은 윤리적 문제도 야기한다. - 컴퓨터 보안 - 워터마크
워터마크는 종이 제조 시 두께 차이를 이용해 만들어지는 표식으로, 위조 방지를 위해 지폐나 여권 등에 사용되며 댄디 롤 등의 제작 기법을 통해 만들어지고 컴퓨터 프린터 인쇄 기술로도 활용된다.
후킹 | |
---|---|
개요 | |
정의 | 소프트웨어나 하드웨어 기능의 정상적인 작동 흐름을 변경하거나 확장하기 위해 사용되는 프로그래밍 기술 |
목표 | 디버깅 기능 추가 악의적인 목적 (예: 멀웨어) |
작동 방식 | 다른 소프트웨어 구성 요소에 의해 호출되거나 실행되는 코드의 위치를 가로채거나 수정 |
기술적 측면 | |
소프트웨어 후킹 | 운영 체제, 응용 프로그램 또는 다른 소프트웨어 구성 요소의 기능을 수정하는 데 사용 메시지 후킹, API 후킹, 시스템 콜 후킹 등 다양한 형태 존재 |
하드웨어 후킹 | 하드웨어 인터럽트 처리 또는 메모리 접근 방식을 변경하는 데 사용 펌웨어 수준에서 발생 가능 |
활용 분야 | |
디버깅 및 모니터링 | 프로그램의 동작을 추적하고 오류를 진단 |
기능 확장 | 기존 소프트웨어에 새로운 기능을 추가 |
보안 | 악성 코드 탐지 및 제거 시스템 무결성 유지 |
게임 핵 | 게임 규칙을 변경하거나 유리한 조건을 만들기 위해 사용 (부정적인 사용 사례) |
위험성 및 윤리적 고려 사항 | |
시스템 불안정 | 잘못된 후킹으로 인해 시스템 충돌 또는 오작동 발생 가능 |
보안 취약점 | 악의적인 사용자가 시스템을 제어하거나 데이터를 훔치는 데 사용될 수 있음 |
법적 문제 | 저작권 침해 또는 라이선스 계약 위반의 소지 |
종류 | |
API 후킹 | 응용 프로그램 프로그래밍 인터페이스(API) 호출을 가로채고 수정 |
메시지 후킹 | 윈도우 메시지 시스템을 통해 전달되는 메시지를 가로채고 처리 |
IAT 후킹 | Import Address Table (IAT)를 수정하여 특정 함수의 호출을 가로채는 방법 |
VTable 후킹 | C++ 객체의 가상 함수 테이블(VTable)을 수정하여 메서드 호출을 가로채는 방법 |
커널 후킹 | 운영 체제 커널의 기능을 수정하는 고급 기술 |
인라인 후킹 | 함수의 시작 부분에 코드를 삽입하여 함수의 실행 흐름을 가로채는 방법 |
2. 방법
후킹은 크게 소스 코드를 수정하는 방법과 런타임에 수정하는 방법으로 나뉜다.
소스 코드 수정에는 리버스 엔지니어링 기술을 이용해 실행 파일이나 라이브러리를 물리적으로 변경하는 방법과 래퍼 라이브러리를 사용하는 방법이 있다.[1]
런타임 수정에는 운영 체제나 소프트웨어에서 제공하는 기능을 활용하는 방법과, 실행 중인 프로세스의 메모리에 있는 명령과 구조를 변경하는 방법이 있다.[1]
2. 1. 소스 코드 수정
일반적으로 훅은 소프트웨어가 이미 실행 중일 때 삽입되지만, 후킹은 응용 프로그램이 실행되기 전에 쓰일 수 있는 전략이다. 후킹은 애플리케이션이 실행되기 전에 실행 파일 또는 라이브러리의 소스를 리버스 엔지니어링 기술을 통해 수정하여 수행할 수 있다. 이는 일반적으로 함수 호출을 가로채서 모니터링하거나 완전히 대체하는 데 사용된다.[1]2. 1. 1. 물리적 수정
리버스 엔지니어링 기법으로 실행 또는 라이브러리를 물리적으로 수정하여 후킹을 할 수 있다. 이러한 기법은 일반적으로 다른 모니터로 함수 호출을 가로채거나 그것들을 완전히 다른 함수로 대체하는데 사용된다.[1]예를 들어, 디스어셈블러를 사용하여 모듈 내에서 함수의 시작점을 찾을 수 있다. 그 다음, 다른 라이브러리 모듈을 동적으로 로드하고, 로드된 라이브러리 내에서 원하는 함수를 실행하도록 변경할 수 있다. 해당되는 경우, 후킹으로 얻을 수 있는 관련된 다른 접근 방법은 실행 파일의 임포트 테이블을 변경하는 것이다. 이 테이블은 추가 라이브러리 모듈을 로드하도록 수정될 수 있을뿐만 아니라 함수가 응용 프로그램에 의해 호출될 때 외부 코드가 호출되는 것으로 바뀔 수도 있다.[1]
2. 1. 2. 래퍼 라이브러리
함수 후킹을 수행하는 또 다른 방법은 래퍼 라이브러리를 통해 함수 호출을 가로채는 것이다. 래퍼는 응용 프로그램이 로드하는 라이브러리의 버전으로, 교체할 원본 라이브러리와 동일한 모든 기능을 갖는다. 즉, 접근 가능한 모든 함수는 원본과 대체 라이브러리 간에 본질적으로 동일하다. 이 래퍼 라이브러리는 원본 라이브러리의 기능을 호출하거나, 완전히 새로운 논리 집합으로 대체하도록 설계할 수 있다.2. 2. 런타임 수정
소프트웨어 실행 중, 즉 런타임에 훅을 삽입하는 방법은 크게 두 가지가 있다. 운영체제나 소프트웨어에서 제공하는 기능을 이용하거나, 메모리 상의 코드를 직접 수정하는 것이다.[1]기계어 수준의 단순한 제어가 이루어지는 8비트 컴퓨터에서는 훅으로 호출 주소가 정의되어 있다. 예를 들어, MSX는 워크 에어리어 안에 약 100개의 훅용 주소가 정의되어 있으며, 각각 타이머 인터럽트나 각종 입출력 등에 할당되어 있다. 훅은 5바이트 간격으로 배치되어 있으며, 초기 상태에서는 RET(Z80에서의 서브루틴으로부터의 복귀 명령)이 기록되어 있다. 사용자는 메모리의 다른 영역에 독자적인 처리를 배치하고, 훅용 5바이트에는 독자 처리로의 점프 명령을 기록한다. 이러한 방식은 독자적인 처리를 프로시저나 함수로 정의해두고, 등록 API를 사용하여 등록하는 현대의 고급 프로그래밍 언어에서 자주 보이는 구현 형태이다.
훅이 변수명이나 함수명으로 정의된 시스템도 있다. 인터프리터형 시스템이나 컴파일러형에서도 이용자가 기술한 처리를 링커에 함께 걸리는 경우(라이브러리가 제공하는 훅처럼) 등에 보인다.
예를 들어 텍스트 편집기인 GNU Emacs는 Emacs Lisp라는 언어로 다양한 기능이 기술되는데, 대부분의 Emacs Lisp 패키지는 변수명 뒤에 "-hook"가 붙는 변수를 훅으로 정의하고 있다. GNU Emacs 시작 시에는 emacs-startup-hook가, c-mode를 호출했을 때에는 c-mode-hook가 평가된다. Lisp 프로그램은 S식이라는 리스트로 되어 있으며, 변수에 S식을 설정해두면 변수를 평가할 때 해당 프로그램이 실행된다.
2. 2. 1. 운영체제/소프트웨어 제공 기능
운영체제나 소프트웨어는 훅을 삽입하는 프로세스에 충분한 권한이 있다면 런타임 도중에 이벤트 훅을 쉽게 삽입할 수 있도록 지원한다. 예를 들어, 마이크로소프트 윈도우에서는 시스템 및 응용 프로그램 이벤트를 처리하거나 수정하기 위해 훅을 삽입할 수 있다. 이러한 훅은 대화 상자, 스크롤바, 메뉴뿐만 아니라 키보드 및 마우스 이벤트에도 적용될 수 있다. 리눅스에서는 Netfilter를 통해 커널 내의 네트워크 이벤트를 처리하는 데 훅을 사용할 수 있다.마이크로소프트 윈도우의 경우, OS가 제공하는 훅은 정수 ID를 가지며, `SetWindowsHookEx` 함수를 사용하여 등록할 수 있다. 훅 ID와 사용자 정의 처리 함수 포인터를 인수로 `SetWindowsHookEx` 함수를 호출하면 훅 처리로 등록된다.[2]
2. 2. 2. 메모리 직접 수정
프로세스가 만든 라이브러리 함수 호출을 가로채는 특별한 형태의 후킹은 대상 함수의 처음 몇 개의 코드 명령을 수정하여 주입된 코드로 점프하도록 구현된다. 또는 공유 라이브러리 개념을 사용하는 시스템에서는 인터럽트 벡터 테이블 또는 가져오기 설명자 테이블을 메모리에서 수정할 수 있다. 이는 이미 실행 중인 프로세스의 메모리에 있는 명령과 구조를 변경하는 방식으로 이루어진다.[1]3. 구현 예제
후킹은 다양한 프로그래밍 언어와 환경에서 구현될 수 있다. 몇 가지 예시는 다음과 같다.
- 가상 메서드 테이블 후킹 (C++): C++에서 클래스가 가상 함수를 정의하거나 상속할 때, 컴파일러는 가상 메서드 테이블(VMT)을 가리키는 숨겨진 멤버 변수를 추가한다. 이 VMT는 클래스의 인스턴스가 호출할 수 있는 모든 가상 함수에 대한 포인터 배열이다. 가상 함수는 VMT 내에서 해당 함수에 대한 포인터를 교체하여 후킹할 수 있다.[1]
```cpp
#include
#include "windows.h"
using namespace std;
class VirtualClass
{
public:
int number;
virtual void VirtualFn1() // 후킹될 가상 함수입니다.
{
cout << "VirtualFn1 called " << number++ "\n\n";
}
};
using VirtualFn1_t = void(__thiscall*)(void* thisptr);
VirtualFn1_t orig_VirtualFn1;
void __fastcall hkVirtualFn1(void* thisptr, int edx) // 후킹 함수입니다.
{
cout << "Hook function called" << "\n";
orig_VirtualFn1(thisptr); // 원래 함수를 호출합니다.
}
int main()
{
VirtualClass* myClass = new VirtualClass(); // VirtualClass의 인스턴스를 생성합니다.
void vTablePtr = *reinterpret_cast
DWORD oldProtection;
VirtualProtect(vTablePtr, 4, PAGE_EXECUTE_READWRITE, &oldProtection); // VMT의 페이지 보호를 변경합니다.
orig_VirtualFn1 = reinterpret_cast
- vTablePtr = &hkVirtualFn1; // VirtualFn1의 포인터를 후킹 함수로 덮어씁니다.
VirtualProtect(vTablePtr, 4, oldProtection, 0); // 페이지 보호를 복원합니다.
myClass->VirtualFn1(); // 후킹된 함수를 호출합니다.
myClass->VirtualFn1();
myClass->VirtualFn1();
delete myClass;
return 0;
}
```
- JMP 명령어를 이용한 API/함수 후킹: 대상 함수의 처음 몇 바이트를 새 함수로의 JMP 명령어로 덮어쓰는 방식으로 후킹한다. 이 코드는 동적 연결 라이브러리 파일로 컴파일된 다음, DLL 인젝션을 사용하여 대상 프로세스에 로드된다.[2]
```c
/*
chrom-lib 기반, GNU LGPL 라이선스
소스: https://github.com/linuxexp/chrom-lib
- /
#include
#define SIZE 6
typedef int (WINAPI *pMessageBoxW)(HWND, LPCWSTR, LPCWSTR, UINT); // Messagebox 프로토타입
int WINAPI MyMessageBoxW(HWND, LPCWSTR, LPCWSTR, UINT); // 우회 함수
void BeginRedirect(LPVOID);
pMessageBoxW pOrigMBAddress = NULL; // 원본 주소
BYTE oldBytes[SIZE] = {0}; // 백업
BYTE JMP[SIZE] = {0}; // JMP 명령어
DWORD oldProtect, myProtect = PAGE_EXECUTE_READWRITE;
INT APIENTRY DllMain(HMODULE hDLL, DWORD Reason, LPVOID Reserved)
{
switch (Reason)
{
case DLL_PROCESS_ATTACH: // 연결 시
pOrigMBAddress = (pMessageBoxW)
GetProcAddress(GetModuleHandleA("user32.dll"), // 원본 주소
"MessageBoxW");
if (pOrigMBAddress != NULL)
BeginRedirect(MyMessageBoxW); // 우회
break;
case DLL_PROCESS_DETACH:
VirtualProtect((LPVOID)pOrigMBAddress, SIZE, myProtect, &oldProtect); // 읽기/쓰기
memcpy(pOrigMBAddress, oldBytes, SIZE); // 복원
VirtualProtect((LPVOID)pOrigMBAddress, SIZE, oldProtect, &myProtect); // 보호
case DLL_THREAD_ATTACH:
case DLL_THREAD_DETACH:
break;
}
return TRUE;
}
void BeginRedirect(LPVOID newFunction)
{
BYTE tempJMP[SIZE] = {0xE9, 0x90, 0x90, 0x90, 0x90, 0xC3}; // JMP, NOP, RET
memcpy(JMP, tempJMP, SIZE); // JMP 명령어
DWORD JMPSize = ((DWORD)newFunction - (DWORD)pOrigMBAddress - 5); // 점프 거리
VirtualProtect((LPVOID)pOrigMBAddress, SIZE, // 읽기/쓰기
PAGE_EXECUTE_READWRITE, &oldProtect);
memcpy(oldBytes, pOrigMBAddress, SIZE); // 백업
memcpy(&JMP[1], &JMPSize, 4); // 점프 거리
memcpy(pOrigMBAddress, JMP, SIZE); // 점프
VirtualProtect((LPVOID)pOrigMBAddress, SIZE, oldProtect, &myProtect); // 보호
}
int WINAPI MyMessageBoxW(HWND hWnd, LPCWSTR lpText, LPCWSTR lpCaption, UINT uiType)
{
VirtualProtect((LPVOID)pOrigMBAddress, SIZE, myProtect, &oldProtect); // 읽기/쓰기
memcpy(pOrigMBAddress, oldBytes, SIZE); // 백업
int retValue = MessageBoxW(hWnd, lpText, lpCaption, uiType); // 반환 값
memcpy(pOrigMBAddress, JMP, SIZE); // 점프
VirtualProtect((LPVOID)pOrigMBAddress, SIZE, oldProtect, &myProtect); // 보호
return retValue; // 반환
}
```
- 내부 IAT 후킹: 다른 모듈에서 가져온 함수를 후킹한다. PE(Portable Executable, 실행 파일) 헤더의 Import Address Table, 가져오기 주소 테이블(IAT)를 조작한다. 안티바이러스 소프트웨어나 안티 치트 소프트웨어에 의해 감지될 가능성이 적다.[1]
```c
#include
typedef int(__stdcall *pMessageBoxA) (HWND hWnd, LPCSTR lpText, LPCSTR lpCaption, UINT uType); // MessageBoxA 타입
pMessageBoxA RealMessageBoxA; // 원래 함수 포인터
void DetourIATptr(const char* function, void* newfunction, HMODULE module);
int __stdcall NewMessageBoxA(HWND hWnd, LPCSTR lpText, LPCSTR lpCaption, UINT uType) { // 가짜 함수
printf("MessageBoxA: %s\n", lpText);
return RealMessageBoxA(hWnd, lpText, lpCaption, uType); // 실제 함수 호출
}
int main(int argc, CHAR *argv[]) {
DetourIATptr("MessageBoxA",(void*)NewMessageBoxA,0); // 함수 후킹
MessageBoxA(NULL, "Just A MessageBox", "Just A MessageBox", 0); // 함수 호출
return 0;
}
void **IATfind(const char *function, HMODULE module) { // IAT 항목 찾기
int ip = 0;
if (module == 0)
module = GetModuleHandle(0);
PIMAGE_DOS_HEADER pImgDosHeaders = (PIMAGE_DOS_HEADER)module;
PIMAGE_NT_HEADERS pImgNTHeaders = (PIMAGE_NT_HEADERS)((LPBYTE)pImgDosHeaders + pImgDosHeaders->e_lfanew);
PIMAGE_IMPORT_DESCRIPTOR pImgImportDesc = (PIMAGE_IMPORT_DESCRIPTOR)((LPBYTE)pImgDosHeaders + pImgNTHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress);
if (pImgDosHeaders->e_magic != IMAGE_DOS_SIGNATURE)
printf("libPE 오류\n");
for (IMAGE_IMPORT_DESCRIPTOR *iid = pImgImportDesc; iid->Name != NULL; iid++) {
for (int funcIdx = 0; *(funcIdx + (LPVOID*)(iid->FirstThunk + (SIZE_T)module)) != NULL; funcIdx++) {
char *modFuncName = (char*)(*(funcIdx + (SIZE_T*)(iid->OriginalFirstThunk + (SIZE_T)module)) + (SIZE_T)module + 2);
const uintptr_t nModFuncName = (uintptr_t)modFuncName;
bool isString = !(nModFuncName & (sizeof(nModFuncName) == 4 ? 0x80000000 : 0x8000000000000000));
if (isString) {
if (!_stricmp(function, modFuncName))
return funcIdx + (LPVOID*)(iid->FirstThunk + (SIZE_T)module);
}
}
}
return 0;
}
void DetourIATptr(const char *function, void *newfunction, HMODULE module) {
void **funcptr = IATfind(function, module);
if (*funcptr == newfunction)
return;
DWORD oldrights, newrights = PAGE_READWRITE;
VirtualProtect(funcptr, sizeof(LPVOID), newrights, &oldrights); // READWRITE
RealMessageBoxA = (pMessageBoxA)*funcptr;
- funcptr = newfunction;
VirtualProtect(funcptr, sizeof(LPVOID), oldrights, &newrights); // 복원
}
```
- Netfilter 후킹 (리눅스): 리눅스 커널에서 Netfilter를 사용하여 네트워크 트래픽을 변경한다.
```c
#include
#include
#include
#include
#include
#include
#include
#include
static const uint16_t port = 25; // 드롭할 포트
static unsigned int hook_func(unsigned int hooknum, // 후크 함수
struct sk_buff **pskb,
const struct net_device *in,
const struct net_device *out,
int (*okfn)(struct sk_buff *))
{
struct iphdr *iph = ip_hdr(*pskb);
struct tcphdr *tcph, tcpbuf;
if (iph->protocol != IPPROTO_TCP)
return NF_ACCEPT;
tcph = skb_header_pointer(*pskb, ip_hdrlen(*pskb), sizeof(*tcph), &tcpbuf);
if (tcph == NULL)
return NF_ACCEPT;
return (tcph->dest == port) ? NF_DROP : NF_ACCEPT;
}
static struct nf_hook_ops nfho = { // 훅 등록
.hook = hook_func,
.hooknum = NF_IP_PRE_ROUTING,
.pf = NFPROTO_IPV4,
.priority = NF_IP_PRI_FIRST,
};
static __init int my_init(void)
{
return nf_register_hook(&nfho);
}
static __exit void my_exit(void)
{
nf_unregister_hook(&nfho);
}
module_init(my_init);
module_exit(my_exit);
```
- 오픈 소스 Chrom(크롬) 라이브러리를 사용한 API/함수 후킹: 오픈 소스인 Chrom 라이브러리를 사용하는 API/함수 후킹 예시이다. 대상 함수의 처음 6바이트를 JMP 명령어로 덮어써서 후킹하며, 동적 연결 라이브러리로 컴파일 후 DLL 인젝션으로 로드된다.
```cpp
/*
Firefox_hook (C) Raja Jamwal 2011
GNU GPL License
Firefox_hook: Chrom Library 예제, Firefox HTTP/HTTPS 요청 기록
Chrom: windows API/함수 가로채기/후킹 라이브러리
Copyright (C) 2011 Raja Jamwal
- /
#include "stdafx.h"
#include "chrom.h"
Hook Firefox; // Firefox 후킹
DWORD PR_Write_H (DWORD *fd, void *buf,DWORD amount); // 재정의 함수
typedef DWORD (*prWrite)(DWORD*,void*,DWORD); // 원래 함수 정의
prWrite prw = NULL; // 원래 함수 포인터
char log_file[]="c://log.txt"; // 로그 파일
// 로그 기록
int write_log(char * log, char * data)
{
ofstream fout(log, ios::app);
fout << data;
fout.close();
return TRUE;
}
// 후킹 초기화, 원래 함수 주소에 점프 명령어 추가
int create_hooks()
{
// nspr4.dll의 PR_Write 함수를 PR_Write_H로 재정의
// nspr4.dll은 이미 로드되어 있어야 함
Firefox.Initialize("PR_Write", "nspr4.dll", PR_Write_H);
// 원래 함수 주소에 점프 명령어 쓰기
Firefox.Start();
return TRUE;
}
// 재정의 함수
DWORD PR_Write_H (DWORD *fd, void *buf,DWORD amount)
{
// 후킹 리셋, 점프 명령어를 원래 데이터로 교체
Firefox.Reset();
// Firefox.Reset() 호출 후 원래 함수 주소는
// 원래 함수의 데이터를 포함하므로 아래 코드 생략 가능
// prw(함수)를 원래 함수로 지정 (선택 사항)
prw = (prWrite)Firefox.original_function;
// 헤더 기록
write_log(log_file, (char*) buf);
// 실제 PR_Write 함수 호출
DWORD ret = prw(fd, buf, amount);
// 원래 함수에 다시 점프 명령어 배치
Firefox.Place_Hook();
return ret;
}
3. 1. C# 키보드 이벤트 후킹
.NET 프레임워크를 사용하여 마이크로소프트 윈도우에서 키보드 이벤트를 후킹하는 예제는 다음과 같다.```csharp
using System.Runtime.InteropServices;
namespace Hooks;
public class KeyHook
{
/* 멤버 변수 */
protected static int Hook;
protected static LowLevelKeyboardDelegate Delegate;
protected static readonly object Lock = new object();
protected static bool IsRegistered = false;
/* DLL 가져오기 */
[DllImport("user32")]
private static extern int SetWindowsHookEx(int idHook, LowLevelKeyboardDelegate lpfn,
int hmod, int dwThreadId);
[DllImport("user32")]
private static extern int CallNextHookEx(int hHook, int nCode, int wParam, KBDLLHOOKSTRUCT lParam);
[DllImport("user32")]
private static extern int UnhookWindowsHookEx(int hHook);
/* 유형 및 상수 */
protected delegate int LowLevelKeyboardDelegate(int nCode, int wParam, ref KBDLLHOOKSTRUCT lParam);
private const int HC_ACTION = 0;
private const int WM_KEYDOWN = 0x0100;
private const int WM_KEYUP = 0x0101;
private const int WH_KEYBOARD_LL = 13;
[StructLayout(LayoutKind.Sequential)]
public struct KBDLLHOOKSTRUCT
{
public int vkCode;
public int scanCode;
public int flags;
public int time;
public int dwExtraInfo;
}
/* 메서드 */
static private int LowLevelKeyboardHandler(int nCode, int wParam, ref KBDLLHOOKSTRUCT lParam)
{
if (nCode == HC_ACTION)
{
if (wParam == WM_KEYDOWN)
System.Console.Out.WriteLine("키 다운: " + lParam.vkCode);
else if (wParam == WM_KEYUP)
System.Console.Out.WriteLine("키 업: " + lParam.vkCode);
}
return CallNextHookEx(Hook, nCode, wParam, lParam);
}
public static bool RegisterHook()
{
lock (Lock)
{
if (IsRegistered)
return true;
Delegate = LowLevelKeyboardHandler;
Hook = SetWindowsHookEx(
WH_KEYBOARD_LL, Delegate,
Marshal.GetHINSTANCE(
System.Reflection.Assembly.GetExecutingAssembly().GetModules()[0]
).ToInt32(), 0
);
if (Hook != 0)
return IsRegistered = true;
Delegate = null;
return false;
}
}
public static bool UnregisterHook()
{
lock (Lock)
{
return IsRegistered = (UnhookWindowsHookEx(Hook) != 0);
}
}
}
```
`SetWindowsHookEx` API 함수를 사용하여 키보드 이벤트를 감지하고, `CallNextHookEx` 함수를 통해 다음 훅 프로시저로 이벤트를 전달한다.
3. 2. DirectDraw 후킹
다음은 윈도우에서 다이렉트드로 함수 호출을 후킹한 예이다. APIHijack(에이피아이 하이잭)이라는 후킹 라이브러리를 활용했고, 소스는 DLL(동적 링크 라이브러리)로 컴파일되었다. 인스톨훅(InstallHook)을 호출할 추가적인 응용 프로그램도 필요하다.[3]#include
#include
#include
#include "testdll.h"
#include "..\apihijack.h"
char temp[256];
HINSTANCE hDLL;
// type defs
typedef HRESULT (WINAPI *DirectDrawCreateEx_Type)( GUID FAR *lpGUID, LPVOID *lplpDD, REFIID iid, IUnknown FAR *pUnkOuter );
// function prototypes
HRESULT WINAPI MyDirectDrawCreateEx( GUID FAR * lpGuid, LPVOID *lplpDD, REFIID iid,IUnknown FAR *pUnkOuter );
// hook structure
enum
{
D3DFN_DirectDrawCreateEx = 0
};
SDLLHook D3DHook =
{
"DDRAW.DLL",
false, NULL,
{
{ "DirectDrawCreateEx", MyDirectDrawCreateEx },
{ NULL, NULL }
}
};
// Hook function.
HRESULT WINAPI MyDirectDrawCreateEx(GUID FAR* lpGuid, LPVOID *lplpDD, REFIID iid,IUnknown FAR *pUnkOuter )
{
DirectDrawCreateEx_Type OldFn = (DirectDrawCreateEx_Type)D3DHook.Functions[D3DFN_DirectDrawCreateEx].OrigFn;
return OldFn( lpGuid, lplpDD, iid, pUnkOuter );
}
// CBT Hook-style injection.
BOOL APIENTRY DllMain( HINSTANCE hModule, DWORD fdwReason, LPVOID lpReserved )
{
if ( fdwReason == DLL_PROCESS_ATTACH ) // When initializing....
{
hDLL = hModule;
// Only hook the APIs if this is the fsim proess.
GetModuleFileName(GetModuleHandle(NULL), temp, sizeof(temp));
PathStripPath(temp);
if(stricmp(temp, "fsim.exe" ) == 0)
HookAPICalls( &D3DHook );
}
return TRUE;
}
// This segment must be defined as SHARED in the .DEF
#pragma data_seg (".HookSection")
// Shared instance for all processes.
HHOOK hHook = NULL;
#pragma data_seg ()
TESTDLL_API LRESULT CALLBACK HookProc(int nCode, WPARAM wParam, LPARAM lParam)
{
return CallNextHookEx( hHook, nCode, wParam, lParam);
}
TESTDLL_API void InstallHook()
{
hHook = SetWindowsHookEx(WH_CBT, HookProc, hDLL, 0);
}
TESTDLL_API void RemoveHook()
{
UnhookWindowsHookEx( hHook );
}
독자적인 처리를 프로시저나 함수로 정의해두고, 등록 API를 사용하여 등록한다. 현대의 고급 프로그래밍 언어에 의한 프로그래밍에서는 자주 보이는 구현 형태이다.
예를 들어 마이크로소프트 윈도우의 경우, OS가 제공하는 훅에는 정수 ID가 부여되며, 등록용 함수 SetWindowsHookEx가 제공된다. 훅 ID와 독자적인 처리로의 함수 포인터를 인수로 SetWindowsHookEx 함수를 호출하면, 훅 처리로 등록된다.
3. 3. JMP 명령어를 이용한 API/함수 후킹
다음은 오픈 소스인 Chrom(크롬) 라이브러리를 사용하는 API/함수 후킹의 예시이다. 이는 새로운 함수에 대한 JMP 명령으로 대상 함수의 처음 6바이트를 덮어써서 훅한다. 코드는 동적 연결 라이브러리로 컴파일된 후, DLL 인젝션의 방법을 사용하여 대상 프로세스에 로드된다. 이 예제 코드는 "nspr4.dll[4]"의 "PR_Write" 함수와 "c:/log.txt"에 기록된 모든 HTTP/HTTPS 요청을 훅한다.[5]/*
Firefox_hook (C) Raja Jamwal 2011
Distributed under GNU GPL License
Firefox_hook is a example code for Chrom Library, Firefox_hook log every
HTTP/HTTPS requests that firefox makes
Chrom, is API/Funtion interception/hijacking library for windows systems
Copyright (C) 2011 Raja Jamwal
This file is part of Chrom.
- /
#include "stdafx.h"
#include "chrom.h"
Hook Firefox; // Hook firefox
DWORD PR_Write_H (DWORD *fd, void *buf,DWORD amount); // this is our overiding-function
typedef DWORD (*prWrite)(DWORD*,void*,DWORD); // defination of our original function
prWrite prw = NULL; // create a orginal function, we later point this to orginal function
// address
char log_file[]="c://log.txt"; // logfile
// this will log our data
int write_log(char * log, char * data)
{
ofstream fout(log, ios::app);
fout << data;
fout.close();
return TRUE;
}
// initialize hooking, this adds the jump instruction to orginal function address
int create_hooks()
{
// Override PR_Write function in nspr4.dll with our PR_Write_H,
// Note nspr4.dll must already be
// loaded in process space
Firefox.Initialize("PR_Write", "nspr4.dll", PR_Write_H);
// Write jump instruction on orginal function address
Firefox.Start();
return TRUE;
}
// our overriding function
DWORD PR_Write_H (DWORD *fd, void *buf,DWORD amount)
{
// reset hooks, this will replace the jump instruction to original data
Firefox.Reset();
// You may skip the code shown below and call the original function directly
// since after calling Firefox.Reset() the address of the original function,
// now contains the original function's data
// point prw(function) to original function (optional)
prw = (prWrite)Firefox.original_function;
// log the headers
write_log(log_file, (char*) buf);
// call the real PR_Write function
DWORD ret = prw(fd, buf, amount);
// again place the jump instruction on the original function
Firefox.Place_Hook();
return ret;
}
다음은 대상 함수의 처음 6바이트를 새 함수로의 JMP 명령어로 덮어쓰는 방식으로 후킹하는 API/함수 후킹 방법의 예시이다. 이 코드는 동적 연결 라이브러리 파일로 컴파일된 다음, DLL 인젝션을 사용하여 대상 프로세스에 로드된다. 원래 함수의 백업을 사용하여 처음 6바이트를 다시 복원하여 호출이 중단되지 않도록 할 수 있다. 이 예제에서는 win32 API 함수인 MessageBoxW가 후킹된다.[2]
/*
이 아이디어는 chrom-lib 접근 방식을 기반으로 하며, GNU LGPL 라이선스에 따라 배포됩니다.
소스 chrom-lib: https://github.com/linuxexp/chrom-lib
Copyright (C) 2011 Raja Jamwal
- /
#include
#define SIZE 6
typedef int (WINAPI *pMessageBoxW)(HWND, LPCWSTR, LPCWSTR, UINT); // Messagebox 프로토타입
int WINAPI MyMessageBoxW(HWND, LPCWSTR, LPCWSTR, UINT); // 우리 우회
void BeginRedirect(LPVOID);
pMessageBoxW pOrigMBAddress = NULL; // 원본 주소
BYTE oldBytes[SIZE] = {0}; // 백업
BYTE JMP[SIZE] = {0}; // 6바이트 JMP 명령어
DWORD oldProtect, myProtect = PAGE_EXECUTE_READWRITE;
INT APIENTRY DllMain(HMODULE hDLL, DWORD Reason, LPVOID Reserved)
{
switch (Reason)
{
case DLL_PROCESS_ATTACH: // 연결된 경우
pOrigMBAddress = (pMessageBoxW)
GetProcAddress(GetModuleHandleA("user32.dll"), // 원본 주소 가져오기
"MessageBoxW");
if (pOrigMBAddress != NULL)
BeginRedirect(MyMessageBoxW); // 우회 시작
break;
case DLL_PROCESS_DETACH:
VirtualProtect((LPVOID)pOrigMBAddress, SIZE, myProtect, &oldProtect); // 읽기/쓰기 보호 할당
memcpy(pOrigMBAddress, oldBytes, SIZE); // 백업 복원
VirtualProtect((LPVOID)pOrigMBAddress, SIZE, oldProtect, &myProtect); // 보호 재설정
case DLL_THREAD_ATTACH:
case DLL_THREAD_DETACH:
break;
}
return TRUE;
}
void BeginRedirect(LPVOID newFunction)
{
BYTE tempJMP[SIZE] = {0xE9, 0x90, 0x90, 0x90, 0x90, 0xC3}; // 0xE9 = JMP 0x90 = NOP 0xC3 = RET
memcpy(JMP, tempJMP, SIZE); // JMP에 jmp 명령어 저장
DWORD JMPSize = ((DWORD)newFunction - (DWORD)pOrigMBAddress - 5); // 점프 거리 계산
VirtualProtect((LPVOID)pOrigMBAddress, SIZE, // 읽기/쓰기 보호 할당
PAGE_EXECUTE_READWRITE, &oldProtect);
memcpy(oldBytes, pOrigMBAddress, SIZE); // 백업 생성
memcpy(&JMP[1], &JMPSize, 4); // 점프 거리를 NOP에 채움 (JMP,distance(4bytes),RET)
memcpy(pOrigMBAddress, JMP, SIZE); // 원본 함수의 시작 부분에 점프 명령어 설정
VirtualProtect((LPVOID)pOrigMBAddress, SIZE, oldProtect, &myProtect); // 보호 재설정
}
int WINAPI MyMessageBoxW(HWND hWnd, LPCWSTR lpText, LPCWSTR lpCaption, UINT uiType)
{
VirtualProtect((LPVOID)pOrigMBAddress, SIZE, myProtect, &oldProtect); // 읽기/쓰기 보호 할당
memcpy(pOrigMBAddress, oldBytes, SIZE); // 백업 복원
int retValue = MessageBoxW(hWnd, lpText, lpCaption, uiType); // 원본 함수의 반환 값 가져오기
memcpy(pOrigMBAddress, JMP, SIZE); // 점프 명령어 다시 설정
VirtualProtect((LPVOID)pOrigMBAddress, SIZE, oldProtect, &myProtect); // 보호 재설정
return retValue; // 원본 반환 값 반환
}
3. 4. Netfilter 후킹
이 예제에서는 넷필터를 사용하여 리눅스 커널에서 네트워크 트래픽을 변경할 수 있는 훅을 사용하는 방법을 보여준다.`nf_hook_ops` 구조체를 사용하여 훅 함수를 등록하고, `nf_register_hook` 함수를 통해 커널에 훅을 등록한다.
```c
#include
#include
#include
#include
#include
#include
#include
#include
/* 패킷을 드롭할 포트 */
static const uint16_t port = 25;
/* 후크 함수 자체 */
static unsigned int hook_func(unsigned int hooknum,
struct sk_buff **pskb,
const struct net_device *in,
const struct net_device *out,
int (*okfn)(struct sk_buff *))
{
struct iphdr *iph = ip_hdr(*pskb);
struct tcphdr *tcph, tcpbuf;
if (iph->protocol != IPPROTO_TCP)
return NF_ACCEPT;
tcph = skb_header_pointer(*pskb, ip_hdrlen(*pskb), sizeof(*tcph), &tcpbuf);
if (tcph == NULL)
return NF_ACCEPT;
return (tcph->dest == port) ? NF_DROP : NF_ACCEPT;
}
/* 후크 함수를 등록하는 데 사용 */
static struct nf_hook_ops nfho = {
.hook = hook_func,
.hooknum = NF_IP_PRE_ROUTING,
.pf = NFPROTO_IPV4,
.priority = NF_IP_PRI_FIRST,
};
static __init int my_init(void)
{
return nf_register_hook(&nfho);
}
static __exit void my_exit(void)
{
nf_unregister_hook(&nfho);
}
module_init(my_init);
module_exit(my_exit);
3. 5. 가상 메서드 테이블 후킹
클래스가 가상 함수 (또는 메서드)를 정의/상속할 때마다 컴파일러는 클래스에 숨겨진 멤버 변수를 추가하여 가상 메서드 테이블 (VMT 또는 Vtable)을 가리키게 한다. 대부분의 컴파일러는 숨겨진 VMT 포인터를 클래스의 각 인스턴스 처음 4바이트에 배치한다. VMT는 기본적으로 클래스의 인스턴스가 호출할 수 있는 모든 가상 함수에 대한 포인터의 배열이다. 실행 시간에 이러한 포인터는 올바른 함수를 가리키도록 설정된다. 컴파일 시간에 기본 함수를 호출할지 아니면 파생 클래스에서 함수의 재정의된 버전을 호출할지 (따라서 다형성을 허용) 아직 알 수 없기 때문이다. 따라서 가상 함수는 해당 함수가 나타나는 모든 VMT 내에서 해당 함수에 대한 포인터를 교체하여 후킹할 수 있다. 아래 코드는 C++로 작성된 Microsoft Windows에서 전형적인 VMT 후킹의 예시를 보여준다.[1]```cpp
#include
#include "windows.h"
using namespace std;
class VirtualClass
{
public:
int number;
virtual void VirtualFn1() // 후킹될 가상 함수입니다.
{
cout << "VirtualFn1 called " << number++ << "\n\n";
}
};
using VirtualFn1_t = void(__thiscall*)(void* thisptr);
VirtualFn1_t orig_VirtualFn1;
void __fastcall hkVirtualFn1(void* thisptr, int edx) // 후킹이 완료된 후 원래 VirtualFn1 함수 대신 프로그램에서 호출하도록 할 후킹 함수입니다.
{
cout << "Hook function called" << "\n";
orig_VirtualFn1(thisptr); // 원래 함수를 호출합니다.
}
int main()
{
VirtualClass* myClass = new VirtualClass(); // VirtualClass의 동적으로 할당된 인스턴스에 대한 포인터를 생성합니다.
void vTablePtr = *reinterpret_cast
DWORD oldProtection;
VirtualProtect(vTablePtr, 4, PAGE_EXECUTE_READWRITE, &oldProtection); // VMT 시작 부분의 페이지 보호를 제거하여 첫 번째 포인터를 덮어쓸 수 있도록 합니다.
orig_VirtualFn1 = reinterpret_cast
// 후킹 함수로 덮어 쓴 후 다시 액세스할 수 있도록 합니다.
- vTablePtr = &hkVirtualFn1; // 가상 테이블 내의 VirtualFn1에 대한 포인터를 후킹 함수(hkVirtualFn1)에 대한 포인터로 덮어씁니다.
VirtualProtect(vTablePtr, 4, oldProtection, 0); // 이전 페이지 보호를 복원합니다.
myClass->VirtualFn1(); // 클래스 인스턴스에서 가상 함수를 호출합니다. 이제 후킹되었으므로 실제로는 후킹 함수(hkVirtualFn1)를 호출합니다.
myClass->VirtualFn1();
myClass->VirtualFn1();
delete myClass;
return 0;
}
```
모든 가상 함수는 클래스 멤버 함수여야 하며, 모든 (정적이지 않은) 클래스 멤버 함수는 __thiscall 호출 규칙으로 호출된다 (멤버 함수가 가변 개수의 인수를 사용하는 경우 __cdecl로 호출됨). __thiscall 호출 규칙은 호출하는 클래스 인스턴스에 대한 포인터 (일반적으로 "this" 포인터라고 함)를 ECX 레지스터를 통해 전달한다 (x86 아키텍처에서). 따라서 후킹 함수가 전달된 "this" 포인터를 적절하게 가로채 인수로 사용하려면 ECX 레지스터를 살펴봐야 한다. 위의 예에서, 이것은 후킹 함수 (hkVirtualFn1)가 인수를 위해 ECX 레지스터를 살펴보는 유일한 다른 호출 규칙인 __fastcall 호출 규칙을 사용하도록 설정하여 수행된다.
또한 위의 예에서 후킹 함수 (hkVirtualFn1)는 자체적으로 멤버 함수가 아니므로 __thiscall 호출 규칙을 사용할 수 없다. 인수를 위해 ECX 레지스터를 살펴보는 유일한 다른 호출 규칙이기 때문에 대신 __fastcall을 사용해야 한다.
3. 6. 내부 IAT 후킹
다음 코드는 다른 모듈에서 가져온 함수를 후킹하는 방법을 보여준다. 이는 호출 프로세스와 다른 프로세스의 함수를 후킹하는 데 사용될 수 있다. 이를 위해 코드는 DLL 파일로 컴파일된 다음 DLL 인젝션의 모든 방법을 사용하여 대상 프로세스에 로드되어야 한다. 이 방법의 장점은 안티바이러스 소프트웨어 및/또는 안티 치트 소프트웨어에 의해 감지될 가능성이 적다는 것이다. 악의적인 호출을 사용하지 않는 외부 후크로 만들 수 있다.PE(Portable Executable, 실행 파일) 헤더는 아래 소스와 같이 조작할 수 있는 Import Address Table, 가져오기 주소 테이블(IAT)을 포함한다. 아래 소스는 마이크로소프트 윈도우에서 실행된다.
#include
typedef int(__stdcall *pMessageBoxA) (HWND hWnd, LPCSTR lpText, LPCSTR lpCaption, UINT uType); //이것은 MessageBoxA 호출의 '유형'입니다.
pMessageBoxA RealMessageBoxA; //이것은 원래 함수에 대한 포인터를 저장합니다.
void DetourIATptr(const char* function, void* newfunction, HMODULE module);
int __stdcall NewMessageBoxA(HWND hWnd, LPCSTR lpText, LPCSTR lpCaption, UINT uType) { //우리의 가짜 함수
printf("MessageBoxA로 전송된 문자열은 다음과 같습니다: %s\n", lpText);
return RealMessageBoxA(hWnd, lpText, lpCaption, uType); //실제 함수를 호출합니다
}
int main(int argc, CHAR *argv[]) {
DetourIATptr("MessageBoxA",(void*)NewMessageBoxA,0); //함수 후킹
MessageBoxA(NULL, "Just A MessageBox", "Just A MessageBox", 0); //함수를 호출합니다 -- 이것은 우리의 가짜 후크를 호출합니다.
return 0;
}
void **IATfind(const char *function, HMODULE module) { //주어진 함수에 특정한 IAT(Import Address Table, 가져오기 주소 테이블) 항목을 찾습니다.
int ip = 0;
if (module == 0)
module = GetModuleHandle(0);
PIMAGE_DOS_HEADER pImgDosHeaders = (PIMAGE_DOS_HEADER)module;
PIMAGE_NT_HEADERS pImgNTHeaders = (PIMAGE_NT_HEADERS)((LPBYTE)pImgDosHeaders + pImgDosHeaders->e_lfanew);
PIMAGE_IMPORT_DESCRIPTOR pImgImportDesc = (PIMAGE_IMPORT_DESCRIPTOR)((LPBYTE)pImgDosHeaders + pImgNTHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress);
if (pImgDosHeaders->e_magic != IMAGE_DOS_SIGNATURE)
printf("libPE 오류 : e_magic이 유효한 DOS 서명이 아닙니다\n");
for (IMAGE_IMPORT_DESCRIPTOR *iid = pImgImportDesc; iid->Name != NULL; iid++) {
for (int funcIdx = 0; *(funcIdx + (LPVOID*)(iid->FirstThunk + (SIZE_T)module)) != NULL; funcIdx++) {
char *modFuncName = (char*)(*(funcIdx + (SIZE_T*)(iid->OriginalFirstThunk + (SIZE_T)module)) + (SIZE_T)module + 2);
const uintptr_t nModFuncName = (uintptr_t)modFuncName;
bool isString = !(nModFuncName & (sizeof(nModFuncName) == 4 ? 0x80000000 : 0x8000000000000000));
if (isString) {
if (!_stricmp(function, modFuncName))
return funcIdx + (LPVOID*)(iid->FirstThunk + (SIZE_T)module);
}
}
}
return 0;
}
void DetourIATptr(const char *function, void *newfunction, HMODULE module) {
void **funcptr = IATfind(function, module);
if (*funcptr == newfunction)
return;
DWORD oldrights, newrights = PAGE_READWRITE;
//보호 권한을 READWRITE로 업데이트합니다.
VirtualProtect(funcptr, sizeof(LPVOID), newrights, &oldrights);
RealMessageBoxA = (pMessageBoxA)*funcptr; //일부 컴파일러는 캐스트(예: "MinGW")를 요구하지만 MSVC는 확실하지 않습니다.
- funcptr = newfunction;
//이전 메모리 보호 플래그를 복원합니다.
VirtualProtect(funcptr, sizeof(LPVOID), oldrights, &newrights);
}
독자적인 처리를 프로시저나 함수로 정의해두고, 등록 API를 사용하여 등록한다. 현대의 고급 프로그래밍 언어에 의한 프로그래밍에서는 자주 보이는 구현 형태이다.
예를 들어 마이크로소프트 윈도우의 경우, OS가 제공하는 훅에는 정수 ID가 부여되며, 등록용 함수 SetWindowsHookEx가 제공된다. 훅 ID와 독자적인 처리로의 함수 포인터를 인수로 SetWindowsHookEx 함수를 호출하면, 훅 처리로 등록된다.[1]
4. 후킹 프로그램 목록
다음은 후킹을 지원하거나 후킹을 이용하는 프로그램 및 라이브러리 목록이다.
운영체제 | 이름 | 설명 |
---|---|---|
Windows | AGTH | |
[http://research.microsoft.com/sn/detours Detours] | 마이크로소프트 리서치에서 개발한 C/C++용 범용 함수 후킹 라이브러리 | |
[https://easyhook.github.io/ EasyHook] | Windows x86 및 x64를 지원하는 오픈 소스 후킹 엔진 (사용자 및 커널 영역 모두에서 사용 가능) | |
[http://www.nektra.com/products/spystudio-api-monitor/ SpyStudio Application Trace] | 호출을 후킹하고 결과를 구조화된 방식으로 표시하는 응용 프로그램 추적기 | |
[http://www.rohitab.com/apimonitor rohitab.com API Monitor] | 32비트 및 64비트 응용 프로그램과 서비스에서 10,000개 이상의 Windows API 및 COM 인터페이스를 후킹하고 표시할 수 있는 프리웨어 응용 프로그램 | |
[http://www.nektra.com/products/deviare-api-hook-windows/ Deviare API Hook] | 다른 프로세스의 API 호출을 가로채고 전체 매개변수 정보를 표시하거나 API 모니터를 만들 수 있는 프리웨어 프로세스 간 후킹 프레임워크 | |
[http://jacquelin.potier.free.fr/winapioverride32/ WinAPIOverride] | 비상업적 용도로 사용할 수 있는 프리웨어 (32비트 및 64비트 프로세스에서 Win32 API, COM, OLE, ActiveX, .NET을 후킹, 사후 분석 모니터링 도구 포함) | |
[https://github.com/urShadow/urmem urmem] | 메모리 작업(후크, 패치, 포인터 래퍼, 시그니처 스캐너 등)을 위한 C++11 크로스 플랫폼 라이브러리(x86) | |
[http://www.internals.com/ APISpy32] | Win32 API를 후킹하는 데 사용되는 응용 프로그램 | |
[http://www.codeproject.com/KB/threads/winspy.aspx winspy] | 다른 프로세스에 코드를 주입하는 세 가지 방법을 제공 | |
[http://www.codeproject.com/KB/system/hooksys.aspx HookTool SDK (ACF SDK)] | API 후킹 및 코드 주입에 대한 포괄적인 개요를 제공 (상용 제품도 존재) | |
[http://help.madshi.net/ApiCodeHooking.htm madCodeHook] | C++ 및 Delphi용 상용 x86 및 x64 API 후킹 및 DLL 주입 라이브러리 | |
Linux | `LD_PRELOAD` | 공유 라이브러리 호출 후킹 |
`ptrace` | 소프트웨어가 다른 프로세스의 실행을 관찰하고 제어할 수 있게 해주는 기능[1] | |
Emacs | Emacs Hooks | Emacs의 중요한 사용자 정의 메커니즘 (어떤 잘 정의된 경우에 호출될 함수 목록을 담고 있는 Lisp 변수) |
OS X 및 iOS | AGTH | |
Cydia Substrate | 탈옥된 iOS 기기용 프레임워크 (개발자가 다른 프레임워크나 응용 프로그램에 후킹 가능) | |
OS X | harpoon | OS X 런타임 함수 후킹용 라이브러리 |
4. 1. Windows
다음은 Windows용 후킹 프로그램 및 라이브러리 목록이다.- AGTH
- [http://research.microsoft.com/sn/detours Detours]: 마이크로소프트 리서치에서 개발한 C/C++용 범용 함수 후킹 라이브러리이다.
- [https://easyhook.github.io/ EasyHook]: Windows x86 및 x64를 지원하는 오픈 소스 후킹 엔진으로, 사용자 및 커널 영역 모두에서 사용 가능하다.
- [http://www.nektra.com/products/spystudio-api-monitor/ SpyStudio Application Trace]: 호출을 후킹하고 결과를 구조화된 방식으로 표시하는 응용 프로그램 추적기이다.
- [http://www.rohitab.com/apimonitor rohitab.com API Monitor]: 32비트 및 64비트 응용 프로그램과 서비스에서 10,000개 이상의 Windows API 및 COM 인터페이스를 후킹하고 표시할 수 있는 프리웨어 응용 프로그램이다.
- [http://www.nektra.com/products/deviare-api-hook-windows/ Deviare API Hook]: 다른 프로세스의 API 호출을 가로채고 전체 매개변수 정보를 표시하거나 API 모니터를 만들 수 있는 프리웨어 프로세스 간 후킹 프레임워크이다.
- [http://jacquelin.potier.free.fr/winapioverride32/ WinAPIOverride]: 비상업적 용도로 사용할 수 있는 프리웨어이다. 32비트 및 64비트 프로세스에서 Win32 API, COM, OLE, ActiveX, .NET을 후킹할 수 있으며, 사후 분석 모니터링 도구가 포함되어 있다.
- [https://github.com/urShadow/urmem urmem]: 메모리 작업(후크, 패치, 포인터 래퍼, 시그니처 스캐너 등)을 위한 C++11 크로스 플랫폼 라이브러리(x86)이다.
- [http://www.internals.com/ APISpy32]: Win32 API를 후킹하는 데 사용되는 응용 프로그램이다.
- [http://www.codeproject.com/KB/threads/winspy.aspx winspy]: 다른 프로세스에 코드를 주입하는 세 가지 방법을 제공한다.
- [http://www.codeproject.com/KB/system/hooksys.aspx HookTool SDK (ACF SDK)]: API 후킹 및 코드 주입에 대한 포괄적인 개요를 제공하며, 상용 제품도 있다.
- [http://help.madshi.net/ApiCodeHooking.htm madCodeHook]: C++ 및 Delphi용 상용 x86 및 x64 API 후킹 및 DLL 주입 라이브러리이다.
4. 2. Linux
Linux 환경에서 후킹을 활용하는 방법은 여러 가지가 있다.`LD_PRELOAD` 환경 변수를 사용하면 공유 라이브러리 호출을 후킹할 수 있다. 이를 통해 특정 함수 호출을 가로채 원하는 동작을 수행하도록 변경할 수 있다.
`ptrace`는 소프트웨어가 다른 프로세스의 실행을 관찰하고 제어할 수 있게 해주는 기능이다.[1]
4. 3. Emacs
GNU Emacs는 텍스트 편집기로, Emacs Lisp이라는 언어를 사용하여 다양한 기능을 구현한다. 대부분의 Emacs Lisp 패키지는 변수명 뒤에 "-hook"가 붙는 변수를 훅으로 정의한다. 예를 들어, GNU Emacs 시작 시에는 `emacs-startup-hook`가 호출되고, C 모드를 호출했을 때는 `c-mode-hook`가 평가된다. Lisp 프로그램은 S식이라는 리스트로 되어 있으며, 변수에 S식을 설정해두면 변수를 평가할 때 해당 프로그램이 실행된다.4. 4. OS X and iOS
- AGTH
- Cydia Substrate는 탈옥된 iOS 기기용 프레임워크로, 개발자가 다른 프레임워크나 응용 프로그램에 후킹할 수 있게 해준다.
- harpoon은 OS X 런타임 함수 후킹용 라이브러리이다.
5. 심층 API 후킹
x86 API 후킹의 모든 것 문서는 x86 아키텍처에서 사용되는 다양한 API 후킹 기법을 심층적으로 설명한다.[1] API 후킹은 운영체제나 다른 애플리케이션의 API 호출을 가로채는 기술로, 디버깅, 보안, 기능 확장 등에 주로 사용된다. x86 아키텍처에서는 여러 방법으로 API 후킹을 구현할 수 있다.
참조
[1]
Github
psyfl
https://github.com/p[...]
[2]
웹사이트
Hooking Explained Detouring Library
http://ntvalk.blogsp[...]
[3]
웹사이트
codeproject.com
http://www.codeproje[...]
[4]
문서
파일 기술을 쓰기 위해 파이어폭스에서 사용된다.
[5]
구글 코드
chrom-lib
http://code.google.c[...]
본 사이트는 AI가 위키백과와 뉴스 기사,정부 간행물,학술 논문등을 바탕으로 정보를 가공하여 제공하는 백과사전형 서비스입니다.
모든 문서는 AI에 의해 자동 생성되며, CC BY-SA 4.0 라이선스에 따라 이용할 수 있습니다.
하지만, 위키백과나 뉴스 기사 자체에 오류, 부정확한 정보, 또는 가짜 뉴스가 포함될 수 있으며, AI는 이러한 내용을 완벽하게 걸러내지 못할 수 있습니다.
따라서 제공되는 정보에 일부 오류나 편향이 있을 수 있으므로, 중요한 정보는 반드시 다른 출처를 통해 교차 검증하시기 바랍니다.
문의하기 : help@durumis.com