小标
2019-04-22
来源 :
阅读 2173
评论 0
摘要:本文主要向大家介绍了C/C++知识点之MachOView源码(Attach.mm),通过具体的内容向大家展示,希望对大家学习C/C++知识点有所帮助。
本文主要向大家介绍了C/C++知识点之MachOView源码(Attach.mm),通过具体的内容向大家展示,希望对大家学习C/C++知识点有所帮助。
//Attach.h
/* * Attach.h * MachOView * * Created by fG! on 08/09/13. * reverser@put.as * */#ifndef machoview_Attach_h#define machoview_Attach_h#include #include #include #include //获取指定pid对就节镜像大小int64_t get_image_size(mach_vm_address_t address, pid_t pid, uint64_t *vmaddr_slide);//找到入口kern_return_t find_main_binary(pid_t pid, mach_vm_address_t *main_address);//从 pid读取指定地址的指定大小数据到缓冲区kern_return_t dump_binary(mach_vm_address_t address, pid_t pid, uint8_t *buffer, uint64_t aslr_slide);#endif//相关函数用法 //mach_vm_protect//kern_return_t mach_vm_protect//(// vm_map_t target_task,// mach_vm_address_t address,// mach_vm_size_t size,// boolean_t set_maximum,// vm_prot_t new_protection// );//对address到address+size这一段的内存设置内存保护策略,new_protection就是最后设置成为的保护机制//mach_vm_write//kern_return_t mach_vm_write//(// vm_map_t target_task,// mach_vm_address_t address,// vm_offset_t data,// mach_msg_type_number_t dataCnt// );//对address指向的内存改写内容// Ports//Ports是一种Mach提供的task之间相互交互的机制,通过Ports可以完成类似进程间通信的行为。每个Ports都会有自己的权限。//#!c//#define MACH_PORT_RIGHT_SEND ((mach_port_right_t) 0)//#define MACH_PORT_RIGHT_RECEIVE ((mach_port_right_t) 1)//#define MACH_PORT_RIGHT_SEND_ONCE ((mach_port_right_t) 2)//#define MACH_PORT_RIGHT_PORT_SET ((mach_port_right_t) 3)//#define MACH_PORT_RIGHT_DEAD_NAME ((mach_port_right_t) 4)//#define MACH_PORT_RIGHT_LABELH ((mach_port_right_t) 5)//#define MACH_PORT_RIGHT_NUMBER ((mach_port_right_t) 6)//Ports可以在不同的task之间传递,通过传递可以赋予其他task对ports的操作权限。例如POC中使用的就是在父进程与子进程之间传递Port得到了对内存操作的权限
Attach.mm
/*
* Attach.mm
* MachOView
*
* Created by fG! on 08/09/13.
* reverser@put.as
*
* Contains functions for the attach to process feature.
*
*/#include ""Attach.h""#include /* local functions *///读内存的相应信息static kern_return_t readmem(mach_vm_offset_t *buffer, mach_vm_address_t address, mach_vm_size_t size, pid_t pid, vm_region_basic_info_data_64_t *info);#pragma mark Public functions//通过迭代内存区域查找主二进制文件,假设只有一个二进制文件带有MH_EXECUTE文件类型//获取加载基址在区域中的入口地址 这里不是程序入口 下面这个结构体的entryoff才是真正的入口//如: entryoff =0xA0 74 90 +加载基址 0x1 00 00 00 00=0x1 00 A0 74 90//struct entry_point_command {// uint32_t cmd; /* LC_MAIN only used in MH_EXECUTE filetypes */// uint32_t cmdsize; /* 24 */// uint64_t entryoff; /* file (__TEXT) offset of main() */// uint64_t stacksize;/* if not zero, initial stack size *///};kern_return_tfind_main_binary(pid_t pid, mach_vm_address_t *main_address)
{ vm_map_t targetTask = 0; kern_return_t kr = 0; //获取任务端口 if (task_for_pid(mach_task_self(), pid, &targetTask))
{
NSLog(@""[ERROR]权限不够或者没有签名\n""); return KERN_FAILURE;
} //区域 vm_address_t iter = 0; //从 0区域开始 while (1)
{ struct mach_header mh = {0}; //区域基址 vm_address_t addr = iter; //区域大小 vm_size_t lsize = 0; uint32_t depth; mach_vm_size_t bytes_read = 0; struct vm_region_submap_info_64 info; mach_msg_type_number_t count = VM_REGION_SUBMAP_INFO_COUNT_64; //与mach_vm_region相似 只是可以指定深度 if (vm_region_recurse_64( //任务虚拟内存地址
targetTask, //vm区域的起始地址
&addr, //大小
&lsize, //深度
&depth, //返回的信息
(vm_region_info_t)&info, //条目
&count))
{ break;
} //按 size 大小读取与给定的 target_task 相同区域中的数据
kr = mach_vm_read_overwrite( //任务虚拟内存地址
targetTask, //vm的起始地址
(mach_vm_address_t)addr, //大小
(mach_vm_size_t)sizeof(struct mach_header), //缓冲区
(mach_vm_address_t)&mh, //实际大小
&bytes_read
); //判断它的实际大小 if (kr == KERN_SUCCESS && bytes_read == sizeof(struct mach_header))
{ //在MH_EXECUTE 文件类型中只有一个 //4277009103 == 0xfeedface if ( (mh.magic == MH_MAGIC || mh.magic == MH_MAGIC_64) && mh.filetype == MH_EXECUTE)
{#if DEBUG
NSLog(@""Found main binary mach-o image @ %p!\n"", (void*)addr);#endif //保存地址
*main_address = addr; break;
}
} //下一区域
iter = addr + lsize;
} return KERN_SUCCESS;
}//获取指定地址镜像大小(文件中) vmaddr_slide为偏移int64_tget_image_size(mach_vm_address_t address, pid_t pid, uint64_t *vmaddr_slide)
{ vm_region_basic_info_data_64_t region_info = {0}; //分配缓冲区 读取文件头 //这不是完全正确的因为 64 位的有额外的 4 字节 //这里就没做处理 struct mach_header header = {0}; //获取指定地址的内存数据与内存的相关作息 //获取文件头 if (readmem((mach_vm_offset_t*)&header, address, sizeof(struct mach_header), pid, ®ion_info))
{
NSLog(@""Can't read header!""); return -1;
} //文件标识 if (header.magic != MH_MAGIC && header.magic != MH_MAGIC_64)
{ printf(""[ERROR] Target is not a mach-o binary!\n""); return -1;
} //用于保存所有节的总大小(文件) int64_t imagefilesize = -1; //读取加载命令 uint8_t *loadcmds = (uint8_t*)malloc(header.sizeofcmds); //文件头大小 uint16_t mach_header_size = sizeof(struct mach_header); //判断 mach头 if (header.magic == MH_MAGIC_64)
{ //文件头大小
mach_header_size = sizeof(struct mach_header_64);
} //获取指定地址的内存数据与内存的相关作息 //获取所有加载命令节 if (readmem( //缓冲区
(mach_vm_offset_t*)loadcmds, //加载命令基址
address+mach_header_size, //加载命令总大小
header.sizeofcmds, //pid
pid, //内存信息
®ion_info))
{
NSLog(@""Can't read load commands""); //释放 free(loadcmds); return -1;
} ///处理和检索链接地址和大小 uint8_t *loadCmdAddress = 0; //所有命令的基址
loadCmdAddress = (uint8_t*)loadcmds; //加载命令结构体 struct load_command *loadCommand = NULL; //节结构体 struct segment_command *segCmd = NULL; //节结构体 64位 struct segment_command_64 *segCmd64 = NULL; //遍历所有的加载命令 for (uint32_t i = 0; i < header.ncmds; i++)
{ //加载命令
loadCommand = (struct load_command*)loadCmdAddress; // 判断是否为节加载命令(32位) if (loadCommand->cmd == LC_SEGMENT)
{ //获取节的加载命令
segCmd = (struct segment_command*)loadCmdAddress; //节的名称 if (strncmp(segCmd->segname, ""__PAGEZERO"", 16) != 0)
{ if (strncmp(segCmd->segname, ""__TEXT"", 16) == 0)
{
*vmaddr_slide = address - segCmd->vmaddr;
} //节文件大小
imagefilesize += segCmd->filesize;
}
} // 64位节 else if (loadCommand->cmd == LC_SEGMENT_64)
{
segCmd64 = (struct segment_command_64*)loadCmdAddress; if (strncmp(segCmd64->segname, ""__PAGEZERO"", 16) != 0)
{ if (strncmp(segCmd64->segname, ""__TEXT"", 16) == 0)
{
*vmaddr_slide = address - segCmd64->vmaddr;
} //在文件中的大小
imagefilesize += segCmd64->filesize;
}
} //下一个命令
loadCmdAddress += loadCommand->cmdsize;
} free(loadcmds); //返回所有节大小和 return imagefilesize;
}//将二进制文件每个段转储到所分配的缓冲区中 aslr_slide为偏移kern_return_tdump_binary(mach_vm_address_t address, pid_t pid, uint8_t *buffer, uint64_t aslr_slide)
{ //内存信息结构体 vm_region_basic_info_data_64_t region_info = {0}; //文件头 struct mach_header header = {0}; //获取指定地址的内存数据与内存的相关作息 if (readmem((mach_vm_offset_t*)&header, address, sizeof(struct mach_header), pid, ®ion_info))
{
NSLog(@""Can't read header!""); return KERN_FAILURE;
} //文件头标识 if (header.magic != MH_MAGIC && header.magic != MH_MAGIC_64)
{ printf(""[ERROR] Target is not a mach-o binary!\n""); exit(1);
} //在LINKEDIT中读取头信息 uint8_t *loadcmds = (uint8_t*)malloc(header.sizeofcmds); //文件头大小 uint16_t mach_header_size = sizeof(struct mach_header); //判断位数 if (header.magic == MH_MAGIC_64)
{
mach_header_size = sizeof(struct mach_header_64);
} //检索加载命令 //获取指定地址的内存数据与内存的相关作息 if (readmem((mach_vm_offset_t*)loadcmds, address+mach_header_size, header.sizeofcmds, pid, ®ion_info))
{
NSLog(@""Can't read load commands""); return KERN_FAILURE;
} //处理和检索链接地址和大小 uint8_t *loadCmdAddress = 0; //加载命令基址
loadCmdAddress = (uint8_t*)loadcmds; struct load_command *loadCommand = NULL; struct segment_command *segCmd = NULL; struct segment_command_64 *segCmd64 = NULL; //遍历加载命令 for (uint32_t i = 0; i < header.ncmds; i++)
{
loadCommand = (struct load_command*)loadCmdAddress; if (loadCommand->cmd == LC_SEGMENT)
{
segCmd = (struct segment_command*)loadCmdAddress; if (strncmp(segCmd->segname, ""__PAGEZERO"", 16) != 0)
{#if DEBUG printf(""[DEBUG] Dumping %s at %llx with size %x (buffer:%x)\n"", segCmd->segname, segCmd->vmaddr+aslr_slide, segCmd->filesize, (uint32_t)*buffer);#endif //获取指定地址的内存数据与内存的相关作息
readmem((mach_vm_offset_t*)buffer, segCmd->vmaddr+aslr_slide, segCmd->filesize, pid, ®ion_info);
} //指向下一节
buffer += segCmd->filesize;
} else if (loadCommand->cmd == LC_SEGMENT_64)
{
segCmd64 = (struct segment_command_64*)loadCmdAddress; if (strncmp(segCmd64->segname, ""__PAGEZERO"", 16) != 0)
{#if DEBUG printf(""[DEBUG] Dumping %s at %llx with size %llx (buffer:%x)\n"", segCmd64->segname, segCmd64->vmaddr+aslr_slide, segCmd64->filesize, (uint32_t)*buffer);#endif //获取指定地址的内存数据与内存的相关作息
readmem((mach_vm_offset_t*)buffer, segCmd64->vmaddr+aslr_slide, segCmd64->filesize, pid, ®ion_info);
} //指向下一节
buffer += segCmd64->filesize;
} // 下一个
loadCmdAddress += loadCommand->cmdsize;
} free(loadcmds); return KERN_SUCCESS;
}#pragma mark Local functions//获取指定地址的内存数据与内存的相关作息static kern_return_treadmem(mach_vm_offset_t *buffer, mach_vm_address_t address, mach_vm_size_t size, pid_t pid, vm_region_basic_info_data_64_t *info){ // get task for pid vm_map_t port; kern_return_t kr; #if DEBUG printf(""[DEBUG] Readmem of address %llx to buffer %llx with size %llx\n"", address, buffer, size); #endif //获取任务端口 if (task_for_pid(mach_task_self(), pid, &port))
{ fprintf(stderr, ""[ERROR]权限不够或者没有签名\n""); return KERN_FAILURE;
} mach_msg_type_number_t info_cnt = sizeof (vm_region_basic_info_data_64_t); mach_port_t object_name; mach_vm_size_t size_info; mach_vm_address_t address_info = address;// kern_return_t mach_vm_region// (// vm_map_t target_task,// mach_vm_address_t *address,// mach_vm_size_t *size,// vm_region_flavor_t flavor,// vm_region_info_t info,// mach_msg_type_number_t *infoCnt,// mach_port_t *object_name// ); //获取map指向的任务内,address地址起始的VM region(虚拟内存区域)的信息。目前标记为flavor只有VM_BASIC_INFO_64 //获得的info的数据结构如下
kr = mach_vm_region( //任务虚拟地址
port, //vm 的起始地址
&address_info, //大小
&size_info, // struct vm_region_basic_info_64 { // vm_prot_t protection; // vm_prot_t max_protection; // vm_inherit_t inheritance; // boolean_t shared; // boolean_t reserved; // memory_object_offset_t offset; // vm_behavior_t behavior; // unsigned short user_wired_count; // }; //方式
VM_REGION_BASIC_INFO_64, //返回信息
(vm_region_info_t)info, //条目
&info_cnt,
&object_name); if (kr)
{ fprintf(stderr, ""[ERROR] mach_vm_region failed with error %d\n"", (int)kr); return KERN_FAILURE;
} //实际读取 mach_vm_size_t nread; //按 size 大小读取与给定的 target_task 相同区域中的数据
kr = mach_vm_read_overwrite( //任务虚拟地址
port, //vm 的起始地址
address, //大小
size, //缓冲区
(mach_vm_address_t)buffer, //实际大小
&nread); if (kr)
{ fprintf(stderr, ""[ERROR]读取失败%d\n"", kr); return KERN_FAILURE;
} else if (nread != size)
{ fprintf(stderr, ""[ERROR] 读取失败,请求大小 size: 0x%llx 实际: 0x%llx\n"", size, nread); return KERN_FAILURE;
} return KERN_SUCCESS;
}本文由职坐标整理并发布,希望对同学们有所帮助。了解更多详情请关注职坐标编程语言C/C+频道!
喜欢 | 0
不喜欢 | 0
您输入的评论内容中包含违禁敏感词
我知道了

请输入正确的手机号码
请输入正确的验证码
您今天的短信下发次数太多了,明天再试试吧!
我们会在第一时间安排职业规划师联系您!
您也可以联系我们的职业规划师咨询:
版权所有 职坐标-一站式AI+学习就业服务平台 沪ICP备13042190号-4
上海海同信息科技有限公司 Copyright ©2015 www.zhizuobiao.com,All Rights Reserved.
沪公网安备 31011502005948号