1 Star 6 Fork 2

刘柏江/adcpp-ios-dump

加入 Gitee
与超过 1400万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
文件
克隆/下载
adcpp-ios-dump.mm 5.41 KB
一键复制 编辑 原始数据 按行查看 历史
GeekNeo 提交于 2022-02-21 15:18 +08:00 . fix dump macho without LC_VERSION_MIN_IPHONEOS.
/*
一键Dump iOS加密MachO至A64Dbg与之对应的缓存目录。
*/
// 参见 def adcpp_ios_dump(data) 函数
#define dump_recv_name "adcpp_ios_dump"
#define logprefix "adcpp_ios_dump : "
// adcpp的api接口定义在:https://gitee.com/geekneo/A64Dbg/blob/master/adcpp/adcpp.hpp
#define dump2py(...) buf2py(dump_recv_name, __VA_ARGS__)
// adcpp.hpp已经自动包含了常用的C/C++/ObjC头文件,此处我们只需要额外包含一些不常用的头文件即可
#include <mach-o/dyld.h>
#include <mach/mach.h>
#include <mach/mach_init.h>
#include <mach/mach_port.h>
#include <mach/task.h>
#include <mach/thread_act.h>
#include <mach/thread_status.h>
#define max_file_name 64
// 文件传输协议字段
struct dump_info {
// 为了简化结构,直接固定名字长度
char filename[max_file_name];
// version_min_command
// uint32_t version; /* X.Y.Z is encoded in nibbles xxxx.yy.zz */
// 用于确定应该存放在decache的哪一个版本目录
char min_version[12];
// 文件大小和数据
int filesize;
char filebuff[0];
};
// 对当前指定的MachO进行Dump操作
static void do_dump(const char *path, const mach_header_64 *macho) {
const char *name = strrchr(path, '/') + 1;
if (strlen(name) >= max_file_name) {
printf(logprefix "%s is too long, please reset the size of dump_info.name.\n", name);
return;
}
printf(logprefix "Dumping %s.\n", path);
FILE *file = fopen(path, "rb");
if (!file) {
printf(logprefix "Failed to read %s.\n", path);
return;
}
uint32_t magic;
fread(&magic, 1, sizeof(magic), file);
if (magic != MH_MAGIC_64) {
fclose(file);
printf(logprefix "%s is not MH_MAGIC_64 magic, ignored dumping it.\n", name);
return;
}
fseek(file, 0, SEEK_END);
size_t filelength = ftell(file);
fseek(file, 0, SEEK_SET);
char *filebuff = new char[filelength];
fread(filebuff, 1, filelength, file);
fclose(file);
printf(logprefix "Readed file %s, size %ld.\n", name, filelength);
mach_header_64 *filehdr = (mach_header_64 *)filebuff;
load_command *filelc = (load_command *)&filehdr[1];
version_min_command *fileverlc = NULL;
encryption_info_command_64 *fileenclc = NULL;
for (auto i = 0; i < filehdr->ncmds; i++) {
switch (filelc->cmd) {
case LC_VERSION_MIN_IPHONEOS:
fileverlc = (version_min_command *)filelc;
break;
case LC_ENCRYPTION_INFO_64:
fileenclc = (encryption_info_command_64 *)filelc;
break;
default:
break;
}
filelc = (load_command *)((char *)filelc + filelc->cmdsize);
}
if (!fileverlc) {
static version_min_command verlc0 = { 0 };
printf(logprefix
"There's no LC_VERSION_MIN_IPHONEOS, reset to 0.\n");
fileverlc = &verlc0;
}
if (!fileenclc) {
printf(logprefix
"Bad macho file, there's no LC_ENCRYPTION_INFO_64.\n");
return;
}
printf(logprefix "Min version %x, encrypt info 0x%x,%d.\n", fileverlc->version,
fileenclc->cryptoff, fileenclc->cryptsize);
// 拷贝内存中已经解密的MachO数据
memcpy(filebuff + fileenclc->cryptoff, (char *)macho + fileenclc->cryptoff, fileenclc->cryptsize);
// 设置为非加密状态
fileenclc->cryptid = 0;
// 传回至adcpp_ios_dump函数进一步处理
size_t dumpsize = sizeof(dump_info) + filelength;
dump_info *dumpbuff = (dump_info *)malloc(dumpsize);
dumpbuff->filesize = (int)filelength;
sprintf(dumpbuff->min_version, "%d.%d.0", fileverlc->version >> 16,
(uint8_t)(fileverlc->version >> 8));
memcpy(dumpbuff->filebuff, filebuff, filelength);
strcpy(dumpbuff->filename, name);
printf(logprefix "Sending %s, %s, %d.\n", dumpbuff->filename, dumpbuff->min_version,
dumpbuff->filesize);
dump2py(dumpbuff, dumpsize);
free(dumpbuff);
delete[] filebuff;
}
// 暂停/恢复其他线程
static void thread_control(bool suspend) {
mach_port_t threadcur = mach_thread_self();
mach_port_t task = mach_task_self();
thread_act_array_t threads;
mach_msg_type_number_t count;
kern_return_t kr = task_threads(task, &threads, &count);
if (kr != KERN_SUCCESS) {
printf(logprefix "Failed to get task threads with error %d, ignored thread control.\n", kr);
return;
}
for (mach_msg_type_number_t i = 0; i != count; i++) {
thread_t thread = threads[i];
if (thread != threadcur) {
if (suspend) {
kr = thread_suspend(thread);
} else {
kr = thread_resume(thread);
}
printf(logprefix "%s task thread %08x with kernel result %d.\n",
suspend ? "Suspending" : "Resuming", thread, kr);
} else {
printf(logprefix "Ignored adcpp thread %08x.\n", thread);
}
}
for (mach_msg_type_number_t i = 0; i != count; i++) {
mach_port_deallocate(task, threads[i]);
}
vm_deallocate(task, (vm_address_t)threads, count * sizeof(thread_t));
}
// 由于dump操作是一次性代码,所以不需要使用常驻内存的接口adc_main_thread
void adc_main() {
printf(logprefix "Start dumping process %d (Build %s %s).\n", getpid(), __DATE__, __TIME__);
thread_control(true);
// 遍历所有App的MachO并解密Dump传回至adcpp_ios_dump接口
// 然后由adcpp_ios_dump负责保存至A64Dbg对应的缓存目录
for (auto i = 0; i < _dyld_image_count(); i++) {
const char *path = _dyld_get_image_name(i);
if (strstr(path, ".app/")) {
do_dump(path, (mach_header_64 *)_dyld_get_image_header(i));
}
}
thread_control(false);
printf(logprefix "Finished dumping.\n");
}
Loading...
马建仓 AI 助手
尝试更多
代码解读
代码找茬
代码优化
C++
1
https://gitee.com/geekneo/adcpp-ios-dump.git
git@gitee.com:geekneo/adcpp-ios-dump.git
geekneo
adcpp-ios-dump
adcpp-ios-dump
master

搜索帮助