iOS逆向之五 MACH O檔案解析

aron1992發表於2019-04-04

MachO檔案是蘋果可執行二進位制檔案的格式

Load Commands

LC_SEGMENT_64
將可執行檔案(64位)對映到程式地址空間
32位系統的是LC_SEGMENT
是載入的主要命令,負責指導核心來設定程式的記憶體空間

LC_DYLD_INFO_ONLY
動態連結相關資訊

LC_SYMTAB
符號表地址

LC_DYSYMTAB
動態符號地址表

LC_LOAD_DYLINKER
載入一個動態連結器,路徑“/usr/lib/dyld”

LC_UUID
二進位制檔案的標識ID,dSYM檔案、crash中都存在這個值,確定兩個檔案是否匹配,分析出對應的崩潰位置

LC_VERSION_MIN_MACOSX
二進位制檔案要求的最低系統版本,和xcode中配置的target有關

LC_MAIN
設定程式的入口,編譯型的語言需要指定入口地址,直譯器語言對此沒有邀請

LC_SOURCE_VERSION
構建二進位制檔案使用的原始碼版本

LC_FUNCTION_STARTS
定義函式的起始地址表,使我們的偵錯程式很容易看到地址

LC_DATA_IN_CODE
定義在程式碼段內的非指令資料

segment資料結構

#import <mach-o/loader.h>
 struct segment_command { /* for 32-bit architectures */
	uint32_t	cmd;		/* LC_SEGMENT */
	uint32_t	cmdsize;	/* includes sizeof section structs */
	char		segname[16];	/* segment name */
	uint32_t	vmaddr;		/* memory address of this segment */
	uint32_t	vmsize;		/* memory size of this segment */
	uint32_t	fileoff;	/* file offset of this segment */
	uint32_t	filesize;	/* amount to map from the file */
	vm_prot_t	maxprot;	/* maximum VM protection */
	vm_prot_t	initprot;	/* initial VM protection */
	uint32_t	nsects;		/* number of sections in segment */
	uint32_t	flags;		/* flags */
};
複製程式碼
struct segment_command_64 { /* for 64-bit architectures */
	uint32_t	cmd;		/* LC_SEGMENT_64 */
	uint32_t	cmdsize;	/* includes sizeof section_64 structs */
	char		segname[16];	/* segment name */
	uint64_t	vmaddr;		/* memory address of this segment */
	uint64_t	vmsize;		/* memory size of this segment */
	uint64_t	fileoff;	/* file offset of this segment */
	uint64_t	filesize;	/* amount to map from the file */
	vm_prot_t	maxprot;	/* maximum VM protection */
	vm_prot_t	initprot;	/* initial VM protection */
	uint32_t	nsects;		/* number of sections in segment */
	uint32_t	flags;		/* flags */
};
複製程式碼

** 段結構圖(程式碼段__TEXT): ** 

段結構圖
段結構圖

段(segment)和節(section)

"__TEXT"代表的是segment,  
“__text”代表的是section  
複製程式碼

__PAGEZERO段
一個全用0填充的段,使用者抓取空指標引用(非法記憶體訪問)。通常不會佔用實體記憶體

__TEXT段
程式碼段:只有可執行程式碼和其他只讀資料

	__text		主程式的程式碼
	__stubs	動態庫連結的樁
	__stub_helper	動態庫連結的樁的輔助
	__cstring	字串常量(程式碼中寫固定的字元)立即數(程式碼中直接寫的數字,成為模數)
			常量字串表的描述資訊,通過該資訊,可以獲取常量字元符號的表地址
	__unwind_info	用於異常處理
複製程式碼

__DATA段
用於讀取和寫入資料的一個段

	__nl_symbol_ptr	非延時匯入符號表指標
	_la_symbol_ptr		延時匯入符號表指標
複製程式碼

__LINKEDIT
包含給動態連結庫連結器(ldyd)的原始資料段,包含符號表和字串表,壓縮動態連結資訊,以及動態符號表

節的資料結構

struct section { /* for 32-bit architectures */
	char		sectname[16];	/* name of this section */
	char		segname[16];	/* segment this section goes in */
	uint32_t	addr;		/* memory address of this section */
	uint32_t	size;		/* size in bytes of this section */
	uint32_t	offset;		/* file offset of this section */
	uint32_t	align;		/* section alignment (power of 2) */
	uint32_t	reloff;		/* file offset of relocation entries */
	uint32_t	nreloc;		/* number of relocation entries */
	uint32_t	flags;		/* flags (section type and attributes)*/
	uint32_t	reserved1;	/* reserved (for offset or index) */
	uint32_t	reserved2;	/* reserved (for count or sizeof) */
};
複製程式碼
struct section_64 { /* for 64-bit architectures */
	char		sectname[16];	/* name of this section */
	char		segname[16];	/* segment this section goes in */
	uint64_t	addr;		/* memory address of this section */
	uint64_t	size;		/* size in bytes of this section */
	uint32_t	offset;		/* file offset of this section */
	uint32_t	align;		/* section alignment (power of 2) */
	uint32_t	reloff;		/* file offset of relocation entries */
	uint32_t	nreloc;		/* number of relocation entries */
	uint32_t	flags;		/* flags (section type and attributes)*/
	uint32_t	reserved1;	/* reserved (for offset or index) */
	uint32_t	reserved2;	/* reserved (for count or sizeof) */
	uint32_t	reserved3;	/* reserved */
};
複製程式碼

節結構圖(程式碼段):

節結構圖
節結構圖

	char		sectname[16];	/* Section名字 */
	char		segname[16];	/* Section所在的segment的名字 */
	uint32_t	addr;		/* 記憶體中起始位置 */
	uint32_t	size;		/* Section大小 */
	uint32_t	offset;		/* Section的檔案偏移 */
	uint32_t	align;		/* 對其   */
	uint32_t	reloff;		/* file offset of relocation entries */
	uint32_t	nreloc;		/* number of relocation entries */
	uint32_t	flags;		/* flags (section type and attributes)*/
	uint32_t	reserved1;	/* reserved (for offset or index) */
	uint32_t	reserved2;	/* reserved (for count or sizeof) */
複製程式碼

__TEXT:__cstring 分析 原始碼:

static int PI = 3.1415;

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        // insert code here...
        NSString* hello = @"hello";
        NSLog(@"%@", hello);
        NSLog(@"Hello, World!!");
        NSLog(@"Hello Hello");
        NSLog(@"Hello Hello");
        NSLog(@"PI = %d", PI);
        int a = 100;
        int b = 200;
        int c = a + b;
        NSLog(@"c = %d", c);
    }
    return 0;
}
複製程式碼

對應的_cstring 節內容如下:

cstring 節內容
cstring 節內容

可以看到所有的字串儲存在一張字串表中,對應的內容是可以被修改的,執行的時候執行的結果也會變成修改之後的內容。

動態庫連結資訊

LC_DYLD_INFO_ONLY

資料結構:

struct dyld_info_command {
   uint32_t   cmd;		/* LC_DYLD_INFO or LC_DYLD_INFO_ONLY */
   uint32_t   cmdsize;		/* sizeof(struct dyld_info_command) */

    uint32_t   rebase_off;	/* file offset to rebase info  */
    uint32_t   rebase_size;	/* size of rebase info   */
    
    uint32_t   bind_off;	/* file offset to binding info   */
    uint32_t   bind_size;	/* size of binding info  */
        
    uint32_t   weak_bind_off;	/* file offset to weak binding info   */
    uint32_t   weak_bind_size;  /* size of weak binding info  */
    
    uint32_t   lazy_bind_off;	/* file offset to lazy binding info */
    uint32_t   lazy_bind_size;  /* size of lazy binding infs */

    uint32_t   export_off;	/* file offset to lazy binding info */
    uint32_t   export_size;	/* size of lazy binding infs */
};
複製程式碼

REBASE_INFO

REBASE_INFO
REBASE_INFO

** 重定向資料rebase(命令碼:高四位、低四位) ** 11:REBASE_OPCODE_SET_TYPE_IMM 高四位0x10表示設定立即數型別 低四0x01位表示立即數型別為指標
22:REBASE_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB + 2 重定向到資料段的第2個section

REBASE_INFO Action1
REBASE_INFO Action1

Action 為翻譯出來的內容,指向的是資料段的第2個節(Load Commands中的所以) 

REBASE_INFO Action2
REBASE_INFO Action2
具體的內容
REBASE_INFO Action3
REBASE_INFO Action3

NSLog是庫中的函式,所以需要重定向,其他的幾個函式也是類似的。

Binding Info

Binding Info
Binding Info

繫結資料bind: 進行動態繫結的dyld的函式(dyld_stub_binder)

弱繫結資料 weak_bind

延時繫結 lazy_bind
對於需要從動態庫載入的函式符號(_printf)

Export Info

可以使用nm工具檢視二進位制檔案的匯出符號 【 nm - display name list (symbol table)】

➜  Debug nm MachOTest 
                 U _NSLog
0000000100001100 d _PI
                 U ___CFConstantStringClassReference
0000000100000000 T __mh_execute_header
0000000100000e40 T _main
                 U _objc_autoreleasePoolPop
                 U _objc_autoreleasePoolPush
                 U _objc_retain
                 U _objc_storeStrong
                 U dyld_stub_binder
複製程式碼

Export Info
Export Info

使用工具檢視可以看到匯出符號有兩個

動態連結庫的執行方式

TODO

相關文章