Program-Compilation-and-ELF-Format-Lab-2
实验目的
1、学习在 Linux 操作系统上进行分析所需的基本工具。
2、找出隐藏在 payload 中的 flag。
实验原理
使用常用分析工具。
实验环境
使用 binary 虚拟机,即在 Lab 1 中已经配置过的环境(包括时间戳)。
Task 1:使用 file 解决类型问题
步骤:
1、进入 chapter5 文件中,使用 file payload 指令查看 payload 文件的类型。
2、使用 head payload 指令查看 payload 文件前十行得内容。
Q:这个文件使用了什么编码方式?
A:进入在线分析网站 https://www.boxentriq.com/code-breaking/cipher-identifier 中分析可得该编码方式为 base64 编码。并且,判断一段文本是否为 Base64 编码可以使用以下几个方法:
检查字符集:Base64 编码只使用 A-Z、a-z、0-9 和 +/= 这 64 个字符。如果一段文本只包含这些字符,那很可能是 Base64 编码的。
检查长度:Base64 编码后的字符串长度通常是 4 的倍数。如果一段文本的长度是 4 的倍数,那也很可能是 Base64 编码的。
检查结尾:Base64 编码的结尾通常会有 = 或 == 来表示编码字符串的长度不是 4 的倍数。
尝试解码:可以尝试使用 base64 命令对该文本进行解码,如果解码成功并且内容可读,那就证明该文本是 Base64 编码的。
3、输入 base64 -d payload > decoded_payload 指令解码 payload 文件并将解码内容保存在 decoded_payload 文件中
4、输入 file decode_payload 指令发现无法打开该文件,输入 file -c decode_payload 指令发现该压缩文件中有两个文件 ctf 和 67b8601。
5、输入 tar zxvf decoded_payload 指令解压该文件,输入 file ctf 指令发现该文件为一个 ELF 文件,输入 file 67b8601 指令发现该文件为一个 PC bitmap 文件。
Q:这两个文件分别是什么类型?
A:ctf文件是一个该文件为一个 ELF 文件,67b8601文件为一个 PC bitmap 文件。
Task 2:使用 ldd 探索依赖性
步骤:
1、输入 ./ctf 指令,动态链接器会提示缺少某个库文件。
2、输入 ldd ctf 指令,显示出共享库和依赖关系。
Q:这个文件存在哪些未解析的依赖项?
A:从给出的 ldd 命令输出结果可以看出,存在一个未解析的依赖项 lib5ae9b7f.so。
3、输入 grep 'ELF' ./* 指令,查看所有 ELF 文件的头部信息,发现"ELF"字符出现在了 67b8601 文件中(输入 grep 'ELF' 67b8601 指令可以发现)。
Task 3:使用 xxd 查看文件内容
步骤:
输入 xxd 67b8601 | head -n 15 指令分析 67b8601 文件的前 15 行。
Q:xxd 默认每行显示多少字节的内容?
A:xxd 默认每行显示16字节的内容。
Task 4:使用 readelf 解析并提取 ELF 库文件
步骤:
1、0x7f 在偏移量为 0x35 的位置处,转换为10进制为53,因此输入 tail -c +53 67b8601 > temp_elf 指令截取从偏移量 0x35 到文件末尾。
2、输入 readelf -h temp_elf 指令查看 temp_elf 文件的 ELF 头部信息。
3、由上图可知,Start of section headers = 8568 (bytes into file),Size of section headers = 64 (bytes),Number of section headers = 27,由此可知 size = 10296。输入head -c 10296 temp_elf > lib5ae9b7f.so 指令将 temp_elf 文件中开头至偏移量为 10296 的内容截取到 lib5ae9b7f.so 文件中。
4、输入 export LD_LIBRARY_PATH=/home/binary/code/chapter5 指令将当前目录添加到 LD_LIBRARY_PATH 环境变量中。
5、输入 .\ctf 指令,发现程序成功运行,没有报错。
Q:是否一定要去除隐藏的 ELF 文件的尾部无效部分?不去除的情况下这个库文件是否还有效?
A:不一定。这个库文件依然是有效的。
Task 5:使用 nm 解析符号
步骤:
1、输入 echo $? 指令,发现其中包含的 ctf 退出状态为 1,表示有错误。
2、输入 readelf -s lib5ae9b7f.so 指令,查看库文件的符号表。
3、输入 nm lib5ae9b7f.so 指令,显示 no symbols,说明文件已经被剥离。
4、输入 nm -D lib5ae9b7f.so 指令,获得被修饰的符号名称。
5、输入 nm -D --demangle lib5ae9b7f.so 指令,获得多个有意义的函数。
......
0000000000000c60 T rc4_decrypt(rc4_state_t*, unsigned char*, int)
0000000000000c70 T rc4_decrypt(rc4_state_t*, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >&)
0000000000000b40 T rc4_encrypt(rc4_state_t*, unsigned char*, int)
0000000000000bc0 T rc4_encrypt(rc4_state_t*, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >&)
0000000000000cb0 T rc4_init(rc4_state_t*, unsigned char*, int)
U std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >::_M_create(unsigned long&, unsigned long)
U std::__throw_logic_error(char const*)
Q:是哪个加密算法?包含了这个加密算法的什么功能?
A:这是一个包含RC4加密算法实现的库文件。这个文件包含的与RC4加密相关的函数有:rc4_init(rc4_state_t*, unsigned char*, int) - 初始化RC4加密状态,使用给定的密钥。rc4_encrypt(rc4_state_t*, unsigned char*, int) - 使用RC4加密给定的明文数据。rc4_decrypt(rc4_state_t*, unsigned char*, int) - 使用RC4解密给定的密文数据。还有一些重载函数如rc4_encrypt和rc4_decrypt接受std::string类型的数据。
Task 6:使用 strings 查找 Hints
步骤:
输入 strings ctf 指令查看二进制文件的字符串,并未找到有关的指令或者内容字符。
Task 7:使用 strace 和 ltrace 跟踪系统调用和库文件调用
步骤:
1、输入 strace ./ctf show_me_the_flag 指令获得跟踪 ctf 的系统调用行为。
......
write(1, "checking 'show_me_the_flag'\n", 28checking 'show_me_the_flag') = 28
write(1, "ok\n", 3ok) = 3
exit_group(1) = ?
Q:这 3 个系统调用分别是做什么的?
A:向终端输出 "checking 'show_me_the_flag'\n";向终端输出 "ok\n";终止当前进程及其子进程,退出状态码为 1。
2、输入 ltrace -i -C ./ctf show_me_the_flag 指令可以看到初始化了加密函数,该函数位于之前提取的库文件中;接着为一个 C++ 字符串赋值,大概是用加密消息对其进行初始化;然后调用解密函数解密此消息,并将解密后的消息分配到新的 C++ 字符串。
[0x400fe9] __libc_start_main(0x400bc0, 2, 0x7ffcb01844d8, 0x4010c0 <unfinished ...>
[0x400c44] __printf_chk(1, 0x401158, 0x7ffcb01862da, 160checking 'show_me_the_flag') = 28
[0x400c51] strcmp("show_me_the_flag", "show_me_the_flag") = 0
[0x400cf0] puts("ok"ok) = 3
[0x400d07] rc4_init(rc4_state_t*, unsigned char*, int)(0x7ffcb01842a0, 0x4011c0, 66, -1) = 0
[0x400d14] std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >::assign(char const*)(0x7ffcb01841e0, 0x40117b, 58, 3) = 0x7ffcb01841e0
[0x400d29] rc4_decrypt(rc4_state_t*, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >&)(0x7ffcb0184240, 0x7ffcb01842a0, 0x7ffcb01841e0, 0x7e889f91) = 0x7ffcb0184240
[0x400d36] std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >::_M_assign(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&)(0x7ffcb01841e0, 0x7ffcb0184240, 0x7ffcb0184250, 0) = 0
[0x400d53] getenv("GUESSME") = nil
[0xffffffffffffffff] +++ exited (status 1) +++
3、输入 GUESSME='foobar' ./ctf show_me_the_flag 指令,将 GUESSME 环境变量设置为虚拟值,发现输出新的一行,提示再猜一次。
4、输入 GUESSME="123" ltrace -i -C ./ctf show_me_the_fla 指令,ctf 继续分配并解密另一个 C++ 字符串,但是看不到有关 GUESSME 的任何提示。
Task 8:使用 objdump 检查指令级别的行为
步骤:
1、输入 objdump -d -M intel ctf 指令,
ctf: file format elf64-x86-64
Contents of section .rodata:
401140 01000200 44454255 473a2061 7267765b ....DEBUG: argv[
401150 315d203d 20257300 63686563 6b696e67 1] = %s.checking
401160 20272573 270a0073 686f775f 6d655f74 '%s'..show_me_t
401170 68655f66 6c616700 6f6b004f 89df919f he_flag.ok.O....
401180 887e009a 5b38babe 27ac0e3e 434d6285 .~..[8..'..>CMb.
401190 55868954 3848a34d 00192d76 40505e3a U..T8H.M..-v@P^:
4011a0 00726200 666c6167 203d2025 730a0067 .rb.flag = %s..g
4011b0 75657373 20616761 696e2100 00000000 uess again!.....
4011c0 49742773 206b696e 6461206c 696b6520 It's kinda like
4011d0 4c6f7569 7369616e 612e204f 72204461 Louisiana. Or Da
4011e0 676f6261 682e2044 61676f62 6168202d gobah. Dagobah -
4011f0 20576865 72652059 6f646120 6c697665 Where Yoda live
401200 73210000 00000000 s!......
Q:"guess again"存放的地址位置是什么?
A:0x4011af到0x4011b9。
2、输入 objdump -d -M intel ctf 指令,查看"guess again"周围的指令。
Task 9:使用 GDB 转存动态字符串缓冲区
步骤:
1、输入 gdb ./ctf 进入gbd调试程序。
2、由于 rcx 出现的地址为 0x400dc8,输入 break *0x400dc8 指令设置断点。
3、输入 set env GUESSME=123 指令设置 GUESSME 值为 123,输入 run show_me_the_flag 指令运行该程序,输入 output (char* )rcx 指令获得 rcx 指向的字符串"Crackers Don't Matter"。
4、输入 quit 指令推出 gbd 调试,输入 GUESSME="Crackers Don't Matter" ./ctf show_me_the_flag 指令获得最终的 flag = 84b34c124b2ba5ca224af8e33b077e9e。