macOS Apps - Inspecting, debugging and Fuzzing
Last updated
Last updated
AWSハッキングを学び、実践する:HackTricks Training AWS Red Team Expert (ARTE) GCPハッキングを学び、実践する:HackTricks Training GCP Red Team Expert (GRTE)
ここからdisarmをダウンロードできます。
ここからjtool2をダウンロードできます または brew
を使ってインストールできます。
jtoolはdisarmに取って代わられました
Codesign
は macOS にあり、ldid
は iOS にあります
SuspiciousPackage は、.pkg ファイル(インストーラー)を検査し、インストールする前にその内容を確認するのに役立つツールです。
これらのインストーラーには、マルウェア作成者が通常悪用する preinstall
および postinstall
bash スクリプトがあります。これにより、マルウェアを持続させることができます。
このツールは、Apple のディスクイメージ(.dmg)ファイルをマウントして、何かを実行する前にそれらを検査することを可能にします:
It will be mounted in /Volumes
高エントロピーをチェック
文字列をチェック(理解できる文字列がほとんどない場合、パックされている)
MacOS用のUPXパッカーは「__XHDR」というセクションを生成します
Objective-Cで書かれたプログラムは、Mach-Oバイナリにコンパイルされるときに、クラス宣言を保持します。このようなクラス宣言には、以下の名前とタイプが含まれます:
定義されたインターフェース
インターフェースメソッド
インターフェースインスタンス変数
定義されたプロトコル
この名前は、バイナリのリバースエンジニアリングをより困難にするために難読化される可能性があることに注意してください。
Objective-Cを使用するバイナリで関数が呼び出されると、コンパイルされたコードはその関数を呼び出すのではなく、**objc_msgSend
**を呼び出します。これが最終的な関数を呼び出します:
この関数が期待するパラメータは次のとおりです:
最初のパラメータ(self)は「メッセージを受け取るクラスのインスタンスを指すポインタ」です。簡単に言えば、これはメソッドが呼び出されるオブジェクトです。メソッドがクラスメソッドの場合、これはクラスオブジェクトのインスタンス(全体)になりますが、インスタンスメソッドの場合、selfはクラスのインスタンス化されたインスタンスをオブジェクトとして指します。
2番目のパラメータ(op)は「メッセージを処理するメソッドのセレクタ」です。再度、簡単に言えば、これは単にメソッドの名前です。
残りのパラメータは、メソッド(op)によって必要とされる値です。
この情報を**lldb
を使用してARM64で簡単に取得する方法**については、このページを参照してください:
x64:
引数
レジスタ
(for) objc_msgSend
1番目の引数
rdi
self: メソッドが呼び出されるオブジェクト
2番目の引数
rsi
op: メソッドの名前
3番目の引数
rdx
メソッドへの1番目の引数
4番目の引数
rcx
メソッドへの2番目の引数
5番目の引数
r8
メソッドへの3番目の引数
6番目の引数
r9
メソッドへの4番目の引数
7番目以降の引数
rsp+ (スタック上)
メソッドへの5番目以降の引数
Dynadumpは、Objective-Cバイナリをクラスダンプするためのツールです。GitHubではdylibsが指定されていますが、実行可能ファイルでも動作します。
執筆時点では、これは現在最も効果的なものです。
class-dump は、ObjectiveC形式のコードでクラス、カテゴリ、およびプロトコルの宣言を生成するための元のツールです。
古くてメンテナンスされていないため、正しく動作しない可能性があります。
iCDump は、モダンでクロスプラットフォームのObjective-Cクラスダンプです。既存のツールと比較して、iCDumpはAppleエコシステムから独立して実行でき、Pythonバインディングを公開しています。
Swiftバイナリでは、Objective-Cとの互換性があるため、時々class-dumpを使用して宣言を抽出できますが、常に可能ではありません。
**jtool -l
またはotool -l
コマンドラインを使用すると、__swift5
**プレフィックスで始まるいくつかのセクションを見つけることができます:
さらに、このセクションに保存されている情報についての詳細はこのブログ記事で見つけることができます。
さらに、Swiftバイナリにはシンボルが含まれている可能性があります(例えば、ライブラリはその関数を呼び出すためにシンボルを保存する必要があります)。シンボルには通常、関数名と属性に関する情報が含まれていますが、見栄えが悪いため非常に便利であり、元の名前を取得できる「デマンガラー」があります:
バイナリをデバッグするには、SIPを無効にする必要があります(csrutil disable
またはcsrutil enable --without debug
)またはバイナリを一時フォルダにコピーし、codesign --remove-signature <binary-path>
で署名を削除するか、バイナリのデバッグを許可する必要があります(このスクリプトを使用できます)。
macOSでシステムバイナリ(例えばcloudconfigurationd
)を計測するには、SIPを無効にする必要があります(署名を削除するだけでは機能しません)。
macOSはプロセスに関する情報を提供するいくつかの興味深いAPIを公開しています:
proc_info
: 各プロセスに関する多くの情報を提供する主要なAPIです。他のプロセスの情報を取得するにはroot権限が必要ですが、特別な権限やmachポートは必要ありません。
libsysmon.dylib
: XPCで公開された関数を介してプロセスに関する情報を取得することを可能にしますが、com.apple.sysmond.client
の権限が必要です。
スタックショットは、プロセスの状態をキャプチャするための技術で、すべての実行中のスレッドのコールスタックを含みます。これは、デバッグ、パフォーマンス分析、特定の時点でのシステムの動作を理解するのに特に役立ちます。iOSおよびmacOSでは、**sample
やspindump
**などのツールや方法を使用してスタックショットを実行できます。
このツール(/usr/bini/ysdiagnose
)は、ps
、zprint
などの数十の異なるコマンドを実行してコンピュータから多くの情報を収集します。
rootとして実行する必要があり、デーモン/usr/libexec/sysdiagnosed
には、com.apple.system-task-ports
やget-task-allow
などの非常に興味深い権限があります。
そのplistは/System/Library/LaunchDaemons/com.apple.sysdiagnose.plist
にあり、3つのMachServicesを宣言しています:
com.apple.sysdiagnose.CacheDelete
: /var/rmp内の古いアーカイブを削除します
com.apple.sysdiagnose.kernel.ipc
: 特殊ポート23(カーネル)
com.apple.sysdiagnose.service.xpc
: Libsysdiagnose
Obj-Cクラスを介したユーザーモードインターフェース。辞書内に3つの引数を渡すことができます(compress
、display
、run
)
MacOSは、アプリケーションを実行して何をしているのかを理解する際に非常に役立つ多くのログを生成します。
さらに、いくつかのログには<private>
タグが含まれ、ユーザーまたはコンピュータの識別可能な情報を隠すために使用されます。ただし、この情報を開示するために証明書をインストールすることが可能です。詳細はこちらを参照してください。
Hopperの左パネルでは、バイナリのシンボル(ラベル)、手続きと関数のリスト(Proc)、および文字列(Str)を見ることができます。これらはすべての文字列ではなく、Mac-Oファイルのいくつかの部分で定義されたもの(_cstringやobjc_methname
など)です。
中央パネルでは、逆アセンブルされたコードを見ることができます。また、生の逆アセンブル、グラフ、デコンパイルされたもの、バイナリとしてそれぞれのアイコンをクリックすることで表示できます:
コードオブジェクトを右クリックすると、そのオブジェクトへの参照や名前の変更ができます(これはデコンパイルされた擬似コードでは機能しません):
さらに、中央下部ではPythonコマンドを記述できます。
右パネルでは、ナビゲーション履歴(現在の状況にどのように到達したかを知るため)、コールグラフ(この関数を呼び出すすべての関数と、この関数が呼び出すすべての関数を見ることができます)、およびローカル変数の情報など、興味深い情報を見ることができます。
これは、ユーザーがアプリケーションに非常に低レベルでアクセスできるようにし、プログラムをトレースし、その実行フローを変更する方法を提供します。Dtraceは、カーネル全体に配置されたプローブを使用し、システムコールの開始と終了などの場所にあります。
DTraceは、各システムコールのプローブを作成するために**dtrace_probe_create
関数を使用します。これらのプローブは、各システムコールのエントリポイントとエグジットポイント**で発火することができます。DTraceとのインタラクションは、/dev/dtraceを介して行われ、これはrootユーザーのみが利用可能です。
SIP保護を完全に無効にせずにDtraceを有効にするには、リカバリモードで次のコマンドを実行できます:csrutil enable --without dtrace
また、dtrace
またはdtruss
バイナリをコンパイルしたものを使用できます。
dtraceの利用可能なプローブは次のコマンドで取得できます:
プローブ名は、プロバイダー、モジュール、関数、および名前(fbt:mach_kernel:ptrace:entry
)の4つの部分で構成されています。名前の一部を指定しない場合、Dtraceはその部分をワイルドカードとして適用します。
DTraceを構成してプローブをアクティブにし、それらが発火したときに実行するアクションを指定するには、D言語を使用する必要があります。
より詳細な説明と例については、https://illumos.org/books/dtrace/chp-intro.htmlを参照してください。
man -k dtrace
を実行して利用可能なDTraceスクリプトのリストを表示します。例:sudo dtruss -n binary
スクリプト
これはカーネルトレース機能です。文書化されたコードは /usr/share/misc/trace.codes
にあります。
latency
、sc_usage
、fs_usage
、および trace
のようなツールは内部でこれを使用します。
kdebug
とインターフェースするために、sysctl
が kern.kdebug
名前空間を介して使用され、使用する MIB は bsd/kern/kdebug.c
に実装された関数を持つ sys/sysctl.h
にあります。
カスタムクライアントで kdebug と対話するための一般的な手順は次のとおりです:
KERN_KDSETREMOVE で既存の設定を削除
KERN_KDSETBUF と KERN_KDSETUP でトレースを設定
KERN_KDGETBUF を使用してバッファエントリの数を取得
KERN_KDPINDEX でトレースから自分のクライアントを取得
KERN_KDENABLE でトレースを有効化
KERN_KDREADTR を呼び出してバッファを読み取る
各スレッドをそのプロセスにマッチさせるために KERN_KDTHRMAP を呼び出す。
この情報を取得するために、Apple のツール trace
またはカスタムツール kDebugView (kdv)** を使用することができます。**
Kdebug は同時に 1 つの顧客にのみ利用可能であることに注意してください。 したがって、同時に実行できる k-debug 対応ツールは 1 つだけです。
ktrace_*
API は libktrace.dylib
から来ており、これらは Kdebug
のラッパーです。クライアントは ktrace_session_create
と ktrace_events_[single/class]
を呼び出して特定のコードにコールバックを設定し、次に ktrace_start
で開始できます。
これは SIP が有効な状態でも使用できます。
クライアントとしてユーティリティ ktrace
を使用できます:
Or tailspin
.
これはカーネルレベルのプロファイリングを行うために使用され、Kdebug
コールアウトを使用して構築されています。
基本的に、グローバル変数 kernel_debug_active
がチェックされ、設定されている場合は kperf_kdebug_handler
を Kdebug
コードとカーネルフレームのアドレスで呼び出します。Kdebug
コードが選択されたものと一致する場合、ビットマップとして構成された「アクション」を取得します(オプションについては osfmk/kperf/action.h
を確認してください)。
Kperf には sysctl MIB テーブルもあります:(root として)sysctl kperf
。これらのコードは osfmk/kperf/kperfbsd.c
にあります。
さらに、Kperf の機能の一部は kpc
に存在し、マシンのパフォーマンスカウンタに関する情報を提供します。
ProcessMonitor は、プロセスが実行しているプロセス関連のアクションを確認するための非常に便利なツールです(例えば、プロセスが作成している新しいプロセスを監視します)。
SpriteTree は、プロセス間の関係を印刷するツールです。
sudo eslogger fork exec rename create > cap.json
のようなコマンドで Mac を監視する必要があります(これを起動するターミナルには FDA が必要です)。その後、このツールに json を読み込んで、すべての関係を表示できます:
FileMonitor は、ファイルイベント(作成、変更、削除など)を監視し、そのようなイベントに関する詳細情報を提供します。
Crescendo は、Windows ユーザーが Microsoft Sysinternal の Procmon から知っているかもしれないルックアンドフィールを持つ GUI ツールです。このツールは、さまざまなイベントタイプの記録を開始および停止でき、ファイル、プロセス、ネットワークなどのカテゴリによってこれらのイベントをフィルタリングでき、記録されたイベントを json 形式で保存する機能を提供します。
Apple Instruments は Xcode の開発者ツールの一部で、アプリケーションのパフォーマンスを監視し、メモリリークを特定し、ファイルシステムのアクティビティを追跡するために使用されます。
プロセスによって実行されるアクションを追跡することができます:
Taskexplorer は、バイナリで使用されている ライブラリ、使用中の ファイル、および ネットワーク 接続を確認するのに便利です。 また、バイナリプロセスを virustotal と照合し、バイナリに関する情報を表示します。
このブログ記事 では、SIP が無効になっていてもデバッグを防ぐために PT_DENY_ATTACH
を使用した 実行中のデーモンをデバッグする 方法の例を見つけることができます。
lldb は macOS バイナリ デバッグ のための事実上のツールです。
あなたは、次の行を含む**.lldbinit
**というファイルをホームフォルダに作成することで、lldbを使用する際にintelフレーバーを設定できます:
lldb内で、process save-core
を使用してプロセスをダンプします
(lldb) コマンド
説明
run (r)
実行を開始し、ブレークポイントがヒットするかプロセスが終了するまで継続します。
process launch --stop-at-entry
エントリポイントで停止する実行を開始します
continue (c)
デバッグ中のプロセスの実行を続けます。
nexti (n / ni)
次の命令を実行します。このコマンドは関数呼び出しをスキップします。
stepi (s / si)
次の命令を実行します。nextiコマンドとは異なり、このコマンドは関数呼び出しに入ります。
finish (f)
現在の関数(“フレーム”)内の残りの命令を実行し、戻って停止します。
control + c
実行を一時停止します。プロセスが実行(r)または続行(c)されている場合、これによりプロセスは現在実行中の場所で停止します。
breakpoint (b)
b main
#mainと呼ばれる任意の関数
b <binname>`main
#バイナリのメイン関数
b set -n main --shlib <lib_name>
#指定されたバイナリのメイン関数
breakpoint set -r '\[NSFileManager .*\]$'
#任意のNSFileManagerメソッド
breakpoint set -r '\[NSFileManager contentsOfDirectoryAtPath:.*\]$'
break set -r . -s libobjc.A.dylib
#そのライブラリのすべての関数でブレーク
b -a 0x0000000100004bd9
br l
#ブレークポイントリスト
br e/dis <num>
#ブレークポイントを有効/無効にする
breakpoint delete <num>
help
help breakpoint #ブレークポイントコマンドのヘルプを取得
help memory write #メモリへの書き込みのヘルプを取得
reg
x/s <reg/memory address>
メモリをヌル終端の文字列として表示します。
x/i <reg/memory address>
メモリをアセンブリ命令として表示します。
x/b <reg/memory address>
メモリをバイトとして表示します。
print object (po)
これは、パラメータで参照されるオブジェクトを印刷します
po $raw
{
dnsChanger = {
"affiliate" = "";
"blacklist_dns" = ();
AppleのObjective-C APIやメソッドのほとんどはオブジェクトを返すため、"print object" (po) コマンドを介して表示する必要があります。poが意味のある出力を生成しない場合はx/b
を使用してください。
memory
memory read 0x000.... memory read $x0+0xf2a memory write 0x100600000 -s 4 0x41414141 #そのアドレスにAAAAを書き込みます memory write -f s $rip+0x11f+7 "AAAA" #そのアドレスにAAAAを書き込みます
disassembly
dis #現在の関数を逆アセンブル
dis -n <funcname> #関数を逆アセンブル
dis -n <funcname> -b <basename> #関数を逆アセンブル dis -c 6 #6行を逆アセンブル dis -c 0x100003764 -e 0x100003768 #1つのアドレスから別のアドレスまで dis -p -c 4 #現在のアドレスから逆アセンブルを開始
parray
parray 3 (char **)$x1 # x1レジスタの3コンポーネントの配列を確認
image dump sections
現在のプロセスメモリのマップを印刷します
image dump symtab <library>
image dump symtab CoreNLP
#CoreNLPのすべてのシンボルのアドレスを取得
objc_sendMsg
関数を呼び出すと、rsiレジスタにはメソッドの名前がヌル終端の(“C”)文字列として格納されます。lldbを介して名前を印刷するには、次のようにします:
(lldb) x/s $rsi: 0x1000f1576: "startMiningWithPort:password:coreCount:slowMemory:currency:"
(lldb) print (char*)$rsi:
(char *) $1 = 0x00000001000f1576 "startMiningWithPort:password:coreCount:slowMemory:currency:"
(lldb) reg read $rsi: rsi = 0x00000001000f1576 "startMiningWithPort:password:coreCount:slowMemory:currency:"
コマンド**sysctl hw.model
**は、ホストがMacOSの場合は「Mac」を返しますが、VMの場合は異なるものを返します。
**hw.logicalcpu
とhw.physicalcpu
**の値を操作することで、一部のマルウェアはVMかどうかを検出しようとします。
一部のマルウェアは、MACアドレス(00:50:56)に基づいてVMwareであるかどうかを検出することもできます。
簡単なコードを使用して、プロセスがデバッグされているかどうかを確認することも可能です:
if(P_TRACED == (info.kp_proc.p_flag & P_TRACED)){ //プロセスがデバッグされています }
ptrace
システムコールをPT_DENY_ATTACH
フラグで呼び出すこともできます。これにより、デバッガがアタッチしてトレースするのを防ぎます。
sysctl
またはptrace
関数がインポートされているかどうかを確認できます(ただし、マルウェアは動的にインポートする可能性があります)。
この書き込みで指摘されているように、「デバッグ防止技術の克服:macOS ptraceバリアント」: “メッセージProcess # exited with status = 45 (0x0000002d)は、デバッグ対象がPT_DENY_ATTACHを使用していることを示す兆候です”
コアダンプは次の場合に作成されます:
kern.coredump
sysctlが1に設定されている(デフォルト)
プロセスがsuid/sgidでない場合、またはkern.sugid_coredump
が1である(デフォルトは0)
AS_CORE
制限が操作を許可します。ulimit -c 0
を呼び出すことでコアダンプの作成を抑制でき、ulimit -c unlimited
で再度有効にできます。
これらのケースでは、コアダンプはkern.corefile
sysctlに従って生成され、通常は/cores/core/.%P
に保存されます。
ReportCrashはクラッシュしたプロセスを分析し、クラッシュレポートをディスクに保存します。クラッシュレポートには、開発者がクラッシュの原因を診断するのに役立つ情報が含まれています。
ユーザーごとのlaunchdコンテキストで実行されているアプリケーションや他のプロセスの場合、ReportCrashはLaunchAgentとして実行され、ユーザーの~/Library/Logs/DiagnosticReports/
にクラッシュレポートを保存します。
デーモン、システムlaunchdコンテキストで実行されている他のプロセスおよび他の特権プロセスの場合、ReportCrashはLaunchDaemonとして実行され、システムの/Library/Logs/DiagnosticReports
にクラッシュレポートを保存します。
クラッシュレポートがAppleに送信されることを心配している場合は、それを無効にできます。そうでない場合、クラッシュレポートはサーバーがどのようにクラッシュしたかを把握するのに役立ちます。
MacOSでファジングを行う際は、Macがスリープしないようにすることが重要です:
systemsetup -setsleep Never
pmset, システム環境設定
SSH接続を介してファジングを行っている場合、セッションが切断されないようにすることが重要です。次のようにsshd_configファイルを変更してください:
TCPKeepAlive Yes
ClientAliveInterval 0
ClientAliveCountMax 0
次のページを確認してください どのアプリが 指定されたスキームまたはプロトコルを処理しているかを見つける方法を知るために:
macOS File Extension & URL scheme app handlersこれはネットワークデータを管理しているプロセスを見つけるのに興味深いです:
または netstat
または lsof
を使用します。
CLIツールに対応しています。
macOS GUIツールで「そのまま動作します」。いくつかのmacOSアプリには、ユニークなファイル名、正しい拡張子、サンドボックスからファイルを読み取る必要があるなど、特定の要件があります(~/Library/Containers/com.apple.Safari/Data
)...
いくつかの例:
Learn & practice AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE) Learn & practice GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)