Hello World, binary
0x00
自从不久之前决定专职称为 binary 选手之后,便开始学习汇编。大体读了一下王爽的《汇编语言》,之后操刀,让锐锐菊苣出了一道逆向题。做了很久,刚开始去做总是很容易踩到坑。不过题目是很简单的,主要是练习一下如何去熟悉 gdb 的用法以及操作。
以上是题外话。binary 娘养成系列对我来说虽然是一块大石头,但是对于菊苣们来说肯定很简单,所以还望谅解,如有错误还请不吝赐教。
0x01
这里我用到的工具是 gdb,配上 peda。
首先拿到锐锐老师的题目,gdb FuckRuirui
,进入 gdb 进行调试。
先看一下 main 函数的代码,利用 disassem main
:
....
0x080485e9 <+21>: mov DWORD PTR [esp+0x28],0x0
0x080485f1 <+29>: mov DWORD PTR [esp+0x2c],0x0
0x080485f9 <+37>: mov DWORD PTR [esp+0x30],0x0
0x08048601 <+45>: mov DWORD PTR [esp+0x34],0x0
0x08048609 <+53>: mov DWORD PTR [esp+0x38],0x0
0x08048611 <+61>: mov DWORD PTR [esp],0x8048749
0x08048618 <+68>: call 0x80483a0 <puts@plt>
0x0804861d <+73>: mov DWORD PTR [esp],0x804875d
0x08048624 <+80>: call 0x80483a0 <puts@plt>
0x08048629 <+85>: mov BYTE PTR [esp+0x24],0x0
0x0804862e <+90>: lea eax,[esp+0x27]
0x08048632 <+94>: mov DWORD PTR [esp+0x10],eax
0x08048636 <+98>: lea eax,[esp+0x26]
0x0804863a <+102>: mov DWORD PTR [esp+0xc],eax
0x0804863e <+106>: lea eax,[esp+0x25]
0x08048642 <+110>: mov DWORD PTR [esp+0x8],eax
0x08048646 <+114>: lea eax,[esp+0x24]
0x0804864a <+118>: mov DWORD PTR [esp+0x4],eax
0x0804864e <+122>: mov DWORD PTR [esp],0x8048778
0x08048655 <+129>: call 0x80483e0 <__isoc99_scanf@plt>
0x0804865a <+134>: movzx eax,BYTE PTR [esp+0x24]
0x0804865f <+139>: mov BYTE PTR [esp+0x28],al
0x08048663 <+143>: movzx eax,BYTE PTR [esp+0x25]
0x08048668 <+148>: mov BYTE PTR [esp+0x29],al
0x0804866c <+152>: movzx eax,BYTE PTR [esp+0x26]
0x08048671 <+157>: mov BYTE PTR [esp+0x2a],al
0x08048675 <+161>: movzx eax,BYTE PTR [esp+0x27]
0x0804867a <+166>: mov BYTE PTR [esp+0x2b],al
0x0804867e <+170>: lea eax,[esp+0x28]
0x08048682 <+174>: mov DWORD PTR [esp],eax
0x08048685 <+177>: call 0x80484ed <here> // 关键函数
....
题外话:锐锐老师还讲了一种得到汇编代码的方法,x/10i main
。
在 main 函数下一个断点(b main
),然后 r
运行,n
单步调试,跟着程序流程走一下。发现程序是获取 4 个字符,然后进入 here 函数进行比较,如果条件满足就输出 flag,不满足就输出错误信息然后退出。
接着 disassem here
:
....
0x0804852e <+65>: mov eax,DWORD PTR [ebp-0x2c]
0x08048531 <+68>: movzx eax,BYTE PTR [eax]
0x08048534 <+71>: cmp al,0xbb // 第一个字符为 0xbb
0x08048536 <+73>: je 0x8048546 <here+89>
0x08048538 <+75>: mov DWORD PTR [esp],0x8048740
0x0804853f <+82>: call 0x80483a0 <puts@plt>
0x08048544 <+87>: jmp 0x80485c1 <here+212>
0x08048546 <+89>: mov eax,DWORD PTR [ebp-0x2c]
0x08048549 <+92>: add eax,0x1
0x0804854c <+95>: movzx eax,BYTE PTR [eax]
0x0804854f <+98>: movsx edx,al
0x08048552 <+101>: mov eax,DWORD PTR [ebp-0x2c]
0x08048555 <+104>: add eax,0x2
0x08048558 <+107>: movzx eax,BYTE PTR [eax]
0x0804855b <+110>: movsx eax,al
0x0804855e <+113>: add edx,eax
0x08048560 <+115>: mov eax,DWORD PTR [ebp-0x2c]
0x08048563 <+118>: add eax,0x3
0x08048566 <+121>: movzx eax,BYTE PTR [eax]
0x08048569 <+124>: movsx eax,al
0x0804856c <+127>: add eax,edx
0x0804856e <+129>: cmp eax,0x50 // 第2、3、4个字符加起来为 0x50
0x08048571 <+132>: je 0x8048581 <here+148>
0x08048573 <+134>: mov DWORD PTR [esp],0x8048740
0x0804857a <+141>: call 0x80483a0 <puts@plt> // 输出错误信息
0x0804857f <+146>: jmp 0x80485c1 <here+212>
0x08048581 <+148>: mov DWORD PTR [ebp-0x24],0x0
0x08048588 <+155>: jmp 0x80485af <here+194>
0x0804858a <+157>: lea edx,[ebp-0x20] // 解密得到 flag
0x0804858d <+160>: mov eax,DWORD PTR [ebp-0x24]
0x08048590 <+163>: add eax,edx
0x08048592 <+165>: movzx edx,BYTE PTR [eax]
0x08048595 <+168>: mov eax,DWORD PTR [ebp-0x2c]
0x08048598 <+171>: movzx eax,BYTE PTR [eax]
0x0804859b <+174>: xor eax,0x52
0x0804859e <+177>: xor eax,edx
0x080485a0 <+179>: movsx eax,al
0x080485a3 <+182>: mov DWORD PTR [esp],eax
0x080485a6 <+185>: call 0x80483d0 <putchar@plt>
0x080485ab <+190>: add DWORD PTR [ebp-0x24],0x1
0x080485af <+194>: cmp DWORD PTR [ebp-0x24],0x12
0x080485b3 <+198>: jle 0x804858a <here+157>
....
逻辑比较清楚。首先程序获取输入的第一个字符,为 0xbb,接着获取第二个字符,存入 edx,然后获取第三个字符,相加:add edx,eax
,接着获取第四个字符,再相加存入 eax:add eax,edx
,最后比较:cmp eax,0x50
。
于是我们可以构造输入:\xbb\x20\x20\x10
。
➜ ~ python -c "print '\xbb\x20\x20\x10'" | ./FuckRuirui
come on and fuck me
maybe you can not fuck me
WHAT_A_SMART_RICTER
➜ ~
0x02
实际上,很难通过读汇编然后做出逆向(对于我这种渣渣来讲),单步调试也许是一个更好的选择。因为我不知道如何在 gdb 中输入不可见字符,所以锐锐老师教我利用 nc 监听端口,然后重定向到 FuckRuirui,接着利用 gdb attach 上去。
nc -lvvp 4444 -e FuckRuirui
注意这里的 nc 是 netcat-traditional 包里的。
接着利用 python socket 连接上去。
>>> sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
>>> sock.connect(('localhost', 4444))
>>>
ps -a
得到 pid,然后 gdb attach
。
➜ ~ ps -a
PID TTY TIME CMD
8576 pts/1 00:00:00 python
8957 pts/3 00:00:00 FuckRuirui
8963 pts/0 00:00:00 ps
➜ ~ gdb attach 8957
不过在这里我卡了很久,在 main 函数下断点一直失败,可能是因为我脸太黑。不过把断点直接下载 here 函数就好了,因为我们主要是跟 here 函数。
下好断点后,c 继续执行,然后 python 发送数据:sock.send('aaaa\n')
。
gdb-peda$ b here
Breakpoint 1 at 0x80484f3
gdb-peda$ c
[------------------------------------------------registers-------------------------------------------------]
EAX: 0xbfa81a88 --> 0x102020bb
EBX: 0xb7706ff4 --> 0x1a6d7c
ECX: 0x4
EDX: 0xb77088c4 --> 0x0
ESI: 0x0
EDI: 0x0
EBP: 0xbfa81a58 --> 0xbfa81aa8 --> 0x0
ESP: 0xbfa81a20 --> 0xb7707ac0 --> 0xfbad2088
EIP: 0x80484f3 (<here+6>: mov eax,DWORD PTR [ebp+0x8])
[---------------------------------------------------code---------------------------------------------------]
0x80484ed <here>: push ebp
0x80484ee <here+1>: mov ebp,esp
0x80484f0 <here+3>: sub esp,0x38
=> 0x80484f3 <here+6>: mov eax,DWORD PTR [ebp+0x8]
0x80484f6 <here+9>: mov DWORD PTR [ebp-0x2c],eax
0x80484f9 <here+12>: mov eax,gs:0x14
0x80484ff <here+18>: mov DWORD PTR [ebp-0xc],eax
0x8048502 <here+21>: xor eax,eax
[--------------------------------------------------stack---------------------------------------------------]
00:0000| esp 0xbfa81a20 --> 0xb7707ac0 --> 0xfbad2088
01:0004| 0xbfa81a24 --> 0xb77088c4 --> 0x0
02:0008| 0xbfa81a28 --> 0xb755f900 (0xb755f900)
03:0012| 0xbfa81a2c --> 0xb75b66eb (<__isoc99_scanf+139>: mov ecx,eax)
04:0016| 0xbfa81a30 --> 0xb7707ac0 --> 0xfbad2088
05:0020| 0xbfa81a34 --> 0x8048778 ("%c%c%c%c")
06:0024| 0xbfa81a38 --> 0xbfa81a64 --> 0xbfa81a84 --> 0x102020bb
07:0028| 0xbfa81a3c --> 0x0
[----------------------------------------------------------------------------------------------------------]
Legend: stack, code, data, heap, rodata, value
Breakpoint 1, 0x080484f3 in here ()
gdb-peda$
接着 n 下去,修改 send 的数据,直到满足条件,得到 flag。
>>> print sock.recv(1024)
come on and fuck me
maybe you can not fuck me
WHAT_A_SMART_RICTER
>>>
PS:每次失败后都要重新 nc 一次,好累QAQ。
0x04
好想成为 binary 菊苣_(:3」∠)_说一下我为什么想转成 binary 选手。
之前一段时间在刷 wechall,当 wechall 的 PHP 题目刷到七七八八的时候,遇到了一个利用 unserialize 来进行获取 flag 的题目。这个虽然有具体的利用,但是不知道为什么脱离了 gdb 就会失败..
想了想,web 最终还是会走向 bin,国内的圈子都是以 web 为主,浮躁而无趣,学习一下 bin 也无妨。
嘛,就这样。