方式一:
通过mach_vm_region系统API
参考代码:https://github.com/davidrhodus/misc/blob/master/iOS-internals/vmmap.c

#include <mach/vm_map.h>
#include <stdio.h>#include <mach-o/dyld_images.h>/*** vmmap(1) clone for OS X and iOS* -------------------------------** This is a simple example of using the mach_vm_region_info APIs in order to * obtain a process' (technically, a task's) virtual memory address space, in a* manner akin to /proc/[pid]/maps on Linux.** The process is simple - get the task port, then call mach_vm_region_info until* you've exhausted the address space (in iOS this happens around 0x40000000, * where the commpage is). On iOS 6, for some peculiar reason the task port is* invalidated after each call, so the quick workaround here solves the problem* by regetting the port. The actual mach error code to check for is in the header* files, though the code simply tries regetting.** N.B - For this code to work, you MUST provide the entitlements to allow * task-for-pid to work, else you'll fail with error 5. The entitlements are in * the output in Chapter 3, but for those of you who haven't bought the book, it would be:*
--- Cut here <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict><key>com.apple.springboard.debugapplications</key><true/><key>get-task-allow</key><true/><key>proc_info-allow</key><true/><key>task_for_pid-allow</key><true/><key>run-unsigned-code</key><true/>
</dict>
</plist>--- Ok, enough :-)**  so - copy the above XML to a file, say, "ent.xml", and be sure to run "ldid -Sent.xml vmmap"*  before trying to run this. You can download the binary (already thus signed) if you're lazy*  (and trust me, because you *will* need root on your i-Device for this)**  As the book clearly states, once you have the task port, the world is your oyster. You can*  control the entire virtual memory space, reading and writing it as you please. Stay tuned*  for the corrupt tool (which will be provided soon in binary form)**/int g_pid = 0; // required in iOS 6 (read below)/* 03/08/13 - Added List of Mach-O images: */struct dyld_image_info *g_dii = NULL;
int     g_imageCount;
//读取进程内存数据
unsigned char *
readProcessMemory (int pid, mach_vm_address_t addr, mach_msg_type_number_t *size)
{// Helper function to read process memory (a la Win32 API of same name)// To make it easier for inclusion elsewhere, it takes a pid, and// does the task_for_pid by itself. Given that iOS invalidates task ports// after use, it's actually a good idea, since we'd need to reget anywaytask_t  t;//获取进程中的任务task_for_pid(mach_task_self(),pid, &t);mach_msg_type_number_t  dataCnt = size;vm_offset_t readMem;// Use vm_read, rather than mach_vm_read, since the latter is different// in iOS.//获取内存kern_return_t kr = vm_read(t,        // vm_map_t target_task,  任务addr,     // mach_vm_address_t address, 任务中对应地址*size,     // mach_vm_size_t size&readMem,     //vm_offset_t *data,size);     // mach_msg_type_number_t *dataCntif (kr) {// DANG..fprintf (stderr, "Unable to read target task's memory @%p - kr 0x%x\n" , addr, kr);return NULL;}return ( (unsigned char *) readMem);}kern_return_t mach_vm_read (vm_map_t, mach_vm_address_t, mach_vm_size_t, vm_offset_t *, mach_msg_type_number_t *);
//暴利搜索内存
void
findListOfBinaries(task_t  t, mach_vm_address_t    addr, int size)
{kern_return_t kr;mach_msg_type_number_t  dataCnt = size;// 读取指定内存地址上指定大小的内容unsigned char *readData = readProcessMemory (g_pid, addr, &dataCnt);int machsig = 0xfeedface;// Checking only 0xfeedfa is a simple way to catch both 64-bit (facf) and 32-bit (face) headers// Machine endianness is automatically taken care of, too..// 判断是否是macho头if (readData && memcmp (readData + 1, ((unsigned char *) &machsig) + 1 , 3) == 0){// This is a Mach headerint i = 0;// A MUCH better way would be to iterate through the LC and find the name of dyld// but this would require my machlib.c (closed source) and really get the same result.// This works because on both iOS and OS X dyld is at /usr/lib.for (i = 0; i <dataCnt; i++){   // 循环比较内存内容是否是"lib/dyld"if (memcmp(readData+i, "lib/dyld", 8) == 0){unsigned int dyld_all_image_infos_offset ;int imageCount = 0;// 拷贝导入模块信息memcpy (&dyld_all_image_infos_offset, readData+DYLD_ALL_IMAGE_INFOS_OFFSET_OFFSET, sizeof (unsigned int));struct dyld_all_image_infos *dyldaii ;// Safeguard: should check that dyld_all_image_infos_offset is < size..if (dyld_all_image_infos_offset > size){// This is to be expected, since the dyld_all_image_infos is in a data region//printf ("Offset %x is greater than region size : %x\n", dyld_all_image_infos_offset, size);dataCnt = sizeof(dyld_all_image_infos);readData = readProcessMemory (g_pid, addr + dyld_all_image_infos_offset , &dataCnt);if (!readData) { return;}dyldaii = (struct dyld_all_image_infos *) readData;}else{dyldaii = (struct dyld_all_image_infos *) (readData +dyld_all_image_infos_offset);}printf ("Version: %d, %d images at offset %p\n",dyldaii->version, dyldaii->infoArrayCount, dyldaii->infoArray);// Go to dyldaii->infoArray addressimageCount = dyldaii->infoArrayCount;// 保存导入模块信息dataCnt = imageCount * sizeof(struct dyld_image_info);g_dii = (struct dyld_image_info *) malloc (dataCnt);g_imageCount = imageCount;readData = readProcessMemory(g_pid, dyldaii->infoArray, &dataCnt);if (!readData) { return;}struct dyld_image_info *dii = (struct dyld_image_info *) readData;// We don't need i anymore, anywayfor (i = 0; i < imageCount; i++){dataCnt = 1024;char *imageName = readProcessMemory (g_pid, dii[i].imageFilePath, &dataCnt);if (imageName) g_dii[i].imageFilePath = strdup(imageName);else g_dii[i].imageFilePath = NULL;g_dii[i].imageLoadAddress = dii[i].imageLoadAddress;}break;}}}}/* End 03/08/13 */
char *
behavior_to_text (vm_behavior_t b)
{switch (b){case VM_BEHAVIOR_DEFAULT: return("default");case VM_BEHAVIOR_RANDOM:  return("random");case VM_BEHAVIOR_SEQUENTIAL: return("fwd-seq");case VM_BEHAVIOR_RSEQNTL: return("rev-seq");case VM_BEHAVIOR_WILLNEED: return("will-need");case VM_BEHAVIOR_DONTNEED: return("will-need");case VM_BEHAVIOR_FREE: return("free-nowb");case VM_BEHAVIOR_ZERO_WIRED_PAGES: return("zero-wire");case VM_BEHAVIOR_REUSABLE: return("reusable");case VM_BEHAVIOR_REUSE: return("reuse");case VM_BEHAVIOR_CAN_REUSE: return("canreuse");default: return ("?");}}
//内存属性
char *
protection_bits_to_rwx (vm_prot_t p)
{// previous version of this somehow lost the "p&", always returning rwx..static char returned[4];returned[0] = (p &VM_PROT_READ    ? 'r' : '-');returned[1] = (p &VM_PROT_WRITE   ? 'w' : '-');returned[2] = (p & VM_PROT_EXECUTE ? 'x' : '-');returned[3] = '\0';// memory leak here. No biggyreturn (strdup(returned));}
//当前内存状态
const char *
unparse_inheritance (vm_inherit_t i)
{switch (i){case VM_INHERIT_SHARE:return "share";case VM_INHERIT_COPY:return "copy";case VM_INHERIT_NONE:return "none";default:return "???";}
}macosx_debug_regions (task_t task, mach_vm_address_t address, int max)
{kern_return_t kret;mach_vm_address_t prev_address;/* @TODO: warning - potential overflow here - gotta fix this.. */vm_region_basic_info_data_t prev_info,info;mach_vm_size_t size, prev_size;mach_port_t object_name;mach_msg_type_number_t count;//模块总共读的块数int nsubregions = 0;// 已遍历的模块数int num_printed = 0;count = VM_REGION_BASIC_INFO_COUNT_64;kret = mach_vm_region (task, &address, &size, VM_REGION_BASIC_INFO,(vm_region_info_t) &info, &count, &object_name);if (kret != KERN_SUCCESS){printf ("mach_vm_region: Error %d - %s", kret, mach_error_string(kret));return;}//保存上一次遍历的模块信息memcpy (&prev_info, &info, sizeof (vm_region_basic_info_data_t));prev_address = address;prev_size = size;// 已获取1块nsubregions = 1;for (;;){int print = 0;int done = 0;address = prev_address + prev_size;/* Check to see if address space has wrapped around. */// 遍历模块结束if (address == 0){ print = done = 1;}if (!done){// Even on iOS, we use VM_REGION_BASIC_INFO_COUNT_64. This works.count = VM_REGION_BASIC_INFO_COUNT_64;// 读取从address开始的第一个正确的模块信息,注意这里可能不是一个模块头kret =mach_vm_region (task, &address, &size, VM_REGION_BASIC_INFO,(vm_region_info_t) &info, &count, &object_name);if (kret != KERN_SUCCESS){/* iOS 6 workaround - attempt to reget the task port to avoiD *//* "(ipc/send) invalid destination port" (1000003 or something) */task_for_pid(mach_task_self(),g_pid, &task);// 重新获取一遍kret =mach_vm_region (task, &address, &size, VM_REGION_BASIC_INFO,(vm_region_info_t) &info, &count, &object_name);}if (kret != KERN_SUCCESS){fprintf (stderr,"mach_vm_region failed for address %p - Error: %x\n", address,(kret));size = 0;if (address >= 0x4000000) return;print = done = 1;}}// 如果上一个内存起始地址+内存大小!=当前的地址,说明是新的模块if (address != prev_address + prev_size)print = 1;// 如果内存属性不同,说明是新的模块if ((info.protection != prev_info.protection)|| (info.max_protection != prev_info.max_protection)|| (info.inheritance != prev_info.inheritance)|| (info.shared != prev_info.reserved)|| (info.reserved != prev_info.reserved))print = 1;//如果是新的模块,获取它的其它信息if (print){int   print_size;char *print_size_unit;if (num_printed == 0)printf ("Region ");elseprintf ("   ... ");findListOfBinaries(task, prev_address, prev_size);/* Quick hack to show size of segment, which GDB does not */print_size = prev_size;if (print_size > 1024) { print_size /= 1024; print_size_unit = "K"; }if (print_size > 1024) { print_size /= 1024; print_size_unit = "M"; }if (print_size > 1024) { print_size /= 1024; print_size_unit = "G"; }/* End Quick hack */printf (" %p-%p [%d%s](%s/%s; %s, %s, %s) %s",(prev_address),(prev_address + prev_size),print_size,print_size_unit,protection_bits_to_rwx (prev_info.protection),protection_bits_to_rwx (prev_info.max_protection),unparse_inheritance (prev_info.inheritance),prev_info.shared ? "shared" : "private",prev_info.reserved ? "reserved" : "not-reserved",behavior_to_text (prev_info.behavior));if (nsubregions > 1)printf (" (%d sub-regions)", nsubregions);printf ("\n");// 把此次内存信息保存起来prev_address = address;prev_size = size;memcpy (&prev_info, &info, sizeof (vm_region_basic_info_data_t));nsubregions = 1;num_printed++;}else{//如果不是新的模块,只增加模块大小prev_size += size;nsubregions++;}if ((max > 0) && (num_printed >= max)){printf ("Max %d num_printed %d\n", max, num_printed);done = 1;}if (done)break;}
}void
main(int argc, char **argv)
{struct vm_region_basic_info vmr;kern_return_t   rc;mach_port_t task;mach_vm_size_t  size = 8;vm_region_info_t    info = (vm_region_info_t) malloc(10000);mach_msg_type_number_t  info_count;mach_port_t     object_name;//初始值设为1mach_vm_address_t   addr =1;int pid;//需要提供参数1为目标进程的PIDif (!argv[1]) { printf ("Usage: %s <PID>\n"); exit (1);}//把参数1转换成pidpid = atoi(argv[1]);g_pid = pid; // req for iOS 6// 获取目标进程的taskrc = task_for_pid(mach_task_self(),pid, &task);if (rc) { fprintf (stderr, "task_for_pid() failed with error %d - %s\n", rc, mach_error_string(rc)); exit(1); }printf ("RC %d - Task: %d\n",rc, task);macosx_debug_regions (task, addr, 1000);int i ;for ( i = 0; i < g_imageCount; i++){printf("Image: %s loaded @%p\n",g_dii[i].imageFilePath, g_dii[i].imageLoadAddress);}printf("Done\n");}

方式二:
通过_dyld_getimage*系列API

头文件:dyld.h
函数原型:
//获取当前进程加载的模块数。
uint32_t                    _dyld_image_count(void);
//获取内存中某个模块的头部指针。
const struct mach_header*   _dyld_get_image_header(uint32_t image_index);
//获取内存中某个模块的slide值。
intptr_t                    _dyld_get_image_vmaddr_slide(uint32_t image_index);
// 获取内存中某个模块的名称。
const char*                 _dyld_get_image_name(uint32_t image_index);
实现:
#import
int32_t nModNums= _dyld_image_count();
const char *pszModName = NULL;
intptr_t pModSlide = 0;
const struct mach_header* pModHeader = NULL;for (uint32_t i = 0; i < nModNums; i++)
{
pModSlide  = _dyld_get_image_vmaddr_slide(i);
pszModName = _dyld_get_image_name(i);
pModHeader = _dyld_get_image_header(i);
}

转载于:https://blog.51cto.com/haidragon/2164203

ios遍历模块的方式相关推荐

  1. iOS热更新实现方式

    转发 链接:https://www.jianshu.com/p/00111d447e7e 苹果静止热更新,可惜我的是企业app,没有这些约束了,随便用.(当然有些热更新已经可以通过苹果审核了,比如JS ...

  2. linux内核采取,采用动态加载模块的方式Linux内核编译

    Linux内核是一种单体内核,但是通过动态加载模块的方式,使它的开发非常灵活方便.那么,它是如何编译内核的呢?我们可以通过分析它的Makefile入手.以下是一个简单的hello内核模块的Makefi ...

  3. OpenCV优化:图像的遍历4种方式

    小知识:反色 反色原理很简单,在一个rgb色彩空间中,可将任何一种颜色看成笛卡尔坐标中的一个点,对于任意点,反色就是计算以(128, 128,128)为中心时该点的对称点,比如rgb(100, 150 ...

  4. 二叉树 —— 创建二叉树 先序遍历 、中序遍历、后序遍历(递归方式、非递归方式)

    #include<stdio.h> #include<malloc.h> #include<stdlib.h> typedef char DataType; #de ...

  5. python的模块、包、库区别。导入包/模块的方式

    直接进入主题,如果觉得文章可以,记得点点赞和关注,我主要会写一些在学习过程中遇到的困难 模块 为了使代码更容易维护,提高代码重用价值:可以将一组相关功能的代码写入一个单独的.py文件中:使别人导入使用 ...

  6. python中导入模块的方式_Python中几种导入模块的方式总结

    模块内部封装了很多实用的功能,有时在模块外部调用就需要将其导入.常见的方式有如下几种: 1 . import >>> import sys >>> sys.path ...

  7. 回溯算法-01遍历所有排列方式问题

    遍历所有排列方式 前言 回溯采用试错的方法解决问题,一旦发现当前步骤失败,回溯算法就返回上一个步骤,继续另一种方案继续试错. 回溯算法的优点是速度快,没有尝试所有路径就可能找到答案.当然,如果运气不好 ...

  8. python写日志文件_Python logging日志模块 配置文件方式

    在一些微服务或web服务中我们难免需要日志功能,用来记录一些用户的登录记录,操作记录,以及一些程序的崩溃定位,执行访问定位等等; Python内置 非常强大的日志模块 ==> logging 今 ...

  9. Java里面遍历list的方式

    问题:Java里面遍历list的方式 对于Java语言有点陌生,我尝试熟悉遍历list(或者其他集合)的所有方法(或者是其他正确的语法)和它们每一个方法的优缺点 给定 List的list对象,我知道有 ...

最新文章

  1. 指定hdfs中namenode的地址_HDFS文件上传下载步骤
  2. android 关闭2g网络,中国联通将关闭2G网络,速来了解联通2g卡升级4g套餐方法
  3. expect to check grammarly intime
  4. NRedis-Proxy - 高性能中间件服务器
  5. ssl1010-方格取数
  6. 【POJ - 3249】Test for Job(DAG线性求带负权的最长路,dp)
  7. Facebook将在印度测试联网无人机,可连续飞行90天
  8. android通讯录简单的_安卓手机删了联系人怎么恢复?超简单的恢复方法,一看就懂!...
  9. Linux Redis安装及使用
  10. Microsoft Visual Studio Web 创作组件安装失败的解决方法
  11. css工程师技巧,web前端工程师必须掌握的技巧–CSS Sprites技术(附基础操作教程)...
  12. Linux 用户的 3 个命令行小技巧
  13. python程序设计简明教程课后答案_Python简明教程最后的练习题
  14. 2022年系统分析师论文模拟题--押题
  15. C语言实现英文句子单词提取并分离
  16. 请求接口报502错误
  17. 【渝粤题库】陕西师范大学201511先秦历史散文研究 作业(专升本)
  18. opengl绘制三维人物luweiqi
  19. Oracle文件的导出
  20. uniapp微信小程序授权登录流程(代码直接复制可用)

热门文章

  1. 四川省天然气行业发展战略及投资形势分析报告2021版
  2. win8.1安装veket的方法
  3. VB中使用PNG格式图片的一种新方法
  4. 谋定国际农民丰收节贸易会·万祥军:以色列农业逆袭变局
  5. PostgreSQL 数据库备份
  6. 巨蟒django之CRM5 学习记录课程记录班级管理私户的数量上限
  7. 组件中props参数的应用
  8. 【linux】tcpdump抓包
  9. zz让你成功的九个心理定律
  10. 数据产品经理从零到一:数据产品能力模型构建