概述
ELF文件头包含了该可执行文件的一些系统级信息:
1 | e_ident[16]:ELF文件的标识 包括魔数和其他信息 |
更改其中的某些数据 例如程序入口点, 操作系统位数, 大小端序等 会导致程序运行流程改变 从而使得文件无法被工具识别 达到隐藏程序主要功能的目的
文件头结构
1 |
|
其中各个字段所占字节数如下:
In ELF32/bytes | In ELF64/bytes | |
---|---|---|
e_ident[16] | 16 | 16 |
e_type | 2 | 2 |
e_machine | 2 | 2 |
e_version | 4 | 4 |
e_entry | 4 | 8 |
e_phoff | 4 | 8 |
e_shoff | 4 | 8 |
e_flags | 4 | 4 |
e_ehsize | 2 | 2 |
e_phentsize | 2 | 2 |
e_phnum | 2 | 2 |
e_shentsize | 2 | 2 |
e_shnum | 2 | 2 |
e_shstrndx | 2 | 2 |
Total | 52 | 64 |
各个字段所含值的含义
e_ident
参考信息:https://refspecs.linuxfoundation.org/elf/gabi4+/ch4.eheader.html#elfid
其中索引对应的信息如下:
Name | Value | Purpose |
---|---|---|
EI_MAG0 |
0 |
File identification |
EI_MAG1 |
1 |
File identification |
EI_MAG2 |
2 |
File identification |
EI_MAG3 |
3 |
File identification |
EI_CLASS |
4 |
File class |
EI_DATA |
5 |
Data encoding |
EI_VERSION |
6 |
File version |
EI_OSABI |
7 |
Operating system/ABI identification |
EI_ABIVERSION |
8 |
ABI version |
EI_PAD |
9 |
Start of padding bytes |
EI_NIDENT |
16 |
Size of e_ident[] |
前4字节通常是b"\x7fELF"
e_ident[CLASS]指定了该程序为多少位程序:
Name | Value | Meaning |
---|---|---|
ELFCLASSNONE |
0 |
Invalid class |
ELFCLASS32 |
1 |
32-bit objects |
ELFCLASS64 |
2 |
64-bit objects |
e_ident[DATA]制定了该程序中数据的编码方式:
Name | Value | Meaning |
---|---|---|
ELFDATANONE |
0 |
Invalid data encoding |
ELFDATA2LSB |
1 |
Little ending |
ELFDATA2MSB |
2 |
Big ending |
e_ident[VERSION]指定当前ELF的版本 必须为EV_CURRENT EV_CURRENT宏(正常为1)会根据未经更改的ELF文件版本确定
e_ident[OSABI]制定了程序在哪种平台上运行 使用哪种ABI(ABI 是一个接口标准 用于定义二进制接口的规范 包括函数调用约定, 数据类型大小, 系统调用等方面的规定
):
Name | Value | Meaning |
---|---|---|
ELFOSABI_NONE |
0 |
No extensions or unspecified |
ELFOSABI_HPUX |
1 |
Hewlett-Packard HP-UX |
ELFOSABI_NETBSD |
2 |
NetBSD |
ELFOSABI_LINUX |
3 |
Linux |
ELFOSABI_SOLARIS |
6 |
Sun Solaris |
ELFOSABI_AIX |
7 |
AIX |
ELFOSABI_IRIX |
8 |
IRIX |
ELFOSABI_FREEBSD |
9 |
FreeBSD |
ELFOSABI_TRU64 |
10 |
Compaq TRU64 UNIX |
ELFOSABI_MODESTO |
11 |
Novell Modesto |
ELFOSABI_OPENBSD |
12 |
Open BSD |
ELFOSABI_OPENVMS |
13 |
Open VMS |
ELFOSABI_NSK |
14 |
Hewlett-Packard Non-Stop Kernel |
64-255 |
Architecture-specific value range |
e_ident[EI_ABIVERSION]指定了所使用的ABI版本 区分不兼容的版本
e_ident[EI_PAD]制定了e_ident未使用的字节数 一开始为0 在未来使用了这些未使用的字节数时会改变
以64位Ubuntu的ls程序为例:
e_type
标记该文件具体类型:
Name | Value | Meaning |
---|---|---|
ET_NONE |
0 |
No file type |
ET_REL |
1 |
Relocatable file |
ET_EXEC |
2 |
Executable file |
ET_DYN |
3 |
Shared object file |
ET_CORE |
4 |
Core file |
ET_LOOS |
0xfe00 |
Operating system-specific |
ET_HIOS |
0xfeff |
Operating system-specific |
ET_LOPROC |
0xff00 |
Processor-specific |
ET_HIPROC |
0xffff |
Processor-specific |
对于ls 这个值是03 00
也就是小端序的3
e_machine
指定了程序机器码所使用的汇编体系:
Name | Value | Meaning |
---|---|---|
EM_NONE |
0 |
No machine |
EM_M32 |
1 |
AT&T WE 32100 |
EM_SPARC |
2 |
SPARC |
EM_386 |
3 |
Intel 80386 |
EM_68K |
4 |
Motorola 68000 |
EM_88K |
5 |
Motorola 88000 |
reserved | 6 |
Reserved for future use (was EM_486 ) |
EM_860 |
7 |
Intel 80860 |
EM_MIPS |
8 |
MIPS I Architecture |
EM_S370 |
9 |
IBM System/370 Processor |
EM_MIPS_RS3_LE |
10 |
MIPS RS3000 Little-endian |
reserved | 11-14 |
Reserved for future use |
EM_PARISC |
15 |
Hewlett-Packard PA-RISC |
reserved | 16 |
Reserved for future use |
EM_VPP500 |
17 |
Fujitsu VPP500 |
EM_SPARC32PLUS |
18 |
Enhanced instruction set SPARC |
EM_960 |
19 |
Intel 80960 |
EM_PPC |
20 |
PowerPC |
EM_PPC64 |
21 |
64-bit PowerPC |
EM_S390 |
22 |
IBM System/390 Processor |
reserved | 23-35 |
Reserved for future use |
EM_V800 |
36 |
NEC V800 |
EM_FR20 |
37 |
Fujitsu FR20 |
EM_RH32 |
38 |
TRW RH-32 |
EM_RCE |
39 |
Motorola RCE |
EM_ARM |
40 |
Advanced RISC Machines ARM |
EM_ALPHA |
41 |
Digital Alpha |
EM_SH |
42 |
Hitachi SH |
EM_SPARCV9 |
43 |
SPARC Version 9 |
EM_TRICORE |
44 |
Siemens TriCore embedded processor |
EM_ARC |
45 |
Argonaut RISC Core, Argonaut Technologies Inc. |
EM_H8_300 |
46 |
Hitachi H8/300 |
EM_H8_300H |
47 |
Hitachi H8/300H |
EM_H8S |
48 |
Hitachi H8S |
EM_H8_500 |
49 |
Hitachi H8/500 |
EM_IA_64 |
50 |
Intel IA-64 processor architecture |
EM_MIPS_X |
51 |
Stanford MIPS-X |
EM_COLDFIRE |
52 |
Motorola ColdFire |
EM_68HC12 |
53 |
Motorola M68HC12 |
EM_MMA |
54 |
Fujitsu MMA Multimedia Accelerator |
EM_PCP |
55 |
Siemens PCP |
EM_NCPU |
56 |
Sony nCPU embedded RISC processor |
EM_NDR1 |
57 |
Denso NDR1 microprocessor |
EM_STARCORE |
58 |
Motorola Star*Core processor |
EM_ME16 |
59 |
Toyota ME16 processor |
EM_ST100 |
60 |
STMicroelectronics ST100 processor |
EM_TINYJ |
61 |
Advanced Logic Corp. TinyJ embedded processor family |
EM_X86_64 |
62 |
AMD x86-64 architecture |
EM_PDSP |
63 |
Sony DSP Processor |
EM_PDP10 |
64 |
Digital Equipment Corp. PDP-10 |
EM_PDP11 |
65 |
Digital Equipment Corp. PDP-11 |
EM_FX66 |
66 |
Siemens FX66 microcontroller |
EM_ST9PLUS |
67 |
STMicroelectronics ST9+ 8/16 bit microcontroller |
EM_ST7 |
68 |
STMicroelectronics ST7 8-bit microcontroller |
EM_68HC16 |
69 |
Motorola MC68HC16 Microcontroller |
EM_68HC11 |
70 |
Motorola MC68HC11 Microcontroller |
EM_68HC08 |
71 |
Motorola MC68HC08 Microcontroller |
EM_68HC05 |
72 |
Motorola MC68HC05 Microcontroller |
EM_SVX |
73 |
Silicon Graphics SVx |
EM_ST19 |
74 |
STMicroelectronics ST19 8-bit microcontroller |
EM_VAX |
75 |
Digital VAX |
EM_CRIS |
76 |
Axis Communications 32-bit embedded processor |
EM_JAVELIN |
77 |
Infineon Technologies 32-bit embedded processor |
EM_FIREPATH |
78 |
Element 14 64-bit DSP Processor |
EM_ZSP |
79 |
LSI Logic 16-bit DSP Processor |
EM_MMIX |
80 |
Donald Knuth’s educational 64-bit processor |
EM_HUANY |
81 |
Harvard University machine-independent object files |
EM_PRISM |
82 |
SiTera Prism |
EM_AVR |
83 |
Atmel AVR 8-bit microcontroller |
EM_FR30 |
84 |
Fujitsu FR30 |
EM_D10V |
85 |
Mitsubishi D10V |
EM_D30V |
86 |
Mitsubishi D30V |
EM_V850 |
87 |
NEC v850 |
EM_M32R |
88 |
Mitsubishi M32R |
EM_MN10300 |
89 |
Matsushita MN10300 |
EM_MN10200 |
90 |
Matsushita MN10200 |
EM_PJ |
91 |
picoJava |
EM_OPENRISC |
92 |
OpenRISC 32-bit embedded processor |
EM_ARC_A5 |
93 |
ARC Cores Tangent-A5 |
EM_XTENSA |
94 |
Tensilica Xtensa Architecture |
EM_VIDEOCORE |
95 |
Alphamosaic VideoCore processor |
EM_TMM_GPP |
96 |
Thompson Multimedia General Purpose Processor |
EM_NS32K |
97 |
National Semiconductor 32000 series |
EM_TPC |
98 |
Tenor Network TPC processor |
EM_SNP1K |
99 |
Trebia SNP 1000 processor |
EM_ST200 |
100 |
STMicroelectronics (www.st.com) ST200 microcontroller |
在ls中这个值位3E 00
即EM_X86_64
e_version
如上述e_ident[VERSION]所说 标记当前程序版本号
Name | Value | Meaning |
---|---|---|
EV_NONE |
0 |
Invalid version |
EV_CURRENT |
1 |
Current version |
在ls中这个值为01 00 00 00
e_entry
指定一个虚拟地址来让系统从这个相对文件头的这个偏移地址开始翻译机器码并生成进程 要修改程序功能主要就是通过修改这个值
在ls中 这个值为A0 6A 00 00 00 00 00 00 00
意味着ls的程序入口点是0x6AA0
e_phoff & e_shoff
分别指定了程序头表和段表的偏移量 若没有则为0
e_flags
储存了决定处理器如何处理程序的标志 对不同处理器有不同的含义 常见的如下:
- 执行模式标志: 指示程序运行的特定执行模式或特权级别 如用户模式 , 内核模式等
- 处理器特性: 指示处理器的一些特定特性或功能 如浮点运算支持 , 多核处理器等
- 优化标志: 指示编译器在生成代码时应该使用的优化级别或优化策略
- 安全标志: 指示一些安全特性或安全策略 如栈保护 , 内存保护等
- ABI 版本标志: 指示使用的 ABI 版本或兼容性标志 用于指定程序与操作系统或其他库的兼容性
e_ehsize
(eh 是那个eh吗)标记了文件头的大小 正如上面统计的 ls中这个值为40 00
即64
e_phentsize
标记了程序头表中每个字段的字节大小(每个字段的大小相同) 在ls中为38 00
e_phnum
标记了程序头表的个数 与e_phentsize
的成绩就是程序头表的大小 若没有则为0 在ls中为0D 00
e_shentsize & e_shnum
标记段信息 与上述两个同理 不同的是段头表的数量如果超出了预留索引值0xff00
的话e_shnum
的值会被设为0 并将真正的值放入段头表的sh_size
数组的索引为0的位置
e_shstrndx
与段名表一同标记段头表的入口索引 如果没有段名表则储存SHN_UNDEF
与段头表数量类似超出0xff00
的话会储存在段头表的sh_link
数组的索引为0的位置
应用场合
修改某些与程序运行无关信息不会导致程序无法运行 同时可以干扰分析软件对其的识别 同时改变程序入口点还会导致程序跳过某段机器码 从而隐藏真实操作 例如:
LACTF2024/rev/technically_correct | 魔改ELF文件头
(没错我就是为了这碗醋包的饺子)DIE无法检测到可执行文件信息 binwalk识别错误 直接010打开 与ls做一下对比:
不难看出文件头已经被改得很抽象了
用IDA打开也提示入口错误:
对比多个ELF文件的入口点可以发现以下特征:
可以看得出来其实这是个64位程序 再根据表头等偏移也可以看出实际上这应该是一个小端序ELF 更改文件头中对应的标志位 再次打开IDA可以看到start
入口:
1 | LOAD:000005C7D084C137 start: |
可以看到是一个SMC 再通过strace
指令可以看到启用了ptrace
反调试:
1 | execve("./technically_correct", ["./technically_correct"], 0x7ffde09bb8d8 /* 32 vars */) = 0 |
所以编写一个脚本来进行SMC:
1 | import idc |
得到处理后的汇编代码:
1 | LOAD:000005C7D084C570 loc_5C7D084C570: ; CODE XREF: LOAD:000005C7D084C158↑j |
可以在https://filippo.io/linux-syscall-table/查找系统调用ID 目的是输出yes
flag通过命令行参数传入 具体逻辑写在注释中 用readelf -a
读出所有虚拟内存:
1 | ELF Header: |
数据在虚拟内存和其在二进制文件中相对文件头的偏移的关系是data_offset = seg_offset + data_virtaddr - seg_virtaddr
据此可以写出脚本解密:
1 | # segment_addr = """ |
以后遇到再更新应用案例…