macOS Universal binaries & Mach-O Format
Last updated
Last updated
AWSハッキングの学習と実践:HackTricks Training AWS Red Team Expert (ARTE) GCPハッキングの学習と実践:HackTricks Training GCP Red Team Expert (GRTE)
Mac OSのバイナリは通常、universal binariesとしてコンパイルされます。Universal binaryは1つのファイル内で複数のアーキテクチャをサポートできます。
これらのバイナリは基本的にMach-O構造に従います。Mach-O構造は次のように構成されています:
ヘッダー
ロードコマンド
データ
次のコマンドでファイルを検索します:mdfind fat.h | grep -i mach-o | grep -E "fat.h$"
ヘッダーにはマジックバイトが続き、ファイルが含むアーキテクチャの数(nfat_arch
)と各アーキテクチャにはfat_arch
構造体があります。
次のコマンドで確認します:
またはMach-O Viewツールを使用して:
通常、2つのアーキテクチャ向けにコンパイルされたuniversal binaryは、1つのアーキテクチャ向けにコンパイルされたものと比べてサイズが倍になることが多いです。
ヘッダーには、ファイルを識別するためのマジックバイトや対象アーキテクチャに関する情報など、ファイルに関する基本情報が含まれています。次のコマンドで確認できます:mdfind loader.h | grep -i mach-o | grep -E "loader.h$"
異なるファイルタイプがあります。これらはこちらのソースコードで定義されています。最も重要なものは次のとおりです:
MH_OBJECT
: リロケータブルオブジェクトファイル(コンパイルの中間生成物であり、まだ実行可能ではありません)。
MH_EXECUTE
: 実行可能ファイル。
MH_FVMLIB
: 固定VMライブラリファイル。
MH_CORE
: コードダンプ
MH_PRELOAD
: プリロードされた実行可能ファイル(XNUではもはやサポートされていません)
MH_DYLIB
: ダイナミックライブラリ
MH_DYLINKER
: ダイナミックリンカ
MH_BUNDLE
: "プラグインファイル"。gccの-bundleを使用して生成され、NSBundle
またはdlopen
によって明示的にロードされます。
MH_DYSM
: 付随する.dSym
ファイル(デバッグ用のシンボルを含むファイル)。
MH_KEXT_BUNDLE
: カーネル拡張。
またはMach-O Viewを使用する:
ソースコードでは、ライブラリの読み込みに役立ついくつかのフラグも定義されています:
MH_NOUNDEFS
: 未定義の参照なし(完全にリンクされています)
MH_DYLDLINK
: Dyld リンキング
MH_PREBOUND
: ダイナミック参照が事前にバインドされています。
MH_SPLIT_SEGS
: ファイルが r/o および r/w セグメントに分割されています。
MH_WEAK_DEFINES
: バイナリには弱く定義されたシンボルがあります
MH_BINDS_TO_WEAK
: バイナリが弱いシンボルを使用しています
MH_ALLOW_STACK_EXECUTION
: スタックを実行可能にします
MH_NO_REEXPORTED_DYLIBS
: ライブラリに LC_REEXPORT コマンドがありません
MH_PIE
: 位置に依存しない実行可能ファイル
MH_HAS_TLV_DESCRIPTORS
: スレッドローカル変数を持つセクションがあります
MH_NO_HEAP_EXECUTION
: ヒープ/データページの実行がありません
MH_HAS_OBJC
: バイナリに oBject-C セクションがあります
MH_SIM_SUPPORT
: シミュレータサポート
MH_DYLIB_IN_CACHE
: 共有ライブラリキャッシュ内の dylibs/frameworks で使用されます。
メモリ内のファイルのレイアウトがここで指定され、シンボルテーブルの位置、実行開始時のメインスレッドのコンテキスト、および必要な共有ライブラリの詳細が示されています。バイナリのメモリへの読み込みプロセスに関する指示が、動的ローダー(dyld)に提供されます。
これには、loader.h
で定義されたload_command構造が使用されます。
システムが異なる50種類のロードコマンドを異なる方法で処理しています。最も一般的なものは、LC_SEGMENT_64
、LC_LOAD_DYLINKER
、LC_MAIN
、LC_LOAD_DYLIB
、およびLC_CODE_SIGNATURE
です。
基本的に、このタイプのロードコマンドは、バイナリが実行されるときに**__TEXT**(実行コード)および**__DATA**(プロセス用のデータ)セグメントをどのようにロードするかを、データセクションで示されたオフセットに従って定義します。
これらのコマンドは、プロセスの仮想メモリ空間にマップされるセグメントを定義します。
異なる種類のセグメントがあり、プログラムの実行コードを保持する**__TEXTセグメントや、プロセスによって使用されるデータを含む__DATAセグメントなどがあります。これらのセグメントは、Mach-Oファイルのデータセクションに配置**されています。
各セグメントはさらに複数のセクションに分割できます。ロードコマンド構造には、それぞれのセグメント内のこれらのセクションに関する情報が含まれています。
ヘッダー内にはまずセグメントヘッダーがあります:
セグメントヘッダーの例:
このヘッダーは、その後に表示されるセクションヘッダーの数を定義しています。
例: セクションヘッダー:
もし、セクションオフセット(0x37DC)にアーキテクチャが始まるオフセット(この場合 0x18000
)を追加すると、0x37DC + 0x18000 = 0x1B7DC
になります。
また、コマンドラインからヘッダー情報を取得することも可能です。
以下は、このコマンドによって読み込まれる一般的なセグメントです:
__PAGEZERO
: カーネルにアドレスゼロをマップするよう指示し、読み取り、書き込み、実行ができないようにします。この構造体内のmaxprotとminprot変数はゼロに設定され、このページには読み取り書き込み実行権限がないことを示します。
この割り当てはNULLポインターのデリファレンスの脆弱性を緩和するために重要です。これは、XNUが最初のメモリページ(i386を除く)がアクセスできないようにする厳格なページゼロを強制するためです。バイナリは、最初の4kをカバーする小さな__PAGEZERO(-pagezero_size
を使用)を作成し、残りの32ビットメモリをユーザーモードとカーネルモードの両方でアクセス可能にすることで、これらの要件を満たすことができます。
__TEXT
: 読み取りおよび実行権限(書き込み不可)を持つ実行可能なコードが含まれています。このセグメントの一般的なセクション:
__text
: コンパイルされたバイナリコード
__const
: 定数データ(読み取り専用)
__[c/u/os_log]string
: C、Unicode、またはosログの文字列定数
__stubs
および__stubs_helper
: ダイナミックライブラリの読み込みプロセス中に関与
__unwind_info
: スタックアンワインドデータ
このコンテンツはすべて署名されていますが、実行可能としてマークされています(特権が必要ないセクションの悪用のためのさらなるオプションを作成します、例えば文字列専用セクション)。
__DATA
: 読み取りおよび書き込み可能なデータが含まれています(実行不可)。
__got:
グローバルオフセットテーブル
__nl_symbol_ptr
: 遅延なし(ロード時にバインド)シンボルポインター
__la_symbol_ptr
: 遅延(使用時にバインド)シンボルポインター
__const
: 読み取り専用データであるべき(実際にはそうではない)
__cfstring
: CoreFoundation文字列
__data
: 初期化されたグローバル変数
__bss
: 初期化されていない静的変数
__objc_*
(__objc_classlist、__objc_protolistなど): Objective-Cランタイムで使用される情報
__DATA_CONST
: __DATA.__constは定数であることが保証されていません(書き込み権限があります)、他のポインターやGOTも同様です。このセクションは、mprotect
を使用して__const
、一部の初期化子、およびGOTテーブル(解決後)を読み取り専用にします。
__LINKEDIT
: リンカー(dyld)のための情報を含み、シンボル、文字列、および再配置テーブルエントリが含まれます。これは__TEXT
または__DATA
に含まれないコンテンツのための一般的なコンテナであり、その内容は他のロードコマンドで説明されています。
dyld情報: リベース、遅延なし/遅延/弱いバインディングオペコードおよびエクスポート情報
関数開始: 関数の開始アドレスのテーブル
コード内データ: __text内のデータアイランド
シンボルテーブル: バイナリ内のシンボル
間接シンボルテーブル: ポインター/スタブシンボル
文字列テーブル
コード署名
__OBJC
: Objective-Cランタイムで使用される情報を含みます。ただし、この情報は、さまざまな__objc_*セクション内にも見つかる可能性があります。
__RESTRICT
: コンテンツのないセグメントで、**__restrict
**という単一のセクションがあり、バイナリを実行する際にDYLD環境変数を無視することを保証します。
コードで見られたように、セグメントにはフラグもサポートされています(あまり使用されていませんが):
SG_HIGHVM
: コアのみ(使用されていません)
SG_FVMLIB
: 使用されていません
SG_NORELOC
: セグメントに再配置がない
SG_PROTECTED_VERSION_1
: 暗号化。例えば、Finderが__TEXT
セグメントのテキストを暗号化するために使用されます。
LC_UNIXTHREAD/LC_MAIN
LC_MAIN
にはentryoff属性内のエントリーポイントが含まれています。ロード時に、dyldは単にこの値を(メモリ内の)バイナリのベースに追加し、その後この命令にジャンプしてバイナリのコードの実行を開始します。
**LC_UNIXTHREAD
には、メインスレッドを開始する際にレジスタが持つ必要がある値が含まれています。これはすでに非推奨となっていますが、dyld
**はまだ使用しています。これによって設定されるレジスタの値を次のように確認できます:
LC_CODE_SIGNATURE
Macho-Oファイルのコード署名に関する情報を含みます。署名ブロブを指すオフセットのみを含んでいます。通常、ファイルの最後にあります。 ただし、このセクションに関する情報は、このブログ投稿とこのgistsで見つけることができます。
LC_ENCRYPTION_INFO[_64]
バイナリの暗号化をサポートします。ただし、攻撃者がプロセスを侵害してメモリを復号化することができる場合があります。
LC_LOAD_DYLINKER
共有ライブラリをプロセスのアドレス空間にマップする動的リンカー実行ファイルへのパスを含みます。値は常に/usr/lib/dyld
に設定されています。macOSでは、dylibのマッピングがカーネルモードではなくユーザーモードで行われることに注意することが重要です。
LC_IDENT
古いですが、パニック時にダンプを生成するように構成されている場合、Mach-Oコアダンプが作成され、カーネルバージョンがLC_IDENT
コマンドに設定されます。
LC_UUID
ランダムUUIDです。直接的には何にも役立ちませんが、XNUはプロセス情報と一緒にキャッシュします。クラッシュレポートで使用できます。
LC_DYLD_ENVIRONMENT
プロセスが実行される前にdyldに環境変数を示すことを許可します。これはプロセス内で任意のコードを実行できる可能性があるため、非常に危険です。このロードコマンドは、#define SUPPORT_LC_DYLD_ENVIRONMENT
で構築されたdyldでのみ使用され、DYLD_..._PATH
形式の変数のみを指定して処理をさらに制限します。
LC_LOAD_DYLIB
このロードコマンドは、ローダー(dyld)にライブラリをロードしてリンクするよう指示する 動的ライブラリ依存関係を記述します。Mach-Oバイナリが必要とする各ライブラリにはLC_LOAD_DYLIB
ロードコマンドがあります。
このロードコマンドは、**dylib_command
**型の構造体であり(実際の依存する動的ライブラリを記述するstruct dylibを含む)、
次のコマンドラインからもこの情報を取得できます:
いくつかの潜在的なマルウェア関連ライブラリは次のとおりです:
DiskArbitration: USB ドライブの監視
AVFoundation: 音声とビデオのキャプチャ
CoreWLAN: Wifi スキャン
Mach-O バイナリには、LC_MAIN で指定されたアドレスの前に実行される1つ以上のコンストラクタが含まれる可能性があります。 任意のコンストラクタのオフセットは、__DATA_CONST セグメントの**__mod_init_func** セクションに保持されます。
ファイルの中心には、ロードコマンド領域で定義された複数のセグメントで構成されるデータ領域があります。各セグメントにはさまざまなデータセクションが収められており、各セクションにはコードまたはデータが特定のタイプに固有のものが含まれています。
データは基本的に、ロードコマンドLC_SEGMENTS_64によって読み込まれるすべての情報を含む部分です。
これには次のものが含まれます:
関数テーブル:プログラム関数に関する情報を保持します。
シンボルテーブル:バイナリで使用される外部関数に関する情報を含みます
内部関数、変数名なども含まれる可能性があります。
確認するには、Mach-O View ツールを使用できます:
または、CLI から:
__TEXT
セグメント(r-x)内:
__objc_classname
: クラス名(文字列)
__objc_methname
: メソッド名(文字列)
__objc_methtype
: メソッドタイプ(文字列)
__DATA
セグメント(rw-)内:
__objc_classlist
: すべてのObjective-Cクラスへのポインタ
__objc_nlclslist
: 遅延ロードされないObjective-Cクラスへのポインタ
__objc_catlist
: カテゴリへのポインタ
__objc_nlcatlist
: 遅延ロードされないカテゴリへのポインタ
__objc_protolist
: プロトコルリスト
__objc_const
: 定数データ
__objc_imageinfo
, __objc_selrefs
, objc__protorefs
...
_swift_typeref
, _swift3_capture
, _swift3_assocty
, _swift3_types, _swift3_proto
, _swift3_fieldmd
, _swift3_builtin
, _swift3_reflstr