├── .gitignore
├── README.md
├── compiled
└── mobdevim
├── media
└── color_wow.png
├── mobdevim.xcodeproj
├── project.pbxproj
├── project.xcworkspace
│ └── contents.xcworkspacedata
└── xcshareddata
│ └── xcschemes
│ ├── distribute.xcscheme
│ └── mobdevim.xcscheme
└── mobdevim
├── Console
├── console_print.h
└── console_print.m
├── Debug Application
├── debug_application.h
└── debug_application.m
├── Delete Application
├── delete_application.h
└── delete_application.m
├── Device Info
├── get_device_info.h
└── get_device_info.m
├── Install Application
├── install_application.h
└── install_application.m
├── Instruments
├── instruments.h
└── instruments.m
├── List Applications
├── list_applications.h
└── list_applications.m
├── Open Program
├── open_program.h
└── open_program.mm
├── Provisioning Porfiles
├── get_provisioning_profiles.h
└── get_provisioning_profiles.m
├── SimulateLocation
├── sim_location.h
└── sim_location.m
├── TODOs
├── backup_device
├── backup_device.h
└── backup_device.m
├── console.temp_caseinsensitive_rename.m
├── install_ddi
├── install_ddi.h
└── install_ddi.m
├── logs
├── get_logs.h
└── get_logs.m
├── main.m
├── misc
├── Entitlements.entitlements
├── ExternalDeclarations.h
├── InstrumentsPlugin.h
├── NSArray+Output.h
├── NSArray+Output.m
├── Progress Bar
│ ├── progressbar.c
│ ├── progressbar.h
│ ├── statusbar.c
│ └── statusbar.h
├── helpers.h
└── helpers.m
├── mobdevim.l
├── notifications
├── notifications.h
└── notifications.mm
├── on_load.m
├── process
├── LICENSE
├── cftypes.h
├── cftypes.mm
├── common.h
├── common.mm
├── ios_instruments_client.h
├── ios_instruments_client.mm
├── makefile
├── mobile_device.h
├── mobile_device.mm
├── process.h
└── process.mm
├── remove_file
├── remove_file.h
└── remove_file.m
├── send_files
├── send_files.h
└── send_files.m
├── springboardservices
├── springboardservices.h
└── springboardservices.m
├── wifi connect
├── wifi_connect.m
└── wifie_connect.h
└── yoink
├── yoink.h
└── yoink.m
/.gitignore:
--------------------------------------------------------------------------------
1 | /.DS_Store
2 | /mobdevim/.DS_Store
3 | *.xcuserstate
4 | *.xcbkptlist
5 | .DS_Store
6 | UserInterfaceState.xcuserstate
7 | Breakpoints_v2.xcbkptlist
8 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # mobdevim
2 | **Mobile Device Improved**: Command line utility that interacts with plugged in iOS devices. Uses Apple's MobileDevice framework
3 |
4 | ---
5 |
6 | Yet another MobileDevice utility
7 |
8 | ---
9 |
10 | ## Installation
11 |
12 | 1. clone
13 | 2. build project
14 | 3. Upon build success,` mobdevim` will be placed in **/usr/local/bin**
15 |
16 | Make sure you have permissions to write to `/usr/local/bin` or else the Xcode build script will fail
17 |
18 | ---
19 |
20 | Alternatively, a precompiled version is available here.
21 |
22 | ## Commands
23 |
24 | ```
25 | Name
26 | mobdevim -- (mobiledevice-improved) Interact with an iOS device (compiled Nov 7 2023, 15:50:43)
27 |
28 | The mobdevim utlity interacts with your plugged in iOS device over USB using Apple's
29 | private framework, MobileDevice.
30 |
31 | The options are as follows:
32 | -F # List all available connections, or connect to a specific device
33 |
34 | mobdevim -F # List all known devices
35 | mobdevim -F 00234 # Connect to first device that has a UDID containing 00234
36 | mobdevim -F\? # Check for devices
37 |
38 | mobdevim -U # Prefer connection over USB
39 |
40 | mobdevim -W # Prefer connection over WIFI
41 |
42 | -f # Get device info to a connected device (defaults to first USB connection)
43 |
44 | -g # Get device logs/issues (TODO Broken in 16.5)
45 |
46 | mobdevim -g com.example.name # Get issues for com.example.name app
47 | mobdevim -g 3 # Get the 3rd most recent issue
48 | mobdevim -g __all # Get all the logs
49 |
50 | -l # List app information
51 |
52 | mobdevim -l # List all apps
53 | mobdevim -l com.example.test # Get detailed info about app, com.example.test
54 | mobdevim -l com.example.test Entitlements # List "Entitlements" key from com.example.test
55 |
56 | -r # Remove file
57 |
58 | mobdevim -r /fullpath/to/file # removes file (sandbox permitting)
59 |
60 | -y # Yoink sandbox content
61 |
62 | mobdevim -y com.example.test # Yoink contacts from app
63 |
64 | -s # Send content to device (use content from yoink command)
65 |
66 | mobdevim -s com.example.test /tmp/com.example.test # Send contents in /tmp/com.example.test to app
67 |
68 | -i # Install application (expects path to bundle)
69 |
70 | -I # Install/mount a DDI (via Xcode subdir or repos like https://github.com/mspvirajpatel/Xcode_Developer_Disk_Images
71 |
72 | mobdevim -I /path/to/ddi.signature /path/to/ddi.dmg # Install DDI
73 |
74 | -M # unMount a DDI (via Xcode subdir or repos like https://github.com/mspvirajpatel/Xcode_Developer_Disk_Images
75 |
76 | mobdevim -M # Unmount an alreaady mounted dev DDI
77 |
78 | -u # Uninstall application, expects bundleIdentifier
79 |
80 | mobdevim -u com.example.test # Uninstall app
81 |
82 | -w # Connect device to WiFi mode
83 |
84 | mobdevim -w # Connect device to wifi for this computer
85 | mobdevim -w uuid_here # Connect device to wifi for UUID
86 | mobdevim -w off # Disable device wifi
87 | mobdevim -w uuid # Display the computer's host uuid
88 |
89 | -S # Arrange SpringBoard icons
90 |
91 | mobdevim -S # Get current SpringBoard icon layout
92 | mobdevim -S /path/to/plist # Set SpringBoard icon layout from plist file
93 | mobdevim -S asshole # Set SpringBoard icon layout to asshole mode
94 | mobdevim -S restore # Restore SpringBoard icon layout (if backup was created)
95 |
96 | -L # Simulate location (requires DDI)
97 |
98 | mobdevim -L 0 0 # Remove location simulation
99 | mobdevim -L 40.7128 -73.935242 # Simulate phone in New York
100 |
101 | -c # Dump out the console information. Use ctrl-c to terminate
102 |
103 | -C # Get developer certificates on device
104 |
105 | -p # Display running processes on the device (requiers DDI)
106 |
107 | -k # Kill a process (requiers DDI)
108 |
109 | TODO -b # Backup device
110 |
111 | -P # Display developer provisioning profile info
112 |
113 | mobdevim -P # List all installed provisioning profiles
114 | mobdevim -P b68410a1-d825-4b7c-8e5d-0f76a9bde6b9 # Get detailed provisioning UUID info
115 |
116 | -o # Open application (requires DDI)
117 |
118 | mobdevim -o com.reverse.domain # open app
119 | mobdevim -o com.reverse.domain -A "Some args here" -V AnEnv=EnValue -V A=Bmobdevim # open app with launch args and env vars
120 |
121 | -R # Use color
122 |
123 | -Q # Quiet mode, ideal for limiting output or checking if a value exists based upon return status
124 |
125 | Environment variables: DSCOLOR - Use color (same as -R)
126 |
127 | DSDEBUG - verbose debugging
128 |
129 | DSPLIST - Display output in plist form (mobdevim -l com.test.example)
130 |
131 | OS_ACTIVITY_DT_MODE - Combine w/ DSDEBUG to enable MobileDevice logging
132 |
133 | ```
134 |
135 | 
136 |
137 |
--------------------------------------------------------------------------------
/compiled/mobdevim:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DerekSelander/mobdevim/68cddb31bf80f9adf030d51bc799740f042d5958/compiled/mobdevim
--------------------------------------------------------------------------------
/media/color_wow.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DerekSelander/mobdevim/68cddb31bf80f9adf030d51bc799740f042d5958/media/color_wow.png
--------------------------------------------------------------------------------
/mobdevim.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/mobdevim.xcodeproj/xcshareddata/xcschemes/distribute.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
15 |
21 |
22 |
23 |
24 |
25 |
30 |
31 |
32 |
33 |
43 |
45 |
51 |
52 |
53 |
54 |
60 |
61 |
62 |
63 |
69 |
71 |
77 |
78 |
79 |
80 |
82 |
83 |
86 |
87 |
88 |
--------------------------------------------------------------------------------
/mobdevim.xcodeproj/xcshareddata/xcschemes/mobdevim.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
15 |
21 |
22 |
23 |
24 |
25 |
30 |
31 |
37 |
38 |
39 |
40 |
41 |
42 |
56 |
58 |
64 |
65 |
66 |
67 |
70 |
71 |
74 |
75 |
78 |
79 |
82 |
83 |
86 |
87 |
90 |
91 |
94 |
95 |
98 |
99 |
102 |
103 |
106 |
107 |
110 |
111 |
114 |
115 |
118 |
119 |
122 |
123 |
126 |
127 |
130 |
131 |
134 |
135 |
138 |
139 |
142 |
143 |
146 |
147 |
150 |
151 |
154 |
155 |
158 |
159 |
162 |
163 |
166 |
167 |
170 |
171 |
174 |
175 |
178 |
179 |
182 |
183 |
186 |
187 |
188 |
189 |
193 |
194 |
198 |
199 |
203 |
204 |
208 |
209 |
213 |
214 |
215 |
216 |
222 |
224 |
230 |
231 |
232 |
233 |
235 |
236 |
239 |
240 |
241 |
--------------------------------------------------------------------------------
/mobdevim/Console/console_print.h:
--------------------------------------------------------------------------------
1 | //
2 | // Console.h
3 | // mobdevim
4 | //
5 | // Created by Derek Selander
6 | // Copyright © 2020 Selander. All rights reserved.
7 | //
8 |
9 | #import
10 | #import "ExternalDeclarations.h"
11 | #import "helpers.h"
12 |
13 | /// The name of the process to explore, no supplying this will snoop all output
14 | extern NSString * const kConsoleProcessName;
15 |
16 | /// Prints console output coming from the device
17 | int console_print(AMDeviceRef d, NSDictionary* options);
18 |
--------------------------------------------------------------------------------
/mobdevim/Console/console_print.m:
--------------------------------------------------------------------------------
1 | //
2 | // Console.m
3 | // mobdevim
4 | //
5 | // Created by Derek Selander
6 | // Copyright © 2020 Selander. All rights reserved.
7 | //
8 |
9 | #import "console_print.h"
10 | //#import
11 | #import
12 |
13 | #define SIZE 2000
14 | NSString *const kConsoleProcessName = @"com.selander.console.processname";
15 |
16 | int console_print(AMDeviceRef d, NSDictionary* options) {
17 |
18 | AMDServiceConnectionRef connection = NULL;
19 | handle_err(AMDeviceSecureStartService(d, @"com.apple.syslog_relay",
20 | @{@"UnlockEscrowBag" : @YES},
21 | &connection));
22 | handle_err(AMDeviceStopSession(d));
23 |
24 | const char *processName = [[options objectForKey:kConsoleProcessName] UTF8String];
25 | int proc_len = 0;
26 | if (processName) {
27 | proc_len = (int)strlen(processName);
28 | }
29 |
30 | char buffer[SIZE];
31 | // memset(buffer, '\0', SIZE);
32 |
33 | int amountRead = 0;
34 | setbuf(stdout, NULL);
35 | bool shouldSkip = false;
36 | while (1) {
37 |
38 | memset(buffer, '\0', SIZE);
39 | amountRead = (int)AMDServiceConnectionReceive(connection, buffer, SIZE - 1 ); // Get those "P-\x01" bytes then end, easiest way to fix
40 | if (amountRead <= 0) {
41 | break;
42 | }
43 |
44 | if (buffer[0] == '\x00') {
45 | char *tmp = strchr(&buffer[1], '[');
46 | pid_t pidnumber = tmp ? atoi(tmp + 1) : -1;
47 |
48 | tmp = strchr(&buffer[1], '>');
49 | const char* msg = tmp ? tmp + 3 : "?";
50 |
51 | const char* msgtype = "???";
52 | const char* msgtypeend = "???";
53 | tmp = strchr(&buffer[1], '<');
54 | if (tmp) {
55 | msgtype = tmp + 1;
56 | tmp = strchr(tmp, '>');
57 | if (tmp) {
58 | msgtypeend = tmp;
59 | }
60 | }
61 |
62 | tmp = strrchr( tmp ? tmp : &buffer[1], '(');
63 | #if 0
64 | Oct 23 10:57:18 Dereks-iPhone symptomsd(SymptomNetworkUsage)[148] : Flow 322462 Unexpected attribution change, was procname pid 74 epid 74 uuid euuid now 457 457
65 | #endif
66 | const char* pidName = "?";
67 | const char* pidNamEend = "?";
68 | const char* dateStr = "?";
69 | const char* deviceStr = "?";
70 |
71 | tmp = strchr(&buffer[1], ' ');
72 | if (tmp) {
73 | dateStr = &buffer[1];
74 | tmp = strchr(tmp + 1, ' ');
75 | if (tmp) {
76 | tmp = strchr(tmp + 1, ' ');
77 | if (tmp) { // Dereks-iPhone devicestr
78 | deviceStr = tmp + 1;
79 | tmp = strchr(tmp + 1, ' '); // date end, assign deviceStr
80 | if (tmp) {
81 | pidName = tmp + 1;
82 | if (processName) {
83 | if (strncmp(pidName, processName, proc_len) != 0) {
84 | shouldSkip = true;
85 | continue;
86 | }
87 | shouldSkip = false;
88 | }
89 |
90 | tmp = strchr(tmp + 1, '['); // subsystem start
91 | if (tmp) {
92 | pidNamEend = tmp;
93 | }
94 | }
95 | }
96 | }
97 | }
98 |
99 | const char *strColor = "";
100 | if (strncmp("Notice", msgtype, 6) == 0) {
101 | msgtype = "+";
102 | strColor = dcolor(dc_gray);
103 | } else {
104 | msgtype = "-";
105 | strColor = dcolor(dc_red);
106 | }
107 |
108 | if (deviceStr) {
109 | fprintf(stdout, "%s%s%s %s%.*s%s %s%6d%s %s%.*s:%s\t%s%s%s", strColor, msgtype, colorEnd(), dcolor(dc_gray), (int)(deviceStr - &buffer[1]) - 1 , &buffer[1], colorEnd(), dcolor(dc_magenta), pidnumber, colorEnd(), dcolor(dc_yellow), (int)(pidNamEend - pidName), pidName, colorEnd(), dcolor(dc_cyan), msg, colorEnd());
110 | } else {
111 | fprintf(stdout, "%s%.*s%s", dcolor(dc_gray), amountRead - 1, &buffer[1], colorEnd());
112 | }
113 |
114 | } else {
115 | // only messages here
116 | if (!shouldSkip) {
117 | fprintf(stdout, "--- %s%.*s%s", dcolor(dc_cyan), amountRead , &buffer[0], colorEnd());
118 | }
119 | }
120 |
121 | // memset(buffer, '\0', SIZE);
122 | }
123 |
124 | return 0;
125 | }
126 |
--------------------------------------------------------------------------------
/mobdevim/Debug Application/debug_application.h:
--------------------------------------------------------------------------------
1 | //
2 | // DebugServer.h
3 | // mobdevim
4 | //
5 | // Created by Derek Selander
6 | // Copyright © 2020 Selander. All rights reserved.
7 | //
8 |
9 | #import
10 | #import "ExternalDeclarations.h"
11 |
12 | /// The path to the IPA file
13 | extern NSString * const kDebugApplicationIdentifier;
14 | extern NSString * const kDebugQuickLaunch;
15 | extern NSString * const kProcessEnvVars;
16 |
17 | int debug_application(AMDeviceRef d, NSDictionary* options);
18 |
--------------------------------------------------------------------------------
/mobdevim/Debug Application/debug_application.m:
--------------------------------------------------------------------------------
1 | //
2 | // DebugServer.m
3 | // mobdevim
4 | //
5 | // Created by Derek Selander
6 | // Copyright © 2020 Selander. All rights reserved.
7 | //
8 |
9 | #import "debug_application.h"
10 | #import "helpers.h"
11 | #import
12 | @import Cocoa;
13 | @import Darwin;
14 | @import AppKit;
15 | @import MachO;
16 |
17 | #define DUMMY_TARGET_BLOB @"/tmp/SBTargetDummy/dsblob"
18 | #define LLDB_SCRIPT_PATH @"/tmp/mobdevim_setupscript"
19 |
20 | NSString * const kDebugApplicationIdentifier = @"com.selander.debug.bundleidentifier";
21 | NSString * const kDebugQuickLaunch = @"com.selander.debug.quicklaunch";
22 | NSString * const kProcessEnvVars = @"com.selander.debug.envvar";;
23 |
24 | NSString* extractDummyTarget(NSString *name) {
25 | NSFileManager* manager = [NSFileManager defaultManager];
26 | NSString *dummyPath = [[DUMMY_TARGET_BLOB stringByDeletingLastPathComponent] stringByAppendingPathComponent:name];
27 | if ([manager fileExistsAtPath:dummyPath]) {
28 | return dummyPath;
29 | }
30 |
31 | NSError *err = nil;
32 | NSString* targetFolder = @"/tmp/SBTargetDummy/";
33 | BOOL isDirectory = NO;
34 | if ([manager fileExistsAtPath:targetFolder isDirectory:&isDirectory]) {
35 | if (!isDirectory) {
36 | ErrorMessageThenDie("%s should be a directory", [targetFolder UTF8String]);
37 | }
38 | } else {
39 | [manager createDirectoryAtPath:targetFolder withIntermediateDirectories:YES
40 | attributes:nil error:&err];
41 | if (err) {
42 | ErrorMessageThenDie("%s\n", [[err localizedDescription] UTF8String]);
43 | }
44 | }
45 |
46 |
47 | if (![manager fileExistsAtPath:DUMMY_TARGET_BLOB isDirectory:&isDirectory] && isDirectory) {
48 | unsigned long size = 0;
49 | uint8_t *dataStart = getsectiondata(&_mh_execute_header, "__TEXT", "__SBDummyTarget", &size);
50 | if (!dataStart) {
51 | ErrorMessageThenDie("Can't find embedded DummyTarget, exiting\n");
52 | }
53 | NSData *data = [NSData dataWithBytes:dataStart length:size];
54 | [data writeToFile:DUMMY_TARGET_BLOB atomically:YES];
55 | }
56 | NSArray *arguments = @[@"-q", DUMMY_TARGET_BLOB, @"-d", targetFolder];
57 |
58 | NSTask *unzipTask = [[NSTask alloc] init];
59 | [unzipTask setLaunchPath:@"/usr/bin/unzip"];
60 | [unzipTask setCurrentDirectoryPath:targetFolder];
61 | [unzipTask setArguments:arguments];
62 | [unzipTask launch];
63 | dsprintf(stdout, "Extracting dummy target... ");
64 | [unzipTask waitUntilExit]; //remove this to start the task concurrently
65 | err = nil;
66 | [manager moveItemAtPath:@"/tmp/SBTargetDummy/WOMP.app" toPath:dummyPath error:&err];
67 | if (err) {
68 | ErrorMessageThenDie("%s\n", [[err localizedDescription] UTF8String]);
69 | }
70 | dsprintf(stdout, "Extracted!\n");
71 |
72 | return dummyPath;
73 | }
74 |
75 | void generateSetupScript(const char *localExecutablePath, const char * remoteExecutablePath, const char *launchCommand, int port) {
76 | NSString *setupScript =
77 | @"platform select remote-ios\n\
78 | target create \"%s\"\n\
79 | script lldb.target.module[0].SetPlatformFileSpec(lldb.SBFileSpec(\"%s\"))\n\
80 | process connect connect://127.0.0.1:%d\n";
81 |
82 | if (launchCommand) {
83 | setupScript = [setupScript stringByAppendingFormat:@"\
84 | script lldb.debugger.SetAsync(True)\n\
85 | %s\n\
86 | process detach", launchCommand];
87 | }
88 | NSError *error = nil;
89 | if (!localExecutablePath) {
90 | dsprintf(stderr, "Attaching by bundleIdentifier not implemented yet, please use bundle path\n");
91 | exit(1);
92 | }
93 | NSString *script = [NSString stringWithFormat:setupScript, localExecutablePath, remoteExecutablePath, port];
94 |
95 | [script writeToFile:LLDB_SCRIPT_PATH atomically:YES encoding:NSUTF8StringEncoding error:&error];
96 | if (error) {
97 | ErrorMessageThenDie("Couldn't write script to file: %s", [[error localizedDescription] UTF8String]);
98 | }
99 | }
100 |
101 | static NSString *appPath;
102 |
103 | /********************************************************************************
104 | Source start https://github.com/phonegap/ios-deploy GPL v3 license
105 | ********************************************************************************/
106 | static int lldbfd = 0;
107 | static CFSocketRef server_socket;
108 | static CFSocketRef lldb_socket;
109 | static CFSocketRef fdvendor;
110 | static int port = 0;
111 |
112 | void lldb_callback(CFSocketRef s, CFSocketCallBackType callbackType, CFDataRef address, const void *data, void *info)
113 | {
114 | static dispatch_once_t onceToken;
115 | dispatch_once(&onceToken, ^{
116 | dsprintf(stdout, "Connected!\n");
117 | });
118 |
119 | NSData *objcData = (__bridge NSData*)data;
120 | NSString *str = [[NSString alloc] initWithData:objcData encoding:NSUTF8StringEncoding];
121 | dsdebug("%s: %s\n", __FUNCTION__, [str UTF8String]);
122 | if (CFDataGetLength (data) == 0) {
123 | // close the socket on which we've got end-of-file, the lldb_socket.
124 | CFSocketInvalidate(s);
125 | CFRelease(s);
126 | dsprintf(stdout, "Connection terminated\n");
127 | CFRunLoopStop(CFRunLoopGetMain());
128 | return;
129 | }
130 | write(lldbfd, CFDataGetBytePtr (data), CFDataGetLength (data));
131 | }
132 |
133 | int kill_ptree(pid_t root, int signum);
134 | void
135 | server_callback (CFSocketRef s, CFSocketCallBackType callbackType, CFDataRef address, const void *data, void *info)
136 | {
137 | ssize_t res;
138 | NSData *objcData = (__bridge NSData*)data;
139 | NSString *str = [[NSString alloc] initWithData:objcData encoding:NSUTF8StringEncoding];
140 | dsdebug("%s: %s\n", __FUNCTION__, [str UTF8String]);
141 | if (CFDataGetLength (data) == 0) {
142 | // close the socket on which we've got end-of-file, the server_socket.
143 | CFSocketInvalidate(s);
144 | CFRelease(s);
145 |
146 | return;
147 | }
148 | res = write (CFSocketGetNative (lldb_socket), CFDataGetBytePtr (data), CFDataGetLength (data));
149 | }
150 |
151 | static const char* generateLaunchString(NSDictionary *options) {
152 | if (!options[kDebugQuickLaunch]) {
153 | return NULL;;
154 | }
155 | NSArray *envVars = options[kProcessEnvVars];
156 |
157 | NSMutableString *str = [NSMutableString stringWithString:@"process launch -X true "];
158 | for (NSString *env in envVars) {
159 | [str appendFormat:@" -v %@", env];
160 | }
161 | [str appendString:@" -- "];
162 | return str.UTF8String;
163 | }
164 |
165 | void fdvendor_callback(CFSocketRef s, CFSocketCallBackType callbackType, CFDataRef address, const void *data, void *info) {
166 | CFSocketNativeHandle socket = (CFSocketNativeHandle)(*((CFSocketNativeHandle *)data));
167 |
168 | dsprintf(stdout, "Connecting to debugserver...");
169 | assert (callbackType == kCFSocketAcceptCallBack);
170 |
171 | lldb_socket = CFSocketCreateWithNative(NULL, socket, kCFSocketDataCallBack, &lldb_callback, NULL);
172 | int flag = 1;
173 | int res = setsockopt(socket, IPPROTO_TCP, TCP_NODELAY, (char *) &flag, sizeof(flag));
174 | assert(res == 0);
175 | CFRunLoopAddSource(CFRunLoopGetMain(), CFSocketCreateRunLoopSource(NULL, lldb_socket, 0), kCFRunLoopCommonModes);
176 |
177 | CFSocketInvalidate(s);
178 | CFRelease(s);
179 | }
180 |
181 | /********************************************************************************
182 | Source end https://github.com/phonegap/ios-deploy GPL v3 license
183 | ********************************************************************************/
184 |
185 | static pid_t childPID = 0;
186 | void Kill_The_Spare__AVADA_KEDAVRA(void) {
187 | if (childPID) {
188 | dsdebug("Killing the child PID %d\n", childPID);
189 | kill(childPID, SIGKILL);
190 | }
191 | }
192 |
193 | int debug_application(AMDeviceRef d, NSDictionary* options) {
194 |
195 | NSDictionary *dict = nil;
196 | AMDeviceLookupApplications(d, @{@"ReturnAttributes": @[@"ProfileValidated", @"CFBundleIdentifier", @"Path"], @"ShowLaunchProhibitedApps" : @YES}, &dict);
197 | NSString *applicationIdentifier = options[kDebugApplicationIdentifier];
198 |
199 | BOOL isDir = NO;
200 | NSString * localApplicationPath = nil;
201 | if ([[NSFileManager defaultManager] fileExistsAtPath:applicationIdentifier
202 | isDirectory:&isDir] && isDir) {
203 | NSBundle *bundle = [NSBundle bundleWithPath:applicationIdentifier];
204 | NSString *correctedBundleIdentifier = [bundle bundleIdentifier];
205 | if (correctedBundleIdentifier) {
206 | localApplicationPath = applicationIdentifier;
207 | applicationIdentifier = correctedBundleIdentifier;
208 | }
209 |
210 | }
211 |
212 | if (!applicationIdentifier) {
213 | ErrorMessageThenDie("Invalid application identifier\n");
214 | }
215 | NSDictionary *appDict = dict[applicationIdentifier];
216 | if (!appDict) {
217 | ErrorMessageThenDie("Invalid application identifier \"%s\", use mobdevim -l to see application identifiers\n", [applicationIdentifier UTF8String]);
218 | }
219 | appPath = appDict[@"Path"];
220 | if (!appPath) {
221 | ErrorMessageThenDie("Couldn't find app path for \"%s\"\n", [applicationIdentifier UTF8String]);
222 | }
223 |
224 | // At this point, we have a valid path for the app, let's continue
225 | AMDServiceConnectionRef connection = NULL;
226 | NSDictionary *params = @{@"CloseOnInvalidate" : @YES, @"InvalidateOnDetach" : @YES};
227 | AMDeviceSecureStartService(d, @"com.apple.debugserver", params, &connection);
228 | if (!connection) {
229 | ErrorMessageThenDie("Unable to create a debugserver connection\n");
230 | }
231 | lldbfd = (int)AMDServiceConnectionGetSocket(connection);
232 | if (lldbfd == -1) {
233 | ErrorMessageThenDie("Invalid socket\n");
234 | }
235 |
236 | /********************************************************************************
237 | Source end https://github.com/phonegap/ios-deploy GPL v3 license
238 | ********************************************************************************/
239 |
240 | server_socket = CFSocketCreateWithNative (NULL, lldbfd, kCFSocketDataCallBack, &server_callback, NULL);
241 | CFRunLoopAddSource(CFRunLoopGetMain(), CFSocketCreateRunLoopSource(NULL, server_socket, 0), kCFRunLoopCommonModes);
242 |
243 | struct sockaddr_in addr;
244 | memset(&addr, 0, sizeof(addr));
245 | addr.sin_len = sizeof(addr);
246 | addr.sin_family = AF_INET;
247 | addr.sin_port = htons(port);
248 | addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
249 |
250 | fdvendor = CFSocketCreate(NULL, PF_INET, 0, 0, kCFSocketAcceptCallBack, &fdvendor_callback, NULL);
251 |
252 | if (port) {
253 | int yes = 1;
254 | setsockopt(CFSocketGetNative(fdvendor), SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes));
255 | }
256 |
257 | CFDataRef address_data = CFDataCreate(NULL, (const UInt8 *)&addr, sizeof(addr));
258 |
259 | CFSocketSetAddress(fdvendor, address_data);
260 | CFRelease(address_data);
261 | socklen_t addrlen = sizeof(addr);
262 | int res = getsockname(CFSocketGetNative(fdvendor),(struct sockaddr *)&addr,&addrlen);
263 | assert(res == 0);
264 | port = ntohs(addr.sin_port);
265 |
266 | if (port == 0) {
267 | return 0;
268 | // ErrorMessageThenDie("Unable to bind port, exiting\n");
269 | }
270 | CFRunLoopSourceRef runLoopSourceRef = CFSocketCreateRunLoopSource(NULL, fdvendor, 0);
271 | CFRunLoopAddSource(CFRunLoopGetMain(), runLoopSourceRef, kCFRunLoopCommonModes);
272 |
273 | /********************************************************************************
274 | Source end https://github.com/phonegap/ios-deploy GPL v3 license
275 | ********************************************************************************/
276 |
277 | // The connection has yet to be established, but everything is good, generate the setup script
278 | if (!localApplicationPath) {
279 | localApplicationPath = extractDummyTarget([appPath lastPathComponent]);
280 | }
281 |
282 |
283 |
284 | generateSetupScript([localApplicationPath UTF8String], [appPath UTF8String], generateLaunchString(options), port);
285 |
286 |
287 | pid_t pid;
288 | if ((pid = fork()) == 0) {
289 | childPID = getpid();
290 | char *const params[] = {"lldb", "-s", "/tmp/mobdevim_setupscript", NULL};
291 | execv("/usr/bin/lldb", params);
292 | } else if ((childPID = pid) > 0) {
293 | atexit(Kill_The_Spare__AVADA_KEDAVRA);
294 | signal(SIGINT, (void (*)(int))Kill_The_Spare__AVADA_KEDAVRA);
295 | } else {
296 | ErrorMessageThenDie("Couldn't fork(), exiting...\n");
297 | }
298 |
299 | return 0;
300 | }
301 |
302 |
--------------------------------------------------------------------------------
/mobdevim/Delete Application/delete_application.h:
--------------------------------------------------------------------------------
1 | //
2 | // install_application.h
3 | // mobdevim
4 | //
5 | // Created by Derek Selander
6 | // Copyright © 2020 Selander. All rights reserved.
7 | //
8 |
9 | #import
10 | #import "ExternalDeclarations.h"
11 | #import "helpers.h"
12 |
13 | /// The path to the IPA file
14 | extern NSString * const kDeleteApplicationIdentifier;
15 |
16 | /// Delete an application. Expects a path to an IPA in options
17 | int delete_application(AMDeviceRef d, NSDictionary *options);
18 |
--------------------------------------------------------------------------------
/mobdevim/Delete Application/delete_application.m:
--------------------------------------------------------------------------------
1 | //
2 | // install_application.m
3 | // mobdevim
4 | //
5 | // Created by Derek Selander
6 | // Copyright © 2020 Selander. All rights reserved.
7 | //
8 |
9 | #import "install_application.h"
10 |
11 | NSString * const kDeleteApplicationIdentifier = @"com.selander.delete.bundleidentifier";
12 |
13 |
14 | int delete_application(AMDeviceRef d, NSDictionary *options) {
15 |
16 | NSDictionary *dict;
17 | NSString *name = [options objectForKey:kDeleteApplicationIdentifier];
18 | if (!name) {
19 | dsprintf(stderr, "You must provide a bundleIdentifier to delete\n");
20 | return 1;
21 | }
22 |
23 | AMDeviceLookupApplications(d, @{ @"ReturnAttributes": @YES, @"ShowLaunchProhibitedApps" : @YES }, &dict);
24 | if (![dict objectForKey:name]) {
25 | dsprintf(stderr, "%sCouldn't find the bundleIdentifier \"%s\", try listing all bundleIDs with %s%smobdevim -l%s\n", dcolor(dc_yellow), [name UTF8String], colorEnd(), dcolor(dc_bold), colorEnd());
26 | return 1;
27 | }
28 |
29 | if (!global_options.quiet) {
30 | dsprintf(stdout, "Are you sure you want to delete \"%s\"? [Y] ", [name UTF8String]);
31 | if (getchar() != 89) {
32 | dsprintf(stdout, "Exiting...\n");
33 | return 0;
34 | }
35 | }
36 |
37 | AMDServiceConnectionRef serviceConnection = nil;
38 | NSDictionary *inputDict = @{@"CloseOnInvalidate" : @YES};
39 | AMDeviceSecureStartService(d, @"com.apple.mobile.installation_proxy", inputDict, &serviceConnection);
40 | if (!serviceConnection) {
41 | return EACCES;
42 | }
43 | int error = AMDeviceSecureUninstallApplication(serviceConnection, NULL, name, @{}, NULL);
44 | if (error) {
45 | dsprintf(stderr, "Error removing \"%s\"\n", [name UTF8String]);
46 | return 1;
47 | }
48 |
49 | dsprintf(stdout, "Successfully removed \"%s\"\n", [name UTF8String]);
50 | return 0;
51 | }
52 |
--------------------------------------------------------------------------------
/mobdevim/Device Info/get_device_info.h:
--------------------------------------------------------------------------------
1 | //
2 | // get_device_info.h
3 | // mobdevim
4 | //
5 | // Created by Derek Selander
6 | // Copyright © 2020 Selander. All rights reserved.
7 | //
8 |
9 | #import
10 | #import "ExternalDeclarations.h"
11 | #import "helpers.h"
12 |
13 | /// Get basic device info, i.e. phone number (if iPhone), bonjour address etc...
14 | int get_device_info(AMDeviceRef d, NSDictionary *options);
15 |
16 |
--------------------------------------------------------------------------------
/mobdevim/Device Info/get_device_info.m:
--------------------------------------------------------------------------------
1 | //
2 | // get_device_info.m
3 | // mobdevim
4 | //
5 | // Created by Derek Selander
6 | // Copyright © 2020 Selander. All rights reserved.
7 | //
8 |
9 | #import "get_device_info.h"
10 |
11 | int get_device_info(AMDeviceRef d, NSDictionary *options) {
12 |
13 | NSString *udid = AMDeviceGetName(d);
14 | NSString *deviceName = AMDeviceCopyValue(d, nil, @"DeviceName", 0);
15 | NSString *activationState = AMDeviceCopyValue(d, nil, @"ActivationState", 0);
16 | NSString *phoneNumber = AMDeviceCopyValue(d, nil, @"PhoneNumber", 0);
17 | NSString *regionInfo = AMDeviceCopyValue(d, nil, @"RegionInfo", 0);
18 |
19 | // NSNumber *passwordProtected = AMDeviceCopyValue(d, nil, @"PasswordProtected", 0);
20 | NSNumber *battery = AMDeviceCopyValue(d, @"com.apple.mobile.battery",
21 | @"BatteryCurrentCapacity", 0);
22 | NSString *classType = AMDeviceCopyValue(d, nil, @"DeviceClass", 0);
23 | NSString *productVersion = AMDeviceCopyValue(d, nil, @"ProductVersion", 0);
24 |
25 | NSNumber *diskAmountAvailable = AMDeviceCopyValue(d, @"com.apple.disk_usage",
26 | @"AmountDataAvailable", 0);
27 | NSNumber *totalDataCapacity = AMDeviceCopyValue(d, @"com.apple.disk_usage",
28 | @"TotalDataCapacity", 0);
29 | double diskSpace = 100.0 * ((double)[diskAmountAvailable doubleValue] / (long)[totalDataCapacity doubleValue]);
30 | NSString *serialNumber = AMDeviceCopyValue(d, nil, @"SerialNumber", 0);
31 | NSString *hardwareModel = AMDeviceCopyValue(d, nil, @"HardwareModel", 0);
32 |
33 | NSString *productType = AMDeviceCopyValue(d, nil, @"ProductType", 0);
34 |
35 | NSString *bonjour = AMDeviceCopyValue(d, @"com.apple.mobile.wireless_lockdown",
36 | @"BonjourFullServiceName", 0);
37 | NSString *wifiAddress = AMDeviceCopyValue(d, nil, @"WiFiAddress", 0);
38 | NSString *bluetoothAddress = AMDeviceCopyValue(d, nil, @"BluetoothAddress", 0);
39 |
40 | NSString *developerStatus = AMDeviceCopyValue(d, @"com.apple.xcode.developerdomain", @"DeveloperStatus", 0);
41 |
42 | char *s = dcolor(dc_gray);
43 | char *e = colorEnd();
44 | dsprintf(stdout, "\n%sname%s\t%s\n"
45 | "%sUDID%s\t%s\n"
46 | "%sProduct Type%s\t%s\n\n"
47 | "%sState%s\t%s\n"
48 | "%sType%s\t%s\n"
49 | "%sVersion%s\t%s\n"
50 | "%sNumber%s\t%s\n"
51 | "%sRegion%s\t%s\n"
52 | "%sBattery%s\t%s%%\n\n"
53 |
54 | "%sDskSpce%s\t%d%%\n"
55 | "%sSerial%s\t%s\n"
56 | "%sHardwr%s\t%s\n"
57 | "%sdevstat%s\t%s\n\n"
58 |
59 | "%sBonjour%s\t%s\n"
60 | "%sWiFi%s\t%s\n"
61 | "%sBL Addr%s\t%s\n",
62 | s, e, [deviceName UTF8String],
63 | s, e, [udid UTF8String],
64 | s, e, [productType UTF8String],
65 | s, e, [activationState UTF8String],
66 | s, e, [classType UTF8String],
67 | s, e, [productVersion UTF8String],
68 | s, e, phoneNumber ? [phoneNumber UTF8String] : "Not Available",
69 | s, e, [regionInfo UTF8String],
70 | s, e, [[battery stringValue] UTF8String],
71 |
72 | s, e, (int)diskSpace,
73 | s, e, [serialNumber UTF8String],
74 | s, e, [hardwareModel UTF8String],
75 | s, e, [developerStatus UTF8String],
76 |
77 | s, e, [bonjour UTF8String],
78 | s, e, [wifiAddress UTF8String],
79 | s, e, [bluetoothAddress UTF8String]
80 | );
81 | return 0;
82 | }
83 |
--------------------------------------------------------------------------------
/mobdevim/Install Application/install_application.h:
--------------------------------------------------------------------------------
1 | //
2 | // install_application.h
3 | // mobdevim
4 | //
5 | // Created by Derek Selander
6 | // Copyright © 2020 Selander. All rights reserved.
7 | //
8 |
9 | #import
10 | #import "ExternalDeclarations.h"
11 | #import "helpers.h"
12 |
13 | /// The path to the IPA file
14 | extern NSString * const kInstallApplicationPath;
15 |
16 | /// Install an application over toe the device. Expects a path to an IPA in options
17 | int install_application(AMDeviceRef d, NSDictionary *options);
18 |
--------------------------------------------------------------------------------
/mobdevim/Install Application/install_application.m:
--------------------------------------------------------------------------------
1 | //
2 | // install_application.m
3 | // mobdevim
4 | //
5 | // Created by Derek Selander
6 | // Copyright © 2020 Selander. All rights reserved.
7 | //
8 |
9 | #import "install_application.h"
10 | #import "progressbar.h"
11 | #import
12 |
13 | NSString * const kInstallApplicationPath = @"com.selander.installapplication.path";
14 | static progressbar *progress = nil;
15 |
16 |
17 | void installCallback(CFDictionaryRef d) {
18 |
19 | if (progress) {
20 | NSDictionary *dict = (__bridge NSDictionary *)(d);
21 | NSNumber *complete = dict[@"PercentComplete"];
22 | if (complete) {
23 | unsigned long value = [complete unsignedIntegerValue];
24 | progressbar_update(progress, value);
25 | }
26 | }
27 | }
28 |
29 | void printInstallErrorAndDie(mach_error_t error, const char *path) {
30 | switch (error) {
31 | default:
32 | dsprintf(stderr, "Error installing \"%s\", err: 0x%x\n", path, error);
33 | break;
34 | }
35 |
36 | exit(1);
37 | }
38 |
39 | int install_application(AMDeviceRef d, NSDictionary *options) {
40 | // Get path to generated file
41 | NSString *path = [(NSString *)[options objectForKey:kInstallApplicationPath] stringByStandardizingPath];
42 |
43 | extern int gLogLevel;
44 |
45 | if (!path || ![[NSFileManager defaultManager] fileExistsAtPath:path]) {
46 | ErrorMessageThenDie("Couldn't find a valid path at \"%s\"\n", [path fileSystemRepresentation]);
47 | }
48 | dsdebug("Installing app from \"%s\"\n", [path UTF8String]);
49 | NSURL *local_app_url = [NSURL fileURLWithPath:path isDirectory:TRUE];
50 | NSDictionary *params = @{@"PackageType" : @"Customer"};
51 | NSString *deviceName = AMDeviceCopyValue(d, nil, @"DeviceName", 0);
52 |
53 | // Get a secure path
54 |
55 | if (global_options.quiet) {
56 | assert(!AMDeviceSecureTransferPath(0, d, local_app_url, params, NULL, 0));
57 | return AMDeviceSecureInstallApplication(0, d, local_app_url, params, NULL, 0);
58 | } else {
59 | progress = progressbar_new("Processing... ", 100);
60 | assert(!AMDeviceSecureTransferPath(0, d, local_app_url, params, installCallback, 0));
61 | progressbar_update(progress, 100);
62 | int error = 0;
63 | progressbar_update_label(progress, "Installing...");
64 | progressbar_update(progress, 0);
65 | error = AMDeviceSecureInstallApplication(0, d, local_app_url, params, installCallback, 0);
66 |
67 | progressbar_update(progress, 100);
68 | if (error) {
69 | progressbar_update_label(progress, "Error:");
70 | progressbar_update(progress, 0);
71 | progressbar_finish(progress);
72 | printInstallErrorAndDie(error, [path UTF8String]);
73 | } else {
74 | progressbar_update_label(progress, "Installed!");
75 | progressbar_finish(progress);
76 | dsprintf(stdout, "Success: \"%s\" app successfully installed on \"%s\"\n", [[[path lastPathComponent] stringByDeletingPathExtension] UTF8String], [deviceName UTF8String]);
77 | }
78 | }
79 |
80 | return 0;
81 | }
82 |
--------------------------------------------------------------------------------
/mobdevim/Instruments/instruments.h:
--------------------------------------------------------------------------------
1 | //
2 | // install_application.h
3 | // mobdevim
4 | //
5 | // Created by Derek Selander
6 | // Copyright © 2020 Selander. All rights reserved.
7 | //
8 |
9 | #import
10 | #import "ExternalDeclarations.h"
11 | #import "helpers.h"
12 |
13 | /// The path to the IPA file
14 | //extern NSString * const kInstallApplicationPath;
15 |
16 | /// Install an application over toe the device. Expects a path to an IPA in options
17 | int instruments(AMDeviceRef d, NSDictionary *options);
18 |
--------------------------------------------------------------------------------
/mobdevim/Instruments/instruments.m:
--------------------------------------------------------------------------------
1 | //
2 | // install_application.m
3 | // mobdevim
4 | //
5 | // Created by Derek Selander
6 | // Copyright © 2020 Selander. All rights reserved.
7 | //
8 |
9 | #import "instruments.h"
10 | @import Cocoa;
11 |
12 | int instruments(AMDeviceRef d, NSDictionary *options) {
13 |
14 |
15 | return 0;
16 | }
17 |
--------------------------------------------------------------------------------
/mobdevim/List Applications/list_applications.h:
--------------------------------------------------------------------------------
1 | //
2 | // list_applications.h
3 | // mobdevim
4 | //
5 | // Created by Derek Selander
6 | // Copyright © 2020 Selander. All rights reserved.
7 | //
8 |
9 | #import
10 | #import "ExternalDeclarations.h"
11 | #import "helpers.h"
12 |
13 | /// List detailed information about a specific application bundle
14 | extern NSString *const kListApplicationsName;
15 |
16 | /// Only dump the info for the key, expects kListApplicationsName
17 | extern NSString *const kListApplicationsKey;
18 |
19 | /// List applications
20 | int list_applications(AMDeviceRef d, NSDictionary *options);
21 |
--------------------------------------------------------------------------------
/mobdevim/List Applications/list_applications.m:
--------------------------------------------------------------------------------
1 | //
2 | // list_applications.m
3 | // mobdevim
4 | //
5 | // Created by Derek Selander
6 | // Copyright © 2020 Selander. All rights reserved.
7 | //
8 |
9 | #import "list_applications.h"
10 |
11 | NSString *const kListApplicationsName = @"com.selander.listapplications.appname";
12 | NSString *const kListApplicationsKey = @"com.selander.listapplications.key";
13 |
14 | int list_applications(AMDeviceRef d, NSDictionary *options) {
15 |
16 | NSDictionary *dict;
17 | NSString *name = [options objectForKey:kListApplicationsName];
18 |
19 | if (name) {
20 | AMDeviceLookupApplications(d, @{ @"ReturnAttributes": @YES, @"ShowLaunchProhibitedApps" : @YES }, &dict);
21 | if (![dict objectForKey:name]) {
22 | dsprintf(stderr, "%sCouldn't find the bundleIdentifier \"%s\", try listing all bundleIDs with %s%smobdevim -l%s\n", dcolor(dc_yellow), [name UTF8String], colorEnd(), dcolor(dc_bold), colorEnd());
23 | return 1;
24 | }
25 |
26 | NSString *key = [options objectForKey:kListApplicationsKey];
27 | if (key) {
28 | if (getenv("DSPLIST")) {
29 | global_options.quiet = NO;
30 | dsprintf(stdout, "%s\n", [[[dict objectForKey:name] objectForKey:key] dsformattedOutput]);
31 | } else {
32 | dsprintf(stdout, "Dumping info for \"%s%s%s\" with key: \"%s%s%s\"\n%s\n", dcolor(dc_red), [name UTF8String], colorEnd(), dcolor(dc_red), [key UTF8String], colorEnd(), [[[dict objectForKey:name] objectForKey:key] dsformattedOutput]);
33 | }
34 | if (![[dict objectForKey:name] objectForKey:key]) {
35 | return 1;
36 | }
37 | } else {
38 | if (getenv("DSPLIST")) {
39 | global_options.quiet = NO;
40 | dsprintf(stdout, "%s\n", [[dict objectForKey:name] dsformattedOutput]);
41 | } else {
42 | dsprintf(stdout, "%sDumping info for \"%s\"%s\n\n%s\n", dcolor(dc_red), [name UTF8String], colorEnd(), [[dict objectForKey:name] dsformattedOutput]);
43 | }
44 | }
45 | } else {
46 | // name is nil
47 | AMDeviceLookupApplications(d, @{@"ReturnAttributes": @[@"ProfileValidated", @"CFBundleIdentifier", @"SBAppTags", @"ApplicationType", @"CFBundleDisplayName"], @"ShowLaunchProhibitedApps" : @YES}, &dict);
48 | NSMutableString *output = [NSMutableString string];
49 | for (NSString *key in [dict allKeys]) {
50 | NSDictionary *appDict = dict[key];
51 | NSString *appName = appDict[@"CFBundleDisplayName"];
52 | if ([appDict[@"ProfileValidated"] boolValue]) {
53 | [output appendString:[NSString stringWithFormat:@"%s%@, %@%s\n", dcolor(dc_cyan), key, appName, colorEnd()]];
54 | } else if ([appDict[@"ApplicationType"] isEqualToString:@"Internal"]) {
55 | [output appendString:[NSString stringWithFormat:@"%s%@, %@%s\n", dcolor(dc_magenta), key, appName, colorEnd()]];
56 | } else if ([appDict[@"SBAppTags"] containsObject:@"hidden"]) {
57 | [output appendString:[NSString stringWithFormat:@"%s%@, %@%s\n", dcolor(dc_yellow), key, appName, colorEnd()]];
58 | } else if ([appDict[@"ApplicationType"] isEqualToString:@"System"]) {
59 | [output appendString:[NSString stringWithFormat:@"%s%@, %@%s\n", dcolor(dc_red), key, appName, colorEnd()]];
60 | }
61 | else {
62 | [output appendString:[NSString stringWithFormat:@"%@, %@\n", key, appName]];
63 | }
64 |
65 | }
66 |
67 | NSString *colorHelper = @"";
68 | if (getenv("DSCOLOR")) {
69 | colorHelper = [NSString stringWithFormat:@"\t%s█ Hidden%s\t%s█ Developer%s\t%s█ System%s\t%s█ Internal%s\n", dcolor(dc_yellow), colorEnd(), dcolor(dc_cyan), colorEnd(), dcolor(dc_red), colorEnd(), dcolor(dc_magenta), colorEnd()];
70 | }
71 | dsprintf(stdout, "Dumping bundleIDs for all apps\n\n%s%s", [colorHelper UTF8String], [output UTF8String]);
72 | }
73 |
74 | AMDeviceDisconnect(d);
75 |
76 | return 0;
77 | }
78 |
--------------------------------------------------------------------------------
/mobdevim/Open Program/open_program.h:
--------------------------------------------------------------------------------
1 | //
2 | // open_program.h
3 | // mobdevim
4 | //
5 | //
6 | // Copyright © 2020 Selander. All rights reserved.
7 | //
8 |
9 | #import
10 | #import "ExternalDeclarations.h"
11 | #import "helpers.h"
12 |
13 | /// Opens program
14 | #ifdef __cplusplus
15 | extern "C" {
16 | #endif
17 | int open_program(AMDeviceRef d, NSDictionary *options);
18 | #ifdef __cplusplus
19 | }
20 | #endif
21 |
--------------------------------------------------------------------------------
/mobdevim/Open Program/open_program.mm:
--------------------------------------------------------------------------------
1 | //
2 | // open_program.m
3 | // mobdevim
4 | //
5 | //
6 | // Copyright © 2020 Selander. All rights reserved.
7 | //
8 |
9 | #import "open_program.h"
10 | #import "../misc/InstrumentsPlugin.h"
11 | #import "../Debug Application/debug_application.h"
12 | #import "ios_instruments_client.h"
13 | #import
14 |
15 |
16 | int open_program(AMDeviceRef d, NSDictionary *options) {
17 |
18 | NSString *name = global_options.programBundleID;
19 | NSDictionary *dict = nil;
20 | mach_error_t err = AMDeviceLookupApplications(d, @{ @"ReturnAttributes": @YES, @"ShowLaunchProhibitedApps" : @YES }, &dict);
21 | if (err) {
22 | derror("Err looking up application, exiting...\n");
23 | return 1;
24 | }
25 |
26 | if (!name) {
27 | dsprintf(stderr, "%sCouldn't find the bundleIdentifier \"%s\", try listing all bundleIDs with %s%smobdevim -l%s\n", dcolor(dc_yellow), [name UTF8String], colorEnd(), dcolor(dc_bold), colorEnd());
28 | return 1;
29 | }
30 |
31 | NSDictionary *appParams = [dict objectForKey:name];
32 | NSString *path = appParams[@"Path"];
33 | if (!path) {
34 | derror("couldn't get the path for app %s\n", name.UTF8String);
35 | return 1;
36 | }
37 | NSString *bundleID = appParams[@"CFBundleIdentifier"];
38 | if (!bundleID) {
39 | derror("couldn't get the bundleID\n");
40 | return 1;
41 | }
42 |
43 | NSString *arguments = global_options.programArguments;
44 | NSArray *environment = options[kProcessEnvVars];
45 |
46 | NSMutableDictionary *dictionaryEnvironment = [NSMutableDictionary new];
47 | for (NSString *val in environment) {
48 | NSArray *components = [val componentsSeparatedByString:@"="];;
49 | if ([components count] != 2) {
50 | dsprintf(stderr, "Couldn't process \"%s\"\n", val.UTF8String);
51 | continue;
52 | }
53 | NSString *key = components.firstObject;
54 | NSString *object = components.lastObject;
55 | [dictionaryEnvironment setObject:object forKey:key];
56 | }
57 |
58 |
59 | am_device_service_connection* instruments_connection = (__bridge am_device_service_connection*) connect_to_instruments_server(d);
60 |
61 | launch_application(instruments_connection, bundleID.UTF8String, [arguments componentsSeparatedByString:@" "], dictionaryEnvironment);
62 |
63 | return 0;
64 | }
65 |
--------------------------------------------------------------------------------
/mobdevim/Provisioning Porfiles/get_provisioning_profiles.h:
--------------------------------------------------------------------------------
1 | //
2 | // get_provisioning_profiles.h
3 | // mobdevim
4 | //
5 | // Created by Derek Selander
6 | // Copyright © 2020 Selander. All rights reserved.
7 | //
8 |
9 | #import
10 | #import "ExternalDeclarations.h"
11 | #import "helpers.h"
12 |
13 | /// If this key is found in the options, the developer certs are copied to device
14 | extern NSString * const kProvisioningProfilesCopyDeveloperCertificates;
15 |
16 | /// Get detailed information based upon a provisioning proviles UUID
17 | extern NSString * const kProvisioningProfilesFilteredByDevice;
18 |
19 | /// Get the provisioning info or the certificates stored on the device
20 | int get_provisioning_profiles(AMDeviceRef d, NSDictionary *options);
21 |
--------------------------------------------------------------------------------
/mobdevim/Provisioning Porfiles/get_provisioning_profiles.m:
--------------------------------------------------------------------------------
1 | //
2 | // get_provisioning_profiles.m
3 | // mobdevim
4 | //
5 | // Created by Derek Selander
6 | // Copyright © 2020 Selander. All rights reserved.
7 | //
8 |
9 | @import Security;
10 | #import "get_provisioning_profiles.h"
11 | NSString * const kProvisioningProfilesCopyDeveloperCertificates = @"com.selander.provisioningprofiles.copydevelopercertificates";
12 |
13 | NSString * const kProvisioningProfilesFilteredByDevice = @"com.selander.provisioningprofiles.filteredbydevice";
14 |
15 | int get_provisioning_profiles(AMDeviceRef d, NSDictionary *options) {
16 |
17 | NSArray *profiles = AMDeviceCopyProvisioningProfiles(d);
18 |
19 | BOOL copyDeveloperCertificates = [[options objectForKey:kProvisioningProfilesCopyDeveloperCertificates] boolValue];
20 | NSString* filterProvisioninProfilesThatOnlyFitDevice = [options objectForKey:kProvisioningProfilesFilteredByDevice];
21 | for (id a in profiles) {
22 | extern int AMDeviceRemoveProvisioningProfile(AMDeviceRef b, NSString* a);
23 | int b =AMDeviceRemoveProvisioningProfile(d, [MISProfileCopyPayload(a) objectForKey:@"UUID"]);
24 | printf("%d\n", b);
25 | }
26 |
27 | NSArray *filteredProfiles = [profiles filteredArrayUsingPredicate:[NSPredicate predicateWithBlock:^BOOL(id _Nullable evaluatedObject, NSDictionary * _Nullable bindings) {
28 |
29 | if (filterProvisioninProfilesThatOnlyFitDevice) {
30 | return [[MISProfileCopyPayload(evaluatedObject) objectForKey:@"UUID"] containsString:filterProvisioninProfilesThatOnlyFitDevice];
31 | }
32 |
33 | return (BOOL)[MISProfileCopyPayload(evaluatedObject) objectForKey:@"Name"];
34 | }]];
35 |
36 | if (!filterProvisioninProfilesThatOnlyFitDevice) {
37 | dsprintf(stdout, "Dumping provisioning profiles\n\n");
38 | }
39 |
40 |
41 | NSString *appName = AMDeviceCopyDeviceIdentifier(d);
42 |
43 | NSString *directory = [NSString stringWithFormat:@"/tmp/%@_certificates", appName];
44 |
45 | for (id i in filteredProfiles) {
46 | NSDictionary *dict = MISProfileCopyPayload(i);
47 | NSString *teamName = dict[@"TeamName"];
48 | NSString *appIDName = dict[@"AppIDName"];
49 | NSString *appID = dict[@"Entitlements"][@"application-identifier"];
50 | // NSString *apsEnv = dict[@"Entitlements"][@"aps-environment"];
51 | NSString *uuid = dict[@"UUID"];
52 | NSString *name = dict[@"Name"];
53 | NSArray *certs = dict[@"DeveloperCertificates"];
54 | NSDate *expirationDate = dict[@"ExpirationDate"];
55 | NSMutableArray *certificateNames = [NSMutableArray new];
56 | for (int i = 0; i < [certs count]; i++) {
57 | NSData *data = certs[i];
58 | CFDataRef dataRef = CFDataCreate(NULL, [data bytes], [data length]);
59 | SecCertificateRef secref = SecCertificateCreateWithData(nil, dataRef);
60 |
61 |
62 | // Common name
63 | CFStringRef commonNameRef = NULL;
64 | SecCertificateCopyCommonName(secref, &commonNameRef);
65 | NSString *commonName = CFBridgingRelease(commonNameRef);
66 |
67 | // NSString* objectSummary = CFBridgingRelease(SecCertificateCopySubjectSummary(secref));
68 |
69 | CFArrayRef keysRef = NULL;
70 | NSDictionary* certDict = CFBridgingRelease(SecCertificateCopyValues(secref, keysRef, nil));
71 |
72 | NSNumber *validBefore = nil;
73 | NSNumber *validAfter = nil;
74 | NSString *serialValue = nil;
75 | for (NSDictionary *key in certDict) {
76 | if ([certDict[key][@"label"] isEqualToString:@"Serial Number"]) {
77 | serialValue = certDict[key][@"value"];
78 | continue;
79 | } else if ([certDict[key][@"label"] isEqualToString:@"Not Valid Before"]) {
80 | validBefore = certDict[key][@"value"];
81 | continue;
82 | } else if ([certDict[key][@"label"] isEqualToString:@"Not Valid After"]) {
83 | validAfter = certDict[key][@"value"];
84 | continue;
85 | }
86 | }
87 |
88 | [certificateNames addObject:@{@"Common Name" : commonName,
89 | @"Serial Number": serialValue ? serialValue : @"NULL",
90 | @"Valid Before" : (validBefore ? [NSDate dateWithTimeIntervalSinceReferenceDate:[validBefore doubleValue]] : @"NULL"),
91 | @"Valid After" : (validAfter ? [NSDate dateWithTimeIntervalSinceReferenceDate:[validAfter doubleValue]] : @"NULL"),
92 | }];
93 |
94 | }
95 |
96 | // if (filterProvisioninProfilesThatOnlyFitDevice) {
97 | // NSArray *provisionedDevices = dict[@"ProvisionedDevices"];
98 | // if(![provisionedDevices containsObject:deviceIdentifier]) {
99 | // continue;
100 | // }
101 | // }
102 |
103 | if (copyDeveloperCertificates) {
104 | NSFileManager *fileManager= [NSFileManager defaultManager];
105 |
106 | if(![fileManager fileExistsAtPath:directory isDirectory:NULL]) {
107 | [fileManager createDirectoryAtPath:directory withIntermediateDirectories:YES attributes:nil error:NULL];
108 | }
109 | for (NSData *data in dict[@"DeveloperCertificates"]) {
110 | NSString *certPath = [NSString stringWithFormat:@"%@/%@_%@.cer", directory, appID, uuid];
111 | [data writeToFile:certPath atomically:YES];
112 | }
113 | continue;
114 | }
115 |
116 |
117 | NSMutableString *outputString = [NSMutableString stringWithFormat:@"\n%s**************************************%s\nApplication-identifier: %s%@%s\nTeamName: %s%@%s\nAppIDName: %s%@%s\nProvisioning Profile: %s%@%s\nExpiration: %s%s%s\nUUID: %s%@%s",
118 | dcolor(dc_yellow), colorEnd(),
119 | dcolor(dc_bold), appID, colorEnd(),
120 | dcolor(dc_bold), teamName, colorEnd(),
121 | dcolor(dc_bold), appIDName, colorEnd(),
122 | dcolor(dc_bold), name, colorEnd(),
123 | dcolor(dc_bold), [expirationDate dsformattedOutput], colorEnd(),
124 | dcolor(dc_bold), uuid, colorEnd()];
125 | if (filterProvisioninProfilesThatOnlyFitDevice) {
126 | NSMutableDictionary *outputDict = [NSMutableDictionary dictionaryWithDictionary:dict];
127 | [outputDict removeObjectForKey:@"DeveloperCertificates"];
128 | [outputDict setObject:certificateNames forKey:@"DeveloperCertificates"];
129 | dsprintf(stdout, "Dumping Provisioning Profile info for UUID \"%s%s%s\"...\n%s\n", dcolor(dc_cyan), [filterProvisioninProfilesThatOnlyFitDevice UTF8String], colorEnd(), [outputDict dsformattedOutput]);
130 | } else {
131 | dsprintf(stdout, "%s\n", [outputString UTF8String]);
132 | }
133 | }
134 |
135 |
136 | if (copyDeveloperCertificates) {
137 | dsprintf(stdout, "Opening directory containing dev certificates from device...\n");
138 | [[NSWorkspace sharedWorkspace] openFile:directory];
139 | }
140 |
141 | return 0;
142 | }
143 |
--------------------------------------------------------------------------------
/mobdevim/SimulateLocation/sim_location.h:
--------------------------------------------------------------------------------
1 | //
2 | // list_applications.h
3 | // mobdevim
4 | //
5 | // Created by Derek Selander
6 | // Copyright © 2020 Selander. All rights reserved.
7 | //
8 |
9 | #import
10 | #import "ExternalDeclarations.h"
11 | #import "helpers.h"
12 |
13 |
14 | /// Lat
15 | extern NSString *const kSimLocationLat;
16 |
17 | /// Lon
18 | extern NSString *const kSimLocationLon;
19 |
20 | /// Sim Location, expects a lat / lon
21 | int sim_location(AMDeviceRef d, NSDictionary *options);
22 |
--------------------------------------------------------------------------------
/mobdevim/SimulateLocation/sim_location.m:
--------------------------------------------------------------------------------
1 | //
2 | // sim_location.m
3 | // mobdevim
4 | //
5 | // Created by Derek Selander
6 | // Copyright © 2020 Selander. All rights reserved.
7 | //
8 |
9 | #import "sim_location.h"
10 |
11 | NSString *const kSimLocationLat = @"com.selander.simlocation.lat";
12 | NSString *const kSimLocationLon = @"com.selander.simlocation.lon";
13 |
14 |
15 | static int write_string(AMDServiceConnectionRef ref, const char* str) {
16 |
17 | int length = (int)strlen(str);
18 | mach_error_t err = 0;
19 | int swapped_length = htonl(length);
20 |
21 | int result = AMDServiceConnectionSend(ref, &swapped_length, 4);
22 | if (result) {
23 | err = AMDServiceConnectionSend(ref, (void*)str, length);
24 | }
25 | return err;
26 | }
27 |
28 | #define SERVICE_START 0x0000000
29 | #define SERVICE_STOP 0x1000000
30 |
31 | int sim_location(AMDeviceRef d, NSDictionary *options) {
32 | NSString *lat = [options objectForKey:kSimLocationLat];
33 | NSString *lon = [options objectForKey:kSimLocationLon];
34 |
35 | int service = SERVICE_START;
36 | if ([lon integerValue] == 0 || [lat integerValue] == 0) {
37 | service = SERVICE_STOP;
38 | }
39 |
40 | AMDServiceConnectionRef serviceConnection = nil;
41 | AMDStartService(d, @"com.apple.dt.simulatelocation", &serviceConnection);
42 |
43 |
44 | int result = AMDServiceConnectionSend(serviceConnection, &service, 4);
45 | if (result && service == SERVICE_STOP) {
46 | dsprintf(stdout, "Successfully suspended location simulation\n");
47 | return 0;
48 | }
49 |
50 | if (!write_string(serviceConnection, [lat UTF8String])) {
51 | dsprintf(stderr, "Error writing to the location service\n");
52 | return 1;
53 | }
54 |
55 | if (!write_string(serviceConnection, [lon UTF8String])) {
56 | dsprintf(stderr, "Error writing to the location service\n");
57 | return 1;
58 | }
59 |
60 | AMDeviceDisconnect(d);
61 | dsprintf(stdout, "Successfully simulated location at: %s, %s\n", [lat UTF8String], [lon UTF8String]);
62 | return 0;
63 | }
64 |
--------------------------------------------------------------------------------
/mobdevim/TODOs:
--------------------------------------------------------------------------------
1 |
2 |
3 | com.apple.iosdiagnostics.relay Provides detailed network
4 | usage per-application on a per-day basis
5 |
6 | com.apple.mobile.installation_proxy Given an enterprise
7 | certificate, can use this to load custom software onto the
8 | device (which can run invisibly and in the background)
9 |
10 | com.apple.syslog_relay: Syslog, provides a lot of details
11 | about what the device is doing, and often leaks user
12 | credentials from 3rd party apps via NSLog()
13 |
--------------------------------------------------------------------------------
/mobdevim/backup_device/backup_device.h:
--------------------------------------------------------------------------------
1 | //
2 | // install_application.h
3 | // mobdevim
4 | //
5 | // Created by Derek Selander
6 | // Copyright © 2020 Selander. All rights reserved.
7 | //
8 |
9 | #import
10 | #import "ExternalDeclarations.h"
11 | #import "helpers.h"
12 |
13 | /// The path to the IPA file
14 | //extern NSString * const kInstallApplicationPath;
15 |
16 | /// Install an application over toe the device. Expects a path to an IPA in options
17 | int backup_device(AMDeviceRef d, NSDictionary *options);
18 |
--------------------------------------------------------------------------------
/mobdevim/backup_device/backup_device.m:
--------------------------------------------------------------------------------
1 | //
2 | // install_application.m
3 | // mobdevim
4 | //
5 | // Created by Derek Selander
6 | // Copyright © 2020 Selander. All rights reserved.
7 | //
8 |
9 | #import "backup_device.h"
10 | #import "progressbar.h"
11 | #import
12 |
13 | //
14 | //NSString * const kInstallApplicationPath = @"com.selander.installapplication.path";
15 | //static progressbar *progress = nil;
16 | //
17 | //
18 | //void installCallback(CFDictionaryRef d) {
19 | //
20 | // if (progress) {
21 | // NSDictionary *dict = (__bridge NSDictionary *)(d);
22 | // NSNumber *complete = dict[@"PercentComplete"];
23 | // if (complete) {
24 | // unsigned long value = [complete unsignedIntegerValue];
25 | // progressbar_update(progress, value);
26 | // }
27 | // }
28 | //}
29 | //
30 | //void printInstallErrorAndDie(mach_error_t error, const char *path) {
31 | // switch (error) {
32 | // default:
33 | // dsprintf(stderr, "Error installing \"%s\", err: 0x%x\n", path, error);
34 | // break;
35 | // }
36 | //
37 | // exit(1);
38 | //}
39 | static progressbar *progress = nil;
40 | // iff percent is less than zero, it's gonna be an error
41 | static void backup_callback(NSString * identifier, int percent, void *context) {
42 | if (percent == -35) {
43 | dprint("user canceled backup request\n");
44 | return;
45 | }
46 | if (percent < 0) {
47 | printf(" err: ProcessLinkSetupParent%d\n", percent);
48 | return;
49 | }
50 |
51 | if (!progress) {
52 | dprint("Remote side complete!\n");
53 | progress = progressbar_new("Extracting...", 100);
54 | }
55 |
56 | progressbar_update(progress, percent);
57 |
58 | }
59 |
60 | int backup_device(AMDeviceRef d, NSDictionary *options) {
61 | #if 0
62 | {
63 | "Display Name" = "iPhone (8)";
64 | "Product Type" = "iPhone14,4";
65 | "Product Version" = "16.5";
66 | "Target Identifier" = "00008110-...";
67 | "Target Type" = Tahoe;
68 | }
69 | #endif
70 |
71 | NSString *deviceUUID = AMDeviceGetName(d);
72 |
73 |
74 |
75 | NSString *deviceName = AMDeviceCopyValue(d, nil, @"DeviceName", 0);
76 | NSString *productVersion = AMDeviceCopyValue(d, nil, @"ProductVersion", 0);
77 | NSString *productType = AMDeviceCopyValue(d, nil, @"ProductType", 0);
78 |
79 |
80 | NSDictionary *info = @{
81 | @"Display Name" : deviceName,
82 | @"Product Type" : productType,
83 | @"Target Identifier" : deviceUUID,
84 | @"Product Version" : productVersion,
85 | @"Notes" : @"created by mobdevim",
86 | };
87 |
88 | ams_err err;
89 |
90 | if ((err = AMSInitialize(@"/System/Library/PrivateFrameworks/MobileDevice.framework/Versions/Current/"))) {
91 | derror("err %d\n", err);
92 | goto cleanup;
93 | }
94 |
95 |
96 | // progressbar_update_label(progress, "Extracting...");
97 | // progressbar_update(progress, 0);
98 | dprint("Enter the device password (present iOS 16+), your will start preparing the backup remotely. You can verify with the rotating circular arrows in the upper corner of the device. Once complete, you'll start to see the download transfer progress.\nDo not exit this program\n");
99 |
100 | if ((err = AMSBackupWithOptions(@"-1", deviceUUID, info, @{@"ForceFullBackup" : @YES, @"WillEncrypt": @NO}, backup_callback, d))) {
101 | derror("err %d\n", err);
102 | goto cleanup;
103 | }
104 |
105 | if (err) {
106 | printf("err %d\n", err);
107 | } else {
108 |
109 | dprint("backup created @ \"%s/Library/Application Support/MobileSync/Backup/%s\"\n", [[[NSFileManager defaultManager] homeDirectoryForCurrentUser] path].UTF8String, deviceUUID.UTF8String);
110 | }
111 | cleanup:
112 |
113 | err = AMSCleanup();
114 |
115 | return 0;
116 | }
117 |
--------------------------------------------------------------------------------
/mobdevim/console.temp_caseinsensitive_rename.m:
--------------------------------------------------------------------------------
1 | //
2 | // Console.m
3 | // mobdevim
4 | //
5 | // Created by Derek Selander on 11/23/17.
6 | // Copyright © 2017 Selander. All rights reserved.
7 | //
8 |
9 | #import "console.h"
10 | #import
11 |
12 |
13 | NSString *const kConsoleProcessName = @"com.selander.console.processname";
14 |
15 | int console(AMDeviceRef d, NSDictionary* options) {
16 |
17 | AMDServiceConnectionRef connection = NULL;
18 | AMDeviceSecureStartService(d, @"com.apple.syslog_relay",
19 | @{@"UnlockEscrowBag" : @YES},
20 | &connection);
21 |
22 | int socket = (int)AMDServiceConnectionGetSocket(connection);
23 | while (1) {
24 | void *opt = NULL;
25 | socklen_t len = 0x8;
26 | char *buffer = calloc(1, len + 1);
27 | if (getsockopt(socket, SOL_SOCKET, SO_NREAD, &opt, &len) == 0) {
28 | AMDServiceConnectionReceive(connection, buffer, len);
29 | printf("%s", buffer);
30 | } else {
31 | dsprintf(stdout, "error, exiting\n");
32 | break;
33 | }
34 | free(buffer);
35 | }
36 |
37 | return 0;
38 | }
39 |
--------------------------------------------------------------------------------
/mobdevim/install_ddi/install_ddi.h:
--------------------------------------------------------------------------------
1 | //
2 | // conditions.h
3 | // mobdevim
4 | //
5 | // Created by Derek Selander on 4/15/20.
6 | // Copyright © 2020 Selander. All rights reserved.
7 | //
8 |
9 | #import
10 | #import "ExternalDeclarations.h"
11 | #import "helpers.h"
12 |
13 | /// The application ID to remove files from, WILL NOT WORK IF YOU DON'T HAVE THE MATCHING PP/CERTS!
14 | //extern NSString * const kSBSFileBundleID;
15 |
16 |
17 | /// The path to remove a file on the remote device
18 | //extern NSString * const kSBCommand;
19 |
20 | /// Copies the Library, Documents, Caches directories over to the computer
21 | int install_ddi(AMDeviceRef d, NSDictionary *options);
22 | int uninstall_ddi(AMDeviceRef d, NSDictionary *options);
23 |
--------------------------------------------------------------------------------
/mobdevim/install_ddi/install_ddi.m:
--------------------------------------------------------------------------------
1 | //
2 | // springboardservices.m
3 | // mobdevim
4 | //
5 | // Created by Derek Selander
6 | // Copyright © 2020 Selander. All rights reserved.
7 | //
8 |
9 |
10 |
11 |
12 | #import "install_ddi.h"
13 | #import "progressbar.h"
14 |
15 | static progressbar *progress = nil;
16 |
17 | static void image_callback(NSDictionary *dict, id something) {
18 | if (progress) {
19 | NSNumber *complete = dict[@"PercentComplete"];
20 | if (complete) {
21 | unsigned long value = [complete unsignedIntegerValue];
22 | progressbar_update(progress, value);
23 | }
24 | }
25 | }
26 |
27 | int uninstall_ddi(AMDeviceRef d, NSDictionary *options) {
28 |
29 | dprint("attempting to unmount the /Developer image... ");
30 | amd_err er = AMDeviceUnmountImage(d, @"/Developer");
31 | if ( er != ERR_SUCCESS)
32 | {
33 | derror("Error (%s) %d\n", AMDErrorString(er), er);
34 | return 1;
35 | } else {
36 | dprint("Image successfully unmounted!\n");
37 | }
38 | return 0;
39 | }
40 |
41 |
42 | int install_ddi(AMDeviceRef d, NSDictionary *options) {
43 |
44 | if (!global_options.ddiInstallPath || !global_options.ddiSignatureInstallPath)
45 | {
46 | derror(" needed\n");
47 | exit(1);
48 | }
49 |
50 |
51 | NSData *dataSIG = [NSData dataWithContentsOfFile:global_options.ddiSignatureInstallPath];
52 | // for debugging ...
53 | // NSData *dataSIG = [NSData dataWithContentsOfFile:@"/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/DeviceSupport/15.0/DeveloperDiskImage.dmg.signature"];
54 | if (!dataSIG) {
55 | dprint("Need a valid signature\n");
56 | exit(1);
57 | }
58 | NSDictionary *op = @{@"ImageSignature" : dataSIG, @"ImageType": @"Developer", @"DiskImage" : @"/Developer"};
59 |
60 | progress = progressbar_new("Installing... ", 100);
61 | progressbar_update(progress, 0);
62 | amd_err er = AMDeviceMountImage(d, global_options.ddiInstallPath, op, image_callback, nil);
63 |
64 | if (er != ERR_SUCCESS) {
65 | progressbar_update_label(progress, "Error:");
66 | progressbar_update(progress, 0);
67 | progressbar_finish(progress);
68 | derror("Error (%s) %d\n", AMDErrorString(er), er);
69 | return 1;
70 | } else {
71 | progressbar_update(progress, 100);
72 | progressbar_update_label(progress, "DDI mounted to /Developer!");
73 | progressbar_finish(progress);
74 | }
75 | AMDeviceStopSession(d);
76 | AMDeviceDisconnect(d);
77 | return 0;
78 | }
79 |
--------------------------------------------------------------------------------
/mobdevim/logs/get_logs.h:
--------------------------------------------------------------------------------
1 | //
2 | // send_files.h
3 | // mobdevim
4 | //
5 | //
6 | // Copyright © 2020 Selander. All rights reserved.
7 | //
8 |
9 | #import
10 | #import "ExternalDeclarations.h"
11 | #import "helpers.h"
12 |
13 |
14 | /// The path to send up the files
15 | extern NSString * const kGetLogsFilePath;
16 |
17 |
18 | /// The path to send up the files
19 | extern NSString * const kGetLogsAppBundle;
20 |
21 | /// Should delete all crash logs
22 | extern NSString * const kGetLogsDelete;
23 |
24 | /// gets the device logs -g list all, -g bundleIdentifier all crashes for app,
25 | /// -g bundleIdentifier path all crashes written to path
26 | int get_logs(AMDeviceRef d, NSDictionary *options);
27 |
--------------------------------------------------------------------------------
/mobdevim/logs/get_logs.m:
--------------------------------------------------------------------------------
1 | //
2 | // send_files.m
3 | // mobdevim
4 | //
5 | //
6 | // Copyright © 2020 Selander. All rights reserved.
7 | //
8 |
9 | #import "send_files.h"
10 | #import
11 | @import Security;
12 | NSString * const kGetLogsFilePath = @"com.selander.get_logs.sendfilepath";
13 |
14 | NSString * const kGetLogsAppBundle = @"com.selander.get_logs.appbundle";
15 |
16 | NSString * const kGetLogsDelete = @"com.selander.get_logs.delete";
17 |
18 | @implementation NSString (STUFF)
19 |
20 | - (BOOL)ds_isAllDigits {
21 | NSCharacterSet* nonNumbers = [[NSCharacterSet decimalDigitCharacterSet] invertedSet];
22 | NSRange r = [self rangeOfCharacterFromSet: nonNumbers];
23 | return r.location == NSNotFound && self.length > 0;
24 | }
25 |
26 | @end
27 |
28 |
29 | static void _AFCConnectionCallBack(AFCConnectionRef c, AFConnectionCallbackType callbackType, NSObject* reference) {
30 |
31 |
32 |
33 | printf("callback type %d %p, %p\n", callbackType, AFCConnectionGetSecureContext(c), reference);
34 | printf("");
35 | }
36 |
37 | int get_logs(AMDeviceRef d, NSDictionary *options) {
38 |
39 | // NSDictionary *dict = nil;
40 | NSString *requestedFile = [options objectForKey:kGetLogsAppBundle];
41 | bool should_delete = [[options objectForKey:kGetLogsDelete] boolValue];
42 | bool get_all_logs = [requestedFile isEqualToString:@"ALL"];
43 | if (get_all_logs) {
44 | should_delete = true;
45 | }
46 | // NSDictionary *opts = @{ @"ApplicationType" : @"Any",
47 | // @"ReturnAttributes" : @[@"CFBundleExecutable",
48 | // @"CFBundleIdentifier",
49 | // @"CFBundleDisplayName"]};
50 | //
51 | // NSString *executableName = nil;
52 | // if ([appBundle ds_isAllDigits] && [appBundle integerValue] < 1) {
53 | // dsprintf(stderr, "Must use positive integer value\n");
54 | // return 1;
55 | // }
56 | // AMDeviceLookupApplications(d, opts, &dict);
57 | //
58 | // if (appBundle && ![appBundle isEqualToString:@"__all"] && ![appBundle integerValue]) {
59 | // executableName = [[dict objectForKey:appBundle] objectForKey:@"CFBundleExecutable"];
60 | // if (!executableName) {
61 | // dsprintf(stderr, "%sCouldn't find the bundleIdentifier \"%s\", try listing all bundleIDs with %s%smobdevim -l%s\n", dcolor(dc_yellow), [appBundle UTF8String], colorEnd(), dcolor(dc_bold), colorEnd());
62 | // return 1;
63 | // }
64 | // }
65 |
66 | NSString *basePath = [[options objectForKey:kGetLogsFilePath] stringByExpandingTildeInPath];
67 | if (!basePath) {
68 | basePath = @"/tmp/ios_crashes";
69 | }
70 |
71 | NSURL *baseURL = [NSURL URLWithString:basePath];
72 | if (!baseURL) {
73 | dsprintf(stderr, "Couldn't create access path \"%s\", exiting\n", baseURL);
74 | return 1;
75 | }
76 | NSError *error = nil;
77 | if (get_all_logs) {
78 | [[NSFileManager defaultManager] createDirectoryAtURL:baseURL withIntermediateDirectories:YES attributes:nil error:&error];
79 | }
80 | AMDServiceConnectionRef serviceConnection = nil;
81 | AMDStartService_NOUNLOCK(d, @"com.apple.crashreportcopymobile", &serviceConnection);
82 | long soc = AMDServiceConnectionGetSocket(serviceConnection);
83 | if (!soc) {
84 | dsprintf(stderr, "Couldn't get underlying socket\n");
85 | return EACCES;
86 | }
87 | void* secureContext = AMDServiceConnectionGetSecureIOContext(serviceConnection);
88 |
89 |
90 | // we are doing a bunch of short lived requests so don't close on invalidate
91 | AFCConnectionRef connectionRef = AFCConnectionCreate(NULL, (int)soc, false /* closeOnIvalidate */, 0, 0);
92 | if (!connectionRef) {
93 | dsprintf(stderr, "%sCould not obtain a valid connection. Aborting%s\n", dcolor(dc_yellow), colorEnd());
94 | return EACCES;
95 | }
96 | if (secureContext) {
97 | AFCConnectionSetSecureContext(connectionRef, secureContext);
98 | }
99 |
100 | NSMutableSet * unexploredDirectories = [NSMutableSet set];
101 | [unexploredDirectories addObject:@"."];
102 |
103 |
104 | while ([unexploredDirectories count]) {
105 | AFCIteratorRef iteratorRef = NULL;
106 | NSString *currentDir = [unexploredDirectories anyObject];
107 | [unexploredDirectories removeObject:currentDir];
108 | afc_err e = AFCDirectoryOpen(connectionRef, [currentDir UTF8String], &iteratorRef);
109 |
110 | if (e) {
111 | dprint("got error on AFCDirectoryOpen %s (%d)\n", [AFCCopyErrorString(e) UTF8String], e);
112 | continue;
113 | }
114 |
115 |
116 |
117 |
118 | char *remotePath = NULL;
119 | // BOOL shouldDelete = [[options objectForKey:kGetLogsDelete] boolValue];
120 | // if (shouldDelete && !global_options.quiet) {
121 | // dsprintf(stdout, "About to delete all logs, please confirm [Y] ");
122 | // if (getchar() != 89) {
123 | // dsprintf(stdout, "\nExiting\n");
124 | // exit(0);
125 | // }
126 | // }
127 | #define GOOD_E_NUFF_BUFF 0x12000000
128 |
129 |
130 |
131 | char* file_buffer = malloc(GOOD_E_NUFF_BUFF);
132 | while (AFCDirectoryRead(connectionRef, iteratorRef, &remotePath) == AMD_SUCCESS && remotePath) {
133 |
134 | // always does current and prev dir so skip it
135 | if (remotePath && (!strcmp(remotePath, ".") || !strcmp(remotePath, ".."))) {
136 | continue;
137 | }
138 |
139 | AFCFileDescriptorRef descriptorRef = NULL;
140 | NSString *resolvedRemoteFile = [NSString stringWithFormat:@"%@/%s", currentDir, remotePath];
141 | AFCFileInfoRef fileInfo = NULL;
142 | afc_err e = AFCFileInfoOpen(connectionRef, [resolvedRemoteFile UTF8String], &fileInfo);
143 |
144 | bool isDir = false;
145 | if (!e && fileInfo) {
146 | char *value = NULL;
147 | char *key = NULL;
148 | while (AFCKeyValueRead(fileInfo, &key, &value) == AMD_SUCCESS) {
149 | if (!key) {
150 | break;
151 | }
152 | if (strcmp("S_IFDIR", value) == 0 && strcmp("st_ifmt", key) == 0) {
153 | isDir = true;
154 | if (strcmp(remotePath, "Retired") != 0) {
155 | [unexploredDirectories addObject:[NSString stringWithFormat:@"%@/%s", currentDir, remotePath]];
156 | NSError *error = NULL;
157 | // NSURL *localCurrentDir = [baseURL URLByAppendingPathComponent:[resolvedRemoteFile stringByStandardizingPath]];
158 |
159 |
160 | if (error) {
161 | dprint("%s. exiting...\n", [[error localizedDescription] UTF8String]);
162 | // return 1;
163 | }
164 | }
165 | break;
166 | }
167 | }
168 | AFCKeyValueClose(fileInfo);
169 | }
170 |
171 | if (e || isDir) {
172 | continue;
173 | }
174 |
175 | if (!requestedFile) {
176 | dprint("%s\n", [resolvedRemoteFile UTF8String]);
177 | continue;
178 | }
179 |
180 | if (!get_all_logs && strcmp([requestedFile UTF8String], [resolvedRemoteFile UTF8String]) != 0) {
181 | continue;
182 | }
183 |
184 | e = AFCFileRefOpen(connectionRef, [resolvedRemoteFile UTF8String], 0x1, &descriptorRef);
185 | if (e) {
186 | continue;
187 | }
188 |
189 |
190 | size_t len = GOOD_E_NUFF_BUFF;
191 | NSString *finalPath = [NSString stringWithFormat:@"%@/%s", currentDir, remotePath];
192 | NSString *resolvedLocalPath = [[baseURL URLByAppendingPathComponent:[resolvedRemoteFile stringByStandardizingPath]] path];
193 | FILE*p = NULL;
194 |
195 | if (get_all_logs) {
196 | p = fopen([resolvedLocalPath UTF8String], "w");
197 | dprint("\"%s\" -> \"%s\"\n", [finalPath UTF8String
198 | ], [resolvedLocalPath UTF8String]);
199 | }
200 | while (AFCFileRefRead(connectionRef, descriptorRef, file_buffer, &len) == AMD_SUCCESS && len) {
201 | if (get_all_logs) {
202 | if (p) {
203 | fwrite(file_buffer, len, 1, p);
204 | }
205 |
206 | } else {
207 | fwrite(file_buffer, len, 1, stdout);
208 | }
209 | len = GOOD_E_NUFF_BUFF;
210 | }
211 | if (p) {
212 | fclose(p);
213 | }
214 |
215 | AFCFileRefClose(connectionRef, descriptorRef);
216 | if (should_delete) {
217 | e = AFCRemovePath(connectionRef, [finalPath UTF8String]);
218 | }
219 | if (e) {
220 | dprint("error removing %s\n", [resolvedLocalPath UTF8String]);
221 | }
222 |
223 |
224 | }
225 | AFCDirectoryClose(connectionRef, iteratorRef);
226 | free((void*)file_buffer);
227 | }
228 |
229 |
230 | AFCConnectionClose(connectionRef);
231 | AMDServiceConnectionInvalidate(serviceConnection);
232 | close((int)soc);
233 |
234 | return 0;
235 | }
236 |
--------------------------------------------------------------------------------
/mobdevim/misc/Entitlements.entitlements:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | com.apple.security.temporary-exception.mach-lookup.global-name
6 |
7 | com.apple.remoted
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/mobdevim/misc/InstrumentsPlugin.h:
--------------------------------------------------------------------------------
1 | //
2 | // InternalApi.h
3 | // FreeTheSandbox
4 | //
5 | // Created by codecolorist on 2020/2/17.
6 | // Copyright © 2020 CC. All rights reserved.
7 | //
8 |
9 | #ifndef InternalApi_h
10 | #define InternalApi_h
11 |
12 |
13 |
14 | @interface DVTDeveloperPaths : NSObject
15 | + (void)initializeApplicationDirectoryName:(id)arg1;
16 | @end
17 |
18 | @interface DVTPlugInManager : NSObject
19 | - (BOOL)scanForPlugIns:(id *)arg1;
20 | @end
21 |
22 |
23 | @protocol XRIssueResponder
24 | - (void)handleIssue:(NSError *)arg1 type:(unsigned long long)arg2 from:(id)arg3;
25 | @end
26 |
27 | @interface XRPackageConflictErrorAccumulator : NSObject
28 | - (id)initWithNextResponder:(id)arg1;
29 | @end
30 |
31 |
32 | @interface XRUniqueIssueAccumulator : NSObject
33 |
34 | @end
35 |
36 | @interface DTXMessage : NSObject
37 | @property(copy, nonatomic) id payloadObject;
38 | + messageWithSelector:(SEL)sel objectArguments: (id)args, ...;
39 | - (NSError*)error;
40 | @end
41 |
42 |
43 | @interface DTXChannel : NSObject
44 | - (void)receiveMobileAgent:(id)arg1;
45 | - (id)messageReplyTicketForControlMessage:(id)arg1 agent:(id)arg2;
46 | - (void)sendMessageSync:(id)arg1 replyHandler:(void (^)(id, int))handler;
47 | @end
48 |
49 | @interface DVTFilePath : NSObject
50 | @end
51 |
52 | @interface DVTPlatform : NSObject
53 |
54 | @property(readonly, copy) NSString *platformVersion;
55 | @property(readonly, copy) NSDictionary *deviceProperties;
56 | @property(readonly) DVTFilePath *iconPath;
57 |
58 | @end
59 |
60 | @protocol DTXDSProtocol
61 | -(id)makeChannelWithIdentifier:(NSString*)identifier;
62 | -(int)remoteCapabilityVersion:(NSString*)identifier;
63 | -(void)cancel;
64 | -(void)suspend;
65 | @end
66 |
67 | @interface XRDevice : NSObject
68 |
69 | @property(retain) DTXChannel *deviceInfoService;
70 | -(id)connection;
71 | @property(copy) NSImage *downsampledDeviceImage;
72 | @property(retain) DTXChannel *capabilitiesChannel;
73 | @property double timeDifference;
74 | @property(readonly, copy) NSString *activePairedWatchDeviceIdentifier;
75 | @property(readonly, copy) NSString *companionDeviceIdentifier;
76 | @property long long symbolsState;
77 | @property(retain) DVTPlatform *platform;
78 | @property(copy) NSString *modelName;
79 | @property(copy) NSString *modelUTI;
80 | @property(copy) NSString *productType;
81 | @property(copy) NSString *productVersion;
82 | @property(copy) NSString *buildVersion;
83 | @property(copy) NSString *deviceDescription;
84 | @property(copy) NSString *rawDeviceDisplayName;
85 | @property(copy) NSString *deviceDisplayName;
86 | @property(copy) NSString *deviceHostName;
87 | @property(copy) NSString *deviceIdentifier;
88 | @property(readonly) unsigned int deviceNumber;
89 | @property(readonly) int connectionCount;
90 | @property(copy) NSImage *deviceSmallRepresentationIcon;
91 | @end
92 |
93 | @interface XRRemoteDevice : XRDevice
94 | @property(retain) DTXChannel *companionControlChannel;
95 | @property(retain) DTXChannel *applicationChannel;
96 | @property(retain) DTXChannel *notificationsChannel;
97 | @property(retain) DTXChannel *posixProcessControlChannel;
98 | @property(retain) DTXChannel *defaultProcessControlChannel;
99 | @property(retain) DTXChannel *xpcLauncherService;
100 | @property(retain) DTXChannel *launchDaemonService;
101 | @property(retain) DTXChannel *wirelessControlChannel;
102 | @property(copy, nonatomic) NSString *wirelessServiceName;
103 | - (id)queryCompanionForActiveWatchDeviceIdentifier;
104 | - (id)activePairedWatchDeviceIdentifier;
105 | - (void)willUnpairFromCompanion:(id)arg1;
106 | - (void)didPairWithCompanion:(id)arg1;
107 | - (instancetype)initWithDevice:(AMDeviceRef)device;
108 | - (void)handleNewRawDeviceDisplayName;
109 | - (void)simulateMemoryWarning:(id)arg1;
110 | - (id)targetControlDataElementsForProcess:(id)arg1;
111 | - (id)launchControlDataElementsForProcess:(id)arg1;
112 | - (void)outputReceived:(id)arg1 fromProcess:(int)arg2 atTime:(unsigned long long)arg3;
113 | - (void)thermalLevelNotification:(id)arg1;
114 | - (void)memoryLevelNotification:(id)arg1;
115 | - (void)applicationStateNotification:(id)arg1;
116 | - (unsigned long long)_traceRelativeTimestampForNotification:(id)arg1 inTrace:(id)arg2;
117 | - (void)setMemoryConstraint:(int)arg1;
118 | - (int)memoryConstraint;
119 | - (id)availableNetworkInterfaces;
120 | - (id)displayNameForNetworkInterface:(id)arg1;
121 | - (BOOL)supportsDeviceIO;
122 | - (BOOL)daemonsSupported;
123 | - (BOOL)legacyDaemonsSupported;
124 | @property(readonly, copy) NSString *cpuArchitecture;
125 | - (id)cpuDescription;
126 | - (id)deviceArchitecture;
127 | - (int)speedOfCpus;
128 | - (int)numberOfPhysicalCpus;
129 | - (int)numberOfCpus;
130 | - (BOOL)resumeProcess:(id)arg1;
131 | - (BOOL)suspendProcess:(id)arg1;
132 | - (void)terminateProcess:(id)arg1;
133 | - (int)launchProcess:(id)arg1 suspended:(BOOL)arg2 error:(id *)arg3;
134 | - (void)pidDiedCallback:(id)arg1;
135 | - (void)removeObserver:(id)arg1 forPid:(int)arg2;
136 | - (void)addObserver:(id)arg1 forPid:(int)arg2;
137 | - (BOOL)allowsChoosingExecutable;
138 | - (void)dyldNotificationReceived:(id)arg1;
139 | - (struct _CSTypeRef)createKernelSymbolicator;
140 | - (BOOL)supportsKernelBacktracing;
141 | - (struct _CSTypeRef)createSymbolicatorForPid:(int)arg1;
142 | - (BOOL)executableIsRestricted:(id)arg1 launchOptions:(id)arg2;
143 | - (BOOL)isRunningPid:(int)arg1;
144 | - (id)execnameForPid:(int)arg1;
145 | - (id)userForUID:(id)arg1;
146 | - (NSImage *)iconForAppPath:(id)arg1 executableName:(id)arg2;
147 | - (NSArray*)runningProcesses;
148 | - (id)defaultAppIcon;
149 | - (id)marketizedPlatformName;
150 | - (id)platformName;
151 | - (BOOL)updateInstalledExecutables;
152 | - (id)fileSystem;
153 | - (id)processControlServiceForPid:(int)arg1;
154 | - (void)checkForSymbols;
155 | - (void)xcodeWasTerminated:(unsigned int)arg1;
156 | - (void)xcodeWasLaunched:(unsigned int)arg1;
157 | - (void)symbolsDownloadedAtPath:(id)arg1;
158 | - (id)externalSDKPath;
159 | - (id)internalSDKPath;
160 | - (id)baseSymbolsPath;
161 | - (void)teardownConnection;
162 | - (int)processControlServiceVersion;
163 | - (id)makeConnection;
164 | - (void)prepareConnection:(id)arg1;
165 | - (id)_faultConnection;
166 | - (id)initWithTemplateData:(id)arg1;
167 | - (id)templateData;
168 | - (BOOL)supportsProcessControlEventDictionaries;
169 | - (void)preflightDevice;
170 | - (void)dealloc;
171 | - (id)initWithIdentifier:(id)arg1;
172 |
173 | @end
174 |
175 | @interface XRMobileDevice : XRRemoteDevice
176 | - (instancetype)initWithDevice:(id)device;
177 | @end
178 |
179 | @interface PFTProcess : NSObject
180 | {
181 | NSString *_userProvidedArgs;
182 | NSDictionary *_userProvidedEnvironment;
183 | NSMutableDictionary *_mutatedEnvironment;
184 | NSMutableArray *_stopActions;
185 | NSString *_executablePath;
186 | NSString *_processName;
187 | NSString *_displayName;
188 | NSString *_bundleIdentifier;
189 | XRRemoteDevice *_device;
190 | int _pid;
191 | BOOL _watchingForTermination;
192 | BOOL _didLaunchProcess;
193 | int _specifiedType;
194 | BOOL _subtaskPaused;
195 | BOOL _restricted;
196 | NSDate *_startDate;
197 | PFTProcess *_hostProcess;
198 | NSDictionary *_properties;
199 | NSMutableDictionary *_launchControlProperties;
200 | NSImage *_atomicProcessImage;
201 | }
202 |
203 | + (int)targetTypeForProcess:(id)arg1;
204 | + (id)processFromData:(id)arg1 device:(id)arg2;
205 | + (id)unspecifiedProcess;
206 | + (void)initialize;
207 | @property(retain) NSImage *atomicProcessImage;
208 | @property(readonly) NSMutableDictionary *launchControlProperties;
209 | @property(copy) NSDictionary *properties;
210 | @property(copy, nonatomic) PFTProcess *hostProcess;
211 | @property BOOL restricted;
212 | @property(retain, nonatomic) NSDate *startDate;
213 | @property(readonly) __weak XRRemoteDevice *device;
214 | @property(readonly) int processIdentifier;
215 | @property(readonly) NSString *executablePath;
216 | @property(copy) NSString *argumentsString;
217 | - (id)functionSymbolNames;
218 | @property(readonly, copy) NSString *description;
219 | @property int type;
220 | @property(nonatomic, getter=isPaused) BOOL paused;
221 | @property(copy) id image;
222 | - (void)stop;
223 | - (void)addStopAction:(id)arg1;
224 | - (void)notifyOnTermination;
225 | @property(readonly, getter=isRunning) BOOL running;
226 | - (BOOL)runSuspended:(BOOL)arg1 error:(id *)arg2;
227 | - (void)processDeathDetectedForPid:(int)arg1;
228 | - (void)resetInitialEnvironmentAndArgs;
229 | - (void)addEnvironmentVariable:(id)arg1 value:(id)arg2;
230 | @property(readonly) NSArray *arguments;
231 | @property(copy) NSDictionary *environment;
232 | @property(readonly, nonatomic) int processDomain;
233 | @property(readonly, nonatomic) NSArray *canonicalHostProcesses;
234 | @property(readonly, nonatomic) NSDictionary *extensionInfo;
235 | @property(readonly) NSString *bundleIdentifier; // @synthesize bundleIdentifier=_bundleIdentifier;
236 | @property(copy, nonatomic) NSString *displayName;
237 | @property(readonly) NSString *processName;
238 | - (id)templateData;
239 | - (id)copyWithZone:(struct _NSZone *)arg1;
240 | - (BOOL)isEqual:(id)arg1;
241 | - (int)_targetType;
242 | - (void)_setPid:(int)arg1 asOwner:(BOOL)arg2;
243 | @property(readonly) BOOL _isProcessOwner;
244 | - (id)initWithDevice:(id)arg1 path:(NSString*)arg2 bundleIdentifier:(NSString *)arg3 arguments:(NSString*)arg4 environment:(NSDictionary *)arg5 launchOptions:(id)arg6;
245 |
246 | @end
247 |
248 | @interface XRDeviceDiscovery : NSObject
249 |
250 | + (void)xcodeTerminated:(unsigned int)arg1;
251 | + (void)xcodeLaunched:(unsigned int)arg1;
252 | + (id)devicesMatching:(id)arg1;
253 | + (id)deviceForIdentifier:(id)arg1;
254 | + (id)allKnownDevices;
255 | + (NSArray *)availableDevices;
256 | + (void)unregisterForDeviceNotifications:(unsigned int)arg1;
257 | + (void)forgetDevice:(id)arg1;
258 | + (void)deviceStateChanged:(id)arg1;
259 | + (void)deviceConnected:(id)arg1;
260 | + (void)initialize;
261 | + (id)deviceDiscoveryImplementations;
262 | + (void)registerDeviceObserver:(id)arg1;
263 |
264 | - (void)stopListeningForDevices;
265 | - (void)startListeningForDevices;
266 | - (id)deviceList;
267 | - (id)deviceManagementItems;
268 | - (id)deviceManagementName;
269 | - (id)imageForDeviceType:(id)arg1 deviceColorString:(id)arg2 deviceEnclosureColorString:(id)arg3;
270 | - (id)imageForDeviceType:(id)arg1;
271 |
272 | @end
273 |
274 | #endif /* InternalApi_h */
275 |
--------------------------------------------------------------------------------
/mobdevim/misc/NSArray+Output.h:
--------------------------------------------------------------------------------
1 | //
2 | // NSArray+Output.h
3 | // mobdevim
4 | //
5 | //
6 | // Copyright © 2020 Selander. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | @interface NSArray (Output)
12 | - (const char *)dsformattedOutput;
13 | @end
14 |
15 | @interface NSDictionary (Output)
16 | - (const char *)dsformattedOutput;
17 | @end
18 |
19 | @interface NSDate (Output)
20 | - (const char *)dsformattedOutput;
21 | @end
22 |
23 | @interface NSObject (Output)
24 | @property (nonatomic, strong) NSNumber* dsIndentOffset;
25 | @end
26 |
27 |
28 |
--------------------------------------------------------------------------------
/mobdevim/misc/NSArray+Output.m:
--------------------------------------------------------------------------------
1 | //
2 | // NSArray+Output.m
3 | // mobdevim
4 | //
5 | //
6 | // Copyright © 2020 Selander. All rights reserved.
7 | //
8 |
9 | #import "NSArray+Output.h"
10 | #import "helpers.h"
11 | @import ObjectiveC.runtime;
12 |
13 | @implementation NSObject (Output)
14 |
15 | @dynamic dsIndentOffset;
16 |
17 | - (void)setDsIndentOffset:(NSNumber*)object {
18 | objc_setAssociatedObject(self, @selector(dsIndentOffset), object, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
19 | }
20 |
21 | - (NSNumber*)dsIndentOffset {
22 | return objc_getAssociatedObject(self, @selector(dsIndentOffset));
23 | }
24 |
25 | - (const char *)dsformattedOutput {
26 | return [[self description] UTF8String];
27 | }
28 | @end
29 |
30 |
31 | @implementation NSNumber (Output)
32 | - (const char *)dsformattedOutput {
33 | BOOL plistOutput = (getenv("DSPLIST") != NULL);
34 |
35 | NSNumber *currentOffset = [self dsIndentOffset];
36 | if (!currentOffset) {
37 | currentOffset = @1;
38 | }
39 |
40 | if (plistOutput) {
41 |
42 | Class boolClass = [[NSNumber numberWithBool:YES] class];
43 |
44 | if([self isKindOfClass:boolClass]) {
45 | return [[NSString stringWithFormat:@"%*s<%s/>\n", ([currentOffset intValue]-1) * 4 , "", [self boolValue] ? "true": "false"] UTF8String];
46 | } else {
47 | return [[NSString stringWithFormat:@"%*s%@\n", ([currentOffset intValue]-1) * 4 , "", self] UTF8String];
48 | }
49 | }
50 |
51 | return [[NSString stringWithFormat:@"%*s%@\n", ([currentOffset intValue]-1) * 4 , "", self] UTF8String];
52 | }
53 | @end
54 |
55 | @implementation NSString (Output)
56 | - (const char *)dsformattedOutput {
57 | BOOL plistOutput = (getenv("DSPLIST") != NULL);
58 |
59 | NSNumber *currentOffset = [self dsIndentOffset];
60 | if (!currentOffset) {
61 | currentOffset = @1;
62 | }
63 |
64 | if (plistOutput) {
65 | return [[NSString stringWithFormat:@"%*s%@\n", ([currentOffset intValue]-1) * 4 , "", self] UTF8String];
66 | }
67 |
68 | return [[NSString stringWithFormat:@"%*s%@\n", ([currentOffset intValue]-1) * 4 , "", self] UTF8String];
69 | }
70 |
71 |
72 | @end
73 |
74 | @implementation NSArray (Output)
75 |
76 | - (const char *)dsformattedOutput {
77 | NSMutableString *outputString = [NSMutableString string];
78 | BOOL plistOutput = (getenv("DSPLIST") != NULL);
79 | NSNumber *currentOffset = [self dsIndentOffset];
80 | if (!currentOffset) {
81 | currentOffset = @1;
82 | }
83 | if ([currentOffset intValue] == 1 && plistOutput) {
84 | [outputString appendString:@"\n\n\n"];
85 | }
86 |
87 |
88 | if ([self count] == 0) {
89 | if (plistOutput) {
90 | [outputString appendString:@"\n"];
91 | } else {
92 | [outputString appendFormat:@"%s[ ]%s", dcolor(dc_bold), colorEnd()];
93 | }
94 | return [outputString UTF8String];
95 | }
96 |
97 | if (plistOutput) {
98 | [outputString appendFormat:@"%*s\n", ([currentOffset intValue]-1) * 4 , ""];
99 | } else {
100 | [outputString appendFormat:@"%*s%s[%s\n", ([currentOffset intValue]-1) * 4 , "", dcolor(dc_bold), colorEnd()];
101 | }
102 | for (id itemObject in self) {
103 | [itemObject setDsIndentOffset:@([currentOffset intValue] + 1)];
104 | [outputString appendFormat:@"%s", [itemObject dsformattedOutput]];
105 | }
106 |
107 |
108 | if (plistOutput) {
109 | [outputString appendFormat:@"%*s\n", ([currentOffset intValue]-1) * 4 , ""];
110 | } else {
111 | [outputString appendFormat:@"%*s%s]%s", ([currentOffset intValue]-1) * 4 , "", dcolor(dc_bold), colorEnd()];
112 | }
113 |
114 | if ([currentOffset intValue] == 1 && plistOutput) {
115 | [outputString appendString:@"\n"];
116 | }
117 |
118 |
119 | return [outputString UTF8String];
120 |
121 | }
122 |
123 | @end
124 |
125 | @implementation NSDictionary (Output)
126 |
127 | - (const char *)dsformattedOutput {
128 | NSMutableString *outputString = [NSMutableString string];
129 | BOOL plistOutput = (getenv("DSPLIST") != NULL);
130 |
131 | NSNumber *currentOffset = [self dsIndentOffset];
132 | if (!currentOffset) {
133 | currentOffset = @1;
134 | }
135 |
136 | if ([currentOffset intValue] == 1 && plistOutput) {
137 | [outputString appendString:@"\n\n\n"];
138 | }
139 |
140 | if (plistOutput) {
141 | [outputString appendFormat:@"%*s\n", ([currentOffset intValue] -1) * 4 , ""];
142 | } else {
143 | [outputString appendFormat:@"%*s%s{%s\n", ([currentOffset intValue] -1) * 4 , "", dcolor(dc_bold), colorEnd()];
144 | }
145 | for (id key in self) {
146 | id itemObject = [self objectForKey:key];
147 | [itemObject setDsIndentOffset:@([currentOffset integerValue] + 1)];
148 |
149 | if (plistOutput) {
150 | [outputString appendFormat:@"%*s%@\n%s", [currentOffset intValue] * 4 , "", key, [itemObject dsformattedOutput]];
151 | } else if ([itemObject isKindOfClass:[NSDictionary class]] || [itemObject isKindOfClass:[NSArray class]]) {
152 | [outputString appendFormat:@"%*s%s%@%s:\n%s\n", [currentOffset intValue] * 4 , "", dcolor(dc_cyan), key, colorEnd(), [itemObject dsformattedOutput]];
153 | } else {
154 | [outputString appendFormat:@"%*s%s%@%s: %@\n", [currentOffset intValue] * 4 , "", dcolor(dc_cyan), key, colorEnd(), itemObject];
155 | }
156 | }
157 | if (plistOutput) {
158 | [outputString appendFormat:@"%*s\n", ([currentOffset intValue] -1) * 4 , ""];
159 | } else {
160 | [outputString appendFormat:@"%*s%s}%s\n", ([currentOffset intValue] -1) * 4 , "", dcolor(dc_bold), colorEnd()];
161 | }
162 |
163 | if ([currentOffset intValue] == 1 && plistOutput) {
164 | [outputString appendString:@"\n"];
165 | }
166 |
167 | return [outputString UTF8String];
168 | }
169 |
170 | @end
171 |
172 |
173 | @implementation NSDate (Output)
174 |
175 | - (const char *)dsformattedOutput {
176 | NSMutableString *outputString = [NSMutableString string];
177 |
178 | NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
179 | [formatter setDateFormat:@"MMMM d, yyyy h:mm a"];
180 | [outputString appendFormat:@"%@", [formatter stringFromDate:self] ];
181 | return [outputString UTF8String];
182 | }
183 |
184 | @end
185 |
--------------------------------------------------------------------------------
/mobdevim/misc/Progress Bar/progressbar.c:
--------------------------------------------------------------------------------
1 | /**
2 | * \file
3 | * \author Trevor Fountain
4 | * \author Johannes Buchner
5 | * \author Erik Garrison
6 | * \date 2010-2014
7 | * \copyright BSD 3-Clause
8 | *
9 | * progressbar -- a C class (by convention) for displaying progress
10 | * on the command line (to stderr).
11 | */
12 |
13 | #include /* tgetent, tgetnum */
14 | #include
15 | #include
16 | #include "progressbar.h"
17 |
18 | /// How wide we assume the screen is if termcap fails.
19 | enum { DEFAULT_SCREEN_WIDTH = 80 };
20 | /// The smallest that the bar can ever be (not including borders)
21 | enum { MINIMUM_BAR_WIDTH = 10 };
22 | /// The format in which the estimated remaining time will be reported
23 | static const char *const ETA_FORMAT = "ETA:%2dh%02dm%02ds";
24 | /// The maximum number of characters that the ETA_FORMAT can ever yield
25 | enum { ETA_FORMAT_LENGTH = 13 };
26 | /// Amount of screen width taken up by whitespace (i.e. whitespace between label/bar/ETA components)
27 | enum { WHITESPACE_LENGTH = 2 };
28 | /// The amount of width taken up by the border of the bar component.
29 | enum { BAR_BORDER_WIDTH = 2 };
30 |
31 | /// Models a duration of time broken into hour/minute/second components. The number of seconds should be less than the
32 | /// number of seconds in one minute, and the number of minutes should be less than the number of minutes in one hour.
33 | typedef struct {
34 | int hours;
35 | int minutes;
36 | int seconds;
37 | } progressbar_time_components;
38 |
39 | static void progressbar_draw(const progressbar *bar);
40 |
41 | /**
42 | * Create a new progress bar with the specified label, max number of steps, and format string.
43 | * Note that `format` must be exactly three characters long, e.g. "<->" to render a progress
44 | * bar like "<---------->". Returns NULL if there isn't enough memory to allocate a progressbar
45 | */
46 | progressbar *progressbar_new_with_format(const char *label, unsigned long max, const char *format)
47 | {
48 | progressbar *new = malloc(sizeof(progressbar));
49 | if(new == NULL) {
50 | return NULL;
51 | }
52 |
53 | new->max = max;
54 | new->value = 0;
55 | new->start = time(NULL);
56 | assert(3 == strlen(format) && "format must be 3 characters in length");
57 | new->format.begin = format[0];
58 | new->format.fill = format[1];
59 | new->format.end = format[2];
60 |
61 | progressbar_update_label(new, label);
62 | progressbar_draw(new);
63 |
64 | return new;
65 | }
66 |
67 | /**
68 | * Create a new progress bar with the specified label and max number of steps.
69 | */
70 | progressbar *progressbar_new(const char *label, unsigned long max)
71 | {
72 | return progressbar_new_with_format(label, max, "|=|");
73 | }
74 |
75 | void progressbar_update_label(progressbar *bar, const char *label)
76 | {
77 | bar->label = label;
78 | }
79 |
80 | /**
81 | * Delete an existing progress bar.
82 | */
83 | void progressbar_free(progressbar *bar)
84 | {
85 | free(bar);
86 | }
87 |
88 | /**
89 | * Increment an existing progressbar by `value` steps.
90 | */
91 | void progressbar_update(progressbar *bar, unsigned long value)
92 | {
93 | bar->value = value;
94 | progressbar_draw(bar);
95 | }
96 |
97 | /**
98 | * Increment an existing progressbar by a single step.
99 | */
100 | void progressbar_inc(progressbar *bar)
101 | {
102 | progressbar_update(bar, bar->value+1);
103 | }
104 |
105 | static void progressbar_write_char(FILE *file, const int ch, const size_t times) {
106 | size_t i;
107 | for (i = 0; i < times; ++i) {
108 | fputc(ch, file);
109 | }
110 | }
111 |
112 | static int progressbar_max(int x, int y) {
113 | return x > y ? x : y;
114 | }
115 |
116 | static unsigned int get_screen_width(void) {
117 | char termbuf[2048];
118 | if (tgetent(termbuf, getenv("TERM")) >= 0) {
119 | return tgetnum("co") /* -2 */;
120 | } else {
121 | return DEFAULT_SCREEN_WIDTH;
122 | }
123 | }
124 |
125 | static int progressbar_bar_width(int screen_width, int label_length) {
126 | return progressbar_max(MINIMUM_BAR_WIDTH, screen_width - label_length - ETA_FORMAT_LENGTH - WHITESPACE_LENGTH);
127 | }
128 |
129 | static int progressbar_label_width(int screen_width, int label_length, int bar_width) {
130 | int eta_width = ETA_FORMAT_LENGTH;
131 |
132 | // If the progressbar is too wide to fit on the screen, we must sacrifice the label.
133 | if (label_length + 1 + bar_width + 1 + ETA_FORMAT_LENGTH > screen_width) {
134 | return progressbar_max(0, screen_width - bar_width - eta_width - WHITESPACE_LENGTH);
135 | } else {
136 | return label_length;
137 | }
138 | }
139 |
140 | static int progressbar_remaining_seconds(const progressbar* bar) {
141 | double offset = difftime(time(NULL), bar->start);
142 | if (bar->value > 0 && offset > 0) {
143 | return (offset / (double) bar->value) * (bar->max - bar->value);
144 | } else {
145 | return 0;
146 | }
147 | }
148 |
149 | static progressbar_time_components progressbar_calc_time_components(int seconds) {
150 | int hours = seconds / 3600;
151 | seconds -= hours * 3600;
152 | int minutes = seconds / 60;
153 | seconds -= minutes * 60;
154 |
155 | progressbar_time_components components = {hours, minutes, seconds};
156 | return components;
157 | }
158 |
159 | static void progressbar_draw(const progressbar *bar)
160 | {
161 | int screen_width = get_screen_width();
162 | int label_length = (int)strlen(bar->label);
163 | int bar_width = progressbar_bar_width(screen_width, label_length);
164 | int label_width = progressbar_label_width(screen_width, label_length, bar_width);
165 |
166 | int progressbar_completed = (bar->value >= bar->max);
167 | int bar_piece_count = bar_width - BAR_BORDER_WIDTH;
168 | int bar_piece_current = (progressbar_completed)
169 | ? bar_piece_count
170 | : bar_piece_count * ((double) bar->value / bar->max);
171 |
172 | progressbar_time_components eta = (progressbar_completed)
173 | ? progressbar_calc_time_components(difftime(time(NULL), bar->start))
174 | : progressbar_calc_time_components(progressbar_remaining_seconds(bar));
175 |
176 | if (label_width == 0) {
177 | // The label would usually have a trailing space, but in the case that we don't print
178 | // a label, the bar can use that space instead.
179 | bar_width += 1;
180 | } else {
181 | // Draw the label
182 | fwrite(bar->label, 1, label_width, stderr);
183 | fputc(' ', stderr);
184 | }
185 |
186 | // Draw the progressbar
187 | fputc(bar->format.begin, stderr);
188 | progressbar_write_char(stderr, bar->format.fill, bar_piece_current);
189 | progressbar_write_char(stderr, ' ', bar_piece_count - bar_piece_current);
190 | fputc(bar->format.end, stderr);
191 |
192 | // Draw the ETA
193 | fputc(' ', stderr);
194 | fprintf(stderr, ETA_FORMAT, eta.hours, eta.minutes, eta.seconds);
195 | fputc('\r', stderr);
196 | }
197 |
198 | /**
199 | * Finish a progressbar, indicating 100% completion, and free it.
200 | */
201 | void progressbar_finish(progressbar *bar)
202 | {
203 | // Make sure we fill the progressbar so things look complete.
204 | progressbar_draw(bar);
205 |
206 | // Print a newline, so that future outputs to stderr look prettier
207 | fprintf(stderr, "\n");
208 |
209 | // We've finished with this progressbar, so go ahead and free it.
210 | progressbar_free(bar);
211 | }
212 |
--------------------------------------------------------------------------------
/mobdevim/misc/Progress Bar/progressbar.h:
--------------------------------------------------------------------------------
1 | /**
2 | * \file
3 | * \author Trevor Fountain
4 | * \author Johannes Buchner
5 | * \author Erik Garrison
6 | * \date 2010-2014
7 | * \copyright BSD 3-Clause
8 | *
9 | * progressbar -- a C class (by convention) for displaying progress
10 | * on the command line (to stderr).
11 | */
12 |
13 | #ifndef PROGRESSBAR_H
14 | #define PROGRESSBAR_H
15 |
16 | #include
17 | #include
18 | #include
19 | #include
20 |
21 | #ifdef __cplusplus
22 | extern "C" {
23 | #endif
24 |
25 | /**
26 | * Progressbar data structure (do not modify or create directly)
27 | */
28 | typedef struct _progressbar_t
29 | {
30 | /// maximum value
31 | unsigned long max;
32 | /// current value
33 | unsigned long value;
34 |
35 | /// time progressbar was started
36 | time_t start;
37 |
38 | /// label
39 | const char *label;
40 |
41 | /// characters for the beginning, filling and end of the
42 | /// progressbar. E.g. |### | has |#|
43 | struct {
44 | char begin;
45 | char fill;
46 | char end;
47 | } format;
48 | } progressbar;
49 |
50 | /// Create a new progressbar with the specified label and number of steps.
51 | ///
52 | /// @param label The label that will prefix the progressbar.
53 | /// @param max The number of times the progressbar must be incremented before it is considered complete,
54 | /// or, in other words, the number of tasks that this progressbar is tracking.
55 | ///
56 | /// @return A progressbar configured with the provided arguments. Note that the user is responsible for disposing
57 | /// of the progressbar via progressbar_finish when finished with the object.
58 | progressbar *progressbar_new(const char *label, unsigned long max);
59 |
60 | /// Create a new progressbar with the specified label, number of steps, and format string.
61 | ///
62 | /// @param label The label that will prefix the progressbar.
63 | /// @param max The number of times the progressbar must be incremented before it is considered complete,
64 | /// or, in other words, the number of tasks that this progressbar is tracking.
65 | /// @param format The format of the progressbar. The string provided must be three characters, and it will
66 | /// be interpretted with the first character as the left border of the bar, the second
67 | /// character of the bar and the third character as the right border of the bar. For example,
68 | /// "<->" would result in a bar formatted like "<------ >".
69 | ///
70 | /// @return A progressbar configured with the provided arguments. Note that the user is responsible for disposing
71 | /// of the progressbar via progressbar_finish when finished with the object.
72 | progressbar *progressbar_new_with_format(const char *label, unsigned long max, const char *format);
73 |
74 | /// Free an existing progress bar. Don't call this directly; call *progressbar_finish* instead.
75 | void progressbar_free(progressbar *bar);
76 |
77 | /// Increment the given progressbar. Don't increment past the initialized # of steps, though.
78 | void progressbar_inc(progressbar *bar);
79 |
80 | /// Set the current status on the given progressbar.
81 | void progressbar_update(progressbar *bar, unsigned long value);
82 |
83 | /// Set the label of the progressbar. Note that no rendering is done. The label is simply set so that the next
84 | /// rendering will use the new label. To immediately see the new label, call progressbar_draw.
85 | /// Does not update display or copy the label
86 | void progressbar_update_label(progressbar *bar, const char *label);
87 |
88 | /// Finalize (and free!) a progressbar. Call this when you're done, or if you break out
89 | /// partway through.
90 | void progressbar_finish(progressbar *bar);
91 |
92 | #ifdef __cplusplus
93 | }
94 | #endif
95 |
96 | #endif
97 |
--------------------------------------------------------------------------------
/mobdevim/misc/Progress Bar/statusbar.c:
--------------------------------------------------------------------------------
1 | /**
2 | * \file
3 | * \author Trevor Fountain
4 | * \author Johannes Buchner
5 | * \author Erik Garrison
6 | * \date 2010-2014
7 | * \copyright BSD 3-Clause
8 | *
9 | * statusbar -- a C class (by convention) for displaying progress
10 | * on the command line (to stderr).
11 | */
12 | #include "statusbar.h"
13 |
14 | statusbar *statusbar_new_with_format(const char *label, const char *format)
15 | {
16 | statusbar *new = malloc(sizeof(statusbar));
17 | if(new == NULL) {
18 | return NULL;
19 | }
20 |
21 | new->label = label;
22 | new->start_time = (unsigned int)time(0);
23 | new->format_length = (unsigned int)strlen(format);
24 | new->format = malloc( sizeof(char) * (new->format_length + 1) );
25 | if(new->format == NULL) {
26 | free(new);
27 | return NULL;
28 | }
29 |
30 | strncpy(new->format, format, new->format_length);
31 | new->format_index = 0;
32 | new->last_printed = 0;
33 |
34 | return new;
35 | }
36 |
37 | statusbar *statusbar_new(const char *label)
38 | {
39 | return statusbar_new_with_format(label, "-\\|/");
40 | }
41 |
42 | void statusbar_free(statusbar *bar)
43 | {
44 | // We malloc'd a string, so let's be sure to free it...
45 | free(bar->format);
46 | // ...before we free the struct itself.
47 | free(bar);
48 |
49 | return;
50 | }
51 |
52 | void statusbar_inc(statusbar *bar)
53 | {
54 | bar->format_index++;
55 | if (bar->format_index >= bar->format_length) {
56 | bar->format_index = 0;
57 | }
58 | statusbar_draw(bar);
59 |
60 | return;
61 | }
62 |
63 | void statusbar_draw(statusbar *bar)
64 | {
65 | // Erase the last draw. If anything else has been printed to stderr,
66 | // things are going to look mighty interesting...
67 | for(int i=0; i < bar->last_printed; i++) {
68 | fprintf(stderr,"\b");
69 | }
70 |
71 | fprintf(
72 | stderr,
73 | "%s: %c%n",
74 | bar->label,
75 | bar->format[bar->format_index],
76 | &(bar->last_printed)
77 | );
78 |
79 | return;
80 | }
81 |
82 | void statusbar_finish(statusbar *bar)
83 | {
84 | // Draw one more time, with the actual time to completion.
85 | unsigned int offset = (unsigned int)(time(0) - (bar->start_time));
86 |
87 | // Convert the time to display into HHH:MM:SS
88 | unsigned int h = offset/3600;
89 | offset -= h*3600;
90 | unsigned int m = offset/60;
91 | offset -= m*60;
92 | unsigned int s = offset;
93 |
94 | // Erase the last draw
95 | for(int i=0; i < bar->last_printed; i++) {
96 | fprintf(stderr,"\b");
97 | }
98 |
99 | // Calculate number of spaces for right-justified time to completion
100 | fprintf(stderr,"%s: %3d:%02d:%02d%n",bar->label,h,m,s,&(bar->last_printed));
101 | for(int i=0; i < bar->last_printed; i++) {
102 | fprintf(stderr,"\b");
103 | }
104 |
105 | // Print right-justified
106 | fprintf(stderr,"%s: ",bar->label);
107 | for(int i=0; i < (80 - (bar->last_printed)); i++) {
108 | fprintf(stderr," ");
109 | }
110 | fprintf(stderr,"%3d:%02d:%02d\n",h,m,s);
111 |
112 | // We've finished with this statusbar, so go ahead and free it.
113 | statusbar_free(bar);
114 |
115 | return;
116 | }
117 |
--------------------------------------------------------------------------------
/mobdevim/misc/Progress Bar/statusbar.h:
--------------------------------------------------------------------------------
1 | /**
2 | * \file
3 | * \author Trevor Fountain
4 | * \author Johannes Buchner
5 | * \author Erik Garrison
6 | * \date 2010-2014
7 | * \copyright BSD 3-Clause
8 | *
9 | * statusbar -- a C class (by convention) for displaying indefinite progress
10 | * on the command line (to stderr).
11 | */
12 |
13 | #ifndef STATUSBAR_H
14 | #define STATUSBAR_H
15 |
16 | #include
17 | #include
18 | #include
19 | #include
20 |
21 | #ifdef __cplusplus
22 | extern "C" {
23 | #endif
24 |
25 | /**
26 | * Statusbar data structure (do not modify or create directly)
27 | */
28 | typedef struct _statusbar_t
29 | {
30 | unsigned int start_time;
31 | const char *label;
32 | int format_index;
33 | int format_length;
34 | char *format;
35 | int last_printed;
36 | } statusbar;
37 |
38 | /// Create a new statusbar with the specified label and format string
39 | statusbar *statusbar_new_with_format(const char *label, const char *format);
40 |
41 | /// Create a new statusbar with the specified label
42 | statusbar *statusbar_new(const char *label);
43 |
44 | /// Free an existing progress bar. Don't call this directly; call *statusbar_finish* instead.
45 | void statusbar_free(statusbar *bar);
46 |
47 | /// Increment the given statusbar.
48 | void statusbar_inc(statusbar *bar);
49 |
50 | /// Finalize (and free!) a statusbar. Call this when you're done.
51 | void statusbar_finish(statusbar *bar);
52 |
53 | /// Draw a statusbar to the screen. Don't call this directly,
54 | /// as it's called internally by *statusbar_inc*.
55 | void statusbar_draw(statusbar *bar);
56 |
57 | #ifdef __cplusplus
58 | }
59 | #endif
60 |
61 | #endif
62 |
--------------------------------------------------------------------------------
/mobdevim/misc/helpers.h:
--------------------------------------------------------------------------------
1 | //
2 | // colors.h
3 | // mobdevim
4 | //
5 | // Created by Derek Selander
6 | // Copyright © 2020 Selander. All rights reserved.
7 | //
8 |
9 | #import
10 | #import "NSArray+Output.h"
11 | #import "ExternalDeclarations.h"
12 |
13 | #ifdef __cplusplus
14 | extern "C" {
15 | #endif
16 |
17 | /// Version String
18 | extern const char *version_string;
19 |
20 | /// Program Name
21 | extern const char *program_name;
22 |
23 | /// Usage of program
24 | extern const char *usage;
25 |
26 |
27 | /**
28 | Uses color of the DSCOLOR env var is set or -r option is used
29 | Possible options are:
30 |
31 | cyan
32 | yellow
33 | magenta
34 | red
35 | blue
36 | gray
37 | bold
38 |
39 | You must use the *colorEnd* function to stop using that color
40 | */
41 | typedef enum {
42 | dc_cyan = 0,
43 | dc_yellow,
44 | dc_magenta,
45 | dc_red,
46 | dc_blue,
47 | dc_gray,
48 | dc_bold,
49 | dc_none,
50 | } dc_colors;
51 | const char* dcolor(dc_colors color);
52 |
53 | /// Ends the color option if the DSCOLOR env var is set
54 | char *colorEnd(void);
55 |
56 | /// My printf
57 | void dsprintf(FILE * f, const char *format, ...);
58 |
59 | #define derror(STR_, ...) \
60 | {\
61 | if (!global_options.quiet) {\
62 | if (global_options.verbose) {\
63 | fprintf(stderr, "err: [%s:%d] " STR_, __FILE__, __LINE__, ##__VA_ARGS__);\
64 | } else {\
65 | fprintf(stderr, STR_, ##__VA_ARGS__);\
66 | } \
67 | }\
68 | }
69 |
70 | #define AMDStartService(_D, _S, _C) {\
71 | NSDictionary *_inputDict = @{@"InvalidateOnDetach": @YES, @"CloseOnInvalidate" : @YES, @"UnlockEscrowBag": @YES};\
72 | amd_err err__ = (AMDeviceSecureStartService(_D, _S, _inputDict, _C));\
73 | if (!(*_C)) {\
74 | derror("error: \"%s\" invalid connection to %s\n%s:%s:%d\n", AMDErrorString(err__), [_S UTF8String], __FILE__,__PRETTY_FUNCTION__, __LINE__);\
75 | return err__;\
76 | }\
77 | }
78 |
79 | #define AMDStartService_NOUNLOCK(_D, _S, _C) {\
80 | NSDictionary *_inputDict = @{@"InvalidateOnDetach": @YES, @"CloseOnInvalidate" : @YES };\
81 | amd_err err__ = (AMDeviceSecureStartService(_D, _S, _inputDict, _C));\
82 | if (!(*_C)) {\
83 | derror("error: \"%s\" invalid connection to %s\n%s:%s:%d\n", AMDErrorString(err__), [_S UTF8String], __FILE__,__PRETTY_FUNCTION__, __LINE__);\
84 | return err__;\
85 | }\
86 | }
87 | #define handle_err(_D) { amd_err err__ = ((_D)); if (err__) {derror("%s:%d err: \"%s\" (%d)", __FILE__, __LINE__, AMDErrorString(err__), err__); return err__; }}
88 | void dprint(const char *format, ...);
89 |
90 | /// Enabled by DSDEBUG env var
91 | void dsdebug(const char *format, ...);
92 |
93 | /// Message then die
94 | void ErrorMessageThenDie(const char *message, ...);
95 |
96 | /// Self explanatory, right?... right?
97 | void print_manpage(void);
98 |
99 | /// Makes sure the optarg is valid, exit(1) if false
100 | void assert_opt_arg(void);
101 |
102 |
103 | ///
104 | extern NSString * const kOptionArgumentDestinationPath;
105 |
106 | typedef struct {
107 | AMDeviceRef device;
108 | InterfaceType type;
109 | } DeviceSelection;
110 |
111 | typedef struct {
112 | /// open_program, expects bundleID like com.apple.mobileslideshow
113 | NSString *programBundleID;
114 |
115 | /// open_program, list of args i.e. "-NSBLahblah YES -UIFOOFOO NO"
116 | NSString *programArguments;
117 |
118 | /// springboardservices, i.e. restore, asshole
119 | NSString *springboardCommand;
120 |
121 | /// For -n, the payload path is specified after the bundle ID
122 | NSString *pushNotificationPayloadPath;
123 |
124 | NSString *ddiInstallPath;
125 | NSString *ddiSignatureInstallPath;
126 |
127 | /// Supress output
128 | BOOL quiet;
129 |
130 | /// Used for hunting for a particular device and connection
131 | DeviceSelection deviceSelection;
132 |
133 | NSString *expectedPartialUDID;
134 |
135 | BOOL choose_specific_device;
136 | int verbose;
137 | } option_params;
138 |
139 | extern option_params global_options;
140 |
141 | void String4Interface(InterfaceType interface, char **out_str);
142 | BOOL isWIFIConnected(AMDeviceRef d, NSString *uuid);
143 |
144 | NSString *GetHostUUID(void);
145 |
146 |
147 | #ifdef __cplusplus
148 | }
149 | #endif
150 |
--------------------------------------------------------------------------------
/mobdevim/misc/helpers.m:
--------------------------------------------------------------------------------
1 | //
2 | //
3 | // mobdevim
4 | //
5 | // Created by Derek Selander
6 | // Copyright © 2020 Selander. All rights reserved.
7 | //
8 |
9 | #import "helpers.h"
10 |
11 |
12 | //*****************************************************************************/
13 | #pragma mark - Externals
14 | //*****************************************************************************/
15 |
16 | const char *version_string = "0.0.1";
17 | const char *program_name = "mobdevim";
18 |
19 | const char *usage = "mobdevim [-v] [-l|-l appIdent][-i path_to_app_dir] [-p|-p UUID_PROVSIONPROFILE] [-c] [-C] [-s bundleIdent path] [-f]";
20 |
21 |
22 |
23 |
24 | const char* dcolor(dc_colors color) {
25 | static BOOL useColor = NO;
26 |
27 | static dispatch_once_t onceToken;
28 | dispatch_once(&onceToken, ^{
29 | if (getenv("DSCOLOR")) {
30 | useColor = YES;
31 | }
32 | });
33 | if (!useColor) {
34 | return "";
35 | }
36 | switch (color) {
37 | case dc_cyan:
38 | return "\e[36m";
39 | case dc_yellow:
40 | return "\e[33m";
41 | case dc_magenta:
42 | return "\e[95m";
43 | case dc_red:
44 | return "\e[91m";
45 | case dc_blue:
46 | return "\e[34m";
47 | case dc_gray:
48 | return "\e[90m";
49 | case dc_bold:
50 | return "\e[1m";
51 | break;
52 | default:
53 | assert(0);
54 | break;
55 | }
56 | return "";
57 | }
58 |
59 | char *colorEnd(void) {
60 | static BOOL useColor = NO;
61 | static dispatch_once_t onceToken;
62 | dispatch_once(&onceToken, ^{
63 | if (getenv("DSCOLOR")) {
64 | useColor = YES;
65 | }
66 | });
67 | if (useColor) {
68 | return "\e[0m";
69 | }
70 |
71 | return "";
72 | }
73 |
74 | void dprint(const char *format, ...) {
75 | va_list args;
76 | va_start( args, format );
77 | vfprintf(stdout, format, args );
78 | va_end( args );
79 | }
80 |
81 | void dsprintf(FILE * f, const char *format, ...) {
82 | if (global_options.quiet) {
83 | return;
84 | }
85 | va_list args;
86 | va_start( args, format );
87 | vfprintf(f, format, args );
88 | va_end( args );
89 | }
90 |
91 | void dsdebug(const char *format, ...) {
92 | if (global_options.quiet) { return; }
93 | static dispatch_once_t onceToken;
94 | static BOOL debugFlag = 0;
95 | dispatch_once(&onceToken, ^{
96 | if (getenv("DSDEBUG")) {
97 | debugFlag = YES;
98 | } else {
99 | debugFlag = NO;
100 | }
101 | });
102 |
103 | if (debugFlag) {
104 | va_list args;
105 | va_start( args, format);
106 | vfprintf(stdout, format, args );
107 | va_end( args );
108 | }
109 |
110 | }
111 |
112 | void String4Interface(InterfaceType interface, char **out_str) {
113 | InterfaceType type = interface;//global_options.deviceSelection.type;
114 |
115 | switch (type) {
116 | case InterfaceTypeYOLODontCare:
117 | *out_str = "Any";
118 | break;
119 | case InterfaceTypeUSB:
120 | *out_str = "USB";
121 | break;
122 | case InterfaceTypeWIFI:
123 | *out_str = "WIFI";
124 | break;
125 | default:
126 | *out_str = NULL;
127 | break;
128 | }
129 |
130 | }
131 |
132 | void ErrorMessageThenDie(const char *message, ...) {
133 | if (!global_options.quiet) {
134 | va_list args;
135 | va_start(args, message);
136 | vfprintf(stderr, message, args);
137 | va_end( args );
138 | }
139 | exit(1);
140 | }
141 |
142 |
143 | void print_manpage(void) {
144 | dprint("\nName\n %s -- (mobiledevice-improved) Interact with an iOS device (compiled %s, %s)\n\n"
145 | " The mobdevim utlity interacts with your plugged in iOS device over USB using Apple's\n"
146 | " private framework, MobileDevice.\n\n"
147 | " The options are as follows:\n"
148 | , program_name, __DATE__, __TIME__);
149 |
150 |
151 | dprint("\t-F\t# List all available connections, or connect to a specific device\n\n");
152 | dprint("\t\tmobdevim -F # List all known devices\n");
153 | dprint("\t\tmobdevim -F 00234 # Connect to first device that has a UDID containing 00234\n");
154 | dprint("\t\tmobdevim -F\\? # Check for devices\n\n");
155 |
156 | dprint("\t\tmobdevim -U # Prefer connection over USB\n\n");
157 |
158 | dprint("\t\tmobdevim -W # Prefer connection over WIFI\n\n");
159 |
160 | dprint("\t-f\t# Get device info to a connected device (defaults to first USB connection)\n\n");
161 |
162 | dprint("\t-g\t# Get device logsn\n"
163 | "\t\tmobdevim -g # list logs\n"
164 | "\t\tmobdevim -g ./crashquick.ips # print the log out\n"
165 | "\t\tmobdevim -g ./name.ips -D # print the log out and delete\n"
166 | "\t\tmobdevim -g ALL # pulls all logs into /tmp and deletes on device\n\n");
167 |
168 | dprint("\t-l\t# List app information\n\n"
169 | "\t\tmobdevim -l # List all apps\n"
170 | "\t\tmobdevim -l com.example.test # Get detailed info about app, com.example.test\n"
171 | "\t\tmobdevim -l com.example.test Entitlements # List \"Entitlements\" key from com.example.test\n\n");
172 |
173 | dprint("\t-r\t# Remove file\n\n"
174 | "\t\tmobdevim -r /fullpath/to/file # removes file (sandbox permitting)\n\n");
175 |
176 | dprint("\t-y\t# Yoink sandbox content\n\n"
177 | "\t\tmobdevim -y com.example.test # Yoink contacts from app\n\n");
178 |
179 | dprint("\t-s\t# Send content to device (use content from yoink command)\n\n"
180 | "\t\tmobdevim -s com.example.test /tmp/com.example.test # Send contents in /tmp/com.example.test to app\n\n");
181 |
182 | dprint("\t-i\t# Install application (expects path to bundle)\n\n");
183 | dprint("\t-I\t# Install/mount a DDI (via Xcode subdir or repos like https://github.com/mspvirajpatel/Xcode_Developer_Disk_Images\n\n"
184 | "\t\tmobdevim -I /path/to/ddi.signature /path/to/ddi.dmg # Install DDI\n\n");
185 | dprint("\t-M\t# unMount a DDI (via Xcode subdir or repos like https://github.com/mspvirajpatel/Xcode_Developer_Disk_Images\n\n"
186 | "\t\tmobdevim -M # Unmount an alreaady mounted dev DDI\n\n");
187 |
188 | dprint("\t-u\t# Uninstall application, expects bundleIdentifier\n\n"
189 | "\t\tmobdevim -u com.example.test # Uninstall app\n\n");
190 |
191 | dprint("\t-w\t# Connect device to WiFi mode\n\n"
192 | "\t\tmobdevim -w # Connect device to wifi for this computer\n"
193 | "\t\tmobdevim -w uuid_here # Connect device to wifi for UUID\n"
194 | "\t\tmobdevim -w off # Disable device wifi\n"
195 | "\t\tmobdevim -w uuid # Display the computer's host uuid\n\n");
196 |
197 | dprint("\t-S\t# Arrange SpringBoard icons\n\n"
198 | "\t\tmobdevim -S # Get current SpringBoard icon layout\n"
199 | "\t\tmobdevim -S /path/to/plist # Set SpringBoard icon layout from plist file\n"
200 | "\t\tmobdevim -S asshole # Set SpringBoard icon layout to asshole mode\n"
201 | "\t\tmobdevim -S restore # Restore SpringBoard icon layout (if backup was created)\n\n");
202 |
203 | dprint("\t-L\t# Simulate location (requires DDI)\n\n"
204 | "\t\tmobdevim -L 0 0 # Remove location simulation\n"
205 | "\t\tmobdevim -L 40.7128 -73.935242 # Simulate phone in New York\n\n");
206 |
207 | dprint("\t-c\t# Dump out the console information. Use ctrl-c to terminate\n\n");
208 | dprint("\t-C\t# Get developer certificates on device\n\n");
209 | dprint("\t-p\t# Display running processes on the device (requiers DDI)\n\n");
210 |
211 | dprint("\t-k\t# Kill a process (requiers DDI)\n\n");
212 | dprint("\t\tTODO");
213 | dprint("\t-b\t# Backup device\n\n");
214 |
215 |
216 | dprint("\t-P\t# Display developer provisioning profile info\n\n"
217 | "\t\tmobdevim -P # List all installed provisioning profiles\n"
218 | "\t\tmobdevim -P b68410a1-d825-4b7c-8e5d-0f76a9bde6b9 # Get detailed provisioning UUID info\n\n");
219 |
220 | dprint("\t-o\t# Open application (requires DDI)\n\n"
221 | "\t\tmobdevim -o com.reverse.domain # open app\n"
222 | "\t\tmobdevim -o com.reverse.domain -A \"Some args here\" -V AnEnv=EnValue -V A=B # open app with launch args and env vars\n\n");
223 |
224 |
225 | dprint("\t-R\t# Use color\n\n"
226 | "\t-Q\t# Quiet mode, ideal for limiting output or checking if a value exists based upon return status\n\n");
227 |
228 | dprint("Environment variables:\
229 | \tDSCOLOR - Use color (same as -R)\n\n\
230 | \tDSDEBUG - verbose debugging\n\n\
231 | \tDSPLIST - Display output in plist form (mobdevim -l com.test.example)\n\n\
232 | \tOS_ACTIVITY_DT_MODE - Combine w/ DSDEBUG to enable MobileDevice logging\n");
233 | }
234 |
235 | __attribute__((visibility("hidden")))
236 | void assert_opt_arg(void) {
237 | return;
238 | // if (!optarg) {
239 | //// print_manpage();
240 | //// exit(5);
241 | // }
242 | }
243 |
244 | NSString *GetHostUUID(void) {
245 | CFUUIDBytes hostuuid;
246 | const struct timespec tmspec = { 0 };
247 | gethostuuid(&hostuuid.byte0, &tmspec);
248 | CFUUIDRef ref = CFUUIDCreateFromUUIDBytes(kCFAllocatorDefault, hostuuid);
249 | return CFBridgingRelease(CFUUIDCreateString(kCFAllocatorDefault, ref));
250 | }
251 |
252 | BOOL isWIFIConnected(AMDeviceRef d, NSString *uuid) {
253 | NSNumber *isWifiDebugged = AMDeviceCopyValue(d, @"com.apple.mobile.wireless_lockdown", @"EnableWifiDebugging", 0);
254 | id wirelessHosts = AMDeviceCopyValue(d, @"com.apple.xcode.developerdomain", @"WirelessHosts", 0);
255 | if (![isWifiDebugged boolValue]) {
256 | return NO;
257 | }
258 |
259 | if ([wirelessHosts containsObject:uuid]) {
260 | return YES;
261 | }
262 |
263 | return NO;
264 | }
265 |
266 | /// Options used for getopt_long
267 | option_params global_options = {};
268 |
269 | NSString * const kOptionArgumentDestinationPath = @"com.selander.destination";
270 |
271 |
272 | char* InterfaceTypeString(InterfaceType type) {
273 | switch (type) {
274 | case InterfaceTypeYOLODontCare:
275 | return "Unknown";
276 | case InterfaceTypeUSB:
277 | return "USB";
278 | case InterfaceTypeWIFI:
279 | return "WIFI";
280 | default:
281 | break;
282 | }
283 | }
284 |
285 |
--------------------------------------------------------------------------------
/mobdevim/mobdevim.l:
--------------------------------------------------------------------------------
1 | .Dd __DATE__
2 | .Dt mobdevim 1
3 | .Os Darwin
4 | .Sh NAME
5 | .Nm mobdevim
6 | .Nd (mobiledevice-improved) Query an iOS device
7 | .Sh SYNOPSIS
8 | .Nm
9 | .Op option...
10 | .Ar
11 | .Sh DESCRIPTION
12 | The mobdevim utlity interacts with your plugged in iOS device over USB/WIFI using Apple's MobileDevice framework and friends
13 | .Sh OPTIONS
14 | .Bl -tag -width indent
15 | .It Fl c, -color
16 | Adds color to output
17 | .It Fl l, -library
18 | Instead of dumping symbols, search all procs for library
19 | .It Fl O, -opcs
20 | Dump the DYLD opcodes used to bind external symbols at load time
21 | .It Fl f, -filter Ar FilterWord
22 | Specify classes to filter by (case insensitive, can be used multiple times)
23 | .It Fl a, -arch Ar architecture
24 | Specify the arichtecture if file is FAT. Understands x86_64h, x86_64, arm64, arm64e
25 | .It Fl u, -undefined
26 | Only display undefined (externally referenced) symbols or classes
27 | .It Fl U, -defined
28 | Only display defined (internally implemented) symbols or classes
29 | .It Fl v, -verbose
30 | Specifies the verbosity level. The -v option can be used multiple times, while the long argument sets the exact level 0-5. Kind of like codesign(1)'s verbosity that everyone complains about...
31 | .It Fl -objc
32 | Dump the Objective-C classes
33 | .It Fl -swift
34 | Dump the Swift type descriptors (classes, structs, enums)
35 | .It Fl s
36 | Sets mode to Swift mode and verbosity to level 4
37 | .It Fl h, -help
38 | Print out this beautiful, helpful document
39 | .El
40 | .Sh EXAMPLES
41 | List ObjC internal/external classes referenced/implemented by vmmap:
42 | .Dl dsdump --objc $(which vmmap)
43 | .Pp
44 | List all alive processes that have the MobileDevice loaded
45 | .Dl sudo dsdump -l /S*/L*/P*/MobileDevice.framework/MobileDevice
46 | .Pp
47 | List the Objective-C external classes called by vmmap:
48 | .Dl dsdump --objc $(which vmmap) -u
49 | .Pp
50 | List the Objective-C internal classes implemented by vmmap:
51 | .Dl dsdump --objc $(which vmmap) -U
52 | .Pp
53 | Perform an Objective-C "class-dump" in color of vmmap
54 | .Dl dsdump --objc $(which vmmap) -U -vvvc
55 | .Pp
56 | Thoroughly dump the Swift content in color in the Console app
57 | .Dl dsdump --swift /Applications/Utilities/Console.app/Contents/MacOS/Console -cvvvv
58 | .Pp
59 | .Sh VERBOSITY
60 | dsdump can output a range of verbosity between the 3 different modes (--sym, --swift, --objc). The verbosity level can be set by the long form (--verbose=3) or by specifying a count via short form (-vvv). The breakdown of these levels are shown below:
61 | .Pp
62 | --sym:
63 | .Dl 0. Print symbol
64 | .Dl 1. 0 + library path or Mach-O section
65 | .Dl 2. 1 + fullpath to library
66 | .Dl 3. 2 + nlist struct output
67 | .Dl 4. Same as 3... for now
68 | .Dl 5. Same as 3... for now
69 | .Pp
70 | --swift:
71 | .Dl 0. List swift types
72 | .Dl 1. 0 + Parent classes
73 | .Dl 2. 1 + Protocols
74 | .Dl 3. 2 + Swift "type dump"
75 | .Dl 4. 3 + Extended type dump, ObjC bridge methods
76 | .Dl 5. 4 + Commenting in methods
77 | .Pp
78 | --objc:
79 | .Dl 0. List Objective-C classes
80 | .Dl 1. 0 + Parent classes & library basename for external
81 | .Dl 2. 1 + Fullpath to libraries for external + protocols
82 | .Dl 3. 2 + Objective-C "class dump"
83 | .Dl 4. 3 + Print properties
84 | .Dl 5. 4 + Print ivars & offsets
85 | .Pp
86 | .Sh ENVIRONMENT
87 | .Pp
88 | .Bl -tag -width indent
89 | .Ev DSCOLOR
90 | Enables color. Alternatively, use -c
91 | .Pp
92 | .Ev ARCH
93 | .Ar
94 | Specify the architecture if inspecting a FAT executable, Alternatively use --arch
95 | .El
96 | .Sh SEE ALSO
97 | .Xr nm 1 ,
98 | .Xr objdump 1 ,
99 | .Xr vmmap 1
100 | .Sh BUGS
101 | There's a situation where occassionally dsdump will think the parent class is a RO_ROOT where it will in fact won't be. I'll print this out for now so I can hunt it down
102 | .Pp
103 | ARM64e still needs some luv, especially on the Swift side, especially with Protocols... and not crashing
104 | .Sh AUTHORS
105 | .An "Derek Selander"
106 | .Mt @LOLgrep
107 |
108 |
--------------------------------------------------------------------------------
/mobdevim/notifications/notifications.h:
--------------------------------------------------------------------------------
1 | //
2 | // springboardservices.h
3 | // mobdevim
4 | //
5 | // Created by Derek Selander
6 | // Copyright © 2020 Selander. All rights reserved.
7 | //
8 |
9 | #import
10 | #import "ExternalDeclarations.h"
11 | #import "helpers.h"
12 |
13 | /// The application ID to remove files from, WILL NOT WORK IF YOU DON'T HAVE THE MATCHING PP/CERTS!
14 | //extern NSString * const kSBSFileBundleID;
15 |
16 |
17 | /// The path to remove a file on the remote device
18 | //extern NSString * const kSBCommand;
19 |
20 | /// Copies the Library, Documents, Caches directories over to the computer
21 | int notification_proxy(AMDeviceRef d, NSDictionary *options);
22 |
--------------------------------------------------------------------------------
/mobdevim/notifications/notifications.mm:
--------------------------------------------------------------------------------
1 | //
2 | // springboardservices.m
3 | // mobdevim
4 | //
5 | // Created by Derek Selander
6 | // Copyright © 2020 Selander. All rights reserved.
7 | //
8 |
9 | #import "springboardservices.h"
10 | #include
11 | #include
12 | #include
13 |
14 |
15 | //NSString * const kSBSFileBundleID = @"com.selander.springboard_services.bundleid";
16 |
17 | //NSString * const kSBCommand = @"com.selander.springboard_services.command";
18 |
19 | //
20 | // open_program.m
21 | // mobdevim
22 | //
23 | //
24 | // Copyright © 2020 Selander. All rights reserved.
25 | //
26 |
27 | #import "open_program.h"
28 | #import "../misc/InstrumentsPlugin.h"
29 | #import "../Debug Application/debug_application.h"
30 | #import
31 |
32 | //static void preload() {
33 | // XRUniqueIssueAccumulator *responder = [XRUniqueIssueAccumulator new];
34 | // XRPackageConflictErrorAccumulator *accumulator = [[XRPackageConflictErrorAccumulator alloc] initWithNextResponder:responder];
35 | // [DVTDeveloperPaths initializeApplicationDirectoryName:@"Instruments"];
36 | //
37 | // void (*PFTLoadPlugin)(id, id) = dlsym(RTLD_DEFAULT, "PFTLoadPlugins");
38 | // PFTLoadPlugin(nil, accumulator);
39 | //}
40 |
41 | #ifdef __cplusplus
42 | extern "C" {
43 | #endif
44 |
45 | static NSDictionary* handleNoFilepathGiven() {
46 | if (global_options.pushNotificationPayloadPath) {
47 | NSDictionary* payload = [NSDictionary dictionaryWithContentsOfFile:global_options.pushNotificationPayloadPath];
48 | if (!payload) {
49 | printf("Invalid payload from \"%s\"\n", global_options.pushNotificationPayloadPath.UTF8String);
50 | exit(1);
51 | }
52 | return payload;
53 | }
54 | NSString *path = @"/tmp/apns_payload.plist";
55 | if (![[NSFileManager defaultManager] fileExistsAtPath:path]) {
56 | NSDictionary * tmpDictionary = @{@"aps" :
57 | @{@"alert" : @{ @"title": @"Alert title",
58 | @"body" : @"Alert body" },
59 | @"badge" : @1,
60 | @"sound" : @"default" }};
61 |
62 | [tmpDictionary writeToFile:path atomically:YES];
63 | printf("No payload dictionary given! Created one at \"%s\", see https://developer.apple.com/library/archive/documentation/NetworkingInternet/Conceptual/RemoteNotificationsPG/CreatingtheNotificationPayload.html\n", path.UTF8String);
64 | } else {
65 | printf("No payload dictionary given! See https://developer.apple.com/library/archive/documentation/NetworkingInternet/Conceptual/RemoteNotificationsPG/CreatingtheNotificationPayload.html\n");
66 | }
67 | exit(1);
68 | }
69 |
70 | int notification_proxy2(AMDeviceRef d, NSDictionary *options) {
71 | NSString *name = global_options.programBundleID;
72 | NSDictionary *dict = nil;
73 | mach_error_t err = AMDeviceLookupApplications(d, @{ @"ReturnAttributes": @YES, @"ShowLaunchProhibitedApps" : @YES }, &dict);
74 | if (err) {
75 | derror("Err looking up application, exiting...\n");
76 | exit(1);
77 | }
78 |
79 | if (!name) {
80 | derror("%sCouldn't find the bundleIdentifier \"%s\", try listing all bundleIDs with %s%smobdevim -l%s\n", dcolor(dc_yellow), [name UTF8String], colorEnd(), dcolor(dc_bold), colorEnd());
81 | return 1;
82 | }
83 |
84 | NSDictionary *appParams = [dict objectForKey:name];
85 | NSString *path = appParams[@"Path"];
86 | if (!path) {
87 | derror("couldn't get the path for app %s\n", name.UTF8String);
88 | return 1;
89 | }
90 | NSString *bundleID = appParams[@"CFBundleIdentifier"];
91 | if (!bundleID) {
92 | derror("couldn't get the bundleID\n");
93 | return 1;
94 | }
95 | AMDServiceConnectionRef serviceConnection = nil;
96 | AMDStartService(d, @"com.apple.mobile.notification_proxy", &serviceConnection);
97 |
98 | // gets the max counts for what springboard will allow
99 | handle_err(AMDServiceConnectionSendMessage(serviceConnection, @{ @"command" : @"getHomeScreenIconMetrics" }, kCFPropertyListXMLFormat_v1_0));
100 |
101 | NSDictionary *metricDict = nil;
102 | handle_err(AMDServiceConnectionReceiveMessage(serviceConnection, &metricDict, nil));
103 |
104 | return 0;
105 | }
106 |
107 | int notification_proxy(AMDeviceRef d, NSDictionary *options) {
108 | // if (!getenv("YAYYAY")) {
109 | // return 0;
110 | // }
111 | NSString *name = global_options.programBundleID;
112 | NSDictionary *dict = nil;
113 | NSDictionary *payloadDictionary = handleNoFilepathGiven();
114 | mach_error_t err = AMDeviceLookupApplications(d, @{ @"ReturnAttributes": @YES, @"ShowLaunchProhibitedApps" : @YES }, &dict);
115 | if (err) {
116 | dsprintf(stderr, "Err looking up application, exiting...\n");
117 | exit(1);
118 | }
119 |
120 | if (!name) {
121 | dsprintf(stderr, "%sCouldn't find the bundleIdentifier \"%s\", try listing all bundleIDs with %s%smobdevim -l%s\n", dcolor(dc_yellow), [name UTF8String], colorEnd(), dcolor(dc_bold), colorEnd());
122 | return 1;
123 | }
124 |
125 | NSDictionary *appParams = [dict objectForKey:name];
126 | NSString *path = appParams[@"Path"];
127 | if (!path) {
128 | dsprintf(stderr, "couldn't get the path for app %s\n", name.UTF8String);
129 | return 1;
130 | }
131 | NSString *bundleID = appParams[@"CFBundleIdentifier"];
132 | if (!bundleID) {
133 | dsprintf(stderr, "couldn't get the bundleID\n");
134 | return 1;
135 | }
136 |
137 | // preload();
138 | XRMobileDevice* device = [[NSClassFromString(@"XRMobileDevice") alloc] initWithDevice:d];
139 | if (!device) {
140 | dsprintf(stderr, "couldn't maintain a device connection\n");
141 | return 1;
142 | }
143 | id connection = [device connection];
144 | NSString *identifier = @"com.apple.instruments.server.services.processcontrol.feature.deviceio";
145 | int version = [connection remoteCapabilityVersion:identifier];
146 | if (!version) {
147 | printf("Couldn't find capability on device!\n");
148 | [connection cancel];
149 | return 1;
150 | }
151 |
152 | id springboardChannel = [connection makeChannelWithIdentifier:identifier];
153 | // id springboardChannel = [connection makeChannelWithIdentifier:@"com.apple.instruments.server.services.processcontrol.feature.deviceio"];
154 | // com.apple.instruments.server.services.processcontrol.feature.deviceio
155 | // [DTXConnection remoteCapabilityVersion:@"com.apple.instruments.server.services.processcontrol.capability.signal"];
156 |
157 |
158 | // id channel = [device deviceInfoService];
159 | // id processControlChannel = [device defaultProcessControlChannel];
160 | id msg = [NSClassFromString(@"DTXMessage") messageWithSelector:NSSelectorFromString(@"processIdentifierForBundleIdentifier:") objectArguments: name, nil];
161 |
162 | dispatch_group_t group = dispatch_group_create();
163 | dispatch_group_enter(group);
164 | __block NSNumber* pidNumber = nil;
165 | [springboardChannel sendMessageSync:msg replyHandler:^(DTXMessage *response, int extra) {
166 | if ([response error]) {
167 | printf("%s\n", response.error.description.UTF8String);
168 | }
169 | pidNumber = response.payloadObject;
170 | dispatch_group_leave(group);
171 | }];
172 | dispatch_group_wait(group, 10);
173 | if (!pidNumber) {
174 | printf("Couldn't find pid for \"%s\"", name.UTF8String);
175 | [connection cancel];
176 | return 1;
177 | }
178 |
179 | // We here if we have a valid PID
180 | simulateNotificationForBundleID:payload:withError:
181 | dispatch_group_enter(group);
182 | NSDictionary *payload = @{@"PCEventType" : @"SimulateNotification",
183 | @"BundleIdentifier": name,
184 | @"NotificationPayload" : payloadDictionary};
185 | id msg_2 = [NSClassFromString(@"DTXMessage") messageWithSelector:NSSelectorFromString(@"sendProcessControlEvent:toPid:") objectArguments: [NSKeyedArchiver archivedDataWithRootObject:payload], pidNumber, nil];
186 | [springboardChannel sendMessageSync:msg_2 replyHandler:^(DTXMessage *response, int extra) {
187 | if ([response error]) {
188 | printf("%s\n", response.error.description.UTF8String);
189 | }
190 | dispatch_group_leave(group);
191 | }];
192 |
193 | dispatch_group_wait(group, 10);
194 |
195 |
196 | return 0;
197 | }
198 |
199 | #ifdef __cplusplus
200 | }
201 | #endif
202 |
--------------------------------------------------------------------------------
/mobdevim/on_load.m:
--------------------------------------------------------------------------------
1 | //
2 | // OnLoad.c
3 | // mobdevim
4 | //
5 | //
6 | // Copyright © 2020 Selander. All rights reserved.
7 | //
8 |
9 | #import
10 | #import
11 | #import "Helpers.h"
12 | #import
13 | #import
14 |
15 | __attribute__((constructor)) void onLoad(void) {
16 | if (getenv("DSPLIST")) {
17 | global_options.quiet = YES;
18 | }
19 |
20 | /*
21 | MobileDevice has a function called mobdevlog that logs what's happening
22 | It's gated by a global var called "gLogLevel"
23 |
24 | This gLogLevel doesn't have external linkage (i.e. can't use dlsym), but
25 | does have the symbol name present. It is an aligned int, so jump by 4 to find it
26 | */
27 | if (getenv("DSDEBUG")) {
28 | dsdebug("Verbose mode enabled...\n");
29 | unsigned long size = 0;
30 | uint32_t* data = (uint32_t*)getsectdatafromFramework("MobileDevice", "__DATA", "__data", &size);
31 | for (int i = 0; i < size / sizeof(uint32_t); i++) {
32 | Dl_info info;
33 | dladdr(&data[i], &info);
34 | if (strcmp(info.dli_sname, "gLogLevel") == 0) {
35 | // Let's crank it ALLLLLL THE WAY UP
36 | *(uint32_t*)info.dli_saddr = INT32_MAX - 1;
37 | break;
38 | }
39 | }
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/mobdevim/process/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2018 Hex-Rays
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/mobdevim/process/cftypes.h:
--------------------------------------------------------------------------------
1 | #ifndef CFTYPES_H
2 | #define CFTYPES_H
3 |
4 | #include
5 | #include "common.h"
6 |
7 | //------------------------------------------------------------------------------
8 | // convert the given CFString to an STL string
9 | string_t to_stlstr(CFStringRef ref);
10 |
11 | //------------------------------------------------------------------------------
12 | // get a human readable description of the given CF object
13 | string_t get_description(CFTypeRef ref);
14 |
15 | //------------------------------------------------------------------------------
16 | // serialize a CF object
17 | void archive(bytevec_t *buf, CFTypeRef ref);
18 |
19 | void archive(bytevec_t *buf, id ref);
20 |
21 | //------------------------------------------------------------------------------
22 | // deserialize a CF object
23 | id unarchive(const uint8_t *buf, size_t bufsize);
24 |
25 | //------------------------------------------------------------------------------
26 | // deserialize an array of CF objects
27 | NSArray* deserialize(const uint8_t *buf, size_t bufsize, string_t *errbuf = NULL);
28 |
29 | #endif // CFTYPES_H
30 |
--------------------------------------------------------------------------------
/mobdevim/process/cftypes.mm:
--------------------------------------------------------------------------------
1 | #include
2 | #include "cftypes.h"
3 | #pragma clang diagnostic push
4 | #pragma clang diagnostic ignored "-Wdeprecated-declarations"
5 | //------------------------------------------------------------------------------
6 | string_t to_stlstr(CFStringRef ref)
7 | {
8 | if ( ref == NULL )
9 | return "";
10 |
11 | CFIndex length = CFStringGetLength(ref);
12 | if ( length <= 0 )
13 | return "";
14 |
15 | CFIndex bufsize = CFStringGetMaximumSizeForEncoding(length, kCFStringEncodingUTF8) + 1;
16 | char *buf = (char *)calloc(bufsize, 1);
17 |
18 | string_t ret;
19 | if ( CFStringGetCString(ref, buf, bufsize, kCFStringEncodingUTF8) )
20 | ret = buf;
21 |
22 | free(buf);
23 | return ret;
24 | }
25 |
26 | //------------------------------------------------------------------------------
27 | string_t get_description(CFTypeRef ref)
28 | {
29 | CFStringRef desc = CFCopyDescription(ref);
30 | string_t ret = to_stlstr(desc);
31 | CFRelease(desc);
32 | return ret;
33 | }
34 |
35 | //-----------------------------------------------------------------------------
36 | void archive(bytevec_t *buf, CFTypeRef ref)
37 | {
38 |
39 | id object = (__bridge id)ref;
40 | NSData *data = [NSKeyedArchiver archivedDataWithRootObject:object];
41 | const void *bytes = [data bytes];
42 | int length = (int)[data length];
43 | append_v(*buf, bytes, length);
44 |
45 | }
46 |
47 | void archive(bytevec_t *buf, id ref)
48 | {
49 |
50 | id object = ref;
51 | NSData *data = [NSKeyedArchiver archivedDataWithRootObject:object];
52 | const void *bytes = [data bytes];
53 | int length = (int)[data length];
54 | append_v(*buf, bytes, length);
55 |
56 | }
57 |
58 | //-----------------------------------------------------------------------------
59 | id unarchive(const uint8_t *buf, size_t bufsize)
60 | {
61 |
62 | NSData *data = [NSData dataWithBytesNoCopy:(void *)buf length:bufsize freeWhenDone:false];
63 | id object = [NSKeyedUnarchiver unarchiveObjectWithData:data];
64 | return object;
65 |
66 | }
67 |
68 | //-----------------------------------------------------------------------------
69 | NSArray* deserialize(
70 | const uint8_t *buf,
71 | size_t bufsize,
72 | string_t *errbuf)
73 | {
74 | if ( bufsize < 16 )
75 | {
76 | sprnt(errbuf, "Error: buffer of size 0x%lx is too small for a serialized array", bufsize);
77 | return NULL;
78 | }
79 |
80 | uint64_t size = *((uint64_t *)buf+1);
81 | if ( size > bufsize )
82 | {
83 | sprnt(errbuf, "size of array object (%llx) is larger than total length of data (%lx)", size, bufsize);
84 | return NULL;
85 | }
86 |
87 | NSMutableArray *array = [NSMutableArray array];
88 | // CFMutableArrayRef array = CFArrayCreateMutable(NULL, 0, NULL);
89 |
90 | uint64_t off = sizeof(uint64_t) * 2;
91 | uint64_t end = off + size;
92 |
93 | while ( off < end )
94 | {
95 | int length = 0;
96 | int type = *((int *)(buf+off));
97 | off += sizeof(int);
98 |
99 | id ref = NULL;
100 |
101 | switch ( type )
102 | {
103 | case 2:
104 | // archived object
105 | length = *((int *)(buf+off));
106 | off += sizeof(int);
107 | ref = unarchive(buf+off, length);
108 | break;
109 |
110 | case 3:
111 | case 5:
112 | // 32-bit int
113 | ref = @(*(int32_t*)(buf + off)); // CFNumberCreate(NULL, kCFNumberSInt32Type, buf+off);
114 | length = 4;
115 | break;
116 |
117 | case 4:
118 | case 6:
119 | // 64-bit int
120 | ref = @(*(int64_t*)(buf + off)); // CFNumberCreate(NULL, kCFNumberSInt64Type, buf+off);
121 | length = 8;
122 | break;
123 |
124 | case 10:
125 | // dictionary key. for arrays, the keys are empty and we ignore them
126 | continue;
127 |
128 | default:
129 | // there are more. we will deal with them as necessary
130 | break;
131 | }
132 |
133 | if ( ref == NULL )
134 | {
135 | sprnt(errbuf, "invalid object at offset %llx, type: %d\n", off, type);
136 | return NULL;
137 | }
138 |
139 |
140 | [array addObject:ref];
141 | // CFArrayAppendValue(array, ref);
142 | off += length;
143 | // CFRelease(ref);
144 | }
145 |
146 | return array;
147 | }
148 |
149 | #pragma clang diagnostic pop
150 |
--------------------------------------------------------------------------------
/mobdevim/process/common.h:
--------------------------------------------------------------------------------
1 | #ifndef COMMON_H
2 | #define COMMON_H
3 |
4 | #include
5 | #include
6 | #include
7 | #include
8 | #include
9 |
10 | typedef std::string string_t;
11 | typedef std::vector bytevec_t;
12 |
13 | void append_d(bytevec_t &out, uint32_t num);
14 | void append_q(bytevec_t &out, uint64_t num);
15 | void append_b(bytevec_t &out, const bytevec_t &bv);
16 | void append_v(bytevec_t &out, const void *v, size_t len);
17 |
18 | void sprnt(string_t *out, const char *format, ...);
19 | void vsprnt(string_t *out, const char *format, va_list va);
20 |
21 | #endif // COMMON_H
22 |
--------------------------------------------------------------------------------
/mobdevim/process/common.mm:
--------------------------------------------------------------------------------
1 | #include "common.h"
2 |
3 | #define MAXSTR 1024
4 |
5 | //-----------------------------------------------------------------------------
6 | void append_v(bytevec_t &out, const void *v, size_t len)
7 | {
8 | const uint8_t *begin = (const uint8_t *)v;
9 | const uint8_t *end = begin + len;
10 | out.insert(out.end(), begin, end);
11 | }
12 |
13 | //-----------------------------------------------------------------------------
14 | void append_d(bytevec_t &out, uint32_t num)
15 | {
16 | append_v(out, &num, sizeof(num));
17 | }
18 |
19 | //-----------------------------------------------------------------------------
20 | void append_q(bytevec_t &out, uint64_t num)
21 | {
22 | append_v(out, &num, sizeof(num));
23 | }
24 |
25 | //-----------------------------------------------------------------------------
26 | void append_b(bytevec_t &out, const bytevec_t &bv)
27 | {
28 | append_v(out, bv.data(), bv.size());
29 | }
30 |
31 | //-----------------------------------------------------------------------------
32 | void vsprnt(string_t *out, const char *format, va_list va)
33 | {
34 | char buf[MAXSTR];
35 | if ( out != NULL && vsnprintf(buf, sizeof(buf), format, va) > 0 )
36 | *out = buf;
37 | }
38 |
39 | //-----------------------------------------------------------------------------
40 | void sprnt(string_t *out, const char *format, ...)
41 | {
42 | va_list va;
43 | va_start(va, format);
44 | vsprnt(out, format, va);
45 | va_end(va);
46 | }
47 |
--------------------------------------------------------------------------------
/mobdevim/process/ios_instruments_client.h:
--------------------------------------------------------------------------------
1 | #ifndef IOS_INSTRUMENTS_CLIENT
2 | #define IOS_INSTRUMENTS_CLIENT
3 |
4 | #include
5 | #include "cftypes.h"
6 | #include "mobile_device.h"
7 |
8 | //-----------------------------------------------------------------------------
9 | struct DTXMessageHeader
10 | {
11 | uint32_t magic;
12 | uint32_t cb;
13 | uint16_t fragmentId;
14 | uint16_t fragmentCount;
15 | uint32_t length;
16 | uint32_t identifier;
17 | uint32_t conversationIndex;
18 | uint32_t channelCode;
19 | uint32_t expectsReply;
20 | };
21 |
22 | //-----------------------------------------------------------------------------
23 | struct DTXMessagePayloadHeader
24 | {
25 | uint32_t flags;
26 | uint32_t auxiliaryLength;
27 | uint64_t totalLength;
28 | };
29 |
30 | //------------------------------------------------------------------------------
31 | // helper class for serializing method arguments
32 | class message_aux_t
33 | {
34 | bytevec_t buf;
35 |
36 | public:
37 | void append_int(int32_t val);
38 | void append_long(int64_t val);
39 | void append_obj(CFTypeRef obj);
40 | void append_obj(id obj);
41 |
42 | void get_bytes(bytevec_t *out) const;
43 | };
44 |
45 | bool print_proclist(am_device_service_connection *conn);
46 | bool launch_application(am_device_service_connection *conn, const char *_bid, NSArray* args, NSDictionary* env);
47 | bool perform_handshake(am_device_service_connection *conn);
48 | bool kill(am_device_service_connection *conn, int pid);
49 | void* load_extern_implementation(void);
50 | NSArray* get_proclist_matching_name(am_device_service_connection *conn, NSString *searchedName);
51 | #endif // IOS_INSTRUMENTS_CLIENT
52 |
--------------------------------------------------------------------------------
/mobdevim/process/makefile:
--------------------------------------------------------------------------------
1 | T = ios_instruments_client
2 | O = obj
3 | all: $(T)
4 | .PHONY: all clean
5 |
6 | CFLAGS = -g -O0 -mmacosx-version-min=10.9
7 |
8 | ifeq ($(wildcard $(O)/.),)
9 | $(shell mkdir -p 2>/dev/null $(O))
10 | endif
11 |
12 | MAIN = $(O)/$(T).o
13 | COMMON = $(O)/common.o
14 | CFTYPES = $(O)/cftypes.o
15 | MD = $(O)/mobile_device.o
16 |
17 | $(T): $(MAIN) $(COMMON) $(CFTYPES) $(MD)
18 | g++ $(CFLAGS) -o $@ $^ -lobjc -framework Foundation -framework CoreFoundation
19 |
20 | $(MAIN): $(T).cpp $(T).h cftypes.h common.h mobile_device.h
21 | g++ $(CFLAGS) -c -o $@ $<
22 |
23 | $(COMMON): common.cpp common.h
24 | g++ $(CFLAGS) -c -o $@ $<
25 |
26 | $(CFTYPES): cftypes.cpp cftypes.h common.h
27 | g++ $(CFLAGS) -c -o $@ -x objective-c++ $<
28 |
29 | $(MD): mobile_device.cpp mobile_device.h cftypes.h common.h
30 | g++ $(CFLAGS) -c -o $@ $<
31 |
32 | clean:
33 | rm -rf $(T) $(T).dSYM $(O)/*
34 |
--------------------------------------------------------------------------------
/mobdevim/process/mobile_device.h:
--------------------------------------------------------------------------------
1 | #ifndef MOBILE_DEVICE_H
2 | #define MOBILE_DEVICE_H
3 |
4 | #include "cftypes.h"
5 | #include
6 |
7 | #define kAMDSuccess 0
8 |
9 | // opaque structures
10 | struct am_device;
11 | struct am_device_notification;
12 | struct am_device_service_connection;
13 |
14 | #define ADNCI_MSG_CONNECTED 1
15 | #define ADNCI_MSG_DISCONNECTED 2
16 | #define ADNCI_MSG_UNKNOWN 3
17 |
18 | // callback info for AMDeviceNotificationSubscribe()
19 | struct am_device_notification_callback_info
20 | {
21 | am_device *dev; // device handle
22 | uint32_t code; // one of ADNCI_MSG_...
23 | };
24 | typedef void am_device_notification_callback_t(am_device_notification_callback_info *cbi, void *arg);
25 |
26 | // manage access to the MobileDevice library
27 | class mobile_device_lib_t
28 | {
29 | // dll handle
30 | void *dhandle;
31 |
32 | // pointers to functions in MobileDevice. not to be used directly
33 | mach_error_t (*_AMDeviceNotificationSubscribe)(am_device_notification_callback_t *, int, int, void *, am_device_notification **);
34 | mach_error_t (*_AMDeviceNotificationUnsubscribe)(am_device_notification *);
35 | CFStringRef (*_AMDeviceCopyDeviceIdentifier)(am_device *);
36 | mach_error_t (*_AMDeviceConnect)(am_device *);
37 | int (*_AMDeviceIsPaired)(am_device *);
38 | mach_error_t (*_AMDeviceValidatePairing)(am_device *);
39 | mach_error_t (*_AMDeviceStartSession)(am_device *);
40 | mach_error_t (*_AMDeviceStopSession)(am_device *);
41 | mach_error_t (*_AMDeviceDisconnect)(am_device *);
42 | mach_error_t (*_AMDeviceSecureStartService)(am_device *, CFStringRef, int *, am_device_service_connection **);
43 | void (*_AMDServiceConnectionInvalidate)(am_device_service_connection *);
44 | int (*_AMDServiceConnectionGetSocket)(am_device_service_connection *);
45 | ssize_t (*_AMDServiceConnectionSend)(am_device_service_connection *, const void *, size_t);
46 | ssize_t (*_AMDServiceConnectionReceive)(am_device_service_connection *, void *, size_t);
47 |
48 | public:
49 | mobile_device_lib_t(void) { reset(); }
50 | ~mobile_device_lib_t(void) { unload(); }
51 |
52 | void reset(void) { memset(this, 0, sizeof(*this)); }
53 | bool load(void);
54 | void unload(void);
55 |
56 | mach_error_t AMDeviceNotificationSubscribe(am_device_notification_callback_t *, int, int, void *, am_device_notification **) const;
57 | mach_error_t AMDeviceNotificationUnsubscribe(am_device_notification *) const;
58 | CFStringRef AMDeviceCopyDeviceIdentifier(am_device *) const;
59 | mach_error_t AMDeviceConnect(am_device *) const;
60 | int AMDeviceIsPaired(am_device *) const;
61 | mach_error_t AMDeviceValidatePairing(am_device *) const;
62 | mach_error_t AMDeviceStartSession(am_device *) const;
63 | mach_error_t AMDeviceStopSession(am_device *) const;
64 | mach_error_t AMDeviceDisconnect(am_device *) const;
65 | mach_error_t AMDeviceSecureStartService(am_device *, CFStringRef, int *, am_device_service_connection **) const;
66 | void AMDServiceConnectionInvalidate(am_device_service_connection *) const;
67 | int AMDServiceConnectionGetSocket(am_device_service_connection *) const;
68 | ssize_t AMDServiceConnectionSend(am_device_service_connection *handle, const void *buf, size_t len) const;
69 | ssize_t AMDServiceConnectionReceive(am_device_service_connection *handle, void *buf, size_t size) const;
70 | };
71 |
72 | extern mobile_device_lib_t MobileDevice;
73 |
74 | #endif // MOBILE_DEVICE_H
75 |
--------------------------------------------------------------------------------
/mobdevim/process/mobile_device.mm:
--------------------------------------------------------------------------------
1 | #include "mobile_device.h"
2 | #include
3 | #include
4 |
5 | mobile_device_lib_t MobileDevice;
6 |
7 | //------------------------------------------------------------------------------
8 | bool mobile_device_lib_t::load(void)
9 | {
10 | if ( dhandle != NULL )
11 | return true;
12 |
13 | const char *md_path = "/System/Library/PrivateFrameworks/MobileDevice.framework/MobileDevice";
14 | dhandle = dlopen(md_path, RTLD_NOW);
15 | if ( dhandle == NULL )
16 | {
17 | fprintf(stderr, "dlopen() failed for %s: %s", md_path, dlerror());
18 | return false;
19 | }
20 |
21 | #define BINDFUN(name, type) \
22 | _##name = reinterpret_cast(dlsym(dhandle, #name)); \
23 | if ( _##name == NULL ) \
24 | { \
25 | unload(); \
26 | fprintf(stderr, "Could not find function " #name " in %s", md_path); \
27 | return false; \
28 | }
29 |
30 | BINDFUN(AMDeviceNotificationSubscribe, mach_error_t (*)(am_device_notification_callback_t *, int, int, void *, am_device_notification **));
31 | BINDFUN(AMDeviceNotificationUnsubscribe, mach_error_t (*)(am_device_notification *));
32 | BINDFUN(AMDeviceCopyDeviceIdentifier, CFStringRef (*)(am_device *));
33 | BINDFUN(AMDeviceConnect, mach_error_t (*)(am_device *));
34 | BINDFUN(AMDeviceIsPaired, int (*)(am_device *));
35 | BINDFUN(AMDeviceValidatePairing, mach_error_t (*)(am_device *));
36 | BINDFUN(AMDeviceStartSession, mach_error_t (*)(am_device *));
37 | BINDFUN(AMDeviceStopSession, mach_error_t (*)(am_device *));
38 | BINDFUN(AMDeviceDisconnect, mach_error_t (*)(am_device *));
39 | BINDFUN(AMDeviceSecureStartService, mach_error_t (*)(am_device *, CFStringRef, int *, am_device_service_connection **));
40 | BINDFUN(AMDServiceConnectionInvalidate, void (*)(am_device_service_connection *));
41 | BINDFUN(AMDServiceConnectionGetSocket, int (*)(am_device_service_connection *));
42 | BINDFUN(AMDServiceConnectionSend, ssize_t (*)(am_device_service_connection *, const void *, size_t));
43 | BINDFUN(AMDServiceConnectionReceive, ssize_t (*)(am_device_service_connection *, void *, size_t));
44 |
45 | #undef BINDFUN
46 |
47 | return true;
48 | }
49 |
50 | //------------------------------------------------------------------------------
51 | void mobile_device_lib_t::unload()
52 | {
53 | if ( dhandle != NULL )
54 | {
55 | dlclose(dhandle);
56 | reset();
57 | }
58 | }
59 |
60 | //------------------------------------------------------------------------------
61 | mach_error_t mobile_device_lib_t::AMDeviceNotificationSubscribe(
62 | am_device_notification_callback_t *callback,
63 | int unused1,
64 | int unused2,
65 | void *arg,
66 | am_device_notification **hptr) const
67 | {
68 | return _AMDeviceNotificationSubscribe(callback, unused1, unused2, arg, hptr);
69 | }
70 |
71 | //------------------------------------------------------------------------------
72 | mach_error_t mobile_device_lib_t::AMDeviceNotificationUnsubscribe(am_device_notification *handle) const
73 | {
74 | return _AMDeviceNotificationUnsubscribe(handle);
75 | }
76 |
77 | //------------------------------------------------------------------------------
78 | CFStringRef mobile_device_lib_t::AMDeviceCopyDeviceIdentifier(am_device *device) const
79 | {
80 | return _AMDeviceCopyDeviceIdentifier(device);
81 | }
82 |
83 | //------------------------------------------------------------------------------
84 | mach_error_t mobile_device_lib_t::AMDeviceConnect(am_device *device) const
85 | {
86 | return _AMDeviceConnect(device);
87 | }
88 |
89 | //------------------------------------------------------------------------------
90 | int mobile_device_lib_t::AMDeviceIsPaired(am_device *device) const
91 | {
92 | return _AMDeviceIsPaired(device);
93 | }
94 |
95 | //------------------------------------------------------------------------------
96 | mach_error_t mobile_device_lib_t::AMDeviceValidatePairing(am_device *device) const
97 | {
98 | return _AMDeviceValidatePairing(device);
99 | }
100 |
101 | //------------------------------------------------------------------------------
102 | mach_error_t mobile_device_lib_t::AMDeviceStartSession(am_device *device) const
103 | {
104 | return _AMDeviceStartSession(device);
105 | }
106 |
107 | //------------------------------------------------------------------------------
108 | mach_error_t mobile_device_lib_t::AMDeviceStopSession(am_device *device) const
109 | {
110 | return _AMDeviceStopSession(device);
111 | }
112 |
113 | //------------------------------------------------------------------------------
114 | mach_error_t mobile_device_lib_t::AMDeviceDisconnect(am_device *device) const
115 | {
116 | return _AMDeviceDisconnect(device);
117 | }
118 |
119 | //------------------------------------------------------------------------------
120 | mach_error_t mobile_device_lib_t::AMDeviceSecureStartService(
121 | am_device *device,
122 | CFStringRef name,
123 | int *unused,
124 | am_device_service_connection **hptr) const
125 | {
126 | return _AMDeviceSecureStartService(device, name, unused, hptr);
127 | }
128 |
129 | //------------------------------------------------------------------------------
130 | void mobile_device_lib_t::AMDServiceConnectionInvalidate(am_device_service_connection *handle) const
131 | {
132 | _AMDServiceConnectionInvalidate(handle);
133 | }
134 |
135 | //------------------------------------------------------------------------------
136 | int mobile_device_lib_t::AMDServiceConnectionGetSocket(am_device_service_connection *handle) const
137 | {
138 | return _AMDServiceConnectionGetSocket(handle);
139 | }
140 |
141 | //------------------------------------------------------------------------------
142 | ssize_t mobile_device_lib_t::AMDServiceConnectionSend(
143 | am_device_service_connection *handle,
144 | const void *buf,
145 | size_t len) const
146 | {
147 | return _AMDServiceConnectionSend(handle, buf, len);
148 | }
149 |
150 | //------------------------------------------------------------------------------
151 | ssize_t mobile_device_lib_t::AMDServiceConnectionReceive(
152 | am_device_service_connection *handle,
153 | void *buf,
154 | size_t size) const
155 | {
156 | return _AMDServiceConnectionReceive(handle, buf, size);
157 | }
158 |
--------------------------------------------------------------------------------
/mobdevim/process/process.h:
--------------------------------------------------------------------------------
1 | //
2 | // conditions.h
3 | // mobdevim
4 | //
5 | // Created by Derek Selander on 4/15/20.
6 | // Copyright © 2020 Selander. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | #ifdef __cplusplus
12 | extern "C" {
13 | #endif
14 |
15 | #import "helpers.h"
16 | #import "ExternalDeclarations.h"
17 | /// The application ID to remove files from, WILL NOT WORK IF YOU DON'T HAVE THE MATCHING PP/CERTS!
18 | //extern NSString * const kSBSFileBundleID;
19 |
20 |
21 | /// The path to remove a file on the remote device
22 | //extern NSString * const kSBCommand;
23 |
24 |
25 | /// The path to remove a file on the remote device
26 | extern NSString * const kProcessKillPID;
27 | int kill_process(AMDeviceRef d, NSDictionary *options) ;
28 | /// Copies the Library, Documents, Caches directories over to the computer
29 | int running_processes(AMDeviceRef d, NSDictionary *options);
30 |
31 | #ifdef __cplusplus
32 | }
33 | #endif
34 |
--------------------------------------------------------------------------------
/mobdevim/process/process.mm:
--------------------------------------------------------------------------------
1 | //
2 | // springboardservices.m
3 | // mobdevim
4 | //
5 | // Created by Derek Selander
6 | // Copyright © 2020 Selander. All rights reserved.
7 | //
8 |
9 | //#import "springboardservices.h"
10 | #include
11 | #include
12 | #include
13 | #import
14 |
15 | #import "ios_instruments_client.h"
16 | #import "process.h"
17 |
18 | extern bool ssl_enabled;
19 |
20 | AMDServiceConnectionRef connect_to_instruments_server(AMDeviceRef d) {
21 | AMDServiceConnectionRef serviceConnection = nil;
22 | NSDictionary *inputDict = @{@"CloseOnInvalidate" : @YES};
23 | mach_error_t err = AMDeviceSecureStartService(d, @"com.apple.instruments.remoteserver", inputDict, &serviceConnection);
24 |
25 | if ( err != kAMDSuccess ) {
26 | mach_error_t err = AMDeviceSecureStartService(d, @"com.apple.instruments.remoteserver.DVTSecureSocketProxy", inputDict, &serviceConnection);
27 |
28 | if ( err != kAMDSuccess ) {
29 | derror("Unable to establish connection: %s\n", AMDErrorString(err));
30 | exit(1);
31 | } else {
32 | ssl_enabled = true;
33 | }
34 |
35 | }
36 |
37 | if ( serviceConnection ) {
38 |
39 | load_extern_implementation();
40 | perform_handshake((__bridge am_device_service_connection*)serviceConnection);
41 |
42 | }
43 | else {
44 | derror("Couldn't establish connection\n");
45 | exit(1);
46 | }
47 |
48 | return serviceConnection;
49 | }
50 |
51 | int kill_process(AMDeviceRef d, NSDictionary *options) {
52 | am_device_service_connection *serviceConnection = (__bridge am_device_service_connection*)connect_to_instruments_server(d);
53 |
54 |
55 | // print_proclist(serviceConnection);
56 | NSString *killpid = options[kProcessKillPID];
57 | if (killpid == nil) {
58 | printf("No PID!\n");
59 | exit(1);
60 | }
61 |
62 | NSString *trimmedString = [killpid stringByTrimmingCharactersInSet:[NSCharacterSet decimalDigitCharacterSet]];
63 |
64 | if([trimmedString length])
65 | {
66 | NSArray* pids = get_proclist_matching_name(serviceConnection, killpid);
67 | for (NSNumber *p in pids) {
68 | printf("killing \"%s\" %d\n", killpid.UTF8String, p.intValue);
69 | kill(serviceConnection, p.intValue);
70 | }
71 | }
72 | else
73 | {
74 | kill(serviceConnection, killpid.intValue);
75 | }
76 |
77 | AMDeviceStopSession(d);
78 | AMDeviceDisconnect(d);
79 | return 0;
80 | }
81 |
82 | int running_processes(AMDeviceRef d, NSDictionary *options) {
83 |
84 |
85 | // NSString *name = global_options.programBundleID;
86 | // NSDictionary *dict = nil;
87 | // mach_error_t err = AMDeviceLookupApplications(d, @{ @"ReturnAttributes": @YES, @"ShowLaunchProhibitedApps" : @YES }, &dict);
88 | // if (err) {
89 | // derror("Err looking up application, exiting...\n");
90 | // return 1;
91 | // }
92 | //
93 | // if (!name) {
94 | // dsprintf(stderr, "%sCouldn't find the bundleIdentifier \"%s\", try listing all bundleIDs with %s%smobdevim -l%s\n", dcolor(dc_yellow), [name UTF8String], colorEnd(), dcolor(dc_bold), colorEnd());
95 | // return 1;
96 | // }
97 | //
98 | // NSDictionary *appParams = [dict objectForKey:name];
99 | // NSString *path = appParams[@"Path"];
100 | // if (!path) {
101 | // derror("couldn't get the path for app %s\n", name.UTF8String);
102 | // return 1;
103 | // }
104 | // NSString *bundleID = appParams[@"CFBundleIdentifier"];
105 | // if (!bundleID) {
106 | // derror("couldn't get the bundleID\n");
107 | // return 1;
108 | // }
109 | //
110 | // NSString *arguments = global_options.programArguments;
111 | // NSArray *environment = options[kProcessEnvVars];
112 | //
113 | // NSMutableDictionary *dictionaryEnvironment = [NSMutableDictionary new];
114 | // for (NSString *val in environment) {
115 | // NSArray *components = [val componentsSeparatedByString:@"="];;
116 | // if ([components count] != 2) {
117 | // dsprintf(stderr, "Couldn't process \"%s\"\n", val.UTF8String);
118 | // continue;
119 | // }
120 | // NSString *key = components.firstObject;
121 | // NSString *object = components.lastObject;
122 | // [dictionaryEnvironment setObject:object forKey:key];
123 | // }
124 | //
125 |
126 | am_device_service_connection* instruments_connection = (__bridge am_device_service_connection*) connect_to_instruments_server(d);
127 |
128 | // launch_application(instruments_connection, bundleID.UTF8String, [arguments componentsSeparatedByString:@" "], dictionaryEnvironment);
129 |
130 | // am_device_service_connection *serviceConnection = (am_device_service_connection*)connect_to_instruments_server(d);
131 | //
132 | //
133 | print_proclist(instruments_connection);
134 | //
135 | // AMDeviceStopSession(d);
136 | // AMDeviceDisconnect(d);
137 | return 0;
138 | }
139 |
--------------------------------------------------------------------------------
/mobdevim/remove_file/remove_file.h:
--------------------------------------------------------------------------------
1 | //
2 | // remove_file.h
3 | // mobdevim
4 | //
5 | // Created by Derek Selander
6 | // Copyright © 2020 Selander. All rights reserved.
7 | //
8 |
9 | #import
10 | #import "ExternalDeclarations.h"
11 | #import "helpers.h"
12 |
13 | /// The application ID to remove files from, WILL NOT WORK IF YOU DON'T HAVE THE MATCHING PP/CERTS!
14 | extern NSString * const kRemoveFileBundleID;
15 |
16 |
17 | /// The path to remove a file on the remote device
18 | extern NSString * const kRemoveFileRemotePath;
19 |
20 | /// Copies the Library, Documents, Caches directories over to the computer
21 | int remove_file(AMDeviceRef d, NSDictionary *options);
22 |
--------------------------------------------------------------------------------
/mobdevim/remove_file/remove_file.m:
--------------------------------------------------------------------------------
1 | //
2 | // yoink.m
3 | // mobdevim
4 | //
5 | // Created by Derek Selander
6 | // Copyright © 2020 Selander. All rights reserved.
7 | //
8 |
9 | #import "remove_file.h"
10 | #include
11 | #include
12 | #include
13 |
14 | NSString * const kRemoveFileBundleID = @"com.selander.removefile.bundleid";
15 |
16 | NSString * const kRemoveFileRemotePath = @"com.selander.removefile.removefilepath";
17 |
18 | /// Max file size found in AFCFileRefRead
19 | //#define MAX_TRANSFER_FILE_SIZE 8191
20 |
21 | int remove_file(AMDeviceRef d, NSDictionary *options) {
22 | NSDictionary *dict;
23 | int returnError = 0;
24 | NSString *appBundle = [options objectForKey:kRemoveFileBundleID];
25 | NSDictionary *opts = @{ @"ApplicationType" : @"Any",
26 | @"ReturnAttributes" : @[@"ApplicationDSID",
27 | @"ApplicationType",
28 | @"CFBundleDisplayName",
29 | @"CFBundleExecutable",
30 | @"CFBundleIdentifier",
31 | @"CFBundleName",
32 | @"CFBundleShortVersionString",
33 | @"CFBundleVersion",
34 | @"Container",
35 | @"Entitlements",
36 | @"EnvironmentVariables",
37 | @"MinimumOSVersion",
38 | @"Path",
39 | @"ProfileValidated",
40 | @"SBAppTags",
41 | @"SignerIdentity",
42 | @"UIDeviceFamily",
43 | @"UIRequiredDeviceCapabilities"]};
44 |
45 | AMDeviceLookupApplications(d, opts, &dict);
46 | NSString *appPath = [[dict objectForKey:appBundle] objectForKey:@"Path"];
47 |
48 | if (!appPath) {
49 | dsprintf(stderr, "%sCouldn't find the bundleIdentifier \"%s\", try listing all bundleIDs with %s%smobdevim -l%s\n", dcolor(dc_yellow), [appBundle UTF8String], colorEnd(), dcolor(dc_bold), colorEnd());
50 | return ENOENT;
51 | }
52 |
53 | NSString *remotePath = [options objectForKey:kRemoveFileRemotePath];
54 | if (remotePath) {
55 | dsprintf(stdout, "Searching for \"%s\" file on for \"%s\"\n", [remotePath UTF8String], [appBundle UTF8String]);
56 |
57 | } else {
58 | dsprintf(stdout, "Listing remote files for \"%s\", use the fullpath for a remote file to delete\n", [appBundle UTF8String]);
59 | }
60 |
61 |
62 | AMDServiceConnectionRef serviceConnection = nil;
63 | AMDStartService(d, @"com.apple.mobile.house_arrest", &serviceConnection);
64 |
65 |
66 | NSDictionary *inputDictionary = @{ @"Command" : @"VendContainer", @"Identifier" : appBundle };
67 | if (AMDServiceConnectionSendMessage(serviceConnection, inputDictionary, kCFPropertyListXMLFormat_v1_0)) {
68 | return EACCES;
69 | }
70 |
71 | __unused NSString *outputDirectory = [options objectForKey:kOptionArgumentDestinationPath] ? [options objectForKey:kOptionArgumentDestinationPath] : [NSString stringWithFormat:@"/tmp/%@_app", appBundle];
72 | [[NSFileManager defaultManager] createDirectoryAtPath:outputDirectory withIntermediateDirectories:YES attributes:nil error:nil];
73 | long socket = AMDServiceConnectionGetSocket(serviceConnection);
74 |
75 | id info = nil;
76 | AMDServiceConnectionReceiveMessage(serviceConnection, &info, nil);
77 |
78 |
79 | NSString *currentDirectory = @"/";
80 |
81 | NSMutableSet *unexploredDirectories = [NSMutableSet set];
82 | NSMutableSet *exploredDirectories = [NSMutableSet set];
83 | NSMutableSet *exploredFiles = [NSMutableSet set];
84 | NSMutableSet *unexploredFiles = [NSMutableSet set];
85 |
86 | NSMutableDictionary *filePermissions = [NSMutableDictionary dictionary];
87 |
88 | [unexploredDirectories addObject:currentDirectory];
89 |
90 | AFCConnectionRef connectionRef = AFCConnectionCreate(0, (int)socket, 1, 0, 0);
91 | if (!connectionRef) {
92 | dsprintf(stderr, "%sCould not obtain a valid connection. Aborting%s\n", dcolor(dc_yellow), colorEnd());
93 | return EACCES;
94 | }
95 |
96 | BOOL successfullyReadADirectory = NO;
97 | while ([unexploredDirectories count] > 0) {
98 |
99 | char* remotePath = nil;
100 | AFCIteratorRef iteratorRef = nil;
101 |
102 | AFCDirectoryOpen(connectionRef, [currentDirectory UTF8String], &iteratorRef);
103 |
104 | while (AFCDirectoryRead(connectionRef, iteratorRef, &remotePath) == 0 && remotePath) {
105 |
106 | successfullyReadADirectory = YES;
107 | if (strcmp(remotePath, ".") == 0 || strcmp(remotePath, "..") == 0) {
108 | continue;
109 | }
110 |
111 | AFCIteratorRef fileIterator = NULL;
112 | NSString *pathReference = [currentDirectory stringByAppendingPathComponent:[NSString stringWithUTF8String:remotePath]];
113 | AFCFileInfoOpen(connectionRef, [pathReference UTF8String], &fileIterator);
114 |
115 | if (!fileIterator) {
116 | [exploredFiles addObject:pathReference];
117 | continue;
118 | }
119 |
120 | NSDictionary *filePermissionOptions = [(__bridge NSDictionary *)(fileIterator->fileAttributes) copy];
121 | NSString *file = [currentDirectory stringByAppendingPathComponent:[NSString stringWithUTF8String:remotePath]];
122 |
123 | if ([filePermissionOptions[@"st_ifmt"] isEqualToString:@"S_IFDIR"]) {
124 | [unexploredDirectories addObject:file];
125 | [exploredDirectories addObject:file];
126 | } else {
127 | [unexploredFiles addObject:file];
128 | [exploredFiles addObject:file];
129 | }
130 |
131 | NSString *finalizedFile = [outputDirectory stringByAppendingString:file];
132 | [filePermissions setObject:filePermissionOptions forKey:finalizedFile];
133 | }
134 |
135 | [unexploredDirectories removeObject:currentDirectory];
136 | NSString *nextDirectory = [unexploredDirectories anyObject];
137 | currentDirectory = nextDirectory;
138 | AFCDirectoryClose(connectionRef, iteratorRef);
139 | }
140 |
141 |
142 | NSMutableDictionary *outputContent = [NSMutableDictionary new];
143 | for (NSString *file in exploredFiles) {
144 | NSString *path = [file stringByDeletingLastPathComponent];
145 | if (![outputContent objectForKey:path]) {
146 | NSMutableArray *array = [NSMutableArray new];
147 | [outputContent setObject:array forKey:path];
148 | }
149 |
150 | NSMutableArray *ar = [outputContent objectForKey:path];
151 |
152 | NSString *finalizedString = [NSString stringWithFormat:@"\"%@\"", file];
153 | [ar addObject:finalizedString];
154 | }
155 |
156 | if (remotePath) {
157 | if (AFCRemovePath(connectionRef, [remotePath UTF8String])) {
158 | dsprintf(stderr, "couldn't remove file %s", [remotePath UTF8String]);
159 | } else {
160 | dsprintf(stdout, "Successfully removed file %s", [remotePath UTF8String]);
161 | }
162 |
163 | return 0;
164 | }
165 | dsprintf(stdout, "\n");
166 | for (NSString *key in outputContent) {
167 |
168 | NSArray *ar = [outputContent objectForKey:key];
169 |
170 | dsprintf(stdout, "%s%s%s\n", dcolor(dc_yellow),[key UTF8String], colorEnd());
171 | for (NSString *filename in ar) {
172 | dsprintf(stdout, "\t%s\n", [filename UTF8String]);
173 | }
174 | dsprintf(stdout, "\n");
175 | }
176 |
177 | // NSFileManager *manager = [NSFileManager defaultManager];
178 | //
179 | // // write the directories first
180 | // for (NSString *path in exploredDirectories) {
181 | // NSString *finalizedDirectory = [outputDirectory stringByAppendingPathComponent:path];
182 | // [manager createDirectoryAtPath:finalizedDirectory withIntermediateDirectories:YES attributes:nil error:nil];
183 | // }
184 |
185 | // write the files
186 | // NSArray *paths = [exploredFiles allObjects];
187 | // for (NSString *path in paths) {
188 | // NSString *finalizedFile = [outputDirectory stringByAppendingPathComponent:path];
189 | //
190 | //
191 | // AFCFileDescriptorRef ref = NULL;
192 | // if (AFCFileRefOpen(connectionRef, [path UTF8String], 0x1, &ref) || !ref) {
193 | // continue;
194 | // }
195 | //
196 | // NSError *err = NULL;
197 | // [[NSFileManager defaultManager] createFileAtPath:finalizedFile contents:nil attributes:nil];
198 | // NSFileHandle *handle = [NSFileHandle fileHandleForWritingToURL:[NSURL URLWithString:finalizedFile] error:&err];
199 | // if (err) {
200 | // dsprintf(stdout, "%s\nExiting...\n", [[err localizedDescription] UTF8String]);
201 | // return 1;
202 | // }
203 | // int fd = [handle fileDescriptor];
204 | //
205 | // if (fd == -1) {
206 | // dsprintf(stderr, "%sCan't open \"%s\" to write to, might be an existing file there.\n", dcolor(dc_yellow), [finalizedFile UTF8String], colorEnd());
207 | // returnError = 1;
208 | // continue;
209 | // }
210 | //
211 | // size_t size = BUFSIZ;
212 | // void *buffer[BUFSIZ];
213 | // while (AFCFileRefRead(connectionRef, ref, buffer, &size) == 0 && size != 0 && size != -1) {
214 | // write(fd, buffer, size);
215 | // }
216 | //
217 | // [handle closeFile];
218 | // AFCFileRefClose(connectionRef, ref);
219 | // }
220 | //
221 | // AFCConnectionClose(connectionRef);
222 | //
223 | // for (NSString *file in filePermissions) {
224 | // NSDictionary *permissions = filePermissions[file];
225 | // struct stat filestat;
226 | // if (stat([file UTF8String], &filestat) != 0) { continue; }
227 | //
228 | //
229 | //
230 | // for (NSString *permission in permissions) {
231 | //
232 | // if ([permission isEqualToString:@"st_birthtime"]) {
233 | // // filestat.st_birthtimespec = time(<#time_t *#>);
234 | //
235 | // } else if ([permission isEqualToString:@"st_mtime"]) {
236 | //
237 | // }
238 | // }
239 | // }
240 | //
241 | // if (successfullyReadADirectory) {
242 | // dsprintf(stdout, "Opening \"%s\"...\n", [outputDirectory UTF8String]);
243 | // if (!quiet_mode) {
244 | // NSString *systemCMDString = [NSString stringWithFormat:@"open -R %@", outputDirectory];
245 | // system([systemCMDString UTF8String]);
246 | // }
247 | // } else {
248 | // dsprintf(stderr, "%sUnable to open \"%s\", likely due to not having certificates that match on this device%s\n", dcolor(dc_yellow), [appBundle UTF8String], colorEnd());
249 | // returnError = EACCES;
250 | // }
251 |
252 | return returnError;
253 | }
254 |
255 |
256 |
--------------------------------------------------------------------------------
/mobdevim/send_files/send_files.h:
--------------------------------------------------------------------------------
1 | //
2 | // send_files.h
3 | // mobdevim
4 | //
5 | //
6 | // Copyright © 2020 Selander. All rights reserved.
7 | //
8 |
9 | #import
10 | #import "ExternalDeclarations.h"
11 | #import "helpers.h"
12 |
13 |
14 | /// The path to send up the files
15 | extern NSString * const kSendFilePath;
16 |
17 |
18 | /// The path to send up the files
19 | extern NSString * const kSendAppBundle;
20 |
21 | int send_files(AMDeviceRef d, NSDictionary *options);
22 |
--------------------------------------------------------------------------------
/mobdevim/send_files/send_files.m:
--------------------------------------------------------------------------------
1 | //
2 | // send_files.m
3 | // mobdevim
4 | //
5 | //
6 | // Copyright © 2020 Selander. All rights reserved.
7 | //
8 |
9 | #import "send_files.h"
10 | #import
11 |
12 | NSString * const kSendFilePath = @"com.selander.sendfilepath";
13 |
14 | NSString * const kSendAppBundle = @"com.selander.appbundle";
15 |
16 | int send_files(AMDeviceRef d, NSDictionary *options) {
17 |
18 | NSString *writingFromDirectory = [options objectForKey:kSendFilePath];
19 | if ([writingFromDirectory hasSuffix:@"xcappdata"]) {
20 | writingFromDirectory = [writingFromDirectory stringByAppendingPathComponent:@"AppData"];
21 | }
22 |
23 | NSURL *localFileURL = [[[NSURL fileURLWithPath:writingFromDirectory] URLByStandardizingPath] URLByResolvingSymlinksInPath];
24 | if (!localFileURL) {
25 | dsprintf(stderr, "Couldn't find directory \"%s\"\nExiting", [[localFileURL description] UTF8String]);
26 | return EACCES;
27 | }
28 |
29 | NSFileManager* manager = [NSFileManager defaultManager];
30 |
31 | NSDirectoryEnumerator *dirEnumerator = [manager enumeratorAtURL:localFileURL includingPropertiesForKeys:@[NSURLIsDirectoryKey] options:0 errorHandler:^BOOL(NSURL * _Nonnull url, NSError * _Nonnull error) {
32 |
33 |
34 | if (error) {
35 | dsprintf(stderr, "Couldn't enumerate directory\n%s", [[error localizedDescription] UTF8String]);
36 | exit(EACCES);
37 | }
38 |
39 | return YES;
40 | }];
41 |
42 | // At this point, we are good on the local OS X, search for appID now
43 |
44 | NSDictionary *dict;
45 | NSString *appBundle = [options objectForKey:kSendAppBundle];
46 | NSDictionary *opts = @{ @"ApplicationType" : @"Any",
47 | @"ReturnAttributes" : @[@"ApplicationDSID",
48 | // @"ApplicationType",
49 | // @"CFBundleDisplayName",
50 | // @"CFBundleExecutable",
51 | @"CFBundleIdentifier",
52 | // @"CFBundleName",
53 | // @"CFBundleShortVersionString",
54 | // @"CFBundleVersion",
55 | // @"Container",
56 | // @"Entitlements",
57 | // @"EnvironmentVariables",
58 | // @"MinimumOSVersion",
59 | @"Path",
60 | // @"ProfileValidated",
61 | // @"SBAppTags",
62 | // @"SignerIdentity",
63 | // @"UIDeviceFamily",
64 | // @"UIRequiredDeviceCapabilities"
65 | ]};
66 |
67 | AMDeviceLookupApplications(d, opts, &dict);
68 | NSString *appPath = [[dict objectForKey:appBundle] objectForKey:@"Path"];
69 |
70 | if (!appPath) {
71 | dsprintf(stderr, "%sCouldn't find the bundleIdentifier \"%s\", try listing all bundleIDs with %s%smobdevim -l%s\n", dcolor(dc_yellow), [appBundle UTF8String], colorEnd(), dcolor(dc_bold), colorEnd());
72 | return 1;
73 | }
74 |
75 | AMDServiceConnectionRef serviceConnection = nil;
76 | NSDictionary *inputDict = @{@"CloseOnInvalidate" : @NO, @"UnlockEscrowBag": @YES};
77 | AMDeviceSecureStartService(d, @"com.apple.mobile.house_arrest", inputDict, &serviceConnection);
78 | if (!serviceConnection) {
79 | return EACCES;
80 | }
81 |
82 | NSDictionary *inputDictionary = @{ @"Command" : @"VendContainer", @"Identifier" : appBundle };
83 | if (AMDServiceConnectionSendMessage(serviceConnection, inputDictionary, kCFPropertyListXMLFormat_v1_0)) {
84 | return EACCES;
85 | }
86 |
87 | long socket = AMDServiceConnectionGetSocket(serviceConnection);
88 | id info = nil;
89 | AMDServiceConnectionReceiveMessage(serviceConnection, &info, nil);
90 |
91 | AFCConnectionRef connectionRef = AFCConnectionCreate(0, (int)socket, 1, 0, 0);
92 | if (!connectionRef) {
93 | dsprintf(stderr, "%sCould not obtain a valid connection. Aborting%s\n", dcolor(dc_yellow), colorEnd());
94 | return EACCES;
95 | }
96 |
97 |
98 |
99 | NSString *baseResolvedPath = nil;
100 | for (NSURL *fileURL in dirEnumerator) {
101 |
102 | // TODO symlinked issues, quick hack for now...
103 | if (!baseResolvedPath) {
104 | baseResolvedPath = [[fileURL path] stringByDeletingLastPathComponent];
105 | }
106 | NSString *basePath = [NSString stringWithUTF8String:[fileURL fileSystemRepresentation]];
107 | NSRange range = [basePath rangeOfString:baseResolvedPath];
108 |
109 | range.location = range.length;
110 | range.length = [basePath length] - range.location;
111 | assert(range.length != 0);
112 |
113 | NSString *remotePath = [basePath substringWithRange:range];
114 |
115 |
116 | BOOL isDirectory = NO;
117 | NSString *remoteDirectory = nil;
118 | NSString *fileName = [fileURL lastPathComponent];
119 |
120 | [manager fileExistsAtPath:[fileURL path] isDirectory:&isDirectory];
121 | if ([fileName isEqualToString:@".DS_Store"]) {
122 | continue;
123 | }
124 |
125 | if (isDirectory) {
126 | remoteDirectory = remotePath;
127 | } else {
128 | remoteDirectory = [remotePath stringByDeletingLastPathComponent];
129 | }
130 |
131 | AFCFileDescriptorRef fileDescriptor = NULL;
132 | AFCIteratorRef iteratorRef = NULL;
133 | AFCDirectoryOpen(connectionRef, [remotePath fileSystemRepresentation], &iteratorRef);
134 |
135 | // directory might not exist on the remote side, create it
136 | if (isDirectory && !iteratorRef) {
137 | if (AFCDirectoryCreate(connectionRef, [remotePath fileSystemRepresentation])) {
138 | dsprintf(stderr, "Couldn't create directory: %s on the remote side\n", [remotePath fileSystemRepresentation]);
139 | }
140 | AFCDirectoryClose(connectionRef, iteratorRef);
141 | iteratorRef = NULL;
142 |
143 | }
144 |
145 | else if (!isDirectory) {
146 | AFCFileRefOpen(connectionRef, [remotePath fileSystemRepresentation], 0x3, &fileDescriptor);
147 | if (!fileDescriptor) {
148 | dsprintf(stderr, "Couldn't open file \"%s\" on the device side\n", [remotePath fileSystemRepresentation]);
149 | continue;
150 | }
151 |
152 | NSData *data = [NSData dataWithContentsOfURL:fileURL];
153 | if (!data) {
154 | dsprintf(stderr, "Invalid directory to write from: %s\n", [fileURL fileSystemRepresentation]);
155 | return EACCES;
156 | }
157 |
158 |
159 | mach_error_t err = AFCFileRefWrite(connectionRef, fileDescriptor, [data bytes], (uint32_t)[data length]);
160 | if (err) {
161 | dsprintf(stderr, "Error writing to \"%s\", %d\n", [remotePath fileSystemRepresentation], err);
162 | }
163 |
164 | AFCFileRefClose(connectionRef, fileDescriptor);
165 | }
166 | }
167 |
168 | return 0;
169 | }
170 |
171 |
172 |
--------------------------------------------------------------------------------
/mobdevim/springboardservices/springboardservices.h:
--------------------------------------------------------------------------------
1 | //
2 | // springboardservices.h
3 | // mobdevim
4 | //
5 | // Created by Derek Selander
6 | // Copyright © 2020 Selander. All rights reserved.
7 | //
8 |
9 | #import
10 | #import "ExternalDeclarations.h"
11 | #import "helpers.h"
12 |
13 | /// The application ID to remove files from, WILL NOT WORK IF YOU DON'T HAVE THE MATCHING PP/CERTS!
14 | //extern NSString * const kSBSFileBundleID;
15 |
16 |
17 | /// The path to remove a file on the remote device
18 | extern NSString * const kSBCommand;
19 |
20 | /// Copies the Library, Documents, Caches directories over to the computer
21 | int springboard_services(AMDeviceRef d, NSDictionary *options);
22 |
--------------------------------------------------------------------------------
/mobdevim/springboardservices/springboardservices.m:
--------------------------------------------------------------------------------
1 | //
2 | // springboardservices.m
3 | // mobdevim
4 | //
5 | // Created by Derek Selander
6 | // Copyright © 2020 Selander. All rights reserved.
7 | //
8 |
9 | #import "springboardservices.h"
10 | #include
11 | #include
12 | #include
13 |
14 |
15 | //NSString * const kSBSFileBundleID = @"com.selander.springboard_services.bundleid";
16 | NSString * const kProcessKillPID = @"com.selander.process.kill";
17 | NSString * const kSBCommand = @"com.selander.springboard_services.command";
18 |
19 | /// Max file size found in AFCFileRefRead
20 | //#define MAX_TRANSFER_FILE_SIZE 8191
21 |
22 | int springboard_services(AMDeviceRef d, NSDictionary *options) {
23 |
24 | NSString *deviceUDID = AMDeviceGetName(d);
25 | NSString *savePath = [NSString stringWithFormat:@"%s%@_sbicons.backup", getenv("TMPDIR"), deviceUDID];
26 |
27 |
28 | int returnError = 0;
29 | AMDServiceConnectionRef serviceConnection = nil;
30 | AMDStartService(d, @"com.apple.springboardservices", &serviceConnection);
31 |
32 |
33 |
34 | // gets the max counts for what springboard will allow
35 | if (AMDServiceConnectionSendMessage(serviceConnection, @{ @"command" : @"getHomeScreenIconMetrics" }, kCFPropertyListXMLFormat_v1_0)) {
36 | return EACCES;
37 | }
38 | NSDictionary *metricDict = nil;
39 | if (AMDServiceConnectionReceiveMessage(serviceConnection, &metricDict, nil)) {
40 | return EACCES;
41 | }
42 | NSInteger maxSpringboardPages = [metricDict[@"homeScreenIconMaxPages"] integerValue];
43 | NSInteger maxFolderPages = [metricDict[@"homeScreenIconFolderMaxPages"] integerValue];
44 | NSInteger homeScreenIconRows = [metricDict[@"homeScreenIconRows"] integerValue];
45 | NSInteger homeScreenIconColumns = [metricDict[@"homeScreenIconColumns"] integerValue];
46 |
47 |
48 | // Get the icons and put them in an NSArray called iconsInfo
49 | if (AMDServiceConnectionSendMessage(serviceConnection, @{ @"command" : @"getIconState", @"formatVersion" : @2 }, kCFPropertyListXMLFormat_v1_0)) {
50 | return EACCES;
51 | }
52 | NSArray *iconsInfo = nil;
53 | if (AMDServiceConnectionReceiveMessage(serviceConnection, &iconsInfo, nil)) {
54 | return EACCES;
55 | }
56 |
57 | NSString *optionsCommand = options[kSBCommand];
58 |
59 | if ([[NSFileManager defaultManager] fileExistsAtPath:savePath]) {
60 | if ([optionsCommand isEqualToString:@"restore"]) {
61 | NSData *data = [NSData dataWithContentsOfFile:savePath];
62 | NSArray *newIcons = [NSPropertyListSerialization propertyListWithData:data options:0 format:0 error:nil];
63 | dsprintf(stdout, "Attempting to restore icons from \"%s\"\n", [savePath UTF8String]);
64 | if (AMDServiceConnectionSendMessage(serviceConnection, @{@"command" : @"setIconState", @"iconState" : [newIcons copy]}, kCFPropertyListXMLFormat_v1_0)) {
65 | return EACCES;
66 | }
67 |
68 | return returnError;
69 | }
70 | dsprintf(stdout, "Overwrite backup file? [Y/n] ");
71 | char c = getchar();
72 | if (c == 'Y') {
73 | dsprintf(stdout, "Writing backup to \"%s\"\n", [savePath UTF8String]);
74 | [[NSPropertyListSerialization dataWithPropertyList:iconsInfo format:NSPropertyListXMLFormat_v1_0 options:0 error:nil] writeToFile:savePath atomically:YES];
75 | }
76 | } else {
77 | dsprintf(stdout, "Writing backup to \"%s\"\n", [savePath UTF8String]);
78 | [[NSPropertyListSerialization dataWithPropertyList:iconsInfo format:NSPropertyListXMLFormat_v1_0 options:0 error:nil] writeToFile:savePath atomically:YES];
79 | }
80 |
81 | if (!optionsCommand) {
82 | NSError *err = nil;
83 | NSData *data = [NSPropertyListSerialization dataWithPropertyList:iconsInfo format:NSPropertyListXMLFormat_v1_0 options:0 error:&err];
84 | if (err) {
85 | dsprintf(stderr, "%s\n", [[err localizedDescription] UTF8String]);
86 | }
87 | NSString *iconPlistPath = [NSString stringWithFormat:@"/tmp/%@_sbicons.plist", deviceUDID];
88 | dsprintf(stdout, "%Writing Springboard icons to \"%s\"\n", [iconPlistPath UTF8String]);
89 | [data writeToFile:iconPlistPath atomically:YES];
90 | [[NSWorkspace sharedWorkspace] openFile:iconPlistPath];
91 | return returnError;
92 | }
93 |
94 |
95 | // Check if kSBCommand gives a valid path
96 | if ([[NSFileManager defaultManager] fileExistsAtPath:optionsCommand]) {
97 | dsprintf(stdout, "Attempting to write Springboard icons from \"%s\"\n", [optionsCommand UTF8String]);
98 | NSData *data = [NSData dataWithContentsOfFile:optionsCommand];
99 | if (!data) {
100 | dsprintf(stderr, "Invalid property list from \"%s\"s\n", optionsCommand);
101 | return returnError;
102 | }
103 | NSError *err = nil;
104 | id newIcons = [NSPropertyListSerialization propertyListWithData:data options:0 format:0 error:&err];
105 | if (err) {
106 | dsprintf(stderr, "%s\n", [[err localizedDescription] UTF8String]);
107 | return returnError;
108 | }
109 | if (AMDServiceConnectionSendMessage(serviceConnection, @{@"command" : @"setIconState", @"iconState" : [newIcons copy]}, kCFPropertyListXMLFormat_v1_0)) {
110 | return EACCES;
111 | }
112 | return returnError;
113 | }
114 |
115 |
116 | if (![optionsCommand isEqualToString:@"asshole"]) {
117 | return returnError;
118 | }
119 |
120 | dsprintf(stdout, "Arranging apps in \"asshole\" mode\n");
121 | NSMutableArray *flatIcons = [NSMutableArray arrayWithCapacity:400];
122 | for (int pageIndex = 0; pageIndex < [iconsInfo count]; pageIndex++) {
123 |
124 | NSArray *page = iconsInfo[pageIndex];
125 | for (int j = 0; j < [page count]; j++) {
126 | NSDictionary *appDict = [page objectAtIndex:j];
127 | // if we have this key, then its an app
128 | if (appDict[@"displayIdentifier"]) {
129 | [flatIcons addObject:appDict];
130 | continue;
131 | }
132 |
133 | // Drill into the folders....
134 | NSArray *folderArray = appDict[@"iconLists"];
135 | if (folderArray) {
136 | for (int folderPageIndex = 0; folderPageIndex < [folderArray count]; folderPageIndex++) {
137 | NSArray *folderPage = folderArray[folderPageIndex];
138 | [flatIcons addObjectsFromArray:folderPage];
139 | }
140 | }
141 | }
142 | }
143 |
144 | // Shuffle the icons
145 | NSInteger c = [flatIcons count];
146 | // for (int i = 0 ; i< c; i++) {
147 | // printf("%s\n", [[flatIcons[i][@"bundleIdentifier"] description] UTF8String]);
148 | // }
149 | for (NSUInteger i = 0; i < c - 1; ++i) {
150 | NSInteger remainingCount = c - i;
151 | NSInteger exchangeIndex = i + arc4random_uniform((u_int32_t )remainingCount);
152 | [flatIcons exchangeObjectAtIndex:i withObjectAtIndex:exchangeIndex];
153 | }
154 |
155 |
156 |
157 | NSMutableArray *newIcons = [NSMutableArray arrayWithCapacity:maxSpringboardPages];
158 | // blank array for the dock, don't want any in there
159 | [newIcons addObject:@[]];
160 | int currentPageIndex = (int)maxSpringboardPages - 1;
161 |
162 | NSUInteger count = [flatIcons count];
163 | for (int i = 0; i < count; i++) {
164 | NSDictionary *icon = flatIcons[i];
165 |
166 | // Max out each springboard page first with an initial folder
167 | if ([newIcons count] < maxSpringboardPages) {
168 | // NSString *randomName = [flatIcons[arc4random_uniform((uint32_t)count)] objectForKey:@"displayName"];
169 | NSMutableArray *iconPages = [NSMutableArray arrayWithObject:icon];
170 |
171 | NSMutableDictionary *dict = [NSMutableDictionary dictionaryWithDictionary:@{@"displayName" : @"U mad, bro?", @"iconLists" : [NSMutableArray arrayWithObject:iconPages], @"listType" : @"folder"}];
172 | NSMutableArray *iconLists = [NSMutableArray arrayWithObject:dict];
173 |
174 | [newIcons addObject:iconLists];
175 | continue;
176 | }
177 |
178 | // If there's a folder for each SB page, max out the latest folder's pages
179 | do {
180 |
181 | NSMutableArray *page = newIcons[currentPageIndex];
182 | if ([page count] >= homeScreenIconRows * homeScreenIconColumns) {
183 | currentPageIndex--;
184 | continue;
185 | }
186 |
187 | if (currentPageIndex < 0) {
188 | dsprintf(stderr, "Couldn't make room for all the icons\n");
189 | return EACCES;
190 | }
191 |
192 | NSMutableDictionary* foldersDict = [page lastObject];
193 |
194 | NSMutableArray *folderPage = foldersDict[@"iconLists"];
195 |
196 | if ([folderPage count] < maxFolderPages) {
197 | [folderPage addObject:[NSMutableArray arrayWithObject:icon]];
198 | break;
199 | } else {
200 | currentPageIndex--;
201 | continue;
202 | // NSMutableArray *iconPages = [NSMutableArray arrayWithObject:icon];
203 | // [folderPage addObject:iconPages];
204 | break;
205 | }
206 |
207 | } while (1);
208 | }
209 |
210 |
211 | if (AMDServiceConnectionSendMessage(serviceConnection, @{@"command" : @"setIconState", @"iconState" : [newIcons copy]}, kCFPropertyListXMLFormat_v1_0)) {
212 | return EACCES;
213 | }
214 |
215 | id info = nil;
216 | while(!AMDServiceConnectionReceive(serviceConnection, &info, 8)) { }
217 |
218 |
219 | AMDServiceConnectionInvalidate(serviceConnection);
220 | return returnError;
221 | }
222 |
223 |
224 |
--------------------------------------------------------------------------------
/mobdevim/wifi connect/wifi_connect.m:
--------------------------------------------------------------------------------
1 | //
2 | // install_application.m
3 | // mobdevim
4 | //
5 | // Created by Derek Selander
6 | // Copyright © 2020 Selander. All rights reserved.
7 | //
8 |
9 | #import "wifie_connect.h"
10 | #import
11 | #import
12 |
13 | //#define CURRENT_DMG @"/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/DeviceSupport/14.2/DeveloperDiskImage.dmg"
14 |
15 | //#define CURRENT_DMG_SIG @"/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/DeviceSupport/14.2/DeveloperDiskImage.dmg.signature"
16 |
17 | NSString *const kWifiConnectUUID = @"com.selander.wificonnect.uuid";
18 | NSString *const kWifiConnectUUIDDisable = @"com.selander.wificonnect.uuid.disable";
19 |
20 | int wifi_connect(AMDeviceRef d, NSDictionary *options) {
21 |
22 | long flags;
23 | BOOL should_disable = [[options objectForKey:kWifiConnectUUIDDisable] boolValue];
24 | NSString *uuid_param = [options objectForKey:kWifiConnectUUID];
25 | if (uuid_param) {
26 | CFUUIDRef ref = CFUUIDCreateFromString(kCFAllocatorDefault, (CFStringRef)uuid_param);
27 | NSString *resolved_uuid = CFBridgingRelease(CFUUIDCreateString(kCFAllocatorDefault, ref));
28 | if ([resolved_uuid isEqualToString:@"00000000-0000-0000-0000-000000000000"]) {
29 | derror("Couldn't resolve UUID: \"%s\"\n", uuid_param.UTF8String);
30 | exit(1);
31 | }
32 | uuid_param = resolved_uuid;
33 | } else {
34 | uuid_param = GetHostUUID();
35 | }
36 |
37 | handle_err(AMDeviceGetWirelessBuddyFlags(d, &flags));
38 | dprint("original wifi flags are 0x%x\n", flags);
39 | if (should_disable) {
40 | dprint("disbling wifi...\n");
41 | handle_err(AMDeviceSetWirelessBuddyFlags(d, 0));
42 | AMDeviceSetValue(d, @"com.apple.mobile.wireless_lockdown", @"EnableWifiDebugging", @NO);
43 | AMDeviceSetValue(d, @"com.apple.mobile.wireless_lockdown", @"EnableWifiConnections", @NO);
44 | AMDeviceSetValue(d, @"com.apple.xcode.developerdomain", @"WirelessHosts", @[]);
45 | return 0;
46 | } else {
47 | handle_err(AMDeviceSetWirelessBuddyFlags(d, flags | 3)) // 1 enable wifi, 2 broadcast;
48 | }
49 |
50 | NSArray *hosts = AMDeviceCopyValue(d, @"com.apple.xcode.developerdomain", @"WirelessHosts", 0);
51 | if (!hosts) {
52 | hosts = @[];
53 | }
54 | BOOL foundIt = NO;
55 | for (NSString *h in hosts) {
56 | if ([h containsString:uuid_param]) {
57 | foundIt = YES;
58 | }
59 | }
60 |
61 | if (!foundIt) {
62 | NSMutableArray *mutableHosts = [hosts mutableCopy];
63 | [mutableHosts addObject:uuid_param];
64 | AMDeviceSetValue(d, @"com.apple.xcode.developerdomain", @"WirelessHosts", mutableHosts);
65 | }
66 |
67 | AMDeviceSetValue(d, @"com.apple.mobile.wireless_lockdown", @"EnableWifiDebugging", @YES);
68 | AMDeviceSetValue(d, @"com.apple.mobile.wireless_lockdown", @"EnableWifiConnections", @YES);
69 |
70 | dprint("Enabled WIFI debugging on host \"%s\"\n", uuid_param.UTF8String);
71 | return 0;
72 | }
73 |
--------------------------------------------------------------------------------
/mobdevim/wifi connect/wifie_connect.h:
--------------------------------------------------------------------------------
1 | //
2 | // install_application.h
3 | // mobdevim
4 | //
5 | // Created by Derek Selander
6 | // Copyright © 2020 Selander. All rights reserved.
7 | //
8 |
9 | #import
10 | #import "ExternalDeclarations.h"
11 | #import "helpers.h"
12 |
13 | /// The path to the IPA file
14 | extern NSString *const kWifiConnectUUID;
15 | extern NSString *const kWifiConnectUUIDDisable;
16 | /// Install an application over toe the device. Expects a path to an IPA in options
17 | int wifi_connect(AMDeviceRef d, NSDictionary *options);
18 |
19 | //void *AMDeviceMountImage(AMDeviceRef d, NSString* imagePath, NSDictionary *options, void (*callback)(NSDictionary *status, id deviceToken), id deviceToken, NSError **error);
20 | //
21 | //
22 |
--------------------------------------------------------------------------------
/mobdevim/yoink/yoink.h:
--------------------------------------------------------------------------------
1 | //
2 | // yoink.h
3 | // mobdevim
4 | //
5 | // Created by Derek Selander
6 | // Copyright © 2020 Selander. All rights reserved.
7 | //
8 |
9 | #import
10 | #import "ExternalDeclarations.h"
11 | #import "helpers.h"
12 |
13 | /// The application ID to yoink from, WILL NOT WORK IF YOU DON'T HAVE THE MATCHING PP/CERTS!
14 | extern NSString * const kYoinkBundleIDContents;
15 |
16 | /// Copies the Library, Documents, Caches directories over to the computer
17 | int yoink_app(AMDeviceRef d, NSDictionary *options);
18 |
--------------------------------------------------------------------------------
/mobdevim/yoink/yoink.m:
--------------------------------------------------------------------------------
1 | //
2 | // yoink.m
3 | // mobdevim
4 | //
5 | // Created by Derek Selander
6 | // Copyright © 2020 Selander. All rights reserved.
7 | //
8 |
9 | #import "yoink.h"
10 | #include
11 | #include
12 | #include
13 |
14 | NSString * const kYoinkBundleIDContents = @"com.selander.yoink.bundleid";
15 |
16 | /// Max file size found in AFCFileRefRead
17 | #define MAX_TRANSFER_FILE_SIZE 8191
18 |
19 | int yoink_app(AMDeviceRef d, NSDictionary *options) {
20 | NSDictionary *dict;
21 | int returnError = 0;
22 | NSString *appBundle = [options objectForKey:kYoinkBundleIDContents];
23 | NSDictionary *opts = @{ @"ApplicationType" : @"Any",
24 | @"ReturnAttributes" : @[@"ApplicationDSID",
25 | @"ApplicationType",
26 | @"CFBundleDisplayName",
27 | @"CFBundleExecutable",
28 | @"CFBundleIdentifier",
29 | @"CFBundleName",
30 | @"CFBundleShortVersionString",
31 | @"CFBundleVersion",
32 | @"Container",
33 | @"Entitlements",
34 | @"EnvironmentVariables",
35 | @"MinimumOSVersion",
36 | @"Path",
37 | @"ProfileValidated",
38 | @"SBAppTags",
39 | @"SignerIdentity",
40 | @"UIDeviceFamily",
41 | @"UIRequiredDeviceCapabilities"]};
42 |
43 | AMDeviceLookupApplications(d, opts, &dict);
44 | NSString *appPath = [[dict objectForKey:appBundle] objectForKey:@"Path"];
45 |
46 | if (!appPath) {
47 | dsprintf(stderr, "%sCouldn't find the bundleIdentifier \"%s\", try listing all bundleIDs with %s%smobdevim -l%s\n", dcolor(dc_yellow), [appBundle UTF8String], colorEnd(), dcolor(dc_bold), colorEnd());
48 | return ENOENT;
49 | }
50 |
51 | dsprintf(stdout, "Searching through directory contents for \"%s\"...\n", [appBundle UTF8String]);
52 |
53 | AMDServiceConnectionRef serviceConnection = nil;
54 | NSDictionary *inputDict = @{@"CloseOnInvalidate" : @NO, @"UnlockEscrowBag": @YES};
55 | AMDeviceSecureStartService(d, @"com.apple.mobile.house_arrest", inputDict, &serviceConnection);
56 | if (!serviceConnection) {
57 | return EACCES;
58 | }
59 |
60 |
61 | NSDictionary *inputDictionary = @{ @"Command" : @"VendContainer", @"Identifier" : appBundle };
62 | if (AMDServiceConnectionSendMessage(serviceConnection, inputDictionary, kCFPropertyListXMLFormat_v1_0)) {
63 | return EACCES;
64 | }
65 |
66 | __unused NSString *outputDirectory = [options objectForKey:kOptionArgumentDestinationPath] ? [options objectForKey:kOptionArgumentDestinationPath] : [NSString stringWithFormat:@"/tmp/%@_app", appBundle];
67 | if ([outputDirectory hasPrefix:@"/tmp/"]) {
68 | [[NSFileManager defaultManager] removeItemAtPath:outputDirectory error:nil];
69 | }
70 | [[NSFileManager defaultManager] createDirectoryAtPath:outputDirectory withIntermediateDirectories:YES attributes:nil error:nil];
71 | long socket = AMDServiceConnectionGetSocket(serviceConnection);
72 |
73 | id info = nil;
74 | AMDServiceConnectionReceiveMessage(serviceConnection, &info, nil);
75 |
76 |
77 | NSString *currentDirectory = @"/";
78 |
79 | NSMutableSet *unexploredDirectories = [NSMutableSet set];
80 | NSMutableSet *exploredDirectories = [NSMutableSet set];
81 | NSMutableSet *exploredFiles = [NSMutableSet set];
82 | NSMutableSet *unexploredFiles = [NSMutableSet set];
83 |
84 | NSMutableDictionary *filePermissions = [NSMutableDictionary dictionary];
85 |
86 | [unexploredDirectories addObject:currentDirectory];
87 |
88 | AFCConnectionRef connectionRef = AFCConnectionCreate(0, (int)socket, 1, 0, 0);
89 | if (!connectionRef) {
90 | dsprintf(stderr, "%sCould not obtain a valid connection. Aborting%s\n", dcolor(dc_yellow), colorEnd());
91 | return EACCES;
92 | }
93 |
94 | BOOL successfullyReadADirectory = NO;
95 | while ([unexploredDirectories count] > 0) {
96 |
97 | char* remotePath = nil;
98 | AFCIteratorRef iteratorRef = nil;
99 |
100 | AFCDirectoryOpen(connectionRef, [currentDirectory UTF8String], &iteratorRef);
101 |
102 | while (AFCDirectoryRead(connectionRef, iteratorRef, &remotePath) == 0 && remotePath) {
103 |
104 | successfullyReadADirectory = YES;
105 | if (strcmp(remotePath, ".") == 0 || strcmp(remotePath, "..") == 0) {
106 | continue;
107 | }
108 |
109 | AFCIteratorRef fileIterator = NULL;
110 | NSString *pathReference = [currentDirectory stringByAppendingPathComponent:[NSString stringWithUTF8String:remotePath]];
111 | AFCFileInfoOpen(connectionRef, [pathReference UTF8String], &fileIterator);
112 |
113 | if (!fileIterator) {
114 | [exploredFiles addObject:pathReference];
115 | continue;
116 | }
117 |
118 | NSDictionary *filePermissionOptions = [(__bridge NSDictionary *)(fileIterator->fileAttributes) copy];
119 | NSString *file = [currentDirectory stringByAppendingPathComponent:[NSString stringWithUTF8String:remotePath]];
120 |
121 | if ([filePermissionOptions[@"st_ifmt"] isEqualToString:@"S_IFDIR"]) {
122 | [unexploredDirectories addObject:file];
123 | [exploredDirectories addObject:file];
124 | } else {
125 | [unexploredFiles addObject:file];
126 | [exploredFiles addObject:file];
127 | }
128 |
129 | NSString *finalizedFile = [outputDirectory stringByAppendingString:file];
130 | [filePermissions setObject:filePermissionOptions forKey:finalizedFile];
131 | }
132 |
133 | [unexploredDirectories removeObject:currentDirectory];
134 | NSString *nextDirectory = [unexploredDirectories anyObject];
135 | currentDirectory = nextDirectory;
136 | AFCDirectoryClose(connectionRef, iteratorRef);
137 | }
138 |
139 | NSFileManager *manager = [NSFileManager defaultManager];
140 |
141 | // write the directories first
142 | for (NSString *path in exploredDirectories) {
143 | NSString *finalizedDirectory = [outputDirectory stringByAppendingPathComponent:path];
144 | [manager createDirectoryAtPath:finalizedDirectory withIntermediateDirectories:YES attributes:nil error:nil];
145 | }
146 |
147 | // write the files
148 | NSArray *paths = [exploredFiles allObjects];
149 | for (NSString *path in paths) {
150 | NSString *finalizedFile = [outputDirectory stringByAppendingPathComponent:path];
151 |
152 |
153 | AFCFileDescriptorRef ref = NULL;
154 | if (AFCFileRefOpen(connectionRef, [path UTF8String], 0x1, &ref) || !ref) {
155 | continue;
156 | }
157 |
158 | [[NSFileManager defaultManager] createFileAtPath:finalizedFile contents:nil attributes:nil];
159 | NSFileHandle *handle = [NSFileHandle fileHandleForWritingAtPath:finalizedFile];
160 | if (!handle) {
161 | dsprintf(stdout, "%s\nExiting...\n", [finalizedFile UTF8String]);
162 | continue;
163 | // return 1;
164 | }
165 | int fd = [handle fileDescriptor];
166 |
167 | if (fd == -1) {
168 | dsprintf(stderr, "%sCan't open \"%s\" to write to, might be an existing file there.\n", dcolor(dc_yellow), [finalizedFile UTF8String], colorEnd());
169 | returnError = 1;
170 | continue;
171 | }
172 |
173 | size_t size = BUFSIZ;
174 | void *buffer[BUFSIZ];
175 | while (AFCFileRefRead(connectionRef, ref, buffer, &size) == 0 && size != 0 && size != -1) {
176 | write(fd, buffer, size);
177 | }
178 |
179 | [handle closeFile];
180 | AFCFileRefClose(connectionRef, ref);
181 | }
182 |
183 | AFCConnectionClose(connectionRef);
184 |
185 | for (NSString *file in filePermissions) {
186 | NSDictionary *permissions = filePermissions[file];
187 | struct stat filestat;
188 | if (stat([file UTF8String], &filestat) != 0) { continue; }
189 |
190 |
191 |
192 | for (NSString *permission in permissions) {
193 |
194 | if ([permission isEqualToString:@"st_birthtime"]) {
195 | // filestat.st_birthtimespec = time(<#time_t *#>);
196 |
197 | } else if ([permission isEqualToString:@"st_mtime"]) {
198 |
199 | }
200 | }
201 | }
202 |
203 | if (successfullyReadADirectory) {
204 | dsprintf(stdout, "Opening \"%s\"...\n", [outputDirectory UTF8String]);
205 | if (!global_options.quiet) {
206 | NSString *systemCMDString = [NSString stringWithFormat:@"open -R %@", outputDirectory];
207 |
208 | system([systemCMDString UTF8String]);
209 | }
210 | } else {
211 | dsprintf(stderr, "%sUnable to open \"%s\", likely due to not having certificates that match on this device%s\n", dcolor(dc_yellow), [appBundle UTF8String], colorEnd());
212 | returnError = EACCES;
213 | }
214 |
215 | return returnError;
216 | }
217 |
218 |
219 |
--------------------------------------------------------------------------------