linux jar 文件头 sh,Linux逆向---ELF格式分析之文件头和程序头

在Linux下,可以利用vim编辑器来对编译生成后的可执行程序进行编辑,比如说把75jne指令改成74je指令,这样可以在不重新编译的情况下去修改程序的控制流,这样玩感觉还是很有意思的,不过也仅限于此,所以我借了一本书想要学学逆向。。结果发现这本书真的难啃。。如果只是读它的内容的话很快就读过去了,但是会发现读完之后自己还是什么都不知道,于是我决定慢慢读,并且用例子去对照着看,感觉这样或许会有些效果。

这里我的系统是64位Ubuntu,32位和64位的可执行程序的十六进制表示还是有一些区别的,所以这里有必要说明一下,很显著的一个特征就是32位中用4个字节表示的东西,这里需要用8个字节来进行表示。

1.源代码:

这里为了简单期间,我实现了一个helloworld。。然后用它编译后的程序来进行之后的分析。

hello.c:

#include

int main()

{

printf("hello world");

return 0;

}

编译生成hello.out

gcc hello.c -o hello.out

2.文件头

1.查看文件头信息:

readelf -h hello.out

输出:

ELF 头:

Magic: 7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00

类别: ELF64

数据: 2 补码,小端序 (little endian)

版本: 1 (current)

OS/ABI: UNIX - System V

ABI 版本: 0

类型: EXEC (可执行文件)

系统架构: Advanced Micro Devices X86-64

版本: 0x1

入口点地址: 0x400430

程序头起点: 64 (bytes into file)

Start of section headers: 6616 (bytes into file)

标志: 0x0

本头的大小: 64 (字节)

程序头大小: 56 (字节)

Number of program headers: 9

节头大小: 64 (字节)

节头数量: 31

字符串表索引节头: 28

2.查看十六进制格式:

hexedit hello.out

输出

00000000 7F 45 4C 46 02 01 01 00 00 00 00 00 00 00 00 00 .ELF............

00000010 02 00 3E 00 01 00 00 00 30 04 40 00 00 00 00 00 ..>.....0.@.....

00000020 40 00 00 00 00 00 00 00 D8 19 00 00 00 00 00 00 @...............

00000030 00 00 00 00 40 00 38 00 09 00 40 00 1F 00 1C 00 ....@.8...@.....

通过man 5 ELF对ELF手册的查看,可以知道头部可以使用一个结构体表示:

#define EI_NIDENT 16

typedef struct {

unsigned char e_ident[EI_NIDENT];

uint16_t e_type;

uint16_t e_machine;

uint32_t e_version;

ElfN_Addr e_entry;

ElfN_Off e_phoff;

ElfN_Off e_shoff;

uint32_t e_flags;

uint16_t e_ehsize;

uint16_t e_phentsize;

uint16_t e_phnum;

uint16_t e_shentsize;

uint16_t e_shnum;

uint16_t e_shstrndx;

} ElfN_Ehdr;

3.分析

e_ident:

第一行为ELF头,共16个字节 ,表示了e_ident

0~4:MAGIC

5: 02表示64位文件,若为1则为32位,否则都不是

6: 01表明是小端编码,为2则为大端编码

7: 01文件版本,1表明是当前版本

8~16: 暂时未用到,用于以后扩展

e_type

02 00 实际上应该为00 02,后面的部分看的时候也需要转换一下,即数值2,表明为可执行文件,其他的数值及类型对应关系在man手册中也都能查到。

e_machine

003E 为体系架构,

e_version

0001 为当前版本,也就是数值1

e_entry

30 04 40 00 00 00 00 00 ->00 00 00 00 00 40 04 30 即0x400430,为程序入口地址

e_phoff

0000 0000 0000 0040程序头起点 0x40=64byte

e_shoff

0000 0000 0000 19D8节头起点 0x19d8=6616

e_flags

0000 0000 标志:0x0

e_ehsize

0040 ELF头长度 0x40=64

e_phentsize

0038 程序头长度 0x38=56

e_phnum

0009 程序头表的项目数量 9

e_shentsize

0040 节头表的项目大小 0x40=64字节

e_shnum

001F 节头数量0x1f=31

e_shstrndx

001C 字符串索引节头 0x1c=28

3.程序头实例:

程序头对段的描述,是程序装载必需的一部分。

1.查看程序头表:

使用如下命令:

readelf -l hello.out

输出:

Elf 文件类型为 EXEC (可执行文件)

入口点 0x400430

共有 9 个程序头,开始于偏移量 64

程序头:

Type Offset VirtAddr PhysAddr

FileSiz MemSiz Flags Align

PHDR 0x0000000000000040 0x0000000000400040 0x0000000000400040

0x00000000000001f8 0x00000000000001f8 R E 8

INTERP 0x0000000000000238 0x0000000000400238 0x0000000000400238

0x000000000000001c 0x000000000000001c R 1

[Requesting program interpreter: /lib64/ld-linux-x86-64.so.2]

LOAD 0x0000000000000000 0x0000000000400000 0x0000000000400000

0x00000000000006fc 0x00000000000006fc R E 200000

LOAD 0x0000000000000e10 0x0000000000600e10 0x0000000000600e10

0x0000000000000228 0x0000000000000230 RW 200000

DYNAMIC 0x0000000000000e28 0x0000000000600e28 0x0000000000600e28

0x00000000000001d0 0x00000000000001d0 RW 8

NOTE 0x0000000000000254 0x0000000000400254 0x0000000000400254

0x0000000000000044 0x0000000000000044 R 4

GNU_EH_FRAME 0x00000000000005d0 0x00000000004005d0 0x00000000004005d0

0x0000000000000034 0x0000000000000034 R 4

GNU_STACK 0x0000000000000000 0x0000000000000000 0x0000000000000000

0x0000000000000000 0x0000000000000000 RW 10

GNU_RELRO 0x0000000000000e10 0x0000000000600e10 0x0000000000600e10

0x00000000000001f0 0x00000000000001f0 R 1

Section to Segment mapping:

段节...

00

01 .interp

02 .interp .note.ABI-tag .note.gnu.build-id .gnu.hash .dynsym .dynstr .gnu.version .gnu.version_r .rela.dyn .rela.plt .init .plt .plt.got .text .fini .rodata .eh_frame_hdr .eh_frame

03 .init_array .fini_array .jcr .dynamic .got .got.plt .data .bss

04 .dynamic

05 .note.ABI-tag .note.gnu.build-id

06 .eh_frame_hdr

07

08 .init_array .fini_array .jcr .dynamic .got

这个表中的每一项都是对应着结构体Elf64_Phdr的:

typedef struct {

uint32_t p_type;

uint32_t p_flags;

Elf64_Off p_offset;

Elf64_Addr p_vaddr;

Elf64_Addr p_paddr;

uint64_t p_filesz;

uint64_t p_memsz;

uint64_t p_align;

} Elf64_Phdr;

2.对各段的观察

PHDR:

在实例中的地址为0x40~0x238,可以用hexedit去观察它的内容,不过基本都是不可见字符,所以写出来没什么意义。

PHDR段保存了程序头表本身的位置和大小,程序头表则保存了所有的程序头对文件中段的描述信息。

这里我们可以简单的进行一个计算,这个结构体所占空间为4+4+8+8+8+8+8+8=56,一共9个项目,所以

hex(56)+0x40=0x238,这也足以说明这一段的含义。

INTERP:

对程序解释器位置的描述 0x238-0x254,这一段可以用hexedit观察到内容:

从可见字符可知,这个程序的程序解释器为:/lib64/ld-linux-x86-64.so.2

2F 6C 69 62 36 34 2F 6C ......../lib64/l

00000240 64 2D 6C 69 6E 75 78 2D 78 38 36 2D 36 34 2E 73 d-linux-x86-64.s

00000250 6F 2E 32 00 o.2.

LOAD1:

程序代码段: 0x000-0x6fc,这里内容太多就不贴出来了,主要是一些机器指令。

LOAD2:

数据段:0xe10-0x1038,这里大多数是二进制数据,全部贴出来也没有什么意义

DYNAMIC

动态段:0xe28~0xff8

动态段包含了动态链接器所必需的一些信息,在动态段中包含了一些标记和指针。包括运行时所需要的共享库列表、全局偏移表的地址,以及重定位条目的相关信息等。

NOTE:

0x254-0x298,保存了与特定供应商或者系统相关的附加信息,实际的可执行文件运行时并不需要这个段,可以查看一下这一段的内容。

04 00 00 00 10 00 00 00 01 00 00 00 o.2.............

00000260 47 4E 55 00 00 00 00 00 02 00 00 00 06 00 00 00 GNU.............

00000270 20 00 00 00 04 00 00 00 14 00 00 00 03 00 00 00 ...............

00000280 47 4E 55 00 3D F4 DD 79 8B A7 5D 1A 69 C7 CD C9 GNU.=..y..].i...

00000290 1E E5 C1 CD 69 A2 C8 F4

GNU_*:

这部分似乎并不怎么被关注,并且man手册中提的也很少,所以先忽略掉。

3.分析

通过对以上的地址观察,我们可以得到一些结论:

执行所需的程序部分总体上可以看做是代码段和数据段组成的。

PHDR、INTERP、NOTE段被包含在了代码段中。

DYNAMIC段被包含在了数据段中。

4.程序的剩余部分

剩余地址的部分为对ELF节头的描述,如果去掉,程序仍然可以正常运行,但是无法利用节头来引用节,默认是有节头的,这里我也是试验了一下,因为我分析的文件就是一个可执行文件,数据段停止于0x1038,于是我将0x1038之后的所有字节全部清除掉,这里我做了一个副本hello1.out,然后用vim+:%!xxd做到的直接对二进制文件进行编辑。编辑之后再查看差不多是这个样子:

7715710d1dd1c883f4dd8a45a8ad8ec9.png

然后运行一下编辑之后的程序:

e2b9b1d09e7b627094172baf656be59a.png

可以来查看一下编辑后的hello1.out的大小,也发生了变化:

537eae29b2b7261a4d910ec44761fb10.png

可见程序仍可运行,而我们用指令去查询这个新程序的节头表时,会出现以下的现象:

6336f18f3787b9c5c8d4b8c0b4c00404.png

可见,这个程序的确找不到节头表了,尽管没有节头可执行程序仍然可以运行,但是这样的程序会让gdb、objdump这样的工具没法排上用场,也会对逆向造成极大的障碍。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值