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 也无妨。
嘛,就这样。