source: trunk/Cocoa/F-Script Anywhere/Source/mach_inject/mach_inject.c @ 217

Last change on this file since 217 was 217, checked in by rchin, 15 years ago
  • Updated to new mach_inject with support for x86
  • Updated nib files to 10.2+ format so that future updates to the f-script framework won't break fsa
  • Adjusted some code to work with the new x86 mach_inject (sends an additional argument)
  • Updates code to work with newer defines in cctools
File size: 11.6 KB
Line 
1 /*******************************************************************************
2        mach_inject.c
3                Copyright (c) 2003-2005 Jonathan 'Wolf' Rentzsch: <http://rentzsch.com>
4                Some rights reserved: <http://creativecommons.org/licenses/by/2.0/>
5
6        ***************************************************************************/
7
8#include        "mach_inject.h"
9
10#include <mach-o/dyld.h>
11#include <mach-o/getsect.h>
12#include <mach/mach.h>
13#include <sys/stat.h>
14#include <sys/errno.h>
15#include <assert.h>
16#include <stdlib.h> // for malloc()
17#include <stdio.h>  // for printf()
18#include <mach-o/fat.h> // for fat structure decoding
19#include <mach-o/arch.h> // to know which is local arch
20#include <fcntl.h> // for open/close
21// for mmap()
22#include <sys/types.h>
23#include <sys/mman.h>
24
25#ifndef COMPILE_TIME_ASSERT( exp )
26        #define COMPILE_TIME_ASSERT( exp ) { switch (0) { case 0: case (exp):; } }
27#endif
28#define ASSERT_CAST( CAST_TO, CAST_FROM ) \
29        COMPILE_TIME_ASSERT( sizeof(CAST_TO)==sizeof(CAST_FROM) )
30
31#if defined(__i386__)
32void* fixedUpImageFromImage (
33                const void *image, 
34                unsigned long imageSize, 
35                unsigned int jumpTableOffset, 
36                unsigned int jumpTableSize,
37                ptrdiff_t fixUpOffset);
38#endif /* __i386__ */
39
40/*******************************************************************************
41*       
42*       Interface
43*       
44*******************************************************************************/
45#pragma mark    -
46#pragma mark    (Interface)
47
48        mach_error_t
49mach_inject(
50                const mach_inject_entry threadEntry,
51                const void                              *paramBlock,
52                size_t                                  paramSize,
53                pid_t                                   targetProcess,
54                vm_size_t                               stackSize )
55{
56        assert( threadEntry );
57        assert( targetProcess > 0 );
58        assert( stackSize == 0 || stackSize > 1024 );
59       
60        //      Find the image.
61        const void              *image;
62        unsigned long   imageSize;
63        unsigned int    jumpTableOffset;
64        unsigned int    jumpTableSize;
65        mach_error_t    err = machImageForPointer( threadEntry, &image, &imageSize, &jumpTableOffset, &jumpTableSize );
66
67        //      Initialize stackSize to default if requested.
68        if( stackSize == 0 )
69                /** @bug
70                        We only want an 8K default, fix the plop-in-the-middle code below.
71                */
72                stackSize = 16 * 1024;
73       
74        //      Convert PID to Mach Task ref.
75        mach_port_t     remoteTask = 0;
76        if( !err ) {
77                err = task_for_pid( mach_task_self(), targetProcess, &remoteTask );
78#if defined(__i386__)
79                if (err == 5) fprintf(stderr, "Could not access task for pid %d. You probably need to add user to procmod group\n", targetProcess);
80#endif
81        }
82       
83        /** @todo
84                Would be nice to just allocate one block for both the remote stack
85                *and* the remoteCode (including the parameter data block once that's
86                written.
87        */
88       
89        //      Allocate the remoteStack.
90        vm_address_t remoteStack = (vm_address_t)NULL;
91        if( !err )
92                err = vm_allocate( remoteTask, &remoteStack, stackSize, 1 );
93       
94        //      Allocate the code.
95        vm_address_t remoteCode = (vm_address_t)NULL;
96        if( !err )
97                err = vm_allocate( remoteTask, &remoteCode, imageSize, 1 );
98        if( !err ) {
99                ASSERT_CAST( pointer_t, image );
100#if defined (__ppc__) || defined (__ppc64__)
101                err = vm_write( remoteTask, remoteCode, (pointer_t) image, imageSize );
102#elif defined (__i386__)
103                // on intel, jump table use relative jump instructions (jmp), which means
104                // the offset needs to be corrected. We thus copy the image and fix the offset by hand.
105                ptrdiff_t fixUpOffset = (ptrdiff_t) (image - remoteCode); 
106                void * fixedUpImage = fixedUpImageFromImage(image, imageSize, jumpTableOffset, jumpTableSize, fixUpOffset);
107                err = vm_write( remoteTask, remoteCode, (pointer_t) fixedUpImage, imageSize );
108                free(fixedUpImage);
109#endif
110        }
111       
112        //      Allocate the paramBlock if specified.
113        vm_address_t remoteParamBlock = (vm_address_t)NULL;
114        if( !err && paramBlock != NULL && paramSize ) {
115                err = vm_allocate( remoteTask, &remoteParamBlock, paramSize, 1 );
116                if( !err ) {
117                        ASSERT_CAST( pointer_t, paramBlock );
118                        err = vm_write( remoteTask, remoteParamBlock,
119                                        (pointer_t) paramBlock, paramSize );
120                }
121        }
122       
123        //      Calculate offsets.
124        ptrdiff_t       threadEntryOffset, imageOffset;
125        if( !err ) {
126                //assert( (void*)threadEntry >= image && (void*)threadEntry <= (image+imageSize) );
127                ASSERT_CAST( void*, threadEntry );
128                threadEntryOffset = ((void*) threadEntry) - image;
129               
130                ASSERT_CAST( void*, remoteCode );
131                imageOffset = ((void*) remoteCode) - image;
132        }
133       
134        //      Allocate the thread.
135        thread_act_t remoteThread;
136#if defined (__ppc__) || defined (__ppc64__)
137        if( !err ) {
138                ppc_thread_state_t remoteThreadState;
139               
140                /** @bug
141                        Stack math should be more sophisticated than this (ala redzone).
142                */
143                remoteStack += stackSize / 2;
144               
145                bzero( &remoteThreadState, sizeof(remoteThreadState) );
146               
147                ASSERT_CAST( unsigned int, remoteCode );
148                remoteThreadState.srr0 = (unsigned int) remoteCode;
149                remoteThreadState.srr0 += threadEntryOffset;
150                assert( remoteThreadState.srr0 < (remoteCode + imageSize) );
151               
152                ASSERT_CAST( unsigned int, remoteStack );
153                remoteThreadState.r1 = (unsigned int) remoteStack;
154               
155                ASSERT_CAST( unsigned int, imageOffset );
156                remoteThreadState.r3 = (unsigned int) imageOffset;
157               
158                ASSERT_CAST( unsigned int, remoteParamBlock );
159                remoteThreadState.r4 = (unsigned int) remoteParamBlock;
160               
161                ASSERT_CAST( unsigned int, paramSize );
162                remoteThreadState.r5 = (unsigned int) paramSize;
163               
164                ASSERT_CAST( unsigned int, 0xDEADBEEF );
165                remoteThreadState.lr = (unsigned int) 0xDEADBEEF;
166               
167#if 0
168                printf( "remoteCode start: %p\n", (void*) remoteCode );
169                printf( "remoteCode size: %ld\n", imageSize );
170                printf( "remoteCode pc: %p\n", (void*) remoteThreadState.srr0 );
171                printf( "remoteCode end: %p\n",
172                        (void*) (((char*)remoteCode)+imageSize) );
173                fflush(0);
174#endif
175               
176                err = thread_create_running( remoteTask, PPC_THREAD_STATE,
177                                (thread_state_t) &remoteThreadState, PPC_THREAD_STATE_COUNT,
178                                &remoteThread );
179        }
180#elif defined (__i386__)
181        if( !err ) {
182               
183                i386_thread_state_t remoteThreadState;
184                bzero( &remoteThreadState, sizeof(remoteThreadState) );
185                       
186                vm_address_t dummy_thread_struct = remoteStack;
187                remoteStack += (stackSize / 2); // this is the real stack
188                // (*) increase the stack, since we're simulating a CALL instruction, which normally pushes return address on the stack
189                remoteStack -= 4;
190               
191#define PARAM_COUNT 4
192#define STACK_CONTENTS_SIZE ((1+PARAM_COUNT) * sizeof(unsigned int))
193                unsigned int stackContents[1 + PARAM_COUNT]; // 1 for the return address and 1 for each param
194                // first entry is return address (see above *)
195                stackContents[0] = 0xDEADBEEF; // invalid return address.
196                // then we push function parameters one by one.
197                stackContents[1] =  imageOffset;
198                stackContents[2] = remoteParamBlock;
199                stackContents[3] = paramSize;
200                // We use the remote stack we allocated as the fake thread struct. We should probably use a dedicated memory zone.
201                // We don't fill it with 0, vm_allocate did it for us
202                stackContents[4] = dummy_thread_struct; 
203               
204                // push stackContents
205                err = vm_write( remoteTask, remoteStack,
206                                                (pointer_t) stackContents, STACK_CONTENTS_SIZE);
207               
208                // set remote Program Counter
209                remoteThreadState.eip = (unsigned int) (remoteCode);
210                remoteThreadState.eip += threadEntryOffset; 
211               
212                // set remote Stack Pointer
213                ASSERT_CAST( unsigned int, remoteStack );
214                remoteThreadState.esp = (unsigned int) remoteStack;
215               
216#if 0
217                printf( "remoteCode start: %p\n", (void*) remoteCode );
218                printf( "remoteCode size: %ld\n", imageSize );
219                printf( "remoteCode pc: %p\n", (void*) remoteThreadState.eip );
220                printf( "remoteCode end: %p\n",
221                                (void*) (((char*)remoteCode)+imageSize) );
222                fflush(0);
223#endif
224                // create thread and launch it
225                err = thread_create_running( remoteTask, i386_THREAD_STATE,
226                                                                         (thread_state_t) &remoteThreadState, i386_THREAD_STATE_COUNT,
227                                                                         &remoteThread );
228        }
229#else
230#error architecture not supported
231#endif
232       
233        if( err ) {
234                if( remoteParamBlock )
235                        vm_deallocate( remoteTask, remoteParamBlock, paramSize );
236                if( remoteCode )
237                        vm_deallocate( remoteTask, remoteCode, imageSize );
238                if( remoteStack )
239                        vm_deallocate( remoteTask, remoteStack, stackSize );
240        }
241       
242        return err;
243}
244
245        mach_error_t
246machImageForPointer(
247                const void *pointer,
248                const void **image,
249                unsigned long *size,
250                unsigned int *jumpTableOffset,
251                unsigned int *jumpTableSize )
252{
253        assert( pointer );
254        assert( image );
255        assert( size );
256       
257        unsigned long p = (unsigned long) pointer;
258       
259        if (jumpTableOffset && jumpTableSize) {
260                *jumpTableOffset = 0;
261                *jumpTableSize = 0;
262        }
263       
264        unsigned long imageIndex, imageCount = _dyld_image_count();
265        for( imageIndex = 0; imageIndex < imageCount; imageIndex++ ) {
266                const struct mach_header *header = _dyld_get_image_header( imageIndex );
267                const struct section *section = getsectbynamefromheader( header,
268                                                                                                                                        SEG_TEXT,
269                                                                                                                                        SECT_TEXT );
270                long start = section->addr + _dyld_get_image_vmaddr_slide( imageIndex );
271                long stop = start + section->size;
272                if( p >= start && p <= stop ) {
273                        //      It is truely insane we have to stat() the file system in order
274                        //      to discover the size of an in-memory data structure.
275                        const char *imageName = _dyld_get_image_name( imageIndex );
276                        assert( imageName );
277                        struct stat sb;
278                        if( stat( imageName, &sb ) )
279                                return unix_err( errno );
280                        if( image ) {
281                                ASSERT_CAST( void*, header );
282                                *image = (void*) header;
283                        }
284                        if( size ) {
285                                ;//assertUInt32( st_size );
286                                *size = sb.st_size;
287                               
288                                // needed for Universal binaries. Check if file is fat and get image size from there.
289                                int fd = open (imageName, O_RDONLY);
290                                size_t mapSize = *size;
291                                char * fileImage = mmap (NULL, mapSize, PROT_READ, MAP_FILE, fd, 0);
292                               
293                                struct fat_header* fatHeader = (struct fat_header *)fileImage;
294                                if (fatHeader->magic == OSSwapBigToHostInt32(FAT_MAGIC)) {
295                                        //printf("This is a fat binary\n");
296                                        uint32_t archCount = OSSwapBigToHostInt32(fatHeader->nfat_arch);
297                                       
298                                        NXArchInfo const *localArchInfo = NXGetLocalArchInfo();
299                                       
300                                        struct fat_arch* arch = (struct fat_arch *)(fileImage + sizeof(struct fat_header));
301                                        struct fat_arch* matchingArch = NULL;
302                                       
303                                        int archIndex = 0;
304                                        for (archIndex = 0; archIndex < archCount; archIndex++) {
305                                                cpu_type_t cpuType = OSSwapBigToHostInt32(arch[archIndex].cputype);
306                                                cpu_subtype_t cpuSubtype = OSSwapBigToHostInt32(arch[archIndex].cpusubtype);
307                                               
308                                                if (localArchInfo->cputype == cpuType) {
309                                                        matchingArch = arch + archIndex;
310                                                        if (localArchInfo->cpusubtype == cpuSubtype) break;
311                                                }
312                                        }
313                                       
314                                        if (matchingArch) {
315                                                *size = OSSwapBigToHostInt32(matchingArch->size);
316                                                //printf ("found arch-specific size : %p\n", *size);
317                                        }
318                                }
319                               
320                                munmap (fileImage, mapSize);
321                                close (fd);
322                        }
323                        if (jumpTableOffset && jumpTableSize) {
324                                const struct section * jumpTableSection = getsectbynamefromheader( header,
325                                                                                                                                                                   "__IMPORT",
326                                                                                                                                                                   "__jump_table" );
327                                if (jumpTableSection) {
328                                        *jumpTableOffset = jumpTableSection->offset;
329                                        *jumpTableSize = jumpTableSection->size;
330                                }
331                        }
332                        return err_none;
333                }
334        }
335       
336        return err_threadEntry_image_not_found;
337}
338
339#if defined(__i386__)
340void* fixedUpImageFromImage (
341                const void *image, 
342                unsigned long imageSize, 
343                unsigned int jumpTableOffset, 
344                unsigned int jumpTableSize, 
345                ptrdiff_t fixUpOffset)
346{
347        // first copy the full image
348        void *fixedUpImage = (void *) malloc ((size_t)imageSize);
349        bcopy(image, fixedUpImage, imageSize);
350       
351        // address of jump table in copied image
352        void *jumpTable = fixedUpImage + jumpTableOffset;
353        // each JMP instruction is 5 bytes (E9 xx xx xx xx) where E9 is the opcode for JMP
354        int jumpTableCount = jumpTableSize / 5;
355       
356        // skip first "E9"
357        jumpTable++;
358       
359        int entry=0;
360        for (entry = 0; entry < jumpTableCount; entry++) {
361                unsigned int jmpValue = *((unsigned int *)jumpTable);
362                jmpValue += fixUpOffset;
363                *((unsigned int *)jumpTable) = jmpValue;
364                jumpTable+=5;
365        }
366       
367        return fixedUpImage;
368}
369#endif /* __i386__ */
Note: See TracBrowser for help on using the repository browser.