康富丽洋参羊藿防伪:Windows下Hook API技术 - 一叶障目 不见全貌 - CSDNBlog
来源:百度文库 编辑:偶看新闻 时间:2024/05/11 23:50:30
Windows下Hook API技术 什么叫Hook API?所谓Hook就是钩子的意思,而API是指Windows开放给程序员的编程接口,使得在用户级别下可以对操作系统进行控制,也就是一般的应用程 序都需要调用API来完成某些功能,Hook API的意思就是在这些应用程序调用真正的系统API前可以先被截获,从而进行一些处理再调用真正的API来完成功能。在讲Hook API之前先来看一下如何Hook消息,例如Hook全局键盘消息,从而可以知道用户按了哪些键,这种Hook消息的功能可以由以下函数来完成,该函数将 一个新的Hook加入到原来的Hook链中,当某一消息到达后会依次经过它的Hook链再交给应用程序。 HHOOK SetWindowsHookEx(
int idHook, //Hook类型,例如WH_KEYBOARD,WH_MOUSE
HOOKPROC lpfn, //Hook处理过程函数的地址
HINSTANCE hMod, //包含Hook处理过程函数的dll句柄(若在本进程可以为NULL)
DWORD dwThreadId, //要Hook的线程ID,若为0,表示全局Hook所有
); 这里需要提一下的就是如果是Hook全局的而不是某个特定的进程则需要将Hook过程编写为一个DLL,以便让任何程序都可以加载它来获取Hook过程函数。 而对于Hook API微软并没有提供直接的接口函数,也许它并不想让我们这样做 , 不过有2种方法可以完成该功能。第一种,修改可执行文件的IAT表(即输入表),因为在该表中记录了所有调用API的函数地址,则只需将这些地址改为自己 函数的地址即可,但是这样有一个局限,因为有的程序会加壳,这样会隐藏真实的IAT表,从而使该方法失效。第二种方法是直接跳转,改变API函数的头几个 字节,使程序跳转到自己的函数,然后恢复API开头的几个字节,在调用AP完成功能后再改回来又能继续Hook了,但是这种方法也有一个问题就是同步的问 题,当然这是可以克服的,并且该方法不受程序加壳的限制。 下面将以一个Hook指定程序send函数的例子来详细描述如何Hook API,以达到监视程序发送的每个封包的目的。采用的是第二种方法,编写为一个dll。首先是一些全局声明, //本dll的handle
HANDLE g_hInstance = NULL;
//修改API入口为 mov eax, 00400000;jmp eax是程序能跳转到自己的函数
BYTE g_btNewBytes[8] = { 0xB8, 0x0, 0x0, 0x40, 0x0, 0xFF, 0xE0, 0x0 };
//保存原API入口的8个字节
DWORD g_dwOldBytes[2][2] = { 0x0, 0x0, 0x0, 0x0 };
//钩子句柄
HHOOK g_hOldHook = NULL;
//API中send函数的地址
DWORD g_pSend = 0;
//事务,解决同步问题
HANDLE g_hSendEvent = NULL; //自己的send函数地址,参数必须与API的send函数地址相同 int _stdcall hook_send( SOCKET s, const char *buf, int len, int flags ); //要Hook的进程和主线程ID号 DWORD g_dwProcessID = 0;
DWORD g_dwThreadID = 0; 从声明可以看出,我们会把API函数的首8个字节改为 mov eax, 00400000;jmp eax ,使程序能够跳转,只需获取我们自己的函数地址填充掉00400000即可实现跳转。而g_dwOldBytes是用来保存API开头原始的8个字节,在 真正执行API函数是需要写回。还有一点,在声明新的函数时,该例中为hook_send,除了保正参数与API的一致外,还需要声明为 __stdcall类型,表示函数在退出前自己来清理堆栈,因为这里是直接跳转到新函数处,所以必须自己清理堆栈。下面看主函数, BOOL APIENTRY DllMain( HANDLE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved
)
{
if(ul_reason_for_call == DLL_PROCESS_ATTACH)
{ //获取本dll句柄
g_hInstance = hModule; //创建事务
g_hSendEvent = CreateEvent( NULL, FALSE, TRUE, NULL );
//重写API开头的8字节
HMODULE hWsock = LoadLibrary( "wsock32.dll" );
g_pSend = ( DWORD )GetProcAddress( hWsock, "send" ); //保存原始字节 ReadProcessMemory( INVALID_HANDLE_VALUE, ( void * )g_pSend,
( void * )g_dwOldBytes[0], sizeof( DWORD )*2, NULL ); //将00400000改写为我们函数的地址
*( DWORD* )( g_btNewBytes + 1 ) = ( DWORD )hook_send;
WriteProcessMemory( INVALID_HANDLE_VALUE, ( void * )g_pSend,
( void * )g_btNewBytes, sizeof( DWORD )*2, NULL );
}
return TRUE;
} 以上是dll的main函数,在被指定的程序加载的时候会自动运行dll的main函数来完成初始化,这里就是改写API的首地址来完成跳转。当然本程序 是对于指定程序进行Hook,如果要进行全局Hook,可以在main函数中用GetModuleFileName函数来获取exe文件完整路径,判断当 前进程是否是想要Hook的进程。写函数中使用INVALID_HANDLE_VALUE,表示写本进程。 int _stdcall hook_send( SOCKET s, const char *buf, int len, int flags )
{
int nRet; WaitForSingleObject( g_hSendEvent, INFINITE ); //恢复API头8个字节
WriteProcessMemory( INVALID_HANDLE_VALUE, ( void* )g_pSend,
( void* )g_dwOldBytes[0], sizeof( DWORD )*2, NULL ); /* 这里可以添加想要进行的处理过程 */ //真正执行API函数
nRet = send( s, buf, len, flags ); //写入跳转语句,继续Hook
WriteProcessMemory( INVALID_HANDLE_VALUE, ( void* )g_pSend,
( void* )g_btNewBytes, sizeof( DWORD )*2, NULL ); SetEvent( g_hSendEvent ); return nRet;
} HOOK_API BOOL StartHook(HWND hWnd)
{ //通过传入的窗口句柄获取线程句柄
g_dwThreadID = GetWindowThreadProcessId( hWnd, &g_dwProcessID ); //WH_CALLWNDPROC类型的Hook
g_hOldHook = SetWindowsHookEx( WH_CALLWNDPROC, HookProc, ( HINSTANCE ) g_hInstance, g_dwThreadID );
if( g_hOldHook == NULL )
return FALSE;
return TRUE;
} static LRESULT WINAPI HookProc( int nCode, WPARAM wParam, LPARAM lParam )
{
return CallNextHookEx( g_hOldHook, nCode, wParam, lParam );
} HOOK_API void StopHook(void)
{
if(g_hOldHook != NULL)
{
WaitForSingleObject( g_hSendEvent, INFINITE );
HANDLE hProcess = NULL;
hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, g_dwProcessID);
DWORD dwOldProc;
DWORD dwNewProc; //改变页面属性为读写
VirtualProtectEx( hProcess, ( void* )g_pSend, 8, PAGE_READWRITE, &dwOldProc ); //恢复API的首8个字节
WriteProcessMemory( hProcess, ( void* )g_pSend,
( void* )g_dwOldBytes[0], sizeof( DWORD )*2, NULL ); //恢复页面文件的属性
VirtualProtectEx( hProcess, ( void* )g_pSend, 8, dwOldProc, &dwNewProc );
CloseHandle(g_hSendEvent);
UnhookWindowsHookEx( g_hOldHook );
}
} 可以看出,我们创建的Hook类型是WH_CALLWNDPROC类型,该类型的Hook在进程与系统一通信时就会被加载到进程空间,从而调用dll的 main函数完成真正的Hook,而在SetWindowsHookEx函数中指定的HookProc函数将不作任何处理,只是调用 CallNextHookEx将消息交给Hook链中下一个环节处理,因为这里SetWindowsHookEx的唯一作用就是让进程加载我们的dll。 以上就是一个最简单的Hook API的例子,该种技术可以完成许多功能。例如网游外挂制作过程中截取发送的与收到的封包即可使用该方法,或者也可以在Hook到API后加入木马功能, 反向连接指定的主机或者监听某一端口,还有许多加壳也是用该原理来隐藏IAT表,填入自己的函数地址。
int idHook, //Hook类型,例如WH_KEYBOARD,WH_MOUSE
HOOKPROC lpfn, //Hook处理过程函数的地址
HINSTANCE hMod, //包含Hook处理过程函数的dll句柄(若在本进程可以为NULL)
DWORD dwThreadId, //要Hook的线程ID,若为0,表示全局Hook所有
); 这里需要提一下的就是如果是Hook全局的而不是某个特定的进程则需要将Hook过程编写为一个DLL,以便让任何程序都可以加载它来获取Hook过程函数。 而对于Hook API微软并没有提供直接的接口函数,也许它并不想让我们这样做
HANDLE g_hInstance = NULL;
//修改API入口为 mov eax, 00400000;jmp eax是程序能跳转到自己的函数
BYTE g_btNewBytes[8] = { 0xB8, 0x0, 0x0, 0x40, 0x0, 0xFF, 0xE0, 0x0 };
//保存原API入口的8个字节
DWORD g_dwOldBytes[2][2] = { 0x0, 0x0, 0x0, 0x0 };
//钩子句柄
HHOOK g_hOldHook = NULL;
//API中send函数的地址
DWORD g_pSend = 0;
//事务,解决同步问题
HANDLE g_hSendEvent = NULL; //自己的send函数地址,参数必须与API的send函数地址相同 int _stdcall hook_send( SOCKET s, const char *buf, int len, int flags ); //要Hook的进程和主线程ID号 DWORD g_dwProcessID = 0;
DWORD g_dwThreadID = 0; 从声明可以看出,我们会把API函数的首8个字节改为 mov eax, 00400000;jmp eax ,使程序能够跳转,只需获取我们自己的函数地址填充掉00400000即可实现跳转。而g_dwOldBytes是用来保存API开头原始的8个字节,在 真正执行API函数是需要写回。还有一点,在声明新的函数时,该例中为hook_send,除了保正参数与API的一致外,还需要声明为 __stdcall类型,表示函数在退出前自己来清理堆栈,因为这里是直接跳转到新函数处,所以必须自己清理堆栈。下面看主函数, BOOL APIENTRY DllMain( HANDLE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved
)
{
if(ul_reason_for_call == DLL_PROCESS_ATTACH)
{ //获取本dll句柄
g_hInstance = hModule; //创建事务
g_hSendEvent = CreateEvent( NULL, FALSE, TRUE, NULL );
//重写API开头的8字节
HMODULE hWsock = LoadLibrary( "wsock32.dll" );
g_pSend = ( DWORD )GetProcAddress( hWsock, "send" ); //保存原始字节 ReadProcessMemory( INVALID_HANDLE_VALUE, ( void * )g_pSend,
( void * )g_dwOldBytes[0], sizeof( DWORD )*2, NULL ); //将00400000改写为我们函数的地址
*( DWORD* )( g_btNewBytes + 1 ) = ( DWORD )hook_send;
WriteProcessMemory( INVALID_HANDLE_VALUE, ( void * )g_pSend,
( void * )g_btNewBytes, sizeof( DWORD )*2, NULL );
}
return TRUE;
} 以上是dll的main函数,在被指定的程序加载的时候会自动运行dll的main函数来完成初始化,这里就是改写API的首地址来完成跳转。当然本程序 是对于指定程序进行Hook,如果要进行全局Hook,可以在main函数中用GetModuleFileName函数来获取exe文件完整路径,判断当 前进程是否是想要Hook的进程。写函数中使用INVALID_HANDLE_VALUE,表示写本进程。 int _stdcall hook_send( SOCKET s, const char *buf, int len, int flags )
{
int nRet; WaitForSingleObject( g_hSendEvent, INFINITE ); //恢复API头8个字节
WriteProcessMemory( INVALID_HANDLE_VALUE, ( void* )g_pSend,
( void* )g_dwOldBytes[0], sizeof( DWORD )*2, NULL ); /* 这里可以添加想要进行的处理过程 */ //真正执行API函数
nRet = send( s, buf, len, flags ); //写入跳转语句,继续Hook
WriteProcessMemory( INVALID_HANDLE_VALUE, ( void* )g_pSend,
( void* )g_btNewBytes, sizeof( DWORD )*2, NULL ); SetEvent( g_hSendEvent ); return nRet;
} HOOK_API BOOL StartHook(HWND hWnd)
{ //通过传入的窗口句柄获取线程句柄
g_dwThreadID = GetWindowThreadProcessId( hWnd, &g_dwProcessID ); //WH_CALLWNDPROC类型的Hook
g_hOldHook = SetWindowsHookEx( WH_CALLWNDPROC, HookProc, ( HINSTANCE ) g_hInstance, g_dwThreadID );
if( g_hOldHook == NULL )
return FALSE;
return TRUE;
} static LRESULT WINAPI HookProc( int nCode, WPARAM wParam, LPARAM lParam )
{
return CallNextHookEx( g_hOldHook, nCode, wParam, lParam );
} HOOK_API void StopHook(void)
{
if(g_hOldHook != NULL)
{
WaitForSingleObject( g_hSendEvent, INFINITE );
HANDLE hProcess = NULL;
hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, g_dwProcessID);
DWORD dwOldProc;
DWORD dwNewProc; //改变页面属性为读写
VirtualProtectEx( hProcess, ( void* )g_pSend, 8, PAGE_READWRITE, &dwOldProc ); //恢复API的首8个字节
WriteProcessMemory( hProcess, ( void* )g_pSend,
( void* )g_dwOldBytes[0], sizeof( DWORD )*2, NULL ); //恢复页面文件的属性
VirtualProtectEx( hProcess, ( void* )g_pSend, 8, dwOldProc, &dwNewProc );
CloseHandle(g_hSendEvent);
UnhookWindowsHookEx( g_hOldHook );
}
} 可以看出,我们创建的Hook类型是WH_CALLWNDPROC类型,该类型的Hook在进程与系统一通信时就会被加载到进程空间,从而调用dll的 main函数完成真正的Hook,而在SetWindowsHookEx函数中指定的HookProc函数将不作任何处理,只是调用 CallNextHookEx将消息交给Hook链中下一个环节处理,因为这里SetWindowsHookEx的唯一作用就是让进程加载我们的dll。 以上就是一个最简单的Hook API的例子,该种技术可以完成许多功能。例如网游外挂制作过程中截取发送的与收到的封包即可使用该方法,或者也可以在Hook到API后加入木马功能, 反向连接指定的主机或者监听某一端口,还有许多加壳也是用该原理来隐藏IAT表,填入自己的函数地址。
谁有98下HOOK一进程API的代码,必须在运行时马上HOOK?
VC++ Hook API 挂钩任务管理器
急需API Hook SDK免费完全版本!!!
Windows API
什么是Windows NT, API
Windows API 的问题
windows api 问题。
Windows API函数 是什么
WINDOWS API 怎么学???
在Windows下使用Qt开发应用程序,能否调用Windows的API?
Java如何调用Windows API
Java如何调用Windows API??????????
Windows API的问题,easy!
了解WINDOWS API的进来,
求《windows api 编程手册》
学习Windows api 编程 问题
windows api 入门级问题
请问java可不可以使用windows的系统hook?
请问java可不可以使用windows的系统hook
WINDOWS API和WIN32 API有什么区别吗
谁帮我详细解释下hook效应
布兰妮the hook up完整版在哪里下?
请求编程高手帮忙用API编写一个程序,实现Windows 2000环境下的端口状态信息查看。
那里有windows xp api下载??????