DDCTF2019 RE obfuscating macros

思路借鉴自大佬sayhi的博客:http://sayhi2urmom.top/2019/04/18/ddctf2019/

源文件下载:http://file.eonew.cn/ctf/re/obfuscating_macros.out

这道题原本是一道比较难的逆向题,但是它的比较方式是逐字比较,便使得我们有了可乘之机。具体思路可以看看这篇博客:2019-04-23.硬件断点分析-解逆向题的一种思路

分析

主函数

__int64 __fastcall main(__int64 a1, char **a2, char **a3)
{
  bool v3; // al
  __int64 v4; // rax
  char v6; // [rsp+0h] [rbp-40h]
  unsigned __int64 v7; // [rsp+28h] [rbp-18h]

  v7 = __readfsqword(0x28u);
  std::__cxx11::basic_string,std::allocator>::basic_string(&v6, a2, a3);
  std::operator>>,std::allocator>(&std::cin, &v6);
  v3 = (unsigned __int8)sub_4069D6(&v6) && (unsigned __int8)sub_4013E6(&v6, 10LL);
  if ( v3 )
    v4 = std::operator<<>(&std::cout, "WELL DONE!");
  else
    v4 = std::operator<<>(&std::cout, "wrong answer");
  std::ostream::operator<<(v4, &std::endl>);
  std::__cxx11::basic_string,std::allocator>::~basic_string(&v6);
  return 0LL;
}

主函数是比较简单的,通过动态调试,很容易发现sub_4069D6函数就是检查我们的输入是否为字符串形式的十六进制,并将其转换成十六进制字节流。而sub_4013E6函数则需要我们使用硬件断点分析来做动态分析。

硬件断点分析

对我们的输入下了硬件断点之后你会碰到下面三个硬件断点。

breakpoint1 :0x406521

.text:000000000040650F loc_40650F:                             ; DATA XREF: sub_4013E6+50E5o
.text:000000000040650F mov     [rbp+var_168], 0AEh
.text:000000000040651A mov     rax, [rbp+var_220]
.text:0000000000406521 movzx   eax, byte ptr [rax]
.text:0000000000406524 test    al, al
.text:0000000000406526 jz      short loc_406530
.text:0000000000406528 sub     [rbp+var_168], 4

这里对我们的输入进行了'\x00'判断

breakpoint2 :0x405FB5

.text:0000000000405FA3 loc_405FA3:                             ; DATA XREF: sub_4013E6+4BACo
.text:0000000000405FA3 mov     rax, [rbp+var_220]
.text:0000000000405FAA lea     rdx, [rax+1]
.text:0000000000405FAE mov     [rbp+var_220], rdx
.text:0000000000405FB5 movzx   edx, byte ptr [rax]
.text:0000000000405FB8 mov     rax, [rbp+var_210]
.text:0000000000405FBF movzx   eax, byte ptr [rax]
.text:0000000000405FC2 mov     ecx, eax
.text:0000000000405FC4 mov     eax, edx
.text:0000000000405FC6 sub     ecx, eax
.text:0000000000405FC8 mov     eax, ecx
.text:0000000000405FCA mov     edx, eax
.text:0000000000405FCC mov     rax, [rbp+var_210]
.text:0000000000405FD3 mov     [rax], dl
.text:0000000000405FD5 mov     rax, [rbp+var_280]
.text:0000000000405FDC test    rax, rax
.text:0000000000405FDF jnz     short loc_405FEC
.text:0000000000405FE1 mov     [rbp+var_280], 0A2h

这里将我们的输入的字符与[rbp+var_210]指针指向的内存的字符进行比较。并把结果覆盖掉[rbp+var_210]指针指向的内存中,所以对这块内存继续下硬件断点。

breakpoint3 :0x4061C1

.text:00000000004061AF loc_4061AF:                             ; DATA XREF: sub_4013E6+4D85o
.text:00000000004061AF mov     [rbp+var_178], 0A3h
.text:00000000004061BA mov     rax, [rbp+var_210]
.text:00000000004061C1 movzx   eax, byte ptr [rax]
.text:00000000004061C4 test    al, al
.text:00000000004061C6 jz      short loc_4061D0
.text:00000000004061C8 sub     [rbp+var_178], 2

这里把上面比较的结果拿出来在进行一个判断,应该是判断转移,在继续对该段代码分析发现,如果这里判断不成功,程序就会失败,所以猜测breakpoint2的判断是关键点。

断点分析

对上面三个断点仔细分析之后,发现breakpoint2的判断是关键点,所以我们只要在0x405FBF下断点,观察其值并记录下来,并把我们输入的字符(现在存储在rdx中),也改成这个将要进行判断的值。这样反复多次之后,我们便得到了完整的输入79406C61E5EEF319CECEE2ED8498

运行实例

ex@Ex:~/test$ ./obfuscating_macros.out 
79406C61E5EEF319CECEE2ED8498
WELL DONE!

总结

像这种需要动态分析的题目,要求我们解题者要有很好的耐心。