iOS逆向?qū)崙?zhàn)--032:越獄防護(hù)

Tweak原理

執(zhí)行make命令時(shí),在.theos的隱藏目錄中,編譯出obj/debug目錄,包含arm64、armv7兩種架構(gòu),同時(shí)生成RedDemo.dylib動(dòng)態(tài)庫(kù)

arm64armv7目錄中,有各自架構(gòu)的RedDemo.dylib,而debug目錄中的RedDemo.dylib,是一個(gè)Fat Binary文件

file RedDemo.dylib
-------------------------
RedDemo.dylib: Mach-O universal binary with 2 architectures: [arm_v7:Mach-O dynamically linked shared library arm_v7] [arm64:Mach-O 64-bit dynamically linked shared library arm64]
RedDemo.dylib (for architecture armv7):    Mach-O dynamically linked shared library arm_v7
RedDemo.dylib (for architecture arm64):    Mach-O 64-bit dynamically linked shared library arm64

Tweak的編譯產(chǎn)物是動(dòng)態(tài)庫(kù),將其注入的方式有兩種:

  • 修改MachO文件的Load Commands,注入LC_LOAD_DYLIB (XXX),然后根據(jù)路徑找到動(dòng)態(tài)庫(kù)。這種方式對(duì)程序的污染比較嚴(yán)重,容易被開發(fā)者檢測(cè)出來
  • 通過DYLD_INSERT_LIBRARIES環(huán)境變量,插入動(dòng)態(tài)庫(kù)

Tweak插件,使用的是方式二,因?yàn)槌绦驔]有被污染。在MachO中,并沒有找到LC_LOAD_DYLIB (XXX)

執(zhí)行make package命令時(shí),在packages目錄中,生成.deb文件。每執(zhí)行一次打包命令,都會(huì)生成一個(gè)新的.deb文件

.deb格式類似于.ipa格式

  • .ipa包通過AppStore安裝,將.ipa包中的App安裝到設(shè)備中
  • .deb包通過Cydia安裝,將.deb包中的動(dòng)態(tài)庫(kù)安裝到設(shè)備中

執(zhí)行make install命令時(shí),在.deb包中的動(dòng)態(tài)庫(kù),會(huì)被安裝到設(shè)備的/Library/MobileSubstrate/DynamicLibraries目錄中

以相同的名稱,分別存儲(chǔ).dylib.plist文件

.dylib為動(dòng)態(tài)庫(kù),而.plist,記錄.dylib所依附的App包名

DYLD_INSERT_LIBRARIES

在早期的dyld源碼中,有進(jìn)程限制的判斷。一旦符合條件,使用DYLD_INSERT_LIBRARIES環(huán)境變量插入的動(dòng)態(tài)庫(kù)將被清空

打開dyld-519.2.2源碼

搜索DYLD_INSERT_LIBRARIES

進(jìn)入dyld.cpp文件,來到5907

  • DYLD_INSERT_LIBRARIESNULL的判斷

這段代碼的上面,來到5692

  • 判斷進(jìn)程限制
  • 符合條件,調(diào)用pruneEnvironmentVariables方法,清空插入的動(dòng)態(tài)庫(kù)

一旦插入的動(dòng)態(tài)庫(kù)被清空,意味著越獄插件將會(huì)全部失效。如果我們找到進(jìn)程限制的開啟條件,并將其使用在項(xiàng)目中,相當(dāng)于對(duì)越獄插件進(jìn)行了防護(hù)

找到processIsRestricted設(shè)置為true的代碼

  • 判斷條件有兩個(gè),分別是issetugidhasRestrictedSegment兩個(gè)函數(shù)
  • issetugid函數(shù),無法在上架的App中設(shè)置,放棄使用
  • hasRestrictedSegment函數(shù),判斷主程序的MachO是否受限,可以使用

進(jìn)入hasRestrictedSegment函數(shù)

  • 傳入主程序的Header
  • 讀取segment,如果為__RESTRICT
  • 讀取section,如果為__restrict節(jié)
  • 如果都存在,返回trur,表示進(jìn)程限制
__RESTRICT段防護(hù)

在項(xiàng)目中,添加__RESTRICT段,__restrict節(jié),開啟進(jìn)程限制,對(duì)越獄插件進(jìn)行防護(hù)

搭建App項(xiàng)目,命名:antiTweak

打開ViewController.m文件,寫入以下代碼:

- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
   exit(0);
}

進(jìn)程限制,是早期dyld源碼中的邏輯,在低系統(tǒng)下才能生效

使用iOS9.1系統(tǒng)運(yùn)行項(xiàng)目,點(diǎn)擊屏幕就會(huì)閃退

搭建Tweak插件,附加antiTweak應(yīng)用

打開Tweak.x文件,寫入以下代碼:

#import <UIKit/UIKit.h>

%hook ViewController

-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
   NSLog(@"??????????");
}

%end

安裝插件,啟動(dòng)應(yīng)用,touchesBegan方法被插件HOOK。點(diǎn)擊屏幕,閃退變?yōu)榇蛴?/p>

antiTweak項(xiàng)目,添加__RESTRICT段,__restrict節(jié)

Build SettingOther Linker Flags中,加入以下設(shè)置:

-Wl,-sectcreate,__RESTRICT,__restrict,/dev/null

編譯項(xiàng)目,查看MachO文件

  • 成功插入__RESTRICT段,__restrict節(jié)

運(yùn)行項(xiàng)目,點(diǎn)擊屏幕閃退。說明插入的動(dòng)態(tài)庫(kù)已被清空,越獄插件全部失效

這種防護(hù)手段,在早期系統(tǒng)中比較有效。但在iOS11及更高系統(tǒng)中,dyld源碼發(fā)生變化,這種方式已失去作用

修改MachO破解

在老系統(tǒng)的越獄設(shè)備上,遇到使用此方式防護(hù)的應(yīng)用,導(dǎo)致我們的越獄插件無法使用,可以通過修改MachO文件破解防護(hù)

使用MachOView打開MachO文件

修改Data值,將72改為73,52改為53。只在以前的數(shù)值上替換,位數(shù)不要改變

當(dāng)MachO文件修改后,使用重簽名安裝應(yīng)用,此時(shí)__RESTRICT段和__restrict節(jié)已經(jīng)不存在了,進(jìn)程限制不會(huì)啟動(dòng),越獄插件可正常使用

使用dyld源碼防護(hù)

如果是自己的App,我們開啟了進(jìn)程限制,如何禁止攻擊者的肆意修改呢?

借鑒dyld的代碼,循環(huán)讀取segmentsection,如果缺少__RESTRICT段或__restrict節(jié),說明我們的防護(hù)代碼被人篡改

延用antiTweak項(xiàng)目,將dyld中的代碼遷移到項(xiàng)目中

打開ViewController.m文件,寫入以下代碼:

導(dǎo)入頭文件

#import <mach-o/loader.h>
#import <mach-o/dyld.h>

添加宏定義

#if __LP64__
   #define macho_header              mach_header_64
   #define LC_SEGMENT_COMMAND        LC_SEGMENT_64
   #define LC_SEGMENT_COMMAND_WRONG LC_SEGMENT
   #define LC_ENCRYPT_COMMAND        LC_ENCRYPTION_INFO
   #define macho_segment_command    segment_command_64
   #define macho_section            section_64
#else
   #define macho_header              mach_header
   #define LC_SEGMENT_COMMAND        LC_SEGMENT
   #define LC_SEGMENT_COMMAND_WRONG LC_SEGMENT_64
   #define LC_ENCRYPT_COMMAND        LC_ENCRYPTION_INFO_64
   #define macho_segment_command    segment_command
   #define macho_section            section
#endif

添加hasRestrictedSegment函數(shù),循環(huán)讀取segmentsection。如果缺少__RESTRICT段或__restrict節(jié),返回false

static bool hasRestrictedSegment(const struct macho_header* mh)
{
   const uint32_t cmd_count = mh->ncmds;
   const struct load_command* const cmds = (struct load_command*)(((char*)mh)+sizeof(struct 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;

               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;
}

加入load方法,調(diào)用防護(hù)代碼

+(void)load{
   
   struct macho_header* mhmh= _dyld_get_image_header(0);
   
   if(hasRestrictedSegment(mhmh)){        
       NSLog(@"防護(hù)代碼有效");
   }
   else{       
       NSLog(@"被篡改");
   }
}

修改Other Linker Flags中的配置,模擬MachO被篡改

-Wl,-sectcreate,__SESTRICT,__sestrict,/dev/null

運(yùn)行項(xiàng)目,輸出以下結(jié)果:

antiTweak[2535:549785] 被篡改

當(dāng)檢測(cè)到MachO被篡改,不要使用痕跡明顯的代碼進(jìn)行防護(hù),例如:exit(0)。此類代碼相當(dāng)于記號(hào),讓攻擊者很容易找到防護(hù)的位置和邏輯

高明的防護(hù)手段,應(yīng)該讓攻擊者不易察覺,在不知不覺中被系統(tǒng)屏蔽封殺

白名單檢測(cè)

進(jìn)程限制的防護(hù)手段,僅低版本系統(tǒng)有效。對(duì)于高版本系統(tǒng)的防護(hù),我們可以自制白名單進(jìn)行檢測(cè)

延用antiTweak項(xiàng)目

整理出App依賴庫(kù)的白名單

打開ViewController.m文件,寫入以下代碼:

#import "ViewController.h"
#import <mach-o/loader.h>
#import <mach-o/dyld.h>

@implementation ViewController

+(void)load{
   uint32_t intCount = _dyld_image_count();
   
   for (int intIndex=0; intIndex<intCount; intIndex++) {
       const char* strName = _dyld_get_image_name(intIndex);
       printf("%s",strName);
   }
}

@end

在未越獄的設(shè)備上,運(yùn)行項(xiàng)目,遍歷所有image名稱

打印結(jié)果,相當(dāng)于一份白名單。如果App運(yùn)行時(shí),加載了白名單以外的動(dòng)態(tài)庫(kù),該庫(kù)很可能是被第三方注入的

檢測(cè)注入的動(dòng)態(tài)庫(kù)

打開ViewController.m文件,寫入以下代碼:

#import "ViewController.h"
#import <mach-o/loader.h>
#import <mach-o/dyld.h>

const char* strList = "/private/var/containers/Bundle/Application/E7D8C05C-D581-463F-96AC-791B816265C6/antiTweak...";

@implementation ViewController

+(void)load{
   uint32_t intCount = _dyld_image_count();
   
   for (int intIndex=0; intIndex<intCount; intIndex++) {
       
       const char* strName = _dyld_get_image_name(intIndex);
       
       if(intIndex==0 || strstr(strList, strName)){
           continue;
       }
       
       printf("注入動(dòng)態(tài)庫(kù):%s\n",strName);
   }
}

@end

load方法中,循環(huán)遍歷依賴的動(dòng)態(tài)庫(kù)。如果動(dòng)態(tài)庫(kù)不是當(dāng)前MachO文件,或者包含白名單中,屬于合法庫(kù),直接跳過。否則,將其打印

當(dāng)前MachO文件,不需要判斷,因?yàn)樯澈新窂綗o法固定

在越獄設(shè)備上運(yùn)行項(xiàng)目,輸出很多白名單以外的動(dòng)態(tài)庫(kù),其中包含自制的antiTweakDemo插件

使用此方法進(jìn)行防護(hù),需要注意以下幾點(diǎn):

  • 在不同系統(tǒng)下運(yùn)行項(xiàng)目,整理出盡可能完善的白名單
  • 檢測(cè)到白名單以外的動(dòng)態(tài)庫(kù),不要直接處理。這里建議先收集數(shù)據(jù),如果此動(dòng)態(tài)庫(kù)是我們?nèi)甭┑?,將其補(bǔ)充到白名單中。如果確認(rèn)是惡意注入,再做處理
  • 白名單列表,由服務(wù)端下發(fā),或者將邏輯直接做到服務(wù)端

白名單寫在客戶端的弊端:

  • 白名單的字符串,位于MachO的常量區(qū),容易被攻擊者發(fā)現(xiàn)并HOOK
  • 當(dāng)系統(tǒng)更新,可能會(huì)出現(xiàn)白名單以外的依賴庫(kù),老版本App將無法使用
ptrace

App可以被lldb動(dòng)態(tài)調(diào)試,因?yàn)?code>App被設(shè)備中的debugserver附加,它會(huì)跟蹤我們的應(yīng)用進(jìn)程(trace process),而這一過程利用的就是ptrace函數(shù)

ptrace是系統(tǒng)內(nèi)核函數(shù),它可以決定應(yīng)用能否被debugserver附加。如果我們?cè)陧?xiàng)目中,調(diào)用ptrace函數(shù),將程序設(shè)置為拒絕附加,即可對(duì)lldb動(dòng)態(tài)調(diào)試進(jìn)行有效的防護(hù)

ptraceiOS系統(tǒng)中,無法直接使用,需要導(dǎo)入頭文件

ptrace函數(shù)的定義:

int ptrace(int _request, pid_t _pid, caddr_t _addr, int _data);
  • request:請(qǐng)求ptrace執(zhí)行的操作
  • pid:目標(biāo)進(jìn)程的ID
  • addr:目標(biāo)進(jìn)程的地址值,和request參數(shù)有關(guān)
  • data:根據(jù)request的不同而變化。如果需要向目標(biāo)進(jìn)程中寫入數(shù)據(jù),data存放的是需要寫入的數(shù)據(jù)。如果從目標(biāo)進(jìn)程中讀數(shù)據(jù),data將存放返回的數(shù)據(jù)

搭建App項(xiàng)目,命名:antiDebug

導(dǎo)入MyPtraceHeader.h頭文件

打開ViewController.m文件,寫入以下代碼:

#import "ViewController.h"
#import "MyPtraceHeader.h"

@implementation ViewController

- (void)viewDidLoad {
   [super viewDidLoad];
   ptrace(PT_DENY_ATTACH, 0, 0, 0);
}

@end

使用Xcode運(yùn)行項(xiàng)目,啟動(dòng)后立即退出。使用ptrace設(shè)置為拒絕附加,只能手動(dòng)啟動(dòng)App

也就是說,用戶在使用App時(shí),不會(huì)有任何影響。一旦被debugserver附加,就會(huì)閃退

如果在越獄環(huán)境,手動(dòng)對(duì)App進(jìn)行debugserver附加呢?

找到antiDebug進(jìn)程

ps -A | grep antiDebug
-------------------------
12233 ??         0:00.27 /var/containers/Bundle/Application/5DC00A3B-C095-46D1-9842-A3C35401DD07/antiDebug.app/antiDebug

手動(dòng)對(duì)App進(jìn)行debugserver附加

debugserver localhost:12346 -a 12233
-------------------------
debugserver-@(#)PROGRAM:LLDB  PROJECT:lldb-900.3.87
for arm64.
Attaching to process 12233...
Segmentation fault: 11

同樣附加失敗,無論以何種方式,都會(huì)被ptrace函數(shù)阻止

破解ptrace

ptrace是系統(tǒng)內(nèi)核函數(shù),被開發(fā)者所熟知。ptrace的防護(hù)痕跡也很明顯,手動(dòng)運(yùn)行程序正常,Xcode運(yùn)行程序閃退

我們?cè)谀嫦蛞豢?code>App時(shí),遇到上述情況,第一時(shí)間就會(huì)想到ptrace防護(hù)

由于ptrace是系統(tǒng)函數(shù),需要間接符號(hào)表,我們可以試探性的下一個(gè)ptrace的符號(hào)斷點(diǎn)

ptrace的斷點(diǎn)命中,我們確定了對(duì)方的防護(hù)手段,想要破解并非難事

延用antiDebug項(xiàng)目,模擬應(yīng)用重簽名,注入動(dòng)態(tài)庫(kù)

創(chuàng)建Inject動(dòng)態(tài)庫(kù),創(chuàng)建InjectCode

Inject動(dòng)態(tài)庫(kù)中,導(dǎo)入fishhook,導(dǎo)入MyPtraceHeader.h頭文件

打開InjectCode.m文件,寫入以下代碼:

#import "InjectCode.h"
#import "MyPtraceHeader.h"
#import "fishhook.h"

@implementation InjectCode

+(void)load{
   
   struct rebinding reb;
   reb.name="ptrace";
   reb.replacement=my_ptrace;
   reb.replaced=(void *)&sys_ptrace;
   
   struct rebinding rebs[]={reb};
   rebind_symbols(rebs, 1);
}

int (*sys_ptrace)(int _request, pid_t _pid, caddr_t _addr, int _data);

int my_ptrace(int _request, pid_t _pid, caddr_t _addr, int _data){
   
   if(_request==PT_DENY_ATTACH){
       return 0;
   }
   
   return sys_ptrace(_request, _pid, _addr, _data);
}

@end

ptrace_my函數(shù)中,如果是PT_DENY_ATTACH枚舉值,直接返回。如果是其他類型,系統(tǒng)有特定的作用,需要執(zhí)行ptrace原始函數(shù)

運(yùn)行項(xiàng)目,進(jìn)入lldb動(dòng)態(tài)調(diào)試,ptrace破解成功

總結(jié)

Tweak原理

  • Tweak編譯產(chǎn)物是動(dòng)態(tài)庫(kù)
  • 打包時(shí),將動(dòng)態(tài)庫(kù)打包成.deb格式
  • 插件安裝到/Library/MobileSubstrate/DynamicLibraries目錄中
    ? 安裝.dylib.plist文件
    ? .plist記錄.dylib所依附的App包名
  • Tweak插件使用DYLD_INSERT_LIBRARIES方式,插入動(dòng)態(tài)庫(kù)

DYLD_INSERT_LIBRARIES

  • 早期dyld源碼中,有進(jìn)程限制的判斷(processIsRestricted
  • 啟用進(jìn)程限制,segment存在__RESTRICT段,section存在__restrict節(jié)
  • 符合進(jìn)程限制的條件,清空插入動(dòng)態(tài)庫(kù),越獄插件失效

__RESTRICT段防護(hù)

  • Build SettingOther Linker Flags中配置
    ? -Wl,-sectcreate,__RESTRICT,__restrict,/dev/null
  • iOS11及更高系統(tǒng),此防護(hù)無效

修改MachO破解

  • 使用MachOView打開MachO文件,修改Data
  • 只在以前的數(shù)值上替換,不要對(duì)其增減,位數(shù)不要改變

使用dyld源碼防護(hù)

  • 借鑒dyld源碼,讀取segmentsection。如果缺少__RESTRICT段或__restrict節(jié),說明我們的防護(hù)代碼被人篡改
  • 檢測(cè)到程序被篡改,不要使用痕跡明顯的代碼進(jìn)行防護(hù),容易暴露
  • 盡量讓攻擊者在不知不覺中被系統(tǒng)屏蔽封殺

白名單檢測(cè)

  • 遍歷image名稱
    ? _dyld_image_count()
    ? _dyld_get_image_name(i)
  • 在不同系統(tǒng)下運(yùn)行項(xiàng)目,整理出盡可能完善的白名單
  • 檢測(cè)到白名單以外的動(dòng)態(tài)庫(kù),不要直接處理
  • 白名單列表,由服務(wù)端下發(fā),或者將邏輯直接做到服務(wù)端

ptrace

  • 可阻止Appdebugserver附加
  • iOS系統(tǒng)中,無法直接使用,需要導(dǎo)入頭文件
  • ptrace函數(shù)的定義
    ? int ptrace(int _request, pid_t _pid, caddr_t _addr, int _data);

破解ptrace

  • 防護(hù)效果:手動(dòng)運(yùn)行程序正常,Xcode運(yùn)行程序閃退
  • 使用ptrace符號(hào)斷點(diǎn)試探
  • 使用fishhook對(duì)ptrace函數(shù)HOOK
  • PT_DENY_ATTACH枚舉值,直接返回。其他類型,執(zhí)行原始函數(shù)
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 概敘 越獄防護(hù)是指防止別人修改自己的APP作出的防護(hù)手段。 1.了解代碼注入方式 了解防護(hù)之前需要了解代碼注入的方...
    Hanfank閱讀 1,542評(píng)論 4 14
  • 在應(yīng)用開發(fā)過程中,我們不僅僅需要完成正常的業(yè)務(wù)邏輯,考慮應(yīng)用性能、代碼健壯相關(guān)的問題,我們有時(shí)還需要考慮到應(yīng)用安全...
    喵喵唔的老巢閱讀 905評(píng)論 0 0
  • [TOC] 修改系統(tǒng)應(yīng)用 目標(biāo):消除對(duì)手機(jī)桌面的提醒氣泡 通過cycript分析氣泡連接手機(jī)并登陸,通過進(jìn)程列表指...
    _順_1896閱讀 1,883評(píng)論 0 3
  • layout: wikititle: iOS逆向分析筆記categories: Reverse_Engineeri...
    超哥__閱讀 11,434評(píng)論 3 16
  • tweak插件開發(fā) 1、連接手機(jī) usb或者wifi 通過ssh連手機(jī) 2、ps -A查看所有應(yīng)用 3、導(dǎo)出目標(biāo)...
    looha閱讀 1,762評(píng)論 0 1

友情鏈接更多精彩內(nèi)容