滴水三期:day33.1-新增节、扩大节-添加代码

一、新增节思路

1.满足新增节条件

  • 为什么新增节来添加代码呢?因为有时所有节的空白区可能都不够存放我们要添加的代码,所以我们自己新增足够大的节来添加代码

  • 首先判断是否有足够的空间,可以添加一个节表(新增节就要新增一个节表来记录此节信息);判断方法:SizeOfHeader - (DOS + 垃圾数据 + PE标记 + 标准PE头 + 可选PE头 + 已存在节表) >= 2个节表的大小

    为什么需要2个节表大小:有时windows会根据一个结构后面是否有多个0x00来判断这个结构是否结束。所以算是windows规定的格式吧:故最后一个节表后面还要补跟节表宽度一样的0;但是其实最后一个节表后面不是40个0或者跟了一些其他数据也是可行的,但由于没有满足规定的格式要求,不知道什么时候就不好使了,所以我们新增节表时还是按照标准规定来

  • 如果满足添加节表的条件,此时我们可以将已有的一个节表的40字节信息复制一份紧挨着最后一个节表的末尾(节表之间必须要挨着!这是规定),然后再往后的40字节全部补0,接下来就是根据新增节的信息,去修改对应节表的字段值即可

  • 需要修改的数据:

    • 修改标准PE头中NumberOfSections字段(节的数量)

    • 修改SizeOfImage的大小(原有值 + 新增节的大小,最后还要内存对齐)

    • 在文件的最后,新增节(添加时节的大小如果设置为内存对齐的整数倍,后面就不用再担心对齐的事)

    • 修正新增节表的属性:

      • Name,这个按照ASCII码随便取,最好长度限制在8字节内

      • Misc.VirtualSize,如果此时我们还不知道添加的数据长度(没对齐前),那么直接就按照对齐后的大小写即可,比如我们要新增节大小为0x1000(考虑了内存对齐),那么不管里面要用多少字节数据,VirtualSize的值就按照0x1000写

      • VirtualAddress,内存起始偏移地址,我们通过上一个节表的VirtualAddress + VirtualSize内存对齐后,得到上一个节内存对齐后的结尾位置,所以新增节表的VirtualAddress就是这个值。

        千万不能用SizeOfRawData来判断,因为这个节的VirtualSize可能大于SizeOfRawData,因为包含未初始化数据,那么此时在内存中应该按照VirtualAddress + VirtualSize内存对齐后的得到的值来存储

        • 举例:

          • 我们发现有时候文件内存中未对齐的大小确实比文件中对齐的大小大(由于节中包含初始化数据的缘故),比如notepad.exe文件的第二个节.data中,内存中的大小为0x1BA8,但是文件中对齐后的大小为0x600。此时我们可以发现,第三个节在文件中就按照上一个节在文件中对齐后的大小往后接着存放,即0x7200 + 0x600 = 0x7800,所以第三个节的PointerToRawData为0x7800,而第三个节在内存中就按照上一个节在内存中对齐后的大小往后接着存放(就不是再按照上一个节的文件中对齐大小挨着存放了,因为此时VirtualSize比SizeOfRawData大),即0x8000 + 0x1BA8 = 0x9BA8,对齐后为0xA000,所以第三个节内存偏移为0xA000

            image-20211229171857518
      • SizeOfRawData,根据文件对齐粒度来修正这个值,比如上面如果设定了VirtualSize的值为0x1000,说明这个节的这0x1000字节都是有用数据,那么此节在硬盘上时也应该有这0x1000字节的数据,所以此时根据文件对齐粒度来修正这个值,如果为0x200或者0x1000,就不用修改0x1000,因为已经满足是文件对齐的整数倍了

      • PointerToRawData,文件对齐后的文件地址,我们可以通过上一个节表的PointerToRawData + SizeOfRawData计算得到此值

      • Characteristics,按照我们想要的属性来修改即可(可读、可写、可执行等)

  • 如图所示:

    image-20211227103312545
  • SizeOfHeaders的值不要随便改变,因为如果这个值变了,后面的节都要跟着往后或者往前跟着变,但是这些节当中凡是涉及到地址的计算相关的就会改变,比如说我们昨天加的E8和E9后面的值,是通过其他的地址计算出来的,如果SizeOfHeaders变了,节地址跟着变,那么这些值也要改变,所以代价很高,不建议修改

2.如果节表后面有编译器添加的数据

  • 有些程序会在节表到节的空白区之间添加一些信息,由于添加的新节表一定要与原来的节表紧挨着,但是我们又不知道程序的这些信息是否有用、是否影响程序的正常运行,所以此时我们不能轻易修改覆盖这些内容,那么我们如果要添加新节表就需要想办法:我们知道程序的DOS头到PE签名之间有一处区域DOS Stub,不影响程序的运行,数据也是程序的一些说明信息,对我们来说就是垃圾数据,所以我们可以将NT头到节表末尾这一部分整体上移,把Dos Stub这块数据覆盖了,接着修改DOS头中的e_lfanew字段的值为上移后PE签名的地址即可,那么此时下面就会空出来一部分,我们就可以先将这部分全部修改成0x00,再往这片区域新增节表即可

3.整体上移后空间还不够大

  • 如果向上覆盖DOS Stub之后,还不够新增节表的大小,那么我们就采用扩大最后一个节的方式,将添加的代码加到最后一个节扩大的空间中,这样可以保证不影响上面的所有地址

4.注意事项

  • 文件加载到内存中,最后一个节的VirtualAddress + VirtualSize对齐后的结尾一定就是一个文件在内存中的结束位置,即SizeOfImage;文件在硬盘上时,最后一个节的PointerToRawData + SizeOfRawData的值一定就是整个文件的结尾!
  • 不管是往节的空白区中添加代码还是新增节、扩大节,如果用编程来实现,应该在ImageBuffer中操作更方便,即拉伸后来操作,不管是一些值的计算还是位置的选择上都要更方便一些
  • 加载到内存中,要看节之间怎么排的,应该根据VirtualAddress和VirtualSize以及内存对齐来判断;如果在硬盘上,节之间怎么排的,应该根据PointerToRawData和SizeOfRawData以及文件对齐来判断

二、手动新增节添加代码

1.条件都满足时

  • 我们使用飞鸽ipmsg.exe来新增节,将ipmsg.exe用winhex打开

  • 先判断节表末尾到第一个节之间是否够80字节:可选PE头中SizeOfHeaders值为0x1000;标准PE头中SizeOfOptionalHeader值为0xE0,NumberOfSections值为0x4;DOS头中e_lfanew值为0xE0。通过0x1000 - (0xE0 + 0x4 + 0x14 + 0xE0 + 0x4 * 0x28)= 0xD88,因为0xD88 > 0x50,所以可以存下新增节表

  • 我们将.text节表的40字节复制一份到节表末尾地址0x278:选中.text40字节复制,再选中0x278到后面40字节数据,鼠标定位到0x278右键–edit–clipboard data–write,即可把.text节表内容复制一份添加到节表末尾作为新节表的40字节

    屏幕截图 2021-12-27 114117

    winhex中write是覆盖选中数据;paste是插入

  • 接着将新增节后面的40字节数据改为0,为了满足格式要求(已经是了就不用修改了)

    image-20211227114251284
  • 修改PE标准头中的NumberOfSections值为4 + 1 = 5

    image-20211227114432665
  • 先看一下文件对齐和内存对齐都是0x1000,所以我们新增的节大小为了方便后面修改可以设定为0x1000字节大小,足够存放我们想要添加的代码了,而且还考虑了对齐问题。(如果文件对齐和内存对齐不一样,那可以选择两个的最小公倍数,方便后面的修改)

    image-20211227114730586
  • 接着修改可选PE头中SizeOfImage值:由于我们想要新增节的大小为0x1000(考虑了内存对齐),所以将SizeOfImage + 0x1000即可,0x3D000 + 0x1000 = 0x3E000

    image-20211227142252100
  • 然后修改节表中的字段:

    • Name:名字改为.tttt

    • Misc.VirtualSize:直接写0x1000

    • VirtualAddress:0x30000 + 0xD000 = 0x3D000

    • SizeOfRawData:0x1000

    • PointerToRawData:0x2C000 + 0xD000 = 0x39000

    • 属性值不用变,因为满足了可执行

      image-20211227145500834
  • 现在将我们要在文件末添加新节,选中文件最后一个数据,右键–edit–paste zero bytes–输入新节的大小0x1000,由于只能输入十进制数,所以要将0x1000转换成4096再输入!!(这里易错)

    image-20211227155047235
  • 新增节后我们将要添加的代码加到新节中,这里就使用day32的shellcode,就是运行飞鸽后先弹一个框,再正常运行,但是此时还要计算一下E8和E9后面的值:

    • E8后面的值:X = 0x77D36476 - (0x400000 + 0x3D000 + 0x8 + 0x5) = 0x778F9469

    • E9后面的值:X = 0x400000 + 0x1D26F - (0x400000 + 0x3D000 + 0xD + 0x5) = 0xFFFE025D

    • 所以将完整的代码(硬编码)添加到新节中:

      image-20211227150303568
  • 最后将OEP的值改为0x3D000

    image-20211227155121633
  • 保存,运行后发现先弹框再正常打开程序,成功

2.节表结尾有数据整体上移的情况

  • 我们打开记事本复件notepad_test.exe,找到节表末,发现这里有一些我们不认识的数据,而且是紧挨着节表的,所以此时我们没法在节表末新增节表(新增的节表必须和前面的节表连起来,不能断开)

    image-20211227155945371
  • 此时我们看一看DOS头结尾到PE签名之间是垃圾数据,如果将PE签名一直到节表末之间的所有数据整体上紧挨着DOS头后面,接着将e_lfanew字段值改为0x40,那么节表到那些不知道什么意思的数据之间就空出来了0xE8 - 0x40 = 0xA8字节内存,足够存放80字节,即两个节表大小了

    image-20211227160149320
  • 选中0xE8到0x258的全部数据,复制,从0x40开始write覆盖0x258 - 0xE8 = 0x170字节数据,接着将此时节表末尾到那些不知道什么数据之间的数据改为0,再修改e_lfanew的值为0x40即可。此时0x1B0到0x258之间就空出来了0xA8字节,在这里新增节即可

    image-20211227161358322
  • 后面新增节的过程与条件都满足时一模一样,不再演示

3.整体上移后空间还不够,扩大最后一个节

  • 如果整体上移覆盖DOS Stub后空间还不够两节表宽度,那么就要采用扩大最后一个节的方式。不要扩大上面的节,因为如果不是最后一个节扩大的话,其他的节扩大会影响下面的全部节的偏移,很多数据、地址就会有问题

  • 需要修改SizeOfImage,比如我想扩大500字节,那么要满足内存对齐,所以SizeOfImage应该 + 0x1000,这里为了方便后面的修改,直接就按照内存对齐来扩大,所以扩大0x1000字节即可

  • 接着就是修改最后一个节表的属性:

    • VirtualAddress和PointerToRawData的值都不用改变
    • VirtualSize:SizeOfImage - VirtualAddress + Ex,再内存对齐(这里为了方便,Ex可以直接选文件对齐粒度和内存对齐粒度的最小公倍数,这样就不用再考虑内存对齐了),就是最后注入shellcode时不好判断Ex+原来空白区大小是否足够存下shellcode了,不过可以直接在Ex区域注入shellcode即可,不用考虑原来此节的空白区
    • SizeOfRawData:因为文件对齐一般都为0x200或0x1000,这里直接等于VirtualSize即可(因为VirtualSize此时肯定即是0x200的整数倍,也是0x1000的整数倍)
    • 因为扩大节的目的是为了添加shellcode,所以还要看最后一个节的属性是否可执行
  • 此时就可以在最后一个节的末尾到结束这之间添加代码了

    image-20211230205545082

三、代码实现

1.编程实现:新增一个节,并添加代码

下面是最简单的新增节并添加代码,满足新增节的条件;注意问题:如果我们增加节或者扩大节,在ImageBuffer中操作更方便,而且==一定要新申请一块内存NewImageBuffer来存储新增或扩大后的PE文件的数据==,不能直接在原有的ImageBuffer的内存中操作,因为这块内存是动态分配的,大小是指定过的malloc(size),不能修改或者新增。

#include "stdafx.h"
#include <stdlib.h>
#include <string.h>

typedef unsigned short WORD;
typedef unsigned int DWORD;
typedef unsigned char BYTE;

#define MZ 0x5A4D
#define PE 0x4550
#define IMAGE_SIZEOF_SHORT_NAME 8
#define MessageBox_Address 0x77D36476 //宏定义记事本的MessageBox内存地址

//shellcode
BYTE shellcode[] = {
	0x6A,0x00,0x6A,0x00,0x6A,0x00,0x6A,0x00,
	0xE8,0x00,0x00,0x00,0x00,
	0xE9,0x00,0x00,0x00,0x00
};

//DOS头
struct _IMAGE_DOS_HEADER {
	WORD e_magic;  //MZ标记
	WORD e_cblp;
	WORD e_cp;
	WORD e_crlc;
	WORD e_cparhdr;
	WORD e_minalloc;
	WORD e_maxalloc;
	WORD e_ss;
	WORD e_sp;
	WORD e_csum;
	WORD e_ip;
	WORD e_cs;
	WORD e_lfarlc;
	WORD e_ovno;
	WORD e_res[4];
	WORD e_oemid;
	WORD e_oeminfo;
	WORD e_res2[10];
	DWORD e_lfanew;  //PE文件真正开始的偏移地址
};

//标准PE头
struct _IMAGE_FILE_HEADER {
	WORD Machine;  //文件运行平台
	WORD NumberOfSections;  //节数量
	DWORD TimeDateStamp;  //时间戳
	DWORD PointerToSymbolTable;
	DWORD NumberOfSymbols;
	WORD SizeOfOptionalHeader;  //可选PE头大小
	WORD Characteristics;  //特征值
};

//可选PE头
struct _IMAGE_OPTIONAL_HEADER {
	WORD Magic;  //文件类型
	BYTE MajorLinkerVersion;
	BYTE MinorLinkerVersion;
	DWORD SizeOfCode;   //代码节文件对齐后的大小
	DWORD SizeOfInitializedData;  //初始化数据文件对齐后的大小
	DWORD SizeOfUninitializedData;  //未初始化数据文件对齐后大小
	DWORD AddressOfEntryPoint;  //程序入口点(偏移量)
	DWORD BaseOfCode;  //代码基址
	DWORD BaseOfData;  //数据基址
	DWORD ImageBase;   //内存镜像基址
	DWORD SectionAlignment;  //内存对齐粒度
	DWORD FileAlignment;  //文件对齐粒度
	WORD MajorOperatingSystemVersion;
	WORD MinorOperatingSystemVersion;
	WORD MajorImageVersion;
	WORD MinorImageVersion;
	WORD MajorSubsystemVersion;
	WORD MinorSubsystemVersion;
	DWORD Win32VersionValue;
	DWORD SizeOfImage;  //文件装入虚拟内存后大小
	DWORD SizeOfHeaders;  //DOS、NT头和节表大小
	DWORD CheckSum;  //校验和
	WORD Subsystem;
	WORD DllCharacteristics;
	DWORD SizeOfStackReserve;  //预留堆栈大小
	DWORD SizeOfStackCommit;  //实际分配堆栈大小
	DWORD SizeOfHeapReserve;  //预留堆大小
	DWORD SizeOfHeapCommit;  //实际分配堆大小
	DWORD LoaderFlags;
	DWORD NumberOfRvaAndSizes;  //目录项数目
	//_IMAGE_DATA_DIRECTORY DataDirectory[16];  //这个先不管
};

//NT头
struct _IMAGE_NT_HEADERS {
	DWORD Signature;  //PE签名
	_IMAGE_FILE_HEADER FileHeader;
	_IMAGE_OPTIONAL_HEADER OptionalHeader;
};

//节表
struct _IMAGE_SECTION_HEADER{
	BYTE Name[IMAGE_SIZEOF_SHORT_NAME];  //节表名
	union{
		DWORD PhysicalAddress;
		DWORD VirtualSize;  //内存中未对齐大小
	}Misc;
	DWORD VirtualAddress;  //该节在内存中偏移地址
	DWORD SizeOfRawData;  //该节在硬盘上文件对齐后大小
	DWORD PointerToRawData;  //该节在硬盘上文件对齐后偏移地址
	DWORD PointerToRelocations;
	DWORD PointerToLinenumbers;
	WORD NumberOfRelocations;
	WORD NumberOfLinenumbers;
	DWORD Characteristics;  //该节特征属性
};

/*计算文件大小函数
参数:文件绝对路径
返回值:返回文件大小(单位字节)
*/
int compute_file_size(char* filePath){
	FILE* fp = fopen(filePath,"rb");
    if(!fp){
		printf("打开文件失败");
		exit(0);
	}
    fseek(fp,0,2);
	int size = ftell(fp);
	//fseek(fp,0,0); 单纯计算文件大小,就不需要还原指针了
	fclose(fp);
	return size;
}

/*将文件读入FileBuffer函数
参数:文件绝对路径
返回值:FileBuffer起始地址
*/
char* to_FileBuffer(char* filePath){
    FILE* fp = fopen(filePath,"rb");
    if(!fp){
		printf("打开文件失败");
		exit(0);
	}
    int size = compute_file_size(filePath);
    
	char* mp = (char*)malloc(sizeof(char) * size);  //分配内存空间
    if(!mp){
        printf("分配空间失败");
        fclose(fp);
        exit(0);
    }
    
    int isSucceed = fread(mp,size,1,fp);
    if(!isSucceed){
        printf("读取数据失败");
        free(mp);
        fclose(fp);
        exit(0);
    }
    
    fclose(fp);
    return mp;
}

/*FileBuffer到ImageBuffer函数
参数:FileBuffer起始地址
返回值:ImageBuffer起始地址
*/
char* fileBuffer_to_ImageBuffer(char* fileBufferp){

	_IMAGE_DOS_HEADER* _image_dos_header = NULL;
	_IMAGE_FILE_HEADER* _image_file_header = NULL;
	_IMAGE_OPTIONAL_HEADER* _image_optional_header = NULL;
	_IMAGE_SECTION_HEADER* _image_section_header = NULL;

	_image_dos_header = (_IMAGE_DOS_HEADER*)fileBufferp;
	_image_file_header = (_IMAGE_FILE_HEADER*)(fileBufferp + _image_dos_header->e_lfanew + 4);
	_image_optional_header = (_IMAGE_OPTIONAL_HEADER*)((char*)_image_file_header + 20);
	_image_section_header = (_IMAGE_SECTION_HEADER*)((char*)_image_optional_header + _image_file_header->SizeOfOptionalHeader);

	DWORD sizeofImage = _image_optional_header->SizeOfImage;
	char* imageBufferp = (char*)malloc(sizeofImage);
	if(NULL == imageBufferp){
        printf("动态申请ImageBuffer内存失败\n");
        exit(0);
    }
	for(DWORD i = 0;i < sizeofImage;i++){
		*(imageBufferp + i) = 0x00;
	}

	//strncpy(imageBufferp,fileBufferp,_image_optional_header->SizeOfHeaders); 因为imageBufferp,fileBufferp值会变,所以这个方法不太好
	for(i = 0;i < _image_optional_header->SizeOfHeaders;i++){
		*(imageBufferp + i) = *(fileBufferp + i);
	}
	

	for(i = 0;i < _image_file_header->NumberOfSections;i++){
		for(DWORD j = 0;j < _image_section_header->SizeOfRawData;j++){
			*(imageBufferp + _image_section_header->VirtualAddress + j) = *(fileBufferp + 
				_image_section_header->PointerToRawData + j);
		}
		_image_section_header++;
	}

	return imageBufferp;
}

/*计算NewBuffer大小函数
参数:NewBuffer起始地址
返回值:unsigned int类型(单位:字节)
*/
DWORD compute_NewBuffer_size(char* newBufferp){
	_IMAGE_DOS_HEADER* _image_dos_header = NULL;
	_IMAGE_FILE_HEADER* _image_file_header = NULL;
	_IMAGE_OPTIONAL_HEADER* _image_optional_header = NULL;
	_IMAGE_SECTION_HEADER* _image_section_header = NULL;

	_image_dos_header = (_IMAGE_DOS_HEADER*)newBufferp;
	_image_file_header = (_IMAGE_FILE_HEADER*)(newBufferp + _image_dos_header->e_lfanew + 4);
	_image_optional_header = (_IMAGE_OPTIONAL_HEADER*)((char*)_image_file_header + 20);
	_image_section_header = (_IMAGE_SECTION_HEADER*)((char*)_image_optional_header + _image_file_header->SizeOfOptionalHeader);
	
	_IMAGE_SECTION_HEADER* last_image_section_header = _image_section_header + _image_file_header->NumberOfSections - 1;
	DWORD sizeofNewBuffer = last_image_section_header->PointerToRawData + last_image_section_header->SizeOfRawData;
	return sizeofNewBuffer;
}

/*ImageBuffer到NewBuffer函数
参数:ImageBuffer起始地址
返回值:NewBuffer起始地址
*/
char* imageBuffer_to_NewBuffer(char* imageBufferp){
	
	_IMAGE_DOS_HEADER* _image_dos_header = NULL;
	_IMAGE_FILE_HEADER* _image_file_header = NULL;
	_IMAGE_OPTIONAL_HEADER* _image_optional_header = NULL;
	_IMAGE_SECTION_HEADER* _image_section_header = NULL;

	_image_dos_header = (_IMAGE_DOS_HEADER*)imageBufferp;
	_image_file_header = (_IMAGE_FILE_HEADER*)(imageBufferp + _image_dos_header->e_lfanew + 4);
	_image_optional_header = (_IMAGE_OPTIONAL_HEADER*)((char*)_image_file_header + 20);
	_image_section_header = (_IMAGE_SECTION_HEADER*)((char*)_image_optional_header + _image_file_header->SizeOfOptionalHeader);

	//重新计算newBuffer需要大小
	/*方法一:SizeOfHEADER + 所有节的SizeOfRawData之和
	int sizeofSections = 0;  
	_IMAGE_SECTION_HEADER* _image_section_header_temp = _image_section_header;
	for(DWORD i = 0;i < _image_file_header->NumberOfSections;i++){
		sizeofSections += _image_section_header_temp->SizeOfRawData;
		_image_section_header_temp++;
	}
	char* newBufferp = (char*)malloc(_image_optional_header->SizeOfHeaders + sizeofSections);
	*/
	
	//方法二:使用最后一个节的文件偏移地址 + 最后一个节对齐后的大小
	DWORD size = compute_NewBuffer_size(imageBufferp);
	char* newBufferp = (char*)malloc(size);
	if(NULL == newBufferp){
        printf("NewBuffer内存分配失败\n");
        exit(0);
    }
	for(DWORD i = 0;i < size;i++){
		*(newBufferp + i) = 0x00;
	}
	
	
	for(i = 0;i < _image_optional_header->SizeOfHeaders;i++){
		*(newBufferp + i) = *(imageBufferp + i);
	}
	
	for(i = 0;i < _image_file_header->NumberOfSections;i++){
		for(DWORD j = 0;j < _image_section_header->SizeOfRawData;j++){ //不用VirtualSize因为害怕会多复制覆盖下一个节
			*(newBufferp + _image_section_header->PointerToRawData + j) = *(imageBufferp + 
				_image_section_header->VirtualAddress + j);
		}
		_image_section_header++;
	}

	return newBufferp;
}	

/*NewBuffer存盘函数
参数:需要写出数据的内存首地址,保存绝对路径,写出的数据大小(单位:字节)
返回值:成功返回1,失败返回0
*/
int save_to_disk(char* newBufferp,char* storagePath,DWORD size){
	FILE* fp = fopen(storagePath,"wb");
	if(!fp){
		printf("打开文件失败");
		return 0;
	}
	int isSucceed = fwrite(newBufferp,size,1,fp);
	if(!isSucceed){
        free(newBufferp);
        fclose(fp);
        return 0;
    }
	fclose(fp);
	return 1;
}

/*向新增节中注入shellcode函数
参数:要注入的可执行文件的ImageBuffer起始地址
返回值:0为注入失败、1为注入成功
*/
int add_shellcode_to_new_section(char* imageBufferp){
	_IMAGE_DOS_HEADER* _image_dos_header = NULL;
	_IMAGE_FILE_HEADER* _image_file_header = NULL;
	_IMAGE_OPTIONAL_HEADER* _image_optional_header = NULL;
	_IMAGE_SECTION_HEADER* _image_section_header = NULL;

	_image_dos_header = (_IMAGE_DOS_HEADER*)imageBufferp;
	_image_file_header = (_IMAGE_FILE_HEADER*)(imageBufferp + _image_dos_header->e_lfanew + 4);
	_image_optional_header = (_IMAGE_OPTIONAL_HEADER*)((char*)_image_file_header + 20);
	_image_section_header = (_IMAGE_SECTION_HEADER*)((char*)_image_optional_header + _image_file_header->SizeOfOptionalHeader);
	
    //定位到新节表起始地址
	_IMAGE_SECTION_HEADER* target_image_section_header = _image_section_header + _image_file_header->NumberOfSections - 1;
	
	//判断新节是否存的下shellcode
	if((int)(target_image_section_header->Misc.VirtualSize) < (int)(sizeof(shellcode) / sizeof(shellcode[0]))){
		printf("新节大小不够,添加失败");
		return 0;
	}

	//将shellcode注入到新节
	char* shellcodep = imageBufferp + target_image_section_header->VirtualAddress;
	char* newOEP = shellcodep;  //此shellcode注入起始地址即为新的OEP地址
	memcpy(shellcodep,shellcode,sizeof(shellcode) / sizeof(shellcode[0])); //注意memcpy函数不改变传入的指针的值!!
	
	//修改call MessageBox的硬编码E8后面的4字节
	DWORD E8_fill_value = MessageBox_Address - ((shellcodep - imageBufferp + _image_optional_header->ImageBase) + 0xD);
	*(DWORD*)(shellcodep + 9) = E8_fill_value;

	//修改jmp 原OEP的硬编码E9后面的4字节
	DWORD E9_fill_value = _image_optional_header->AddressOfEntryPoint + _image_optional_header->ImageBase - ((shellcodep - imageBufferp + _image_optional_header->ImageBase) + 0x12);
	*(DWORD*)(shellcodep + 14) = E9_fill_value;

	//修改OEP指向shellcode
	_image_optional_header->AddressOfEntryPoint = (DWORD)(newOEP - imageBufferp);

	return 1;
}

/*计算最小公倍数函数
参数:传入要计算的两个数
返回值:两数的最小公倍数
*/
int least_common_multiple(int a,int b){
	if(a <= 0 || b <= 0)
		return -1;
	int max = (a > b ? a : b);
    while(1){
        if(max % a == 0 && max % b == 0){
            break;
        }
        max++;
    }
	return max;
}

/*在文件最后新增节函数
参数:要新增节的可执行文件的ImageBuffer起始地址
返回值:新增节后的NewImageBuffer内存的起始地址
*/
char* add_one_section(char* imageBufferp){
	_IMAGE_DOS_HEADER* _image_dos_header = NULL;
	_IMAGE_FILE_HEADER* _image_file_header = NULL;
	_IMAGE_OPTIONAL_HEADER* _image_optional_header = NULL;
	_IMAGE_SECTION_HEADER* _image_section_header = NULL;

    //定义原来的ImageBuffer几个指针(因为要用到几个值用于创建新的NewImageBuffer内存)
	_image_dos_header = (_IMAGE_DOS_HEADER*)imageBufferp;
	_image_file_header = (_IMAGE_FILE_HEADER*)(imageBufferp + _image_dos_header->e_lfanew + 4);
	_image_optional_header = (_IMAGE_OPTIONAL_HEADER*)((char*)_image_file_header + 20);
	
	//先计算添加新节后的NewImageBuffer的大小,并初始化内存
	int expand_value = least_common_multiple(_image_optional_header->FileAlignment,_image_optional_header->SectionAlignment);//这里为了方便直接把新节大小设为文件对齐粒度和内存对齐粒度的最小公倍数即可
	char* newImageBufferp = (char*)malloc(_image_optional_header->SizeOfImage + expand_value);
	for(int i = 0;i < (int)_image_optional_header->SizeOfImage + expand_value;i++){
		*(newImageBufferp + i) = 0x00;
	}
	memcpy(newImageBufferp,imageBufferp,_image_optional_header->SizeOfImage);//不改变指针哦
	_image_dos_header = (_IMAGE_DOS_HEADER*)newImageBufferp;
	_image_file_header = (_IMAGE_FILE_HEADER*)(newImageBufferp + _image_dos_header->e_lfanew + 4);
	_image_optional_header = (_IMAGE_OPTIONAL_HEADER*)((char*)_image_file_header + 20);
	_image_section_header = (_IMAGE_SECTION_HEADER*)((char*)_image_optional_header + _image_file_header->SizeOfOptionalHeader);

	//判断节表末尾是否有空间新增节表
	if(_image_optional_header->SizeOfHeaders - _image_dos_header->e_lfanew - 4 - 20 - _image_file_header->SizeOfOptionalHeader - 
		_image_file_header->NumberOfSections * 40 < 80){
		printf("空间不足,无法新增节表");
		getchar();
		exit(0);
	}
	for(i = 0;i < 80;i++){
		if(*((char*)(_image_section_header + _image_file_header->NumberOfSections)+ i) != 0){
			printf("节表后80字节数据不全为0x00,有文件数据,无法新增节表");
			getchar();
			exit(0);
		}
	}
	
	//新增节
	DWORD original_SizeOfImage = _image_optional_header->SizeOfImage; //记录下原来的SizeOfImage
	_image_optional_header->SizeOfImage += expand_value;

	//新增节表
	_IMAGE_SECTION_HEADER* _image_section_header_new = (_IMAGE_SECTION_HEADER*)(char*)_image_section_header + _image_file_header->NumberOfSections;
	for(i = 0;i < 40;i++){
		*((char*)_image_section_header_new + i) = *((char*)_image_section_header + i);
	}
	_image_file_header->NumberOfSections++;

	//修改新增节表信息
	char* name = (char*)_image_section_header_new->Name;
	char* newName = ".newsec";
	strncpy(name,newName,IMAGE_SIZEOF_SHORT_NAME);
	_image_section_header_new->Misc.VirtualSize = expand_value;
	_image_section_header_new->VirtualAddress = original_SizeOfImage;
	_image_section_header_new->SizeOfRawData = expand_value;
	_image_section_header_new->PointerToRawData = (_image_section_header_new - 1)->PointerToRawData + (_image_section_header_new - 1)->SizeOfRawData;
	_image_section_header_new->Characteristics = 0x60000020;

	return newImageBufferp;
}

int main(int argc,char* argv[]){
    //char* filePath = "D:/C-language/file/notepad.exe";  //你要打开的PE文件绝对路径
    //char* storagePath = "D:/C-language/file/notepad_new.exe"; //保存路径

	char* filePath = "D:/IPMsg/ipmsg.exe";  //你要打开的PE文件绝对路径
    char* storagePath = "D:/IPMsg/ipmsg_new.exe"; //保存路径

	char* fileBufferp = to_FileBuffer(filePath);
	char* imageBufferp = fileBuffer_to_ImageBuffer(fileBufferp);
	char* newImageBufferp = add_one_section(imageBufferp);

	if(!add_shellcode_to_new_section(newImageBufferp)){
		printf("在新节中注入shellcode失败");
		getchar();
		return 0;
	}
	
	char* newBufferp = imageBuffer_to_NewBuffer(newImageBufferp);
	DWORD size = compute_NewBuffer_size(newBufferp);
	int isSucceed = save_to_disk(newBufferp,storagePath,size);
	if(!isSucceed){
		printf("存盘失败");
    	getchar();
    }else
		printf("存盘成功");
	
	free(fileBufferp);
	free(imageBufferp);
	free(newImageBufferp);
	free(newBufferp);

	return 0;
}

2.编程实现:扩大最后一个节,并添加代码

一定要新申请一块内存NewImageBuffer,来存储扩大后的PE文件,不能在原有的ImageBuffer上面扩大

#include "stdafx.h"
#include <stdlib.h>
#include <string.h>

typedef unsigned short WORD;
typedef unsigned int DWORD;
typedef unsigned char BYTE;

#define MZ 0x5A4D
#define PE 0x4550
#define IMAGE_SIZEOF_SHORT_NAME 8
#define MessageBox_Address 0x77D36476 //宏定义记事本的MessageBox内存地址

//shellcode
BYTE shellcode[] = {
	0x6A,0x00,0x6A,0x00,0x6A,0x00,0x6A,0x00,
	0xE8,0x00,0x00,0x00,0x00,
	0xE9,0x00,0x00,0x00,0x00
};

//DOS头
struct _IMAGE_DOS_HEADER {
	WORD e_magic;  //MZ标记
	WORD e_cblp;
	WORD e_cp;
	WORD e_crlc;
	WORD e_cparhdr;
	WORD e_minalloc;
	WORD e_maxalloc;
	WORD e_ss;
	WORD e_sp;
	WORD e_csum;
	WORD e_ip;
	WORD e_cs;
	WORD e_lfarlc;
	WORD e_ovno;
	WORD e_res[4];
	WORD e_oemid;
	WORD e_oeminfo;
	WORD e_res2[10];
	DWORD e_lfanew;  //PE文件真正开始的偏移地址
};

//标准PE头
struct _IMAGE_FILE_HEADER {
	WORD Machine;  //文件运行平台
	WORD NumberOfSections;  //节数量
	DWORD TimeDateStamp;  //时间戳
	DWORD PointerToSymbolTable;
	DWORD NumberOfSymbols;
	WORD SizeOfOptionalHeader;  //可选PE头大小
	WORD Characteristics;  //特征值
};

//可选PE头
struct _IMAGE_OPTIONAL_HEADER {
	WORD Magic;  //文件类型
	BYTE MajorLinkerVersion;
	BYTE MinorLinkerVersion;
	DWORD SizeOfCode;   //代码节文件对齐后的大小
	DWORD SizeOfInitializedData;  //初始化数据文件对齐后的大小
	DWORD SizeOfUninitializedData;  //未初始化数据文件对齐后大小
	DWORD AddressOfEntryPoint;  //程序入口点(偏移量)
	DWORD BaseOfCode;  //代码基址
	DWORD BaseOfData;  //数据基址
	DWORD ImageBase;   //内存镜像基址
	DWORD SectionAlignment;  //内存对齐粒度
	DWORD FileAlignment;  //文件对齐粒度
	WORD MajorOperatingSystemVersion;
	WORD MinorOperatingSystemVersion;
	WORD MajorImageVersion;
	WORD MinorImageVersion;
	WORD MajorSubsystemVersion;
	WORD MinorSubsystemVersion;
	DWORD Win32VersionValue;
	DWORD SizeOfImage;  //文件装入虚拟内存后大小
	DWORD SizeOfHeaders;  //DOS、NT头和节表大小
	DWORD CheckSum;  //校验和
	WORD Subsystem;
	WORD DllCharacteristics;
	DWORD SizeOfStackReserve;  //预留堆栈大小
	DWORD SizeOfStackCommit;  //实际分配堆栈大小
	DWORD SizeOfHeapReserve;  //预留堆大小
	DWORD SizeOfHeapCommit;  //实际分配堆大小
	DWORD LoaderFlags;
	DWORD NumberOfRvaAndSizes;  //目录项数目
	//_IMAGE_DATA_DIRECTORY DataDirectory[16];  //这个先不管
};

//NT头
struct _IMAGE_NT_HEADERS {
	DWORD Signature;  //PE签名
	_IMAGE_FILE_HEADER FileHeader;
	_IMAGE_OPTIONAL_HEADER OptionalHeader;
};

//节表
struct _IMAGE_SECTION_HEADER{
	BYTE Name[IMAGE_SIZEOF_SHORT_NAME];  //节表名
	union{
		DWORD PhysicalAddress;
		DWORD VirtualSize;  //内存中未对齐大小
	}Misc;
	DWORD VirtualAddress;  //该节在内存中偏移地址
	DWORD SizeOfRawData;  //该节在硬盘上文件对齐后大小
	DWORD PointerToRawData;  //该节在硬盘上文件对齐后偏移地址
	DWORD PointerToRelocations;
	DWORD PointerToLinenumbers;
	WORD NumberOfRelocations;
	WORD NumberOfLinenumbers;
	DWORD Characteristics;  //该节特征属性
};

/*计算文件大小函数
参数:文件绝对路径
返回值:返回文件大小(单位字节)
*/
int compute_file_size(char* filePath){
	FILE* fp = fopen(filePath,"rb");
    if(!fp){
		printf("打开文件失败");
		exit(0);
	}
    fseek(fp,0,2);
	int size = ftell(fp);
	//fseek(fp,0,0); 单纯计算文件大小,就不需要还原指针了
	fclose(fp);
	return size;
}

/*将文件读入FileBuffer函数
参数:文件绝对路径
返回值:FileBuffer起始地址
*/
char* to_FileBuffer(char* filePath){
    FILE* fp = fopen(filePath,"rb");
    if(!fp){
		printf("打开文件失败");
		exit(0);
	}
    int size = compute_file_size(filePath);
    
	char* mp = (char*)malloc(sizeof(char) * size);  //分配内存空间
    if(!mp){
        printf("分配空间失败");
        fclose(fp);
        exit(0);
    }
    
    int isSucceed = fread(mp,size,1,fp);
    if(!isSucceed){
        printf("读取数据失败");
        free(mp);
        fclose(fp);
        exit(0);
    }
    
    fclose(fp);
    return mp;
}

/*FileBuffer到ImageBuffer函数
参数:FileBuffer起始地址
返回值:ImageBuffer起始地址
*/
char* fileBuffer_to_ImageBuffer(char* fileBufferp){

	_IMAGE_DOS_HEADER* _image_dos_header = NULL;
	_IMAGE_FILE_HEADER* _image_file_header = NULL;
	_IMAGE_OPTIONAL_HEADER* _image_optional_header = NULL;
	_IMAGE_SECTION_HEADER* _image_section_header = NULL;

	_image_dos_header = (_IMAGE_DOS_HEADER*)fileBufferp;
	_image_file_header = (_IMAGE_FILE_HEADER*)(fileBufferp + _image_dos_header->e_lfanew + 4);
	_image_optional_header = (_IMAGE_OPTIONAL_HEADER*)((char*)_image_file_header + 20);
	_image_section_header = (_IMAGE_SECTION_HEADER*)((char*)_image_optional_header + _image_file_header->SizeOfOptionalHeader);

	DWORD sizeofImage = _image_optional_header->SizeOfImage;
	char* imageBufferp = (char*)malloc(sizeofImage);
	if(NULL == imageBufferp){
        printf("动态申请ImageBuffer内存失败\n");
        exit(0);
    }
	for(DWORD i = 0;i < sizeofImage;i++){
		*(imageBufferp + i) = 0x00;
	}

	//strncpy(imageBufferp,fileBufferp,_image_optional_header->SizeOfHeaders); 因为imageBufferp,fileBufferp值会变,所以这个方法不太好
	for(i = 0;i < _image_optional_header->SizeOfHeaders;i++){
		*(imageBufferp + i) = *(fileBufferp + i);
	}
	

	for(i = 0;i < _image_file_header->NumberOfSections;i++){
		for(DWORD j = 0;j < _image_section_header->SizeOfRawData;j++){
			*(imageBufferp + _image_section_header->VirtualAddress + j) = *(fileBufferp + 
				_image_section_header->PointerToRawData + j);
		}
		_image_section_header++;
	}

	return imageBufferp;
}

/*计算NewBuffer大小函数
参数:NewBuffer起始地址
返回值:unsigned int类型(单位:字节)
*/
DWORD compute_NewBuffer_size(char* newBufferp){
	_IMAGE_DOS_HEADER* _image_dos_header = NULL;
	_IMAGE_FILE_HEADER* _image_file_header = NULL;
	_IMAGE_OPTIONAL_HEADER* _image_optional_header = NULL;
	_IMAGE_SECTION_HEADER* _image_section_header = NULL;

	_image_dos_header = (_IMAGE_DOS_HEADER*)newBufferp;
	_image_file_header = (_IMAGE_FILE_HEADER*)(newBufferp + _image_dos_header->e_lfanew + 4);
	_image_optional_header = (_IMAGE_OPTIONAL_HEADER*)((char*)_image_file_header + 20);
	_image_section_header = (_IMAGE_SECTION_HEADER*)((char*)_image_optional_header + _image_file_header->SizeOfOptionalHeader);
	
	_IMAGE_SECTION_HEADER* last_image_section_header = _image_section_header + _image_file_header->NumberOfSections - 1;
	DWORD sizeofNewBuffer = last_image_section_header->PointerToRawData + last_image_section_header->SizeOfRawData;
	return sizeofNewBuffer;
}

/*ImageBuffer到NewBuffer函数
参数:ImageBuffer起始地址
返回值:NewBuffer起始地址
*/
char* imageBuffer_to_NewBuffer(char* imageBufferp){
	
	_IMAGE_DOS_HEADER* _image_dos_header = NULL;
	_IMAGE_FILE_HEADER* _image_file_header = NULL;
	_IMAGE_OPTIONAL_HEADER* _image_optional_header = NULL;
	_IMAGE_SECTION_HEADER* _image_section_header = NULL;

	_image_dos_header = (_IMAGE_DOS_HEADER*)imageBufferp;
	_image_file_header = (_IMAGE_FILE_HEADER*)(imageBufferp + _image_dos_header->e_lfanew + 4);
	_image_optional_header = (_IMAGE_OPTIONAL_HEADER*)((char*)_image_file_header + 20);
	_image_section_header = (_IMAGE_SECTION_HEADER*)((char*)_image_optional_header + _image_file_header->SizeOfOptionalHeader);

	//重新计算newBuffer需要大小
	/*方法一:SizeOfHEADER + 所有节的SizeOfRawData之和
	int sizeofSections = 0;  
	_IMAGE_SECTION_HEADER* _image_section_header_temp = _image_section_header;
	for(DWORD i = 0;i < _image_file_header->NumberOfSections;i++){
		sizeofSections += _image_section_header_temp->SizeOfRawData;
		_image_section_header_temp++;
	}
	char* newBufferp = (char*)malloc(_image_optional_header->SizeOfHeaders + sizeofSections);
	*/
	
	//方法二:使用最后一个节的文件偏移地址 + 最后一个节对齐后的大小
	DWORD size = compute_NewBuffer_size(imageBufferp);
	char* newBufferp = (char*)malloc(size);
	if(NULL == newBufferp){
        printf("NewBuffer内存分配失败\n");
        exit(0);
    }
	for(DWORD i = 0;i < size;i++){
		*(newBufferp + i) = 0x00;
	}
	
	
	for(i = 0;i < _image_optional_header->SizeOfHeaders;i++){
		*(newBufferp + i) = *(imageBufferp + i);
	}
	
	for(i = 0;i < _image_file_header->NumberOfSections;i++){
		for(DWORD j = 0;j < _image_section_header->SizeOfRawData;j++){ //不用VirtualSize因为害怕会多复制覆盖下一个节
			*(newBufferp + _image_section_header->PointerToRawData + j) = *(imageBufferp + 
				_image_section_header->VirtualAddress + j);
		}
		_image_section_header++;
	}

	return newBufferp;
}	

/*NewBuffer存盘函数
参数:需要写出数据的内存首地址,保存绝对路径,写出的数据大小(单位:字节)
返回值:成功返回1,失败返回0
*/
int save_to_disk(char* newBufferp,char* storagePath,DWORD size){
	FILE* fp = fopen(storagePath,"wb");
	if(!fp){
		printf("打开文件失败");
		return 0;
	}
	int isSucceed = fwrite(newBufferp,size,1,fp);
	if(!isSucceed){
        free(newBufferp);
        fclose(fp);
        return 0;
    }
	fclose(fp);
	return 1;
}

/*向新增节中注入shellcode函数
参数:要注入的可执行文件的ImageBuffer起始地址
返回值:0为注入失败、1为注入成功
*/
/*int add_shellcode_to_new_section(char* imageBufferp){
	_IMAGE_DOS_HEADER* _image_dos_header = NULL;
	_IMAGE_FILE_HEADER* _image_file_header = NULL;
	_IMAGE_OPTIONAL_HEADER* _image_optional_header = NULL;
	_IMAGE_SECTION_HEADER* _image_section_header = NULL;

	_image_dos_header = (_IMAGE_DOS_HEADER*)imageBufferp;
	_image_file_header = (_IMAGE_FILE_HEADER*)(imageBufferp + _image_dos_header->e_lfanew + 4);
	_image_optional_header = (_IMAGE_OPTIONAL_HEADER*)((char*)_image_file_header + 20);
	_image_section_header = (_IMAGE_SECTION_HEADER*)((char*)_image_optional_header + _image_file_header->SizeOfOptionalHeader);
	
    //定位到新节表起始地址
	_IMAGE_SECTION_HEADER* target_image_section_header = _image_section_header + _image_file_header->NumberOfSections - 1;
	
	//判断新节是否存的下shellcode
	if((int)(target_image_section_header->Misc.VirtualSize) < (int)(sizeof(shellcode) / sizeof(shellcode[0]))){
		printf("新节大小不够,添加失败");
		return 0;
	}

	//将shellcode注入到新节
	char* shellcodep = imageBufferp + target_image_section_header->VirtualAddress;
	char* newOEP = shellcodep;  //此shellcode注入起始地址即为新的OEP地址
	memcpy(shellcodep,shellcode,sizeof(shellcode) / sizeof(shellcode[0])); //注意memcpy函数不改变传入的指针的值!!
	
	//修改call MessageBox的硬编码E8后面的4字节
	DWORD E8_fill_value = MessageBox_Address - ((shellcodep - imageBufferp + _image_optional_header->ImageBase) + 0xD);
	*(DWORD*)(shellcodep + 9) = E8_fill_value;

	//修改jmp 原OEP的硬编码E9后面的4字节
	DWORD E9_fill_value = _image_optional_header->AddressOfEntryPoint + _image_optional_header->ImageBase - ((shellcodep - imageBufferp + _image_optional_header->ImageBase) + 0x12);
	*(DWORD*)(shellcodep + 14) = E9_fill_value;

	//修改OEP指向shellcode
	_image_optional_header->AddressOfEntryPoint = (DWORD)(newOEP - imageBufferp);

	return 1;
}*/

/*计算最小公倍数函数
参数:传入要计算的两个数
返回值:两数的最小公倍数
*/
int least_common_multiple(int a,int b){
	if(a <= 0 || b <= 0)
		return -1;
	int max = (a > b ? a : b);
    while(1){
        if(max % a == 0 && max % b == 0){
            break;
        }
        max++;
    }
	return max;
}

/*在文件最后新增节函数
参数:要新增节的可执行文件的ImageBuffer起始地址
返回值:新增节后的NewImageBuffer内存的起始地址
*/
/*char* add_one_section(char* imageBufferp){
	_IMAGE_DOS_HEADER* _image_dos_header = NULL;
	_IMAGE_FILE_HEADER* _image_file_header = NULL;
	_IMAGE_OPTIONAL_HEADER* _image_optional_header = NULL;
	_IMAGE_SECTION_HEADER* _image_section_header = NULL;

    //定义原来的ImageBuffer几个指针(因为要用到几个值用于创建新的NewImageBuffer内存)
	_image_dos_header = (_IMAGE_DOS_HEADER*)imageBufferp;
	_image_file_header = (_IMAGE_FILE_HEADER*)(imageBufferp + _image_dos_header->e_lfanew + 4);
	_image_optional_header = (_IMAGE_OPTIONAL_HEADER*)((char*)_image_file_header + 20);
	
	//先计算添加新节后的NewImageBuffer的大小,并初始化内存
	int expand_value = least_common_multiple(_image_optional_header->FileAlignment,_image_optional_header->SectionAlignment);//这里为了方便直接把新节大小设为文件对齐粒度和内存对齐粒度的最小公倍数即可
	char* newImageBufferp = (char*)malloc(_image_optional_header->SizeOfImage + expand_value);
	for(int i = 0;i < (int)_image_optional_header->SizeOfImage + expand_value;i++){
		*(newImageBufferp + i) = 0x00;
	}
	memcpy(newImageBufferp,imageBufferp,_image_optional_header->SizeOfImage);//不改变指针哦
	_image_dos_header = (_IMAGE_DOS_HEADER*)newImageBufferp;
	_image_file_header = (_IMAGE_FILE_HEADER*)(newImageBufferp + _image_dos_header->e_lfanew + 4);
	_image_optional_header = (_IMAGE_OPTIONAL_HEADER*)((char*)_image_file_header + 20);
	_image_section_header = (_IMAGE_SECTION_HEADER*)((char*)_image_optional_header + _image_file_header->SizeOfOptionalHeader);

	//判断节表末尾是否有空间新增节表
	if(_image_optional_header->SizeOfHeaders - _image_dos_header->e_lfanew - 4 - 20 - _image_file_header->SizeOfOptionalHeader - 
		_image_file_header->NumberOfSections * 40 < 80){
		printf("空间不足,无法新增节表");
		getchar();
		exit(0);
	}
	for(i = 0;i < 80;i++){
		if(*((char*)(_image_section_header + _image_file_header->NumberOfSections)+ i) != 0){
			printf("节表后80字节数据不全为0x00,有文件数据,无法新增节表");
			getchar();
			exit(0);
		}
	}
	
	//新增节
	DWORD original_SizeOfImage = _image_optional_header->SizeOfImage; //记录下原来的SizeOfImage
	_image_optional_header->SizeOfImage += expand_value;

	//新增节表
	_IMAGE_SECTION_HEADER* _image_section_header_new = (_IMAGE_SECTION_HEADER*)(char*)_image_section_header + _image_file_header->NumberOfSections;
	for(i = 0;i < 40;i++){
		*((char*)_image_section_header_new + i) = *((char*)_image_section_header + i);
	}
	_image_file_header->NumberOfSections++;

	//修改新增节表信息
	char* name = (char*)_image_section_header_new->Name;
	char* newName = ".newsec";
	strncpy(name,newName,IMAGE_SIZEOF_SHORT_NAME);
	_image_section_header_new->Misc.VirtualSize = expand_value;
	_image_section_header_new->VirtualAddress = original_SizeOfImage;
	_image_section_header_new->SizeOfRawData = expand_value;
	_image_section_header_new->PointerToRawData = (_image_section_header_new - 1)->PointerToRawData + (_image_section_header_new - 1)->SizeOfRawData;
	_image_section_header_new->Characteristics = 0x60000020;

	return newImageBufferp;
}*/

/*扩大最后一个节函数
参数:要扩大节的可执行文件的ImageBuffer起始地址
返回值:扩大节后的NewImageBuffer内存的起始地址
*/
char* expand_last_section(char* imageBufferp){
	_IMAGE_DOS_HEADER* _image_dos_header = NULL;
	_IMAGE_FILE_HEADER* _image_file_header = NULL;
	_IMAGE_OPTIONAL_HEADER* _image_optional_header = NULL;
	_IMAGE_SECTION_HEADER* _image_section_header = NULL;

	_image_dos_header = (_IMAGE_DOS_HEADER*)imageBufferp;
	_image_file_header = (_IMAGE_FILE_HEADER*)(imageBufferp + _image_dos_header->e_lfanew + 4);
	_image_optional_header = (_IMAGE_OPTIONAL_HEADER*)((char*)_image_file_header + 20);

	//计算扩大多少,并初始化NewImageBuffer
	int expand_value = least_common_multiple(_image_optional_header->FileAlignment,_image_optional_header->SectionAlignment);//这里为了方便直接把需要扩大的大小设为文件对齐粒度和内存对齐粒度的最小公倍数即可
	char* newImageBufferp = (char*)malloc(_image_optional_header->SizeOfImage + expand_value);
	for(int i = 0;i < (int)_image_optional_header->SizeOfImage + expand_value;i++){
		*(newImageBufferp + i) = 0x00;
	}
	memcpy(newImageBufferp,imageBufferp,_image_optional_header->SizeOfImage);//不改变指针哦
	_image_dos_header = (_IMAGE_DOS_HEADER*)newImageBufferp;
	_image_file_header = (_IMAGE_FILE_HEADER*)(newImageBufferp + _image_dos_header->e_lfanew + 4);
	_image_optional_header = (_IMAGE_OPTIONAL_HEADER*)((char*)_image_file_header + 20);
	_image_section_header = (_IMAGE_SECTION_HEADER*)((char*)_image_optional_header + _image_file_header->SizeOfOptionalHeader);

	//修改SizeofImage:原来的SizeOfImage + Ex(因为原来的SizeOfImage是内存对齐的整数倍,Ex也是通过内存对齐和文件对齐的最小公倍数算出来的,所以相加一定还是内存对齐的整数倍)
	DWORD original_SizeOfImage = _image_optional_header->SizeOfImage; //记录下原来的SizeOfImage
	_image_optional_header->SizeOfImage += expand_value;

	//修改节表信息
	_IMAGE_SECTION_HEADER* _image_section_header_last = _image_section_header + _image_file_header->NumberOfSections - 1;
	_image_section_header_last->Misc.VirtualSize = original_SizeOfImage - _image_section_header_last->VirtualAddress + expand_value;
	_image_section_header_last->SizeOfRawData = _image_section_header_last->Misc.VirtualSize;

	//修改节属性,保证可执行
	_image_section_header_last->Characteristics = _image_section_header_last->Characteristics | 0x60000020;
	return newImageBufferp;
}

/*向扩大的节中注入shellcode函数
参数:要注入的可执行文件的ImageBuffer起始地址
返回值:0表示注入失败,1表示注入成功
*/
int add_shellcode_to_expanded_section(char* imageBufferp){
	_IMAGE_DOS_HEADER* _image_dos_header = NULL;
	_IMAGE_FILE_HEADER* _image_file_header = NULL;
	_IMAGE_OPTIONAL_HEADER* _image_optional_header = NULL;
	_IMAGE_SECTION_HEADER* _image_section_header = NULL;

	_image_dos_header = (_IMAGE_DOS_HEADER*)imageBufferp;
	_image_file_header = (_IMAGE_FILE_HEADER*)(imageBufferp + _image_dos_header->e_lfanew + 4);
	_image_optional_header = (_IMAGE_OPTIONAL_HEADER*)((char*)_image_file_header + 20);
	_image_section_header = (_IMAGE_SECTION_HEADER*)((char*)_image_optional_header + _image_file_header->SizeOfOptionalHeader);
	
	//判断是否存的下shellcode,这里直接在Ex区域内注入,不考虑原来的空白区
	if((int)(least_common_multiple(_image_optional_header->FileAlignment,_image_optional_header->SectionAlignment)) < (int)(sizeof(shellcode) / sizeof(shellcode[0]))){
		printf("新节大小不够,添加失败");
		return 0;
	}

	//将shellcode注入到Ex
	char* shellcodep = imageBufferp + _image_optional_header->SizeOfImage - least_common_multiple(_image_optional_header->FileAlignment,_image_optional_header->SectionAlignment);
	char* newOEP = shellcodep;  //此shellcode注入起始地址即为新的OEP地址
	memcpy(shellcodep,shellcode,sizeof(shellcode) / sizeof(shellcode[0])); //注意memcpy函数不改变传入的指针的值!!
	
	//修改call MessageBox的硬编码E8后面的4字节
	DWORD E8_fill_value = MessageBox_Address - ((shellcodep - imageBufferp + _image_optional_header->ImageBase) + 0xD);
	*(DWORD*)(shellcodep + 9) = E8_fill_value;

	//修改jmp 原OEP的硬编码E9后面的4字节
	DWORD E9_fill_value = _image_optional_header->AddressOfEntryPoint + _image_optional_header->ImageBase - ((shellcodep - imageBufferp + _image_optional_header->ImageBase) + 0x12);
	*(DWORD*)(shellcodep + 14) = E9_fill_value;

	//修改OEP指向shellcode
	_image_optional_header->AddressOfEntryPoint = (DWORD)(newOEP - imageBufferp);

	return 1;
}

int main(int argc,char* argv[]){
    char* filePath = "D:/C-language/file/notepad.exe";  //你要打开的PE文件绝对路径
    char* storagePath = "D:/C-language/file/notepad_new.exe"; //保存路径

	//char* filePath = "D:/IPMsg/ipmsg.exe";  //你要打开的PE文件绝对路径
    //char* storagePath = "D:/IPMsg/ipmsg_new.exe"; //保存路径

	char* fileBufferp = to_FileBuffer(filePath);
	char* imageBufferp = fileBuffer_to_ImageBuffer(fileBufferp);
	char* newImageBufferp = expand_last_section(imageBufferp);

	/*if(!add_shellcode_to_new_section(newImageBufferp)){
		printf("在新节中注入shellcode失败");
		getchar();
		return 0;
	}*/
	if(!add_shellcode_to_expanded_section(newImageBufferp)){
		printf("在新节中注入shellcode失败");
		getchar();
		return 0;
	}
	
	char* newBufferp = imageBuffer_to_NewBuffer(newImageBufferp);
	DWORD size = compute_NewBuffer_size(newBufferp);
	int isSucceed = save_to_disk(newBufferp,storagePath,size);
	if(!isSucceed){
		printf("存盘失败");
    	getchar();
    }else
		printf("存盘成功");
	
	free(fileBufferp);
	free(imageBufferp);
	free(newImageBufferp);
	free(newBufferp);

	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值