2

I'm trying to write a 64-bit shellcode to read a file called '/proc/flag'. However, I'm getting some random errors when I'm compiling the assembly and don't know why its occuring.

This is my assembly file 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

These are the errors I'm getting when I compile the assembly:

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

I thought push byte 60 was considered a valid instruction in Intel syntax. I'm not sure where the errors are coming from. Would appreciate any help.

9
  • 1
    You're trying to use NASM syntax which the GNU Assembler doesn't support. Intel syntax is the syntax used by MASM. Commented Jun 22, 2017 at 3:39
  • @AjayBrahmakshatriya The problem is that it can't be made to use NASM syntax. Commented Jun 22, 2017 at 3:44
  • 1
    I think the real issue that X86_64 doesn't allow pushing single bytes. You need to push a qword and qword only. Commented Jun 22, 2017 at 3:44
  • @RossRidge My bad! Haven't been really good with the names. Commented Jun 22, 2017 at 3:49
  • 4
    @AjayBrahmakshatriya NASM is similar to MASM syntax, but there are differences. MASM uses DWORD PTR where NASM uses DWORD. MASM uses hexadecimal numbers of the form 7fh where NASM supports both MASM and C-style (0x7f) hexadecimal numbers. Commented Jun 22, 2017 at 3:59

2 Answers 2

4

When you specify the .intel_syntax noprefix option, you instruct the Gnu assembler that you will be using MASM syntax. What you've written is actually NASM syntax, which is similar to MASM syntax in many ways, but subtly different in others.

For a complete discussion of the differences, see this section of the NASM manual.
For a quick overview of NASM vs. MASM syntax, refer to this document.
(This second document used to be hosted online, in a more readable HTML format, here, but the link has gone down and I unfortunately can't find a copy in the Wayback Machine.)

The big thing that needs to change in your code is that you need to include the PTR directive after each of the size specifiers. So, for example, instead of:

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

you need to write:

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

Also, while MASM syntax would normally write hexadecimal constants with a trailing h, instead of the leading 0x, Gas's "MASM" mode doesn't support this, and you need to use the C-style 0x notation, even when using Intel syntax.

I thought push byte 60 was considered a valid instruction in Intel syntax.

No, not really. The only size values you can PUSH and POP from the stack are the processor's native register width. So, in 32-bit binaries, you must push and pop 32-bit values, whereas in 64-bit binaries, you must push and pop 64-bit values.* That means these lines of code are technically wrong:

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

MASM will give you a warning about an invalid operand size for the latter two instructions that have a size explicitly specified, but it will implicitly extend these constants to be 64-bit values and still assemble successfully. I assume the Gnu assembler can do this automatic value-extension, too, so you should just drop the size directive:

push    'ag'
push    0x7f
push    60

__
* Technically, you can use an operand size override prefix to allow you to push a 16-bit immediate onto the stack, both in 32-bit and 64-bit mode. But you really shouldn't ever do that because it misaligns the stack, creating performance problems (and, if you're interoperating with code compiled in other languages, breaks the ABI). Only push 16-bit values onto the stack when writing 16-bit code. If you want to push a 16-bit value in 32-bit or 64-bit mode, just let the assembler extend it to 32-bit or 64-bit, accordingly.

Sign up to request clarification or add additional context in comments.

4 Comments

In NASM, I think push byte 60 might explicitly request the push imm8 encoding instead of the push imm32 encoding. Also note that 32 and 64-bit code can still use 16-bit push/pop. (Operand-size prefix works, REX.W=0 doesn't.)
Although Gas might support either form as a convenience, even when using Intel syntax. No, even in .intel_syntax noprefix mode, gas doesn't support trailing suffixes on constants. You have to use the C-style prefix notation. godbolt.org/g/gQdYhT. It's like MASM in a lot of ways, but it's a huge overstatement to say that it is MASM syntax. For example, you can still use movabs for 64-bit immediates / addresses, and IIRC the fsub st(i) swapped with fsubr st(i) AT&T syntax bug is still present (same for fdiv / fdivr, etc.) And of course the directives are still GNU.
I think you have to use STRICT in NASM to override the optimizer, so push byte 60 wouldn't be sufficient. But I'm not certain about that. One cannot remember the nuances of every assembler out there. I fixed the issue about hex notation. That was a good catch. I never actually use Gas's Intel mode because it's just too confusing and incompatible, even as much as I detest AT&T syntax.
Yeah, I don't recommend actually writing anything in GNU .intel_syntax. I use it read-only, i.e. objdump -Mintel, perf report -Mintel, or gcc -S -masm=intel, but if I want to write in a syntax like that I use YASM. And you're right, I think strict is needed to override encoding choices.
-1

This is wrong syntax for GAS. You have to use movl '/pro', [rsp] ALso, use **$**xx for moving actual value xx into a location. Refer to the full syntax here.

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

1 Comment

The question is using GAS's .intel_syntax noprefix mode for the MASM-like syntax. You seem to be answering about AT&T syntax, which is the default for GAS but not the only option.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.