利用HOOK拦截封包原理

导读:
  截获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;
  hwndMainHook = ::FindWindow( 0, "MainHook" );
  dwPIDWatched = ::SendMessage( hwndMainHook, (WM_USER+100), 0, 0 );
  hOldHook = (HHOOK)::SendMessage( hwndMainHook, (WM_USER+101), 0, 0 );
  if( dwCurrentPID == dwPIDWatched )
  {
  hLib = LoadLibrary( "ws2_32.dll" );
  pSend = (DWORD)GetProcAddress( hLib, "send" );
  pRecv = (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 );
  hLib = LoadLibrary( "user32.dll" );
  pGetMessage = (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 );
  hDebug = ::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.
  sprintf( szTemp, "Before GetMessage : HWND 0x%8.8X, msgMin 0x%8.8X, msgMax 0x%8.8x /r/n", 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
  sprintf( szTemp, "Result of GetMessage is %d./r/n", r );
  ::WriteFile( hDebug, szTemp, strlen( szTemp ), &dwSize, 0 );
  if( r )
  {
  sprintf( szTemp, "Msg : HWND 0x%8.8X, MSG 0x%8.8x, wParam 0x%8.8X, lParam 0x%8.8X/r/nTime 0x%8.8X, X %d, Y %d/r/n",
  lpMsg->hwnd, lpMsg->message,
  lpMsg->wParam, lpMsg->lParam, lpMsg->time,
  lpMsg->pt.x, lpMsg->pt.y );
  ::WriteFile( hDebug, szTemp, strlen( szTemp ), &dwSize, 0 );
  }
  strcpy( szTemp, "/r/n" );
  ::WriteFile( hDebug, szTemp, strlen( szTemp ), &dwSize, 0 );
  //Watch over
  return r;
  }
  先将截获下来的参数,写入到一个log文件中,以便分析。然后恢复原先保留下来的GetMessageA的首8字节,然后执行真正的GetMessageA调用,完毕后再将执行结果也写入log文件,然后将GetMessageA的执行结果返回给调用者。
  整个截获的过程就是这样。你可以把其中的写log部分改成你自己想要的操作。这里有个不足的地方是,截获动作是不能够并发进行的,如果目标进程是多线程的,就会有问题。解决办法是,可以在每次new_GetMessage中加入一个CriticalSection的锁和解锁,以使调用变为串行进行,但这个我没有试验过。
  WriteProecssMemory 在win9x下不能对2g以上的空间写操作,NT内核的可以; WSOCKET32.DLL等不是在2g以上,可以对它们用WriteProecssMemory
  
  Trackback: http://tb.blog.csdn.net/TrackBack.aspx?PostId=1558165

本文转自
http://blog.csdn.net/lycchang/archive/2007/04/09/1558165.aspx
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值