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, 16 years ago

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

File size: 11.8 KB
RevLine 
[217]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/>
[153]5
[217]6 ***************************************************************************/
[153]7
8#include "mach_inject.h"
9
[217]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
[401]20#include <fcntl.h> // for open()
21#include <unistd.h> // for close()
[217]22// for mmap()
23#include <sys/types.h>
24#include <sys/mman.h>
[153]25
[217]26#ifndef COMPILE_TIME_ASSERT( exp )
[153]27 #define COMPILE_TIME_ASSERT( exp ) { switch (0) { case 0: case (exp):; } }
28#endif
[217]29#define ASSERT_CAST( CAST_TO, CAST_FROM ) \
30 COMPILE_TIME_ASSERT( sizeof(CAST_TO)==sizeof(CAST_FROM) )
[153]31
[217]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/*******************************************************************************
[153]42*
43* Interface
44*
[217]45*******************************************************************************/
[153]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,
[217]55 vm_size_t stackSize )
56{
57 assert( threadEntry );
58 assert( targetProcess > 0 );
59 assert( stackSize == 0 || stackSize > 1024 );
[153]60
61 // Find the image.
62 const void *image;
63 unsigned long imageSize;
[217]64 unsigned int jumpTableOffset;
65 unsigned int jumpTableSize;
66 mach_error_t err = machImageForPointer( threadEntry, &image, &imageSize, &jumpTableOffset, &jumpTableSize );
67
[153]68 // Initialize stackSize to default if requested.
69 if( stackSize == 0 )
[217]70 /** @bug
71 We only want an 8K default, fix the plop-in-the-middle code below.
72 */
[153]73 stackSize = 16 * 1024;
74
75 // Convert PID to Mach Task ref.
76 mach_port_t remoteTask = 0;
[217]77 if( !err ) {
[153]78 err = task_for_pid( mach_task_self(), targetProcess, &remoteTask );
[217]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 }
[153]83
[217]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.
[153]88 */
89
90 // Allocate the remoteStack.
[217]91 vm_address_t remoteStack = (vm_address_t)NULL;
[153]92 if( !err )
93 err = vm_allocate( remoteTask, &remoteStack, stackSize, 1 );
94
95 // Allocate the code.
[217]96 vm_address_t remoteCode = (vm_address_t)NULL;
[153]97 if( !err )
98 err = vm_allocate( remoteTask, &remoteCode, imageSize, 1 );
99 if( !err ) {
100 ASSERT_CAST( pointer_t, image );
[217]101#if defined (__ppc__) || defined (__ppc64__)
[153]102 err = vm_write( remoteTask, remoteCode, (pointer_t) image, imageSize );
[217]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
[153]111 }
112
113 // Allocate the paramBlock if specified.
[217]114 vm_address_t remoteParamBlock = (vm_address_t)NULL;
[153]115 if( !err && paramBlock != NULL && paramSize ) {
116 err = vm_allocate( remoteTask, &remoteParamBlock, paramSize, 1 );
117 if( !err ) {
118 ASSERT_CAST( pointer_t, paramBlock );
[217]119 err = vm_write( remoteTask, remoteParamBlock,
120 (pointer_t) paramBlock, paramSize );
[153]121 }
122 }
123
124 // Calculate offsets.
125 ptrdiff_t threadEntryOffset, imageOffset;
126 if( !err ) {
[217]127 //assert( (void*)threadEntry >= image && (void*)threadEntry <= (image+imageSize) );
[153]128 ASSERT_CAST( void*, threadEntry );
[217]129 threadEntryOffset = ((void*) threadEntry) - image;
[153]130
131 ASSERT_CAST( void*, remoteCode );
[217]132 imageOffset = ((void*) remoteCode) - image;
[153]133 }
134
135 // Allocate the thread.
136 thread_act_t remoteThread;
[217]137#if defined (__ppc__) || defined (__ppc64__)
[153]138 if( !err ) {
139 ppc_thread_state_t remoteThreadState;
140
[217]141 /** @bug
142 Stack math should be more sophisticated than this (ala redzone).
143 */
[153]144 remoteStack += stackSize / 2;
145
146 bzero( &remoteThreadState, sizeof(remoteThreadState) );
147
148 ASSERT_CAST( unsigned int, remoteCode );
[401]149 remoteThreadState.__srr0 = (unsigned int) remoteCode;
150 remoteThreadState.__srr0 += threadEntryOffset;
151 assert( remoteThreadState.__srr0 < (remoteCode + imageSize) );
[153]152
153 ASSERT_CAST( unsigned int, remoteStack );
[401]154 remoteThreadState.__r1 = (unsigned int) remoteStack;
[153]155
156 ASSERT_CAST( unsigned int, imageOffset );
[401]157 remoteThreadState.__r3 = (unsigned int) imageOffset;
[153]158
159 ASSERT_CAST( unsigned int, remoteParamBlock );
[401]160 remoteThreadState.__r4 = (unsigned int) remoteParamBlock;
[153]161
162 ASSERT_CAST( unsigned int, paramSize );
[401]163 remoteThreadState.__r5 = (unsigned int) paramSize;
[153]164
165 ASSERT_CAST( unsigned int, 0xDEADBEEF );
[401]166 remoteThreadState.__lr = (unsigned int) 0xDEADBEEF;
[153]167
[217]168#if 0
[153]169 printf( "remoteCode start: %p\n", (void*) remoteCode );
170 printf( "remoteCode size: %ld\n", imageSize );
[401]171 printf( "remoteCode pc: %p\n", (void*) remoteThreadState.__srr0 );
[217]172 printf( "remoteCode end: %p\n",
173 (void*) (((char*)remoteCode)+imageSize) );
[153]174 fflush(0);
[217]175#endif
[153]176
177 err = thread_create_running( remoteTask, PPC_THREAD_STATE,
178 (thread_state_t) &remoteThreadState, PPC_THREAD_STATE_COUNT,
179 &remoteThread );
180 }
[217]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
[401]210 remoteThreadState.__eip = (unsigned int) (remoteCode);
211 remoteThreadState.__eip += threadEntryOffset;
[217]212
213 // set remote Stack Pointer
214 ASSERT_CAST( unsigned int, remoteStack );
[401]215 remoteThreadState.__esp = (unsigned int) remoteStack;
[217]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
[153]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,
[217]250 unsigned long *size,
251 unsigned int *jumpTableOffset,
252 unsigned int *jumpTableSize )
253{
254 assert( pointer );
255 assert( image );
256 assert( size );
[153]257
258 unsigned long p = (unsigned long) pointer;
259
[217]260 if (jumpTableOffset && jumpTableSize) {
261 *jumpTableOffset = 0;
262 *jumpTableSize = 0;
263 }
264
[153]265 unsigned long imageIndex, imageCount = _dyld_image_count();
266 for( imageIndex = 0; imageIndex < imageCount; imageIndex++ ) {
[217]267 const struct mach_header *header = _dyld_get_image_header( imageIndex );
268 const struct section *section = getsectbynamefromheader( header,
269 SEG_TEXT,
270 SECT_TEXT );
[153]271 long start = section->addr + _dyld_get_image_vmaddr_slide( imageIndex );
272 long stop = start + section->size;
273 if( p >= start && p <= stop ) {
[217]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 );
[153]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;
[217]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;
[401]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 }
[217]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);
[153]327 }
[217]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 }
[153]337 return err_none;
338 }
339 }
340
341 return err_threadEntry_image_not_found;
[217]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.