4.4 EAT Hook 挂钩技术

eat,hook,挂钩,技术 · 浏览次数 : 27

小编点评

**EAT(Export Address Table)**是一种用于修改动态链接库(DLL)中导出函数的调用。 **关键步骤:** 1. 获取目标 DLL 的基址。 2. 遍历导出表,找到与目标函数名称或序号匹配的导出函数地址。 3. 将目标函数的地址保存到导出表中。 4. 修改导出函数地址,将新的导出函数地址写入导出表。 5. 实现自定义函数,该函数会在被钩子函数被调用时执行。 **与IAT (Import Address Table) 的区别:** * EAT 存储导出函数地址的偏移,而 IAT 存储导入函数地址。 * EAT 可以劫持导出函数的调用,而 IAT 是在应用程序运行时进行劫持。 **导出表劫持的详细流程:** 1. 获取 DOS 头和 NT 头。 2. 从数据目录表中获取导出表地址。 3. 循环查找导出表,提取函数地址。 4. 设置导出函数地址的读写属性。 5. 写入替换的导出函数地址。 **注意:** * EAT 的获取需要权限,通常需要使用管理员权限。 * EAT 只能劫持导出函数的调用,无法劫持导入函数的调用。

正文

EAT(Export Address Table)用于修改动态链接库(DLL)中导出函数的调用。与IAT Hook不同,EAT Hook是在DLL自身中进行钩子操作,而不是修改应用程序的导入表。它的原理是通过修改DLL的导出函数地址,将原本要导出的函数指向另一个自定义的函数。这样,在应用程序调用DLL的导出函数时,实际上会执行自定义的函数。

EAT Hook的步骤通常包括以下几个步骤:

  • 获取目标DLL的基址:通过模块加载和遍历PE文件的导出表,找到目标DLL的基址。
  • 定位导出函数:根据导出函数的名称或序号,在导出表中找到目标函数的位置。
  • 保存原始函数地址:将目标函数的地址保存下来,以便后续恢复。
  • 修改导出函数地址:将目标函数在导出表中对应的地址修改为自定义函数的地址。
  • 实现自定义函数:编写自定义的函数,该函数会在被钩子函数被调用时执行。
  • 调用原始函数:在自定义函数中,可以选择是否调用原始的被钩子函数。

与IAT不同是EAT存放的不是函数地址,而是导出函数地址的偏移,使用时需要加上指定Dll的模块基地址,当Hook挂钩之后,所有试图通过导出表获取函数地址的行为都会受到影响,EATHook并不会直接生效,它只能影响Hook之后对该函数地址的获取。

实现导出表劫持的详细流程如下所示:

  • 首先获取到DOS头,并加上偏移得到NT头,再通过Nt头得到数据目录表基地址。
  • 数据目录表DataDirectory中的第0个成员指向导出表的首地址,直接拿到导出表的虚拟地址。
  • 循环查找导出表的导出函数是否与我们的函数名称一致,一致则取出导出函数地址。
  • 设置导出函数位置读写属性,将新的导出函数地址写入到该位置

根据上述流程,读者可以很容易的写出导出表劫持代码,如下所示则是完整的代码片段,当运行EATHook时,进程内如果再次获取MessageBox函数的内存地址时则会返回MyMessageBox自定函数地址,此时功能将被替换。

#include <windows.h>
#include <cstdio>
#include <tchar.h>

// 导出表劫持
BOOL EATHook(LPCTSTR szDllName, LPCTSTR szFunName, LPVOID NewFun)
{
  DWORD addr = 0, index = 0, dwProtect = 0;

  // 装入模块
  HMODULE DllBase = LoadLibrary(szDllName);
  if (NULL == DllBase)
  {
    return(FALSE);
  }

  // 得到Dos头NT头数据目录表
  PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)DllBase;
  PIMAGE_NT_HEADERS pNtHeader = (PIMAGE_NT_HEADERS)((DWORD)pDosHeader + pDosHeader->e_lfanew);
  PIMAGE_OPTIONAL_HEADER pOptHeader = (PIMAGE_OPTIONAL_HEADER)(&pNtHeader->OptionalHeader);

  // 得到导出表的虚拟地址
  PIMAGE_EXPORT_DIRECTORY pExpDes = (PIMAGE_EXPORT_DIRECTORY)
    ((PBYTE)DllBase + pOptHeader->DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress);

  // 获取导出表的函数地址,函数名称,函数序号
  PULONG pAddressOfFunctions = (PULONG)((PBYTE)DllBase + pExpDes->AddressOfFunctions);
  PULONG pAddressOfNames = (PULONG)((PBYTE)DllBase + pExpDes->AddressOfNames);
  PUSHORT pAddressOfNameOrdinals = (PUSHORT)((PBYTE)DllBase + pExpDes->AddressOfNameOrdinals);

  // 循环查找
  for (int i = 0; i < pExpDes->NumberOfNames; ++i)
  {
    index = pAddressOfNameOrdinals[i];

    // 得到导出函数名
    LPCTSTR pFuncName = (LPTSTR)((PBYTE)DllBase + pAddressOfNames[i]);

    // 对比函数名
    if (!_tcscmp((LPCTSTR)pFuncName, szFunName))
    {
      // 返回地址
      addr = pAddressOfFunctions[index];
      break;
    }
  }

  // 设置读写属性
  VirtualProtect(&pAddressOfFunctions[index], 0x1000, PAGE_READWRITE, &dwProtect);

  // 设置导出函数
  pAddressOfFunctions[index] = (DWORD)NewFun - (DWORD)DllBase;

  // 写出替换
  WriteProcessMemory(GetCurrentProcess(), &pAddressOfFunctions[index],
    (LPCVOID)((DWORD)NewFun - (DWORD)DllBase), sizeof(NewFun), &dwProtect);
  return(TRUE);
}

// 自定义导出函数
int __stdcall MyMessageBox(HWND hWnd, LPCTSTR lpText, LPCTSTR lpCaption, UINT uType)
{
  printf("hello lyshark \n");
  return(0);
}

typedef int (WINAPI* LPFNMESSAGEBOX)(HWND hWnd, LPCTSTR lpText, LPCTSTR lpCaption, UINT uType);

int main(int argc, char *argv[])
{
  // 对MessageBoxA进行Eat Hook
  EATHook("USER32.dll", "MessageBoxA", MyMessageBox);

  // 模拟下次调用后就是执行我们的Hook代码
  LoadLibrary("USER32.dll");
  HMODULE hDll = GetModuleHandle("USER32.dll");

  LPFNMESSAGEBOX lpMessageBox = (LPFNMESSAGEBOX)GetProcAddress(hDll, "MessageBoxA");
  lpMessageBox(NULL, "Hello, EAT Hook", "Info", MB_OK);

  system("pause");
  return(0);
}

上述代码被运行后,针对外部调用MessageBoxA函数都会转向到MyMessageBox函数上,至此传入的参数将会失效,如下图所示;

本文作者: 王瑞
本文链接: https://www.lyshark.com/post/b550753d.html
版权声明: 本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!

与4.4 EAT Hook 挂钩技术相似的内容:

4.4 EAT Hook 挂钩技术

EAT(Export Address Table)用于修改动态链接库(DLL)中导出函数的调用。与`IAT Hook`不同,EAT Hook是在DLL自身中进行钩子操作,而不是修改应用程序的导入表。它的原理是通过修改DLL的导出函数地址,将原本要导出的函数指向另一个自定义的函数。这样,在应用程序调用DLL的导出函数时,实际上会执行自定义的函数。与IAT不同是EAT存放的不是函数地址,而是导出函数地

4.4 x64dbg 绕过反调试保护机制

在Windows平台下,应用程序为了保护自己不被调试器调试会通过各种方法限制进程调试自身,通常此类反调试技术会限制我们对其进行软件逆向与漏洞分析,我们以第一种`IsDebuggerPresent`反调试为例,该函数用于检查当前程序是否在调试器的环境下运行。函数返回一个布尔值,如果当前程序正在被调试,则返回True,否则返回False。函数通过检查特定的内存地址来判断是否有调试器在运行。具体来说,该

4.4 C++ Boost 数据集序列化库

Boost 库是一个由C/C++语言的开发者创建并更新维护的开源类库,其提供了许多功能强大的程序库和工具,用于开发高质量、可移植、高效的C应用程序。Boost库可以作为标准C库的后备,通常被称为准标准库,是C标准化进程的重要开发引擎之一。使用Boost库可以加速C应用程序的开发过程,提高代码质量和性能,并且可以适用于多种不同的系统平台和编译器。Boost库已被广泛应用于许多不同领域的C++应用程序

初步动态规划讲解:数字三角形

题目描述 观察下面的数字金字塔。 写一个程序来查找从最高点到底部任意处结束的路径,使路径经过数字的和最大。每一步可以走到左下方的点也可以到达右下方的点。 7 3 8 8 1 0 2 7 4 4 4 5 2 6 5 在上面的样例中,从 7 → 3 → 8 → 7 → 5 7 \to 3 \to 8 \

.NET周报 【4月第4期 2023-04-23】

国内文章 2023成都.NET线下技术沙龙圆满结束 https://www.cnblogs.com/edisonchou/p/2023_chengdu_dotnet_club_activity_review.html 2023年4月15日周六,由MASA技术团队和成都.NET俱乐部共同主办的2023

日志采集/分析

目录EFK1. 日志系统2. 部署ElasticSearch2.1 创建handless服务2.2 创建sts3. 部署kibana4. 部署ilogtail(docker-compose)4.1 编写docker-compose4.2 配置ilogtail采集4.3 查看容器采集的日志4.4 采集

各大插件市场智能助手评分榜出炉!百度Comate稳居第一

近日,在VSCode、Jetbrains等各大插件市场智能助手评分榜中,百度Comate分别以4.5和4.4位列第一,通义灵码位居第二、第三,CodeGeeX、iFlyCode、aiXcoder、GitHub Copilot等跟随其后。 从外界获悉,百度Comate自发布以来,得到了广大程序员们的好

leetcode - 对称二叉树

给你一个二叉树的根节点 root , 检查它是否轴对称。 示例 1: 输入:root = [1,2,2,3,4,4,3] 输出:true 示例 2: 输入:root = [1,2,2,null,3,null,3] 输出:false 解法思路 也是递归的思想 检查当前两个节点是否为null,是,则说明

痞子衡嵌入式全部原创文章 - 汇总索引

职场经验与见闻感悟 痞子衡在嵌入式行业也摸打滚爬了不少年,有一些个人经验可以给大家参考。所谓他山之石可以攻玉,希望痞子衡的经验对大家的职场之路有所帮助。 职场经验篇(持续更新中...4/4) 职场上有效地向师傅请教问题的几点建议 关于做技术的工作态度方面的几点建议 工作多年的工程师且看这四条进阶之路

【转帖】bpftrace 指南

文章目录 0. bpftrace0.1 bpftrace组件0.2 bpftrace 帮助信息0.3 bpftrace 工具速览表0.4 bpftrace 探针0.4.1 tracepoint0.4.2 usdt0.4.3 kprobe和kretprobe0.4.4 uprobe和uretprobe