sys_execve system call from Assembly
Solution 1
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:
.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.movl file_to_run, %edi
moves the value pointed to by thefile_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 saymovl $file_to_run, %ebx
a few lines further down. (This is a common source of confusion between AT&T syntax and Intel syntax!)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 theleal
instruction should beleal -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
Solution 2
You actually don't need to load anything in the other arguments. If you are doing this in x86 the following simpler code will also work:
.global _main
.section .text
.data
file_to_run:
.asciz "/bin/sh"
.section .text
.globl main
_main:
pushl %ebp
movl %esp, %ebp
movl $11, %eax # sys_execve
movl $file_to_run, %ebx # file to execute
movl $0, %ecx # Null value will work too
movl $0, %edx # Null will works too
int $0x80
leave
ret
This will essentially open a shell terminal after invoking the system call.
Alex F
Updated on August 01, 2022Comments
-
Alex F almost 2 years
asm_execve.s:
.section .data file_to_run: .ascii "/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, -0x4(%ebp) movl $0, -0x8(%ebp) movl $11, %eax # sys_execve movl file_to_run, %ebx # file to execute leal -4(%ebp), %ecx # command line parameters movl $0, %edx # environment block int $0x80 leave ret
makefile:
NAME = asm_execve $(NAME) : $(NAME).s gcc -o $(NAME) $(NAME).s
Program is executed, but sys_execve is not called:
alex@alex32:~/project$ make gcc -o asm_execve asm_execve.s alex@alex32:~/project$ ./asm_execve alex@alex32:~/project$
Expected output is:
alex@alex32:~/project$ ./asm_execve $ exit alex@alex32:~/project$
This Assembly program is supposed to work like the following C code:
char *data[2]; data[0] = "/bin/sh"; data[1] = NULL; execve(data[0], data, NULL);
Something wrong in system call parameters?
-
Peter Cordes over 6 yearsUse
strace -e execve
to trace the execve call your program actually makes.
-
-
Peter Cordes over 6 yearsYou should use
xor %ecx,%ecx
to zero registers. Are you sure that passing non-zero garbage works? Anything other than0
(or a valid pointer) should make the system call return-EFAULT
. NULL pointers forargv
andenvp
are specially documented as Linux-specific behaviour (man7.org/linux/man-pages/man2/execve.2.html), and being equivalent to passing a pointer to a NULL pointer. But that doesn't imply that other bad pointers will "work". That would actually be a bug in the implementation if it went and execed something instead of returning-EFAULT
with non-0 bad pointers -
Peter Cordes over 6 yearsAs a non-portable extension, Linux allows
argv
orenvp
to actually be NULL (like you're doing for envp), soxor %ecx,%ecx
;xor %edx,%edx
. An easier way to createargv[]
on the stack would bemov $file_to_run, %ebx
;push $0
(or a zeroed reg);push %ebx
;mov %esp, %ecx
. -
Shank over 6 yearsYes you are correct. I am sorry about that. I basically meant that you if you wish to open a terminal, you don't need to give any specific arguments and it will still work if you give in null values.
-
Peter Cordes over 6 yearsAre you sure you tested this? You need
$file_to_run
, otherwise you're just loading the first 4 bytes of the string and passing it as a pointer. Also, you save/restoreebp
, but your code clobbersebx
. If you want to return safely instead of crashing whenexecve
returns an error instead of execcing, you shouldpush/pop
ebx as well / instead of making a stack frame. (Perhaps the CRT code is ok if you clobber ebx though.) Optional improvements: your string can go in.rodata
so you don't need a.data
section. You can use.asciiz
to get a zero-terminated string. -
Ruslan almost 5 years@PeterCordes non-portable? We are at the assembly level, what portability could we have here?
-
Peter Cordes almost 5 years@Ruslan: Exactly; there's no downside to taking advantage if you're writing in asm, because there's no reason to expect this non-standard behaviour to change on Linux. The
execve(2)
man page documenting this is talking about C, where it is non-portable.