米葫芦网

利用HOOK拦截封包原理

热度:53℃ 发布时间:2024-02-22 23:00:18
;;;;;截获API是个很有用的东西,比如你想分析一下别人的程序是怎样工作的。这里我介绍一下一种我自己试验通过的方法。;
首先,我们必须设法把自己的代码放到目标程序的进程空间里去。Windows Hook可以帮我们实现这一点。SetWindowsHookEx的声明如下:;
HHOOK SetWindowsHookEx(;
int idHook, // hook type;
HOOKPROC lpfn, // hook procedure;
HINSTANCE hMod, // handle to application instance;
DWORD dwThreadId // thread identifier;
)
具体的参数含义可以翻阅msdn,没有msdn可谓寸步难行。;
这里Hook本身的功能并不重要,我们使用它的目的仅仅只是为了能够让Windows把我们的代码植入别的进程里去。hook Type我们任选一种即可,只要保证是目标程序肯定会调用到就行,这里我用的是WH_CALLWNDPROC。lpfn和hMod分别指向我们的钩子代码及其所在的dll,dwThreadId设为0,表示对所有系统内的线程都挂上这样一个hook,这样我们才能把代码放到别的进程里去。;;
之后,我们的代码就已经进入了系统内的所有进程空间了。必须注意的是,我们只需要截获我们所关心的目标程序的调用,因此还必须区分一下进程号。我们自己的钩子函数中,第一次运行将进行最重要的API重定向的工作。也就是通过将所需要截获的API的开头几个字节改为一个跳转指令,使其跳转到我们的API中来。这是最关键的部分。这里我想截三个调用,ws2_32.dll中的send和recv、user32.dll中的GetMessageA。;

DWORD dwCurrentPID = 0
HHOOK hOldHook = NULL
DWORD pSend = 0
DWORD pRecv = 0
GETMESSAGE pGetMessage = NULL

BYTE btNewBytes[8] = { 0x0B8, 0x0, 0x0, 0x40, 0x0, 0x0FF, 0x0E0, 0 }
DWORD dwOldBytes[3][2]

HANDLE hDebug = INVALID_HANDLE_value

LRESULT CALLBACK CallWndProc( int nCode, WPARAM wParam, LPARAM lParam );
{;
DWORD dwSize
DWORD dwPIDWatched
HMODULE hLib

if( dwCurrentPID == 0 );
{;
dwCurrentPID = GetCurrentProcessId()
HWND hwndMainHook
wndMainHook = ::FindWindow( 0, "MainHook )
dwPIDWatched = ::SendMessage( hwndMainHook, (WM_USER+100), 0, 0 )
hOldHook = (HHOOK)::SendMessage( hwndMainHook, (WM_USER+101), 0, 0 )

if( dwCurrentPID == dwPIDWatched );
{;
Lib = LoadLibrary( "ws2_32.dll )
Send = (DWORD)GetProcAddress( hLib, "send )
Recv = (DWORD)GetProcAddress( hLib, "recv )

::ReadProcessMemory( INVALID_HANDLE_value, (void *)pSend, (void *)dwOldBytes[0], sizeof(DWORD)*2, &dwSize )
*(DWORD *)( btNewBytes + 1 ) = (DWORD)new_send
::WriteProcessMemory( INVALID_HANDLE_value, (void *)pSend, (void *)btNewBytes, sizeof(DWORD)*2, &dwSize )

::ReadProcessMemory( INVALID_HANDLE_value, (void *)pRecv, (void *)dwOldBytes[1], sizeof(DWORD)*2, &dwSize )
*(DWORD *)( btNewBytes + 1 ) = (DWORD)new_recv
::WriteProcessMemory( INVALID_HANDLE_value, (void *)pRecv, (void *)btNewBytes, sizeof(DWORD)*2, &dwSize )

Lib = LoadLibrary( "user32.dll )
GetMessage = (GETMESSAGE)GetProcAddress( hLib, "GetMessageA )
::ReadProcessMemory( INVALID_HANDLE_value, (void *)pGetMessage, (void *)dwOldBytes[2], sizeof(DWORD)*2, &dwSize )
*(DWORD *)( btNewBytes + 1 ) = (DWORD)new_GetMessage
::WriteProcessMemory( INVALID_HANDLE_value, (void *)pGetMessage, (void *)btNewBytes, sizeof(DWORD)*2, &dwSize )

Debug = ::CreateFile( "C:Trace.log, GENERIC_WRITE, 0, 0, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0 )
};
};

if( hOldHook != NULL );
{;
return CallNextHookEx( hOldHook, nCode, wParam, lParam )
};

return 0
};

上面的钩子函数,只有第一次运行时有用,就是把三个函数的首8字节修改一下(实际上只需要7个)。btNewBytes中的指令实际就是;
mov eax, 0x400000;
jmp eax;
这里的0x400000就是新的函数的地址,比如new_recv/new_send/new_GetMessage,此时,偷梁换柱已经完成。再看看我们的函数中都干了些什么。以GetMessageA为例:;

BOOL _stdcall new_GetMessage( LPMSG lpMsg, HWND hWnd, UINT wMsgFilterMin, UINT wMsgFilterMax );
{;
DWORD dwSize
char szTemp[256]
BOOL r = false

//Watch here before it"s executed.;
printf( szTemp, "Before GetMessage : HWND 0x%8.8X, msgMin 0x%8.8X, msgMax 0x%8.8x , hWnd, wMsgFilterMin, wMsgFilterMax )
::WriteFile( hDebug, szTemp, strlen(szTemp), &dwSize, 0 )
//Watch over;

// restore it at first;
::WriteProcessMemory( INVALID_HANDLE_value, (void *)pGetMessage, (void *)dwOldBytes[2], sizeof(DWORD)*2, &dwSize )

// execute it;
r = pGetMessage( lpMsg, hWnd, wMsgFilterMin, wMsgFilterMax )

// hook it again;
*(DWORD *)( btNewBytes + 1 ) = (DWORD)new_GetMessage
::WriteProcessMemory( INVALID_HANDLE_value, (void *)pGetMessage, (void *)btNewBytes, sizeof(DWORD)*2, &dwSize )

//Watch here after it"s executed;
printf( szTemp, "Result of GetMessage is %d., r )
::WriteFile( hDebug, szTemp, strlen( szTemp ), &dwSize, 0 )
if( r );
{;
printf( szTemp, "Msg : HWND 0x%8.8X, MSG 0x%8.8x, wParam 0x%8.8X, lParam 0x%8.8XTime 0x%8.8X, X %d, Y %d,;
lpMsg->hwnd, lpMsg->message,;
lpMsg->wParam, lpMsg->lParam, lpMsg->time,;
lpMsg->pt.x, lpMsg->pt.y )
::WriteFile( hDebug, szTemp, strlen( szTemp ), &dwSize, 0 )
};
trcpy( szTemp, " )
::WriteFile( hDebug, szTemp, strlen( szTemp ), &dwSize, 0 )

//Watch over;

return r
};

先将截获下来的参数,写入到一个log文件中,以便分析。然后恢复原先保留下来的GetMessageA的首8字节,然后执行真正的GetMessageA调用,完毕后再将执行结果也写入log文件,然后将GetMessageA的执行结果返回给调用者。;
整个截获的过程就是这样。你可以把其中的写log部分改成你自己想要的操作。这里有个不足的地方是,截获动作是不能够并发进行的,如果目标进程是多线程的,就会有问题。解决办法是,可以在每次new_GetMessage中加入一个CriticalSection的锁和解锁,以使调用变为串行进行,但这个我没有试验过。
网友评论
评论
发 布

更多软件教程
  • 软件教程推荐
更多+
Greenfoot设置中文的方法

Greenfoot设置中文的方法

Greenfoot是一款简单易用的Java开发环境,该软件界面清爽简约,既可以作为一个开发框使用,也能够作为集成开发环境使用,操作起来十分简单。这款软件支持多种语言,但是默认的语言是英文,因此将该软件下载到电脑上的时候,会发现软件的界面语言是英文版本的,这对于英语基础较差的朋友来说,使用这款软件就会...

07-05

Egret UI Editor修改快捷键的方法

Egret UI Editor修改快捷键的方法

Egret UI Editor是一款开源的2D游戏开发代码编辑软件,其主要功能是针对Egret项目中的Exml皮肤文件进行可视化编辑,功能十分强大。我们在使用这款软件的过程中,可以将一些常用操作设置快捷键,这样就可以简化编程,从而提高代码编辑的工作效率。但是这款软件在日常生活中使用得不多,并且专业性...

07-05

KittenCode新建项目的方法

KittenCode新建项目的方法

KittenCode是一款十分专业的编程软件,该软件给用户提供了可视化的操作界面,支持Python语言的编程开发以及第三方库管理,并且提供了很多实用的工具,功能十分强大。我们在使用这款软件进行编程开发的过程中,最基本、最常做的操作就是新建项目,因此我们很有必要掌握新建项目的方法。但是这款软件的专业性...

07-05

Thonny设置中文的方法

Thonny设置中文的方法

Thonny是一款十分专业的Python编辑软件,该软件界面清爽简单,给用户提供了丰富的编程工具,具备代码补全、语法错误显示等功能,非常的适合新手使用。该软件还支持多种语言,所以在下载这款软件的时候,有时候下载到电脑中的软件是英文版本的,这对于英语基础较差的小伙伴来说,使用这款软件就会变得十分困难,...

07-05

最新软件下载