学习ELF文件结构

起因

ELF是Linux的可执行程序的文件格式,包含(可执行程序,及静态库.a文件和共享库.so文件),学习ELF文件结构,主要修改自定义节的内容.可以不用Gcc编译器便可以修改节表中的内容.

指定代码生成到节中

#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;
}

在Linux系统上,使用GCC进行编译.

在Windows上进行解析,并修改

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


#define EI_NIDENT     16

typedef unsigned short __u16;
typedef int __s32;
typedef unsigned int __u32;


typedef  long long __i64;
typedef unsigned long long __u64;

typedef __u16 Elf64_Half;

typedef __u32 Elf64_Word;
typedef	__u32  Elf64_Sword;

typedef __u64 Elf64_Xword;
typedef	__i64  Elf64_Sxword;
typedef __u32 Elf64_Addr;
typedef __u64 Elf64_Off;

typedef __u64 Elf64_Section;

typedef Elf64_Half Elf64_Versym;

//文件头  Header Data 
typedef struct
{
	unsigned char	e_ident[EI_NIDENT];	/* Magic number and other info */
	Elf64_Half	e_type;			/* Object file type */
	Elf64_Half	e_machine;		/* Architecture */
	Elf64_Word	e_version;		/* Object file version */
	Elf64_Addr	e_entry;		/* Entry point virtual address */
	Elf64_Off	e_phoff;		/* Program header table file offset */
	Elf64_Off	e_shoff;		/* Section header table file offset */
	Elf64_Word	e_flags;		/* Processor-specific flags */
	Elf64_Half	e_ehsize;		/* ELF header size in bytes */
	Elf64_Half	e_phentsize;		/* Program header table entry size */
	Elf64_Half	e_phnum;		/* Program header table entry count */
	Elf64_Half	e_shentsize;		/* Section header table entry size */
	Elf64_Half	e_shnum;		/* Section header table entry count */
	Elf64_Half	e_shstrndx;		/* Section header string table index */
} Elf64_Ehdr;

//程序头
typedef struct
{
	Elf64_Word	p_type;			/* Segment type */
	Elf64_Word	p_flags;		/* Segment flags */
	Elf64_Off	p_offset;		/* Segment file offset */
	Elf64_Addr	p_vaddr;		/* Segment virtual address */
	Elf64_Addr	p_paddr;		/* Segment physical address */
	Elf64_Xword	p_filesz;		/* Segment size in file */
	Elf64_Xword	p_memsz;		/* Segment size in memory */
	Elf64_Xword	p_align;		/* Segment alignment */
} Elf64_Phdr;

//段表
typedef struct
{
	Elf64_Word	sh_name;		/* Section name (string tbl index) */
	Elf64_Word	sh_type;		/* Section type */
	Elf64_Xword	sh_flags;		/* Section flags */
	Elf64_Addr	sh_addr;		/* Section virtual addr at execution */
	Elf64_Off	sh_offset;		/* Section file offset */
	Elf64_Xword	sh_size;		/* Section size in bytes */
	Elf64_Word	sh_link;		/* Link to another section */
	Elf64_Word	sh_info;		/* Additional section information */
	Elf64_Xword	sh_addralign;		/* Section alignment */
	Elf64_Xword	sh_entsize;		/* Entry size if section holds table */
} Elf64_Shdr;


//获取文件大小
int file_size(char* file)
{
	int size = -1;
	FILE* fp = fopen(file, "r");
	if (fp != NULL)
	{
		fseek(fp, 0, SEEK_END);
		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
	{
		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);
		*buffer = file_buffer;
		return result;
	}
}

int write_to_file(void* mem_buffer, size_t size, char* file_name)
{
	FILE* fp = fopen(file_name, "wb+");
	if (fp == NULL)
	{
		return -1;
	}
	fwrite(mem_buffer, size, 1, fp);
	if (fp != NULL)
	{
		fclose(fp);
	}
	return 0;
}



//生成新的elf文件(linux)
int write_elf_code(char* filename, int size)
{
	char* buf;
	file_to_memory(filename, size, &buf);

	Elf64_Ehdr* p_ehdr = (Elf64_Ehdr*)buf;
	{
		printf("elf header =======================\n");
		printf("e_ident= %s\n", p_ehdr->e_ident);
		printf("e_type=%d\n", p_ehdr->e_type);
		printf("e_machine=%d\n", p_ehdr->e_machine);
		printf("e_version=%d\n", p_ehdr->e_version);
		printf("e_entry=%#10x\n", p_ehdr->e_entry);
		printf("e_phoff=%d\n", p_ehdr->e_phoff);
		printf("e_shoff=%d\n", p_ehdr->e_shoff);
		printf("e_ehsize=%d\n", p_ehdr->e_ehsize);
		printf("e_phentsize=%d\n", p_ehdr->e_phentsize);
		printf("e_phnum=%d\n", p_ehdr->e_phnum);
		printf("e_shentsize=%d\n", p_ehdr->e_shentsize);
		printf("e_shnum=%d\n", p_ehdr->e_shnum);
		printf("e_shstrndx=%d\n", p_ehdr->e_shstrndx);
	}

	printf("program header=========================\n");

	Elf64_Phdr* p_phdr = (Elf64_Phdr*)(buf + p_ehdr->e_phoff);
	{

		Elf64_Phdr* tmp = p_phdr;
		for (int i = 0; i < p_ehdr->e_phnum; i++, tmp++)
		{
			printf("%d  off:%#10x filesize:%#10x memsize:%#10x\n", i, tmp->p_offset, tmp->p_filesz, tmp->p_memsz);
		}
	}


	printf("section header=========================\n");


	Elf64_Shdr* p_shdr = (Elf64_Shdr*)(buf + p_ehdr->e_shoff);
	char* section_start = (char*)(buf + p_shdr[p_ehdr->e_shstrndx].sh_offset);
	{
		Elf64_Shdr* tmp = p_shdr;
		for (int i = 0; i < p_ehdr->e_shnum; i++, tmp++)
		{
			char* section_name = (char*)(section_start + tmp->sh_name);

			printf("%d sh_offset:%#10x  sh_size:%#10x sh_entsize:%#10x  ", i, tmp->sh_offset, tmp->sh_size, tmp->sh_entsize);
			if (strcmp(section_name, ".mydata") == 0)
			{
				printf("============.mydata=====start==============\n");
				char* content = "i love c,i love rust,i love c#....";
				char* mydata = buf + tmp->sh_offset;
				memset(mydata, 0, tmp->sh_size);

				memcpy(mydata, content, strlen(content) + 1);
				printf("============.mydata=======end============\n");
			}
			printf(" %20s \n", section_name);
		}
	}

	write_to_file(buf, size, "myelf-new");
}
int main(int argc, char* argv[])
{
	if (argc < 2)
	{
		printf("argc < 2!\n");
	}
	else
	{
		char* target_file = argv[1];
		int target_size = file_size(target_file);
		if (target_size < 0)
		{
			printf("file not found!\n");
		}
		else
		{

			write_elf_code(target_file, target_size);

			printf("ok!\n");
		}
	}
	return 0;
}

在Windows系统编译执行之后,再把用程序生成的文件,放到Linux系统,看文件是否能够运行.

#先修改文件,让文件能够执行
chmod +x myelf-new

运行修改过ELF文件,看输出的内容是否修改成功

秋风 2020-05-16