如何从目标文件(Mach-o)获取SEL(@selector())? SEL如何存储在Mach-o中?

时间:2013-04-05 07:36:40

标签: iphone objective-c reverse-engineering ida cydia-substrate

从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字符串

enter image description here

但此处没有任何选择器字符串,因此我们可以看到@selector()未转换为静态CFString

我非常有兴趣找到传递给_MSHookMessageEx函数的选择器和类的String表示。

如何从目标文件(Mach-o)中获取SEL(@selector())? SEL如何存储在Mach-o?

谢谢!

更新

我确实在调用之前在ida方法表示中有一些字符串 方法

enter image description here

我猜有些传入函数的选择器。我是对的吗?

1 个答案:

答案 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(可能还有其他地方)。