iOS越狱插件开发教程(hook VIP)


1.安装ldid
ldid用来专门签名iOS可执行文件的工具,用以在越狱iOS中取代Xcode自带的codesign。如果不安装,那么产生的deb文件就安装不到手机上

brew install ldid

2.Theos安装
官方建议把Theos安装在/opt/theos目录下

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

然后把/opt/theos的权限改为自己所拥有

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

3.环境变量
编辑配置文件:vim ~/.bash_profile

export THEOS=/opt/theos
export PATH=/opt/theos/bin/:$PATH
iOS越狱插件开发教程(hook VIP)-清风博客
  • 更新立即生效
    source ~/.bash_profile
  • 查看是否生效:echo $THEOSecho $PATH
iOS越狱插件开发教程(hook VIP)-清风博客
  • 如果关闭命令端,再打开就失效是因为采用了zsh代替bash,而zsh加载的是 ~/.zshrc文件,而 .zshrc文件中并没有定义任务环境变量。解决办法在~/.zshrc文件最后,增加一行:
    source ~/.bash_profile
    然后立即生效
    source ~/.zshrc
  • 测试安装是否成功

在任意路径下输入nic.pl

nic.pl
NIC 2.0 - New Instance Creator
------------------------------
  [1.] iphone/activator_event
  [2.] iphone/application_modern
  [3.] iphone/application_swift
  [4.] iphone/flipswitch_switch
  [5.] iphone/framework
  [6.] iphone/library
  [7.] iphone/preference_bundle_modern
  [8.] iphone/tool
  [9.] iphone/tool_swift
  [10.] iphone/tweak
  [11.] iphone/xpc_service
Choose a Template (required):

Tweak项目创建

1. 输入nic.pl后,选择模板输入10

Choose a Template (required): 10

2.输入tweak的工程名

Project Name (required): readerTweak

3.输入deb包名(格式类似bundle Identifier)

Package Name [com.yourcompany.readertweak]: com.lb.readertweak

4. 输入作者或维护人名字

Author/Maintainer Name [mac]: Lucky Blue

5. 输入tweak作用的对象,就是需要需要HOOK的APP的bundle Identifier

[iphone/tweak] MobileSubstrate Bundle filter [com.apple.springboard]: com.tencent.ied.app.comic

6.输入tweak安装完成后需要重启的应用,以进程名表示,比如系统的桌面:

[iphone/tweak] List of applications to terminate upon installation (space-separated, '-' for none) [SpringBoard]: SpringBoard

成功终端就会显示

Instantiating iphone/tweak in readertweak/...
Done.
iOS越狱插件开发教程(hook VIP)-清风博客

完整步骤

成功后会生成下面四个文件

iOS越狱插件开发教程(hook VIP)-清风博客

Tweak项目编写

在Makefile文件最上面加入下面两行代码

  • THEOS_DEVICE_IP:本机IP
  • THEOS_DEVICE_PORT:本机与iPhone通信的端口
export THEOS_DEVICE_IP=127.0.0.1
export THEOS_DEVICE_PORT=5757
iOS越狱插件开发教程(hook VIP)-清风博客

删除所有Tweak.x文件里默认的注释,然后输入如下
Logos官方文档


%hook ComicHomeViewController //%hook 类名
-(void)viewWillAppear:(BOOL)animated{
    %orig;// %orig是Logos语法,先调用上一层的代码,跟oc的 [super xxxx]差不多的意思
//创建UIAlertView
    UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"注意" 
        message:@"这是第一个tweak项目" 
        delegate:nil 
        cancelButtonTitle:@"确定" 
        otherButtonTitles:nil];
    [alert show];
}
%end

编译,打包和安装

make clean && make && make package && make install
iOS越狱插件开发教程(hook VIP)-清风博客

编译打包安装成功

iOS越狱插件开发教程(hook VIP)-清风博客

Cydia可以看到已安装

iOS越狱插件开发教程(hook VIP)-清风博客

因为按键精灵ios手机版需要购买vip才能使用,于是…

iOS越狱插件开发教程(hook VIP)-清风博客

原理是修改用户模块的变量

iOS越狱插件开发教程(hook VIP)-清风博客
  • 用户模块的变量被我强制改成
  arg1.UserName = @"用户名";//写你注册d 名字
  arg1.IsActivated = [NSNumber numberWithInt:1];
  arg1.IsVIP = [NSNumber numberWithInt:1];
  arg1.VIPExpTime = @"2099-12-31";

注入方式是用theos编写tweak生成deb包。

@interface MAUser : NSObject

@property(copy, nonatomic) NSString *UserName; // @synthesize 
@property(copy, nonatomic) NSNumber *IsActivated; // @synthesize 
@property(copy, nonatomic) NSString *VIPExpTime; // @synthesize 
@property(copy, nonatomic) NSNumber *IsVIP; // @synthesize 

@end

%hook MAMineHeaderView
- (void)updateWithUser:(MAUser *)arg1 {
  arg1.UserName = @"修改注入的名字!";
  arg1.IsActivated = [NSNumber numberWithInt:1];
  arg1.IsVIP = [NSNumber numberWithInt:1];
  arg1.VIPExpTime = @"2022-06-15";
    %orig;
}
%end
  • 生成tweak模板的时候bundle ID 指定为:com.cyjh.MobileAnjian

Cycript调试VIP内容

iOS越狱插件开发教程(hook VIP)-清风博客
iOS越狱插件开发教程(hook VIP)-清风博客

adv-cmds


先通过SSH登录到服务器(iPhone

ps 命令

  • 列出所有进程  ps -A(当前手机打开了腾讯动漫)
  • 可以从上图找到11095是腾讯动漫的进程id(pid),
  • ComicReader是腾讯动漫的进程名称
  • 搜索进程:ps –A | grep 关键词
i-57:~ root# ps -A | grep ComicReader
11095 ??         0:05.50 /var/containers/Bundle/Application/015C9AB9-4440-492F-911B-6DEC6915FBFC/ComicReader.app/ComicReader
11121 ttys000    0:00.01 grep ComicReader
i-57:~ root#

附加App直接调试 cycript -p 进程名称/进程的编号

cycript -p 11095
或者
cycript -p ComicReader
cy#

启动和退出
启动:cycript -p 进程名称
退出:control + d
清屏:command + r
Cycript 常用语法
UIApp = UIApplication.sharedApplication()

cy# UIApp
#"<UIApplication: 0x104f04470>"

用内存地址获取对象:#内存地址

cy# #0x104f04470
#"<UIApplication: 0x104f04470>"

查看对象的所有成员变量:*对象

cy# *UIApp
{isa:UIApplication,_responderFlags:@error,_delegate:#"<TencentXGPushAppDelegate: 0x280e46cc0>",_remoteControlEventObservers:0,_topLevelNibObjects:null,_networkResourcesCurrentlyLoadingCount:0,_hideNetworkActivityIndicatorTimer:null,_statusBar:null,_statusBarRequestedStyle:0,_statusBarWindow:null,_observerBlocks:@[],_postCommitActions:@[],_postCommitActionsNeedToSynchronize:false,_mainStoryboardName:null,_idleModeController:null,_displayLayoutMonitor:null,_systemUserInterfaceStyle:0,_eventFetcher:#"<UIEventFetcher: 0x282264540>",_eventDispatcher:#"<UIEventDispatcher: 0x281569620>",_applicationFlags:@error,_keyCommandToken:#"<BSSimpleAssertion: 0x281445110; identifier: com.apple.backboard.hid.delivery; reason: 2-keycmds; valid: YES>",_physicalKeyCommandMap:@{},_physicalKeycodeMap:[NSOrderedSet orderedSetWithArray:@[]]],_alwaysHitTestsForMainScreen:false,_backgroundHitTestWindow:null,_appInfo:#"<_UIApplicationInfoParser: 0x2830656c0>",_actionsPendingInitialization:null,_idleTimerDisabledReasons:[NSSet setWithArray:@[]]],_keyRepeatAction:null,_currentTimestampWhenFirstTouchCameDown:0,_currentLocationWhereFirstTouchCameDown:{x:0,y:0},_saveStateRestorationArchiveWithFileProtectionCompleteUntilFirstUserAuthentication:false,_fenceTaskAssertion:null,_cachedSystemAnimationFence:null,_systemNavigationAction:null,_activityContinuationManager:#"<UIActivityContinuationManager: 0x28156e310>",__gestureEnvironment:#"<UIGestureEnvironment: 0x283061d50>",_forceStageObservable:null,_HIDGameControllerEventObserver:null,_HIDGameControllerEventQueue:null,_motionNotificationGenerator:null,_appState:#"<UISApplicationState: 0x281b633c0>",_applicationPushRegistry:#"<PKPushRegistry: 0x2838401e0>",_storyboardInitialMenu:null,_endpointMonitor:#"<BSServiceConnectionEndpointMonitor: 0x283861ef0; service: com.apple.frontboard.open; active>",optOutOfRTL:false,_isDisplayingActivityContinuationUI:false,_applicationWantsGESEvents:false,_shortcutService:null,___queuedOrientationChange:null,__expectedViewOrientation:1}

递归打印view的所有子控件:view.recursiveDescription().toString()

UIApp.keyWindow.recursiveDescription().toString()

定义变量:var 变量名 = 变量值

cy# var window = UIApp.keyWindow
#"<UIWindow: 0x104f1be40; frame = (0 0; 375 667); gestureRecognizers = <NSArray: 0x281552d30>; layer = <UIWindowLayer: 0x281b753e0>>"

定义函数:function 函数名(...) { ... }

cy# function sum(a, b) {return a+b;}
cy# sum(2,3)
5

筛选出某种类型的对象:choose(类型)

cy# choose(UIViewController)
[#"<UISystemInputAssistantViewController: 0x106468630>",#"<UIEditingOverlayViewController: 0x10649e1d0>",#"<ComicHomeViewController: 0x105843000>",#"<ComicNavgationController: 0x105849000>",#"<VPlaygoundViewController: 0x10584e200>",#"<ComicNavgationController: 0x105850200>",#"<WaitHomeViewController: 0x105853a00>",#"<BookShelfViewController: 0x10585aa00>",#"<ComicNavgationController: 0x10585b000>",#"<MyViewController: 0x10585b600>",#"<ComicNavgationController: 0x10585bc00>",#"<ComicNavgationController: 0x105867400>",#"<UpdateChannelViewController: 0x105883a00>",#"<UpdateChannelSubViewController: 0x1058ee400>",#"<UpdateChannelSubViewController: 0x1058f5000>",#"<UIInputWindowController: 0x10595b000>",#"<ADWebViewController: 0x105972a00>",#"<ComicTabBarController: 0x105040400>",#"<ADWebViewController: 0x105137000>",#"<GenderSelectionViewController: 0x1051e8000>"]

对.cy文件的应用
.cy文件是对Cycript的封装,方便提供一些比较常用的函数。

(function(exports) {
    var invalidParamStr = 'Invalid parameter';
    var missingParamStr = 'Missing parameter';

    // app id
    LBAppId = [NSBundle mainBundle].bundleIdentifier;
    // mainBundlePath
    LBAppPath = [NSBundle mainBundle].bundlePath;
    // document path
    LBDocPath = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES)[0];
    // caches path
    LBCachesPath = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES)[0]; 

    // 加载系统动态库
    LBLoadFramework = function(name) {
        var head = "/System/Library/";
        var foot = "Frameworks/" + name + ".framework";
        var bundle = [NSBundle bundleWithPath:head + foot] || [NSBundle bundleWithPath:head + "Private" + foot];
        [bundle load];
        return bundle;
    };

    // keyWindow
    LBKeyWin = function() {
        return UIApp.keyWindow;
    };

    // 根控制器
    LBRootVc =  function() {
        return UIApp.keyWindow.rootViewController;
    };

    // 找到显示在最前面的控制器
    var _LBFrontVc = function(vc) {
        if (vc.presentedViewController) {
            return _LBFrontVc(vc.presentedViewController);
        }else if ([vc isKindOfClass:[UITabBarController class]]) {
            return _LBFrontVc(vc.selectedViewController);
        } else if ([vc isKindOfClass:[UINavigationController class]]) {
            return _LBFrontVc(vc.visibleViewController);
        } else {
            var count = vc.childViewControllers.count;
            for (var i = count - 1; i >= 0; i--) {
                var childVc = vc.childViewControllers[i];
                if (childVc && childVc.view.window) {
                    vc = _LBFrontVc(childVc);
                    break;
                }
            }
            return vc;
        }
    };

    LBFrontVc = function() {
        return _LBFrontVc(UIApp.keyWindow.rootViewController);
    };

    // 递归打印UIViewController view的层级结构
    LBVcSubviews = function(vc) { 
        if (![vc isKindOfClass:[UIViewController class]]) throw new Error(invalidParamStr);
        return vc.view.recursiveDescription().toString(); 
    };

    // 递归打印最上层UIViewController view的层级结构
    LBFrontVcSubViews = function() {
        return LBVcSubviews(_LBFrontVc(UIApp.keyWindow.rootViewController));
    };

    // 获取按钮绑定的所有TouchUpInside事件的方法名
    LBBtnTouchUpEvent = function(btn) { 
        var events = [];
        var allTargets = btn.allTargets().allObjects()
        var count = allTargets.count;
        for (var i = count - 1; i >= 0; i--) { 
            if (btn != allTargets[i]) {
                var e = [btn actionsForTarget:allTargets[i] forControlEvent:UIControlEventTouchUpInside];
                events.push(e);
            }
        }
       return events;
    };

    // CG函数
    LBPointMake = function(x, y) { 
        return {0 : x, 1 : y}; 
    };

    LBSizeMake = function(w, h) { 
        return {0 : w, 1 : h}; 
    };

    LBRectMake = function(x, y, w, h) { 
        return {0 : LBPointMake(x, y), 1 : LBSizeMake(w, h)}; 
    };

    // 递归打印controller的层级结构
    LBChildVcs = function(vc) {
        if (![vc isKindOfClass:[UIViewController class]]) throw new Error(invalidParamStr);
        return [vc _printHierarchy].toString();
    };

    // 递归打印view的层级结构
    LBSubviews = function(view) { 
        if (![view isKindOfClass:[UIView class]]) throw new Error(invalidParamStr);
        return view.recursiveDescription().toString(); 
    };

    // 判断是否为字符串 "str" @"str"
    LBIsString = function(str) {
        return typeof str == 'string' || str instanceof String;
    };

    // 判断是否为数组 []、@[]
    LBIsArray = function(arr) {
        return arr instanceof Array;
    };

    // 判断是否为数字 666 @666
    LBIsNumber = function(num) {
        return typeof num == 'number' || num instanceof Number;
    };

    var _LBClass = function(className) {
        if (!className) throw new Error(missingParamStr);
        if (LBIsString(className)) {
            return NSClassFromString(className);
        } 
        if (!className) throw new Error(invalidParamStr);
        // 对象或者类
        return className.class();
    };

    // 打印所有的子类
    LBSubclasses = function(className, reg) {
        className = _LBClass(className);

        return [c for each (c in ObjectiveC.classes) 
        if (c != className 
            && class_getSuperclass(c) 
            && [c isSubclassOfClass:className] 
            && (!reg || reg.test(c)))
            ];
    };

    // 打印所有的方法
    var _LBGetMethods = function(className, reg, clazz) {
        className = _LBClass(className);

        var count = new new Type('I');
        var classObj = clazz ? className.constructor : className;
        var methodList = class_copyMethodList(classObj, count);
        var methodsArray = [];
        var methodNamesArray = [];
        for(var i = 0; i < *count; i++) {
            var method = methodList[i];
            var selector = method_getName(method);
            var name = sel_getName(selector);
            if (reg && !reg.test(name)) continue;
            methodsArray.push({
                selector : selector, 
                type : method_getTypeEncoding(method)
            });
            methodNamesArray.push(name);
        }
        free(methodList);
        return [methodsArray, methodNamesArray];
    };

    var _LBMethods = function(className, reg, clazz) {
        return _LBGetMethods(className, reg, clazz)[0];
    };

    // 打印所有的方法名字
    var _LBMethodNames = function(className, reg, clazz) {
        return _LBGetMethods(className, reg, clazz)[1];
    };

    // 打印所有的对象方法
    LBInstanceMethods = function(className, reg) {
        return _LBMethods(className, reg);
    };

    // 打印所有的对象方法名字
    LBInstanceMethodNames = function(className, reg) {
        return _LBMethodNames(className, reg);
    };

    // 打印所有的类方法
    LBClassMethods = function(className, reg) {
        return _LBMethods(className, reg, true);
    };

    // 打印所有的类方法名字
    LBClassMethodNames = function(className, reg) {
        return _LBMethodNames(className, reg, true);
    };

    // 打印所有的成员变量
    LBIvars = function(obj, reg){ 
        if (!obj) throw new Error(missingParamStr);
        var x = {}; 
        for(var i in *obj) { 
            try { 
                var value = (*obj)[i];
                if (reg && !reg.test(i) && !reg.test(value)) continue;
                x[i] = value; 
            } catch(e){} 
        } 
        return x; 
    };

    // 打印所有的成员变量名字
    LBIvarNames = function(obj, reg) {
        if (!obj) throw new Error(missingParamStr);
        var array = [];
        for(var name in *obj) { 
            if (reg && !reg.test(name)) continue;
            array.push(name);
        }
        return array;
    };
})(exports);

将lbtool.cy文件拷贝到iPhone的/usr/lib/cycript0.9目录下

scp /Users/mac/Desktop/lbtool.cy root@192.168.2.6:/usr/lib/cycript0.9
lbtool.cy                                     100% 6178  760.8KB/s   00:00
iOS越狱插件开发教程(hook VIP)-清风博客

lbtool.cy文件用法
用@import + 文件名导入文件

i-57:~ root# cycript -p 11424
cy# @import lbtool
{}
  • 获取当前显示的控制器

cy# LBFrontVc()
#"<ACReaderViewController: 0x1048ae600>"

获取view的层级结构

cy# LBSubviews(LBFrontVc().view)
`<UIView: 0x117c54a40; frame = (0 0; 375 667); autoresize = W+H; layer = <CALayer: 0x283adab00>>
   | <ACReaderEngineView: 0x117c4b850; frame = (0 0; 375 667); autoresize = W+H; gestureRecognizers = <NSArray: 0x283483720>; layer = <CALayer: 0x283acd3c0>>
   |    | <ACZoomScrollView: 0x105b15600; baseClass = UIScrollView; frame = (0 0; 375 667); clipsToBounds = YES; autoresize = W+H; gestureRecognizers = <NSArray: 0x2834be790>; layer = <CALayer: 0x283acd660>; contentOffset: {0, 0}; contentSize: {0, 0}; adjustedContentInset: {0, 0, 0, 0}>
   |    |    | <UICollectionView: 0x105b19600; frame = (0 0; 375 667); clipsToBounds = YES; autoresize = W+H; gestureRecognizers = <NSArray: 0x283491920>; layer = <CALayer: 0x283acdbe0>; contentOffset: {0, 10}; contentSize: {375, 2130}; adjustedContentInset: {0, 0, 0, 0}; layout: <UICollectionViewFlowLayout: 0x11bf46e50>; dataSource: <ACReaderEngineView: 0x117c4b850; frame = (0 0; 375 667); autoresize = W+H; gestureRecognizers = <NSArray: 0x283483720>; layer = <CALayer: 0x283acd3c0>>>

最后小试牛刀把腾讯动漫底部充值view隐藏
iOS越狱插件开发教程(hook VIP)-清风博客
  • 隐藏前

cy# #0x11bfeb950.hidden = YES
true
iOS越狱插件开发教程(hook VIP)-清风博客

温馨提示:本文最后更新于2022-04-14 01:19:05,某些文章具有时效性,若有错误或已失效,请在下方留言或联系清风#
© 版权声明
THE END
文章不错?点个赞呗!
点赞377 分享
评论 抢沙发

请登录后发表评论

    暂无评论内容