游戏安全实验室 首页 技术入门 查看内容

 阅读目录

IOS越狱环境注入实现

发布于:2016-4-29 11:59   |    297345次阅读 作者: 管理员    |   原作者: TP   |   来自: 原创

       在对一个应用或游戏进行静态分析或动态调试找到关键逻辑点后,要实现特殊功能可以直接修改其内存的opcode,但这种修改方法只能在当前进程生效一次,程序重启后又得重新修改。因此,比较通用、有效的方法是注入程序,利用代码实现自己想修改的逻辑。类似于android平台的ptrace注入,ios平台也有一套成熟的注入框架,本章就将着重介绍ios平台的注入实现。
一、越狱以及Theos环境搭建
      在实现IOS注入前,需要搭建相关的越狱开发环境,具体需要安装的工具和步骤可以参考上一节《IOS平台GDB动态调试介绍》中第一小节的内容。
二、利用Theos实现注入
      在相关的环境配置好后,就可以着手对ios平台的程序进行注入了,这里使用的是Theos越狱工具开发工具包,该工具功能强大、易于安装、操作简单,是ios平台理想的越狱开发工具,有兴趣的读者可以在github上查看工具的源码。接下来主要介绍theos的相关操作。
2.1 新建工程
      在Mac终端的命令行中切换目录至常用的工程目录,并输入“/opt/theos/bin/nic.pl”命令,此时命令行会显示以下列表:
UseriMac:~ user$ /opt/theos/bin/nic.pl
NIC 2.0 - New Instance Creator
------------------------------
  [1.] iphone/application
  [2.] iphone/library
  [3.] iphone/preference_bundle
  [4.] iphone/tool
  [5.] iphone/tweak
      接下来选择“5”就能创建一个tweak工程,然后按照命令行提示输入要创建工程的配置信息,具体内容如下:
Choose a Template (required): 5
Project Name (required): test
Package Name [com.yourcompany.test]: com.gameprotect.test
Author/Maintainer Name [xx]: xxx
[iphone/tweak] MobileSubstrate Bundle filter [com.apple.springboard]: com.apple.springboard
[iphone/tweak] List of applications to terminate upon installation (space-separated, '-' for none) [SpringBoard]: SpringBoard
Instantiating iphone/tweak in testdxy/...
Done.
      其中Project Name、Package Name和Author Name都比较好理解;第四个MobileSubstrate Bundle filter是要注入程序的BundleID,默认的进程为com.apple.springboard,为了演示方便,在此先使用默认进程作为注入对象,读者可以根据各自需要输入对应的进程名;List of applications to terminate upon installation是tweak安装后要重启的进程名,该信息和刚输入的进程BundleID对应,此处笔者同样输入默认的进程名SpringBoard。配置输入完后命令行出现“Done.”时即完成了工程的创建工作。
2.2 工程文件说明
      Tweak工程新建完成后,其工程目录的文件如下所示:

2.2.1  control文件
      首先介绍的是control文件,该文件记录了工程的基本信息,新建的test工程中control文件各字段内容如下:
Package: com.gameprotect.test
Name: test
Depends: mobilesubstrate
Version: 0.0.1
Architecture: iphoneos-arm
Description: An awesome MobileSubstrate tweak!
Maintainer: xxx
Author: xxx
Section: Tweaks
       从上面的内容可以看出,Package、Name、Maintainer、Author等字段是在建立工程时由命令行输入的;而Depends、Version、Description等字段从字面意义也很好理解,值得说明的是Depends字段,读者可以根据需要指定工程的固件版本、软件程序等依赖条件,若运行环境不满足依赖条件则程序无法运行,这三个字段在一般情况下可以不用更改;最后是Architecture和Section字段,该字段指定了要安装的程序的设备架构以及程序类型,不能更改。
2.2.2  MakeFile文件
      接下来介绍MakeFile文件,该文件用来指定工程要用到的文件、框架等信息,新建的test工程中MakeFile文件各字段内容如下:
include theos/makefiles/common.mk
TWEAK_NAME = test
test_FILES = Tweak.xm
include $(THEOS_MAKE_PATH)/tweak.mk
after-install::
    install.exec "killall -9 SpringBoard"
       其中第一行的include字段指定了工程的common.mk文件,该文件通常不会变化,因此不需修改;TWEAK_NAME字段填入的是建立工程时命令行输入的Project Name;test_FILES字段指定工程包含的源文件,如果工程中需要用到多个源文件则用空格将各个文件名分开;第三行的include字段指定工程的mk文件,这里新建的是tweak工程,所以填入的是tweak.mk文件,还可以根据需求填入application.mk以及tweak.mk文件;最后一行after-install字段指定安装程序后需要执行的操作,这里需要注入SpringBoard进程并执行自己的代码,因此需要重启SpringBoard进程达到目的。
       MakeFile文件除了自动生成的这些字段外,还可以根据功能手动添加其他字段:ARCHS字段可以用来指定处理器架构,一般情况下填写“ARCHS = armv7 arm64”即可;TARGET字段用来指定SDK版本;framework字段可以指定要导入的框架,比如这里的测试demo中填写的是“test_FRAMEWORKS = UIKit”,UIKit为后续测试代码需要用到的框架,另一方面,还可以通过test_PRIVATE_FRAMEWORKS字段指定要导入的私有库,格式不变。鉴于篇幅有限,MakeFile常用的字段就介绍到这里,想了解更多MakeFile相关信息可以通过这个链接访问。
     测试的MakeFile文件修改后的完整内容如下:

ARCHS = armv7 arm64
include theos/makefiles/common.mk
TWEAK_NAME = test
test_FILES = Tweak.xm
test_FRAMEWORKS = UIKit
include $(THEOS_MAKE_PATH)/tweak.mk
after-install::
    install.exec "killall -9 SpringBoard"
2.2.3  plist文件
    接下来介绍plist文件。plist文件记录了工程的配置文件,主要作用是指定程序的作用范围,具体内容如下所示:

       可以看到,该文件的内容是一系列的Dictionary,最外层为Root键,Root键下为Filter键,Filter键下的array即为要设置的部分。该部分分为三类,一类是Bundles,即所编写程序的作用对象,这个类型的字段就是所要注入的程序Bundle名,这里要注入的进程为SpringBoard,所以填入com.apple.springboard;第二类是Classes,即指定要注入的类名,同样是根据填入的字符串来筛选注入的类名;第三类是Executables,即指定要注入的可执行文件名。这三类Array可以根据需要来设定,测试程序中只需注入SpringBoard,没有其他特殊筛选规则,因此此处设定便如上图所示,无需更改。

2.2.4  Tweak.xm文件
      最后需要介绍的是Tweak.xm文件,该文件是实现具体功能的关键所在,是实现具体功能的源文件,这个文件支持Logos和C、C++语法,这两种语法的编写在下文都会涉及,读者可以根据功能需要自己选择对应的语法来实现相关功能。为了更好地理解编写规则,这里先介绍默认生成的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和%end,其中%hook后面指定要hook的类名,另一方面,hook的整块逻辑完成后结尾要加上%end,在hook逻辑中可以添加要hook的函数,并在函数体内部实现想要添加的代码逻辑;第二类是%orig,该语句代表执行原函数逻辑,即完成hook操作后可以选择是否调用原函数的代码,若需要调用则加上“%orig;”即可;第三类是%log,这类代码的作用是在log中打印hook的函数的类名、参数等信息。
       除了注释中展示的三种语法外,Logos还支持%group、%init、%ctor等语法,感兴趣的读者可以参考这篇文档
      理解了注释的Logos语法后可以对源码进行如下修改:
%hook SpringBoard
- (void)applicationDidFinishLaunching:(id)application
{
    %orig;
    UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"Hello World!" message:nil delegate:self cancelButtonTitle:@"sure" otherButtonTitles:nil];
    [alert show];
    [alert release];
}
%end
       根据之前介绍的Logos语法,以上代码的含义是hook SpringBoard类的applicationDidFinishLaunching()函数,设备调用该函数时首先执行原来的代码逻辑,随后利用UIAlertView类弹出“Hello World!”字样的对话框。

2.3  编译和安装
      经过上面的步骤已经完成了一个Tweak工程的新建、配置和编写代码等操作,最终要在设备上运行还得编译和安装。此处采用的编译和安装方法都是命令行操作,Tweak工程的越狱开发都可以在xcode中使用图形化界面操作,具体的操作流程可以参考 《IOS 动态链接库(Dylib文件)编写及测试》中的相关章节。
Tweak工程的命令行编译操作也比较简单:首先在命令行下将目录切换至要编译的工程目录,随后使用make命令或make package命令编译工程,其中make命令编译生成的是dylib文件,make package命令生成的是deb安装包文件。除此之外还可以通过“make package install”命令编译并将程序直接安装到手机上,不同于前两种编译方式,该方法需要在MakeFile文件的头部加上手机的IP地址“THEOS_DEVICE_IP = [device_ip_address]”。
      若出现编译不通过的情况,请读者确认以下文件的正确性及权限:
     (1) Ldid下载到/opt/theos/bin/,随后执行chmod 777 提升权限;
     (2) Libsubstrate.dylib:scp root@[ios_device_ip]: 
     /Library/Frameworks/CydiaSubstrate.framework/CydiaSubstrate     /opt/theos/lib/libsubstrate.dylib (手机安装了CydiaSubstrate的情况下)
     (3) dkpg-deb:下载到/opt/theos/bin/并重命名为dkpg-deb,随后执行chmod 777 提升权限;
     (4) 命令行输入命令:sudo perl -i -pe ‘y|\r||d’ /Users/[username]/[project]/theos/bin/dpkg-deb,其中[username]为Mac的用户名,[project]为要编译的工程路径。
    当输入“make package install”命令出现以下内容时,说明程序已经编译并运行成功:
UseriMac:test xxx$ make package install
Making all for tweak test...
make[2]: Nothing to be done for `internal-library-compile'.
Making stage for tweak test...
dm.pl: building package `com.gameprotect.test:iphoneos-arm' in `./com.gameprotect.test_0.0.1-6_iphoneos-arm.deb'
install.exec "cat > /tmp/_theos_install.deb; dpkg -i /tmp/_theos_install.deb && rm /tmp/_theos_install.deb" < "./com.gameprotect.test_0.0.1-6_iphoneos-arm.deb"
root@10.66.213.161's password: 
Selecting previously deselected package com.gameprotect.test.
(Reading database ... 1117 files and directories currently installed.)
Unpacking com.gameprotect.test (from /tmp/_theos_install.deb) ...
Setting up com.gameprotect.test (0.0.1-6) ...
install.exec "killall -9 SpringBoard"
root@10.66.213.161's password: 

    整个过程会要求输入两次手机端的ssh密码,输入成功后手机便会执行killall -9 SpringBoard命令重启,重启完成后便可以看到预期的弹框效果:

    至此整套的命令行注入流程便到此结束,读者可以根据自己的需要注入感兴趣的程序并尝试修改更复杂的逻辑。

三、 Ios注入原理
    为了使读者能更好的理解和运用ios注入,本章最后将简要介绍ios平台注入的原理和所使用的框架。
    细心的读者在上一节的control文件中的depend字段可能会注意到测试的demo依赖库为mobilesubstrate,该工具便是实现ios注入的关键所在,整个工具主要分为MobileHooker、MobileLoader以及Safe mode三部分。
3.1 MobileHooker
    MobileHooker组件主要提供了MSHookMessageEx和MSHookFunction两个函数针对不同语言的inline hook功能,其中MSHookMessageEx负责用来hook Objective-C函数,MSHookFunction负责用来hook C/C++函数,虽然两个函数的实现原理完全不同,但它们的本质功能都是替换被注入程序的原函数功能,具体的工作原理如下图所示:

      其中左边的部分为程序的正常执行流程,即程序在执行完instruction a,b,c...后会跳转到方法Original Function,Original Function执行完后会跳转到instruction x,y,z...;而hook后的程序执行流程如图中右边的部分所示,即程序在执行完instruction a,b,c...后会跳转到Replaced Function,此时Replaced Function内部在执行完编写的新增代码逻辑后会有两种后续的执行方式,一种是跳回Original Function继续执行原函数的逻辑,另一种是直接执行Original Function后面的指令,根据不同的需求可以在Replaced Function内部选择是否调用原函数。接下来具体说明MSHookMessageEx和MSHookFunction两个函数的实现原理和调用方式。

3.1.2  MSHookMessageEx函数
    MSHookMessageEx函数的作用对象是Objective-C函数,其原理是调用Objective-C中高等级的运行时函数API class_getInstanceMethod、method_setImplementation、method_exchangeImplementations等来替换原函数的逻辑,比如method_exchangeImplementations函数在Objective-C官方文档中描述的作用便是交换两个函数的实现,其内部的调用逻辑类似于:
IMP imp1 = method_getImplementation(m1);
IMP imp2 = method_getImplementation(m2);
method_setImplementation(m1, imp2);
method_setImplementation(m2, imp1);
    因此,MSHookMessageEx就是利用Objective-C中运行时的API来替换原函数逻辑。接下来介绍MSHookMessageEx函数的使用方法,在CydiaSubstrate官网中该函数的描述如下:
void MSHookMessageEx(Class _class, SEL message, IMP hook, IMP *old);
       其中第一个参数_class为要Hook的Objective-C函数的类名;第二个参数message为要Hook的Objective-C函数的message;第三个参数hook为hook后新的对应该message的执行逻辑,即替换后的函数地址;第四个参数old为对应该message的原函数的地址,若无需调用原函数则该参数可以设为NULL。其官方的使用方法如下:

NSString *(*oldDescription)(id self, SEL _cmd);
// implicit self and _cmd are explicit with IMP ABI
NSString *newDescription(id self, SEL _cmd) {
    NSString *description = (*oldDescription)(self, _cmd);
    description = [description stringByAppendingString:@"!"];
    return description;
}
MSHookMessageEx(
    [NSObject class], @selector(description),
    &newDescription, &oldDescription
);

       以上的代码首先用指针oldDescription保存了原来的NSString类description方法的地址,随后实现了新的description方法,最后调用MSHookMessageEx函数来替换原来的description方法,可以看到第一个参数为[NSObject class]类,第二个参数为方法description的message,第三个参数为替换后新的description方法地址,第四个参数为description方法的原地址。
        按照官网的示例可以在Tweak工程的Tweak.xm源文件中调用MSHookMessageEx函数来hook想要hook的方法,另一方面,也可以使用上文中提到的Logos语法来hook相关函数,实际上Logos语法只是对MSHookMessageEx函数进行了封装,实际上其调用的还是MSHookMessageEx函数。想要了解MSHookMessageEx更多信息可以参考以下链接

3.1.2 MSHookFunction函数
      MSHookFunction函数的作用对象是C/C++函数,和Objective-C这类高级语言不同,C/C++并不提供运行时这种高级的API来直接替换方法实现,因此实现Hook要更加困难一些,MSHookFunction实现Hook采用的是在内存中写入汇编指令来达到目的:简单说来就是首先修改要Hook函数的前N个字节的内存,使其跳转到替换后的函数头,这样就能执行自己的代码;同时会保存原函数的前N个字节的内容以便执行完自己的逻辑后能正确执行原函数的逻辑。具体的汇编实现可以参考这篇分析文章
      接下来将着重介绍MSHookFunction的使用方法,在CydiaSubstrate官网中函数的描述如下:
void MSHookFunction(void *symbol, void *hook, void **old);

    其中第一个参数为所要Hook的函数地址,值得注意的是该地址不一定限于函数头,也可以是函数内部的任一代码地址;第二个参数为Hook后要替换的函数地址;第三个参数为指向Hook地址的指针,用来保存被Hook函数替换掉的汇编指令方便执行完自己的代码逻辑后能够继续执行原函数的逻辑,若不需要调用原函数,则此处可以设为“NULL”。MSHookFunction官方给出的使用方法如下:
void *(*oldConnect)(int, const sockaddr *, socklen_t);
void *newConnect(
    int socket, const sockaddr *address, socklen_t length
) {
    if (address->sa_family == AF_INET) {
        sockaddr_in *address_in = address;
        if (address_in->sin_port == htons(6667)) {
            sockaddr_in copy = *address_in;
            address_in->sin_port = htons(7001);
            return oldConnect(socket, ©, length);
        }
    }
    return oldConnect(socket, address, length);
}
MSHookFunction(&connect, &newConnect, &oldConnect);

       这段代码首先定义了一个名为oldConnect的指针用于保存要Hook函数被替换的指令;然后实现了新的newConnect方法,即Hook后实际想要执行的代码逻辑;最后使用MSHookFunction来对目标函数进行Hook,第一个参数为原方法connect的地址,第二个参数为实现的新方法地址,第三个参数为用于保存connect被替换的汇编指令的地址。其中“&connect”是MSHookFunction内部调用了CydiaSubstrate的另一个MSFindSymbol函数来实现根据函数名查找其函数地址,更多的MSHookFunction实现原理可以参考以下链接
      以上便是mobilesubstrate框架MobileHooker的主要原理,读者可以按照代码示例在Tweak.xm源文件中直接调用MSHookMessageEx和MSHookFunction这两个函数以达到Hook Objective-C和C\C++函数的目的。

3.2 MobileLoader
        除了MobileHooker外mobilesubstrate框架还提供了一个重要的组件MobileLoader,该组件的作用是让应用程序加载第三方的dylib,比如本章第二小节中的test demo编译后生成的dylib就需要MobileLoader组件来加载。
       MobileLoader的原理主要是在系统启动时由launchd进程将MobileLoader加载进内存,随后MobileLoader会利用 DYLD_INSERT_LIBRARIES环境变量将自己加载进设备的各个进程中,并会遍历/Library/MobileSubstrate/DynamicLibraries/目录下的文件,根据和每个dylib同名的plist文件来确定该dylib的作用范围,若当前进程满足该作用范围,则会使用dlopen函数动态加载对应的dylib。
       比如第二小节中实验设备/Library/MobileSubstrate/DynamicLibraries/目录下便可已看到以下文件:
XXXX-iPhone:~ root# ls /Library/MobileSubstrate/DynamicLibraries/
MobileSafety.dylib  MobileSafety.plist test.dylib  test.plist

    其中test.plist文件便是之前介绍过的plist工程配置文件,MobileLoader读取该文件内容如下:
XXXX-iPhone:~ root# cat /Library/MobileSubstrate/DynamicLibraries/test.plist
{
Filter = {
Bundles = (
"com.apple.springboard",
);
};
    即进程Bundles为com.apple.springboard时便会加载对应的test.dylib。值得注意的是,MobileLoader加载完每个dylib后会首先调用dylib中用__attribute__((constructor))声明的入口函数,官网给出的示例代码如下:
__attribute__((constructor))
static void initialize() {
  NSLog(@"MyExt: Loaded");
  MSHookFunction(CFShow, replaced_CFShow, &original_CFShow);
}
       以上代码便用__attribute__((constructor))声明了入口函数initialize(),MobileLoader加载完该dylib后便会调用initialize()方法打印log并使用MSHookFunction Hook CFShow函数。之前的demo中并未用__attribute__((constructor))声明是因为Logos语法中%hook便封装了该声明,因此会直接进行hook操作。

3.3 SafeMode
      最后一个要介绍的mobilesubstrate组件是SafeMode。注入别的进程并改变其逻辑一定存在风险,难免会造成程序崩溃的现象,如果崩溃的是SpringBoard等系统进程,则会造成系统瘫痪。为了避免这类情况,SafeMode会捕获SIGABRT、SIGILL、SIGBUS、SIGSEGV、SIGSYS这几种信号,捕获到目标信号后SafeMode会使设备进入安全模式,在安全模式下所有第三方插件(即dylib)都会被禁用,便于修复系统。

四、小结
      本章主要介绍了IOS越狱环境下的注入流程和相关原理,通过阅读本章内容读者能够掌握注入IOS的技巧并对注入框架和实现有初步的认识。

分享到:
踩1 赞3

收藏

上一篇:Android平台感染ELF文件实现模块注入

下一篇:Android平台进程模块信息获取

最新评论
引用 猜猜我是谁 2017-12-19 18:27
1111
引用 ╮( ̄▽ ̄")╭ 2017-12-19 18:26
前排沙发你们看到的永远是我!
引用 ╮( ̄▽ ̄")╭ 2017-12-19 18:26
前排沙发你们看到的永远是我!

查看全部评论(3)

B Color Image Link Quote Code Smilies

发表评论