Skip to content

Commit ac4f2a7

Browse files
committed
add cli
1 parent 97a17c2 commit ac4f2a7

File tree

2 files changed

+213
-2
lines changed

2 files changed

+213
-2
lines changed

Makefile

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
11
TARGET := iphone:clang:latest:15.0
22
INSTALL_TARGET_PROCESSES = SpringBoard appstored installd TrollDecrypt
33
ARCHS = arm64 arm64e
4-
THEOS_DEVICE_IP = 192.168.1.160
4+
THEOS_DEVICE_IP = 192.168.1.82
55

66
include $(THEOS)/makefiles/common.mk
77

88
APPLICATION_NAME = TrollDecrypt
99
TWEAK_NAME = TrollDecryptHook
10-
TOOL_NAME = TDDaemonKiller
10+
TOOL_NAME = TDDaemonKiller trolldecryptjb
1111

1212
# Application files
1313
TrollDecrypt_FILES = SSZipArchive/minizip/unzip.c SSZipArchive/minizip/crypt.c SSZipArchive/minizip/ioapi_buf.c SSZipArchive/minizip/ioapi_mem.c SSZipArchive/minizip/ioapi.c SSZipArchive/minizip/minishared.c SSZipArchive/minizip/zip.c SSZipArchive/minizip/aes/aes_ni.c SSZipArchive/minizip/aes/aescrypt.c SSZipArchive/minizip/aes/aeskey.c SSZipArchive/minizip/aes/aestab.c SSZipArchive/minizip/aes/fileenc.c SSZipArchive/minizip/aes/hmac.c SSZipArchive/minizip/aes/prng.c SSZipArchive/minizip/aes/pwd2key.c SSZipArchive/minizip/aes/sha1.c SSZipArchive/SSZipArchive.m
@@ -29,6 +29,15 @@ TDDaemonKiller_CFLAGS = -fobjc-arc
2929
TDDaemonKiller_CODESIGN_FLAGS = -SappstoretrollerKiller/entitlements.plist
3030
TDDaemonKiller_INSTALL_PATH = /usr/local/bin
3131

32+
# CLI tool for decrypting apps from command line
33+
trolldecryptjb_FILES = cli_main.m TDDumpDecrypted.m TDUtils.m LSApplicationProxy+AltList.m appstoretrollerKiller/TSUtil.m
34+
trolldecryptjb_FILES += SSZipArchive/minizip/unzip.c SSZipArchive/minizip/crypt.c SSZipArchive/minizip/ioapi_buf.c SSZipArchive/minizip/ioapi_mem.c SSZipArchive/minizip/ioapi.c SSZipArchive/minizip/minishared.c SSZipArchive/minizip/zip.c SSZipArchive/minizip/aes/aes_ni.c SSZipArchive/minizip/aes/aescrypt.c SSZipArchive/minizip/aes/aeskey.c SSZipArchive/minizip/aes/aestab.c SSZipArchive/minizip/aes/fileenc.c SSZipArchive/minizip/aes/hmac.c SSZipArchive/minizip/aes/prng.c SSZipArchive/minizip/aes/pwd2key.c SSZipArchive/minizip/aes/sha1.c SSZipArchive/SSZipArchive.m
35+
trolldecryptjb_FRAMEWORKS = Foundation MobileCoreServices
36+
trolldecryptjb_CFLAGS = -fobjc-arc
37+
trolldecryptjb_CODESIGN_FLAGS = -Sentitlements.plist
38+
trolldecryptjb_INSTALL_PATH = /usr/local/bin
39+
trolldecryptjb_BUNDLE_RESOURCES = flexdecrypt_bin dlopentool
40+
3241
# dlopen tool - compile and bundle with app
3342
DLOPEN_TOOL_NAME = dlopentool
3443
DLOPEN_TOOL_FILES = dlopentool.c

cli_main.m

Lines changed: 202 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,202 @@
1+
#import <Foundation/Foundation.h>
2+
#import "TDUtils.h"
3+
#import "LSApplicationProxy+AltList.h"
4+
#import "TDDumpDecrypted.h"
5+
6+
// Helper function to find tool in common locations
7+
NSString *findTool(NSString *toolName) {
8+
NSFileManager *fm = [NSFileManager defaultManager];
9+
10+
// Check common locations
11+
NSArray *searchPaths = @[
12+
@"/usr/local/bin",
13+
@"/usr/bin",
14+
[[NSBundle mainBundle] resourcePath] ?: @"",
15+
[[NSFileManager defaultManager] currentDirectoryPath],
16+
@"/var/jb/usr/local/bin", // For rootless jailbreaks
17+
@"/usr/bin"
18+
];
19+
20+
for (NSString *path in searchPaths) {
21+
if ([path length] == 0) continue;
22+
NSString *fullPath = [path stringByAppendingPathComponent:toolName];
23+
if ([fm fileExistsAtPath:fullPath]) {
24+
return fullPath;
25+
}
26+
}
27+
28+
// Check if tool is in PATH
29+
NSString *whichPath = [NSString stringWithFormat:@"/usr/bin/which %@", toolName];
30+
FILE *pipe = popen([whichPath UTF8String], "r");
31+
if (pipe) {
32+
char buffer[1024];
33+
if (fgets(buffer, sizeof(buffer), pipe) != NULL) {
34+
NSString *result = [[NSString stringWithUTF8String:buffer] stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
35+
pclose(pipe);
36+
if ([result length] > 0 && [fm fileExistsAtPath:result]) {
37+
return result;
38+
}
39+
}
40+
pclose(pipe);
41+
}
42+
43+
return nil;
44+
}
45+
46+
void printUsage(const char *programName) {
47+
printf("Usage: %s <bundle_id> <output_folder>\n", programName);
48+
printf("\n");
49+
printf("Decrypts an iOS app and creates a decrypted IPA file.\n");
50+
printf("\n");
51+
printf("Arguments:\n");
52+
printf(" bundle_id - Bundle identifier of the app to decrypt (e.g., com.apple.calculator)\n");
53+
printf(" output_folder - Path to output folder where the decrypted IPA will be saved\n");
54+
printf("\n");
55+
printf("Example:\n");
56+
printf(" %s com.apple.calculator /var/mobile/Documents/decrypted\n", programName);
57+
}
58+
59+
NSDictionary *getAppInfoFromBundleID(NSString *bundleID) {
60+
LSApplicationProxy *appProxy = [LSApplicationProxy applicationProxyForIdentifier:bundleID];
61+
62+
if (!appProxy) {
63+
fprintf(stderr, "Error: App with bundle ID '%s' not found\n", [bundleID UTF8String]);
64+
return nil;
65+
}
66+
67+
NSString *name = [appProxy atl_nameToDisplay];
68+
NSString *version = [appProxy atl_shortVersionString];
69+
NSString *executable = appProxy.canonicalExecutablePath;
70+
71+
if (!name || !version || !executable) {
72+
fprintf(stderr, "Error: Failed to get app information for bundle ID '%s'\n", [bundleID UTF8String]);
73+
return nil;
74+
}
75+
76+
NSDictionary *appInfo = @{
77+
@"bundleID": bundleID,
78+
@"name": name,
79+
@"version": version,
80+
@"executable": executable
81+
};
82+
83+
return appInfo;
84+
}
85+
86+
void decryptAppCLI(NSDictionary *app, NSString *outputFolder) {
87+
NSString *bundleID = app[@"bundleID"];
88+
NSString *name = app[@"name"];
89+
NSString *version = app[@"version"];
90+
91+
printf("[trolldecrypt] Starting decryption for: %s (%s)\n", [name UTF8String], [bundleID UTF8String]);
92+
printf("[trolldecrypt] Version: %s\n", [version UTF8String]);
93+
94+
// Get the app bundle path
95+
LSApplicationProxy *appProxy = [LSApplicationProxy applicationProxyForIdentifier:bundleID];
96+
if (!appProxy) {
97+
fprintf(stderr, "[trolldecrypt] Error: Failed to get app proxy for %s\n", [bundleID UTF8String]);
98+
exit(1);
99+
}
100+
101+
NSString *appPath = [appProxy bundleURL].path;
102+
if (!appPath) {
103+
fprintf(stderr, "[trolldecrypt] Error: Failed to get app path for %s\n", [bundleID UTF8String]);
104+
exit(1);
105+
}
106+
107+
printf("[trolldecrypt] App path: %s\n", [appPath UTF8String]);
108+
109+
// Find all mach-o files in the app
110+
NSArray *machOFiles = findAllMachOFiles(appPath);
111+
printf("[trolldecrypt] Found %lu mach-o files\n", (unsigned long)machOFiles.count);
112+
113+
if (machOFiles.count == 0) {
114+
fprintf(stderr, "[trolldecrypt] Error: No mach-o files found in %s\n", [appPath UTF8String]);
115+
exit(1);
116+
}
117+
118+
// Create temporary working directory
119+
NSString *tempDir = [NSTemporaryDirectory() stringByAppendingPathComponent:[NSString stringWithFormat:@"%@_decrypt_work", name]];
120+
NSFileManager *fm = [NSFileManager defaultManager];
121+
[fm removeItemAtPath:tempDir error:nil]; // Clean up any existing temp dir
122+
NSError *error;
123+
if (![fm createDirectoryAtPath:tempDir withIntermediateDirectories:YES attributes:nil error:&error]) {
124+
fprintf(stderr, "[trolldecrypt] Error: Failed to create temp directory: %s\n", [error.localizedDescription UTF8String]);
125+
exit(1);
126+
}
127+
128+
// Copy the entire app bundle to temp directory
129+
NSString *tempAppPath = [tempDir stringByAppendingPathComponent:[appPath lastPathComponent]];
130+
NSError *copyError;
131+
if (![fm copyItemAtPath:appPath toPath:tempAppPath error:&copyError]) {
132+
fprintf(stderr, "[trolldecrypt] Error: Failed to copy app bundle: %s\n", [copyError.localizedDescription UTF8String]);
133+
exit(1);
134+
}
135+
136+
printf("[trolldecrypt] Copied app bundle to: %s\n", [tempAppPath UTF8String]);
137+
138+
// Decrypt each mach-o file and replace it in the temp app bundle
139+
NSUInteger totalFiles = machOFiles.count;
140+
for (NSUInteger i = 0; i < machOFiles.count; i++) {
141+
NSString *machOFile = machOFiles[i];
142+
NSString *relativePath = [machOFile substringFromIndex:appPath.length + 1];
143+
NSString *tempMachOPath = [tempAppPath stringByAppendingPathComponent:relativePath];
144+
145+
printf("[trolldecrypt] Decrypting [%lu/%lu]: %s\n", (unsigned long)(i + 1), (unsigned long)totalFiles, [relativePath UTF8String]);
146+
147+
// Decrypt the file directly to the temp location (replacing original)
148+
decryptMachOFile(machOFile, tempMachOPath);
149+
}
150+
151+
// Create output directory if it doesn't exist
152+
if (![fm fileExistsAtPath:outputFolder isDirectory:NULL]) {
153+
if (![fm createDirectoryAtPath:outputFolder withIntermediateDirectories:YES attributes:nil error:&error]) {
154+
fprintf(stderr, "[trolldecrypt] Error: Failed to create output directory: %s\n", [error.localizedDescription UTF8String]);
155+
exit(1);
156+
}
157+
}
158+
159+
// Create IPA from the modified app bundle
160+
NSString *ipaFileName = [NSString stringWithFormat:@"%@_%@_decrypted.ipa", name, version];
161+
NSString *ipaPath = [outputFolder stringByAppendingPathComponent:ipaFileName];
162+
163+
printf("[trolldecrypt] Creating IPA file: %s\n", [ipaPath UTF8String]);
164+
createIPAFromAppBundle(tempAppPath, ipaPath);
165+
166+
// Clean up temp directory
167+
[fm removeItemAtPath:tempDir error:nil];
168+
169+
printf("[trolldecrypt] ========================================\n");
170+
printf("[trolldecrypt] Decryption completed successfully!\n");
171+
printf("[trolldecrypt] IPA saved to: %s\n", [ipaPath UTF8String]);
172+
printf("[trolldecrypt] ========================================\n");
173+
}
174+
175+
int main(int argc, char *argv[]) {
176+
@autoreleasepool {
177+
if (argc != 3) {
178+
printUsage(argv[0]);
179+
exit(1);
180+
}
181+
182+
NSString *bundleID = [NSString stringWithUTF8String:argv[1]];
183+
NSString *outputFolder = [NSString stringWithUTF8String:argv[2]];
184+
185+
// Validate output folder path
186+
if (![outputFolder hasPrefix:@"/"]) {
187+
fprintf(stderr, "Error: Output folder must be an absolute path\n");
188+
exit(1);
189+
}
190+
191+
// Get app information from bundle ID
192+
NSDictionary *appInfo = getAppInfoFromBundleID(bundleID);
193+
if (!appInfo) {
194+
exit(1);
195+
}
196+
197+
// Decrypt the app
198+
decryptAppCLI(appInfo, outputFolder);
199+
200+
return 0;
201+
}
202+
}

0 commit comments

Comments
 (0)