设为首页 加入收藏

TOP

4.3 IAT Hook 挂钩技术(二)
2023-09-23 15:44:03 】 浏览:646
Tags:4.3 IAT Hook

接着我们继续来分析IMAGE_IMPORT_DESCRIPTOR 导入结构中的Name字段,其对应的是第一张图中的红色部分0001A54A将该偏移与基址00400000相加后直接定位过去,可以看到0041A54A对应的字符串正是USER32.dll动态链接库,而后面会有两个00标志着字符串的结束。

最后我们来分析IMAGE_IMPORT_DESCRIPTOR中最复杂的一个字段OriginalFirstThunk 为什么说它复杂呢?是因为他的内部并不是一个数值而是嵌套了另一个结构体 IMAGE_THUNK_DATA ,我们先来看一下微软对该结构的定义:

typedef struct _IMAGE_THUNK_DATA32
{
    union
    {
        DWORD ForwarderString;        // PBYTE 
        DWORD Function;               // PDWORD
        DWORD Ordinal;                // 序号
        DWORD AddressOfData;          // 指向 PIMAGE_IMPORT_BY_NAME
    } u1;
} IMAGE_THUNK_DATA32;
typedef IMAGE_THUNK_DATA32 * PIMAGE_THUNK_DATA32;

接着来找到OriginalFirstThunk字段在内存中的位置,由第一张图可知,图中的标红部分第一个四字节0001A38C 就是它。我们加上基址00400000然后直接怼过去,并结合上方的结构定义研究一下;

该结构中我们需要关注AddressOfData结构成员,该成员中的数据最高位(红色)如果为1(去掉1)说明是函数的导出序号,而如果最高位为0则说明是一个指向IMAGE_IMPROT_BY_NAME结构(导入表)的RVA(蓝色)地址,此处因为我们找的是导入表所以最高位全部为零。

我们以上图中的第一个RVA地址0001A53E与基址相加,来看下该AddressOfData字段中所指向的内容是什么。

上图黄色部分是编译器生成的,而蓝色部分则为LoadIconW字符串与FirstThunk中的0041A15C地址指针是相互对应的,而最后面的00则表明字符串的结束,对比以下结构声明就很好理解了。

typedef struct _IMAGE_IMPORT_BY_NAME
{
    WORD    Hint;           // 编译器生成的
    CHAR   Name[1];         // 函数名称,以0结尾的字符串
} IMAGE_IMPORT_BY_NAME, *PIMAGE_IMPORT_BY_NAME;

为了能更加充分的理解,笔者为大家用Excel画了一张图,如下所示:

如上图IMAGE_IMPORT_DESCRIPTO导入表结构中的FirstThunkOriginalFirstThunk分别指向两个相同的IMAGE_THUNK_DATA结构,其中内存INT(Improt Name Table)表中存储的就是导入函数的名称,而IAT(Improt Address Table)表中存放的是导入函数的地址,他们都共同指向IMAGE_IMPORT_BY_NAME结构,而之所以使用两份IMAGE_THUNK_DATA结构,是为了最后还可以留下一份备份数据用来反过来查询地址所对应的导入函数名,看了这张图再结合上面的实验相信你已经理解了;

实现导入表劫持

在之前的内容中我们已经分析了导入表结构,接着我们将实现对导入表的劫持功能,我们需要使用IAT Hook就必须要首先找到导入表中特定的函数地址,首先我们先实现枚举定位功能,通过枚举程序中的IMAGE_IMPORT_DESCRIPTOR结构在其中找到对应的导入模块user32.dll并在该模块内寻找对应的函数名MessageBox,通过使用双层循环即可实现对特定导入函数的枚举,如下是一段枚举导入表函数的功能;

#include <iostream>
#include <Windows.h>
#include <Dbghelp.h>

#pragma comment (lib, "Dbghelp")

int main(int argc, char* argv[])
{
  // 打开文件
  HANDLE hFile = CreateFile("d://lyshark.exe", GENERIC_READ, FILE_SHARE_READ,
    NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);

  // 创建内存映射
  HANDLE hMap = CreateFileMapping(hFile, NULL, PAGE_READONLY | SEC_IMAGE, 0, 0, 0);
  LPVOID lpBase = MapViewOfFile(hMap, FILE_MAP_READ, 0, 0, 0);
  
  // 得到DOS头部
  PIMAGE_DOS_HEADER pDosHdr = (PIMAGE_DOS_HEADER)lpBase;
  if (pDosHdr->e_magic != IMAGE_DOS_SIGNATURE)
  {
    UnmapViewOfFile(lpBase);
    return -1;
  }

  // 得到NT头部
  PIMAGE_NT_HEADERS pNtHdr = (PIMAGE_NT_HEADERS)((DWORD)lpBase + pDosHdr->e_lfanew);
  if (pNtHdr->Signature != IMAGE_NT_SIGNATURE)
  {
    return -1;
  }

  DWORD dwNum = 0;

  // 数据目录表
  PIMAGE_IMPORT_DESCRIPTOR pImpDes = (PIMAGE_IMPORT_DESCRIPTOR)
    ImageDirectoryEntryToData(lpBase, TRUE, IMAGE_DIRECTORY_ENTRY_IMPORT, &dwNum);

  PIMAGE_IMPORT_DESCRIPTOR pTmpImpDes = pImpDes;

  // 枚举导入表
  while (pTmpImpDes->Name)
  {
    printf("[*] 链接库名称: %s \n", (DWORD)lpBase + (DWORD)pTmpImpDes->Name);
    PIMAGE_THUNK_DATA thunk = (PIMAGE_THUNK_DATA)(pTmpImpDes->FirstThunk + (DWORD)lpBase);

    int index = 0;
    while (thunk->u1.Function)
    {
      if (thunk->u1.Ordinal & IMAGE_ORDINAL_FLAG)
      {
        printf("导入序号: %08X \r\n", thunk->u1.Ordinal & 0xFFFF);
      }
      else
      {
        PIMAGE_IMPORT_BY_NAME pImName = (PIMAGE_IMPORT_BY_NAME)thunk->u1.Function;
        printf("函数名称: %-30s \t", (DWORD)lpBase + pImName->
首页 上一页 1 2 3 4 下一页 尾页 2/4/4
】【打印繁体】【投稿】【收藏】 【推荐】【举报】【评论】 【关闭】 【返回顶部
上一篇使用Vulkan-Loader将ncnn代码改成.. 下一篇C++ 学习笔记、01 | 开发简单职工..

最新文章

热门文章

Hot 文章

Python

C 语言

C++基础

大数据基础

linux编程基础

C/C++面试题目