├── LICENSE.md ├── README.md ├── dylibify-arm64 ├── dylibify-x86 ├── main.m ├── make-ios.sh └── make.sh /LICENSE.md: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 Jake James 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 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # dylibify 2 | Transform any ARM macho executable to a dynamic library 3 | 4 | # How this works? 5 | It's my really first time playing with Mach-O's like this so don't expect top quality code/explanation, but in a nutshell here's what it does: 6 | 7 | - Patch mach header so it is identified as a dylib instead of an executable and add MH_NO_REEXPORTED_DYLIBS flag 8 | - Get rid of PAGEZERO since with it we can't load the dylib 9 | - Add a LC_ID_DYLIB command where PAGEZERO previously was to identify the dylib 10 | - Patch opcodes: Since we got rid of PAGEZERO we have one less segment thus we need to patch whatever is referencing to SEGMENT X to SEGMENT X-1. 11 | - Look into the code comments for more details 12 | -------------------------------------------------------------------------------- /dylibify-arm64: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jakeajames/dylibify/17cc528402714ad1bd918cbb2ebcb6976e80540a/dylibify-arm64 -------------------------------------------------------------------------------- /dylibify-x86: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jakeajames/dylibify/17cc528402714ad1bd918cbb2ebcb6976e80540a/dylibify-x86 -------------------------------------------------------------------------------- /main.m: -------------------------------------------------------------------------------- 1 | // 2 | // main.m 3 | // dylibify 4 | // 5 | // Created by Jake James on 7/13/18. 6 | // Copyright © 2018 Jake James. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | #import 12 | #import 13 | 14 | #include 15 | #include 16 | #include 17 | 18 | #define SWAP32(p) __builtin_bswap32(p) 19 | 20 | static void *load_bytes(FILE *obj_file, off_t offset, size_t size) { 21 | void *buf = calloc(1, size); 22 | fseek(obj_file, offset, SEEK_SET); 23 | fread(buf, size, 1, obj_file); 24 | return buf; 25 | } 26 | 27 | void write_bytes(FILE *obj_file, off_t offset, size_t size, void *bytes) { 28 | fseek(obj_file, offset, SEEK_SET); 29 | fwrite(bytes, size, 1, obj_file); 30 | } 31 | 32 | void patch_mach_header(FILE *obj_file, off_t offset, void *mh, BOOL is64bit) { 33 | if (is64bit) { 34 | printf("[*] Patching filetype & flags\n"); 35 | printf("[-] FILETYPE was 0x%x\n", ((struct mach_header_64 *)mh)->filetype); 36 | printf("[-] FLAGS were: 0x%x\n", ((struct mach_header_64 *)mh)->flags); 37 | 38 | //----Change MH_EXECUTE to MH_DYLIB----// 39 | ((struct mach_header_64 *)mh)->filetype = MH_DYLIB; 40 | ((struct mach_header_64 *)mh)->flags |= MH_NO_REEXPORTED_DYLIBS; 41 | 42 | printf("[+] FILETYPE is 0x%x\n", ((struct mach_header_64 *)mh)->filetype); 43 | printf("[+] FLAGS are: 0x%x\n", ((struct mach_header_64 *)mh)->flags); 44 | 45 | write_bytes(obj_file, offset, sizeof(struct mach_header_64), mh); 46 | } 47 | else { 48 | printf("[*] Patching filetype & flags\n"); 49 | printf("[-] FILETYPE was 0x%x\n", ((struct mach_header *)mh)->filetype); 50 | printf("[-] FLAGS were: 0x%x\n", ((struct mach_header *)mh)->flags); 51 | 52 | ((struct mach_header *)mh)->filetype = MH_DYLIB; 53 | ((struct mach_header *)mh)->flags |= MH_NO_REEXPORTED_DYLIBS; 54 | 55 | printf("[+] FILETYPE is 0x%x\n", ((struct mach_header *)mh)->filetype); 56 | printf("[+] FLAGS are: 0x%x\n", ((struct mach_header *)mh)->flags); 57 | 58 | write_bytes(obj_file, offset, sizeof(struct mach_header), mh); 59 | } 60 | } 61 | 62 | void patch_pagezero(FILE *obj_file, off_t offset, struct load_command *cmd, BOOL copied, void *seg, size_t sizeofseg, const char *target) { 63 | 64 | uint32_t size = cmd->cmdsize; 65 | 66 | printf("\t\t[*] Patching __PAGEZERO\n"); 67 | printf("\t\t[*] Nullifying\n"); 68 | 69 | //----Nullify it----// 70 | memset(seg, 0, sizeofseg); 71 | 72 | //----Allocate data for our new command + @executable_path/NAME_OF_TARGET.dylib----// 73 | //----So, if you plan to link with it, don't rename the file and put it on same location as binary----// 74 | //----Obviously, you can easily patch that yourself, if for some reason you want to----// 75 | struct dylib_command *dylib_cmd = (struct dylib_command*)malloc(sizeof(struct dylib_command) + [@(target) lastPathComponent].length + 18); 76 | 77 | dylib_cmd->cmd = LC_ID_DYLIB; 78 | dylib_cmd->cmdsize = size; 79 | //----The string will be located where our dylib command ends----// 80 | dylib_cmd->dylib.name.offset = sizeof(struct dylib_command); 81 | dylib_cmd->dylib.timestamp = 1; 82 | dylib_cmd->dylib.current_version = 0; 83 | dylib_cmd->dylib.compatibility_version = 0; 84 | 85 | //----If it's a FAT binary do not copy it twice----// 86 | if (!copied) { 87 | strcpy((char *)dylib_cmd + sizeof(struct dylib_command), ([[NSString stringWithFormat:@"@executable_path/%@", [@(target) lastPathComponent]] UTF8String])); 88 | } 89 | 90 | printf("\t\t[*] Doing the magic\n"); 91 | 92 | write_bytes(obj_file, offset, sizeofseg, dylib_cmd); 93 | 94 | free(dylib_cmd); 95 | } 96 | 97 | void patch_dyldinfo(FILE *file, off_t offset, struct dyld_info_command *dyldinfo) { 98 | if (dyldinfo->rebase_off != 0) { 99 | 100 | //----Some maths takes place in here, we need to iterate over the opcodes----// 101 | //----Some of them are just 1 byte, some are 2 bytes, some are whole strings----// 102 | //----We only need the ones referencing to segments, which are 1 byte----// 103 | 104 | for (int i = 0; i < dyldinfo->rebase_size; i++) { 105 | uint8_t *bytes = load_bytes(file, offset + dyldinfo->rebase_off + i, sizeof(uint8_t)); 106 | 107 | if ((*bytes & REBASE_OPCODE_MASK) == REBASE_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB) { 108 | printf("\t\t[-] REBASE_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB before = 0x%x\n", *bytes); 109 | *bytes -= 1; // "-1" -> one less segment = previous segment 110 | write_bytes(file, offset + dyldinfo->rebase_off + i, sizeof(uint8_t), bytes); 111 | bytes = load_bytes(file, offset + dyldinfo->rebase_off + i, sizeof(uint8_t)); 112 | printf("\t\t[+] REBASE_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB now = 0x%x\n", *bytes); 113 | break; 114 | } 115 | free(bytes); 116 | } 117 | } 118 | if(dyldinfo->bind_off != 0) { 119 | for (int i = 0; i < dyldinfo->bind_size; i++) { 120 | uint8_t *bytes = load_bytes(file, offset + dyldinfo->bind_off + i, sizeof(uint8_t)); 121 | 122 | switch (*bytes & BIND_OPCODE_MASK) { 123 | case BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB: 124 | printf("\t\t[-] BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB before = 0x%x\n", *bytes); 125 | if ((*bytes & 0xF) == 2) *bytes -= 1; 126 | write_bytes(file, offset + dyldinfo->bind_off + i, sizeof(uint8_t), bytes); 127 | bytes = load_bytes(file, offset + dyldinfo->bind_off + i, sizeof(uint8_t)); 128 | printf("\t\t[+] BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB now = 0x%x\n", *bytes); 129 | while (*bytes != BIND_OPCODE_DO_BIND) { 130 | i += 1; 131 | bytes = load_bytes(file, offset + dyldinfo->bind_off + i, sizeof(uint8_t)); 132 | } 133 | break; 134 | case BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM: // this means a string is coming next 135 | while (*bytes != 0) { //all strings end with 0 (null byte) and don't have 0 in their body 136 | i += 1; 137 | bytes = load_bytes(file, offset + dyldinfo->bind_off + i, sizeof(uint8_t)); 138 | } 139 | break; 140 | case BIND_OPCODE_ADD_ADDR_ULEB: 141 | while (*bytes != BIND_OPCODE_DO_BIND) { 142 | i += 1; 143 | bytes = load_bytes(file, offset + dyldinfo->bind_off + i, sizeof(uint8_t)); 144 | } 145 | break; 146 | default: 147 | break; 148 | } 149 | free(bytes); 150 | } 151 | } 152 | if(dyldinfo->lazy_bind_off != 0) { 153 | for (int i = 0; i < dyldinfo->lazy_bind_size; i++) { 154 | uint8_t *bytes = load_bytes(file, offset + dyldinfo->lazy_bind_off + i, sizeof(uint8_t)); 155 | 156 | switch (*bytes & BIND_OPCODE_MASK) { 157 | case BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB: 158 | printf("\t\t[-] BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB before = 0x%x\n", *bytes); 159 | if ((*bytes & 0xF) == 2) *bytes -= 1; 160 | write_bytes(file, offset + dyldinfo->lazy_bind_off + i, sizeof(uint8_t), bytes); 161 | bytes = load_bytes(file, offset + dyldinfo->lazy_bind_off + i, sizeof(uint8_t)); 162 | printf("\t\t[+] BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB now = 0x%x\n", *bytes); 163 | while (*bytes != BIND_OPCODE_DO_BIND) { 164 | i += 1; 165 | bytes = load_bytes(file, offset + dyldinfo->lazy_bind_off + i, sizeof(uint8_t)); 166 | } 167 | break; 168 | case BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM: 169 | while (*bytes != 0) { 170 | i += 1; 171 | bytes = load_bytes(file, offset + dyldinfo->lazy_bind_off + i, sizeof(uint8_t)); 172 | } 173 | break; 174 | case BIND_OPCODE_ADD_ADDR_ULEB: 175 | while (*bytes != BIND_OPCODE_DO_BIND) { 176 | i += 1; 177 | bytes = load_bytes(file, offset + dyldinfo->lazy_bind_off + i, sizeof(uint8_t)); 178 | } 179 | break; 180 | default: 181 | break; 182 | } 183 | free(bytes); 184 | } 185 | } 186 | if(dyldinfo->weak_bind_off != 0) { 187 | for (int i = 0; i < dyldinfo->weak_bind_size; i++) { 188 | uint8_t *bytes = load_bytes(file, offset + dyldinfo->weak_bind_off + i, sizeof(uint8_t)); 189 | 190 | switch (*bytes & BIND_OPCODE_MASK) { 191 | case BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB: 192 | printf("\t\t[-] BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB before = 0x%x\n", *bytes); 193 | if ((*bytes & 0xF) == 2) *bytes -= 1; 194 | write_bytes(file, offset + dyldinfo->weak_bind_off + i, sizeof(uint8_t), bytes); 195 | bytes = load_bytes(file, offset + dyldinfo->weak_bind_off + i, sizeof(uint8_t)); 196 | printf("\t\t[+] BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB now = 0x%x\n", *bytes); 197 | while (*bytes != BIND_OPCODE_DO_BIND) { 198 | i += 1; 199 | bytes = load_bytes(file, offset + dyldinfo->weak_bind_off + i, sizeof(uint8_t)); 200 | } 201 | break; 202 | case BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM: 203 | while (*bytes != 0) { 204 | i += 1; 205 | bytes = load_bytes(file, offset + dyldinfo->weak_bind_off + i, sizeof(uint8_t)); 206 | } 207 | break; 208 | case BIND_OPCODE_ADD_ADDR_ULEB: 209 | while (*bytes != BIND_OPCODE_DO_BIND) { 210 | i += 1; 211 | bytes = load_bytes(file, offset + dyldinfo->weak_bind_off + i, sizeof(uint8_t)); 212 | } 213 | break; 214 | default: 215 | break; 216 | } 217 | free(bytes); 218 | } 219 | } 220 | } 221 | 222 | int dylibify(const char *macho, const char *saveto) { 223 | 224 | NSError *error; 225 | NSFileManager *fileManager = [NSFileManager defaultManager]; 226 | 227 | //----Make sure we don't overwrite any file----// 228 | if ([fileManager fileExistsAtPath:@(saveto)]) { 229 | printf("[!] %s file exists!\n", saveto); 230 | return -1; 231 | } 232 | 233 | //----Create a copy of the file on the target destination----// 234 | [fileManager copyItemAtPath:@(macho) toPath:@(saveto) error:&error]; 235 | 236 | //----Handle errors----// 237 | if (error) { 238 | printf("[!] %s\n", [[error localizedDescription] UTF8String]); 239 | return -1; 240 | } 241 | 242 | 243 | 244 | //----Open the copied file for updating, in binary mode----// 245 | FILE *file = fopen(saveto, "r+b"); 246 | 247 | //----This variable will hold the binary location as we move on through reading it----// 248 | size_t offset = 0; 249 | BOOL copied = false; 250 | int ncmds = 0; 251 | struct load_command *cmd = NULL; 252 | uint32_t *magic = load_bytes(file, offset, sizeof(uint32_t)); //at offset 0 we have the magic number 253 | printf("[i] MAGIC = 0x%x\n", *magic); 254 | 255 | //----64bit magic number----// 256 | if (*magic == 0xFEEDFACF) { 257 | 258 | printf("[i] 64bit binary\n"); 259 | 260 | struct mach_header_64 *mh64 = load_bytes(file, offset, sizeof(struct mach_header_64)); 261 | 262 | //----Patch filetype and add MH_NO_REEXPORTED_DYLIB flag (required for linking with it)----// 263 | patch_mach_header(file, offset, mh64, true); //patch 264 | offset += sizeof(struct mach_header_64); 265 | ncmds = mh64->ncmds; 266 | free(mh64); 267 | 268 | printf("[i] %d LOAD COMMANDS\n", ncmds); 269 | 270 | for (int i = 0; i < ncmds; i++) { 271 | cmd = load_bytes(file, offset, sizeof(struct load_command)); 272 | 273 | if (cmd->cmd == LC_SEGMENT_64) { 274 | struct segment_command_64 *seg64 = load_bytes(file, offset, sizeof(struct segment_command_64)); 275 | 276 | printf("\t[i] LC_SEGMENT_64 (%s)\n", seg64->segname); 277 | 278 | //----Dylibs don't have the PAGEZERO segment, replace it with a LC_ID_DYLIB command----// 279 | if (!strcmp(seg64->segname, "__PAGEZERO")) { 280 | patch_pagezero(file, offset, cmd, copied, seg64, sizeof(struct segment_command_64), saveto); 281 | } 282 | free(seg64); 283 | } 284 | else if (cmd->cmd == LC_DYLD_INFO_ONLY) { 285 | printf("[*] Found DYLD_INFO_ONLY!\n"); 286 | struct dyld_info_command *dyldinfo = load_bytes(file, offset, sizeof(struct dyld_info_command)); 287 | 288 | //----Since we removed one segment we have to to rework opcodes so DATA is not confused with LINKEDIT----// 289 | patch_dyldinfo(file, 0, dyldinfo); 290 | free(dyldinfo); 291 | } 292 | else { 293 | printf("[i] LOAD COMMAND %d = 0x%x\n", i, cmd->cmd); 294 | } 295 | offset += cmd->cmdsize; 296 | free(cmd); 297 | } 298 | } 299 | //----32bit magic number----// 300 | else if (*magic == 0xFEEDFACE) { 301 | 302 | printf("[i] 32bit binary\n"); 303 | 304 | struct mach_header *mh = load_bytes(file, offset, sizeof(struct mach_header)); 305 | patch_mach_header(file, offset, mh, false); 306 | offset += sizeof(struct mach_header); 307 | ncmds = mh->ncmds; 308 | free(mh); 309 | 310 | printf("[i] %d LOAD COMMANDS\n", ncmds); 311 | 312 | for (int i = 0; i < ncmds; i++) { 313 | cmd = load_bytes(file, offset, sizeof(struct load_command)); 314 | if (cmd->cmd == LC_SEGMENT) { 315 | struct segment_command *seg = load_bytes(file, offset, sizeof(struct segment_command)); 316 | 317 | printf("\t[i] LC_SEGMENT (%s)\n", seg->segname); 318 | 319 | if (!strcmp(seg->segname, "__PAGEZERO")) { 320 | patch_pagezero(file, offset, cmd, copied, seg, sizeof(struct segment_command), saveto); 321 | } 322 | 323 | free(seg); 324 | } 325 | else if (cmd->cmd == LC_DYLD_INFO_ONLY) { 326 | printf("[*] Found DYLD_INFO_ONLY!\n"); 327 | struct dyld_info_command *dyldinfo = load_bytes(file, offset, sizeof(struct dyld_info_command)); 328 | patch_dyldinfo(file, 0, dyldinfo); 329 | free(dyldinfo); 330 | } 331 | else { 332 | printf("[i] LOAD COMMAND %d = 0x%x\n", i, cmd->cmd); 333 | } 334 | offset += cmd->cmdsize; 335 | free(cmd); 336 | } 337 | } 338 | //----More than one architecture----// 339 | else if (*magic == 0xBEBAFECA) { 340 | 341 | printf("[i] FAT binary\n"); 342 | 343 | size_t arch_offset = sizeof(struct fat_header); 344 | struct fat_header *fat = load_bytes(file, offset, sizeof(struct fat_header)); 345 | struct fat_arch *arch = load_bytes(file, arch_offset, sizeof(struct fat_arch)); 346 | int n = SWAP32(fat->nfat_arch); 347 | 348 | printf("[i] %d ARCHS\n", n); 349 | 350 | while (n-- > 0) { 351 | offset = SWAP32(arch->offset); 352 | magic = load_bytes(file, offset, sizeof(uint32_t)); 353 | 354 | if (*magic == 0xFEEDFACF) { 355 | printf("[i] Found 64bit architecture\n"); 356 | 357 | struct mach_header_64 *mh64 = load_bytes(file, offset, sizeof(struct mach_header_64)); 358 | patch_mach_header(file, offset, mh64, true); 359 | offset += sizeof(struct mach_header_64); 360 | ncmds = mh64->ncmds; 361 | free(mh64); 362 | 363 | printf("[i] %d LOAD COMMANDS\n", ncmds); 364 | 365 | for (int i = 0; i < ncmds; i++) { 366 | cmd = load_bytes(file, offset, sizeof(struct load_command)); 367 | if (cmd->cmd == LC_SEGMENT_64) { 368 | struct segment_command_64 *seg64 = load_bytes(file, offset, sizeof(struct segment_command_64)); 369 | 370 | printf("\t[i] LC_SEGMENT_64 (%s)\n", seg64->segname); 371 | 372 | if (!strcmp(seg64->segname, "__PAGEZERO")) { 373 | patch_pagezero(file, offset, cmd, copied, seg64, sizeof(struct segment_command_64), saveto); 374 | copied = true; 375 | } 376 | free(seg64); 377 | } 378 | else if (cmd->cmd == LC_DYLD_INFO_ONLY) { 379 | printf("[*] Found DYLD_INFO_ONLY!\n"); 380 | struct dyld_info_command *dyldinfo = load_bytes(file, offset, sizeof(struct dyld_info_command)); 381 | patch_dyldinfo(file, SWAP32(arch->offset), dyldinfo); 382 | free(dyldinfo); 383 | } 384 | else { 385 | printf("[i] LOAD COMMAND %d = 0x%x\n", i, cmd->cmd); 386 | } 387 | offset += cmd->cmdsize; 388 | free(cmd); 389 | } 390 | } 391 | else if (*magic == 0xFEEDFACE) { 392 | printf("[i] Found 32bit architecture\n"); 393 | 394 | struct mach_header *mh = load_bytes(file, offset, sizeof(struct mach_header)); 395 | patch_mach_header(file, offset, mh, false); 396 | offset += sizeof(struct mach_header); 397 | ncmds = mh->ncmds; 398 | free(mh); 399 | 400 | printf("[i] %d LOAD COMMANDS\n", ncmds); 401 | 402 | for (int i = 0; i < ncmds; i++) { 403 | cmd = load_bytes(file, offset, sizeof(struct load_command)); 404 | if (cmd->cmd == LC_SEGMENT) { 405 | struct segment_command *seg = load_bytes(file, offset, sizeof(struct segment_command)); 406 | printf("\t[i] LC_SEGMENT (%s)\n", seg->segname); 407 | if (!strcmp(seg->segname, "__PAGEZERO")) { 408 | patch_pagezero(file, offset, cmd, copied, seg, sizeof(struct segment_command), saveto); 409 | copied = true; 410 | } 411 | free(seg); 412 | } 413 | else if (cmd->cmd == LC_DYLD_INFO_ONLY) { 414 | printf("[*] Found DYLD_INFO_ONLY!\n"); 415 | struct dyld_info_command *dyldinfo = load_bytes(file, offset, sizeof(struct dyld_info_command)); 416 | patch_dyldinfo(file, SWAP32(arch->offset), dyldinfo); 417 | free(dyldinfo); 418 | } 419 | else { 420 | printf("[i] LOAD COMMAND %d = 0x%x\n", i, cmd->cmd); 421 | } 422 | offset += cmd->cmdsize; 423 | free(cmd); 424 | } 425 | } 426 | else { 427 | printf("[!] Unrecognized architecture with MAGIC = 0x%x\n", *magic); 428 | continue; 429 | } 430 | arch_offset += sizeof(struct fat_arch); 431 | arch = load_bytes(file, arch_offset, sizeof(struct fat_arch)); 432 | } 433 | 434 | free(fat); 435 | free(arch); 436 | } 437 | else { 438 | printf("[!] Unrecognized file\n"); 439 | goto err; 440 | } 441 | 442 | err: 443 | fclose(file); 444 | return -1; 445 | } 446 | 447 | int main(int argc, const char * argv[]) { 448 | if (argc != 3) { 449 | printf("Usage:\n\t%s \nExample:\n\t%s /usr/bin/executable /usr/lib/dylibified.dylib\n", argv[0], argv[0]); 450 | return -1; 451 | } 452 | 453 | dylibify(argv[1], argv[2]); 454 | return 0; 455 | } 456 | -------------------------------------------------------------------------------- /make-ios.sh: -------------------------------------------------------------------------------- 1 | clang -isysroot /var/theos/sdks/*.sdk main.m -framework Foundation -fobjc-arc -o dylibify 2 | echo 'platform-application' > /tmp/ENTS.xml 3 | ldid -S/tmp/ENTS.xml dylibify 4 | rm /tmp/ENTS.xml 5 | -------------------------------------------------------------------------------- /make.sh: -------------------------------------------------------------------------------- 1 | clang main.m -framework Foundation -fobjc-arc -o dylibify 2 | --------------------------------------------------------------------------------