1. The ELF format

Let’s look at the ELF header first, and if you want to know more about it, you can look at the ELF man Page documentation.

+-------------------+ | 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 |<--------+ +-------------------+Copy the code

2. Executable Headers

The first part of an ELF file is the Executable Header, which contains information about the type of ELF file. ELF files are common on all platforms. There are 32-bit and 64-bit versions of ELF files. The header content is the same, but some members have different sizes. It also comes in two versions: “Elf32_Ehdr” and “Elf64_Ehdr”. The following uses the 32-bit version as an example:

#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;
Copy the code

Use readelf to analyze the ELF file format

# readelf -h /bin/ls
ELF Header:
  Magic:   7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00 
  Class:                             ELF64
  Data:                              2 's complement, little endian
  Version:                           1 (current)
  OS/ABI:                            UNIX - System V
  ABI Version:                       0
  Type:                              DYN (Shared object file)
  Machine:                           Advanced Micro Devices X86- 64.
  Version:                           0x1
  Entry point address:               0x6130
  Start of program headers:          64 (bytes into file)
  Start of section headers:          137000 (bytes into file)
  Flags:                             0x0
  Size of this header:               64 (bytes)
  Size of program headers:           56 (bytes)
  Number of program headers:         11
  Size of section headers:           64 (bytes)
  Number of section headers:         29
  Section header string table index: 28
Copy the code

We can calculate the size of the entire binary using the following calculation:

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
Copy the code

3. Program Headers

The header describes the segments in the file and tells the system how to create the process image.

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;
Copy the code

4. Section Headers

Section Headers contain information that describes the Section area of the file, such as size, offset, and so on, but are not important to the binary execution process.

  • Sections or segments: Segments describe the ELF file from the runtime point of view, sections describe the ELF file from the link point of view. In other words, during the link stage, we can ignore the Program header table to process the file. The section header table can be ignored at runtime to process this program (so many hardening methods remove the Section header table). From the figure, we can also see that segments are contained in the relationship between segments. A segment contains several sections.

typedef struct {
  Elf32_Word    sh_name;                /* Name of section (string TBL index) */
  Elf32_Word    sh_type;                / * * / section category
  Elf32_Word    sh_flags;               /* Features that the section performs in the process (read, write) */
  Elf32_Addr    sh_addr;                /* The virtual address in memory */
  Elf32_Off     sh_offset;              /* The offset of this section in the file */
  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;
Copy the code

5. Table (Section)

5.1 .bss Section

Save uninitialized data, such as global variables that are not initialized.

5.2. The data Section

Save the initialized data.

5.3 .rodata Section

Save read-only data in the program.

5.4. The text Section

This section contains the actual code, logical flow of the program. Use readelf to view the ELF file table structure

# 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)
Copy the code

Complete the simple CTF challenge

Now that you know something about ELF files, let’s try a CTF problem.

The binary file can be downloaded from ufile. IO/BLVPM

Domestic download: www.lanzous.com/i34qg6f

1. Run this program and pass some random characters to it. The result is as follows:

# ./nix_5744af788e6cbdb29bb41e8b0e5f3cd5 aaaa

[+] No flag for you. [+]
Copy the code

2. Then use strings to look at the program’s strings and see if you can find any useful information

# 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.cCopy the code

We can see that “%c” is the string that prints the flag, and the number is 15.

3. We can look at the offsets in the “.rodata “section to get a better view of these characters

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

4, check the Symbols table (Symbols) nm command to view the Symbols in the library file

# nm -D nix_5744af788e6cbdb29bb41e8b0e5f3cd5 
         w __cxa_finalize
         U exitw __gmon_start__ 00002004 R _IO_stdin_used w _ITM_deregisterTMCloneTable w _ITM_registerTMCloneTable U __libc_start_main  Uprintf
         U puts
         U sprintf
         U strlen
Copy the code

Note: -d or -dynamic: displays dynamic symbols. This option is only meaningful for dynamic targets (such as shared libraries of a particular type). We can find printf, puts, sprintf, strlen functions. These functions are undefined. We can use tools like Strace to track System Calls

# 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 with26 +++
Copy the code

To better understand, we can use ltrace to decode C++ to track library calls made by function names. We can see that the string length check is in progress.

# 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) + + +Copy the code

Let’s take a look at the disassembly of the.text section and try to understand

# objdump -D -M intel -j .text nix_5744af788e6cbdb29bb41e8b0e5f3cd5 

nix_5744af788e6cbdb29bb41e8b0e5f3cd5:     file format elf32-i386


Disassembly of section .text:

000010b0 <_start>:
    10bZero:31 ed                	xor    ebp,ebp
    10b2:	5e                   	pop    esi
    10b3:	89 e1                	mov    ecx,esp
    10b5:	83 e4 f0             	and    esp,0xfffffff0
    10b8:	50                   	push   eax
    10b9:	54                   	push   esp
    10ba:	52                   	push   edx
    10bb:	e8 22 00 00 00       	call   10e2 <_start+0x32>
    10c0:	81 c3 40 2f 00 00    	add    ebx,0x2f40
    10c6:	8d 83 60 d4 ff ff    	lea    eax,[ebx-0x2ba0]
    10cc:	50                   	push   eax
    10cd:	8d 83 00 d4 ff ff    	lea    eax,[ebx-0x2c00]
    10d3:	50                   	push   eax
    10d4:	51                   	push   ecx
    10d5:	56                   	push   esi
    10d6:	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,ax
    10e8:	66 90                	xchg   ax,ax
    10ea:	66 90                	xchg   ax,ax
    10ec:	66 90                	xchg   ax,ax
    10ee:	66 90                	xchg   ax,ax

... Output Omitted ...

000011e9 <main>:
    11e9:	8d 4c 24 04          	lea    ecx,[esp+0x4]
    11ed:	83 e4 f0             	and    esp,0xfffffff0
    11f0:	ff 71 fc             	push   DWORD PTR [ecx-0x4]
    11f3:	55                   	push   ebp
    11f4:	89 e5                	mov    ebp,esp
    11f6:	56                   	push   esi
    11f7:	53                   	push   ebx
    11f8:	51                   	push   ecx
    11f9:	83 ec 1c             	sub    esp,0x1c
    11fc:	e8 ef fe ff ff       	call   10f0 <__x86.get_pc_thunk.bx>
    1201:	81 c3 ff 2d 00 00    	add    ebx,0x2dff
    1207:	89 ce                	mov    esi,ecx
    1209:	c7 45 e4 00 00 00 00 	mov    DWORD PTR [ebp-0x1c].0x0
    1210:	c7 45 dc 07 00 00 00 	mov    DWORD PTR [ebp-0x24].0x7
    1217:	83 3e 02             	cmp    DWORD PTR [esi],0x2
    121a:	74 1c                	je     1238 <main+0x4f>
    121c:	83 ec 0c             	sub    esp,0xc
    121f:	8d 83 08 e0 ff ff    	lea    eax,[ebx-0x1ff8]
    1225:	50                   	push   eax
    1226:	e8 15 fe ff ff       	call   1040 <printf@plt>
    122b:	83 c4 10             	add    esp,0x10
    122e:	83 ec 0c             	sub    esp,0xc
    1231:	6a 01                	push   0x1
    1233:	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,0x4
    123e:	8b 00                	mov    eax,DWORD PTR [eax]
    1240:	83 ec 0c             	sub    esp,0xc
    1243:	50                   	push   eax
    1244:	e8 27 fe ff ff       	call   1070 <strlen@plt>
    1249:	83 c4 10             	add    esp,0x10
    124c:	83 f8 0f             	cmp    eax,0xf
    124f:	76 1c                	jbe    126d <main+0x84>
    1251:	83 ec 0c             	sub    esp,0xc
    1254:	8d 83 20 e0 ff ff    	lea    eax,[ebx-0x1fe0]
    125a:	50                   	push   eax
    125b:	e8 f0 fd ff ff       	call   1050 <puts@plt>
    1260:	83 c4 10             	add    esp,0x10
    1263:	83 ec 0c             	sub    esp,0xc
    1266:	6a 01                	push   0x1
    1268:	e8 f3 fd ff ff       	call   1060 <exit@plt>
    126d:	c7 45 e0 00 00 00 00 	mov    DWORD PTR [ebp-0x20].0x0
    1274:	eb 1a                	jmp    1290 <main+0xa7>
    1276:	8b 46 04             	mov    eax,DWORD PTR [esi+0x4]
    1279:	83 c0 04             	add    eax,0x4
    127c:	8b 10                	mov    edx,DWORD PTR [eax]
    127e:	8b 45 e0             	mov    eax,DWORD PTR [ebp-0x20]
    1281:	01 d0                	add    eax,edx
    1283:	0f b6 00             	movzx  eax,BYTE PTR [eax]
    1286:	0f be c0             	movsx  eax,al
    1289:	01 45 e4             	add    DWORD PTR [ebp-0x1c],eax
    128c:	83 45 e0 01          	add    DWORD PTR [ebp-0x20].0x1
    1290:	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].0x321
    129f:	75 1a                	jne    12bb <main+0xd2>
    12a1:	e8 33 00 00 00       	call   12d9 <comp_key>
    12a6:	83 ec 08             	sub    esp,0x8
    12a9:	50                   	push   eax
    12aa:	8d 83 3c e0 ff ff    	lea    eax,[ebx-0x1fc4]
    12bZero:50                   	push   eax
    12b1:	e8 8a fd ff ff       	call   1040 <printf@plt>
    12b6:	83 c4 10             	add    esp,0x10
    12b9:	eb 12                	jmp    12cd <main+0xe4>
    12bb:	83 ec 0c             	sub    esp,0xc
    12be:	8d 83 5e e0 ff ff    	lea    eax,[ebx-0x1fa2]
    12c4:	50                   	push   eax
    12c5:	e8 86 fd ff ff       	call   1050 <puts@plt>
    12ca:	83 c4 10             	add    esp,0x10
    12cd:	90                   	nop
    12ce:	8d 65 f4             	lea    esp,[ebp-0xc]
    12d1:	59                   	pop    ecx
    12d2:	5b                   	pop    ebx
    12d3:	5e                   	pop    esi
    12d4:	5d                   	pop    ebp
    12d5:	8d 61 fc             	lea    esp,[ecx-0x4]
    12d8:	c3                   	ret    

000012d9 <comp_key>:
    12d9:	55                   	push   ebp
    12da:	89 e5                	mov    ebp,esp
    12dc:	57                   	push   edi
    12dd:	56                   	push   esi
    12de:	53                   	push   ebx
    12df:	83 ec 7c             	sub    esp,0x7c
    12e2:	e8 09 fe ff ff       	call   10f0 <__x86.get_pc_thunk.bx>
    12e7:	81 c3 19 2d 00 00    	add    ebx,0x2d19
    12ed:	c7 45 e4 00 00 00 00 	mov    DWORD PTR [ebp-0x1c].0x0
    12f4:	c7 45 a8 4c 00 00 00 	mov    DWORD PTR [ebp-0x58].0x4c
    12fb:	c7 45 ac 33 00 00 00 	mov    DWORD PTR [ebp-0x54].0x33
    1302:	c7 45 b0 74 00 00 00 	mov    DWORD PTR [ebp-0x50].0x74
    1309:	c7 45 b4 73 00 00 00 	mov    DWORD PTR [ebp-0x4c].0x73
    1310:	c7 45 b8 5f 00 00 00 	mov    DWORD PTR [ebp-0x48].0x5f
    1317:	c7 45 bc 67 00 00 00 	mov    DWORD PTR [ebp-0x44].0x67
    131e:	c7 45 c0 33 00 00 00 	mov    DWORD PTR [ebp-0x40].0x33
    1325:	c7 45 c4 74 00 00 00 	mov    DWORD PTR [ebp-0x3c].0x74
    132c:	c7 45 c8 5f 00 00 00 	mov    DWORD PTR [ebp-0x38].0x5f
    1333:	c7 45 cc 69 00 00 00 	mov    DWORD PTR [ebp-0x34].0x69
    133a:	c7 45 d0 6e 00 00 00 	mov    DWORD PTR [ebp-0x30].0x6e
    1341:	c7 45 d4 32 00 00 00 	mov    DWORD PTR [ebp-0x2c].0x32
    1348:	c7 45 d8 5f 00 00 00 	mov    DWORD PTR [ebp-0x28].0x5f
    134f:	c7 45 dc 52 00 00 00 	mov    DWORD PTR [ebp-0x24].0x52
    1356:	c7 45 e0 33 00 00 00 	mov    DWORD PTR [ebp-0x20].0x33
    135d:	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],eax
    1369:	8b 4d d4             	mov    ecx,DWORD PTR [ebp-0x2c]
    136c:	89 4d a0             	mov    DWORD PTR [ebp-0x60],ecx
    136f:	8b 7d d0             	mov    edi,DWORD PTR [ebp-0x30]
    1372:	89 7d 9c             	mov    DWORD PTR [ebp-0x64],edi
    1375:	8b 45 cc             	mov    eax,DWORD PTR [ebp-0x34]
    1378:	89 45 98             	mov    DWORD PTR [ebp-0x68],eax
    137b:	8b 4d c8             	mov    ecx,DWORD PTR [ebp-0x38]
    137e:	89 4d 94             	mov    DWORD PTR [ebp-0x6c],ecx
    1381:	8b 7d c4             	mov    edi,DWORD PTR [ebp-0x3c]
    1384:	89 7d 90             	mov    DWORD PTR [ebp-0x70],edi
    1387:	8b 45 c0             	mov    eax,DWORD PTR [ebp-0x40]
    138a:	89 45 8c             	mov    DWORD PTR [ebp-0x74],eax
    138d:	8b 4d bc             	mov    ecx,DWORD PTR [ebp-0x44]
    1390:	89 4d 88             	mov    DWORD PTR [ebp-0x78],ecx
    1393:	8b 7d b8             	mov    edi,DWORD PTR [ebp-0x48]
    1396:	89 7d 84             	mov    DWORD PTR [ebp-0x7c],edi
    1399:	8b 45 b4             	mov    eax,DWORD PTR [ebp-0x4c]
    139c:	89 45 80             	mov    DWORD PTR [ebp-0x80],eax
    139f:	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,0xc
    13ab:	52                   	push   edx
    13ac:	56                   	push   esi
    13ad:	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   edi
    13cc:	51                   	push   ecx
    13cd:	50                   	push   eax
    13ce:	8d 83 78 e0 ff ff    	lea    eax,[ebx-0x1f88]
    13d4:	50                   	push   eax
    13d5:	8d 83 30 00 00 00    	lea    eax,[ebx+0x30]
    13db:	50                   	push   eax
    13dc:	e8 af fc ff ff       	call   1090 <sprintf@plt>
    13e1:	83 c4 50             	add    esp,0x50
    13e4:	8d 83 30 00 00 00    	lea    eax,[ebx+0x30]
    13ea:	8d 65 f4             	lea    esp,[ebp-0xc]
    13ed:	5b                   	pop    ebx
    13ee:	5e                   	pop    esi
    13ef:	5f                   	pop    edi
    13fZero:5d                   	pop    ebp
    13f1:	c3                   	ret    
    13f2:	66 90                	xchg   ax,ax
    13f4:	66 90                	xchg   ax,ax
    13f6:	66 90                	xchg   ax,ax
    13f8:	66 90                	xchg   ax,ax
    13fa:	66 90                	xchg   ax,ax
    13fc:	66 90                	xchg   ax,ax
    13fe:	66 90                	xchg   ax,ax

... Output Omitted ...
Copy the code

In this binary, the symbols are not stripped away, so we can see the function names, which makes it easier to understand. If you can read assembly code, you can see exactly what’s going on. If you can’t read assembly code, let’s do some real-time debugging and try to understand better. Here we use gDB-PEda for real-time debugging. We first examine the functions in the binary. We can see main, comp_key, and so on

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
Copy the code

Debug method: first use break main to jump to the main function, using n to step and ni to execute each instruction

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   esi
   0x565561f7 <main+14>:	push   ebx
   0x565561f8 <main+15>:	push   ecx
=> 0x565561f9 <main+16>:	sub    esp,0x1c
   0x565561fc <main+19>:	call   0x565560f0 <__x86.get_pc_thunk.bx>
   0x56556201 <main+24>:	add    ebx,0x2dff
   0x56556207 <main+30>:	mov    esi,ecx
   0x56556209 <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, value

Breakpoint 1.0x565561f9 in main ()
1: main = {<text variable, no debug info>} 0x565561e9 <main>
2: puts = {<text variable, no debug info>} 0xf7e25e40 <puts>
gdb-peda$ 
Copy the code

Let’s look at the logic of the program. The program first tries to compare the number of parameters. It is stored in the ECX register and moved to ESI, which is used to compare the value to 0x2

 0x56556207 <+30>:	mov    esi,ecx
 0x56556209 <+32>:	mov    DWORD PTR [ebp-0x1c],0x0
 0x56556210 <+39>:	mov    DWORD PTR [ebp-0x24],0x7
 0x56556217 <+46>:	cmp    DWORD PTR [esi],0x2
 0x5655621a <+49>:	je     0x56556238 <main+79>
 0x5655621c <+51>:	sub    esp,0xc
 0x5655621f <+54>:	lea    eax,[ebx-0x1ff8]
 0x56556225 <+60>:	push   eax
 0x56556226 <+61>:	call   0x56556040 <printf@plt>
 0x5655622b <+66>:	add    esp,0x10
 0x5655622e <+69>:	sub    esp,0xc
 0x56556231 <+72>:	push   0x1
 0x56556233 <+74>:	call   0x56556060 <exit@plt>
Copy the code

The pseudocode looks like this:

if(argc ! =2) {
   printf("Usage: script.exe <key>");
   exit(1);
}
Copy the code
   0x56556238 <+79>:	mov    eax,DWORD PTR [esi+0x4]
   0x5655623b <+82>:	add    eax,0x4
   0x5655623e <+85>:	mov    eax,DWORD PTR [eax]
   0x56556240 <+87>:	sub    esp,0xc
   0x56556243 <+90>:	push   eax
   0x56556244 <+91>:	call   0x56556070 <strlen@plt>
   0x56556249 <+96>:	add    esp,0x10
   0x5655624c <+99>:	cmp    eax,0xf
   0x5655624f <+102>:	jbe    0x5655626d <main+132>
   0x56556251 <+104>:	sub    esp,0xc
   0x56556254 <+107>:	lea    eax,[ebx-0x1fe0]
   0x5655625a <+113>:	push   eax
   0x5655625b <+114>:	call   0x56556050 <puts@plt>
   0x56556260 <+119>:	add    esp,0x10
   0x56556263 <+122>:	sub    esp,0xc
   0x56556266 <+125>:	push   0x1
   0x56556268 <+127>:	call   0x56556060 <exit@plt>
Copy the code

The code looks like this:

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

If you examine this code, you can see that there is a loop iterating over each character of the string that we entered.

   0x5655626d <+132>:	mov    DWORD PTR [ebp-0x20],0x0
   0x56556274 <+139>:	jmp    0x56556290 <main+167>
   0x56556276 <+141>:	mov    eax,DWORD PTR [esi+0x4]
   0x56556279 <+144>:	add    eax,0x4
   0x5655627c <+147>:	mov    edx,DWORD PTR [eax]
   0x5655627e <+149>:	mov    eax,DWORD PTR [ebp-0x20]
   0x56556281 <+152>:	add    eax,edx
   0x56556283 <+154>:	movzx  eax,BYTE PTR [eax]
   0x56556286 <+157>:	movsx  eax,al
   0x56556289 <+160>:	add    DWORD PTR [ebp-0x1c],eax
   0x5655628c <+163>:	add    DWORD PTR [ebp-0x20],0x1
   0x56556290 <+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],0x321
   0x5655629f <+182>:	jne    0x565562bb <main+210>
   0x565562a1 <+184>:	call   0x565562d9 <comp_key>
   0x565562a6 <+189>:	sub    esp,0x8
   0x565562a9 <+192>:	push   eax
   0x565562aa <+193>:	lea    eax,[ebx-0x1fc4]
   0x565562b0 <+199>:	push   eax
   0x565562b1 <+200>:	call   0x56556040 <printf@plt>
   0x565562b6 <+205>:	add    esp,0x10
   0x565562b9 <+208>:	jmp    0x565562cd <main+228>
   0x565562bb <+210>:	sub    esp,0xc
   0x565562be <+213>:	lea    eax,[ebx-0x1fa2]
   0x565562c4 <+219>:	push   eax
   0x565562c5 <+220>:	call   0x56556050 <puts@plt>
   0x565562ca <+225>:	add    esp,0x10
   0x565562cd <+228>:	nop
   0x565562ce <+229>:	lea    esp,[ebp-0xc]
   0x565562d1 <+232>:	pop    ecx
   0x565562d2 <+233>:	pop    ebx
   0x565562d3 <+234>:	pop    esi
   0x565562d4 <+235>:	pop    ebp
   0x565562d5 <+236>:	lea    esp,[ecx-0x4]
   0x565562d8 <+239>:	ret    
Copy the code

How many characters does it loop through? In general, our passwords are seven characters long.

[----------------------------------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],eax
   0x5655628c <main+163>:	add    DWORD PTR [ebp-0x20],0x1
   0x56556290 <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],0x321
   0x5655629f <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
Copy the code

The code looks like this:

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());
Copy the code

As you can see, if the sum of 7 characters equals 801, you get flag. You can use any character, as long as the sum is 801. When the check is complete, the comp_key function is called and the flag is printed. For example: 114 * 6 + 177 = 801 we find the numbers corresponding to the ASCII characters 114 is’ r ‘and 117 is’ 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 p 1 01 SOH 17 11 DC1 33 21 ! 49 31 1 65 41 A 81 51 Q 97 61 a 113 71 q 2 02 STX 18 12 DC2 34 22" 50 32 2 66 42 B 82 52 R 98 62 b 114 72 r 3 03 ETX 19 13 DC3 35 23 # 51 33 3 67 43 C 83 53 S 99 63 c 115 73 s 4 04 EOT 20 14 DC4 36 24 $ 52 34 4 68 44 D 84 54 T 100 64 d 116 74 t 5 05 ENQ 21 15 NAK 37 25 % 53 35 5 69 45 E 85 55 U 101 65 e 117 75 u 6 06 ACK 22 16 SYN 38 26 & 54 36 6 70 46 F 86 56 V 102 66 f 118 76 v 7 07 BEL 23 17 ETB 39 27 ' 55 37 7 71 47 G  87 57 W 103 67 g 119 77 w 8 08 BS 24 18 CAN 40 28 ( 56 38 8 72 48 H 88 58 X 104 68 h 120 78 x 9 09 HT 25 19 EM 41 29 ) 57 39 9 73 49 I 89 59 Y 105 69 i 121 79 y 10 0A LF 26 1A SUB 42 2A * 58 3A : 74 4A J 90 5A Z 106 6A j 122 7A z 11 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 DELCopy the code

We then take the character as input and run the program to get FLAG

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

7, description,

This article is compiled by hetian Network security Laboratory, please indicate the source of the original:

Linux Reverse Engineering CTFs for Beginners | 🔐 Blog of Osanda osandamalith.com/2019/02/11/…

Reference:

The ELF file structure description – yooooooo – blog garden www.cnblogs.com/linhaostudy…

About hetian Net security laboratory

Hetian Network security Laboratory (www.hetianlab.com) – a leading practical online education platform for network security

Real environment, online practice learning network security;

The experiment covers: system security, software security, network security, Web security, mobile security, CTF, forensic analysis, penetration testing, network security awareness education, etc.