DLL Injection 을 통해 API Hooking.
아래의 과정을 따른다.
1. API Hook을 하고자 하는 프로세스에 DLL Injection을 한다. (주소공간 공유)
2. 후킹할 API의 주소를 구한다.
3. 후킹할 API의 시작주소부분 5바이트를, 바꿔치기 하고자 하는 함수의 주소로 점프시키는 코드($E9)로 덮어 씌운다.
다음은 어떤 에디터의 에디터창에서 현재 캐럿의 위치를 알기 위해 SetCaretPos() 를 후킹하여,
캐럿의 위치가 바뀔 때마다 내가 접근가능한 변수에 캐럿의 위치를 저장하여 언제든 꺼내 쓸 수 있도록 하는 예제이다.
Delphi2009 기준.
(DLL은 어떻게든 밀어넣었다고 가정한다)
DLL 메인 소스 (XXX.dpr 혹은 XXX.dproj)
library ApiHook;
uses
Windows, SysUtils, Classes, Messages,
API_SetCaretPos;
{$R *.res}
// DLL 메인함수
function DllMain(dwReason: DWORD): Boolean;
begin
Result:=True;
case dwReason of
// DLL Attach 시에 API Hook 걸기
DLL_PROCESS_ATTACH: begin
API_SetCaretPos.Hook;
end;
// DLL Detach 시에 API Hook 뽑기
DLL_PROCESS_DETACH: begin
API_SetCaretPos.Unhook;
end;
end;
end;
begin
// 메인함수를 연결하여 실행
DLLProc:=@DllMain;
if Assigned(DLLProc) then DllMain(DLL_PROCESS_ATTACH);
end.
uses
Windows, SysUtils, Classes, Messages,
API_SetCaretPos;
{$R *.res}
// DLL 메인함수
function DllMain(dwReason: DWORD): Boolean;
begin
Result:=True;
case dwReason of
// DLL Attach 시에 API Hook 걸기
DLL_PROCESS_ATTACH: begin
API_SetCaretPos.Hook;
end;
// DLL Detach 시에 API Hook 뽑기
DLL_PROCESS_DETACH: begin
API_SetCaretPos.Unhook;
end;
end;
end;
begin
// 메인함수를 연결하여 실행
DLLProc:=@DllMain;
if Assigned(DLLProc) then DllMain(DLL_PROCESS_ATTACH);
end.
APIHookFunc.pas (API Hook 기본 유닛)
unit APIHookFunc;
interface
uses
Windows;
function APIHook(szDllName: String; szFuncName: String; pfnNew: Pointer; pOrgBytes: PBYTE): Boolean;
function APIUnHook(szDllName: String; szFuncName: String; pOrgBytes: PBYTE): Boolean;
implementation
// Hook
// _________________________________________________________________________________________________
function APIHook(szDllName: String; szFuncName: String; pfnNew: Pointer; pOrgBytes: PBYTE): Boolean;
var
pfnOrg: Pointer;
pbByte: PBYTE;
pBuf: array [0..4] of Byte;
dwOldProtect, dwAddress: DWORD;
begin
// 후킹할 API의 주소를 구함
pfnOrg:=GetProcAddress(GetModuleHandle(PWideChar(szDllName)), PWideChar(szFuncName));
pbByte:=PBYTE(pfnOrg);
// 이미 후킹되어 있다면 Exit
if pbByte[0]=$E9 then begin
Result:=False;
Exit;
end;
// 후킹할 API의 주소위치에 5바이트만큼 WRITE 속성 추가
VirtualProtect(pfnOrg, 5, PAGE_EXECUTE_READWRITE, dwOldProtect);
// 후킹할 API의 주소위치에 있던 코드 5바이트를 전역변수에 백업
CopyMemory(pOrgBytes, pfnOrg, 5);
// JMP 주소계산
// JMP = $E9
// 나머지 4바이트는 내가 만든 함수의 주소까지의 상대값
// 계산공식 = 후킹대상API주소 - 대체될함수주소 - 5 (JMP 명령어 자체길이)
dwAddress:=DWORD(pfnNew)-DWORD(pfnOrg)-5;
pBuf[0]:=$E9;
// JMP (1byte) + 상대거리 (4byte)를 pBuf에 저장
CopyMemory(@pBuf[1], @dwAddress, 4);
// Hook
// 후킹할 API의 주소위치에 5바이트를 덮어 씌움
CopyMemory(pfnOrg, @pBuf[0], 5);
// 메모리 속성 복원
VirtualProtect(pfnOrg, 5, dwOldProtect, dwOldProtect);
Result:=True;
end;
// UnHook
// _________________________________________________________________________________________________
function APIUnHook(szDllName: String; szFuncName: String; pOrgBytes: PBYTE): Boolean;
var
pF: Pointer;
dwOldProtect: DWORD;
begin
// 후킹한 함수 주소를 가져옴
pF:=GetProcAddress(GetModuleHandle(PWideChar(szDllName)), PWideChar(szFuncName));
// 쓰기권한 추가 (5바이트)
VirtualProtect(pF, 5, PAGE_EXECUTE_READWRITE, dwOldProtect);
// 원래 함수의 내용으로 복구
CopyMemory(pF, pOrgBytes, 5);
// 원래권한 복구
VirtualProtect(pF, 5, dwOldProtect, dwOldProtect);
Result:=True;
end;
end.
interface
uses
Windows;
function APIHook(szDllName: String; szFuncName: String; pfnNew: Pointer; pOrgBytes: PBYTE): Boolean;
function APIUnHook(szDllName: String; szFuncName: String; pOrgBytes: PBYTE): Boolean;
implementation
// Hook
// _________________________________________________________________________________________________
function APIHook(szDllName: String; szFuncName: String; pfnNew: Pointer; pOrgBytes: PBYTE): Boolean;
var
pfnOrg: Pointer;
pbByte: PBYTE;
pBuf: array [0..4] of Byte;
dwOldProtect, dwAddress: DWORD;
begin
// 후킹할 API의 주소를 구함
pfnOrg:=GetProcAddress(GetModuleHandle(PWideChar(szDllName)), PWideChar(szFuncName));
pbByte:=PBYTE(pfnOrg);
// 이미 후킹되어 있다면 Exit
if pbByte[0]=$E9 then begin
Result:=False;
Exit;
end;
// 후킹할 API의 주소위치에 5바이트만큼 WRITE 속성 추가
VirtualProtect(pfnOrg, 5, PAGE_EXECUTE_READWRITE, dwOldProtect);
// 후킹할 API의 주소위치에 있던 코드 5바이트를 전역변수에 백업
CopyMemory(pOrgBytes, pfnOrg, 5);
// JMP 주소계산
// JMP = $E9
// 나머지 4바이트는 내가 만든 함수의 주소까지의 상대값
// 계산공식 = 후킹대상API주소 - 대체될함수주소 - 5 (JMP 명령어 자체길이)
dwAddress:=DWORD(pfnNew)-DWORD(pfnOrg)-5;
pBuf[0]:=$E9;
// JMP (1byte) + 상대거리 (4byte)를 pBuf에 저장
CopyMemory(@pBuf[1], @dwAddress, 4);
// Hook
// 후킹할 API의 주소위치에 5바이트를 덮어 씌움
CopyMemory(pfnOrg, @pBuf[0], 5);
// 메모리 속성 복원
VirtualProtect(pfnOrg, 5, dwOldProtect, dwOldProtect);
Result:=True;
end;
// UnHook
// _________________________________________________________________________________________________
function APIUnHook(szDllName: String; szFuncName: String; pOrgBytes: PBYTE): Boolean;
var
pF: Pointer;
dwOldProtect: DWORD;
begin
// 후킹한 함수 주소를 가져옴
pF:=GetProcAddress(GetModuleHandle(PWideChar(szDllName)), PWideChar(szFuncName));
// 쓰기권한 추가 (5바이트)
VirtualProtect(pF, 5, PAGE_EXECUTE_READWRITE, dwOldProtect);
// 원래 함수의 내용으로 복구
CopyMemory(pF, pOrgBytes, 5);
// 원래권한 복구
VirtualProtect(pF, 5, dwOldProtect, dwOldProtect);
Result:=True;
end;
end.
API_SetCaretPos.pas (SetCaretPos 함수를 후킹하는 파일)
unit API_SetCaretPos;
interface
uses
Windows, SysUtils, APIHookFunc;
function Hook: Boolean;
function UnHook: Boolean;
function GetCaretPos: TPoint;
implementation
const
DLL_NAME = 'user32.dll'; // SetCaretPos 함수는 user32.dll에 있다
FUNC_NAME = 'SetCaretPos';
var
gX, gY: Integer; // 캐럿위치
gOrgBytes: array [0..4] of Byte; // 원상복구시킬 주소를 저장할 바이트 배열
// 바꿔칠 함수. 캐럿 위치만 몰래 빼오고 나머지는 원래대로 진행시킴.
// _________________________________________________________________________________________________
function MySetCaretPos(x, y: Integer): Bool; stdcall;
begin
// 전역변수에 caret position 저장
gX:=x;
gY:=y;
// 1. 잠시 원상복구
APIUnHook(DLL_NAME, FUNC_NAME, @gOrgBytes);
// 2. 원래 함수 실행
Result:=SetCaretPos(x, y);
// 3. 다시 Hooking
APIHook(DLL_NAME, FUNC_NAME, @MySetCaretPos, @gOrgBytes);
end;
// Hook을 통해 실시간으로 저장되어 있는 caret position 반환
// _________________________________________________________________________________________________
function GetCaretPos: TPoint;
begin
Result.X:=gX;
Result.Y:=gY;
end;
function Hook: Boolean;
begin
Result:=APIHook(DLL_NAME, FUNC_NAME, @MySetCaretPos, @gOrgBytes);
end;
function UnHook: Boolean;
begin
Result:=APIUnHook(DLL_NAME, FUNC_NAME, @gOrgBytes);
end;
end.
interface
uses
Windows, SysUtils, APIHookFunc;
function Hook: Boolean;
function UnHook: Boolean;
function GetCaretPos: TPoint;
implementation
const
DLL_NAME = 'user32.dll'; // SetCaretPos 함수는 user32.dll에 있다
FUNC_NAME = 'SetCaretPos';
var
gX, gY: Integer; // 캐럿위치
gOrgBytes: array [0..4] of Byte; // 원상복구시킬 주소를 저장할 바이트 배열
// 바꿔칠 함수. 캐럿 위치만 몰래 빼오고 나머지는 원래대로 진행시킴.
// _________________________________________________________________________________________________
function MySetCaretPos(x, y: Integer): Bool; stdcall;
begin
// 전역변수에 caret position 저장
gX:=x;
gY:=y;
// 1. 잠시 원상복구
APIUnHook(DLL_NAME, FUNC_NAME, @gOrgBytes);
// 2. 원래 함수 실행
Result:=SetCaretPos(x, y);
// 3. 다시 Hooking
APIHook(DLL_NAME, FUNC_NAME, @MySetCaretPos, @gOrgBytes);
end;
// Hook을 통해 실시간으로 저장되어 있는 caret position 반환
// _________________________________________________________________________________________________
function GetCaretPos: TPoint;
begin
Result.X:=gX;
Result.Y:=gY;
end;
function Hook: Boolean;
begin
Result:=APIHook(DLL_NAME, FUNC_NAME, @MySetCaretPos, @gOrgBytes);
end;
function UnHook: Boolean;
begin
Result:=APIUnHook(DLL_NAME, FUNC_NAME, @gOrgBytes);
end;
end.
'Delphi' 카테고리의 다른 글
[Delphi] 윈도우 핸들로 브라우저의 HTML 가져오기 (0) | 2012.10.25 |
---|---|
[Delphi] 파일시스템 변경 감시 (0) | 2011.08.07 |
[Delphi] UAC 권한상승 manifest를 리소스에 포함시켜 컴파일 (UAC requireAdministrator manifest resource file) (6) | 2010.11.23 |
[Delphi] 에디트플러스와 DDE 통신 (DDE, EditPlus) (0) | 2010.10.25 |
[Delphi] .lnk (바로가기) 파일에서 실행파일 경로 가져오기 (0) | 2010.10.25 |