深入IAT HOOK

在上一篇文章手动打造一个弹窗程序中,我们自己手写了一份导入表,在调用函数的时候,我们CALL的是导入地址表的一个地址,为什么要调用这里,而且在构造导入表的时候,导入名称表(INT)和导入地址表(IAT)里面装的内容是一样的,程序又是怎么去调用的,在这篇文章中就来分析一下。

注:以下操作是在 XP 上实现的,其他版本注意写保护机制

目录

0x00 IAT表的填写

0x01 IAT HOOK的原理

0x02 实现代码

0x00 IAT表的填写

在上一篇文章中,我们构造导入表的时候,将 IAT 表和 INT 表都指向的是函数名称所在的位置,然后在运行的时候,IAT 表中的内容会被替换成对应函数的地址,在调用的时候使用间接 CALL ,来调用其中所储存的地址。

下面先来验证一下,函数调用的地址是 0x4010D8 ,在 OD 中进行查看

深入IAT HOOK_第1张图片

可以发现,已经正确的填写了,那么操作系统又是根据什么来填写的

深入IAT HOOK_第2张图片

首先操作系统会通过 Name 字段找到当前导入表的名字,然后调用 LoadLibrary 得到句柄,如果没有找到的话会提示找不到 dll 文件,报错情况如下

深入IAT HOOK_第3张图片

接着会根据 OriginalFirstThunk 找名字,OriginalFirstThunk 所指向的也就是咱们前面所说的 INT 表,通过 INT 表中的 RVA 地址来找函数的名字。

当然,这里也不一定存储的就是名字,也可能是导出序号,如果是导出需要的话,就会直接调用 GetProcAddress 得到函数地址,然后填写到 FirstThunk 所指向的对应位置,也就是 IAT 表中的对应位置。

如果存储的是名字的话,也就是指向了_IMAGE_IMPORT_BY_NAME结构,跳过第一个的Hint字节,就得到了调用函数的名字,接着还是调用 GetProcAddress 得到函数地址,填写到对应的位置。

如果 GetProcAddress 函数没有找到的话,会报如下的错误

深入IAT HOOK_第4张图片

前面一直在说,填写到对应的位置,是在说 INT 表和 IAT 表的对应关系,在 INT 表中处于第一项,对应的填写到 IAT 表的时候,也是填写到第一项的位置。

0x01 IAT HOOK的实现

既然我们明白了 IAT 表的填写方法,那么就可以通过更改 IAT 表中对应的地址,来达到我们执行自己但是代码的目的,同时为了保证原函数的功能不受影响,还需要进行一些其他的处理。

为了能更好的理解原理,我们采用在函数内部 HOOK 自己的方式来进行,如果想要 HOOK 其他程序的话,可以通过注入等方式来进行。

我们使用MessageBox函数来举例,为了保证原来的函数不受影响,我们先将函数的地址保存下来,获得函数地址的方法就是前面所描述的过程

OldAddr = (int)GetProcAddress(LoadLibrary("user32.dll"), "MessageBoxA");

接下来就需要找到MessageBox函数所对应在IAT表中的位置,因为已经得到了函数的地址,那么我们就可以通过对比 IAT 表中所指向的地址与当前地址是否一致,来判断是不是我们想要找的函数。

在对比之前,首先要做的就是找到导入表所在的位置,这个就比较简单了,需要注意的是 Optional 头的大小不是固定的,需要根据 File 头中的SizeOfOptionalHeader来确定,然后通过数据目录中的第二项,就可以找到导入表所在的位置了

ImageBase = (DWORD)GetModuleHandle(NULL);

在获取句柄以后,把它赋值给了ImageBase变量,是因为所谓的句柄,实际上就是当前进程的起始位置,也就是在不考虑没有抢占到建议装载地址时候的基址。

在找到导入表之后,就需要进行遍历和对比了,因为导入表是依靠一个全零的结构来判断结束的,所以我们就采取对比FirstThunk和OriginalFirstThunk都为0时,来判断结束,然后在其中判断函数地址是否与我们所得到的原函数地址一致,如果一致说明找到了。


  while (pImportDirectory->FirstThunk != 0 && pImportDirectory->OriginalFirstThunk != 0)
  {
    pFunAddr = (PDWORD)(ImageBase + pImportDirectory->FirstThunk);
    while (*pFunAddr)
    {
      if (OldAddr == *pFunAddr)
      {
        *pFunAddr = NewAddr;
        break;
      }
      pFunAddr++;
    }
    pImportDirectory++;
  }

接下来,还需要处理的就是我们 HOOK 以后需要进行的操作,为了保证程序的稳定,我们需要构造与被 HOOK 的函数一样结构的函数,同时为了保证原函数功能的正常运行,再定义一个函数指针,在自己的功能执行完成后,调用原来程序正常的功能。


int WINAPI NewMessageBox
(
  HWND   hWnd,
  LPCSTR lpText,
  LPCSTR lpCaption,
  UINT   uType
)
{
  typedef int (WINAPI* BOX)(HWND hWnd,LPCSTR lpText,LPCSTR lpCaption,UINT uType);
  ((BOX)OldAddr)(0, "IAT Hook", "New", 0);
  return 0;
}

到这里也就大功告成了,我们来进行一次测试

HOOK 前

深入IAT HOOK_第5张图片

HOOK 后

深入IAT HOOK_第6张图片

0x02 实现代码


#include 
#include 

int OldAddr;

int SetIATHook(int OldAddr, int NewAddr)
{
  DWORD ImageBase = 0;
  PDWORD pFunAddr = 0;
  PIMAGE_DOS_HEADER pDosHeader = NULL;
  PIMAGE_NT_HEADERS pNtHeader = NULL;
  PIMAGE_FILE_HEADER pFileHeader = NULL;
  PIMAGE_OPTIONAL_HEADER pOptionalHeader = NULL;
  PIMAGE_DATA_DIRECTORY pDataDirectory = NULL;
  PIMAGE_IMPORT_DESCRIPTOR pImportDirectory = NULL;

  ImageBase = (DWORD)GetModuleHandle(NULL);
  pDosHeader = (PIMAGE_DOS_HEADER)ImageBase;
  pNtHeader = (PIMAGE_NT_HEADERS)(ImageBase + pDosHeader->e_lfanew);
  pFileHeader = (PIMAGE_FILE_HEADER)((DWORD)pNtHeader + 4);
  pOptionalHeader = (PIMAGE_OPTIONAL_HEADER)((DWORD)pFileHeader + sizeof(_IMAGE_FILE_HEADER));
  pDataDirectory = pOptionalHeader->DataDirectory;
  pImportDirectory = (PIMAGE_IMPORT_DESCRIPTOR)(ImageBase + (pDataDirectory + 1)->VirtualAddress);

  while (pImportDirectory->FirstThunk != 0 && pImportDirectory->OriginalFirstThunk != 0)
  {
    pFunAddr = (PDWORD)(ImageBase + pImportDirectory->FirstThunk);
    while (*pFunAddr)
    {
      if (OldAddr == *pFunAddr)
      {
        *pFunAddr = NewAddr;
        break;
      }
      pFunAddr++;
    }
    pImportDirectory++;
  }

  return 0;
}

int WINAPI NewMessageBox
(
  HWND   hWnd,
  LPCSTR lpText,
  LPCSTR lpCaption,
  UINT   uType
)
{
  typedef int (WINAPI* BOX)(HWND hWnd,LPCSTR lpText,LPCSTR lpCaption,UINT uType);
  ((BOX)OldAddr)(0, "IAT Hook", "New", 0);
  return 0;
}

int main(int argc, char* argv[])
{
  OldAddr = (int)GetProcAddress(LoadLibrary("user32.dll"), "MessageBoxA");
  MessageBox(0, "test", "Old", 0);
  SetIATHook(OldAddr, (int)NewMessageBox);
  MessageBox(0, "test", "Old", 0);
  return 0;
}

理解原理,方可变化

深入IAT HOOK_第7张图片

公众号:信安本原(sec-source)

你可能感兴趣的:(信息安全,安全)