Simple code injection using DYLD_INSERT_LIBRARIES

Posted: December 18th, 2012 | Author: | Filed under: code injection, Debugging, DYLD_INSERT_LIBRARIES, macOS, Programming | 15 Comments »

In the following article I will describe a simple method to inject code into executables on Mac OS X 10.8 using the DYLD_INSERT_LIBRARIES environment variable.
I also wrote a simple launcher that starts Calculator.app and injects code to modify the About Box of Calculator.app. When bringing the About Box window of Calculator.app, a custom alert will be displayed:

Modifying Calculator.app AboutBox to display a custom alert

Note: Code injection should be used with care. You should probably not ship applications using code injection and the discussed environment variable DYLD_INSERT_LIBRARIES.

On Mac OS X 10.8, there are several ways to inject code into an arbitrary 64-bit process:

  • writing a plugin if the targeted application supports plugins. Such a solution is possible for applications like Safari, Mail, Xcode… but not for the Calculator.app.
  • Injecting code through Scripting Additions: you create a daemon to watch the launch of the targeted application and this daemon sends a custom Apple event to trigger the code injection. This solution is apparently used by 1Password 3.
  • you can inject code using Mach ports by using mach_override and mach_inject but it has some downsides (you need to be in the procmod group or root).
  • Injecting code through a kernel extension: This is really powerful but the code runs in the kernel.
  • Modifying the binary of the application but this can’t be reused and you need to reapply the changes for each new version of the application.
  • Injecting code using the DYLD_INSERT_LIBRARIES environment variable: this is a really simple solution to implement but you can only inject code in the application you launch.

Now that we know the different ways for injecting code, let’s look more in details at the DYLD_INSERT_LIBRARIES environment variable.

Using DYLD_INSERT_LIBRARIES has several advantages:

  • It is simple to implement.
  • All the code runs in userland and you don’t need to be root or run with the procmod group.
  • You don’t rely on complex third party code.
  • It can be used to inject code in any application you start.
  • You only inject code in a specific application.

It has some downsides:

  • You can only inject code in the applications you start.

On Mac OS X, the dynamic linker (dyld) can be used to load a dynamic library specified in the DYLD_INSERT_LIBRARIES environment variable into an executable. I created a simple dynamic library that replaces the -[CalculatorController showAbout:] method of Calculator.app with a custom implementation. Following is the code of the dynamic library (ACCalculatorOverrides.m). Here is what it does:

  • I created an object called ACCalculatorOverrides with a +(void)load class method. This code is invoked really early by the runtime. It exchanges the implementation of the method -[CalculatorController showAbout:] by the implementation of -[ACCalculatorOverrides patchedShowAbout:].
  • The method -[ACCalculatorOverrides patchedShowAbout:] will call the original method to display the original About Box and then display a custom alert.

#import "ACCalculatorOverrides.h"

#include <stdio.h>
#include <objc/runtime.h>
#include <Foundation/Foundation.h>
#include <AppKit/AppKit.h>

static IMP sOriginalImp = NULL;

@implementation ACCalculatorOverrides

+(void)load
{
	// We replace the method -[CalculatorController showAbout:] with the method -[ACCalculatorOverrides patchedShowAbout:]
	Class originalClass = NSClassFromString(@"CalculatorController");
	Method originalMeth = class_getInstanceMethod(originalClass, @selector(showAbout:));
	sOriginalImp = method_getImplementation(originalMeth);
	
	Method replacementMeth = class_getInstanceMethod(NSClassFromString(@"ACCalculatorOverrides"), @selector(patchedShowAbout:));
	method_exchangeImplementations(originalMeth, replacementMeth);
}

-(void)patchedShowAbout:(id)sender
{
	// We first call the original method to display the original About Box
	sOriginalImp(self, @selector(showAbout:), self);
	
	// Run our custom code which simply display an alert
	NSAlert *alert = [NSAlert alertWithMessageText:@"Code has been injected!" defaultButton:@"OK" alternateButton:nil otherButton:nil informativeTextWithFormat:@"The code has been injected using DYLD_INSERT_LIBRARIES into Calculator.app"];
	[alert runModal];
}

@end

It is possible to manually build this dynamic library by running in the Terminal:

gcc -framework AppKit -framework Foundation -o CalculatorOverrides.dylib -dynamiclib ACCalculatorOverrides.m

and manually injecting it into Calculator.app by running in the Terminal:

DYLD_INSERT_LIBRARIES=/PATH_TO/CalculatorOverrides.dylib /Applications/Calculator.app/Contents/MacOS/Calculator &

But to make it simpler to use, let’s write a launcher. The launcher will be a background-only application (LSUIElement set to YES) that will execute the previously mentioned command line and then quit itself. Here is the code of the launcher:


#import "AppDelegate.h"

@implementation AppDelegate

- (void)dealloc
{
    [super dealloc];
}

-(void)bringToFrontApplicationWithBundleIdentifier:(NSString*)inBundleIdentifier
{
	// Try to bring the application to front
	NSArray* appsArray = [NSRunningApplication runningApplicationsWithBundleIdentifier:inBundleIdentifier];
	if([appsArray count] > 0)
	{
		[[appsArray objectAtIndex:0] activateWithOptions:NSApplicationActivateIgnoringOtherApps];
	}
	
	// Quit ourself
	[[NSApplication sharedApplication] terminate:self];
}

-(void)launchApplicationWithPath:(NSString*)inPath andBundleIdentifier:(NSString*)inBundleIdentifier
{
	if(inPath != nil)
	{
		// Run Calculator.app and inject our dynamic library
		NSString *dyldLibrary = [[NSBundle bundleForClass:[self class]] pathForResource:@"CalculatorOverrides" ofType:@"dylib"];
		NSString *launcherString = [NSString stringWithFormat:@"DYLD_INSERT_LIBRARIES=\"%@\" \"%@\" &", dyldLibrary, inPath];
		system([launcherString UTF8String]);
		
		// Bring it to front after a delay
		[self performSelector:@selector(bringToFrontApplicationWithBundleIdentifier:) withObject:inBundleIdentifier afterDelay:1.0];
	}
}

- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
{
	NSString *calculatorPath = @"/Applications/Calculator.app/Contents/MacOS/Calculator";
	if([[NSFileManager defaultManager] fileExistsAtPath:calculatorPath])
		[self launchApplicationWithPath:calculatorPath andBundleIdentifier:@"com.apple.calculator"];
}

@end

Download: You can download here the compiled Calculator Launcher. If you launch it, it will launch the Calculator.app and inject the code to display an alert when you display the About Box of Calculator.app. If you are interested by the source code, the full sources are available here.


15 Comments on “Simple code injection using DYLD_INSERT_LIBRARIES”

  1. 1 Qusic said at 6:53 am on April 27th, 2013:

    This helps me a lot. Thanks for sharing!

  2. 2 Don Martinez said at 4:38 pm on May 18th, 2014:

    does this work for the new OS X versions like Mavericks and Lion 10.8.3?

  3. 3 Don Martinez said at 5:12 pm on May 18th, 2014:

    could you give an example of this being used to override an integer. like say the year returned or total count of something.

  4. 4 Timac said at 6:25 pm on May 18th, 2014:

    This method works on 10.8.x and 10.9.x.

  5. 5 Don said at 7:53 pm on May 18th, 2014:

    good to know it works for both.

    does it override the method for the whole runtime or only at execution of the application?

    meaning if the method is called multiple times and not only at launch.

  6. 6 Don Martinez said at 6:44 am on May 19th, 2014:

    Testing on a method that returns a value.

    The return value determines the number of days.

    There’s also another methos that’s a boolean that determines if the days have hit zero.

  7. 7 Timac said at 8:34 am on May 19th, 2014:

    This method loads a dynamic library into the targeted application. The changes made are for the whole execution of the targeted application.

    In this example with Calculator, the method showAbout: is replaced. Each time the original showAbout: is called, patchedShowAbout: will in fact be executed. Note that the method patchedShowAbout: first calls the original method showAbout: and then display an alert. You could change the implementation of patchedShowAbout: to always call showAbout: except for the first time.

  8. 8 Timac said at 8:37 am on May 19th, 2014:

    This works whatever the signature of the method is, if it has a return value or not.

  9. 9 Don Martinez said at 11:22 am on May 19th, 2014:

    Timac thanks! This is very useful.

  10. 10 Don Martinez said at 1:23 pm on May 19th, 2014:

    //
    // ACCalculatorOverrides.h
    // CalculatorOverrides
    //
    // Created by Alexandre Colucci on 18/12/12.
    // Copyright (c) 2012 Alexandre Colucci. All rights reserved.
    //

    #import

    @interface ACCalculatorOverrides : NSObject

    -(int)patchedDays:(id)sender;

    @end

    ————————————-

    //
    // ACCalculatorOverrides.m
    // CalculatorOverrides
    //
    // Created by Alexandre Colucci on 18/12/12.
    // Copyright (c) 2012 Alexandre Colucci. All rights reserved.
    //

    #import “ACCalculatorOverrides.h”

    #include
    #include
    #include
    #include

    static IMP sOriginalImp = NULL;

    @implementation ACCalculatorOverrides

    +(void)load
    {
    // We replace the method -[CalculatorController showAbout:] with the method -[ACCalculatorOverrides patchedShowAbout:]
    Class originalClass = NSClassFromString(@”SubscrManager”);
    Method originalMeth = class_getInstanceMethod(originalClass, @selector(daysRemainingOnSubscription:));
    sOriginalImp = method_getImplementation(originalMeth);

    Method replacementMeth = class_getInstanceMethod(NSClassFromString(@”ACCalculatorOverrides”), @selector(patchedDays:));
    method_exchangeImplementations(originalMeth, replacementMeth);
    }

    -(int)patchedDays:(id)sender
    {
    return 30;
    }

    @end

    —————–

    original class is SubscrManager.h and method is daysRemainingOnSubscription

  11. 11 Don Martinez said at 8:18 pm on May 19th, 2014:

    Timac thanks again!

    I was able to get it working. I had a : leftover from the name of the old method. Once I removed that this worked beautifully.

  12. 12 Vlad Korolev said at 6:01 pm on August 14th, 2014:

    Thanks a lot. I had a project long time ago that was doing the same thing on the Solaris. I had to resurrect it and bring back to modern times. Your blog entry was very valuable. Here is the result of my modernization.

    https://github.com/vladistan/ProvTrace

  13. 13 Don Martinez said at 12:00 pm on October 13th, 2014:

    Is there a methos to replace a cfstring with this?

    __cfstring db “apple”, 0

    but i want to change apple to orange

    is that something that could be done with dylib?

  14. 14 vidya said at 5:03 pm on October 16th, 2014:

    could i use this DYLD_INSERT_LIBRARIES to swizzle finder’s context menu?

  15. 15 Timac said at 10:33 pm on November 20th, 2014:

    Well you should probably rather use a public API to do that. There is several possible API depending on your needs:
    – NSService. I wrote some articles about it – see http://blog.timac.org/?tag=nsservice
    – The new Finder App Extension for Yosemite – see https://developer.apple.com/app-extensions/


Leave a Reply