0%

ELF文件头

概述

ELF文件头包含了该可执行文件的一些系统级信息:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
e_ident[16]:ELF文件的标识 包括魔数和其他信息
e_type: 文件类型 例如可执行文件, 目标文件, 共享目标文件等
e_machine: 文件运行的CPU体系结构
e_version: ELF文件的版本号
e_entry: 程序的入口点地址
e_phoff: 程序头表在文件中的偏移量
e_shoff: 节头表在文件中的偏移量
e_flags: 处理器专用标志
e_ehsize: ELF文件头的大小
e_phentsize:程序头表中每个条目的大小
e_phnum: 程序头表中条目的数量
e_shentsize:节头表中每个条目的大小
e_shnum: 节头表中条目的数量
e_shstrndx: 节头字符串表的索引

更改其中的某些数据 例如程序入口点, 操作系统位数, 大小端序等 会导致程序运行流程改变 从而使得文件无法被工具识别 达到隐藏程序主要功能的目的

文件头结构

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
#define EI_NIDENT 16

typedef struct {
unsigned char e_ident[EI_NIDENT];
Elf32_Half e_type;
Elf32_Half e_machine;
Elf32_Word e_version;
Elf32_Addr e_entry;
Elf32_Off e_phoff;
Elf32_Off e_shoff;
Elf32_Word e_flags;
Elf32_Half e_ehsize;
Elf32_Half e_phentsize;
Elf32_Half e_phnum;
Elf32_Half e_shentsize;
Elf32_Half e_shnum;
Elf32_Half e_shstrndx;
} Elf32_Ehdr;

typedef struct {
unsigned char e_ident[EI_NIDENT];
Elf64_Half e_type;
Elf64_Half e_machine;
Elf64_Word e_version;
Elf64_Addr e_entry;
Elf64_Off e_phoff;
Elf64_Off e_shoff;
Elf64_Word e_flags;
Elf64_Half e_ehsize;
Elf64_Half e_phentsize;
Elf64_Half e_phnum;
Elf64_Half e_shentsize;
Elf64_Half e_shnum;
Elf64_Half e_shstrndx;
} Elf64_Ehdr;

其中各个字段所占字节数如下:

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程序为例:

QQ截图20240226163704

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 00EM_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

储存了决定处理器如何处理程序的标志 对不同处理器有不同的含义 常见的如下:

  1. 执行模式标志: 指示程序运行的特定执行模式或特权级别 如用户模式 , 内核模式等
  2. 处理器特性: 指示处理器的一些特定特性或功能 如浮点运算支持 , 多核处理器等
  3. 优化标志: 指示编译器在生成代码时应该使用的优化级别或优化策略
  4. 安全标志: 指示一些安全特性或安全策略 如栈保护 , 内存保护等
  5. 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做一下对比:

image-20240226195321494

不难看出文件头已经被改得很抽象了

用IDA打开也提示入口错误:

image-20240226195519637

对比多个ELF文件的入口点可以发现以下特征:

image-20240226200148494

可以看得出来其实这是个64位程序 再根据表头等偏移也可以看出实际上这应该是一个小端序ELF 更改文件头中对应的标志位 再次打开IDA可以看到start入口:

1
2
3
4
5
6
7
8
9
10
11
12
LOAD:000005C7D084C137                               start:
LOAD:000005C7D084C137 48 8D 35 32 04 00 00 lea rsi, unk_5C7D084C570
LOAD:000005C7D084C13E 48 C7 C1 14 00 00 00 mov rcx, 14h
LOAD:000005C7D084C145 49 B8 F7 51 A4 C4 C0 28 FC 8F mov r8, 8FFC28C0C4A451F7h
LOAD:000005C7D084C145
LOAD:000005C7D084C14F
LOAD:000005C7D084C14F loc_5C7D084C14F: ; CODE XREF: LOAD:000005C7D084C156↓j
LOAD:000005C7D084C14F 4C 31 06 xor [rsi], r8
LOAD:000005C7D084C152 48 83 C6 08 add rsi, 8
LOAD:000005C7D084C156 E2 F7 loop loc_5C7D084C14F
LOAD:000005C7D084C156
LOAD:000005C7D084C158 E9 13 04 00 00 jmp near ptr unk_5C7D084C570

可以看到是一个SMC 再通过strace指令可以看到启用了ptrace反调试:

1
2
3
4
execve("./technically_correct", ["./technically_correct"], 0x7ffde09bb8d8 /* 32 vars */) = 0
ptrace(PTRACE_TRACEME) = -1 EPERM (Operation not permitted)
exit(0) = ?
+++ exited with 0 +++

所以编写一个脚本来进行SMC:

1
2
3
4
5
6
7
8
9
import idc
import ida_idaapi

start = 0x5C7D084C570
xorkey = 0x8FFC28C0C4A451F7
round = 0x14
for i in range(round):
idc.patch_qword(start + 8 * i, idc.get_qword(start + 8 * i) ^ xorkey)
print("Round %d: 0x%016X" % (i, start + 8 * i))

得到处理后的汇编代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
LOAD:000005C7D084C570                               loc_5C7D084C570:                        ; CODE XREF: LOAD:000005C7D084C158↑j
LOAD:000005C7D084C570 ; DATA XREF: LOAD:start↑o
LOAD:000005C7D084C570 90 nop ; Keypatch filled range [0x5C7D084C570:0x5C7D084C576] (7 bytes), replaced:
LOAD:000005C7D084C570 ; mov eax, 65h
LOAD:000005C7D084C570 ; syscall
LOAD:000005C7D084C571 90 nop
LOAD:000005C7D084C572 90 nop
LOAD:000005C7D084C573 90 nop
LOAD:000005C7D084C574 90 nop
LOAD:000005C7D084C575 90 nop
LOAD:000005C7D084C576 90 nop
LOAD:000005C7D084C577 85 C0 test eax, eax
LOAD:000005C7D084C579 0F 85 84 00 00 00 jnz loc_5C7D084C603
LOAD:000005C7D084C579
LOAD:000005C7D084C57F 5E pop rsi
LOAD:000005C7D084C580 48 83 FE 02 cmp rsi, 2
LOAD:000005C7D084C584 7C 58 jl short no ; no
LOAD:000005C7D084C584
LOAD:000005C7D084C586 5E pop rsi
LOAD:000005C7D084C587 5E pop rsi
LOAD:000005C7D084C588 48 BB E8 88 1F BC 84 0F 00 00 mov rbx, 0F84BC1F88E8h
LOAD:000005C7D084C588
LOAD:000005C7D084C592
LOAD:000005C7D084C592 loc_5C7D084C592: ; CODE XREF: LOAD:000005C7D084C5CD↓j
LOAD:000005C7D084C592 0F B6 06 movzx eax, byte ptr [rsi] ; get input
LOAD:000005C7D084C595 3C 0A cmp al, 0Ah
LOAD:000005C7D084C597 74 36 jz short yes_1
LOAD:000005C7D084C597
LOAD:000005C7D084C599 3C 00 cmp al, 0
LOAD:000005C7D084C59B 74 32 jz short yes_1
LOAD:000005C7D084C59B
LOAD:000005C7D084C59D 3C 7E cmp al, 7Eh ; '~'
LOAD:000005C7D084C59F 77 3D ja short no ; no
LOAD:000005C7D084C59F
LOAD:000005C7D084C5A1 2C 20 sub al, 20h ; ' '
LOAD:000005C7D084C5A3 72 39 jb short no ; no
LOAD:000005C7D084C5A3
LOAD:000005C7D084C5A5
LOAD:000005C7D084C5A5 loc_5C7D084C5A5: ; CODE XREF: LOAD:000005C7D084C60C↓j
LOAD:000005C7D084C5A5 48 8D 14 C3 lea rdx, [rbx+rax*8] ; 0xF84BC1F88E8 + input[i]
LOAD:000005C7D084C5A9 48 8B 1A mov rbx, [rdx] ; mem[0xF84BC1F88E8 + input[i]]
LOAD:000005C7D084C5AC 48 31 D3 xor rbx, rdx ; 0xF84BC1F88E8 + input[i] ^ mem[0xF84BC1F88E8 + input[i]]
LOAD:000005C7D084C5AF 49 B8 93 E6 C1 48 3C CB 16 B2 mov r8, 0B216CB3C48C1E693h
LOAD:000005C7D084C5B9 49 0F AF D8 imul rbx, r8 ; 0xF84BC1F88E8 + input[i] ^ mem[0xF84BC1F88E8 + input[i]] * 0xB216CB3C48C1E693
LOAD:000005C7D084C5BD 49 B8 9D 52 7C 26 D3 C6 00 C2 mov r8, 0C200C6D3267C529Dh
LOAD:000005C7D084C5C7 4C 01 C3 add rbx, r8 ; 0xF84BC1F88E8 + input[i] ^ mem[0xF84BC1F88E8 + input[i]] * 0xB216CB3C48C1E693 + 0xC200C6D3267C529D
LOAD:000005C7D084C5CA 48 FF C6 inc rsi ; i++
LOAD:000005C7D084C5CD EB C3 jmp short loc_5C7D084C592 ; get input
LOAD:000005C7D084C5CD
LOAD:000005C7D084C5CF ; ---------------------------------------------------------------------------
LOAD:000005C7D084C5CF
LOAD:000005C7D084C5CF yes_1: ; CODE XREF: LOAD:000005C7D084C597↑j
LOAD:000005C7D084C5CF ; LOAD:000005C7D084C59B↑j
LOAD:000005C7D084C5CF 48 B9 E0 0B C0 8F 03 07 00 00 mov rcx, 7038FC00BE0h
LOAD:000005C7D084C5D9 48 39 CB cmp rbx, rcx
LOAD:000005C7D084C5DC 74 0C jz short yes ; yes
LOAD:000005C7D084C5DC
LOAD:000005C7D084C5DE
LOAD:000005C7D084C5DE no: ; CODE XREF: LOAD:000005C7D084C584↑j
LOAD:000005C7D084C5DE ; LOAD:000005C7D084C59F↑j
LOAD:000005C7D084C5DE ; LOAD:000005C7D084C5A3↑j
LOAD:000005C7D084C5DE 68 6E 6F 0A 00 push 0A6F6Eh ; no
LOAD:000005C7D084C5E3 BA 03 00 00 00 mov edx, 3
LOAD:000005C7D084C5E8 EB 0A jmp short loc_5C7D084C5F4
LOAD:000005C7D084C5E8
LOAD:000005C7D084C5EA ; ---------------------------------------------------------------------------
LOAD:000005C7D084C5EA
LOAD:000005C7D084C5EA yes: ; CODE XREF: LOAD:000005C7D084C5DC↑j
LOAD:000005C7D084C5EA 68 79 65 73 0A push 0A736579h ; yes
LOAD:000005C7D084C5EF BA 04 00 00 00 mov edx, 4
LOAD:000005C7D084C5EF
LOAD:000005C7D084C5F4
LOAD:000005C7D084C5F4 loc_5C7D084C5F4: ; CODE XREF: LOAD:000005C7D084C5E8↑j
LOAD:000005C7D084C5F4 B8 01 00 00 00 mov eax, 1
LOAD:000005C7D084C5F9 BF 01 00 00 00 mov edi, 1
LOAD:000005C7D084C5FE
LOAD:000005C7D084C5FE loc_5C7D084C5FE: ; CODE XREF: LOAD:000005C7D084C616↓j
LOAD:000005C7D084C5FE 48 89 E6 mov rsi, rsp
LOAD:000005C7D084C601 0F 05 syscall ; write
LOAD:000005C7D084C601
LOAD:000005C7D084C603
LOAD:000005C7D084C603 loc_5C7D084C603: ; CODE XREF: LOAD:000005C7D084C579↑j
LOAD:000005C7D084C603 B8 3C 00 00 00 mov eax, 3Ch ; '<'
LOAD:000005C7D084C608 31 FF xor edi, edi
LOAD:000005C7D084C60A 0F 05 syscall

可以在https://filippo.io/linux-syscall-table/查找系统调用ID 目的是输出yes flag通过命令行参数传入 具体逻辑写在注释中 用readelf -a读出所有虚拟内存:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
ELF Header:
Magic: 7f 45 4c 46 02 01 a8 9e b6 21 74 80 06 55 b8 e5
Class: ELF64
Data: 2's complement, little endian
Version: 168 <unknown>
OS/ABI: <unknown: 9e>
ABI Version: 182
Type: EXEC (Executable file)
Machine: Advanced Micro Devices X86-64
Version: 0xc7b4d76e
Entry point address: 0x5c7d084c137
Start of program headers: 58 (bytes into file)
Start of section headers: 39047220244264590 (bytes into file)
Flags: 0xb214c4dd
Size of this header: 16830 (bytes)
Size of program headers: 56 (bytes)
Number of program headers: 61
Size of section headers: 1 (bytes)
Number of section headers: 0
Section header string table index: 47031 <corrupt: out of range>

There are no section groups in this file.

Program Headers:
Type Offset VirtAddr PhysAddr
FileSiz MemSiz Flags Align
LOAD 0x0000000000001000 0x00000265a3ce8000 0xc28f4b0d8a362161
0x0000000000001000 0x0000000000001000 RWE 0x3cc4d55e9677dc9e
LOAD 0x0000000000002000 0x00000516ef99c000 0x3656c86e821bb521
....
....
LOAD 0x000000000003c000 0x00000ae8f3641000 0x8da8b4d80a2f5556
0x0000000000001000 0x0000000000001000 RWE 0x3fce6f654e1eaf66
LOAD 0x000000000003d000 0x00000ccd481d4000 0x353d5269c3fd6e77
0x0000000000001000 0x0000000000001000 RWE 0x10eaf695dc252738

There is no dynamic section in this file.

数据在虚拟内存和其在二进制文件中相对文件头的偏移的关系是data_offset = seg_offset + data_virtaddr - seg_virtaddr 据此可以写出脚本解密:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
# segment_addr = """
# 0x00000265a3ce8000
# 0x00000516ef99c000
# ...
# 0x00000ae8f3641000
# 0x00000ccd481d4000
# """
# segment_start = [int(k, 16) for k in segment_addr.split("\n")[1:-1]]
segment_start = [2635563171840, 5596067250176, 242648760320, 5890218143744, 2735873134592, 12132385898496, 16697267249152, 3326682263552, 10460356464640, 8642733731840, 988726030336, 8708637028352, 2773839282176, 8133109415936, 3575512096768, 9797392764928, 1880326082560, 5535382552576, 5309783875584, 14846201360384, 15151867842560, 1420534173696, 3162423922688, 9884210810880, 3293418479616, 2848477061120, 9760137510912, 13268869967872, 6355754991616, 15773356351488, 17095304347648, 11089330114560, 8872599801856, 119536611328, 11962295312384, 14545242775552, 6175697338368, 3295986577408, 4857408086016, 1006577762304, 6169422487552, 10338827309056, 5784443174912, 7711878021120, 15179293179904, 2675108958208, 5810459635712, 15978780405760, 15805306486784, 12812716527616, 13144427872256, 5770609094656, 14127787970560, 17062766280704, 7485916786688, 3136088481792, 12072940883968, 3003644882944, 7554512248832, 11995632111616, 14075817705472]
# base = ((base + input[i]) ^ mem[base + input[i]]) * 0xB216CB3C48C1E693 + 0xC200C6D3267C529D
def find_physddr(addr:int):
for i in range(len(segment_start)):
if addr - segment_start[i] >= 0 and addr - segment_start[i] < 0x1000:
return (i + 1) * 0x1000 + addr - segment_start[i]
return 0

musk1 = 0x7FFFFFFFFFFFFFFF
musk2 = 0xFFFFFFFFFFFFFFFF
with open("technically_correct_edit", "rb") as f:
flag = [ord(i) - 0x20 for i in "lactf{"]
base = 0xF84BC1F88E8
i = 0
flags = []
stack = list()
visited = set()
while True:
data1 = (flag[i] * 0x8 + base) & musk2
f.seek(find_physddr(data1))
data2 = int.from_bytes(f.read(8), byteorder='little')
data3 = data1 ^ data2
data4 = (data3 * 0xB216CB3C48C1E693) & musk1
base = (data4 + 0xC200C6D3267C529D) & musk2
# print(hex(base))
i += 1
if i == len(flag):
break
visited.add(base)

stack.append((base, flag))
while stack:
base, flag = stack.pop()
if base in visited:
continue
visited.add(base)
if base == 0x7038FC00BE0:
flags.append(flag)
continue
for char in range(0x5E):
if find_physddr((char * 0x8 + base) & musk2):
data1 = (char * 0x8 + base) & musk2
f.seek(find_physddr(data1))
data2 = int.from_bytes(f.read(8), byteorder='little')
data3 = data1 ^ data2
data4 = (data3 * 0xB216CB3C48C1E693) & musk1
base_ = (data4 + 0xC200C6D3267C529D) & musk2
stack.append((base_, flag + [char]))
for flag in flags:
print("".join([chr(i + 0x20) for i in flag]))
# lactf{i_l0v3_l1nux_elf_p4rs1ng}

以后遇到再更新应用案例…