9

asm_execve.s:

.section .data
file_to_run:
.ascii "/bin/sh"
.section .text
.globl main
main:
 pushl %ebp
 movl %esp, %ebp
 subl 0ドルx8, %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 0ドルx80 
 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?

Matthew Slattery
47.7k8 gold badges112 silver badges123 bronze badges
asked Feb 18, 2012 at 15:21
1
  • Use strace -e execve to trace the execve call your program actually makes. Commented Oct 3, 2017 at 22:24

2 Answers 2

13

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 0ドルx8, %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 0ドルx80 
 leave
 ret
answered Feb 19, 2012 at 0:51
Sign up to request clarification or add additional context in comments.

3 Comments

As a non-portable extension, Linux allows argv or envp to actually be NULL (like you're doing for envp), so xor %ecx,%ecx ; xor %edx,%edx. An easier way to create argv[] on the stack would be mov $file_to_run, %ebx; push 0ドル (or a zeroed reg); push %ebx; mov %esp, %ecx.
@PeterCordes non-portable? We are at the assembly level, what portability could we have here?
@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.
1

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 0ドルx80 
leave
ret

This will essentially open a shell terminal after invoking the system call.

answered Oct 3, 2017 at 21:10

3 Comments

You should use xor %ecx,%ecx to zero registers. Are you sure that passing non-zero garbage works? Anything other than 0 (or a valid pointer) should make the system call return -EFAULT. NULL pointers for argv and envp 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
Yes 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.
Are 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/restore ebp, but your code clobbers ebx. If you want to return safely instead of crashing when execve returns an error instead of execcing, you should push/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.

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.