zoukankan      html  css  js  c++  java
  • Theos

    在利用theos开发一些插件时,我们经常会用到以下几个指令:

    %hook 指定需要hook的类名,以%end结尾

    //hook的是SpringBoard这个类里面的方法
    %hook SpringBoard
     -(void)_menuButtonDown:(id)down
     {
       NSLog(@"You've  pressed home button");
       %orig; //call the original _menuButtonDown
     }
     %end

    %orig 执行被hook函数的原始代码,类似于super.method功能

    %hook ClassName
    - (void) _menuButtonDown: (id)down
    {
       NSlog(@"ss");
        //如果去掉%orig,那么原始函数不会得到执行。
       %orig;
    }
    @end

    %new 该指令用来给现有的class添加一个新的函数。与Runtime中的class_addMethod相同。

    %hook SpringBoard
    //hook内部的代码 默认都是替换被hook类中函数的实现,所以如果不加%new,theos默认是去类中找namespaceNewMethod这个方法,替换它的方法实现。所以如果我们是新增的函数而不是更改原函数的内部实现则需要加%new这个指令
    %new
    -(void) namespaceNewMethod
    {
            NSlog(@"你好");
    }
    @end

    %log 用来打印log的,将信息输入到syslog中,可以以%log([(<type>)<expr>,...])的格式追加其打印信息,如下:

    %hook SpringBoard
    -(void) _menuButtonDown :(id)down 
    {
      %log((NSString * )@"IOSER",(NSString *)@"Debug");
      %orig;
    }
    @end

    %group 该指令用于将%hook分组,便于代码管理及按条件初始化分组,必须以%end结尾:一个%group可以包含多个%hook,所有不属于某个自定义group的%hook会被隐式归类到%group _ungrounped中,%gruop的用法如下:

     1 //这段代码的含义为在%group iOS7hook中勾住了iOS7Class的iOS7Method,同理在iOS8Class的iOS8Method。然后在%group _ungrouped中勾住SpringBoard类的powerDown函数。
     2 
     3 %group iOS7Hook
     4 %hook iOS7Class
     5 -(id) iOS7Method
     6 {
     7 
     8     id result = %orig;
     9     NSlog(@"This class & method only exist in ios 8.");
    10     return result;
    11 }
    12 @end
    13 @end // iOS7Hook
    14 
    15 
    16 %group iOS8Hook
    17 %hook iOS8Class
    18 -(id) iOS8Method
    19 {
    20 
    21     id result = %orig;
    22     NSlog(@"This class & method only exist in ios 8.");
    23     return result;
    24 }
    25 @end
    26 @end // iOS8Hook
    27 
    28 
    29 //所有不属于某个自定义group的%hook会被隐式归类到%group _ungrounped 所以其实下面powerDown函数其实是属于group_ungrounped 组的
    30 %hook SpringBoard
    31 -(void) powerDown 
    32 {
    33     %orig;
    34 }
    35 @end
    36 
    37 //%ctor: tweak 的constructor ,完成初始化工作;如果不是显示定义,theos会自动生成一个一个%ctor并在其中调用%init(_ungrouped)。默认只会自动初始化_ungrouped,不会初始化自定义的group
    38 %ctor {
    39     //只有调用了%init,对应的%group才能起作用、
    40     %init(iOS7Hook);
    41     %init(iOS8Hook);
    42    
    43     //默认组    
    44      %init(_ungrouped);    
    45 }
    View Code

    %init 该指令用于初始化某个%group,必须在%hook%ctor内调用,如果带参数,则初始化指定的group,如果不带参数,则初始化_ungrouped

    只有调用了%init,对应的%group才能起作用

    %ctor {
          //带参数的话是初始化自定义的group  
        %init(iOS8);
         //不带参数的话是初始化默认的_ungrouped
        %init;
    }

    %ctor tweak的构造器,用来初始化。如果开发者没有重写这个方法,theos会自动生成%ctor并在其中调用%init(_ungrouped)。%ctor一般可以用来初始化%group,以及进行MSHookFunction等操作。

    注意:%ctor不需要以%end结尾

     1 %hook SpringBoard
     2 
     3 -(void) reboot
     4 {
     5     NSlog(@"你好");
     6     %orig;
     7 }
     8 %end
     9 
    10 //如果开发者不去重写这个方法,theos默认是实现了这个方法,并在这个方法里初始化了_ungrouped  所以默认hook都会生效,但是如果用户实现了这个方法但却没做初始化操作那就回导致hook失效
    11 %ctor
    12 {
    13     // need to call %init  explicitly!
    14 }
    15 //这里 %hook无法生效,因为这里显示定义了%ctor,却没有显示的调用%init,因此%group(_ungrouped)不起作用。

    %c 该指令用来获取一个类的名称,类似于objc_getClass。%c([+|-]Class)

    %dtor 在程序退出是调用。

    tweak工程文件

       我们创建完tweak项目后,会在文件夹内看到以下几个文件:

    control文件该文件记录了工程的基本信息,会被打包进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无法正常工作,可以按需更改。例如:
    //表示当前iOS版本必须在6.0以上,且必须安装MobileSubstrate,才能正常运行这个tweak。
    Depends: mobilesubstrate, firmware (>=6.0)
    • Version字段:用于描述这个deb包的版本号,可以按需更改
    • Architecture字段:用于描述deb包安装的目标设备架构,不要更改
    • Description字段:deb包的简单介绍,可以按需更改
    • Maintainer字段:用于描述deb包的维护人,即deb包的制作者而非tweak的作者,可以按需更改
    • Author字段:用于描述tweak的作者,可以按需更改
    • Section字段:用于描述deb包所属的程序类别,不要更改

      control文件中可以自定义的字段还有很多,一般上面的信息就已经足够了。更全面的可以查看官方网站

      值得注意的是:Theos在打包deb时会对control文件做进一步处理。比如更改Version字段为:0.0.1-2,标识Theos的打包次数,方便管理;增加Installed-Size字段,用于描述deb包安装后的估算大小,与实际大小可能有偏差,不要更改。

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

    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:指定工程中参与编译的源文件,如果工程中需要用到多个源文件则用空格将各个文件名分开,也可以用通配符,但是需要制定具体的路径,比如scr文件夹内有二十个xm文件,如果一个个输入太繁琐,所以可以写成ReverseDemo_FILES = Tweak.xm scr/*.m 可以按需修改。
    • 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
    • THEOS_DEVICE_IP =192.168.1.100        指定安装的手机ip(ssh)
    • THEOS_DEVICE_PORT = 10010            指定端口
    • framework字段可以指定要导入的框架,比如这里的测试demo中填写的是“ReverseDemo_FRAMEWORKS = UIKit”,UIKit为后续测试代码需要用到的框架,另一方面,还可以通过ReverseDemo_PRIVATE_FRAMEWORKS字段指定要导入的私有库,格式不变。例如:
    ReverseDemo_FRAMEWORKS = UIKit CoreTelephony CoreAudio
    ReverseDemo_PRIVATE_FRAMEWORKS = AppSupport ChatKit

    ReverseDemo.plist:记录工程的配置信息,描述了tweak的作用范围,内容如下:

    Filter下是一系列Array,可以分为三类:

      Bundle:指定若干bundle为tweak的作用对象。如:com.apple.springboard

      Classes:指定若干class为tweak的作用对象。如:NSString

      Executables:指定若干可执行文件为tweak的作用对象。如:callservicesd

    这三类Array可以混合使用,但当Filter下有不同类的Array时,需要添加一个“Mode: Any”键值对。当Filter下的Array只有一类时,不需要添加。

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

    %hook ClassName
    
    // 要替换方法的实现
    - (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.)
    }
    
    %end

    Tweak插件的图片资源

    iOS中常用的两种加载图片资源的方式:

    + (nullable UIImage *)imageNamed:(NSString *)name;      // load from main bundle
    - (nullable instancetype)initWithContentsOfFile:(NSString *)path; //load image by path

      +imageNamed:方式从程序的main bundle加载图片,由于我们自己单独开发的插架,资源是需要单独管理的,无法从宿主app的MainBundle中读取图片。所以我们需要使用第二种方式,给定图片的路径去加载图片。那么图片的存放的路径应该存放在手机的那个目录下呢?

      在tweak项目中,可以创建名称为layout文件夹,将所有图片等资源文件存放在这里。在打包安装插架到手机中时,layout中的资源会打包到手机的根目录下。也就是说layout就对应手机的根目录/

      

      图片资源放到创建的layout文件夹中相当放到了设备的根路径下,这时候我们可以在插件中引用该图片资源。但是放到根路径下显然是不合适的,因为如果开发的插件图片资源较多的话,根路径会很乱。所以我们需要将插件中用到的图片放到特定的文件夹内,也就是这个文件夹内是设备上所有插件的图片资源库,即下面这个preference文件夹。当然这里只是小的建议,并不是强制要求,直接放根路径或任何路径下都可以引用。

      我们还可以为开发的插件在preference文件夹下新建一个新的文件夹,比如新建一个TweakWechatImage的文件夹,里面存放的都是我们开发的wechat插件的图片。所以我们需要在layout中建立一个Library/preferenceLoader/Preferences/TweakWechatImage的文件夹路径,这样插件打包安装时会自动将图片资源放到设备上的这个路径中。

     

      这个时候,我们在调用图片时,只需要填写全路径即可:

    [UIImage imageWithContentsOfFile:@"/Library/PreferenceLoader/Preferences/TweakWechatImage/test.png"];

      当然,如果多处地方用到图片资源的话,我们可以写一个宏定义,不用每次都写这么一大串:

    //这是宏定义的语法,一个是@“a”空格@"b”含义就是: a/b    第二个是# :字符串操作符,用于将参数序列化成一个字符串;即#abc 含义为@"abc"
    #define IMAGE_PATH(IMG_NAME) @"/Library/PreferenceLoader/Preferences/TweakWechatImage" #IMG_NAME

      我们下次再调用图片时就很简洁了:

     [UIImage imageWithContentsOfFile:IMAGE_PATH(test.png)];

    Tweak插件的实现原理

      我们通过Theos开发的插件其实只是改变app中某些方法在内存中的调用实现,并没有修改app的执行文件。在点击app图标运行时,crype中的Substrate插件(Substrate负责管理Device/Library/MobileSubstrate文件夹中的内容,这个插件是越狱后自动装好的)会去这个路径查看各个plist(plist中规定了对应插件的应用范围)文件的内容。查看是否存在该app的插件,有的话则在调用插件中的方法时修改其内部实现。

      实现过程为:

       

      所以,theos的Tweak插件不会对原来的可执行文件进行修改,仅仅是修改了内存中的代码逻辑。

    未脱壳的App是否支持tweak?
        支持,因为tweak是在内存中实现的,并没有修改.app包中的可执行文件
    tweak效果是否永久性?
        视情况而定,如果更新了App,新的版本没有了这个类,或者不使用这个方法了,那么我们将无法hook到这个方法,tweak将会失效
    未越狱的手机是否支持tweak?
        不可以,未越狱的手机就没有Cydia,也就没有Substrate。所以就不能去查找插件了。
    能不能对Swift/C函数进行tweak ?
        原理是可以,但是方法跟oc不一样
    能不能对游戏项目进行tweak?
        可以,但游戏很多是用C#、c++代码编写的,而且一般都有混淆的,所以很难。

    tweak插件的卸载

    方式一

    • 直接从 /Library/MobileSubstrate/DynamicLibraries 文件夹删除插件对应的Plist文件和dylib文件
    • 这种方式卸载不是很干净

    方法二

    • 通过Cydia卸载,在cydia的已安装模块去查找对应的插件 然后在插件详情页将其卸载。
    • 推荐这种方式, 卸载比较彻底

    注意点:

    1、我们通过make package打包,默认是打debug包。如果想打release的包,需要将make package指令换成make package debug=0  debug一般用在开发测试,release一般用在正式环境,一般况下release包会比debug包小一点。

    2、编写插件并不一定只能在Tweak.x文件中,可以创建多文件来开发,这样目录清晰,而且文件的格式并不一定限于.x文件,也可以是.h .m .xm等等。只是注意要在makefile中加入参与编译的文件,文件中引入其他文件时要写全路径。

      

  • 相关阅读:
    如何快速取得股票交易历史数据
    ArcSDE性能优化系列之ArcSDE参数篇
    2020年8月29日
    2020年8月31日
    9.2
    2020年8月25日
    2020年8月30日
    2020年8月27日
    2020年8月26日
    2020年8月28日
  • 原文地址:https://www.cnblogs.com/gaoxiaoniu/p/11797555.html
Copyright © 2011-2022 走看看