├── Makefile ├── README └── dumpdecrypted.c /Makefile: -------------------------------------------------------------------------------- 1 | GCC_BIN=`xcrun --sdk iphoneos --find gcc` 2 | GCC_UNIVERSAL=$(GCC_BASE) -arch armv7 -arch armv7s -arch arm64 3 | SDK=`xcrun --sdk iphoneos --show-sdk-path` 4 | 5 | CFLAGS = 6 | GCC_BASE = $(GCC_BIN) -Os $(CFLAGS) -Wimplicit -isysroot $(SDK) -F$(SDK)/System/Library/Frameworks -F$(SDK)/System/Library/PrivateFrameworks 7 | 8 | all: dumpdecrypted.dylib 9 | 10 | dumpdecrypted.dylib: dumpdecrypted.o 11 | $(GCC_UNIVERSAL) -dynamiclib -o $@ $^ 12 | 13 | %.o: %.c 14 | $(GCC_UNIVERSAL) -c -o $@ $< 15 | 16 | clean: 17 | rm -f *.o dumpdecrypted.dylib 18 | -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- 1 | Dumps decrypted iPhone Applications to a file - better solution than those GDB scripts for non working GDB versions 2 | (C) Copyright 2011-2014 Stefan Esser 3 | 4 | 5 | Compile: 6 | 7 | First adjust the Makefile if you have a different iOS SDK installed. 8 | 9 | And then just: make 10 | 11 | 12 | Usage: 13 | 14 | iPod:~ root# DYLD_INSERT_LIBRARIES=dumpdecrypted.dylib /var/mobile/Applications/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/Scan.app/Scan 15 | mach-o decryption dumper 16 | 17 | DISCLAIMER: This tool is only meant for security research purposes, not for application crackers. 18 | 19 | [+] Found encrypted data at address 00002000 of length 1826816 bytes - type 1. 20 | [+] Opening /private/var/mobile/Applications/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/Scan.app/Scan for reading. 21 | [+] Reading header 22 | [+] Detecting header type 23 | [+] Executable is a FAT image - searching for right architecture 24 | [+] Correct arch is at offset 2408224 in the file 25 | [+] Opening Scan.decrypted for writing. 26 | [-] Failed opening. Most probably a sandbox issue. Trying something different. 27 | [+] Opening /private/var/mobile/Applications/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/tmp/Scan.decrypted for writing. 28 | [+] Copying the not encrypted start of the file 29 | [+] Dumping the decrypted data into the file 30 | [+] Copying the not encrypted remainder of the file 31 | [+] Closing original file 32 | [+] Closing dump file 33 | -------------------------------------------------------------------------------- /dumpdecrypted.c: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | Dumps decrypted iPhone Applications to a file - better solution than those GDB scripts for non working GDB versions 4 | (C) Copyright 2011-2014 Stefan Esser 5 | 6 | iPod:~ root# DYLD_INSERT_LIBRARIES=dumpdecrypted.dylib /var/mobile/Applications/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/Scan.app/Scan 7 | mach-o decryption dumper 8 | 9 | DISCLAIMER: This tool is only meant for security research purposes, not for application crackers. 10 | 11 | [+] Found encrypted data at address 00002000 of length 1826816 bytes - type 1. 12 | [+] Opening /private/var/mobile/Applications/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/Scan.app/Scan for reading. 13 | [+] Reading header 14 | [+] Detecting header type 15 | [+] Executable is a FAT image - searching for right architecture 16 | [+] Correct arch is at offset 2408224 in the file 17 | [+] Opening Scan.decrypted for writing. 18 | [-] Failed opening. Most probably a sandbox issue. Trying something different. 19 | [+] Opening /private/var/mobile/Applications/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/tmp/Scan.decrypted for writing. 20 | [+] Copying the not encrypted start of the file 21 | [+] Dumping the decrypted data into the file 22 | [+] Copying the not encrypted remainder of the file 23 | [+] Closing original file 24 | [+] Closing dump file 25 | 26 | */ 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | 35 | struct ProgramVars { 36 | struct mach_header* mh; 37 | int* NXArgcPtr; 38 | const char*** NXArgvPtr; 39 | const char*** environPtr; 40 | const char** __prognamePtr; 41 | }; 42 | 43 | #define swap32(value) (((value & 0xFF000000) >> 24) | ((value & 0x00FF0000) >> 8) | ((value & 0x0000FF00) << 8) | ((value & 0x000000FF) << 24) ) 44 | 45 | __attribute__((constructor)) 46 | void dumptofile(int argc, const char **argv, const char **envp, const char **apple, struct ProgramVars *pvars) 47 | { 48 | struct load_command *lc; 49 | struct encryption_info_command *eic; 50 | struct fat_header *fh; 51 | struct fat_arch *arch; 52 | struct mach_header *mh; 53 | char buffer[1024]; 54 | char rpath[4096],npath[4096]; /* should be big enough for PATH_MAX */ 55 | unsigned int fileoffs = 0, off_cryptid = 0, restsize; 56 | int i,fd,outfd,r,n,toread; 57 | char *tmp; 58 | 59 | printf("mach-o decryption dumper\n\n"); 60 | 61 | printf("DISCLAIMER: This tool is only meant for security research purposes, not for application crackers.\n\n"); 62 | 63 | /* detect if this is a arm64 binary */ 64 | if (pvars->mh->magic == MH_MAGIC_64) { 65 | lc = (struct load_command *)((unsigned char *)pvars->mh + sizeof(struct mach_header_64)); 66 | printf("[+] detected 64bit ARM binary in memory.\n"); 67 | } else { /* we might want to check for other errors here, too */ 68 | lc = (struct load_command *)((unsigned char *)pvars->mh + sizeof(struct mach_header)); 69 | printf("[+] detected 32bit ARM binary in memory.\n"); 70 | } 71 | 72 | /* searching all load commands for an LC_ENCRYPTION_INFO load command */ 73 | for (i=0; imh->ncmds; i++) { 74 | /*printf("Load Command (%d): %08x\n", i, lc->cmd);*/ 75 | 76 | if (lc->cmd == LC_ENCRYPTION_INFO || lc->cmd == LC_ENCRYPTION_INFO_64) { 77 | eic = (struct encryption_info_command *)lc; 78 | 79 | /* If this load command is present, but data is not crypted then exit */ 80 | if (eic->cryptid == 0) { 81 | break; 82 | } 83 | off_cryptid=(off_t)((void*)&eic->cryptid - (void*)pvars->mh); 84 | printf("[+] offset to cryptid found: @%p(from %p) = %x\n", &eic->cryptid, pvars->mh, off_cryptid); 85 | 86 | printf("[+] Found encrypted data at address %08x of length %u bytes - type %u.\n", eic->cryptoff, eic->cryptsize, eic->cryptid); 87 | 88 | if (realpath(argv[0], rpath) == NULL) { 89 | strlcpy(rpath, argv[0], sizeof(rpath)); 90 | } 91 | 92 | printf("[+] Opening %s for reading.\n", rpath); 93 | fd = open(rpath, O_RDONLY); 94 | if (fd == -1) { 95 | printf("[-] Failed opening.\n"); 96 | _exit(1); 97 | } 98 | 99 | printf("[+] Reading header\n"); 100 | n = read(fd, (void *)buffer, sizeof(buffer)); 101 | if (n != sizeof(buffer)) { 102 | printf("[W] Warning read only %d bytes\n", n); 103 | } 104 | 105 | printf("[+] Detecting header type\n"); 106 | fh = (struct fat_header *)buffer; 107 | 108 | /* Is this a FAT file - we assume the right endianess */ 109 | if (fh->magic == FAT_CIGAM) { 110 | printf("[+] Executable is a FAT image - searching for right architecture\n"); 111 | arch = (struct fat_arch *)&fh[1]; 112 | for (i=0; infat_arch); i++) { 113 | if ((pvars->mh->cputype == swap32(arch->cputype)) && (pvars->mh->cpusubtype == swap32(arch->cpusubtype))) { 114 | fileoffs = swap32(arch->offset); 115 | printf("[+] Correct arch is at offset %u in the file\n", fileoffs); 116 | break; 117 | } 118 | arch++; 119 | } 120 | if (fileoffs == 0) { 121 | printf("[-] Could not find correct arch in FAT image\n"); 122 | _exit(1); 123 | } 124 | } else if (fh->magic == MH_MAGIC || fh->magic == MH_MAGIC_64) { 125 | printf("[+] Executable is a plain MACH-O image\n"); 126 | } else { 127 | printf("[-] Executable is of unknown type\n"); 128 | _exit(1); 129 | } 130 | 131 | /* extract basename */ 132 | tmp = strrchr(rpath, '/'); 133 | if (tmp == NULL) { 134 | printf("[-] Unexpected error with filename.\n"); 135 | _exit(1); 136 | } 137 | strlcpy(npath, tmp+1, sizeof(npath)); 138 | strlcat(npath, ".decrypted", sizeof(npath)); 139 | strlcpy(buffer, npath, sizeof(buffer)); 140 | 141 | printf("[+] Opening %s for writing.\n", npath); 142 | outfd = open(npath, O_RDWR|O_CREAT|O_TRUNC, 0644); 143 | if (outfd == -1) { 144 | if (strncmp("/private/var/mobile/Applications/", rpath, 33) == 0) { 145 | printf("[-] Failed opening. Most probably a sandbox issue. Trying something different.\n"); 146 | 147 | /* create new name */ 148 | strlcpy(npath, "/private/var/mobile/Applications/", sizeof(npath)); 149 | tmp = strchr(rpath+33, '/'); 150 | if (tmp == NULL) { 151 | printf("[-] Unexpected error with filename.\n"); 152 | _exit(1); 153 | } 154 | tmp++; 155 | *tmp++ = 0; 156 | strlcat(npath, rpath+33, sizeof(npath)); 157 | strlcat(npath, "tmp/", sizeof(npath)); 158 | strlcat(npath, buffer, sizeof(npath)); 159 | printf("[+] Opening %s for writing.\n", npath); 160 | outfd = open(npath, O_RDWR|O_CREAT|O_TRUNC, 0644); 161 | } 162 | if (outfd == -1) { 163 | perror("[-] Failed opening"); 164 | printf("\n"); 165 | _exit(1); 166 | } 167 | } 168 | 169 | /* calculate address of beginning of crypted data */ 170 | n = fileoffs + eic->cryptoff; 171 | 172 | restsize = lseek(fd, 0, SEEK_END) - n - eic->cryptsize; 173 | lseek(fd, 0, SEEK_SET); 174 | 175 | printf("[+] Copying the not encrypted start of the file\n"); 176 | /* first copy all the data before the encrypted data */ 177 | while (n > 0) { 178 | toread = (n > sizeof(buffer)) ? sizeof(buffer) : n; 179 | r = read(fd, buffer, toread); 180 | if (r != toread) { 181 | printf("[-] Error reading file\n"); 182 | _exit(1); 183 | } 184 | n -= r; 185 | 186 | r = write(outfd, buffer, toread); 187 | if (r != toread) { 188 | printf("[-] Error writing file\n"); 189 | _exit(1); 190 | } 191 | } 192 | 193 | /* now write the previously encrypted data */ 194 | printf("[+] Dumping the decrypted data into the file\n"); 195 | r = write(outfd, (unsigned char *)pvars->mh + eic->cryptoff, eic->cryptsize); 196 | if (r != eic->cryptsize) { 197 | printf("[-] Error writing file\n"); 198 | _exit(1); 199 | } 200 | 201 | /* and finish with the remainder of the file */ 202 | n = restsize; 203 | lseek(fd, eic->cryptsize, SEEK_CUR); 204 | printf("[+] Copying the not encrypted remainder of the file\n"); 205 | while (n > 0) { 206 | toread = (n > sizeof(buffer)) ? sizeof(buffer) : n; 207 | r = read(fd, buffer, toread); 208 | if (r != toread) { 209 | printf("[-] Error reading file\n"); 210 | _exit(1); 211 | } 212 | n -= r; 213 | 214 | r = write(outfd, buffer, toread); 215 | if (r != toread) { 216 | printf("[-] Error writing file\n"); 217 | _exit(1); 218 | } 219 | } 220 | 221 | if (off_cryptid) { 222 | uint32_t zero=0; 223 | off_cryptid+=fileoffs; 224 | printf("[+] Setting the LC_ENCRYPTION_INFO->cryptid to 0 at offset %x\n", off_cryptid); 225 | if (lseek(outfd, off_cryptid, SEEK_SET) != off_cryptid || write(outfd, &zero, 4) != 4) { 226 | printf("[-] Error writing cryptid value\n"); 227 | } 228 | } 229 | 230 | printf("[+] Closing original file\n"); 231 | close(fd); 232 | printf("[+] Closing dump file\n"); 233 | close(outfd); 234 | 235 | _exit(1); 236 | } 237 | 238 | lc = (struct load_command *)((unsigned char *)lc+lc->cmdsize); 239 | } 240 | printf("[-] This mach-o file is not encrypted. Nothing was decrypted.\n"); 241 | _exit(1); 242 | } 243 | --------------------------------------------------------------------------------