首页
社区
课程
招聘
[原创]由两道CTF汇编题目来学习ARM和X86汇编
2023-10-17 20:06 10238

[原创]由两道CTF汇编题目来学习ARM和X86汇编

2023-10-17 20:06
10238

这两道题目是我朋友发给我的,题目比较简单,但是考察了二进制汇编基础。
个人觉得基础很重要,所以花了些时间来认真看这道题目,巩固自己的基础。
如果是在线下,没有GPT的帮助,此时就需要选手汇编底子,对题目进行细致的分析。
如果学习汇编有题目带着学,耐心分析,相信收获会很大的,有助于理解汇编操作的逻辑和方法。
开始今天的题目讲解,题目会打包上传
如果有错误,还请指出哈哈

1、x86汇编

原题目我就不粘出来了哈,附件里面有的,这里粘一下我注释分析后的
题目给的是AT&T风格的x86汇编,那么和我们常见的intel语法有什么不同呢?

  1. 操作数顺序不同:在AT&T语法中,操作数的顺序是“源,目的”,而在Intel语法中是“目的,源”。例如,在AT&T语法中,movl %eax, %ebx表示将eax寄存器中的值移动到ebx寄存器中,而在Intel语法中,相应的指令为mov ebx, eax
  2. 寄存器名称不同:在AT&T语法中,寄存器名称以%开头,而在Intel语法中没有前缀。例如,在AT&T语法中,%eax表示EAX寄存器,而在Intel语法中,相应的寄存器名称为EAX
  3. 立即数和内存地址表示不同:在AT&T语法中,立即数使用$前缀,而内存地址使用方括号[]包围。例如,movl $0x123, %eax表示将立即数0x123移动到eax寄存器中,而movl (%ebx), %eax表示将ebx寄存器指向的内存地址中的数据移动到eax寄存器中。
  4. 符号扩展不同:在AT&T语法中,符号扩展是默认进行的,而在Intel语法中需要使用movsxmovzx指令来进行。例如,在AT&T语法中,movb -1(%eax), %bl表示将eax-1地址处的字节符号扩展后移动到bl寄存器中,而在Intel语法中,相应的指令应为movsx bl, byte ptr [eax-1]

(建议自己先看一下原题目,手动分析一下,分析完尝试自己解密,看是否能得到正确的flag)
有了上面的基础之后,开始看题目:

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
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
```@定义了一些辅助信息,比如:文件名,段名称
.file   "main.c"
    .text
    .section    .rodata
    .align 32
    .type   encode, @object
    .size   encode, 39
encode:
    .string "**************************************"
    .text
    .globl  main               @将main标记为全局可见的。
    .type   main, @function    @声明main是一个函数。
main:
.LFB0:
    .cfi_startproc  @CFI (Call Frame Information)指令,用于生成调试信息。
    endbr64         @对应特定的处理器指令,提供分支目标地址的溢出检测。
    pushq   %rbp
    .cfi_def_cfa_offset 16
    .cfi_offset 6, -16
    movq    %rsp, %rbp
    .cfi_def_cfa_register 6
    subq    $16, %rsp     @rsp=rsp-16
    @堆栈操作指令,用于保存和恢复寄存器的值。 上面除去CFI指令后是经典的开辟栈空间指令
    movl    $0, -4(%rbp)  @ [rbp-4]=0
    jmp .L2               @无条件跳转到L2处开始执行
 
@L5 由下文的分析可知道,这是个循环
.L5:
    addl    $1, -4(%rbp)  @[rbp-4]++
.L2:
    movl    -4(%rbp), %eax      @eax=[rbp-4]
    cltq                        @将eax寄存器的低32位符号扩展为64位,结果存储在rax寄存器中(在x86-64中,eax是低32位的rax)。
    leaq    encode(%rip), %rdx  @rdx=encode[rip]  就是将flag传给rdx
    movzbl  (%rax,%rdx), %eax   @从以rax+rdx为基址的内存地址中读取一个字节,并将其零扩展为32位。然后将结果存储在eax寄存器中
    movsbl  %al, %ecx           @ ECX = EAX
    movl    -4(%rbp), %eax      @ eax = [rbp-4]
    andl    $1, %eax            @ 判断奇偶性
    testl   %eax, %eax
    je  .L3                     @ 若为偶数,则跳转到L3      
    movl    -4(%rbp), %eax      @ eax=[rbp-4]
    subl    $1, %eax            @ eax=eax-1
    cltq                      
    leaq    encode(%rip), %rdx  @rdx = encode[rip]
    movzbl  (%rax,%rdx), %eax   @eax = [rax+rdx] rax相当于下标i
    movsbl  %al, %eax   
    jmp .L4
.L3:
    movl    -4(%rbp), %eax   @eax=[rbp-4]
.L4:
    xorl    %ecx, %eax    @eax = eax ^ ecx
    movl    %eax, %edi    @edi = eax
    call    putchar@PLT   @putchar
    movl    -4(%rbp), %eax   @eax=[rbp-4]
    cltq                   
    cmpq    $37, %rax        @ cmp(37,rax)
    jbe .L5             @rax<37则跳转到L5 ,继续循环
    movl    $0, %eax    @eax =0
    leave
    .cfi_def_cfa 7, 8
    ret
    .cfi_endproc
 
    @一些调试信息
.LFE0:
    .size   main, .-main
    .ident  "GCC: (Ubuntu 11.4.0-1ubuntu1~22.04) 11.4.0"
    .section    .note.GNU-stack,"",@progbits
    .section    .note.gnu.property,"a"
    .align 8
    .long   1f - 0f
    .long   4f - 1f
    .long   5
0:
    .string "GNU"
1:
    .align 8
    .long   0xc0000002
    .long   3f - 2f
2:
    .long   0x3
3:
    .align 8
4:

这题有点绕的地方是[rbp-4]、eax和ecx之间的关系
这里给出完整的解题wp,crypto密文呢是题目附件给出了

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
 
 
 
int main() {
    char crypto[] = { 0x66,0x0a,0x63,0x06,0x7f,0x1e,0x37,0x00,0x38,0x03,0x6f,0x04,0x6e,0x56,0x3d,0x55,0x22,0x06,0x26,0x51,0x72,0x04,0x21,0x03,0x21,0x01,0x7c,0x05,0x2b,0x0e,0x7c,0x50,0x17,0x56,0x10,0x0b,0x16,0x4f,0x26 };
    for (size_t i = 0; i < 39; i++)
    {
        if (i % 2 == 0)//偶数
        {
            printf("%c", crypto[i] ^i);
        }
        else {
            char tmp = i;
            crypto[i - 1] = crypto[i - 1] ^ (i - 1);
            printf("%c", crypto[i-1] ^ crypto[i]);
        }
    }
 
    return 0;
}

2、ARM汇编

这里简单的介绍一下ARM汇编,来自个人笔记
ARM汇编是一种面向ARM架构的低级编程语言,用于编写与硬件紧密相关的程序。以下是一些关于ARM汇编的特点:

  1. 精简指令集:ARM架构以精简指令集(Reduced Instruction Set Computing,RISC)著称。它的指令集设计简洁高效,提供了基本的算术、逻辑、数据传输和流程控制等操作。
  2. 寄存器:ARM架构具有大量的通用寄存器。32位ARM架构(ARMv7和ARMv8)提供16个通用寄存器(R0-R15),用于存储常用的数据和临时计算结果。
  3. 三地址指令格式:ARM指令通常采用三地址指令格式,即指令中包含源操作数、目的操作数和要进行的操作。这使得ARM汇编相对简洁,并且减少了内存访问次数。
  4. Thumb指令集:在某些ARM处理器上,还存在Thumb指令集,它使用16位指令(相对于32位的ARM指令)来减小代码的大小。Thumb指令集可以提供更高的代码密度,适用于内存有限的嵌入式系统。
  5. 跨平台:ARM架构广泛用于移动设备、嵌入式系统和低功耗应用,包括智能手机、平板电脑、物联网设备等。由于ARM处理器家族的多样性,ARM汇编在不同的ARM处理器之间可以共享,但也会有一些差异和特定的指令。

给出注释后的题目汇编

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
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
@ ARM gcc 11.1(linux)
.Ltext0:
flag:
        .ascii  "***************************\000"
.LC0:
        .ascii  "%02x\000"
main:
.LFB0:
        @ 这是注释,表示该函数不接受任何参数,使用一个8字节大小的栈帧,并且需要保存现场数据。
        @ args = 0, pretend = 0, frame = 8
        @ frame_needed = 1, uses_anonymous_args = 0
 
        push    {r7, lr}   @保存r7和lr寄存器的值到栈中,以便后续恢复现场使用。
        sub     sp, sp, #8 @在栈上分配8个字节的空间。
        add     r7, sp, #0 @将栈指针保存到r7寄存器中,形成一个帧指针。
.LBB2:
        movs    r3, #0
        str     r3, [r7, #4] @将0赋值给r3寄存器,并将其存储到帧指针偏移为4的位置。
        b       .L2          @无条件跳转到标签.L2处。
.L5:                         @这是一个标签,标识一个循环的开始(为什么是循环,后面分析即可)
        ldr     r3, [r7, #4]
        and     r3, r3, #1
        cmp     r3, #0
        bne     .L3         
        @从帧指针偏移为4的位置加载一个值到r3寄存器,并将其与1进行按位与运算。然后,将结果与0进行比较,如果不相等,跳转到标签.L3处。
        movw    r3, #:lower16:flag @将全局变量flag的低16位地址加载到r3寄存器。
        movt    r3, #:upper16:flag @将全局变量flag的高16位地址加载到r3寄存器的高16位。
        ldr     r2, [r7, #4]       @从帧指针偏移为4的位置加载一个值到r2寄存器。这个值可能是一个偏移量,用于对r3寄存器中的地址进行修正。
        add     r3, r3, r2         @将r2寄存器的值加到r3寄存器中,修正flag的地址。
        ldrb    r3, [r3]        @ zero_extendqisi2 从r3寄存器中的地址处加载一个值到r3寄存器,并进行零扩展。这个值即为flag的值。
        eor     r3, r3, #57   @将r3寄存器的值与57进行按位异或操作。
        uxtb    r3, r3        @将r3寄存器中的值零扩展为一个字节。
        mov     r1, r3        @将r3寄存器中的值复制到r1寄存器,作为printf函数的第二个参数。
        movw    r0, #:lower16:.LC0   @相同的道理,加载字符串
        movt    r0, #:upper16:.LC0
        bl      printf          @调用函数
        b       .L4            @无条件跳转
 
.L3:    @flag[i] 为奇数 ,不操作
        movw    r3, #:lower16:flag
        movt    r3, #:upper16:flag
        ldr     r2, [r7, #4]
        add     r3, r3, r2      @这里的作用,就是取下标 flag[i] 的意思
        ldrb    r3, [r3]        @ zero_extendqisi2
        mov     r1, r3          @
        movw    r0, #:lower16:.LC0
        movt    r0, #:upper16:.LC0
        bl      printf          @调用函数
 
 
.L4:    @从帧指针偏移为4的位置加载一个值到r3寄存器,并将其加1。然后将结果保存回帧指针偏移为4的位置
        ldr     r3, [r7, #4]
        adds    r3, r3, #1
        str     r3, [r7, #4]
 
 
.L2:    @从帧指针偏移为4的位置加载一个值到r3寄存器,并将其与26进行比较。如果r3小于等于26,则跳转到标签.L5处,否则继续执行。
        ldr     r3, [r7, #4]
        cmp     r3, #26
        ble     .L5
 
 
.LBE2:  @将0赋值给r3寄存器,将其作为返回值存储到r0寄存器,增加帧指针的值,并将栈指针设置为帧指针
        movs    r3, #0
        mov     r0, r3
        adds    r7, r7, #8
        mov     sp, r7
        @ sp needed
        pop     {r7, pc}
.LFE0:
.Letext0:
.Ldebug_info0:
.Ldebug_abbrev0:
.Ldebug_line0:
.LASF6:
.LASF10:
.LASF0:
.LASF11:
.LASF3:
.LASF7:
.LASF9:
.LASF1:
.LASF15:
.LASF8:
.LASF12:
.LASF2:
.LASF14:
.LASF5:
.LASF13:
.LASF4:
@ 7d416a436d4642315c740c64095f7872745f6d720d315769574744

写出对应的解密脚本

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#include <stdio.h>
 
char flag[] = { 0x7d,0x41,0x6a,0x43,0x6d,0x46,0x42,0x31,0x5c,0x74,0x0c,0x64,0x09,0x5f,0x78,0x72,0x74,0x5f,0x6d,0x72,0x0d,0x31,0x57,0x69,0x57,0x47,0x44 }; // 这里的字符串内容应该与实际代码中的 flag 相同
 
int main() {
    int i = 0;
    while (i <= 26) {
        if ((i & 1) != 0) { // 如果 i 是奇数则直接输出
            printf("%c", flag[i]);
        } else {
            printf("%c", flag[i] ^ 57);
        }
        i++;
    }
    return 0;
}

可以看到,这两道题目都判断了奇偶性,不像之前就是简单的异或或者相加相减,这样通过奇偶性判断,更加考察选手对汇编流程的理解。


[培训]二进制漏洞攻防(第3期);满10人开班;模糊测试与工具使用二次开发;网络协议漏洞挖掘;Linux内核漏洞挖掘与利用;AOSP漏洞挖掘与利用;代码审计。

上传的附件:
收藏
点赞6
打赏
分享
最新回复 (3)
雪    币: 19410
活跃值: (29069)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
秋狝 2023-10-18 09:41
2
1
感谢分享
雪    币: 3740
活跃值: (623)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
nszy007 2023-10-18 15:25
3
0
这是什么比赛呀,刚好前天朋友也来问了我这个第二题。
雪    币: 997
活跃值: (1518)
能力值: ( LV4,RANK:40 )
在线值:
发帖
回帖
粉丝
WMBa0 2023-10-19 22:09
4
0
nszy007 这是什么比赛呀,刚好前天朋友也来问了我这个第二题。
我也不清楚
游客
登录 | 注册 方可回帖
返回