Linux CTF 逆向入門
我們先來看看 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_Halfe_type;/* Object file type */ Elf32_Halfe_machine;/* Architecture */ Elf32_Worde_version;/* Object file version */ Elf32_Addre_entry;/* Entry point virtual address */ Elf32_Offe_phoff;/* Program header table file offset */ Elf32_Offe_shoff;/* Section header table file offset */ Elf32_Worde_flags;/* Processor-specific flags */ Elf32_Halfe_ehsize;/* ELF header size in bytes */ Elf32_Halfe_phentsize;/* Program header table entry size */ Elf32_Halfe_phnum;/* Program header table entry count */ Elf32_Halfe_shentsize;/* Section header table entry size */ Elf32_Halfe_shnum;/* Section header table entry count */ Elf32_Halfe_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: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 複製程式碼
我們可以使用以下計算方法來計算整個二進位制檔案的大小:
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_Wordp_type;/* Segment type */ Elf32_Offp_offset;/* Segment file offset */ Elf32_Addrp_vaddr;/* Segment virtual address */ Elf32_Addrp_paddr;/* Segment physical address */ Elf32_Wordp_filesz;/* Segment size in file */ Elf32_Wordp_memsz;/* Segment size in memory */ Elf32_Wordp_flags;/* Segment flags */ Elf32_Wordp_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_Wordsh_name;/* section的名字 (string tbl index) */ Elf32_Wordsh_type;/*section類別 */ Elf32_Wordsh_flags;/* section在程序中執行的特性(讀、寫) */ Elf32_Addrsh_addr;/* 在記憶體中開始的虛地址 */ Elf32_Offsh_offset;/* 此section在檔案中的偏移 */ Elf32_Wordsh_size;/* Section size in bytes */ Elf32_Wordsh_link;/* Link to another section */ Elf32_Wordsh_info;/* Additional section information */ Elf32_Wordsh_addralign;/* Section alignment */ Elf32_Wordsh_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] NameTypeAddressOffSizeES Flg Lk Inf Al [ 0]NULL0000000000000000 000000 000000 00000 [ 1] .interpPROGBITS00000000000002a8 0002a8 00001c 00A001 [ 2] .note.ABI-tagNOTE00000000000002c4 0002c4 000020 00A004 [ 3] .note.gnu.build-id NOTE00000000000002e4 0002e4 000024 00A004 [ 4] .gnu.hashGNU_HASH0000000000000308 000308 0000c0 00A508 [ 5] .dynsymDYNSYM00000000000003c8 0003c8 000c90 18A618 [ 6] .dynstrSTRTAB0000000000001058 001058 0005d8 00A001 [ 7] .gnu.versionVERSYM0000000000001630 001630 00010c 02A502 [ 8] .gnu.version_rVERNEED0000000000001740 001740 000070 00A618 [ 9] .rela.dynRELA00000000000017b0 0017b0 001350 18A508 [10] .rela.pltRELA0000000000002b00 002b00 0009f0 18AI5248 [11] .initPROGBITS0000000000004000 004000 000017 00AX004 [12] .pltPROGBITS0000000000004020 004020 0006b0 10AX00 16 [13] .plt.gotPROGBITS00000000000046d0 0046d0 000018 08AX008 [14] .textPROGBITS00000000000046f0 0046f0 01253e 00AX00 16 [15] .finiPROGBITS0000000000016c30 016c30 000009 00AX004 [16] .rodataPROGBITS0000000000017000 017000 005129 00A00 32 [17] .eh_frame_hdrPROGBITS000000000001c12c 01c12c 0008fc 00A004 [18] .eh_framePROGBITS000000000001ca28 01ca28 002ed0 00A008 [19] .init_arrayINIT_ARRAY0000000000021390 020390 000008 08WA008 [20] .fini_arrayFINI_ARRAY0000000000021398 020398 000008 08WA008 [21] .data.rel.roPROGBITS00000000000213a0 0203a0 000a38 00WA00 32 [22] .dynamicDYNAMIC0000000000021dd8 020dd8 0001f0 10WA608 [23] .gotPROGBITS0000000000021fc8 020fc8 000038 08WA008 [24] .got.pltPROGBITS0000000000022000 021000 000368 08WA008 [25] .dataPROGBITS0000000000022380 021380 000268 00WA00 32 [26] .bssNOBITS0000000000022600 0215e8 0012d8 00WA00 32 [27] .gnu_debuglinkPROGBITS0000000000000000 0215e8 000034 00004 [28] .shstrtabSTRTAB0000000000000000 02161c 00010a 00001 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題目來試試吧。
二進位制檔案下載地址:ufile.io/blvpm
國內下載: 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_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. 複製程式碼
4、檢查符號表(Symbols) nm命令檢視庫檔案的符號
# nm -D nix_5744af788e6cbdb29bb41e8b0e5f3cd5 w __cxa_finalize U exit w __gmon_start__ 00002004 R _IO_stdin_used w _ITM_deregisterTMCloneTable w _ITM_registerTMCloneTable U __libc_start_main U printf U puts U sprintf U 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-i386 Disassembly of section .text: 000010b0 <_start>: 10b0:31 edxorebp,ebp 10b2:5epopesi 10b3:89 e1movecx,esp 10b5:83 e4 f0andesp,0xfffffff0 10b8:50pusheax 10b9:54pushesp 10ba:52pushedx 10bb:e8 22 00 00 00call10e2 <_start+0x32> 10c0:81 c3 40 2f 00 00addebx,0x2f40 10c6:8d 83 60 d4 ff ffleaeax,[ebx-0x2ba0] 10cc:50pusheax 10cd:8d 83 00 d4 ff ffleaeax,[ebx-0x2c00] 10d3:50pusheax 10d4:51pushecx 10d5:56pushesi 10d6:ff b3 f8 ff ff ffpushDWORD PTR [ebx-0x8] 10dc:e8 9f ff ff ffcall1080 <__libc_start_main@plt> 10e1:f4hlt 10e2:8b 1c 24movebx,DWORD PTR [esp] 10e5:c3ret 10e6:66 90xchgax,ax 10e8:66 90xchgax,ax 10ea:66 90xchgax,ax 10ec:66 90xchgax,ax 10ee:66 90xchgax,ax ... Output Omitted ... 000011e9 <main>: 11e9:8d 4c 24 04leaecx,[esp+0x4] 11ed:83 e4 f0andesp,0xfffffff0 11f0:ff 71 fcpushDWORD PTR [ecx-0x4] 11f3:55pushebp 11f4:89 e5movebp,esp 11f6:56pushesi 11f7:53pushebx 11f8:51pushecx 11f9:83 ec 1csubesp,0x1c 11fc:e8 ef fe ff ffcall10f0 <__x86.get_pc_thunk.bx> 1201:81 c3 ff 2d 00 00addebx,0x2dff 1207:89 cemovesi,ecx 1209:c7 45 e4 00 00 00 00 movDWORD PTR [ebp-0x1c],0x0 1210:c7 45 dc 07 00 00 00 movDWORD PTR [ebp-0x24],0x7 1217:83 3e 02cmpDWORD PTR [esi],0x2 121a:74 1cje1238 <main+0x4f> 121c:83 ec 0csubesp,0xc 121f:8d 83 08 e0 ff ffleaeax,[ebx-0x1ff8] 1225:50pusheax 1226:e8 15 fe ff ffcall1040 <printf@plt> 122b:83 c4 10addesp,0x10 122e:83 ec 0csubesp,0xc 1231:6a 01push0x1 1233:e8 28 fe ff ffcall1060 <exit@plt> 1238:8b 46 04moveax,DWORD PTR [esi+0x4] 123b:83 c0 04addeax,0x4 123e:8b 00moveax,DWORD PTR [eax] 1240:83 ec 0csubesp,0xc 1243:50pusheax 1244:e8 27 fe ff ffcall1070 <strlen@plt> 1249:83 c4 10addesp,0x10 124c:83 f8 0fcmpeax,0xf 124f:76 1cjbe126d <main+0x84> 1251:83 ec 0csubesp,0xc 1254:8d 83 20 e0 ff ffleaeax,[ebx-0x1fe0] 125a:50pusheax 125b:e8 f0 fd ff ffcall1050 <puts@plt> 1260:83 c4 10addesp,0x10 1263:83 ec 0csubesp,0xc 1266:6a 01push0x1 1268:e8 f3 fd ff ffcall1060 <exit@plt> 126d:c7 45 e0 00 00 00 00 movDWORD PTR [ebp-0x20],0x0 1274:eb 1ajmp1290 <main+0xa7> 1276:8b 46 04moveax,DWORD PTR [esi+0x4] 1279:83 c0 04addeax,0x4 127c:8b 10movedx,DWORD PTR [eax] 127e:8b 45 e0moveax,DWORD PTR [ebp-0x20] 1281:01 d0addeax,edx 1283:0f b6 00movzxeax,BYTE PTR [eax] 1286:0f be c0movsxeax,al 1289:01 45 e4addDWORD PTR [ebp-0x1c],eax 128c:83 45 e0 01addDWORD PTR [ebp-0x20],0x1 1290:8b 45 e0moveax,DWORD PTR [ebp-0x20] 1293:3b 45 dccmpeax,DWORD PTR [ebp-0x24] 1296:7c dejl1276 <main+0x8d> 1298:81 7d e4 21 03 00 00 cmpDWORD PTR [ebp-0x1c],0x321 129f:75 1ajne12bb <main+0xd2> 12a1:e8 33 00 00 00call12d9 <comp_key> 12a6:83 ec 08subesp,0x8 12a9:50pusheax 12aa:8d 83 3c e0 ff ffleaeax,[ebx-0x1fc4] 12b0:50pusheax 12b1:e8 8a fd ff ffcall1040 <printf@plt> 12b6:83 c4 10addesp,0x10 12b9:eb 12jmp12cd <main+0xe4> 12bb:83 ec 0csubesp,0xc 12be:8d 83 5e e0 ff ffleaeax,[ebx-0x1fa2] 12c4:50pusheax 12c5:e8 86 fd ff ffcall1050 <puts@plt> 12ca:83 c4 10addesp,0x10 12cd:90nop 12ce:8d 65 f4leaesp,[ebp-0xc] 12d1:59popecx 12d2:5bpopebx 12d3:5epopesi 12d4:5dpopebp 12d5:8d 61 fcleaesp,[ecx-0x4] 12d8:c3ret 000012d9 <comp_key>: 12d9:55pushebp 12da:89 e5movebp,esp 12dc:57pushedi 12dd:56pushesi 12de:53pushebx 12df:83 ec 7csubesp,0x7c 12e2:e8 09 fe ff ffcall10f0 <__x86.get_pc_thunk.bx> 12e7:81 c3 19 2d 00 00addebx,0x2d19 12ed:c7 45 e4 00 00 00 00 movDWORD PTR [ebp-0x1c],0x0 12f4:c7 45 a8 4c 00 00 00 movDWORD PTR [ebp-0x58],0x4c 12fb:c7 45 ac 33 00 00 00 movDWORD PTR [ebp-0x54],0x33 1302:c7 45 b0 74 00 00 00 movDWORD PTR [ebp-0x50],0x74 1309:c7 45 b4 73 00 00 00 movDWORD PTR [ebp-0x4c],0x73 1310:c7 45 b8 5f 00 00 00 movDWORD PTR [ebp-0x48],0x5f 1317:c7 45 bc 67 00 00 00 movDWORD PTR [ebp-0x44],0x67 131e:c7 45 c0 33 00 00 00 movDWORD PTR [ebp-0x40],0x33 1325:c7 45 c4 74 00 00 00 movDWORD PTR [ebp-0x3c],0x74 132c:c7 45 c8 5f 00 00 00 movDWORD PTR [ebp-0x38],0x5f 1333:c7 45 cc 69 00 00 00 movDWORD PTR [ebp-0x34],0x69 133a:c7 45 d0 6e 00 00 00 movDWORD PTR [ebp-0x30],0x6e 1341:c7 45 d4 32 00 00 00 movDWORD PTR [ebp-0x2c],0x32 1348:c7 45 d8 5f 00 00 00 movDWORD PTR [ebp-0x28],0x5f 134f:c7 45 dc 52 00 00 00 movDWORD PTR [ebp-0x24],0x52 1356:c7 45 e0 33 00 00 00 movDWORD PTR [ebp-0x20],0x33 135d:8b 55 e0movedx,DWORD PTR [ebp-0x20] 1360:8b 75 dcmovesi,DWORD PTR [ebp-0x24] 1363:8b 45 d8moveax,DWORD PTR [ebp-0x28] 1366:89 45 a4movDWORD PTR [ebp-0x5c],eax 1369:8b 4d d4movecx,DWORD PTR [ebp-0x2c] 136c:89 4d a0movDWORD PTR [ebp-0x60],ecx 136f:8b 7d d0movedi,DWORD PTR [ebp-0x30] 1372:89 7d 9cmovDWORD PTR [ebp-0x64],edi 1375:8b 45 ccmoveax,DWORD PTR [ebp-0x34] 1378:89 45 98movDWORD PTR [ebp-0x68],eax 137b:8b 4d c8movecx,DWORD PTR [ebp-0x38] 137e:89 4d 94movDWORD PTR [ebp-0x6c],ecx 1381:8b 7d c4movedi,DWORD PTR [ebp-0x3c] 1384:89 7d 90movDWORD PTR [ebp-0x70],edi 1387:8b 45 c0moveax,DWORD PTR [ebp-0x40] 138a:89 45 8cmovDWORD PTR [ebp-0x74],eax 138d:8b 4d bcmovecx,DWORD PTR [ebp-0x44] 1390:89 4d 88movDWORD PTR [ebp-0x78],ecx 1393:8b 7d b8movedi,DWORD PTR [ebp-0x48] 1396:89 7d 84movDWORD PTR [ebp-0x7c],edi 1399:8b 45 b4moveax,DWORD PTR [ebp-0x4c] 139c:89 45 80movDWORD PTR [ebp-0x80],eax 139f:8b 7d b0movedi,DWORD PTR [ebp-0x50] 13a2:8b 4d acmovecx,DWORD PTR [ebp-0x54] 13a5:8b 45 a8moveax,DWORD PTR [ebp-0x58] 13a8:83 ec 0csubesp,0xc 13ab:52pushedx 13ac:56pushesi 13ad:ff 75 a4pushDWORD PTR [ebp-0x5c] 13b0:ff 75 a0pushDWORD PTR [ebp-0x60] 13b3:ff 75 9cpushDWORD PTR [ebp-0x64] 13b6:ff 75 98pushDWORD PTR [ebp-0x68] 13b9:ff 75 94pushDWORD PTR [ebp-0x6c] 13bc:ff 75 90pushDWORD PTR [ebp-0x70] 13bf:ff 75 8cpushDWORD PTR [ebp-0x74] 13c2:ff 75 88pushDWORD PTR [ebp-0x78] 13c5:ff 75 84pushDWORD PTR [ebp-0x7c] 13c8:ff 75 80pushDWORD PTR [ebp-0x80] 13cb:57pushedi 13cc:51pushecx 13cd:50pusheax 13ce:8d 83 78 e0 ff ffleaeax,[ebx-0x1f88] 13d4:50pusheax 13d5:8d 83 30 00 00 00leaeax,[ebx+0x30] 13db:50pusheax 13dc:e8 af fc ff ffcall1090 <sprintf@plt> 13e1:83 c4 50addesp,0x50 13e4:8d 83 30 00 00 00leaeax,[ebx+0x30] 13ea:8d 65 f4leaesp,[ebp-0xc] 13ed:5bpopebx 13ee:5epopesi 13ef:5fpopedi 13f0:5dpopebp 13f1:c3ret 13f2:66 90xchgax,ax 13f4:66 90xchgax,ax 13f6:66 90xchgax,ax 13f8:66 90xchgax,ax 13fa:66 90xchgax,ax 13fc:66 90xchgax,ax 13fe:66 90xchgax,ax ... Output Omitted ... 複製程式碼
在這個二進位制檔案中,符號沒有被剝離,因此我們可以看到函式名稱,這使得它更容易理解。 如果你可以閱讀彙編程式碼,你可以很清楚的知道發生了什麼。 如果不能閱讀彙編程式碼,讓我們做一些實時除錯,並嘗試更好地理解。 7、實時除錯 這裡我們使用 GDB-Peda
進行實時除錯 我們首先檢查二進位制檔案中的函式。我們可以看到 main,comp_key
等函式
gdb-peda$ info functions All defined functions: Non-debugging symbols: 0x00001000_init 0x00001040printf@plt 0x00001050puts@plt 0x00001060exit@plt 0x00001070strlen@plt 0x00001080__libc_start_main@plt 0x00001090sprintf@plt 0x000010a0__cxa_finalize@plt 0x000010a8__gmon_start__@plt 0x000010b0_start 0x000010f0__x86.get_pc_thunk.bx 0x00001100deregister_tm_clones 0x00001140register_tm_clones 0x00001190__do_global_dtors_aux 0x000011e0frame_dummy 0x000011e5__x86.get_pc_thunk.dx 0x000011e9main 0x000012d9comp_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>:subesp,0x1c) EFLAGS: 0x282 (carry parity adjust zero SIGN trap INTERRUPT direction overflow) [-------------------------------------code-------------------------------------] 0x565561f6 <main+13>:pushesi 0x565561f7 <main+14>:pushebx 0x565561f8 <main+15>:pushecx => 0x565561f9 <main+16>:subesp,0x1c 0x565561fc <main+19>:call0x565560f0 <__x86.get_pc_thunk.bx> 0x56556201 <main+24>:addebx,0x2dff 0x56556207 <main+30>:movesi,ecx 0x56556209 <main+32>:movDWORD 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>:addesp,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$ 複製程式碼
讓我們來看看程式的邏輯,程式首先嚐試比較引數的數量。它儲存在ecx暫存器中並移動到esi,它用於將值與0x2進行比較
0x56556207 <+30>:movesi,ecx 0x56556209 <+32>:movDWORD PTR [ebp-0x1c],0x0 0x56556210 <+39>:movDWORD PTR [ebp-0x24],0x7 0x56556217 <+46>:cmpDWORD PTR [esi],0x2 0x5655621a <+49>:je0x56556238 <main+79> 0x5655621c <+51>:subesp,0xc 0x5655621f <+54>:leaeax,[ebx-0x1ff8] 0x56556225 <+60>:pusheax 0x56556226 <+61>:call0x56556040 <printf@plt> 0x5655622b <+66>:addesp,0x10 0x5655622e <+69>:subesp,0xc 0x56556231 <+72>:push0x1 0x56556233 <+74>:call0x56556060 <exit@plt> 複製程式碼
其虛擬碼看起來是這樣的:
if(argc != 2) { printf("Usage: script.exe <key>"); exit(1); } 複製程式碼
0x56556238 <+79>:moveax,DWORD PTR [esi+0x4] 0x5655623b <+82>:addeax,0x4 0x5655623e <+85>:moveax,DWORD PTR [eax] 0x56556240 <+87>:subesp,0xc 0x56556243 <+90>:pusheax 0x56556244 <+91>:call0x56556070 <strlen@plt> 0x56556249 <+96>:addesp,0x10 0x5655624c <+99>:cmpeax,0xf 0x5655624f <+102>:jbe0x5655626d <main+132> 0x56556251 <+104>:subesp,0xc 0x56556254 <+107>:leaeax,[ebx-0x1fe0] 0x5655625a <+113>:pusheax 0x5655625b <+114>:call0x56556050 <puts@plt> 0x56556260 <+119>:addesp,0x10 0x56556263 <+122>:subesp,0xc 0x56556266 <+125>:push0x1 0x56556268 <+127>:call0x56556060 <exit@plt> 複製程式碼
其程式碼是這樣的:
if(strlen(argv[1]) > 15) { puts("Length of argv[1] too long."); exit(1); } 複製程式碼
如果你檢查這個程式碼,可以看到有一個迴圈正在迭代我們輸入字串的每個字元。
0x5655626d <+132>:movDWORD PTR [ebp-0x20],0x0 0x56556274 <+139>:jmp0x56556290 <main+167> 0x56556276 <+141>:moveax,DWORD PTR [esi+0x4] 0x56556279 <+144>:addeax,0x4 0x5655627c <+147>:movedx,DWORD PTR [eax] 0x5655627e <+149>:moveax,DWORD PTR [ebp-0x20] 0x56556281 <+152>:addeax,edx 0x56556283 <+154>:movzxeax,BYTE PTR [eax] 0x56556286 <+157>:movsxeax,al 0x56556289 <+160>:addDWORD PTR [ebp-0x1c],eax 0x5655628c <+163>:addDWORD PTR [ebp-0x20],0x1 0x56556290 <+167>:moveax,DWORD PTR [ebp-0x20] 0x56556293 <+170>:cmpeax,DWORD PTR [ebp-0x24] 0x56556296 <+173>:jl0x56556276 <main+141> 0x56556298 <+175>:cmpDWORD PTR [ebp-0x1c],0x321 0x5655629f <+182>:jne0x565562bb <main+210> 0x565562a1 <+184>:call0x565562d9 <comp_key> 0x565562a6 <+189>:subesp,0x8 0x565562a9 <+192>:pusheax 0x565562aa <+193>:leaeax,[ebx-0x1fc4] 0x565562b0 <+199>:pusheax 0x565562b1 <+200>:call0x56556040 <printf@plt> 0x565562b6 <+205>:addesp,0x10 0x565562b9 <+208>:jmp0x565562cd <main+228> 0x565562bb <+210>:subesp,0xc 0x565562be <+213>:leaeax,[ebx-0x1fa2] 0x565562c4 <+219>:pusheax 0x565562c5 <+220>:call0x56556050 <puts@plt> 0x565562ca <+225>:addesp,0x10 0x565562cd <+228>:nop 0x565562ce <+229>:leaesp,[ebp-0xc] 0x565562d1 <+232>:popecx 0x565562d2 <+233>:popebx 0x565562d3 <+234>:popesi 0x565562d4 <+235>:popebp 0x565562d5 <+236>:leaesp,[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>:cmpeax,DWORD PTR [ebp-0x24]) EFLAGS: 0x206 (carry PARITY adjust zero sign trap INTERRUPT direction overflow) [-------------------------------------code-------------------------------------] 0x56556289 <main+160>:addDWORD PTR [ebp-0x1c],eax 0x5655628c <main+163>:addDWORD PTR [ebp-0x20],0x1 0x56556290 <main+167>:moveax,DWORD PTR [ebp-0x20] => 0x56556293 <main+170>:cmpeax,DWORD PTR [ebp-0x24] 0x56556296 <main+173>:jl0x56556276 <main+141> 0x56556298 <main+175>:cmpDWORD PTR [ebp-0x1c],0x321 0x5655629f <main+182>:jne0x565562bb <main+210> 0x565562a1 <main+184>:call0x565562d9 <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 HexDec HexDec HexDec HexDec HexDec HexDec HexDec Hex 0 00 NUL16 10 DLE32 2048 30 064 40 @80 50 P96 60 `112 70 p 1 01 SOH17 11 DC133 21 !49 31 165 41 A81 51 Q97 61 a113 71 q 2 02 STX18 12 DC234 22 "50 32 266 42 B82 52 R98 62 b114 72 r 3 03 ETX19 13 DC335 23 #51 33 367 43 C83 53 S99 63 c115 73 s 4 04 EOT20 14 DC436 24 $52 34 468 44 D84 54 T100 64 d116 74 t 5 05 ENQ21 15 NAK37 25 %53 35 569 45 E85 55 U101 65 e117 75 u 6 06 ACK22 16 SYN38 26 &54 36 670 46 F86 56 V102 66 f118 76 v 7 07 BEL23 17 ETB39 27 '55 37 771 47 G87 57 W103 67 g119 77 w 8 08 BS24 18 CAN40 28 (56 38 872 48 H88 58 X104 68 h120 78 x 9 09 HT25 19 EM41 29 )57 39 973 49 I89 59 Y105 69 i121 79 y 10 0A LF26 1A SUB42 2A *58 3A :74 4A J90 5A Z106 6A j122 7A z 11 0B VT27 1B ESC43 2B +59 3B ;75 4B K91 5B [107 6B k123 7B { 12 0C FF28 1C FS44 2C ,60 3C <76 4C L92 5C \108 6C l124 7C | 13 0D CR29 1D GS45 2D -61 3D =77 4D M93 5D ]109 6D m125 7D } 14 0E SO30 1E RS46 2E .62 3E >78 4E N94 5E ^110 6E n126 7E ~ 15 0F SI31 1F US47 2F /63 3F ?79 4F O95 5F _111 6F o127 7F DEL 複製程式碼
然後我們將字元作為輸入,執行程式即可得到FLAG
# ./nix_5744af788e6cbdb29bb41e8b0e5f3cd5 rrrrrru [+] The flag is: SAYCURE{L3ts_g3t_in2_R3} [+] 複製程式碼