分析一下别人的程序是怎样工作的

截获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的锁和解锁,以使调用变为串行进行,但这个我没有试验过。 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值