CTFshow-PWN-前置基础-pwn5
x86 处理器是小端序,即将一个多位数的低位放在较小的地址处,高位放在较大的地址处,小端序与人类的阅读习惯相反,但更符合计算机读取内存的方式,因为CPU读取内存中的数据时,是从低地址向高地址方向进行读取的,因此这里应该是 Welc。使用 nasm 命令对该汇编文件进行编译,指定输出格式为 ELF 格式,这种格式是一种在 UNIX 系统中广泛使用的可执行文件格式,它包含了程序的二进制代码、数据、符号
题目给了两个文件
我们先看 .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 函数,我们对其进行反编译
伪代码解释:
void __noreturn start()
: 这行定义了一个函数start()
,它没有参数,并且声明为不返回任何值。函数名前的void
表示函数不返回任何值,__noreturn
是一个函数属性,表示该函数不会返回,因此编译器不会生成函数返回的代码。
int v0; // eax
和int v1; // eax
: 这两行声明了两个整型变量v0
和v1
,它们用来存储系统调用的返回值。
v0 = sys_write(1, &dword_80490E8, 0x16u);
: 这行调用了sys_write
系统调用,用于将数据写入文件描述符为 1 的文件(标准输出)。第一个参数1
表示标准输出文件描述符,第二个参数&dword_80490E8
是一个字符串的地址,第三个参数0x16u
表示要写入的字符数量(22个字符)。sys_write
的返回值(成功写入的字符数)被存储在变量v0
中。
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}
更多推荐
所有评论(0)