sys_execve system call from Assembly

31,606

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:

  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

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.

Share:
31,606
Alex F
Author by

Alex F

Updated on August 01, 2022

Comments

  • Alex F
    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
      Peter Cordes over 6 years
      Use strace -e execve to trace the execve call your program actually makes.
  • Peter Cordes
    Peter Cordes over 6 years
    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
  • Peter Cordes
    Peter Cordes over 6 years
    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.
  • Shank
    Shank over 6 years
    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.
  • Peter Cordes
    Peter Cordes over 6 years
    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.
  • Ruslan
    Ruslan almost 5 years
    @PeterCordes non-portable? We are at the assembly level, what portability could we have here?
  • Peter Cordes
    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.