iOS插件开发的一些知识点

我们在写插件的时候都知道很多时候用到theos去写更为方便
因为theos在线调试后可以直接hook我们需要的东西

比如广告,基址,地址,等各种UIView或者conterller等

今天就记录一下我们theos在写插件的时候用的语法等一些记录

创建项目

首先我们需要创建项目,这些问题我不在强调了
终端输入nic.pl创建tweak项目
创建后我们需要打开tweak.x文件去写代码

注释:
你也可以xcode安装monkeydev直接创建logos插件
这样也是同样的打开xm文件去写代码
xm文件可以写oc,c,swift,logos等代码,但是需要知道xm文件没有代码提示
或者直接删除文件引用m的oc或者swift文件解决

开始记录

%group

%group Groupname

group用于条件初始化或代码组织。分组可用于管理与旧代码的向后兼容性。

以组名开头的hook group。group不能在另一个%group块内。所有未分组的hook都在隐式“_ungrouped” group中。如果没有其他group,则会为您初始化_ungrouped组。您可以使用%init指令手动初始化它。其他组必须使用%init(Groupname)指令初始化。

%group iOS8
%hook IOS8_SPECIFIC_CLASS
    // your code here
%end // end hook
%end // end group ios8

%group iOS9
%hook IOS9_SPECIFIC_CLASS
    // your code here
%end // end hook
%end // end group ios9

%ctor {
    if (kCFCoreFoundationVersionNumber > 1200) {
        %init(iOS9);
    } else {
        %init(iOS8);
    }
}

%hook

%hook Classname

为名为类名的类打开一个hook块。

可以在%group块内。

这里有一个微不足道的例子:

%hook SBApplicationController
-(void)uninstallApplication:(SBApplication *)application {
    NSLog(@"Hey, we're hooking uninstallApplication:!");
    %orig; // Call the original implementation of this method
    return;
}
%end

%new

`%new
%new(signature)`

通过在方法定义上方添加此指令,将新方法添加到hook类或子类中。签名是新方法的Objective-C类型编码;如果省略,将生成一个。

必须位于%hook块内。

您还应该在界面中声明新方法,因为Logos不会自动声明。例如:

@interface TheClass (TweakMethods)
-(void)yourNewMethod;
@end

%hook TheClass
%new
-(void)yourNewMethod { /* code */ }
%end

%subclass

%subclass Classname: Superclass

子类块-该类在运行时创建并填充方法。尚未支持ivars(使用关联对象)。对于超类中不存在的方法,需要%new说明符。要实例化新类的对象,您可以使用%c运算符。

可以在%group块内。

这里有一个例子:

%subclass MyObject : NSObject

- (id)init {
    self = %orig;
    [self setSomeValue:@"value"];
    return self;
}

//the following two new methods act as `@property (nonatomic, retain) id someValue;`
%new
- (id)someValue {
    return objc_getAssociatedObject(self, @selector(someValue));
}

%new
- (void)setSomeValue:(id)value {
    objc_setAssociatedObject(self, @selector(someValue), value, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}

%end

%ctor {
    MyObject *myObject = [[%c(MyObject) alloc] init];
    NSLog(@"myObject: %@", [myObject someValue]);
}

%property

%property (nonatomic|assign|retain|copy|weak|strong|getter|setter) Type name;

将属性添加到%subclass中,就像使用@property添加到正常的Objective-C子类中一样,并在%hook中向现有类添加新属性。

必须位于%subclass或%hook块内。

%end

%end

关闭组/钩子/子类块。

%config

%config(Key=Value);

设置logos标致

%hookf

%hookf(rtype, symbolName, args…) { … }

为名为symbolName的函数生成一个函数钩子。如果名称作为字面字符串传递,则将动态查找该函数。

// Given the function prototype
FILE *fopen(const char *path, const char *mode);
// The hook is thus made
%hookf(FILE *, fopen, const char *path, const char *mode) {
    NSLog(@"Hey, we're hooking fopen to deny relative paths!");
    if (path[0] != '/') {
        return NULL;
    }
    return %orig; // Call the original implementation of this function
}

人们通常会挂钩其地址在运行时解析的函数,例如MGGetBoolAnswer,例如:

bool (*orig_MGGetBoolAnswer)(CFStringRef);
bool fixed_MGGetBoolAnswer(CFStringRef string)
{
    if (CFEqual(string, CFSTR("StarkCapability"))) {
        return kCFBooleanTrue;
    }
    return orig_MGGetBoolAnswer(string);
}

%ctor {
    MSHookFunction(((void *)MSFindSymbol(NULL, "_MGGetBoolAnswer")), (void *)fixed_MGGetBoolAnswer, (void **)&orig_MGGetBoolAnswer);
    ...
}

您还可以做:

%hookf(bool, MGGetBoolAnswer, CFStringRef string)
{
    if (CFEqual(string, CFSTR("StarkCapability"))) {
        return true;
    }
    return %orig;
}
%ctor() {
    %init(MGGetBoolAnswer = MSFindSymbol(NULL, "_MGGetBoolAnswer"));
}

并将MSFindSymbol(NULL,“_MGGetBoolAnswer”)替换为函数指针的任何表达式。

%ctor

%ctor { … }

生成匿名构造函数(默认优先级)。

%dtor

%dtor { … }

生成一个匿名解构函数(默认优先级)。

%init

%init;
%init([=, …]);
%init(Group[, [+|-]=, …]);

初始化组(或默认组)。不传递组名将初始化“_ungrouped”,传递class=expr参数将在初始化时用给定的表达式替换这些类。+ sigil(如Objective-C中的类方法)可以前置于类名,以替换元类的表达式。如果没有指定,sigil默认为-,以替换类本身。如果没有指定,元类就是从类派生出来的。类名替换对于包含不能用作%hook指令的类名令牌的类,例如空格和点,类名替换特别有用。

Usage:

%hook SomeClass
-(id)init {
    return %orig;
}
%end

%ctor {
    %init(SomeClass=objc_getClass("class with spaces or dots in the name"));
}

%class

%class Class;

向前宣布一个类。过时%c,但仍然存在。创建一个$Class变量,并使用“_ungrouped”组初始化它。

%c

%c([+|-]Class)

在运行时向Class进行评估。如果指定了+ sigil,它将计算为MetaClass而不是Class。如果没有指定,sigil默认为-,评估为类。

%orig

%orig

%orig(arg1, …)

调用原始挂钩方法。它在%new’d方法中不起作用。奇怪的是,它在子类中工作,因为MobileSubstrate将在钩子时间生成一个超级调用闭包。(如果挂钩方法在我们挂钩的类中不存在,它会创建一个仅调用超类实现的存根。)args传递给原始函数-不包括self和_cmd,Logos会为您这样做。

%log

%log;

%log([(), …]);

将方法参数转储到syslog。%Log中包含的类型参数也将被记录。

文件扩展名

.x文件 将由Logos处理,然后作为 objective-c进行预处理和编译。
.xm文件 将由Logos处理,然后作为 objective-c++进行预处理和编译。
.xi文件 将首先作为 objective-c进行预处理,然后Logos将处理结果,然后进行编译。
.xmi文件 将首先作为 objective-c++进行预处理,然后Logos将处理结果,然后编译。

xi或xmi文件可以在#define宏中使用Logos指令。

默认情况下,Logos预处理器只会在构建时处理一个.xm文件。但是,可以将徽标挂钩代码拆分为多个文件。

首先,主文件必须重命名为.xmi文件。然后,可以使用#include指令包含其他.xm文件。Logos预处理器将在处理之前将这些文件添加到主文件中。

通常,无法在多个徽标文件中初始化钩子组,但有一个变通办法可用于解锁此功能。这是通过将组初始化包装在静态方法中来完成的,然后可以从其他文件调用。

请查看以下代码。它所做的就是在SpringBoard应用程序启动完成后记录一条消息。它位于一个名为TweakGroup的组内,该组在名为InitGroup()的静态函数中初始化。

// Group.xm
#import "Shared.h"

%group TweakGroup
%hook SpringBoard

- (void)applicationDidFinishLaunching:(id)arg1 {
    %orig;
    NSLog(@"[Group Test] SpringBoard has finished launching");
}

%end
%end

extern "C" void InitGroup() {
    %init(TweakGroup);
}

您可能已经注意到,Group.xm的顶部有一个Shared.h的导入。这只是一个头文件,将导入到我们的主Logos文件中,以便我们可以在那里调用该函数:

// Shared.h
extern "C" void InitGroup();

最后,Shared.h可以导入包含构造函数的Logos文件中。调用静态函数将从Group.xm初始化组并运行其hook:

// Tweak.xm
#import "Shared.h"

%ctor {
    NSLog(@"[Group Test] Our hook for SpringBoard should show up below this");
    InitGroup();
}

如果操作正确且编译没有错误,这应该可以记录两条消息:一条来自构造函数,另一条来自组内的方法。请记住,这不适用于不在组内的hook。

需要注意的几件事:

这在正常的C中不起作用,因此您必须为您的组和构造函数使用.xm文件。
你必须注意你调用初始化的次数,因为Logos将不再告诉你它是否被调用不止一次。

您可以使用logify.pl从头文件创建Logos源文件,该标头文件将记录该标头文件的所有功能。以下是logify.pl生成的非常简单的徽标调整示例

给定一个标题文件:

@interface SSDownloadAsset : NSObject
- (NSString *)finalizedPath;
- (NSString *)downloadPath;
- (NSString *)downloadFileName;
+ (id)assetWithURL:(id)url type:(int)type;
- (id)initWithURLRequest:(id)urlrequest type:(int)type;
- (id)initWithURLRequest:(id)urlrequest;
- (id)_initWithDownloadMetadata:(id)downloadMetadata type:(id)type;
@end

您可以在$THEOS/bin/logify.pl上找到logify.pl,并以这样使用它:

$THEOS/bin/logify.pl ./SSDownloadAsset.h

打印的结果应该是:

%hook SSDownloadAsset
- (NSString *)finalizedPath { %log; NSString * r = %orig; NSLog(@" = %@", r); return r; }
- (NSString *)downloadPath { %log; NSString * r = %orig; NSLog(@" = %@", r); return r; }
- (NSString *)downloadFileName { %log; NSString * r = %orig; NSLog(@" = %@", r); return r; }
+ (id)assetWithURL:(id)url type:(int)type { %log; id r = %orig; NSLog(@" = %@", r); return r; }
- (id)initWithURLRequest:(id)urlrequest type:(int)type { %log; id r = %orig; NSLog(@" = %@", r); return r; }
- (id)initWithURLRequest:(id)urlrequest { %log; id r = %orig; NSLog(@" = %@", r); return r; }
- (id)_initWithDownloadMetadata:(id)downloadMetadata type:(id)type { %log; id r = %orig; NSLog(@" = %@", r); return r; }
%end

这样,我们就可以轻松的运用我们的tweak进行hook了

更多交流后期会有教程!

© 版权声明
THE END
喜欢就支持一下吧
点赞15赞赏 分享
评论 抢沙发

请登录后发表评论

    暂无评论内容