hhkb.dev

BLOG

2025-10-09 / 어셈블리 · 03

명령어 집합과 제어 흐름

어셈블리에는 if, while 같은 고수준 제어문이 없다. 대신 비교와 점프 명령을 조합해서 흐름을 만든다. 결국 분기 구조를 읽는 핵심도 이 두 개다.

1. 기본 명령어

먼저 자주 보는 기본 명령어부터 익히면 된다.

  • MOV A, B: B 값을 A로 복사한다.
  • ADD / SUB: 덧셈과 뺄셈을 수행한다.
  • INC / DEC: 값을 1씩 증가시키거나 감소시킨다.
  • LEA(Load Effective Address): 메모리 안의 값이 아니라 주소 자체를 계산한다. 포인터 계산이나 간단한 산술에 자주 쓰인다.

2. 분기문의 원리: CMP와 JMP

조건문은 보통 두 단계로 처리된다. 먼저 CMP로 플래그를 세팅하고, 그다음 Jcc 계열 명령으로 분기한다.

(1) 비교: CMP(Compare)

cmp rax, rbx

두 값의 차이(rax - rbx)를 계산하지만, 결과 자체를 저장하지는 않는다. 대신 RFLAGS 안의 상태 비트만 갱신한다.

  • ZF(Zero Flag): 결과가 0이면, 즉 두 값이 같으면 1이 된다.
  • SF(Sign Flag): 결과가 음수면 1이 된다.

Note: TEST 명령어와의 차이
리버싱을 하다 보면 CMP 대신 TEST 명령어가 자주 보인다. (예: test rax, rax)
CMP는 뺄셈 연산(-)을 통해 차이를 확인하지만, TEST는 논리곱(AND) 연산을 수행한다. 주로 "값이 0인지 아닌지" 또는 **"NULL 포인터 체크"**를 할 때 CMP보다 효율적이라서 자주 사용된다.

(2) 분기: Jcc(Jump Condition)

Jcc 계열 명령은 플래그 상태를 보고 흐름을 바꾼다.

명령어의미조건
JEJump if Equal값이 같을 때 (ZF=1)
JNEJump if Not Equal값이 다를 때 (ZF=0)
JGJump if Greater클 때
JLJump if Less작을 때

3. If-Else 구현 패턴

고수준 언어의 if-else는 어셈블리로 내려오면 대체로 이런 모양이 된다.

    cmp rax, 10
    je  is_ok       ; 조건 만족 시 점프

    ; [Else 블록]
    call fail
    jmp  finish     ; 필수: 여기서 점프하지 않으면 아래 코드로 흘러내림(Fall-through)

is_ok:
    ; [If 블록]
    call ok

finish:
    ; 종료

Else 블록 끝에 jmp가 없으면, 아래 is_ok 블록까지 그대로 흘러 들어가는 fall-through가 생길 수 있다. 제어 흐름을 읽을 때 이 점을 항상 봐야 한다.