Mac OS X PPC Shellcode
Tricks
H D Moore
Chapter 4
Avoiding NULLs
One of
the most common problems encountered with shellcode development in general and
RISC processors in particular is avoiding NULL bytes in the assembled code. On
the IA32 platform, NULL bytes are fairly easy to dodge, mostly due to the
variable-length instruction set and multiple opcodes available
for a
given task. Fixed-width opcode architectures, like PowerPC, have fixed field
sizes and often pad those fields with all zero bits. Instructions that have a
set of undefined bits often set these bits to zero as well. The result is that
a many of the available opcodes are impossible to use with NULL-free shellcode
without
modification.
On many
platforms, self-modifying code can be used to work around NULL byte
restrictions. This technique is not useful for single-instruction patching on PowerPC,
since the instruction pre-fetch and instruction cache can result in the non-modified
instruction being executed instead.
4.1 Undefined Bits
To write
interesting shellcode for Mac OS X, you need to use system calls. One of the
first problems encountered with the PowerPC platform is that the system call
instruction assembles to 0x44000002, which contains two NULL bytes. If we take
a look at the IBM PowerPC reference for the ’sc’ instruction, we see that the
bit layout is as follows:
010001 00000 00000 0000
0000000 000 1 0
------ ----- ----- ----
------- --- - -
A B C D E F G H
These 32
bits are broken down into eight specific fields. The first field (A), which is
5 bits wide, must be set to the value 17. The bits that make up B, C, and D are
all marked as undefined. Field E is must either be set to 1 or 0.
Fields F
and H are undefined, and G must always be set to 1. We can modify the undefined
bits to anything we like, in order to make the corresponding byte values
NULL-free. The first step is to reorder these bits along byte boundaries and
mark what we are able to change.
? = undefined
# = zero or one
[010001??] [????????]
[????0000] [00#???1?]
The first
byte of this instruction can be either 68, 69, 70, or 71 (DEFG). The second
byte can be any character at all. The third byte can either be 0, 16, 32, 48,
64, 80, 96, 112, 128, 144, 160, 176, 192, 208, 224, or 240 (which contains ’0’,
’P’, and ’p’, among others). The fourth value can be any of the following
values:
2, 3, 6, 7, 10, 11, 14, 15, 18, 19, 22, 23, 26, 27, 30, 31, 34, 35, 38, 39, 42,
43, 46, 47, 50, 51, 54, 55, 58, 59, 62, 63. As you can see, it is possible to
create thousands of different opcodes that are all treated by the processor as
a system call. The same technique can be applied to almost any other
instruction that has undefined bits. (Although the current line of
PowerPC chips used with Mac OS X seem to ignore the
undefined
bits, future processors may actually use these bits. It is entirely possible
that undefined bit abuse can prevent your code from working on newer
processors).
;;
;; Patching the
undefined bits in the ’sc’ opcode
;;
main:
li r0, 1 ; sys_exit
li r3, 0 ; exit status
.long 0x45585037 ; sc patched as
"EXP7"
4.2 Index Registers
On the
PowerPC platform, immediate values are encoded using all 16 bits. If the
assembled value of your immediate contains a NULL, you will need to find
another way to load it into the target register. The most common technique is
to first load a NULL-free value into a register, then substract that value
minus the difference to your immediate.
;;
;; Demonstrate index
register usage
;;
main:
li r7, 1999 ; place a
NULL-free value into the index
subi r5, r7, 1999-1 ;
substract our value minus the target
; the r5 register is now
set to 1
If you
have a rough idea of the immediate values you will need in your shellcode, you
can take this a step further. Set your initial index register to a value, that when
decremented by the immediate value, actually results in a character of your
choice. If you have two distant ranges (1-10 and 50-60), then consider using
two index registers. The example below demonstrates an index register that
works for the system call number as well as the arguments, leaving the assembled
bytes NULL-free. As you can see, besides the four bytes required to set the
index register, this method does not significantly increase the size of the code.
;;
;; Create a TCP socket
without NULL bytes
;;
main:
li r7, 0x3330 ;
0x38e03330 = NULL-free index value
subi r0, r7, 0x3330-97 ;
0x3807cd31 = system call for sys_socket
subi r3, r7, 0x3330-2 ;
0x3867ccd2 = socket domain
subi r4, r7, 0x3330-1 ;
0x3887ccd1 = socket type
subi r5, r7, 0x3330-6 ;
0x38a7ccd6 = socket protocol
.long 0x45585037 ; patched ’sc’ instruction
4.3 Branching
Branching
to a forward address without using NULL bytes can be tricky on PowerPC systems.
If you try branching forward, but less than 256 bytes, your opcode will contain
a NULL. If you obtain your current address and want to branch to an offset from
it, you will need to place the target address into the count register (ctr) or
the link register (lr). If you decide to use the link register, you will notice
that every valid form of ”blr” has a NULL byte. You can avoid the NULL byte by
setting the branch hint bits (19-20) to ”11” (unpredictable branch, do not
optimize). The resulting opcode becomes 0x4e804820 instead of 0x4e800020 for
the standard ”blr” instruction.
The
branch prediction bit (bit 10) can also come in handy, it is useful if you need
to change the second byte of the branch instruction to a different character.
The
prediction bit tells the processor how likely it is that the instruction will result
in a branch. To specify the branch prediction bit in the assembly source, just
place ’-’ or ’+’ after the branch instruction.
Tidak ada komentar:
Posting Komentar