version 1.0
This commit is contained in:
parent
7a7c452ba3
commit
635d0986cf
48
README.md
48
README.md
|
@ -1,2 +1,50 @@
|
|||
# frida-ios-dump
|
||||
pull decrypted ipa from jailbreak device
|
||||
|
||||
### Usage
|
||||
|
||||
## 1. install [frida](http://www.frida.re/) on device and mac
|
||||
|
||||
## 2. iproxy 2222 22
|
||||
|
||||
## 3. ./dump.py 微信
|
||||
|
||||
```
|
||||
➜ frida-ios-dump ./dump.py 微信
|
||||
open target app......
|
||||
start dump target app......
|
||||
start dump /var/containers/Bundle/Application/6665AA28-68CC-4845-8610-7010E96061C6/WeChat.app/WeChat
|
||||
WeChat 100% 68MB 11.4MB/s 00:05
|
||||
start dump /private/var/containers/Bundle/Application/6665AA28-68CC-4845-8610-7010E96061C6/WeChat.app/Frameworks/WCDB.framework/WCDB
|
||||
WCDB 100% 2555KB 11.0MB/s 00:00
|
||||
start dump /private/var/containers/Bundle/Application/6665AA28-68CC-4845-8610-7010E96061C6/WeChat.app/Frameworks/MMCommon.framework/MMCommon
|
||||
MMCommon 100% 979KB 10.6MB/s 00:00
|
||||
start dump /private/var/containers/Bundle/Application/6665AA28-68CC-4845-8610-7010E96061C6/WeChat.app/Frameworks/MultiMedia.framework/MultiMedia
|
||||
MultiMedia 100% 6801KB 11.1MB/s 00:00
|
||||
start dump /private/var/containers/Bundle/Application/6665AA28-68CC-4845-8610-7010E96061C6/WeChat.app/Frameworks/mars.framework/mars
|
||||
mars 100% 7462KB 11.1MB/s 00:00
|
||||
AppIcon60x60@2x.png 100% 2253 230.9KB/s 00:00
|
||||
AppIcon60x60@3x.png 100% 4334 834.8KB/s 00:00
|
||||
AppIcon76x76@2x~ipad.png 100% 2659 620.6KB/s 00:00
|
||||
AppIcon76x76~ipad.png 100% 1523 358.0KB/s 00:00
|
||||
AppIcon83.5x83.5@2x~ipad.png 100% 2725 568.9KB/s 00:00
|
||||
Assets.car 100% 10MB 11.1MB/s 00:00
|
||||
.......
|
||||
AppIntentVocabulary.plist 100% 197 52.9KB/s 00:00
|
||||
AppIntentVocabulary.plist 100% 167 43.9KB/s 00:00
|
||||
AppIntentVocabulary.plist 100% 187 50.2KB/s 00:00
|
||||
InfoPlist.strings 100% 1720 416.4KB/s 00:00
|
||||
TipsPressTalk@2x.png 100% 14KB 2.2MB/s 00:00
|
||||
mm.strings 100% 404KB 10.2MB/s 00:00
|
||||
network_setting.html 100% 1695 450.4KB/s 00:00
|
||||
InfoPlist.strings 100% 1822 454.1KB/s 00:00
|
||||
mm.strings 100% 409KB 10.2MB/s 00:00
|
||||
network_setting.html 100% 1819 477.5KB/s 00:00
|
||||
InfoPlist.strings 100% 1814 466.8KB/s 00:00
|
||||
mm.strings 100% 409KB 10.3MB/s 00:00
|
||||
network_setting.html 100% 1819 404.9KB/s 00:00
|
||||
```
|
||||
|
||||
congratulations!!! You've got a decrypted ipa file.
|
||||
|
||||
Drag to [MonkeyDev](https://github.com/AloneMonkey/MonkeyDev), Happy hacking!
|
|
@ -0,0 +1,31 @@
|
|||
//by: AloneMonkey
|
||||
|
||||
const LSApplicationWorkspace = ObjC.classes.LSApplicationWorkspace;
|
||||
|
||||
function openApplication(appid){
|
||||
const workspace = LSApplicationWorkspace.defaultWorkspace();
|
||||
return workspace.openApplicationWithBundleID_(appid);
|
||||
}
|
||||
|
||||
function getbundleid(name){
|
||||
const workspace = LSApplicationWorkspace.defaultWorkspace();
|
||||
const apps = workspace.allApplications();
|
||||
var result;
|
||||
for(var index = 0; index < apps.count(); index++){
|
||||
var proxy = apps.objectAtIndex_(index);
|
||||
if(proxy.localizedName().toString() == name){
|
||||
return proxy.bundleIdentifier().toString();
|
||||
}
|
||||
}
|
||||
return ""
|
||||
};
|
||||
|
||||
function handleMessage(message) {
|
||||
const bundleid = getbundleid(message);
|
||||
if(bundleid.length > 0){
|
||||
openApplication(bundleid);
|
||||
}
|
||||
send({opened: "ok"});
|
||||
}
|
||||
|
||||
recv(handleMessage);
|
|
@ -0,0 +1,323 @@
|
|||
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;
|
||||
|
||||
function allocStr(str) {
|
||||
return Memory.allocUtf8String(str);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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"]);
|
||||
remove = getExportFunction("f", "remove", "int", ["pointer"]);
|
||||
|
||||
function getDocumentDir() {
|
||||
var NSDocumentDirectory = 9;
|
||||
var NSUserDomainMask = 1;
|
||||
var npdirs = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, 1);
|
||||
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);
|
||||
}
|
||||
|
||||
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 FAT_MAGIC = 0xcafebabe;
|
||||
var FAT_CIGAM = 0xbebafeca;
|
||||
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;
|
||||
|
||||
function pad(str, n) {
|
||||
return Array(n-str.length+1).join("0")+str;
|
||||
}
|
||||
|
||||
function swap32(value) {
|
||||
value = pad(value.toString(16),8)
|
||||
var result = "";
|
||||
for(var i = 0; i < value.length; i=i+2){
|
||||
result += value.charAt(value.length - i - 2);
|
||||
result += value.charAt(value.length - i - 1);
|
||||
}
|
||||
return parseInt(result,16)
|
||||
}
|
||||
|
||||
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");
|
||||
return;
|
||||
}
|
||||
var modbase = modules[i].base;
|
||||
var modsize = modules[i].size;
|
||||
var newmodname = modules[i].name;
|
||||
var newmodpath = getDocumentDir() + "/" + newmodname;
|
||||
var oldmodpath = modules[i].path;
|
||||
|
||||
remove(allocStr(newmodpath));
|
||||
|
||||
var fmodule = open(newmodpath, O_CREAT | O_RDWR, 0);
|
||||
var foldmodule = open(oldmodpath, O_RDONLY, 0);
|
||||
|
||||
if (fmodule == -1 || foldmodule == -1) {
|
||||
console.log("Cannot open file" + newmodpath);
|
||||
return;
|
||||
}
|
||||
|
||||
var is64bit = false;
|
||||
var size_of_mach_header = 0;
|
||||
var magic = getU32(modbase);
|
||||
var cur_cpu_type = getU32(modbase.add(4));
|
||||
var cur_cpu_subtype = getU32(modbase.add(8));
|
||||
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 BUFSIZE = 4096;
|
||||
var buffer = malloc(BUFSIZE);
|
||||
|
||||
read(foldmodule, buffer, BUFSIZE);
|
||||
|
||||
var fileoffset = 0;
|
||||
var filesize = 0;
|
||||
magic = getU32(buffer);
|
||||
if(magic == FAT_CIGAM || magic == FAT_MAGIC){
|
||||
var off = 4;
|
||||
var archs = swap32(getU32(buffer.add(off)));
|
||||
for (var i = 0; i < archs; i++) {
|
||||
var cputype = swap32(getU32(buffer.add(off + 4)));
|
||||
var cpusubtype = swap32(getU32(buffer.add(off + 8)));
|
||||
if(cur_cpu_type == cputype && cur_cpu_subtype == cpusubtype){
|
||||
fileoffset = swap32(getU32(buffer.add(off + 12)));
|
||||
filesize = swap32(getU32(buffer.add(off + 16)));
|
||||
break;
|
||||
}
|
||||
off += 20;
|
||||
}
|
||||
|
||||
if(fileoffset == 0 || filesize == 0)
|
||||
return;
|
||||
|
||||
lseek(fmodule, 0, SEEK_SET);
|
||||
lseek(foldmodule, fileoffset, SEEK_SET);
|
||||
for(var i = 0; i < parseInt(filesize / BUFSIZE); i++) {
|
||||
read(foldmodule, buffer, BUFSIZE);
|
||||
write(fmodule, buffer, BUFSIZE);
|
||||
}
|
||||
if(filesize % BUFSIZE){
|
||||
read(foldmodule, buffer, filesize % BUFSIZE);
|
||||
write(fmodule, buffer, filesize % BUFSIZE);
|
||||
}
|
||||
}else{
|
||||
var readLen = 0;
|
||||
lseek(foldmodule, 0, SEEK_SET);
|
||||
lseek(fmodule, 0, SEEK_SET);
|
||||
while(readLen = read(foldmodule, buffer, BUFSIZE)) {
|
||||
write(fmodule, buffer, readLen);
|
||||
}
|
||||
}
|
||||
|
||||
var ncmds = getU32(modbase.add(16));
|
||||
var off = size_of_mach_header;
|
||||
var offset_cryptid = -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_cryptid = off + 16;
|
||||
crypt_off = getU32(modbase.add(off + 8));
|
||||
crypt_size = getU32(modbase.add(off + 12));
|
||||
}
|
||||
off += cmdsize;
|
||||
}
|
||||
|
||||
if (offset_cryptid != -1) {
|
||||
var tpbuf = malloc(8);
|
||||
putU64(tpbuf, 0);
|
||||
lseek(fmodule, offset_cryptid, SEEK_SET);
|
||||
write(fmodule, tpbuf, 4);
|
||||
lseek(fmodule, crypt_off, SEEK_SET);
|
||||
write(fmodule, modbase.add(crypt_off), crypt_size);
|
||||
}
|
||||
|
||||
close(fmodule);
|
||||
close(foldmodule);
|
||||
return newmodpath
|
||||
}
|
||||
|
||||
function handleMessage(message) {
|
||||
//start dump
|
||||
modules = getAllAppModules();
|
||||
for (var i = 0; i < modules.length; i++) {
|
||||
console.log("start dump " + modules[i].path);
|
||||
result = dumpModule(modules[i].path);
|
||||
send({ dump: result, path: modules[i].path});
|
||||
}
|
||||
send({app: ObjC.classes.NSBundle.mainBundle().bundlePath().toString()});
|
||||
send({done: "ok"});
|
||||
recv(handleMessage);
|
||||
}
|
||||
|
||||
recv(handleMessage);
|
|
@ -0,0 +1,129 @@
|
|||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
#Author : AloneMonkey
|
||||
#blog: www.alonemonkey.com
|
||||
|
||||
import sys
|
||||
import codecs
|
||||
import frida
|
||||
import threading
|
||||
import os
|
||||
import shutil
|
||||
|
||||
DUMP_JS = './dump.js'
|
||||
APP_JS = './app.js'
|
||||
OUTPUT = "Payload"
|
||||
file_dict = {}
|
||||
|
||||
opened = threading.Event()
|
||||
finished = threading.Event()
|
||||
|
||||
global session
|
||||
|
||||
def get_usb_iphone():
|
||||
dManager = frida.get_device_manager();
|
||||
changed = threading.Event()
|
||||
def on_changed():
|
||||
changed.set()
|
||||
dManager.on('changed',on_changed)
|
||||
|
||||
device = None
|
||||
while device is None:
|
||||
devices = [dev for dev in dManager.enumerate_devices() if dev.type == 'tether']
|
||||
if len(devices) == 0:
|
||||
print 'Waiting for usb device...'
|
||||
changed.wait()
|
||||
else:
|
||||
device = devices[0]
|
||||
|
||||
dManager.off('changed',on_changed)
|
||||
|
||||
return device
|
||||
|
||||
def gen_ipa(target):
|
||||
app_name = file_dict["app"]
|
||||
for key, value in file_dict.items():
|
||||
if key != "app":
|
||||
shutil.move(target+"/"+key, target + "/" + app_name + "/" + value);
|
||||
(shotname,extension) = os.path.splitext(app_name)
|
||||
os.system("zip -qr %s.ipa ./Payload" % shotname);
|
||||
os.system("rm -rf ./Payload");
|
||||
|
||||
def on_message(message,data):
|
||||
if message.has_key('payload'):
|
||||
payload = message['payload']
|
||||
if payload.has_key("opened"):
|
||||
opened.set();
|
||||
if payload.has_key("dump"):
|
||||
orign_path = payload["path"]
|
||||
dumppath = payload["dump"]
|
||||
os.system(u''.join(("scp -P 2222 root@localhost:", dumppath, u" ./" + OUTPUT + u"/")).encode('utf-8').strip())
|
||||
os.system(u''.join(("chmod 655 ", u'./' + OUTPUT + u'/', os.path.basename(dumppath))).encode('utf-8').strip())
|
||||
index = orign_path.find(".app/")
|
||||
file_dict[os.path.basename(dumppath)] = orign_path[index+5:]
|
||||
if payload.has_key("app"):
|
||||
apppath = payload["app"]
|
||||
os.system(u''.join(("scp -r -P 2222 root@localhost:", apppath, u" ./" + OUTPUT + u"/")).encode('utf-8').strip())
|
||||
os.system(u''.join(("chmod 755 ", u'./' + OUTPUT + u'/', os.path.basename(apppath))).encode('utf-8').strip())
|
||||
file_dict["app"] = os.path.basename(apppath)
|
||||
if payload.has_key("done"):
|
||||
gen_ipa(os.getcwd()+"/"+OUTPUT)
|
||||
finished.set();
|
||||
|
||||
def loadJsFile(session, filename):
|
||||
source = ''
|
||||
with codecs.open(filename,'r','utf-8') as f:
|
||||
source = source + f.read();
|
||||
script = session.create_script(source);
|
||||
script.on("message",on_message)
|
||||
script.load()
|
||||
return script
|
||||
|
||||
def ClearAndQuit(session):
|
||||
if session:
|
||||
session.detach()
|
||||
sys.exit(0)
|
||||
|
||||
def createDir(path):
|
||||
path = path.strip()
|
||||
path = path.rstrip("\\")
|
||||
if not os.path.exists(path):
|
||||
os.makedirs(path)
|
||||
else:
|
||||
print path + u" is existed!";
|
||||
|
||||
def main(target):
|
||||
global session
|
||||
session = None
|
||||
device = get_usb_iphone();
|
||||
#open app
|
||||
name = u'SpringBoard';
|
||||
print "open target app......"
|
||||
session = device.attach(name);
|
||||
script = loadJsFile(session, APP_JS);
|
||||
name = target.decode('utf8');
|
||||
script.post(name);
|
||||
opened.wait();
|
||||
session.detach();
|
||||
createDir(os.getcwd()+"/"+OUTPUT)
|
||||
print "start dump target app......"
|
||||
session = device.attach(name);
|
||||
script = loadJsFile(session, DUMP_JS);
|
||||
script.post("dump");
|
||||
finished.wait();
|
||||
ClearAndQuit(session);
|
||||
|
||||
if __name__ == "__main__":
|
||||
if len(sys.argv) < 2:
|
||||
sys.exit(0)
|
||||
else:
|
||||
try:
|
||||
main(sys.argv[1])
|
||||
except KeyboardInterrupt:
|
||||
print 2
|
||||
if session:
|
||||
session.detach()
|
||||
sys.exit()
|
||||
except:
|
||||
pass
|
Loading…
Reference in New Issue