- Timestamp:
- 05/09/06 05:56:05 (18 years ago)
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/Cocoa/F-Script Anywhere/Source/mach_inject/mach_inject.c
r153 r217 1 /**************************************************************************************** 2 mach_inject.c $Revision: 1.1.1.1 $ 3 4 Copyright (c) 2003 Red Shed Software. All rights reserved. 5 by Jonathan 'Wolf' Rentzsch (jon * redshed * net) 6 7 ************************************************************************************/ 8 9 #ifdef __cplusplus 10 extern "C" { 11 #endif 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 ***************************************************************************/ 12 7 13 8 #include "mach_inject.h" 14 9 15 #include <mach-o/dyld.h> 16 #include <mach-o/getsect.h> 17 #include <mach/message.h> 18 #include <mach/mach.h> 19 #include <sys/stat.h> 20 #include <assert.h> 21 #include <stdio.h> 22 #include <errno.h> 23 24 /* doesn't show up, for some reason */ 25 void bzero(void *, size_t); 26 27 #ifdef __cplusplus 28 } 29 #endif 30 31 #ifndef COMPILE_TIME_ASSERT 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 ) 32 26 #define COMPILE_TIME_ASSERT( exp ) { switch (0) { case 0: case (exp):; } } 33 27 #endif 34 #define ASSERT_CAST( CAST_TO, CAST_FROM ) COMPILE_TIME_ASSERT( sizeof(CAST_TO)==sizeof(CAST_FROM) ) 35 36 /**************************************************************************************** 28 #define ASSERT_CAST( CAST_TO, CAST_FROM ) \ 29 COMPILE_TIME_ASSERT( sizeof(CAST_TO)==sizeof(CAST_FROM) ) 30 31 #if defined(__i386__) 32 void* 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 /******************************************************************************* 37 41 * 38 42 * Interface 39 43 * 40 ******************************************************************************* *********/44 *******************************************************************************/ 41 45 #pragma mark - 42 46 #pragma mark (Interface) … … 48 52 size_t paramSize, 49 53 pid_t targetProcess, 50 vm_size_t stackSize ) {51 ;//assertCodePtr( threadEntry ); 52 ;//assertPtrIfNotNull( paramBlock);53 ;//assertPositive( targetProcess);54 ;//assertIsTrue( stackSize == 0 || stackSize > 1024 );54 vm_size_t stackSize ) 55 { 56 assert( threadEntry ); 57 assert( targetProcess > 0 ); 58 assert( stackSize == 0 || stackSize > 1024 ); 55 59 56 60 // Find the image. 57 61 const void *image; 58 62 unsigned long imageSize; 59 mach_error_t err = machImageForPointer( threadEntry, &image, &imageSize ); 60 63 unsigned int jumpTableOffset; 64 unsigned int jumpTableSize; 65 mach_error_t err = machImageForPointer( threadEntry, &image, &imageSize, &jumpTableOffset, &jumpTableSize ); 66 61 67 // Initialize stackSize to default if requested. 62 68 if( stackSize == 0 ) 63 /** @bug We only want an 8K default, fix the plop-in-the-middle code below. */ 69 /** @bug 70 We only want an 8K default, fix the plop-in-the-middle code below. 71 */ 64 72 stackSize = 16 * 1024; 65 73 66 74 // Convert PID to Mach Task ref. 67 75 mach_port_t remoteTask = 0; 68 if( !err ) 76 if( !err ) { 69 77 err = task_for_pid( mach_task_self(), targetProcess, &remoteTask ); 70 71 /** @todo Would be nice to just allocate one block for both the remote stack 72 *and* the remoteCode (including the parameter data block once that's 73 written. 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. 74 87 */ 75 88 76 89 // Allocate the remoteStack. 77 vm_address_t remoteStack = NULL;90 vm_address_t remoteStack = (vm_address_t)NULL; 78 91 if( !err ) 79 92 err = vm_allocate( remoteTask, &remoteStack, stackSize, 1 ); 80 93 81 94 // Allocate the code. 82 vm_address_t remoteCode = NULL;95 vm_address_t remoteCode = (vm_address_t)NULL; 83 96 if( !err ) 84 97 err = vm_allocate( remoteTask, &remoteCode, imageSize, 1 ); 85 98 if( !err ) { 86 99 ASSERT_CAST( pointer_t, image ); 100 #if defined (__ppc__) || defined (__ppc64__) 87 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 88 110 } 89 111 90 112 // Allocate the paramBlock if specified. 91 vm_address_t remoteParamBlock = NULL;113 vm_address_t remoteParamBlock = (vm_address_t)NULL; 92 114 if( !err && paramBlock != NULL && paramSize ) { 93 115 err = vm_allocate( remoteTask, &remoteParamBlock, paramSize, 1 ); 94 116 if( !err ) { 95 117 ASSERT_CAST( pointer_t, paramBlock ); 96 err = vm_write( remoteTask, remoteParamBlock, (pointer_t) paramBlock, paramSize ); 118 err = vm_write( remoteTask, remoteParamBlock, 119 (pointer_t) paramBlock, paramSize ); 97 120 } 98 121 } … … 101 124 ptrdiff_t threadEntryOffset, imageOffset; 102 125 if( !err ) { 103 ;//assertIsWithinRange( threadEntry, image, image+imageSize);126 //assert( (void*)threadEntry >= image && (void*)threadEntry <= (image+imageSize) ); 104 127 ASSERT_CAST( void*, threadEntry ); 105 threadEntryOffset = (( long) threadEntry) - (long)image;128 threadEntryOffset = ((void*) threadEntry) - image; 106 129 107 130 ASSERT_CAST( void*, remoteCode ); 108 imageOffset = (( long) remoteCode) - (long)image;131 imageOffset = ((void*) remoteCode) - image; 109 132 } 110 133 111 134 // Allocate the thread. 112 135 thread_act_t remoteThread; 136 #if defined (__ppc__) || defined (__ppc64__) 113 137 if( !err ) { 114 138 ppc_thread_state_t remoteThreadState; 115 139 116 /** @bug Stack math should be more sophisticated than this (ala redzone). */ 140 /** @bug 141 Stack math should be more sophisticated than this (ala redzone). 142 */ 117 143 remoteStack += stackSize / 2; 118 144 … … 139 165 remoteThreadState.lr = (unsigned int) 0xDEADBEEF; 140 166 167 #if 0 141 168 printf( "remoteCode start: %p\n", (void*) remoteCode ); 142 169 printf( "remoteCode size: %ld\n", imageSize ); 143 170 printf( "remoteCode pc: %p\n", (void*) remoteThreadState.srr0 ); 144 printf( "remoteCode end: %p\n", (void*) (((char*)remoteCode)+imageSize) ); 171 printf( "remoteCode end: %p\n", 172 (void*) (((char*)remoteCode)+imageSize) ); 145 173 fflush(0); 174 #endif 146 175 147 176 err = thread_create_running( remoteTask, PPC_THREAD_STATE, … … 149 178 &remoteThread ); 150 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 151 232 152 233 if( err ) { … … 166 247 const void *pointer, 167 248 const void **image, 168 unsigned long *size ) { 169 ;//assertCodePtr( pointer ); 170 ;//assertPtr( image ); 171 ;//assertPtr( size ); 249 unsigned long *size, 250 unsigned int *jumpTableOffset, 251 unsigned int *jumpTableSize ) 252 { 253 assert( pointer ); 254 assert( image ); 255 assert( size ); 172 256 173 257 unsigned long p = (unsigned long) pointer; 258 259 if (jumpTableOffset && jumpTableSize) { 260 *jumpTableOffset = 0; 261 *jumpTableSize = 0; 262 } 174 263 175 264 unsigned long imageIndex, imageCount = _dyld_image_count(); 176 265 for( imageIndex = 0; imageIndex < imageCount; imageIndex++ ) { 177 struct mach_header *header = _dyld_get_image_header( imageIndex ); 178 const struct section *section = getsectbynamefromheader( header, SEG_TEXT, SECT_TEXT ); 266 const struct mach_header *header = _dyld_get_image_header( imageIndex ); 267 const struct section *section = getsectbynamefromheader( header, 268 SEG_TEXT, 269 SECT_TEXT ); 179 270 long start = section->addr + _dyld_get_image_vmaddr_slide( imageIndex ); 180 271 long stop = start + section->size; 181 272 if( p >= start && p <= stop ) { 182 // It is tru ly insane we have to stat() the file system in order to183 // discover the size of an in-memory data structure.184 c har *imageName = _dyld_get_image_name( imageIndex );185 ;//assertPath( imageName );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 ); 186 277 struct stat sb; 187 278 if( stat( imageName, &sb ) ) … … 194 285 ;//assertUInt32( st_size ); 195 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 } 196 331 } 197 332 return err_none; … … 201 336 return err_threadEntry_image_not_found; 202 337 } 338 339 #if defined(__i386__) 340 void* 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 TracChangeset
for help on using the changeset viewer.