[Delphi] API Hooking

Delphi 2010. 12. 15. 16:27


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.





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.




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.





Posted by bloodguy
,