Windows 逆向-合并节

Oyst3r 于 2024-02-19 发布

前言

这个越来越简单了昂,比扩大节还简单,不用再创造一个 newimagebuffer 了,就是像写那个添加 shellcode 的代码一样,不需要返回什么地址,就直接在传入函数的地址上做文章就行

课堂

合并节

这里我也写的不想写了,直接看课件吧,本来这节课也没啥好说的

就跟着作业一起给了,下面是 main 函数内容

#include <iostream>
#include <windows.h>
#include "Fuction.h"

int main()
{
	char* FilePath = (char*)"D:/Everything333.exe";  //打开的PE文件绝对路径
	char* SavePath = (char*)"D:/333.exe"; //保存的路径

	char* FileBufferPoint = ReadPeFile(FilePath);
	char* ImageBufferPoint = CopyFileBufferToImageBuffer(FileBufferPoint);/*
	char* NewImageBufferPoint = AddLastSection(ImageBufferPoint);
	int flag = AddShellCodeToSection(NewImageBufferPoint, 4);
	if (!flag) {
		printf("shellcode注入失败\n");
		return 0;
	}*/
	MergeSection(ImageBufferPoint);
	char* NewBufferPoint = CopyImageBufferToNewBuffer(ImageBufferPoint);
	int flag_2 = MemeryToFile(NewBufferPoint, SavePath);
	if (flag_2) {
		printf("全部成功,程序已在对应路径生成\n");
	}
	else {
		printf("失败,再检查检查\n");
	}

	free(FileBufferPoint);
	free(ImageBufferPoint);
	//free(NewImageBufferPoint);
	free(NewBufferPoint);
	return 0;
}

然后是主要的功能函数

#include "Fuction.h"
#include <cstdio>
#include <atomic>
//这个模块里面写的就是PE所要用到功能的集合,统一写在了这里
//给变量换个名字,写起来更方便一点
typedef unsigned int DWORD;
typedef unsigned short WORD;
typedef unsigned char BYTE;

//这个是shellcode的代码
BYTE shellcode[] = {
	0x6A,0x00,0x6A,0x00,0x6A,0x00,0x6A,0x00,
	0xE8,0x00,0x00,0x00,0x00,
	0xE9,0x00,0x00,0x00,0x00
};

//这个就是一些PE里面固定的值
#define MZ 0x5A4D
#define PE 0x4550
#define IMAGE_SIZEOF_SHORT_NAME 8
#define MessageBox_Address 0x758BA000//这个是Everything2.exe的
#define Example_Add_Section_Size 0x1000

//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;  //该节特征属性
};
//OK至此所有用到的结构体就都定义完了,这个海东老师的课件里面都有,气死但他不给代码

int PeFileSize(char* FilePath) {
	//PeFileSize:计算文件在硬盘上的大小
	//参数说明:
	//FilePath:指向文件的绝对路径
	//返回值说明:
	//读取成功返回文件在硬盘上的大小,读取失败则返回0
	FILE* pf = fopen(FilePath, "rb");
	if (pf == NULL) {
		perror("打开文件错误");
		fclose(pf);
		return 0;
	}
	fseek(pf, 0, 2);
	int length = ftell(pf);
	fseek(pf, 0, 0);
	fclose(pf);
	printf("已经成功读取该文件的大小\n");
	return length;
}

char* ReadPeFile(char* FilePath) {
	//ReadPeFile:将可执行文件从硬盘读取到FileBuffer
	//参数说明:
	//FilePath:指向文件的绝对路径
	//返回值说明:
	//读取成功返回FileBuffer的首地址,读取失败则返回0

	FILE* pf = fopen(FilePath, "rb");
	if (pf == NULL) {
		perror("打开文件错误");
		fclose(pf);
		return 0;
	}

	int length = PeFileSize(FilePath);
	char* ptr_1 = (char*)malloc(sizeof(char) * length);
	if (ptr_1 == NULL) {
		perror("File堆内存分配失败");
		fclose(pf);
		return 0;
	}
	memset(ptr_1, 0, sizeof(char) * length);
	int flag = fread(ptr_1, length, 1, pf);
	if (flag == NULL) {
		perror("读取数据失败,请检查文件路径");
		fclose(pf);
		free(ptr_1);
		return 0;
	}

	fclose(pf);
	//这里之所以没有free(ptr),原因是咱们下面还要用到这块堆的内存,所以可以在main函数结束之前释放掉就行
	printf("已成功将可执行文件从硬盘读取到FileBuffer\n");
	return ptr_1;
}

char* CopyFileBufferToImageBuffer(char* FileBufferPoint) {
	//CopyFileBufferToImageBuffer:将可执行文件从FileBuffer读取到ImageBuffer
	//参数说明:
	//FileBufferPoint:指向可执行文件在FileBuffer的地址
	//返回值说明:
	//读取成功返回ImageBuffer的首地址,读取失败则返回0

	_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*)FileBufferPoint;
	//下面这个别忘记了还有一个PE标记的大小,为4个字节
	_image_file_header = (_IMAGE_FILE_HEADER*)(FileBufferPoint + _image_dos_header->e_lfanew + sizeof(PE));
	_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);

	int size = _image_optional_header->SizeOfImage;
	char* ptr_2 = (char*)malloc(size);
	if (ptr_2 == NULL) {
		perror("Image堆内存分配失败");
		return 0;
	}
	memset(ptr_2, 0, size);

	for (unsigned int i = 0; i < _image_optional_header->SizeOfHeaders; i++) {
		*(ptr_2 + i) = *(FileBufferPoint + i);
	}

	for (int i = 0; i < _image_file_header->NumberOfSections; i++) {
		char* temp_1 = FileBufferPoint + _image_section_header->PointerToRawData;
		char* temp_2 = ptr_2 + _image_section_header->VirtualAddress;
		for (unsigned int j = 0; j < _image_section_header->SizeOfRawData; j++) {
			*(temp_2 + j) = *(temp_1 + j);
		}
		_image_section_header++;
	}

	printf("已成功将可执行文件从FileBuffer读取到ImageBuffer\n");
	return ptr_2;
}

char* CopyImageBufferToNewBuffer(char* ImageBufferPoint) {
	//CopyImageBufferToNewBuffer:将可执行文件从ImageBuffer读取到NewBuffer(其实也就是FileBuffer)
	//参数说明:
	//ImageBufferPoint:指向可执行文件在ImageBuffer的地址
	//返回值说明:
	//读取成功返回NewBuffer(其实也就是FileBuffer)的首地址,读取失败则返回0

	_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*)ImageBufferPoint;
	//下面这个别忘记了还有一个PE标记的大小,为4个字节
	_image_file_header = (_IMAGE_FILE_HEADER*)(ImageBufferPoint + _image_dos_header->e_lfanew + sizeof(PE));
	_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);

	int ImageSectionSize = 0;
	_IMAGE_SECTION_HEADER* _image_section_header_temp = _image_section_header;
	for (int i = 0; i < _image_file_header->NumberOfSections; i++) {
		ImageSectionSize += _image_section_header_temp->SizeOfRawData;
		_image_section_header_temp++;
	}
	char* ptr_3 = (char*)malloc(_image_optional_header->SizeOfHeaders + ImageSectionSize);
	if (ptr_3 == NULL) {
		perror("NewBuffer堆内存分配失败");
		return 0;
	}
	memset(ptr_3, 0, _image_optional_header->SizeOfHeaders + ImageSectionSize);

	for (unsigned int i = 0; i < _image_optional_header->SizeOfHeaders; i++) {
		*(ptr_3 + i) = *(ImageBufferPoint + i);
	}

	for (int i = 0; i < _image_file_header->NumberOfSections; i++) {
		char* temp_1 = ImageBufferPoint + _image_section_header->VirtualAddress;
		char* temp_2 = ptr_3 + _image_section_header->PointerToRawData;
		for (unsigned int j = 0; j < _image_section_header->SizeOfRawData; j++) {
			*(temp_2 + j) = *(temp_1 + j);
		}
		_image_section_header++;
	}

	printf("已成功将可执行文件从ImageBuffer读取到NewBuffer\n");
	return ptr_3;
}

int MemeryToFile(char* NewBufferPoint, char* SavePath) {
	//MemeryTOFile:将可执行文件从NewBuffer读取到硬盘
	//参数说明:
	//NewBufferPoint:指向NewBuffer的首地址
	//SavePath:指向另存为文件的绝对路径
	//返回值说明:
	//读取成功返回1,读取失败则返回0

	_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*)NewBufferPoint;
	//下面这个别忘记了还有一个PE标记的大小,为4个字节
	_image_file_header = (_IMAGE_FILE_HEADER*)(NewBufferPoint + _image_dos_header->e_lfanew + sizeof(PE));
	_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);

	int ImageSectionSize = 0;
	_IMAGE_SECTION_HEADER* _image_section_header_temp = _image_section_header;
	for (int i = 0; i < _image_file_header->NumberOfSections; i++) {
		ImageSectionSize += _image_section_header_temp->SizeOfRawData;
		_image_section_header_temp++;
	}

	int size = _image_optional_header->SizeOfHeaders + ImageSectionSize;
	FILE* pf = fopen(SavePath, "wb");
	if (pf == NULL) {
		perror("打开文件错误");
		fclose(pf);
		return 0;
	}


	int flag = fwrite(NewBufferPoint, size, 1, pf);
	if (flag == NULL) {
		perror("存文件出现错误,请检查文件路径是否有效");
		free(NewBufferPoint);
		fclose(pf);
		return 0;
	}
	fclose(pf);
	return 1;
}

int ImageAddressToFileAddress(char* FileBufferPoint, int ImageAddress) {
	//ImageAddressToFileAddress:将ImageBuffer里面的节地址转换为对应的FileBuffer的节地址
	//参数说明:
	//FileBufferPoint:指向FileBuffer的首地址
	//ImageAddress:传入ImageBuffer里面的节地址
	//返回值说明:
	//转换成功返回节地址,地址不在节内或在空白区则返回0
	_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*)FileBufferPoint;
	//下面这个别忘记了还有一个PE标记的大小,为4个字节
	_image_file_header = (_IMAGE_FILE_HEADER*)(FileBufferPoint + _image_dos_header->e_lfanew + sizeof(PE));
	_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);

	int flag = 0;
	if (_image_section_header->VirtualAddress > ImageAddress - _image_optional_header->ImageBase) {
		return 0;
	}
	for (int i = 0; i < _image_file_header->NumberOfSections; i++) {
		if (ImageAddress - _image_optional_header->ImageBase >= _image_section_header->VirtualAddress && ImageAddress - _image_optional_header->ImageBase < _image_section_header->VirtualAddress + _image_section_header->Misc.VirtualSize) {
			flag = 1;
			break;
		}
		else {
			_image_section_header++;
		}
	}
	if (flag == 0) {
		return 0;
	}
	int TempAddress = ImageAddress - _image_optional_header->ImageBase - _image_section_header->VirtualAddress;
	return _image_section_header->PointerToRawData + TempAddress;
}

int AddShellCodeToSection(char* ImageBufferPoint, int SectionNum) {
	//AddShellCodeToSection:将shellcode注入到ImageBuffer里面的任意节
	//参数说明:
	//ImageBufferPoint:指向ImageBuffer的首地址
	//SectionNum:要注入的节的位置
	//返回值说明:
	//注入成功返回1,反之则返回0

	_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*)ImageBufferPoint;
	//下面这个别忘记了还有一个PE标记的大小,为4个字节
	_image_file_header = (_IMAGE_FILE_HEADER*)(ImageBufferPoint + _image_dos_header->e_lfanew + sizeof(PE));
	_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 (SectionNum < 0 || SectionNum > _image_file_header->NumberOfSections) {
		printf("输入的节不存在,请重新输入\n");
		return 0;
	}

	_IMAGE_SECTION_HEADER* temp_image_section_header = _image_section_header + SectionNum - 1;

	if ((int)(temp_image_section_header->SizeOfRawData - temp_image_section_header->Misc.VirtualSize) < sizeof(shellcode) / sizeof(shellcode[0])) {

		printf("该节空间不足,无法加壳");
		return 0;
	}

	char* ShellCodePoint = ImageBufferPoint + _image_optional_header->SizeOfImage - Example_Add_Section_Size;
	for (int i = 0; i < sizeof(shellcode) / sizeof(shellcode[0]); i++) {
		*(ShellCodePoint + i) = shellcode[i];
	}

	DWORD E8Address = (DWORD)ShellCodePoint - (DWORD)ImageBufferPoint + _image_optional_header->ImageBase + 8;
	DWORD E8Data = MessageBox_Address - (E8Address + 5);
	*(DWORD*)(ShellCodePoint + 9) = E8Data;

	DWORD E9Address = E8Address + 5;
	DWORD E9Data = _image_optional_header->ImageBase + _image_optional_header->AddressOfEntryPoint - (E9Address + 5);
	*(DWORD*)(ShellCodePoint + 14) = E9Data;

	_image_optional_header->AddressOfEntryPoint = (DWORD)(ShellCodePoint - ImageBufferPoint);
	temp_image_section_header->Characteristics = temp_image_section_header->Characteristics | 0x60000020;

	return 1;
}

char* AddSection(char* ImageBufferPoint) {
	//AddSection:给ImageBuffer添加一个节
	//参数说明:
	//ImageBufferPoint:指向ImageBuffer的首地址
	//返回值说明:
	//成功添加则返回NewImageBuffer的地址
	_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*)ImageBufferPoint;
	//下面这个别忘记了还有一个PE标记的大小,为4个字节
	_image_file_header = (_IMAGE_FILE_HEADER*)(ImageBufferPoint + _image_dos_header->e_lfanew + sizeof(PE));
	_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);

	//这里就给一个添加0x1000h作为示例
	char* NewImageBuffuerPoint = (char*)malloc(_image_optional_header->SizeOfImage + Example_Add_Section_Size);
	if (NewImageBuffuerPoint == NULL) {
		perror("Image堆内存分配失败");
		return 0;
	}
	memset(NewImageBuffuerPoint,0 ,_image_optional_header->SizeOfImage + Example_Add_Section_Size);
	memcpy(NewImageBuffuerPoint, ImageBufferPoint, _image_optional_header->SizeOfImage);

	_image_dos_header = (_IMAGE_DOS_HEADER*)NewImageBuffuerPoint;
	//下面这个别忘记了还有一个PE标记的大小,为4个字节
	_image_file_header = (_IMAGE_FILE_HEADER*)(NewImageBuffuerPoint + _image_dos_header->e_lfanew + sizeof(PE));
	_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) {
		perror("空间不足,无法新增节表");
		return 0;
	}
	for (int i = 0; i < 80;i++) {
		if (*((char*)(_image_section_header + _image_file_header->NumberOfSections)) != 0x00) {
			perror("剩余空间存在非0,无法新增节表,请选择前移或者合并末尾节");
			return 0;
		}
	}

	DWORD original_SizeOfImage = _image_optional_header->SizeOfImage;
	_image_optional_header->SizeOfImage += Example_Add_Section_Size;
	_IMAGE_SECTION_HEADER* NewSectionPoint = _image_section_header + _image_file_header->NumberOfSections;

	for (int i = 0; i < 40;i++) {
		*((char*)NewSectionPoint + i) = *((char*)_image_section_header + i);
	}

	_image_file_header->NumberOfSections++;

	char* name = (char*)NewSectionPoint->Name;
	char* newName = (char*)".newsec";
	strncpy(name, newName, IMAGE_SIZEOF_SHORT_NAME);
	NewSectionPoint->Misc.VirtualSize = IMAGE_SIZEOF_SHORT_NAME;
	NewSectionPoint->VirtualAddress = original_SizeOfImage;
	NewSectionPoint->PointerToRawData = (NewSectionPoint - 1)->PointerToRawData + (NewSectionPoint - 1)->SizeOfRawData;
	NewSectionPoint->SizeOfRawData = Example_Add_Section_Size + 0x200;
	NewSectionPoint->Characteristics = 0x60000020;

	return NewImageBuffuerPoint;
}

char* AddLastSection(char* ImageBufferPoint) {
	//AddSection:给ImageBuffer的最后一个节扩展
	//参数说明:
	//ImageBufferPoint:指向ImageBuffer的首地址
	//返回值说明:
	//成功添加则返回NewImageBuffer的地址
	_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*)ImageBufferPoint;
	//下面这个别忘记了还有一个PE标记的大小,为4个字节
	_image_file_header = (_IMAGE_FILE_HEADER*)(ImageBufferPoint + _image_dos_header->e_lfanew + sizeof(PE));
	_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);

	//这里就给一个添加0x1000h作为示例
	char* NewImageBuffuerPoint = (char*)malloc(_image_optional_header->SizeOfImage + Example_Add_Section_Size);
	if (NewImageBuffuerPoint == NULL) {
		perror("Image堆内存分配失败");
		return 0;
	}
	memset(NewImageBuffuerPoint, 0, _image_optional_header->SizeOfImage + Example_Add_Section_Size);
	memcpy(NewImageBuffuerPoint, ImageBufferPoint, _image_optional_header->SizeOfImage);

	_image_dos_header = (_IMAGE_DOS_HEADER*)NewImageBuffuerPoint;
	//下面这个别忘记了还有一个PE标记的大小,为4个字节
	_image_file_header = (_IMAGE_FILE_HEADER*)(NewImageBuffuerPoint + _image_dos_header->e_lfanew + sizeof(PE));
	_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 original_SizeOfImage = _image_optional_header->SizeOfImage;
	_image_optional_header->SizeOfImage += Example_Add_Section_Size;
	_IMAGE_SECTION_HEADER* LastSectionPoint = _image_section_header + _image_file_header->NumberOfSections - 1;

	LastSectionPoint->Misc.VirtualSize = _image_optional_header->SizeOfImage - LastSectionPoint->VirtualAddress;
	LastSectionPoint->SizeOfRawData = LastSectionPoint->Misc.VirtualSize + 0x200;
	LastSectionPoint->Characteristics = 0x60000020;

	return NewImageBuffuerPoint;
}

void MergeSection(char* ImageBufferPoint) {
	//MergeSection:给ImageBuffer的全部节都合并为一个节
	//参数说明:
	//ImageBufferPoint:指向ImageBuffer的首地址
	//返回值说明:
	//无返回值,直接运行看结果

	_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*)ImageBufferPoint;
	//下面这个别忘记了还有一个PE标记的大小,为4个字节
	_image_file_header = (_IMAGE_FILE_HEADER*)(ImageBufferPoint + _image_dos_header->e_lfanew + sizeof(PE));
	_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->Misc.VirtualSize = _image_optional_header->SizeOfImage - _image_section_header->VirtualAddress;
	_image_section_header->SizeOfRawData = _image_section_header->Misc.VirtualSize;

	//这步必不可少,一开始没弄对程序跑不起来,这个就是合并所有节的属性
	for (int i = 1; i < _image_file_header->NumberOfSections; i++) {
		_image_section_header->Characteristics = _image_section_header->Characteristics | (_image_section_header + i)->Characteristics;
	}

	for (int i = 0; i < 40 * _image_file_header->NumberOfSections; i++) {
		*((char*)(_image_section_header + 1) + i) = 0x00;
	}

	_image_file_header->NumberOfSections = 1;
}

确实没问题

数据目录

简介

当时在学 PE 的时候,咱们跳过了一个东西,就是在可选 PE 头里面的最后一个部分,就是叫这个_IMAGE_DATA_DIRECTORY DataDirectory[16];这个就是咱们今天学的数据目录

作用(为什么要有这个)

Win32 下,可执行文件的 PE 结构分节:有代码节,数据节等等节,但是不能理解成这些节中只有程序本身的数据等,编译器也会替我们往每一个节中加很多重要的内容:

综上所述:一个 PE 文件中,除了有程序员自己添加的内容(程序自身的数据),编译器也会向当中添加很多内容,编译器添加的内容有很多种类,各有各的目的和使用途径,那么这么多数据和内容肯定需要一个表格记录–数据目录,而且要编译器在添加时要遵循某些规则,以至于程序在运行时,会很方便的找到需要的信息和数据在哪里

总结:16 个数据目录分别记录了编译器往 PE 文件中写的 16 种不同的信息存在哪里

数据目录内容

可选 PE 头最后一个成员,就是数据目录,一共有 16 个

分别是:导出表、导入表、资源表、异常信息表、安全证书表、重定位表、调试信息表、版权所以表、全局指针表 TLS 表、加载配置表、绑定导入表、IAT 表、延迟导入表、COM 信息表 最后一个保留未使用

别看很多,然后最重要的其实就是这四张表

结构

struct _IMAGE_DATA_DIRECTORY{
        DWORD VirtualAddress;  //内存偏移,必须有
	DWORD Size;  //大小,破坏了也不会影响程序运行
};

别看每个表只有 8 个字节,但这 8 个字节仅仅就是记录了位置和大小,真正难的是它这个地址指向的内容,给大家一个 PE 的高清无码 pdf,快谢谢我

PE-Format下载

作业

4.就直接给代码了

main 函数的

#include <iostream>
#include "Fuction.h"

int main(){
	char* FilePath = (char*)"D:/Everything333.exe";  //打开的PE文件绝对路径
	PrintDirectory(FilePath);
	return 0;
}

头文件的

#pragma once
int PeFileSize(char* FilePath);
char* ReadPeFile(char* FilePath);
void PrintDirectory(char* FilePath);

功能文件的

#include "Fuction.h"
#include <stdlib.h>
#include <cstdio>
#include <atomic>

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

#define MZ 0x5A4D
#define PE 0x4550
#define IMAGE_SIZEOF_SHORT_NAME 8

//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;  //特征值
};

//数据目录
struct _IMAGE_DATA_DIRECTORY {
	DWORD VirtualAddress;
	DWORD Size;
};

//可选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 PeFileSize(char* FilePath) {
	//PeFileSize:计算文件在硬盘上的大小
	//参数说明:
	//FilePath:指向文件的绝对路径
	//返回值说明:
	//读取成功返回文件在硬盘上的大小,读取失败则返回0
	FILE* pf = fopen(FilePath, "rb");
	if (pf == NULL) {
		perror("打开文件错误");
		fclose(pf);
		return 0;
	}
	fseek(pf, 0, 2);
	int length = ftell(pf);
	fseek(pf, 0, 0);
	fclose(pf);
	printf("已经成功读取该文件的大小\n");
	return length;
}

char* ReadPeFile(char* FilePath) {
	//ReadPeFile:将可执行文件从硬盘读取到FileBuffer
	//参数说明:
	//FilePath:指向文件的绝对路径
	//返回值说明:
	//读取成功返回FileBuffer的首地址,读取失败则返回0

	FILE* pf = fopen(FilePath, "rb");
	if (pf == NULL) {
		perror("打开文件错误");
		fclose(pf);
		return 0;
	}

	int length = PeFileSize(FilePath);
	char* ptr_1 = (char*)malloc(sizeof(char) * length);
	if (ptr_1 == NULL) {
		perror("File堆内存分配失败");
		fclose(pf);
		return 0;
	}
	memset(ptr_1, 0, sizeof(char) * length);
	int flag = fread(ptr_1, length, 1, pf);
	if (flag == NULL) {
		perror("读取数据失败,请检查文件路径");
		fclose(pf);
		free(ptr_1);
		return 0;
	}

	fclose(pf);
	//这里之所以没有free(ptr),原因是咱们下面还要用到这块堆的内存,所以可以在main函数结束之前释放掉就行
	printf("已成功将可执行文件从硬盘读取到FileBuffer\n");
	return ptr_1;
}

void PrintDirectory(char* FilePath) {

	//PrintDirectory:打印一个程序的数据目录
	//参数说明:
	//FilePath:指向硬盘中文件的首地址
	//返回值说明:
	//无返回值,直接打印看结果
	char* FileBufferPoint = ReadPeFile(FilePath);
	_IMAGE_DOS_HEADER* _image_dos_header = NULL;
	_IMAGE_FILE_HEADER* _image_file_header = NULL;
	_IMAGE_OPTIONAL_HEADER* _image_optional_header = NULL;
	_IMAGE_DATA_DIRECTORY* _image_data_directory = NULL;

	_image_dos_header = (_IMAGE_DOS_HEADER*)FileBufferPoint;
	//下面这个别忘记了还有一个PE标记的大小,为4个字节
	_image_file_header = (_IMAGE_FILE_HEADER*)(FileBufferPoint + _image_dos_header->e_lfanew + sizeof(PE));
	_image_optional_header = (_IMAGE_OPTIONAL_HEADER*)((char*)_image_file_header + 20);
	_image_data_directory = _image_optional_header->DataDirectory;

	const char* tableNameArr[16] = {
		"IMAGE_DIRECTORY_ENTRY_EXPORT(导出表)",
		"IMAGE_DIRECTORY_ENTRY_IMPORT(导入表)",
		"IMAGE_DIRECTORY_ENTRY_RESOURCE(资源表)",
		"IMAGE_DIRECTORY_ENTRY_EXCEPTION(异常信息表)",
		"IMAGE_DIRECTORY_ENTRY_SECURITY(安全证书表)",
		"IMAGE_DIRECTORY_ENTRY_BASERELOC(重定位表)",
		"IMAGE_DIRECTORY_ENTRY_DEBUG(调试信息表)",
		"IMAGE_DIRECTORY_ENTRY_COPYRIGHT(版权所有表)",
		"IMAGE_DIRECTORY_ENTRY_GLOBALPTR(全局指针表)",
		"IMAGE_DIRECTORY_ENTRY_TLS(TLS表)",
		"IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG(加载配置表)",
		"IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT(绑定导入表)",
		"IMAGE_DIRECTORY_ENTRY_IAT(IAT表)",
		"IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT(延迟导入表)",
		"IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR(COM信息表)",
		"保留"
	};

	for (DWORD i = 0; i < _image_optional_header->NumberOfRvaAndSizes; i++) {
		printf("%s\nVirtualSize:%08X  Size:%08X\n\n", tableNameArr[i], _image_data_directory->VirtualAddress, _image_data_directory->Size);
		_image_data_directory++;
	}

}

3.再加一个小函数

下面是 fuction 的代码

int	AlignSize(int num, int align_num) {  //用int也行
	if ((int)num <= 0 || (int)align_num <= 0) {
		printf("输入的数据不合法\n");
		return 0;
	}
	if (num % align_num == 0) {
		return num;
	}
	else {
		return align_num * (num / align_num + 1);
	}
}

效果图如下