Skip to content

Latest commit

 

History

History
437 lines (298 loc) · 15.1 KB

logos_guide.md

File metadata and controls

437 lines (298 loc) · 15.1 KB

Logos

原文

Logos 是 Theos 开发套件的一个组件,他允许使用一组特殊的预处理指令(宏)来轻松、清晰地编写代码。

概述

Logos 提供的语法极大地简化了 MobileSubstrate 扩展(tweaks)的开发,可以在整个操作系统 hook 其他方法。此处,“method hooking”是指用于替换或修改在手机上应用程序中找到的类的方法的技术。

获取 Logos

Logos 随 Theos 一起发布,你可以在任意 Theos 构建的项目中使用 Logos 语法,而无需任何额外的设置。有关 Theos 的更多信息,请访问该页面

Logos 指令列表

块级别

在该类别中的指令,打开一个代码块必须使用 %end 指令来关闭(如下所示)。而这些指令不应写在函数和方法内存中。

%group

%group Groupname

使用一个__组名__开始一个 hook 组(用于条件初始化或组织代码)。所有未分组的 hook 代码都在隐式的,名为“_ungrouped”的组内。

%group 块不能嵌套使用。

分组可用于管理与旧代码的向后兼容性。

%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

开启名为 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 类或其子类。signature 是 Objective-C 类名,如果省略(omitted),将新生成一个。

必须在 %hook 块中使用。

%subclass

%subclass Classname: Superclass <Protocol list>

子类块:在运行时创建类并使用方法填充。尚不支持 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

关闭 group/hook/subclass 块。

顶层

该类别中的指令不应写在于 group/hook/subclass 块中。

%config

%config(Key=Value);

设置 logos 配置标记。

配置标记
  • generator
    • MobileSubstrate
    • internal
      • 生成只使用内部 Objective-C runtime 方法进行 hook 的代码
  • warnings
    • none
      • 无警告
    • default
      • 仅保留非致命警告
    • error
      • 让所有警告致命
  • dump
    • yaml
      • 适用 YAML 格式 dump 内部解析树

%hookf

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

hook 名为 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
}

为适配 MGGetBoolAnswerIgnition hook,你以前必须这样做:

CFBooleanRef (*orig_MGGetBoolAnswer)(CFStringRef);
CFBooleanRef 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(CFBooleanRef, "_MGGetBoolAnswer", CFStringRef string)
{
	if (CFEqual(string, CFSTR("StarkCapability"))) {
		return kCFBooleanTrue;
	}
	return %orig;
}

%ctor

%ctor { … }

创建匿名构造函数(默认优先级)。

%dtor

%dtor { … }

创建匿名析构函数(默认优先级)。

函数级别

该类别中的指令仅能用于函数块中。

%init

%init;
%init([<class>=<expr>, …]);
%init(Group[, [+|-]<class>=<expr>, …]);

初始化一个组(或默认组)。不传递组名则会初始化“_ungrouped”的组,并且在传递 class=expr 参数时,在初始化时会使用给定表达式替换这些类。+ 号(Objective-C 类方法)可以添加到类名前面,以通过表达式替换元类(metaclass)。如果没有指定,默认是用 - 号,来替换该类自身的方法。若果没有指定,该元类(metaclass)会由该类派生。类名替换尤其适用于其类名包含不能用于 %hook 指令的字符,例如空格和点。

用法:

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

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

%class

%class Class;

%class 指定已被启用,请不要在新代码中使用。

前向声明一个类。%c 过时,但仍然可用。创建 $Class 变量,然后用“_ungrouped”组初始化。

%c

%c([+|-]Class)

Evaluates to Class at runtime. If the + sigil is specified, it evaluates to MetaClass instead of Class. If not specified, the sigil defaults to -, evaluating to Class.

在运行时求 Class 的值。如果指定了 + 号,则结果将是 MetaClass 而不是 Class。如果没有指定,默认使用 - 号,结果为 Class。

%orig

%orig
%orig(arg1, …)

调用原始的 hook 方法。在 %new 创建的方法中不起作用。但在子类中是可行的,奇怪得很,因为 MobileSubstrate 会在在 hook 是生成超类调用闭包。(如果我们 hook 的类不存在已经被 hooked 的方法,它会创建一个只调用父类实现的存根(stub)。)参数传递给原始函数,不包括 self_cmd 参数,Logos 会帮你传递这些。

%log

%log;
%log([(<type>)<expr>, …]);

Dump the method arguments to syslog. Typed arguments included in %log will be logged as well.

将方法参数 dump 到系统日志中。还将记录 %log 包含的类型的参数。

Logos 文件扩展

  • .x
    • 将由 Logos 处理,然后进行预处理,并编译为 Objective-C。
  • .xm
    • 将由 Logos 处理,然后进行预处理,并编译为 Objective-C++。
  • .xi
    • 首先作为 Objective-C 进行预处理,然后 Logos 处理结果,然后进行编译。
  • .xmi
    • 首先作为 Objective-C++ 进行预处理,然后 Logos 处理结果,然后进行编译。

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

拆分 Logo hook 代码为多个文件

默认,Logos 预处理器仅在构建式处理一个 .xm 文件。但是,可以将 Logos hook 代码拆分为多个文件。

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

logify.pl

你可以使用 logify.pl,来从头文件创建 Logos 源文件,该 Logos 源文件将记录头文件的所有函数。以下是 logify.pl 生成的非常简单的 Logos tweak。

给定一个头文件:

@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

扩展链接