游戏安全实验室 首页 技术入门 查看内容

 阅读目录

iOS模块遍历方法

发布于:2015-8-21 15:13   |    280846次阅读 作者: 管理员    |   原作者: TP


一、背景
ios遍历模块有两种办法,一种是通过mach_vm_region系统API,这是推荐的方法;另一种是通过_dyld_get_image_*系列API获取模块信息,不过这种方法不是线程安全的。下面方面介绍两种方法的实现。
 
二、模块遍历实现
1. 通过mach_vm_region系统API获取
参考代码:https://github.com/davidrhodus/misc/blob/master/iOS-internals/vmmap.c
下面以该代码来讲解下遍历模块实现方法:
#include
#include
 
#include
 
int g_pid = 0; // required in iOS 6 (read below)
 
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 anyway
 
       task_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 *dataCnt
 
       if (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 header
              int 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               {
            // 循环比较内存内容是否是"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 address
 
                            imageCount = 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, anyway
                            for (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;
                     }
              }
       }
}
 
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 biggy
       return (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;
       nsubregions = 1; // 已获取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.shared)
                     || (info.reserved != prev_info.reserved))
              {
                     print = 1;
              }
 
        //如果是新的模块,获取它的其它信息
              if (print)
              {
                     int   print_size;
                     char *print_size_unit;
                    
                     if (num_printed == 0)
                            printf ("Region ");
                     else
                            printf ("   ... ");
 
                     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;
       mach_vm_address_t       addr =1; //初始值设为1
       int pid;
 
       if (!argv[1])  //需要提供参数1为目标进程的PID
       {
              printf ("Usage: %s \n");
              exit (1);
       }
      
       pid = atoi(argv[1]); //把参数1转换成pid
       g_pid = pid; // req for iOS 6
       rc = task_for_pid(mach_task_self(),pid, &task); // 获取目标进程的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");
}
2. 通过_dyld_get_image_*系列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://gslab.qq.com

分享到:
踩0 赞2

收藏

上一篇:Android下的GOT表Hook实现原理

下一篇:Lua游戏逆向及破解方法介绍

相关阅读
最新评论
B Color Image Link Quote Code Smilies

发表评论