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

Last change on this file since 401 was 401, checked in by Nicholas Riley, 12 years ago

Update mach_inject to build and run on Leopard without #define DARWIN_UNIX03 0.

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