CTF汇编题目:学习ARM和X86汇编
2023-10-30 18:6:23 Author: mp.weixin.qq.com(查看原文) 阅读量:8 收藏

题目比较简单,但是考察了二进制汇编基础。个人觉得基础很重要,所以花了些时间来认真看这道题目,巩固自己的基础。如果是在线下,没有GPT的帮助,此时就需要选手汇编底子,对题目进行细致的分析。


如果学习汇编有题目带着学,耐心分析,相信收获会很大的,有助于理解汇编操作的逻辑和方法。


开始今天的题目讲解,题目会打包上传。如有错误,还请指出。


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)


有了上面的基础之后,开始看题目:

```@定义了一些辅助信息,比如:文件名,段名称
.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密文呢是题目附件给出了。

#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汇编

这里简单的介绍一下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处理器之间可以共享,但也会有一些差异和特定的指令。

给出注释后的题目汇编:

@ 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

写出对应的解密脚本:

#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;
}

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

看雪ID:NYSECbao

https://bbs.kanxue.com/user-home-971547.htm

*本文为看雪论坛优秀文章,由 NYSECbao 原创,转载请注明来自看雪社区

# 往期推荐

1、IOFILE exploit入门

2、入门编译原理之前端体验

3、如何用纯猜的方式逆向喜马拉雅xm文件加密(wasm部分)

4、反恶意软件扫描接口(AMSI)如何帮助您防御恶意软件

5、sRDI — Shellcode反射式DLL注入技术

6、对APP的检测以及参数计算分析

球分享

球点赞

球在看


文章来源: https://mp.weixin.qq.com/s?__biz=MjM5NTc2MDYxMw==&mid=2458526796&idx=1&sn=d171be55f71f60b9ad8f7ebadb574a98&chksm=b18d14c686fa9dd05256663b1b0ac01e0db11bae20bb48099961bbe9c3b199c66b4f1d852d0e&scene=58&subscene=0#rd
如有侵权请联系:admin#unsafe.sh