Ошибка: мусор `0x7f 'после выражения при сборке с использованием газа

Я пытаюсь написать 64-битный шеллкод для чтения файла с именем '/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 считается действительной инструкцией в синтаксисе Intel. Я не уверен, откуда происходят ошибки. Поблагодарили бы за любую помощь.

Ответы

Ответ 1

Когда вы указываете параметр .intel_syntax noprefix, вы указываете ассемблеру Gnu, что вы будете использовать синтаксис MASM. То, что вы написали, это, по сути, синтаксис NASM, который во многом похож на синтаксис MASM, но в другом случае он тонко отличается.

Для полного обсуждения различий см. этот раздел руководства NASM.
Для краткого обзора синтаксиса NASM и MASM см. этот документ.
(Этот второй документ использовался для размещения в Интернете, в более читаемом формате HTML, здесь, но ссылка опустилась, и я к сожалению, не удается найти копию в 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, режим "MASM" газа не поддерживает это, и вам нужно использовать стиль C 0x, даже при использовании синтаксиса Intel.

Я думал, что push byte 60 считается действительной инструкцией в синтаксисе Intel.

Нет, не совсем. Единственными значениями размера, которые вы можете PUSH и POP из стека, являются собственная ширина регистра процессора. Итак, в 32-битных двоичных файлах вы должны нажать и поместить 32-битные значения, тогда как в 64-битных двоичных файлах вы должны нажать и поместить 64-битные значения. * Это означает, что эти строки кода технически неправильно:

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

MASM предоставит вам предупреждение о недопустимом размере операнда для последних двух инструкций, размер которых явно указан, но он будет неявно расширять эти константы до 64-битных значений и успешно собираться. Я предполагаю, что ассемблер Gnu тоже может сделать это автоматическое расширение значений, поэтому вам нужно просто отбросить директиву размера:

push    'ag'
push    0x7f
push    60

__
* С технической точки зрения вы можете использовать префикс переопределения размера операнда, чтобы вы могли нажимать 16-битное мгновенное на стек, как в 32-битном, так и в 64-битном режиме. Но вы действительно не должны этого делать, потому что это неправильно выравнивает стек, создавая проблемы с производительностью (и, если вы взаимодействуете с кодом, скомпилированным на других языках, ломает ABI). Вставляйте только 16-битные значения в стек при записи 16-битного кода. Если вы хотите нажать 16-битное значение в 32-битном или 64-битном режиме, просто позвольте ассемблеру расширять его до 32-разрядного или 64-битного, соответственно.