Windows DLL注入和API拦截简介

1.函数

LONG_PTR WINAPI SetWindowLongPtr(
  __in  HWND hWnd,
  __in  int nIndex,
  __in  LONG_PTR dwNewLong
);
函数介绍参见http://msdn.microsoft.com/en-us/library/ms644898(v=VS.85).aspx。可以调用SetWindowLongPtr(hWnd,GWLP_WNDPROC,MySubclassProc),告诉系统所有发到hWnd窗口的消息,应该由MySubclassProc来处理,而不是由该窗口的标准窗口过程来处理。也就是当系统需要向指定窗口的WndProc发送消息的时候,会先查看窗口过程的地址,然后通过该地址来调用WndProc。

问题:请看下面代码,函数过程在进程B中实现。

void SomeFunc(void){
HWND hWnd = FindWindow(TEXT("Class-A"),NULL);
SetWindowLongPtr(hWnd,GWLP_WNDPROC,MySubclassProc);
}
LRESULT MySubclassProc(HWND hWnd,UINT Msg,...){}

这里B调用了SetWindowLongPtr,试图改变A进程对应窗口的WndProc地址,结果就是返回NULL。因为SetWindowLongPtr中的代码会检查一个进程试图修改的WndProc的地址是否属于另一个进程创建的窗口,如果是这种情况的话,函数会直接忽略该调用。

2.函数
HHOOK WINAPI SetWindowsHookEx(
  __in  int idHook,
  __in  HOOKPROC lpfn,
  __in  HINSTANCE hMod,
  __in  DWORD dwThreadId
);
函数介绍参见http://msdn.microsoft.com/en-us/library/ms644990(VS.85).aspx。调用SetWindowsHookEx(WH_GETMESSAGE,GetMsgProc,hInstDll,0),WH_GETMESSAGE表示监控发送到消息队列中的消息,GetMsgProc挂钩的处理函数,如果dwThreadId参数是零或指定不同进程创建的一个线程标识符lpfn参数必须指向DLL中的钩子函数否则lpfn指向一个当前进程相关钩子函数。hInstDll为包含GetMsgProc过程的DLL,其值是进程地址空间中的DLL被映射到的虚拟内存地址。

 3.函数
HANDLE WINAPI CreateRemoteThread(
  __in   HANDLE hProcess,
  __in   LPSECURITY_ATTRIBUTES lpThreadAttributes,
  __in   SIZE_T dwStackSize,
  __in   LPTHREAD_START_ROUTINE lpStartAddress,
  __in   LPVOID lpParameter,
  __in   DWORD dwCreationFlags,
  __out  LPDWORD lpThreadId
);

函数介绍参见 http://msdn.microsoft.com/en-us/library/ms682437(VS.85).aspx 。参数pfnStartAddr是线程函数的在远程进程的内存地址。要执行DLL注入,让远程线程载入我们的DLL,需要让该线程调用LoadLibrary函数。因此我们只需要执行如下代码:
HANDLE hThread = CreateRemoteThread(hProcessRemote,NULL,0,LoadLibraryW,L"C:\\MyLib.dll",0,NULL);
如果我们想要使用ANSI版本的,那么代码将是
HANDLE hThread = CreateRemoteThread(hProcessRemote,NULL,0,LoadLibraryA,"C:\\MyLib.dll",0,NULL)。
这里有两个问题:
1.直接把LoadLibraryW或LoadLibraryA作为第4个参数传给CreateRemoteThread,原因不是那么明显。 在编译和链接一个程序的时候,生成的二进制文件中会包含一个导入段,这个段由一系列转换函数构成,这些转换函数用来跳转到导入的函数。因此在调用CreateRemoteThread的时候直接引用LoadLibraryW,该引用会被解析为我们模块的导入段中的LoadLibraryW转换函数的地址,这就可能导致访问违规。为了强制调用LoadLibraryW函数,我们必须通过调用GetProcAddress来得到LoadLibraryW的确切地址。由于每个 应用程序都需要Kernel32.dll,而系统每个进程都会将Kernel32.dll映射到同一个地址,所以必须如下调用CreateRemoteThread:
// Get the real address of LoadLibraryW in Kernel32.dll.
PTHREAD_START_ROUTINE pfnThreadRtn = (PTHREAD_START_ROUTINE)
   GetProcAddress(GetModuleHandle(TEXT("Kernel32")), "LoadLibraryW");

HANDLE hThread = CreateRemoteThread(hProcessRemote, NULL, 0,
   pfnThreadRtn, L"C:\\MyLib.dll", 0, NULL);

如果在ANSI下,对应函数调用如下:

// Get the real address of LoadLibraryA in Kernel32.dll.
PTHREAD_START_ROUTINE pfnThreadRtn = (PTHREAD_START_ROUTINE)
   GetProcAddress(GetModuleHandle(TEXT("Kernel32")), "LoadLibraryA");

HANDLE hThread = CreateRemoteThread(hProcessRemote, NULL, 0,
   pfnThreadRtn, "C:\\MyLib.dll", 0, NULL);
2.DLL路径字符并不在远程调用进程内存中。  字符串"C:\\MyLib.dll "位于调用进程的地址空间中,我们把这个地址传给新创建的远程线程,远程线程再把它传给LoadLibraryW,但当LoadLibraryW去访问这个内存地址的时候,DLL的路径字符串并不在那里,远程进程的线程很可能发生访问违规,导致远程进程终止。为了解决这个问题,我们需要把DLL的路径字符串存放到远程进程的地址空间中。Windows为我们提供了VirtualAllocEx函数,它可以让一个进程在另一个进程的地址空间中分配一块内存。
LPVOID WINAPI VirtualAllocEx(
  __in      HANDLE hProcess,
  __in_opt  LPVOID lpAddress,
  __in      SIZE_T dwSize,
  __in      DWORD flAllocationType,
  __in      DWORD flProtect
);

释放该内存的函数为:

BOOL WINAPI VirtualFreeEx(
  __in  HANDLE hProcess,
  __in  LPVOID lpAddress,
  __in  SIZE_T dwSize,
  __in  DWORD dwFreeType
);

一旦为字符串分配了一块内存,我们就需要将字符串从本地进程的地址空间复制到远程进程的地址空间中去。Windows提供了一些函数,可以让一个进程对另一个进程的地址空间进行读写:
BOOL WINAPI ReadProcessMemory(
  __in   HANDLE hProcess,
  __in   LPCVOID lpBaseAddress,
  __out  LPVOID lpBuffer,
  __in   SIZE_T nSize,
  __out  SIZE_T *lpNumberOfBytesRead
);
BOOL WINAPI WriteProcessMemory(
  __in   HANDLE hProcess,
  __in   LPVOID lpBaseAddress,
  __in   LPCVOID lpBuffer,
  __in   SIZE_T nSize,
  __out  SIZE_T *lpNumberOfBytesWritten
);
4.修改模块的导入段来拦截API
为了拦截一个特定的函数,我们所需要做的就是要修改它在模块中的导入段的地址。
void CAPIHook::ReplaceIATEntryInOneMod(PCSTR pszCalleeModName,
   PROC pfnCurrent, PROC pfnNew, HMODULE hmodCaller) {

   // Get the address of the module's import section
   ULONG ulSize;

   // An exception was triggered by Explorer (when browsing the content of
   // a folder) into imagehlp.dll. It looks like one module was unloaded...
   // Maybe some threading problem: the list of modules from Toolhelp might
   // not be accurate if FreeLibrary is called during the enumeration.
   PIMAGE_IMPORT_DESCRIPTOR pImportDesc = NULL;
   __try {
      pImportDesc = (PIMAGE_IMPORT_DESCRIPTOR) ImageDirectoryEntryToData(
         hmodCaller, TRUE, IMAGE_DIRECTORY_ENTRY_IMPORT, &ulSize);
   }
   __except (InvalidReadExceptionFilter(GetExceptionInformation())) {
      // Nothing to do in here, thread continues to run normally
      // with NULL for pImportDesc
   }

   if (pImportDesc == NULL)
      return; // This module has no import section or is no longer loaded

   // Find the import descriptor containing references to callee's functions
   for (; pImportDesc->Name; pImportDesc++) {
      PSTR pszModName = (PSTR) ((PBYTE) hmodCaller + pImportDesc->Name);
      if (lstrcmpiA(pszModName, pszCalleeModName) == 0) {

         // Get caller's import address table (IAT) for the callee's functions
         PIMAGE_THUNK_DATA pThunk = (PIMAGE_THUNK_DATA)
            ((PBYTE) hmodCaller + pImportDesc->FirstThunk);

         // Replace current function address with new function address
         for (; pThunk->u1.Function; pThunk++) {

            // Get the address of the function address
            PROC* ppfn = (PROC*) &pThunk->u1.Function;

            // Is this the function we're looking for?
            BOOL bFound = (*ppfn == pfnCurrent);
            if (bFound) {
               if (!WriteProcessMemory(GetCurrentProcess(), ppfn, &pfnNew,
                    sizeof(pfnNew), NULL) && (ERROR_NOACCESS == GetLastError())) {
                  DWORD dwOldProtect;
                  if (VirtualProtect(ppfn, sizeof(pfnNew), PAGE_WRITECOPY,
                     &dwOldProtect)) {

                     WriteProcessMemory(GetCurrentProcess(), ppfn, &pfnNew,
                        sizeof(pfnNew), NULL);
                     VirtualProtect(ppfn, sizeof(pfnNew), dwOldProtect,
                        &dwOldProtect);
                  }
               }
               return; // We did it, get out
           }
        }
      } // Each import section is parsed until the right entry is found and patched
   }
}
PCSTR pszCalleeModName:调用模块(含要被替换的函数)。
PROC pfnCurrent:调用模块中被替换的函数。
PROC pfnNew:需要执行的函数。
HMODULE hmodCaller:调用模块。

你可能感兴趣的:(windows,null,dll,import,Descriptor,winapi)