ELF 文件有三种类型:可重定位文件:也就是通常称的目标文件,后缀为.o。共享文件:也就是通常称的库文件,后缀为.so。可执行文件:本文主要讨论的文件格 式,总的来说,可执行文件的格式与上述两种文件的格式之间的区别主要在于观察的角度不同:一种称为连接视图(Linking View),一种称为执行视图(Execution View)。
一个典型的ELF文件有两种描述视图:program header和section header.
program header:是对程序运行时所使用的段的描述.
section header: 是对所有二进制段的描述.
每一个ELF文件是由一个ELF 文件头(ELF header)和其余的文件数据构成.这些文件数据包括一下一些内容:
·Program header table 描述0个或是多个段(segments)
·Section header table, 描述0个或是多个节(sections)
·要写到上面两个表中的数据.
段(segments)包含的是程序运行是必要的信息
节(sections)包含的是链接和重定向时所需要的重要数据
同一时间整个文件中的每个beyt不会属于一个以上的段,但是也可以存在不属于任何段的字节.
linux下ELF文件分析工具:
readelfis: 是一个unix下的二进制工具,用来显示一个或多个ELF文件的信息.
elfdump: 是一个Solaris命令,用来查看单个ELF文件的信息.
objdump: 可以查看ELF文件或是其它对象格式的更多信息.
这里自己写了个简单的分析ELF文件头和修改文件头的小程序
//elf_head.h
#ifndef ELF_HEAD_H
#define ELF_HEAD_H
#define MAGIC "\177ELF"
#define INVAL 0
#define INFO_POS 40
#define FOPEN_FAILED -1
#define FORMAT_ERROR -2
#define SUCCESS 0
#define MODTIP() do{puts("\tEnter new value:");}while(0)
#define CUR_FTYPE_MAX 4
#define CUR_ARCH_MAX 40
typedef struct elf_head{
char magic[4]; //Magic Number
char addr_width; //Class 1 - 32bits(ELF32);2 - 64bits
char byteorder; //Byte order 1 - little-endian;2 - big-endian
char hversion; //Header version 1
char pad[9]; //Padding bytes
short filetype; //1 - relocatable;2 - executable;3 - shared object;4 - core-image
short archtype; //Architecture 2 - SPARC;3 - x86;4 - 68K
int fversion; //File version 1
int entry;//Entry point if executable
int phdrpos; //Program header position
int shdrpos; //Section header position
int flags; //Architecture relative
short hdrsize; //ELF header size
short phdrent;//Size of program headers
short phdrcnt;//Number of program headers
short shdrent;//Size of section headers
short shdrcnt;//Number of section headers
short strsec;//Section header string table index
}elf_header;
int read_elf_header(elf_header *ehdr,char *filename); //读取文件头信息
int print_elfhdr_info(elf_header *ehdr); //打印相关信息
int modify_elfhdr(elf_header *ehdr,char *filename); //修改文件头
#endif /*ELF_HEAD_H*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "elf_head.h"
static char *file_type[] = { //文件类型字段的描述字符串数组
"Unknown",
"Relocatable",
"Executable",
"Shared object",
"Core imgae"
};
static char *arch_type[] = { //硬件平台的描述字符串数组
"Unknown",
"Reserved",
"SPARC",
"x86",
"68K",
" ", //5
" ",
" ",
" ",
" ",
" ", //10
" ",
" ",
" ",
" ",
" ", //15
" ",
" ",
" ",
" ",
" ", //20
" ",
" ",
" ",
" ",
" ", //25
" ",
" ",
" ",
" ",
" ", //30
" ",
" ",
" ",
" ",
" ", //35
" ",
" ",
" ",
" ",
"ARM", //40
};
//Check if the file is an elf format file
static int is_elf(elf_header *ehdr)
{
if(strncmp(ehdr->magic,MAGIC,strlen(MAGIC)) == 0)
return 1;
else
return 0;
}
int read_elf_header(elf_header *ehdr,char *filename)
{
FILE *fp;
fp = fopen(filename,"r");
if(fp == NULL)
return FOPEN_FAILED;
bzero(ehdr,sizeof(elf_header));
fread(ehdr,sizeof(elf_header),1,fp);
if(strncmp(ehdr->magic,MAGIC,strlen(MAGIC)) != 0)
{
fclose(fp);
return FORMAT_ERROR;
}
fclose(fp);
return SUCCESS;
}
static void put_tip(char *tip)
{
printf(" %s",tip);
printf("\033[%dC",INFO_POS - strlen(tip)); //每次输出第二列信息,即读出的数据的时候,先向右移动光固定个单位,保证对齐。详见ANSI控制码表
}
int print_elfhdr_info(elf_header *ehdr)
{
if(strncmp(ehdr->magic,MAGIC,strlen(MAGIC)) != 0)
{
puts("Not an elf format file.");
return FORMAT_ERROR;
}
puts("ELF header:");
put_tip("Magic:");
printf("%2x %2x %2x %2x (%c%c%c%c)\n"
,ehdr->magic[0],ehdr->magic[1],ehdr->magic[2],ehdr->magic[3]
,ehdr->magic[0],ehdr->magic[1],ehdr->magic[2],ehdr->magic[3]);
put_tip("Class:");
if(ehdr->addr_width == 1)
printf("ELF32\n");
if(ehdr->addr_width == 2)
printf("ELF64\n");
put_tip("Byte order:");
if(ehdr->byteorder == 1)
printf("Little-endian\n");
else
printf("Big-endian\n");
put_tip("Version:");
printf("0x%02x\n",ehdr->hversion);
put_tip("File type:");
if(ehdr->filetype > CUR_FTYPE_MAX)
ehdr->filetype = 0;
puts(file_type[ehdr->filetype]);
put_tip("Machine:");
if(ehdr->archtype > CUR_ARCH_MAX)
{
ehdr->archtype = 0;
}
puts(arch_type[ehdr->archtype]);
put_tip("File version");
printf("0x%x\n",ehdr->fversion);
put_tip("Entry point address:");
printf("0x%x\n%",ehdr->entry);
put_tip("Start of program headers:");
printf("%d (bytes into file)\n",ehdr->phdrpos);
put_tip("Start of section headers:");
printf("%d (bytes into file)\n",ehdr->shdrpos);
put_tip("flags:");
printf("%d(0x%x)\n",ehdr->flags,ehdr->flags);
put_tip("Size of this header:");
printf("%d bytes\n",ehdr->hdrsize);
put_tip("Size of program headers:");
printf("%d bytes\n",ehdr->phdrent);
put_tip("Number of program headers:");
printf("%d\n",ehdr->phdrcnt);
put_tip("Size of section headers:");
printf("%d bytes\n",ehdr->shdrent);
put_tip("Number of section headers:");
printf("%d\n",ehdr->shdrcnt);
put_tip("Section header string table index:");
printf("%d(0x%x)\n",ehdr->strsec,ehdr->strsec);
return SUCCESS;
}
static void mod_help()
{
puts("Select the part you want to modify:");
puts("1 -- Magic number.");
puts("2 -- Class(1:ELF32 2:ELF64).");
puts("3 -- Byteorder(1:Little-endian 2:Big-endian).");
puts("4 -- File type(1:relocatable 2:executable 3:shared object 4:core-image)");
puts("s -- save");
puts("q -- quit without saving");
puts("x -- save and quit");
}
static int atoh(char *buf)//将16进制形式的字符串转换成数值
{
int tmp = 0;
while(*buf != '\0')
{
if(*buf >= '0' && *buf <= '9')
tmp = tmp * 16 + *buf - '0';
else
{
if((*buf >= 'a' && *buf <= 'f'))
tmp = tmp * 16 + *buf - 'a' + 10;
else
{
if((*buf >= 'A' && *buf <= 'F'))
tmp = tmp * 16 + *buf - 'A' + 10;
else
return INVAL;
}
}
buf++;
}
return tmp;
}
//以下是修改ELF文件头的函数, 只为了测试,如果随意修改会使文件无法使用
static void mod_magic(elf_header *ehdr)//修改MAGIC号
{
int i;
int magic;
int cur_val;
char buf[20];
char *tmp;
printf("\tOld magic number: %x %x %x %x\n",ehdr->magic[0],ehdr->magic[1],ehdr->magic[2],ehdr->magic[3]);
MODTIP();
puts("\te.g: 0a0b0c0d or 0A0B0C0D");
puts("\tEnter new value");
bzero(buf,20);
putchar('\t');
fgets(buf,19,stdin);
tmp = buf;
while(*tmp != '\0')
{
if((*tmp >= '0' && *tmp <= '9') || (*tmp >= 'a' && *tmp <= 'f') || (*tmp >= 'A' && *tmp <= 'Z'))
{
*(tmp + 8) = 0;
magic = atoh(tmp);
break;
}
tmp++;
}
//如果输入的是0x11223344,那么存在文件上就是0x44332211(小端模式),而访问magic字段的时候是以char型来访问,所以访问顺序正好相反,这里要交换字节序存入文件。
tmp = (char *)(&magic) + 3;
for(i = 0;i < 4;i++)
ehdr->magic[i] = *(tmp - i);
}
static void mod_class(elf_header *ehdr) //修改地址宽度,32位或是64位
{
char opt[2];
printf("\tOld class: %d \n\t1:ELF32 \n\t2:ELF64\n",ehdr->addr_width);
MODTIP();
while(1)
{
opt[0] = getchar();
opt[1] = getchar();
if(opt[0] > '2' || opt[0] < '1')
puts("\tWrong choice.Value should be 1 or 2");
else
break;
}
ehdr->addr_width = opt[0] - '0';
}
static void mod_byteorder(elf_header *ehdr) //修改字节序
{
char opt[2];
printf("\tOld byte order: %d \n\t1:Little-endian \n\t2:Big-endian\n",ehdr->byteorder);
MODTIP();
while(1)
{
opt[0] = getchar();
opt[1] = getchar();
if(opt[0] > '2' || opt[0] < '1')
puts("\tWrong choice.Value should be 1 or 2");
else
break;
}
ehdr->byteorder = opt[0] - '0';
}
static void mod_filetype(elf_header *ehdr)//修改文件类型
{
char opt[2];
printf("\tOld file type: %d \n\t1:Relocatable \n\t2:Executable \n\t3:Shared object \n\t4:Core image\n)",ehdr->filetype);
MODTIP();
while(1)
{
opt[0] = getchar();
opt[1] = getchar();
if(opt[0] > '4' || opt[0] < '1')
puts("\tWrong choice.Value should be 1 or 2");
else
break;
}
ehdr->filetype = opt[0] - '0';
}
int modify_elfhdr(elf_header *ehdr,char *filename)
{
char opt[2];
FILE *fp;
// elf_header ehdr_backup;
fp = fopen(filename,"r+");
if(fp == NULL)
return FOPEN_FAILED;
fread(ehdr,sizeof(elf_header),1,fp);
if(!is_elf(ehdr))
{
puts("Not an elf format file.");
return FORMAT_ERROR;
}
// memcpy(&ehdr_backup,ehdr,sizeof(elf_header));
while(1)
{
mod_help();
opt[0] = getchar();
opt[1] = getchar();
switch(opt[0])
{
case '1':
mod_magic(ehdr);
break;
case '2':
mod_class(ehdr);
break;
case '3':
mod_byteorder(ehdr);
break;
case '4':
mod_filetype(ehdr);
break;
case 's':
fseek(fp,0,SEEK_SET);
fwrite(ehdr,sizeof(elf_header),1,fp);
break;
case 'q':
goto out;
break;
case 'x':
fseek(fp,0,SEEK_SET);
fwrite(ehdr,sizeof(elf_header),1,fp);
goto out;
break;
default:
puts("Wrong choice.");
}
}
out:
fclose(fp);
return SUCCESS;
}
//main.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/ioctl.h>
#include "elf_head.h"
static void help()
{
puts("******************************************");
puts("* ELF header reader and modifier *");
puts("* 0 - exit *");
puts("* 1 - print header info *");
puts("* 2 - modify elf header *");
puts("******************************************");
}
int main(int argc,char **argv)
{
char cmd;
elf_header ehdr;
if(argc != 2)
{
printf("Usage: %s [filename]\n",strchr(argv[0],'/') + 1);
return 0;
}
switch(read_elf_header(&ehdr,argv[1]))
{
case FOPEN_FAILED:
printf("Can not open %s.\n",argv[1]);
goto out;
case FORMAT_ERROR:
printf("%s is not an elf format file.\n",argv[1]);
goto out;
}
while(cmd != '0')
{
help();
cmd = getchar();
switch(cmd)
{
case '1':
getchar();//由于终端不是非标准模式,所以输入字符后都要使用回车,回车符本身会当作输入的字符,这里使用getchar()目的就是使下次获得输入的字符时不为回车符
print_elfhdr_info(&ehdr);
break;
case '2':
getchar();
modify_elfhdr(&ehdr,argv[1]);
break;
default:
break;
}
}
out:
return 0;
}