文档章节

DelphiHookApi(经典)

simpower
 simpower
发布于 2017/08/12 15:16
字数 1515
阅读 7
收藏 0

论坛里有关于HOOK API的贴子, 但其实现在方式显示得麻烦, 其实现在拦截API一般不用那种方式, 大都采用inline Hook API方式。其实也就是直接修改了要拦截的API源码的头部,让它无条件跳转到我们自己的处理过程。

   不多说别的了,开始我们自己的Hook API吧。

   我们今天要拦截的API如下:

   MessageBoxA、MessageBoxW、MessageBeep 和 OpenProcess 。

   首先,大家都知道要在整个系统范围中拦截,需要使用Dll来完成。现在我们打开Delphi 2009,新建一个Dll工程:hookDll。需要说明的是,Delphi是完全面向对象的编程语言,所以我们不要浪费,这个Dll打算用类的方式完成。于是,在新建的DLL工程中在添加一个Unit Pas,命名为unitHook, 用来写拦截类的处理。unitHook.pas中的代码如下:

unit unitHook;

interface

uses
  Windows, Messages, Classes, SysUtils;

type

  //NtHook类相关类型
  TNtJmpCode=packed record  //8字节
    MovEax:Byte;
    Addr:DWORD;
    JmpCode:Word;
    dwReserved:Byte;
  end;

  TNtHookClass=class(TObject)
  private
    hProcess:THandle;
    NewAddr:TNtJmpCode;
    OldAddr:array[0..7] of Byte;
    ReadOK:Boolean;
  public
    BaseAddr:Pointer;
    constructor Create(DllName,FuncName:string;NewFunc:Pointer);
    destructor Destroy; override;
    procedure Hook;
    procedure UnHook;
  end;

implementation

//==================================================
//NtHOOK 类开始
//==================================================
constructor TNtHookClass.Create(DllName: string; FuncName: string;NewFunc:Pointer);
var
  DllModule:HMODULE;
  dwReserved:DWORD;
begin
  //获取模块句柄
  DllModule:=GetModuleHandle(PChar(DllName));
  //如果得不到说明未被加载
  if DllModule=0 then DllModule:=LoadLibrary(PChar(DllName));
  //得到模块入口地址(基址)
  BaseAddr:=Pointer(GetProcAddress(DllModule,PChar(FuncName)));
  //获取当前进程句柄
  hProcess:=GetCurrentProcess;
  //指向新地址的指针
  NewAddr.MovEax:=$B8;
  NewAddr.Addr:=DWORD(NewFunc);
  NewAddr.JmpCode:=$E0FF;
  //保存原始地址
  ReadOK:=ReadProcessMemory(hProcess,BaseAddr,@OldAddr,8,dwReserved);
  //开始拦截
  Hook;
end;

//释放对象
destructor TNtHookClass.Destroy;
begin
  UnHook;
  CloseHandle(hProcess);

  inherited;
end;

//开始拦截
procedure TNtHookClass.Hook;
var
  dwReserved:DWORD;
begin
  if (ReadOK=False) then Exit;
  //写入新的地址
  WriteProcessMemory(hProcess,BaseAddr,@NewAddr,8,dwReserved);
end;

//恢复拦截
procedure TNtHookClass.UnHook;
var
  dwReserved:DWORD;
begin
  if (ReadOK=False) then Exit;
  //恢复地址
  WriteProcessMemory(hProcess,BaseAddr,@OldAddr,8,dwReserved);
end;

end.
至此,unitHook.pas的代码OK了,其中加了详细的注释,在此就不再多做解释。现在切换到Dll的代码页,
写入以下代码:
 
library hookdll;

uses
  SysUtils, Windows,
  Classes,
  unitHook in 'unitHook.pas';

{$R *.res}

const
  HOOK_MEM_FILENAME  =  'tmp.hkt';

var
  hhk: HHOOK;
  Hook: array[0..3] of TNtHookClass;

  //内存映射
  MemFile: THandle;
  startPid: PDWORD;   //保存PID

{--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--}

//拦截 MessageBoxA
function NewMessageBoxA(_hWnd: HWND; lpText, lpCaption: PAnsiChar; uType: UINT):
Integer; stdcall;
type
  TNewMessageBoxA = function (_hWnd: HWND; lpText, lpCaption: PAnsiChar; uType:
UINT): Integer; stdcall;
begin
  lpText := PAnsiChar('已经被拦截 MessageBoxA');
  Hook[0].UnHook;
  Result := TNewMessageBoxA(Hook[0].BaseAddr)(_hWnd, lpText, lpCaption, uType);
  Hook[0].Hook;
end;

//拦截 MessageBoxW
function NewMessageBoxW(_hWnd: HWND; lpText, lpCaption: PWideChar; uType: UINT):
Integer; stdcall;
type
  TNewMessageBoxW = function (_hWnd: HWND; lpText, lpCaption: PWideChar; uType:
UINT): Integer; stdcall;
begin
  lpText := '已经被拦截 MessageBoxW';
  Hook[2].UnHook;
  Result := TNewMessageBoxW(Hook[2].BaseAddr)(_hWnd, lpText, lpCaption, uType);
  Hook[2].Hook;
end;

//拦截 MessageBeep
function NewMessageBeep(uType: UINT): BOOL; stdcall;
type
  TNewMessageBeep = function (uType: UINT): BOOL; stdcall;
begin
  Result := True;
end;

//拦截 OpenProcess , 防止关闭
function NewOpenProcess(dwDesiredAccess: DWORD; bInheritHandle: BOOL; dwProcessId:
DWORD): THandle; stdcall;
type
  TNewOpenProcess = function (dwDesiredAccess: DWORD; bInheritHandle: BOOL;
dwProcessId: DWORD): THandle; stdcall;
begin
  if startPid^ = dwProcessId then begin
    result := 0;
    Exit;
  end;
  Hook[3].UnHook;
  Result := TNewOpenProcess(Hook[3].BaseAddr)(dwDesiredAccess, bInheritHandle,
dwProcessId);
  Hook[3].Hook;
end;

{--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--}

//安装API Hook
procedure InitHook;
begin
  Hook[0] := TNtHookClass.Create('user32.dll', 'MessageBoxA', @NewMessageBoxA);
  Hook[1] := TNtHookClass.Create('user32.dll', 'MessageBeep', @NewMessageBeep);
  Hook[2] := TNtHookClass.Create('user32.dll', 'MessageBoxW', @NewMessageBoxW);
  Hook[3] := TNtHookClass.Create('kernel32.dll', 'OpenProcess', @NewOpenProcess);
end;

//删除API Hook
procedure UninitHook;
var
  I: Integer;
begin
  for I := 0 to High(Hook) do
  begin
    FreeAndNil(Hook[I]);
  end;
end;

{--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--}

//内存映射共想
procedure MemShared();
begin
  MemFile:=OpenFileMapping(FILE_MAP_ALL_ACCESS,False, HOOK_MEM_FILENAME);
if MemFile = 0 then begin  //打开失败则衉c2建内存映射文件
    MemFile := CreateFileMapping($FFFFFFFF, nil, PAGE_READWRITE, 0,
                              4, HOOK_MEM_FILENAME);
  end;
  if MemFile <> 0 then
    //映射文件到变量
    startPid := MapViewOfFile(MemFile,FILE_MAP_ALL_ACCESS,0,0,0);
end;

//传递消息
function HookProc(nCode, wParam, lParam: Integer): Integer; stdcall;
begin
  Result := CallNextHookEx(hhk, nCode, wParam, lParam);
end;

//开始HOOK
procedure StartHook(pid: DWORD); stdcall;
begin
  startPid^ := pid;
  hhk := SetWindowsHookEx(WH_CALLWNDPROC, HookProc, hInstance, 0);
end;

//结束HOOK
procedure EndHook; stdcall;
begin
  if hhk <> 0 then
    UnhookWindowsHookEx(hhk);
end;

//环境处理
procedure DllEntry(dwResaon: DWORD);
begin
  case dwResaon of
    DLL_PROCESS_ATTACH: InitHook;   //DLL载入
    DLL_PROCESS_DETACH: UninitHook; //DLL删除
  end;
end;

exports
  StartHook, EndHook;

begin
  MemShared;

  { 分配DLL程序到 DllProc 变量 }
  DllProc := @DllEntry;
  { 调用DLL加载处理 }
  DllEntry(DLL_PROCESS_ATTACH);
end.

     这样,我们用来hook API 的 Dll 就完工了。 在Dll中,我们还使用到了内存映射,用来实现在拦

截全局时的内存共享,如这个例子中需要保存调用此hook的进程句柄,以防止通过任务管理器关闭示例程序。

   编译生成 hookdll.dll 文件,就可以使用了。现在我们再来建立一个测试用的程序。

   如附图所示,画3个按钮,分别为"Hook"、"UnHook"、"MessageBox",前两个用来

安装和删除钩子,第三个用来显示一个消息框,你将会看到被Hook后的情况。测试工程的代码如下:


unit FMain;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls;

type
  TfrmMain = class(TForm)
    btnHook: TButton;
    btnUnhook: TButton;
    Button1: TButton;
    procedure btnHookClick(Sender: TObject);
    procedure btnUnhookClick(Sender: TObject);
    procedure Button1Click(Sender: TObject);
    procedure FormCreate(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  frmMain: TfrmMain;

  procedure StartHook(pid: DWORD); stdcall; external 'hookdll.dll';
  procedure EndHook; stdcall; external 'hookdll.dll';

implementation

{$R *.dfm}

procedure TfrmMain.btnHookClick(Sender: TObject);
begin
  StartHook(GetCurrentProcessId);
end;

procedure TfrmMain.btnUnhookClick(Sender: TObject);
begin
  EndHook;
end;

procedure TfrmMain.Button1Click(Sender: TObject);
begin
  MessageBox(0, 'abdfadfasdf', nil, 0);
end;

procedure TfrmMain.FormCreate(Sender: TObject);
begin

end;

end.
 
      完成后运行,先不点击"hook"按钮,直接点击MessageBox,你会发现现在已经被拦截了
。为什么我们还没有安装钩子就被拦截了呢?程序出错了吗?呵呵。当然没有出错。反过来看看DLL中
的一处代码:
.............

//环境处理
procedure DllEntry(dwResaon: DWORD);
begin
  case dwResaon of
    DLL_PROCESS_ATTACH: InitHook;   //DLL载入
    DLL_PROCESS_DETACH: UninitHook; //DLL删除
  end;
end;

............

begin
  MemShared;

  { 分配DLL程序到 DllProc 变量 }
  DllProc := @DllEntry;
  { 调用DLL加载处理 }
  DllEntry(DLL_PROCESS_ATTACH);
end.
 
     可以看到,在DLL装入内存的时候其实就已经调用了InitHook,将要拦截的API拦截了
。这时候看看任务管理器能不能关闭我们的程序,试一下就知道还可以,因为我们还没有调用
StartHook来传入我们程序的PID,所以还可以被关闭。       到此这篇文章就结束了, 本人从小语文没及过格(^_^),文章写的不太好,不过源代码都贴上了,
有详细的注释,相信大家也能看明白。如果你发现有什么错误的地方,要记得告诉我哦!     最后感谢 cxwr(菜新)大大的支持,能完成这篇文章少不了他的功劳。

本文转载自:http://www.cnblogs.com/linyawen/archive/2011/03/25/1995624.html

simpower
粉丝 27
博文 646
码字总数 46970
作品 0
海淀
程序员
私信 提问
维基百科上的算法和数据结构链接很强大

突然发现维基百科上的算法和数据结构比百度百科强多啦,图文并茂。 其实这个网站不错:http://www.sorting-algorithms.com 冒泡排序: bubble冒泡的意思 http://zh.wikipedia.org/wiki/%E5%8...

晨曦之光
2012/03/09
2.3K
1
SLAM方向公众号、知乎、博客上有哪些大V可以关注?

一、公众号 泡泡机器人:泡泡机器人由一帮热爱探索并立志推广机器人同时定位与地图构建(SLAM)技术的极客创办而成,通过原创文章、公开课等方式分享SLAM领域的数学理论、编程实践和学术前沿。...

计算机视觉life
07/03
0
0
人工智能再出逆天技能 读出诗词秒变成歌

欠缺爆发力,没有海豚音,甚至五音不太全,没关系!有了这个人工智能最新技能,你也能用自己的声音,唱出谭维维《墨梅》的清气满乾坤、演绎张靓颖《春夜喜雨》的好雨知时节,比肩王力宏《三字...

央视
02/01
0
0
在中国开源在推荐软件的时候排名中的问题。

我觉得软件的排名应该分为经典的和非经典两类,像什么eclipse都还要排上去,eclipse分明属于经典区,要是让一些经典的软件都拿出来排名的话,那我们新发明的有价值的开源软件起码都要排到第几...

快速开发师
2015/01/15
122
0
数据挖掘领域十大经典算法之—K-Means算法(超详细附代码)

相关文章: 数据挖掘领域十大经典算法之—C4.5算法(超详细附代码) 数据挖掘领域十大经典算法之—SVM算法(超详细附代码) 数据挖掘领域十大经典算法之—Apriori算法 数据挖掘领域十大经典算...

fuqiuai
2018/03/06
0
0

没有更多内容

加载失败,请刷新页面

加载更多

OSChina 周六乱弹 —— 早上儿子问我他是怎么来的

Osc乱弹歌单(2019)请戳(这里) 【今日歌曲】 @凉小生 :#今日歌曲推荐# 少点戾气,愿你和这个世界温柔以待。中岛美嘉的单曲《僕が死のうと思ったのは (曾经我也想过一了百了)》 《僕が死の...

小小编辑
35分钟前
36
0
Excption与Error包结构,OOM 你遇到过哪些情况,SOF 你遇到过哪些情况

Throwable 是 Java 中所有错误与异常的超类,Throwable 包含两个子类,Error 与 Exception 。用于指示发生了异常情况。 Java 抛出的 Throwable 可以分成三种类型。 被检查异常(checked Exc...

Garphy
今天
9
0
计算机实现原理专题--二进制减法器(二)

在计算机实现原理专题--二进制减法器(一)中说明了基本原理,现准备说明如何来实现。 首先第一步255-b运算相当于对b进行按位取反,因此可将8个非门组成如下图的形式: 由于每次做减法时,我...

FAT_mt
昨天
6
0
好程序员大数据学习路线分享函数+map映射+元祖

好程序员大数据学习路线分享函数+map映射+元祖,大数据各个平台上的语言实现 hadoop 由java实现,2003年至今,三大块:数据处理,数据存储,数据计算 存储: hbase --> 数据成表 处理: hive --> 数...

好程序员官方
昨天
7
0
tabel 中含有复选框的列 数据理解

1、el-ui中实现某一列为复选框 实现多选非常简单: 手动添加一个el-table-column,设type属性为selction即可; 2、@selection-change事件:选项发生勾选状态变化时触发该事件 <el-table @sel...

everthing
昨天
6
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部