21.Tweak原理&防护.md

[TOC]

修改系统应用

目标:消除对手机桌面的提醒气泡

  • 通过cycript分析气泡

    • 连接手机并登陆,通过进程列表指令ps -A查看springboard在系统的路径,得到结果:System/Library/CoreServices/SpringBoard.app/SpringBoard,在该目录下找到springboardmacho文件(未进行加密),
    • 终端进行Mac桌面,将上述MachO拷贝至桌面,可以使用IFunBox和指令,指令:$scp -r -P 12345 root@localhost:/System/Library/CoreServices/SpringBoard.app/SpringBoard ~/Desktop,通过file SpringBoard指令确认SpringBoard: Mach-O 64-bit executable arm64,通过otool -l SpringBoard | grep c查看加密信息,并未查找到crypt字段;
    • 通过class-dump导出SpringBoard的头文件;
    • 进入springboard应用进程cycript -p SpringBoard,在cy环境中导入自定义cy文件@import cmds,获取当前的VC,递归打印页面元素,并未找到相关可疑视图。
    • 向下一响应者推进,打印KeyWindow的视图层级,找到可疑类型:SBIconViewSBIconParallaxBadgeView,通过对视图进行隐藏显示操作可以得到SBIconParallaxBadgeView即是气泡视图类;
  • 编写Tweak代码

    • 创建Tweak工程,指定工程名、包名、作者名、依附进程APPID;
    • 配置Tweak安装地址和端口:THEOS_DEVICE_IPTHEOS_DEVICE_PORT;
    • 查看SBIconParallaxBadgeView头文件可知,其内部实现了-init方法,所以直接通过暴力方式对-init进行hook,并返回nil;
    • 对工程执行makemake packagemake install,将插件安装到手机重新SpringBoard即可将气泡消失;

    在调试过程中,springBoard的KeyWindow可能随着锁屏之类的操作,发生变化,所以最好不要在调试操作中有其他页面出现。

    Tweak在每次编译后都会重新生成一个安装包,版本也会随之增加,当低版本覆盖高版本时安装将会报错。

Tweak原理

工作步骤

  1. 通过Theos工具创建一个Tweak项目,在项目中编写logos代码;
  2. 代码完成后,通过make工具将.xm文件编译生成目标动态库文件:./.theos/obj/debug/xxx.dylib
  3. 通过make package指令对xxx.dylib以及xxx.plist进行打包,生成.deb包;
  4. 通过make install.deb拷贝到手机并进行解压,将 xxx.dylib拷贝到 /Library/MobileSubstrate/DynamicLibraries路径下,完成安装;
  5. 重启Springboard程序,附加xxx.plist所指向的进程进行hook;

原理即生效过程

在将插件安装到手机后,重启Springboard后,再次启动hook的目标程序Cydia将扫描DynamicLibraries目录下的plist列表,如果重启的程序的包名包含在plist列表中,那么将通过DYLD_INSERT_LIBRIRES方式将动态库依附到目标程序上,然后通过hook功能将原方法重新指向到hook方法上。

大致流程(从安装插件后):DYLD_INSERT_LIBRIRES ---->fishhook/method_swlizze;

实例:1. 写一个正向小target程序在touchBegin:中打印一句话,运行工程,2. 然后新建一个dylib的工程,在+load方法中对touchBegin方法进行methodSwlizze,打印另外一句话,3. 将dylib拷贝到手机,通过DYLD_INSERT_LIBRIRES将dylib依附到target进程,再次点击,4. 查看前后2次的差别。

DYLD_INSERT_LIBRARIES源码分析

DYLD是一个可执行应用程序,他的源码苹果开源公布在:Source Code

文章 防止tweak依附,App有高招;破解App保护,tweak留一手中,提及的美团能够强力阻止各种dylib的注入,使得一切tweak均为狗比。其防止注入的方式在DYLD的源码中能找到相应的体现。

阅读源码后,分析DYLD的加载流程:在dyld-519.2.2版中,

  1. 在5906行开始,加载任何库

    // load any inserted libraries
    if   ( sEnv.DYLD_INSERT_LIBRARIES != NULL ) {
        for (const char* const* lib = sEnv.DYLD_INSERT_LIBRARIES; *lib != NULL; ++lib) 
            loadInsertedDylib(*lib);
    }
    
  2. 在第一步前的还有一个控制流程,在5691行:

    if ( gLinkContext.processIsRestricted ) {
        pruneEnvironmentVariables(envp, &apple);
        // set again because envp and apple may have changed or moved
        setContext(mainExecutableMH, argc, argv, envp, apple);
    }    
    

    该判断会根据gLinkContext.processIsRestricted中的值决定是否继续加载动态库内容。

  3. 而控制gLinkContext.processIsRestricted是否为真的语句,在4696行:

    if ( issetugid() || hasRestrictedSegment(mainExecutableMH) ) {
     gLinkContext.processIsRestricted = true;
    }
    

    该部分表明,issetugid() || hasRestrictedSegment(mainExecutableMH)其中一个为真即可满足条件,而上架的应用中issetugid()是禁止使用的,所以hasRestrictedSegment变得关键。

  4. 通过搜索hasRestrictedSegment可以找到对应的函数,第4293行:

static bool hasRestrictedSegment(const macho_header* mh)
{
    const uint32_t cmd_count = mh->ncmds;
    const struct load_command* const cmds = (struct load_command*)(((char*)mh)+sizeof(macho_header));
    const struct load_command* cmd = cmds;
    for (uint32_t i = 0; i < cmd_count; ++i) {
        switch (cmd->cmd) {
            case LC_SEGMENT_COMMAND:
            {
                const struct macho_segment_command* seg = (struct macho_segment_command*)cmd;
                
                //dyld::log("seg name: %s\n", seg->segname);
                if (strcmp(seg->segname, "__RESTRICT") == 0) {
                    const struct macho_section* const sectionsStart = (struct macho_section*)((char*)seg + sizeof(struct macho_segment_command));
                    const struct macho_section* const sectionsEnd = &sectionsStart[seg->nsects];
                    for (const struct macho_section* sect=sectionsStart; sect < sectionsEnd; ++sect) {
                        if (strcmp(sect->sectname, "__restrict") == 0) 
                            return true;
                    }
                }
            }
            break;
        }
        cmd = (const struct load_command*)(((char*)cmd)+cmd->cmdsize);
    }
        
    return false;
}

该函数表明,只要macho文件的Header中有名为__RESTRICT的Segment段、且该段有名为__restrict的sections,则macho就会阻止进程的依附。

修改RESTRICT段防护注入

为工程的Other Link Flags添加 -Wl -seccreate __RESTRICT __restrict /dev/null内容即可在macho中添加 __RESTRICT __restrict,即可以使hasRestrictedSegment函数的返回值为true,从而达到防止Tweak的注入问题。

Snip20180626_2.png

但是测试时发现,添加了该段数据后,在自己工程中添加关联的动态库好像也没法正常在注入了。所以将依赖库的MachOType修改为Static Library,也是正常加载。

Snip20180626_1.png

  • 实例:编写一个Tweak项目,安装到越狱手机,查看Tweak是否依附成功。

破坏防护

利用二进制修改器将MachO中的__restrict进行篡改,篡改后APP使用MonkeyDev进行重签名,就可以再次对进程进行动态库注入依附了。

利用源码防止MachO被修改

将Mac平台的hasRestrictedSegment函数拷贝出来,在项目中使用该函数来判断指定段内容是否与期望值一致,否则表示MachO已经被修改了。

在拷贝出来的代码中会有很多未定义的头文件,需要导入<mach-o/loader.h><mach-o/dyld.h>

在loader中有mach_header[_64]的结构体,表示32、64位的macho的Header。在dyld中有mach_header* _dyld_get_image_header(uint32_t image_index) 函数,该函数通过index获取指定macho的Header,当传入index为0时即是当前启动的App的可执行程序(DYLD启动App时,首先加载的是App的可执行文件(可以通过LLDB:image list指令查看脚标进行验证))。

导入头文件后需要对部分宏定义进行定义,从dyld.cpp文件中可以拷贝出来,包括LC_SEGMENT_COMMANDmach_header[_64]等。

将该函数调试完成后,就可以在+load中进行调用并传入_dyld_get_image_header获取的macho_header,验证当前macho的段内容是否与期望的内容一致。

总结

  1. 攻:一般裸奔环境中可以使用DYLD_INSERT_LIBRARIES进行动态库临时注入;
  2. 防:通过添加__RESTRICT段可以起到防护进程依附的问题;
  3. 攻:对加有__RESTRICT字段的防御手段,通过二进制修改该字段的内容并进行代码重签名就可以做到再次依附的效果,如monkeyDev。
  4. 防:使用DYLD中hasRestrictedSegment函数对macho中的__RESTRICT内容进行检测,如果与预期值不同就可以主动退出应用。

花絮

关于ldid

ldid的描述:为执行文件添加签名,以便在手机上进行运行,在操作keychain_dumper时出现killed:9的问题的修复,参见:iPhone: keychain dumper – killed 9 problem,其中的重点是:

On the newer versions of iOS (v5) running this tool ends up with killed 9 error.

The problem here is iOS kernel signature checks the binary file at several places. Jailbreak tools cannot patch all these signature checks because it is difficult for them to patch each and every signature check. When we copy keychaindumper to iPhone, it does not have a signature. So running the binary exits with killed 9 message because iOS kernel does not have the signature of keychain_dumper.

To get rid of this problem add the signature of keychain_dumper to kernel cache (list of hashes) by running the below command. After adding the signature, you can run the rest of commands to dump the keychain entries.

ldid -S keychain_dumper

Tweak原理其他解释

iOS逆向之二-一个简单的Tweak插件原理解析:重点

  1. 输入项目名称、项目包名,作者,需要注入的宿主程序包名和宿主进程名称

    Project Name (required): FirstReProj        #项目名
    Package Name [com.yourcompany.firstreproj]: com.zyt.firstreproj        #项目包名
    Author/Maintainer Name [aron]: zyt        #作者
    [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 firstreproj/...
    Done.
    
  2. 工程目录:

    FirstReProj.plist    - 注入的宿主程序包名配置文件 
    Makefile    -make文件 
    Tweak.xm    -源码文件,xm格式文件支持c/oc/logo语法,x格式支持logo语法 
    control    -控制文件,保存项目的配置信息
    
  3. 原理:

    (1) Cydia Substrate 越狱机器插件、软件运行的基础依赖包,提供动态注入的功能 Sbustrate 主要由三部分组成:MobileHooker,MobileLoader,safe mode
    
    (1.1) MobileHooker 用于替换系统的方法,这个过程称为Hooking
    有两个有 Cydia Substrate 框架提供的方法
    
    MSHookMessageEx 用于注入OC函数
    函数原型 void MSHookMessageEx(Class _class, SEL message, IMP hook, IMP *old)
    
    MSHookFunction 用于注入c/c++函数
    
    Logos语法中的 %hook 就是对此函数的封装,代码更简洁、直观
    
    (1.2) MobileLoader
    将tweak插件注入到第三方应用程序中(动态注入:ptrace)
    MobileLoader 查找 /Library/MobileSubstrate/DynamicLibraries 下 plist 文件指定的作用范围,把对应的dylib动态的注入到进程中
    
    safe mode : 不加载第三方驱动,只加载系统自带的驱动
    安装了第三方插件导致系统的在启动的时候导致桌面的崩溃,会启动 safe mode,不会重新加载桌面
    *可以ssh到iPhone,使用dpkg命令删除Tweak插件包
    
    dpkg -r com.zyt.reapp 
    
  4. DEB包解析

    使用make package 会在项目目录下面生成package文件夹,里面包含了deb包, DEB包本质上就是一种特殊格式的压缩包,解压里面的内容,可以看到Library的目录结构和安装到iPhone上对应文件目录结构是一致的,是一种映射关系,简单来说,安装deb包就是一个解压操作而已,把deb包中的内容解压到iPhone就完成了安装流程。
    

坑爹的打印

下面这段logos截图的源码在被我复制到md之前被我折腾了一个小时。一直报错<compose failure [invalid utf8]>,就是打印不出来。

源码截图:

Snip20180626_5.png

从截图看上去没有一点问题,只要有和打印后跟的内容都打印不来,比如尝试了打印没打印了都打印不出来,本来想记一下logos中是不是奇葩得不支持打印这个中文符号,结果复制粘贴到.md才发现下面的问题。

源码如下:

#import <UIKit/UIKit.h>
%hook ViewController
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
    // NSLog(@"biabsd:%@", @"打打分");
    // NSLog(@"sdfrie:%@", @"被hook后打印没有点到我,被hook了");
    // NSLog(@"当前输入密码:");
    // NSLog(@"当前输入密码:%@", touches);

    NSLog(@"打印了");
    // %orig;
}
%end
image.png

在复制到.md中才发现有一红点,输入法是没法打出来的,简书也没显示出来。那这个红点是哪里来的呢?

这段文字本来是被放在xib的label.text中的,在打印后面整好用alt+enter强行换了行,结果粘贴过来的时候格式依然还在,但是sublime却没显示出来,而是在一行中,当然.md 也没也换行显示。。。就这样一直用了一个小时才觉得是logos奇葩不支持打印这样的中文符,结果是有特殊的换行符,NSLog不支持这样的特殊换行符。。。。

泪奔啊,,-_-||😂😂

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,937评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,503评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,712评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,668评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,677评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,601评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,975评论 3 396
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,637评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,881评论 1 298
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,621评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,710评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,387评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,971评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,947评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,189评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,805评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,449评论 2 342

推荐阅读更多精彩内容