21

如何将“对象”添加到现有应用程序?

例如,EasyRefresh for Chrome调整可以在 iOS Chrome 应用程序中启用一个新按钮,就像许多其他调整一样。

例如,我如何添加一个简单UIButton的 Twitter 应用程序?

是否有任何GitHub项目可以帮助我理解它是如何完成的?


图片

图片来源:ModMyI


谢谢。

4

2 回答 2

53

这个技巧涉及一些(非常基本的)逆向工程,由几个步骤组成;我会尽可能清楚地解释它们。

步骤 0:如果应用程序是从 AppStore 下载的,则它是加密的。您必须使用通常用于破解应用程序的脚本/应用程序之一对其进行解密;一个命令行脚本是 poedCrack.sh(谷歌它,你会在其中一个粘贴站点上快速找到它),一个 GUI 应用程序是 Crakculous(它在 Cydia 中可用)。请注意,轻松(自动)解密需要其中之一——手动解密方法过于复杂,无法放入 StackOverflow 答案,这就是我建议使用这些工具的原因。)但是,我绝不鼓励你来破解应用程序!(基本上,我要求您不要将这些工具用于其原始目的:) 如果您想查看手动解密过程,请前往此处。

第一步:您需要执行应用程序使用/创建的类。为此,您需要 class-dump 或 class-dump-z 实用程序。此命令行应用程序反转应用程序的二进制可执行文件,并为应用程序使用和内部拥有的所有 Objective-C 类生成接口声明。您可以在这里找到更高级和首选的变体 class-dump-z

第二步:在你有了类声明之后,你必须猜测哪个类在什么时候做什么(是的,有点混乱)。例如,在通过 class-dump-z 从上述应用程序 Google Chrome 生成的文件之一中,您可能会发现类似的内容:

@interface ChromeUrlToolbar: UIToolbar {
    UISearchBar *urlBar;
}

- (id)initWithFrame:(CGRect)frame;
- (void)loadURL:(NSURL *)url;

@end

嗯,这听起来不错,不是吗?您可以看到它的实现有一个 initWithFrame: 方法(作为所有 UIView 子类)——为什么不尝试修改它呢?

第三步:对于这个修改,你需要MobileSubstrate。MobileSubstrate 是由 Cydia 的创建者 Saurik 创建的开发人员库,旨在使代码注入应用程序变得容易。你可以在网上找到一些非常好的教程,包括这个。所以,你有一个类,你想“挂钩”它——所以你写了一些这样的代码:

static IMP __original_init; // A

id __modified_init(id __self, SEL __cmd, CGRect frame) // B
{
    __self = __original_init(__self, __cmd, frame); // C

    // D
    UIButton *newButton = [UIButton buttonWithType:UIButtonTypeRoundedRect];
    [newButton setTitle:@"Chrome Pwned"];
    newButton.frame = CGRectMake(0, 0, 100, 40);
    [__self addSubview:newButton];

    return __self;
}

// E
__attribute__((constructor))
void init()
{
    Class clazz = objc_getClass("ChromeUrlToolbar"); // F
    MSHookMessageEx(clazz, @selector(initWithFrame:), __modified_init, &__original_init); // G
}

解释:让我们从头开始。函数( initE) 被声明__attribute__((constructor))。这意味着当我们将使用此代码创建的库将加载到 Chrome 中时,它会自动调用。这正是我们想要的,因为我们想在启动应用程序之前改变它的行为。

在标记的行上// F,我们捕获了我们想要修改的类对象本身。Objective-C 是一种高度动态的语言;这意味着我们可以在运行时获取和修改有关类和对象的信息。在标记的行上// G,我们使用了 MobileSubstrate API 中最重要的函数:MSHookMes​​sageEx。要了解它的工作原理(而不是它的作用),您必须了解以下内容: Objective-C 本身是作为一个普通的 C 库实现的——语言本身,在底层,只是简单的 C。所以每条消息都在 Obejctive 中发送-C 实际上是一个 C 函数调用。这些 C 函数有两个特殊参数:selfcmd-- 前者是指向正在发送消息的对象的指针,后者是选择器(一个特殊的、唯一的指针,指向正在发送的消息的名称)。所以 MSHookMes​​sageEx 所做的是它接受一个类和一个选择器,找到对应于它们的函数的实现,并将该函数与它的第三个参数本身提供的函数交换(__modified_init在这种情况下)。为了不丢失数据,它还在其第 4 个参数中返回该函数(此处为__original_init)。

那么,现在Chrome URL工具栏的初始化被重定向到了我们的函数中,接下来要做什么呢?好吧,没什么特别的:首先我们只是调用原始初始化函数(注意前两个特殊参数,__self 和 __cmd!),它像正常一样创建工具栏(这行代码用 表示// C)。然后,我们进行实际的修改:在 section 中// D,我们创建一个 UIButton,设置它的标题和位置,并作为子视图添加到我们新创建的工具栏。然后,知道这是一个初始化函数,我们返回原始实例以及注入其中的按钮代码。

好吧,这基本上就是您需要了解的内容;如果您对 Objective-C 的工作原理以及如何创建酷炫的 iOS 调整感兴趣,我建议您阅读 Apple关于该主题的官方文档,您可以浏览我的一些开源 Cydia 调整。也是。

我希望这能帮到您!

于 2012-07-22T17:47:38.073 回答
12

为此,您需要了解 Objective-C 运行时的工作原理。尤其是消息传递系统(即调用方法)。特别是,调用的方法是在运行时确定的,而不是在编译时确定的其他语言。这允许对特定方法进行全局更改,也就是方法调配

使用Mobile Substrate 库,您将可以用自己的方法替换任何方法实现,甚至可以调用原始实现。当然,您需要知道方法的名称和它所接受的参数,以及它所属的类。

因此,例如要修改 SpringBoard,您必须知道包含哪个类和哪个方法。您必须使用class-dumporclass-dump-z实用程序来为您执行此操作(class-dump-z更新并更多地用于 iOS 开发,class-dump更通用并与较旧的二进制文件以及 64 位兼容)。

因此,要对 SpringBoard 进行类转储,您需要输入 Terminal.app

class-dump -H /Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS5.0.sdk/System/Library/CoreServices/SpringBoard.app/SpringBoard -o ~/Desktop/SpringBoard

对于 class-dump-z,该-p选项将生成@property而不是 getters/setters,这更清楚,所以你可能会输入

class-dump-z -p -H /Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS5.0.sdk/System/Library/CoreServices/SpringBoard.app/SpringBoard -o ~/Desktop/SpringBoard

该行将在您的桌面上创建一个文件夹,其中包含 SpringBoard 的所有类定义。当然,您可能需要将路径更改为适合您系统的路径(关于这一点,对于最新版本的 Xcode,Developer 文件夹位于Xcode 中,因此您需要类似

/Applications/Xcode/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS5.1.sdk/System/Library/CoreServices/SpringBoard.app/SpringBoard

您还可以在 Internet 上找到为大多数现有框架为您执行此操作的人员,如果您确保他们的版本适合您的系统,这将非常方便。

现在,对于 AppStore 应用程序,您首先需要对它们进行解密,因为它们受到保护。您可能需要自己查找名称和链接,因为这可能违反 Stack Overflow 的 ToS,尽管使用gdb可以达到该目的。

为了简化工作,已经创建了一些工具,例如Logos(您可能还需要查看Theos )来减少所需的样板代码。还有一个(相当老的)xcode 模板和用于 mobilesubstrate 的教程,它提供了很好的帮助。

Logos 可以很容易地method从类中挂钩方法classname

%hook classname //declares the class from your application you're going to override

-(void)method {

    dosomethingnew(); //put all your new code for the method here
    return %orig;     //this calls the original definition of the method
}
%end //end hooking classname

有关系统中框架的列表以及它们的用途,请参见此处

最后一件事:开源的流行调整列表(尽可能链接到 GitHub):

一些小调整

最后,看看WeekTweak,他们每周都会发布开源调整,因此您可以通过查看其他人的源代码来学习并尝试和做自己的事情。IRC (irc.saurik.com) 上的#theos chan 也会提供帮助,如果您提出要求的话。

于 2012-07-22T18:03:58.567 回答