65.9K
CodeProject 正在变化。 阅读更多。
Home

API 挂钩 (LoadLibrary)

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.96/5 (27投票s)

2009年6月29日

CPL

4分钟阅读

viewsIcon

203720

downloadIcon

8415

一种简单的挂钩 LoadLibrary API 的方法,附带示例

引言

如果 Windows 能够防御 API Hooking,那么木马就不会影响我们的系统了。我认为这是微软的一个漏洞。但从程序员的角度来看,这是一种内核级别的 PE(可移植可执行文件)签名修改方法。API Hooking 是在不修改其代码的情况下获取可执行文件所有信息的最卑鄙的方法之一。首先,我提醒你一件事,永远不要将我文章中的任何内容用于不正当目的。

在进行 API Hooking 之前,你需要了解 DLL 注入,这是 API Hooking 的一个主要步骤。在正常情况下,你无法创建一个指向另一个进程的指针。为了实现这一点,我们需要将我们的代码注入到另一个进程中。

让我们来熟悉一下 DLL 注入。DLL 注入是一种技术,通过它可以将我们自己的 DLL 注入到第三方可执行文件中,而无需修改第三方可执行文件的代码。DLL 注入可以通过多种方法实现,它们是:

  1. 通过注册表注入 DLL
  2. 通过 Windows Hook 注入 DLL
  3. 通过远程线程注入 DLL
  4. 通过木马 DLL 注入 DLL
  5. 作为调试器注入 DLL

嗯,对于这篇文章,我们只处理方法 3,即通过远程线程注入 DLL。

背景

在这种技术中,我们必须借助 *Kernel32.dll* 的 `LoadLibrary` API 将我们的 DLL 加载到第三方进程中。将 DLL 加载到另一个进程中的主要困难在于,你无法控制第三方进程的线程。我们有必要在该进程中创建自己的线程,为此 Windows 本身提供了 `WINAPI CreateRemoteThread`,我们可以在其中指定目标进程句柄作为参数。我将在代码部分详细解释这些 API 及其用法。

为了解释 API Hooking 技术,我在本文附加的示例中引入了 3 个组件,它们将解释如何 Hook API。在这里,我正在 Hook API `LoadLibrary`(`LoadLibraryA`)。附加到本文的三个组件是:

  1. APIHandler.exe:这个 EXE 会在 DLLCheck.dll 的帮助下将 DLL 注入到其他应用程序中。
  2. DLLCheck.dll:要注入到第三方应用程序中的 DLL。
  3. SampleUsingLoadLibrary.exe:第三方应用程序,实际上这个应用程序被 Hook 了。

APIHandler.exe 包含 DLL 路径(提供要注入的 DLL 的路径),而 Injected EXE 是我们要将 DLL 注入其中的 EXE。一旦我们的 DLL 被注入,它就会像 *SampleUsingLoadLibrary.exe* 的一部分一样工作。

这个方法有点曲折,一旦 *DLLCheck.dll* 被注入到 EXE 中,*DLLCheck.DLL* 的下一个任务就是找到要 Hook 的函数名(`LoadLibrary`)的地址。这可以通过解析 PE 文件(我们的可执行文件 *SampleUsingLoadLibrary.exe*)并找到可执行文件内部的 *Kernel32.dll* 的 `IMAGE_IMPORT_DESCRIPTOR`(这个数据结构保存了 PE 文件的导入表信息)来实现,该描述符在我们的情况下保存了 `LoadLibrary` 的地址。

一旦我们获得了加载到 EXE 中的 Kernel32 的 `LoadLibrary` API 的地址,下一步就是找到我们本机 API(`MyFunction`)的地址(使用 *DLLCheck.dll* 的 `GetCurrentFunctAddr`,参考代码部分),并使用 `VirtualProtect` WinAPI 替换加载地址,这使得在远程线程中更改函数地址成为可能,而不会出现保护错误。

一旦完成上述步骤,我们的函数将拥有第三方 EXE 中 `LoadLibrary` API 的加载地址,因此,每当该第三方 EXE 中调用 *Kernel32.dll* 的 `LoadLibrary` 函数时,在后台,我们的函数(即我们的 `MyFunction` API)就会被调用,因为 PE 中的地址已经被更改了。这些都是基于修改 EXE 的 PE(可移植可执行文件)签名。我们修改了可执行文件内部加载的 *Kernel32.dll* 的 `IMPORT` 表。这是一个非常简单有效的方法。为了更深入地理解,让我们详细研究代码。

Using the Code

这段代码解释了 API 重定向的各个阶段。

// To Inject a DLL, we need three steps in APIHandler side.
// Allocate memory in the remote process for our library
// with write permission using the below mentioned WINAPI.
HANDLE hProc;
pLibRemote = ::VirtualAllocEx( hProcess,
NULL, sizeof(szLibPath), MEM_COMMIT, PAGE_READWRITE );
// This API writes the library path name to third party process.
 ::WriteProcessMemory( hProcess, pLibRemote, (void*)szLibPath,
                       sizeof( szLibPath ), NULL ); 
// Load the DLL to third party process by creating a thread in that
// process, using the below mentioned WIN API, this API enable
hThread = ::CreateRemoteThread( hProcess, NULL, 0,
      (LPTHREAD_START_ROUTINE)::GetProcAddress( hKernel32,"LoadLibraryA" ),
                    pLibRemote,0,NULL );
// The below mentioned code is from Injected DLL part, i.e. DLLCheck.DLL 
// To redirect LoadLibrary API with our MyFunction we have do certain steps
// We have parse the PE file of third party EXE and find the import table and find
// the address of the Loadlibrary API in reference to the executable base address.
// This function gets the address of the import descriptor table.
IMAGE_IMPORT_DESCRIPTOR* GetImportDescriptor(HMODULE hMod, char* pszDllName )
{
  // Get the DOS Header from which we can get the optional header.
  IMAGE_DOS_HEADER* pDOSHeader = (IMAGE_DOS_HEADER*)hMod;
  IMAGE_OPTIONAL_HEADER* pOptionHeader = (IMAGE_OPTIONAL_HEADER*)((BYTE*)hMod +
                                       pDOSHeader->e_lfanew + 24);
  // To get the IMport descriptor datastructure.
  IMAGE_IMPORT_DESCRIPTOR* pImportDesc = (IMAGE_IMPORT_DESCRIPTOR*)((BYTE*)hMod +
  OptionHeader->DataDirectory[ IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress);
  char* pszImpAddr = 0;
  // Iterate through the IMPORT DESCRIPTOR till we find the address of
  // Kernel32.dll in our case, since LoadLibrary is from Kernel32.dll
  while( pImportDesc->FirstThunk)
  {
   // Get the load address of the DLL.
   pszImpAddr = (char*)(( BYTE* )hMod+ pImportDesc->Name );
   if( stricmp( pszDllName, pszImpAddr ))
   {
    pImportDesc++;
    continue;
   }
   else
   {
    return pImportDesc;
   }
  }
  return NULL;
}
// This function gets the address of the LoadLibraryA function.
DWORD* GetCurrentFunctAddr(  HMODULE hMod,
        IMAGE_THUNK_DATA* pOriginalFirstThunk,
        IMAGE_THUNK_DATA* pFirstThunk,
        char* pszFunctionName )
{
   // From the IMAGE thunk datastructure we
   // get the address of the imported function.
   char* szTest;
   while(pOriginalFirstThunk->u1.Function)
   {
      // Get the import load address of the function,
      // in our case LoadLibraryA.
      szTest = (char*)((BYTE*)hMod +
                    (DWORD)pOriginalFirstThunk->u1.AddressOfData+2);
      if(stricmp(pszFunctionName,szTest)==0)
     {
        return &pFirstThunk->u1.Function;
     }
     pOriginalFirstThunk++;
     pFirstThunk++;
   }
   return NULL;
}
// Once address is found, we have to redirect using the VirtualProtect WinAPI
// that enables write permission to third party EXE.

bool ChangeAddress(DWORD* dwOldAddress,DWORD dwNewAddress)
{
  // Change the old address of the function with the new address.
  // Firstly this address is changed.
  DWORD dwOld;
  if (!(VirtualProtect(dwOldAddress,4,PAGE_READWRITE,&dwOld)))
  {
      return false;
  }
  *dwOldAddress = dwNewAddress;
  // Once changed it updated in the executable.
  if (!(VirtualProtect(dwOldAddress,4,PAGE_EXECUTE,&dwOld)))
  {
      return false;
  }
  else
  {
      OutputDebugString( "Change Address Final.." );
      return true;
  }
}

关注点

API Hooking 是黑客创建木马最广泛使用的技术之一,它可以帮助他们跟踪系统信息,而全球的杀毒软件公司则使用低级 API Hooking 来制作反间谍软件。我写这篇文章的目的是让你熟悉 API Hooking 以及你系统中在你不知情的情况下存在的漏洞。我的文章解释了一个有用的 API Hooking,它可以帮助你跟踪在特定 EXE 中使用 `LoadLibrary` API 加载的库,而这是 Dependency Walker 无法做到的。这是一个 ASCII 版本,只会 Hook `LoadLibraryA`。如果你想 Hook Unicode DLL,那么就 Hook `LoadlbraryW`。

如果你有任何疑问,请随时提问,如果真主给我机会,我会写更多你感兴趣的文章。

历史

  • 版本 1.0
© . All rights reserved.