在可执行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结构:添加新的节区的更多相关文章

  1. PE结构笔记

    提示:前面加*为必须背下来的 DOS头: typedef struct _IMAGE_DOS_HEADER { // DOS .EXE header WORD e_magic; //* Magic n ...

  2. Reverse Core 第二部分 - 16&17章 - 基址重定位表&.reloc节区

    第16-17章 - 基址重定位表&.reloc节区 @date: 2016/11/31 @author: dlive 0x00 前言 这几天忙着挖邮箱漏洞,吃火锅,马上要被关禁闭,看书进度比较 ...

  3. 基址重定位表&.reloc节区

    第16-17章 - 基址重定位表&.reloc节区 @date: 2016/11/31 @author: dlive 0x01 PE重定位 若加载的是DLL.SYS文件,且在ImageBase ...

  4. 羽夏笔记——PE结构(不包含.Net)

    写在前面   本笔记是由本人独自整理出来的,图片来源于网络.本人非计算机专业,可能对本教程涉及的事物没有了解的足够深入,如有错误,欢迎批评指正. 如有好的建议,欢迎反馈.码字不易,如果本篇文章有帮助你 ...

  5. 【PE结构】由浅入深PE基础学习-菜鸟手动查询导出表、相对虚拟地址(RVA)与文件偏移地址转换(FOA)

    0 前言 此篇文章想写如何通过工具手查导出表.PE文件代码编程过程中的原理.文笔不是很好,内容也是查阅了很多的资料后整合出来的.希望借此加深对PE文件格式的理解,也希望可以对看雪论坛有所贡献.因为了解 ...

  6. 手写PE结构解析工具

    PE格式是 Windows下最常用的可执行文件格式,理解PE文件格式不仅可以了解操作系统的加载流程,还可以更好的理解操作系统对进程和内存相关的管理知识,而有些技术必须建立在了解PE文件格式的基础上,如 ...

  7. 修改记事本PE结构弹计算器Shellcode

    目录 修改记事本PE结构弹计算器Shellcode 0x00 前言 0x01 添加新节 修改节数量 节表位置 添加新节表信息 0x02 添加弹计算器Shellcode 修改代码 0x03 修改入口点 ...

  8. ASP.NET MVC 5 - 给电影表和模型添加新字段

    在本节中,您将使用Entity Framework Code First来实现模型类上的操作.从而使得这些操作和变更,可以应用到数据库中. 默认情况下,就像您在之前的教程中所作的那样,使用 Entit ...

  9. [转] 添加新的系统调用 _syscall0(int, mysyscall)

    实验目的阅读 Linux 内核源代码,通过添加一个简单的系统调用实验,进一步理解Linux操作系统处理系统调用的统一流程.通过用kernel module的方法来实现一个系统调用实验,进一步理解Lin ...

  10. 【译】ASP.NET MVC 5 教程 - 9:添加新字段

    原文:[译]ASP.NET MVC 5 教程 - 9:添加新字段 在本节中,我们将使用Entity Framework Code First 数据迁移功能将模型类的改变应用到数据库中. 默认情况下,当 ...

随机推荐

  1. VWAP 订单的最佳执行方法:随机控制法

    更多精彩内容,欢迎关注公众号:数量技术宅,也可添加技术宅个人微信号:sljsz01,与我交流. 引言:相关研究 在当今的投资领域,算法交易正迅速成为客户获取和清算股票头寸的首选方法. 通常,被委托者会 ...

  2. 深入理解web协议(二):DNS、WebSocket

    本文首发于 vivo互联网技术 微信公众号链接:https://mp.weixin.qq.com/s/AkbAN4UZLDf841g1ZLFPBQ作者:Wu Yue 本文系统性的讲述了 DNS 协议与 ...

  3. java读取解析endnote文件

    有些项目中会要求代码解析endnote文献资料获取一些标准的信息,例如XX在某著名期刊上发表了某篇文章,关于发表文章的这个事情的描述就会给坐着一个endnote文件来记录文章名称.作者.期刊名称.出版 ...

  4. vue权限管理

    https://www.bilibili.com/video/BV1nq4y1i7BU/?spm_id_from=333.788.recommend_more_video.6&vd_sourc ...

  5. SpringBoot AOP 记录操作日志、异常日志

    使用SpringBoot AOP 记录操作日志.异常日志 我们在做项目时经常需要对一些重要功能操作记录日志,方便以后跟踪是谁在操作此功能.在操作某些功能时也有可能会发生异常,但是每次发生异常要定位原因 ...

  6. MES系统初探(一)

    什么是MES系统 MES系统是制造执行系统(Manufacturing Execution System)的缩写,是一种用于监控.控制和优化制造过程的软件系统.它主要负责协调生产计划.生产调度.生产执 ...

  7. The project description file (.project) for XXX is missing

    在STS中切换项目分支的时候,出现一个项目打不开了,提示:The project description file (.project) for XXX is missing 试了下网上的方法都没有解 ...

  8. 08-避免Latch的产生

    1.Latch简介 Latch就是锁存器,是一种在异步电路系统中,对输入信号电平敏感的单元,用来存储信息 锁存器在数据未锁存时,输出端的信号随输入信号变化,就像信号通过一个缓冲器,一旦锁存信号有效,数 ...

  9. JS - 兼容到 IE 8

    使用 jQuery可以有效的兼容IE 浏览器 , 但jQuery从2.0开始不兼容IE8,最低支持IE9,所以需要引入更低的jQuery版本来兼容 <script type="text ...

  10. Java进程内线程数量限制的相关学习

    Java进程内线程数量限制的相关学习 背景 还是之前出现 cannot create native thread 的问题的后续 周末在家学习了下如何在容器外抓取dump. 也验证了下能否开启超过宿主机 ...