奇怪的私有api调用出错问题

CALayer有一个私有api doubleBounds, 想访问这个私有api返回的数据,用了下面的代码:
id valuedoubleBounds = [view.layer performSelector:NSSelectorFromString(@“doubleBounds”)];

结果发现崩了,提示-[CALayer doubleBounds]: unrecognized selector sent to instance
根据链接:CALayer.h
可以知道doubleBounds是下面这样定义的,返回一个结构体数据,并且查看历史记录知道从iOS7到iOS10这个方法是一直存在于CALayer里面的

  • (struct CADoubleRect { struct CADoublePoint { double x_1_1_1; double x_1_1_2; } x1; struct CADoubleSize { double x_2_1_1; double x_2_1_2; } x2; })doubleBounds;

我是在非越狱环境下调用这个私有api的,目前测试iOS10.3.3和iOS7越狱设备调用这个api都会崩,虽然调用这个私有api 不是必需,但很想知道为什么调用这个私有api就会出错

同样的环境下,调用UIView的私有api recursiveDescription就完全没问题

为什么我只看到了setDoubleBounds:

你再搜搜看,doubleBounds 这个方法因为返回的是一个结构体,所以方法名超长

试了一下没崩溃

设备 11.0 未越狱

我用新建的Xcode工程试了你的代码都是会崩的,这真是要命
但是,但是我灵机一动,把这段代码复制到以前做的一个大型app中,居然就不崩了
我以为是没有引用头文件的问题,在新建Xcode工程里加上了#import <QuartzCore/QuartzCore.h> 还是会崩
猜测这个方法是category里面的,如果没有正确import对应的framework,所以就会必崩?

还是没有定位出来要添加什么东东才能导致调用这个私有api不出错
从另一方面来说,这个私有api隐藏得很不错

分类的话,可能不是,因为根据上面的 CALayer.h 的头文件,方法是在

// Image: /System/Library/Frameworks/QuartzCore.framework/QuartzCore

下的,应该就在 QuartzCore

我运行时用 image list 打印加载的库会有如下的输出

Developer/Xcode/iOS DeviceSupport/11.0 (15A372)/Symbols/System/Library/Frameworks/QuartzCore.framework/QuartzCore

然后用 respondsToSelector 返回也是 1

如果是设备,系统 ,Xcode版本都是一致的,我还是比较倾向于是工程配置的问题,可能比较 Build Settings 会有一些启发吧

要说某个工程配置能隐藏私有api这么深倒是有点不相信啊,recursiveDescription也是私有api,怎么就用得好好的

是有点神奇, 怀疑是不是发错类了, 或者调用前打印下_shortMethodDescription

没理由发错类的,因为崩的时候报的错都是 -[CALayer doubleBounds]: unrecognized selector sent to instance,说明类CALayer没有错吧

那天在庆哥群里就跟你说了在category里拿IMP看下实现在哪自己加载

我知道可以通过 NSSelectorFromString 和 methodForSelector 拿到一个OC方法的IMP,可是怎么拿IMP看下实现在哪?这个实在不会啊,求教

额… 不是说工程配置有专门隐藏私有API的东西,而是说你原来的工程既然可以,那是不是原来的工程是不是手动引入了一些 framework,而那个里面有那个 doubleBounds

根据下面的那个截图, doubleBounds 应该是在 MapKit 而不是 QuartzCore ,
我用 9 的设备,试了下 同样 image list 没有找到 MapKit,respondsToSelector = 0

不是直接dladdr不就知道实现在哪了咩

1 个赞

好吧,水落石出,万分感谢,做梦都没有想到是在MapKit里啊,因为我是在逆向UIScrollView里的某一个方法看到这个方法的,image list 真是个好东西
刚才还发现我在老项目调用 id valuedoubleBounds = [self.view.layer performSelector:NSSelectorFromString(@“doubleBounds”)]; 还是有问题的,居然返回的是一个CALayer对象,这个还是不可靠,最后还是通过下面的代码得到真实的值:

SEL selector = NSSelectorFromString(@"doubleBounds");
IMP imp = [self.view.layer methodForSelector:selector];
CGRect (*func)(id, SEL) = (void*)imp;
CGRect doubleBounds = func(self.view.layer, selector);
NSLog(@"doubleBounds is %@", NSStringFromCGRect(doubleBounds));

搜了一下老项目的代码,果然是有一个地方写了 #import “MapKit/MapKit.h”,不过是在SDWebImage中,没见项目配置中添加有MapKit.framework,而新建的项目必须要添加这个framework才会不崩,猜测前者是某个第三方framework自行添加了MapKit.framework吧

大神就是大神, 有关键字就好说了,新技能Get:

#import <objc/runtime.h>
#include <dlfcn.h>
#include <objc/objc.h>

    Dl_info info;
    if (dladdr(imp, &info)) {
        printf("dli_fname: %s\n", info.dli_fname);
        printf("dli_sname: %s\n", info.dli_sname);
        printf("dli_fbase: %p\n", info.dli_fbase);
        printf("dli_saddr: %p\n", info.dli_saddr);
    } else {
        printf("error: can't find that symbol.\n");
    }

结果:

dli_fname: /System/Library/Frameworks/MapKit.framework/MapKit
dli_sname: <redacted>
dli_fbase: 0x194256000
dli_saddr: 0x1943c7c0c

原来不知道,现在好像知道了点,非常感谢。
_shortMethodDescription 找地址, 用 image lookup 去定位了

(lldb) image lookup -a 0x18bd06218
      Address: MapKit[0x000000018b116218] (MapKit.__TEXT.__text + 1068056)
      Summary: MapKit`-[CALayer(MKDoubleLayer) doubleBounds]
``

SDWebImage 上的 .podspec 文件,确实指明了依赖 MapKit

但是我这里的老项目集成 SDWebImage 并不是通过pod集成的

原来你的截图已经明示了是在MapKit里,哎