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

3

I was writing a simple shellcode that would call execve() for an ARM platform (Linux on Raspberry PI) and got stuck with the second argument to execve. As per documentation:

int execve(const char *filename, char *const argv[], char *const envp[]);

Which totally cuts it for me if I call execve("/bin/sh", {NULL}, {NULL}); (from the assembly standpoint):

.data

.section .rodata

.command:
        .string "/bin/sh"

.text

.globl _start

_start: 
        mov r7, #11
        ldr r0, =.command
        eor r1, r1 @ temporarily forget about argv
        eor r2, r2 @ don't mind envp too
        svc #0

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

The assembly above compiles nicely and evokes a shell when run on my test machine that has true /bin/sh. However, all my trouble is that on the particular target box there's no /bin/sh per se, but only a symlink to busybox which necessitates me to execute something like execve("/bin/busybox", {"/bin/busybox", "sh", NULL}, {NULL}).

As to what I understand, arrays are continuous in memory, so all I have to do is to allocate bytes in memory in a continuous manner and then feed pointer to the beginning of what I deem as such "array". With that in mind I tried to the following:

.data

.section .rodata

.command:
        .string "/bin/busybox"

.args:  
        .ascii "/bin/busybox\0"
        .ascii "sh\0"
        .ascii "\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

however with no success. Tried to play around with bytes and just create a series of bytes with null bytes filled to align to 4 bytes, which also didn't work. If the .args label looks like this:

.args:  
        .ascii "/bin/sh\0"
        .ascii "-c\0\0\0"
        .ascii "ls\0\0\0"
        .ascii "\0\0\0\0"

then strace of the program being executed is as below:

$ strace ./shell
execve("./shell", ["./shell"], [/* 19 vars */]) = 0
dup2(0, 4)                              = 4
dup2(1, 4)                              = 4
dup2(2, 4)                              = 4
execve("/bin/sh", [0x6e69622f, 0x68732f, 0x632d, 0x736c00], [/* 0 vars */]) = -1 EFAULT (Bad address)
exit(0)                                 = ?
+++ exited with 0 +++

(Trying to execute /bin/sh -c ls first on the testing machine before coding for /bin/busybox sh).

I ran a similar C program and then debugged it to see how it's done. It appears the location that's passed to r1 contains a bunch of pointers to strings and then, naturally, 0x00:

(gdb) x/4xw 0xbefff764
0xbefff764:     0x000105d0      0x000105d8      0x000105dc      0x00000000

... snip ...

(gdb) p argv
$3 = {0x105d0 "/bin/sh", 0x105d8 "-c", 0x105dc "ls", 0x0}

Question Now that I figured out how memory is laid out, how do I prepare such layout in assembly and correctly pass the second parameter to execve() as an "array" in ARM assembly parlance?

linux
assembly
arm
shellcode
execve
asked on Stack Overflow May 25, 2015 by JSmyth • edited May 25, 2015 by JSmyth

2 Answers

6

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
answered on Stack Overflow May 25, 2015 by JSmyth • edited May 25, 2015 by JSmyth
1

You can use stack pointer to pass parameters. When program is started, first argument (arg[1]) will be in sp+8.

shell.s:

    .text
    .globl _start
    _start: 
        .code 32
            add r3,pc,#1
            bx r3
        .code 16
            ldr r0, [sp, #8] @ load argv[1] to r0
            add r1, sp, #8   @ set &argv[1] to r1
            eor r2, r2       @ set NULL to r2
            mov r7, #11
            svc #1

This code does same as next c code:

#include <unistd.h>

int main(int argc, char *argv[])
{
    execve(argv[1], &argv[1], NULL);
    return 0;
}

Third parameter is envp, it can be set to NULL.

To start /bin/sh:

shell /bin/sh

I hope this helps someone

answered on Stack Overflow Nov 30, 2019 by Sergey Musikhin • edited Dec 1, 2019 by Sergey Musikhin

User contributions licensed under CC BY-SA 3.0