Assembly - 1

신우빈·2022년 6월 30일
0

Dreamhack

목록 보기
3/7
post-thumbnail

What is Assembler?

통역사
개발자들이 어셈블리어로 코드를 작성하면 컴퓨터가 이해할 수 있는 기계어로 코드를 변환시켜주는 것.

Input : Assembly code => Output : 기계어

Disassembler

역어셈블러 : 기계어로 구성된 SW를 disassmbler에 입력 => Assembley code

Input : 기계어 => Output : Assembly Code

x86-64를 비롯한 대중적으로 많이 사용되는 아키텍처들은 시중에서 Disassembler 구하기 쉬움.
! 따라서 ! 어셈블리어만 이해할 수 있다면, Disassembler를 이용해 SW 분석 가능


1. Assembly

컴퓨터의 기계어와 치환되는 언어. 종류가 매우 다양하다.
x64 아키텍처를 대상으로 하는 x64 assembly를 다룸.
ARM의 세계에는 ARM Assembly가 존재한다.

즉, CPU에 사용되는 ISA에 따라 Assembly가 달라진다


2. x64 Assembly

기본 구조
명령어(Operation Code, Opcode)[동사] & 피연산자(Operand)[목적어]

예시

mov eax, 3
  • mov : 명령어. 대입하라!
  • eax, 3 : 피연산자. eax에 3을

2-1 피연산자

  1. 상수 ( Immediate Value )
  2. 레지스터 ( Register )
  3. 메모리 ( Memory )
  • 메모리 피연산자는 [ ] 에 둘러싸여 표현
  • 크기 지정자 ( Size Directive ) TYPE PTR 사용 가능
    o TYPE에는 BYTE, WORD, DWORD, QWORD 가 있으며 각각 1, 2, 4, 8 byte의 크기를 가짐

예시

예시설명
QWORD PTR [0x8048000]0x8048000의 데이터를 8byte 만큼 참조
DWORD PTR [0x8048000]0x8048000의 데이터를 4byte 만큼 참조
WORD PTR [rax]rax가 가리키는 주소에서 데이터를 2byte 만큼 참조

2-2 명령어

한 번에 보기

21개의 명령어

종류코드
데이터 이동 - Data Transfermov, lea
산술 연산 - Arithmeticinc, dec, add, sub
논리 연산 - Logicaland, or, xor, not
비교 - Comparisoncmp, test
분기 - Branchjmp, je, jg
스택 -Stackpush, pop
프로시져 - Procedurecall, ret, leave
시스템 콜 - System callsyscall

데이터 이동

어떤 값을 레지스터나 메모리에 옮기도록 지시

코드설명
mov dst, srcsrc에 들어있는 값을 dst 에 대입
mov rdi, rsirsi의 값을 rdi 에 대입
mov QWORD PTR[rdi], rsirsi 값을 rdi 가 가리키는 주소에 대입
mov QWORD PTR[rdi+8*rcx], rsirsi의 값을 rdi+8*rcx 가 가리키는 주소에 대입
lea dst, srcsrc의 유효 주소를 dst에 저장
lea rsi, [rbx+8*rcx]rbx+8*rcx를 rsi에 대입

참고 자료

  • rdi : destination index, 데이터 옮길 때 목적지를 가리키는 포인터
  • rcx : counter register, 반복문의 반복 횟수, 각종 연산의 실행 횟수
  • rbx : base register, x64에서는 주된 용도 X
  • 유효 주소 = Effective Address(EA)

산술 연산

덧셈, 뺄셈, 곱셈, 나눗셈 연산을 지시

  1. add dst, src : dst 에 src 의 값을 더함
  2. sub dst, src : dst 에서 src 의 값을 뺀다.
  3. inc op : op의 값 1 증가
  4. dec op : op의 값 1 감소
코드설명
add eax, 3eax += 3
add ax, WORD PTR[rdi]ax += *(WORD*)rdi
sub eax, 3eax -= 3
sub ax, WORD PTR[rdi]ax -= *(WORD*)rdi
inc eaxeax += 1
dec eaxeax -= 1

논리 연산

and, or, xor, neg 등의 비트 연산을 지시하며, 연산은 비트 단위로 이루어짐.
즉, 모든 값들은 2진수로 변환한 후 계산

AND

add dst, srt : dst와 src의 비트가 모두 1이면 1, 아니면 0

[Register]
eax = 0xffff0000
ebx = 0xcafebabe

[Code]
and eax, ebx

[Result]
eax = 0xcafe0000

OR

or dst, src : dst와 src의 비트 중 하나라도 1이면 1, 아니면 0

[Register]
eax = 0xffff0000
ebx = 0xcafebabe

[Code]
or eax, ebx

[Result]
eax = 0xffffbabe

xor

xor dst, src : dst와 src의 비트가 다르면 1, 같으면 0

[Register]
eax = 0xffff0000
ebx = 0xcafebabe

[Code]
xor eax, ebx

[Result]
eax = 0x35014541

not

not op : op의 비트 전부 반전

[Register]
eax = 0xffffffff

[Code]
not eax

[Result]
eax = 0x00000000

비교

두 피연산자의 값을 비교하고, 플래그를 설정
cmp

cmp op1, op2 : op1과 op2를 비교
cmp 는 두 피연산자를 빼기를 통해 대소를 비교한다. 연산 결과 op1에 대입 X

[Code]
1: mov rax, 0xA
2. mov rbx, -XA
3. cmp rax, rbx

3의 결과로 ZF

test

test op1, op2 : op1과 op2를 비교
test 는 두 피연산자에 AND 비트 연산을 취한다. 연산 결과 op1에 대입 X

1: xor rax, rax
2: test rax, rax
  • xor rax, rax를 통해 rax = 0이 된다.
  • rax가 0으로 통일된 상태에서 test 하면 AND 연산의 결과가 1임. 이를 통해 같음을 알 수 있다.

참고 자료

  • ZF : Zero Flag, 연산의 결과가 0인 경우 설정
  • rip : 레지스터의 한 종류. 프로세서가 읽고 있는 현재 명령의 위치를 가리키는 명령 포인터. 즉, 현재 명령 실행 주소

분기

rip를 이동시켜 실행 흐름을 바꾼다.

jmp

jmp addr : addr로 rip를 이동

je

je addr : 직전에 비교한 두 피연산자가 같으면 점프. jump if equal

[Code]
1: mov rax, 0xcafebabe
2: mov rbx, 0xcafebabe
3: cmp rax, rbx
4: je 1

rax == rbx. 따라서 je 의 결과는 jump to 1

jg

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

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

rax > rbx. 따라서 jg 의 결과는 jump to 1

0개의 댓글