hacktricks/macos-hardening/macos-security-and-privilege-escalation/macos-files-folders-and-binaries/universal-binaries-and-mach-o-format.md

18 KiB
Raw Blame History

macOSのUniversalバイナリとMach-Oフォーマット

htARTEHackTricks AWS Red Team Expert でAWSハッキングをゼロからヒーローまで学びましょう

HackTricksをサポートする他の方法

基本情報

Mac OSのバイナリは通常、universal binariesとしてコンパイルされます。Universal binary同じファイル内で複数のアーキテクチャをサポートできます。

これらのバイナリは、基本的に以下のようなMach-O構造に従います:

  • ヘッダー
  • ロードコマンド
  • データ

https://alexdremov.me/content/images/2022/10/6XLCD.gif

Fatヘッダー

次のコマンドでファイルを検索します:mdfind fat.h | grep -i mach-o | grep -E "fat.h$"

#define FAT_MAGIC	0xcafebabe
#define FAT_CIGAM	0xbebafeca	/* NXSwapLong(FAT_MAGIC) */

struct fat_header {
	uint32_t	magic;		/* FAT_MAGIC or FAT_MAGIC_64 */
	uint32_t	nfat_arch;	/* 後続する構造体の数 */
};

struct fat_arch {
cpu_type_t	cputype;	/* CPU指定子int */
cpu_subtype_t	cpusubtype;	/* マシン指定子int */
uint32_t	offset;		/* このオブジェクトファイルへのファイルオフセット */
uint32_t	size;		/* このオブジェクトファイルのサイズ */
uint32_t	align;		/* 2の累乗としてのアライメント */
};

ヘッダーにはmagicバイトが続き、ファイルが含むarchsの数(nfat_arch)と各アーキテクチャがfat_arch構造体を持ちます。

次のコマンドで確認します:

% file /bin/ls
/bin/ls: Mach-O universal binary with 2 architectures: [x86_64:Mach-O 64-bit executable x86_64] [arm64e:Mach-O 64-bit executable arm64e]
/bin/ls (for architecture x86_64):	Mach-O 64-bit executable x86_64
/bin/ls (for architecture arm64e):	Mach-O 64-bit executable arm64e

% otool -f -v /bin/ls
Fat headers
fat_magic FAT_MAGIC
nfat_arch 2
architecture x86_64
    cputype CPU_TYPE_X86_64
cpusubtype CPU_SUBTYPE_X86_64_ALL
capabilities 0x0
    offset 16384
    size 72896
    align 2^14 (16384)
architecture arm64e
    cputype CPU_TYPE_ARM64
cpusubtype CPU_SUBTYPE_ARM64E
capabilities PTR_AUTH_VERSION USERSPACE 0
    offset 98304
    size 88816
    align 2^14 (16384)

またはMach-O Viewツールを使用する:

通常、2つのアーキテクチャ向けにコンパイルされたuniversal binaryは、1つのアーキテクチャ向けにコンパイルされたもののサイズを倍にします。

Mach-Oヘッダー

ヘッダーには、ファイルを識別するためのマジックバイトや対象アーキテクチャに関する情報など、ファイルに関する基本情報が含まれています。次のコマンドで確認できます:mdfind loader.h | grep -i mach-o | grep -E "loader.h$"

#define	MH_MAGIC	0xfeedface	/* the mach magic number */
#define MH_CIGAM	0xcefaedfe	/* NXSwapInt(MH_MAGIC) */
struct mach_header {
uint32_t	magic;		/* mach magic number identifier */
cpu_type_t	cputype;	/* cpu specifier (e.g. I386) */
cpu_subtype_t	cpusubtype;	/* machine specifier */
uint32_t	filetype;	/* type of file (usage and alignment for the file) */
uint32_t	ncmds;		/* number of load commands */
uint32_t	sizeofcmds;	/* the size of all the load commands */
uint32_t	flags;		/* flags */
};

#define MH_MAGIC_64 0xfeedfacf /* the 64-bit mach magic number */
#define MH_CIGAM_64 0xcffaedfe /* NXSwapInt(MH_MAGIC_64) */
struct mach_header_64 {
uint32_t	magic;		/* mach magic number identifier */
int32_t		cputype;	/* cpu specifier */
int32_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 */
uint32_t	reserved;	/* reserved */
};

ファイルタイプ:

  • MH_EXECUTE (0x2): 標準のMach-O実行ファイル
  • MH_DYLIB (0x6): Mach-Oダイナミックリンクライブラリ.dylib
  • MH_BUNDLE (0x8): Mach-Oバンドル.bundle
# Checking the mac header of a binary
otool -arch arm64e -hv /bin/ls
Mach header
magic  cputype cpusubtype  caps    filetype ncmds sizeofcmds      flags
MH_MAGIC_64    ARM64          E USR00     EXECUTE    19       1728   NOUNDEFS DYLDLINK TWOLEVEL PIE

または、Mach-O Viewを使用します:

Mach-O ロードコマンド

メモリ内のファイルのレイアウトがここで指定され、シンボルテーブルの場所、実行開始時のメインスレッドのコンテキスト、および必要な共有ライブラリが詳細に説明されています。動的ローダー**(dyld)**には、バイナリのメモリへの読み込みプロセスに関する指示が提供されます。

これには、loader.hで定義されたload_command構造が使用されます。

struct load_command {
uint32_t cmd;           /* type of load command */
uint32_t cmdsize;       /* total size of command in bytes */
};

システムが異なる50種類のロードコマンドを異なる方法で処理します。最も一般的なものは、LC_SEGMENT_64LC_LOAD_DYLINKERLC_MAINLC_LOAD_DYLIB、およびLC_CODE_SIGNATUREです。

LC_SEGMENT/LC_SEGMENT_64

{% hint style="success" %} 基本的に、このタイプのロードコマンドは、バイナリが実行されるときに、__TEXT(実行コード)および**__DATA**(プロセス用のデータ)セグメントをどのようにロードするかを、データセクションで示されたオフセットに従って定義します。 {% endhint %}

これらのコマンドは、プロセスの仮想メモリ空間にマップされるセグメント定義します。

異なる種類のセグメントがあり、プログラムの実行コードを保持する**__TEXTセグメントや、プロセスで使用されるデータを含む__DATAセグメントなどがあります。これらのセグメントは、Mach-Oファイルのデータセクションに配置**されています。

各セグメントは、さらに複数のセクション分割できます。ロードコマンド構造には、それぞれのセグメント内のこれらのセクションに関する情報が含まれています。

ヘッダー内にはまず、セグメントヘッダーがあります:

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 */
int32_t		maxprot;	/* maximum VM protection */
int32_t		initprot;	/* initial VM protection */
	uint32_t	nsects;		/* number of sections in segment */
	uint32_t	flags;		/* flags */
};

セグメントヘッダーの例:

このヘッダーは、その後に表示されるセクションヘッダーの数を定義しています。

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

例: セクションヘッダーの例:

もしセクションオフセット0x37DCアーキテクチャが始まるオフセット(この場合 0x18000)を追加すると、0x37DC + 0x18000 = 0x1B7DC となります。

また、コマンドラインからもヘッダー情報を取得することが可能です。

otool -lv /bin/ls

このコマンドによってロードされる一般的なセグメント:

  • __PAGEZERO: カーネルにアドレスゼロ読み取り、書き込み、実行できないようにマップするよう指示します。この構造体内のmaxprotとminprot変数は、このページに読み取り書き込み実行権限がないことを示すためにゼロに設定されています。
  • この割り当てはNULLポインターのデリファレンス脆弱性を緩和するために重要です。
  • __TEXT: 読み取りおよび実行権限(書き込み権限なし)を持つ実行可能なコードを含みます。このセグメントの一般的なセクション:
    • __text: コンパイルされたバイナリコード
    • __const: 定数データ
    • __cstring: 文字列定数
    • __stubsおよび__stubs_helper: ダイナミックライブラリの読み込みプロセス中に関与します
  • __DATA: 読み取り書き込み可能なデータを含みます(実行権限なし)。
    • __data: 初期化されたグローバル変数
    • __bss: 初期化されていない静的変数
    • __objc_*__objc_classlist、__objc_protolistなど: Objective-Cランタイムで使用される情報
  • __LINKEDIT: リンカーdyldのための情報を含みます。「シンボル、文字列、および再配置テーブルエントリー」など。
  • __OBJC: Objective-Cランタイムで使用される情報を含みます。ただし、この情報は__DATAセグメント内のさまざまな__objc_*セクションでも見つけることができます。

LC_MAIN

entryoff属性にエントリーポイントを含みます。ロード時に、dyldは単にこの値を(メモリ内の)バイナリのベースに追加し、その後この命令にジャンプしてバイナリのコードの実行を開始します。

LC_CODE_SIGNATURE

Macho-Oファイルのコード署名に関する情報を含みます。署名ブロブを指すオフセットのみを含みます。通常、これはファイルの最後にあります。
ただし、このセクションに関する情報は、このブログ投稿やこのgistsで見つけることができます。

LC_LOAD_DYLINKER

プロセスのアドレス空間に共有ライブラリをマップする動的リンカー実行ファイルへのパスを含みます。値は常に/usr/lib/dyldに設定されます。macOSでは、dylibのマッピングがカーネルモードではなくユーザーモードで行われることに注意することが重要です。

LC_LOAD_DYLIB

このロードコマンドは、ローダーdyldライブラリのロードとリンクを指示する動的ライブラリの依存関係を記述します。Mach-Oバイナリが必要とする各ライブラリに対してLC_LOAD_DYLIBロードコマンドがあります。

  • このロードコマンドは、**実際の依存する動的ライブラリを記述するstruct dylibを含むdylib_command**型の構造体です。
struct dylib_command {
uint32_t        cmd;            /* LC_LOAD_{,WEAK_}DYLIB */
uint32_t        cmdsize;        /* includes pathname string */
struct dylib    dylib;          /* the library identification */
};

struct dylib {
union lc_str  name;                 /* library's path name */
uint32_t timestamp;                 /* library's build time stamp */
uint32_t current_version;           /* library's current version number */
uint32_t compatibility_version;     /* library's compatibility vers number*/
};

以下のコマンドでもこの情報を取得できます:

otool -l /path/to/binary | grep -A 3 LC_VERSION_MIN_MACOSX
otool -L /bin/ls
/bin/ls:
/usr/lib/libutil.dylib (compatibility version 1.0.0, current version 1.0.0)
/usr/lib/libncurses.5.4.dylib (compatibility version 5.4.0, current version 5.4.0)
/usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 1319.0.0)

いくつかの潜在的なマルウェア関連ライブラリは次のとおりです:

  • DiskArbitrationUSBドライブの監視
  • AVFoundation:オーディオとビデオのキャプチャ
  • CoreWLANWifiスキャン

{% hint style="info" %} Mach-Oバイナリには、LC_MAINで指定されたアドレスの前に実行される1つ以上のコンストラクタが含まれる可能性があります。
任意のコンストラクタのオフセットは、__DATA_CONSTセグメントの**__mod_init_func**セクションに保持されます。 {% endhint %}

Mach-Oデータ

ファイルの中心には、ロードコマンド領域で定義された複数のセグメントで構成されるデータ領域があります。各セグメントにはさまざまなデータセクションが収容され、各セクションには特定のタイプに固有のコードまたはデータが含まれます

{% hint style="success" %} データは基本的に、ロードコマンドLC_SEGMENTS_64によって読み込まれるすべての情報を含む部分です。 {% endhint %}

https://www.oreilly.com/api/v2/epubs/9781785883378/files/graphics/B05055_02_38.jpg

これには次のものが含まれます:

  • 関数テーブル:プログラム関数に関する情報を保持します。
  • シンボルテーブル:バイナリで使用される外部関数に関する情報を含みます。
  • 内部関数、変数名なども含まれる可能性があります。

確認するには、Mach-O Viewツールを使用できます:

または、CLIから

size -m /bin/ls
ゼロからヒーローまでAWSハッキングを学ぶ htARTEHackTricks AWS Red Team Expert

HackTricksをサポートする他の方法: