思路借鉴自大佬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+50E5↑o
.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+4BAC↑o
.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+4D85↑o
.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!
总结
像这种需要动态分析的题目,要求我们解题者要有很好的耐心。