linux下可执行文件分析Word文档下载推荐.docx
- 文档编号:18365615
- 上传时间:2022-12-15
- 格式:DOCX
- 页数:24
- 大小:29.43KB
linux下可执行文件分析Word文档下载推荐.docx
《linux下可执行文件分析Word文档下载推荐.docx》由会员分享,可在线阅读,更多相关《linux下可执行文件分析Word文档下载推荐.docx(24页珍藏版)》请在冰豆网上搜索。
6:
动态连接器对程序的外部引用进行重定位,通俗的讲,就是告诉程序其引用的外部变量/函数的地址,此地址位于共享库被加载在内存的区间内。
动态连接还有一个延迟(Lazy)定位的特性,即只在"
真正"
需要引用符号时才重定位,这对提高程序运行效率有极大帮助。
7:
动态连接器执行在ELF文件中标记为.init的节的代码,进行程序运行的初始化。
在早期系统中,初始化代码对应函数_init(void)(函数名强制固定),在现代系统中,则对应形式为
void
__attribute((constructor))
init_function(void)
{
……
}
其中函数名为任意。
8:
动态连接器把控制传递给程序,从ELF文件头部中定义的程序进入点开始执行。
在a.out格式和ELF格式中,程序进入点的值是显式存在的,在COFF格式中则是由规范隐含定义。
从上面的描述可以看出,加载文件最重要的是完成两件事情:
加载程序段和数据段到内存;
进行外部定义符号的重定位。
重定位是程序连接中一个重要概念。
我们知道,一个可执行程序通常是由一个含有main()的主程序文件、若干目标文件、若干共享库(SharedLibraries)组成。
(注:
采用一些特别的技巧,也可编写没有main函数的程序,请参阅参考资料2)一个C程序可能引用共享库定义的变量或函数,换句话说就是程序运行时必须知道这些变量/函数的地址。
在静态连接中,程序所有需要使用的外部定义都完全包含在可执行程序中,而动态连接则只在可执行文件中设置相关外部定义的一些引用信息,真正的重定位是在程序运行之时。
静态连接方式有两个大问题:
如果库中变量或函数有任何变化都必须重新编译连接程序;
如果多个程序引用同样的变量/函数,则此变量/函数会在文件/内存中出现多次,浪费硬盘/内存空间。
比较两种连接方式生成的可执行文件的大小,可以看出有明显的区别。
回页首
a.out文件格式分析
a.out格式在不同的机器平台和不同的UNIX操作系统上有轻微的不同,例如在MC680x0平台上有6个section。
下面我们讨论的是最"
标准"
的格式。
a.out文件包含7个section,格式如下:
execheader(执行头部,也可理解为文件头部)
textsegment(文本段)
datasegment(数据段)
textrelocations(文本重定位段)
datarelocations(数据重定位段)
symboltable(符号表)
stringtable(字符串表)
执行头部的数据结构:
structexec{
unsignedlonga_midmag;
/*魔数和其它信息*/
unsignedlonga_text;
/*文本段的长度*/
unsignedlonga_data;
/*数据段的长度*/
unsignedlonga_bss;
/*BSS段的长度*/
unsignedlonga_syms;
/*符号表的长度*/
unsignedlonga_entry;
/*程序进入点*/
unsignedlonga_trsize;
/*文本重定位表的长度*/
unsignedlonga_drsize;
/*数据重定位表的长度*/
};
文件头部主要描述了各个section的长度,比较重要的字段是a_entry(程序进入点),代表了系统在加载程序并初试化各种环境后开始执行程序代码的入口。
这个字段在后面讨论的ELF文件头部中也有出现。
由a.out格式和头部数据结构我们可以看出,a.out的格式非常紧凑,只包含了程序运行所必须的信息(文本、数据、BSS),而且每个section的顺序是固定的。
这种结构缺乏扩展性,如不能包含"
现代"
可执行文件中常见的调试信息,最初的UNIX黑客对a.out文件调试使用的工具是adb,而adb是一种机器语言调试器!
a.out文件中包含符号表和两个重定位表,这三个表的内容在连接目标文件以生成可执行文件时起作用。
在最终可执行的a.out文件中,这三个表的长度都为0。
a.out文件在连接时就把所有外部定义包含在可执行程序中,如果从程序设计的角度来看,这是一种硬编码方式,或者可称为模块之间是强藕和的。
在后面的讨论中,我们将会具体看到ELF格式和动态连接机制是如何对此进行改进的。
a.out是早期UNIX系统使用的可执行文件格式,由AT&
T设计,现在基本上已被ELF文件格式代替。
a.out的设计比较简单,但其设计思想明显的被后续的可执行文件格式所继承和发扬。
可以参阅参考资料16和阅读参考资料15源代码加深对a.out格式的理解。
参考资料12讨论了如何在"
的红帽LINUX运行a.out格式文件。
COFF文件格式分析
COFF格式比a.out格式要复杂一些,最重要的是包含一个节段表(sectiontable),因此除了.text,.data,和.bss区段以外,还可以包含其它的区段。
另外也多了一个可选的头部,不同的操作系统可一对此头部做特定的定义。
COFF文件格式如下:
FileHeader(文件头部)
OptionalHeader(可选文件头部)
Section1Header(节头部)
………
SectionnHeader(节头部)
RawDataforSection1(节数据)
RawDataforSectionn(节数据)
RelocationInfoforSect.1(节重定位数据)
RelocationInfoforSect.n(节重定位数据)
LineNumbersforSect.1(节行号数据)
LineNumbersforSect.n(节行号数据)
Symboltable(符号表)
Stringtable(字符串表)
文件头部的数据结构:
structfilehdr
{
unsignedshortf_magic;
/*魔数*/
unsignedshortf_nscns;
/*节个数*/
longf_timdat;
/*文件建立时间*/
longf_symptr;
/*符号表相对文件的偏移量*/
longf_nsyms;
/*符号表条目个数*/
unsignedshortf_opthdr;
/*可选头部长度*/
unsignedshortf_flags;
/*标志*/
};
COFF文件头部中魔数与其它两种格式的意义不太一样,它是表示针对的机器类型,例如0x014c相对于I386平台,而0x268相对于Motorola68000系列等。
当COFF文件为可执行文件时,字段f_flags的值为F_EXEC(0X00002),同时也表示此文件没有未解析的符号,换句话说,也就是重定位在连接时就已经完成。
由此也可以看出,原始的COFF格式不支持动态连接。
为了解决这个问题以及增加一些新的特性,一些操作系统对COFF格式进行了扩展。
Microsoft设计了名为PE(PortableExecutable)的文件格式,主要扩展是在COFF文件头部之上增加了一些专用头部,具体细节请参阅参考资料18,某些UNIX系统也对COFF格式进行了扩展,如XCOFF(extendedcommonobjectfileformat)格式,支持动态连接,请参阅参考资料5。
紧接文件头部的是可选头部,COFF文件格式规范中规定可选头部的长度可以为0,但在LINUX系统下可选头部是必须存在的。
下面是LINUX下可选头部的数据结构:
typedefstruct
charmagic[2];
charvstamp[2];
/*版本号*/
chartsize[4];
/*文本段长度*/
chardsize[4];
/*已初始化数据段长度*/
charbsize[4];
/*未初始化数据段长度*/
charentry[4];
chartext_start[4];
/*文本段基地址*/
chardata_start[4];
/*数据段基地址*/
COFF_AOUTHDR;
字段magic为0413时表示COFF文件是可执行的,注意到可选头部中显式定义了程序进入点,标准的COFF文件没有明确的定义程序进入点的值,通常是从.text节开始执行,但这种设计并不好。
前面我们提到,COFF格式比a.out格式多了一个节段表,一个节头条目描述一个节数据的细节,因此COFF格式能包含更多的节,或者说可以根据实际需要,增加特定的节,具体表现在COFF格式本身的定义以及稍早提及的COFF格式扩展。
我个人认为,节段表的出现可能是COFF格式相对a.out格式最大的进步。
下面我们将简单描述COFF文件中节的数据结构,因为节的意义更多体现在程序的编译和连接上,所以本文不对其做更多的描述。
此外,ELF格式和COFF格式对节的定义非常相似,在随后的ELF格式分析中,我们将省略相关讨论。
structCOFF_scnhdr
chars_name[8];
/*节名称*/
chars_paddr[4];
/*物理地址*/
chars_vaddr[4];
/*虚拟地址*/
chars_size[4];
/*节长度*/
chars_scnptr[4];
/*节数据相对文件的偏移量*/
chars_relptr[4];
/*节重定位信息偏移量*/
chars_lnnoptr[4];
/*节行信息偏移量*/
chars_nreloc[2];
/*节重定位条目数*/
chars_nlnno[2];
/*节行信息条目数*/
chars_flags[4];
/*段标记*/
有一点需要注意:
LINUX系统中头文件coff.h中对字段s_paddr的注释是"
physicaladdress"
,但似乎应该理解为"
节被加载到内存中所占用的空间长度"
。
字段s_flags标记该节的类型,如文本段、数据段、BSS段等。
在COFF的节中也出现了行信息,行信息描述了二进制代码与源代码的行号之间的对映关系,在调试时很有用。
参考资料19是一份对COFF格式详细描述的中文资料,更详细的内容请参阅参考资料20。
ELF文件格式分析
ELF文件有三种类型:
可重定位文件:
也就是通常称的目标文件,后缀为.o。
共享文件:
也就是通常称的库文件,后缀为.so。
可执行文件:
本文主要讨论的文件格式,总的来说,可执行文件的格式与上述两种文件的格式之间的区别主要在于观察的角度不同:
一种称为连接视图(LinkingView),一种称为执行视图(ExecutionView)。
首先看看ELF文件的总体布局:
ELFheader(ELF头部)
Programheadertable(程序头表)
Segment1(段1)
Segment2(段2)
Sengmentn(段n)
Setionheadertable(节头表,可选)
段由若干个节(Section)构成,节头表对每一个节的信息有相关描述。
对可执行程序而言,节头表是可选的。
参考资料1中作者谈到把节头表的所有数据全部设置为0,程序也能正确运行!
ELF头部是一个关于本文件的路线图(roadmap),从总体上描述文件的结构。
下面是ELF头部的数据结构:
typedefstruct
unsignedchare_ident[EI_NIDENT];
/*魔数和相关信息*/
Elf32_Halfe_type;
/*目标文件类型*/
Elf32_Halfe_machine;
/*硬件体系*/
Elf32_Worde_version;
/*目标文件版本*/
Elf32_Addre_entry;
Elf32_Offe_phoff;
/*程序头部偏移量*/
Elf32_Offe_shoff;
/*节头部偏移量*/
Elf32_Worde_flags;
/*处理器特定标志*/
Elf32_Halfe_ehsize;
/*ELF头部长度*/
Elf32_Halfe_phentsize;
/*程序头部中一个条目的长度*/
Elf32_Halfe_phnum;
/*程序头部条目个数*/
Elf32_Halfe_shentsize;
/*节头部中一个条目的长度*/
Elf32_Halfe_shnum;
/*节头部条目个数*/
Elf32_Halfe_shstrndx;
/*节头部字符表索引*/
}Elf32_Ehdr;
下面我们对ELF头表中一些重要的字段作出相关说明,完整的ELF定义请参阅参考资料6和参考资料7。
e_ident[0]-e_ident[3]包含了ELF文件的魔数,依次是0x7f、'
E'
、'
L'
F'
注意,任何一个ELF文件必须包含此魔数。
参考资料3中讨论了利用程序、工具、/Proc文件系统等多种查看ELF魔数的方法。
e_ident[4]表示硬件系统的位数,1代表32位,2代表64位。
e_ident[5]表示数据编码方式,1代表小印第安排序(最大有意义的字节占有最低的地址),2代表大印第安排序(最大有意义的字节占有最高的地址)。
e_ident[6]指定ELF头部的版本,当前必须为1。
e_ident[7]到e_ident[14]是填充符,通常是0。
ELF格式规范中定义这几个字节是被忽略的,但实际上是这几个字节完全可以可被利用。
如病毒Lin/Glaurung.676/666(参考资料1)设置e_ident[7]为0x21,表示本文件已被感染;
或者存放可执行代码(参考资料2)。
ELF头部中大多数字段都是对子头部数据的描述,其意义相对比较简单。
值得注意的是某些病毒可能修改字段e_entry(程序进入点)的值,以指向病毒代码,例如上面提到的病毒Lin/Glaurung.676/666。
一个实际可执行文件的文件头部形式如下:
(利用命令readelf)
ELFHeader:
Magic:
7f454c46010101000000000000000000
Class:
ELF32
Data:
2'
scomplement,littleendian
Version:
1(current)
OS/ABI:
UNIX-SystemV
ABIVersion:
0
Type:
EXEC(Executablefile)
Machine:
Intel80386
0x1
Entrypointaddress:
0x80483cc
Startofprogramheaders:
52(bytesintofile)
Startofsectionheaders:
14936(bytesintofile)
Flags:
0x0
Sizeofthisheader:
52(bytes)
Sizeofprogramheaders:
32(bytes)
Numberofprogramheaders:
6
Sizeofsectionheaders:
40(bytes)
Numberofsectionheaders:
34
Sectionheaderstringtableindex:
31
紧接ELF头部的是程序头表,它是一个结构数组,包含了ELF头表中字段e_phnum定义的条目,结构描述一个段或其他系统准备执行该程序所需要的信息。
typedefstruct{
Elf32_Wordp_type;
/*段类型*/
Elf32_Offp_offset;
/*段位置相对于文件开始处的偏移量*/
Elf32_Addrp_vaddr;
/*段在内存中的地址*/
Elf32_Addrp_paddr;
/*段的物理地址*/
Elf32_Wordp_filesz;
/*段在文件中的长度*/
Elf32_Wordp_memsz;
/*段在内存中的长度*/
Elf32_Wordp_flags;
/*段的标记*/
Elf32_Wordp_align;
/*段在内存中对齐标记*/
}Elf32_Phdr;
在详细讨论可执行文件程序头表之前,首先查看一个实际文件的输出:
ProgramHeaders:
TypeOffsetVirtAddrPhysAddrFileSizMemSizFlgAlign
PHDR0x0000340x080480340x080480340x000c00x000c0RE0x4
INTERP0x0000f40x080480f40x080480f40x000130x00013R0x1
[Requestingprograminterpreter:
/lib/ld-linux.so.2]
LOAD0x0000000x080480000x080480000x006840x00684RE0x1000
LOAD0x0006840x080496840x080496840x001180x00130RW0x1000
DYNAMIC0x0006900x080496900x080496900x000c80x000c8RW0x4
NOTE0x0001080x080481080x080481080x000200x00020R0x4
SectiontoSegmentmapping:
SegmentSections...
00
01.interp
02.interp.note.ABI-tag.hash.dynsym.dynstr.gnu.version.gnu.version_r.rel.dyn.rel.plt.init.plt.text.fini.rodata.eh_frame
03.data.dynamic.ctors.dtors.jcr.got.bss
04.dynamic
05.note.ABI-tag
SectionHeaders:
[Nr]NameTypeAddrOffSizeESFlgLkInfAl
[0]NULL0000000000000000000000000
[1].interpPROGBITS080480f40000f400001300A001
[2].note.ABI-tagNOTE0804810800010800002000A004
[3].hashHASH0804812800012800004004A404
[4].dynsymDYNSYM080481680001680000b010A514
[5].dynstrSTRTAB0804821800021800007b00A001
[6].gnu.versionVERSYM0804829400029400001602A4
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- linux 可执行文件 分析