NASM汇编教程翻译07 第七讲 换行符

本文介绍NASM汇编语言中处理换行符的方法。通过编写子程序sprintLF实现字符串输出后自动换行,提高了代码的可维护性和灵活性。文章详细展示了如何利用栈来传递换行符,并给出了完整的代码示例。

英文原版地址:NASM Assembly Language Tutorials - asmtutor.com

对于像”Hello world“这样的命令行程序,换行符时必不可少的。一旦我们开始构建要求用户输入的程序,它们变得更重要。但是换行符维护起来可能会变得麻烦。有时想在字符串包含它们,有时也想删去它们。如果你继续通过在声明消息文本后添加 0AH 的方式在你的变量中硬编码换行符,它将变成一个问题。如果这是代码中一处我们不想输出这个变量的换行符的地方,需要去写一些额外逻辑在运行时从字符串中删去。

如果我们写一个子程序输出我们的消息后打印一个换行符,对于程序的维护性是有好处的。这样,当我们需要换行符时调用这个子程序,不需要时就调用现在的sprint子程序。

调用 sys_write 需要我们传递指向想输出的字符串在内存中地址的指针,所以我们不能只传一个换行符给我们的打印函数。我们也不想仅为了保存换行符去创建另一个变量,所以用栈来代替。

这个方法是通过将换行符移到EAX寄存器中运作的。我们将EAX压到栈上然后通过扩展栈指针寄存器ESP获取地址。ESP是另一个寄存器。当ESP指向一个字符在内存中的地址,sys_write将被允许使用它去打印。

functions.asm文件

;------------------------------------------
; int slen(String message)
; 字符串长度计算函数
slen:
    push    ebx
    mov     ebx, eax
 
nextchar:
    cmp     byte [eax], 0
    jz      finished
    inc     eax
    jmp     nextchar
 
finished:
    sub     eax, ebx
    pop     ebx
    ret
 
;------------------------------------------
; void sprint(String message)
; 字符串打印函数
sprint:
    push    edx
    push    ecx
    push    ebx
    push    eax
    call    slen
 
    mov     edx, eax
    pop     eax
 
    mov     ecx, eax
    mov     ebx, 1
    mov     eax, 4
    int     80h
 
    pop     ebx
    pop     ecx
    pop     edx
    ret
 
;------------------------------------------
; void sprintLF(String message)
; 打印字符串和换行符函数
sprintLF:
    call    sprint
 
    push    eax         ; 当我们在这个函数中使用EAX时通过将EAX压入栈来进行保护
    mov     eax, 0AH    ; 将0AH移到EAX中 - 0AH是换行符的ascii码
    push    eax         ; 将换行符放到栈上,以便我们获取地址
    mov     eax, esp    ; 将当前栈指针的地址放到EAX寄存器中给sprint函数
    call    sprint      ; 调用sprint函数
    pop     eax         ; 从栈上移除换行符
    pop     eax         ; 恢复调用函数前EAX原本的值
    ret                 ; 返回程序
 
;------------------------------------------
; void exit()
; 退出程序并复原资源
quit:
    mov     ebx, 0
    mov     eax, 1
    int     80h
    ret

hello.asm文件

%include        'functions.asm'                             ; 包含我们的外部文件
 
SECTION .data
msg1    db      'Hello, brave new world!', 0h               ; 注:已经移除了换行符
msg2    db      'This is how we recycle in NASM.', 0h       ; 注:已经移除了换行符
 
SECTION .text
global  _start
 
_start:
 
    mov     eax, msg1       ; 将第一个字符串的地址给EAX
    call    sprintLF        ; 注:调用带有换行符的字符串打印函数
 
    mov     eax, msg2       ; 将第二个字符串的地址给EAX
    call    sprintLF        ; 注:调用带有换行符的字符串打印函数
 
    call    quit            ; 调用退出函数

编译命令

nasm -f elf hello.asm

链接命令

ld -m elf_i386 hello.o -o hello

执行命令

./hello

<think> 我们正在Ubuntu系统中编写汇编代码并使用pwndbg调试运行。以下是步骤: 1. 编写汇编代码:使用文本编辑器(如nano、vim)编写一个简单的汇编程序(例如hello.asm)。这里我们以输出"Hello, World!"为例(使用Intel语法,通过系统调用写入)。 2. 汇编和链接:使用nasm进行汇编,然后使用ld进行链接,生成可执行文件。注意要生成ELF64格式(适用于64位Ubuntu)。 3. 启动pwndbg调试:使用pwndbg启动调试会话。 具体步骤: ### 1. 编写汇编代码 创建一个名为`hello.asm`的文件,内容如下: ```assembly section .data msg db 'Hello, World!', 0xa ; 字符串和换行符 len equ $ - msg ; 字符串长度 section .text global _start _start: ; 系统调用号: write (1) mov rax, 1 ; sys_write mov rdi, 1 ; 文件描述符 stdout mov rsi, msg ; 字符串地址 mov rdx, len ; 字符串长度 syscall ; 调用内核 ; 系统调用号: exit (60) mov rax, 60 ; sys_exit mov rdi, 0 ; 退出状态码 0 syscall ``` ### 2. 汇编和链接 安装nasm(如果未安装): ```bash sudo apt update sudo apt install nasm ``` 汇编(生成目标文件): ```bash nasm -f elf64 hello.asm -o hello.o ``` 链接(生成可执行文件): ```bash ld hello.o -o hello ``` ### 3. 使用pwndbg调试 确保已安装pwndbg(参考引用[1]中提到的安装方法,但注意引用提供的是Windows安装包,我们在Ubuntu下安装)。在Ubuntu中安装pwndbg的方法如下(从源码安装): ```bash git clone https://github.com/pwndbg/pwndbg cd pwndbg ./setup.sh ``` 然后启动pwndbg调试程序: ```bash pwndbg ./hello ``` 在pwndbg中,可以使用以下命令: - `starti`:在程序入口点停下(这里我们也可以直接运行,因为程序简单) - `break _start`:在`_start`处设置断点 - `run`:运行程序 - `stepi`(或`si`):单步执行一条指令 - `context`:查看当前上下文(寄存器、反汇编、堆栈等) 例如,我们可以在`_start`处设置断点并运行: ``` pwndbg> break _start pwndbg> run ``` 然后使用`stepi`逐步执行,观察寄存器的变化。 ### 注意事项: - 在汇编时,使用`-g`选项可以生成调试信息(虽然nasm本身不支持生成DWARF调试信息,但可以通过`-F dwarf`选项尝试,不过对于简单的调试,我们通常直接看汇编指令即可)。 - 上面的汇编代码是64位的,确保你的系统是64位。 这样就完成了在Ubuntu系统中编写汇编代码并使用pwndbg调试运行的过程。 §§相关问题§§ 1. 如何在汇编程序中调用C标准库函数(如printf)? 2. 如何在pwndbg中查看内存内容? 3. 如何利用pwndbg调试堆溢出漏洞? </think> ### 在Ubuntu系统中编写汇编代码并使用pwndbg调试运行的方法 #### 1. 编写汇编代码 首先创建汇编文件(如`hello.asm`),示例代码(NASM语法): ```assembly section .data msg db 'Hello, Pwndbg!', 0xA ; 字符串和换行符 len equ $ - msg ; 计算字符串长度 section .text global _start _start: ; 系统调用: write(1, msg, len) mov rax, 1 ; sys_write mov rdi, 1 ; fd=stdout mov rsi, msg ; 字符串地址 mov rdx, len ; 字符串长度 syscall ; 系统调用: exit(0) mov rax, 60 ; sys_exit xor rdi, rdi ; 退出码=0 syscall ``` #### 2. 安装必要工具 ```bash # 安装汇编器和链接器 sudo apt update sudo apt install nasm binutils # 安装pwndbg(参考引用[1]) git clone https://github.com/pwndbg/pwndbg cd pwndbg ./setup.sh ``` #### 3. 编译汇编程序(带调试信息) ```bash nasm -f elf64 -g -F dwarf hello.asm # 生成目标文件(含调试符号) ld -o hello hello.o # 链接为可执行文件 ``` **关键说明**: `-g -F dwarf`参数生成DWARF格式调试信息(参考引用[3]),使pwndbg能显示源代码级信息。 #### 4. 使用pwndbg调试 启动调试会话: ```bash pwndbg ./hello ``` 常用调试命令: ```bash starti # 在入口点暂停执行 break _start # 在_start处设置断点 context # 查看寄存器/反汇编/堆栈(参考引用[4]) stepi # 单步执行指令 x/10x $rsp # 查看栈内存 continue # 继续执行 ``` #### 5. 调试过程示例 ```bash $ pwndbg ./hello pwndbg> starti # 启动并暂停在第一条指令 pwndbg> break _start # 设置断点 pwndbg> context # 查看当前状态(寄存器+反汇编) ► 0x401000 <_start> mov eax, 1 0x401005 <_start+5> mov edi, 1 0x40100a <_start+10> lea rsi, [rip + 0x2f7] pwndbg> stepi # 单步执行观察寄存器变化 pwndbg> continue # 执行到最后 Hello, Pwndbg! # 程序输出 [Inferior 1 exited normally] ``` #### 关键技巧 1. **内存分析**: 使用`x/[数量][格式] [地址]`查看内存,如: ```bash x/s &msg # 查看字符串内容 x/8gx $rsp # 查看栈内存 ``` 2. **插件优势**: pwndbg自动高亮关键寄存器/内存区域(参考引用[4]),比原生GDB更易用。 3. **调试优化**: 若需修改代码,重新编译时保留`-g`选项确保调试信息完整(参考引用[3])。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值