1.ELF格式

我们先来看看 ELF 文件头,如果想详细了解,可以查看ELF的man page文档。

关于ELF更详细的说明:
e_shoff:节头表的文件偏移量(字节)。如果文件没有节头表,则此成员值为零。
sh_offset:表示了该section(节)离开文件头部位置的距离

 +-------------------+| ELF header        |---+
+---------> +-------------------+   | e_shoff
|           |                   |<--+
| Section   | Section header 0  |
|           |                   |---+ sh_offset
| Header    +-------------------+   |
|           | Section header 1  |---|--+ sh_offset
| Table     +-------------------+   |  |
|           | Section header 2  |---|--|--+
+---------> +-------------------+   |  |  || Section 0         |<--+  |  |+-------------------+      |  | sh_offset| Section 1         |<-----+  |+-------------------+         || Section 2         |<--------++-------------------+

2.可执行头部(Executable Header)

ELF文件的第一部分是可执行文件头部(Executable Header),其中包含有关ELF文件类型的信息。
ELF文件在各种平台下都通用,ELF文件有32位版本和64位版本,其文件头内容是一样的,只不过有些成员的大小不一样。它的文件图也有两种版本:分别叫"Elf32_Ehdr"和"Elf64_Ehdr"。
这里以32位版本为例:

#define EI_NIDENT (16)typedef struct {unsigned char e_ident[EI_NIDENT];     /* Magic number and other info */Elf32_Half    e_type;                 /* Object file type */Elf32_Half    e_machine;              /* Architecture */Elf32_Word    e_version;              /* Object file version */Elf32_Addr    e_entry;                /* Entry point virtual address */Elf32_Off     e_phoff;                /* Program header table file offset */Elf32_Off     e_shoff;                /* Section header table file offset */Elf32_Word    e_flags;                /* Processor-specific flags */Elf32_Half    e_ehsize;               /* ELF header size in bytes */Elf32_Half    e_phentsize;            /* Program header table entry size */Elf32_Half    e_phnum;                /* Program header table entry count */Elf32_Half    e_shentsize;            /* Section header table entry size */Elf32_Half    e_shnum;                /* Section header table entry count */Elf32_Half    e_shstrndx;             /* Section header string table index */
} Elf32_Ehdr;

使用readelf对ELF文件格式进行分析

# readelf -h /bin/ls
ELF Header:Magic:   7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00 Class:                             ELF64Data:                              2's complement, little endianVersion:                           1 (current)OS/ABI:                            UNIX - System VABI Version:                       0Type:                              DYN (Shared object file)Machine:                           Advanced Micro Devices X86-64Version:                           0x1Entry point address:               0x6130Start of program headers:          64 (bytes into file)Start of section headers:          137000 (bytes into file)Flags:                             0x0Size of this header:               64 (bytes)Size of program headers:           56 (bytes)Number of program headers:         11Size of section headers:           64 (bytes)Number of section headers:         29Section header string table index: 28

我们可以使用以下计算方法来计算整个二进制文件的大小:

size = e_shoff + (e_shnum * e_shentsize)

size = Start of section headers + (Number of section headers * Size of section headers)

size = 137000 + (29*64) = 138856
计算结果验证:

# ls -l /bin/ls
-rwxr-xr-x 1 root root 138856 Aug 29 21:20 /bin/ls

3、程序头部(Program Headers)

程序头部是描述文件中的各种segments(段),用来告诉系统如何创建进程映像的。

typedef struct {Elf32_Word    p_type;                 /* Segment type */Elf32_Off     p_offset;               /* Segment file offset */Elf32_Addr    p_vaddr;                /* Segment virtual address */Elf32_Addr    p_paddr;                /* Segment physical address */Elf32_Word    p_filesz;               /* Segment size in file */Elf32_Word    p_memsz;                /* Segment size in memory */Elf32_Word    p_flags;                /* Segment flags */Elf32_Word    p_align;                /* Segment alignment */
} Elf32_Phdr;

4、节表头部(Section Headers)

节表头部(Section Headers)包含了描述文件节区的信息,比如大小、偏移等,但这些对二进制文件的执行流程来说并不重要。

  • sections 或者 segments:segments是从运行的角度来描述elf文件,sections是从链接的角度来描述elf文件,也就是说,在链接阶段,我们可以忽略program header table来处理此文件,在运行阶段可以忽略section header table来处理此程序(所以很多加固手段删除了section header table)。从图中我们也可以看出, segments与sections是包含的关系,一个segment包含若干个section。

typedef struct {Elf32_Word    sh_name;                /* section的名字 (string tbl index) */Elf32_Word    sh_type;                /*section类别 */Elf32_Word    sh_flags;               /* section在进程中执行的特性(读、写) */Elf32_Addr    sh_addr;                /* 在内存中开始的虚地址 */Elf32_Off     sh_offset;              /* 此section在文件中的偏移 */Elf32_Word    sh_size;                /* Section size in bytes */Elf32_Word    sh_link;                /* Link to another section */Elf32_Word    sh_info;                /* Additional section information */Elf32_Word    sh_addralign;           /* Section alignment */Elf32_Word    sh_entsize;             /* Entry size if section holds table */
} Elf32_Shdr;

5、表(Section)

5.1 .bss Section

保存未初始化的数据,比如那些未初始化的全局变量。

5.2 .data Section

保存已初始化的数据。

5.3 .rodata Section

保存程序中的只读数据。

5.4 .text Section

本节包含程序的实际代码,逻辑流程。
使用readelf查看ELF文件表结构

# readelf -S --wide /bin/ls
There are 29 section headers, starting at offset 0x21728:Section Headers:[Nr] Name              Type            Address          Off    Size   ES Flg Lk Inf Al[ 0]                   NULL            0000000000000000 000000 000000 00      0   0  0[ 1] .interp           PROGBITS        00000000000002a8 0002a8 00001c 00   A  0   0  1[ 2] .note.ABI-tag     NOTE            00000000000002c4 0002c4 000020 00   A  0   0  4[ 3] .note.gnu.build-id NOTE            00000000000002e4 0002e4 000024 00   A  0   0  4[ 4] .gnu.hash         GNU_HASH        0000000000000308 000308 0000c0 00   A  5   0  8[ 5] .dynsym           DYNSYM          00000000000003c8 0003c8 000c90 18   A  6   1  8[ 6] .dynstr           STRTAB          0000000000001058 001058 0005d8 00   A  0   0  1[ 7] .gnu.version      VERSYM          0000000000001630 001630 00010c 02   A  5   0  2[ 8] .gnu.version_r    VERNEED         0000000000001740 001740 000070 00   A  6   1  8[ 9] .rela.dyn         RELA            00000000000017b0 0017b0 001350 18   A  5   0  8[10] .rela.plt         RELA            0000000000002b00 002b00 0009f0 18  AI  5  24  8[11] .init             PROGBITS        0000000000004000 004000 000017 00  AX  0   0  4[12] .plt              PROGBITS        0000000000004020 004020 0006b0 10  AX  0   0 16[13] .plt.got          PROGBITS        00000000000046d0 0046d0 000018 08  AX  0   0  8[14] .text             PROGBITS        00000000000046f0 0046f0 01253e 00  AX  0   0 16[15] .fini             PROGBITS        0000000000016c30 016c30 000009 00  AX  0   0  4[16] .rodata           PROGBITS        0000000000017000 017000 005129 00   A  0   0 32[17] .eh_frame_hdr     PROGBITS        000000000001c12c 01c12c 0008fc 00   A  0   0  4[18] .eh_frame         PROGBITS        000000000001ca28 01ca28 002ed0 00   A  0   0  8[19] .init_array       INIT_ARRAY      0000000000021390 020390 000008 08  WA  0   0  8[20] .fini_array       FINI_ARRAY      0000000000021398 020398 000008 08  WA  0   0  8[21] .data.rel.ro      PROGBITS        00000000000213a0 0203a0 000a38 00  WA  0   0 32[22] .dynamic          DYNAMIC         0000000000021dd8 020dd8 0001f0 10  WA  6   0  8[23] .got              PROGBITS        0000000000021fc8 020fc8 000038 08  WA  0   0  8[24] .got.plt          PROGBITS        0000000000022000 021000 000368 08  WA  0   0  8[25] .data             PROGBITS        0000000000022380 021380 000268 00  WA  0   0 32[26] .bss              NOBITS          0000000000022600 0215e8 0012d8 00  WA  0   0 32[27] .gnu_debuglink    PROGBITS        0000000000000000 0215e8 000034 00      0   0  4[28] .shstrtab         STRTAB          0000000000000000 02161c 00010a 00      0   0  1
Key to Flags:W (write), A (alloc), X (execute), M (merge), S (strings), I (info),L (link order), O (extra OS processing required), G (group), T (TLS),C (compressed), x (unknown), o (OS specific), E (exclude),l (large), p (processor specific)

6、完成简单的CTF挑战

既然已经对ELF文件有所了解了,那找一个CTF题目来试试吧。

二进制文件下载地址:https://ufile.io/blvpm

国内下载:https://www.lanzous.com/i34qg6f

1、运行这个程序,并传递一些随机字符给它,得到的结果如下:

# ./nix_5744af788e6cbdb29bb41e8b0e5f3cd5 aaaa[+] No flag for you. [+]

2、接着使用strings 查看一下程序的字符串,看是否能找到有用的信息

# strings nix_5744af788e6cbdb29bb41e8b0e5f3cd5
/lib/ld-linux.so.2
Mw1i#'0
libc.so.6
_IO_stdin_used
exit
sprintf
puts
strlen
__cxa_finalize
__libc_start_main
GLIBC_2.1.3
Y[^]
[^_]
UWVS
[^_]
Usage: script.exe <key>
Length of argv[1] too long.
[+] The flag is: SAYCURE{%s} [+]
[+] No flag for you. [+]
%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c
;*2$"
GCC: (Debian 8.2.0-8) 8.2.0
crtstuff.c

我们可以看到 “%c” 是打印flag的字符串,数量是15个。

3、我们可以查看“.rodata ”部分的偏移量,可以更好的查看这些字符

# readelf  -x .rodata nix_5744af788e6cbdb29bb41e8b0e5f3cd5Hex dump of section '.rodata':0x00002000 03000000 01000200 55736167 653a2073 ........Usage: s0x00002010 63726970 742e6578 65203c6b 65793e00 cript.exe <key>.0x00002020 4c656e67 7468206f 66206172 67765b31 Length of argv[10x00002030 5d20746f 6f206c6f 6e672e00 5b2b5d20 ] too long..[+] 0x00002040 54686520 666c6167 2069733a 20534159 The flag is: SAY0x00002050 43555245 7b25737d 205b2b5d 0a000a5b CURE{%s} [+]...[0x00002060 2b5d204e 6f20666c 61672066 6f722079 +] No flag for y0x00002070 6f752e20 5b2b5d00 25632563 25632563 ou. [+].%c%c%c%c0x00002080 25632563 25632563 25632563 25632563 %c%c%c%c%c%c%c%c0x00002090 25632563 256300                     %c%c%c.

4、检查符号表(Symbols)
nm命令查看库文件的符号

# nm -D nix_5744af788e6cbdb29bb41e8b0e5f3cd5 w __cxa_finalizeU exitw __gmon_start__
00002004 R _IO_stdin_usedw _ITM_deregisterTMCloneTablew _ITM_registerTMCloneTableU __libc_start_mainU printfU putsU sprintfU strlen

说明:
-D或–dynamic:显示动态符号。该任选项仅对于动态目标(例如特定类型的共享库)有意义
我们可以发现 printf, puts, sprintf, strlen functions.这些函数未定义。
5、跟踪系统调用(System Calls)
我们可以使用strace之类的工具去跟踪程序的系统调用

# strace ./nix_5744af788e6cbdb29bb41e8b0e5f3cd5 aaaa
execve("./nix_5744af788e6cbdb29bb41e8b0e5f3cd5", ["./nix_5744af788e6cbdb29bb41e8b0e"..., "aaaa"], 0x7ffd5ff92d18 /* 46 vars */) = 0
strace: [ Process PID=59965 runs in 32 bit mode. ]
brk(NULL)                               = 0x56f14000
access("/etc/ld.so.nohwcap", F_OK)      = -1 ENOENT (No such file or directory)
mmap2(NULL, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0xf7ef0000
access("/etc/ld.so.preload", R_OK)      = -1 ENOENT (No such file or directory)
openat(AT_FDCWD, "/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3
fstat64(3, {st_mode=S_IFREG|0644, st_size=220471, ...}) = 0
mmap2(NULL, 220471, PROT_READ, MAP_PRIVATE, 3, 0) = 0xf7eba000
close(3)                                = 0
access("/etc/ld.so.nohwcap", F_OK)      = -1 ENOENT (No such file or directory)
openat(AT_FDCWD, "/lib/i386-linux-gnu/libc.so.6", O_RDONLY|O_CLOEXEC) = 3
read(3, "\177ELF\1\1\1\3\0\0\0\0\0\0\0\0\3\0\3\0\1\0\0\0 \233\1\0004\0\0\0"..., 512) = 512
fstat64(3, {st_mode=S_IFREG|0755, st_size=1930924, ...}) = 0
mmap2(NULL, 1940000, PROT_READ, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0xf7ce0000
mprotect(0xf7cf9000, 1814528, PROT_NONE) = 0
mmap2(0xf7cf9000, 1359872, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x19000) = 0xf7cf9000
mmap2(0xf7e45000, 450560, PROT_READ, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x165000) = 0xf7e45000
mmap2(0xf7eb4000, 12288, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x1d3000) = 0xf7eb4000
mmap2(0xf7eb7000, 10784, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0xf7eb7000
close(3)                                = 0
set_thread_area({entry_number=-1, base_addr=0xf7ef10c0, limit=0x0fffff, seg_32bit=1, contents=0, read_exec_only=0, limit_in_pages=1, seg_not_present=0, useable=1}) = 0 (entry_number=12)
mprotect(0xf7eb4000, 8192, PROT_READ)   = 0
mprotect(0x5664d000, 4096, PROT_READ)   = 0
mprotect(0xf7f1e000, 4096, PROT_READ)   = 0
munmap(0xf7eba000, 220471)              = 0
fstat64(1, {st_mode=S_IFCHR|0620, st_rdev=makedev(0x88, 0x2), ...}) = 0
brk(NULL)                               = 0x56f14000
brk(0x56f35000)                         = 0x56f35000
brk(0x56f36000)                         = 0x56f36000
write(1, "\n", 1
)                       = 1
write(1, "[+] No flag for you. [+]\n", 25[+] No flag for you. [+]
) = 25
exit_group(26)                          = ?
+++ exited with 26 +++

为了更好地理解,我们可以使用ltrace解码C++来跟踪函数名所做的库调用。
我们可以看到正在进行字符串长度检查。

# ltrace -i -C ./nix_5744af788e6cbdb29bb41e8b0e5f3cd5 aaaaaaaa
[0x565570e1] __libc_start_main(0x565571e9, 2, 0xffe3a584, 0x56557400 <unfinished ...>
[0x56557249] strlen("aaaaaaaa")                                                                                      = 8
[0x565572ca] puts("\n[+] No flag for you. [+]"
[+] No flag for you. [+]
)                                                                      = 26
[0xffffffffffffffff] +++ exited (status 26) +++

6、反编译 ".text"部分
让我们看一下.text部分的反汇编并尝试理解

# objdump -D -M intel -j .text nix_5744af788e6cbdb29bb41e8b0e5f3cd5 nix_5744af788e6cbdb29bb41e8b0e5f3cd5:     file format elf32-i386Disassembly of section .text:000010b0 <_start>:10b0:    31 ed                    xor    ebp,ebp10b2:    5e                       pop    esi10b3:    89 e1                    mov    ecx,esp10b5:    83 e4 f0                 and    esp,0xfffffff010b8:    50                       push   eax10b9:    54                       push   esp10ba:    52                       push   edx10bb:    e8 22 00 00 00           call   10e2 <_start+0x32>10c0:    81 c3 40 2f 00 00        add    ebx,0x2f4010c6:    8d 83 60 d4 ff ff        lea    eax,[ebx-0x2ba0]10cc:    50                       push   eax10cd:    8d 83 00 d4 ff ff        lea    eax,[ebx-0x2c00]10d3:    50                       push   eax10d4:    51                       push   ecx10d5:    56                       push   esi10d6:    ff b3 f8 ff ff ff        push   DWORD PTR [ebx-0x8]10dc:    e8 9f ff ff ff           call   1080 <__libc_start_main@plt>10e1:    f4                       hlt    10e2:    8b 1c 24                 mov    ebx,DWORD PTR [esp]10e5:    c3                       ret    10e6:    66 90                    xchg   ax,ax10e8:    66 90                    xchg   ax,ax10ea:    66 90                    xchg   ax,ax10ec:    66 90                    xchg   ax,ax10ee:    66 90                    xchg   ax,ax... Output Omitted ...000011e9 <main>:11e9:    8d 4c 24 04              lea    ecx,[esp+0x4]11ed:    83 e4 f0                 and    esp,0xfffffff011f0:    ff 71 fc                 push   DWORD PTR [ecx-0x4]11f3:    55                       push   ebp11f4:    89 e5                    mov    ebp,esp11f6:    56                       push   esi11f7:    53                       push   ebx11f8:    51                       push   ecx11f9:    83 ec 1c                 sub    esp,0x1c11fc:    e8 ef fe ff ff           call   10f0 <__x86.get_pc_thunk.bx>1201:    81 c3 ff 2d 00 00        add    ebx,0x2dff1207:    89 ce                    mov    esi,ecx1209:    c7 45 e4 00 00 00 00     mov    DWORD PTR [ebp-0x1c],0x01210:    c7 45 dc 07 00 00 00     mov    DWORD PTR [ebp-0x24],0x71217:    83 3e 02                 cmp    DWORD PTR [esi],0x2121a:    74 1c                    je     1238 <main+0x4f>121c:    83 ec 0c                 sub    esp,0xc121f:    8d 83 08 e0 ff ff        lea    eax,[ebx-0x1ff8]1225:    50                       push   eax1226:    e8 15 fe ff ff           call   1040 <printf@plt>122b:    83 c4 10                 add    esp,0x10122e:    83 ec 0c                 sub    esp,0xc1231:    6a 01                    push   0x11233:    e8 28 fe ff ff           call   1060 <exit@plt>1238:    8b 46 04                 mov    eax,DWORD PTR [esi+0x4]123b:    83 c0 04                 add    eax,0x4123e:    8b 00                    mov    eax,DWORD PTR [eax]1240:    83 ec 0c                 sub    esp,0xc1243:    50                       push   eax1244:    e8 27 fe ff ff           call   1070 <strlen@plt>1249:    83 c4 10                 add    esp,0x10124c:    83 f8 0f                 cmp    eax,0xf124f:    76 1c                    jbe    126d <main+0x84>1251:    83 ec 0c                 sub    esp,0xc1254:    8d 83 20 e0 ff ff        lea    eax,[ebx-0x1fe0]125a:    50                       push   eax125b:    e8 f0 fd ff ff           call   1050 <puts@plt>1260:    83 c4 10                 add    esp,0x101263:    83 ec 0c                 sub    esp,0xc1266:    6a 01                    push   0x11268:    e8 f3 fd ff ff           call   1060 <exit@plt>126d:    c7 45 e0 00 00 00 00     mov    DWORD PTR [ebp-0x20],0x01274:    eb 1a                    jmp    1290 <main+0xa7>1276:    8b 46 04                 mov    eax,DWORD PTR [esi+0x4]1279:    83 c0 04                 add    eax,0x4127c:    8b 10                    mov    edx,DWORD PTR [eax]127e:    8b 45 e0                 mov    eax,DWORD PTR [ebp-0x20]1281:    01 d0                    add    eax,edx1283:    0f b6 00                 movzx  eax,BYTE PTR [eax]1286:    0f be c0                 movsx  eax,al1289:    01 45 e4                 add    DWORD PTR [ebp-0x1c],eax128c:    83 45 e0 01              add    DWORD PTR [ebp-0x20],0x11290:    8b 45 e0                 mov    eax,DWORD PTR [ebp-0x20]1293:    3b 45 dc                 cmp    eax,DWORD PTR [ebp-0x24]1296:    7c de                    jl     1276 <main+0x8d>1298:    81 7d e4 21 03 00 00     cmp    DWORD PTR [ebp-0x1c],0x321129f:    75 1a                    jne    12bb <main+0xd2>12a1:    e8 33 00 00 00           call   12d9 <comp_key>12a6:    83 ec 08                 sub    esp,0x812a9:    50                       push   eax12aa:    8d 83 3c e0 ff ff        lea    eax,[ebx-0x1fc4]12b0:    50                       push   eax12b1:    e8 8a fd ff ff           call   1040 <printf@plt>12b6:    83 c4 10                 add    esp,0x1012b9:    eb 12                    jmp    12cd <main+0xe4>12bb:    83 ec 0c                 sub    esp,0xc12be:    8d 83 5e e0 ff ff        lea    eax,[ebx-0x1fa2]12c4:    50                       push   eax12c5:    e8 86 fd ff ff           call   1050 <puts@plt>12ca:    83 c4 10                 add    esp,0x1012cd:    90                       nop12ce:    8d 65 f4                 lea    esp,[ebp-0xc]12d1:    59                       pop    ecx12d2:    5b                       pop    ebx12d3:    5e                       pop    esi12d4:    5d                       pop    ebp12d5:    8d 61 fc                 lea    esp,[ecx-0x4]12d8:    c3                       ret    000012d9 <comp_key>:12d9:    55                       push   ebp12da:    89 e5                    mov    ebp,esp12dc:    57                       push   edi12dd:    56                       push   esi12de:    53                       push   ebx12df:    83 ec 7c                 sub    esp,0x7c12e2:    e8 09 fe ff ff           call   10f0 <__x86.get_pc_thunk.bx>12e7:    81 c3 19 2d 00 00        add    ebx,0x2d1912ed:    c7 45 e4 00 00 00 00     mov    DWORD PTR [ebp-0x1c],0x012f4:    c7 45 a8 4c 00 00 00     mov    DWORD PTR [ebp-0x58],0x4c12fb:    c7 45 ac 33 00 00 00     mov    DWORD PTR [ebp-0x54],0x331302:    c7 45 b0 74 00 00 00     mov    DWORD PTR [ebp-0x50],0x741309:    c7 45 b4 73 00 00 00     mov    DWORD PTR [ebp-0x4c],0x731310:    c7 45 b8 5f 00 00 00     mov    DWORD PTR [ebp-0x48],0x5f1317:    c7 45 bc 67 00 00 00     mov    DWORD PTR [ebp-0x44],0x67131e:    c7 45 c0 33 00 00 00     mov    DWORD PTR [ebp-0x40],0x331325:    c7 45 c4 74 00 00 00     mov    DWORD PTR [ebp-0x3c],0x74132c:    c7 45 c8 5f 00 00 00     mov    DWORD PTR [ebp-0x38],0x5f1333:    c7 45 cc 69 00 00 00     mov    DWORD PTR [ebp-0x34],0x69133a:    c7 45 d0 6e 00 00 00     mov    DWORD PTR [ebp-0x30],0x6e1341:    c7 45 d4 32 00 00 00     mov    DWORD PTR [ebp-0x2c],0x321348:    c7 45 d8 5f 00 00 00     mov    DWORD PTR [ebp-0x28],0x5f134f:    c7 45 dc 52 00 00 00     mov    DWORD PTR [ebp-0x24],0x521356:    c7 45 e0 33 00 00 00     mov    DWORD PTR [ebp-0x20],0x33135d:    8b 55 e0                 mov    edx,DWORD PTR [ebp-0x20]1360:    8b 75 dc                 mov    esi,DWORD PTR [ebp-0x24]1363:    8b 45 d8                 mov    eax,DWORD PTR [ebp-0x28]1366:    89 45 a4                 mov    DWORD PTR [ebp-0x5c],eax1369:    8b 4d d4                 mov    ecx,DWORD PTR [ebp-0x2c]136c:    89 4d a0                 mov    DWORD PTR [ebp-0x60],ecx136f:    8b 7d d0                 mov    edi,DWORD PTR [ebp-0x30]1372:    89 7d 9c                 mov    DWORD PTR [ebp-0x64],edi1375:    8b 45 cc                 mov    eax,DWORD PTR [ebp-0x34]1378:    89 45 98                 mov    DWORD PTR [ebp-0x68],eax137b:    8b 4d c8                 mov    ecx,DWORD PTR [ebp-0x38]137e:    89 4d 94                 mov    DWORD PTR [ebp-0x6c],ecx1381:    8b 7d c4                 mov    edi,DWORD PTR [ebp-0x3c]1384:    89 7d 90                 mov    DWORD PTR [ebp-0x70],edi1387:    8b 45 c0                 mov    eax,DWORD PTR [ebp-0x40]138a:    89 45 8c                 mov    DWORD PTR [ebp-0x74],eax138d:    8b 4d bc                 mov    ecx,DWORD PTR [ebp-0x44]1390:    89 4d 88                 mov    DWORD PTR [ebp-0x78],ecx1393:    8b 7d b8                 mov    edi,DWORD PTR [ebp-0x48]1396:    89 7d 84                 mov    DWORD PTR [ebp-0x7c],edi1399:    8b 45 b4                 mov    eax,DWORD PTR [ebp-0x4c]139c:    89 45 80                 mov    DWORD PTR [ebp-0x80],eax139f:    8b 7d b0                 mov    edi,DWORD PTR [ebp-0x50]13a2:    8b 4d ac                 mov    ecx,DWORD PTR [ebp-0x54]13a5:    8b 45 a8                 mov    eax,DWORD PTR [ebp-0x58]13a8:    83 ec 0c                 sub    esp,0xc13ab:    52                       push   edx13ac:    56                       push   esi13ad:    ff 75 a4                 push   DWORD PTR [ebp-0x5c]13b0:    ff 75 a0                 push   DWORD PTR [ebp-0x60]13b3:    ff 75 9c                 push   DWORD PTR [ebp-0x64]13b6:    ff 75 98                 push   DWORD PTR [ebp-0x68]13b9:    ff 75 94                 push   DWORD PTR [ebp-0x6c]13bc:    ff 75 90                 push   DWORD PTR [ebp-0x70]13bf:    ff 75 8c                 push   DWORD PTR [ebp-0x74]13c2:    ff 75 88                 push   DWORD PTR [ebp-0x78]13c5:    ff 75 84                 push   DWORD PTR [ebp-0x7c]13c8:    ff 75 80                 push   DWORD PTR [ebp-0x80]13cb:    57                       push   edi13cc:    51                       push   ecx13cd:    50                       push   eax13ce:    8d 83 78 e0 ff ff        lea    eax,[ebx-0x1f88]13d4:    50                       push   eax13d5:    8d 83 30 00 00 00        lea    eax,[ebx+0x30]13db:    50                       push   eax13dc:    e8 af fc ff ff           call   1090 <sprintf@plt>13e1:    83 c4 50                 add    esp,0x5013e4:    8d 83 30 00 00 00        lea    eax,[ebx+0x30]13ea:    8d 65 f4                 lea    esp,[ebp-0xc]13ed:    5b                       pop    ebx13ee:    5e                       pop    esi13ef:    5f                       pop    edi13f0:    5d                       pop    ebp13f1:    c3                       ret    13f2:    66 90                    xchg   ax,ax13f4:    66 90                    xchg   ax,ax13f6:    66 90                    xchg   ax,ax13f8:    66 90                    xchg   ax,ax13fa:    66 90                    xchg   ax,ax13fc:    66 90                    xchg   ax,ax13fe:    66 90                    xchg   ax,ax... Output Omitted ...

在这个二进制文件中,符号没有被剥离,因此我们可以看到函数名称,这使得它更容易理解。
如果你可以阅读汇编代码,你可以很清楚的知道发生了什么。
如果不能阅读汇编代码,让我们做一些实时调试,并尝试更好地理解。
7、实时调试
这里我们使用GDB-Peda进行实时调试
我们首先检查二进制文件中的函数。我们可以看到main,comp_key等函数

gdb-peda$ info functions
All defined functions:Non-debugging symbols:
0x00001000  _init
0x00001040  printf@plt
0x00001050  puts@plt
0x00001060  exit@plt
0x00001070  strlen@plt
0x00001080  __libc_start_main@plt
0x00001090  sprintf@plt
0x000010a0  __cxa_finalize@plt
0x000010a8  __gmon_start__@plt
0x000010b0  _start
0x000010f0  __x86.get_pc_thunk.bx
0x00001100  deregister_tm_clones
0x00001140  register_tm_clones
0x00001190  __do_global_dtors_aux
0x000011e0  frame_dummy
0x000011e5  __x86.get_pc_thunk.dx
0x000011e9  main
0x000012d9  comp_key
0x00001400  __libc_csu_init
0x00001460  __libc_csu_fini
0x00001464  _fini

调试方法:首先使用 break main 跳到主函数,使用n来step和ni来执行每条指令

gdb-peda$ break main
Breakpoint 1 at 0x11f9
gdb-peda$ run aaaaaaaa
Starting program: /mnt/hgfs/shared/Linux RE/nix_5744af788e6cbdb29bb41e8b0e5f3cd5 aaaaaaaa[----------------------------------registers-----------------------------------]
EAX: 0xf7f95dd8 --> 0xffffd2f0 --> 0xffffd4d1 ("NVM_DIR=/root/.nvm")
EBX: 0x0
ECX: 0xffffd250 --> 0x2
EDX: 0xffffd274 --> 0x0
ESI: 0xf7f94000 --> 0x1d5d8c
EDI: 0x0
EBP: 0xffffd238 --> 0x0
ESP: 0xffffd22c --> 0xffffd250 --> 0x2
EIP: 0x565561f9 (<main+16>:    sub    esp,0x1c)
EFLAGS: 0x282 (carry parity adjust zero SIGN trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]0x565561f6 <main+13>:    push   esi0x565561f7 <main+14>:    push   ebx0x565561f8 <main+15>:    push   ecx
=> 0x565561f9 <main+16>:    sub    esp,0x1c0x565561fc <main+19>:    call   0x565560f0 <__x86.get_pc_thunk.bx>0x56556201 <main+24>:    add    ebx,0x2dff0x56556207 <main+30>:    mov    esi,ecx0x56556209 <main+32>:    mov    DWORD PTR [ebp-0x1c],0x0
[------------------------------------stack-------------------------------------]
0000| 0xffffd22c --> 0xffffd250 --> 0x2
0004| 0xffffd230 --> 0x0
0008| 0xffffd234 --> 0xf7f94000 --> 0x1d5d8c
0012| 0xffffd238 --> 0x0
0016| 0xffffd23c --> 0xf7dd79a1 (<__libc_start_main+241>:    add    esp,0x10)
0020| 0xffffd240 --> 0xf7f94000 --> 0x1d5d8c
0024| 0xffffd244 --> 0xf7f94000 --> 0x1d5d8c
0028| 0xffffd248 --> 0x0
[------------------------------------------------------------------------------]
Legend: code, data, rodata, valueBreakpoint 1, 0x565561f9 in main ()
1: main = {<text variable, no debug info>} 0x565561e9 <main>
2: puts = {<text variable, no debug info>} 0xf7e25e40 <puts>
gdb-peda$ 

让我们来看看程序的逻辑,程序首先尝试比较参数的数量。它存储在ecx寄存器中并移动到esi,它用于将值与0x2进行比较

 0x56556207 <+30>:    mov    esi,ecx0x56556209 <+32>:    mov    DWORD PTR [ebp-0x1c],0x00x56556210 <+39>:    mov    DWORD PTR [ebp-0x24],0x70x56556217 <+46>:    cmp    DWORD PTR [esi],0x20x5655621a <+49>:    je     0x56556238 <main+79>0x5655621c <+51>:    sub    esp,0xc0x5655621f <+54>:    lea    eax,[ebx-0x1ff8]0x56556225 <+60>:    push   eax0x56556226 <+61>:    call   0x56556040 <printf@plt>0x5655622b <+66>:    add    esp,0x100x5655622e <+69>:    sub    esp,0xc0x56556231 <+72>:    push   0x10x56556233 <+74>:    call   0x56556060 <exit@plt>

其伪代码看起来是这样的:

if(argc != 2) {printf("Usage: script.exe <key>");exit(1);
}
   0x56556238 <+79>:    mov    eax,DWORD PTR [esi+0x4]0x5655623b <+82>:    add    eax,0x40x5655623e <+85>:    mov    eax,DWORD PTR [eax]0x56556240 <+87>:    sub    esp,0xc0x56556243 <+90>:    push   eax0x56556244 <+91>:    call   0x56556070 <strlen@plt>0x56556249 <+96>:    add    esp,0x100x5655624c <+99>:    cmp    eax,0xf0x5655624f <+102>:    jbe    0x5655626d <main+132>0x56556251 <+104>:    sub    esp,0xc0x56556254 <+107>:    lea    eax,[ebx-0x1fe0]0x5655625a <+113>:    push   eax0x5655625b <+114>:    call   0x56556050 <puts@plt>0x56556260 <+119>:    add    esp,0x100x56556263 <+122>:    sub    esp,0xc0x56556266 <+125>:    push   0x10x56556268 <+127>:    call   0x56556060 <exit@plt>

其代码是这样的:

if(strlen(argv[1]) > 15) {puts("Length of argv[1] too long.");exit(1);
}

如果你检查这个代码,可以看到有一个循环正在迭代我们输入字符串的每个字符。

   0x5655626d <+132>:    mov    DWORD PTR [ebp-0x20],0x00x56556274 <+139>:    jmp    0x56556290 <main+167>0x56556276 <+141>:    mov    eax,DWORD PTR [esi+0x4]0x56556279 <+144>:    add    eax,0x40x5655627c <+147>:    mov    edx,DWORD PTR [eax]0x5655627e <+149>:    mov    eax,DWORD PTR [ebp-0x20]0x56556281 <+152>:    add    eax,edx0x56556283 <+154>:    movzx  eax,BYTE PTR [eax]0x56556286 <+157>:    movsx  eax,al0x56556289 <+160>:    add    DWORD PTR [ebp-0x1c],eax0x5655628c <+163>:    add    DWORD PTR [ebp-0x20],0x10x56556290 <+167>:    mov    eax,DWORD PTR [ebp-0x20]0x56556293 <+170>:    cmp    eax,DWORD PTR [ebp-0x24]0x56556296 <+173>:    jl     0x56556276 <main+141>0x56556298 <+175>:    cmp    DWORD PTR [ebp-0x1c],0x3210x5655629f <+182>:    jne    0x565562bb <main+210>0x565562a1 <+184>:    call   0x565562d9 <comp_key>0x565562a6 <+189>:    sub    esp,0x80x565562a9 <+192>:    push   eax0x565562aa <+193>:    lea    eax,[ebx-0x1fc4]0x565562b0 <+199>:    push   eax0x565562b1 <+200>:    call   0x56556040 <printf@plt>0x565562b6 <+205>:    add    esp,0x100x565562b9 <+208>:    jmp    0x565562cd <main+228>0x565562bb <+210>:    sub    esp,0xc0x565562be <+213>:    lea    eax,[ebx-0x1fa2]0x565562c4 <+219>:    push   eax0x565562c5 <+220>:    call   0x56556050 <puts@plt>0x565562ca <+225>:    add    esp,0x100x565562cd <+228>:    nop0x565562ce <+229>:    lea    esp,[ebp-0xc]0x565562d1 <+232>:    pop    ecx0x565562d2 <+233>:    pop    ebx0x565562d3 <+234>:    pop    esi0x565562d4 <+235>:    pop    ebp0x565562d5 <+236>:    lea    esp,[ecx-0x4]0x565562d8 <+239>:    ret    

它到底循环了多少个字符?通常来说,我们的密码长度为7个字符。

[----------------------------------registers-----------------------------------]
EAX: 0x6
EBX: 0x56559000 --> 0x3efc
ECX: 0x6
EDX: 0xffffd4c6 ("1234567890")
ESI: 0xffffd250 --> 0x2
EDI: 0x0
EBP: 0xffffd238 --> 0x0
ESP: 0xffffd210 --> 0xf7f943fc --> 0xf7f95200 --> 0x0
EIP: 0x56556293 (<main+170>:    cmp    eax,DWORD PTR [ebp-0x24])
EFLAGS: 0x206 (carry PARITY adjust zero sign trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]0x56556289 <main+160>:    add    DWORD PTR [ebp-0x1c],eax0x5655628c <main+163>:    add    DWORD PTR [ebp-0x20],0x10x56556290 <main+167>:    mov    eax,DWORD PTR [ebp-0x20]
=> 0x56556293 <main+170>:    cmp    eax,DWORD PTR [ebp-0x24]0x56556296 <main+173>:    jl     0x56556276 <main+141>0x56556298 <main+175>:    cmp    DWORD PTR [ebp-0x1c],0x3210x5655629f <main+182>:    jne    0x565562bb <main+210>0x565562a1 <main+184>:    call   0x565562d9 <comp_key>
[------------------------------------stack-------------------------------------]
0000| 0xffffd210 --> 0xf7f943fc --> 0xf7f95200 --> 0x0
0004| 0xffffd214 --> 0x7
0008| 0xffffd218 --> 0x6
0012| 0xffffd21c --> 0x135
0016| 0xffffd220 --> 0x2
0020| 0xffffd224 --> 0xffffd2e4 --> 0xffffd487 ("/mnt/hgfs/shared/Linux RE/nix_5744af788e6cbdb29bb41e8b0e5f3cd5")
0024| 0xffffd228 --> 0xffffd2f0 --> 0xffffd4d1 ("NVM_DIR=/root/.nvm")
0028| 0xffffd22c --> 0xffffd250 --> 0x2
[------------------------------------------------------------------------------]
Legend: code, data, rodata, value
0x56556293 in main ()
gdb-peda$ print $ebp-0x24
$24 = (void *) 0xffffd214
gdb-peda$ x/x 0xffffd214
0xffffd214:    0x00000007

代码看起来是这样的:

for (i = 0; i < 7; i++) value += argv[1][i];
if (value != 801) return puts("\n[+] No flag for you. [+]");
return printf("[+] The flag is: SAYCURE{%s} [+]\n", comp_key());

可以看出,如果7个字符总和等于801,即可得到flag。您可以使用任何字符,只要总和是801即可。检查完成后,调用comp_key函数并打印出flag。
比如这样:
114 * 6 + 177 = 801
我们找到数字对应的ASCII字符
114是 ‘r’ 117 是 ‘u’。

  Dec Hex    Dec Hex    Dec Hex  Dec Hex  Dec Hex  Dec Hex   Dec Hex   Dec Hex  0 00 NUL  16 10 DLE  32 20    48 30 0  64 40 @  80 50 P   96 60 `  112 70 p1 01 SOH  17 11 DC1  33 21 !  49 31 1  65 41 A  81 51 Q   97 61 a  113 71 q2 02 STX  18 12 DC2  34 22 "  50 32 2  66 42 B  82 52 R   98 62 b  114 72 r3 03 ETX  19 13 DC3  35 23 #  51 33 3  67 43 C  83 53 S   99 63 c  115 73 s4 04 EOT  20 14 DC4  36 24 $  52 34 4  68 44 D  84 54 T  100 64 d  116 74 t5 05 ENQ  21 15 NAK  37 25 %  53 35 5  69 45 E  85 55 U  101 65 e  117 75 u6 06 ACK  22 16 SYN  38 26 &  54 36 6  70 46 F  86 56 V  102 66 f  118 76 v7 07 BEL  23 17 ETB  39 27 '  55 37 7  71 47 G  87 57 W  103 67 g  119 77 w8 08 BS   24 18 CAN  40 28 (  56 38 8  72 48 H  88 58 X  104 68 h  120 78 x9 09 HT   25 19 EM   41 29 )  57 39 9  73 49 I  89 59 Y  105 69 i  121 79 y10 0A LF   26 1A SUB  42 2A *  58 3A :  74 4A J  90 5A Z  106 6A j  122 7A z11 0B VT   27 1B ESC  43 2B +  59 3B ;  75 4B K  91 5B [  107 6B k  123 7B {12 0C FF   28 1C FS   44 2C ,  60 3C <  76 4C L  92 5C \  108 6C l  124 7C |13 0D CR   29 1D GS   45 2D -  61 3D =  77 4D M  93 5D ]  109 6D m  125 7D }14 0E SO   30 1E RS   46 2E .  62 3E >  78 4E N  94 5E ^  110 6E n  126 7E ~15 0F SI   31 1F US   47 2F /  63 3F ?  79 4F O  95 5F _  111 6F o  127 7F DEL

然后我们将字符作为输入,执行程序即可得到FLAG

# ./nix_5744af788e6cbdb29bb41e8b0e5f3cd5 rrrrrru
[+] The flag is: SAYCURE{L3ts_g3t_in2_R3} [+]

7、说明

本文由合天网安实验室编译,转载请注明来源
原文:

Linux Reverse Engineering CTFs for Beginners | ?Blog of Osanda https://osandamalith.com/2019...

参考:

ELF文件结构描述 - yooooooo - 博客园 https://www.cnblogs.com/linha...

关于合天网安实验室

合天网安实验室(www.hetianlab.com)-领先的实践型网络安全在线教育平台

真实环境,在线实操学网络安全 ;

实验内容涵盖:系统安全,软件安全,网络安全,Web安全,移动安全,CTF,取证分析,渗透测试,网安意识教育等。

Linux CTF 逆向入门相关推荐

  1. 【CTF资料-0x0002】PWN简易Linux堆利用入门教程by arttnba3

    [CTF资料-0x0002]简易Linux堆利用入门教程by arttnba3 老生常谈,[GITHUB BLOG ADDR](https://arttnba3.cn/2021/05/10/NOTE- ...

  2. CTF逆向-IDA Pro攻防世界Hello CTF

    文章目录 前言 Hello CTF IDA反汇编 Flag值计算 总结 前言 交互式反汇编器专业版(Interactive Disassembler Professional),人们常称其为 IDA ...

  3. 逆向入门学习路线及渠道

    关于逆向入门. 一.语言 1.汇编. 分Inte|和AT&T两种语法.win平台主要是Intel ,Linux反汇编默认是AT&T ,目前CTF里Intel汇编占据主导地位.入门推荐王 ...

  4. CTF从入门到提升之宽字节注入

    CTF入门到放弃 为什么说是从入门到放弃呢?(开个玩笑)如果说大家对CTF有了解的话,其实应该知道CTF是一个什么类型的比赛,这个比赛涉及的范围和影响有多大.如果说你真的想打好比赛,那也是真的非常不容 ...

  5. 010 Android之逆向入门

    文章目录 Android APK文件结构 META-INF res AndroidManifest.xml classes.dex resources.arsc lib Assets Android ...

  6. linux内核二当家,Linux PWN从入门到熟练(二)

    前言 上回说到,如何利用程序中system函数以及bin/sh字符串来进行pwn.这里我们会介绍,如何在栈可执行而system函数以及参数没有的情况下,如何自己布置payload进行pwn.此外,还提 ...

  7. 攻防世界逆向入门题之no-strings-attached

    攻防世界逆向入门题之no-strings-attached 继续开启全栈梦想之逆向之旅~ 这题是攻防世界逆向入门题的no-strings-attached 下载附件: 扔入Exepeinfo中查壳和其 ...

  8. [免费专栏] Android安全之APK逆向入门介绍

    也许每个人出生的时候都以为这世界都是为他一个人而存在的,当他发现自己错的时候,他便开始长大 少走了弯路,也就错过了风景,无论如何,感谢经历 Android安全付费专栏长期更新,本篇最新内容请前往: [ ...

  9. SO逆向入门实战教程一:OASIS

    文章目录 一.前言 二.准备 三.Unidbg模拟执行 四.ExAndroidNativeEmu 模拟执行 五.算法分析 六.尾声 一.前言 这是SO逆向入门实战教程的第一篇,总共会有十三篇,十三个实 ...

最新文章

  1. if for switch语句
  2. python 时间time()及日期date()函数
  3. 好惨!程序员修电脑遇到了人生滑铁卢 | 每日趣闻
  4. 无限容量数据库架构设计
  5. shell实例第0讲:shell脚本完整pdf文档下载
  6. 在linux中安装rpm包
  7. 广告banner:手动滑动切换,自动切换,点击跳转,异步加载网络图片
  8. PHP第三天!!黑人无表情 面向对象的特点等等!!
  9. 关于JAVA是值传递还是引用传递的问题
  10. linux瘦身软件下载,瘦身相机APP全新安卓版下载-瘦身相机APPV1.3.0-Linux公社
  11. JavaScript编码风格指南(中文版)
  12. aso优化时高权重的积分墙关键词_怎样做好积分墙关键词的优化
  13. win10定时语音提醒
  14. 嵌入式系统的开发概述(三星s5p6818系统为例)
  15. SLF4J: Class path contains multiple SLF4J bindings(log4j与logback冲突了)
  16. abaqus .cae文件默认程序设置
  17. SSAO与HBAO学习笔记(持续改进)
  18. 基于32单片机的智能插座
  19. 【Spring Boot JPA】ManyToOne OneToMany学习笔记
  20. 普通本科菜菜海淘无人搭理,苦心闭关修炼一个月,出关后成功拿下阿里,蚂蚁金服,美团三个大厂意向书

热门文章

  1. 海明距离mysql查询_海量数据,海明距离高效检索(smlar) - 阿里云RDS PosgreSQL最佳实践-阿里云开发者社区...
  2. 名字英文name域名_企业建站必读:六大方法锁定最佳企业域名
  3. 洛谷 题解 P1135 【奇怪的电梯】
  4. 大量删除的表、查询卡顿的表,重建索引
  5. struts2.1笔记02:servlet简介
  6. vim快捷键使用记录
  7. 为什么python不需要编译_为什么我用Go写机器学习部署平台,而偏偏不用Python?...
  8. 事件循环机制 + ES7:Async/Await(基于generator原理实现)附详细示例分析
  9. Java中大数值是什么?数值想存多大存多大
  10. 矩阵论思维导图_全新思维导图