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

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

2023-10-17 20:06
11841

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

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

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

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

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

给出注释后的题目汇编

写出对应的解密脚本

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

```@定义了一些辅助信息,比如:文件名,段名称
.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:
```@定义了一些辅助信息,比如:文件名,段名称
.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:
#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;
}
#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;
}
@ 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
@ ARM gcc 11.1(linux)
.Ltext0:
flag:

[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!

上传的附件:
收藏
免费 6
支持
分享
最新回复 (3)
雪    币: 2787
活跃值: (30801)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
2
感谢分享
2023-10-18 09:41
1
雪    币: 5921
活跃值: (633)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
3
这是什么比赛呀,刚好前天朋友也来问了我这个第二题。
2023-10-18 15:25
0
雪    币: 906
活跃值: (1773)
能力值: ( LV4,RANK:40 )
在线值:
发帖
回帖
粉丝
4
nszy007 这是什么比赛呀,刚好前天朋友也来问了我这个第二题。
我也不清楚
2023-10-19 22:09
0
游客
登录 | 注册 方可回帖
返回
//