[理論] C言語 vs アセンブリ言語、完全マッピングガイド! 🧩 (リバーシングの基礎体力作り)

こんにちは、セキュリティの卵の皆さん! 🌱

マルウェア解析やリバースエンジニアリングを学ぶ上で、避けて通れない大きな壁があります。それがアセンブリ言語(Assembly Language)です。

MOV, LEA, CMP, JE…といった見慣れない英語の略語がずらりと並んだ黒い画面を見ると、「私は誰?ここはどこ?」と自然と思ってしまいますよね。😵‍💫

でも、怖がらないでください!アセンブリ言語は宇宙語ではありません。私たちが普段使っているC言語(高水準言語)が、ただ服を着替えただけなのです。コンパイラは決められた規則(Pattern)に従ってC言語を機械語に翻訳するため、この「変換規則(Mapping)」さえ知っていれば、アセンブリ言語をスラスラと読めるようになります。

今日は、リバースエンジニアリングの核心である、C言語とアセンブリ言語の1:1マッピング構造を徹底的に解剖していきます。この原理を知れば、デコンパイラなしでもコードの流れが見えるようになるでしょう! 🚀


1. 変数の割り当て (Variable Assignment) 📥

C言語で変数に値を代入する行為は、アセンブリではデータ移動(Move)として表現されます。

📌 C言語

int a = 10;
int b = a;

⚙️ アセンブリ言語 (Assembly)

MOV [RBP-4], 0xA    ; 1. 메모리(변수 a의 위치)에 10(0xA)을 넣는다.
MOV EAX, [RBP-4]    ; 2. 변수 a의 값을 레지스터(EAX)로 가져온다.
MOV [RBP-8], EAX    ; 3. 레지스터 값을 메모리(변수 b의 위치)에 넣는다.
  • 解説:
  • CPUはメモリからメモリへ直接データを移動できません。そのため、EAXのようなレジスタ(一時記憶領域)を経由して移動します。
  • [RBP-4]のような形式は、スタックメモリのアドレス(ローカル変数)を意味します。
  • 核心: MOV A, BはA = Bと考えると良いでしょう。

2. 算術演算 (Arithmetic Operations) 🧮

足し算、引き算のような演算は非常に直感的です。

📌 C言語

a = a + 5;
a++;

⚙️ アセンブリ言語 (Assembly)

ADD [RBP-4], 5      ; a = a + 5 (ADD: 더하기)
INC [RBP-4]         ; a++ (INC: 1 증가)
  • 解説:
  • ADD: 足し算
  • SUB: 引き算
  • INC: 1増加 (Increment)
  • DEC: 1減少 (Decrement)
  • IMUL / IDIV: 掛け算 / 割り算

3. 条件文 (If-Else) ⚖️

プログラムの流れを変える分岐文です。アセンブリでは、比較(CMP)ジャンプ(JUMP)の2段階で構成されます。

📌 C言語

if (a == 10) {
    // Trueブロック
    b = 1;
} else {
    // Falseブロック
    b = 0;
}

⚙️ アセンブリ言語 (Assembly)

CMP [RBP-4], 0xA    ; 1. a와 10을 비교한다. (실제로는 a - 10을 해봄)
JNE 0x401050        ; 2. 결과가 같지 않다면(Not Equal), Else 블록(0x401050)으로 점프!

; --- True Block ---
MOV [RBP-8], 1      ; b = 1
JMP 0x401060        ; Else 블록을 건너뛰고 탈출!

; --- False Block (0x401050) ---
MOV [RBP-8], 0      ; b = 0
  • 解説:
  • CMP A, B: AとBを比較します。(内部的にA – Bを実行し、0になれば等しいと判断)
  • JE (Jump Equal): 等しければジャンプ
  • JNE (Jump Not Equal): 等しくなければジャンプ
  • JG / JL: 大きければ(Greater) / 小さければ(Less) ジャンプ
  • 高水準言語の { } ブロック構造が、アセンブリではGOTO (ジャンプ)の形に変わることを覚えておきましょう!

4. 繰り返し文 (Loop: For / While) 🔄

繰り返し文は、条件文とジャンプの組み合わせです。コードが下に進んだ後、再び上に上がる循環構造を持ちます。

📌 C言語

for (int i = 0; i < 10; i++) {
    sum += i;
}

⚙️ アセンブリ言語 (Assembly)

MOV [RBP-4], 0      ; i = 0 (초기화)
JMP Check_Condition ; 조건 확인하러 가기

Loop_Start:         ; 루프 시작 지점
MOV EAX, [RBP-4]    ; i 값을 가져옴
ADD [RBP-8], EAX    ; sum += i
INC [RBP-4]         ; i++ (증감)

Check_Condition:    ; 조건 확인 지점
CMP [RBP-4], 0xA    ; i와 10 비교
JL Loop_Start       ; i < 10 (작으면) Loop_Start로 다시 점프!
  • 解説:
  • C言語のfor文は、初期化 -> 条件比較 -> 本体 -> 増減 -> 条件比較の順序で分解されます。
  • アセンブリコードで矢印が上方向に上がるJMPやJccが見えたら、「ああ、ここは繰り返し文だな!」と考えて良いでしょう。

5. 関数呼び出し (Function Call) 📞

リバーシングで最も重要な関数呼び出し規約(Calling Convention)です。関数を呼び出す前にパラメータ(引数)を準備し、関数が終了したら戻り値を受け取ります。

📌 C言語

int result = Add(10, 20);

⚙️ アセンブリ言語 (Assembly – x64基準)

MOV EDX, 20         ; 2번째 인자(20)를 EDX(또는 RSI) 레지스터에 넣음
MOV ECX, 10         ; 1번째 인자(10)를 ECX(또는 RDI) 레지스터에 넣음
CALL 0x401200       ; Add 함수 호출 (스택에 복귀 주소 저장하고 이동)
MOV [RBP-4], EAX    ; 결과값(EAX)을 result 변수에 저장
  • 解説:
  • パラメータ渡し: x64環境では、主にレジスタ(RDI, RSI, RDX, RCX…)の順で引数を渡します。(x86はスタックPUSHを使用)
  • CALL: 関数があるアドレスへジャンプします。この時、関数が終了して戻るアドレスをスタックに密かに保存します。
  • 戻り値(RAX/EAX): 関数が実行された結果の値は、常にEAX(またはRAX)レジスタに格納されています。そのため、CALL直後にEAXを確認すれば戻り値を知ることができます。

💡 裏技: AIと一緒に勉強する

今日学んだパターンがまだ馴染まなくても大丈夫です。私たちにはAIがいますから!

実習中に分からないアセンブリコードが出てきたら、このように尋ねてみてください。

「このアセンブリコードをC言語に変換(Decompile)して、どのような構造(if, forなど)なのか説明して!」

AIは、私たちが今学んだこのマッピング規則に基づいてコードを解釈してくれるでしょう。しかし、理論を知って結果を見るのと、知らずに見るのとでは天地の差があることを忘れないでくださいね! 😉

終わりに 🎁

C言語とアセンブリ言語は、まさに「コインの裏表」のようなものです。

片面(アセンブリ)だけを見て、もう片面(C言語)を想像できる能力が身につけば、皆さんはすでに中級アナリストへの切符を手にしたも同然です。

次回は、この理論を基に、AIに「アセンブリコードを渡すので、Pythonコードで再実装して」と指示する魔法のような実習を行います。

今日も楽しいリバーシングを!ファイト! 💪



Comments

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です