source: trunk/Cocoa/F-Script Anywhere/Source/SCPatch/SCPatchController/SCPatchController.cpp @ 217

Last change on this file since 217 was 217, checked in by rchin, 16 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: 16.8 KB
Line 
1#include <mach-o/dyld.h>
2#include <syslog.h>
3#include <pthread.h>
4#include <CoreFoundation/CoreFoundation.h>
5#include "SCPatchPrivate.h"
6#include "SCPatchCommon.h"
7#include "SCPatchController.h"
8#include "mach_inject.h"
9
10typedef list<SCPatchRecord>::iterator   SCPatchRecordIterator;
11
12//-------------------------------------------------------------------------------------------------------------
13mach_error_t            SCmac_err_FromOSErr(OSErr err) {
14        return err ? (err_mac|err) : err_none;
15}
16
17//-------------------------------------------------------------------------------------------------------------
18OSErr                           SCOSErrFrom_mac_err(mach_error_t error) {
19        return (error & err_mac) ? (err_mac) : noErr;
20}
21
22//-------------------------------------------------------------------------------------------------------------
23#pragma mark -
24//-------------------------------------------------------------------------------------------------------------
25SCPatchController::SCPatchController()
26{
27        mBundle = CFBundleGetMainBundle();
28        CFRetain(mBundle);
29       
30        mApplicationBundleIdentifier = CFBundleGetIdentifier(mBundle);
31        CFRetain(mApplicationBundleIdentifier);
32}
33
34//-------------------------------------------------------------------------------------------------------------
35SCPatchController::SCPatchController(CFStringRef bundleIdentifier)
36{
37    mBundle = CFBundleGetBundleWithIdentifier(bundleIdentifier);
38        CFRetain(mBundle);
39
40        mApplicationBundleIdentifier = bundleIdentifier;
41        CFRetain(mApplicationBundleIdentifier);
42}
43
44//-------------------------------------------------------------------------------------------------------------
45SCPatchController::~SCPatchController(void)
46{
47        if(mApplicationBundleIdentifier)
48                CFRelease(mApplicationBundleIdentifier);
49       
50        if(mBundle)
51                CFRelease(mBundle);
52}
53       
54//-------------------------------------------------------------------------------------------------------------
55#pragma mark -
56//-------------------------------------------------------------------------------------------------------------
57void SCPatchController::AddPatch(CFStringRef bundleIdentifier,
58                                                                CFStringRef subPath,
59                                                                CFStringRef name)
60{
61        SCPatchRecord   patch(mBundle, bundleIdentifier, subPath, name);
62       
63        mPatchList.push_back(patch);
64}
65
66//-------------------------------------------------------------------------------------------------------------
67// Inject the patches into running applications and start watching app launches   
68OSErr SCPatchController::InstallPatches(void)
69{
70        OSErr                                   procErr = noErr, err = noErr;
71        EventTypeSpec                   appSpec[]  = { { kEventClassApplication, kEventAppLaunched },
72                                                                                   { kEventClassApplication, kEventAppTerminated } };
73       
74        // Start listening for messages from patches
75        SetMessageRetryInterval(0.1);   // Try resending messages to patches every 1/10 second.
76        SetMessageRetryLimit(600);              // Give up after 60 seconds.
77       
78        if((err = StartListening(mApplicationBundleIdentifier, false, true)) != noErr)
79        {
80                printf("SCPC: controller could not open port for listening\n");
81        }
82       
83        if(err == noErr)
84        {
85                InstallApplicationEventHandler(NewEventHandlerUPP(ApplicationEventHandler),  2, appSpec, this, NULL);
86               
87            ProcessSerialNumber psn = { 0, kNoProcess };
88           
89            while(!procErr)
90            {
91                if((procErr = GetNextProcess(&psn)) == noErr)
92                {
93                                if(!IsProcessPatched(&psn))
94                                {
95                                InstallPatchesInProcess(&psn);
96                                }
97                                else
98                                {
99                                        ProcessInfoRec                  info;
100                                        CFStringRef                             str;
101                                        Str255                                  pStr;
102                                           
103                                    info.processInfoLength = sizeof(ProcessInfoRec);
104                                    info.processName = pStr;
105                                    info.processAppSpec = nil;
106                                           
107                                        if((err = GetProcessInformation(&psn, &info)) == noErr && 
108                                           (str = CFStringCreateWithPascalString(NULL, pStr, kCFStringEncodingMacRoman)) != NULL)
109                                        {
110                                                PatchNotification(&psn, info.processSignature, info.processType, str, info.processMode);
111                                                CFRelease(str);
112                                        }
113
114                                }
115                        }
116                }
117        }
118        return err;
119}
120
121// Patch a single process (ignoring return value of ShouldPatchProcess)
122mach_error_t SCPatchController::PatchProcess(ProcessSerialNumber *psn)
123{
124        return InstallPatchesInProcess(psn, true);
125}
126
127//-------------------------------------------------------------------------------------------------------------
128// Get info and keep tabs on patched processes
129Boolean SCPatchController::IsProcessPatched(ProcessSerialNumber *inPSN)
130{
131        SCPatchRecordIterator   iter;
132
133        if(mPatchContextList.GetContext(inPSN))
134        {
135                // fprintf(stderr, "Process patched (found context)\n");
136                return true;
137        }
138       
139        for(iter = mPatchList.begin(); iter != mPatchList.end(); iter++)
140        {
141                OSErr   err;
142               
143                err = SendPing(iter->GetIdentifier(), inPSN);
144               
145                // If we get a destPortErr, no one's listening on the desired port.
146                // If we get noErr or noResponseErr, there's someone listening.
147                if(err == noErr || err == noResponseErr)
148                {
149                        pid_t                                   pid;
150                        ProcessInfoRec                  info;
151                        Str255                                  pStr;
152                           
153                    info.processInfoLength = sizeof(ProcessInfoRec);
154                    info.processName = pStr;
155                    info.processAppSpec = nil;
156                           
157                        if((err = GetProcessInformation(inPSN, &info)) == noErr && 
158                           (err = GetProcessPID(inPSN, &pid)) == noErr)
159                        {
160                                mPatchContextList.NewContext(inPSN, pid, info.processSignature);
161                        }
162                        // fprintf(stderr, "Process patched (ping succeeded)\n");
163                        return true;
164                }
165                else
166                {
167                        // fprintf(stderr, "Process not patched (ping failed)\n");
168                }
169        }
170       
171        return false;
172}
173
174//-------------------------------------------------------------------------------------------------------------
175UInt32 SCPatchController::GetPatchFlags(ProcessSerialNumber *inPSN)
176{
177        SCPatchContext  *context = mPatchContextList.GetContext(inPSN);
178       
179        if(context)
180                return context->flags;
181        else
182                return 0;
183}
184
185//-------------------------------------------------------------------------------------------------------------
186void SCPatchController::SetPatchFlags(ProcessSerialNumber *inPSN, UInt32 flags, UInt32 whichFlags)
187{
188        SCPatchContext  *context = mPatchContextList.GetContext(inPSN);
189       
190        if(context)
191                context->flags = (context->flags & ~whichFlags) | flags;
192}
193
194//-------------------------------------------------------------------------------------------------------------
195OSErr SCPatchController::ForEachPatchedProcess(SCPatchIterationProc proc, void *data)
196{
197        SCPatchContextIterator  iter;
198        OSErr                                   err = noErr;
199       
200        for(iter = mPatchContextList.begin(); iter != mPatchContextList.end(); iter++)
201                if((err = proc(&iter->second.psn, iter->second.creator, iter->second.flags, data)) != noErr)
202                        break;
203        return err;
204}
205
206//-------------------------------------------------------------------------------------------------------------
207#pragma mark -
208//-------------------------------------------------------------------------------------------------------------
209OSErr SCPatchController::HandleMessage(const AppleEvent *theAE)
210{
211        OSErr                           err = eventNotHandledErr;
212        OSType                          eventClass, eventID;
213        Size                            actualSize;
214        DescType                        actualType;
215        ProcessSerialNumber     psn;
216
217        if((err = AEGetAttributePtr(theAE, keyEventIDAttr, typeType, &actualType, &eventID, sizeof(OSType), &actualSize)) != noErr ||
218           (err = AEGetAttributePtr(theAE, keyEventClassAttr, typeType, &actualType, &eventClass, sizeof(OSType), &actualSize)) != noErr)
219        {
220                return err;
221        }
222       
223        if(eventClass == kSCMessageClass && eventID == kSCPatchSuccess &&
224           (err = AEGetParamPtr(theAE, keyPSN, typeProcessSerialNumber, &actualType, &psn, sizeof(ProcessSerialNumber), &actualSize)) == noErr)
225        {
226                RecordPatchAndNotify(&psn, NULL);
227        }
228       
229        // Return eventNotHandledErr even though we've done our
230        // thing so that the subclass gets a chance at it too.
231        return eventNotHandledErr;
232}
233
234//-------------------------------------------------------------------------------------------------------------
235pascal OSStatus SCPatchController::ApplicationEventHandler(EventHandlerCallRef handlerRef, 
236                                                                                                                   EventRef event, void *userData)
237{
238        #pragma unused (handlerRef)
239       
240        SCPatchController       *self = (SCPatchController *)userData;
241        UInt32                          kind = GetEventKind(event);
242        OSStatus                        err;
243        ProcessSerialNumber     psn;
244       
245        err = GetEventParameter(event, kEventParamProcessID, typeProcessSerialNumber, 
246                                                    NULL, sizeof(ProcessSerialNumber), NULL, &psn);
247       
248    fprintf(stderr, "ApplicationEventHandler called\n");
249        if(err == noErr)
250        {
251                if(kind == kEventAppLaunched)
252                {
253                        // fprintf(stderr, "SCPC: App launched\n");
254                        if(!self->IsProcessPatched(&psn))
255                                self->InstallPatchesInProcess(&psn);
256                }
257                else if(kind == kEventAppTerminated)
258                {
259                        pid_t                                   pid;
260                        ProcessInfoRec                  info;
261                        CFStringRef                             str;
262                        Str255                                  pStr;
263                           
264                    info.processInfoLength = sizeof(ProcessInfoRec);
265                    info.processName = pStr;
266                    info.processAppSpec = nil;
267                           
268                        if((err = GetProcessInformation(&psn, &info)) == noErr && 
269                           (err = GetProcessPID(&psn, &pid)) == noErr &&
270                           (str = CFStringCreateWithPascalString(NULL, pStr, kCFStringEncodingMacRoman)) != NULL)
271                        {
272                                self->UnpatchNotification(&psn, info.processSignature, info.processType, str, info.processMode);
273                                CFRelease(str);
274                        }
275                        // fprintf(stderr, "SCPC: App died\n");
276                        self->mPatchContextList.DeleteContext(&psn);
277                }
278        }
279       
280        // Always pass the event down
281        return eventNotHandledErr;
282}
283
284//-------------------------------------------------------------------------------------------------------------
285mach_error_t SCPatchController::InstallPatchesInProcess(ProcessSerialNumber *psn, bool onDemand)
286{
287        SCPatchRecordIterator   iter;
288        pid_t                                   pid;
289        ProcessInfoRec                  info;
290        CFStringRef                             str;
291        Str255                                  pStr;
292        OSErr                                   err = noErr;
293        mach_error_t                    error = err_none;
294        SCPatchLoaderParams             *params = NULL;
295           
296    info.processInfoLength = sizeof(ProcessInfoRec);
297    info.processName = pStr;
298    info.processAppSpec = nil;
299           
300        if((err = GetProcessInformation(psn, &info)) == noErr && 
301           (err = GetProcessPID(psn, &pid)) == noErr &&
302           (str = CFStringCreateWithPascalString(NULL, pStr, kCFStringEncodingMacRoman)) != NULL)
303        {
304                // fprintf(stderr, "SCPC: examining application (sig = %.4s, type = %.4s, flags = 0x%x, name = %s)\n",
305                                // &info.processSignature, &info.processType, info.processMode,
306                                // CFStringGetCStringPtr(str, kCFStringEncodingMacRoman));
307                               
308                if((onDemand ||
309                        ShouldPatchProcess(psn, info.processSignature, info.processType, str, info.processMode)) &&
310                   (params = (SCPatchLoaderParams *)malloc(sizeof(SCPatchLoaderParams))) != NULL)
311                {
312                        // fprintf(stderr, "SCPC: patching application (sig = %.4s, type = %.4s, flags = 0x%x, name = %s)\n",
313                                        // &info.processSignature, &info.processType, info.processMode,
314                                        // CFStringGetCStringPtr(str, kCFStringEncodingMacRoman));
315                       
316                        params->version = 1;
317                        params->size = sizeof(SCPatchLoaderParams);
318                        params->patchCount = 0;
319                       
320                        for(iter = mPatchList.begin(); iter != mPatchList.end() && err == noErr; iter++)
321                                err = AddPatchToParams(iter->GetIdentifier(), iter->GetURL(), &params);
322                       
323                        if(err == noErr)
324                                error = InjectPatches(psn, params);
325                        free(params);
326                }
327                CFRelease(str);
328        }
329
330        if (error != err_none) return error;
331        else return mac_err(err);
332}
333
334//-------------------------------------------------------------------------------------------------------------
335OSErr SCPatchController::AddPatchToParams(CFStringRef bundleIdentifier, CFURLRef url, SCPatchLoaderParams **params)
336{
337        OSErr           err = err_couldnt_find_patch_bundle;
338        CFStringRef     patchPath = NULL;
339        size_t          newSize;
340       
341        if(bundleIdentifier == NULL || url == NULL || params == NULL)
342                return paramErr;
343       
344#if 0
345        if((patchPath = CFURLGetString(url)) != NULL)
346                err = noErr;
347#else
348        CFBundleRef     patchBundle;
349        CFURLRef        patchURL;
350
351        // Find out where the patch lives
352        if((patchBundle = CFBundleCreate(kCFAllocatorDefault, url)) != NULL &&
353           (patchURL = CFBundleCopyExecutableURL(patchBundle)) != NULL)
354        {
355                CFURLRef        absoluteURL;
356
357                if((absoluteURL = CFURLCopyAbsoluteURL(patchURL)) != NULL)
358                {
359                        CFRelease(patchURL);
360                        patchURL = absoluteURL;
361                }
362               
363                if((patchPath = CFURLCopyFileSystemPath(patchURL, kCFURLPOSIXPathStyle)) != NULL)
364                        err = noErr;
365
366                CFRelease(patchURL);
367                CFRelease(patchBundle);
368        }
369#endif
370
371        // Increase the size of params.  This allocation may have waste - CFStringGetLength * 2 is the max _possible_ size.
372        if(!err)
373        {
374                newSize = (*params)->size + sizeof(SCPatchLoaderData) + CFStringGetLength(patchPath) * 2;
375               
376                if((*params = (SCPatchLoaderParams *)realloc(*params, newSize)) != NULL)
377                        (*params)->size = newSize;
378                else
379                        err = ENOMEM;
380        }
381       
382        // Fill in all the params
383        if(!err)
384        {
385                HFSUniStr255    *bundleID = SCPatchGetHFSUniStrPointer(*params, (*params)->patchCount);
386                char                    *urlData =SCPatchGetStringPointer(*params, (*params)->patchCount);
387               
388                if((bundleID->length = CFStringGetLength(bundleIdentifier)) > 256)
389                {
390                        fprintf(stderr, "SCPC: patch bundleIdentifier is too long.  It must be 256 chars or less.\n");
391                        err = err_couldnt_load_injection_bundle;
392                }
393                else
394                {
395                        CFStringGetCharacters(bundleIdentifier, CFRangeMake(0, bundleID->length), bundleID->unicode);
396                        if(CFStringGetCString(patchPath, urlData, CFStringGetLength(patchPath) * 2, kCFStringEncodingUTF8))
397                                (*params)->patchCount++;
398                        else
399                                err = err_couldnt_load_injection_bundle;
400                }
401        }
402#if 1
403        if(patchPath)
404                CFRelease(patchPath);
405#endif
406
407        return err;
408}
409
410//-------------------------------------------------------------------------------------------------------------
411mach_error_t SCPatchController::InjectPatches(ProcessSerialNumber *psn, SCPatchLoaderParams *params)
412{
413        mach_error_t            err = err_none;
414       
415        //      Convert PSN to PID.
416        pid_t pid;
417        if(!err)
418                err = mac_err(GetProcessPID(psn, &pid));
419       
420        // Fill in all the params
421        if(!err)
422        {
423                // Make copies of the bundle identifiers and set up other params
424                if((params->parentBundleID.length = CFStringGetLength(mApplicationBundleIdentifier)) > 256)
425                {
426                        fprintf(stderr, "SCPC: app bundleIdentifier is too long.  It must be 256 chars or less.\n");
427                        err = err_couldnt_load_injection_bundle;
428                }
429                else
430                {
431                        params->parent = pid;
432                        CFStringGetCharacters(mApplicationBundleIdentifier, CFRangeMake(0, params->parentBundleID.length), params->parentBundleID.unicode);
433                }
434        }
435       
436        // Find the loader
437        CFURLRef loaderURL = NULL;
438#if defined(__ppc__) || defined(__ppc64__)
439        if(!err)
440                if((loaderURL = CFBundleCopyResourceURL(mBundle, CFSTR("SCPatchLoader"), CFSTR("bundle"), NULL)) == NULL)
441                        err = err_couldnt_find_injection_bundle;
442#else
443        if(!err)
444                if((loaderURL = CFBundleCopyResourceURL(mBundle, CFSTR("SCPatchLoader-intel"), CFSTR("bundle"), NULL)) == NULL)
445                        err = err_couldnt_find_injection_bundle;
446#endif 
447
448#if 0
449        // If we get "file not found", the user must have moved our application bundle.  Try finding it
450        // with Launch Services and going on that way.
451        if(err == err_couldnt_load_injection_bundle)  // REVISIT
452        {
453                CFURLRef        appURL, patchURL;
454                CFStringRef     patchPath;
455               
456                if(LSFindApplicationForInfo(NULL, sApplicationBundleIdentifier, NULL, NULL, &appURL) == noErr)
457                {
458                        if((patchURL = CFURLCreateCopyAppendingPathComponent(NULL, appURL, subPath, false)) != NULL &&
459                           (patchPath = CFURLCopyFileSystemPath(patchURL, kCFURLPOSIXPathStyle)) != NULL)
460                        {
461                                err = PatchControllerLoadPatchForProcess(pid, bundleID, patchPath, true);
462                                CFRelease(patchPath);
463                                CFRelease(patchURL);
464                        }
465                        CFRelease(appURL);
466                }
467        }
468#endif
469
470        //      Create injection bundle instance.
471        CFBundleRef injectionBundle = NULL;
472        if(!err) 
473                if((injectionBundle = CFBundleCreate(kCFAllocatorDefault, loaderURL)) == NULL)
474                        err = err_couldnt_load_injection_bundle;
475       
476        //      Load the thread code injection.
477        void *injectionCode = NULL;
478        if(!err) 
479                if((injectionCode = CFBundleGetFunctionPointerForName(injectionBundle, CFSTR(INJECT_ENTRY_SYMBOL))) == NULL)
480                        err = err_couldnt_find_injectedThread_symbol;
481
482        //      Inject the code.
483        if(!err)
484        {
485                err = ::mach_inject((mach_inject_entry)injectionCode, params, params->size, pid, 0);
486        }
487        else
488        {
489                fprintf(stderr, "Patcher got error %d\n", err);
490        }
491
492        //      Clean up.
493        if(loaderURL)
494                CFRelease(loaderURL);
495        if(injectionBundle)
496                CFRelease(injectionBundle);
497       
498        return err;
499}
500
501//-------------------------------------------------------------------------------------------------------------
502OSErr SCPatchController::RecordPatchAndNotify(ProcessSerialNumber *psn, ProcessInfoRec *info)
503{
504        OSErr                                   err = noErr;
505        ProcessInfoRec                  localInfo;
506        Str255                                  pStr;
507        CFStringRef                             str;
508        pid_t                                   pid;
509       
510        // Make sure we haven't already added this process
511        if(mPatchContextList.GetContext(psn))
512                return noErr;
513
514        // If no info was given, get it ourselves
515        if(!info)
516        {
517                info = &localInfo;
518            info->processInfoLength = sizeof(ProcessInfoRec);
519            info->processName = pStr;
520            info->processAppSpec = nil;
521                err = GetProcessInformation(psn, info);
522        }
523
524        // Now add it to our context list and let the subclass know     
525        if(err == noErr && (err = GetProcessPID(psn, &pid)) == noErr &&
526           (str = CFStringCreateWithPascalString(NULL, info->processName, kCFStringEncodingMacRoman)) != NULL)
527        {
528                mPatchContextList.NewContext(psn, pid, info->processSignature);
529                PatchNotification(psn, info->processSignature, info->processType, str, info->processMode);
530                CFRelease(str);
531        }
532        return err;
533}
Note: See TracBrowser for help on using the repository browser.