题目解析

第一步当然是 file 啦:

$ file dont_panic 
dont_panic: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), statically linked, stripped

64 位,静态编译,而且 stripped。

看一下段吧:

$ 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 语言编写的程序。好吧,运行它:

$ ./dont_panic 
usage: ./dont_panic flag
$ ./dont_panic abcd
Nope.
$ 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

[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

根据我们的分析(详见注释),密码判断逻辑应该如下:

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,我们针对这一题稍微做一点修改,如下:

#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;
}

主要是修改了两个地方:

// 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

// 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 个字节。

$ 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{“。但是最后一次是例外,因为完全匹配成功后直接跳转返回,不会再进行匹配。

和预期结果一样,下面写个脚本来自动化这一过程:

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))

可惜就是速度有点慢,大概跑了一个小时吧。。。

hxp{k3eP_C4lM_AnD_D0n't_P4n1c__G0_i5_S4F3}
$ ./dont_panic "hxp{k3eP_C4lM_AnD_D0n't_P4n1c__G0_i5_S4F3}"
Seems like you got a flag...

参考资料里的 gdb 脚本就快得多:

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 二进制文件的函数名,这将大大简化我们的分析。

参考资料