这篇文章在今年《黑客防线》第5期上发表过。 大虾就不必看了,浪费时间   看这个版人气不旺,就帖出来充充数 
不过现在用户态下的API HOOK已经很普遍,没什么稀奇的了,有空的话下次写些底层东东的吧。

===========================================
Author:xyzreg[E.S.T]
URL:www.xyzreg.net
E-mail:xyzreg@163.com
=============================================

   随着安全意识的不断提高,许多软件都增强的自我保护性,结束其进程时将会调用ExitWindowsEx函数关机或者重启。这种技术在收费管理系统、病毒木马、加壳软件、反破解领域里很常见,因而采用APIHOOK技术挂接ExitWindowsEx函数使其失效,对与我们查杀病毒以及软件破解是很有用的。当然还能运用它免费上网呢!不可思议吧?现在许多收费软件都采取会员制,下机时点结帐下机按钮后,客户端就发送数据报给主机,告知用户已下机从而停止记费,同时重新启动或关闭计算机。我们挂接ExitWindowsEx后就可以让机子不重启,怎么样?免费上网还可以这么搞,嘿嘿,你没想到吧?(俺正得意,突然面前飞来小李飞刀,我闪,好险啦!)
   
      记得去年第12期的黑防上有篇关于APIHOOK的文章, 那篇所采用的方法是陷阱法(改写内存地址jmp法),我这里就用另外一种方法吧——改写IAT(引入表)法。这种方法的优点是较稳定,不要担心线程同步的问题。
APIHOOK技术应用广泛,常用于屏幕取词、网络防火墙、病毒木马、加壳软件、串口红外通信、游戏外挂、Internet通信等领域。HOOK的中文意思就是钩子,APIHOOK就是钩住API,对API进行预处理,先执行我们的函数。这类思路是不是在我们中华民族文化瑰宝《三十六计》中有所体现啦!俺们先辈实在太伟大了,敬佩中。其实HOOK是黑客领域里的一种不可或缺的思想。是不是有点跑题了?言归正传。

       改写IAT表法实现APIHOOK的思路如下:根据PE文件格式穷举模块中的image_impot_desciptor引入函数数组, 检查进程空间里是否有要HOOK的API的所在的DLL,如果有则通过WiteProcessMemory、VirtualProtect等函数改写IAT中的目标API函数地址,将其改为我们自定义函数的地址。由于地址不能跨进程引用,所以我们可以使用Windows消息钩子将我们APIHOOK所在的DLL注入到其他进程里, 为了取得较好的效果,推荐使用WH_GETMESSAGE类性的钩子。


现在就来解析核心代码:
void WINAPI HookOneAPI(LPCTSTR pszCalleeModuleName, PROC pfnOriginApiAddress, PROC pfnDummyFuncAddress, HMODULE hModCallerModule) 
//对于挂接ExitWindowsEx函数而言,pszCalleeModuleName为user32.dll, pfnOriginApiAddress为ExitWindowsEx,pfnDummyFuncAddress 为MyExitWindowsEx

 ULONG size; 

 //获取指向PE文件中的Import中IMAGE_DIRECTORY_DESCRIPTOR数组的指针 

 PIMAGE_IMPORT_DESCRIPTOR  pImportDesc= 
(PIMAGE_IMPORT_DESCRIPTOR)ImageDirectoryEntryToData(hModCallerModule,TRUE,IMAGE_DIRECTORY_ENTRY_IMPORT,&size); 
  //查找记录,查找是否存在目标API函数ExitWindwosEx所在的user32.dll 
 for (;pImportDesc->Name;pImportDesc++) 
 { 
  LPSTR pszDllName = (LPSTR)((PBYTE)hModCallerModule+pImportDesc->Name); 
  if (lstrcmpiA(pszDllName,pszCalleeModuleName) == 0) 
   break; 
 } 
 //寻找我们所要HOOK的ExitWindwosEx函数
 PIMAGE_THUNK_DATA pThunk =  
(PIMAGE_THUNK_DATA)((PBYTE)hModCallerModule+pImportDesc->FirstThunk);
for (;pThunk->u1.Function;pThunk++) 
 { 
  //ppfn记录了与IAT项相应的函数的地址 
  PROC * ppfn= (PROC *)&pThunk->u1.Function;   
  if (*ppfn == pfnOriginApiAddress)  
  { 
   //如果地址相同,就说明找到了我们要HOOK的函数,进行改写,将其指向我们所
定义的函数 
   WriteProcessMemory(GetCurrentProcess(),ppfn,&(pfnDummyFuncAddress), 
    sizeof(pfnDummyFuncAddress),NULL); 
   return; 
  } 
 } 
}


若想HOOK所有模块对目标API函数的调用,则需要使用ToolHelp函数枚举所有模块,然后逐一调用HookOneAPI函数。关键代码如下:
BOOL WINAPI HookAllAPI(LPCTSTR pszCalleeModuleName,PROC pfnOriginApiAddress, 
        PROC pfnDummyFuncAddress,HMODULE hModCallerModule) 

  if (hModCallerModule == NULL) 
 { 
  MEMORY_BASIC_INFORMATION mInfo; 
  HMODULE hModHookDLL; 
  HANDLE hSnapshot; 
  MODULEENTRY32 me = {sizeof(MODULEENTRY32)}; 
  //MODULEENTRY32:存放被指定进程所应用的模块信息的结构
  VirtualQuery(HookOneAPI,&mInfo,sizeof(mInfo)); 
  hModHookDLL=(HMODULE)mInfo.AllocationBase;    
  hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE,0); 
  BOOL bOk = Module32First(hSnapshot,&me); 
  while (bOk)
  { 
   if (me.hModule != hModHookDLL) 
   { 
    hModCallerModule = me.hModule;//赋值 
    //对每一个模块进行HOOK
    HookOneAPI(pszCalleeModuleName,pfnOriginApiAddress, 
     pfnDummyFuncAddress,hModCallerModule); 
   } 
   bOk = Module32Next(hSnapshot,&me); 
  } 
  return TRUE;   
 } 
 }


接着就要调用调用SetWindowsHookEx来安装WH_GETMESSAGE 类型的钩子把APIHOOK的DLL映射到其它进程空间里:SetWindowsHookEx(WH_GETMESSAGE,(HOOKPROC)GetMsgProc,hmodDll,dwThreadId)。 GetMsgProc函数里只要写上return CallNextHookEx(hHook,code,wParam,lParam);即可,实现循环。


现在就剩下自定义ExitWindowsEx函数了,我们只需注意参数格式和原函数一样即可,自定义的MyExitWindowsEx函数什么都不做,这样就实现的取消关机的功能了。
另外还有一点要注意一下,对于winlogon.exe,我们要提升权限至调试权限,代码如下:
if(OpenProcessToken(GetCurrentProcess(),TOKEN_ADJUST_PRIVILEGES|TOKEN_QUERY,&hToken))
 {
  LookupPrivilegeValue(NULL,SE_DEBUG_NAME,&luid);
  tp.PrivilegeCount=1;
  tp.Privileges[0].Luid=luid;
  tp.Privileges[0].Attributes=bUp?SE_PRIVILEGE_ENABLED:0;
  if(!AdjustTokenPrivileges(hToken,FALSE,&tp,sizeof(TOKEN_PRIVILEGES),NULL,NULL))
  {
   MessageBox(NULL,"提升权限失败!","",0);
   return;
  }
 }


整个框架就是这样了,由于篇幅原因,具体的不再细说了。光盘内收有网上留传的完整的程序代码,具体细节大家可参阅。