본문 바로가기

Reversing/Dreamhack : Reverse Engineering

x86 Assembly🤖: Essential Part

x64 어셈블리 언어

기본 구조🧱

동사에 해당하는 Operation Code, OP (명령어)와 목적에 해당하는 Operand (피연산자)로 구성

 

명령어🔫

Data Transfer 데이터 이동
mov, lea
Arithmetic 산술 연산
inc, dec, add, sub
Logical 논리 연산
and, or, xor, not
Comparison 비교
cmp, test
Branch 분기
jmp, je, jg
Stack 스택
push. pop
Procedure
call, ret, leave
System call 시스템 콜
syscall

 

피연산자🎯

  • 상수(Immediate Value)
  • 레지스터(Register)
  • 메모리(Memory)

 

크기 지정자 (Size Directive)

BYTE
1byte = 8bit
WORD
2byte = 16bit
DWORD
4byte = 32bit
QWORD
8byte = 64bit

 

메모리 피연산자 예시

QWORD PTR [0x8048000]
0x8048000의 데이터를 8바이트만큼 참조
DWORD PTR [0x8048000]
0x8048000의 데이터를 4바이트만큼 참조
WORD PTR [rax]
rax가 가르키는 주소에서 데이터를 2바이트 만큼 참조

 

x86-64 어셈블리 명령어

 

데이터 이동🚚 - 어떤 값을 레지스터나 메모리에 옮기도록 지시

 

1. mov dst, src : src에 들어있는 을 dst에 대입

mov rdi, rsi
rsi의 값을 rdi에 대입
mov QWORD PTR[rdi], rsi
rsi의 값을 rdi가 가리키는 주소에 대입
move QWORD PTR[rdi+8*rcs], rsi
rsi의 값을 rdi+8*rcs가 가리키는 주소에 대입

2.  lea dst, src : src의 Effective Address,EA (유효주소)를 dst에 저장

lea rsi, [rbx+8*rcx]
rbx+8*rcx 를 rsi에 대입

 

산술 연산❌

  1. add dst, src : dst = dst + src
  2. sub dst, src : dst = dst - src
  3. inc op : op++
  4. dec op : op—

 

논리 연산🤔

  1. and dst, src : dst와 src가 모두 1이면 1, 아니면 0
  2. or dst, src : dst와 src 중 하나라도 1이면 1, 아니면 0
  3. xor dst, src : dst와 src의 비트가 다르면 1, 같으면 0
  4. not op : op 비트 반전

 

비교⚖️

1. cmp op1, op2

  • 두 피연산자를 빼서 대소를 비교함
  • 연산 결과를 op1에 대입하는 것은 아님
[Code]
1: mov rax, 0xA
2: mov rbx, 0xA
3: cmp rax, rbx ; ZF=1

 

2. test op1, op2

  • 두 피연산자에 AND 비트연산을 취함
  • 연산 결과를 op1에 대입하지 않음

 

분기🔀

  • rip(⇒ 현재 실행되고 있는 코드를 가리킴)을 이동시켜 실행 흐름을 바꿈
  1. jmp addr : addr로 rip을 이동시킴
  2. je addr : 직전 비교한 두 피연산자가 같으면 점프 (jump if equal)
[Code]
1: mov rax, 0xcafebabe
2: mov rbx, 0xcafebabe
3: cmp rax, rbx ; rax == rbx
4: je 1 ; jump to 1

 

   3. jg addr : 직전에 비교한 두 연산자 중 전자가 더 크면 점프 (jump if greater)

[Code]
1: mov rax, 0x31337
2: mov rbx, 0x13337
3: cmp rax, rbx ; rax > rbx
4: jg 1  ; jump to 1

 

 

Opcode: 스택🧱

  1. push val : val을 stack 최상단에 쌓음
  2. pop reg : stack 최상단 값을 꺼내서 reg에 대입

 

Opcode: 프로시저📜

  • Procedure 프로시저 : 특정 기능을 수행하는 코드 조각 == 함수
  • Call 호출 : 프로시저를 부르는 행위
  • Return 반환 : 프로시저에서 돌아오는 것
  • 프로시저를 호출할 때는 프로시저를 실행하고 나서 원래의 실행 흐름으로 돌아와야하므로, call 다음의 명령어 주소 ( = 반환 주소, Return Address)를 Stack에 저장하고 프로시저로 rip을 이동시킴
  1. call addr : addr에 위치한 프로시져(함수) 호출
  2. leave : 스택프레임 정리
  • 스택프레임 : 스택은 함수별로 자신의 지역변수 또는 임시값들을 저장하는 영역, 만약 스택 영역을 아무런 구분 없이 사용하면, 서로 다른 두 함수가 같은 메모리 영역을 사용하게될 수도 있음 → 스택의 영역을 명확히 구분하기 위해서 스택 프레임이 사용됨. 보통 함수는 호출될 때 자신의 스택 프레임을 만들고 반환할 때 이를 정리함.

    3. ret : return address로 반환