在iOS中,方法替换私有框架API

9
我知道有很多关于方法混淆的资源。但是是否可以从私有API中混淆方法?问题在于没有头文件。我想从PrivateFramework中的私有类(例如(随机示例)Message.framework方法)混淆一个方法。
这是为了个人测试,我知道它会被苹果拒绝。

1
如果你想使用私有API,需要头文件,你可以通过反向工程来获取。可以使用class-dumpclass-dump-z等工具。很多人已经将反向工程的头文件发布在网上。然后,将该头文件作为源文件包含在项目中即可。如果遇到反向工程的头文件无法编译的问题,通常只需要手动检查并删除不需要的#imports,或者将头文件精简到只包含你感兴趣的函数即可。 - Nate
那是我最初所做的,认为一定有更简单的方法。无论如何,多亏了Bryan(见下文),我能够扭曲这个私有API。问题是其他框架不能被扭曲。也就是说,如果我直接从我的测试代码调用该方法,我会得到扭曲后的函数,但如果在某些其他框架中调用该方法,则不会调用我的扭曲后的函数。 - John Smith
你可能需要发布一个新的跟进问题。此外,请具体说明。了解您正在调整哪个API将是有用的,并且您是否在说当该API从您的应用程序进程内部调用,但直接由框架(而不是您的代码)调用时,交换不起作用? - Nate
2个回答

11
您可以使用 NSClassFromString 来获取 Class,然后使用运行时库执行方法交换。不需要头文件,您只需要知道类名和方法签名。
当使用 @selector(somePrivateMethod) 时出现错误,指出 somePrivateMethod 不是有效的选择器(因为头文件不可用),可以使用 sel_getUid
代码来自 我的 Xcode 插件
SEL sel = sel_getUid("codeDiagnosticsAtLocation:withCurrentFileContentDictionary:forIndex:");
Class IDEIndexClangQueryProviderClass = NSClassFromString(@"IDEIndexClangQueryProvider");

Method method = class_getInstanceMethod(IDEIndexClangQueryProviderClass, sel);
IMP originalImp = method_getImplementation(method);

IMP imp = imp_implementationWithBlock(^id(id me, id loc, id dict, IDEIndex *idx) {
    id ret = ((id (*)(id,SEL,id,id,id))originalImp)(me, sel, loc, dict, idx);

    // do work

    return ret;
});

method_setImplementation(method, imp);

发生了什么?还要确保这些代码已经运行...将其放在+[load]中。 - Bryan Chen
代码肯定是在运行的。但是为了验证这一点,我可以调用私有API并查看它是否调用原始方法或交换后的方法。我尝试过了,但是我得到了“向类发送未识别的选择器”的错误,这意味着我的私有API根本没有被调用。例如,我使用了声明的类:[IDEIndexClangQueryProviderClass codeDiagnosticsAtLocation:0 withCurrentFileContentDictionary:0 forIndex:NULL]。 - John Smith
等等...你为什么要调用[IDEIndexClangQueryProviderClass codeDiagnosticsAtLocation:0 withCurrentFileContentDictionary:0 forIndex:NULL]?这只是一个例子。除非你也在编写Xcode插件,否则它不会起作用。你应该将类名、选择器名称和方法签名更改为iOS上存在的内容。 - Bryan Chen
更新- 不,我在调用自己混淆的私有API,并且它起作用了。无论如何,我意识到我试图混淆的方法是一个实例方法,所以我应该使用allocate/init一个实例,这样就可以工作了 :) 谢谢! - John Smith
补充你的答案:你也可以使用NSSelectorFromString()来获取一个选择器,而不是使用sel_getUid,以便在仍然绕过编译器检查的情况下抓取它。我发现NSSelectorFromString更容易与Swift进行互操作。 - Charlton Provatas

0
在类上创建一个类别,并添加您想要调用的方法的声明。然后,您只需实例化该类的一个实例并调用该方法即可。
这也适用于在代码中单元测试私有方法。

我并不是试图添加一个方法,我是在试图替换一个方法。你是在暗示声明该方法会替换现有的方法吗? - John Smith
声明该方法将提供访问该方法以进行交换的功能。在问题中,抱怨没有标题方法。通过创建类别,您可以有效地为想要交换的方法创建一个标题方法。 - zaph
这里没有使用类别的理由,而且由于这并不是类别的真正用途,所以这是一种应该避免的令人困惑的技术。如果问题在于框架是私有的,并且您没有头文件,只需反向工程化头文件(或您感兴趣的各个方法),并将该头文件包含在您的项目中即可。它没有必要成为一个类别。OP在私有API开发中缺少一种基本技术,即反向工程需要的头文件。这是这类问题的标准模式。 - Nate
可能是类是公共的并且有一个公共头文件,但方法是私有的。至于使用分类,我同意应该避免使用它们。整个交换私有类通常是一个坏主意,但在这种情况下,OP声明:“用于个人测试”。 - zaph

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接