Notice
Recent Posts
05-06 04:38
«   2024/05   »
1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31
Today
Total
관리 메뉴

프로그래밍 잡화점

0 - 2 기본적인 Assembly 개념 - 문법과 instruction Part 1 본문

Assembly

0 - 2 기본적인 Assembly 개념 - 문법과 instruction Part 1

Luana7 2022. 9. 29. 22:08

어셈을 통한 구조 분석을 하기 위해서는 먼저 역어셈된 파일을 분석할 줄 알아야 한다.

이에 이번 포스팅에서는 문법과 자주 보이는 명령어들을 알아볼 것이다.

이전 포스팅에서도 말했듯이 intel nasm을 기반으로 작성하였다.

(* 분석을 위한 설명 위주로 진행하며, 추후 가능하다면 어셈블리 강좌를 만들때 자세히 설명 할 생각이입니다)


Syntax

x86/x64 nasm 어셈블리는 기본적으로 다음과 같은 구조를 지닌다

section .data
    ; 초기값을 갖는 전역 변수 선언

section .bss
    ; 비어있는 전역 변수 선언

section .text
    ; 함수를 외부에 노출
    ; ld링킹의 경우 _start를 사용
    ; gcc링킹의 경우 main을 사용
    global _start

; _start 레이블
_start:
    ; Write Code here

_start를 쓰냐 main을 쓰냐는 추후 따로 포스팅 할 예정이다.

일단 우리의 목표는 C 역어셈 분석을 위한 것이기 때문에 gcc 링킹에 따라 시작점이 main으로 잡힐 것이다.

gcc의 경우 bss와 data를 사용하지 않으므로 우리는 main만 보면 된다.

어셈블리에서 명령어는 instruction이라고 한다. 검색할 때 참고하자.

어셈블리에 instruction은 다음과 같이 사용한다

inst op1, op2

inst에는 instruction을, op1과 op2에는 레지스터나 변수를 넣어주면 된다.

instruction에 따라서 op2를 안넣을 수도 있으며 op1을 생략할 수 있거나 둘 다 없는 경우도 존재한다.

다음 4가지 예제를 보자

mov eax, 0
int 0x80
ret
nop

3번째의 ret의 경우 인자를 쓸 수도 있고 안 쓸수도 있다.

intel 문법에서는 op1이 dest(목적지), op2가 src(소스) 역할을 한다.
대입 연산 처럼 오른쪽에서 왼쪽으로 간다고 생각하면 된다.

Basic Instruction

분석을 위한 아주 기초적인 것들만 알아볼 예정이다.
모르는 것을 발견했다면 검색해서 알아보자

분석을 위한 아주 기초적인 것들만 알아볼 예정이다.
모르는 것을 발견했다면 검색해서 알아보자

들어가기 앞서
기본적으로 어셈블리에는 비슷한 기능을 하면서 형태가 다른 명령어가 존재한다.
예를들어 mov이외에도 비슷한 모양을 갖는 movq movl과 같은 애들이 존재하는데, 이는 데이터의 크기나 형태에 따라 달라지는 것이다.
이 포스팅에서는 기본적인 명령어를 설명한다.
따라서 다음과 같은 명령어는 따로 검색을 통해서 찾아보길 바란다.

1. MOV

어셈블리에서 아주 기본이 되는 명령어로, 단순히 대입해주는 역할을 한다고 보면 된다.

mov eax, 3

다음 코드가 실행되면 eax에는 3이 들어가게 된다.

단순히 C로 치환하면 다음과 같다고 볼 수 있을 것이다.

eax = 3;


2. LEA

이 녀석은 mov와 비슷하다. Load Effective Address의 약자로 변수의 주소를 가져올 수 있다.

lea eax, [ebp-4]

[] 이 표시는 C에서 역참조 연산자인 *이라고 보면 된다.
따라서 다음 코드를 C로 표현하면 다음과 같다.

eax = &*(ebp - 4);


3. 산술 연산

산술 연산에는 4가지가 있다. (+, -, *, /)
(%가 존재하지 않는 이유는 /에서 설명한다.)

덧셈과 뺄셈은 각각 add, sub이며 op1, op2를 가지고 계산하여 op1에다가 넣어준다.

가령 다음과 같은 코드가 있다면

add eax, ebx
sub ecx, edx

C에서는 이러한 코드일 것이다.

eax += ebx;
ecx -= edx;


그 다음으로 알아 볼 것은 곱셈과 나눗셈인데 각각 mul, div이며 op1만 사용한다.
이때, op1은 레지스터여야 한다.

어셈블리는 특이하게 op1만 사용하는데, 그 이유는 바로 대상이 eax(rax)로 고정되기 때문이다.

이게 무슨 얘기냐 하면 다음 예제를 보면 이해할 수 있을 것이다.

eax *= ebx;
eax /= edx;

곱셈과 나눗셈에서 eax는 항상 저 위치에 고정이다.

값을 저장하는 방식 또한 위에 코드와는 조금 다르다.

mul의 레지스터에 따라 저장되는 곳이 달라진다.

  • AL * op1 -> AX
  • AX * op1 -> DX: AX
  • EAX * op1 -> EDX: EAX
  • RAX * op1 -> RDX: RAX

여기서 DX: AX는 두 레지스터를 이어서 저장된다는 것이다.
즉, DX를 왼쪽으로 16만큼 밀고 AX와 or연산을 해줘야 한 레지스터 안에 곱셈한 값이 들어가게 되는 것이다.

div의 경우는 다음과 같다

  • AX / op1 -> AL(몫), AH(나머지)
  • DX: AX / op1 -> AX(몫), DX(나머지)
  • EDX: EAX / op1 -> EAX(몫), EDX(나머지)
  • RDX: RAX / op1 -> RAX(몫), RDX(나머지)

DX: AX로 이어진 것을 op1으로 나눠서 AX와 DX에 저장하는 식이다.

다음 포스팅에서는 이어서 비트연산자와 조건 분기에 대해 다룰 예정이다.

Comments