什么是“跨进程 API Hook”?

用AIDL完成可跨进程通信的WebView框架 至今为止做过的项目里,H5和android的通信都是一个个方法写好,用注解标识,然后互相调用,而且还都写在同一个地方,甚至有些没把WebView放到另一个进程中去,而且方法多了,显得特别臃肿和混乱。之前写过一个可跨进程的WebView框架,经过学习之后做出了改进。虽然还有很多可优化的点,这就算先记录下来吧。 阅读详情
什么是“跨进程 API Hook”?

Org:http://blog.youkuaiyun.com/detrox/archive/2004/01/29/17511.aspx

众所周知Windows应用程序的各种系统功能是通过调用API函数来实现。API Hook就是给系统的API附加上一段小程序,它能监视甚至控制应用程序对API函数的调用。所谓跨进程也就是让自己的程序来控制别人程序的API调用了。
API Hook 理论

通过对Win32 PE文件的分析(如果你还不熟悉PE文件格式,可以看看Iczelion的PE教程或者LUEVELSMEYER的<<The PE File Format>>)。我们知道在PE文件中的IMPORT TABLE内存储着API函数的很多信息。其中包括API的函数名,调用地址等等。而操作系统在执行PE文件时会先将其映射到内存中。在映射的同时还会把当前版本操作系统中API函数的入口地址写入IMPORT TABLE中一组与API调用相关的结构体内,用于该应用程序的API调用。 当应用程序调用API时,他会在自己内存映像里寻找API的入口地址,然后执行CALL指令。如此一来,我们通过修改应用程序内存映像的IMPORT TABLE中API函数的入口地址,就可以达到重定向API的目的。将API地址改为我们自己函数的地址,这样我们的函数就可以完成对API的监视和控制了。
API Hook 的实现
/* 1 */HANDLE hCurrent = GetModuleHandle(NULL);
/* 2 */IMAGE_DOS_HEADER *pidh;
/* 3 */IMAGE_NT_HEADERS *pinh;
/* 4 */IMAGE_DATA_DIRECTORY *pSymbolTable;
/* 5 */IMAGE_IMPORT_DESCRIPTOR *piid;
 
/* 6 */pidh = (IMAGE_DOS_HEADER *)hCurrent; 
/* 7 */pinh = (IMAGE_NT_HEADERS *)((DWORD)hCurrent + pidh->e_lfanew);
/* 8 */pSymbolTable = &pinh->OptionalHeader.DataDirectory[1];
/* 9 */piid =(IMAGE_IMPORT_DESCRIPTOR *)((DWORD)hCurrent + pSymbolTable->VirtualAddress);
/*10 */do {
/*11 */ IMAGE_THUNK_DATA *pitd,*pitd2;
/*12 */ pitd = (IMAGE_THUNK_DATA *)((DWORD)hCurrent + piid->OriginalFirstThunk);
/*13 */ pitd2 = (IMAGE_THUNK_DATA *)((DWORD)hCurrent + piid->FirstThunk);
/*14 */ do {
/*15 */ IMAGE_IMPORT_BY_NAME *piibn;
/*16 */ piibn = (IMAGE_IMPORT_BY_NAME *)((DWORD)hCurrent + *((DWORD *)pitd));
/*17 */ PROC *ppfn = (PROC *)(pitd2->u1.Function);
/*18 */ if (!strcmp("MessageBoxW",(char *)piibn->Name)) {
/*19 */ oldMsg = (MsgBoxType)(ppfn);
/*20 */ DWORD addr = (DWORD)MyMessage;
/*21 */ DWORD written = 0;
  /* 改变内存读写状态 */
/*22 */ DWORD oldAccess;
/*23 */ VirtualProtect(&pitd2->u1.Function,sizeof(DWORD),PAGE_WRITECOPY,&oldAccess);
/*24 */ APIAddress = (DWORD)&pitd2->u1.Function;
  /* 向内存映像写入数据 */
/*25 */ WriteProcessMemory(GetCurrentProcess(),&pitd2->u1.Function, &addr,sizeof(DWORD), &written);
/*26 */ }
/*27 */ pitd++;pitd2++;
/*28 */ } while (pitd->u1.Function);
 
/*29 */ piid++;
/*30 */} while (piid->FirstThunk + piid->Characteristics 
  + piid->ForwarderChain + piid->Name + piid->TimeDateStamp);
分析:
寻觅IMPORT TALBE

在/*1*/中我们使用GetModuleHandle(NULL)来返回当前进程在内存中映像的基地址。但这个值在文档中仅仅被描述为"a module handle for the specified module",虽然他确实是进程内存映像的基地址。如果你不太放心的话也可以使用,GetModuleInformation函数来获得基地址,只不过你要额外包含psapi.h和psapi.lib了(这个库在VC6里没有,所以我就没有用这个函数了)。在/* 6 */里我们先找到IMAGE_DOS_HEADER结构,他的起始地址就是映像的基地址。/*7*/通过IMAGE_DOS_HEADER给出的PE文件头的偏移量,找到IMAGE_NT_HEADERS结构。顺藤摸瓜,IMAGE_NT_HEADERS里的OptionalHeader中的DataDirectory数组里的第二个元素正是指向我们想要的IMPORT TABLE的地址。在/*9*/中我们将其转化为一个IMAGE_IMPORT_DESCRIPTOR的结构指针存入piid中。
替换的API函数入口地址

在/*12*/和/*13*/中我们分别取得OriginalFirstThunk和FirstThunk结构,用于以后得到API函数的名称和入口地址。/*10*/的do循环让我们遍历每一个IMAGE_IMPORT_DESCRIPTOR结构也就是应用程序引用的每个DLL。在/*14*/的循环中我们遍历DLL中的IMAGE_THUNK_DATA结构来一一查询API的信息。/*16*/中我们将OriginalFirstThunk转换为IMAGE_IMPORT_BY_NAME结构用于获得API函数的名称进行比对。在/*18*/我们找到MessageBoxW函数之后,在/*19*/保存其原始入口地址便于以后恢复时使用。在/*23*/我们需要用VirtualProtect改变一下内存区域的读写性,因为一般应用程序的映像都是只读的,直接写入会造成一个非法访问的异常出现。在/*25*/我们写入自己函数的地址。

这样就基本完成一个API函数的重定向。
其他

恢复函数的API入口地址相对比较简单。只要把保存的值再写回去就可以了。上面的程序中/*24*/我用APIAddress保存了存有MessageBoxW入口地址的地方的地址,便于以后调用WriteProcessMemory恢复时使用。
跨进程理论

我们要用自己的函数来替代别人程序里的API函数,但我们的函数与别人的程序处于不同的进程空间内啊。不同的进程空间是不能相互调用函数的。因此我们要想办法把自己的函数放入别人的进程空间去。这时我们就需要使用DLL injection技术了。如果你对她还不是十分熟悉的话,建议看看Jeffrey Richter大师的<<Programming Applications for Microsoft Windows>>,也可以参考陈宽达先生的<<C++ Builder深度历险>>。

简而言之,DLL injection就是想办法让对方的进程加载我们的一个DLL程序,把需要替换的函数放在我们这个DLL里。如此一来,我们的函数就进入了别人的进程空间了。DLL injection方法很多,Richter大师在书中对各方法利弊有详细解释,陈宽大先生的书中也有深入的分析。我在这里使用SetWindowsHookEx函数来达到目的。主要有这几个原因: 1, 不用重新启动系统,调试方便。2, 可以利用消息循环机制进行两个进程之间的通信,可以较好的掌握Hook的状态。便于安装与卸载。

SetWindowsHookEx之所以能完成DLL injection是因为它要给一个应用程序某个环节加上一个Hook,而Hook就要有Hook Procedure也就是Hook函数。如果这个Hook函数在一个DLL中,那么系统就会把这个DLL加载到SetWindowsHookEx的目标进程上。从而也就达到了我们DLL injection的目的了。当然这里我们会用WH_GETMESSAGE的Hook进行injection,因为这个Hook可以用来监视目标进程的消息循环方便我们的进程与目标进程通信。 
跨进程的实现和几点注意
/* DllPart.Dll */
#include <windows.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef (WINAPI *MsgBoxType)(HWND,LPCWSTR,LPCWSTR,UINT); 
MsgBoxType oldMsg; /*API原入口地址*/
DWORD APIAddress; /*存储API入口地址的地方的地址*/
int WINAPI MyMessage(HWND hWnd ,LPCWSTR M1,LPCWSTR M2, UINT M3) {
 /* 这是用来替换的函数 */
 return oldMsg(hWnd,buf,M2,MB_OK);
}
const char szApp[] = "DllPart.dll";
HHOOK hHook; /*Hook的句柄*/
HMODULE hInst; /*DLL 模块句柄,用于SetWindowsHookEx函数*/
HWND hTarget; /*目标窗口句柄*/
/*DLL 入口*/
BOOL WINAPI DllMain(HINSTANCE inst, DWORD reason, LPVOID lpvReserved)
{
  hInst = inst;
  switch (reason) {
 case DLL_PROCESS_ATTACH:
  /*调试信息,表示DLL已经加载*/
  MessageBox(NULL,"DLL_PROCESS_ATTACH",szApp,MB_OK);
  break;
 case DLL_PROCESS_DETACH:
  /*调试信息,表示DLL已经卸载*/
  MessageBox(NULL,"DLL_PROCESS_DETACH",szApp,MB_OK);
  break;
  }
  return true;
}
/*显示GetLastError的信息*/
void showerr(const char *m) {
  char message[255];
  FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,0,GetLastError()
 ,MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),message,255, 0);
  MessageBox(NULL,message,m,MB_OK);
}
//-----------------------
void UnHookApi() {
 /*卸载API Hook用*/
}
void HookApi() {
 /*加载API Hook同上面介绍的函数一样*/
}
//-----------------------
/*用于WH_GETMESSAGE的Hook Procedure*/
LRESULT CALLBACK GetMsgProc(int nCode,WPARAM wParam, LPARAM lParam) {
 if (nCode == HC_ACTION) {
  MSG *msg = (MSG *)lParam;
  if (msg->message == WM_CHAR) {
  if (msg->wParam == 'h') HookApi();
  if (msg->wParam == 'u') UnHookApi();
  }
 }
  return CallNextHookEx(hHook,nCode,wParam,lParam);
}
extern "C" __declspec(dllexport) SetAPIHook(HWND handle) {
  DWORD ThreadId = GetWindowThreadProcessId(handle, NULL);
  hTarget = handle;
  MessageBox(NULL, "Enabling CallWndProc Hook", szApp, MB_OK);
  hHook = SetWindowsHookEx(WH_GETMESSAGE,GetMsgProc,hInst,ThreadId); 
  if (hHook) {
 MessageBox(NULL,"Hook OK!", szApp, MB_OK);
  } else {
 showerr("SetWindowsHookEx");
  }
}
extern "C" __declspec(dllexport) UnHookAPIHook() {
  MessageBox(NULL, "Disenabling CallWndProc Hook", szApp, MB_OK);
  if (UnhookWindowsHookEx(hHook)) {
  MessageBox(NULL,"UnHook OK!", szApp, MB_OK); 
  } else {
 showerr("UnHookWindowsHookEx");
  }
}
分析
几个需要注意的问题

SetAPIHook和UnHookAPIHook是我们自己进程调用的用来加载WH_GETMESSAGE Hook的函数。由于我们的程序要用LoadLibrary加载这个Dll因此这两个函数要用__declspec(dllexport)修饰,使其成为导出函数,才能被GetAddressProc函数找到。加上 extern "C"是让编译器使用C语言编码方式。因为C++编译器会进行Dynamic Binding(C++函数重载的实现),将函数的参数类型附加到名称上。是函数的导出名看起来像SetAPIHook@XYTZX之类的,不利于GetAddressProc进行引用。因此使用extern "C"让编译器不使用Dynamic Binding,自然使用extern"C"的函数也就不能被重载了。

不要忘记在GetMsgProc最后要调用CallNextHookEx函数,保证Hook链的完整性。

一定要在Hook Procedure中调用HookApi和UnHookApi函数,因为保存API入口地址的地方在目标进程中,你必须在目标进程的进程空间内完成卸载操作,不能在UnHookAPIHook或是SetAPIHook函数中调用,因为UnHookAPIHook是我们的进程调用的,因此在我们的进程空间中。在这里使用UnHookApi会造成一个非法访问的错误。而使用HookApi会给自己的DLL加上API Hook。

SetWindowsHookEx的最后参数是ThreadId不是Handle,因此要通过调用GetWindowThreadProcessId转换一下。 
在跨进程API HOOK时可能用到的其他技术
主进程与目标进程的信息交互和共享

由于使用了WH_GETMESSAGE钩子我们可以利用Windows消息机制实现进程间通讯。需要注意的是应该使用PostThreadMessage来发送让WH_GETMESSAGE得到的消息而不是SendMessage或者PostMessage,因为后两个是用来给窗口发送消息的。而我们的WH_GETMESSAGE是Hook在线程上面的,因此需使用PostThreadMessage.

传递不太大的数据可以使用WM_COPYDATA消息来进行。同样也应该注意,如果使用MFC的窗口过程获得消息就需要用SendMessage发送了。WM_COPYDATA的使用相对简单可以参考MSDN的文档。也可以参考附录里的程序Hook.cpp的showerr函数部分。

如果传递较大的数据或者希望数据共享比较方便可以开辟共享内存来进行数据共享。这里简单分析一下使用共享内存的代码 
HANDLE hMap;
switch (reason) {
  case DLL_PROCESS_ATTACH:
  /*创建/打开共享内存区域*/
  hMap = CreateFileMapping((HFILE *)0xFFFFFFFF,NULL,PAGE_READWRITE,0,sizeof(GLOBALDATA),ID_MAP);
  pg_data = (GLOBALDATA*)MapViewOfFile(hMap,FILE_MAP_ALL_ACCESS,0 ,0 ,0);
  if (!pg_data) {
 MessageBox(NULL,"无法建立共享内存,程序终止!",szApp,MB_OK);
 if (hMap) {
  CloseHandle(hMap);
  hMap = NULL;
  return 0;
  }
  }
  pg_data->hInst = hInst;
  showerr("共享内存映像文件");
  showerr("DLL装载中...",FALSE);
  break;
  case DLL_PROCESS_DETACH:
  if (pg_data) {
  UnmapViewOfFile(pg_data);
 pg_data = NULL;
  }
  if (hMap) {
 CloseHandle(hMap);
 hMap = NULL;
  }
  break;
}
上面的代码通过CreateFileMapping建立共享区域。将其第一个参数设置为0xFFFFFFFF使其能创建一个内存共享区域而不是文件。并标记为可读写的(PAGE_READWRITE).其大小为我们定义的结构体GLOBALDATA的大小。最后的ID_MAP是一个用来标示这个区域的字符串。打开或者创建完共享区域后,我们用MapViewOfFile来获得这个区域的地址。之后就可以直接使用pg_data来操作共享区域了。不要忘记在DLL退出的时候安全的删除共享区域释放内存。 
消息等待与安全卸载

在我们卸载WH_GETMESSAGE钩子之前必须先把目标程序的API调用恢复正常。我们不能再调用UnHookApi之后就立刻调用UnhookWindowsHookEx,因为很有可能UnHookApi还没来得急完成API入口地址的恢复操作,WH_GETMESSAGE钩子就已经被卸载了。因此需要等待一段时间,等UnHookApi完成了恢复操作在调用UnhookWindowsHookEx。以防错误发生。
extern "C" __declspec(dllexport) void UnHookAPIHook() {
  /*向目标线程发送消息进行API UNHOOK*/
  PostThreadMessage(pg_data->idTarget,WM_DISABLEAPIHOOK,(WPARAM)GetCurrentThreadId(),0);
  showerr("WM_DISABLEAPIHOOK");
  /*等待目标进程返回WM_UNHOOKOK消息,确认可以将WH_GETMESSAGE的HOOK去掉*/
   
  MSG Msg;
  do {
  GetMessage(&Msg,NULL,0,0);
  }while(Msg.message != WM_UNHOOKOK);
  UnhookWindowsHookEx(pg_data->hHook);
  PostThreadMessage(pg_data->idTarget,WM_DISABLEAPIHOOKOK,(WPARAM)GetCurrentThreadId(),0);
  showerr("UnHookWindowsHookEx");
}
上面的代码中我们使用一个含有GetMessage的循环来等待消息的到达,一旦UnHookApi完成他就会发送WM_UNHOOKOK消息。等我们接收到消息确认一切安全了在来卸载WH_GETMESSAGE钩子。 
弄清消息对象

我们一定要清楚代码是在主程序进程空间中执行的还是在目标程序进程空间中执行的。像上面的UnHookAPIHook函数就是通过主程序调用的,因此在主程序进程空间中执行。这样一来用来恢复目标程序API信息的UnHookApi完成后就应该向主程序发送消息,而不是目标程序。
目标进程加载了其他DLL

如果目标进程动态加载了其他的DLL文件,我们必须监视LoadLibrary函数。保证DLL中的API入口地址也被正确修改。防止出现混乱的情况。我从LoadLibrary获得DLL的路径用于GetModuleHandle来取得他的ImageBase的地址。
不知道文章写的如何,希望大家能多给些批评意见。发现问题我马上改正

Email: detrox@yang.com.cn
参考资料

<<Programming Applications for Microsoft Windows>>, Jeffrey Richter, Microsoft Press

<<C++ Builder 深度历险>>,陈宽达,华中科大出版社

<<The PE File Format>>, LUEVELSMEYER

<<Iczelion's PE Tutorial>>, Iczelion
附:跨进程APIHook的例子

先打开一个记事本程序并输入几个字符,运行下面的程序,加载APIHook.之后在记事本的文件菜单中选择新建就会看到API Hook将MessageBoxW 函数Hook的结果了.
代码在WinXP SP1 + VC6.0测试成功。

下载源代码

(特别感谢老罗的代码着色器,比我自己那个好多了)
这是DLL的程序,Hook.dll
#include <windows.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "mydef.h"

const char szApp[] = "Hook.dll"; /*应用程序名称*/
HANDLE hMap;/*在共享内存映像的句柄*/
GLOBALDATA *pg_data; /*在共享内存中的全局数据*/
LRESULT CALLBACK GetMsgProc(int,WPARAM, LPARAM);

/*显示GetLastError指出的错误*/
void showerr(const char *m, BOOL GetError = TRUE) {
  char message[127];
  char buf[255];
  COPYDATASTRUCT cds;
  if (GetError) 
  FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,0
  ,GetLastError(),MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),message,127, 0);
  else 
  *message = 0;
  if (GetCurrentThreadId() != pg_data->idMain)
  sprintf(buf,"目标程序空间DLL: %-30s [%-40s]",m, message);
  else
  sprintf(buf,"主程序空间DLL : %-30s [%-40s]",m, message);
  cds.lpData = buf;
  cds.cbData = sizeof(buf);
  cds.dwData = 0;
 
  SendMessage(pg_data->hWndMain,WM_COPYDATA,(WPARAM)pg_data->hWndTarget,(LPARAM)&cds);
  
  SetLastError(0);
}
int WINAPI MyMessageBoxW(HWND hWnd ,LPCWSTR M1,LPCWSTR M2, UINT M3) {
  wchar_t buf[255];
  swprintf(buf,L"!!这个窗口的API被Hook了!!/nHWND: 0x%08X/nMessage: %s/nCaption: %s"
  ,(DWORD *)hWnd
  , M1
  , M2);
  pg_data->oldAPIFunction(hWnd,buf,M2,MB_OK);
  return pg_data->oldAPIFunction(hWnd,M1,M2,M3);
}

/*DLL 入口函数*/
BOOL WINAPI DllMain(HINSTANCE hInst, DWORD reason,LPVOID lpvReserved)
{
  switch (reason) {
  case DLL_PROCESS_ATTACH:
  /*创建/打开共享内存区域*/
  hMap = CreateFileMapping((HFILE *)0xFFFFFFFF,NULL,PAGE_READWRITE,0,sizeof(GLOBALDATA),ID_MAP);
  pg_data = (GLOBALDATA*)MapViewOfFile(hMap,FILE_MAP_ALL_ACCESS,0 ,0 ,0);
  if (!pg_data) {
  MessageBox(NULL,"无法建立共享内存,程序终止!",szApp,MB_OK);
  if (hMap) {
  CloseHandle(hMap);
  hMap = NULL;
  return 0;
  }
  }
  pg_data->hInst = hInst;
  showerr("共享内存映像文件");
  showerr("DLL装载中...",FALSE);
  break;
  case DLL_PROCESS_DETACH:
  showerr("DLL卸载中...",FALSE);
  if (pg_data) {
  UnmapViewOfFile(pg_data);
  pg_data = NULL;
  }
  if (hMap) {
  CloseHandle(hMap);
  hMap = NULL;
  }
  break;
  }
  return true;
}
/*卸载API Hook*/
void UnHookApi() {
  DWORD written = 0;
  DWORD oldaddrAPIFunction = (DWORD)pg_data->oldAPIFunction;
  WriteProcessMemory(GetCurrentProcess(),(DWORD *)pg_data->addrAPIEntryPoint
  , &oldaddrAPIFunction,sizeof(DWORD), &written);
  showerr("WriteProcessMemory on UnHook");
  /*向主线程发送 API UNHOOK 处理完毕的消息*/
  PostThreadMessage(pg_data->idMain,WM_UNHOOKOK,0,0);
}

/*加载API Hook*/
void HookApi(const char* szApiName, tAPIFunction newAddr, DWORD ImageBase) {
  /*这段代码请参考文章中的分析*/
  IMAGE_DOS_HEADER *pidh;
  IMAGE_NT_HEADERS *pinh;
  IMAGE_DATA_DIRECTORY *pSymbolTable;
  IMAGE_IMPORT_DESCRIPTOR *piid;
  
  pidh = (IMAGE_DOS_HEADER *)ImageBase;  
  pinh = (IMAGE_NT_HEADERS *)((DWORD)ImageBase + pidh->e_lfanew);
  pSymbolTable = &pinh->OptionalHeader.DataDirectory[1];
  piid =(IMAGE_IMPORT_DESCRIPTOR *)((DWORD)ImageBase + pSymbolTable->VirtualAddress);

  do {
  IMAGE_THUNK_DATA *pitd_org,*pitd_1st;
  pitd_org = (IMAGE_THUNK_DATA *)((DWORD)ImageBase + piid->OriginalFirstThunk);
  pitd_1st = (IMAGE_THUNK_DATA *)((DWORD)ImageBase + piid->FirstThunk);
  do {
  IMAGE_IMPORT_BY_NAME *piibn;
  piibn = (IMAGE_IMPORT_BY_NAME *)((DWORD)ImageBase + *((DWORD *)pitd_org));
  PROC *pAPIFunction = (PROC *)(pitd_1st->u1.Function);
  if (!strcmp(szApiName,(char *)piibn->Name)) {
  DWORD addrNewAPIFunction = (DWORD)MyMessageBoxW;
  DWORD written = 0;
  DWORD oldAccess;

  pg_data->oldAPIFunction = (tAPIFunction)(pAPIFunction);
  /*Change Memeory State*/
  VirtualProtect(&pitd_1st->u1.Function,sizeof(DWORD),PAGE_WRITECOPY,&oldAccess);
  showerr("VirtualProtect");
  pg_data->addrAPIEntryPoint = (DWORD)&pitd_1st->u1.Function;
  /*Write Process Memory*/
  WriteProcessMemory(GetCurrentProcess(),&pitd_1st->u1.Function
  , &addrNewAPIFunction,sizeof(DWORD), &written);
  showerr("WriteProcessMemory on Hook");
  }
  pitd_org++;
  pitd_1st++;
  } while (pitd_1st->u1.Function);
   
  piid++;
  } while (piid->FirstThunk + piid->Characteristics 
 + piid->ForwarderChain + piid->Name + piid->TimeDateStamp);
}

//-----------------------
extern "C" __declspec(dllexport) BOOL SetAPIHook(HWND _target) {
  pg_data->hHook = SetWindowsHookEx(WH_GETMESSAGE,GetMsgProc,pg_data->hInst,pg_data->idTarget);  
  showerr("SetWindowsHookEx");
  /*向目标线程发送消息进行API HOOK*/
  if (pg_data->hHook) {
  PostThreadMessage(pg_data->idTarget,WM_ENABLEAPIHOOK,0,0);
  } else {
  return FALSE;
  }
  showerr("WM_ENABLEAPIHOOK");
  return TRUE;
}

extern "C" __declspec(dllexport) void UnHookAPIHook() {
  /*向目标线程发送消息进行API UNHOOK*/
  PostThreadMessage(pg_data->idTarget,WM_DISABLEAPIHOOK,(WPARAM)GetCurrentThreadId(),0);
  showerr("WM_DISABLEAPIHOOK");
  /*等待目标进程返回WM_UNHOOKOK消息,确认可以将WH_GETMESSAGE的HOOK去掉*/
  MSG Msg;
  do {
  GetMessage(&Msg,NULL,0,0);
  }while(Msg.message != WM_UNHOOKOK);

  UnhookWindowsHookEx(pg_data->hHook);
  PostThreadMessage(pg_data->idTarget,WM_DISABLEAPIHOOKOK,(WPARAM)GetCurrentThreadId(),0);
  showerr("UnHookWindowsHookEx");
}

LRESULT CALLBACK GetMsgProc(int nCode,WPARAM wParam, LPARAM lParam) {
  if (nCode == HC_ACTION) {
  MSG *msg = (MSG *)lParam;
   
  if (msg->message == WM_ENABLEAPIHOOK) {
  HookApi("MessageBoxW",MyMessageBoxW,(DWORD)GetModuleHandle(NULL));
  }
  if (msg->message == WM_DISABLEAPIHOOK) {
  UnHookApi();
  }
  if (msg->message == WM_DESTROY) {
  showerr("目标进程退出!",FALSE);
  }
  }
  return CallNextHookEx(pg_data->hHook,nCode,wParam,lParam);
}
 
这个是主程序的关键部分 HookGui.cpp

用MFC建立一个窗口程序,包含两个按钮和一个ListBox
typedef void (*PUnHookAPIHook)();
typedef BOOL (*PSetAPIHook)(HWND);
HMODULE hDll = NULL;
HWND hNotePad = NULL;
PSetAPIHook SetAPIHook;
PUnHookAPIHook UnHookAPIHook;
GLOBALDATA *pg_data; /*在共享内存中的全局数据*/
HANDLE hMap; /*在共享内存映像的句柄*/
int CHookGUIDlg::showerr(const char* m) {
  char message[127];
  char buf[255];
  int errId = GetLastError();
  FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,0
 ,errId,MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),message,127, 0);
  sprintf(buf,"主程序 : %-30s [%-40s]/n",m, message);
  c_List1.InsertString(c_List1.GetCount(),buf);
  UpdateData(FALSE);
  return errId;
}

void CHookGUIDlg::OnSetapihook() 
{
  // TODO: Add your control notification handler code here
  hDll = LoadLibrary("Hook.dll");
  showerr("LoadLibrary");
  hMap = OpenFileMapping(FILE_MAP_ALL_ACCESS,false,ID_MAP);
  showerr("OpenFileMapping");  
  pg_data = (GLOBALDATA *)MapViewOfFile(hMap,FILE_MAP_ALL_ACCESS,0,0,0);
  showerr("MapViewOfFile");  
  if (!pg_data) {
  MessageBox("不能打开共享内存程序终止!");
  return;
  }
  SetAPIHook = (PSetAPIHook)GetProcAddress(hDll,"SetAPIHook");
  showerr("GetProcAddress-SetAPIHOOK");
   
  pg_data->hWndTarget = ::FindWindow("NotePad",NULL);
  pg_data->hWndMain = m_hWnd;
  pg_data->idMain = GetWindowThreadProcessId(m_hWnd,NULL);
  pg_data->idTarget = GetWindowThreadProcessId(pg_data->hWndTarget,NULL);
  if (!showerr("FindWindow")) {
  if (SetAPIHook) { 
  if (SetAPIHook(hNotePad))
  PostThreadMessage(pg_data->idTarget
  , WM_SETCALLERID,(LPARAM)GetCurrentThreadId(),0);
  else {
  MessageBox("SetWindowHookEx时出错,程序终止!");
  return;
  }
  } else {
  MessageBox("无法取得SetAPIHook函数!程序终止!");  
  }
  } else {
  MessageBox("内存中没有找到NOTEPAD.EXE");  
  }
  c_SetApiHook.EnableWindow(FALSE);
  c_UnsetApiHook.EnableWindow();
}

void CHookGUIDlg::OnUnsetapihook() 
{
  // TODO: Add your control notification handler code here
  if (hDll) {
  if ( !IsWindow(pg_data->hWndTarget)) {
  MessageBox("目标进程不在内存中");
  return;
  }
  UnHookAPIHook = (PUnHookAPIHook)GetProcAddress(hDll,"UnHookAPIHook");
  showerr("GetProcAddress-UnHookAPIHook");
  if (UnHookAPIHook) 
  UnHookAPIHook();
  FreeLibrary(hDll);
  showerr("FreeLibrary");
  hDll = NULL;
  } else {
  MessageBox("请先加载DLL");
  }
  c_SetApiHook.EnableWindow();
  c_UnsetApiHook.EnableWindow(FALSE);
}

void CHookGUIDlg::OnOK() 
{
  // TODO: Add extra validation here
  if (hDll) {
  OnUnsetapihook();
  }
  CDialog::OnOK();
}

BOOL CHookGUIDlg::OnCopyData(CWnd* pWnd, COPYDATASTRUCT* pCopyDataStruct) 
{
  // TODO: Add your message handler code here and/or call default
  c_List1.InsertString(c_List1.GetCount(),(char *)pCopyDataStruct->lpData);
  return CDialog::OnCopyData(pWnd, pCopyDataStruct);
}

Android组件化跨进程通信框架Andromeda解析 作者2013年从java开发,转做Android开发,在小厂待过,也去过华为,OPPO等大厂待过,18年四月份进了阿里一直到现在。参与过不少面试,也当面试官 面试过很多人。深知大多数初中级Android工程师,想要提升技能,往往是自己摸索成长,不成体系的学习效果低效漫长,而且极易碰到天花板技术停滞不前!我整理了一份阿里P7级别的最系统的Android开发主流技术,特别适合有3-5年以上经验的小伙伴深入学习提升。主要包括阿里,以及字节跳动,腾讯,华为,小米,等一线互联网公司主流架构技术。 阅读详情

相关推荐

API Hook完全手册

注:本文是根据我两年前写的一个系统行为监测程序写成(参考了一些书籍和文章)。最近在论坛上看到有不少人在问关于API Hook的问题,便写成此文,希望能对朋友们在写API Hook代码的时候能够有所帮助。1 基本原理API Hook是什么我就不多说了,直接进入正题。API Hook技术主要有下面的技术难点:1.     如何将自己的的代码Inject到其他进程2.     如何Hoo

张羿的优快云专栏 1万+

Android Framework跨进程通信IPC之6——Binder框架

Android 从下而上分了内核层、硬件抽象层、系统服务层、Binder IPC 层、应用程序框架层Android 中"应用程序框架层"以 SDK 的形式开放给开发者使用,“系统服务层” 中的核心服务随系统启动而运行,通过应用层序框架层提供的 Manager 实时为应用程序提供服务调用。系统服务层中每一个服务运行在自己独立的进程空间中,应用程序框架层中的 Manager 通过 Binder IPC 的方式调用系统服务层中的服务。简单的说,Binder是Android平台上的一种跨进程通信技术。

Gaga246的博客 5448

HOOK其他进程API和全局HOOK-API

HOOK是一种WINDOWS下存在很久的技术了。 HOOK一般分两种1。HOOK MESSAGE  2。HOOK API 本问讨论的是HOOK API。(如果你是HOOK高手就不要看了) 在最初学HOOK-API的时候通常都是通过覆盖地址和修改IAT的方法。通过这两种技术,我们基本都可以实现对本进程的API函数进行HOOK了。但是在高兴之余会有点遗憾,怎么才能HOOK其他进程的API函数呢?怎么才

4292

Android跨进程通信二

1. Binder是什么 从机制上来说: Binder是Android中一种跨进程通信的方式 从Binder机制实现来说: Binder是一种虚拟的物理设备驱动 从Java层面来说: Binder是一个类,实现了IBinder接口。 2. Binder机制 2.1 背景知识 2.1.1 进程空间划分 一个进程空间分为 用户空间 & 内核空间(Kernel),即把进程内 用户 & 内...

吓成一坨兔子 3123

Android-安卓跨进程跨app通信框架

本框架实现了安卓跨进程跨app通信的方便调用和封装

Materials Studio实战技巧(八)——利用片段库高效构建有机金属配合物

本文详细介绍了在Materials Studio中利用Fragment Browser(片段浏览器)高效构建有机金属配合物的实战技巧。通过调用预置的Metal Templates和Ligands库,用户可以快速搭建如茂金属催化剂等复杂分子骨架,并利用对称性操作精确控制Meso与Rac构型。文章还涵盖了自定义片段库的创建与使用,能显著提升建模效率,适用于计算化学与材料模拟领域的研究者。

wine的专栏 936

跨进程API Hook

<!--pre.prog { font-family: "Courier New", "Courier", "Tahoma"; font-size: 10.8pt; background-color: #EEEEEE;}h1 { font-family: "隶书", "Georgia"; font-size: 200%; background-color:

7357

基于灰度信息的图像配准方法

图像配准方法分为三种:基于灰度信息方法、基于变换域方法和基于特征方法。基于灰度信息方法是直接根据图像或图像块灰度信息进行像素上的对齐,该方法主要思想是直接最小化图像信息差异。然而,图像信息差异的判断准则衍生出不同的配准方法。常见的基于灰度信息的图像配准方法包括互相关法、互信息法和误差平方和法。本节将分别对互相关法、互信息法和误差平方和算法展开介绍,并通过MATLAB进行演示。基于灰度的图像方法容易实现,但同样存在不足。该方法对成像条件(光照,遮挡)、图像形变旋转以及噪声极其敏感,同时具有较高的计算复杂度。

weixin_42068054的博客 3776

跨进程hook api的资料

 什么是“跨进程   API   Hook”?     众所周知Windows应用程序的各种系统功能是通过调用API函数来实现。API   Hook就是给系统的API附加上一段小程序,它能监视甚至控       制应用程序对API函数的调用。所谓跨进程也就是让自己的程序来控制别人程序的API调用了。         API   Hook   理论     通过对Win32   PE文件的分析(如果

yaozhu88的专栏 1544

CANoe开发入门到精通读书笔记(2)——CAPL编程

CAPL函数的语法跟C语言很类似,但也包含一些C语言所不具备的功能大部分CAPL支持的数据类型都可以直接声明为函数参数, 例如,整型、浮点型、枚举、结构、定时器以及它们的引用。但有一些类型不能被直接声明,而需要加上*号(注意该符号并不是C语言中 指针的意思)。例 如 : signal * s 、 envvarInt * ev 、 sysvarFloat * sv 、 diagRequest * dr、diagResponse * dr、int matrix[][],以及。

weixin_66275497的博客 6459

基于 Python 的 LIF 模型:探索神经元同步与小世界网络

首先,我们定义了一个LIFNeuronself.soma.L = self.soma.diam = 12.6157 # 固定神经元形状self.cm = cm # 电容self.V_rest = V_rest # 静息电位self.V = V_rest # 初始化膜电位在初始化过程中,每个神经元被赋予初始的静息电位,并且其膜电位会在后续的模拟过程中动态变化。

qq_36224726的博客 1424

跨进程 API Hook

跨进程API Hook(1)  跨进程API Hook作者:detrox 什么是“跨进程 API Hook”?众所周知Windows应用程序的各种系统功能是通过调用API函数来实现。API Hook就是给系统的API附加上一段小程序,它能监视甚至控制应用程序对API函数的调用。所谓跨进程也就是让自己的程序来控制别人程序的API调用了。API Hook 理论通过对Win32 PE文件

Delphizhou 的专栏 1272

Python实战:5分钟搞定固定翼无人机三自由度仿真(附完整代码)

本文提供了一个基于Python的固定翼无人机三自由度仿真快速实现方案。通过面向对象设计,封装了核心运动学模型,并对比了欧拉法与龙格-库塔法两种积分方法。文章附有完整实例代码,帮助算法工程师和研究人员在5分钟内搭建轻量级仿真环境,用于快速验证控制律、路径规划等核心算法逻辑。

docker7nomad的博客 749

跨进程API HOOK

什么是“跨进程 API Hook”?众所周知Windows应用程序的各种系统功能是通过调用API函数来实现。API Hook就是给系统的API附加上一段小程序,它能监视甚至控制应用程序对API函数的调用。所谓跨进程也就是让自己的程序来控制别人程序的API调用了。API Hook 理论通过对Win32 PE文件的分析(如果你还不熟悉PE文件格式,可以看看Iczelion的PE教程或者LU

zy122的专栏 731

Python+Vue计算机毕业设计风电场监控系统的设计与实现7bd6i(程序+LW+源码+部署)

Python3.7.7+Django+Mysql5.7+pip list+HBuilderX(Vscode也行)+Vue+Pychram社区版。2. 前端:vue+css+javascript+jQuery+easyUI+highcharts。2.IDE环境:Python和Python 社区版都可以。Django + Vue +Python+Mysql 等等组成,B/S模式等等。该项目含有源码、文档、程序、数据库、配套开发软件、软件安装教程。1. 后端:Python、Vue、Django、Mysql。

华信计算机毕设源码程序 341

【人工智能的数学基础】最优传输(Optimal Transport)问题与Wasserstein距离

代表两堆石子,则问题等价于如何移动一堆石子,通过最小的累积移动距离把它堆成另外一堆石子。的不同位置分配的值。为离散型概率分布,则该优化问题可以表示为矩阵形式。表示寻找总运输成本最小的方案。放大后的上确界不会小于原来的上确界,因此不妨取。具有相同的概率分布。是概率分布,因此积分可以写作采样形式。描述了一种可行的运输方案,表示应该从位置。则最优传输问题的目标函数可以用列向量。对于一个线性规划问题,总可以写出其。的所有可能联合分布的集合,下确界。其中矩阵的每一行代表概率分布。表示寻找使得目标函数最大的。

AI天才研究院 6328

跨进程 API hook

跨进程API Hook(初稿) 什么是“跨进程 API Hook”? 众所周知Windows应用程序的各种系统功能是通过调用API函数来实现。API Hook就是给系统的API附加上一段小程序,它能监视甚至控制应用程序对API函数的调用。所谓跨进程也就是让自己的程序来控制

1783

机器学习笔记 - 基于深度学习(HomographyNet)的图像单应性估计

论文提出了一个深度卷积神经网络来估计一对图像之间的相对单应性。我们的前馈网络有10层,以两个堆叠的灰度图像作为输入,并产生一个8自由度的单应性,可用于映射从第一个图像到第二个图像的像素。我们为HomographyNet提出了两种卷积神经网络架构一个直接估计实值单应性参数的回归网络,以及一个在量化单应性上产生分布的分类网络。我们使用4点单应性参数化,将一个图像的四个角映射到第二个图像。我们的网络使用扭曲的MS-COCO图像以端到端的方式进行训练。论文的贡献如下。........................

学以致用 知行合一 3083

[转]跨进程API Hook(初稿)

[转-收藏]跨进程API Hook(初稿)作者: detrox什么是“跨进程 API Hook”?众所周知Windows应用程序的各种系统功能是通过调用API函数来实现。API Hook就是给系统的API附加上一段小程序,它能监视甚至控制应用程序对API函数的调用。所谓跨进程也就是让自己的程序来控制别人程序的API调用了。API Hook 理论通过对Win32 PE文件的分析(如果你还不熟悉PE文

梦的归宿 3289

锂电池充电器电路设计 里面有电路

锂电池充电器电路设计 里面有电路 3.7V升压到5V电路设计。

计算机控制技术及应用

《计算机控制技术及应用》通过总结作者的科研成果和吸收国内外的先进理论、方法和技术,以计算机控制系统的网络化、开放化、智能化和集成化发展趋势为主线索,主要介绍计算机控制系统的基本概念和计算机控制系统的典型形式,计算机控制系统的数学描述与基本分析方法,以及数字PID控制器、最少拍控制系统、达林算法、状态变量设计法等设计方法及其各种改进算法。重点突出计算机控制系统输入输出通道接口的设计方法和典型控制系统的设计案例与实现方法等。

android 跨进程hook,偶然间想到一种无需DLL跨进程HOOK的方法

这个方法的原理就是:①首先在API头部写一个CC断点②中断的时候程序收到消息,并且读出参数需要注意的地方只有修正EIP寄存器,和读取参数的问题。因为API采用STDCALL的方式调用,所以参数是从右向左压进堆栈的。还有就是API的开头是mov edi,edipush ebpmov ebp,esp我们要注意,因为我们是在头部断下的,还没有执行push ebp,而执行了push ebp之后esp会减4...

weixin_28865303的博客 713

Data-Structures-and-Algorithm-Analysis-in-Java.pdf

作者: Weiss, Mark A. 出版社: Addison Wesley Data Structures and Algorithm Analysis in Java is an “advanced algorithms” book that fits between traditional CS2 and Algorithms Analysis courses. In the old ACM Curriculum Guidelines, this course was known as CS7. This text is for readers who want to learn good programming and algorithm analysis skills simultaneously so that they can develop such programs with the maximum amo...

Hook技术之API拦截(API Hook)

Inline Hook就是修改

bobopeng 3万+

LinePhone3.2.7(android端最新版本) sip客户端

SIP客户端For android:LinePhone3.2.7(最新版本),官网地址:http://www.linphone.org/

hook api 保护进程

hook api 保护进程 用过卡巴斯基的朋友都知道,卡巴斯基的进程是无法杀掉的,在任务管理器中杀卡巴进程的话,会弹出一个消息框提示拒绝访问!那么这是怎么实现的呢?很简单,就是使用了HOOK API的方法。兄弟门,做毒别做地根武汉那小子那么傻,有本事你也得搁着点,别去进局子!我用delphi来写程序好了,先写个dll。const  PRG_NAME = ddos.exe

Delphizhou 的专栏 4219
上一篇: Windows下Hook API技术
下一篇: API Hook完全手册
killcpp
博客等级 码龄20年 13粉丝 16原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值