2.11 PE结构:添加新的节区
在可执行PE文件中,节(section)是文件的组成部分之一,用于存储特定类型的数据。每个节都具有特定的作用和属性,通常来说一个正常的程序在被编译器创建后会生成一些固定的节,通过将数据组织在不同的节中,可执行文件可以更好地管理和区分不同类型的数据,并为运行时提供必要的信息和功能。节的作用是对可执行文件进行有效的分段和管理,以便操作系统和加载器可以正确加载和执行程序。
可执行文件常用的节包括了:
| 节名 | 作用 | 功能说明 | 权限 |
|---|---|---|---|
| .text | 代码节 | 包含可执行代码,例如程序的指令和函数体 | 执行和读取 |
| .data | 数据节 | 包含程序的全局和静态变量的初始化数据。 | 读取和写入 |
| .rdata | 只读数据节 | 包含只读的静态数据,如常量字符串和只读的全局变量。 | 只读 |
| .idata | 导入表节 | 包含程序所依赖的外部函数和符号的引用信息,用于动态链接。 | 读取和写入 |
| .edata | 导出表节 | 包含程序导出的函数和符号的信息,用于供其他程序或模块使用。 | 读取 |
| .rsrc | 资源节 | 包含程序所使用的资源数据,如图标、位图、字符串等。 | 读取 |
| .reloc | 重定位节 | 包含程序的重定位信息,用于在加载时修正代码和数据的内存地址。 | 读取和写入 |
当然了并不是每一个标准的PE文件都具备这些节,某些程序可能会使用特殊的PE编辑工具新增一些特殊的节,或者当程序被加密压缩后该程序的节区也会发生不同的变化,对于新增节来说需要具备如下几个关键步骤:
- 计算新节的偏移量和大小:确定要添加的新节的偏移量和大小。偏移量是新节在文件中的位置,大小是新节的长度。
- 更新PE文件头:修改PE文件头中的相关字段,更新文件头中的
NumberOfSections字段和SizeOfImage字段。 - 创建新节:在PE文件末尾添加新的节表项,并填充新节的各个字段,例如名称、虚拟大小、文件大小、内存对齐等。
对于操作PE文件来说,在头文件中需要引入ImageHlp.h并且包含#pragma comment(lib,"Imagehlp.lib")库,该库提供了用于处理PE文件的函数和结构体,是Image Help Library库的一部分,读者可自行在项目内引入这个库。
当引入后我们来实现第一个功能,为可执行文件分配空间,如下AllocateSpace函数,该函数通过使用CreateFileA打开一个文件,并调用SetFilePointer将文件指针移动到FILE_END文件末尾处,接着通过循环的方式WriteFile填充开辟空间。
// 计算取整
DWORD AlignSize(int nSecSize, DWORD Alignment)
{
int nSize = nSecSize;
if (nSize %Alignment != 0)
{
nSecSize = (nSize / Alignment + 1) * Alignment;
}
return nSecSize;
}
// 为可执行文件分配空间
DWORD AllocateSpace(char *FileName, int FileSize)
{
HANDLE hFile = CreateFileA(FileName, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_WRITE |
FILE_SHARE_READ, NULL, OPEN_ALWAYS, NULL, NULL);
DWORD ret = SetFilePointer(hFile, 0, 0, FILE_END);
if (ret != NULL)
{
for (int x = 0; x < FileSize; x++)
{
WriteFile(hFile, "", 1, 0, 0);
}
CloseHandle(hFile);
}
return ret;
}
上述程序的使用方法很简单,通过AllocateSpace("d://lyshark.exe",4096)传入两个参数,分别是需要开辟空间的进程,以及开辟空间的长度,该长度可以与节区内的大小保持一致,当程序执行后则返回开辟位置,读者可使用WinHex工具跳转到程序末尾自行查看,如下图所示;

接着我们来实现添加节区功能,如下代码ImplantSection则可实现增加新节功能,该函数传入三个参数,分别是可执行文件地址,节区名称,以及节区长度,程序中通过映射方式打开文件,分别寻找到当前节表首地址,以及节的数量,通过复制一个节,并对该节内存参数进行更新(节内存大小,节文件大小,节内存属性)等,当这些数据被更正后,则加下来就是保存文件,并返回pTmpSec->PointerToRawData节所在文件地址。
// 计算取整
DWORD AlignSize(int nSecSize, DWORD Alignment)
{
int nSize = nSecSize;
if (nSize %Alignment != 0)
{
nSecSize = (nSize / Alignment + 1) * Alignment;
}
return nSecSize;
}
// 添加新的节区 szFileName = 打开exe文件 szSecName = 节名称 nSecSize = 节大小
DWORD ImplantSection(LPSTR szFileName, char szSecName[8], int nSecSize)
{
HANDLE m_hFile = CreateFileA(szFileName, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ,
NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
HANDLE m_hMap = CreateFileMapping(m_hFile, NULL, PAGE_READWRITE, 0, 0, 0);
HANDLE m_lpBase = MapViewOfFile(m_hMap, FILE_MAP_READ | FILE_SHARE_WRITE, 0, 0, 0);
// ------------------------------------------------------------------------
// 定位到DOS/NT头部
// ------------------------------------------------------------------------
// 定位DOS头
PIMAGE_DOS_HEADER m_pDosHdr = (PIMAGE_DOS_HEADER)m_lpBase;
printf("[-] 当前DOS头: 0x%08X \n", m_pDosHdr);
// 定位NT头
PIMAGE_NT_HEADERS m_pNtHdr = (PIMAGE_NT_HEADERS)((DWORD)m_lpBase + m_pDosHdr->e_lfanew);
printf("[-] 当前NT头: 0x%08X \n", m_pNtHdr);
// 定位节表首地址
PIMAGE_SECTION_HEADER m_pSecHdr = (PIMAGE_SECTION_HEADER)((DWORD)&(m_pNtHdr->OptionalHeader) +
m_pNtHdr->FileHeader.SizeOfOptionalHeader);
printf("[-] 定位当前节表首地址: 0x%08X \n", m_pSecHdr);
// 定位节区数量
int nSecNum = m_pNtHdr->FileHeader.NumberOfSections;
DWORD dwFileAlignment = m_pNtHdr->OptionalHeader.FileAlignment;
DWORD dwSecAlignment = m_pNtHdr->OptionalHeader.SectionAlignment;
PIMAGE_SECTION_HEADER pTmpSec = m_pSecHdr + nSecNum;
// 复制节名
strncpy((char *)pTmpSec->Name, szSecName, 7);
printf("[+] 拷贝节名称: %s \n", szSecName);
// ------------------------------------------------------------------------
// 节的内存大小
// ------------------------------------------------------------------------
pTmpSec->Misc.VirtualSize = AlignSize(nSecSize, dwSecAlignment);
printf("[+] 节表内存大小: %d \n", nSecSize);
// 节的内存起始位置
pTmpSec->VirtualAddress = m_pSecHdr[nSecNum - 1].VirtualAddress +
AlignSize(m_pSecHdr[nSecNum - 1].Misc.VirtualSize, dwSecAlignment);
printf("[+] 节内存起始位置: 0x%08X \n", pTmpSec->VirtualAddress);
// ------------------------------------------------------------------------
// 节的文件大小
// ------------------------------------------------------------------------
pTmpSec->SizeOfRawData = AlignSize(nSecSize, dwFileAlignment);
printf("[-] 节的文件大小: %d \n", pTmpSec->SizeOfRawData);
// 节的文件起始位置,这里直接在文件末尾分配空间
pTmpSec->PointerToRawData = SetFilePointer(m_hFile, 0, 0, FILE_END);
printf("[-] 节的文件起始位置: 0x%08X \n", pTmpSec->PointerToRawData);
// 这里开始循环在文件末尾分配nSecSize存储空间
for (int x = 0; x < nSecSize; x++)
WriteFile(m_hFile, "", 1, 0, 0);
// ------------------------------------------------------------------------
// 节访问属性,设置内存属性为可读可执行代码段
// ------------------------------------------------------------------------
pTmpSec->Characteristics = 0xE0000020;
// 修正节区数量
m_pNtHdr->FileHeader.NumberOfSections++;
// 修正映像大小
m_pNtHdr->OptionalHeader.SizeOfImage += pTmpSec->Misc.VirtualSize;
FlushViewOfFile(m_lpBase, 0);
return pTmpSec->PointerToRawData;
}
读者可自行调用上述函数,通过ImplantSection("d://Win32Project.exe", ".hack", 4096)传值,此时分别传入参数为需要修改的文件名,需要增加的节名称,以及创建节长度,在运行后读者可自行观察是否创建成功,如下图所示;

2.11 PE结构:添加新的节区的更多相关文章
- PE结构笔记
提示:前面加*为必须背下来的 DOS头: typedef struct _IMAGE_DOS_HEADER { // DOS .EXE header WORD e_magic; //* Magic n ...
- Reverse Core 第二部分 - 16&17章 - 基址重定位表&.reloc节区
第16-17章 - 基址重定位表&.reloc节区 @date: 2016/11/31 @author: dlive 0x00 前言 这几天忙着挖邮箱漏洞,吃火锅,马上要被关禁闭,看书进度比较 ...
- 基址重定位表&.reloc节区
第16-17章 - 基址重定位表&.reloc节区 @date: 2016/11/31 @author: dlive 0x01 PE重定位 若加载的是DLL.SYS文件,且在ImageBase ...
- 羽夏笔记——PE结构(不包含.Net)
写在前面 本笔记是由本人独自整理出来的,图片来源于网络.本人非计算机专业,可能对本教程涉及的事物没有了解的足够深入,如有错误,欢迎批评指正. 如有好的建议,欢迎反馈.码字不易,如果本篇文章有帮助你 ...
- 【PE结构】由浅入深PE基础学习-菜鸟手动查询导出表、相对虚拟地址(RVA)与文件偏移地址转换(FOA)
0 前言 此篇文章想写如何通过工具手查导出表.PE文件代码编程过程中的原理.文笔不是很好,内容也是查阅了很多的资料后整合出来的.希望借此加深对PE文件格式的理解,也希望可以对看雪论坛有所贡献.因为了解 ...
- 手写PE结构解析工具
PE格式是 Windows下最常用的可执行文件格式,理解PE文件格式不仅可以了解操作系统的加载流程,还可以更好的理解操作系统对进程和内存相关的管理知识,而有些技术必须建立在了解PE文件格式的基础上,如 ...
- 修改记事本PE结构弹计算器Shellcode
目录 修改记事本PE结构弹计算器Shellcode 0x00 前言 0x01 添加新节 修改节数量 节表位置 添加新节表信息 0x02 添加弹计算器Shellcode 修改代码 0x03 修改入口点 ...
- ASP.NET MVC 5 - 给电影表和模型添加新字段
在本节中,您将使用Entity Framework Code First来实现模型类上的操作.从而使得这些操作和变更,可以应用到数据库中. 默认情况下,就像您在之前的教程中所作的那样,使用 Entit ...
- [转] 添加新的系统调用 _syscall0(int, mysyscall)
实验目的阅读 Linux 内核源代码,通过添加一个简单的系统调用实验,进一步理解Linux操作系统处理系统调用的统一流程.通过用kernel module的方法来实现一个系统调用实验,进一步理解Lin ...
- 【译】ASP.NET MVC 5 教程 - 9:添加新字段
原文:[译]ASP.NET MVC 5 教程 - 9:添加新字段 在本节中,我们将使用Entity Framework Code First 数据迁移功能将模型类的改变应用到数据库中. 默认情况下,当 ...
随机推荐
- Spring 学习笔记(4)依赖注入 DI
本篇文章主要对 Spring 框架中的核心功能之一依赖注入 (DI,Dependency Injection) 进行介绍,也是采用 理论+实战 的方式给大家阐述其中的原理以及明确需要注意的地方. 相关 ...
- qsort函数使用方法总结(详细全面+代码)
目录 qsort函数原型 compar参数 int 数组排序 结构体排序 字符串指针数组排序 字符串二维数组排序 整型二维数组(力扣题目) qsort函数原型 void qsort( void *ba ...
- 机器学习-决策树系列-决策树-ID3算法 -C4.5算法-26
目录 1. 决策树 2. 举个例子 计算信息增益 3. C4.5算法 1. 决策树 决策树是属于有监督机器学习的一种,起源非常早,符合直觉并且非常直观, 模型生成:通过大量数据生成一颗非常好的树,用这 ...
- TICK 中Kapacitor功能和使用说明
转载请注明出处: 1.Kapacitor 简介 Kapacitor是InfluxData公司开发的一个实时流数据处理引擎.它可以实时地通过TICK脚本处理InfluxDB中的流数据以及批处理数据. K ...
- 一文搞清楚Java中的方法、常量、变量、参数
写在开头 在上一篇文章:一文搞清楚Java中的包.类.接口 中我们讲了Java中的包.类和接口,今天继续将剩下的方法.常量.变量以及参数梳理完. Java中的变量与常量 在JVM的运转中,承载的是数据 ...
- AI正在改变人类社会 - 内容行业的衰落
现在的 AI 技术,每天都在进化.我有一种感觉,普通人大概没意识到,它马上就要改变人类社会了. 历史上,这种事一再发生.在你不知不觉中,某些大事件悄悄酝酿,突然就冲击到了你的生活,将你的人生全部打乱, ...
- 04 Tcl字符串
Tcl字符串 4.1 Tcl将说有的变量值视作字符串,并将他们作为字符串进行保存. 命令 描述 append 将值追加到字符串尾 binary 二进制化字符串 format 字符串格式化 regexp ...
- Pycharm配置git
原文链接:https://www.jianshu.com/p/ae92970d2062 1.下载Gitee插件 同样在设置页面,选中 Plugins,并搜索 Gitee安装. 安装后,重启一下Pych ...
- Python Code_01
author : 写bug的盼盼 development time : 2021/8/27 19:41 输出数字 print(520) 输出字符串 print('HelloWorld') print( ...
- 百度网盘(百度云)SVIP超级会员共享账号每日更新(2023.11.26)
一.百度网盘SVIP超级会员共享账号 可能很多人不懂这个共享账号是什么意思,小编在这里给大家做一下解答. 我们多知道百度网盘很大的用处就是类似U盘,不同的人把文件上传到百度网盘,别人可以直接下载,避免 ...