按事件分类,有如下的几种常用类型的钩子:

1)键盘钩子可以监视各种键盘消息。

2)鼠标钩子可以监视各种鼠标消息。

3)外壳钩子可以监视各种Shell事件消息。

4)日志钩子可以记录从系统消息队列中取出的各种事件消息。

5)窗口过程钩子监视所有从系统消息队列发往目标窗口的消息。

安装钩子:SetWindowsHookEx

卸载钩子:UnhookWindowsHookEx

钩子回调函数形式:

function GetMsgProc(Code: UINT; lParam: LPARAM; wParam: WPARAM): LRESULT; stdcall;

Code:钩子代码,通常为HA_ACTION时是用户要处理的

lParam, wParam:跟具体安装的钩子类型有关的封装了缴获到的消息结构的参数

系统全局钩子必须在DLL中,因为它影响系统的所有应用程序,需要在消息发生时被系

统映射到其他进程的地址空间,从而调用DLL中的钩子回调函数。钩子所在的DLL被映射

时,是整体映射被加载到被挂钩的进程的地址空间中,而不仅仅是钩子回调函数,这

样,被挂钩的进程就可以访问DLL中的变量和调用其他函数的。利用这个特点,在应用

中就可以做到很多特定的功能,比如屏幕取词、木马、三级跳隐藏进程等。

注意:安装了某类消息的系统全局钩子之后,在该类消息发生时钩子DLL会被系统映射到其他

进程的地址空间,从而调用DLL中的钩子回调函数。

还有一点要注意:当SetWindowsHookEx调用成功后,系统会自动映射这个DLL到被挂钩

的线程,但并不是立即映射。因为所有的Windows钩子都是基于消息的,直到一个适当

的事件发生后这个DLL才被映射。同理,UnhookWindowsHookEx调用之后,也是在某个适

当的事件发生之后DLL才真正地从被挂钩线程卸载。

比如GetMessage钩子,lParam是Removal flag移除标志,wParam是个TMsg结构的指针

typedef struct tagMSG {     // msg

HWND   hwnd;

UINT   message;

WPARAM wParam;

LPARAM lParam;

DWORD  time;

POINT  pt;

} MSG;

三、实现

下面我们来讲如何解决一、4.中提到的问题。

1.在自己的进程中访问其他进程的对象实例

有了上面介绍的必备知识基础,那么现在这个问题对我们来说就不是很困难了,利用钩

子由系统将DLL注入目标进程,这时DLL就在目标进程的地址空间中了,这样,DLL中的

访问目标进程的对象实例的代码就可以工作了。

2.得到其他进程的DBGrid对象实例

DLL注入目标进程之后,实际上DLL和目标进程就在一个进程中了,那么按理说我们用

FindControl函数应该就可以由DBGrid句柄得到DBGrid对象实例的了,但实际并非如此!

实际写代码测试一下我们可以发现它返回的是nil。

function FindControl(Handle: HWnd): TWinControl;

var

OwningProcess: DWORD;

begin

Result := nil;

if (Handle <> 0) and (GetWindowThreadProcessID(Handle, OwningProcess) <> 0) and

(OwningProcess = GetCurrentProcessId) then // 判断调用进程ID是否为Handle所在进程

begin

if GlobalFindAtom(PChar(ControlAtomString)) = ControlAtom then

Result := Pointer(GetProp(Handle, MakeIntAtom(ControlAtom)))

else

Result := ObjectFromHWnd(Handle);

end;

end;

-----

function ObjectFromHWnd(Handle: HWnd): TWinControl;

var

OwningProcess: DWORD;

begin

if (GetWindowThreadProcessID(Handle, OwningProcess) <> 0) and

(OwningProcess = GetCurrentProcessID) then

Result := Pointer(SendMessage(Handle, RM_GetObjectInstance, 0, 0))

else

Result := nil;

end;

再看看其中使用到的ControlAtomString, ControlAtom, RM_GetObjectInstance的值是怎

样的(InitControls中):

procedure InitControls;

var

UserHandle: HMODULE;

begin

WindowAtomString := Format('Delphi%.8X',[GetCurrentProcessID]);

WindowAtom := GlobalAddAtom(PChar(WindowAtomString));

ControlAtomString := Format('ControlOfs%.8X%.8X', [HInstance, GetCurrentThreadID]);

ControlAtom := GlobalAddAtom(PChar(ControlAtomString));

RM_GetObjectInstance := RegisterWindowMessage(PChar(ControlAtomString));

...

end;

看到这里,我们可以发现问题之所在了。ControlAtomString是根据模块句柄(模块加载基

地址)和线程ID动态生成的,目标进程的模块基地址就是EXE基地址,一般是0x00400000,

但DLL的模块加载基地址就不是这个了,默认是0x10000000,而实际上可能因为这个地址

已经被占用(有其他DLL被加载到这个地址)而进行重定位,所以初始化时添加的

ControlAtom和目标进程的ControlAtom的值就不一样,RM_GetObjectInstance也同样是不

一样的,那FindControl当然就不能找到DBGrid对象实例啦。

OK,清楚了这一点,解决起来就简单了,我们自己写个FindControl函数,以目标进程基

地址来动态生成ControlAtomString,添加ControlAtom就可以啦。

在DLL中取EXE的基地址,用GetModuleHandle(nil)即可。

var

ControlAtom: TAtom;

ControlAtomString: string;

RM_GetObjectInstance: DWORD;  // registered window message

function FindControl(Handle: HWnd): TWinControl;

var

OwningProcess: DWORD;

begin

Result := nil;

if (Handle <> 0) and (GetWindowThreadProcessID(Handle, OwningProcess) <> 0) and

(OwningProcess = GetCurrentProcessId) then

begin

if GlobalFindAtom(PChar(ControlAtomString)) = ControlAtom then

Result := Pointer(GetProp(Handle, MakeIntAtom(ControlAtom)))

else

Result := Pointer(SendMessage(Handle, RM_GetObjectInstance, 0, 0));

end;

end;

initialization

ControlAtomString := Format('ControlOfs%.8X%.8X', [GetModuleHandle(nil), GetCurrentThreadID]);

ControlAtom := GlobalAddAtom(PChar(ControlAtomString));

RM_GetObjectInstance := RegisterWindowMessage(PChar(ControlAtomString));

finalization

GlobalDeleteAtom(ControlAtom);

ControlAtomString := '';

end.

嗯,上面的FindControl我是把Controls.pas中FindControl和ObjectFromHWND合成一个函数了

// 获取目标进程中DBGrid的数据集的记录内容,保存到文件中

procedure ProcessDataSet(hCtrl: HWND);

var

F: TextFile;

FileName: string;

Grid: TDBGrid;

DataSet: TDataSet;

I: Integer;

begin

Grid := TDBGrid(FindControl(hCtrl)); // 根据句柄取得对象实例

if (Grid <> nil) and (Grid.DataSource <> nil) and (Grid.DataSource.DataSet <> nil) then

begin

FileName := ExtractFilePath(ParamStr(0)) + 'DataSet.txt'; // 目标程序运行目录下

AssignFile(F, FileName);

if FileExists(FileName) then Append(F) else Rewrite(F);

try

DataSet := Grid.DataSource.DataSet;

Writeln(F, FormatDateTime('yyyy-MM-dd HH:mm:ss', Now), ':');

DataSet.First;

while not DataSet.Eof do

begin

for I := 0 to DataSet.FieldCount - 1 do

Write(F, DataSet.Fields[I].AsString, ', ');

Writeln(F);

DataSet.Next;

end;

Writeln(F);

finally

CloseFile(F);

end;

end;

end;

这个代码,我们在实际测试时可以发现还是存在问题的,运行会出错,问题就在于

Write(F, DataSet.Fields[I].DisplayText, ', ');这一句,如果将这一句改成

Write(F, DataSet.Fields[I].Value, ', ');就不会有问题。

delphi中的HOOK [转贴]的更多相关文章

  1. [转]Delphi 中动态链接库(dll)的建立和使用

    动态链接库是一个能够被应用程序和其它的DLL调用的过程和函数的集合体,它里面包含的是公共代码或资源.由于DLL代码使用了内存共享技术,在某些地方windows也给了DLL一些更高的权限,因而DLL中可 ...

  2. MFC中的HOOK编程

    HOOK,n.钩, 吊钩,通常称钩子. 在计算机中,是Windows消息处理机制的一个平台,应用程序能够在上面设置子程以监视指定窗体的某种消息,并且所监视的窗体能够是其它进程所创建的.当消息到达后,在 ...

  3. Delphi中DLL的创建和使用(转)

    Delphi中DLL的创建和使用     1.DLL简介:   2.调用DLL:   3.创建DLL:   4.两个技巧:   5.初始化:   6.例外处理.            1.DLL简介  ...

  4. Delphi中SendMessage使用说明(所有消息说明) good

    Delphi中SendMessage使用说明 SendMessage基础知识 函数功能:该函数将指定的消息发送到一个或多个窗口.此函数为指定的窗口调用窗口程序,直到窗口程序处理完消息再返回.而函数Po ...

  5. delphi中SendMessage使用说明

    SendMessage基础知识 函数功能:该函数将指定的消息发送到一个或多个窗口.此函数为指定的窗口调用窗口程序,直到窗口程序处理完消息再返回.而函数PostMessage不同,将一个消息寄送到一个线 ...

  6. Delphi中stringlist分割字符串的用法

    Delphi中stringlist分割字符串的用法 TStrings是一个抽象类,在实际开发中,是除了基本类型外,应用得最多的. 常规的用法大家都知道,现在来讨论它的一些高级的用法. 1.CommaT ...

  7. delphi中exit,abort,break,continue 的区别

    from:http://www.cnblogs.com/taofengli288/archive/2011/09/05/2167553.html delphi中表示跳出的有break,continue ...

  8. Delphi中使用比较少的一些语法

    本文是为了加强记忆而写,这里写的大多数内容都是在编程的日常工作中使用频率不高的东西,但是又十分重要. ---Murphy 1,构造和析构函数: a,构造函数: 一般基于TComponent组件的派生类 ...

  9. 如何在 Delphi 中静态链接 SQLite

    搞了我几个小时,终于成功在 Delphi 中静态链接了 SQLite (v3.5.4),下一步就是研究加密了,呵呵中间其实遇到很多问题,今天累了,就不说了,改天补上 下载测试工程 下面说说方法 1.当 ...

随机推荐

  1. Dell Omsa在Linux服务器上安装部署

    前言 本页详述了在一台Linux(RHEL6.4 x86_64)服务器上部署安装OMSA的通用做法,包括OMSA软件的获取方法和安装步骤. 演示环境: PowerEdge R620, RHEL 6.4 ...

  2. Django深入----django.db.transaction

    django 的事务: transaction.py atomic---原子性 def atomic(using=None, savepoint=True): # Bare decorator: @a ...

  3. Zuma (区间DP)

    Genos recently installed the game Zuma on his phone. In Zuma there exists a line of n gemstones, the ...

  4. 【C#】重写和重载的区别

    导读:学习C#的时候,其实没想那么多的.就想着把视频看完,把例子做一下就好了,其实真心不懂那些玩意儿是什么,就好像是又回到了学VB的时候.可是,边上师哥压榨我这本就不聪明的脑袋瓜,问了我好多问题,于是 ...

  5. BZOJ 2194 快速傅立叶之二 ——FFT

    [题目分析] 咦,这不是卷积裸题. 敲敲敲,结果样例也没过. 看看看,卧槽i和k怎么反了. 艹艹艹,把B数组取个反. 靠靠靠,怎么全是零. 算算算,最终的取值范围算错了. 交交交,总算是A掉了. [代 ...

  6. BZOJ 3733 [Pa2013]Iloczyn 模拟爆搜

    Description 给定正整数n和k,问能否将n分解为k个不同正整数的乘积 Input 第一行一个数T(T<=4000)表示测试组数 接下来T行每行两个数n(n<=10^9),k(k& ...

  7. App后台运行通知函数

    
[[UIApplicationsharedApplication] beginBackgroundTaskWithExpirationHandler: ^() { //程序在10分钟内未被系统关闭或 ...

  8. python判断一个字符串是否是小数

    最近在写代码的时候,发现一个问题,想判断一个字符串是不是一个合法的小数,发现字符串没有内置判断小数的方法,然后就写了一个判断字符串是否是小数,可以判断正负小数,代码如下:   1 2 3 4 5 6 ...

  9. [bzoj1345][Baltic2007]序列问题_单调栈

    bzoj-1345 Baltic-2007 序列问题 题目大意:对于一个给定的序列a1,…,an,我们对它进行一个操作reduce(i),该操作将数列中的元素ai和ai+1用一个元素max(ai,ai ...

  10. Java运算基础

    计算机对负数的运算 =  先取绝对值的原码----> 然后取反,----->+1   这是负数的补码表示 例如  -5       5的原码= 0000,0101  取反   1111,1 ...