1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| + (unsigned long long)getOffsetFromVmAddress:(unsigned long long )address fileData:(NSData *)fileData{ mach_header_64 mhHeader; [fileData getBytes:&mhHeader range:NSMakeRange(0, sizeof(mach_header_64))]; unsigned long long currentLcLocation = sizeof(mach_header_64); for (int i = 0; i < mhHeader.ncmds; i++) { load_command* cmd = (load_command *)malloc(sizeof(load_command)); [fileData getBytes:cmd range:NSMakeRange(currentLcLocation, sizeof(load_command))]; if (cmd->cmd == LC_SEGMENT_64) { segment_command_64 segmentCommand; [fileData getBytes:&segmentCommand range:NSMakeRange(currentLcLocation, sizeof(segment_command_64))]; if (address >= segmentCommand.vmaddr && address <= segmentCommand.vmaddr + segmentCommand.vmsize) { free(cmd); return address - (segmentCommand.vmaddr - segmentCommand.fileoff); } } currentLcLocation += cmd->cmdsize; free(cmd); } return address; }
|
通过 section 里集合计算的地址其实是 VmAddress 内存中的地址,那么需要通过偏移量计算,映射到二进制文件里的位置。
在一开始的 Mach-O 文件里,除了 Mach Header 结构,剩下的其实就是 segment_command_64 结构了(segment_command_64 包含了 load command)。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42
| segmentCommand = { cmd = 25 cmdsize = 72 segname = "__PAGEZERO" vmaddr = 0 vmsize = 4294967296 fileoff = 0 filesize = 0 maxprot = 0 initprot = 0 nsects = 0 flags = 0 }
segmentCommand = { cmd = 25 cmdsize = 1912 segname = "__TEXT" vmaddr = 4294967296 vmsize = 34439168 fileoff = 0 filesize = 34439168 maxprot = 5 initprot = 5 nsects = 23 flags = 0 }
segmentCommand = { cmd = 25 cmdsize = 1912 segname = "__DATA" vmaddr = 4329406464 vmsize = 6717440 fileoff = 34439168 filesize = 6635520 maxprot = 3 initprot = 3 nsects = 23 flags = 0 }
|
这里特意复制了连续三个 segmentCommand 的数据,其中的重点字段是
判断条件里是指地址所在的 segmentCommand 段
1
| if (address >= segmentCommand.vmaddr && address <= segmentCommand.vmaddr + segmentCommand.vmsize)
|
计算的后半部分,这里拿到的是在内存中,加载的二进制文件 segmentCommand 段的位置
1 2 3
| address - (segmentCommand.vmaddr - segmentCommand.fileoff)
address - segmentCommand.vmaddr + segmentCommand.fileoff
|
前半部分就是 section 里的遍历计算得到的地址,相对于当前 segmentCommand 的偏移量。然后加上二进制文件里的当前 segmentCommand 的偏移量,做为起始位置,最后得到的就是在二进制文件里的具体位置,然后就可以再拿到对应的数据。
所以倒退回去再看,括号里计算的是 segmentCommand 段从虚拟内存的起始位置到二进制文件的起始位置的偏移量。那么最后得到的也就是二进制里的位置