概述
//Attach.h
/*
* Attach.h
* MachOView
*
* Created by fG! on 08/09/13.
* reverser@put.as
*
*/
#ifndef machoview_Attach_h
#define machoview_Attach_h
#include <mach/mach.h>
#include <mach/mach_vm.h>
#include <mach/vm_map.h>
#include <mach-o/loader.h>
//获取指定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 <stdio.h>
/* 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_t
find_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_t
get_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_t
dump_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_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)
{
// 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 %llxn", 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 %dn", (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]读取失败%dn", kr);
return KERN_FAILURE;
}
else if (nread != size)
{
fprintf(stderr, "[ERROR] 读取失败,请求大小 size: 0x%llx 实际: 0x%llxn", size, nread);
return KERN_FAILURE;
}
return KERN_SUCCESS;
}
转载于:https://blog.51cto.com/haidragon/2141950
最后
以上就是冷静铃铛为你收集整理的MachOView源码(Attach.mm)的全部内容,希望文章能够帮你解决MachOView源码(Attach.mm)所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
发表评论 取消回复