CTF-All-In-One/doc/6.2.1_re_xhpctf2017_dont_panic.md
2018-08-05 17:43:10 +08:00

456 lines
23 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 6.2.1 re XHPCTF2017 dont_panic
- [题目解析](#题目解析)
- [参考资料](#参考资料)
[下载文件](../src/writeup/6.2.1_re_xhpctf2017_dont_panic)
## 题目解析
第一步当然是 file 啦:
```text
$ file dont_panic
dont_panic: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), statically linked, stripped
```
64 位,静态编译,而且 stripped。
看一下段吧:
```text
$ readelf -S dont_panic
There are 13 section headers, starting at offset 0xfa388:
Section Headers:
[Nr] Name Type Address Offset
Size EntSize Flags Link Info Align
[ 0] NULL 0000000000000000 00000000
0000000000000000 0000000000000000 0 0 0
[ 1] .text PROGBITS 0000000000401000 00001000
000000000007ae40 0000000000000000 AX 0 0 16
[ 2] .rodata PROGBITS 000000000047c000 0007c000
0000000000033f5b 0000000000000000 A 0 0 32
[ 3] .typelink PROGBITS 00000000004b0080 000b0080
0000000000000b4c 0000000000000000 A 0 0 32
[ 4] .itablink PROGBITS 00000000004b0bd0 000b0bd0
0000000000000038 0000000000000000 A 0 0 8
[ 5] .gosymtab PROGBITS 00000000004b0c08 000b0c08
0000000000000000 0000000000000000 A 0 0 1
[ 6] .gopclntab PROGBITS 00000000004b0c20 000b0c20
0000000000044d5d 0000000000000000 A 0 0 32
[ 7] .noptrdata PROGBITS 00000000004f6000 000f6000
0000000000002608 0000000000000000 WA 0 0 32
[ 8] .data PROGBITS 00000000004f8620 000f8620
0000000000001cf0 0000000000000000 WA 0 0 32
[ 9] .bss NOBITS 00000000004fa320 000fa310
000000000001a908 0000000000000000 WA 0 0 32
[10] .noptrbss NOBITS 0000000000514c40 000fa310
00000000000046a0 0000000000000000 WA 0 0 32
[11] .note.go.buildid NOTE 0000000000400fc8 00000fc8
0000000000000038 0000000000000000 A 0 0 4
[12] .shstrtab STRTAB 0000000000000000 000fa310
0000000000000073 0000000000000000 0 0 1
```
我们发现一些奇怪的东西,`.gosymtab`、`.gopclantab`Google 一下才知道,它其实是一个用 Go 语言编写的程序。好吧,运行它:
```text
$ ./dont_panic
usage: ./dont_panic flag
$ ./dont_panic abcd
Nope.
```
```text
$ xxd -g1 dont_panic | grep Nope.
000a5240: 3e 45 72 72 6e 6f 45 72 72 6f 72 4e 6f 70 65 2e >ErrnoErrorNope.
$ objdump -d dont_panic | grep a524b
47ba23: 48 8d 05 21 98 02 00 lea 0x29821(%rip),%rax # 0x4a524b
```
字符串“Nope.”应该是判断错误时的输出,我们顺便找到了使用它的地址为 `0x47ba23`,接下来在去 r2 里看吧,经过一番搜索,找到了最重要的函数 `fcn.0047b8a0`
```text
[0x0047ba23]> pdf @ fcn.0047b8a0
/ (fcn) fcn.0047b8a0 947
| fcn.0047b8a0 ();
| ; JMP XREF from 0x0047bc4e (fcn.0047b8a0)
| .-> 0x0047b8a0 64488b0c25f8. mov rcx, qword fs:[0xfffffffffffffff8]
| : 0x0047b8a9 488d442490 lea rax, [rsp - 0x70]
| : 0x0047b8ae 483b4110 cmp rax, qword [rcx + 0x10] ; [0x10:8]=-1 ; 16
| ,==< 0x0047b8b2 0f8691030000 jbe 0x47bc49
| |: 0x0047b8b8 4881ecf00000. sub rsp, 0xf0
| |: 0x0047b8bf 4889ac24e800. mov qword [local_e8h], rbp
| |: 0x0047b8c7 488dac24e800. lea rbp, [local_e8h] ; 0xe8 ; 232
| |: 0x0047b8cf 488b05f2ec07. mov rax, qword [0x004fa5c8] ; [0x4fa5c8:8]=0
| |: 0x0047b8d6 4883f802 cmp rax, 2 ; 2
| ,===< 0x0047b8da 0f8530020000 jne 0x47bb10
| ||: ; JMP XREF from 0x0047bc3d (fcn.0047b8a0)
| .----> 0x0047b8e0 488b05d9ec07. mov rax, qword [0x004fa5c0] ; [0x4fa5c0:8]=0
| :||: 0x0047b8e7 488b0ddaec07. mov rcx, qword [0x004fa5c8] ; [0x4fa5c8:8]=0
| :||: 0x0047b8ee 4883f901 cmp rcx, 1 ; 1
| ,=====< 0x0047b8f2 0f8611020000 jbe 0x47bb09
| |:||: 0x0047b8f8 488b4810 mov rcx, qword [rax + 0x10] ; [0x10:8]=-1 ; 16
| |:||: 0x0047b8fc 48894c2450 mov qword [local_50h], rcx
| |:||: 0x0047b901 488b4018 mov rax, qword [rax + 0x18] ; [0x18:8]=-1 ; 24
| |:||: 0x0047b905 4889442448 mov qword [local_48h], rax
| |:||: 0x0047b90a 4883f82a cmp rax, 0x2a ; '*' ; 42 ; 判断密码长度是否大于 42
| ,======< 0x0047b90e 0f8c0f010000 jl 0x47ba23 ; 若小于,失败
| ||:||: 0x0047b914 31d2 xor edx, edx
| ||:||: 0x0047b916 31db xor ebx, ebx
| ||:||: 0x0047b918 4889542438 mov qword [local_38h], rdx ; 密码字符串 provided_flag 的下标 i
| ||:||: 0x0047b91d 885c2436 mov byte [local_36h], bl
| ||:||: 0x0047b921 4839c2 cmp rdx, rax ; 比较下标 i 与密码长度
| ,=======< 0x0047b924 7d72 jge 0x47b998 ; 若大于或等于,成功
| |||:||: ; JMP XREF from 0x0047b996 (fcn.0047b8a0)
| --------> 0x0047b926 0fb63411 movzx esi, byte [rcx + rdx] ; 循环终点
| |||:||: 0x0047b92a 4080fe80 cmp sil, 0x80 ; 128
| ========< 0x0047b92e 0f83a0010000 jae 0x47bad4
| |||:||: 0x0047b934 400fb6f6 movzx esi, sil
| |||:||: 0x0047b938 488d7a01 lea rdi, [rdx + 1] ; 1 ; 下标 + 1
| |||:||: ; JMP XREF from 0x0047bb04 (fcn.0047b8a0) ; 密码逐位判断逻辑
| --------> 0x0047b93c 48897c2440 mov qword [local_40h], rdi
| |||:||: 0x0047b941 01f3 add ebx, esi
| |||:||: 0x0047b943 885c2437 mov byte [local_37h], bl ; bl 代表 provided_flag[i]
| |||:||: 0x0047b947 881c24 mov byte [rsp], bl
| |||:||: 0x0047b94a e811feffff call fcn.0047b760 ; 该函数会对 bl 做一些处理
| |||:||: 0x0047b94f 0fb6442408 movzx eax, byte [local_8h] ; [0x8:1]=255 ; 8 ; eax 是上面函数的返回值,即 mapanic(provided_flag[i])
| |||:||: 0x0047b954 488b4c2438 mov rcx, qword [local_38h] ; [0x38:8]=-1 ; '8' ; 56
| |||:||: 0x0047b959 4883f92a cmp rcx, 0x2a ; '*' ; 42 ; 判断 rcx 是否大于等于 0x2a
| ========< 0x0047b95d 0f836a010000 jae 0x47bacd ; 如果大于或等于,跳转
| |||:||: 0x0047b963 488d15f6a807. lea rdx, 0x004f6260 ; 读入 constant_binary_blob
| |||:||: 0x0047b96a 0fb60c0a movzx ecx, byte [rdx + rcx] ; ecx 代表 constant_binary_blob[i]
| |||:||: 0x0047b96e 38c8 cmp al, cl ; 比较 mapanic(provided_flag[i]) 和 constant_binary_blob[i]
| ========< 0x0047b970 0f85ad000000 jne 0x47ba23 ; 如果不等于,失败
| |||:||: 0x0047b976 488b442448 mov rax, qword [local_48h] ; [0x48:8]=-1 ; 'H' ; 72
| |||:||: 0x0047b97b 488b4c2450 mov rcx, qword [local_50h] ; [0x50:8]=-1 ; 'P' ; 80
| |||:||: 0x0047b980 488b542440 mov rdx, qword [local_40h] ; [0x40:8]=-1 ; '@' ; 64
| |||:||: 0x0047b985 0fb65c2437 movzx ebx, byte [local_37h] ; [0x37:1]=255 ; '7' ; 55
| |||:||: 0x0047b98a 4889542438 mov qword [local_38h], rdx
| |||:||: 0x0047b98f 885c2436 mov byte [local_36h], bl
| |||:||: 0x0047b993 4839c2 cmp rdx, rax
| ========< 0x0047b996 7c8e jl 0x47b926 ; 循环起点
| |||:||: ; JMP XREF from 0x0047b924 (fcn.0047b8a0)
| `-------> 0x0047b998 488d05d5c902. lea rax, 0x004a8374 ; "Seems like you got a flag." ; 成功
| ||:||: 0x0047b99f 48898424a800. mov qword [local_a8h], rax
| ||:||: 0x0047b9a7 48c78424b000. mov qword [local_b0h], 0x1c ; [0x1c:8]=-1 ; 28
| ||:||: 0x0047b9b3 48c744245800. mov qword [local_58h], 0
| ||:||: 0x0047b9bc 48c744246000. mov qword [local_60h], 0
| ||:||: 0x0047b9c5 488d05b4e300. lea rax, 0x00489d80
| ||:||: 0x0047b9cc 48890424 mov qword [rsp], rax
| ||:||: 0x0047b9d0 488d8c24a800. lea rcx, [local_a8h] ; 0xa8 ; 168
| ||:||: 0x0047b9d8 48894c2408 mov qword [local_8h], rcx
| ||:||: 0x0047b9dd e80efff8ff call fcn.0040b8f0
| ||:||: 0x0047b9e2 488b442410 mov rax, qword [local_10h] ; [0x10:8]=-1 ; 16
| ||:||: 0x0047b9e7 488b4c2418 mov rcx, qword [local_18h] ; [0x18:8]=-1 ; 24
| ||:||: 0x0047b9ec 4889442458 mov qword [local_58h], rax
| ||:||: 0x0047b9f1 48894c2460 mov qword [local_60h], rcx
| ||:||: 0x0047b9f6 488d442458 lea rax, [local_58h] ; 0x58 ; 'X' ; 88
| ||:||: 0x0047b9fb 48890424 mov qword [rsp], rax
| ||:||: 0x0047b9ff 48c744240801. mov qword [local_8h], 1
| ||:||: 0x0047ba08 48c744241001. mov qword [local_10h], 1
| ||:||: 0x0047ba11 e84a8effff call fcn.00474860
| ||:||: 0x0047ba16 48c704240000. mov qword [rsp], 0
| ||:||: 0x0047ba1e e88d1efeff call fcn.0045d8b0
| ||:||: ; JMP XREF from 0x0047b90e (fcn.0047b8a0)
| ||:||: ; JMP XREF from 0x0047b970 (fcn.0047b8a0)
| -`------> 0x0047ba23 488d05219802. lea rax, 0x004a524b ; "Nope." ; 失败
| |:||: 0x0047ba2a 488984248800. mov qword [local_88h], rax
| |:||: 0x0047ba32 48c784249000. mov qword [local_90h], 5
| |:||: 0x0047ba3e 48c784249800. mov qword [local_98h], 0
| |:||: 0x0047ba4a 48c78424a000. mov qword [local_a0h], 0
| |:||: 0x0047ba56 488d0523e300. lea rax, 0x00489d80
| |:||: 0x0047ba5d 48890424 mov qword [rsp], rax
| |:||: 0x0047ba61 488d84248800. lea rax, [local_88h] ; 0x88 ; 136
| |:||: 0x0047ba69 4889442408 mov qword [local_8h], rax
| |:||: 0x0047ba6e e87dfef8ff call fcn.0040b8f0
| |:||: 0x0047ba73 488b442410 mov rax, qword [local_10h] ; [0x10:8]=-1 ; 16
| |:||: 0x0047ba78 488b4c2418 mov rcx, qword [local_18h] ; [0x18:8]=-1 ; 24
| |:||: 0x0047ba7d 488984249800. mov qword [local_98h], rax
| |:||: 0x0047ba85 48898c24a000. mov qword [local_a0h], rcx
| |:||: 0x0047ba8d 488d84249800. lea rax, [local_98h] ; 0x98 ; 152
| |:||: 0x0047ba95 48890424 mov qword [rsp], rax
| |:||: 0x0047ba99 48c744240801. mov qword [local_8h], 1
| |:||: 0x0047baa2 48c744241001. mov qword [local_10h], 1
| |:||: 0x0047baab e8b08dffff call fcn.00474860
| |:||: 0x0047bab0 48c704240100. mov qword [rsp], 1
| |:||: 0x0047bab8 e8f31dfeff call fcn.0045d8b0
| |:||: 0x0047babd 488bac24e800. mov rbp, qword [local_e8h] ; [0xe8:8]=-1 ; 232
| |:||: 0x0047bac5 4881c4f00000. add rsp, 0xf0
| |:||: 0x0047bacc c3 ret
| |:||: ; JMP XREF from 0x0047b95d (fcn.0047b8a0)
| --------> 0x0047bacd e8ee8dfaff call fcn.004248c0
| |:||: 0x0047bad2 0f0b ud2
| |:||: ; JMP XREF from 0x0047b92e (fcn.0047b8a0)
| --------> 0x0047bad4 48890c24 mov qword [rsp], rcx
| |:||: 0x0047bad8 4889442408 mov qword [local_8h], rax
| |:||: 0x0047badd 4889542410 mov qword [local_10h], rdx
| |:||: 0x0047bae2 e869b8fcff call fcn.00447350
| |:||: 0x0047bae7 8b742418 mov esi, dword [local_18h] ; [0x18:4]=-1 ; 24
| |:||: 0x0047baeb 488b7c2420 mov rdi, qword [local_20h] ; [0x20:8]=-1 ; 32
| |:||: 0x0047baf0 488b442448 mov rax, qword [local_48h] ; [0x48:8]=-1 ; 'H' ; 72
| |:||: 0x0047baf5 488b4c2450 mov rcx, qword [local_50h] ; [0x50:8]=-1 ; 'P' ; 80
| |:||: 0x0047bafa 488b542438 mov rdx, qword [local_38h] ; [0x38:8]=-1 ; '8' ; 56
| |:||: 0x0047baff 0fb65c2436 movzx ebx, byte [local_36h] ; [0x36:1]=255 ; '6' ; 54
| ========< 0x0047bb04 e933feffff jmp 0x47b93c
| |:||: ; JMP XREF from 0x0047b8f2 (fcn.0047b8a0)
| `-----> 0x0047bb09 e8b28dfaff call fcn.004248c0
| :||: 0x0047bb0e 0f0b ud2
| :||: ; JMP XREF from 0x0047b8da (fcn.0047b8a0)
| :`---> 0x0047bb10 488d054c9802. lea rax, 0x004a5363 ; "usage:"
| : |: 0x0047bb17 4889442478 mov qword [local_78h], rax
| : |: 0x0047bb1c 48c784248000. mov qword [local_80h], 6
| : |: 0x0047bb28 488d056a9602. lea rax, 0x004a5199 ; "flag"
| : |: 0x0047bb2f 4889442468 mov qword [local_68h], rax
| : |: 0x0047bb34 48c744247004. mov qword [local_70h], 4
| : |: 0x0047bb3d 488dbc24b800. lea rdi, [local_b8h] ; 0xb8 ; 184
| : |: 0x0047bb45 0f57c0 xorps xmm0, xmm0
| : |: 0x0047bb48 4883c7f0 add rdi, 0xfffffffffffffff0
| : |: 0x0047bb4c 48896c24f0 mov qword [rsp - 0x10], rbp
| : |: 0x0047bb51 488d6c24f0 lea rbp, [rsp - 0x10]
| : |: 0x0047bb56 e8851afdff call fcn.0044d5e0
| : |: 0x0047bb5b 488b6d00 mov rbp, qword [rbp]
| : |: 0x0047bb5f 488d051ae200. lea rax, 0x00489d80
| : |: 0x0047bb66 48890424 mov qword [rsp], rax
| : |: 0x0047bb6a 488d4c2478 lea rcx, [local_78h] ; 0x78 ; 'x' ; 120
| : |: 0x0047bb6f 48894c2408 mov qword [local_8h], rcx
| : |: 0x0047bb74 e877fdf8ff call fcn.0040b8f0
| : |: 0x0047bb79 488b442410 mov rax, qword [local_10h] ; [0x10:8]=-1 ; 16
| : |: 0x0047bb7e 488b4c2418 mov rcx, qword [local_18h] ; [0x18:8]=-1 ; 24
| : |: 0x0047bb83 48898424b800. mov qword [local_b8h], rax
| : |: 0x0047bb8b 48898c24c000. mov qword [local_c0h], rcx
| : |: 0x0047bb93 488b052eea07. mov rax, qword [0x004fa5c8] ; [0x4fa5c8:8]=0
| : |: 0x0047bb9a 488b0d1fea07. mov rcx, qword [0x004fa5c0] ; [0x4fa5c0:8]=0
| : |: 0x0047bba1 4885c0 test rax, rax
| :,===< 0x0047bba4 0f8698000000 jbe 0x47bc42
| :||: 0x0047bbaa 48894c2408 mov qword [local_8h], rcx
| :||: 0x0047bbaf 488d05cae100. lea rax, 0x00489d80
| :||: 0x0047bbb6 48890424 mov qword [rsp], rax
| :||: 0x0047bbba e831fdf8ff call fcn.0040b8f0
| :||: 0x0047bbbf 488b442410 mov rax, qword [local_10h] ; [0x10:8]=-1 ; 16
| :||: 0x0047bbc4 488b4c2418 mov rcx, qword [local_18h] ; [0x18:8]=-1 ; 24
| :||: 0x0047bbc9 48898424c800. mov qword [local_c8h], rax
| :||: 0x0047bbd1 48898c24d000. mov qword [local_d0h], rcx
| :||: 0x0047bbd9 488d05a0e100. lea rax, 0x00489d80
| :||: 0x0047bbe0 48890424 mov qword [rsp], rax
| :||: 0x0047bbe4 488d4c2468 lea rcx, [local_68h] ; 0x68 ; 'h' ; 104
| :||: 0x0047bbe9 48894c2408 mov qword [local_8h], rcx
| :||: 0x0047bbee e8fdfcf8ff call fcn.0040b8f0
| :||: 0x0047bbf3 488b442418 mov rax, qword [local_18h] ; [0x18:8]=-1 ; 24
| :||: 0x0047bbf8 488b4c2410 mov rcx, qword [local_10h] ; [0x10:8]=-1 ; 16
| :||: 0x0047bbfd 48898c24d800. mov qword [local_d8h], rcx
| :||: 0x0047bc05 48898424e000. mov qword [local_e0h], rax
| :||: 0x0047bc0d 488d8424b800. lea rax, [local_b8h] ; 0xb8 ; 184
| :||: 0x0047bc15 48890424 mov qword [rsp], rax
| :||: 0x0047bc19 48c744240803. mov qword [local_8h], 3
| :||: 0x0047bc22 48c744241003. mov qword [local_10h], 3
| :||: 0x0047bc2b e8308cffff call fcn.00474860
| :||: 0x0047bc30 48c704240100. mov qword [rsp], 1
| :||: 0x0047bc38 e8731cfeff call fcn.0045d8b0
| `====< 0x0047bc3d e99efcffff jmp 0x47b8e0
| ||: ; JMP XREF from 0x0047bba4 (fcn.0047b8a0)
| `---> 0x0047bc42 e8798cfaff call fcn.004248c0
| |: 0x0047bc47 0f0b ud2
| |: ; JMP XREF from 0x0047b8b2 (fcn.0047b8a0)
| `--> 0x0047bc49 e872f3fcff call fcn.0044afc0
\ `=< 0x0047bc4e e94dfcffff jmp fcn.0047b8a0
```
根据我们的分析(详见注释),密码判断逻辑应该如下:
```C
for (int i=0; i<length(provided_flag[i]); i++) {
if (main_mapanic(provided_flag[i]) != constant_binary_blob[i]) {
badboy();
exit();
}
goodboy();
}
```
如果要硬着头皮调试的话当然也可以,但我们这里采取暴力破解的办法。还记得章节 5.2 里说的 pin 吗,”由于程序具有循环、分支等结构,每次运行时执行的指令数量不一定相同,于是我们可是使用 Pin 来统计执行指令的数量,从而对程序进行分析”。这里就是这样,程序对输入的密码逐位判断,如果错误,就跳出来,所以根据我们密码正确字节数的不同,程序会执行有明显差异的次数。我们还讲过一个官方示例 inscount0.cpp我们针对这一题稍微做一点修改如下
```C
#include <iostream>
#include <fstream>
#include "pin.H"
ofstream OutFile;
// The running count of instructions is kept here
// make it static to help the compiler optimize docount
static UINT64 icount = 0;
// This function is called before every instruction is executed
VOID docount(void *ip) {
if ((long int)ip == 0x0047b96e) icount++; // 0x0047b960: compare mapanic(provided_flag[i]) with constant_binary_blob[i]
}
// Pin calls this function every time a new instruction is encountered
VOID Instruction(INS ins, VOID *v)
{
// Insert a call to docount before every instruction, no arguments are passed
INS_InsertCall(ins, IPOINT_BEFORE, (AFUNPTR)docount, IARG_INST_PTR, IARG_END); // IARG_INST_PTR: Type: ADDRINT. The address of the instrumented instruction.
}
KNOB<string> KnobOutputFile(KNOB_MODE_WRITEONCE, "pintool",
"o", "inscount.out", "specify output file name");
// This function is called when the application exits
VOID Fini(INT32 code, VOID *v)
{
// Write to a file since cout and cerr maybe closed by the application
OutFile.setf(ios::showbase);
OutFile << "Count " << icount << endl;
OutFile.close();
}
/* ===================================================================== */
/* Print Help Message */
/* ===================================================================== */
INT32 Usage()
{
cerr << "This tool counts the number of dynamic instructions executed" << endl;
cerr << endl << KNOB_BASE::StringKnobSummary() << endl;
return -1;
}
/* ===================================================================== */
/* Main */
/* ===================================================================== */
/* argc, argv are the entire command line: pin -t <toolname> -- ... */
/* ===================================================================== */
int main(int argc, char * argv[])
{
// Initialize pin
if (PIN_Init(argc, argv)) return Usage();
OutFile.open(KnobOutputFile.Value().c_str());
// Register Instruction to be called to instrument instructions
INS_AddInstrumentFunction(Instruction, 0);
// Register Fini to be called when the application exits
PIN_AddFiniFunction(Fini, 0);
// Start the program, never returns
PIN_StartProgram();
return 0;
}
```
主要是修改了两个地方:
```C
// This function is called before every instruction is executed
VOID docount(void *ip) {
if ((long int)ip == 0x0047b96e) icount++; // 0x0047b960: compare mapanic(provided_flag[i]) with constant_binary_blob[i]
}
```
该函数会在每条指令执行之前被调用,判断是否是我们需要的 `0x0047b96e` 地址处的指令。
然后由于函数 docount 需要一个参数,所以 Instruction 函数也要修改,加入指令的地址 `IARG_INST_PTR`
```C
// Pin calls this function every time a new instruction is encountered
VOID Instruction(INS ins, VOID *v)
{
// Insert a call to docount before every instruction, no arguments are passed
INS_InsertCall(ins, IPOINT_BEFORE, (AFUNPTR)docount, IARG_INST_PTR, IARG_END); // IARG_INST_PTR: Type: ADDRINT. The address of the instrumented instruction.
}
```
好,接下来 make 并执行。其实我们是知道 flag 结构的”hxp{...}“ ,总共 42 个字节。
```text
$ cp dont_panic.cpp source/tools/MyPintool
[MyPinTool]$ make obj-intel64/dont_panic.so TARGET=intel64
[MyPinTool]$ ../../../pin -t obj-intel64/dont_panic.so -o inscount.out -- ~/dont_panic "hxp{aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa}" ; cat inscount.out
Nope.
Count 5
```
注意,这里的 5 是执行次数,匹配正确的个数是 5-1=4即 "hxp{"。但是最后一次是例外,因为完全匹配成功后直接跳转返回,不会再进行匹配。
和预期结果一样,下面写个脚本来自动化这一过程:
```Python
import os
def get_count(flag):
os.system("../../../pin -t obj-intel64/dont_panic.so -o inscount.out -- ~/dont_panic " + "\"" + flag + "\"")
with open("inscount.out") as f:
count = int(f.read().split(" ")[1])
return count
charset = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_-+*'"
flag = list("hxp{aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa}")
count = 0
while count != 42:
for i in range(4, 41): # only compare "a" in "hex{}"
for c in charset:
flag[i] = c
# print("".join(flag))
count = get_count("".join(flag))
if count == i+2:
break
print("".join(flag))
```
可惜就是速度有点慢,大概跑了一个小时吧。。。
```text
hxp{k3eP_C4lM_AnD_D0n't_P4n1c__G0_i5_S4F3}
```
```text
$ ./dont_panic "hxp{k3eP_C4lM_AnD_D0n't_P4n1c__G0_i5_S4F3}"
Seems like you got a flag...
```
参考资料里的 gdb 脚本就快得多:
```Python
import gdb
CHAR_SUCCESS = 0x47B976
NOPE = 0x47BA23
gdb.execute("set pagination off")
gdb.execute("b*0x47B976") #Success for a given character
gdb.execute("b*0x47BA23") #Block displaying "Nope"
charset = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_-+*{}'"
flag = list('A'*42) #junk
for i in range(0,len(flag)) :
for c in charset:
flag[i] = c
# the number of times we need to hit the
# success bp for the previous correct characters
success_hits = i
gdb.execute("r " + '"' + "".join(flag) + '"')
while success_hits > 0 :
gdb.execute('c')
success_hits -= 1
#we break either on success or on fail
rip = int(gdb.parse_and_eval("$rip"))
if rip == CHAR_SUCCESS:
break #right one. To the next character
if rip == NOPE: #added for clarity
continue
print("".join(flag))
```
在最后一篇参考资料里,介绍了怎样还原 Go 二进制文件的函数名,这将大大简化我们的分析。
## 参考资料
- [Pin Tutorial](http://www.ic.unicamp.br/~rodolfo/mo801/04-PinTutorial.pdf)
- [Reversing GO binaries like a pro](https://rednaga.io/2016/09/21/reversing_go_binaries_like_a_pro/)
- [HXP CTF 2017 - "dont_panic" Reversing 100 Writeup](http://rce4fun.blogspot.com/2017/11/hxp-ctf-2017-dontpanic-reversing-100.html)
- [write-up for dont_panic](http://eternal.red/2017/dont_panic-writeup/)