如何将“对象”添加到现有应用程序?
例如,EasyRefresh for Chrome调整,在iOS Chrome应用中启用了一个新按钮,就像许多其他调整一样。
我如何添加一个简单的UIButton
,例如,Twitter应用程序?
是否有任何GitHub项目可以帮助我理解它是如何完成的?
图片来源:ModMyI
感谢。
答案 0 :(得分:52)
这个技巧涉及一些(非常基本的)逆向工程,由几个步骤组成;我会尽可能清楚地解释它们。
步骤零:如果从AppStore下载应用程序,则会加密。您必须使用通常用于破解应用程序的脚本/应用程序之一来解密它;一个命令行脚本是poedCrack.sh(google it,你可以在其中一个粘贴网站上快速找到它),一个GUI应用程序是Crakculous(它在Cydia中可用)。请注意,其中一个是简单(自动)解密所必需的 - 手动解密方法过于涉及到放入StackOverflow的答案,这就是我建议使用这些工具的原因。)但是,我不以任何方式鼓励你破解应用程序! (基本上我要求你不要将这些工具用于其原始目的:)如果你想看看手动解密过程,head here.
第一步:您需要执行应用程序使用/创建的类。为此,您需要class-dump或class-dump-z实用程序。此命令行应用程序反转应用程序的二进制可执行文件,并为应用程序使用并具有内部的所有Objective-C类生成接口声明。您可以找到class-dump-z,更高级和首选的变体here.
第二步:在你有了类声明之后,你将不得不猜测哪个类做了什么以及什么时候(是的,有点令人困惑)。例如,在上面的应用程序Google Chrome中,通过class-dump-z生成的其中一个文件,您可能会发现类似的内容:
@interface ChromeUrlToolbar: UIToolbar {
UISearchBar *urlBar;
}
- (id)initWithFrame:(CGRect)frame;
- (void)loadURL:(NSURL *)url;
@end
嗯,听起来不错,不是吗?您可以看到它的实现有一个initWithFrame:方法(作为所有UIView子类) - 为什么不尝试修改它?
第三步:对于此次修改,您需要MobileSubstrate。 MobileSubstrate是由Cydia的创建者Saurik创建的开发人员库,用于简化应用程序的代码注入。您可以在网上找到一些非常好的教程,包括this one。 所以,你有一个课程,你想要“挂钩”它 - 所以你写了这样的代码:
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
}
说明:让我们从结束开始。 init
函数(E)声明为__attribute__((constructor))
。这意味着当我们将使用此代码创建的库加载到Chrome中时,它会自动调用。这正是我们想要的,因为我们想要在启动之前改变应用程序的行为。
在标记为// F
的行上,我们捕获了我们想要修改的类对象本身。 Objective-C是一种高度动态的语言;这意味着我们可以在运行时获取和修改有关类和对象的信息。在标记为// G
的行上,我们使用MobileSubstrate API最重要的功能:MSHookMessageEx。要理解它是如何工作的(而不是它的作用),你必须知道以下内容:Objective-C本身是作为一个普通的C库实现的 - 语言本身,在简单的C之下。所以每个消息都在Obejctive中发送-C实际上是一个C函数调用。这些C函数有两个特殊参数:self
和cmd
- 前者是指向被消息对象的指针,后者是选择器(一个特殊的,唯一的指向消息名称的指针)发送)。所以MSHookMessageEx所做的是它需要一个类和一个选择器,找到与它们对应的函数的实现,并用它的第三个参数本身(在这种情况下为__modified_init
)中提供的函数交换该函数。为了不丢失数据,它还返回第4个参数中的函数(这里是__original_init
)。
因此,现在Chrome网址工具栏的初始化会重定向到我们的功能,下一步该怎么做?好吧,没有什么特别之处:首先我们只调用原始初始化函数(注意前两个特殊参数,__ self和__cmd!),这就像正常情况一样创建工具栏(这行代码用// C
表示)。然后,我们进行实际更改:在// D
部分中,我们创建一个UIButton,设置其标题和位置,并将其作为子视图添加到我们新创建的工具栏中。然后,知道这是一个初始化函数,我们返回原始实例以及注入其中的按钮代码。
嗯,这基本上是你需要了解的;如果您对Objective-C如何工作的深层细节以及如何创建酷炫的iOS调整感兴趣,我建议您阅读Apple的official documentation on the topic,您也可以浏览some of my opensource Cydia tweaks.。
我希望这会对你有所帮助!
答案 1 :(得分:11)
您需要这样做才能理解Objective-C运行时的工作原理。特别是消息传递系统(即调用方法)。特别是,调用方法是在运行时确定的,与编译时的其他语言相比。 这允许全局更改特定方法,即方法调配。
使用Mobile Substrate library,您将被允许用您自己的方法替换任何方法实现,甚至可以调用原始实现。当然,您需要知道方法的名称和所需的参数,以及它所属的类。
因此,要修改SpringBoard,您必须知道包含哪个类以及哪个方法。您必须使用为您执行此操作的class-dump
或class-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
而不是getter / setter,这更清晰,所以你可能输入
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
对于大多数现有框架,您也可以find on the internet people who did that for you,这非常方便如果您确定它们是适合您系统的版本。
现在,对于AppStore应用程序,您首先需要解密它们,因为它们受到保护。您可能需要找到自己的名称和链接,因为这可能违反了Stack Overflow的ToS,尽管使用gdb
可以达到这个目的。
为了简化工作,我们创建了一些工具,例如Logos(您可能还需要查看Theos),以减少所需的样板代码。还有一个(相当古老的)xcode template & tutorial for 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
有关系统中框架的列表及其有用信息,请参阅here
最后一件事:开源的流行调整列表(尽可能链接到GitHub):
最后,看看the WeekTweak,他们每周都会发布开源调整,以便您可以通过查看其他人的来源来了解并尝试&做你自己的事情。 IRC(irc.saurik.com)上的#theos chan也会提供帮助,如果你善意的话。