X86_64 Assembly Execve *Char[] Syscall

x86_64 assembly execve *char[] syscall

At rsp+8 you'll find the address of a string with the program path. The pointer to the first argument is at [rsp+16]. But for execve you need a pointer to an array of pointer to strings which begins with a pointer to a program path (you can (ab)use [rsp+8]).

So change

mov rsi, [rsp + 8]

to

lea rsi, [rsp + 8]

Create an arg array for execve on the stack

You can put the argv array onto the stack and load the address of it into rsi. The first member of argv is a pointer to the program name, so we can use the same address that we load into rdi.

xor edx, edx        ; Load NULL to be used both as the third
; parameter to execve as well as
; to push 0 onto the stack later.
push "-aal" ; Put second argument string onto the stack.
mov rax, rsp ; Load the address of the second argument.
mov rcx, "/bin//ls" ; Load the file name string
push rdx ; and place a null character
push rcx ; and the string onto the stack.
mov rdi, rsp ; Load the address of "/bin//ls". This is
; used as both the first member of argv
; and as the first parameter to execve.

; Now create argv.
push rdx ; argv must be terminated by a NULL pointer.
push rax ; Second arg is a pointer to "-aal".
push rdi ; First arg is a pointer to "/bin//ls"
mov rsi, rsp ; Load the address of argv into the second
; parameter to execve.

This also corrects a couple of other problems with the code in the question. It uses an 8-byte push for the file name, since x86-64 doesn't support 4-byte push, and it makes sure that the file name has a null terminator.

This code does use a 64-bit push with a 4-byte immediate to push "-aal" since the string fits in 4 bytes. This also makes it null terminated without needing a null byte in the code.

I used strings with doubled characters as they are in the question to avoid null bytes in the code, but my preference would be this:

mov ecx, "X-al"     ; Load second argument string,
shr ecx, 8 ; shift out the dummy character,
push rcx ; and write the string to the stack.
mov rax, rsp ; Load the address of the second argument.
mov rcx, "X/bin/ls" ; Load file name string,
shr rcx, 8 ; shift out the dummy character,
push rcx ; and write the string onto the stack.

Note that the file name string gets a null terminator via the shift, avoiding the extra push. This pattern works with strings where a doubled character wouldn't work, and it can be used with shorter strings, too.

Unable to perform execve syscall in RISC V assembly

You're not telling the kernel how many entries there are in the argv array. The rule is that argv needs to be an array of pointers whose last entry is a NULL pointer. This would also be true for the envp argument if you were supplying an envp.

I'd suggest you do this with a preinitialized array also in .rodata, like so:

    .section .rodata
shell:
.string "/bin/sh"
.balign 4
argv:
.4byte shell
.4byte 0

and then your code can be something like

    .section .text
.globl _start
.type _start, @function
_start:
# execve syscall
la a0, shell # Pointer to '/bin/sh'
la a1, addr # Pointer to the array that contains '/bin/sh'
li a2, 0 # No environment variables
li a7, 0xDD # 221 = execve
ecall
# _exit syscall
# if execve returns, it failed, so pass 1 to _exit
li a0, 1
li a7, 0x5D # 93 = _exit
ecall

Assembly , syscall not work as expected. Ubuntu Linux x86_64 , using AT&T syntax

mov     $output,%rsi     # address of string to output moved to rsi
^^^^^^

Address of string. The value $12 is not the character sequence "12". If you wanted to print the string 12, you would need to load 0x31 and 0x32 ('1' and '2') into the memory area (making it big enough) the use 2 as the length.

For example, movw $0x3231, output or better movw $0x3231, output(%rip) to use RIP-relative addressing for static data, like normal for x86-64. (Unlike NASM, GAS syntax doesn't $'12' as a way to write the same integer constant.)

If you want to print an integer as a string, you'll probably want to manipulate it mathematically so you can do it one digit at a time. (Printing an integer as a string with AT&T syntax, with Linux system calls instead of printf)

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

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.

exceve syscall is not working via payload

Thanks @Nate and @o11c, there were couple of problems:

  1. Instead of int 0x80 I should have used syscall, as I was using registers for 64bit like rdi instead of ebx to pass the first arguement. Changed that but it was still not working.

  2. Initially I was using /bin/bash instead of /bin/sh, so the string length was 7 not 9, so the address of argv is address of string + 8 (7 for length and 1 for null character) not 0a (10 ).

  3. As @o11c pointed out argv should end with NULL address. After argv[0] I just put NULL byte, but this is not sufficient the whole 8 bytes should be NULL because it's an address.

After this the code is working fine.

Assembly execve failure -14

  1. You should have narrowed it down to a minimal example. See MCVE.
  2. You should comment your code if you want other people to help.
  3. You should learn to use the debugger and/or other tools.

For point #1, you could have gone down to:

section .text
global _start ;must be declared for linker (ld)
_start:
mov eax,11 ; execve syscall
mov ebx,program ; name of program
mov ecx,[esp+4] ; pointer to argument array
mov ebp,[esp] ; number of arguments
lea edx,[esp+4*ebp+2] ; pointer to environ array
int 0x80
section .data
program db '/bin/echo',0

For point #3, using the debugger you could have seen that:

  • ebx is okay
  • ebp is okay
  • ecx is wrong
  • edx is wrong

It's an easy fix. ecx should be loaded with the address, not the value and edx should be skipping 2 pointers which are 4 bytes each, so the offset should be 8 not 2. The fixed code could look like this:

section .text
global _start ;must be declared for linker (ld)
_start:
mov eax,11 ; execve syscall
mov ebx,program ; name of program
lea ecx,[esp+4] ; pointer to argument array
mov ebp,[esp] ; number of arguments
lea edx,[esp+4*ebp+8] ; pointer to environ array (skip argc and NULL)
int 0x80
section .data
program db '/bin/echo',0


Related Topics



Leave a reply



Submit