x86 Assembly : Essential Part(1)
명령코드종류
데이터 이동(Data Transfer) | mov, lea |
산술 연산(Arithmetic) | inc, dec, add, sub |
논리 연산(Logical) | and, or, xor, not |
비교(Comparison) | cmp, test |
스택(Stack) | push, pop |
프로시져(Procedure) | call, ret, leave |
시스템 콜(System call) | syscall |
x64 어셈블리 언어는 우리가 사용하는 한국어보다는 훨씬 단순하다
이들의 문장은 동사에 해당하는 명령어(Operation Code, Opcode)와 목적어에 해당하는 피연산자(Operand)로 구성된다.
피연산자
피연산자에는 총 3가지 종류가 올 수 있다.
1. 상수(Immediate Value)
2. 레지스터(Register)
3. 메모리(Memory)
메모리 피연산자는 []으로 둘러싸인 것으로 표현되며, 앞에 크기 지정자(Size Directive) TYPE PTR이 추가될 수 있다.
여기서 타입에는 BYTE(1), WORD(2), DWORD(4), QWORD(8)가 올 수 있으며, 각각 1바이트, 2바이트, 4바이트, 8바이트의 크기를 지정한다.
메모리 피연산자
QWORD PTR[0x8048000] | 0x8048000의 데이터를 8바이트만큼 참조 |
DWORD PTR[0x8048000] | 0x8048000의 데이터를 4바이트만큼 참조 |
WORD PTR[rax] | rax가 가르키는 주소에서 데이터를 2바이트 만큼 참조 |
메모리 피연산자
데이터 이동 명령어는 어떤 값을 레지스터나 메모리에 옮기도록 지시함
mov dst, src : src에 들어있는 값을 dst에 대입
mov rdi,rsi | rsi의 값을 rdi에 대입 |
mov QWORD PTR[rdi], rsi | rsi의 값을 rdi가 가리키는 주소에 대입 |
mov QWORD PTR[rdi+8*rcx], rsi | rsi의 값을 rdi+8*rcx가 가리키는 주소에 대입 |
산술 연산
산술 연산 명령어는 덧셈, 뺄셈, 곱셈, 나눗셈 연산을 지시한다.
add dst, srcdst에 src의 값을 더한다.
add eax 3 | eax += 3 |
add ax, WORD PTR[rdi] | ax+=(WORD)rdi |
sub dst,srcdst에서 src의 값을 뺀다.
sub eax, 3 | eax -= 3 |
sub ax, WORD PTR[rdi] | ax -= (WORD)rdi |
inc opop의 값을 1 증가시킴
inc eax | eax += 1 |
dec opop의 값을 1 감소 시킴
dec eax | eax -= 1 |
논리 연산 - and & or & xor & not
논리 연산 명령어는 and, or, xor, neg 등의 비트 연산을 지시
and dst, src -> dst와 src의 비트가 모두 1이면 1, 아니면 0
[Register]
eax = 0xffff0000
ebx = 0xcafebabe
[Code]
and eax, ebx
[Result]
eax = 0xcafe0000
or dst, src -> dst와 src의 비트 중 하나라도 1이면 1, 아니면 0
[Register]
eax = 0xffff0000
ebx = 0xcafebabe
[Code]
or eax, ebx
[Result]
eax = 0xffffbabe
xor dst, src -> dst와 src의 비트가 서로 다르면 1, 같으면 0
[Register]
eax = oxffffffff
ebx = oxcafebabe
[Code]
xor eax, ebx
[Result]
eax = 0x35014541
not op -> op의 비트 전부 반전
[Register]
eax = 0xffffffff
[Code]
not eax
[Result]
eax = 0x00000000
비교
비교 명령어는 두 피연산자의 값을 비교하고, 플래그를 설정한다.
cmp op1, op2 -> op1과 op2를 비교
cmp는 두 피연산자를 빼서 대소를 비교한다. 연산의 결과는 op1에 대입하지 않는다.
예를 들어, 서로 같은 두 수를 빼면 결과가 0이 되어 ZF플래그가 설정되는데, 이후에 CPU는 이 플래그를 보고 두 값이 같았는지 판단할 수 있다.
[Code]
1: mov rax, 0xA
2: mov rax, 0xA
3: cmp rax, rbx //ZF=1
test op1, op2 -> op1과 op2를 비교
test는 두 피연산자에 AND 비트연산을 취한다. 연산의 결과는 op1에 대입하지 않는다. 예를 들어, 아래 코드에서 처럼 0이된 rax를 op1과 op2로 삼아 test를 수행하면, 결과가 0이므로 ZF플래그가 설정된다. 이후에 CPU는 이 플래그를 보고 rax가 0이었는지 판단할 수 있다.
[Code]
1: xor rax, rax
2: test rax, rax //ZF=1
분기
분기 명령어는 rip를 이동시켜 실행흐름을 바꾼다.
jmp addr : addr로 rip이동
je addr : 직전 비교에서 두 피연산자의 값이 같을 경우 addr로 rip이동
jg addr : 직전 비교에서 피연산자 중 전자의 값이 더 클 경우 addr로 rip이동