mach-o 檔案分析(解析類)

weixin_34247155發表於2018-05-13

mach-o檔案是macOS/iOS上面可執行檔案、物件程式碼、共享庫、動態載入程式碼和記憶體轉儲的檔案格式記錄的一種格式,就像linux上面的elf檔案。

實現的程式碼:class-dump-unix-like

0x00 方便快速分析的一些輔助工具

  • 010 Editor (方便看二進位制檔案)
  • IDA pro

0x01 檔案頭

在macOS/iOS上面有兩種型別的目標檔案:一種直接是mach-o檔案,另一種是fat檔案(fat檔案包含了多種架構,可以方便支援不同cpu架構的裝置)

fat檔案頭

Magic
arch size
Arch
...
Mach-o

fat檔案大致如下,最開始是一個magic,然後後面跟的是fat檔案中支援不同cpu架構的個數

fat檔案頭的結構體如下:

struct fat_header {
    uint32_t magic;
    uint32_t nfat_arch;
};

然後後面緊跟著的是arch,其結構體如下:

struct fat_arch {
    uint32_t cputype;
    uint32_t cpusubtype;
    uint32_t offset;
    uint32_t size;
    uint32_t align;
};

其中前兩個是代表的的是cpu的型別,第三個引數offset代表的是這個CPU架構在fat檔案裡面的mach-o頭的偏移,第四個引數size這個架構的cpu的mach-o檔案的大小。

8726395-3edb466d2b3dd7de.png
fat_header

mach-o檔案頭(32位)的結構體如下:

struct mach_header {
    uint32_t magic;         /* mach magic number identifier */
    uint32_t cputype;       /* cpu specifier */
    uint32_t cpusubtype;    /* machine specifier */
    uint32_t filetype;      /* type of file */
    uint32_t ncmds;         /* number of load commands */
    uint32_t sizeofcmds;    /* the size of all the load commands */
    uint32_t flags;         /* flags */
};

其中ncmds是load command的個數。

8726395-c608567a7440f063.png
mach_header

0x02 load_command

在mach-o頭後面緊跟著的就是load command,其基本結構體如下:

struct load_command {
    uint32_t cmd; //cmd型別
    uint32_t cmdsize; //大小
};

load command有很多種型別,解析類需要的是SEGMENT這種型別的load command,其結構如下(32位):

struct segment_command {
    uint32_t cmd;
    uint32_t cmdsize;
    char segname[16]; //名字
    uint32_t vmaddr;
    uint32_t vmsize;
    uint32_t fileoff;
    uint32_t filesize;
    uint32_t maxprot;
    uint32_t initprot;
    uint32_t nsects; //section的個數
    uint32_t flags;
};

其中segname是segment load command的名字,nsects是他裡面section的個數。

8726395-667aaf6cccde65b4.png
load_command

0x03 section

section是上面segment load command裡面的,其結構體如下:

struct section {
    char sectname[16]; //section的名字
    char segname[16]; //所在segment的名字
    uint32_t addr; //相對於檔案的偏移(如果是fat檔案就是相對fat開始的偏移)
    uint32_t size;//section的大小
    uint32_t offset; //相對於mach-o頭的偏移
    uint32_t align;
    uint32_t reloff;
    uint32_t nreloc;
    uint32_t flags;
    uint32_t reserved1;
    uint32_t reserved2;
}

其中sectname是section的名字,addr是相對於檔案開始的偏移,offset是相對於mach-o檔案開始的偏移,也就是如果就是單個cpu平臺的mach-o檔案,那麼offset = addr

8726395-dc799b1f26dd6719.png
section

0x04 objc_class

__DATA這個segment的__objc_classlist這個section裡面,儲存了所有類的地址,以下面這個檔案為例分析:

8726395-04630a4bb70b4d4b.png
classlist

這裡__objc_classlist這個段的偏移位0xa3e6e8。我們在010 Editor裡面按command + l跳轉到這個地址,內容如下:

8726395-42eedc3bd6534098.png
class list

每四位都是一個objc_class的地址,以第一為例,第一個objc_class的地址為0x00b416b4

Objc_class的結構體為:

struct objc_class{
    uint32_t isa;
    uint32_t wuperclass;
    uint32_t cache;
    uint32_t vtable;
    uint32_t data;
    uint32_t reserved1;
    uint32_t reserved2;
    uint32_t reserved3;
};

其中isa為meta class的地址,data為objc_classdata的地址,在oc裡面meta class裡面的方法就相當於java裡面static的方法,不需要例項化物件就能呼叫。

我們在ida裡面跳轉到0x00b416b4,這裡面存的就是objc_class

8726395-ff06ecf061da1990.png
objcclass

0x05 objc_classdata

我們在上面的objc_class那裡點選結構體的第五個元素,也就是classdata,
會自動跳轉到這個類的objc_classdata處,其結構體如下:

struct objc_classdata{
    uint32_t flags;
    uint32_t instanceStart;
    uint32_t instanceSize;
    uint32_t ivarlayout;
    uint32_t name;
    uint32_t baseMethod;    //方法
    uint32_t baseProtocol;  //介面
    uint32_t ivars;         //成員變數
    uint32_t weakIvarLayout;
    uint32_t baseProperties;//properties
};

其中name為類的名字,baseMethod為objc_method的偏移,ivars為objc_ivar的偏移,baseProperties為objc_properties的偏移。

8726395-6390ee45607fa11b.png
classdata

0x06 objc_ivar

在ida裡面點選objc_classdata結構體裡面的第8個引數,就會跳轉到objc_ivar列表的地方。其中前面四個位元組為ivar的flag,然後後面四個位元組代表ivar的個數。緊接著後面就是ivar了,ivar的結構體為:

struct objc_ivar{
    uint32_t offset;
    uint32_t name;
    uint32_t type;
    uint32_t alignment;
    uint32_t size;
};

其中name為名字,type成員變數型別。

8726395-72e72fbdfe41442e.png
ivar

0x07 objc_method

在ida裡面點選objc_classdata結構體裡面的第6個引數,就會跳轉到objc_method列表的地方。其中前面四個位元組為method的flag,然後後面四個位元組代表method的個數。緊接著後面就是method了,method的結構體為:

struct objc_method{
    uint32_t name; // 方法的名字
    uint32_t types;
    uint32_t _implementation;
}

其實name為名字

8726395-59c1883e95faaac4.png
method

0x08 objc_properties

在ida裡面點選objc_classdata結構體裡面的第10個引數,就會跳轉到objc_properties列表的地方。其中前面四個位元組為properties的flag,然後後面四個位元組代表properties的個數。緊接著後面就是properties了,properties的結構體為:

struct objc_properties{
    uint32_t name;
    uint32_t attributes;
}

其中name為名字

8726395-ec668864cd6ec46d.png
properties

相關文章