题目给了两个文件

我们先看 .asm 汇编文件

section .data
    msg db "Welcome_to_CTFshow_PWN", 0

section .text
    global _start

_start:

; 立即寻址方式
    mov eax, 11         ; 将11赋值给eax
    add eax, 114504     ; eax加上114504
    sub eax, 1          ; eax减去1

; 寄存器寻址方式
    mov ebx, 0x36d      ; 将0x36d赋值给ebx
    mov edx, ebx        ; 将ebx的值赋值给edx

; 直接寻址方式
    mov ecx, msg      ; 将msg的地址赋值给ecx

; 寄存器间接寻址方式
    mov esi, msg        ; 将msg的地址赋值给esi
    mov eax, [esi]      ; 将esi所指向的地址的值赋值给eax

; 寄存器相对寻址方式
    mov ecx, msg        ; 将msg的地址赋值给ecx
    add ecx, 4          ; 将ecx加上4
    mov eax, [ecx]      ; 将ecx所指向的地址的值赋值给eax

; 基址变址寻址方式
    mov ecx, msg        ; 将msg的地址赋值给ecx
    mov edx, 2          ; 将2赋值给edx
    mov eax, [ecx + edx*2]  ; 将ecx+edx*2所指向的地址的值赋值给eax

; 相对基址变址寻址方式
    mov ecx, msg        ; 将msg的地址赋值给ecx
    mov edx, 1          ; 将1赋值给edx
    add ecx, 8          ; 将ecx加上8
    mov eax, [ecx + edx*2 - 6]  ; 将ecx+edx*2-6所指向的地址的值赋值给eax

; 输出字符串
    mov eax, 4          ; 系统调用号4代表输出字符串
    mov ebx, 1          ; 文件描述符1代表标准输出
    mov ecx, msg        ; 要输出的字符串的地址
    mov edx, 22         ; 要输出的字符串的长度
    int 0x80            ; 调用系统调用

; 退出程序
    mov eax, 1          ; 系统调用号1代表退出程序
    xor ebx, ebx        ; 返回值为0
    int 0x80            ; 调用系统调用

使用 nasm 命令对该汇编文件进行编译,指定输出格式为 ELF 格式,这种格式是一种在 UNIX 系统中广泛使用的可执行文件格式,它包含了程序的二进制代码、数据、符号表等信息。

nasm -f elf Welcome_to_CTFshow.asm

生成了一个 .o 的文件,也就是对象文件 

使用 ld 命令将该文件链接成可执行文件

ld -m elf_i386 -s -o Welcome_to_CTFshow Welcome_to_CTFshow.o

参数解释:

ld: 这是 GNU 链接器的命令。

-m elf_i386: 这个选项指定链接器工作在 i386 架构下,生成针对 ELF 格式的目标文件。i386 是 Intel 的 32 位架构。

-s: 这个选项告诉链接器在生成的可执行文件中去除符号表和调试信息,以减小文件大小。

-o Welcome_to_CTFshow: 这个选项指定生成的可执行文件的文件名为 Welcome_to_CTFshow

Welcome_to_CTFshow.o: 这是要链接的输入文件,是由汇编器生成的对象文件,它包含了汇编代码的已编译二进制表示。

使用 ./ 执行生成的可执行文件

输出 Welcome_to_CTFshow_PWN

使用 flag 格式包裹

得到 flag 为:ctfshow{Welcome_to_CTFshow_PWN}

顺便检查一下我们生成的可执行程序:

checksec Welcome_to_CTFshow

可以看到是一个 32 位程序,并且什么保护都没开

接下来我们分析题目给的可执行程序

和我们手动生成的一样,因此我们使用其中的一个就行

这里不管是 32 还是 64 的 ida 都可以打开  

因为是 32 位,我们就使用 ida32 来看吧 

只有一个 start 函数,我们对其进行反编译

伪代码解释:

  1. void __noreturn start(): 这行定义了一个函数 start(),它没有参数,并且声明为不返回任何值。函数名前的 void 表示函数不返回任何值,__noreturn 是一个函数属性,表示该函数不会返回,因此编译器不会生成函数返回的代码。

  2. int v0; // eaxint v1; // eax: 这两行声明了两个整型变量 v0v1,它们用来存储系统调用的返回值。

  3. v0 = sys_write(1, &dword_80490E8, 0x16u);: 这行调用了 sys_write 系统调用,用于将数据写入文件描述符为 1 的文件(标准输出)。第一个参数 1 表示标准输出文件描述符,第二个参数 &dword_80490E8 是一个字符串的地址,第三个参数 0x16u 表示要写入的字符数量(22个字符)。sys_write 的返回值(成功写入的字符数)被存储在变量 v0 中。

  4. v1 = sys_exit(0);: 这行调用了 sys_exit 系统调用,用于退出程序。参数 0 表示程序正常退出。sys_exit 函数不会返回,但是在一些编译器中,需要将其返回值存储在一个变量中。这里将其存储在变量 v1 中,但实际上并没有使用这个返回值。

双击跟进 dword_80490E8

选中十六进制值,使用快捷键 R 转为字符

x86 处理器是小端序,即将一个多位数的低位放在较小的地址处,高位放在较大的地址处,小端序与人类的阅读习惯相反,但更符合计算机读取内存的方式,因为CPU读取内存中的数据时,是从低地址向高地址方向进行读取的,因此这里应该是 Welc

与后面的拼接起来为:Welcome_to_CTFshow_PWN,也就是写入的 22 个字符

也可以选中使用快捷键 A 将数据转换为字符串

所以 flag 为:ctfshow{Welcome_to_CTFshow_PWN}

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐