使用gcc的attribute,修改pe的内容

起因

利用gcc section特性,将数组放到指定节中,然后通过读取PE修改节的内容,从而实现程序不用重新编译,就能修改exe输出的内容.

__attribute__((section(".mydata")))

#include <stdio.h>
#include <stdlib.h>

//myname在gcc编译时,将myname放到节名为.mydata中
char myname[1024] __attribute__((section(".mydata"))) = "hello gcc ,hello section";

int main(int argc, char* argv[])
{
	printf("%s\n", myname);
	return 0;
}

通过读取pe修改.mydata中的内容

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
#include <Windows.h>

//获取文件大小
int file_size(char* file)
{
	FILE* fp = fopen(file, "r");
	fseek(fp, 0, SEEK_END);
	int size = ftell(fp);
	fclose(fp);
	return size;
}

//将文件读取到内存中
int file_to_memory(char* file, int size, char** buffer)
{
	FILE* fp = fopen(file, "rb+");
	if (fp == NULL)
	{
		return -1;
	}
	else
	{
		//size = size + 1;
		char* file_buffer = calloc(size, sizeof(char));
		if (file_buffer == NULL)
		{
			return -2;
		}
		*buffer = NULL;
		int result = fread(file_buffer, sizeof(char), size, fp);
		fclose(fp);
		//file_buffer[size - 1] = '\0';
		*buffer = file_buffer;
		return 0;
	}
}


unsigned long buffer_to_image(char** file_buffer, char** image_buffer)
{
	PIMAGE_DOS_HEADER dos_header = NULL;
	PIMAGE_NT_HEADERS nt_header = NULL;
	PIMAGE_FILE_HEADER file_header = NULL;
	PIMAGE_OPTIONAL_HEADER optional_header = NULL;
	PIMAGE_SECTION_HEADER section_header = NULL;

	char* ptemp = *file_buffer;

	void* temp_buffer = NULL;
	if (file_buffer == NULL)
	{
		printf("file_buff is null\n");
		return 0;
	}

	if (*(short*)ptemp != IMAGE_DOS_SIGNATURE)
	{
		printf("%08x\n", *(short*)ptemp);
		//校验是否为dos头 pe开头为mz
		return 0;
	}
	dos_header = (PIMAGE_DOS_HEADER)ptemp;

	if (*((unsigned long*)(ptemp + dos_header->e_lfanew)) != IMAGE_NT_SIGNATURE)
	{
		//校验nt头是否有pe标志 PE
		return 0;
	}
	nt_header = (PIMAGE_NT_HEADERS)((unsigned long)ptemp + dos_header->e_lfanew);
	file_header = (PIMAGE_FILE_HEADER)((unsigned long)nt_header + 4);
	optional_header = (PIMAGE_OPTIONAL_HEADER)((unsigned long)file_header + IMAGE_SIZEOF_FILE_HEADER);

	section_header = (PIMAGE_SECTION_HEADER)((unsigned long)optional_header + file_header->SizeOfOptionalHeader);

	//分配空间
	temp_buffer = calloc(1, optional_header->SizeOfImage);
	if (!temp_buffer)
	{
		temp_buffer = NULL;
		return 0;
	}

	//先拷贝头
	memcpy(temp_buffer, dos_header, optional_header->SizeOfHeaders);


	PIMAGE_SECTION_HEADER temp_section_header = section_header;
	for (int i = 0; i < file_header->NumberOfSections; i++)
	{
		PIMAGE_SECTION_HEADER psection = temp_section_header + i;
		void* des = (void*)((unsigned long)temp_buffer + psection->VirtualAddress);
		void* src = (void*)((unsigned long)ptemp + psection->PointerToRawData);  //temp_section_header + 1,移动多少呢?
		memcpy(des, src, psection->SizeOfRawData);
	}

	*image_buffer = temp_buffer;
	temp_buffer = NULL;
	return optional_header->SizeOfImage;
}

int recory_file(void** image_buffer, char* file, int size)
{
	PIMAGE_DOS_HEADER dos_header = NULL;
	PIMAGE_NT_HEADERS nt_header = NULL;
	PIMAGE_FILE_HEADER file_header = NULL;
	PIMAGE_OPTIONAL_HEADER optional_header = NULL;
	PIMAGE_SECTION_HEADER section_header = NULL;

	char* ptemp = *image_buffer;
	dos_header = (PIMAGE_DOS_HEADER)ptemp;
	if (*(short*)((unsigned long)ptemp + dos_header->e_lfanew) != IMAGE_NT_SIGNATURE)
	{
		//校验nt头是否有pe标志 PE
		return 0;
	}

	nt_header = (PIMAGE_NT_HEADERS)((unsigned long)ptemp + dos_header->e_lfanew);
	file_header = (PIMAGE_FILE_HEADER)((unsigned long)nt_header + 4);
	optional_header = (PIMAGE_OPTIONAL_HEADER)((unsigned long)file_header + IMAGE_SIZEOF_FILE_HEADER);
	section_header = (PIMAGE_SECTION_HEADER)((unsigned long)optional_header + file_header->SizeOfOptionalHeader);
	char* file_buffer = calloc(1, size);
	memcpy(file_buffer, dos_header, optional_header->SizeOfHeaders);

	PIMAGE_SECTION_HEADER temp_section_header = section_header;
	for (int i = 0; i < file_header->NumberOfSections; i++)
	{
		PIMAGE_SECTION_HEADER psection = temp_section_header + i;
		void* des = (void*)((unsigned long)file_buffer + psection->PointerToRawData);
		void* src = (void*)((unsigned long)ptemp + psection->VirtualAddress);  //temp_section_header + 1,移动多少呢?
		memcpy(des, src, psection->SizeOfRawData);
	}

	FILE* fp = fopen(file, "wb+");
	if (fp != NULL)
	{
		fwrite((void*)file_buffer, size, 1, fp);
	}
	fclose(fp);
}

int test_write_code(char* file, int size)
{
	PIMAGE_DOS_HEADER dos_header = NULL;
	PIMAGE_NT_HEADERS nt_header = NULL;
	PIMAGE_FILE_HEADER file_header = NULL;
	PIMAGE_OPTIONAL_HEADER optional_header = NULL;
	PIMAGE_SECTION_HEADER section_header = NULL;

	char* buf;
	file_to_memory(file, size, &buf);

	char* img_buf;
	buffer_to_image(&buf, &img_buf);

	dos_header = (PIMAGE_DOS_HEADER)img_buf;
	nt_header = (PIMAGE_NT_HEADERS)((unsigned long)img_buf + dos_header->e_lfanew);
	file_header = (PIMAGE_FILE_HEADER)((unsigned long)nt_header + 4);
	optional_header = (PIMAGE_OPTIONAL_HEADER)((unsigned long)file_header + IMAGE_SIZEOF_FILE_HEADER);

	section_header = (PIMAGE_SECTION_HEADER)((unsigned long)optional_header + file_header->SizeOfOptionalHeader);
	PIMAGE_SECTION_HEADER temp_section_header = section_header;
	char* name = ".mydata";
	for (int i = 0; i < file_header->NumberOfSections; i++)
	{
		PIMAGE_SECTION_HEADER psection = temp_section_header + i;
		if (strcmp(psection->Name, name) == 0)
		{
			char* dest = img_buf+ psection->VirtualAddress;
			char* src = "pe write:hello section";
			strcpy(dest, src);
			break;
		}
	}

	recory_file(&img_buf, "pe-section.exe", size);


	if (buf)
	{
		free(buf);
	}
	if (img_buf)
	{
		free(img_buf);
	}
}

int main(int argc, char* argv[])
{
	char* file = "test-section.exe";

	int size = file_size(file);

	test_write_code(file, size);

	return 0;
}

效果

通过修改section中的内容,来实现输出不同的内容,这样可以免去编译器重新编译
没有重新编译test-section.exe,实现输出不同的内容
秋风 2019-07-30