Recent Tool Contributions

Recently I made minor contributions to the online projects: dumpdecrypted and ipainstaller. Within this post, you will find the details of those changes.

So recently I made a couple of minor contributions to online iOS tools. Whilst the contributions are tiny, it was my first experience of actually submitting merge requests to other tools. For this reason I thought I’d share them with you.

1. ipainstaller

One of the changes introduced in iOS 8 was that applications are laid out differently on the device. Essentially, the application exists as multiple containers spread out across the /private/var/mobile/ directory.

The application bundle and the data (e.g. Documents) directories exist in separate locations now.

Previously, on a jailbroken device you could just navigate to the application directory which was something like:

cd /private/var/mobile/Applications/<GUUID>/

Within this application directory, you would find the Documents directory and the Library directory as well as other standard files and directories.

In iOS 8 however, this is no longer the case. This makes it a little more tedious to identify the install location of an App by trivial grepping and instead requires a bit more command line kung-fu.

Instead, what I wanted to do was modify a common and well used tool called ipainstaller so that not only can it list the apps which are installed but also you can obtain the filesystem locations of the application containers, for a given application (bundle) identifier.

For example, with my additions to ipainstaller, you can now do the following:-

Grant-Douglass-iPad:~ root# ipainstaller -i com.apple.Keynote
Identifier: com.apple.Keynote
Version: 2180
Short Version: 2.6.1
Name: Keynote
Display Name: Keynote
Bundle: /private/var/mobile/Containers/Bundle/Application/CC7CCA80-E5DA-409C-96F4-EC5E786139D4
Application: /private/var/mobile/Containers/Bundle/Application/CC7CCA80-E5DA-409C-96F4-EC5E786139D4/Keynote.app
Data: /private/var/mobile/Containers/Data/Application/5BDB0DA7-2681-4A3C-A1D3-D35CA9A9BED6

The code

The main changes are shown here:

if (isGetInfo)
{
    if ([identifiers count] < 1) 
    {
        printf("You must specify at least one application identifier.\n");
        [pool release];
        return IPA_FAILED;
    }

    NSArray *installedApps = getInstalledApplications();

    for (unsigned int i=0; i<[identifiers count]; i++) 
    {
        if ([installedApps containsObject:[identifiers objectAtIndex:i]]) 
        {
            NSString *identifier = [identifiers objectAtIndex:i];
            NSDictionary *installedAppInfo = getInstalledAppInfo(identifier);

            if (!installedAppInfo) {
                if (quietInstall < 2)
                    printf("Application \"%s\" is not installed.\n", [identifier cStringUsingEncoding:NSUTF8StringEncoding]);
                [pool release];
                return IPA_FAILED;
            } 

            NSString *appDirPath = [installedAppInfo objectForKey:@"BUNDLE_PATH"];
            NSString *appPath = [installedAppInfo objectForKey:@"APP_PATH"];
            NSString *dataPath = [installedAppInfo objectForKey:@"DATA_PATH"];
            NSString *appName = [installedAppInfo objectForKey:@"NAME"];
            NSString *appDisplayName = [installedAppInfo objectForKey:@"DISPLAY_NAME"];
            NSString *appVersion = [installedAppInfo objectForKey:@"VERSION"];
            NSString *appShortVersion = [installedAppInfo objectForKey:@"SHORT_VERSION"];

            printf("------------\n");
            //Stuff I care for
            printf("App Name: \"%s\"\n", [appName cStringUsingEncoding:NSUTF8StringEncoding]);
            printf("BundleID: \"%s\"\n", [identifier cStringUsingEncoding:NSUTF8StringEncoding]);
            printf("Bundle Directory: \"%s\"\n", [appDirPath cStringUsingEncoding:NSUTF8StringEncoding]);
            printf("App Directory: \"%s\"\n", [appPath cStringUsingEncoding:NSUTF8StringEncoding]);
            printf("Data Directory: \"%s\"\n", [dataPath cStringUsingEncoding:NSUTF8StringEncoding]);

            //Stuff im not really caring for
            printf("Display Name: \"%s\"\n", [appDisplayName cStringUsingEncoding:NSUTF8StringEncoding]);
            printf("Full Version: \"%s\"\n", [appVersion cStringUsingEncoding:NSUTF8StringEncoding]);
            printf("Short Version: \"%s\"\n", [appShortVersion cStringUsingEncoding:NSUTF8StringEncoding]);
        }
    }
}

If you’re interested, the full pull request is found here.

2. dumpdecrypted

Dumpdecrypted is a very useful tool developed by the iOS/OSX/PHP security researcher [Stefan Esser](). Dumpdecrypted is a dynamic library that when injected into an iOS application, will dump a non-encrypted version of the application binary to disk. This is useful as it allows for static analysis of the binary.

The library has “just worked” for a long time however Apple decided to make some architectural changes in iOS 9 in that the page size for 32bit processes on arm64 devices was increased from 4K to 16K. This resulted in some alignment issues preventing dumpdecrypted from working for 32bit executables when the host was 64bit. As of yet the pull request has not been accepted, but my changes are available for decryption on iOS 9.

The code

The main changes are shown here:

GCC_BIN=`xcrun --sdk iphoneos --find gcc`
GCC_UNIVERSAL=$(GCC_BASE) -arch armv7 -arch armv7s -arch arm64
SDK=`xcrun --sdk iphoneos --show-sdk-path`

CFLAGS = 
GCC_BASE = $(GCC_BIN) -Os $(CFLAGS) -Wimplicit -Wl,-segalign,4000 -isysroot $(SDK) -F$(SDK)/System/Library/Frameworks -F$(SDK)/System/Library/PrivateFrameworks

all: dumpdecrypted.dylib

dumpdecrypted.dylib: dumpdecrypted.o 
	$(GCC_UNIVERSAL) -dynamiclib -o $@ $^

%.o: %.c
	$(GCC_UNIVERSAL) -c -o $@ $< 

clean:
	rm -f *.o dumpdecrypted.dylib

If you’re interested, the pull request is found here.