前面我们说过在空白节添加代码,想要更多空间的话,可以扩大节,但是在最后一个节进行扩大的话,还需要修改原来节的属性,比较麻烦,所以不如直接新增一个节!
并且我们通过手动新增节,可以直观地看到非常多的细节,帮助我们理解原理!
手动实现新增节
什么是新增一个节?
pe文件有多个节表,我们随便找一个exe文件查看,可以看到有多个节表:
每个节都有自己的任务和功能,所以我们新增一个节可以专门用来实现我们自己想要实现的功能。
了解一下节表
详细看一下每个成员的注释!
重点知道下面4个成员
Name[IMAGE_SIZEOF_SHORT_NAME]:我们可以随便起一个自己喜欢的名字
VirtualSize:是在内存中的真实大小
VirtualAddress:是加载到内存中第一个字节的偏移地址
SizeOfRawData:该节在文件对齐后的大小。
typedef struct _IMAGE_SECTION_HEADER {
BYTE Name[IMAGE_SIZEOF_SHORT_NAME]; //8个字节名字.自己可以起.编译器也可以给定.不重要.
union {
DWORD PhysicalAddress;
DWORD VirtualSize; //节数据没有对齐后的大小.也就是没有对齐.节数据有多大.
} Misc;
DWORD VirtualAddress; //加载到内存中的第一个字节的地址.也就是虚拟地址.节在内存中哪里开始.内存中的VA + ImageBase 才是真正的节开始位置
DWORD SizeOfRawData; //修改这个属性的值,即可扩大节.并且在PE文件中添加相应的0数据进行填充.
DWORD PointerToRawData; //在文件中的偏移.是文件对齐成员倍数.
DWORD PointerToRelocations; //一下都是调试相关.
DWORD PointerToLinenumbers; //
WORD NumberOfRelocations;
WORD NumberOfLinenumbers;
DWORD Characteristics; //节的属性
} IMAGE_SECTION_HEADER, *PIMAGE_SECTION_HEADER;
新增节表的步骤
1.判断是否有足够的空间增加一个新节.
2.节表新增一个成员,在原最后一个成员后面添加
3.修改文件头中节表个数.
4.添加的新节表修改节表的属性. 节.VirtualAddress .这个成员指定了这个节在内存哪里展开.所以需要修改.
5.再原有数据的最后,新增一个节的数据(内存对齐的整数倍)
6.修改新增节表的属性
-
修改扩展头的PE镜像大小. sizeofImage.
这个成员才是关键.如果不按照内存对齐修改镜像大小.那么我们的节就不会映射到内存中.或者PE文件根本无法执行. -
修改节表文件偏移 节.PointerToRawData 我们指定了内存中从哪里展开节.那么也需要指定这个节在文件中在哪里展开
-
修改节表中的 节数据对齐后的大小. 节.SizeofRawData. 我们新增的节.自己需要在PE文件添加一段节数据.数据的大小按照文件对齐添加. 并且填写到这个成员中.
-
节.VirtualSize修改
-
节.Characteristics文件偏移修改
1.判断是否有足够的空间增加一个新节.
我们使用工具直接查看节表区域,我们仅需要40个字节,发现空间足够!
2.节表新增一个成员,在原最后一个成员后面添加
这里我们直接复制.text节表成员,因为text作为代码段,可读可写可执行,非常完美。
这里直接复制过来了,然后我们先将名字修改为.tttt
3.修改节的数量
文件头中有一个属性记录了我们节表的个数.我们新增了一个节.那么就需要在原有的个数上加1.找到文件头记录节表个数位置.并加一即可.
原来为8,现在改为9。
4 修改sizeOfImage的大小
修改前:
可选头中:SizeofImage :0001D000
我们新增了0x1000节数据大小.那么我们的镜像大小也要加0x1000大小进行映射.注意.要按照内存对齐。
我们的原镜像大小以及按照内存对齐的方式存放了. 就是0x1D000. 那么我们加了0x1000的数据就是 0x1E000大小.我们修改为0x1E000。
5.再原有数据的最后,新增一个节的数据(内存对齐的整数倍)
插入了0x1000字节,填充为0.
ps:这里使用pe editor进行插入。
6 修正新增节表的属性
我们新增了一个节表.那么我们就要为这个节表指明内存中开始展开的位置. 文件中展开的位置. 以及节数据的大小.
对应的三个成员分别是:
节.VirtualAddress
节.SizeOfRawData
节.PointerToRawData
节.VirtuallAddress修改
首先第一个成员. 节.virtuallAddress .我们按照文件对齐.与上一个节表对齐存放即可.
例如上一个节表对齐后的展开位置为 0x1c000 那么我们就修改为 0x1d000
修改前:
修改后:
节.sizeofRawData修改
这个成员就是节数据按照文件对齐后的大小.取决于我们给这个节添加多少数据,这里我们之前添加了0x1000个字节。
这里修改为1000
节.PointerRawToData 文件偏移修改
最后修改的就是节在文件中哪里展开的. 这个我们需要看上一个节的文件偏移.以及节数据大小. 算出来的.
例如:
新增节的上一个节,偏移位置为1000,节数据在文件中对齐后的尺寸为100,那么1000-1100都是上一个节的数据
.tttt上一个节在文件中的偏移为8200,大小为600;
那么tttt节的文件偏移就为8800
节.VirtualSize修改
申请的新节空间大小,1000
节.PointerRawToData 文件偏移修改
我们新增节一般都是要求可读可写可执行的,所以这里直接修改为60000020。
实验结果:
新增节后的文件正常执行
实验工具:
CFF
PE Editor