【问题标题】:Error: junk `0x7f' after expression when assembling using Gas错误:使用 Gas 组装时表达式后出现垃圾“0x7f”
【发布时间】:2017-06-22 02:33:57
【问题描述】:

我正在尝试编写一个 64 位 shellcode 来读取一个名为“/proc/flag”的文件。但是,我在编译程序集时遇到了一些随机错误,不知道为什么会发生。

这是我的汇编文件readflag.S:

.intel_syntax noprefix
.global _start
.type _start, @function
_start:
  mov     dword [rsp], '/pro'     /* build filename on stack */
  mov     dword [rsp+4],  'c/fl'
  push    'ag'
  pop     rcx
  mov     [rsp+8], ecx
  lea     rdi, [rsp]              /* rdi now points to filename '/proc/flag' */
  xor     rsi, rsi                /* rsi contains O_RDONLY, the mode with which we'll open the file */
  xor     rax, rax
  inc     rax
  inc     rax                     /* syscall open = 2 */
  syscall
  mov     rbx, rax                /* filehandle of opened file */
  lea     rsi, [rsp]              /* rsi is the buffer to which we'll read the file */
  mov     rdi, rbx                /* rbx was the filehandle */
  push    byte 0x7f              /* read 127 bytes. if we stay below this value, the generated opcode will not contain null bytes */
  pop     rdx
  xor     rax, rax                /* syscall read = 0 */
  syscall
  lea     rsi, [rsp]              /* the contents of the file were on the stack */
  xor     rdi, rdi
  inc     rdi                     /* filehandle; stdout! */
  mov     rdx, rax                /* sys_read() returns number of bytes read in rax, so we move it to rdx */
  xor     rax, rax
  inc     rax
  syscall                     /* syscall write = 1 */
  push    byte 60                /* some bytes left... */
  pop     rax                     /* exit cleanly */
  syscall

这些是我在编译程序集时遇到的错误:

readflag.S: Assembler messages:
readflag.S:7: Error: junk `pro10mov dword [rsp+4]' after expression
readflag.S:21: Error: junk `0x7f' after expression
readflag.S:33: Error: junk `60' after expression
objcopy: 'readflag.o': No such file

我认为push byte 60 被认为是英特尔语法中的有效指令。我不确定错误来自哪里。将不胜感激。

【问题讨论】:

  • 您正在尝试使用 GNU 汇编器不支持的 NASM 语法。 Intel 语法是 MASM 使用的语法。
  • @AjayBrahmakshatriya 问题是它不能使用 NASM 语法。
  • 我认为 X86_64 不允许推送单个字节的真正问题。您只需要推送一个 qword 和 qword。
  • @RossRidge 我的错!名字不太好。
  • @AjayBrahmakshatriya NASM 与 MASM 语法相似,但有区别。 MASM 使用 DWORD PTR,而 NASM 使用 DWORD。 MASM 使用 7fh 形式的十六进制数字,其中 NASM 支持 MASM 和 C 样式 (0x7f) 十六进制数字。

标签: assembly x86 gnu-assembler intel-syntax


【解决方案1】:

当您指定.intel_syntax noprefix 选项时,您指示Gnu 汇编器您将使用MASM 语法。你写的其实是 NASM 语法,和 MASM 语法在很多方面很相似,但在其他方面又有些微妙的不同。

有关差异的完整讨论,请参阅this section of the NASM manual
有关 NASM 与 MASM 语法的快速概述,请参阅 this document
(第二个文档曾经在线托管,采用更易读的 HTML 格式,here,但链接已失效不幸的是,我在 Wayback Machine 中找不到副本。)

代码中需要更改的重要一点是,您需要在每个大小说明符之后包含PTR 指令。因此,例如,而不是:

    mov     dword [rsp],   '/pro'
    mov     dword [rsp+4], 'c/fl'

你需要写:

    mov     dword ptr [rsp],   '/pro'
    mov     dword ptr [rsp+4], 'c/fl'

此外,虽然 MASM 语法通常会写入带有尾随 h 的十六进制常量,而不是前导 0x,但 Gas 的“MASM”模式不支持这一点,您需要使用 C 风格的 @987654331 @ 表示法,即使使用 Intel 语法也是如此。

我认为push byte 60 被认为是 Intel 语法中的有效指令。

不,不是真的。您可以从堆栈PUSHPOP 的唯一大小值是处理器的本机寄存器宽度。因此,在 32 位二进制文​​件中,您必须推送和弹出 32 位值,而在 64 位二进制文​​件中,您必须推送和弹出 64 位值。* 这意味着这些代码行是技术上错误:

push    'ag'
push    byte ptr 0x7f
push    byte ptr 60

MASM 会给你一个关于后两条指令的无效操作数大小的警告,这些指令明确指定了大小,但它会隐式地将这些常量扩展为 64 位值并且仍然可以成功汇编。我假设 Gnu 汇编器也可以执行此自动值扩展,因此您应该删除 size 指令:

push    'ag'
push    0x7f
push    60

__
* 从技术上讲,您可以使用操作数大小覆盖前缀来将 16 位立即数压入堆栈,无论是 32 位还是 64 位位模式。但是你真的不应该这样做,因为它会使堆栈错位,产生性能问题(并且,如果你正在与用其他语言编译的代码进行互操作,则会破坏 ABI)。编写 16 位代码时,仅将 16 位值压入堆栈。如果要在 32 位或 64 位模式下推送 16 位值,只需让汇编器将其相应地扩展为 32 位或 64 位即可。

【讨论】:

  • 在 NASM 中,我认为 push byte 60 可能会明确请求 push imm8 编码而不是 push imm32 编码。另请注意,32 位和 64 位代码仍然可以使用 16 位推送/弹出。 (操作数大小前缀有效,REX.W=0 无效。)
  • 尽管为了方便起见,Gas 可能支持任何一种形式,即使在使用 Intel 语法时也是如此。 不,即使在 .intel_syntax noprefix 模式下,gas 也不支持常量的尾随后缀。您必须使用 C 风格的前缀表示法。 godbolt.org/g/gQdYhT。它在很多方面都像 MASM,但是说它是 MASM 语法是一个巨大的夸大其词。例如,您仍然可以将movabs 用于 64 位立即数/地址,并且 IIRC 与 fsubr st(i) 交换的 fsub st(i) 仍然存在 AT&T 语法错误(对于 fdiv / fdivr 等也是如此)当然还有指令仍然是 GNU。
  • 我认为你必须在 NASM 中使用 STRICT 来覆盖优化器,所以 push byte 60 是不够的。但我不确定。人们无法记住每个汇编程序的细微差别。我修复了有关十六进制表示法的问题。那是一个很好的收获。我从来没有真正使用过 Gas 的 Intel 模式,因为它太混乱且不兼容,即使我讨厌 AT&T 语法。
  • 是的,我不建议实际上用 GNU .intel_syntax 编写任何东西。我以只读方式使用它,即objdump -Mintelperf report -Mintelgcc -S -masm=intel,但如果我想用这样的语法编写,我使用 YASM。你是对的,我认为需要strict 来覆盖编码选择。
【解决方案2】:

这是 GAS 的错误语法。你必须使用 movl '/pro', [rsp] 此外,使用 **$**xx 将实际值 xx 移动到某个位置。请参阅此处的完整语法。

https://en.wikibooks.org/wiki/X86_Assembly/GAS_Syntax

【讨论】:

  • 问题是使用 GAS 的 .intel_syntax noprefix 模式来实现类似 MASM 的语法。您似乎在回答 AT&T 语法,这是 GAS 的默认设置,但不是唯一选项。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多