水平有限,错误在所难免,求指点。
本篇以阿里旺旺mac版本 分析一下如果是object-c编写的程序所对应的mach-o文件结构,试着解析出静态的object-class的信息。
010 editor 打开 /Applications/AliWangwang.app/Contents/MacOS/AliWangwang, 如图
可以看到AliWangwang最开始的四个字节为 0xfeedfacf, 根据mach-o的定义,文件开始的开始对应的结构体为mach_header_64(如下图定义), 也就是说此文件不是个fat格式的复合文件,该文件只包含的一套指令集,同时从mach_header_64.cpu_type字段,我们可以知道包含的指令集是x64格式的。
segment_command_64结构紧跟在mach_header_64头后面,也是从文件偏移的0x20处开始套上load_command结构体继续分析。 这里主要查看TEXT 和 DATA 2个segment。
从segment_command_64.nsects 字段可以得到该segment所带的section64的个数,并且套上section64的结构信息,可以得到section的信息。得到的section的名字列表如下
__TEXT segment下的 section | 用途 |
---|---|
__text | 主程序的代码 |
__stubs | 用于动态链接的存根 |
__text | 主程序的代码 |
__stubs | 用于动态链接的存根 |
__stub_helper | 用于动态链接的存根 |
__const | 程序中const关键字修饰的常量变量 |
__objc_methname | object-c的方法名字 |
__cstring | 程序中硬编码的ANSI的字符串 |
__objc_classname | object-c类的名字 |
__objc_methtype | object-c方法的类型 |
__gcc_except_tab | 异常处理相关 |
__ustring | unicode字符串 |
__unwind_info | 异常处理相关 |
__eh_frame | 异常处理相关 |
__DATA segment下的 section | 用途 |
---|---|
__nl_symbol_ptr | 动态符号链接相关,其实是个指针数组 |
__got | 全局偏移表, Global Offset Table |
__la_symbol_ptr | 动态符号链接相关,也是指针数组,通过dyld_stub_binder辅助链接 |
__mod_init_func | 初始化的全局函数地址,会在main之前被调用 |
__const | const常量 |
__cfstring | core foundation用到的字符串 |
__objc_classlist | object-c的类列表 |
__objc_nlclslist | object-c的+load函数列表,比__mod_init_func更早被执行 |
__objc_catlist | object-c的category列表 |
__objc_protolist | object-c的协议列表 |
__objc_imageinfo | object-c的映像信息 |
__objc_const | object-c的常量 |
__objc_selrefs | object-c 自引用 |
__objc_protorefs | object-c 协议引用 |
__objc_classrefs | object-c 类的引用 |
__objc_superrefs | object-c 父类引用 |
__objc_ivar | ivar变量信息 |
__objc_data | class信息 |
__data | 初始化的可变的变量 |
__common | |
__bss | 未初始化的静态变量 |
object-c是门很动态的语言,实际上编译完成后,类的信息就被保存在mach-o格式里面了,__objc_classlist section保存了所有用到的object-c的类列表信息, 从 section64.offset字段,得到object-c的class信息存储在 0x27bdc0 位置, 此处是个指针的数组,数组的大小为 section64.size / 8, 也就是 0x770 / 8.
取数组的第一项进行分析,也就是 0x01002df618, 0x01002df618 是虚拟地址偏移,需要转换为文件地址便宜,也就是需要减去 DATA.vm_addr , 再加上 DATA.offset得到文件偏移, 也就是 0x01002df618 - 100267000h + 267000h = 0x2df618;
010 editor 中跳到 0x2df618, 根据apple文档,此处数据对应的结构体信息为class64_t 贴个下面用到的结构体的定义的头文件,下面的解析都依赖于此头文件。
struct class64_ro_t { uint32_t flags; uint32_t instanceStart; uint32_t instanceSize; uint32_t reserved; uint64_t ivarLayout; // const uint8_t * (64-bit pointer) uint64_t name; // const char * (64-bit pointer) uint64_t baseMethods; // const method_list_t * (64-bit pointer) uint64_t baseProtocols; // const protocol_list_t * (64-bit pointer) uint64_t ivars; // const ivar_list_t * (64-bit pointer) uint64_t weakIvarLayout; // const uint8_t * (64-bit pointer) uint64_t baseProperties; // const struct objc_property_list * (64-bit pointer) };
struct class64_t { uint64_t isa; // class_t * (64-bit pointer) uint64_t superclass; // class_t * (64-bit pointer) uint64_t cache; // Cache (64-bit pointer) uint64_t vtable; // IMP * (64-bit pointer) uint64_t data; // class_ro_t * (64-bit pointer) };
struct objc_property64_list { uint32_t entsize; uint32_t count; // struct objc_property first; These structures follow inline };
struct objc_property64 { uint64_t name; // const char * (64-bit pointer) uint64_t attributes; // const char * (64-bit pointer) };
struct method64_list_t { uint32_t entsize; uint32_t count; // struct method_t first; These structures follow inline };
struct method64_t { uint64_t name; // SEL (64-bit pointer) uint64_t types; // const char * (64-bit pointer) uint64_t imp; // IMP (64-bit pointer) };
struct ivar64_list_t { uint32_t entsize; uint32_t count; // struct ivar_t first; These structures follow inline };
struct ivar64_t { uint64_t offset; // uintptr_t * (64-bit pointer) uint64_t name; // const char * (64-bit pointer) uint64_t type; // const char * (64-bit pointer) uint32_t alignment; uint32_t size; };
struct protocol64_list_t { uint64_t count; // uintptr_t (a 64-bit value) // struct protocol_t * list[0]; These pointers follow inline };
struct protocol64_t { uint64_t isa; // id * (64-bit pointer) uint64_t name; // const char * (64-bit pointer) uint64_t protocols; // struct protocol_list_t * (64-bit pointer) uint64_t instanceMethods; // method_list_t * (64-bit pointer) uint64_t classMethods; // method_list_t * (64-bit pointer) uint64_t optionalInstanceMethods; // method_list_t * (64-bit pointer) uint64_t optionalClassMethods; // method_list_t * (64-bit pointer) uint64_t instanceProperties; // struct objc_property_list * (64-bit pointer) };
struct objc_property64_list { uint32_t entsize; uint32_t count; // struct objc_property first; These structures follow inline };
struct objc_property64 { uint64_t name; // const char * (64-bit pointer) uint64_t attributes; // const char * (64-bit pointer) };
struct category64_t { uint64_t name; // const char * (64-bit pointer) uint64_t cls; // struct class_t * (64-bit pointer) uint64_t instanceMethods; // struct method_list_t * (64-bit pointer) uint64_t classMethods; // struct method_list_t * (64-bit pointer) uint64_t protocols; // struct protocol_list_t * (64-bit pointer) uint64_t instanceProperties; // struct objc_property_list * (64-bit pointer) };
对应上结构体数据偏移可知 * isa = 0x01002df640 * superclass = 0 * cache = 0 * vtable = 0 * data = 0x010027de88
以上的偏移均为虚拟地址偏移,需要计算得到文件。 如 class64_t.data文件偏移 = 0x010027de88 - 100267000h + 267000h = 27de88h; 从27de88h 按结构体class64_ro_t解析,可知。 * flags = 0x0194 * instanceStart = 0x8 * instanceSize = 0x68 * reserved = 0 * ivarLayout = 0x010021a629 * name = 0x010021a59a * baseMethods = 0x010027d908 * baseProtocols = 0x010027d890 * ivars = 10027DCE8 * weakIvarLayout = 0 * baseProperties = 0x10027DE70
以上的地址皆为虚拟地址,再次相对地址计算可得到文件偏移,
class name在 21a59a 处, 也就是 WWAppDelegate
baseMethods 文件地址偏移在0x27d908处,套上method_list_t可知 method_list_t.obsolete = 0x18, method_list_t.method_count = 0x29, 也就是后面有29个method方法,而且每个方法的结构体大小是0x18,
以第一组为例,套上method64_t结构体信息,可得 method64_t.name = 1001DEE3D, method64_t.types = 0x10021CEDB, method64_t.imp = 0x1000026D8。 再次从虚拟地址转换到文件地址偏移。得到method name为1DEE3Dh,method types = 21CEDBh,
也就是说第一个method是 -dealloc, 同样遍历数组,可以得到所有的method名字,如下,
-dealloc
-init
-checkOSVersion
-showLoginWindow
-showMainWindowAfterLoginSuccess
-checkForUpgrade
-bestValidUpdateInAppcast:forUpdater:
-applicationDidFinishLaunching:
-applicationDidResignActive:
-applicationShouldHandleReopen:hasVisibleWindows:
-applicationWillTerminate:
-applicationShouldTerminate:
-handleURLEvent:withReplyEvent:
-doCheckImportLugeEmotions
-wwLoginResultNotification:
-wwClientWillLogoutNotification:
-wwClientDisconnectedNotification:
-applicationDockMenu:
-chatTo:
-newWangwangProcess:
-showMainWindow
-showMainWindow:
-showFileTransfersWindow:
-showPerferences:
-validateMenuItem:
-showFindUserPanel:
-showFindUserPanel:withSearchText:
-showCustomEmotions:
-showAddFriendsNotification:
-wwSearchContact:
-showAboutWindow:
-visitFAQPage:
-downloadBrowserPlugin:
-setEventUrlString:
-eventUrlString
-mainWindow
-mainWindowController
-updaterShouldRelaunchApplication:
-.cxx_destruct
-updateTimer
-setUpdateTimer:
baseProtocols 在0x27d890处,套上protocol_list_t,可知 protocol64_list_t.count = 4, 也就是实现了4个protocol, 后面跟着的是protocol64_t* 数组, 第一个为0x1002E4080, 也就是文件地址偏移的0x2E4080,此处的对应结构体信息为protocol64_t,可知 protocol64_t.name = 0x10021a5a8, 换成文件地址偏移,也就是0x21a5a8, 得到NSApplicationDelegate,重复操作,可得到4个protocol名字如下,
NSApplicationDelegate
NSOutlineViewDataSource
NSOutlineViewDelegate
ASIHTTPRequestDelegate
ivars 在虚拟地址0x10027DCE8处,也就是文件地址偏移的0x27DCE8, 套上ivar64_list_t可知,ivar64_list_t.count = 0xc, ivar64_list_t.entry = 0x20。 ivar64_t结构体紧跟其后,可知第一个ivar64_t[0].name = 0x1001E0876,换成文件地址偏移,得到 字符串mainWindowController。重复操作,可以得到0xc个ivars变量,如下。
ivar mainWindowController
ivar loginWindowController
ivar aboutWindowController
ivar preferenceMenuItem
ivar findUserMenuItem
ivar customEmotionsItem
ivar mainWindowMenuItem
ivar attentionRequest
ivar isTerminating
ivar userNotificationCenter
ivar eventUrlString
ivar updateTimer
baseProperties在虚拟地址0x10027DE70处,也就是文件偏移的27DE70h,套上objc_property64_list结构体信息,可知objc_property64_list.count = 1, objc_property64_list.entsize = 0x10 , protocol64_t结构体紧随其后,可知第一个objc_property64_list[0].name = 0x1001FEBA0, 也就是文件偏移的 0x1FEBA0
NSTimer updateTimer
综合以上protocol, ivar, property, method 信息。可以得到WWAppDelegate的定义为
@class WWAppDelegate : NSObject < NSApplicationDelegate, NSOutlineViewDataSource, NSOutlineViewDelegate, ASIHTTPRequestDelegate> {
@property NSTimer updateTimer
ivar mainWindowController
ivar loginWindowController
ivar aboutWindowController
ivar preferenceMenuItem
ivar findUserMenuItem
ivar customEmotionsItem
ivar mainWindowMenuItem
ivar attentionRequest
ivar isTerminating
ivar userNotificationCenter
ivar eventUrlString
ivar updateTimer
-dealloc
-init
-checkOSVersion
-showLoginWindow
-showMainWindowAfterLoginSuccess
-checkForUpgrade
-bestValidUpdateInAppcast:forUpdater:
-applicationDidFinishLaunching:
-applicationDidResignActive:
-applicationShouldHandleReopen:hasVisibleWindows:
-applicationWillTerminate:
-applicationShouldTerminate:
-handleURLEvent:withReplyEvent:
-doCheckImportLugeEmotions
-wwLoginResultNotification:
-wwClientWillLogoutNotification:
-wwClientDisconnectedNotification:
-applicationDockMenu:
-chatTo:
-newWangwangProcess:
-showMainWindow
-showMainWindow:
-showFileTransfersWindow:
-showPerferences:
-validateMenuItem:
-showFindUserPanel:
-showFindUserPanel:withSearchText:
-showCustomEmotions:
-showAddFriendsNotification:
-wwSearchContact:
-showAboutWindow:
-visitFAQPage:
-downloadBrowserPlugin:
-setEventUrlString:
-eventUrlString
-mainWindow
-mainWindowController
-updaterShouldRelaunchApplication:
-.cxx_destruct
-updateTimer
-setUpdateTimer:
}
参考文章或代码
https://developer.apple.com/library/mac/documentation/DeveloperTools/Conceptual/MachORuntime/ http://opensource.apple.com/tarballs/ http://sourceforge.net/projects/machoview/?source=directory