C/C++知识点之MachOView源码(Attach.mm)
小标 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
看完这篇文章有何感觉?已经有0人表态,0%的人喜欢 快给朋友分享吧~
评论(0)
后参与评论

您输入的评论内容中包含违禁敏感词

我知道了

助您圆梦职场 匹配合适岗位
验证码手机号,获得海同独家IT培训资料
选择就业方向:
人工智能物联网
大数据开发/分析
人工智能Python
Java全栈开发
WEB前端+H5

请输入正确的手机号码

请输入正确的验证码

获取验证码

您今天的短信下发次数太多了,明天再试试吧!

提交

我们会在第一时间安排职业规划师联系您!

您也可以联系我们的职业规划师咨询:

小职老师的微信号:z_zhizuobiao
小职老师的微信号:z_zhizuobiao

版权所有 职坐标-一站式AI+学习就业服务平台 沪ICP备13042190号-4
上海海同信息科技有限公司 Copyright ©2015 www.zhizuobiao.com,All Rights Reserved.
 沪公网安备 31011502005948号    

©2015 www.zhizuobiao.com All Rights Reserved