读书频道 > 网站 > 网页设计 > iOS应用逆向工程(第2版)
3.2.3 Theos用法介绍
15-06-17    下载编辑
收藏    我要投稿   

本文所属图书 > iOS应用逆向工程(第2版)

仔细阅读了本书从字里行间可以感受到作者的认真,在当今这个浮躁的大环境中,这是非常难得的,单从认真这点来看,这本书绝不是一本水货,强烈推荐大家阅读,尤其是初学者。本书的前六章侧重于理论的介绍,但又不立即去当当网订购
1. 创建工程
 
1)更改工作目录至常用的iOS工程目录(如笔者的是“/Users/snakeninny/Code/”),然后输入“/opt/theos/bin/nic.pl”,启动NIC(New Instance Creator),如下:
snakeninnysiMac:Code snakeninny$ /opt/theos/bin/nic.pl
NIC 2.0 - New Instance Creator
------------------------------

  [1.] iphone/application
  [2.] iphone/cydget
  [3.] iphone/framework
  [4.] iphone/library
  [5.] iphone/notification_center_widget
  [6.] iphone/preference_bundle
  [7.] iphone/sbsettingstoggle
  [8.] iphone/tool
  [9.] iphone/tweak
  [10.] iphone/xpc_service

 

可以看到,这里共有10种模板可供选择,其中1、4、6、8、9是Theos的自带模板,2、3、5、7、10是上一小节下载的。在逆向工程初级阶段,所开发程序的主要类型是tweak,其他模板的用法可以来http://bbs.iosre.com讨论交流。
 
2)选择“9”,即创建一个tweak工程,命令如下:
 
Choose a Template (required): 9
 
3)输入tweak的工程名称,命令如下:
 
Project Name (required): iOSREProject
 
4)输入deb包的名字(类似于bundle identifier),命令如下:
 
Package Name [com.yourcompany.iosreproject]: com.iosre.iosreproject
 
5)输入tweak作者的名字,命令如下:
 
Author/Maintainer Name [snakeninny]: snakeninny
 
6)输入“MobileSubstrate Bundle filter”,也就是tweak作用对象的bundle 
 
identifier,命令如下:
 
[iphone/tweak] MobileSubstrate Bundle filter [com.apple.springboard]: com.apple.springboard
 
7)输入tweak安装完成后需要重启的应用,以进程名表示,如下:
 
[iphone/tweak] List of applications to terminate upon installation (space-separated, '-' for none) [SpringBoard]: SpringBoard
Instantiating iphone/tweak in iosreproject/...
Done.
 
简单的7步完成之后,一个名为iosreproject的文件夹就在当前目录生成了,该文件夹里就是刚创建的tweak工程。
 
2. 定制工程文件
 
用Theos创建tweak工程非常方便,但简洁的工程框架下目前还是些粗糙的内容,需要进一步加工相关的文件。先来看看刚刚生成的工程目录,如下:
 
snakeninnysiMac:iosreproject snakeninny$ ls -l
total 40
-rw-r--r--  1 snakeninny  staff   184 Dec  3 09:05 Makefile
-rw-r--r--  1 snakeninny  staff  1045 Dec  3 09:05 Tweak.xm
-rw-r--r--  1 snakeninny  staff   223 Dec  3 09:05 control
-rw-r--r--  1 snakeninny  staff    57 Dec  3 09:05 iOSREProject.plist
lrwxr-xr-x  1 snakeninny  staff    11 Dec  3 09:05 theos -> /opt/theos

 

 
除去一个指向Theos目录的符号链接外,只有4个文件,从工程复杂度来说完全不会吓跑初学者,反而会让我们跃跃欲试,Theos的产品体验做得很好。
 
古语云:“一粒米中藏世界,半边锅内煮乾坤”。4根顶梁柱就足以撑起tweak的毛坯房,但漂亮的tweak离不开我们的精装修,这4个文件的内容可是大有玄机!
 
(1)Makefile
 
Makefile文件指定工程用到的文件、框架、库等信息,将整个过程自动化。iOSREProject的Makefile内容如下:
 
include theos/makefiles/common.mk

TWEAK_NAME = iOSREProject
iOSREProject_FILES = Tweak.xm

include $(THEOS_MAKE_PATH)/tweak.mk

after-install::
      install.exec "killall -9 SpringBoard"

下面来逐行解读。

include theos/makefiles/common.mk

固定写法,不要更改。

TWEAK_NAME = iOSREProject

tweak的名字,即用Theos创建工程时指定的“Project Name”,跟control文件中的“Name”字段对应,不要更改。

iOSREProject_FILES = Tweak.xm

tweak包含的源文件(不包括头文件),多个文件间以空格分隔,如:

iOSREProject_FILES = Tweak.xm Hook.xm New.x ObjC.m ObjC++.mm

可以按需更改。

include $(THEOS_MAKE_PATH)/tweak.mk 

 

 
根据不同的Theos工程类型,通过include命令指定不同的.mk文件;在逆向工程初级阶段,我们开发的一般是Application、Tweak和Tool三种类型的程序,它们对应的.mk文件分别是application.mk、tweak.mk和tool.mk,可以按需更改。
 
after-install::
 
      install.exec "killall -9 SpringBoard"
 
读者应该从字面意思就能对这两行的作用猜个八九不离十——在tweak安装之后杀掉SpringBoard进程,好让CydiaSubstrate在进程启动时加载对应的dylib。
 
是不是非常简单?Makefile里的默认内容确实非常简单,但有点简单过头了。如何指定SDK版本?怎么导入framework?lib文件在哪里链接?作为iOS开发者的你一定会提出这些问题。别急别急,面包会有的,牛奶也会有的。
 
指定处理器架构
 
ARCHS = armv7 arm64
 
上面的语句在表示不同的处理器架构时,其间以空格分隔。值得注意的是,采用arm64架构的App不兼容armv7/armv7s架构,必须适配arm64架构的dylib。在绝大多数情况下,这里固定填写“arm7 arm64”就行了。
 
指定SDK版本
 
TARGET = iphone:Base SDK:Deployment Target
 
比如:
 
TARGET = iphone:8.1:8.0
 
上面的语句即指定采用8.1版本的SDK,且发布对象为iOS 8.0及以上版本。也可以把“Base SDK”设置为“latest”,指定以Xcode附带的最新版本SDK编译,如:
 
TARGET = iphone:latest:8.0
导入framework
iOSREProject_FRAMEWORKS = framework name
 
例如:
 
iOSREProject_FRAMEWORKS = UIKit CoreTelephony CoreAudio
 
上面的语句所展示的功能没什么多说的,但既然是tweak开发,很多朋友关注的应该是如何导入private framework吧?很简单,用下面的语句即可:
 
iOSREProject_PRIVATE_FRAMEWORKS = private framework name
 
例如:
 
iOSREProject_PRIVATE_FRAMEWORKS = AppSupport ChatKit IMCore
虽然只是多了个“PRIVATE”,但有一点要注意:private framework是AppStore开发所不允许使用的,它的内容在每个iOS版本之间可能发生变化,在导入之前,一定要确定导入的private framework确实存在。举一个例子,如果你的tweak打算兼容iOS 7和iOS 8两个版本,那么Makefile可写成如下内容:
 
ARCHS = armv7 arm64
TARGET = iphone:latest:7.0

include theos/makefiles/common.mk

TWEAK_NAME = iOSREProject
iOSREProject_FILES = Tweak.xm
iOSREProject_PRIVATE_FRAMEWORK = BaseBoard
include $(THEOS_MAKE_PATH)/tweak.mk
after-install::
      install.exec "killall -9 SpringBoard"

 

上面的语句可以成功编译和链接,并不会报错。但是,因为BaseBoard这个private framework只存在于8.0及以上版本的SDK里,在iOS 7里是没有的,所以这个tweak在iOS 7中会因找不到framework而无法正常工作。这种情况可以通过弱链接(谷歌搜索“makefile weak linking”)或dlopen()、dlsym()和dlclose()系列函数动态调用private framework来解决。
 
链接Mach-O对象(Mach-O object)
 
iOSREProject_LDFLAGS = -lx
 
Theos采用GNU Linker来链接Mach-O对象,包括.dylib、.a和.o。在Terminal中输入“man ld”,定位到“-lx”部分,它是这么写的:
 

“-lx This option tells the linker to search for libx.dylib or libx.a in the library search path. If string x is of the form y.o, then that file is searched for in the same places, but without prepending `lib’ or appending `.a’ or `.dylib’ to the filename.”

大致意思是说,-lx代表链接libx.a或libx.dylib,即给“x”加上“lib”的前缀,以及“.a”或“.dylib”的后缀;如果x是“y.o”的形式,则直接链接y.o,不加任何前缀或后缀。由图3-3可知,iOS支持链接的Mach-O对象全是以“libx.dylib”和“y.o”形式命名的,完全兼容GNU Linker。

 
 
这样,链接Mach-O对象就很方便了。例如,要链接libsqlite3.0.dylib、libz.dylib和dylib1.o,像下面这么写就可以了:
 
iOSREProject_LDFLAGS = -lz –lsqlite3.0 –dylib1.o
 
稍后还有一个字段需要介绍,但一般来说,Makefile中定义了以上字段就已经完全够用了;更详细的Makefile介绍,可以参阅
 
http://www.gnu.org/software/make/manual/html_node/Makefiles.html。
 
(2)Tweak.xm
 
用Theos创建tweak工程,默认生成的源文件是Tweak.xm。“xm”中的“x”代表这个文件支持Logos语法,如果后缀名是单独一个“x”,说明源文件支持Logos和C语法;如果后缀名是“xm”,说明源文件支持Logos和C/C++语法,与“m”和“mm”的区别类似。Tweak.xm的内容如下:
 
/* How to Hook with Logos
Hooks are written with syntax similar to that of an Objective-C @implementation.
You don't need to #include , it will be done automatically, as will
the generation of a class list and an automatic constructor.

%hook ClassName

// Hooking a class method
+ (id)sharedInstance {
return %orig;
}

// Hooking an instance method with an argument.
- (void)messageName:(int)argument {
      %log; // Write a message about this call, including its class, name and arguments, to the system log.

      %orig; // Call through to the original function with its original arguments.
      %orig(nil); // Call through to the original function with a custom argument.
      // If you use %orig(), you MUST supply all arguments (except for self and _cmd, the automatically generated ones.)
}

// Hooking an instance method with no arguments.
- (id)noArguments {
      %log;
      id awesome = %orig;
      [awesome doSomethingElse];

      return awesome;
}

// Always make sure you clean up after yourself; Not doing so could have grave consequences!
%end
*/

 

这就是最基本的Logos语法,包含%hook、%log、%orig这3个预处理指令,它们的作用如下。
 
%hook
指定需要hook的class,必须以%end结尾,如下:
%hook SpringBoard
- (void)_menuButtonDown:(id)down
{     
NSLog(@"You've pressed home button.");
%orig; // call the original _menuButtonDown:
}
%end
这段代码的意思是钩住(hook)SpringBoard类里的_menuButtonDown:函数,先将一句话写入syslog,再执行函数的原始操作。
%log
该指令在%hook内部使用,将函数的类名、参数等信息写入syslog,可以以%log([(), ...])的格式追加其他打印信息,如下:
%hook SpringBoard
- (void)_menuButtonDown:(id)down
{     
      %log((NSString *)@"iOSRE", (NSString *)@"Debug");
      %orig; // call the original _menuButtonDown: 
}
%end
打印结果如下:
Dec  3 10:57:44 FunMaker-5 SpringBoard[786]: -[ _menuBu-ttonDown:+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Timestamp:           75607608282
Total Latency:       20266 us
SenderID:            0x0000000100000190
BuiltIn:             1
AttributeDataLength: 16
AttributeData:       01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 
ValueType:           Absolute
EventType:           Keyboard
UsagePage:           12
Usage:               64
Down:                1
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
]: iOSRE, Debug
%orig
该指令在%hook内部使用,执行被钩住(hook)的函数的原始代码,如下:
%hook SpringBoard
- (void)_menuButtonDown:(id)down
{
      NSLog(@"You've pressed home button.");
      %orig; // call the original _menuButtonDown:
}
%end
如果去掉%orig,那么原始函数不会得到执行,例如:
%hook SpringBoard
- (void)_menuButtonDown:(id)down
{     
      NSLog(@"You've pressed home button but it's not functioning.");
}
%end
还可以利用%orig更改原始函数的参数,例如:
%hook SBLockScreenDateViewController
- (void)setCustomSubtitleText:(id)arg1 withColor:(id)arg2
{
      %orig(@"iOS 8 App Reverse Engineering", arg2);
}
%end

 

这样一来,锁屏界面原本显示日期的地方就变成了如图3-4所示的样子。


 
除了%hook、%log、%orig以外,Logos常用的预处理指令还有%group、%init、%ctor、 %new、%c,下面继续逐一介绍。
 
%group
 
该指令用于将%hook分组,便于代码管理及按条件初始化分组(含义稍后有详细解释),必须以%end结尾;一个%group可以包含多个%hook,所有不属于某个自定义group的%hook会被隐式归类到%group _ungrouped中。%group的用法如下:
 
%group iOS7Hook
%hook iOS7Class
- (id)iOS7Method
{
id result = %orig;
NSLog(@"This class & method only exist in iOS 7.");
return result;
}
%end
%end // iOS7Hook

%group iOS8Hook
%hook iOS8Class
- (id)iOS8Method
{
id result = %orig;
NSLog(@"This class & method only exist in iOS 8.");
return result;
}
%end
%end // iOS8Hook

%hook SpringBoard
-(void)powerDown
{
%orig;
}
%end

这段代码的意思是在%group iOS7Hook中钩住iOS7Class的iOS7Method,在%group iOS8Hook中钩住iOS8Class的iOS8Method函数,然后在%group _ungrouped中钩住SpringBoard类的powerDown函数。

需要注意的是,%group必须配合下面的%init使用才能生效。

 
%init
该指令用于初始化某个%group,必须在%hook或%ctor内调用;如果带参数,则初始化指定的group,如果不带参数,则初始化_ungrouped,如下:
#ifndef kCFCoreFoundationVersionNumber_iOS_8_0
#define kCFCoreFoundationVersionNumber_iOS_8_0 1140.10
#endif 

%hook SpringBoard
- (void)applicationDidFinishLaunching:(id)application
{
%orig;

%init; // Equals to %init(_ungrouped)
if (kCFCoreFoundationVersionNumber >= kCFCoreFoundationVersionNumber_iOS_7_0 && kCFCoreFoundationVersionNumber < kCFCoreFoundationVersionNumber_iOS_8_0) %init(iOS7Hook);
if (kCFCoreFoundationVersionNumber >= kCFCoreFoundationVersionNumber_iOS_8_0) init(iOS8Hook);
}
%end
只有调用了%init,对应的%group才能起作用,切记切记!
%ctor
tweak的constructor,完成初始化工作;如果不显式定义,Theos会自动生成一个%ctor,并在其中调用%init(_ungrouped)。因此,
%hook SpringBoard
- (void)reboot
{
NSLog(@"If rebooting doesn't work then I'm screwed.");
%orig;
}
%end
可以成功生效,因为Theos隐式定义了如下内容:
%ctor
{
%init(_ungrouped);
}
而
%hook SpringBoard
- (void)reboot
{
NSLog(@"If rebooting doesn't work then I'm screwed.");
%orig;
}
%end

%ctor
{
// Need to call %init explicitly!
}
里的%hook无法生效,因为这里显式定义了%ctor,却没有显式调用%init,%group(_ungrouped)不起作用。%ctor一般可以用来初始化%group,以及进行MSHookFunction等操作,如下:
#ifndef kCFCoreFoundationVersionNumber_iOS_8_0
#define kCFCoreFoundationVersionNumber_iOS_8_0 1140.10
#endif 
%ctor
{
%init;
if (kCFCoreFoundationVersionNumber >= kCFCoreFoundationVersionNumber_iOS_7_0 && kCFCoreFoundationVersionNumber < kCFCoreFoundationVersionNumber_iOS_8_0) %init(iOS7Hook);
if (kCFCoreFoundationVersionNumber >= kCFCoreFoundationVersionNumber_iOS_8_0) %init(iOS8Hook);
MSHookFunction((void *)&AudioServicesPlaySystemSound,
(void *)&replaced_AudioServicesPlaySystemSound,
(void **)&original_AudioServicesPlaySystemSound);
}
注意,%ctor不需要以%end结尾。
%new
在%hook内部使用,给一个现有class添加新函数,功能与class_addMethod相同。它的用法如下:
%hook SpringBoard
%new
- (void)namespaceNewMethod
{
      NSLog(@"We've added a new method to SpringBoard.");
}
%end

 

有的朋友可能会问,Objective-C的category语法也可以给现有class添加新函数,为什么还需要%new呢?其实原因就在于category与class_addMethod的区别,前者是静态的,而后者是动态的。那么在这种情况下,静态还是动态,有什么关系呢?当然有关系,尤其是当class来自某个可执行文件的时候。举个例子,上面的代码给SpringBoard类添加了一个新方法,如果使用category,代码应该是下面这样:
 
@interface SpringBoard (iOSRE)
- (void)namespaceNewMethod;
@end
@implementation SpringBoard (iOSRE)
- (void)namespaceNewMethod
{
      NSLog(@"We've added a new method to SpringBoard.");
}
@end
如果尝试编译上面的代码,会得到“error:cannot find interface declaration for ‘SpringBoard’”的报错信息,即编译器找不到SpringBoard类的定义。可以构造一个SpringBoard的定义,骗过编译器,如下:
@interface SpringBoard : NSObject
@end
@interface SpringBoard (iOSRE)
- (void)namespaceNewMethod;
@end

@implementation SpringBoard (iOSRE)
- (void)namespaceNewMethod
{
      NSLog(@"We've added a new method to SpringBoard.");
}
@end
重新编译,仍然会报错,如下:
Undefined symbols for architecture armv7:
  "_OBJC_CLASS_$_SpringBoard", referenced from:
      l_OBJC_$_CATEGORY_SpringBoard_$_iOSRE in Tweak.xm.b1748661.o
ld: symbol(s) not found for architecture armv7
clang: error: linker command failed with exit code 1 (use -v to see invocation)
ld找不到“SpringBoard”的定义。一般来说,iOS程序员在碰到这个错误时的第一反应是:“是不是忘了导入哪个framework?”,但是转念一想,SpringBoard类是SpringBoard这个App里的一个类,而不是一个framework,要怎么导入?现在你是不是觉得%new非常可爱了呢?
%c
该指令的作用等同于objc_getClass或NSClassFromString,即动态获取一个类的定义,在%hook或%ctor内使用。
Logos的预处理指令还有%subclass和%config,但笔者到现在也没有用过,感兴趣的读者可以移步http://iphonedevwiki.net/index.php/Logos一探究竟,也可以来http://bbs.iosre.com跟大家一起讨论。
(3)control
control文件记录了deb包管理系统所需的基本信息,会被打包进deb包里。iOSREProject里control文件的内容如下:
Package: com.iosre.iosreproject
Name: iOSREProject
Depends: mobilesubstrate
Version: 0.0.1
Architecture: iphoneos-arm
Description: An awesome MobileSubstrate tweak!
Maintainer: snakeninny
Author: snakeninny
Section: Tweaks

 

其中:
 
Package字段用于描述这个deb包的名字,采用的命名方式同bundle identifier类似,均为反向DNS格式,可以按需更改;
 
Name字段用于描述这个工程的名字,可以按需更改;
 
Depends字段用于描述这个deb包的“依赖”。“依赖”指的是这个程序运行的基本条件,可以填写固件版本或其他程序,如果当前iOS不满足“依赖”中定义的条件,则此tweak无法正常运行。如
 
Depends: mobilesubstrate, firmware (>= 8.0)
表示当前iOS版本必须在8.0以上,且必须安装CydiaSubstrate,才能正常运行这个tweak,可以按需更改。
 
Version字段用于描述这个deb包的版本号,可以按需更改;
 
Architecture字段用于描述deb包安装的目标设备架构,不要更改;
 
Description字段是deb包的简单介绍,可以按需更改;
 
Maintainter字段用于描述deb包的维护人,例如BigBoss源中所有deb包的维护人均为BigBoss,而非软件作者,可以按需更改;
 
Author字段用于描述tweak的作者(注意与Maintainer的区别),可以按需更改;
 
Section字段用于描述deb包所属的程序类别,不要更改。
 
control文件中可以自定义的字段还有很多,但上面这些信息就已经足够了。更全面的说明可以参阅debian的官方网站(http://www.debian.org/doc/debian-policy/ch-controlfields.html)或留意其他deb包里的control文件。值得注意的是,Theos在打包deb时会对control文件作进一步处理,上面的control文件在得到处理后内容变为:
 
Package: com.iosre.iosreproject
Name: iOSREProject
Depends: mobilesubstrate
Architecture: iphoneos-arm
Description: An awesome MobileSubstrate tweak!
Maintainer: snakeninny
Author: snakeninny
Section: Tweaks
Version: 0.0.1-1
Installed-Size: 104

 

 
这里Theos更改了Version字段,用以表示Theos的打包次数,方便管理;增加了一个Installed-Size字段,用以描述deb包安装后的估算大小,可能会与实际大小有偏差,但不要更改。
 
control文件中的很多信息直接体现在Cydia中,如图3-5所示,大家可以对比看看。
 
 
(4)iOSREProject.plist
 
这个plist文件的作用和App中的Info.plist类似,它记录了一些配置信息,描述了tweak的作用范围。我们可以用plutil,也可以用Xcode来编辑它。
 
iOSREProject.plist的最外层是一个dictionary,只有一个名为“Filter”的键,如图3-6所示。
 
 
Filter下是一系列array,可以分为三类。
 
Bundles,指定若干bundle为tweak的作用对象,如图3-7所示。
 
 
按照图3-7中的配置,tweak的作用对象是三个bundle,即SMSNinja、AddressBook.framework和SpringBoard。
 
Classes,指定若干class为tweak的作用对象,如图3-8所示。
 
 
按照图3-8的配置,tweak的作用对象是三个class,即NSString、SBAwayController和SBIconModel。
 
Executables,指定若干可执行文件为tweak的作用对象,如图3-9所示。
 
按照图3-9中的配置,tweak的作用对象是三个可执行文件,即callservicesd、imagent和mediaserverd。
 
 
这三类array可以混合使用,如图3-10所示。
 
 
注意,当Filter下有不同类的array时,需要添加一个“Mode :Any”键值对。当Filter下的array只有一类时,不需要添加“Mode :Any”键值对。
 
3. 编译 + 打包 + 安装
 
前面在完成了Theos的安装后,使用NIC创建了第一个tweak工程,还逐一解读了工程的组成文件,那么现在就剩下最后一步——编译了。完成这一步,一个tweak就算正式完成——我们可以把tweak安装到设备上,开始周而复始的“safe mode”之旅了,是不是很期待呢?
 
(1)编译
 
Theos采用“make”命令来编译Theos工程。在Theos工程目录下运行make命令,
如下:
 
snakeninnysiMac:iosreproject snakeninny$ make
Making all for tweak iOSREProject...
 Preprocessing Tweak.xm...
 Compiling Tweak.xm...
 Linking tweak iOSREProject...
 Stripping iOSREProject...
 Signing iOSREProject...
从输出的信息看,Theos完成了预处理、编译、签名等一系列动作,此时会发现当前目录下多了一个新的“obj”文件夹,如下:
snakeninnysiMac:iosreproject snakeninny$ ls -l
total 32
-rw-r--r--  1 snakeninny  staff  262 Dec  3 09:20 Makefile
-rw-r--r--  1 snakeninny  staff    0 Dec  3 11:28 Tweak.xm
-rw-r--r--  1 snakeninny  staff  223 Dec  3 09:05 control
-rw-r--r--@ 1 snakeninny  staff  175 Dec  3 09:48 iOSREProject.plist
drwxr-xr-x  5 snakeninny  staff  170 Dec  3 11:28 obj
lrwxr-xr-x  1 snakeninny  staff   11 Dec  3 09:05 theos -> /opt/theos
里面有一个.dylib文件,如下:
snakeninnysiMac:iosreproject snakeninny$ ls -l ./obj
total 272
-rw-r--r--  1 snakeninny  staff  33192 Dec  3 11:28 Tweak.xm.b1748661.o
-rwxr-xr-x  1 snakeninny  staff  98784 Dec  3 11:28 iOSREProject.dylib
它就是tweak的核心。
(2)打包
打包使用的“make package”命令来自于Theos本身,其实就是先执行“make”命令,然后再执行“dpkg-deb”命令,如下:
snakeninnysiMac:iosreproject snakeninny$ make package
Making all for tweak iOSREProject...
 Preprocessing Tweak.xm...
 Compiling Tweak.xm...
 Linking tweak iOSREProject...
 Stripping iOSREProject...
 Signing iOSREProject...
Making stage for tweak iOSREProject...
dm.pl: building package `com.iosre.iosreproject' in `./com.iosre.iosreproject_0.0.1-7_iphoneos-arm.deb'.
上面生成了一个名为“com.iosre.iosreproject_0.0.1-7_iphoneos-arm.deb”的文件,这就是可以最终发布的安装包。
“make package”命令还有一个很重要的功能。在执行完“make package”之后,除了“obj”文件夹外,你会发现tweak工程目录下还生成了一个 “_”文件夹,如下:
snakeninnysiMac:iosreproject snakeninny$ ls -l
total 40
-rw-r--r--  1 snakeninny  staff   262 Dec  3 09:20 Makefile
-rw-r--r--  1 snakeninny  staff     0 Dec  3 11:28 Tweak.xm
drwxr-xr-x  4 snakeninny  staff   136 Dec  3 11:35 _
-rw-r--r--  1 snakeninny  staff  2396 Dec  3 11:35 com.iosre.iosreproject_0.0.1-7 _iphoneos-arm.deb
-rw-r--r--  1 snakeninny  staff   223 Dec  3 09:05 control
-rw-r--r--@ 1 snakeninny  staff   175 Dec  3 09:48 iOSREProject.plist
drwxr-xr-x  5 snakeninny  staff   170 Dec  3 11:35 obj
lrwxr-xr-x  1 snakeninny  staff    11 Dec  3 09:05 theos -> /opt/theos
这个文件夹是干什么的?打开它,可以看到2个文件夹,分别是“DEBIAN”和“Library”:
snakeninnysiMac:iosreproject snakeninny$ ls -l _
total 0
drwxr-xr-x  3 snakeninny  staff  102 Dec  3 11:35 DEBIAN
drwxr-xr-x  3 snakeninny  staff  102 Dec  3 11:35 Library
其中“DEBIAN”里只有tweak工程里的control文件,Theos在编译过程中向control文件里稍稍增加了几个字段而已,如下:
snakeninnysiMac:iosreproject snakeninny$ ls -l _/DEBIAN
total 8
-rw-r--r--  1 snakeninny  staff  245 Dec  3 11:35 control

 


 
“Library”的目录结构如图3-11所示。


 
对比生成deb的包内容:
 
snakeninnysiMac:iosreproject snakeninny$ dpkg -c com.iosre.iosreproject_0.0.1-7_iphoneos-arm.deb
drwxr-xr-x snakeninny/staff  0 2014-12-03 11:35 ./
drwxr-xr-x snakeninny/staff  0 2014-12-03 11:35 ./Library/
drwxr-xr-x snakeninny/staff  0 2014-12-03 11:35 ./Library/MobileSubstrate/
drwxr-xr-x snakeninny/staff  0 2014-12-03 11:35 ./Library/MobileSubstrate/DynamicLibraries/
-rwxr-xr-x snakeninny/staff 98784 2014-12-03 11:35 ./Library/MobileSubstrate/DynamicLibraries/iOSREProject.dylib
-rw-r--r-- snakeninny/staff   175 2014-12-03 11:35 ./Library/MobileSubstrate/DynamicLibraries/iOSREProject.plist

 

 
以及在Cydia中iOSREProject的文件系统,如图3-12所示。
 
 
可以看到,三者是完全相同的。到这里,你可能也猜到了,这个deb包其实就是由“DEBIAN”提供debian信息,“Library”提供实际文件的简单组合。事实上,还可以在工程目录下创建一个名为“layout”的文件夹,然后把工程打包成deb并安装到iOS中,此时“layout”中的所有文件会被解包到iOS文件系统的相同位置(这里的“layout”相当于iOS中的根目录“/”),这极大扩充了deb包的作用范围。下面用一个小示例佐以说明。
 
回到刚才的iOSREProject中,在Terminal中输入“make clean”及“rm *.deb”,将工程恢复到最初的状态,如下:
 
snakeninnysiMac:iosreproject snakeninny$ make clean
rm -rf ./obj
rm -rf "/Users/snakeninny/Code/iosreproject/_"
snakeninnysiMac:iosreproject snakeninny$ rm *.deb
snakeninnysiMac:iosreproject snakeninny$ ls -l
total 32
-rw-r--r--  1 snakeninny  staff  262 Dec  3 09:20 Makefile
-rw-r--r--  1 snakeninny  staff    0 Dec  3 11:28 Tweak.xm
-rw-r--r--  1 snakeninny  staff  223 Dec  3 09:05 control
-rw-r--r--@ 1 snakeninny  staff  175 Dec  3 09:48 iOSREProject.plist
lrwxr-xr-x  1 snakeninny  staff   11 Dec  3 09:05 theos -> /opt/theos
然后生成一个空的“layout”目录,如下:
snakeninnysiMac:iosreproject snakeninny$ mkdir layout
并在“layout”下随便放一些空文件,如下:
snakeninnysiMac:iosreproject snakeninny$ touch ./layout/1.test
snakeninnysiMac:iosreproject snakeninny$ mkdir ./layout/Developer
snakeninnysiMac:iosreproject snakeninny$ touch ./layout/Developer/2.test
snakeninnysiMac:iosreproject snakeninny$ mkdir -p ./layout/var/mobile/Library/Preferences
snakeninnysiMac:iosreproject snakeninny$ touch ./layout/var/mobile/Library/Preferences/3.test

 

最后用“make package”打包,并将生成的deb文件拷贝到iOS中,用iFile安装。然后在Cydia中查看iOSREProject的文件系统,如图3-13所示。
 
 
除“DEBIAN”以外的所有文件都被解包到了iOS文件系统的相同位置,本来不存在的中间文件夹也被自动创建。deb包的玄机还有很多,这里也只是管中窥豹,更全面的介绍请移步http://www.debian.org/doc/debian-policy,官方文档总是最好的学习资料。
 
(3)安装
 
最后,要把这个deb文件安装到iOS中去。安装的方法多种多样,这里介绍两种最具代表性的:图形界面安装法和命令行安装法。大多数人的第一直觉是图形界面一定比命令行简单,那好,咱们先介绍图形界面安装法。
 
图形界面安装法
 
这个方法确实简单:通过iFunBox等软件把deb拖到iOS里去,然后用iFile安装它,最后重启iOS。虽然全过程都由图形界面操作,但人机交互太多,又要动电脑又要滑手机,一来二去非常繁琐,并不适用于tweak开发。
 
命令行安装法
 
这个方法要用到简单的ssh命令,故而要求越狱的iOS安装了OpenSSH,如果对这部分知识不了解,请先快速浏览一遍第4章的“OpenSSH”部分。下面具体介绍安装法。
 
首先,需要在Makefile的最上一行加上本机IP地址,如下:
 
THEOS_DEVICE_IP = iOSIP
ARCHS = armv7 arm64
TARGET = iphone:latest:8.0
然后调用“make package install”命令完成编译打包安装一条龙服务,如下:
snakeninnysiMac:iosreproject snakeninny$ make package install
Making all for tweak iOSREProject...
 Preprocessing Tweak.xm...
 Compiling Tweak.xm...
 Linking tweak iOSREProject...
 Stripping iOSREProject...
 Signing iOSREProject...
Making stage for tweak iOSREProject...
dm.pl: building package `com.iosre.iosreproject:iphoneos-arm' in `./com.iosre.iosreproject_0.0.1-15_iphoneos-arm.deb'
install.exec "cat > /tmp/_theos_install.deb; dpkg -i /tmp/_theos_install.deb && rm /tmp/_theos_install.deb" < "./com.iosre.iosreproject_0.0.1-15_iphoneos-arm.deb"
root@iOSIP's password: 
Selecting previously deselected package com.iosre.iosreproject.
(Reading database ... 2864 files and directories currently installed.)
Unpacking com.iosre.iosreproject (from /tmp/_theos_install.deb) ...
Setting up com.iosre.iosreproject (0.0.1-15) ...
install.exec "killall -9 SpringBoard"
root@iOSIP's password: 

从以上信息可以看到,Theos在整个安装过程中要求我们输入两次root密码。虽然多次输入密码给人很安全的感觉,但实在是太麻烦了。好在通过设置iOS的authorized_keys可以省略SSH输密码的步骤,让“make package install”真正地从“一只多脚虫”变成“一条飞天龙”,具体步骤如下:

 
1)删除“/Users/snakeninny/.ssh/known_hosts”中iOSIP对应的条目。
假设iOS的IP地址是iOSIP。编辑“/Users/snakeninny/.ssh/known_hosts”,找到iOSIP所在的那一行,如下:
iOSIP ssh-rsa hXFscxBCVXgqXhwm4PUoUVBFWRrNeG6gVI3Ewm4dqwusoRcyCxZtm5bRiv4bXfkPjsRkWVVfrW3uT52Hhx4RqIuCOxtWE7tZqc1vVap4HIzUu3mwBuxog7WiFbsbbaJY4AagNZmX83Wmvf8li5aYMsuKeNagdJHzJNtjM3vtuskK4jKzBkNuj0M89TrV4iEmKtI4VEoEmHMYzWwMzExXbyX5NyEg5CRFmA46XeYCbcaY0L90GExXsWMMLA27tA1Vt1ndHrKNxZttgAw31J90UDnOGlMbWW4M7FEqRWQsWXxfGPk0W7AlA54vaDXllI5CD5nLAu4VkRjPIUBrdH5O1fqQ3qGkPayhsym3g0VZeYgU4JAMeFc3
完整删掉这一行。
2)生成authorized_keys。
在Terminal中执行如下命令:
snakeninnysiMac:~ snakeninny$ ssh-keygen -t rsa
Generating public/private rsa key pair.
Enter file in which to save the key (/Users/snakeninny/.ssh/id_rsa): 
Enter passphrase (empty for no passphrase): 
Enter same passphrase again: 
Your identification has been saved in /Users/snakeninny/.ssh/id_rsa.
Your public key has been saved in /Users/snakeninny/.ssh/id_rsa.pub.
……
snakeninnysiMac:~ snakeninny$ cp /Users/snakeninny/.ssh/id_rsa.pub ~/authorized_keys
就会在用户目录下生成authorized_keys。
3)配置iOS。
在Terminal中执行如下命令:
FunMaker-5:~ root# ssh-keygen
Generating public/private rsa key pair.
Enter file in which to save the key (/var/root/.ssh/id_rsa): 
Enter passphrase (empty for no passphrase): 
Enter same passphrase again: 
Your identification has been saved in /var/root/.ssh/id_rsa.
Your public key has been saved in /var/root/.ssh/id_rsa.pub.
……
FunMaker-5:~ root# logout
Connection to iOSIP closed.
snakeninnysiMac:iosreproject snakeninny$ scp ~/authorized_keys root@iOSIP:/var/root/.ssh
The authenticity of host 'iOSIP (iOSIP)' can't be established.
RSA key fingerprint is 75:98:9a:05:a3:27:2d:23:08:d3:ee:f4:d1:28:ba:1a.
Are you sure you want to continue connecting (yes/no)? yes
Warning: Permanently added 'iOSIP' (RSA) to the list of known hosts.
root@iOSIP's password: 
authorized_keys                                                   100%  408     
0.4KB/s   00:00   

 

 
重新使用ssh命令进入iOS试试看,还需要输密码吗?此时,“make package install”真正变成了一次配置,一键安装,一劳永逸!
 
(4)清理
 
Theos提供了方便的工程清理命令“make clean”,其实际作用就是依次执行“rm -rf ./obj”和“rm -rf "/Users/snakeninny/Code/iosre/_"”两个命令,从而删除“make”和“make package”命令生成的文件夹。也可以用“rm *.deb”,删除“make package”命令生成的所有deb文件。
点击复制链接 与好友分享!回本站首页
分享到: 更多
您对本文章有什么意见或着疑问吗?请到论坛讨论您的关注和建议是我们前行的参考和动力  
上一篇:1.3 功能
下一篇:1.5 小结
相关文章
图文推荐
JavaScript网页动画设
1.9 响应式
1.8 登陆页式
1.7 主题式
排行
热门
文章
下载
读书

关于我们 | 联系我们 | 广告服务 | 投资合作 | 版权申明 | 在线帮助 | 网站地图 | 作品发布 | Vip技术培训
版权所有: 红黑联盟--致力于做最好的IT技术学习网站