请教:关于HOOK在IDE中的调试问题 (60分)

  • 主题发起人 主题发起人 kaimen
  • 开始时间 开始时间
K

kaimen

Unregistered / Unconfirmed
GUEST, unregistred user!
本人是delphi的新手(所以分不多),希望各位看官能够帮我解惑 :)
偶做了一个WH_KEYBOARD的HOOK的DLL,又做了个EXE容器TestHook。
Build!当我执行TestHook后,当Activity的窗口是这个TestHooK的时候,输入的key全部正确被捕捉。
但是当Activity的窗口是chm或者是我直接写的一个VB form的时候,就会重复捕捉。
如果你输入1,到时候记录到文本文件里面就会出现11,此乃疑问一。
疑问二:发现了上述疑问后,我想调试一下问题的根结,于是设置了这个dll源代码的Parameters为TestHook后,Run。
发现如果当前Activity的窗口是这个TestHooK的时候,可以走到这个dll的源代码中。
但是当Activity的窗口是其他窗口的时候,走不到源代码。
我听说调试这个HOOK在IDE中好像不太容易,不知道各位有啥良策?是不是用了CreateFileMapping就可以了?

BTW: 我在调用dll的方法中增加了hMapObject := CreateFileMapping($FFFFFFFF, nil, PAGE_READWRITE, 0, MAXBUFSIZE, '_CBT');
rHookRec := MapViewOfFile(hMapObject, FILE_MAP_WRITE, 0, 0, 0);
但是调试还是走不到dll里面 :(

 
dll已经HOOK到别的进程了,当然调试不了,要不你用SoftICE/TRW2000之类的系统级调试器.
 
在你要调试的地方加上Beep函数。
 
2 thx1180:
加Beep好像没有用哦。
btw:加Beep对调试有什么影响吗?

就我的理解,如果有不正确的请众位看官指教:
CreateFileMapping创建了内存中的一块公用区域,针对读操作来说,应该总是一致的。
所有这个dll的实例都是放在同一个开始地址,可以说这个dll只有一个实例存在。
那么说来,我在dll中的源代码中的breakpoint其实就是针对内存地址的breakpoint。
那当我把HOOK挂上WH_KEYBOARD后,调用到dll,
就需要走到dll实例所在的某块内存地址,那应该就会走到断点啊。
 
hook不要用delphi的断点调试,否则有可能当机,呵呵
一般在调试的情况下,你的程序会死翘翘

CreateFileMapping跟程序调试是没有关系的,
它是创建共享数据而已,即dll的多个实例访问的是相同的共享数据
就好像是全局变量

如果你使用的是WH_KEYBOARD,判断时要code=HC_ACTION,重复的消息问题可能就在于此
 
同意wbtvc
 
在不好调试的情况下,可以用:
if 条件 then Beep;
通过听声音来判断程序是否运行到此,条件是否满足。
 
自己加调试代码撒!!!
 
昨天好像DFW突然当掉了哦,我的发言没有保存 :(
2 wbtvc:
出现重复的键捕获确实是因为code的判断,一般例程上都这么写:
if code < HC_ACTION then CallNextHookEX() else 保存键。
修改成code=HC_ACTION 为捕获键后正确!
确实用Hook最好不要在IDE中调试,但是好像也不是每次IDE都会当掉,所以我想冒天下之大不违,在尝试一下。

如果我不用CreateFileMapping来创建dll的共享数据。我创建的dll也能捕获所有的keyboard的活动,
这个是什么道理呢?(CreateFileMapping好像没有什么效果)
我只有在文件操作中用过CreateFileMapping,在这儿用也是为了一样的目的吗?

2 thx1180:
我可能没有说清楚,如果按照你的逻辑,在代码中增加响应的代码,
我可以把更详细的信息写到文本文件中,察看文本文件就可以得到结果了。
我的目的是想不增加冗余代码来对Hook进行调试 :(


 
CreateFileMapping在这里确实用处不大的,主要用来传递SetWindowsHookEx的返回值.
 
TO:kaimen
呵呵,在IDE中调试hook当然不一定会当掉,这跟你使用的是哪种hook有关系的。



看一看下面的:
 

用Delphi实现程序间的数据传递

在实际应用中,我们经常需要多个程序相互配合来完成某些特定功能。
例如两个应用程序间的同步、互斥;应用程序在起第二份实例时的参数
自动传递…。要实现这些功能,就必须能实现程序间的数据传递。

有些特殊的高级技术可在不同的程序间传递数据,如剪贴板、动态数据
交换以及OLE自动化,但有条件限制并且相对较复杂。这里,我介绍三种
有效的底层技术,希望对编程爱好者有所帮助。

利用WM_COPYDATA消息
 

使用该消息涉及一个TcopyDataStruct结构类型的指针。该结构中有三个成员:

dwData 是一个32位的附加参数

cbData 表示要传递的数据区的大小

lpData 表示要传递的数据区的指针

下面举个例子。该例子由两个程序构成,分别为SendData和GetData。

SendData程序向GetData程序发送消息,并传递edit1中的字符串;GetData
在收到消息后,把SendData发送的字符串接受下来,并显示在相应的edit1中。

SendData程序:
……

var

Form1: TForm1;

implementation

{$R *.DFM}

procedure TForm1.Button1Click(Sender: TObject);

var

ds: TCopyDataStruct;

hd: THandle;

begin

ds.cbData := Length (Edit1.Text) + 1;

GetMem (ds.lpData, ds.cbData ); //为传递的数据区分配内存

StrCopy (ds.lpData, PChar (Edit1.Text));

Hd := FindWindow (nil, 'Form2'); // 获得接受窗口的句柄

if Hd <> 0 then

SendMessage (Hd, WM_COPYDATA, Handle,

Cardinal(@ds)) // 发送WM_COPYDATA消息

else

ShowMessage ('目标窗口没找到!');

FreeMem (ds.lpData); //释放资源

end;

GetData程序:
 

TForm2 = class(TForm)

Edit1: TEdit;

private

{ Private declarations }

public

procedure Mymessage(var t:TWmCopyData);message WM_COPYDATA;

{ Public declarations }

end;

var

Form2: TForm2;

implementation

procedure TForm2.Mymessage(var t:TWmCopyData);

begin

Edit1.text:=StrPas(t.CopyDataStruct^.lpData);//接受数据并显示。

end;

使用这种方法是WIN32应用程序进行交互的最简单的方法。

使用全局原子
Win32系统中,为了实现信息共享,系统维护了一张全局原子表。
每个原子中存放了一些共享数据。关于对原子的操作,有一组专门的API函数:

GlobalAddAtom 在表中增加全局原子

GlobalDeleteAtom 在表中删除全局原子

GlobalFindAtom 在表中搜索全局原子

GlobalGetAtomName 从表中获取全局原子

笔者用这种方法实现了避免程序二次启动,但把第二次启动所带的
参数传到第一个实例中以进行相应的处理的程序。基本处理如下:

在工程文件中:

program Pvdde;

uses

Forms,shellapi,Windows,dialogs,dde in 'dde.pas' {Form1};

{$R *.RES}

begin

if GlobalFindAtom(PChar('PDDE_IS_RUNNING')) = 0 then

//避免二次启动

begin

K:=GlobalAddAtom(PChar('PDDE_IS_RUNNING'));

Application.Initialize;

Application.CreateForm(TForm1, Form1);

Application.Run;

end

else

begin

//传递二次启动时的参数到第一个实例

H := FindWindow(PChar('TForm1'), PChar('资料保密 严禁外传'));

if ParamCount > 0 then

begin

L := GlobalAddAtom(PChar(ParamStr(1)));

if H<>0 then

SendMessage(H, WM_MYMESSAGE, 0, L);

{ 传递原子句柄 }

GlobalDeleteAtom(L); { 使用后释放 }

end;

Application.Terminate;

end;

end.

在相应的窗口单元dde.pas增加对自定义消息WM_MYMESSAGE的处理:

procedure TForm1.MyMessage(var T:TMessage);

{对 WM_MYMESSAGE消息进行处理 }

var

P:Array [0..255] of char;

begin

GlobalGetAtomName(T.LParam, P,255); { 接受数据到p数组中 }

。。。

end;

使用存储映象文件
 

这种方法相对较复杂一些。

当Win95与Winows Nt向内存中装载文件时,使用了特殊的全局内存区。
在该区域内,应用程序的虚拟内存地址和文件中的相应位置一一对应。
由于所有进程共享了一个用于存储映象文件的全局内存区域,因而当
两个进程装载相同模块(应用程序或DLL文件)时,它们实际可以在
内存中共享其执行代码。

笔者通过调用一个带有特殊参数的CreateFileMapping函数,来间接达到
程序间共享内存的目的。下面简要解释一下该函数。

HANDLE CreateFileMapping(

HANDLE hFile, //文件句柄

LPSECURITY_ATTRIBUTES lpFileMappingAttributes, // 可选安全属性

DWORD flProtect, // 映象文件保护方式

DWORD dwMaximumSizeHigh, // 映象文件区域的底值

DWORD dwMaximumSizeLow, // 映象文件区域的顶值

LPCTSTR lpName // 映象文件的名字

);

如果hFile是0xFFFFFFFF,在调用程序中必须指定dwMaximumSizeHigh
和dwMaximumSizeLow参数的值以确定映象文件的大小。通过这样的参数
指定,该函数就创建了一个由操作系统页文件支持的特殊逻辑映象文件,
而不是由实际操作系统的文件支持的逻辑映象文件。这个逻辑映象文件
可以通过复制、继承或者按名字来达到共享。至于其它参数的详细说明,
请参看在线帮助。

在建立了映象文件之后,我们可以通过调用另外一个API函数MapViewOfFile
来访问它的内存,该函数会返回一个指向共享内存块的特定指针。

LPVOID MapViewOfFile(

HANDLE hFileMappingObject, // 映象文件句柄

DWORD dwDesiredAccess, // 访问方式

DWORD dwFileOffsetHigh, // 映象文件区域的底值

DWORD dwFileOffsetLow, // 映象文件区域的顶值

DWORD dwNumberOfBytesToMap // 映射字节数

);

如果 dwNumberOfBytesToMap 是0,映射整个文件。

以下举例说明:

private

hMapFile: THandle;

MapFilePointer: Pointer;

public

{ Public declarations }

end;

var

Form1: TForm1;

implementation

{$R *.DFM}

procedure TForm1.FormCreate(Sender: TObject);

begin

hMapFile := CreateFileMapping (

$FFFFFFFF, // 特殊内存映射句柄

nil, page_ReadWrite, 0,10000,

'DdhDemoMappedFile'); // 文件名

if hMapFile <> 0 then

MapFilePointer := MapViewOfFile (

hMapFile, // 上面映象文件的句柄

File_Map_All_Access,

0, 0, 0) // 访问整个映象文件

else

ShowMessage ('hMapFile = 0');

if MapFilePointer = nil then

ShowMessage ('MapFilePointer = nil');

end;

 

procedure TForm1.BtnWriteClick(Sender: TObject);

begin

StrCopy (PChar (MapFilePointer),

PChar (EditWrite.Text));//把内容写入共享内存

end;

 

procedure TForm1.BtnReadClick(Sender: TObject);

var

S: string;

begin

S := PChar (MapFilePointer);//从共享内存读出内容

EditRead.Text := S;

end;

用这种方法,不但可以在不同的程序之间共享数据,还可以在同一程序的
不同实例间共享数据。为了及时通知其它进程共享数据的变化,可以自定义
一条用户消息,通过发消息来实现,这里不再赘述。

利用以上三种方法均可以有效地实现数据传递、共享,所有的例子程序均
在Delphi 3.0,4.0下调试通过。如需源码,写信至wj_email@yahoo.com索
 
TO:kaimen
关于thx1180所说的方法是调试hook的一个不错的方法,我也使用类似的方法
如果你想调试hook的话,多写几行代码又有什么呢?
你想在IDE里很好的调试的话,是不可能的
 
收摊了,虽然还有点疑惑 :(
 
后退
顶部