1

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

the_endian
  • 2,259
  • 1
  • 24
  • 49
  • Is either of https://stackoverflow.com/q/28135218/56778 or https://stackoverflow.com/q/52171273/56778 helpful to you? – Jim Mischel Dec 05 '19 at 03:50
  • @JimMischel Unfortunately not. One is x86. The other does not work in this circumstance (tried rsp + 8 and + 16 to load a cl arg). – the_endian Dec 05 '19 at 05:12
  • perhaps https://stackoverflow.com/questions/47897025/assembly-execve-bin-bash-x64 ? – Jim Mischel Dec 05 '19 at 06:09
  • 1
    Also, you should check the return value. If `execve` is successful, it doesn't return. If it fails, it returns -1. You could put some code after the call that outputs a string that says "call failed." At least then you could narrow down where the error exists. – Jim Mischel Dec 05 '19 at 06:14
  • @JimMischel that last link is pretty helpful, but I do still have some confusion, I think because I'm not familiar with AT&T syntax, combined with the fact that the answer there is using `mov`s rather than pushing the args. Mostly, I'm unsure of how to properly terminate both the argument string "this is a test" AND the argv array itself separately, AND also the envp. E.g. must I push 0 for envp then 0 to terminate argv, followed by the stack data? Lastly, I'm not sure what he means by "file name" there and how it would apply here. – the_endian Dec 05 '19 at 17:03
  • Also, in NASM you can write stuff like `mov rax, '/bin//sh' ; multi-character constants works fine without any endian munging. – Peter Cordes Dec 05 '19 at 17:35
  • @PeterCordes I'll look into strace. Note that I do have `default rel` on line 2 of the program, and it applies to the whole file. – the_endian Dec 05 '19 at 17:41
  • Oh my bad, I should have searched instead of visually skimming. Yup, that's fine. – Peter Cordes Dec 05 '19 at 17:46
  • The accepted answer to the linked question uses `mov` instructions to put stuff on the stack. Note that it subtracts 16 bytes from the stack pointer, then moves the data in there. The final code example in that answer pretty clearly comments how it works, including terminating the `argv[]` array and the command string. As for AT&T syntax, I agree that it's an abomination. A good start to converting is https://stackoverflow.com/a/24928853/56778. Or, you could assemble it with gas, and then disassemble it into Intel syntax. – Jim Mischel Dec 05 '19 at 22:42
  • @JimMischel you can see here that I applied what I learned from your answers to this question. I'm getting "EFAULT" error status code. – the_endian Dec 07 '19 at 19:16
  • Can you post the `strace` (or MacOS equivalent) output for the execve line so we can see what string and array of pointers you're actually passing? First of all, are you sure MacOS supports passing NULL instead of a pointer *to* NULL for envp? Also, `argv` is pointing to the same string as the path, rather than an array of pointers. So that's could be where the EFAULT is coming from. – Peter Cordes Dec 07 '19 at 19:51
  • @PeterCordes added to bottom of question but for convenience: `execve("/bin/echo", [0x6863652f6e69622f, 0x6f, 0x7420612073696874, 0x747365], NULL) = -1 EFAULT (Bad address)` my confusion lies because I pushed the addresses to stack and provided them to the syscall so I'm not understanding how this array needs to be fixed up in asm. – the_endian Dec 07 '19 at 20:01

1 Answers1

2

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.

Peter Cordes
  • 328,167
  • 45
  • 605
  • 847
  • So the issue was that I needed the ADDRESSES of the stack memory which held the strings to be adjacent in contiguous memory with a NULL terminator "after." Your answer, plus a bit more time in the debugger, helped me realize this. Thank you very much. I still have 14 hrs before I can reward bounty. – the_endian Dec 08 '19 at 04:53