iOS逆向(8)-Monkey、Logos

栏目: IOS · 发布时间: 7年前

内容简介:由于最近微信大佬发飙,罚了红包外挂5000万大洋,这就让人很慌了,别说罚我5000万,5000块我都吃不消。所以笔者决定以后不用微信做例子了。换成优酷了:smiling_imp:。本文会对优酷的设置页面增加一个开启/关闭屏蔽广告的Cell(仅UI)。效果可见下文配图。在之前的几篇文章里已经介绍了APP重签名,

由于最近微信大佬发飙,罚了红包外挂5000万大洋,这就让人很慌了,别说罚我5000万,5000块我都吃不消。所以笔者决定以后不用微信做例子了。换成优酷了:smiling_imp:。

本文会对优酷的设置页面增加一个开启/关闭屏蔽广告的Cell(仅UI)。效果可见下文配图。

在之前的几篇文章里已经介绍了APP重签名, 代码注入 ,Hook原理,可以发现,将工程建好,脚本写好,我们就可以以代价非常小的方式对一个第三方的APP进行分析。 那么是否一种工具,可以将重签名,代码注入,Hook源代码,class-dump,Cydia Substrate,甚至是恢复符号表这些功能,集成在一个工程里面,让真正的逆向小白也能享受逆向的乐趣呢?

答案是肯定的, Monkey 就是这样的一个非越狱插件开发集成神器!

老规矩,片头先上福利: 点击下载demo 。 这篇文章会用到的 工具 有:

一、Monkey

什么是Monkey? 原有iOSOpenDev的升级,非越狱插件开发集成神器!

可以使用Xcode开发CaptainHook Tweak、Logos Tweak 和 Command-line Tool,在越狱机器开发插件,这是原来iOSOpenDev功能的迁移和改进。

  • 只需拖入一个砸壳应用,自动集成class-dump、restore-symbol、* Reveal、Cycript和注入的动态库并重签名安装到非越狱机器。
  • 支持调试自己编写的动态库和第三方App
  • 支持通过CocoaPods第三方应用集成SDK以及非越狱插件,简单来说就是通过CocoaPods搭建了一个非越狱插件商店。

1、安装

环境要求

使用工具前确保如下几点:

sudo git clone --recursive https://github.com/theos/theos.git /opt/theos
复制代码
  • 安装ldid(如安装theos过程安装了ldid,跳过)
brew install ldid
复制代码

安装

你可以通过以下命令选择指定的Xcode进行安装:

sudo xcode-select -s /Applications/Xcode-beta.app
复制代码

默认安装的Xcode为:

xcode-select -p
复制代码

执行安装命令:

sudo /bin/sh -c "$(curl -fsSL https://raw.githubusercontent.com/AloneMonkey/MonkeyDev/master/bin/md-install)"
复制代码

卸载

sudo /bin/sh -c "$(curl -fsSL https://raw.githubusercontent.com/AloneMonkey/MonkeyDev/master/bin/md-uninstall)"
复制代码

更新

如果没有发布特殊说明,使用如下命令更新即可:

sudo /bin/sh -c "$(curl -fsSL https://raw.githubusercontent.com/AloneMonkey/MonkeyDev/master/bin/md-update)"
复制代码

安装/更新之后重启下Xcode再新建项目。如果看到如下选项,即代表安装成功,如果没有,重复上面步骤再来一遍。

iOS逆向(8)-Monkey、Logos

具体使用方法可以直接查看 官网 ,当然下文也会对其部分的实用用法进行介绍。

二、Logos

Logos是Thoes开发的一套组件,可非常方便用于的Hook OC代码。

接下来我们就介绍下Logos的简单用法,最后运用Monkey和Logos给优酷增加一点UI。

1、创建一个简单的工程

创建工程 SimpleAppDemo ,里面只有一个按钮,点击按钮弹出一个Alert。 点击下载: SimpleAppDemo 按钮对应的方法为:

- (IBAction)tapAction:(id)sender {
    UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"来啦" message:@"老弟:grin::grin::grin:" delegate:nil cancelButtonTitle:@"我知道了" otherButtonTitles:nil, nil];
    [alert show];
}
复制代码

2、砸壳

对SimpleAppDemo参数的ipa文件进行砸壳,砸壳过程就不在这详细描述了,这里有笔者已经砸壳好的ipa:SimpleAppDemo.ipa 提取码: afnc

3、新建一个Monkey工程

取名 LogosDemo ,将下面下载好的 SimpleAppDemo.ipa ,放到工程对应的目录下:

iOS逆向(8)-Monkey、Logos

配好证书(随意一个能在手机上运行的证书即可),Run。运行成功~

4、玩转Logos

在上一步建好的Monkey工程中,可以发现在目录有一个Logos目录:

iOS逆向(8)-Monkey、Logos

默认有两个文件 LogosDemoDylib.xmLogosDemoDylib.mm

其中Logos语句就是写在 LogosDemoDylib.xm 中的, LogosDemoDylib.mm 是根据 LogosDemoDylib.xm 中的内容自动生成的。

接下来,咱们根据几个需求来介绍Logos的一些常用的用法。

  • 更改点击按钮的弹框内容(hook) 由于需要更改弹窗,所以首先导入 UIKit 框架。
#import <UIKit/UIKit.h>
复制代码

由于咱们手上有源码,所以可以直接跳过动态分析的这一步,直接就知道按钮所处的页面是叫做 ViewController ,按钮的响应方法是:

- (IBAction)tapAction:(id)sender
复制代码

利用 hook 命令:

#import <UIKit/UIKit.h>

// hook + 类名
%hook ViewController
// IBAction == void
- (void)tapAction:(id)sender {
    UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"来什么来" message:@":rage::rage::rage:" delegate:nil cancelButtonTitle:@"我知道了" otherButtonTitles:nil, nil];
    [alert show];
}

%end
复制代码

运行项目,发现按钮已经被成功hook了。

iOS逆向(8)-Monkey、Logos
  • 调用原方法(orig)
#import <UIKit/UIKit.h>

%hook ViewController

- (void)tapAction:(id)sender {
    UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"来什么来" message:@":rage::rage::rage:" delegate:nil cancelButtonTitle:@"我知道了" otherButtonTitles:nil, nil];
    [alert show];
    // 调用原方法
    %orig;
}

%end
复制代码
  • 新增一个方法,并且调用(new) 由于在Monkey工程里面是编译不到源码的,所以无论是新增的方法,还是调用原工程中的方法,都是无法通过编译的,所以都需要使用 interface 申明每一个方法。
#import <UIKit/UIKit.h>

// 这里只是为了申明
@interface ViewController

- (void)newFunC;

@end

%hook ViewController

// 新增方法关键字new
%new
- (void)newFunC{
    NSLog(@"newFunC");
}

// IBAction == void
- (void)tapAction:(id)sender {
    UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"来什么来" message:@":rage::rage::rage:" delegate:nil cancelButtonTitle:@"我知道了" otherButtonTitles:nil, nil];
    [alert show];
    [self newFunC];
    // 调用原方法关键字orig
    %orig;
}

%end
复制代码

文中所有的Demo都在这可以下载到: Dmoe

Logos除了以上 hook , end , orig , new 这几种关键字,还有:

%subclass:增加一个类

%log:打印,类似NSLog

%group: 给代码分组,可以用于在不同环境加载不同的代码,比如iOS8加载 group1 ,iOS9加载 group2 ,如果部分中,默认所有代码在名为「_ungrouped」的隐藏分组中。

...

所有的Logos语法都可以在官方文档中查询得到。

5、给优酷加UI

首先在这里下载笔者自己砸壳后优酷ipa包(arm64架构的):优酷(砸壳).ipa 提取码: xtua

Step 1、新建工程YouKu

同样的新建一个Monkey工程,取名YouKu,将下载好的ipa包放入工程对应的TargetApp目录下。Run。同样是重签名成功。

在上面的Demo中,我们是对我们直接的工程进行HOOK,由于我们手上有源码,所以我们越过了最难的一个步骤:动态分析。

而我们现在要对优酷进行Hook,但我们手上是没有优酷的源码的,所以此时此刻就需要对其进行动态分析了。 下面我将结合Xcode和class dump对优酷的设置页面简单的进行分析。

Step 2、class dump

class-dump is a command-line utility for examining the Objective-C segment of Mach-O files. It generates declarations for the classes, categories and protocols. This is the same information provided by using 'otool -ov', but presented as normal Objective-C declarations.

简单说就是一个可以导出一个MachO文件的所有头文件信息(包括Extension)

在文首有提到Monkey除了重签名,还集成了 class dump 的功能,所以我们需要做的就仅仅是开启这个功能:

iOS逆向(8)-Monkey、Logos

Run!成功之后可以发现在工程目录下多了一个文件夹 Youkui4Phone_Headers ,其中就是优酷的所有的头文件了。

iOS逆向(8)-Monkey、Logos

Step 3、分析优酷设置页面

工程Run成功后,点击进入设置页面(不用登录),如下图:

iOS逆向(8)-Monkey、Logos

我们现在要做的就是在这个页面的TableView的最后一行加上Cell,里面有个Switch,用于打开/关闭屏蔽广告功能(只是UI,这篇文章不牵扯到屏蔽广告的具体实现,如果你需要,点个小心心,持续关注我哦:grinning::grinning::grinning:)。

利用伟大的Xcode我们可以非常清晰的看到,设置页面的 DataSourceDelegate 都是在 SettingViewController 中,

iOS逆向(8)-Monkey、Logos

咱们就找到Hook的类名:SettingViewController 需要Hook的方法自然就是TableView的那些 DataSourceDelegate 了。

这里需要额外提到的一点是,在文章开始的时候就说了Monkey已经将Cydia Substrate集成进去了,所以我们可以直接使用Cydia Substrate的相关功能了。

在这里我们需要拿到这个页面TableView的对应的变量,我们就需要使用到Cydia Substrate的功能了。打开上文中获取到优酷的所有的头文件,所有 SettingViewController ,发现其只有一个TableView变量: _tabview

那么毫无疑问,就是他了!

而获取它的方法是:

MSHookIvar <UITableView *>(self,"_tabview")
复制代码

一个reloadData的简单使用:

[MSHookIvar <UITableView *>(self,"_tabview") reloadData];
复制代码

其他的UI代码在这里就不一一解释了,全部代码如下,当然在 Demo 中也是有的,其中包括了数据的简单持久化功能:

#import <UIKit/UIKit.h>
#define FYDefaults  [NSUserDefaults standardUserDefaults]
#define FYSwitchUserDefaultsKey @"FYSwitchUserDefaultsKey"

@interface SettingViewController
- (long long)numberOfSectionsInTableView:(id)arg1;
@end

%hook SettingViewController

%new
-(void)switchChangeAction:(UISwitch *)switchView{
    [FYDefaults setBool:switchView.isOn forKey:FYSwitchUserDefaultsKey];
    [FYDefaults synchronize];
    [MSHookIvar <UITableView *>(self,"_tabview") reloadData];
}

//多少组
- (long long)numberOfSectionsInTableView:(id)arg1{
    UITableView * tableView = MSHookIvar <UITableView *>(self,"_tabview");
    NSLog(@"fy_numberOfSectionsInTableView:");
    // 额外增加一个
    return %orig+1;
}

//每组多少行
- (long long)tableView:(UITableView *)tableView numberOfRowsInSection:(long long)section{
    NSLog(@"fy_numberOfRowsInSection:");
    //定位设置界面,并且是最后一个
    if(section == [self numberOfSectionsInTableView:tableView]-1){
        return 1;
    }
    else{
        return %orig;
    }
}

//返回高度
- (double)tableView:
(UITableView *)tableView heightForRowAtIndexPath:(id)indexPath{
    NSLog(@"fy_heightForRowAtIndexPath:");
    //定位设置界面,并且是最后一个
    if([indexPath section] ==[self numberOfSectionsInTableView:tableView]-1){
        return 44;
    }
    else{
        return %orig;
    }
}


//每一个Cell
- (id)tableView:(UITableView *)tableView cellForRowAtIndexPath:(id)indexPath{
    NSLog(@"fy_cellForRowAtIndexPath:");
    //定位设置界面,并且是最后一组
    if([indexPath section] == [self numberOfSectionsInTableView:tableView]-1){
        UITableViewCell * cell = nil;
        if([indexPath row] == 0){
            static NSString *swCell = @"SwCellIdentifier";
            cell = [tableView dequeueReusableCellWithIdentifier:swCell];
            if(!cell){
                cell = [[UITableViewCell alloc] initWithStyle:(UITableViewCellStyleDefault) reuseIdentifier:nil];
            }
            cell.textLabel.text = @"免广告";
            // 免广告开关
            UISwitch *switchView = [[UISwitch alloc] init];
            switchView.on = [FYDefaults boolForKey:FYSwitchUserDefaultsKey];
            [switchView addTarget:self action:@selector(switchChangeAction:) forControlEvents:(UIControlEventValueChanged)];
            cell.accessoryView = switchView;
            cell.imageView.image = [UIImage imageNamed:([FYDefaults boolForKey:FYSwitchUserDefaultsKey] == 1) ? @"unlocked" : @"locked"];
        }
        cell.backgroundColor = [UIColor whiteColor];
        return cell;
        
    }else{
        return %orig;
    }
}

%end

复制代码

最后的效果

iOS逆向(8)-Monkey、Logos

6、为什么Monkey这么牛逼

查看重新编译后的app文件,可以发现其中的Framework多了很多东西:

iOS逆向(8)-Monkey、Logos

从这可以得知,原来Monkey其实也是通过将诸多的动态库(包括自己的工程)注入的形式,实现了这些功能。

三、总结

在这片文章中主要介绍了Monkey的一些用法已经Logos的基本语法。而在上一篇其实留了一个小尾巴,就是Cycript,笔者将要在下一篇文章中重点讲解Cycript的安装,基础用法和高级用法。之所以放在下一篇,是因为Cycript配合Monkey将会有事半功倍的效果。


以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网

查看所有标签

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Using Google App Engine

Using Google App Engine

Charles Severance / O'Reilly Media / 2009-5-23 / USD 29.99

With this book, you can build exciting, scalable web applications quickly and confidently, using Google App Engine - even if you have little or no experience in programming or web development. App Eng......一起来看看 《Using Google App Engine》 这本书的介绍吧!

XML、JSON 在线转换
XML、JSON 在线转换

在线XML、JSON转换工具

UNIX 时间戳转换
UNIX 时间戳转换

UNIX 时间戳转换

RGB CMYK 转换工具
RGB CMYK 转换工具

RGB CMYK 互转工具