TheOS 被设计为一个在基于 Unix 平台 (Mac OS X、IOS…) 和大多数 的Linux 平台下开发 iOS 程序的集成开发环境。说是集成开发环境,其实就是给我们准备好了一些代码模板、预置一些基本的 Makefile 脚本,我们还是要在 终端命令行 中完成一些关键操作。

1.安装dpkg和LDID

执行如下指令:



brew install dpkg ldid 



2.安装Theos

执行如下指令:



sudo git clone --recursive https://github.com/theos/theos.git /opt/theos



3.配置权限



sudo chown $(id -u):$(id -g) /opt/theos



4.配置环境变量



vim .bash_profile



添加如下两行:



export THEOS=/opt/theos export PATH=/opt/theos/bin/:$PATH



【说明】:环境变量配置,可以查看OS X修改环境变量

5.使用Theos

5.1创建工程

在终端输入如下指令:



nic.pl



如果提示如下错误:

ios依赖库版本_字段

执行以下指令即可:



source .bash_profile



执行完“nic.pl”指令后,会出现如下选择模板的界面:

ios依赖库版本_字段_02

在逆向工程的初级阶段,所开发程序的主要类型是tweak,因此输入15,按Enter键继续。接下来和Xcode创建工程一样,会有一系列的步骤:

  1. 输入工程名字(Project Name (required),必填项);
  2. 输入包名(Package Name [com.yourcompany.helloworld]);
  3. 输入作者的名字(Author/Maintainer Name [LeeGof]);
  4. 要注入程序的BundleID(MobileSubstrate Bundle filter [com.apple.springboard]):要注入程序的BundleID,默认的进程为com.apple.springboard,为了演示方便,在此先使用默认进程作为注入对象。

5.2工程文件说明

Tweak工程新建完成后,其工程目录的文件如下所示:

ios依赖库版本_移动开发_03

5.2.1control文件

该文件记录了工程的基本信息,会被打包进deb包中,字段内容如下: 



Package: com.leegof.reversedemo Name: ReverseDemo Depends: mobilesubstrate Version: 0.0.1 Architecture: iphoneos-arm Description: An awesome MobileSubstrate tweak! Maintainer: LeeGof Author: LeeGof Section: Tweaks



  • Package字段:用于描述这个deb包的名字,采用的命名方式和bundle identifier类似,可以按需更改;
  • Name字段:用于描述这个工程的名字,可以按需更改;
  • Depends字段:用于描述这个deb包的“依赖”。“依赖”指的是这个程序运行的基本条件,可以填写固件版本或其他程序,如果当前iOS不满足“依赖”中所定义的条件,则此tweak无法正常工作,可以按需更改。例如:

Depends: mobilesubstrate, firmware (>=6.0)



表示当前iOS版本必须在6.0以上,且必须安装MobileSubstrate,才能正常运行这个tweak。

  • Version字段:用于描述这个deb包的版本号,可以按需更改;
  • Architecture字段:用于描述deb包安装的目标设备架构,不要更改;
  • Description字段:deb包的简单介绍,可以按需更改;
  • Maintainer字段:用于描述deb包的维护人,即deb包的制作者而非tweak的作者,可以按需更改;
  • Author字段:用于描述tweak的作者,可以按需更改;
  • Section字段:用于描述deb包所属的程序类别,不要更改。

control文件中可以自定义的字段还有很多,一般上面的信息就已经足够了。更全面的可以查看官方网站:http://www.debian.org/doc/debian-policy/ch-controlfields.html。值得注意的是:Theos在打包deb时会对control文件做进一步处理。比如更改Version字段为:0.0.1-2,标识Theos的打包次数,方便管理;增加Installed-Size字段,用于描述deb包安装后的估算大小,与实际大小可能有偏差,不要更改。

5.2.2Makefile

该文件用来指定工程编译和链接要用到的文件、框架、库等信息,将整个过程自动化,字段内容如下:



include $(THEOS)/makefiles/common.mk TWEAK_NAME = ReverseDemo ReverseDemo_FILES = Tweak.xm include $(THEOS_MAKE_PATH)/tweak.mk after-install:: install.exec "killall -9 SpringBoard"



  • 第一行的include字段指定了工程的common.mk文件,固定写法,不要修改;
  • TWEAK_NAME字段填入的是建立工程时命令行输入的Project Name,与control文件中的“Name”字段对应,不要更改;
  • ReverseDemo_FILES字段指定工程包含的源文件,如果工程中需要用到多个源文件则用空格将各个文件名分开,可以按需更改;
  • include字段指定工程的mk文件,这里新建的是tweak工程,所以填入的是tweak.mk文件,还可以根据需求填入application.mk以及tool.mk文件;
  • 最后一行after-install字段指定安装程序后需要执行的操作,这里需要注入SpringBoard进程并执行自己的代码,因此需要重启SpringBoard进程,好让MobileSubstrate加载对应的dylib。

Makefile文件除了自动生成的这些字段外,还可以根据功能手动添加其他字段:

  • ARCHS字段可以用来指定处理器架构,一般情况下填写“ARCHS = armv7 arm64”即可;
  • TARGET字段用来指定SDK版本,例如:

TARGET = iphone:7.0



  • framework字段可以指定要导入的框架,比如这里的测试demo中填写的是“ReverseDemo_FRAMEWORKS = UIKit”,UIKit为后续测试代码需要用到的框架,另一方面,还可以通过ReverseDemo_PRIVATE_FRAMEWORKS字段指定要导入的私有库,格式不变。例如:

ReverseDemo_FRAMEWORKS = UIKit CoreTelephony CoreAudio ReverseDemo_PRIVATE_FRAMEWORKS = AppSupport ChatKit



5.2.3ReverseDemo.plist

plist文件记录工程的配置信息,主要作用是指定程序的作用范围,内容如下:

ios依赖库版本_移动开发_04

从上面可以看到,该文件的内容是一系列的Dictionary,最外层为Root键,Root键下为Filter键,Filter键下的array即为要设置的部分。该部分分为三类:

  • 第一类是Bundles,即所编写程序的作用对象,这个类型的字段就是所要注入的程序Bundle名,如果要注入的进程为SpringBoard,则填入com.apple.springboard,可以指定多个作用对象;
  • 第二类是Classes,即指定要注入的类名,同样是根据填入的字符串来筛选注入的类名;
  • 第三类是Executables,即指定要注入的可执行文件名。

这三类Array可以根据需要来设定。但按照混合配置方式,一个文件只有满足Filter中所有Array下的至少一个条件,tweak才能生效。这样显然不合理。所以额外有一个Mode键,将其值设置为Any,那么文件满足Filter中的任一条件就能成为tweak的作用对象。

当Filter下的array只有一类时,不需要添加Mode和Any键值对。

5.2.4Tweak.xm

该文件是实现具体功能的关键所在,是实现具体功能的源文件,这个文件支持Logos和C、C++语法。文件内容如下:



/* How to Hook with Logos Hooks are written with syntax similar to that of an Objective-C @implementation. You don't need to #include <substrate.h>, 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和%end,其中%hook后面指定要hook的类名,另一方面,hook的整块逻辑完成后结尾要加上%end,在hook逻辑中可以添加要hook的函数,并在函数体内部实现想要添加的代码逻辑;
  • 第二类是%orig,该语句代表执行原函数逻辑,即完成hook操作后可以选择是否调用原函数的代码,若需要调用则加上“%orig;”即可;
  • 第三类是%log,这类代码的作用是在log中打印hook的函数的类名、参数等信息。

 除了注释中展示的三种语法外,Logos还支持%group、%init、%ctor等语法,更多的Logos语法可查看这篇文章

这里我们输入我们简单实现的功能,代码如下:



%hook SpringBoard

-(void)applicationDidFinishLaunching:(id)application {
    NSLog(@"启动");
    %orig;
    
    UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"Hello, Gof!" message:@"LeeGof is very handsome" delegate:self cancelButtonTitle:@"OK" otherButtonTitles:nil];
    [alert show];
}

%end



5.3编译&打包&安装

5.3.1编译

Theos采用与debian相同的make命令来编译Theos工程。执行如下指令:



make



输出结果如下:

ios依赖库版本_ios依赖库版本_05

从上图的输出结果可以看到,Theos完成了预处理、编译、签名等一系列操作,此时目录下多了一个obj文件夹和.theos文件夹。

5.3.2打包

使用如下指令打包:



make package



有时候可能遇到如下所示错误信息:

ios依赖库版本_ios依赖库版本_06

解决方法:修改opt/theos/makefiles/package/deb.mk文件的第六行:

将THEOSPLATFORM_DPKG_DEB_COMPRESSION ?= lzma,最后的lzma 改成 xz 就可以了。

正确执行命令之后,命令行结果如下:

ios依赖库版本_git_07

这时候目录下多了一个packages文件夹,里面有一个deb文件。

5.3.3安装

执行指令:



make package install



看到如下输出:

ios依赖库版本_git_08

可以看到已经成功安装。

有时会遇到如下报错:

ios依赖库版本_字段_09

这种情况一般是deb.mk文件版本不正确,这里更新为下面的版本就可以了。


ifeq ($(_THEOS_PACKAGE_FORMAT_LOADED),)
_THEOS_PACKAGE_FORMAT_LOADED := 1

_THEOS_DEB_PACKAGE_CONTROL_PATH := $(or $(wildcard $(THEOS_PROJECT_DIR)/control),$(wildcard $(THEOS_PROJECT_DIR)/layout/DEBIAN/control))
_THEOS_DEB_CAN_PACKAGE := $(if $(_THEOS_DEB_PACKAGE_CONTROL_PATH),$(_THEOS_TRUE),$(_THEOS_FALSE))

_THEOS_DEB_HAS_DPKG_DEB := $(call __executable,dpkg-deb)
ifneq ($(_THEOS_DEB_HAS_DPKG_DEB),$(_THEOS_TRUE))
internal-package-check:: @echo "$(MAKE) package requires dpkg-deb."; exit 1 endif ifeq ($(_THEOS_DEB_CAN_PACKAGE),$(_THEOS_TRUE)) # Control file found (or layout/ found.) THEOS_PACKAGE_NAME := $(shell grep -i "^Package:" "$(_THEOS_DEB_PACKAGE_CONTROL_PATH)" | cut -d' ' -f2-) THEOS_PACKAGE_ARCH := $(shell grep -i "^Architecture:" "$(_THEOS_DEB_PACKAGE_CONTROL_PATH)" | cut -d' ' -f2-) THEOS_PACKAGE_BASE_VERSION := $(shell grep -i "^Version:" "$(_THEOS_DEB_PACKAGE_CONTROL_PATH)" | cut -d' ' -f2-) $(_THEOS_ESCAPED_STAGING_DIR)/DEBIAN: $(ECHO_NOTHING)mkdir -p "$(THEOS_STAGING_DIR)/DEBIAN"$(ECHO_END) ifeq ($(_THEOS_HAS_STAGING_LAYOUT),1) # If we have a layout/ directory, copy layout/DEBIAN to the staging directory. $(ECHO_NOTHING)[ -d "$(THEOS_PROJECT_DIR)/layout/DEBIAN" ] && rsync -a "$(THEOS_PROJECT_DIR)/layout/DEBIAN/" "$(THEOS_STAGING_DIR)/DEBIAN" $(_THEOS_RSYNC_EXCLUDE_COMMANDLINE) || true$(ECHO_END) endif # _THEOS_HAS_STAGING_LAYOUT $(_THEOS_ESCAPED_STAGING_DIR)/DEBIAN/control: $(_THEOS_ESCAPED_STAGING_DIR)/DEBIAN $(ECHO_NOTHING)sed -e '/^[Vv]ersion:/d' "$(_THEOS_DEB_PACKAGE_CONTROL_PATH)" > "$@"$(ECHO_END) $(ECHO_NOTHING)echo "Version: $(_THEOS_INTERNAL_PACKAGE_VERSION)" >> "$@"$(ECHO_END) $(ECHO_NOTHING)echo "Installed-Size: $(shell du $(_THEOS_PLATFORM_DU_EXCLUDE) DEBIAN -ks "$(THEOS_STAGING_DIR)" | cut -f 1)" >> "$@"$(ECHO_END) before-package:: $(_THEOS_ESCAPED_STAGING_DIR)/DEBIAN/control _THEOS_DEB_PACKAGE_FILENAME = $(THEOS_PACKAGE_DIR)/$(THEOS_PACKAGE_NAME)_$(_THEOS_INTERNAL_PACKAGE_VERSION)_$(THEOS_PACKAGE_ARCH).deb internal-package:: $(ECHO_NOTHING)COPYFILE_DISABLE=1 $(FAKEROOT) -r dpkg-deb -Zgzip -b "$(THEOS_STAGING_DIR)" "$(_THEOS_DEB_PACKAGE_FILENAME)" $(STDERR_NULL_REDIRECT)$(ECHO_END) # This variable is used in package.mk after-package:: __THEOS_LAST_PACKAGE_FILENAME = $(_THEOS_DEB_PACKAGE_FILENAME) else # _THEOS_DEB_CAN_PACKAGE == 0 internal-package:: @echo "$(MAKE) package requires you to have a layout/ directory in the project root, containing the basic package structure, or a control file in the project root describing the package."; exit 1 endif # _THEOS_DEB_CAN_PACKAGE endif # _THEOS_PACKAGE_FORMAT_LOADED


也可以先将deb文件拷贝到设备,然后cd到设备对应的目录,执行如下指令:



dpkg -i *.deb



5.3.4卸载

如果需要卸载安装包,执行如下指令即可:



dpkg -r com.leegof.reversedemo