DelphiHookApi(经典)
论坛里有关于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(菜新)大大的支持,能完成这篇文章少不了他的功劳。

DelphiHookApi(经典)的更多相关文章
- 回首经典的SQL Server 2005
原创文章转载请注明出处:@协思, http://zeeman.cnblogs.com SQL Server是我使用时间最长的数据库,算起来已经有10年了.上世纪90年代,微软在软件开发的所有领域高歌猛 ...
- 微软Azure 经典模式下创建内部负载均衡(ILB)
微软Azure 经典模式下创建内部负载均衡(ILB) 使用之前一定要注意自己的Azure的模式,老版的为cloud service模式,新版为ARM模式(资源组模式) 本文适用于cloud servi ...
- Express 教程 01 - 入门教程之经典的Hello World
目录: 前言 一.Express?纳尼?! 二.开始前的准备工作 三.测试安装之经典的Hello World 四.使用express(1)来生成一个应用程序 五.说明 前言: 本篇文章是建立在Node ...
- 赠书:HTML5 Canvas 2d 编程必读的两本经典
赠书:HTML5 Canvas 2d 编程必读的两本经典 这两年多一直在和HTML5 Canvas 打交道,也带领团队开发了世界首款基于HTML5 Canvas 的演示文档工具---AxeSlide( ...
- 虚拟机体验之 VirtualBox 篇 —— 性能强大的经典架构
前两篇体验了 QEMU 和经过 KVM 加速的 QEMU,并体验了第三方虚拟机管理工具 virt-manager,让我们见识了开源社区的强大和开源虚拟机软件的高质量和高性能.这一篇,我来剖析一下 Vi ...
- Atitit MATLAB 图像处理 经典书籍attilax总结
Atitit MATLAB 图像处理 经典书籍attilax总结 1.1. MATLAB数字图像处理1 1.2. <MATLAB实用教程(第二版)>((美)穆尔 著)[简介_书评_在线阅读 ...
- 在Windows Server 2012中如何快速开关桌面上经典的“计算机、我的文档”等通用图标
我们都知道,在Windows Server 2012系列的服务器版本中都已经引入了Modern的现代界面作为默认的用户交互界面,同时满足视觉一致化,新版的服务器管理程序也做成了扁平化.因此传统的计算机 ...
- Apworks框架实战(四):使用Visual Studio开发面向经典分层架构的应用程序:从EasyMemo案例开始
时隔一年,继续我们的Apworks框架之旅.在接下来的文章中,我将逐渐向大家介绍如何在Visual Studio中结合Apworks框架,使用ASP.NET Web API和MVC来开发面向经典分层架 ...
- 【十大经典数据挖掘算法】PageRank
[十大经典数据挖掘算法]系列 C4.5 K-Means SVM Apriori EM PageRank AdaBoost kNN Naïve Bayes CART 我特地把PageRank作为[十大经 ...
随机推荐
- $router和$route的区别,路由跳转方式name 、 path 和传参方式params 、query的区别
一.$router和$route的区别 $router : 是路由操作对象,只写对象$route : 路由信息对象,只读对象 例子://$router操作 路由跳转 this.$router.push ...
- vue中ref的使用(this.$refs获取为undefined)
如果你获取到的总是空的,你注意一下: 1.你在哪里调用,和你调用的对象 试试在mounted()里面调用有效果没有 调用的对象是本来就存在的,还是需要数据渲染之后才会出现的,同理,在mounted() ...
- Box-Cox变换
简介 编辑 Box-Cox变换的一般形式为: 式中 为经Box-Cox变换后得到的新变量, 为原始连续因变量, 为变换参数.以上变换要求原始变量 取值为正,若取值为负时,可先对 ...
- Deb版本Linux配置Selenium+Chrome+Java实现自动化测试
1.安装chrome sudo apt-get install libxss1 libappindicator1 libindicator7 wget https://dl.google.com/li ...
- echarts点击柱状图时触发点击事件
项目需求:1.通过echarts把数据展示为柱状图2.点击对应的柱状图 显示对应的弹窗 主要用到的时 echarts中的 "click" 事件, 使用demo: var myCha ...
- 41. wait notify 方法
wait() 等待,如果一个线程执行了wait方法,那么该线程就会进去一个以锁对象为标识符的线程池中等待 notity() 唤醒,如果一个线程执行了notity方法,那么就会唤醒以锁对象为标识符的线 ...
- GTK+/GNOME编程(一)
1.开发环境:安装GTK+/GNOME库 #apt-get install gtk+-3.0 (安装GTK+库文件) #apt-get install gnome- ...
- ionic-CSS:ionic tab(选项卡)
ylbtech-ionic-CSS:ionic tab(选项卡) 1.返回顶部 1. ionic tab(选项卡) ionic tab(选项卡) 是水平排列的按钮或者链接,用以页面间导航的切换.它可以 ...
- KEIL, MDK 关于C99结构体变量初始化
C99:here 例如声明了这样的结构体 void test1() { tt_t t1 ={ .a = , .d = 'd', .b = , .c = }; static tt_t t2 = { ,, ...
- Unity中销毁游戏对象的方式
销毁方式 销毁物体的方式有两种:Destroy和DestroyImmediate两种,那两者有什么区别呢?细听分说. 两种方式都能达到销毁物体的目的,有以下区别: Destroy销毁场景中的物体但是内 ...