What Is How to Call Execve with Arguments in Assembly

What is proper way to call execve with arguments in assembly?

In C, arrays are implicitly converted to pointers to their first elements. So in your case, you need to pass a pointer to an array of pointers to strings. Each array is terminated with a null pointer. Each string is terminated with a NUL byte. The arguments to system calls are passed in ebx, ecx, edx, etc. with the system call number being in eax. Like this:

        section .data
; strings
arg0 db "/bin//nc",0
arg1 db "-lnke",0
arg2 db "/bin/bash",0
arg3 db "-p",0
arg4 db "4444",0

; arrays
align 4
argv dd arg0, arg1, arg2, arg3, arg4, 0
envp dd 0

section .text
global _start
_start: mov eax, 11 ; SYS_execve
mov ebx, arg0 ; filanem
mov ecx, argv ; argv
mov edx, envp ; envp
int 0x80 ; syscall

I'm not sure which operating system you are programming for. This example assumes Linux.

If you cannot use the data segment for some reason, proceed like this:

        ; for easier addressing
mov ebp, esp

; push strings

xor eax, eax
push eax ; - 4
push "4444" ; - 8
push "\0-p\0" ; -12
push "bash" ; -16
push "bin/" ; -20
push "ke\0/" ; -24
push "\0-ln" ; -28
push "//nc" ; -32
push "/bin" ; -36

; push argv, right to left
xor eax, eax
push eax ; NULL
lea ebx, [ebp-8]
push ebx ; "4444\0"
lea ebx, [ebp-11]
push ebx ; "-p\0"
lea ebx, [ebp-21]
push ebx ; "/bin/bash\0"
lea ebx, [ebp-27]
push ebx ; "-lnke\0"
lea ebx, [ebp-36] ; filename
push ebx ; "/bin//nc\0"
mov ecx, esp ; argv
lea edx, [ebp-4] ; envp (NULL)

mov al, 11 ; SYS_execve
int 0x80

In case you can't have null bytes in your data for whatever reason you need to do some cloaking beforehand. For example, you could push each byte xor'ed with 0x80 and xor the data on the stack with 0x80 again afterwards.

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.

shellcode: pass arguments to execve in x86_64 assembly

As Kerrek SB and user9000 point out in the comments, the argv array needed to be a null-terminated array of strings.

Once that is fixed, running this program standalone still won't work, as the strings "/bin/sh" and "/dev/tty" presumably don't exist at that location in the program that you have just compiled, but rather exist at that location in the program that the shell code is designed to target. You need to actually inject it into that program so it will execute there, where those strings are at those addresses.

Execve with argument in x64 with gnu asm

You never pushed a pointer to the second argv string. push %rdx; push %rdi pushes NULL and then the pointer to "/bin/ls", but there is no pointer to your "-laaaaa". You need one more push in between the two. For instance:

    push %rdx           // NULL
lea 8(%rdi), %rcx // pointer to "-laaaaa"
push %rcx
push %rdi // pointer to "/bin/ls"
mov %rsp, %rsi // pointer to the argument vector

Passing arguments to execve(2) via registers

I'm stopped at the first instruction of execve module.

execve is not a "module", it's a libc function, which loads arguments into registers, and then performs the actual system call.

i couldn't found Argument_1 in any register

If you stopped on instruction at address 0xf7ea77f2, you would.

But you are stopped on entry into a C function execve, so the arguments are where a C function expects them. On i*86, the arguments are passed on the stack, so that's where you'll find them: x/3wx $esp is what you want (at that point in the program).

sys_execve system call from Assembly

The execve system call is being called, but you are indeed passing it bad parameters.

(You can see this by running your executable using strace.)

There are three problems:

  1. .ascii does not 0-terminate the string. (You might get lucky, as there is nothing following it in your .data section in this example, but that's not guaranteed...) Add a 0, or use .asciz (or .string) instead.

  2. movl file_to_run, %edi moves the value pointed to by the file_to_run symbol into %edi, i.e. the first 4 bytes of the string (0x6e69622f). The address of the string is just the value of the symbol itself, so you need to use the $ prefix for literal values: movl $file_to_run, %edi. Similarly, you need to say movl $file_to_run, %ebx a few lines further down. (This is a common source of confusion between AT&T syntax and Intel syntax!)

  3. The parameters are placed on the stack in the wrong order: -0x8(%ebp) is a lower address than -0x4(%ebp). So the address of the command string should be written to -0x8(%ebp), the 0 should be written to -0x4(%ebp), and the leal instruction should be leal -8(%ebp), %ecx.


Fixed code:

.section .data
file_to_run:
.asciz "/bin/sh"

.section .text
.globl main

main:
pushl %ebp
movl %esp, %ebp
subl $0x8, %esp # array of two pointers. array[0] = file_to_run array[1] = 0

movl $file_to_run, %edi
movl %edi, -0x8(%ebp)
movl $0, -0x4(%ebp)

movl $11, %eax # sys_execve
movl $file_to_run, %ebx # file to execute
leal -8(%ebp), %ecx # command line parameters
movl $0, %edx # environment block
int $0x80

leave
ret

ARM, GNU assembler: how to pass array arguments to execve()?

Gosh, I just came up with this... Several hours of fiddling around and then 2 minutes after posting my own question an answer hit me... Rubber duck debugging works.

.data

.section .rodata

command:
.string "/bin/sh"

arg0:
.string "/bin/sh"

arg1:
.string "-c"

arg2:
.string "ls"

args:
.word arg0
.word arg1
.word arg2
.word 0

.text

.globl _start

_start:
mov r7, #11
ldr r0, =command
ldr r1, =args
eor r2, r2
svc #0

mov r7, #1
eor r0, r0
svc #0

pathname vs arguments for execve parameters

PATHNAME

The pathname in execve() must be the full path to the executable, such as /bin/ls. If using execvpe(), you could use ls alone as the pathname, but as you already specified, you don’t want to use that.

ARGUMENTS

The arguments should be an array of strings, one for each space-separated argument specified on the command line. The last one should be NULL. The first argument should be the pathname itself. For example:

char* args[] = {"/bin/ls", "-la", "foo/bar", NULL};

ENVIRONMENT

The environment variables cannot be omitted when using execve(). In some implementations, NULL can be passed as the last argument to execve(), but this is not standard. Instead, you should pass a pointer to a null pointer; essentially an empty array of environment variables.

Putting it together

char *args[] ={"/bin/ls", "-foo", "bar", NULL};
//Fork, pid, etc...then
char* nullstr = NULL;
execve(args[0], args, &nullstr);


Related Topics



Leave a reply



Submit