[Reversing] 어셈블리어 기초

Mong22·2023년 3월 21일
0
post-thumbnail

어셈블리어란?

  우리가 평소에 이용하는 C언어나 파이썬으로 코딩한 파일은 그대로 실행할 수 없고, 컴퓨터가 이해할 수 있는 기계어로 바꿔줘야 한다. 이러한 기계어를 사람이 알아보기 쉬운 니모닉 기호를 통해 표현한 언어를 어셈블리어라고 한다. 이런 어셈블리어는 Intel과 AT&T 문법으로 나뉘는데, 이번 글은 Intel 문법에 기반해 정리할 예정이다.

기계어는 컴퓨터가 이해할 수 있는 언어로, 0과 1로만 이루어져 있다.


어셈블리어

  어셈블리어는 명령어(Opcode)와 피연산자(Operand)로 구성된다. 어셈블리 코드는 항상 다음과 같은 형태로 작성된다.

Opcode   Operand1, Operand2

이 때, Operand1은 Destination, Operand2는 Source이다. 예를들어, 데이터를 이동시키는 mov 명령어의 경우, "mov eax, ebx" 형태로 사용되며, ebx의 값을 eax로 이동시키라는 의미를 담고 있다.

  어셈블리어를 보면 종종 BYTE PTR [address], WORD PTR [address] 등을 볼 수 있다. 이는 포인터로, 해당 주소에 들어있는 값을 이용할 때 사용된다. 종류는 BYTE PTR, WORD PTR, DWORD PTR,, QWORD PTR이 있으며, 각각 1 byte, 2bytes, 4bytes, 그리고 8bytes를 의미한다. 이는 값을 옮길 때, 저장 공간의 크기를 명시하여 실수를 줄이기 위해 사용된다.


레지스터란?

  레지스터란 데이터를 저장하는 메모리 중 하나다. 레지스터는 CPU에 포함되어 있으며, CPU가 연산을 수행하는 데 필요한 데이터를 저장한다. 레지스터의 종류는 매우 다양하며, 절대적이진 않으나 각각 고유한 기능을 담당하고 있다. 레지스터는 마치 RAX, EAX, AX, AH, AL처럼 비트 크기에 따라 다른 이름으로 불린다. 하지만 모두 동일한 레지스터임을 기억하자.


범용 레지스터

  • EAX: 사칙연산 등 산술 연산에 사용되며, 함수의 반환 값을 처리할 때도 사용된다.
  • EBX: 간접 주소 연산에 사용되고, 산수, 변수를 저장한다.
  • ECS: 반복문에서 반복 카운트의 역할을 수행한다.
  • EDX: EAX를 보조한다. 예를들어, 나누기에서 몫은 EAX에, 나머지는 EDX에 저장된다.

  추가적으로 R8부터 R15까지의 레지스터가 존재하는 데, 이 또한 범용 레지스터로 다양한 용도로 사용된다.

인덱스 레지스터

  • ESI: 복사나 비교를 할 경우 출발지 주소를 저장한다.
  • EDI: 복사나 비교를 할 경우 목적지 주소를 저장한다.

포인터 레지스터

  • EIP: 다음 실행할 명령어의 주소를 가지고 있다. 현재 실행하는 명령어가 끝나면 이 주소로 이동한다.
  • ESP: 스택 포인터로, 가장 최근에 저장된 공간의 주소를 저장한다.
  • EBP: 스택 포인터로, 스택의 가장 바닥 부분의 주소를 저장한다.

EFLAGS 레지스터

  EFLAGS 레지스터의 경우, 값이 저장되는 것이 아니라, 0과 1로 이루어져 해당 플래그들이 의미하는 바와 레지스터 속의 비트를 통해 의미를 해석하는 것이다.

  이러한 EFLAGS 레지스터들은 사칙 연산, 논리 연산 등에서 이용된다.


어셈블리어 명령어



  조건 점프 명령어의 경우, 이전에 cmp 또는 test 명령어를 작성해야 작동된다.

  함수가 호출되고 인자가 전달될 때, RDI, RSI, RCX, R8, R9 순서로 인자가 전달되며, 남는 것은 스택에 쌓이게 된다.


메모리 구조

  위의 그림은 프로그램이 운영체제로부터 할당받는 RAM의 메모리 구조다.

Code

  코드 영역은 실행할 프로그램의 코드가 기계어 형태로 저장된다. CPU는 코드 영역에 저장된 명령어를 하나씩 가져가 처리한다. 프로그램이 끝날 때까지 메모리 공간에 남아있는다.

Data

  프로그램의 초기화된 전역 변수와 정적 변수, 상수가 저장된다. 상수의 경우 데이터 영역 상의 낮은 메모리 주소에 저장되며, 변수의 경우 데이터 영역 상의 높은 메모리 주소에 저장된다.

BSS

  BSS영역에는 초기화되지 않은 전역 변수와 정적 변수가 저장된다.

Heap

  Heap 영역의 경우, 사용자가 직접 관리하는 영역이다. malloc 혹은 new 연산자와 free 또는 delete 연산자를 통해 메모리 공간을 동적으로 할당하고 해제한다. 따라서 런타임에 크기가 결정된다. 메모리의 낮은 주소에서 높은 주소로 성장해나간다.

Stack

  Stack 영역의 경우, 함수의 호출과 관계되는 지역 변수와 매개 변수가 저장된다. 함수의 호출과 함께 할당되고 함수의 호출이 끝나면 소멸한다. 이 때, 함수 별로 스택 영역에 값이 저장되는 데, 각각의 함수 별 스택 데이터를 모아서 스택 프레임이라고 부른다. 컴파일 할 때 크기가 결정된다. 메모리의 높은 주소에서 낮은 주소의 방향으로 성장해나간다.


  메모리 구조 그림을 보면 Heap과 Stack이 서로를 바라보는 방향으로 성장해나가는 것을 볼 수 있다. Heap과 Stack은 사용 공간을 공유한다. 따라서 Heap이 많은 공간을 차지하면 Stack을 그만큼 적게 사용해야 하고, 반대도 마찬가지다. 이 때, 만약 Heap이 Stack의 영역을 넘어가버리면 Heap overflow, Stack이 Heap의 영역을 넘어가버리면 Stack overflow라고 한다.


Reference

  해당 글 속 내용과 사진들은 아래 주소들을 바탕으로 제작되었습니다.
https://coding-factory.tistory.com/651
https://blog.naver.com/mjnms/220460825993
https://live2skull.tistory.com/16
https://velog.io/@hidaehyunlee/%EB%A9%94%EB%AA%A8%EB%A6%AC-%EA%B5%AC%EC%A1%B0%EB%A5%BC-%EC%95%8C%EC%95%84%EB%B3%B4%EC%9E%90

profile
Wah!

0개의 댓글