4

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?

2
  • 1
    Label each of your arguments and create an array of those labels and pass the address to that. That's how C string arrays work (char** type). Commented May 25, 2015 at 11:06
  • @Jester That's what I just realized I could do in assembly by looking at some sources. Thanks. Commented May 25, 2015 at 11:11

2 Answers 2

7

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
Sign up to request clarification or add additional context in comments.

10 Comments

The .ascii "\0\0\0\0" is a funny way to say .word 0 :) Also, it's not a good idea to use symbols starting with a dot (.) as they can be confused with directives.
@Jester yeah it is, the code is a mess when fiddling with something so initially it was just a marker for me to inspect memory then I added zeros. I will brush this up later on.
For C strings, you might also find the .asciz directive handier than manually terminating everything.
@Notlikethat I'm busy with shellcode and particularly interested in the raw bytes than anything so I explicitly state the null bytes. Gotta rid those when adjusting the shellcode anyway. I guess the assembly above is just to explain my issue better. My local version is far from the one above (=
what if I want to execve the arguments the program was invoked with?
|
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

2 Comments

For future readers, eor r2, r2 is only a good idea in shellcode for avoiding a 0 byte in the machine code. Unlike x86, the efficient way to zero a register is mov r2, #0. (Just in case anyone was wondering if it's a good idea in general on ARM: it isn't.)
This would be a better answer if you commented the code, e.g. the ldr is loading argv[1], a pointer to "/bin/sh". And r1 gets a pointer to the whole argv[] array. And Linux supports envp=NULL as being the same as envp=pointer to NULL.

Your Answer

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.