diff --git a/DumpDecrypted.h b/DumpDecrypted.h index 1dc30d0..1b35ad5 100644 --- a/DumpDecrypted.h +++ b/DumpDecrypted.h @@ -8,8 +8,12 @@ @property (assign) NSString *appPath; @property (assign) NSString *docPath; -- (id)initWithPathToBinary:(NSString *)pathToBinary; -- (void)createIPAFile; +-(id)initWithPathToBinary:(NSString *)pathToBinary; +-(void)createIPAFile; -(BOOL)dumpDecryptedImage:(const struct mach_header *)image_mh fileName:(const char *)encryptedImageFilenameStr image:(int)imageNum; +-(NSString *)IPAPath; +-(void)IPAServer:(int)listenPort; +-(int)getSocketForPort:(int)listenPort; +-(NSString *)getIPAddress; -@end + @end diff --git a/DumpDecrypted.m b/DumpDecrypted.m index 2a482c0..a78fed5 100644 --- a/DumpDecrypted.m +++ b/DumpDecrypted.m @@ -24,14 +24,27 @@ #include #include #include -#include #include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#import "SSZipArchive.h" #import #import -#include -#include #include "DumpDecrypted.h" -#import "SSZipArchive.h" + +//#define DEBUG(...) NSLog(__VA_ARGS__); +#define DEBUG(...) {} #define swap32(value) (((value & 0xFF000000) >> 24) | ((value & 0x00FF0000) >> 8) | ((value & 0x0000FF00) << 8) | ((value & 0x000000FF) << 24) ) @@ -263,6 +276,7 @@ NSLog(@"[+] Close files.\n"); close(fd); close(outfd); + sync(); NSLog(@"[+] Dump complete.\n"); return true; @@ -304,9 +318,114 @@ return true; } +-(NSString *)IPAPath { + return [NSString stringWithFormat:@"%@/decrypted-app.ipa", [self docPath]]; +} + +// Based on code taken from Bishop Fox Firecat +-(int)getSocketForPort:(int)listenPort { + struct sockaddr_in a; + int IPAServerSock, clientSock; + int yes = 1; + + // get a fresh juicy socket + DEBUG(@"[dumpDecrypted] socket()"); + if((IPAServerSock = socket(PF_INET, SOCK_STREAM, 0)) < 0) { + NSLog(@"ERROR: socket()"); + return 0; + } + + // make sure it's quickly reusable + DEBUG(@"[dumpDecrypted] setsockopt()"); + if(setsockopt(IPAServerSock, SOL_SOCKET, SO_REUSEADDR, (char *) &yes, sizeof(yes)) < 0) { + NSLog(@"ERROR: setsockopt()"); + close(IPAServerSock); + return 0; + } + + // listen on all of the hosts interfaces/addresses (0.0.0.0) + DEBUG(@"[dumpDecrypted] bind()"); + memset(&a, 0, sizeof(a)); + a.sin_port = htons(listenPort); + a.sin_addr.s_addr = htonl(INADDR_ANY); + a.sin_family = AF_INET; + if(bind(IPAServerSock, (struct sockaddr *) &a, sizeof(a)) < 0) { + NSLog(@"ERROR: bind()"); + close(IPAServerSock); + return 0; + } + DEBUG(@"[dumpDecrypted] listen()"); + listen(IPAServerSock, 10); + + return IPAServerSock; +} + +-(void)IPAServer:(int)listenPort { + unsigned int i; + struct sockaddr_in clientAddr; + int serverSock, clientSock; + + // get a fresh juicy socket + DEBUG(@"[dumpDecrypted] getSocketForPort()"); + if( ! (serverSock = [self getSocketForPort:listenPort])) { + NSLog(@"ERROR: socket()"); + return; + } + + i = sizeof(clientAddr); + + NSLog(@"[bfdecrypt] Waiting for connection on port %d\n",listenPort); + if((clientSock = accept(serverSock, (struct sockaddr *)&clientAddr, &i)) == -1) { + NSLog(@"ERROR: accept(): %s", strerror(errno)); + return; + } + + NSLog(@"[bfdecrypt] Got connection from remote target %s\n", inet_ntoa(clientAddr.sin_addr)); + int fd = open([[self IPAPath] UTF8String], O_RDONLY); + if(!fd) { + NSLog(@"[bfdecrypt] Failed to open the IPA file %@!", [self IPAPath]); + return; + } + + // I wanted to use sendfile(2), but it's sandboxed by the kernel. + char buffer[65535]; + int loopCount=0, totalBytes=0; + DEBUG(@"[bfdecrypt] Entering loop"); + while (1) { + int bytes_read = read(fd, buffer, sizeof(buffer)); + totalBytes += bytes_read; + DEBUG(@"[bfdecrypt] %d: Read %d (%d total) bytes from IPA file", loopCount++, bytes_read, totalBytes); + if(bytes_read == 0) // We're done reading from the file + break; + + if(bytes_read < 0) { + NSLog(@"[bfdecrypt] Failed to read() from IPA file"); + break; + } + + void *p = buffer; + while(bytes_read > 0) { + DEBUG(@"[bfdecrypt] Sending %d bytes", bytes_read); + int bytes_written = send(clientSock, p, bytes_read, 0); + if (bytes_written <= 0) { + // handle errors + NSLog(@"[bfdecrypt] Error sending!"); + break; + } + bytes_read -= bytes_written; + p += bytes_written; + } + } + + close(fd); + shutdown(clientSock, SHUT_RDWR); + shutdown(serverSock, SHUT_RDWR); + close(clientSock); + close(serverSock); +} -(void) createIPAFile { - NSString *IPAFile = [NSString stringWithFormat:@"%@/decrypted-app.ipa", [self docPath]]; + NSString *IPAFile = [self IPAPath]; NSString *appDir = [self appPath]; NSString *appCopyDir = [NSString stringWithFormat:@"%@/ipa/Payload/%s", [self docPath], self->appDirName]; NSString *zipDir = [NSString stringWithFormat:@"%@/ipa", [self docPath]]; @@ -355,8 +474,37 @@ // Clean up. Leave only the .ipa file. [fm removeItemAtPath:zipDir error:nil]; - NSLog(@"[dumpdecrypted] Wrote %s/decrypted-app.ipa", [[self docPath] UTF8String]); + NSLog(@"[dumpdecrypted] Wrote %@", [self IPAPath]); return; } + +// https://stackoverflow.com/questions/6807788/how-to-get-ip-address-of-iphone-programmatically +- (NSString *)getIPAddress { + + NSString *address = @"error"; + struct ifaddrs *interfaces = NULL; + struct ifaddrs *temp_addr = NULL; + int success = 0; + // retrieve the current interfaces - returns 0 on success + success = getifaddrs(&interfaces); + if (success == 0) { + // Loop through linked list of interfaces + temp_addr = interfaces; + while(temp_addr != NULL) { + if(temp_addr->ifa_addr->sa_family == AF_INET) { + // Check if interface is en0 which is the wifi connection on the iPhone + if([[NSString stringWithUTF8String:temp_addr->ifa_name] isEqualToString:@"en0"]) { + // Get NSString from C String + address = [NSString stringWithUTF8String:inet_ntoa(((struct sockaddr_in *)temp_addr->ifa_addr)->sin_addr)]; + } + } + temp_addr = temp_addr->ifa_next; + } + } + // Free memory + freeifaddrs(interfaces); + return address; +} + @end diff --git a/SSZipArchive/SSZipArchive.h b/SSZipArchive/SSZipArchive.h old mode 100644 new mode 100755 diff --git a/SSZipArchive/SSZipArchive.m b/SSZipArchive/SSZipArchive.m old mode 100644 new mode 100755 diff --git a/SSZipArchive/minizip/ioapi.c b/SSZipArchive/minizip/ioapi.c old mode 100644 new mode 100755 diff --git a/SSZipArchive/minizip/ioapi.h b/SSZipArchive/minizip/ioapi.h old mode 100644 new mode 100755 diff --git a/SSZipArchive/minizip/ioapi_buf.c b/SSZipArchive/minizip/ioapi_buf.c old mode 100644 new mode 100755 diff --git a/SSZipArchive/minizip/zip.c b/SSZipArchive/minizip/zip.c old mode 100644 new mode 100755 diff --git a/bfdecrypt.dylib b/bfdecrypt.dylib old mode 100644 new mode 100755 index 0d1cd1d..ca5c4e7 Binary files a/bfdecrypt.dylib and b/bfdecrypt.dylib differ diff --git a/bfdecrypt.dylib.dSYM/Contents/Info.plist b/bfdecrypt.dylib.dSYM/Contents/Info.plist new file mode 100644 index 0000000..cc23205 --- /dev/null +++ b/bfdecrypt.dylib.dSYM/Contents/Info.plist @@ -0,0 +1,20 @@ + + + + + CFBundleDevelopmentRegion + English + CFBundleIdentifier + com.apple.xcode.dsym.bfdecrypt.dylib + CFBundleInfoDictionaryVersion + 6.0 + CFBundlePackageType + dSYM + CFBundleSignature + ???? + CFBundleShortVersionString + 1.0 + CFBundleVersion + 1 + + diff --git a/bfdecrypt.dylib.dSYM/Contents/Resources/DWARF/bfdecrypt.dylib b/bfdecrypt.dylib.dSYM/Contents/Resources/DWARF/bfdecrypt.dylib new file mode 100644 index 0000000..44ea011 Binary files /dev/null and b/bfdecrypt.dylib.dSYM/Contents/Resources/DWARF/bfdecrypt.dylib differ diff --git a/bfdecrypt.m b/bfdecrypt.m index f05d6e3..fd571a3 100644 --- a/bfdecrypt.m +++ b/bfdecrypt.m @@ -11,20 +11,107 @@ #include #include #import +#include #include "DumpDecrypted.h" +#define PORT 31336 +//#define DEBUG(...) NSLog(__VA_ARGS__); +#define DEBUG(...) {} + +// The dylib constructor sets decryptedIPAPath, spawns a thread to do the app decryption, then exits. __attribute__ ((constructor)) static void bfinject_rocknroll() { NSLog(@"[bfdecrypt] Spawning thread to do decryption in the background..."); dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ + NSLog(@"[bfdecrypt] Inside decryption thread"); const char *fullPathStr = _dyld_get_image_name(0); - - NSLog(@"[bfdecrypt] Inside thread. Full path to app: %s", fullPathStr); DumpDecrypted *dd = [[DumpDecrypted alloc] initWithPathToBinary:[NSString stringWithUTF8String:fullPathStr]]; + if(!dd) { + NSLog(@"[bfdecrypt] ERROR: failed to get DumpDecrypted instance"); + return; + } + + NSLog(@"[bfdecrypt] Full path to app: %s /// IPA File: %@", fullPathStr, [dd IPAPath]); + + // Show a "Decrypting!" alert on the device and block the UI + UIAlertController *alertController = [UIAlertController + alertControllerWithTitle:@"Decrypting" + message:@"Please wait, this will take a few seconds..." + preferredStyle:UIAlertControllerStyleAlert]; + UIViewController *root = [[[UIApplication sharedApplication] keyWindow] rootViewController]; + [root presentViewController:alertController animated:YES completion:nil]; + + // Do the decryption [dd createIPAFile]; + // Dismiss the alert box + [alertController dismissViewControllerAnimated:NO completion:nil]; + + // Would the user like to host the IPA on a port for easy retrieval with NetCat? + NSString *message = [NSString stringWithFormat:@"Would you like to serve the IPA on %@:31336 for retrieval with NetCat?\nYou can use nc %@:31336 > /tmp/decrypted.ipa.", [dd getIPAddress], [dd getIPAddress]]; + alertController = [UIAlertController + alertControllerWithTitle:@"Decryption Complete!" + message:message + preferredStyle:UIAlertControllerStyleAlert]; + + UIAlertAction *cancelAction = [UIAlertAction + actionWithTitle:NSLocalizedString(@"No", @"Cancel action") + style:UIAlertActionStyleCancel + handler:^(UIAlertAction *action) + { + NSLog(@"Cancel action"); + [alertController dismissViewControllerAnimated:NO completion:nil]; + }]; + + UIAlertAction *okAction = [UIAlertAction + actionWithTitle:NSLocalizedString(@"Yes", @"OK action") + style:UIAlertActionStyleDefault + handler:^(UIAlertAction *action) + { + NSLog(@"OK action"); + + // Removing the "Decryption Complete!" alert + [alertController dismissViewControllerAnimated:NO completion:nil]; + + // Spawn server on port 31336 by default + // Connections to this port will be sent a raw copy of the IPA file. + NSLog(@"[bfdecrypt] Checking we can start the server..."); + int s; + if( ! (s = [dd getSocketForPort:PORT])) { + // Port 31336 isn't available. Something listening? + NSLog(@"[bfdecrypt] Couldn't listen on port %d, is another decrypted app still running?", PORT); + UIAlertController *errorController = [UIAlertController + alertControllerWithTitle:@"Error!" + message:@"Could not start server on port 31336, perhaps there's another decryption server already listening?" + preferredStyle:UIAlertControllerStyleAlert + ]; + [errorController addAction:[UIAlertAction + actionWithTitle:NSLocalizedString(@"Ok", @"Ok") + style:UIAlertActionStyleDefault + handler:^(UIAlertAction *action) + { + NSLog(@"Cancel action"); + [errorController dismissViewControllerAnimated:NO completion:nil]; + }] + ]; + [root presentViewController:errorController animated:YES completion:nil]; + } else { + close(s); + NSLog(@"[bfdecrypt] Yes we can, starting now."); + [dd IPAServer:PORT]; + NSLog(@"[bfdecrypt] IPAServer finished."); + } + }]; + + [alertController addAction:cancelAction]; + [alertController addAction:okAction]; + [root presentViewController:alertController animated:YES completion:nil]; + NSLog(@"[bfdecrypt] Over and out."); + while(1) + sleep(9999999); }); - NSLog(@"[bfdecrypt] Exiting constructor."); + NSLog(@"[bfdecrypt] All done, exiting constructor."); } +