the_endian
the_endian

Reputation: 2527

Why won't this stack string print in x64 NASM on macOS?

I've been able to successfully print a string using the sys_write to stdout on macOS. However, I cannot get this stack string to print using execve syscall with echo:

global _main
default rel

section .text
_main:
mov rbp, rsp
sub rsp, 32
mov rax, 'this a t'
mov [rbp-16], rax
mov rax, 'est'
mov [rbp-8], rax
mov rax, '/bin/ech'
mov [rbp-32], rax
xor rax, rax
mov al, 'o'
mov [rbp-24], rax
push 0
mov rax, 0
mov [rbp], rax



exit_program:

;rdi filename
;rsi argv
;rdx envp
lea rdi, [rbp-32]
lea rsi, [rbp-32]
mov rdx, 0
mov rax, 0x200003b
syscall

Currently, my return is EFAULT status code from execve.

The memory layout as shown in the screenshot is the string "This is a test" followed by null bytes for termination.

UPDATE: Trace output: execve("/bin/echo", [0x6863652f6e69622f, 0x6f, 0x7420612073696874, 0x747365], NULL) = -1 EFAULT (Bad address) enter image description here

Upvotes: 1

Views: 148

Answers (1)

Peter Cordes
Peter Cordes

Reputation: 364180

execve takes 3 args: a char* and two char *[] arrays, each terminated by a NULL pointer.

Your first arg is fine. It points to a zero-terminated array of ASCII characters which are a valid path.

Your argv is a char[], not char *[], because you passed the same value as your first arg! So when the system call interprets the data as an array of pointers to copy into the new process's arg array, it finds an invalid pointer 0x6863652f6e69622f as the first one. (The bytes of that pointer are ASCII codes.)

The trace output makes that pretty clear.

Your 3rd is NULL, not a pointer to NULL. Linux supports this, treating a NULL as an empty array. I don't know if MacOS does or not; if you still get EFAULT after passing a valid argv[] set RDX to a pointer to a qword 0 somewhere on the stack.

Keeping your existing setup code, you could change the last part to

lea rdi, [rbp-32]  ; pointer to "/bin/echo"

push 0             ; NULL terminator
mov  rdx, rsp      ; envp = empty array
push some_reg    ; holding a pointer to "this is a test"
push rdi           ; pointer to "/bin/echo" = argv[0]
mov  rsi, rsp      ; argv

syscall

Note that envp[] and argv[] are terminated by the same NULL pointer. If you wanted a non-empty envp you couldn't do that.

If this is supposed to be shellcode, you're going to need to replace the push 0 with pushing an xor-zeroed register, and it looks like you could simplify some of the other stuff. But get it working first.

Upvotes: 2

Related Questions