日志钩子(JournalRecord Hook)的使用
---- 钩子是WINDOWS中消息处理机制的一个要点,通过安装各种钩子,应用程序能够设置相应的子例程来监视系统里的消息传递以及在这些消息到达目标窗口程序之前处理它们。钩子的种类很多,每种钩子可以截获并处理相应的消息,如键盘钩子可以截获键盘消息,鼠标钩子可以截获鼠标消息,外壳钩子可以截获启动和关闭应用程序的消息,日志钩子可以监视和记录输入事件。钩子分为线程专用钩子和全局钩子,线程专用钩子只监视指定的线程,要监视系统中的所有线程,必须用到全局钩子。对于全局钩子,钩子函数必须包含在独立的动态链接库(DLL)中,这样才能被各种相关联的应用程序调用。在WINDOWS中,日志钩子是个很特别的钩子,它只有全局钩子一种,是键盘鼠标等输入设备的消息在系统消息队列被取出时发生的,而且系统中只能存在一个这样的日志钩子,更重要是,它不必用在动态链接库中,这样可以省却了为安装一个全局钩子而建立一个动态链接库的麻烦。利用日志钩子,我们可以监视各种输入事件,下面的示例可以用来记录键盘的输入,当有按键发生时,自动记录按键动作的日期和时间以及当前激活的窗口名称。本示例在中文WIN98,Borland C++ Builder4中编译通过。
---- 1.新建一个工程,在窗体Form1中放置两个按钮Button1和Button2, CAPTION分别为"安装日志钩子"和"卸载日志钩子"。
---- 2. 定义如下全局变量:
HHOOK g_hLogHook=NULL; //钩子变量
HWND g_hLastFocus=NULL;
//记录上一次得到焦点的窗口句柄
const int KeyPressMask=0x80000000; //键盘掩码常量
char g_PrvChar; //保存上一次按键值
3.在Button1的OnClick事件中输入:
void __fastcall TForm1::Button1Click(TObject *Sender)
{
if (g_hLogHook==NULL)
g_hLogHook = SetWindowsHookEx
(WH_JOURNALRECORD,
(HOOKPROC)JournalLogProc,
HInstance,0); //安装日志钩子
}
4.在Button2的OnClick事件中输入:
void __fastcall TForm1::Button2Click(TObject *Sender)
{
if (g_hLogHook!=NULL)
{UnhookWindowsHookEx(g_hLogHook);
g_hLogHook=NULL;
} //卸载日志钩子
}
5.输入钩子回调函数:
HOOKPROC JournalLogProc(int iCode,
WPARAM wParam, LPARAM lParam)
{
if (iCode< 0) return (HOOKPROC)CallNextHookEx
(g_hLogHook,iCode,wParam,lParam);
if (iCode==HC_ACTION)
{EVENTMSG *pEvt=(EVENTMSG *)lParam;
int i;
HWND hFocus; //保存当前活动窗口句柄
char szTitle[256]; //当前窗口名称
char szTime[128]; //保存当前的日期和时间
FILE *stream=fopen("c:\\logfile.txt","a+t");
if (pEvt->message==WM_KEYDOWN)
{int vKey=LOBYTE(pEvt- >paramL); // 取得虚拟键值
char ch;
char str[10];
hFocus=GetActiveWindow();
//取得当前活动窗口句柄
if(g_hLastFocus!=hFocus)
//当前活动窗口是否改变
{GetWindowText(hFocus,szTitle,256);
g_hLastFocus=hFocus;
strcpy(szTime,DateTimeToStr(Now())
.c_str()); //得到当前的日期时间
fprintf(stream,"%c%s%c%c%s",
10,szTime,32,32,szTitle); //写入文件
fprintf(stream,"%c%c",32,32);
}
int iShift=GetKeyState(0x10);
//测试SHIFT,CAPTION,NUMLOCK等键是否按下
int iCapital=GetKeyState(0x14);
int iNumLock=GetKeyState(0x90);
bool bShift=(iShift & KeyPressMask)==KeyPressMask;
bool bCapital=(iCapital & 1)==1;
bool bNumLock=(iNumLock & 1)==1;
if (vKey >=48 && vKey< =57)
// 数字0-9
if (!bShift) fprintf(stream,"%c",vKey);
if (vKey >=65 && vKey< =90)
// A-Z a-z
{if (!bCapital)
if (bShift) ch=vKey; else ch=vKey+32;
else
if (bShift) ch=vKey+32; else ch=vKey;
fprintf(stream,"%c",ch);
}
if (vKey >=96 && vKey< =105) // 小键盘0-9
if (bNumLock) fprintf(stream,"%c",vKey-96+48);
if (vKey>=186 && vKey<=222) // 其他键
{switch (vKey)
{case 186:if (!bShift) ch=';'; else ch=':';break;
case 187:if (!bShift) ch='='; else ch='+';break;
case 188:if (!bShift) ch=','; else ch='<' ;break;
case 189:if (!bShift) ch='-'; else ch='_';break;
case 190:if (!bShift) ch='.'; else ch=' >';break;
case 191:if (!bShift) ch='/'; else ch='?';break;
case 192:if (!bShift) ch='`'; else ch='~';break;
case 219:if (!bShift) ch='['; else ch='{';break;
case 220:if (!bShift) ch='\\'; else ch='|';break;
case 221:if (!bShift) ch=']'; else ch='}';break;
case 222:if (!bShift) ch='\''; else ch='\"';break;
default:ch='n';break;
}
if (ch!='n') fprintf(stream,"%c",ch);
}
// if (wParam >=112 && wParam<=123)
// 功能键 [F1]-[F12]
if (vKey >=8 && vKey< =46) //方向键
{switch (vKey)
{case 8:strcpy(str,"[BK]");break;
case 9:strcpy(str,"[TAB]");break;
case 13:strcpy(str,"[EN]");break;
case 32:strcpy(str,"[SP]");break;
case 33:strcpy(str,"[PU]");break;
case 34:strcpy(str,"[PD]");break;
case 35:strcpy(str,"[END]");break;
case 36:strcpy(str,"[HOME]");break;
case 37:strcpy(str,"[LF]");break;
case 38:strcpy(str,"[UF]");break;
case 39:strcpy(str,"[RF]");break;
case 40:strcpy(str,"[DF]");break;
case 45:strcpy(str,"[INS]");break;
case 46:strcpy(str,"[DEL]");break;
default:ch='n';break;
}
if (ch!='n')
{if (g_PrvChar!=vKey)
{fprintf(stream,"%s",str);
g_PrvChar=vKey;
}
}
}
}
if
(pEvt- >message==WM_LBUTTONDOWN || pEvt- >message
==WM_RBUTTONDOWN)
{hFocus=GetActiveWindow();
if (g_hLastFocus!=hFocus)
{g_hLastFocus=hFocus;
GetWindowText(hFocus,szTitle,256);
strcpy(szTime,DateTimeToStr(Now()).c_str());
//得到当前的日期时间
fprintf(stream,"%c%s%c%c%s",
10,szTime,32,32,szTitle); //写入文件
fprintf(stream,"%c%c",32,32);
}
}
fclose(stream);
return (HOOKPROC)CallNextHookEx
(g_hLogHook,iCode,wParam,lParam);
}
---- 将工程编译执行后,每当激活一个窗口时,就会把当前窗口名称写入文件c:\logfile.txt中,当有按键时,按键的名称也会写入此文件中,这里的并没有处理全部的按键,读者可根据需要添加相应的语句。要捕捉键盘的按键动作,用键盘钩子(Keyboard Hook)也同样可以实现,但是用日志钩子却比键盘钩子要方便许多。首先,如果要捕捉其他应用程序的按键,即做成全局钩子,键盘钩子一定要单独放在动态链接库中,而日志钩子却不必;其次,在键盘钩子函数得到的键盘按键之前,系统已经处理过这些输入了,如果系统把这些按键屏蔽掉,键盘钩子就无法检测到它们,例如,当输入屏幕保护程序密码时,键盘钩子无法检测到用户输入了那些字符,而日志钩子却可以检测到。
From: www.delphibbs.com
**************************
{
win9x,nt,w2k 中的系统日志钩子示例程序(delphi 版)
-----------------------------------------------------
windows下的日志钩子是一种很有用的hook类型,他不需要动态链接库*.dll,就能实现
系统级的事件监控,它只能监视两种硬件的事件,即鼠标,键盘的操作,而不能监视其它
消息,被记录的消息可以用日志回放钩子将它还原,下面这个程序用delphi设计,没有
用delphi的控件,只用了win32 api,所以通用于delphi的任何版本,当然你也可以用c
来实现,有看不懂的可以写信给我,这是第一版,可能有bug,大家发现了通知我一下,欢
迎大家和我一起来讨论hook技术:
-----------------------------------------------------
first created:njhhack 2001.6.14 (ver1.0)
电子信箱:njhhack@21cn.com
主页:hotsky.363.net
}
program journal;
//包含如下头文件
uses windows,messages,sysutils;
{$r *.res} //使用资源文件
//定义一个新的结构类型
type
twin = record
msg:tmsg;
wclass:twndclass;
hmain:integer;
lr:trect;
tem:teventmsg;
end;
var
win:twin; //结构变量
hhjournalrecordproc:integer; //日志钩子句柄
//将字符串str写到文件c:\key.txt中
procedure saveinfo(str:string);stdcall;
var
f:textfile;
fname:string;
begin
fname:='c:\key.txt';
assignfile(f,fname);
if fileexists(fname)=false then rewrite(f)
else append(f);
writeln(f,str);
closefile(f);
end;
//将信息写到屏幕
procedure writestr;
var
hdc:integer;
str:string;
begin
hdc:=getdc(win.hmain);
roundrect(hdc,10,10,240,140,12,8);
with win.tem do
begin
str:=format('窗口句柄=%x',[hwnd]);
textout(hdc,30,24*1,pchar(str),length(str));
str:=format('鼠标位置=(%d,%d)',[paraml,paramh]);
textout(hdc,30,24*2,pchar(str),length(str));
str:=format('消息类型=%x',[message]);
textout(hdc,30,24*3,pchar(str),length(str));
str:=format('时间=%d',[time div 1000]);
textout(hdc,30,24*4,pchar(str),length(str));
end;
releasedc(win.hmain,hdc);
end;
//日志钩子的回调函数
function journalrecordproc(ncode:integer;wparam:wparam;lparam:lparam):lresult;stdcall;
begin
win.tem:=teventmsg(peventmsg(lparam)^);
if ncode>=0 then
begin
with win.tem do
begin
with win.lr do
begin
left:=10;
top:=10;
right:=240;
bottom:=140;
end;
invalidaterect(win.hmain,@win.lr,false);
if message=wm_lbuttondown then
begin
saveinfo(format('窗口句柄=%x,鼠标位置=(%d,%d),消息类型=wm_lbuttondown,时间=%d',[hwnd,paraml,paramh,time div 1000]));
end;
end;
end;
result:=callnexthookex(hhjournalrecordproc,ncode,wparam,lparam); //调用下一个钩子
end;
//钩子设置和删除函数
procedure sethook(fset:boolean);
begin
if fset=true then
begin
if hhjournalrecordproc=0 then hhjournalrecordproc:=setwindowshookex(wh_journalrecord,@journalrecordproc,hinstance,0);
end else
begin
if hhjournalrecordproc<>0 then unhookwindowshookex(hhjournalrecordproc);
end;
end;
//主程序的回调函数
function windowproc(hwnd,msg,wparam,lparam:longint):lresult; stdcall;
begin
result:=defwindowproc(hwnd,msg,wparam,lparam);
case msg of
wm_paint:writestr;
wm_destroy:begin sethook(false);halt;end;
end;
end;
//主程序的执行函数
procedure run;stdcall;
begin
win.wclass.hinstance:= hinstance;
with win.wclass do
begin
hicon:= loadicon(hinstance,'mainicon');
hcursor:= loadcursor(0,idc_arrow);
hbrbackground:= color_btnface+1;
style:= cs_parentdc;
lpfnwndproc:= @windowproc;
lpszclassname:='journalrecordhook';
end;
registerclass(win.wclass);
win.hmain:=createwindow(win.wclass.lpszclassname,'系统日志钩子演示程序',ws_visible or ws_overlappedwindow,10,10,260,180,0,0,hinstance,nil);
sethook(true);
while(getmessage(win.msg,win.hmain,0,0))do
begin
translatemessage(win.msg);
dispatchmessage(win.msg);
end;
end;
begin
run; //开始运行主程序
end.