Assembly 기초 이론 및 명령어

⭐⭐⭐ (핵심 섹션)
⭐⭐ (실용적인 내용)
⭐ (기초 이론)

1.어셈블리어 개요⭐

1.1 어셈블리어란?

어셈블리어는 기계어와 일대일 대응되는 저수준 프로그래밍 언어다. CPU가 직접 이해할 수 있는 기계어를 사람이 읽기 쉬운 형태로 표현한 것으로, 하드웨어에 가장 가까운 프로그래밍 언어.

1.2 프로그래밍 언어 계층 구조

고급 언어 (C/C++, Python, Java)
         ↓ 컴파일러
어셈블리어 (MOV, ADD, JMP 등)
         ↓ 어셈블러
기계어 (0101101...)
         ↓ CPU 실행

1.3 어셈블리어 학습이 필요한 이유

  • 시스템 프로그래밍: 운영체제, 드라이버 개발
  • 임베디드 시스템: 마이크로컨트롤러 프로그래밍
  • 성능 최적화: 병목 지점의 수동 최적화
  • 리버스 엔지니어링: 바이너리 분석, 보안 연구
  • 디버깅: 깊은 수준의 문제 해결

2. 컴퓨터 구조 기초 ⭐⭐⭐

2.1 CPU 구조와 동작 원리

┌─────────────┐    ┌──────────────┐
│    CPU      │◄──►│   Memory     │
│ ┌─────────┐ │    │              │
│ │   ALU   │ │    │ Instructions │
│ │Register │ │    │     Data     │
│ │ Control │ │    │              │
│ └─────────┘ │    └──────────────┘
└─────────────┘
      ▲
      │
┌─────▼─────┐
│    I/O    │
└───────────┘

Von neumann architecture

Fetch-Decode-Execute 사이클

  1. Fetch: 메모리에서 명령어를 가져옴
  2. Decode: 명령어를 해석
  3. Execute: 명령어를 실행
  4. Write-back: 결과를 저장

2.2 레지스터의 종류와 역할 (Intel x86-64 기준)

64비트: RAX, RBX, RCX, RDX, RSI, RDI, RBP, RSP
32비트: EAX, EBX, ECX, EDX, ESI, EDI, EBP, ESP
16비트: AX,  BX,  CX,  DX,  SI,  DI,  BP,  SP
8비트:  AL,  BL,  CL,  DL,  SIL, DIL, BPL, SPL
       AH,  BH,  CH,  DH

범용 레지스터 (General Purpose Registers)

각 레지스터의 관습적 역할:

  • RAX (Accumulator): 산술 연산, 함수 반환값
  • RBX (Base): 베이스 포인터, 데이터 참조
  • RCX (Counter): 루프 카운터, 문자열 연산
  • RDX (Data): 산술 연산 확장, I/O 포트 주소
  • RSI (Source Index): 문자열 연산 소스
  • RDI (Destination Index): 문자열 연산 대상
  • RBP (Base Pointer): 스택 프레임 베이스
  • RSP (Stack Pointer): 스택 최상단 주소

해당 레지스터는 전통적으로 저렇게 사용되었다는 역할일뿐 레지스터 사용의 결정 주체는 주로 컴파일러이며 최적화 또는 함수 호출 규약등의 이유로 자유롭게 변경될 수 있음, C/C++ 같은 고수준 언어 내에서 __asm 또는 asm 키워드를 사용하여 어셈블리 코드를 직접 삽입하는 경우에도 특정 레지스터 사용을 지정할 수 있음.

특수 목적 레지스터

  • RIP (Instruction Pointer): 다음 실행할 명령어 주소
  • RFLAGS: 상태 플래그들을 저장

확장 레지스터 (x86-64)

  • R8~R15: 추가 범용 레지스터
  • XMM0~XMM15: SSE 연산용 128비트 레지스터
  • YMM0~YMM15: AVX 연산용 256비트 레지스터

2.3 메모리 구조와 주소 지정

높은 주소 (64비트 = 0xFFFFFFFFFFFFFFFF)
┌─────────────┐ 0xFFFFFFFF80000000
│   커널 공간  │ ← 운영체제 전용 (사용자 접근 불가)
│             │   - 커널 코드/데이터
│             │   - 디바이스 드라이버
│             │   - 시스템 호출 처리
├─────────────┤ 0x00007FFFFFFFFFFF (47비트 주소 공간)
│    스택     │ ← RSP 레지스터가 가리킴
│     ↓      │   - 함수 호출 시 매개변수/지역변수
│             │   - 함수 반환 주소
│             │   - 아래 방향으로 성장 (주소 감소)
├─────────────┤ 약 0x00007FFFFFFFE000 (스택 시작)
│    ...     │   
│  미사용     │ ← 가상 주소 공간의 여유분
│   영역     │   - 메모리 매핑 파일
│             │   - 공유 라이브러리 (.so)
├─────────────┤ 
│      ↑      │
│     힙      │ ← malloc(), new 등의 동적 할당
│             │   - 프로그램 실행 중 크기 변화
│             │   - 위 방향으로 성장 (주소 증가)
├─────────────┤ 약 0x0000000000602000 (힙 시작)
│   BSS 영역  │ ← 초기화되지 않은 전역/정적 변수
│             │   - 프로그램 시작 시 0으로 초기화
│             │   - 실행 파일에는 크기 정보만 저장
├─────────────┤ 약 0x0000000000601000
│  데이터 영역 │ ← 초기화된 전역/정적 변수
│   (.data)   │   - 실행 파일에 초기값이 저장됨
│             │   - 읽기/쓰기 가능
├─────────────┤ 약 0x0000000000600000
│  코드 영역  │ ← 실행 가능한 기계어 코드
│   (.text)   │   - 읽기 전용 (보안상 중요!)
│             │   - 프로그램의 명령어들
└─────────────┘ 0x0000000000400000 (전통적인 시작 주소)
낮은 주소

가상 메모리 레이아웃 (Linux x86-64)

주소 지정 모드 (Addressing Modes)

; 즉시 주소 지정 (Immediate)
mov rax, 100                ; RAX = 100

; 레지스터 주소 지정 (Register)
mov rax, rbx               ; RAX = RBX

; 직접 주소 지정 (Direct)
mov rax, [0x401000]        ; RAX = memory[0x401000]

; 간접 주소 지정 (Indirect)
mov rax, [rbx]             ; RAX = memory[RBX]

; 베이스 + 오프셋 (Base + Displacement)
mov rax, [rbx + 8]         ; RAX = memory[RBX + 8]

; 인덱스 주소 지정 (Indexed)
mov rax, [rbx + rcx*4]     ; RAX = memory[RBX + RCX*4]

; 복합 주소 지정 (Complex)
mov rax, [rbx + rcx*4 + 8] ; RAX = memory[RBX + RCX*4 + 8]
Virtual Memory 심화 이론
💡Basic Assembly의 범위를 넘어선 학습내용 정리. 가상 메모리(VM)레이아웃의 상세 특성 1. 커널 영역 (Kernel Space) * 주소 범위: 0xFFFF800000000000 ~ 0xFFFFFFFFFFFFFFFF * 특징: * 사용자 프로그램에서 직접 접근 불가 * 시스템 호출을 통해서만 간접 접근 * 모든 프로세스가 동일한 커널 공간을 공유 * 용도: 운영체제 코드, 디바이스 드라이버, 시스템 자료구조 2. 스택 (Stack) * 주소 범위:

3. 어셈블리 개발 환경⭐⭐

3.1 주요 어셈블러

  • NASM (Netwide Assembler): Intel 문법, 크로스 플랫폼
  • GAS (GNU Assembler): AT&T 문법, GCC 도구체인
  • MASM (Microsoft Macro Assembler): Windows 전용
  • YASM: NASM 호환 어셈블러

3.2 Intel vs AT&T 문법 비교

; Intel 문법 (NASM)
mov rax, rbx
mov rax, [rbx + 8]
add rax, 100

; AT&T 문법 (GAS)
movq %rbx, %rax
movq 8(%rbx), %rax
addq $100, %rax

3.3 개발 도구 체인

# NASM 기반 개발 과정
nasm -f elf64 program.asm -o program.o    # 어셈블
ld program.o -o program                    # 링킹
./program                                  # 실행

# 디버깅
gdb ./program                             # GDB 디버거
objdump -d program                        # 디스어셈블

4. 기본 명령어 체계 ⭐⭐⭐

4.1 명령어 형식과 구조

일반적인 명령어 형식

[접두사] 연산코드 목적지, 소스 [; 주석]

접두사 (Prefixes)

  • REP/REPE/REPNE: 문자열 연산 반복
  • LOCK: 메모리 잠금 (멀티프로세싱)
  • 접근 크기: BYTE PTR, WORD PTR, DWORD PTR, QWORD PTR

4.2 데이터 이동 명령어

MOV 명령어 패밀리

; 기본 이동
mov rax, rbx        ; RAX = RBX
mov rax, 0x1234     ; RAX = 0x1234
mov [rax], rbx      ; memory[RAX] = RBX

; 크기별 이동
mov al, bl          ; 8비트 이동
mov ax, bx          ; 16비트 이동
mov eax, ebx        ; 32비트 이동 (상위 32비트 자동 클리어)
mov rax, rbx        ; 64비트 이동

; 부호 확장 이동
movsx rax, eax      ; 32비트를 64비트로 부호 확장
movzx rax, ax       ; 16비트를 64비트로 제로 확장

LEA (Load Effective Address) - 강력한 주소 계산

; 주소 계산 (실제 메모리 접근 없음)
lea rax, [rbx + rcx*4 + 8]    ; RAX = RBX + RCX*4 + 8

; 복잡한 산술 연산을 한 번에 처리
lea rax, [rax + rax*2]        ; RAX = RAX * 3 (빠른 곱셈)
lea rax, [rax + rax*4]        ; RAX = RAX * 5

스택 조작 명령어

push rax            ; RSP -= 8, [RSP] = RAX
pop rbx             ; RBX = [RSP], RSP += 8

pushfq              ; 플래그 레지스터를 스택에 저장
popfq               ; 스택에서 플래그 레지스터 복원

4.3 산술 연산 명령어

기본 산술 연산

; 덧셈
add rax, rbx        ; RAX += RBX
add rax, 10         ; RAX += 10
adc rax, rbx        ; RAX += RBX + carry (다정밀도 연산)

; 뺄셈
sub rax, rbx        ; RAX -= RBX
sub rax, 10         ; RAX -= 10
sbb rax, rbx        ; RAX -= RBX - borrow (다정밀도 연산)

; 증가/감소
inc rax             ; RAX++
dec rax             ; RAX--

곱셈 연산

; 부호 없는 곱셈
mul rbx             ; RDX:RAX = RAX * RBX (128비트 결과)

; 부호 있는 곱셈
imul rax, rbx       ; RAX *= RBX
imul rax, rbx, 10   ; RAX = RBX * 10
imul rax, [mem], 5  ; RAX = memory[mem] * 5

나눗셈 연산

; 나눗셈 전 준비 (중요!)
xor rdx, rdx        ; RDX = 0 (상위 64비트 클리어)
; 또는
cqo                 ; RAX를 RDX:RAX로 부호 확장

; 부호 없는 나눗셈
div rbx             ; RAX = RDX:RAX / RBX, RDX = 나머지

; 부호 있는 나눗셈
idiv rbx            ; RAX = RDX:RAX / RBX, RDX = 나머지

4.4 논리 연산 명령어

비트 연산

; 논리곱
and rax, rbx        ; RAX &= RBX
and rax, 0xFF       ; 하위 8비트만 유지

; 논리합
or rax, rbx         ; RAX |= RBX
or rax, 0x80        ; 특정 비트 설정

; 배타적 논리합
xor rax, rbx        ; RAX ^= RBX
xor rax, rax        ; RAX = 0 (자기 자신과의 XOR)

; 논리 부정
not rax             ; RAX = ~RAX (모든 비트 반전)

비트 테스트

test rax, rbx       ; RAX & RBX (결과는 버리고 플래그만 설정)
test rax, rax       ; RAX가 0인지 확인 (자주 사용되는 패턴)

4.5 시프트 연산 명령어

논리 시프트

shl rax, 1          ; RAX <<= 1 (왼쪽으로 1비트, *2 효과)
shl rax, cl         ; CL 레지스터 값만큼 시프트
shr rax, 1          ; RAX >>= 1 (오른쪽으로 1비트, /2 효과)

산술 시프트 (부호 고려)

sal rax, 1          ; 산술 왼쪽 시프트 (SHL과 동일)
sar rax, 1          ; 산술 오른쪽 시프트 (부호 비트 유지)

순환 시프트

rol rax, 1          ; 왼쪽 순환 시프트
ror rax, 1          ; 오른쪽 순환 시프트
rcl rax, 1          ; 캐리를 포함한 왼쪽 순환 시프트
rcr rax, 1          ; 캐리를 포함한 오른쪽 순환 시프트

4.6 플래그 레지스터와 상태 관리

주요 플래그 비트

RFLAGS 레지스터 (64비트)
┌─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┐
│0│0│0│0│O│D│I│T│S│Z│0│A│0│P│1│C│
└─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┘
             F E D C B A 9 8 7 6 5 4 3 2 1 0

C (Carry): 캐리/보로우 발생
P (Parity): 결과의 1비트 개수가 짝수
A (Auxiliary): BCD 연산 보조 캐리
Z (Zero): 결과가 0
S (Sign): 결과가 음수
O (Overflow): 부호 있는 오버플로우
D (Direction): 문자열 연산 방향
I (Interrupt): 인터럽트 허용

플래그 조작

clc                 ; 캐리 플래그 클리어
stc                 ; 캐리 플래그 설정
cmc                 ; 캐리 플래그 반전

cld                 ; 방향 플래그 클리어 (문자열 연산 순방향)
std                 ; 방향 플래그 설정 (문자열 연산 역방향)

cli                 ; 인터럽트 비활성화
sti                 ; 인터럽트 활성화

5. 기본 프로그램 작성⭐⭐

5.1 Hello World 프로그램

section .data
    msg db 'Hello, World!', 0xA    ; 문자열 + 개행
    msg_len equ $ - msg             ; 문자열 길이

section .text
    global _start

_start:
    ; sys_write 시스템 콜
    mov rax, 1          ; sys_write 번호
    mov rdi, 1          ; stdout
    mov rsi, msg        ; 메시지 주소
    mov rdx, msg_len    ; 메시지 길이
    syscall             ; 시스템 콜 실행
    
    ; sys_exit 시스템 콜
    mov rax, 60         ; sys_exit 번호
    mov rdi, 0          ; 종료 코드
    syscall             ; 시스템 콜 실행

Linux x86-64

5.2 간단한 계산 프로그램

section .data
    num1 dq 15          ; 64비트 정수
    num2 dq 25
    result dq 0

section .text
    global _start

_start:
    ; 덧셈 연산
    mov rax, [num1]     ; RAX = num1
    add rax, [num2]     ; RAX += num2
    mov [result], rax   ; result = RAX
    
    ; 곱셈 연산 예제
    mov rax, [num1]     ; RAX = num1
    imul rax, [num2]    ; RAX *= num2
    
    ; 종료
    mov rax, 60
    mov rdi, 0
    syscall

5.3 입출력 처리 기초

section .bss
    buffer resb 64      ; 64바이트 버퍼

section .data
    prompt db 'Enter a number: ', 0
    prompt_len equ $ - prompt

section .text
    global _start

_start:
    ; 프롬프트 출력
    mov rax, 1
    mov rdi, 1
    mov rsi, prompt
    mov rdx, prompt_len
    syscall
    
    ; 입력 받기
    mov rax, 0          ; sys_read
    mov rdi, 0          ; stdin
    mov rsi, buffer     ; 버퍼 주소
    mov rdx, 64         ; 최대 읽을 바이트
    syscall
    
    ; 입력받은 데이터 그대로 출력 (echo)
    mov rdx, rax        ; 읽은 바이트 수
    mov rax, 1          ; sys_write
    mov rdi, 1          ; stdout
    mov rsi, buffer     ; 버퍼 주소
    syscall
    
    ; 종료
    mov rax, 60
    mov rdi, 0
    syscall

핵심 포인트 요약

반드시 기억해야 할 핵심 개념

  1. 레지스터 활용: RAX, RBX, RCX, RDX의 관습적 역할과 64/32/16/8비트 접근
  2. 주소 지정 모드: 특히 [base + index*scale + displacement] 형태
  3. MOV vs LEA: LEA는 주소 계산만, MOV는 실제 데이터 이동
  4. 플래그의 영향: 연산 결과가 플래그에 미치는 영향 이해
  5. 시스템 콜: Linux에서 rax에 시스템 콜 번호, 매개변수는 rdi, rsi, rdx 순서

흔한 실수 방지

  1. 나눗셈 전 RDX 초기화: xor rdx, rdx 또는 cqo 필수
  2. 32비트 연산 시 상위 비트 자동 클리어: mov eax, ebx는 RAX의 상위 32비트를 0으로 만듦
  3. 스택 정렬: x86-64에서는 함수 호출 시 RSP가 16바이트 경계에 정렬되어야 함
  4. 주소 지정 시 크기 명시: mov byte [rax], 10 vs mov qword [rax], 10