如何向现有应用程序添加“对象”?
例如,EasyRefresh for Chrome扩展可以在iOS Chrome应用程序内启用一个新按钮,许多其他扩展也是如此。
我如何将一个简单的UIButton
添加到Twitter应用程序中?
是否有GitHub项目可以帮助我了解如何完成这个操作?
图片来源:ModMyI
谢谢。
如何向现有应用程序添加“对象”?
例如,EasyRefresh for Chrome扩展可以在iOS Chrome应用程序内启用一个新按钮,许多其他扩展也是如此。
我如何将一个简单的UIButton
添加到Twitter应用程序中?
是否有GitHub项目可以帮助我了解如何完成这个操作?
图片来源:ModMyI
谢谢。
这个技巧涉及到一些(非常基础的)反向工程,由几个步骤组成;我将尽可能清晰地解释它们。
零步骤:如果应用程序是从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创建的开发人员库,旨在使代码注入应用程序变得容易。你可以在网上找到一些非常好的教程,包括this one。 所以,你有一个类,想要“hook”它--那么你就写一些像这样的代码:
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所做的就是取一个类和一个选择器,找到对应它们的函数的实现,并将该函数与其第3个参数中提供的函数本身(在本例中为__modified_init
)交换。为了不丢失数据,它还将该函数返回到其第4个参数中(在这里是__original_init
)。
现在,Chrome URL工具栏的初始化已经重定向到我们的函数中,接下来该怎么做呢?其实没什么特别的:首先,我们只需调用原始的初始化函数(注意前两个特殊参数__self和__cmd!),它会像通常一样创建工具栏(这行代码由// C
表示)。然后,在// D
部分进行实际的修改:我们创建一个UIButton,设置其标题和位置,并将其作为子视图添加到我们刚刚创建的工具栏中。然后,知道这是一个初始化函数后,我们将原始实例与我们注入的按钮代码一起返回。
好了,基本上就是这些;如果您对Objective-C的工作原理以及如何创建酷炫的iOS调整感兴趣,我建议您阅读苹果公司的官方文档,您也可以浏览我的一些开源Cydia调整。
希望这能帮到您!
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
-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
/Applications/Xcode/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS5.1.sdk/System/Library/CoreServices/SpringBoard.app/SpringBoard
gdb
可以实现这个目的。classname
中挂钩方法method
变得容易:%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):