Last active
December 27, 2017 06:45
-
-
Save NikoXu/a7222d415fc8c279978ae76571c74d36 to your computer and use it in GitHub Desktop.
$ frida --codeshare lichao890427/dump-ios -f YOUR_BINARY Fingerprint: bde3ec627bac9237aeff029d6014424b51fdbe8503200a8faaf2f1e099d7da95
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| // https://codeshare.frida.re/@lichao890427/dump_ios/ | |
| // https://github.com/lichao890427/frida_script analysis_hook.js => submit issues | |
| /* | |
| Usage: dumpModule("BWA.app"); dumpModule("aaa.dylib") | |
| [iPhone::PID::20457]-> dumpModule(".app") | |
| Fix decrypted at:ac0 | |
| Fix decrypted at:4000 | |
| */ | |
| var O_RDONLY = 0; | |
| var O_WRONLY = 1; | |
| var O_RDWR = 2; | |
| var O_CREAT = 512; | |
| var SEEK_SET = 0; | |
| var SEEK_CUR = 1; | |
| var SEEK_END = 2; | |
| NSString = ObjC.classes.NSString; | |
| NSFileManager = ObjC.classes.NSFileManager; | |
| function allocStr(str) { | |
| return Memory.allocUtf8String(str); | |
| } | |
| function getNSString(str) { | |
| return NSString.stringWithUTF8String_(Memory.allocUtf8String(str)); | |
| } | |
| function getStr(addr) { | |
| if (typeof addr == "number") { | |
| addr = ptr(addr); | |
| } | |
| return Memory.readUtf8String(addr); | |
| } | |
| function getStrSize(addr, size) { | |
| if (typeof addr == "number") { | |
| addr = ptr(addr); | |
| } | |
| return Memory.readUtf8String(addr, size); | |
| } | |
| function putStr(addr, str) { | |
| if (typeof addr == "number") { | |
| addr = ptr(addr); | |
| } | |
| return Memory.writeUtf8String(addr, str); | |
| } | |
| function getByteArr(addr, l) { | |
| if (typeof addr == "number") { | |
| addr = ptr(addr); | |
| } | |
| return Memory.readByteArray(addr, l); | |
| } | |
| function getU8(addr) { | |
| if (typeof addr == "number") { | |
| addr = ptr(addr); | |
| } | |
| return Memory.readU8(addr); | |
| } | |
| function putU8(addr, n) { | |
| if (typeof addr == "number") { | |
| addr = ptr(addr); | |
| } | |
| return Memory.writeU8(addr, n); | |
| } | |
| function getU16(addr) { | |
| if (typeof addr == "number") { | |
| addr = ptr(addr); | |
| } | |
| return Memory.readU16(addr); | |
| } | |
| function putU16(addr, n) { | |
| if (typeof addr == "number") { | |
| addr = ptr(addr); | |
| } | |
| return Memory.writeU16(addr, n); | |
| } | |
| function getU32(addr) { | |
| if (typeof addr == "number") { | |
| addr = ptr(addr); | |
| } | |
| return Memory.readU32(addr); | |
| } | |
| function putU32(addr, n) { | |
| if (typeof addr == "number") { | |
| addr = ptr(addr); | |
| } | |
| return Memory.writeU32(addr, n); | |
| } | |
| function getU64(addr) { | |
| if (typeof addr == "number") { | |
| addr = ptr(addr); | |
| } | |
| return Memory.readU64(addr); | |
| } | |
| function putU64(addr, n) { | |
| if (typeof addr == "number") { | |
| addr = ptr(addr); | |
| } | |
| return Memory.writeU64(addr, n); | |
| } | |
| function getPt(addr) { | |
| if (typeof addr == "number") { | |
| addr = ptr(addr); | |
| } | |
| return Memory.readPointer(addr); | |
| } | |
| function putPt(addr, n) { | |
| if (typeof addr == "number") { | |
| addr = ptr(addr); | |
| } | |
| if (typeof n == "number") { | |
| n = ptr(n); | |
| } | |
| return Memory.writePointer(addr, n); | |
| } | |
| function malloc(size) { | |
| return Memory.alloc(size); | |
| } | |
| function getExportFunction(type, name, ret, args) { | |
| var nptr; | |
| nptr = Module.findExportByName(null, name); | |
| if (nptr === null) { | |
| console.log("cannot find " + name); | |
| return null; | |
| } else { | |
| if (type === "f") { | |
| var funclet = new NativeFunction(nptr, ret, args); | |
| if (typeof funclet === "undefined") { | |
| console.log("parse error " + name); | |
| return null; | |
| } | |
| return funclet; | |
| } else if (type === "d") { | |
| var datalet = Memory.readPointer(nptr); | |
| if (typeof datalet === "undefined") { | |
| console.log("parse error " + name); | |
| return null; | |
| } | |
| return datalet; | |
| } | |
| } | |
| } | |
| function dumpMemory(addr, length) { | |
| console.log(hexdump(Memory.readByteArray(addr, length), { | |
| offset: 0, | |
| length: length, | |
| header: true, | |
| ansi: true | |
| })); | |
| } | |
| NSSearchPathForDirectoriesInDomains = getExportFunction("f", "NSSearchPathForDirectoriesInDomains", "pointer", ["int", "int", "int"]); | |
| wrapper_open = getExportFunction("f", "open", "int", ["pointer", "int", "int"]); | |
| read = getExportFunction("f", "read", "int", ["int", "pointer", "int"]); | |
| write = getExportFunction("f", "write", "int", ["int", "pointer", "int"]); | |
| lseek = getExportFunction("f", "lseek", "int64", ["int", "int64", "int"]); | |
| close = getExportFunction("f", "close", "int", ["int"]); | |
| function getCacheDir(index) { | |
| var NSUserDomainMask = 1; | |
| var npdirs = NSSearchPathForDirectoriesInDomains(index, NSUserDomainMask, 1); | |
| var len = ObjC.Object(npdirs).count(); | |
| if (len == 0) { | |
| return ''; | |
| } | |
| return ObjC.Object(npdirs).objectAtIndex_(0).toString(); | |
| } | |
| function open(pathname, flags, mode) { | |
| if (typeof pathname == "string") { | |
| pathname = allocStr(pathname); | |
| } | |
| return wrapper_open(pathname, flags, mode); | |
| } | |
| // Export function | |
| var modules = null; | |
| function getAllAppModules() { | |
| if (modules == null) { | |
| modules = new Array(); | |
| var tmpmods = Process.enumerateModulesSync(); | |
| for (var i = 0; i < tmpmods.length; i++) { | |
| if (tmpmods[i].path.indexOf(".app") != -1) { | |
| modules.push(tmpmods[i]); | |
| } | |
| } | |
| } | |
| return modules; | |
| } | |
| var MH_MAGIC = 0xfeedface; | |
| var MH_CIGAM = 0xcefaedfe; | |
| var MH_MAGIC_64 = 0xfeedfacf; | |
| var MH_CIGAM_64 = 0xcffaedfe; | |
| var LC_SEGMENT = 0x1; | |
| var LC_SEGMENT_64 = 0x19; | |
| var LC_ENCRYPTION_INFO = 0x21; | |
| var LC_ENCRYPTION_INFO_64 = 0x2C; | |
| // You can dump .app or dylib (Encrypt/No Encrypt) | |
| function dumpModule(name) { | |
| if (modules == null) { | |
| modules = getAllAppModules(); | |
| } | |
| var targetmod = null; | |
| for (var i = 0; i < modules.length; i++) { | |
| if (modules[i].path.indexOf(name) != -1) { | |
| targetmod = modules[i]; | |
| break; | |
| } | |
| } | |
| if (targetmod == null) { | |
| console.log("Cannot find module"); | |
| } | |
| var modbase = modules[i].base; | |
| var modsize = modules[i].size; | |
| var newmodname = modules[i].name + ".decrypted"; | |
| var finddir = false; | |
| var newmodpath = ""; | |
| var fmodule = -1; | |
| var index = 1; | |
| while (!finddir) { // 找到一个可写路径 | |
| try { | |
| var base = getCacheDir(index); | |
| if (base != null) { | |
| newmodpath = getCacheDir(index) + "/" + newmodname; | |
| fmodule = open(newmodpath, O_CREAT | O_RDWR, 0); | |
| if (fmodule != -1) { | |
| break; | |
| }; | |
| } | |
| } | |
| catch(e) { | |
| } | |
| index++; | |
| } | |
| var oldmodpath = modules[i].path; | |
| var foldmodule = open(oldmodpath, O_RDONLY, 0); | |
| if (fmodule == -1 || foldmodule == -1) { | |
| console.log("Cannot open file" + newmodpath); | |
| return; | |
| } | |
| var BUFSIZE = 4096; | |
| var buffer = malloc(BUFSIZE); | |
| while (read(foldmodule, buffer, BUFSIZE)) { | |
| write(fmodule, buffer, BUFSIZE); | |
| } | |
| // Find crypt info and recover | |
| var is64bit = false; | |
| var size_of_mach_header = 0; | |
| var magic = getU32(modbase); | |
| if (magic == MH_MAGIC || magic == MH_CIGAM) { | |
| is64bit = false; | |
| size_of_mach_header = 28; | |
| } | |
| else if (magic == MH_MAGIC_64 || magic == MH_CIGAM_64) { | |
| is64bit = true; | |
| size_of_mach_header = 32; | |
| } | |
| var ncmds = getU32(modbase.add(16)); | |
| var off = size_of_mach_header; | |
| var offset_cryptoff = -1; | |
| var crypt_off = 0; | |
| var crypt_size = 0; | |
| var segments = []; | |
| for (var i = 0; i < ncmds; i++) { | |
| var cmd = getU32(modbase.add(off)); | |
| var cmdsize = getU32(modbase.add(off + 4)); | |
| if (cmd == LC_ENCRYPTION_INFO || cmd == LC_ENCRYPTION_INFO_64) { | |
| offset_cryptoff = off + 8; | |
| crypt_off = getU32(modbase.add(off + 8)); | |
| crypt_size = getU32(modbase.add(off + 12)); | |
| } | |
| off += cmdsize; | |
| } | |
| if (offset_cryptoff != -1) { | |
| var tpbuf = malloc(8); | |
| console.log("Fix decrypted at:" + offset_cryptoff.toString(16)); | |
| putU64(tpbuf, 0); | |
| lseek(fmodule, offset_cryptoff, SEEK_SET); | |
| write(fmodule, tpbuf, 8); | |
| console.log("Fix decrypted at:" + crypt_off.toString(16)); | |
| lseek(fmodule, crypt_off, SEEK_SET); | |
| write(fmodule, modbase.add(crypt_off), crypt_size); | |
| } | |
| console.log("Decrypted file at:" + newmodpath + " 0x" + modsize.toString(16)); | |
| close(fmodule); | |
| close(foldmodule); | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment