6.2.1 re XHPCTF2017 dont_panic

下载文件

题目解析

第一步当然是 file 啦:

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

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

看一下段吧:

  1. $ readelf -S dont_panic
  2. There are 13 section headers, starting at offset 0xfa388:
  3. Section Headers:
  4. [Nr] Name Type Address Offset
  5. Size EntSize Flags Link Info Align
  6. [ 0] NULL 0000000000000000 00000000
  7. 0000000000000000 0000000000000000 0 0 0
  8. [ 1] .text PROGBITS 0000000000401000 00001000
  9. 000000000007ae40 0000000000000000 AX 0 0 16
  10. [ 2] .rodata PROGBITS 000000000047c000 0007c000
  11. 0000000000033f5b 0000000000000000 A 0 0 32
  12. [ 3] .typelink PROGBITS 00000000004b0080 000b0080
  13. 0000000000000b4c 0000000000000000 A 0 0 32
  14. [ 4] .itablink PROGBITS 00000000004b0bd0 000b0bd0
  15. 0000000000000038 0000000000000000 A 0 0 8
  16. [ 5] .gosymtab PROGBITS 00000000004b0c08 000b0c08
  17. 0000000000000000 0000000000000000 A 0 0 1
  18. [ 6] .gopclntab PROGBITS 00000000004b0c20 000b0c20
  19. 0000000000044d5d 0000000000000000 A 0 0 32
  20. [ 7] .noptrdata PROGBITS 00000000004f6000 000f6000
  21. 0000000000002608 0000000000000000 WA 0 0 32
  22. [ 8] .data PROGBITS 00000000004f8620 000f8620
  23. 0000000000001cf0 0000000000000000 WA 0 0 32
  24. [ 9] .bss NOBITS 00000000004fa320 000fa310
  25. 000000000001a908 0000000000000000 WA 0 0 32
  26. [10] .noptrbss NOBITS 0000000000514c40 000fa310
  27. 00000000000046a0 0000000000000000 WA 0 0 32
  28. [11] .note.go.buildid NOTE 0000000000400fc8 00000fc8
  29. 0000000000000038 0000000000000000 A 0 0 4
  30. [12] .shstrtab STRTAB 0000000000000000 000fa310
  31. 0000000000000073 0000000000000000 0 0 1

我们发现一些奇怪的东西,.gosymtab.gopclantab,Google 一下才知道,它其实是一个用 Go 语言编写的程序。好吧,运行它:

  1. $ ./dont_panic
  2. usage: ./dont_panic flag
  3. $ ./dont_panic abcd
  4. Nope.
  1. $ xxd -g1 dont_panic | grep Nope.
  2. 000a5240: 3e 45 72 72 6e 6f 45 72 72 6f 72 4e 6f 70 65 2e >ErrnoErrorNope.
  3. $ objdump -d dont_panic | grep a524b
  4. 47ba23: 48 8d 05 21 98 02 00 lea 0x29821(%rip),%rax # 0x4a524b

字符串“Nope.”应该是判断错误时的输出,我们顺便找到了使用它的地址为 0x47ba23,接下来在去 r2 里看吧,经过一番搜索,找到了最重要的函数 fcn.0047b8a0

  1. [0x0047ba23]> pdf @ fcn.0047b8a0
  2. / (fcn) fcn.0047b8a0 947
  3. | fcn.0047b8a0 ();
  4. | ; JMP XREF from 0x0047bc4e (fcn.0047b8a0)
  5. | .-> 0x0047b8a0 64488b0c25f8. mov rcx, qword fs:[0xfffffffffffffff8]
  6. | : 0x0047b8a9 488d442490 lea rax, [rsp - 0x70]
  7. | : 0x0047b8ae 483b4110 cmp rax, qword [rcx + 0x10] ; [0x10:8]=-1 ; 16
  8. | ,==< 0x0047b8b2 0f8691030000 jbe 0x47bc49
  9. | |: 0x0047b8b8 4881ecf00000. sub rsp, 0xf0
  10. | |: 0x0047b8bf 4889ac24e800. mov qword [local_e8h], rbp
  11. | |: 0x0047b8c7 488dac24e800. lea rbp, [local_e8h] ; 0xe8 ; 232
  12. | |: 0x0047b8cf 488b05f2ec07. mov rax, qword [0x004fa5c8] ; [0x4fa5c8:8]=0
  13. | |: 0x0047b8d6 4883f802 cmp rax, 2 ; 2
  14. | ,===< 0x0047b8da 0f8530020000 jne 0x47bb10
  15. | ||: ; JMP XREF from 0x0047bc3d (fcn.0047b8a0)
  16. | .----> 0x0047b8e0 488b05d9ec07. mov rax, qword [0x004fa5c0] ; [0x4fa5c0:8]=0
  17. | :||: 0x0047b8e7 488b0ddaec07. mov rcx, qword [0x004fa5c8] ; [0x4fa5c8:8]=0
  18. | :||: 0x0047b8ee 4883f901 cmp rcx, 1 ; 1
  19. | ,=====< 0x0047b8f2 0f8611020000 jbe 0x47bb09
  20. | |:||: 0x0047b8f8 488b4810 mov rcx, qword [rax + 0x10] ; [0x10:8]=-1 ; 16
  21. | |:||: 0x0047b8fc 48894c2450 mov qword [local_50h], rcx
  22. | |:||: 0x0047b901 488b4018 mov rax, qword [rax + 0x18] ; [0x18:8]=-1 ; 24
  23. | |:||: 0x0047b905 4889442448 mov qword [local_48h], rax
  24. | |:||: 0x0047b90a 4883f82a cmp rax, 0x2a ; '*' ; 42 ; 判断密码长度是否大于 42
  25. | ,======< 0x0047b90e 0f8c0f010000 jl 0x47ba23 ; 若小于,失败
  26. | ||:||: 0x0047b914 31d2 xor edx, edx
  27. | ||:||: 0x0047b916 31db xor ebx, ebx
  28. | ||:||: 0x0047b918 4889542438 mov qword [local_38h], rdx ; 密码字符串 provided_flag 的下标 i
  29. | ||:||: 0x0047b91d 885c2436 mov byte [local_36h], bl
  30. | ||:||: 0x0047b921 4839c2 cmp rdx, rax ; 比较下标 i 与密码长度
  31. | ,=======< 0x0047b924 7d72 jge 0x47b998 ; 若大于或等于,成功
  32. | |||:||: ; JMP XREF from 0x0047b996 (fcn.0047b8a0)
  33. | --------> 0x0047b926 0fb63411 movzx esi, byte [rcx + rdx] ; 循环终点
  34. | |||:||: 0x0047b92a 4080fe80 cmp sil, 0x80 ; 128
  35. | ========< 0x0047b92e 0f83a0010000 jae 0x47bad4
  36. | |||:||: 0x0047b934 400fb6f6 movzx esi, sil
  37. | |||:||: 0x0047b938 488d7a01 lea rdi, [rdx + 1] ; 1 ; 下标 + 1
  38. | |||:||: ; JMP XREF from 0x0047bb04 (fcn.0047b8a0) ; 密码逐位判断逻辑
  39. | --------> 0x0047b93c 48897c2440 mov qword [local_40h], rdi
  40. | |||:||: 0x0047b941 01f3 add ebx, esi
  41. | |||:||: 0x0047b943 885c2437 mov byte [local_37h], bl ; bl 代表 provided_flag[i]
  42. | |||:||: 0x0047b947 881c24 mov byte [rsp], bl
  43. | |||:||: 0x0047b94a e811feffff call fcn.0047b760 ; 该函数会对 bl 做一些处理
  44. | |||:||: 0x0047b94f 0fb6442408 movzx eax, byte [local_8h] ; [0x8:1]=255 ; 8 ; eax 是上面函数的返回值,即 mapanic(provided_flag[i])
  45. | |||:||: 0x0047b954 488b4c2438 mov rcx, qword [local_38h] ; [0x38:8]=-1 ; '8' ; 56
  46. | |||:||: 0x0047b959 4883f92a cmp rcx, 0x2a ; '*' ; 42 ; 判断 rcx 是否大于等于 0x2a
  47. | ========< 0x0047b95d 0f836a010000 jae 0x47bacd ; 如果大于或等于,跳转
  48. | |||:||: 0x0047b963 488d15f6a807. lea rdx, 0x004f6260 ; 读入 constant_binary_blob
  49. | |||:||: 0x0047b96a 0fb60c0a movzx ecx, byte [rdx + rcx] ; ecx 代表 constant_binary_blob[i]
  50. | |||:||: 0x0047b96e 38c8 cmp al, cl ; 比较 mapanic(provided_flag[i]) constant_binary_blob[i]
  51. | ========< 0x0047b970 0f85ad000000 jne 0x47ba23 ; 如果不等于,失败
  52. | |||:||: 0x0047b976 488b442448 mov rax, qword [local_48h] ; [0x48:8]=-1 ; 'H' ; 72
  53. | |||:||: 0x0047b97b 488b4c2450 mov rcx, qword [local_50h] ; [0x50:8]=-1 ; 'P' ; 80
  54. | |||:||: 0x0047b980 488b542440 mov rdx, qword [local_40h] ; [0x40:8]=-1 ; '@' ; 64
  55. | |||:||: 0x0047b985 0fb65c2437 movzx ebx, byte [local_37h] ; [0x37:1]=255 ; '7' ; 55
  56. | |||:||: 0x0047b98a 4889542438 mov qword [local_38h], rdx
  57. | |||:||: 0x0047b98f 885c2436 mov byte [local_36h], bl
  58. | |||:||: 0x0047b993 4839c2 cmp rdx, rax
  59. | ========< 0x0047b996 7c8e jl 0x47b926 ; 循环起点
  60. | |||:||: ; JMP XREF from 0x0047b924 (fcn.0047b8a0)
  61. | `-------> 0x0047b998 488d05d5c902. lea rax, 0x004a8374 ; "Seems like you got a flag." ; 成功
  62. | ||:||: 0x0047b99f 48898424a800. mov qword [local_a8h], rax
  63. | ||:||: 0x0047b9a7 48c78424b000. mov qword [local_b0h], 0x1c ; [0x1c:8]=-1 ; 28
  64. | ||:||: 0x0047b9b3 48c744245800. mov qword [local_58h], 0
  65. | ||:||: 0x0047b9bc 48c744246000. mov qword [local_60h], 0
  66. | ||:||: 0x0047b9c5 488d05b4e300. lea rax, 0x00489d80
  67. | ||:||: 0x0047b9cc 48890424 mov qword [rsp], rax
  68. | ||:||: 0x0047b9d0 488d8c24a800. lea rcx, [local_a8h] ; 0xa8 ; 168
  69. | ||:||: 0x0047b9d8 48894c2408 mov qword [local_8h], rcx
  70. | ||:||: 0x0047b9dd e80efff8ff call fcn.0040b8f0
  71. | ||:||: 0x0047b9e2 488b442410 mov rax, qword [local_10h] ; [0x10:8]=-1 ; 16
  72. | ||:||: 0x0047b9e7 488b4c2418 mov rcx, qword [local_18h] ; [0x18:8]=-1 ; 24
  73. | ||:||: 0x0047b9ec 4889442458 mov qword [local_58h], rax
  74. | ||:||: 0x0047b9f1 48894c2460 mov qword [local_60h], rcx
  75. | ||:||: 0x0047b9f6 488d442458 lea rax, [local_58h] ; 0x58 ; 'X' ; 88
  76. | ||:||: 0x0047b9fb 48890424 mov qword [rsp], rax
  77. | ||:||: 0x0047b9ff 48c744240801. mov qword [local_8h], 1
  78. | ||:||: 0x0047ba08 48c744241001. mov qword [local_10h], 1
  79. | ||:||: 0x0047ba11 e84a8effff call fcn.00474860
  80. | ||:||: 0x0047ba16 48c704240000. mov qword [rsp], 0
  81. | ||:||: 0x0047ba1e e88d1efeff call fcn.0045d8b0
  82. | ||:||: ; JMP XREF from 0x0047b90e (fcn.0047b8a0)
  83. | ||:||: ; JMP XREF from 0x0047b970 (fcn.0047b8a0)
  84. | -`------> 0x0047ba23 488d05219802. lea rax, 0x004a524b ; "Nope." ; 失败
  85. | |:||: 0x0047ba2a 488984248800. mov qword [local_88h], rax
  86. | |:||: 0x0047ba32 48c784249000. mov qword [local_90h], 5
  87. | |:||: 0x0047ba3e 48c784249800. mov qword [local_98h], 0
  88. | |:||: 0x0047ba4a 48c78424a000. mov qword [local_a0h], 0
  89. | |:||: 0x0047ba56 488d0523e300. lea rax, 0x00489d80
  90. | |:||: 0x0047ba5d 48890424 mov qword [rsp], rax
  91. | |:||: 0x0047ba61 488d84248800. lea rax, [local_88h] ; 0x88 ; 136
  92. | |:||: 0x0047ba69 4889442408 mov qword [local_8h], rax
  93. | |:||: 0x0047ba6e e87dfef8ff call fcn.0040b8f0
  94. | |:||: 0x0047ba73 488b442410 mov rax, qword [local_10h] ; [0x10:8]=-1 ; 16
  95. | |:||: 0x0047ba78 488b4c2418 mov rcx, qword [local_18h] ; [0x18:8]=-1 ; 24
  96. | |:||: 0x0047ba7d 488984249800. mov qword [local_98h], rax
  97. | |:||: 0x0047ba85 48898c24a000. mov qword [local_a0h], rcx
  98. | |:||: 0x0047ba8d 488d84249800. lea rax, [local_98h] ; 0x98 ; 152
  99. | |:||: 0x0047ba95 48890424 mov qword [rsp], rax
  100. | |:||: 0x0047ba99 48c744240801. mov qword [local_8h], 1
  101. | |:||: 0x0047baa2 48c744241001. mov qword [local_10h], 1
  102. | |:||: 0x0047baab e8b08dffff call fcn.00474860
  103. | |:||: 0x0047bab0 48c704240100. mov qword [rsp], 1
  104. | |:||: 0x0047bab8 e8f31dfeff call fcn.0045d8b0
  105. | |:||: 0x0047babd 488bac24e800. mov rbp, qword [local_e8h] ; [0xe8:8]=-1 ; 232
  106. | |:||: 0x0047bac5 4881c4f00000. add rsp, 0xf0
  107. | |:||: 0x0047bacc c3 ret
  108. | |:||: ; JMP XREF from 0x0047b95d (fcn.0047b8a0)
  109. | --------> 0x0047bacd e8ee8dfaff call fcn.004248c0
  110. | |:||: 0x0047bad2 0f0b ud2
  111. | |:||: ; JMP XREF from 0x0047b92e (fcn.0047b8a0)
  112. | --------> 0x0047bad4 48890c24 mov qword [rsp], rcx
  113. | |:||: 0x0047bad8 4889442408 mov qword [local_8h], rax
  114. | |:||: 0x0047badd 4889542410 mov qword [local_10h], rdx
  115. | |:||: 0x0047bae2 e869b8fcff call fcn.00447350
  116. | |:||: 0x0047bae7 8b742418 mov esi, dword [local_18h] ; [0x18:4]=-1 ; 24
  117. | |:||: 0x0047baeb 488b7c2420 mov rdi, qword [local_20h] ; [0x20:8]=-1 ; 32
  118. | |:||: 0x0047baf0 488b442448 mov rax, qword [local_48h] ; [0x48:8]=-1 ; 'H' ; 72
  119. | |:||: 0x0047baf5 488b4c2450 mov rcx, qword [local_50h] ; [0x50:8]=-1 ; 'P' ; 80
  120. | |:||: 0x0047bafa 488b542438 mov rdx, qword [local_38h] ; [0x38:8]=-1 ; '8' ; 56
  121. | |:||: 0x0047baff 0fb65c2436 movzx ebx, byte [local_36h] ; [0x36:1]=255 ; '6' ; 54
  122. | ========< 0x0047bb04 e933feffff jmp 0x47b93c
  123. | |:||: ; JMP XREF from 0x0047b8f2 (fcn.0047b8a0)
  124. | `-----> 0x0047bb09 e8b28dfaff call fcn.004248c0
  125. | :||: 0x0047bb0e 0f0b ud2
  126. | :||: ; JMP XREF from 0x0047b8da (fcn.0047b8a0)
  127. | :`---> 0x0047bb10 488d054c9802. lea rax, 0x004a5363 ; "usage:"
  128. | : |: 0x0047bb17 4889442478 mov qword [local_78h], rax
  129. | : |: 0x0047bb1c 48c784248000. mov qword [local_80h], 6
  130. | : |: 0x0047bb28 488d056a9602. lea rax, 0x004a5199 ; "flag"
  131. | : |: 0x0047bb2f 4889442468 mov qword [local_68h], rax
  132. | : |: 0x0047bb34 48c744247004. mov qword [local_70h], 4
  133. | : |: 0x0047bb3d 488dbc24b800. lea rdi, [local_b8h] ; 0xb8 ; 184
  134. | : |: 0x0047bb45 0f57c0 xorps xmm0, xmm0
  135. | : |: 0x0047bb48 4883c7f0 add rdi, 0xfffffffffffffff0
  136. | : |: 0x0047bb4c 48896c24f0 mov qword [rsp - 0x10], rbp
  137. | : |: 0x0047bb51 488d6c24f0 lea rbp, [rsp - 0x10]
  138. | : |: 0x0047bb56 e8851afdff call fcn.0044d5e0
  139. | : |: 0x0047bb5b 488b6d00 mov rbp, qword [rbp]
  140. | : |: 0x0047bb5f 488d051ae200. lea rax, 0x00489d80
  141. | : |: 0x0047bb66 48890424 mov qword [rsp], rax
  142. | : |: 0x0047bb6a 488d4c2478 lea rcx, [local_78h] ; 0x78 ; 'x' ; 120
  143. | : |: 0x0047bb6f 48894c2408 mov qword [local_8h], rcx
  144. | : |: 0x0047bb74 e877fdf8ff call fcn.0040b8f0
  145. | : |: 0x0047bb79 488b442410 mov rax, qword [local_10h] ; [0x10:8]=-1 ; 16
  146. | : |: 0x0047bb7e 488b4c2418 mov rcx, qword [local_18h] ; [0x18:8]=-1 ; 24
  147. | : |: 0x0047bb83 48898424b800. mov qword [local_b8h], rax
  148. | : |: 0x0047bb8b 48898c24c000. mov qword [local_c0h], rcx
  149. | : |: 0x0047bb93 488b052eea07. mov rax, qword [0x004fa5c8] ; [0x4fa5c8:8]=0
  150. | : |: 0x0047bb9a 488b0d1fea07. mov rcx, qword [0x004fa5c0] ; [0x4fa5c0:8]=0
  151. | : |: 0x0047bba1 4885c0 test rax, rax
  152. | :,===< 0x0047bba4 0f8698000000 jbe 0x47bc42
  153. | :||: 0x0047bbaa 48894c2408 mov qword [local_8h], rcx
  154. | :||: 0x0047bbaf 488d05cae100. lea rax, 0x00489d80
  155. | :||: 0x0047bbb6 48890424 mov qword [rsp], rax
  156. | :||: 0x0047bbba e831fdf8ff call fcn.0040b8f0
  157. | :||: 0x0047bbbf 488b442410 mov rax, qword [local_10h] ; [0x10:8]=-1 ; 16
  158. | :||: 0x0047bbc4 488b4c2418 mov rcx, qword [local_18h] ; [0x18:8]=-1 ; 24
  159. | :||: 0x0047bbc9 48898424c800. mov qword [local_c8h], rax
  160. | :||: 0x0047bbd1 48898c24d000. mov qword [local_d0h], rcx
  161. | :||: 0x0047bbd9 488d05a0e100. lea rax, 0x00489d80
  162. | :||: 0x0047bbe0 48890424 mov qword [rsp], rax
  163. | :||: 0x0047bbe4 488d4c2468 lea rcx, [local_68h] ; 0x68 ; 'h' ; 104
  164. | :||: 0x0047bbe9 48894c2408 mov qword [local_8h], rcx
  165. | :||: 0x0047bbee e8fdfcf8ff call fcn.0040b8f0
  166. | :||: 0x0047bbf3 488b442418 mov rax, qword [local_18h] ; [0x18:8]=-1 ; 24
  167. | :||: 0x0047bbf8 488b4c2410 mov rcx, qword [local_10h] ; [0x10:8]=-1 ; 16
  168. | :||: 0x0047bbfd 48898c24d800. mov qword [local_d8h], rcx
  169. | :||: 0x0047bc05 48898424e000. mov qword [local_e0h], rax
  170. | :||: 0x0047bc0d 488d8424b800. lea rax, [local_b8h] ; 0xb8 ; 184
  171. | :||: 0x0047bc15 48890424 mov qword [rsp], rax
  172. | :||: 0x0047bc19 48c744240803. mov qword [local_8h], 3
  173. | :||: 0x0047bc22 48c744241003. mov qword [local_10h], 3
  174. | :||: 0x0047bc2b e8308cffff call fcn.00474860
  175. | :||: 0x0047bc30 48c704240100. mov qword [rsp], 1
  176. | :||: 0x0047bc38 e8731cfeff call fcn.0045d8b0
  177. | `====< 0x0047bc3d e99efcffff jmp 0x47b8e0
  178. | ||: ; JMP XREF from 0x0047bba4 (fcn.0047b8a0)
  179. | `---> 0x0047bc42 e8798cfaff call fcn.004248c0
  180. | |: 0x0047bc47 0f0b ud2
  181. | |: ; JMP XREF from 0x0047b8b2 (fcn.0047b8a0)
  182. | `--> 0x0047bc49 e872f3fcff call fcn.0044afc0
  183. \ `=< 0x0047bc4e e94dfcffff jmp fcn.0047b8a0

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

  1. for (int i=0; i<length(provided_flag[i]); i++) {
  2. if (main_mapanic(provided_flag[i]) != constant_binary_blob[i]) {
  3. badboy();
  4. exit();
  5. }
  6. goodboy();
  7. }

如果要硬着头皮调试的话当然也可以,但我们这里采取暴力破解的办法。还记得章节 5.2 里说的 pin 吗,”由于程序具有循环、分支等结构,每次运行时执行的指令数量不一定相同,于是我们可是使用 Pin 来统计执行指令的数量,从而对程序进行分析”。这里就是这样,程序对输入的密码逐位判断,如果错误,就跳出来,所以根据我们密码正确字节数的不同,程序会执行有明显差异的次数。我们还讲过一个官方示例 inscount0.cpp,我们针对这一题稍微做一点修改,如下:

  1. #include <iostream>
  2. #include <fstream>
  3. #include "pin.H"
  4. ofstream OutFile;
  5. // The running count of instructions is kept here
  6. // make it static to help the compiler optimize docount
  7. static UINT64 icount = 0;
  8. // This function is called before every instruction is executed
  9. VOID docount(void *ip) {
  10. if ((long int)ip == 0x0047b96e) icount++; // 0x0047b960: compare mapanic(provided_flag[i]) with constant_binary_blob[i]
  11. }
  12. // Pin calls this function every time a new instruction is encountered
  13. VOID Instruction(INS ins, VOID *v)
  14. {
  15. // Insert a call to docount before every instruction, no arguments are passed
  16. INS_InsertCall(ins, IPOINT_BEFORE, (AFUNPTR)docount, IARG_INST_PTR, IARG_END); // IARG_INST_PTR: Type: ADDRINT. The address of the instrumented instruction.
  17. }
  18. KNOB<string> KnobOutputFile(KNOB_MODE_WRITEONCE, "pintool",
  19. "o", "inscount.out", "specify output file name");
  20. // This function is called when the application exits
  21. VOID Fini(INT32 code, VOID *v)
  22. {
  23. // Write to a file since cout and cerr maybe closed by the application
  24. OutFile.setf(ios::showbase);
  25. OutFile << "Count " << icount << endl;
  26. OutFile.close();
  27. }
  28. /* ===================================================================== */
  29. /* Print Help Message */
  30. /* ===================================================================== */
  31. INT32 Usage()
  32. {
  33. cerr << "This tool counts the number of dynamic instructions executed" << endl;
  34. cerr << endl << KNOB_BASE::StringKnobSummary() << endl;
  35. return -1;
  36. }
  37. /* ===================================================================== */
  38. /* Main */
  39. /* ===================================================================== */
  40. /* argc, argv are the entire command line: pin -t <toolname> -- ... */
  41. /* ===================================================================== */
  42. int main(int argc, char * argv[])
  43. {
  44. // Initialize pin
  45. if (PIN_Init(argc, argv)) return Usage();
  46. OutFile.open(KnobOutputFile.Value().c_str());
  47. // Register Instruction to be called to instrument instructions
  48. INS_AddInstrumentFunction(Instruction, 0);
  49. // Register Fini to be called when the application exits
  50. PIN_AddFiniFunction(Fini, 0);
  51. // Start the program, never returns
  52. PIN_StartProgram();
  53. return 0;
  54. }

主要是修改了两个地方:

  1. // This function is called before every instruction is executed
  2. VOID docount(void *ip) {
  3. if ((long int)ip == 0x0047b96e) icount++; // 0x0047b960: compare mapanic(provided_flag[i]) with constant_binary_blob[i]
  4. }

该函数会在每条指令执行之前被调用,判断是否是我们需要的 0x0047b96e 地址处的指令。

然后由于函数 docount 需要一个参数,所以 Instruction 函数也要修改,加入指令的地址 IARG_INST_PTR

  1. // Pin calls this function every time a new instruction is encountered
  2. VOID Instruction(INS ins, VOID *v)
  3. {
  4. // Insert a call to docount before every instruction, no arguments are passed
  5. INS_InsertCall(ins, IPOINT_BEFORE, (AFUNPTR)docount, IARG_INST_PTR, IARG_END); // IARG_INST_PTR: Type: ADDRINT. The address of the instrumented instruction.
  6. }

好,接下来 make 并执行。其实我们是知道 flag 结构的,”hxp{…}“ ,总共 42 个字节。

  1. $ cp dont_panic.cpp source/tools/MyPintool
  2. [MyPinTool]$ make obj-intel64/dont_panic.so TARGET=intel64
  3. [MyPinTool]$ ../../../pin -t obj-intel64/dont_panic.so -o inscount.out -- ~/dont_panic "hxp{aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa}" ; cat inscount.out
  4. Nope.
  5. Count 5

注意,这里的 5 是执行次数,匹配正确的个数是 5-1=4,即 “hxp{“。但是最后一次是例外,因为完全匹配成功后直接跳转返回,不会再进行匹配。

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

  1. import os
  2. def get_count(flag):
  3. os.system("../../../pin -t obj-intel64/dont_panic.so -o inscount.out -- ~/dont_panic " + "\"" + flag + "\"")
  4. with open("inscount.out") as f:
  5. count = int(f.read().split(" ")[1])
  6. return count
  7. charset = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_-+*'"
  8. flag = list("hxp{aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa}")
  9. count = 0
  10. while count != 42:
  11. for i in range(4, 41): # only compare "a" in "hex{}"
  12. for c in charset:
  13. flag[i] = c
  14. # print("".join(flag))
  15. count = get_count("".join(flag))
  16. if count == i+2:
  17. break
  18. print("".join(flag))

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

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

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

  1. import gdb
  2. CHAR_SUCCESS = 0x47B976
  3. NOPE = 0x47BA23
  4. gdb.execute("set pagination off")
  5. gdb.execute("b*0x47B976") #Success for a given character
  6. gdb.execute("b*0x47BA23") #Block displaying "Nope"
  7. charset = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_-+*{}'"
  8. flag = list('A'*42) #junk
  9. for i in range(0,len(flag)) :
  10. for c in charset:
  11. flag[i] = c
  12. # the number of times we need to hit the
  13. # success bp for the previous correct characters
  14. success_hits = i
  15. gdb.execute("r " + '"' + "".join(flag) + '"')
  16. while success_hits > 0 :
  17. gdb.execute('c')
  18. success_hits -= 1
  19. #we break either on success or on fail
  20. rip = int(gdb.parse_and_eval("$rip"))
  21. if rip == CHAR_SUCCESS:
  22. break #right one. To the next character
  23. if rip == NOPE: #added for clarity
  24. continue
  25. print("".join(flag))

在最后一篇参考资料里,介绍了怎样还原 Go 二进制文件的函数名,这将大大简化我们的分析。

参考资料