# 6.2.8 re DefcampCTF2015 entry\_language

* [题目解析](#题目解析)
* [参考资料](#参考资料)

[下载文件](https://github.com/firmianay/CTF-All-In-One/blob/master/src/writeup/6.2.8_re_defcampctf2015_entry_language)

## 题目解析

这是一题标准的密码验证题，输入一个字符串，程序验证对误。

```
$ file entry_language
defcamp_r100: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 2.6.24, BuildID[sha1]=0f464824cc8ee321ef9a80a799c70b1b6aec8168, stripped
```

```
$ ./entry_language
Enter the password: ABCD
Incorrect password!
```

为了与 angr 的自动化做对比，我们先使用传统的方法，逆向算法求解，`main` 函数和验证函数 `fcn.004006fd` 如下：

```
[0x00400610]> pdf @ main
/ (fcn) main 153
|   main ();
|           ; var int local_110h @ rbp-0x110
|           ; var int local_8h @ rbp-0x8
|              ; DATA XREF from 0x0040062d (entry0)
|           0x004007e8      55             push rbp
|           0x004007e9      4889e5         mov rbp, rsp
|           0x004007ec      4881ec100100.  sub rsp, 0x110
|           0x004007f3      64488b042528.  mov rax, qword fs:[0x28]    ; [0x28:8]=-1 ; '(' ; 40
|           0x004007fc      488945f8       mov qword [local_8h], rax
|           0x00400800      31c0           xor eax, eax
|           0x00400802      bf37094000     mov edi, str.Enter_the_password: ; 0x400937 ; "Enter the password: "
|           0x00400807      b800000000     mov eax, 0
|           0x0040080c      e8affdffff     call sym.imp.printf         ; int printf(const char *format)
|           0x00400811      488b15500820.  mov rdx, qword [obj.stdin]  ; [0x601068:8]=0
|           0x00400818      488d85f0feff.  lea rax, [local_110h]
|           0x0040081f      beff000000     mov esi, 0xff               ; 255
|           0x00400824      4889c7         mov rdi, rax
|           0x00400827      e8b4fdffff     call sym.imp.fgets          ; char *fgets(char *s, int size, FILE *stream)
|           0x0040082c      4885c0         test rax, rax
|       ,=< 0x0040082f      7435           je 0x400866
|       |   0x00400831      488d85f0feff.  lea rax, [local_110h]
|       |   0x00400838      4889c7         mov rdi, rax
|       |   0x0040083b      e8bdfeffff     call fcn.004006fd            ; 调用验证函数
|       |   0x00400840      85c0           test eax, eax
|      ,==< 0x00400842      7511           jne 0x400855
|      ||   0x00400844      bf4c094000     mov edi, str.Nice_          ; 0x40094c ; "Nice!"
|      ||   0x00400849      e852fdffff     call sym.imp.puts           ; int puts(const char *s)
|      ||   0x0040084e      b800000000     mov eax, 0
|     ,===< 0x00400853      eb16           jmp 0x40086b
|     |||      ; JMP XREF from 0x00400842 (main)
|     |`--> 0x00400855      bf52094000     mov edi, str.Incorrect_password_ ; 0x400952 ; "Incorrect password!"
|     | |   0x0040085a      e841fdffff     call sym.imp.puts           ; int puts(const char *s)
|     | |   0x0040085f      b801000000     mov eax, 1
|     |,==< 0x00400864      eb05           jmp 0x40086b
|     |||      ; JMP XREF from 0x0040082f (main)
|     ||`-> 0x00400866      b800000000     mov eax, 0
|     ||       ; JMP XREF from 0x00400864 (main)
|     ||       ; JMP XREF from 0x00400853 (main)
|     ``--> 0x0040086b      488b4df8       mov rcx, qword [local_8h]
|           0x0040086f      6448330c2528.  xor rcx, qword fs:[0x28]
|       ,=< 0x00400878      7405           je 0x40087f
|       |   0x0040087a      e831fdffff     call sym.imp.__stack_chk_fail ; void __stack_chk_fail(void)
|       |      ; JMP XREF from 0x00400878 (main)
|       `-> 0x0040087f      c9             leave
\           0x00400880      c3             ret
[0x00400610]> pdf @ fcn.004006fd
/ (fcn) fcn.004006fd 171
|   fcn.004006fd (int arg_bh);
|           ; var int local_38h @ rbp-0x38
|           ; var int local_24h @ rbp-0x24
|           ; var int local_20h @ rbp-0x20
|           ; var int local_18h @ rbp-0x18
|           ; var int local_10h @ rbp-0x10
|           ; arg int arg_bh @ rbp+0xb
|              ; CALL XREF from 0x0040083b (main)
|           0x004006fd      55             push rbp
|           0x004006fe      4889e5         mov rbp, rsp
|           0x00400701      48897dc8       mov qword [local_38h], rdi
|           0x00400705      c745dc000000.  mov dword [local_24h], 0
|           0x0040070c      48c745e01409.  mov qword [local_20h], str.Dufhbmf ; 0x400914 ; "Dufhbmf"
|           0x00400714      48c745e81c09.  mov qword [local_18h], str.pG_imos ; 0x40091c ; "pG`imos"
|           0x0040071c      48c745f02409.  mov qword [local_10h], str.ewUglpt ; 0x400924 ; "ewUglpt"
|           0x00400724      c745dc000000.  mov dword [local_24h], 0
|       ,=< 0x0040072b      eb6e           jmp 0x40079b
|       |      ; JMP XREF from 0x0040079f (fcn.004006fd)
|      .--> 0x0040072d      8b4ddc         mov ecx, dword [local_24h]
|      :|   0x00400730      ba56555555     mov edx, 0x55555556
|      :|   0x00400735      89c8           mov eax, ecx
|      :|   0x00400737      f7ea           imul edx
|      :|   0x00400739      89c8           mov eax, ecx
|      :|   0x0040073b      c1f81f         sar eax, 0x1f
|      :|   0x0040073e      29c2           sub edx, eax
|      :|   0x00400740      89d0           mov eax, edx
|      :|   0x00400742      01c0           add eax, eax
|      :|   0x00400744      01d0           add eax, edx
|      :|   0x00400746      29c1           sub ecx, eax
|      :|   0x00400748      89ca           mov edx, ecx
|      :|   0x0040074a      4863c2         movsxd rax, edx
|      :|   0x0040074d      488b74c5e0     mov rsi, qword [rbp + rax*8 - 0x20]
|      :|   0x00400752      8b4ddc         mov ecx, dword [local_24h]
|      :|   0x00400755      ba56555555     mov edx, 0x55555556
|      :|   0x0040075a      89c8           mov eax, ecx
|      :|   0x0040075c      f7ea           imul edx
|      :|   0x0040075e      89c8           mov eax, ecx
|      :|   0x00400760      c1f81f         sar eax, 0x1f
|      :|   0x00400763      29c2           sub edx, eax
|      :|   0x00400765      89d0           mov eax, edx
|      :|   0x00400767      01c0           add eax, eax
|      :|   0x00400769      4898           cdqe
|      :|   0x0040076b      4801f0         add rax, rsi                ; '+'
|      :|   0x0040076e      0fb600         movzx eax, byte [rax]
|      :|   0x00400771      0fbed0         movsx edx, al
|      :|   0x00400774      8b45dc         mov eax, dword [local_24h]
|      :|   0x00400777      4863c8         movsxd rcx, eax
|      :|   0x0040077a      488b45c8       mov rax, qword [local_38h]
|      :|   0x0040077e      4801c8         add rax, rcx                ; '&'
|      :|   0x00400781      0fb600         movzx eax, byte [rax]
|      :|   0x00400784      0fbec0         movsx eax, al
|      :|   0x00400787      29c2           sub edx, eax
|      :|   0x00400789      89d0           mov eax, edx
|      :|   0x0040078b      83f801         cmp eax, 1                  ; 1
|     ,===< 0x0040078e      7407           je 0x400797                  ; = 1 时跳转，验证成功
|     |:|   0x00400790      b801000000     mov eax, 1                   ; 返回 1，验证失败
|    ,====< 0x00400795      eb0f           jmp 0x4007a6
|    ||:|      ; JMP XREF from 0x0040078e (fcn.004006fd)
|    |`---> 0x00400797      8345dc01       add dword [local_24h], 1     ; i = i + 1
|    | :|      ; JMP XREF from 0x0040072b (fcn.004006fd)
|    | :`-> 0x0040079b      837ddc0b       cmp dword [local_24h], 0xb  ; [0xb:4]=-1 ; 11
|    | `==< 0x0040079f      7e8c           jle 0x40072d                 ; i <= 11 时跳转
|    |      0x004007a1      b800000000     mov eax, 0                   ; 返回 0
|    |         ; JMP XREF from 0x00400795 (fcn.004006fd)
|    `----> 0x004007a6      5d             pop rbp
\           0x004007a7      c3             ret
```

整理后可以得到下面的伪代码：

```
int fcn_004006fd(int *passwd) {
    char *str_1 = "Dufhbmf";
    char *str_2 = "pG`imos";
    char *str_3 = "ewUglpt";
    for (int i = 0; i <= 11; i++) {
        if((&str_3)[i % 3][2 * (1 / 3)] - *(i + passwd) != 1) {
            return 1;
        }
    }
    return 0;
}
```

然后写出逆向脚本：

```python
str_list = ["Dufhbmf", "pG`imos", "ewUglpt"]
passwd = []
for i in range(12):
    passwd.append(chr(ord(str_list[i % 3][2 * (i / 3)]) - 1))
print ''.join(passwd)
```

逆向算法似乎也很简单，但如果连算法都不用逆的话，下面就是见证 angr 魔力的时刻，我们只需要指定让程序运行到 `0x400844`，即验证通过时的位置，而不用管验证的逻辑是怎么样的。

完整的脚本如下：

```python
import angr

project = angr.Project("entry_language", auto_load_libs=False)

@project.hook(0x400844)
def print_flag(state):
    print "FLAG SHOULD BE:", state.posix.dump_fd(0)
    project.terminate_execution()

project.execute()
```

Bingo!!!

```
$ python2 solve_angr.py
FLAG SHOULD BE: Code_Talkers
$ ./entry_language
Enter the password: Code_Talkers
Nice!
```

## 参考资料

* [https://ctftime.org/task/1691](https://ctftime.org/task/1691)
