1.查看文件类型:
(终于学会了贴图:)
发现是ELF格式的可执行文件。所以心中做好了一会儿要转战linux环境的心态。
2.IDA静态分析
放到ida中进行静态分析,查看main函数的反汇编代码:
通过简单地探索,发现除了authenticate()函数外,其他函数对自己没啥用。
所以直接进authenticate()函数进行分析。
可以看到注释我已经在代码段上标注了。
那么接下来就需要分析这个函数要做什么了:
这个函数大体意思就是将输入的字符串数据ws和存在于函数内部的字符串数据s2进行比对,然后再进行下一步操作。、
通过分析可知s2保存的就是flag。(猜也行)
分析s2就需要分析decrypt()函数。
那么decrypt()函数是这样的:
这个函数最后将返回的dest保存到s2中,而dest的值又需要参数s和a2来决定的。
我们暂时不分析while的算法,先回头看看参数s和a2到底是什么。
s和dword_8048A90都是函数中未定义的,所以点开后查看一下参数保存的是什么。
点开s和dword_8048A90发现字符串并不简单,存放在“.rodata(只读代码段)”很长而且没有规律。可以想象一下,参数本身就很复杂,那么参数传递给函数进行处理的过程那么就更加复杂。所以通过参数和对参数的算法操作来获得flag其实是很困难的。
不过我们可以不用像个铁憨憨去分析decrypt(),因为参数和算法都是已经定义好的跟输入没关系,那么函数调用完成后返回的dest会保存在哪里,我们直接找到程序运行时保存dest的地址然后dump出来分析不就ok了?这样就不用做头铁娃了!
带着这个快乐的想法,我们就直接去看看decrypt()的汇编代码:
[Asm] 纯文本查看 复制代码 text:08048658 ; int __cdecl decrypt(wchar_t *s, wchar_t *)
.text:08048658 public decrypt
.text:08048658 decrypt proc near ; CODE XREF: authenticate+18↓p
.text:08048658
.text:08048658 var_1C = dword ptr -1Ch
.text:08048658 var_18 = dword ptr -18h
.text:08048658 var_14 = dword ptr -14h
.text:08048658 var_10 = dword ptr -10h
.text:08048658 dest = dword ptr -0Ch
.text:08048658 s = dword ptr 8
.text:08048658 arg_4 = dword ptr 0Ch
.text:08048658
.text:08048658 ; __unwind {
.text:08048658 push ebp
.text:08048659 mov ebp, esp
.text:0804865B push ebx
.text:0804865C sub esp, 34h
.text:0804865F mov eax, [ebp+s]
.text:08048662 mov [esp], eax ; s
.text:08048665 call _wcslen
.text:0804866A mov [ebp+var_14], eax
.text:0804866D mov eax, [ebp+arg_4]
.text:08048670 mov [esp], eax ; s
.text:08048673 call _wcslen
.text:08048678 mov [ebp+var_10], eax
.text:0804867B mov ebx, [ebp+s]
.text:0804867E mov eax, [ebp+s]
.text:08048681 mov [esp], eax ; s
.text:08048684 call _wcslen
.text:08048689 add eax, 1
.text:0804868C mov [esp], eax ; size
.text:0804868F call _malloc
.text:08048694 mov [ebp+dest], eax
.text:08048697 mov [esp+4], ebx ; src
.text:0804869B mov eax, [ebp+dest] //从这里可以发现dest保存的值传递给了寄存器eax
.text:0804869E mov [esp], eax ; dest
.text:080486A1 call _wcscpy
.text:080486A6 mov [ebp+var_18], 0
.text:080486AD jmp short loc_80486F7
.text:080486AF ; --------------------------------------------------------------------------
通过汇编代码,了解到寄存器eax保存dest的值后返回给s2。
我们刚刚头大的事情仿佛迎刃而解,大体得到flag的方法就是,直接动态调试程序,然后在decrypt()处下断点单步执行到保存dest的值于eax代码后,查看eax保存的值就可以了。
而我们前面也分析了,此文件是ELF格式,所以我们要暂时抛弃微软,进入linux分析。
4.gdb调试
自己百度gdb命令及用途,不用都记住,记住几个本次需要执行用得到的命令就行了,以后用到再说。
而我们需要做什么呢:
1.在decrypt()处下断点。
2.单步执行decrypt()函数。
3.执行完毕后查看寄存器eax保存的值。
实现此想法需要执行的代码:
1.在decrypt()处下断点。
[Asm] 纯文本查看 复制代码 b decrypt
2.单步执行decrypt()(断点处)函数。
3.执行一行源程序代码,此行代码中的函数调用也一并执行,就是执行decrypt()函数。
4.此时decrypt()执行完毕,然后查看寄存器保存的内容即可。
5.查看内存地址(eax)保存的内容。
[Asm] 纯文本查看 复制代码 x/5sw $eax
----------------------------------------------------------------------
第五条指令中各个参数含义:
x:examine–>检测内存地址中保存的值
5:显示5行目标数据
s:以字符串形式打印
w:以双字打印
---------------------------------------------------------------------------
然后就会打印出:
[Asm] 纯文本查看 复制代码 0x8ca9800: U"9447{you_are_an_international_mystery}"
0x8ca989c: U"W\001\xf7a161e8\xf7a161ea\xf7a161ec\xf7a161ee\xf7a161f0\xf7a161f2\xf7a161f4\xf7a161f6\xf7a161f8\xf7a161fa\001\xf7a16200\xf7a16204\xf7a16208\xf7a1620c\xf7a16210\xf7a16214\xf7a16218\xf7a1621c\xf7a16220\xf7a16224\xf7a16228\xf7a1622a\xf7a1622c\xf7a1622e\xf7a16230\xf7a16232\xf7a16234\xf7a16236\xf7a16238\xf7a1623a\060\061\062\063\064\065\066\067\070\071\x175a\xf7a16268\xf7a1bfd0\xf7a27aa0\xf7a2d808\001\xf7a3ff4c"
0x8ca9968: U"\xf7a3ff54"
0x8ca9970: U""
0x8ca9974: U"\xf7a3ff7c\xf7a408b8\xf7a41234\xf7a42a74\xf7a42cec\xf7a42f64\xf7a4329c\xf7a45194\xf7a4708c\xf7a473c4\xf7a4767c\xf7a48f74\xf7a4a7b4\xf7a4b8e4\xf7a4c854\xf7a4cb6c\xf7a52988\xf7a57ba4\021\x435f687a\x54552e4e\x382d46\021\x435f687a\x54552e4e\x382d46!\x8ca9a00\001\x8ca9a80\001"
自己看看哪个是flag吧。:)
个人小结:
- 实践很重要
- “.rodata(只读数据read only data)”段存放的是只读数据,比如字符串常量、全局const变量。
- 多思考,少头铁。
- 写writeup的时候把做题时发现问题解决问题的思路先写一下,准备好注释和截图,按照思路一步步写听起来还是挺有逻辑的。
- 实践相当重要!
|