diff --git a/.github/workflows/build-deb.yml b/.github/workflows/build-deb.yml index dc8da07..fc9628e 100644 --- a/.github/workflows/build-deb.yml +++ b/.github/workflows/build-deb.yml @@ -2,14 +2,10 @@ name: C/C++ CI on: push: - #tags: - # - 'v*' - - #branches: [ master ] jobs: build: - runs-on: macos-latest + runs-on: macos-13 strategy: matrix: provider: [TFP0, LIBKRW, LIBKERNRW] @@ -20,23 +16,29 @@ jobs: submodules: recursive - name: Prepare Theos - uses: Randomblock1/theos-action@v1 + uses: und3fined/theos-action@main - name: Build package run: | + cp -rf include/* $THEOS/vendor/include/ rm -f packages/* - cp control.template control + if [[ ${{matrix.provider}} == TFP0 ]]; then + cp control.tfp0 control USE_TFP0=1 make package FINALPACKAGE=1 elif [[ ${{matrix.provider}} == LIBKRW ]]; then - sed -i '' 's/{{.depends}}/libkrw/g' control + cp control.krw control USE_LIBKRW=1 make package FINALPACKAGE=1 elif [[ ${{matrix.provider}} == LIBKERNRW ]]; then - sed -i '' 's/{{.depends}}/libkernrw0/g' control + cp control.kernrw control USE_LIBKERNRW=1 make package FINALPACKAGE=1 fi + env: + DEVELOPER_DIR: /Applications/Xcode_14.2.app/Contents/Developer + # LDFLAGS: "-L/usr/local/opt/llvm/lib" + # CPPFLAGS: "-I/usr/local/opt/llvm/include" - name: Publish artifact - uses: actions/upload-artifact@v2 + uses: actions/upload-artifact@v4 with: - name: fouldecrypt-${{matrix.provider}} + name: foulfolder-${{matrix.provider}} path: ${{ github.workspace }}/packages/*.deb diff --git a/.gitignore b/.gitignore index 2b6fba8..d19e342 100755 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,37 @@ packages other's proj priv_include +CMakeLists.txt +.idea/ +cmake-build-*/ + +### macOS ### +# General +.DS_Store +.AppleDouble +.LSOverride + +# Icon must end with two \r +Icon + + +# Thumbnails +._* + +# Files that might appear in the root of a volume +.DocumentRevisions-V100 +.fseventsd +.Spotlight-V100 +.TemporaryItems +.Trashes +.VolumeIcon.icns +.com.apple.timemachine.donotpresent + +# Directories potentially created on remote AFP share +.AppleDB +.AppleDesktop +Network Trash Folder +Temporary Items +.apdisk + +control \ No newline at end of file diff --git a/.gitmodules b/.gitmodules index e7745a3..f3f0070 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,3 +1,3 @@ [submodule "kerninfra"] path = kerninfra - url = https://github.com/NyaMisty/KernInfra + url = git@github.com:und3fined/KernInfra.git diff --git a/Makefile b/Makefile index 2400d46..93c0690 100644 --- a/Makefile +++ b/Makefile @@ -1,16 +1,13 @@ -TARGET := iphone:clang:latest:7.0 +TARGET := iphone:clang:12.4:7.0 ARCHS = arm64 arm64e +export ADDITIONAL_CFLAGS = -DTHEOS_LEAN_AND_MEAN -fobjc-arc include $(THEOS)/makefiles/common.mk -TOOL_NAME = fouldecrypt flexdecrypt2 - -# USE_TFP0 = 1 -# USE_LIBKRW = 1 -# USE_LIBKERNRW = 1 +TOOL_NAME = fouldecrypt flexdecrypt2 foulwrapper foulfolder fouldlopen fouldecrypt_FILES = main.cpp foulmain.cpp -fouldecrypt_CFLAGS = -fobjc-arc -Wno-unused-variable # -Ipriv_include +fouldecrypt_CFLAGS = -fobjc-arc -Wno-unused-variable -Ipriv_include fouldecrypt_CCFLAGS = $(fouldecrypt_CFLAGS) fouldecrypt_CODESIGN_FLAGS = -Sentitlements.plist fouldecrypt_INSTALL_PATH = /usr/local/bin @@ -19,7 +16,7 @@ fouldecrypt_LDFLAGS += -Lkerninfra/libs fouldecrypt_CCFLAGS += -std=c++2a flexdecrypt2_FILES = main.cpp flexwrapper.cpp -flexdecrypt2_CFLAGS = -fobjc-arc -Wno-unused-variable # -Ipriv_include +flexdecrypt2_CFLAGS = -fobjc-arc -Wno-unused-variable -Ipriv_include flexdecrypt2_CCFLAGS = $(flexdecrypt2_CFLAGS) flexdecrypt2_CODESIGN_FLAGS = -Sentitlements.plist flexdecrypt2_INSTALL_PATH = /usr/local/bin @@ -27,4 +24,23 @@ flexdecrypt2_SUBPROJECTS = kerninfra flexdecrypt2_LDFLAGS += -Lkerninfra/libs flexdecrypt2_CCFLAGS += -std=c++2a +foulwrapper_FILES = foulwrapper.m +foulwrapper_CFLAGS = -fobjc-arc -Wno-unused-variable -Ix_include +foulwrapper_CCFLAGS = $(foulwrapper_CFLAGS) +foulwrapper_CODESIGN_FLAGS = -Sentitlements.plist +foulwrapper_INSTALL_PATH = /usr/local/bin +foulwrapper_FRAMEWORKS = Foundation MobileCoreServices +foulwrapper_PRIVATE_FRAMEWORKS = MobileContainerManager +foulwrapper_LIBRARIES = applist + +foulfolder_FILES = foulfolder.m +foulfolder_CFLAGS = -fobjc-arc -Wno-unused-variable -Ix_include +foulfolder_CCFLAGS = $(foulfolder_CFLAGS) +foulfolder_CODESIGN_FLAGS = -Sentitlements.plist +foulfolder_INSTALL_PATH = /usr/local/bin +foulfolder_FRAMEWORKS = Foundation MobileCoreServices + +fouldlopen_FILES = fouldlopen.c +fouldlopen_INSTALL_PATH = /usr/local/bin + include $(THEOS_MAKE_PATH)/tool.mk diff --git a/README.md b/README.md index e0334b7..5e5e736 100755 --- a/README.md +++ b/README.md @@ -31,6 +31,12 @@ Install the correct version: Run `fouldecrypt` on an encrypted binary. +## About `foulwrapper` + +`foulwrapper` will find all Mach-Os in a specific application and decrypt them using `fouldecrypt`: + +`usage: foulwrapper (application name or bundle identifier)` + ## Credits @meme: foulplay @JohnCoates: flexdecrypt diff --git a/control.kernrw b/control.kernrw new file mode 100644 index 0000000..ee94f82 --- /dev/null +++ b/control.kernrw @@ -0,0 +1,10 @@ +Package: dev.und3fy.foulfolder.tauri +Name: foulfolder (Tauri) +Version: 0.2.0 +Architecture: iphoneos-arm +Depends: applist,zip,libkernrw0 +Description: Directly decrypt binaries like fouldecrypt, but also supports iOS 14 +Maintainer: und3fined +Author: misty +Section: System +Tag: role::hacker diff --git a/control.krw b/control.krw new file mode 100644 index 0000000..da4d085 --- /dev/null +++ b/control.krw @@ -0,0 +1,10 @@ +Package: dev.und3fy.foulfolder.unc0ver +Name: foulfolder (unc0ver) +Version: 0.2.0 +Architecture: iphoneos-arm +Depends: applist,zip,libkrw +Description: Directly decrypt binaries like fouldecrypt, but also supports iOS 14 +Maintainer: und3fined +Author: misty +Section: System +Tag: role::hacker diff --git a/control.template b/control.template deleted file mode 100644 index 203ec00..0000000 --- a/control.template +++ /dev/null @@ -1,10 +0,0 @@ -Package: moe.misty.fouldecrypt -Name: fouldecrypt -Version: 0.0.4 -Architecture: iphoneos-arm -Depends: {{.depends}} -Description: Directly decrypt binaries like flexdecrypt, but also supports iOS 14 -Maintainer: misty -Author: misty -Section: System -Tag: role::hacker diff --git a/control.tfp0 b/control.tfp0 new file mode 100644 index 0000000..ec2af41 --- /dev/null +++ b/control.tfp0 @@ -0,0 +1,10 @@ +Package: dev.und3fy.foulfolder +Name: foulfolder +Version: 0.2.0 +Architecture: iphoneos-arm +Depends: applist,zip +Description: Directly decrypt binaries like fouldecrypt, but also supports iOS 14 +Maintainer: und3fined +Author: misty +Section: System +Tag: role::hacker diff --git a/entitlements.plist b/entitlements.plist index 73f3176..af68853 100644 --- a/entitlements.plist +++ b/entitlements.plist @@ -2,31 +2,37 @@ - application-identifier - - - com.apple.developer.team-identifier - - - com.apple.diagnosticd.diagnostic - - com.apple.frontboard.debugapplications - - com.apple.multitasking.termination - - com.apple.private.cs.debugger - - com.apple.private.security.no-sandbox - - com.apple.private.skip-library-validation - - com.apple.springboard.launchapplications - - dynamic-codesigning - - get-task-allow - - platform-application - - task_for_pid-allow - + application-identifier + - + com.apple.developer.team-identifier + - + com.apple.diagnosticd.diagnostic + + com.apple.frontboard.debugapplications + + com.apple.multitasking.termination + + com.apple.private.cs.debugger + + com.apple.private.security.no-sandbox + + com.apple.private.skip-library-validation + + com.apple.private.MobileContainerManager.allowed + + com.apple.private.MobileContainerManager.lookup + + com.apple.private.MobileContainerManager.otherIdLookup + + com.apple.springboard.launchapplications + + dynamic-codesigning + + get-task-allow + + platform-application + + task_for_pid-allow + diff --git a/fouldlopen.c b/fouldlopen.c new file mode 100644 index 0000000..35fa05b --- /dev/null +++ b/fouldlopen.c @@ -0,0 +1,11 @@ +#include +#include + +int main(int argc, char **argv) { + void *(*sym_dlopen)(const char *, int) = dlsym(RTLD_DEFAULT, "dlopen"); + for (int i = 1; i < argc; i++) { + void *handle = sym_dlopen(argv[i], RTLD_NOW); + dlclose(handle); + } + return 0; +} diff --git a/foulfolder.m b/foulfolder.m new file mode 100644 index 0000000..32fd09b --- /dev/null +++ b/foulfolder.m @@ -0,0 +1,337 @@ +// foulfolder.m +// Created by und3fined on 2025-Jan-09. + +#import +#import +#import + +#import + +static int VERBOSE = 0; + +#define MH_MAGIC 0xfeedface +#define MH_CIGAM 0xcefaedfe +#define MH_MAGIC_64 0xfeedfacf /* the 64-bit mach magic number */ +#define MH_CIGAM_64 0xcffaedfe /* NXSwapInt(MH_MAGIC_64) */ + +#define FAT_MAGIC 0xcafebabe +#define FAT_CIGAM 0xbebafeca +#define FAT_MAGIC_64 0xcafebabf +#define FAT_CIGAM_64 0xbfbafeca /* NXSwapLong(FAT_MAGIC_64) */ + +#define LC_SEGMENT 0x1 +#define LC_SEGMENT_64 0x19 +#define LC_ENCRYPTION_INFO 0x21 +#define LC_ENCRYPTION_INFO_64 0x2C + +extern char **environ; + +static NSString *shared_shell_path(void) +{ + static NSString *_sharedShellPath = nil; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ @autoreleasepool { + NSArray *possibleShells = @[ + @"/usr/bin/bash", + @"/bin/bash", + @"/usr/bin/sh", + @"/bin/sh", + @"/usr/bin/zsh", + @"/bin/zsh", + @"/var/jb/usr/bin/bash", + @"/var/jb/bin/bash", + @"/var/jb/usr/bin/sh", + @"/var/jb/bin/sh", + @"/var/jb/usr/bin/zsh", + @"/var/jb/bin/zsh", + ]; + NSFileManager *fileManager = [NSFileManager defaultManager]; + for (NSString *shellPath in possibleShells) { + // check if the shell exists and is regular file (not symbolic link) and executable + NSDictionary *shellAttrs = [fileManager attributesOfItemAtPath:shellPath error:nil]; + if ([shellAttrs[NSFileType] isEqualToString:NSFileTypeSymbolicLink]) { + continue; + } + if (![fileManager isExecutableFileAtPath:shellPath]) { + continue; + } + _sharedShellPath = shellPath; + break; + } + } }); + return _sharedShellPath; +} + +int +my_system(const char *ctx) +{ + const char *shell_path = [shared_shell_path() UTF8String]; + const char *args[] = { + shell_path, + "-c", + ctx, + NULL + }; + pid_t pid; + int posix_status = posix_spawn(&pid, shell_path, NULL, NULL, (char **) args, environ); + if (posix_status != 0) + { + errno = posix_status; + fprintf(stderr, "posix_spawn, %s (%d)\n", strerror(errno), errno); + return posix_status; + } + pid_t w; + int status; + do + { + w = waitpid(pid, &status, WUNTRACED | WCONTINUED); + if (w == -1) + { + fprintf(stderr, "waitpid %d, %s (%d)\n", pid, strerror(errno), errno); + return errno; + } + if (WIFEXITED(status)) + { + if (WEXITSTATUS(status) != 0) + { + fprintf(stderr, "pid %d, exited with status %d\n", pid, WEXITSTATUS(status)); + return WEXITSTATUS(status); + } + } + else if (WIFSIGNALED(status)) + { + fprintf(stderr, "pid %d killed by signal %d\n", pid, WTERMSIG(status)); + } + else if (WIFSTOPPED(status)) + { + fprintf(stderr, "pid %d stopped by signal %d\n", pid, WSTOPSIG(status)); + } + // else if (WIFCONTINUED(status)) + // { + // // fprintf(stderr, "pid %d continued\n", pid); + // } + } + while (!WIFEXITED(status) && !WIFSIGNALED(status)); + if (WIFSIGNALED(status)) + { + return WTERMSIG(status); + } + return WEXITSTATUS(status); +} + +NSString * +escape_arg(NSString *arg) +{ + return [arg stringByReplacingOccurrencesOfString:@"\'" withString:@"'\\\''"]; +} + +int +main(int argc, char *argv[]) +{ + if (argc < 2) + { + fprintf(stderr, "usage: foulfolder [-v] \n"); + return 1; + } + + if (argc == 3 && strcmp(argv[1], "-v") == 0) + { + VERBOSE = 1; + argv++; + } + + NSString *internalAppPath = @"/var/containers/Bundle/Application/"; + NSString *keyItemName = @"itemName"; + NSString *keyBundleId = @"softwareVersionBundleId"; + + BOOL isAppPath = NO; + // check argument is path or bundle id. + // path always start with /private/var/containers/Bundle/Application + + // private var path always start with /private/var and join with internalAppPath + NSString *privateVarAppPath = [@"/private" stringByAppendingPathComponent:internalAppPath]; + if ( + strncmp(argv[1], [internalAppPath UTF8String], internalAppPath.length) == 0 || + strncmp(argv[1], [privateVarAppPath UTF8String], privateVarAppPath.length) == 0 + ) { + isAppPath = YES; + } + + NSString *targetPath = nil; + if (isAppPath) { + targetPath = [NSString stringWithUTF8String:argv[1]]; + } + + NSString *iTunesMetadataPath = [targetPath stringByAppendingPathComponent:@"iTunesMetadata.plist"]; + NSDictionary *iTunesMetadataDict = [NSDictionary dictionaryWithContentsOfFile:iTunesMetadataPath]; + NSMutableDictionary *appMaps = [NSMutableDictionary dictionary]; + NSString *appBundleId = iTunesMetadataDict[keyBundleId]; + NSString *appName = iTunesMetadataDict[keyItemName]; + NSString *appVersion = iTunesMetadataDict[@"bundleShortVersionString"]; + + NSFileManager *fileManager = [NSFileManager defaultManager]; + NSArray *contents = [fileManager contentsOfDirectoryAtPath:targetPath error:nil]; + NSString *targetApp = nil; + + for (NSString *content in contents) { + if ([content hasSuffix:@".app"]) { + targetApp = content; + break; + } + } + + if (!targetApp) { + fprintf(stderr, "Application not found\n"); + return 1; + } + + fprintf(stderr, "[start] Target app -> %s (%s)\n", [appName UTF8String], [appBundleId UTF8String]); + + NSError *error = nil; + /* Make a copy of app bundle. */ + NSURL *tempURL = [fileManager URLForDirectory:NSItemReplacementDirectory + inDomain:NSUserDomainMask + appropriateForURL:[NSURL fileURLWithPath:[fileManager currentDirectoryPath]] + create:YES + error:&error]; + if (!tempURL) { + fprintf(stderr, + "cannot create appropriate item replacement directory: %s\n", + [[error localizedDescription] UTF8String]); + return 1; + } + + NSString *tempPath = [[tempURL path] stringByAppendingPathComponent:@"Payload"]; + BOOL didCopy = [fileManager copyItemAtPath:targetPath toPath:tempPath error:&error]; + if (!didCopy) { + fprintf(stderr, "cannot copy app bundle: %s\n", [[error localizedDescription] UTF8String]); + return 1; + } + + /* Enumerate entire app bundle to find all Mach-Os. */ + NSEnumerator *enumerator = [fileManager enumeratorAtPath:tempPath]; + NSString *objectPath = nil; + BOOL didError = 0; + NSNumber *decryptCount = [NSNumber numberWithInteger: 0]; + while (objectPath = [enumerator nextObject]) + { + NSString *objectFullPath = [tempPath stringByAppendingPathComponent:objectPath]; + FILE *fp = fopen(objectFullPath.UTF8String, "rb"); + if (!fp) + { + perror("fopen"); + continue; + } + + int num = getw(fp); + if (num == EOF) + { + fclose(fp); + continue; + } + + if ( + [objectPath containsString:@".app/Info.plist"] + ) { + fclose(fp); + fprintf(stderr, "[change] %s: Remove UISupportedDevices\n", [objectPath UTF8String]); + NSMutableDictionary *infoPlist = [NSMutableDictionary dictionaryWithContentsOfFile:objectFullPath]; + [infoPlist removeObjectForKey:@"UISupportedDevices"]; + [infoPlist writeToFile:objectPath atomically:YES]; + continue; + } + + if ( + num == MH_MAGIC_64 || + num == MH_MAGIC || + num == FAT_MAGIC_64 || + num == FAT_MAGIC || + num == MH_CIGAM || + num == MH_CIGAM_64 || + num == FAT_CIGAM || + num == FAT_CIGAM_64 + ) { + NSString *objectRawPath = [targetPath stringByAppendingPathComponent:objectPath]; + + int decryptStatus = + my_system([[NSString stringWithFormat:@"fouldlopen '%@'", escape_arg(objectRawPath)] UTF8String]); + + if (VERBOSE) { + decryptStatus = my_system([[ + NSString stringWithFormat:@"fouldecrypt -v '%@' '%@'", + escape_arg(objectRawPath), + escape_arg(objectFullPath) + ] UTF8String]); + } else { + decryptStatus = my_system([[ + NSString stringWithFormat:@"fouldecrypt '%@' '%@'", + escape_arg(objectRawPath), + escape_arg(objectFullPath) + ] UTF8String]); + } + + if (decryptStatus != 0) { + didError = decryptStatus; + fprintf(stderr, "[dump] %s: Failed\n", [objectPath UTF8String]); + break; + } + + decryptCount = [NSNumber numberWithInteger: [decryptCount integerValue] + 1]; + fprintf(stderr, "[dump] %s: Success\n", [objectPath UTF8String]); + } + + fclose(fp); + } + + if (didError) { + return didError; + } + + if ([decryptCount integerValue] == 0) { + fprintf(stderr, "[dump] no Mach-O found\n"); + return 1; + } + + + /* Sign the app bundle. */ + NSString *decryptSignPath = [tempPath stringByAppendingPathComponent:targetApp]; + NSString *decryptSign = [decryptSignPath stringByAppendingPathComponent:@"decrypt.day"]; + [fileManager createFileAtPath:decryptSign contents:[@"und3fined" dataUsingEncoding:NSUTF8StringEncoding] attributes:nil]; + + /* remove other files */ + NSString *mobileContainerManager = [tempPath stringByAppendingPathComponent:@".com.apple.mobile_container_manager.metadata.plist"]; + NSString *bundleMetadata = [tempPath stringByAppendingPathComponent:@"BundleMetadata.plist"]; + NSString *iTunesMetadata = [tempPath stringByAppendingPathComponent:@"iTunesMetadata.plist"]; + [fileManager removeItemAtPath:mobileContainerManager error:nil]; + [fileManager removeItemAtPath:bundleMetadata error:nil]; + [fileManager removeItemAtPath:iTunesMetadata error:nil]; + + /* zip: archive */ + NSString *archiveName = + [NSString stringWithFormat:@"%@_%@_und3fined.ipa", appBundleId, appVersion]; + NSString *archivePath = + [[fileManager currentDirectoryPath] stringByAppendingPathComponent:archiveName]; + + BOOL didClean = [fileManager removeItemAtPath:archivePath error:nil]; + fprintf(stderr, "[archive] Creating %s file...\n", [archiveName UTF8String]); + + int zipStatus = + my_system([[ + NSString stringWithFormat:@"set -e; shopt -s dotglob; cd '%@'; zip -qrX '%@' ./Payload; shopt -u dotglob;", + escape_arg([tempURL path]), + escape_arg(archivePath) + ] UTF8String]); + + fprintf(stderr, "[archive] Archive -> %s\n", [archiveName UTF8String]); + fprintf(stderr, "[clean] Remove temp %s\n", [[tempURL path] UTF8String]); + [fileManager removeItemAtPath:[tempURL path] error:nil]; + // [fileManager removeItemAtPath:targetPath error:nil]; + + if (zipStatus != 0) { + fprintf(stderr, "cannot create archive: %s\n", [[error localizedDescription] UTF8String]); + return 1; + } + + fprintf(stderr, "Done.\n"); + return zipStatus; +} diff --git a/foulwrapper.m b/foulwrapper.m new file mode 100644 index 0000000..ba6f32f --- /dev/null +++ b/foulwrapper.m @@ -0,0 +1,293 @@ +#import +#import +#import + +#import +#import + +#import +#import + +static int VERBOSE = 0; + +#define MH_MAGIC 0xfeedface +#define MH_CIGAM 0xcefaedfe +#define MH_MAGIC_64 0xfeedfacf /* the 64-bit mach magic number */ +#define MH_CIGAM_64 0xcffaedfe /* NXSwapInt(MH_MAGIC_64) */ + +#define FAT_MAGIC 0xcafebabe +#define FAT_CIGAM 0xbebafeca +#define FAT_MAGIC_64 0xcafebabf +#define FAT_CIGAM_64 0xbfbafeca /* NXSwapLong(FAT_MAGIC_64) */ + +#define LC_SEGMENT 0x1 +#define LC_SEGMENT_64 0x19 +#define LC_ENCRYPTION_INFO 0x21 +#define LC_ENCRYPTION_INFO_64 0x2C + +extern char **environ; + +int +my_system(const char *ctx) +{ + const char *args[] = { + "/bin/sh", + "-c", + ctx, + NULL + }; + pid_t pid; + int posix_status = posix_spawn(&pid, "/bin/sh", NULL, NULL, (char **) args, environ); + if (posix_status != 0) + { + errno = posix_status; + fprintf(stderr, "posix_spawn, %s (%d)\n", strerror(errno), errno); + return posix_status; + } + pid_t w; + int status; + do + { + w = waitpid(pid, &status, WUNTRACED | WCONTINUED); + if (w == -1) + { + fprintf(stderr, "waitpid %d, %s (%d)\n", pid, strerror(errno), errno); + return errno; + } + if (WIFEXITED(status)) + { + if (WEXITSTATUS(status) != 0) + { + fprintf(stderr, "pid %d, exited with status %d\n", pid, WEXITSTATUS(status)); + return WEXITSTATUS(status); + } + } + else if (WIFSIGNALED(status)) + { + fprintf(stderr, "pid %d killed by signal %d\n", pid, WTERMSIG(status)); + } + else if (WIFSTOPPED(status)) + { + fprintf(stderr, "pid %d stopped by signal %d\n", pid, WSTOPSIG(status)); + } + // else if (WIFCONTINUED(status)) + // { + // // fprintf(stderr, "pid %d continued\n", pid); + // } + } + while (!WIFEXITED(status) && !WIFSIGNALED(status)); + if (WIFSIGNALED(status)) + { + return WTERMSIG(status); + } + return WEXITSTATUS(status); +} + +NSString * +escape_arg(NSString *arg) +{ + return [arg stringByReplacingOccurrencesOfString:@"\'" withString:@"'\\\''"]; +} + +@interface LSApplicationProxy () +- (NSString *)shortVersionString; +@end + +int +main(int argc, char *argv[]) +{ + if (argc < 2) + { + fprintf(stderr, "usage: foulwrapper (application name or application bundle identifier)\n"); + return 1; + } + + + /* AppList: convert app name to app identifier */ + /* or, you can use APIs in `LSApplicationWorkspace`. */ + NSArray *sortedDisplayIdentifiers = nil; + NSDictionary *appMaps = + [[ALApplicationList sharedApplicationList] applicationsFilteredUsingPredicate:[NSPredicate predicateWithFormat:@"isSystemApplication = FALSE"] + onlyVisible:NO titleSortedIdentifiers:&sortedDisplayIdentifiers]; + + NSString *targetIdOrName = [NSString stringWithUTF8String:argv[1]]; + NSString *targetId = nil; + for (NSString *appId in appMaps) + { + if ([appId isEqualToString:targetIdOrName] || [appMaps[appId] isEqualToString:targetIdOrName]) + { + targetId = appId; + break; + } + } + + if (!targetId) { + targetId = [NSString stringWithUTF8String:argv[2]]; + } + + if (!targetId) + { + fprintf(stderr, "Application \"%s\" not found\n", argv[1]); + return 1; + } + + /* MobileContainerManager: locate app bundle container path */ + /* `LSApplicationProxy` cannot provide correct values of container URLs since iOS 12. */ + NSError *error = nil; + id aClass = objc_getClass("MCMAppContainer"); + assert([aClass respondsToSelector:@selector(containerWithIdentifier:error:)]); + + MCMContainer *container = [aClass containerWithIdentifier:targetId error:&error]; + NSString *targetPath = [[container url] path]; + if (!targetPath) + { + fprintf(stderr, + "Application \"%s\" does not have a bundle container: %s\n", + argv[1], + [[error localizedDescription] UTF8String]); + return 1; + } + + fprintf(stderr, "[start] Target app -> %s\n", [targetId UTF8String]); + + /* Make a copy of app bundle. */ + NSURL *tempURL = [[NSFileManager defaultManager] URLForDirectory:NSItemReplacementDirectory + inDomain:NSUserDomainMask + appropriateForURL:[NSURL fileURLWithPath:[[NSFileManager defaultManager] currentDirectoryPath]] + create:YES + error:&error]; + if (!tempURL) { + fprintf(stderr, + "cannot create appropriate item replacement directory: %s\n", + [[error localizedDescription] UTF8String]); + return 1; + } + + NSString *tempPath = [[tempURL path] stringByAppendingPathComponent:@"Payload"]; + BOOL didCopy = [[NSFileManager defaultManager] copyItemAtPath:targetPath toPath:tempPath error:&error]; + if (!didCopy) { + fprintf(stderr, "cannot copy app bundle: %s\n", [[error localizedDescription] UTF8String]); + return 1; + } + + /* Enumerate entire app bundle to find all Mach-Os. */ + NSEnumerator *enumerator = [[NSFileManager defaultManager] enumeratorAtPath:tempPath]; + NSString *objectPath = nil; + BOOL didError = 0; + NSNumber *decryptCount = [NSNumber numberWithInteger: 0]; + while (objectPath = [enumerator nextObject]) + { + NSString *objectFullPath = [tempPath stringByAppendingPathComponent:objectPath]; + FILE *fp = fopen(objectFullPath.UTF8String, "rb"); + if (!fp) + { + perror("fopen"); + continue; + } + + int num = getw(fp); + if (num == EOF) + { + fclose(fp); + continue; + } + + if ( + [objectPath containsString:@".app/Info.plist"] + ) { + fclose(fp); + fprintf(stderr, "[change] %s: Remove UISupportedDevices\n", [objectPath UTF8String]); + NSMutableDictionary *infoPlist = [NSMutableDictionary dictionaryWithContentsOfFile:objectFullPath]; + [infoPlist removeObjectForKey:@"UISupportedDevices"]; + [infoPlist writeToFile:objectPath atomically:YES]; + continue; + } + + if ( + num == MH_MAGIC_64 || + num == MH_MAGIC || + num == FAT_MAGIC_64 || + num == FAT_MAGIC || + num == MH_CIGAM || + num == MH_CIGAM_64 || + num == FAT_CIGAM || + num == FAT_CIGAM_64 + ) { + NSString *objectRawPath = [targetPath stringByAppendingPathComponent:objectPath]; + + int decryptStatus = + my_system([[NSString stringWithFormat:@"fouldecrypt '%@' '%@'", escape_arg(objectRawPath), escape_arg( + objectFullPath)] UTF8String]); + if (decryptStatus != 0) { + didError = decryptStatus; + fprintf(stderr, "[dump] %s: Failed\n", [objectPath UTF8String]); + break; + } + + decryptCount = [NSNumber numberWithInteger: [decryptCount integerValue] + 1]; + fprintf(stderr, "[dump] %s: Success\n", [objectPath UTF8String]); + } + + fclose(fp); + } + + if (didError) { + return didError; + } + + if ([decryptCount integerValue] == 0) { + fprintf(stderr, "[dump] no Mach-O found\n"); + return 1; + } + + LSApplicationProxy *appProxy = [LSApplicationProxy applicationProxyForIdentifier:targetId]; + assert(appProxy); + + /* Sign the app bundle. */ + NSString *decryptSign = [tempPath stringByAppendingPathComponent:@"decrypt.day"]; + [[NSFileManager defaultManager] createFileAtPath:decryptSign contents:[@"und3fined" dataUsingEncoding:NSUTF8StringEncoding] attributes:nil]; + + // NSString *infoPlistPath = [tempPath stringByAppendingPathComponent:@"Info.plist"]; + // NSMutableDictionary *infoPlist = [NSMutableDictionary dictionaryWithContentsOfFile:infoPlistPath]; + // assert(infoPlist); + + // /* Remove UISupportedDevices in Info.plist file */ + // [infoPlist removeObjectForKey:@"UISupportedDevices"]; + // [infoPlist writeToFile:infoPlistPath atomically:YES]; + + /* remove other files */ + NSString *mobileContainerManager = [tempPath stringByAppendingPathComponent:@".com.apple.mobile_container_manager.metadata.plist"]; + NSString *bundleMetadata = [tempPath stringByAppendingPathComponent:@"BundleMetadata.plist"]; + NSString *iTunesMetadata = [tempPath stringByAppendingPathComponent:@"iTunesMetadata.plist"]; + [[NSFileManager defaultManager] removeItemAtPath:mobileContainerManager error:nil]; + [[NSFileManager defaultManager] removeItemAtPath:bundleMetadata error:nil]; + [[NSFileManager defaultManager] removeItemAtPath:iTunesMetadata error:nil]; + + /* zip: archive */ + NSString *archiveName = + [NSString stringWithFormat:@"%@_%@_dump.ipa", targetId, [appProxy shortVersionString]]; + NSString *archivePath = + [[[NSFileManager defaultManager] currentDirectoryPath] stringByAppendingPathComponent:archiveName]; + + BOOL didClean = [[NSFileManager defaultManager] removeItemAtPath:archivePath error:nil]; + fprintf(stderr, "[archive] Creating %s file...\n", [archiveName UTF8String]); + + int zipStatus = + my_system([[ + NSString stringWithFormat:@"set -e; shopt -s dotglob; cd '%@'; zip -qrX '%@' ./Payload; shopt -u dotglob;", + escape_arg([tempURL path]), + escape_arg(archivePath) + ] UTF8String]); + + fprintf(stderr, "[archive] Archive -> %s\n", [archiveName UTF8String]); + fprintf(stderr, "[clean] Remove temp %s\n", [[tempURL path] UTF8String]); + [[NSFileManager defaultManager] removeItemAtPath:[tempURL path] error:nil]; + + if (zipStatus != 0) { + fprintf(stderr, "cannot create archive: %s\n", [[error localizedDescription] UTF8String]); + return 1; + } + + fprintf(stderr, "Done.\n"); + return zipStatus; +} diff --git a/include/AppList/ALApplicationList.h b/include/AppList/ALApplicationList.h new file mode 100644 index 0000000..91d7eba --- /dev/null +++ b/include/AppList/ALApplicationList.h @@ -0,0 +1,30 @@ +#import +#import +#import + +enum { + ALApplicationIconSizeSmall = 29, + ALApplicationIconSizeLarge = 59 +}; +typedef NSUInteger ALApplicationIconSize; + +@interface ALApplicationList : NSObject ++ (ALApplicationList *)sharedApplicationList; + +@property (nonatomic, readonly) NSDictionary *applications; +- (NSDictionary *)applicationsFilteredUsingPredicate:(NSPredicate *)predicate; +- (NSDictionary *)applicationsFilteredUsingPredicate:(NSPredicate *)predicate onlyVisible:(BOOL)onlyVisible titleSortedIdentifiers:(NSArray **)outSortedByTitle; + +- (id)valueForKeyPath:(NSString *)keyPath forDisplayIdentifier:(NSString *)displayIdentifier; +- (id)valueForKey:(NSString *)keyPath forDisplayIdentifier:(NSString *)displayIdentifier; +- (BOOL)applicationWithDisplayIdentifierIsHidden:(NSString *)displayIdentifier; + +- (CGImageRef)copyIconOfSize:(ALApplicationIconSize)iconSize forDisplayIdentifier:(NSString *)displayIdentifier; +- (UIImage *)iconOfSize:(ALApplicationIconSize)iconSize forDisplayIdentifier:(NSString *)displayIdentifier; +- (BOOL)hasCachedIconOfSize:(ALApplicationIconSize)iconSize forDisplayIdentifier:(NSString *)displayIdentifier; + +@end + +extern NSString *const ALIconLoadedNotification; +extern NSString *const ALDisplayIdentifierKey; +extern NSString *const ALIconSizeKey; diff --git a/include/AppList/ALApplicationTableDataSource.h b/include/AppList/ALApplicationTableDataSource.h new file mode 100644 index 0000000..1fd38ce --- /dev/null +++ b/include/AppList/ALApplicationTableDataSource.h @@ -0,0 +1,45 @@ +#import +#import + +@class ALApplicationList; + +@interface ALApplicationTableDataSource : NSObject { +@private + NSMutableArray *_sectionDescriptors; + UITableView *_tableView; + NSBundle *_localizationBundle; + BOOL _loadsAsynchronously; +} + ++ (NSArray *)standardSectionDescriptors; + ++ (id)dataSource; +- (id)init; + +@property (nonatomic, copy) NSArray *sectionDescriptors; +@property (nonatomic, retain) UITableView *tableView; +@property (nonatomic, retain) NSBundle *localizationBundle; +@property (nonatomic, assign) BOOL loadsAsynchronously; + +- (id)cellDescriptorForIndexPath:(NSIndexPath *)indexPath; // NSDictionary if custom cell; NSString if app cell; nil if loading +- (NSString *)displayIdentifierForIndexPath:(NSIndexPath *)indexPath; + +- (void)insertSectionDescriptor:(NSDictionary *)sectionDescriptor atIndex:(NSInteger)index; +- (void)removeSectionDescriptorAtIndex:(NSInteger)index; +- (void)removeSectionDescriptorsAtIndexes:(NSIndexSet *)indexSet; + +- (BOOL)waitUntilDate:(NSDate *)date forContentInSectionAtIndex:(NSInteger)sectionIndex; + +@end + +extern const NSString *ALSectionDescriptorTitleKey; +extern const NSString *ALSectionDescriptorFooterTitleKey; +extern const NSString *ALSectionDescriptorPredicateKey; +extern const NSString *ALSectionDescriptorCellClassNameKey; +extern const NSString *ALSectionDescriptorIconSizeKey; +extern const NSString *ALSectionDescriptorSuppressHiddenAppsKey; +extern const NSString *ALSectionDescriptorVisibilityPredicateKey; + +extern const NSString *ALItemDescriptorTextKey; +extern const NSString *ALItemDescriptorDetailTextKey; +extern const NSString *ALItemDescriptorImageKey; diff --git a/include/AppList/ALValueCell.h b/include/AppList/ALValueCell.h new file mode 100644 index 0000000..bd07c9f --- /dev/null +++ b/include/AppList/ALValueCell.h @@ -0,0 +1,40 @@ +#import + +@protocol ALValueCellDelegate; + +@interface ALValueCell : UITableViewCell { +@private + id delegate; +} + +@property (nonatomic, assign) id delegate; + +- (void)loadValue:(id)value; // Deprecated +- (void)loadValue:(id)value withTitle:(NSString *)title; + +- (void)didSelect; + +@end + +@protocol ALValueCellDelegate +@required +- (void)valueCell:(ALValueCell *)valueCell didChangeToValue:(id)newValue; +@end + +@interface ALSwitchCell : ALValueCell { +@private + UISwitch *switchView; +} + +@property (nonatomic, readonly) UISwitch *switchView; + +@end + +@interface ALCheckCell : ALValueCell + +@end + +@interface ALDisclosureIndicatedCell : ALValueCell + +@end + diff --git a/include/AppList/AppList.h b/include/AppList/AppList.h new file mode 100644 index 0000000..73418fc --- /dev/null +++ b/include/AppList/AppList.h @@ -0,0 +1,3 @@ +#import "ALApplicationList.h" +#import "ALApplicationTableDataSource.h" +#import "ALValueCell.h" diff --git a/include/MobileContainerManager/MCMContainer.h b/include/MobileContainerManager/MCMContainer.h new file mode 100644 index 0000000..f221880 --- /dev/null +++ b/include/MobileContainerManager/MCMContainer.h @@ -0,0 +1,56 @@ +// +// MCMContainer.h +// edgbackup +// +// Created by Mason Rachel on 10/30/19. +// + +#ifndef MCMContainer_h +#define MCMContainer_h + +#import + +@interface MCMContainer : NSObject { + long long _containerClass; + NSString *_identifier; + unsigned int _userId; + NSUUID *_uuid; +} + +@property (nonatomic, readonly) long long containerClass; +@property (nonatomic, readonly) NSString *identifier; +@property (nonatomic, readonly) NSDictionary *info; +@property (getter=isTemporary, nonatomic, readonly) bool temporary; +@property (nonatomic, readonly) NSURL *url; +@property (nonatomic, readonly) NSUUID *uuid; + ++ (id)containerWithIdentifier:(id)arg1 createIfNecessary:(bool)arg2 existed:(bool*)arg3 error:(id*)arg4; ++ (id)containerWithIdentifier:(id)arg1 error:(id*)arg2; ++ (id)temporaryContainerWithIdentifier:(id)arg1 existed:(bool*)arg2 error:(id*)arg3; ++ (long long)typeContainerClass; + +- (void)_errorOccurred; +- (long long)containerClass; +- (void)dealloc; +- (id)description; +- (id)destroyContainerWithCompletion:(id /* block */)arg1; +- (unsigned long long)diskUsageWithError:(id*)arg1; +- (unsigned long long)hash; +- (id)identifier; +- (id)info; +- (id)infoValueForKey:(id)arg1 error:(id*)arg2; +- (id)init; +- (id)initWithIdentifier:(id)arg1 createIfNecessary:(bool)arg2 existed:(bool*)arg3 temp:(bool)arg4 error:(id*)arg5; +- (id)initWithIdentifier:(id)arg1 userId:(unsigned int)arg2 uuid:(id)arg3 error:(id*)arg4; +- (bool)isEqual:(id)arg1; +- (bool)isTemporary; +- (void)markDeleted; +- (bool)recreateDefaultStructureWithError:(id*)arg1; +- (bool)regenerateDirectoryUUIDWithError:(id*)arg1; +- (bool)setInfoValue:(id)arg1 forKey:(id)arg2 error:(id*)arg3; +- (id)url; +- (id)uuid; + +@end + +#endif /* MCMContainer_h */ diff --git a/include/MobileContainerManager/MCMContainerManager.h b/include/MobileContainerManager/MCMContainerManager.h new file mode 100644 index 0000000..35571a3 --- /dev/null +++ b/include/MobileContainerManager/MCMContainerManager.h @@ -0,0 +1,28 @@ +// +// MCMContainerManager.h +// edgbackup +// +// Created by Mason Rachel on 10/31/19. +// + +#ifndef MCMContainerManager_h +#define MCMContainerManager_h + +@interface MCMContainerManager : NSObject + ++ (id)defaultManager; + +- (id)_containersWithClass:(long long)arg1 temporary:(bool)arg2 error:(id*)arg3; +- (id)containerWithContentClass:(long long)arg1 identifier:(id)arg2 createIfNecessary:(bool)arg3 existed:(bool*)arg4 error:(id*)arg5; +- (id)containerWithContentClass:(long long)arg1 identifier:(id)arg2 error:(id*)arg3; +- (id)containersWithClass:(long long)arg1 error:(id*)arg2; +- (id)deleteContainers:(id)arg1 withCompletion:(id /* block */)arg2; +- (id)init; +- (bool)replaceContainer:(id)arg1 withContainer:(id)arg2 error:(id*)arg3; +- (bool)replaceContainer:(id)arg1 withContainer:(id)arg2 error:(id*)arg3 withCompletion:(id /* block */)arg4; +- (id)temporaryContainerWithContentClass:(long long)arg1 identifier:(id)arg2 existed:(bool*)arg3 error:(id*)arg4; +- (id)temporaryContainersWithClass:(long long)arg1 error:(id*)arg2; + +@end + +#endif /* MCMContainerManager_h */ diff --git a/layout/DEBIAN/control b/layout/DEBIAN/control new file mode 100644 index 0000000..5b86892 --- /dev/null +++ b/layout/DEBIAN/control @@ -0,0 +1,10 @@ +Package: dev.und3fy.foulfolder +Name: foulfolder +Version: 0.0.3 +Architecture: iphoneos-arm +Depends: applist,zip +Description: Directly decrypt binaries like fouldecrypt, but also supports iOS 14 +Maintainer: und3fined +Author: misty +Section: System +Tag: role::hacker diff --git a/lib/libapplist.dylib b/lib/libapplist.dylib new file mode 100644 index 0000000..e501362 Binary files /dev/null and b/lib/libapplist.dylib differ diff --git a/link_theos.sh b/link_theos.sh new file mode 100755 index 0000000..3b38bcd --- /dev/null +++ b/link_theos.sh @@ -0,0 +1,4 @@ +#!/bin/sh + +ln -s $THEOS/makefiles makefiles +exit 0 diff --git a/main.cpp b/main.cpp index 809b7ed..211bd7c 100644 --- a/main.cpp +++ b/main.cpp @@ -1,18 +1,18 @@ #include #include +#include +#include +#include +#include +#include #include #include #include #include #include -#include -#include -#include -#include -#include -#include #include -#include +#include +#include #include @@ -23,314 +23,355 @@ #define EXEC_PAGE_SIZE 0x1000 int VERBOSE = 0; -#define DLOG(f_, ...) \ -{ \ - if (VERBOSE) { \ - struct tm _tm123_; \ - struct timeval _xxtv123_; \ - gettimeofday(&_xxtv123_, NULL); \ - localtime_r(&_xxtv123_.tv_sec, &_tm123_); \ - printf("%02d:%02d:%02d.%06d\t", _tm123_.tm_hour, _tm123_.tm_min, _tm123_.tm_sec, _xxtv123_.tv_usec); \ - printf((f_), ##__VA_ARGS__); \ - printf("\n"); \ - } \ -}; - - -static uint8_t* -map(const char *path, bool _mutable, size_t *size, int *descriptor) -{ - int f = open(path, _mutable ? O_CREAT | O_TRUNC | O_RDWR : O_RDONLY, 0755); - if (f < 0) { - perror(_mutable ? "open(map-ro)" : "open(map-rw)"); - return NULL; - } - - if (_mutable) { - if (ftruncate(f, *size) < 0) { - perror("ftruncate(map)"); - return NULL; - } +#define DLOG(f_, ...) \ + { \ + if (VERBOSE) { \ + struct tm _tm123_; \ + struct timeval _xxtv123_; \ + gettimeofday(&_xxtv123_, NULL); \ + localtime_r(&_xxtv123_.tv_sec, &_tm123_); \ + printf("%02d:%02d:%02d.%06d\t", _tm123_.tm_hour, _tm123_.tm_min, \ + _tm123_.tm_sec, _xxtv123_.tv_usec); \ + printf((f_), ##__VA_ARGS__); \ + printf("\n"); \ + } \ + }; + +static uint8_t *map(const char *path, bool _mutable, size_t *size, + int *descriptor) { + int f = open(path, _mutable ? O_CREAT | O_TRUNC | O_RDWR : O_RDONLY, 0755); + if (f < 0) { + perror(_mutable ? "open(map-ro)" : "open(map-rw)"); + return NULL; + } + + if (_mutable) { + if (ftruncate(f, *size) < 0) { + perror("ftruncate(map)"); + return NULL; } - - struct stat s; - if (fstat(f, &s) < 0) { - perror("fstat(map)"); - close(f); - return NULL; - } - - uint8_t *base = (uint8_t *)mmap(NULL, s.st_size, _mutable ? PROT_READ | PROT_WRITE : PROT_READ, - _mutable ? MAP_SHARED : MAP_PRIVATE, f, 0); - if (base == MAP_FAILED) { - perror(_mutable ? "mmap(map-ro)" : "mmap(map-rw)"); - close(f); - return NULL; - } - - *size = s.st_size; - if (descriptor) { - *descriptor = f; - } else { - close(f); - } - return base; + } + + struct stat s; + if (fstat(f, &s) < 0) { + perror("fstat(map)"); + close(f); + return NULL; + } + + uint8_t *base = (uint8_t *)mmap(NULL, s.st_size, + _mutable ? PROT_READ | PROT_WRITE : PROT_READ, + _mutable ? MAP_SHARED : MAP_PRIVATE, f, 0); + if (base == MAP_FAILED) { + perror(_mutable ? "mmap(map-ro)" : "mmap(map-rw)"); + close(f); + return NULL; + } + + *size = s.st_size; + if (descriptor) { + *descriptor = f; + } else { + close(f); + } + return base; } bool has_prep_kernel = false; int prepare_kernel() { - if (!has_prep_kernel) { - int ret = init_kerninfra(KERNLOG_NONE); - if (ret) return ret; - has_prep_kernel = true; - } - return 0; + if (!has_prep_kernel) { + int ret = init_kerninfra(KERNLOG_NONE); + if (ret) + return ret; + has_prep_kernel = true; + } + return 0; } -extern "C" int mremap_encrypted(void*, size_t, uint32_t, uint32_t, uint32_t); - -extern "C" kern_return_t mach_vm_remap(vm_map_t, mach_vm_address_t *, mach_vm_size_t, - mach_vm_offset_t, int, vm_map_t, mach_vm_address_t, - boolean_t, vm_prot_t *, vm_prot_t *, vm_inherit_t); - -void *__mmap(const char *info, void *base, size_t size, int prot, int flags, int fd, size_t off) { - DLOG("-->> %s mmaping(%p, 0x%zx, %d, 0x%x, %d, 0x%zx)", info, base, size, prot, flags, fd, off); - void *ret = mmap(base, size, prot, flags, fd, off); - if (ret == MAP_FAILED) { - perror("mmap"); - } - DLOG("<<-- %s mmaping(%p, 0x%zx, %d, 0x%x, %d, 0x%zx) = %p", info, base, size, prot, flags, fd, off, ret); - return ret; +extern "C" int mremap_encrypted(void *, size_t, uint32_t, uint32_t, uint32_t); + +extern "C" kern_return_t mach_vm_remap(vm_map_t, mach_vm_address_t *, + mach_vm_size_t, mach_vm_offset_t, int, + vm_map_t, mach_vm_address_t, boolean_t, + vm_prot_t *, vm_prot_t *, vm_inherit_t); + +void *__mmap(const char *info, void *base, size_t size, int prot, int flags, + int fd, size_t off) { + DLOG("-->> %s mmaping(%p, 0x%zx, %d, 0x%x, %d, 0x%zx)", info, base, size, + prot, flags, fd, off); + void *ret = mmap(base, size, prot, flags, fd, off); + if (ret == MAP_FAILED) { + perror("mmap"); + } + DLOG("<<-- %s mmaping(%p, 0x%zx, %d, 0x%x, %d, 0x%zx) = %p", info, base, size, + prot, flags, fd, off, ret); + return ret; } -int __mremap_encrypted(const char *info, void *base, size_t cryptsize, uint32_t cryptid, uint32_t cpuType, uint32_t cpuSubType) { - DLOG("<<-- %s mremap_encrypted(%p, 0x%zx, %d, 0x%x, 0x%x)", info, base, cryptsize, cryptid, cpuType, cpuSubType); - int ret = mremap_encrypted(base, cryptsize, cryptid, cpuType, cpuSubType); - if (ret) { - perror("mremap_encrypted"); - } - DLOG("-->> %s mremap_encrypted(%p, 0x%zx, %d, 0x%x, 0x%x) = %d", info, base, cryptsize, cryptid, cpuType, cpuSubType, ret); - return ret; +int __mremap_encrypted(const char *info, void *base, size_t cryptsize, + uint32_t cryptid, uint32_t cpuType, + uint32_t cpuSubType) { + DLOG("<<-- %s mremap_encrypted(%p, 0x%zx, %d, 0x%x, 0x%x)", info, base, + cryptsize, cryptid, cpuType, cpuSubType); + int ret = mremap_encrypted(base, cryptsize, cryptid, cpuType, cpuSubType); + if (ret) { + perror("mremap_encrypted"); + } + DLOG("-->> %s mremap_encrypted(%p, 0x%zx, %d, 0x%x, 0x%x) = %d", info, base, + cryptsize, cryptid, cpuType, cpuSubType, ret); + return ret; } #define LOGINDENT " " void debugprint_vme(addr_t _vmentry) { - auto encVmEntry = _vm_map_entry_p(_vmentry); - DLOG(LOGINDENT"mmaped entry: %p - %p", (void *)encVmEntry.start().load(), (void *)encVmEntry.end().load()); - DLOG(LOGINDENT"mmaped vme_offset: 0x%llx", encVmEntry.vme_offset().load()); - DLOG(LOGINDENT"mmaped vme_flags: 0x%x", encVmEntry.vme_flags().load()); - DLOG(LOGINDENT"mmaped vme_object: 0x%llx", encVmEntry.vme_object().load()); + auto encVmEntry = _vm_map_entry_p(_vmentry); + DLOG(LOGINDENT "mmaped entry: %p - %p", (void *)encVmEntry.start().load(), + (void *)encVmEntry.end().load()); + DLOG(LOGINDENT "mmaped vme_offset: 0x%llx", encVmEntry.vme_offset().load()); + DLOG(LOGINDENT "mmaped vme_flags: 0x%x", encVmEntry.vme_flags().load()); + DLOG(LOGINDENT "mmaped vme_object: 0x%llx", encVmEntry.vme_object().load()); } void debugprint_vmobj(addr_t _vmobj) { - auto vmobj = vm_object_t_p(_vmobj); - DLOG(LOGINDENT"mmaped vmobj *shadow: %p **shadow: %p", (void *)vmobj.shadow().load_addr(), (void *)vmobj.shadow().shadow().load_addr()); - DLOG(LOGINDENT"mmaped vmobj pager: %p shadow pager: %p", (void *)vmobj.pager().load_addr(), (void *)vmobj.shadow().pager().load_addr()); - DLOG(LOGINDENT"mmaped vmobj shadow pager op: %p", (void *)vmobj.shadow().pager().mo_pager_ops().load_addr()); + auto vmobj = vm_object_t_p(_vmobj); + DLOG(LOGINDENT "mmaped vmobj *shadow: %p **shadow: %p", + (void *)vmobj.shadow().load_addr(), + (void *)vmobj.shadow().shadow().load_addr()); + DLOG(LOGINDENT "mmaped vmobj pager: %p shadow pager: %p", + (void *)vmobj.pager().load_addr(), + (void *)vmobj.shadow().pager().load_addr()); + DLOG(LOGINDENT "mmaped vmobj shadow pager op: %p", + (void *)vmobj.shadow().pager().mo_pager_ops().load_addr()); } void debugprint_pager(addr_t _pager) { - auto applePager = apple_protect_pager_t_p(_pager); - DLOG(LOGINDENT"mmaped vme_object apple protect pager: ", NULL) - DLOG(LOGINDENT" backingOff %llx", applePager.backing_offset().load()) - DLOG(LOGINDENT" cryptoBackingOff %llx", applePager.crypto_backing_offset().load()) - DLOG(LOGINDENT" cryptoStart %llx", applePager.crypto_start().load()) - DLOG(LOGINDENT" cryptoEnd %llx", applePager.crypto_end().load()) - DLOG(LOGINDENT" cryptInfo %p", (void *)applePager.crypt_info().load()) + auto applePager = apple_protect_pager_t_p(_pager); + DLOG(LOGINDENT "mmaped vme_object apple protect pager: ", NULL) + DLOG(LOGINDENT " backingOff %llx", applePager.backing_offset().load()) + DLOG(LOGINDENT " cryptoBackingOff %llx", + applePager.crypto_backing_offset().load()) + DLOG(LOGINDENT " cryptoStart %llx", applePager.crypto_start().load()) + DLOG(LOGINDENT " cryptoEnd %llx", applePager.crypto_end().load()) + DLOG(LOGINDENT " cryptInfo %p", (void *)applePager.crypt_info().load()) } #undef LOGINDENT -static int -unprotect(int f, uint8_t *dupe, int cpuType, int cpuSubType, struct encryption_info_command *info, size_t macho_off) -{ +static int unprotect(int f, uint8_t *dupe, int cpuType, int cpuSubType, + struct encryption_info_command *info, size_t macho_off) { #define LOGINDENT " " - assert((info->cryptoff & (EXEC_PAGE_SIZE - 1)) == 0); - - DLOG(LOGINDENT"Going to decrypt crypt page: off 0x%x size 0x%x cryptid %d, cpuType %x cpuSubType %x", info->cryptoff, info->cryptsize, info->cryptid, cpuType, cpuSubType); - //getchar(); - - size_t off_aligned = info->cryptoff & ~(PAGE_SIZE - 1); - //size_t size_aligned = info->cryptsize + info->cryptoff - off_aligned; - size_t map_padding = info->cryptoff - off_aligned; - - int err = 0; - void *decryptedBuf = malloc(info->cryptsize); - - if (!(info->cryptoff & (PAGE_SIZE - 1))) { - DLOG(LOGINDENT"Already 16k aligned, directly go ahead :)"); - void *cryptbase = __mmap("16k-aligned", NULL, info->cryptsize, PROT_READ | PROT_EXEC, MAP_PRIVATE, f, info->cryptoff + macho_off); - // old-school mremap_encrypted - if (__mremap_encrypted("unprotect", cryptbase, info->cryptsize, info->cryptid, cpuType, cpuSubType)) { - munmap(cryptbase, info->cryptsize); - return 1; - } - DLOG(LOGINDENT" copying %p to %p, size %x", (char *)decryptedBuf, cryptbase, info->cryptsize); - memmove(decryptedBuf, cryptbase, info->cryptsize); - munmap(cryptbase, info->cryptsize); + assert((info->cryptoff & (EXEC_PAGE_SIZE - 1)) == 0); + + DLOG(LOGINDENT "Going to decrypt crypt page: off 0x%x size 0x%x cryptid %d, " + "cpuType %x cpuSubType %x", + info->cryptoff, info->cryptsize, info->cryptid, cpuType, cpuSubType); + // getchar(); + + size_t off_aligned = info->cryptoff & ~(PAGE_SIZE - 1); + // size_t size_aligned = info->cryptsize + info->cryptoff - off_aligned; + size_t map_padding = info->cryptoff - off_aligned; + + int err = 0; + void *decryptedBuf = malloc(info->cryptsize); + + if (!(info->cryptoff & (PAGE_SIZE - 1))) { + DLOG(LOGINDENT "Already 16k aligned, directly go ahead :)"); + void *cryptbase = + __mmap("16k-aligned", NULL, info->cryptsize, PROT_READ | PROT_EXEC, + MAP_PRIVATE, f, info->cryptoff + macho_off); + // old-school mremap_encrypted + if (__mremap_encrypted("unprotect", cryptbase, info->cryptsize, + info->cryptid, cpuType, cpuSubType) == 1) { + munmap(cryptbase, info->cryptsize); + return 1; + } + DLOG(LOGINDENT " copying %p to %p, size %x", (char *)decryptedBuf, + cryptbase, info->cryptsize); + memmove(decryptedBuf, cryptbase, info->cryptsize); + munmap(cryptbase, info->cryptsize); + } else { + DLOG(LOGINDENT "Not 16k aligned, trying to do the hack :O"); + + if (!!prepare_kernel()) { + fprintf(stderr, "Failed to init kerninfra!!\n"); + exit(1); } else { - DLOG(LOGINDENT"Not 16k aligned, trying to do the hack :O"); - - if (!!prepare_kernel()) { - fprintf(stderr, "Failed to init kerninfra!!\n"); - exit(1); - } else { - DLOG(LOGINDENT"successfully initialized kerninfra!"); - } - - for (size_t off = off_aligned; off < info->cryptoff + info->cryptsize; off += PAGE_SIZE) { - size_t off_end = MIN(off + PAGE_SIZE, info->cryptoff + info->cryptsize); - size_t curMapLen = (off_end - off) & (PAGE_SIZE - 1); if (!curMapLen) curMapLen = PAGE_SIZE; - size_t inPageStart = off < info->cryptoff ? info->cryptoff - off : 0; - size_t inPageEnd = curMapLen; - size_t cryptOff = off + inPageStart; - DLOG(LOGINDENT" processing file off %lx-%lx, curPage len: %lx, inPageStart: %lx, inPageEnd: %lx", off, off_end, curMapLen, inPageStart, inPageEnd); - char *cryptbase = (char *)__mmap("directly 16k-aligned mmap", NULL, curMapLen, PROT_READ | PROT_EXEC, MAP_PRIVATE, f, off + macho_off); - - if (__mremap_encrypted("unprotect", cryptbase, curMapLen, info->cryptid, cpuType, cpuSubType)) { - munmap(cryptbase, curMapLen); - return 1; - } - - auto curp = proc_t_p(current_proc()); - addr_t _encVmEntry = lookup_vm_map_entry(curp.task()._map().load_addr(), (addr_t)(cryptbase)); - DLOG(LOGINDENT" Got mmaped entry: %p", (void*)_encVmEntry); - debugprint_vme(_encVmEntry); - - auto encVmEntry = _vm_map_entry_p(_encVmEntry); - auto vmobj = encVmEntry.vme_object(); - debugprint_vmobj(vmobj.load_addr()); - auto applePager = apple_protect_pager_t_p(vmobj.shadow().pager().load_addr()); - DLOG(LOGINDENT" mmaped vme_object apple protect pager: ", NULL); - debugprint_pager(applePager.addr()); - - applePager.crypto_backing_offset().store(macho_off + cryptOff); - applePager.crypto_start().store(inPageStart); - - DLOG(LOGINDENT" patched mmaped vme_object apple protect pager: ", NULL) - debugprint_pager(applePager.addr()); - - DLOG(LOGINDENT" copying %p to %p, size %lx", (char *)decryptedBuf + cryptOff - info->cryptoff, cryptbase + inPageStart, curMapLen - inPageStart); - memmove((char *)decryptedBuf + cryptOff - info->cryptoff, cryptbase + inPageStart, curMapLen - inPageStart); - - munmap(cryptbase, curMapLen); - } + DLOG(LOGINDENT "successfully initialized kerninfra!"); } - if (err) { + for (size_t off = off_aligned; off < info->cryptoff + info->cryptsize; + off += PAGE_SIZE) { + size_t off_end = MIN(off + PAGE_SIZE, info->cryptoff + info->cryptsize); + size_t curMapLen = (off_end - off) & (PAGE_SIZE - 1); + if (!curMapLen) + curMapLen = PAGE_SIZE; + size_t inPageStart = off < info->cryptoff ? info->cryptoff - off : 0; + size_t inPageEnd = curMapLen; + size_t cryptOff = off + inPageStart; + DLOG(LOGINDENT " processing file off %lx-%lx, curPage len: %lx, " + "inPageStart: %lx, inPageEnd: %lx", + off, off_end, curMapLen, inPageStart, inPageEnd); + char *cryptbase = (char *)__mmap("directly 16k-aligned mmap", NULL, + curMapLen, PROT_READ | PROT_EXEC, + MAP_PRIVATE, f, off + macho_off); + + if (__mremap_encrypted("unprotect", cryptbase, curMapLen, info->cryptid, + cpuType, cpuSubType)) { + munmap(cryptbase, curMapLen); return 1; + } + + auto curp = proc_t_p(current_proc()); + addr_t _encVmEntry = lookup_vm_map_entry(curp.task()._map().load_addr(), + (addr_t)(cryptbase)); + DLOG(LOGINDENT " Got mmaped entry: %p", (void *)_encVmEntry); + debugprint_vme(_encVmEntry); + + auto encVmEntry = _vm_map_entry_p(_encVmEntry); + auto vmobj = encVmEntry.vme_object(); + debugprint_vmobj(vmobj.load_addr()); + auto applePager = + apple_protect_pager_t_p(vmobj.shadow().pager().load_addr()); + DLOG(LOGINDENT " mmaped vme_object apple protect pager: ", NULL); + debugprint_pager(applePager.addr()); + + applePager.crypto_backing_offset().store(macho_off + cryptOff); + applePager.crypto_start().store(inPageStart); + + DLOG(LOGINDENT " patched mmaped vme_object apple protect pager: ", + NULL) + debugprint_pager(applePager.addr()); + + DLOG(LOGINDENT " copying %p to %p, size %lx", + (char *)decryptedBuf + cryptOff - info->cryptoff, + cryptbase + inPageStart, curMapLen - inPageStart); + memmove((char *)decryptedBuf + cryptOff - info->cryptoff, + cryptbase + inPageStart, curMapLen - inPageStart); + + munmap(cryptbase, curMapLen); } + } - DLOG(LOGINDENT"copying enc pages, size: 0x%x..", info->cryptsize); - memcpy(dupe + info->cryptoff, decryptedBuf, info->cryptsize); + if (err) { + return 1; + } - DLOG(LOGINDENT"cleaning up..."); - free(decryptedBuf); - return 0; + DLOG(LOGINDENT "copying enc pages, size: 0x%x..", info->cryptsize); + memcpy(dupe + info->cryptoff, decryptedBuf, info->cryptsize); + + DLOG(LOGINDENT "cleaning up..."); + free(decryptedBuf); + return 0; #undef LOGINDENT } -int -decrypt_macho_slide(int f, uint8_t *inputData, uint8_t *outputData, size_t macho_off) { +int decrypt_macho_slide(int f, uint8_t *inputData, uint8_t *outputData, + size_t macho_off) { #define LOGINDENT " " - uint32_t offset = 0; - int cpuType = 0, cpuSubType = 0; - int ncmds = 0; - if (*(uint32_t *)inputData == MH_MAGIC_64) { // 64bit - struct mach_header_64* header = (struct mach_header_64*) inputData; - cpuType = header->cputype; - cpuSubType = header->cpusubtype; - ncmds = header->ncmds; - offset = sizeof(struct mach_header_64); - } else if (*(uint32_t *)inputData == MH_MAGIC) { // 32bit - struct mach_header* header = (struct mach_header*) inputData; - cpuType = header->cputype; - cpuSubType = header->cpusubtype; - ncmds = header->ncmds; - offset = sizeof(struct mach_header); + uint32_t offset = 0; + int cpuType = 0, cpuSubType = 0; + int ncmds = 0; + if (*(uint32_t *)inputData == MH_MAGIC_64) { // 64bit + struct mach_header_64 *header = (struct mach_header_64 *)inputData; + cpuType = header->cputype; + cpuSubType = header->cpusubtype; + ncmds = header->ncmds; + offset = sizeof(struct mach_header_64); + } else if (*(uint32_t *)inputData == MH_MAGIC) { // 32bit + struct mach_header *header = (struct mach_header *)inputData; + cpuType = header->cputype; + cpuSubType = header->cpusubtype; + ncmds = header->ncmds; + offset = sizeof(struct mach_header); + } + + DLOG(LOGINDENT "finding encryption_info segment in slide..."); + + // Enumerate all load commands and check for the encryption header, if found + // start "unprotect"'ing the contents. + + struct encryption_info_command *encryption_info = + NULL; // for both 32bit and 64bit macho, the command layout are the same + for (uint32_t i = 0; i < ncmds; i++) { + struct load_command *command = (struct load_command *)(inputData + offset); + + if (command->cmd == LC_ENCRYPTION_INFO || + command->cmd == LC_ENCRYPTION_INFO_64) { + DLOG(LOGINDENT " found encryption_info segment at offset %x", offset); + encryption_info = (struct encryption_info_command *)command; + // There should only be ONE header present anyways, so stop after + // the first one. + // + break; } - DLOG(LOGINDENT"finding encryption_info segment in slide..."); - - // Enumerate all load commands and check for the encryption header, if found - // start "unprotect"'ing the contents. - - struct encryption_info_command *encryption_info = NULL; // for both 32bit and 64bit macho, the command layout are the same - for (uint32_t i = 0; i < ncmds; i++) { - struct load_command* command = (struct load_command*) (inputData + offset); - - if (command->cmd == LC_ENCRYPTION_INFO || command->cmd == LC_ENCRYPTION_INFO_64) { - DLOG(LOGINDENT" found encryption_info segment at offset %x", offset); - encryption_info = (struct encryption_info_command*) command; - // There should only be ONE header present anyways, so stop after - // the first one. - // - break; - } - - offset += command->cmdsize; - } - if (!encryption_info || !encryption_info->cryptid) { - DLOG(LOGINDENT"this slide is not encrypted!"); - return 0; - } - - // If "unprotect"'ing is successful, then change the "cryptid" so that - // the loader does not attempt to decrypt decrypted pages. - // - - DLOG(LOGINDENT"decrypting encrypted data..."); - if (unprotect(f, outputData, cpuType, cpuSubType, encryption_info, macho_off) == 0) { - encryption_info = (struct encryption_info_command*) (outputData + offset); - encryption_info->cryptid = 0; - } else { - return 1; - } - + offset += command->cmdsize; + } + if (!encryption_info || !encryption_info->cryptid) { + DLOG(LOGINDENT "this slide is not encrypted!"); return 0; + } + + // If "unprotect"'ing is successful, then change the "cryptid" so that + // the loader does not attempt to decrypt decrypted pages. + // + + DLOG(LOGINDENT "decrypting encrypted data..."); + if (unprotect(f, outputData, cpuType, cpuSubType, encryption_info, + macho_off) == 0) { + encryption_info = (struct encryption_info_command *)(outputData + offset); + encryption_info->cryptid = 0; + } else { + return 1; + } + + return 0; #undef LOGINDENT } -int -decrypt_macho(const char *inputFile, const char *outputFile) -{ - DLOG("mapping input file: %s", inputFile); - size_t base_size; - int f; - uint8_t *base = map(inputFile, false, &base_size, &f); - if (base == NULL) { - return 1; - } - - DLOG("mapping output file: %s", outputFile); - size_t dupe_size = base_size; - uint8_t *dupe = map(outputFile, true, &dupe_size, NULL); - if (dupe == NULL) { - munmap(base, base_size); - return 1; - } - - DLOG("copying original data of size 0x%zx...", base_size); - memcpy(dupe, base, base_size); - - if (*(uint32_t *)base == FAT_CIGAM || *(uint32_t *)base == FAT_MAGIC) { - bool isBe = *(uint32_t *)base == FAT_CIGAM; - struct fat_header *fat_header = (struct fat_header *) base; - struct fat_arch *fatarches = (struct fat_arch *) (fat_header + 1); - auto fatInt = [isBe](int t) -> int {return isBe ? OSSwapInt32(t) : t;}; - - DLOG("handling %d fat arches...", fatInt(fat_header->nfat_arch)); - for (int fat_i = 0; fat_i < fatInt(fat_header->nfat_arch); fat_i++) { - auto curFatArch = &fatarches[fat_i]; - DLOG(" handling fat arch %d, cpuType 0x%x, cpuSubType 0x%x, fileOff 0x%x, size 0x%x, align 0x%x", fat_i, - fatInt(curFatArch->cputype), fatInt(curFatArch->cpusubtype), fatInt(curFatArch->offset), fatInt(curFatArch->size), fatInt(curFatArch->align)); - decrypt_macho_slide(f, base + fatInt(curFatArch->offset), dupe + fatInt(curFatArch->offset), fatInt(curFatArch->offset)); - } - } else { - DLOG(" not fat binary, directly decrypting it!"); - decrypt_macho_slide(f, base, dupe, 0); - } - +int decrypt_macho(const char *inputFile, const char *outputFile) { + DLOG("mapping input file: %s", inputFile); + size_t base_size; + int f; + uint8_t *base = map(inputFile, false, &base_size, &f); + if (base == NULL) { + return 1; + } + + DLOG("mapping output file: %s", outputFile); + size_t dupe_size = base_size; + uint8_t *dupe = map(outputFile, true, &dupe_size, NULL); + if (dupe == NULL) { munmap(base, base_size); - munmap(dupe, dupe_size); - return 0; + return 1; + } + + DLOG("copying original data of size 0x%zx...", base_size); + memcpy(dupe, base, base_size); + + if (*(uint32_t *)base == FAT_CIGAM || *(uint32_t *)base == FAT_MAGIC) { + bool isBe = *(uint32_t *)base == FAT_CIGAM; + struct fat_header *fat_header = (struct fat_header *)base; + struct fat_arch *fatarches = (struct fat_arch *)(fat_header + 1); + auto fatInt = [isBe](int t) -> int { return isBe ? OSSwapInt32(t) : t; }; + + DLOG("handling %d fat arches...", fatInt(fat_header->nfat_arch)); + for (int fat_i = 0; fat_i < fatInt(fat_header->nfat_arch); fat_i++) { + auto curFatArch = &fatarches[fat_i]; + DLOG(" handling fat arch %d, cpuType 0x%x, cpuSubType 0x%x, fileOff " + "0x%x, size 0x%x, align 0x%x", + fat_i, fatInt(curFatArch->cputype), fatInt(curFatArch->cpusubtype), + fatInt(curFatArch->offset), fatInt(curFatArch->size), + fatInt(curFatArch->align)); + decrypt_macho_slide(f, base + fatInt(curFatArch->offset), + dupe + fatInt(curFatArch->offset), + fatInt(curFatArch->offset)); + } + } else { + DLOG(" not fat binary, directly decrypting it!"); + decrypt_macho_slide(f, base, dupe, 0); + } + + munmap(base, base_size); + munmap(dupe, dupe_size); + return 0; }