PWN1

0.2. C언어보다 넓은 어셈블리어 세계

컴퓨터를 배웠다면 누구나 한번쯤은 C언어라는 것을 접하게 됩니다. C언어를 통해 이미 입출력을 학습하였다면 콘솔창에서 아래와 같은 구문이 익숙하겠죠.

 ---

printf(“Hello world!”);

 ---

여기서 몇 가지 의문이 드는 것이 있습니다. printf 함수는 도대체 어떻게 실행되는걸까요또 안에 있는 문자열은 어떻게 시스템 상에서 전달받게 되는 걸까요마지막으로이런 함수는 어디에서 불러오는 것일까요?

이것의 답은 바로 실행파일의 구조와 실행과정에서 알 수 있습니다.

나중에 설명하겠지만궁금증을 해결하기 위해 잠깐 프로그램의 실행 과정을 보여드리겠습니다.

(사진1)

정체불명의 16진수 숫자들 사이에 printf 함수를 호출하는 부분과 Hello world!라는 문자열이 전달되고 있다는 것을 대강은 알 수 있습니다모두 실행 파일 내부에서 일어나고 있는 일들이었습니다.

 

위와 같은 과정을 거치기 위해서는 C언어 코드가 컴퓨터가 이해할 수 있는 단순한 언어로 변환되어야 합니다여기서 C언어와 같이 사용자가 이해하기 쉬운 언어는 고급언어기계어와 같이 컴퓨터가 이해하기 쉬운 언어는 저급언어라고 부릅니다결론적으로컴퓨터가 C언어 코드를 이해하기 위해서는 고급언어를 저급언어로 변환하는 과정이 필수적이라고 할 수 있겠습니다.

이 과정을 컴파일이라고 합니다.

 (사진2)

컴파일 과정에서는 사용자가 이해할 수 있는 C언어 코드를 기계어로 변환합니다.그러나 이 기계어는 숫자들의 조합으로 이루어져 있어 이해하기 어렵습니다그래서 이것을 이해가 가능하게끔 간단한 기호와 명령으로 표현하게 됩니다.

이 언어를 어셈블리어라고 합니다.

 

어셈블리어는 기계어 1라인 당 어셈블리 명령어 1라인씩 대응된다는 특징이 있습니다기계어와 어셈블리어는 일대일대응 관계라는 것을 알 수 있죠어셈블리어는 컴퓨터의 종류에 따라그리고 문법에 따라 그 종류가 다양합니다주로 사용하는 CPU 8086 CPU에서는 Intel, AT&T 문법을 제공합니다위의 사진에서는 AT&T 방식으로 어셈블리어를 출력해 주었습니다.가독성과 편리성을 위해 지금부터는 Intel 방식으로 설명하도록 하겠습니다.

 

그렇다면 어셈블리어 명령어의 종류에는 어떤 것이 있을까요어셈블리어는 사용자가 기계어를 이해할 수 있도록 아주 간단하게 바꾼 것이기 때문에 단순 연산이나 호출정리를 위한 명령어로 구성되어 있습니다.

대표적인 명령어를 아래에 정리해 보겠습니다.

명령어 (command)

의미 (syntax)

예시 (example)

push

stack에 전달된 값을 저장, esp가 내려감

push eax

pop

esp 위치에 있는 값을 특정 레지스터에 저장, esp가 올라감

pop edx

mov

레지스터나 메모리에 값을 저장

mov eax, 0 / mov ebp, esp

lea

레지스터나 메모리에 변수의 주소를 저장

lea eax, dword ptr ss:[ebp-4]

inc

값을 증가하여 저장

inc eax

dec

값을 감소하여 저장

dec eax

add

레지스터나 메모리의 값으로 덧셈을 함

add eax, 5

sub

레지스터나 메모리의 값으로 뺄셈을 함

sub eax, ebx

mul

레지스터나 메모리의 값으로 곱셈을 함

mul eax, 4

call

다른 함수를 호출함

call 0x401040

ret

호출했던 부분으로 다시 돌아감

ret

cmp

두개의 레지스터 또는 값을 비교

cmp eax, edx / cmp eax, 0

jmp

특정 주소로 프로그램 흐름을 바꿈

jmp 0x8048720

int

OS의 인터럽트 영역을 system call

int 0x80

nop

아무것도 실행하지 않음

nop

 

결과적으로 C언어에서 함수를 호출하는 과정을 어셈블리어의 여러 가지 명령어를 통해 구현할 수 있습니다. 

 

댓글

댓글 본문