前言

这里遇到了一个相关的CTF习题,看到其WP中的思路不错,特别学习一下

题目

其是picoCTFasm4,题目要求获取调用asm4("picoCTF_a3112")的结果,其中附件代码如下所示

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
asm4:
<+0>: push ebp
<+1>: mov ebp,esp
<+3>: push ebx
<+4>: sub esp,0x10
<+7>: mov DWORD PTR [ebp-0x10],0x246
<+14>: mov DWORD PTR [ebp-0xc],0x0
<+21>: jmp 0x518 <asm4+27>
<+23>: add DWORD PTR [ebp-0xc],0x1
<+27>: mov edx,DWORD PTR [ebp-0xc]
<+30>: mov eax,DWORD PTR [ebp+0x8]
<+33>: add eax,edx
<+35>: movzx eax,BYTE PTR [eax]
<+38>: test al,al
<+40>: jne 0x514 <asm4+23>
<+42>: mov DWORD PTR [ebp-0x8],0x1
<+49>: jmp 0x587 <asm4+138>
<+51>: mov edx,DWORD PTR [ebp-0x8]
<+54>: mov eax,DWORD PTR [ebp+0x8]
<+57>: add eax,edx
<+59>: movzx eax,BYTE PTR [eax]
<+62>: movsx edx,al
<+65>: mov eax,DWORD PTR [ebp-0x8]
<+68>: lea ecx,[eax-0x1]
<+71>: mov eax,DWORD PTR [ebp+0x8]
<+74>: add eax,ecx
<+76>: movzx eax,BYTE PTR [eax]
<+79>: movsx eax,al
<+82>: sub edx,eax
<+84>: mov eax,edx
<+86>: mov edx,eax
<+88>: mov eax,DWORD PTR [ebp-0x10]
<+91>: lea ebx,[edx+eax*1]
<+94>: mov eax,DWORD PTR [ebp-0x8]
<+97>: lea edx,[eax+0x1]
<+100>: mov eax,DWORD PTR [ebp+0x8]
<+103>: add eax,edx
<+105>: movzx eax,BYTE PTR [eax]
<+108>: movsx edx,al
<+111>: mov ecx,DWORD PTR [ebp-0x8]
<+114>: mov eax,DWORD PTR [ebp+0x8]
<+117>: add eax,ecx
<+119>: movzx eax,BYTE PTR [eax]
<+122>: movsx eax,al
<+125>: sub edx,eax
<+127>: mov eax,edx
<+129>: add eax,ebx
<+131>: mov DWORD PTR [ebp-0x10],eax
<+134>: add DWORD PTR [ebp-0x8],0x1
<+138>: mov eax,DWORD PTR [ebp-0xc]
<+141>: sub eax,0x1
<+144>: cmp DWORD PTR [ebp-0x8],eax
<+147>: jl 0x530 <asm4+51>
<+149>: mov eax,DWORD PTR [ebp-0x10]
<+152>: add esp,0x10
<+155>: pop ebx
<+156>: pop ebp
<+157>: ret

解题说明

可以看到,要理清这五十多行的汇编代码,还是比较麻烦的。因此理想的方法是直接执行asm4("picoCTF_a3112")

因此一个合理的思路就是在C语言中直接进行符号调用即可

调用函数

这个很简单,就是在C中调用该导出符号即可,如下所示

1
2
3
4
5
6
7
8
#include <stdio.h>

extern int asm4(char *str);

int main(void) {
printf("0x%x\n", asm4("picoCTF_a3112"));
return 0;
}

汇编代码修改

这明显是通过objdump反汇编出来的汇编代码,并且还附带这诸多的标记信息。
我们首先需要将其转换为符合格式的汇编代码,即执行如下几个步骤

  1. 去除代码字节序数
  2. 去除跳转注解
  3. 添加跳转标签,统一为asm4_字节序数
  4. 修改跳转地址为相对应的跳转标签

    最后整理完成的汇编代码如下所示

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    asm4:
    push ebp
    mov ebp,esp
    push ebx
    sub esp,0x10
    mov DWORD PTR [ebp-0x10],0x246
    mov DWORD PTR [ebp-0xc],0x0
    jmp asm4_27
    asm4_23:
    add DWORD PTR [ebp-0xc],0x1
    asm4_27:
    mov edx,DWORD PTR [ebp-0xc]
    mov eax,DWORD PTR [ebp+0x8]
    add eax,edx
    movzx eax,BYTE PTR [eax]
    test al,al
    jne asm4_23
    mov DWORD PTR [ebp-0x8],0x1
    jmp asm4_138
    asm4_51:
    mov edx,DWORD PTR [ebp-0x8]
    mov eax,DWORD PTR [ebp+0x8]
    add eax,edx
    movzx eax,BYTE PTR [eax]
    movsx edx,al
    mov eax,DWORD PTR [ebp-0x8]
    lea ecx,[eax-0x1]
    mov eax,DWORD PTR [ebp+0x8]
    add eax,ecx
    movzx eax,BYTE PTR [eax]
    movsx eax,al
    sub edx,eax
    mov eax,edx
    mov edx,eax
    mov eax,DWORD PTR [ebp-0x10]
    lea ebx,[edx+eax*1]
    mov eax,DWORD PTR [ebp-0x8]
    lea edx,[eax+0x1]
    mov eax,DWORD PTR [ebp+0x8]
    add eax,edx
    movzx eax,BYTE PTR [eax]
    movsx edx,al
    mov ecx,DWORD PTR [ebp-0x8]
    mov eax,DWORD PTR [ebp+0x8]
    add eax,ecx
    movzx eax,BYTE PTR [eax]
    movsx eax,al
    sub edx,eax
    mov eax,edx
    add eax,ebx
    mov DWORD PTR [ebp-0x10],eax
    add DWORD PTR [ebp-0x8],0x1
    asm4_138:
    mov eax,DWORD PTR [ebp-0xc]
    sub eax,0x1
    cmp DWORD PTR [ebp-0x8],eax
    jl asm4_51
    mov eax,DWORD PTR [ebp-0x10]
    add esp,0x10
    pop ebx
    pop ebp
    ret

生成可执行文件

其基本思路很简单——就是将两个部分皆编译、汇编成目标文件,然后进行链接即可,从而生成正常的可执行文件。

对于C部分,其生成很简单;关键问题在于给定的汇编文件生成目标文件。如果此时直接执行gcc asm4.S,则程序会给出编译错误等信息。

其原因也很简单:虽然这是一份正确的汇编代码,但是其使用Intel格式书写;而一般编译器默认使用AT&T格式进行编译和汇编,从而导致无法正常生成目标文件;除此之外,该汇编代码部分还需要声明其符号,方便进行调用。

因此修改过的汇编代码如下所示

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
.intel_syntax noprefix
.global asm4

asm4:
push ebp
mov ebp,esp
push ebx
sub esp,0x10
mov DWORD PTR [ebp-0x10],0x246
mov DWORD PTR [ebp-0xc],0x0
jmp 0x518 <asm4+27>
asm4_23:
add DWORD PTR [ebp-0xc],0x1
asm4_27:
mov edx,DWORD PTR [ebp-0xc]
mov eax,DWORD PTR [ebp+0x8]
add eax,edx
movzx eax,BYTE PTR [eax]
test al,al
jne asm4_23
mov DWORD PTR [ebp-0x8],0x1
jmp asm4_138
asm4_51:
mov edx,DWORD PTR [ebp-0x8]
mov eax,DWORD PTR [ebp+0x8]
add eax,edx
movzx eax,BYTE PTR [eax]
movsx edx,al
mov eax,DWORD PTR [ebp-0x8]
lea ecx,[eax-0x1]
mov eax,DWORD PTR [ebp+0x8]
add eax,ecx
movzx eax,BYTE PTR [eax]
movsx eax,al
sub edx,eax
mov eax,edx
mov edx,eax
mov eax,DWORD PTR [ebp-0x10]
lea ebx,[edx+eax*1]
mov eax,DWORD PTR [ebp-0x8]
lea edx,[eax+0x1]
mov eax,DWORD PTR [ebp+0x8]
add eax,edx
movzx eax,BYTE PTR [eax]
movsx edx,al
mov ecx,DWORD PTR [ebp-0x8]
mov eax,DWORD PTR [ebp+0x8]
add eax,ecx
movzx eax,BYTE PTR [eax]
movsx eax,al
sub edx,eax
mov eax,edx
add eax,ebx
mov DWORD PTR [ebp-0x10],eax
add DWORD PTR [ebp-0x8],0x1
asm4_138:
mov eax,DWORD PTR [ebp-0xc]
sub eax,0x1
cmp DWORD PTR [ebp-0x8],eax
jl asm4_51
mov eax,DWORD PTR [ebp-0x10]
add esp,0x10
pop ebx
pop ebp
ret

通过添加上面两个注解,方便编译器等进行编译和链接。

其最后编译和链接时,仍需要传递相关的参数,表明采用Intel格式的汇编代码,如下所示

1
2
gcc -m32 -c -masm=intel asm4.S
gcc -m32 asm4.S exp.c

最后执行,结果如下所示

1
2
$ ./a.out
0x1d0