一、导入表注入的原理

注入是把DLL加载到另一个进程的4GB地址空间中,实现方式有很多种,导入表注入是我学的第一种注入,是通过修改程序的导入表,把自己的DLL添加到导入表中,来实现这个目的。

导入表是连续存储在节里的,它后面还有其他数据,不能直接追加一个导入表,所以必须找一块足够大的内存,把原来的所有导入表移动过去,再追加我们DLL的导入表。新增节是比较简单的做法,我的代码中也采用新增节。

导入表注入是让系统根据EXE的导入表,帮我们实现加载DLL,需要知道一点,DLL至少要有一个导出函数,系统才会帮我们加载DLL

新增节存储如下数据(顺序随意安排):
原来的所有导出表 IMAGE_IMPORT_DESCRIPTOR
新增DLL的导出表 IMAGE_IMPORT_DESCRIPTOR
导出表结束标记,sizeof(IMAGE_IMPORT_DESCRIPTOR) 个0
INT表
INT表结束标记
IMPORT_BY_NAME 结构,包括Hint和导出函数名所占的字节数
IAT表
IAT表结束标记
模块名

这些数据都写入到内存中后,根据他们的地址,给其中某些结构的属性赋值
导入表中的FirstThunk,OriginalFirstThunk分别存储INT,IAT的RVA;
导入表的Name存储模块名的RVA;
INT,IAT的值是IMPORT_BY_NAME的RVA;

除此之外,完成了所有结构的属性设置后,不要忘了修改数据目录项,让导入表指向新增节首字节。

二、注入代码和运行结果

// 导入表注入demo,通过修改导入表,将 InjectDll.dll 添加到导入表
// DLL只有一个导出函数 ExportFunction,保证至少有一个导出函数DLL才会被加载
// DLL的主函数在加载和分离时会弹窗
DWORD ImportTableInjectDemo(LPVOID pFileBuffer, LPVOID *pNewFileBuffer, DWORD dwFileSize)
{PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)pFileBuffer;PIMAGE_FILE_HEADER pPEHeader = (PIMAGE_FILE_HEADER)(pDosHeader->e_lfanew + (DWORD)pDosHeader + 4);PIMAGE_OPTIONAL_HEADER32 pOptionHeader = (PIMAGE_OPTIONAL_HEADER32)((DWORD)pPEHeader + sizeof(IMAGE_FILE_HEADER));PIMAGE_SECTION_HEADER pSectionHeader = \(PIMAGE_SECTION_HEADER)((DWORD)pOptionHeader + pPEHeader->SizeOfOptionalHeader);PIMAGE_IMPORT_DESCRIPTOR pImportTable = (PIMAGE_IMPORT_DESCRIPTOR)((DWORD)pFileBuffer + \RvaToFoa(pFileBuffer, pOptionHeader->DataDirectory[1].VirtualAddress));// 计算新增节的大小// 新增节存储的内容有:原来的所有导入表,新导入表,新INT, IAT, 模块名,一个_IMAGE_IMPORT_BY_NAME// 上述容器按0为结束标记的,也要包含结束标记DWORD dwNewSectionSize = 0;DWORD dwNumberOfDll = 0;while (pImportTable->OriginalFirstThunk || pImportTable->FirstThunk){//printf("%s\n", (LPCSTR)(RvaToFoa(pFileBuffer, pImportTable->Name) + (DWORD)pFileBuffer));dwNumberOfDll++;pImportTable++;}dwNewSectionSize += (dwNumberOfDll + 2) * sizeof(IMAGE_IMPORT_DESCRIPTOR); // 原有的和新添加的导入表,以及结束标记dwNewSectionSize += 16; // 这里包括一个INT,一个IAT和两个结束标记dwNewSectionSize += strlen("InjectDll.dll") + 1; // 模块名dwNewSectionSize += 2 + strlen("ExportFunction") + 1; // _IMAGE_IMPORT_BY_NAME,包括Hint和函数名DWORD dwNewFileSize = AddCodeSection(pFileBuffer, pNewFileBuffer, dwFileSize, dwNewSectionSize);pDosHeader = (PIMAGE_DOS_HEADER)*pNewFileBuffer;pPEHeader = (PIMAGE_FILE_HEADER)(pDosHeader->e_lfanew + (DWORD)pDosHeader + 4);pOptionHeader = (PIMAGE_OPTIONAL_HEADER32)((DWORD)pPEHeader + sizeof(IMAGE_FILE_HEADER));pSectionHeader = (PIMAGE_SECTION_HEADER)((DWORD)pOptionHeader + pPEHeader->SizeOfOptionalHeader);pImportTable = (PIMAGE_IMPORT_DESCRIPTOR)((DWORD)*pNewFileBuffer + \RvaToFoa(*pNewFileBuffer, pOptionHeader->DataDirectory[1].VirtualAddress));// 设置新增节属性pSectionHeader[pPEHeader->NumberOfSections - 1].Characteristics = 0xC0000040; // 可读写,含已初始化数据// 定义指针指向新增节首字节LPVOID pNewSec = (LPVOID)((DWORD)*pNewFileBuffer + pSectionHeader[pPEHeader->NumberOfSections - 1].PointerToRawData);LPVOID pInsert = pNewSec;   // 复制原有的导入表memcpy(pInsert, pImportTable, dwNumberOfDll * sizeof(IMAGE_IMPORT_DESCRIPTOR));  // 设置新导入表的时间戳,ForwarderChainpImportTable = (PIMAGE_IMPORT_DESCRIPTOR)((DWORD)pInsert + dwNumberOfDll * sizeof(IMAGE_IMPORT_DESCRIPTOR));   pImportTable->TimeDateStamp = 0; // 表示不使用绑定导入pImportTable->ForwarderChain = -1;// 设置导入表结束标记pInsert = (LPVOID)((DWORD)pImportTable + sizeof(IMAGE_IMPORT_DESCRIPTOR)); // 指向结束标记memset(pInsert, 0, sizeof(IMAGE_IMPORT_DESCRIPTOR));// 指定INT表插入点pInsert = (LPVOID)((DWORD)pInsert + sizeof(IMAGE_IMPORT_DESCRIPTOR)); // 现在指向INT表PIMAGE_THUNK_DATA pINT = (PIMAGE_THUNK_DATA)pInsert; // 定义指向INT表的指针// 设置INT结束标志memset(pINT + 1, 0, sizeof(IMAGE_THUNK_DATA));// 设置 IMPORT_BY_NAME,INT表和IAT表共同指向这块内存PIMAGE_IMPORT_BY_NAME pImportByName = (PIMAGE_IMPORT_BY_NAME)((DWORD)pINT + 8); // IMPORT_BY_NAME插入点pImportByName->Hint = 0; // 设置没有用的导出序号strcpy((char*)(pImportByName->Name), "ExportFunction"); // 设置函数名// INT表的值是 IMPORT_BY_NAME 的RVA*((PDWORD)pINT) = FoaToRva(*pNewFileBuffer, (DWORD)pImportByName - (DWORD)*pNewFileBuffer); // 设置INT的值// 设置IAT表pInsert = (LPVOID)((DWORD)pImportByName + 2 + strlen("ExportFunction") + 1); // 指向IAT表 PIMAGE_THUNK_DATA  pIAT = (PIMAGE_THUNK_DATA)pInsert; // IAT插入点memcpy(pIAT, pINT, 8);// 分配模块名的内存,并完成导入表剩余属性的赋值pInsert = (LPVOID)((DWORD)pInsert + 8); strcpy((char *)pInsert, "InjectDll.dll");// 设置导入表属性pImportTable->FirstThunk = FoaToRva(*pNewFileBuffer, (DWORD)pINT - (DWORD)*pNewFileBuffer);pImportTable->OriginalFirstThunk = FoaToRva(*pNewFileBuffer, (DWORD)pIAT - (DWORD)*pNewFileBuffer);pImportTable->Name = FoaToRva(*pNewFileBuffer, (DWORD)pInsert - (DWORD)*pNewFileBuffer);// 更新目录项中导入表的位置pOptionHeader->DataDirectory[1].VirtualAddress = FoaToRva(*pNewFileBuffer, ((DWORD)pNewSec - (DWORD)*pNewFileBuffer));return dwNewFileSize;
}

给ipmsg注入dll,双击运行exe,先弹出对话框

然后才运行程序

程序退出时,再次弹出对话框

三、DLL的代码

这是DLL的主函数,加载时弹一次窗,分离时也弹一次窗。

BOOL APIENTRY DllMain( HANDLE hModule, DWORD  ul_reason_for_call, LPVOID lpReserved)
{switch (ul_reason_for_call){case DLL_PROCESS_ATTACH:Init();break;case DLL_PROCESS_DETACH:Destroy();break;}return TRUE;
}

四、完整代码

至此,PE模块的课程已经结束,有很多PE的知识还没涉及到,这些要在以后学习了相关知识后再了解。

PE所有课后作业的核心代码都在这了!

https://blog.csdn.net/Kwansy/article/details/106234264

导入表注入原理和C语言实现相关推荐

  1. SM4分组加密算法原理和c语言实现

    一.前言 在之前的文章中介绍了<SM3国密杂凑值算法的原理和c语言实现>,这篇文章主要是用c语言撸一个SM4分组加密算法. 随着信息安全的普及以及等级保护等安全政策落地,国密算法越来越频繁 ...

  2. 【数据结构与算法】程序员们常用的10个关键数据结构,包括它们的原理和C语言实现代码

    [数据结构与算法]程序员们常用的10个关键数据结构,包括它们的原理和C语言实现代码 文章目录 [数据结构与算法]程序员们常用的10个关键数据结构,包括它们的原理和C语言实现代码 1. 数组 (Arra ...

  3. 移动导入表/导入表注入(注入导入表后EXE无法运行的BUG解决方案)

    移动导入表其实不难,只需要while循环一下导入表并使用memcpy()拷贝到新加的节当中. 核心的代码如下,新增节和其他解析PE文件的最基础的东西发在最后的完整代码里. pImport_Temp = ...

  4. C/C++ 手工实现IAT导入表注入劫持

    DLL注入有多种方式,今天介绍的这一种注入方式是通过修改导入表,增加一项导入DLL以及导入函数,我们知道当程序在被运行起来之前,其导入表中的导入DLL与导入函数会被递归读取加载到目标空间中,我们向导入 ...

  5. r语言聚类分析_图说层次聚类分析原理和R语言实现

    1.引言 "物以类聚.人以群分".但我们面对一群人或者一堆物的时候,我们都希望将他们分分类,分类之后,我们才能更加有针对性地采取措施,从而提高工作效率. 如,我们将消费者分成若干类 ...

  6. MPEG音频编码 基本原理和C语言代码分析

    背景 MPEG(Moving Picture Experts Group)在汉语中译为活动图像专家组,特指活动影音压缩标准. MPEG 音频文件是MPEG1 标准中的声音部分,也叫MPEG 音频层,它 ...

  7. 【池化技术】内存池技术原理和C语言实现

    文章目录 一.基础概念 1.一个可执行程序占用的内存分为哪几个区?一个进程的虚拟内存区域有哪些? 2.静态内存分配和动态内存分配 二.malloc实现原理 malloc内存分配(下面算是正常一般的情况 ...

  8. SM3国密杂凑值算法的原理和c语言实现

    一.SM3算法介绍 杂凑值算法也可称为摘要算法或者哈希算法.通过对数据资料的填充.分组.扩展压缩等方式计算成特定长度的数值,来作为数据指纹或者数据特征使用.常见的MD5算法长度为128bit(16字节 ...

  9. 北京工业大学微机原理和c语言,北京工业大学 微机原理 实验九

    实验报告 实验九数码转换 一.实验目的 1.掌握计算机常用数据编码之间的相互转换方法. 2.进一步熟悉DEBUG软件的使用方法. 二.实验内容 1. ACSII码转换为非压缩型BCD码 编写并调试正确 ...

最新文章

  1. 百度API地图 ,房产频道的标注方法
  2. webApp之meta标签
  3. android软件开发基础课程(一)
  4. Linux命令:ls、grep、wc统计目录下文件及文件夹的个数。
  5. ETL之Kettle
  6. 天堂2地点坐标(SQL语句,可直接导入数据库)
  7. can协议crc计算_CAN协议教程|CAN报文分析
  8. 如何实现幻灯片效果/图片轮播
  9. 计算机无法关机 总是自动启动不了怎么办,电脑不能关机,小编教你电脑关机后总是重启怎么办...
  10. linux删除缓存文件swp,Vi下删除SWP文件
  11. java bouncycastle,使用BouncyCastle在Java中使用ECIES进行加密
  12. Oracle基础学习
  13. Redis配置主从服务器
  14. [zoj 3587]Marlon's String[kmp]
  15. PTA 盲盒包装流水线 (25 分)
  16. 立统 视频防泄密系统 技术白皮书2021-07-16
  17. Java 安全编程详解
  18. java的基本数据类型_java基本数据类型有哪些?
  19. 微博5亿用户数据泄露:通讯录匹配机制是罪魁祸首!
  20. linux 移动某个文件夹及其所有子文件夹内指定类型的文件

热门文章

  1. 成功解决UnicodeDecodeError: ‘gbk‘ codec can‘t decode byte 0xba in position 2: illegal multibyte sequence
  2. TF:利用TF的train.Saver将训练好的W、b模型文件保存+新建载入刚训练好模型(用于以后预测新的数据)
  3. Pandas matplotlib 无法显示中文
  4. Nginx CONTENT阶段 static模块
  5. Hibernate 之单向多对一映射及其衍生问题
  6. ArcObject开发时,axtoolbarcontrol中一些添加的按钮是灰色的问题
  7. [转]SpringMVC+Hibernate+Spring 简单的一个整合实例
  8. nRF52832的UICR
  9. 操作系统习题4—进程死锁
  10. 最大子序列求和_最大子序列和问题