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;
}