【问题标题】:Printing something in stack (assembly)在堆栈中打印一些东西(组装)
【发布时间】:2018-03-26 17:39:41
【问题描述】:

在 linux 中,您可以使用系统调用号 4 打印一些内容:

mov eax,4       ;system call number
mov ebx,0       ;file descriptor
mov ecx,msg     ;adress of message in data segment
mov edx,length  ;length of message

但是,你如何从堆栈段打印一些东西?

我试过了:

push 'H'
push 'e'
push 'l'
push 'l'
push 'o'
push ' '
push 'w'
push 'o'
push 'r'
push 'l'
push 'd'
mov eax,4       ;system call number
mov ebx,0       ;file descriptor
mov ecx,ebp     ;adress of message
mov edx,11      ;length of message

但不打印任何内容。

编辑:我对我的代码做了一些更改,现在是这样:

section .data
msg: db "Hola mundo",0Ah
ok: db "OK",0Ah

section .text
global _start
_start:
push 'leH'
push 'w ol'
push 'dlro'
mov eax,4       ;system call number
mov ebx,1       ;file descriptor
mov ecx,esp     ;adress of message in data segment
mov edx,11      ;length of message
mov eax,1
xor ebx,ebx     ;same as move ebx,0 but better
int 0x80

EDIT 2(仍然不工作)

section .data
msg: db "Hello world",0Ah

section .text
global _start
_start:
push 'leH'
push 'w ol'
push 'dlro'
mov eax,4       ;system call number
mov ebx,1       ;file descriptor
mov ecx,esp     ;adress of message in data segment
mov edx,11      ;length of message
int 0x80
mov eax,1
xor ebx,ebx     ;same as move ebx,0 but better
int 0x80

响应评论,我组装和编译:

nasm -f elf64 hello.asm && ld hello.o && ./a.out

我正在使用 Ubuntu 64 位 Linux。

【问题讨论】:

  • ebp 不是堆栈指针,esp 是。此外,push 将使用每个字符 4 个字节,这样就不太行了。
  • 更不用说使用push 会反转你的字符串。
  • @Jester 我想测试字符串的反转
  • 正如 Jester 指出的应该是 ESPEBP 并且如果您想向后打印该字符串,则可以将 Pushes 替换为 push 'leH' push 'w ol'push 'dlro'。对于标准输出,EBX 也应该是 1 而不是 0。 (0 是标准输入)
  • 您是将其组装成 64 位程序还是 32 位程序?此代码无法作为 64 位代码正常运行,因为 64 位代码中的 int 0x80 仿真无法处理使用堆栈时所需的 64 位地址。堆栈地址需要 RSP 中的完整 64 位地址。只有通过syscall 指令使用64 位System V Linux 系统调用接口才能做到这一点。可以在Ryan Chapman's blog 中找到有关使用的信息。

标签: assembly memory nasm x86-64 system-calls


【解决方案1】:

该问题最初缺少关键信息。您组装并链接到:

nasm -f elf64 hello.asm && ld hello.o && ./a.out

这会生成一个 64 位的可执行文件。 int 0x80 不应在 64 位可执行文件中使用。在 64 位程序中,堆栈指针不能仅在 32 位 ESP 寄存器中表示。使用 64 位指针的唯一方法是使用 syscall 指令。 Ryan Chapman's blog 提供了有关通过 syscall 指令使用 64 位系统调用接口的详细信息。

如果您修改代码以符合该接口,它可能看起来像:

section .text
global _start
_start:
    mov dword [rsp-4], `rld\n`
                    ; Use back ticks instead of single quotes
                    ; To parse the string like C. We can write safely
                    ; in the 128 bytes below RSP because of the
                    ; Linux red zone.
    mov dword [rsp-8], 'o Wo'
    mov dword [rsp-12],'Hell'
    mov eax,1       ;Write system call number
    mov edi,eax     ;file descriptor (1 = stdout)
    lea rsi,[rsp-12];address of message on the stack
    mov edx,12      ;length of message
    syscall
    mov eax,60      ;Exit system call
    xor edi,edi     ;RDI=0
    syscall

此代码将常量写入 32 位寄存器,因为它们被零扩展至整个 64 位寄存器。我们不能压入 64 位立即数,但我们可以将字符串作为 32 位 DWORD 值直接写入堆栈。我们不需要调整RSP,因为 Linux 有一个 128 字节的red zone,它可以防止被异步事件和信号破坏。

如果你想使用带有 C 转义字符的 C 样式字符串,NASM supports backtick 而不是单引号区别。这允许您使用 \n 之类的东西作为表示换行符的字符。我之所以提到这一点,是因为您似乎想在您的问题中发布的一些代码中放置一个换行符,该代码已被删除。

【讨论】:

  • 对于代码密度,您实际上可以混合使用 pushmov,例如push 'Hell'/mov dword [rsp+4], 'o Wo'。作为奖励,您最终会得到 rsp 指向字符串的前面,因此您只需要 mov 而不是 lea。还有一种简单的方法来获取execve 的空终止字符串,而无需在机器代码中包含文字 0 字节。但是 +1 是一个很好的非push 示例,这更容易理解!
  • (实际上对于代码密度,您希望 push imm32 推送字符串的末尾,然后 mov reg, imm64 / push reg 推送接下来的 8 个字节。12 个立即字节和 2 个操作码+ 1个前缀字节。您可以lea rsi, [rsp-8] / syscall并在使用虚拟pop推送后恢复RSP。)
猜你喜欢
  • 2021-07-06
  • 2016-06-29
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-10-14
  • 2019-05-09
  • 1970-01-01
相关资源
最近更新 更多