PE结构之导入表

server/2024/10/19 1:04:06/

 流程图:

文件中\样式

加载到进程中时
 
加载到进程中时的过程,一张图不够放
 

续图
 

整个流程
 

 补充导入表结构IMAGE_IMPORT_DESCRIPTOR 中的ForwarderChain字段, 该解释为
"某个导入模块涉及转发(即该模块的某些函数从其他模块转发过来),那么 ForwarderChain 字段会包含一个索引,指向这个导入描述符中与第一个转发函数相关的表条目  ,通常情况下,ForwarderChain 被设为 -1(或 0xFFFFFFFF),表示该导入描述符中没有转发函数"
关于函数转发,请查看PE结构之导出表-CSDN博客 中的内容>

这篇中介绍到kernel32.dll中包含了转发函数但是我们可以查看记事本的 exe .

 可以看到 这个成员 依然是用-1 来表达的.即使他有转发函数的情况下

 64位程序注意事项

考虑到 PE32+ 可执行文件(64 位),每个 ILT (导入名称表) 条目总结为:

  • 如果设置了高位(位 63,也称为“序号标志”),则底部 63 位(0 到 62)被视为序号函数号。
  • 如果未设置高位 (即序号标志为 false) ,则整个条目是 Hint/Name 表的 RVA。

导入表的遍历

//打印导入表
BOOL PrintImport(__in char* m_fileName)
{char* Filebuffer = NULL;if (!GetFileBuffer(m_fileName, &Filebuffer)) return FALSE;PIMAGE_DOS_HEADER LPdosHeader = NULL;PIMAGE_NT_HEADERS LPntHeader = NULL;LPdosHeader = (PIMAGE_DOS_HEADER)Filebuffer;LPntHeader = (PIMAGE_NT_HEADERS)((CHAR*)LPdosHeader + LPdosHeader->e_lfanew);//如果是32位程序if (LPntHeader->OptionalHeader.Magic == 0x10b){PIMAGE_NT_HEADERS32 LPntHeader32 = LPntHeader;LPntHeader = NULL;DWORD Characteristics = 0;//定位到导入表PIMAGE_IMPORT_DESCRIPTOR LPimport = (PIMAGE_IMPORT_DESCRIPTOR)((CHAR*)LPdosHeader+ RVAToFOAEX(LPdosHeader, LPntHeader32->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress, &Characteristics));PIMAGE_IMPORT_DESCRIPTOR LPcurrentImport = LPimport;int i = 0;while (LPcurrentImport->OriginalFirstThunk!=0 && LPcurrentImport->FirstThunk!=0){printf("[%d]IMAGE_IMPORT_DESCRIPTOR结构\n",i++);printf("OriginalFirstThunk:0X%X\nTimeDateStamp:0X%X\nName:%s\nFirstThunk:0X%X\n",LPcurrentImport->OriginalFirstThunk,LPcurrentImport->TimeDateStamp,((CHAR*)LPdosHeader+RVAToFOA(LPdosHeader,LPcurrentImport->Name)),LPcurrentImport->FirstThunk);//导入查找表或者叫导入名称表PIMAGE_THUNK_DATA32 INTtable = (PIMAGE_THUNK_DATA32)((CHAR*)LPdosHeader + RVAToFOA(LPdosHeader, LPcurrentImport->OriginalFirstThunk));int j = 0;while (INTtable->u1.AddressOfData != 0){if ((INTtable->u1.AddressOfData & 0x80000000)==0){PIMAGE_IMPORT_BY_NAME hintName=(PIMAGE_IMPORT_BY_NAME) ((CHAR*)LPdosHeader + RVAToFOA(LPdosHeader, INTtable->u1.AddressOfData));printf("\t[%d] 按名称导入  hint:[0x%x] Name:%s\n",j, hintName->Hint, hintName->Name);}else{printf("\t[j] 按序号导入 0x%x\n", INTtable->u1.AddressOfData & (~0x80000000));}INTtable++;j++;}//导入地址表PIMAGE_THUNK_DATA32 IATtable = (PIMAGE_THUNK_DATA32)((CHAR*)LPdosHeader + RVAToFOA(LPdosHeader, LPcurrentImport->FirstThunk));int k = 0;while (IATtable->u1.AddressOfData != 0){if (LPcurrentImport->TimeDateStamp == 0){if ((INTtable->u1.AddressOfData & 0x80000000 )== 0){PIMAGE_IMPORT_BY_NAME hintName = (PIMAGE_IMPORT_BY_NAME)((CHAR*)LPdosHeader + RVAToFOA(LPdosHeader, INTtable->u1.AddressOfData));printf("\t[%d] 按名称导入  hint:[0x%x] Name:%s\n", k, hintName->Hint, hintName->Name);}else{printf("\t[k] 按序号导入 0x%x\n", INTtable->u1.AddressOfData & (~0x80000000));}}else{printf("\t函数绝对地址 0x%x\n", IATtable->u1.Function);}IATtable++; k++;}LPcurrentImport++;}}else{//64位程序PIMAGE_NT_HEADERS64 LPntHeader64 = LPntHeader;LPntHeader = NULL;DWORD Characteristics = 0;//定位到导入表PIMAGE_IMPORT_DESCRIPTOR LPimport = (PIMAGE_IMPORT_DESCRIPTOR)((CHAR*)LPdosHeader + RVAToFOAEX(LPdosHeader, LPntHeader64->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress, &Characteristics));PIMAGE_IMPORT_DESCRIPTOR LPcurrentImport = LPimport;int i = 0;while (LPcurrentImport->OriginalFirstThunk != 0 && LPcurrentImport->FirstThunk != 0){printf("[%d]IMAGE_IMPORT_DESCRIPTOR结构\n", i++);printf("OriginalFirstThunk:0X%X\nTimeDateStamp:0X%X\nName:%s\nFirstThunk:0X%X\n", LPcurrentImport->OriginalFirstThunk, LPcurrentImport->TimeDateStamp,((CHAR*)LPdosHeader + RVAToFOA(LPdosHeader, LPcurrentImport->Name)), LPcurrentImport->FirstThunk);//导入查找表或者叫导入名称表PIMAGE_THUNK_DATA64 INTtable64 = (PIMAGE_THUNK_DATA32)((CHAR*)LPdosHeader + RVAToFOA(LPdosHeader, LPcurrentImport->OriginalFirstThunk));int j = 0;while (INTtable64->u1.AddressOfData != 0){if ((INTtable64->u1.AddressOfData & 0x8000000000000000) == 0){PIMAGE_IMPORT_BY_NAME hintName = (PIMAGE_IMPORT_BY_NAME)((CHAR*)LPdosHeader + RVAToFOA(LPdosHeader, INTtable64->u1.AddressOfData));printf("\t[%d] 按名称导入  hint:[0x%x] Name:%s\n", j, hintName->Hint, hintName->Name);}else{printf("\t[j] 按序号导入 0x%llx\n", INTtable64->u1.AddressOfData & (~0x8000000000000000));}INTtable64++;j++;}//导入地址表PIMAGE_THUNK_DATA64 IATtable64 = (PIMAGE_THUNK_DATA32)((CHAR*)LPdosHeader + RVAToFOA(LPdosHeader, LPcurrentImport->FirstThunk));int k = 0;while (IATtable64->u1.AddressOfData != 0){if (LPcurrentImport->TimeDateStamp == 0){if ((IATtable64->u1.AddressOfData & 0x8000000000000000) == 0){PIMAGE_IMPORT_BY_NAME hintName = (PIMAGE_IMPORT_BY_NAME)((CHAR*)LPdosHeader + RVAToFOA(LPdosHeader, IATtable64->u1.AddressOfData));printf("\t[%d] 按名称导入  hint:[0x%x] Name:%s\n", k, hintName->Hint, hintName->Name);}else{printf("\t[k] 按序号导入 0x%llx\n", IATtable64->u1.AddressOfData & (~0x8000000000000000));}}else{printf("\t函数绝对地址 0x%llx\n", IATtable64->u1.Function);}IATtable64++;k++;}LPcurrentImport++;}}
}

 如果某个导入表的时间戳==-1 ,请查看PE结构之绑定导入表-CSDN博客

导入表注入 (包括创建新的节区,移除Dostub 添加新的节表)

原理:在需要注册的程序中的导入表后添加 需要注入的dll的信息

将导入表移动带新节区
新的节区 属性请务必 给与可读可写的属性.否则你将 遭遇到
Exception Processing Message 0xC0000005 - Unexpected parameters
因为 系统在加载该dll时,将修改IAT表中的内容为函数的rva .如果不给可写属性,那么将遭遇如下的问题. 

 
我在 填写节属性时,复制了导入表的属性,导致无法运行程序

//导入表 HOOK  包含移动导入表 ,修改目录项的RVA ,添加新的导入表
BOOL IATHook(__in char* m_fileName, __in char* m_DllName , __in char* m_savePath)
{char* Filebuffer = NULL;if (!GetFileBuffer(m_fileName, &Filebuffer)) return FALSE;PIMAGE_DOS_HEADER LPdosHeader = NULL;PIMAGE_NT_HEADERS LPntHeader = NULL;LPdosHeader = (PIMAGE_DOS_HEADER)Filebuffer;LPntHeader = (PIMAGE_NT_HEADERS)((CHAR*)LPdosHeader + LPdosHeader->e_lfanew);//如果是32位程序if (LPntHeader->OptionalHeader.Magic == 0x10b){PIMAGE_NT_HEADERS32 LPntHeader32 = LPntHeader;LPntHeader = NULL;//定位到导入表DWORD Characteristics = 0;//原来的节表的属性 /*	DWORD Characteristics = 0xC0000000; //请给可读可写节区属性,否则你会后悔*/PIMAGE_IMPORT_DESCRIPTOR LPoriginalFirstImport = (PIMAGE_IMPORT_DESCRIPTOR)((CHAR*)LPdosHeader + RVAToFOAEX(LPdosHeader, LPntHeader32->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress, &Characteristics));PIMAGE_IMPORT_DESCRIPTOR LPoriginalCurrentImport = LPoriginalFirstImport;//计算原来的所有导入表的大小DWORD sizeofOrinigal = 0;while (LPoriginalCurrentImport->OriginalFirstThunk != 0 && LPoriginalCurrentImport->FirstThunk != 0){sizeofOrinigal += sizeof(IMAGE_IMPORT_DESCRIPTOR);LPoriginalCurrentImport++;}//计算新添加的大小DWORD sizeofNew = 0;sizeofNew += sizeof(IMAGE_IMPORT_DESCRIPTOR)*2;//新增一个导入表结构,和空白区,sizeofNew += sizeof(IMAGE_THUNK_DATA32) * 2;//新增一个INT表和全0结束大小sizeofNew += sizeof(IMAGE_THUNK_DATA32) * 2;//新增一个IAT表和全0结束大小sizeofNew += sizeof(IMAGE_IMPORT_BY_NAME);//添加一个Hint/Name 表sizeofNew += strlen(m_DllName);//再加上一个字符串的小NewSecInfo info = { 0 };info.Characteristics = Characteristics;info.sizeofNewData = sizeofOrinigal + sizeofNew;//要往节中添加多少数据if (!AddSection(Filebuffer, &info)){free(Filebuffer);return FALSE;}//PULONG_PTR pCurrentPoint = NULL;//用于记录当前指针的位置DWORD currentRVA = 0;currentRVA = info.NewSectionVirtualAddress;pCurrentPoint = info.NewSectionBegionPointer;memcpy(pCurrentPoint, LPoriginalFirstImport, sizeofOrinigal);//将原来的节表赋值到新的节区//修改目录项中的导入表的RVAPIMAGE_NT_HEADERS32 LPnewNtHeader32 = (PIMAGE_NT_HEADERS32)(((PIMAGE_DOS_HEADER)info.newFileBuffer)->e_lfanew + (CHAR*)info.newFileBuffer);LPnewNtHeader32->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress = currentRVA;//移动指针和RVA到原来的导入表结束位置pCurrentPoint = (char*)pCurrentPoint + sizeofOrinigal ;//移动指currentRVA += sizeofOrinigal;//移动RVA//记录要加的导入表指针PIMAGE_IMPORT_DESCRIPTOR pnewIDTpoint = (PIMAGE_IMPORT_DESCRIPTOR)pCurrentPoint;再次移动指针和RVA 留出要添加 导入表和空白位置pCurrentPoint = (char*)pCurrentPoint + sizeof(IMAGE_IMPORT_DESCRIPTOR)*2;currentRVA += sizeof(IMAGE_IMPORT_DESCRIPTOR) * 2;//添加Hint/Name 表((PIMAGE_IMPORT_BY_NAME)pCurrentPoint)->Hint = 0;memcpy(((PIMAGE_IMPORT_BY_NAME)pCurrentPoint)->Name, "add", strlen("add")+1);DWORD HintNameRVA = currentRVA;//移动指针和RVApCurrentPoint = (char*)pCurrentPoint + strlen("add") + 1 + sizeof(WORD);currentRVA += strlen("add") + 1 + sizeof(WORD);//添加INT表((PIMAGE_THUNK_DATA32)pCurrentPoint)->u1.Ordinal = HintNameRVA;//RVA不可能超越 0x80000000这个值(表示按序号导入),用户空间的代码在32位时最高地址为7FFF FFFF DWORD INTRVA = currentRVA;//移动指针和RVApCurrentPoint = (char*)pCurrentPoint + sizeof(IMAGE_THUNK_DATA32) * 2;//留空白结尾currentRVA+= sizeof(IMAGE_THUNK_DATA32) * 2;//添加IAT表((PIMAGE_THUNK_DATA32)pCurrentPoint)->u1.Ordinal = HintNameRVA;//RVA不可能超越 0x80000000这个值(表示按序号导入),用户空间的代码在32位时最高地址为7FFF FFFF DWORD IATRVA = currentRVA;//移动指针和RVApCurrentPoint = (char*)pCurrentPoint + sizeof(IMAGE_THUNK_DATA32) * 2;//留空白结尾currentRVA += sizeof(IMAGE_THUNK_DATA32) * 2;//添加DLL的名字memcpy(pCurrentPoint, m_DllName, strlen(m_DllName) + 1);DWORD dllNameRVA = currentRVA;pnewIDTpoint->OriginalFirstThunk = INTRVA;pnewIDTpoint->FirstThunk = IATRVA;pnewIDTpoint->Name = dllNameRVA;pnewIDTpoint->TimeDateStamp = 0;pnewIDTpoint->ForwarderChain = -1;//只有dll中有转发,加链接的过程中,连接器怎么知道这个dll有转发函数,并将导入表的这个位置写入值呢StoringFile(m_savePath, info.newFileBuffer, info.NewFileBufferSize);}else{}}


http://www.ppmy.cn/server/132905.html

相关文章

git的下载安装完整教程

一、下载 官网下载地址:Git - Downloads (git-scm.com) 二、安装 1.双击 2.按照安装向导next

问:JVM当中的垃圾分类怎么搞?

在Java中,JVM(Java虚拟机)的垃圾识别与分类是自动内存管理的重要组成部分。这一过程主要通过垃圾收集器(Garbage Collector)实现,旨在识别和回收不再被程序引用的对象,以释放内存空间。 1. 垃圾…

JNI(Java Native Interface)和NIO(New Input/Output)是什么?

1. JNI(Java Native Interface) JNI是一种接口,允许Java代码与其他编程语言(例如C或C)编写的本地代码进行交互。通过JNI,Java程序可以调用本地代码中的函数或库,反过来,本地代码也可…

C++多款质量游戏及开发建议[OIER建议]

前言 其实C不适合开发大型高质量游戏。 但是,很多人信息学竞赛生(博主)为了竞赛都学习了C,但自小就认为编程就是开发游戏的我们,肯定想着开发一个游戏,但发现C的局限性以及无法和windows非常好的兼容&…

【从零开始的LeetCode-算法】945. 使数组唯一的最小增量

给你一个整数数组 nums 。每次 move 操作将会选择任意一个满足 0 < i < nums.length 的下标 i&#xff0c;并将 nums[i] 递增 1。 返回使 nums 中的每个值都变成唯一的所需要的最少操作次数。 生成的测试用例保证答案在 32 位整数范围内。 示例 1&#xff1a; 输入&am…

react antd redux 全局状态管理 解决修改菜单状态 同步刷新左侧菜单

npm i react-redux1.src新建两个文件 globalState.js 全局状态定义 store.js 全局存储定义 2.globalState.js import { createSlice } from "reduxjs/toolkit";export const globalState createSlice({name: "globalState",initialState: { data: {} },r…

Nginx配置全解析

一、前言 Nginx是一款轻量级的高性能Web服务器、反向代理服务器以及电子邮件&#xff08;IMAP/POP3&#xff09;代理服务器。它在处理高并发连接方面表现出色&#xff0c;被广泛应用于各种互联网服务的部署中。了解Nginx的配置对于优化网站性能、保障服务稳定运行至关重要。 …

C#配置文件怎么自动更新到运行目录下

C# 编程学习 WEB API 编程系列11、编写配置文件&#xff0c;如appsettings.json2、项目文件中增加更新策略3、总结 WEB API 编程系列1 你好&#xff01; 这是你第一次使用 Markdown编辑器 所展示的欢迎页。如果你想学习如何使用Markdown编辑器, 可以仔细阅读这篇文章&#xff…