从objc来源,我们可以看到SEL
被定义为typedef struct objc_selector *SEL;
我用idaq反汇编了我的dylib,我确实调用了_MSHookMessageEx
函数,
从libsubstrate.dylib
_MSHookMessageEx
有以下签名
void MSHookMessageEx(Class class, SEL selector, IMP replacement, IMP *result);
因此我们可以假设源代码中有@selector(someMethod:)
之类的内容
第二个参数
在目标文件的数据部分中,我可以看到源代码中使用的所有CFS字符串
但此处没有任何选择器字符串,因此我们可以看到@selector()
未转换为静态CFString
我非常有兴趣找到传递给_MSHookMessageEx
函数的选择器和类的String表示。
如何从目标文件(Mach-o)中获取SEL(@selector())? SEL如何存储在Mach-o?
谢谢!
更新
我确实在调用之前在ida方法表示中有一些字符串 方法
我猜有些传入函数的选择器。我是对的吗?
答案 0 :(得分:10)
选择器名称存储在__objc_methname
段的__TEXT
部分:
:; otool -v -s __TEXT __objc_methname /System/Library/Frameworks/AppKit.framework/AppKit | head
/System/Library/Frameworks/AppKit.framework/AppKit:
Contents of (__TEXT,__objc_methname) section
0x000000000097cbd8 count
0x000000000097cbde countByEnumeratingWithState:objects:count:
0x000000000097cc09 alloc
0x000000000097cc0f initWithObjects:count:
0x000000000097cc26 release
0x000000000097cc2e autorelease
0x000000000097cc3a copy
0x000000000097cc3f timeIntervalSinceNow
指向选择器的指针存储在__objc_selrefs
段的__DATA
部分中:
:; otool -v -s __DATA __objc_selrefs /System/Library/Frameworks/AppKit.framework/AppKit | head
/System/Library/Frameworks/AppKit.framework/AppKit:
Contents of (__DATA,__objc_selrefs) section
0x0000000000d77d80 __TEXT:__objc_methname:initWithObjects:count:
0x0000000000d77d88 __TEXT:__objc_methname:copy
0x0000000000d77d90 __TEXT:__objc_methname:timeIntervalSinceNow
0x0000000000d77d98 __TEXT:__objc_methname:sharedAppleEventManager
0x0000000000d77da0 __TEXT:__objc_methname:_prepareForDispatch
0x0000000000d77da8 __TEXT:__objc_methname:_setLaunchTaskMaskBits:
0x0000000000d77db0 __TEXT:__objc_methname:_disableSuddenTermination
0x0000000000d77db8 __TEXT:__objc_methname:_appleEventActivationInProgress
源代码中的SEL
实际上(当前)是指向选择器的C字符串名称的指针。所以,如果你这样写:
SEL s = @selector(initWithObjects:count:);
然后s
实际上是char const *
,它指向字符串initWithObjects:count:
。直到最近,您可以通过以下方式打印选择器名称:
NSLog(@"selector is %s", (char *)s);
然而,Apple更改了编译器(我相信Xcode 4.6),禁止将SEL
转换为char *
,因此他们可能会在将来更改选择器的实现。
无论如何,棘手的部分是机器代码使用PC相对寻址从__objc_selrefs
部分加载指针。 PC是“程序计数器”,它是当前正在执行的指令的地址。在x86架构上,它通常称为IP(指令指针)或EIP(扩展IP)。
这就是您反汇编的相关说明中的内容:
1444 LDR R1, =(off_2038 - 0x145C)
...
1454 LDR R1, (PC,R1)
指向选择器的指针从地址0x2038处的字加载。但是常量0x2038实际上并没有出现在机器代码中。您的反汇编程序通过分析程序的数据流为您帮助计算了它。存储在第一个LDR
指令中的常量实际上是0xBDC,因为0xBDC + 0x145C = 0x2038。
当第二个LDR
指令位于地址0x1454时,您可能想知道它为什么使用0x145C。当ARM处理器使用PC相对寻址计算地址时,PC的值实际上是当前正在执行的指令加4或加8 的地址(取决于处理器模式)。 This is documented here(可能还有其他地方)。