본문 바로가기

Language/_Assembly

상황별 기초 어셈블리어

어셈블리 명령어를 상황별로 어떻게 사용되는지 정리해보며 공부해보자.



1. 데이터 이동 :

어셈블리에서 데이터를 옮기고자 할때는 MOV 명령어를 사용한다.

MOV [복사될 곳], [읽어들일 곳]

MOV EAX, EBX의 경우 EBX 레지스터를 EAX에 대입한다.
MOV EAX, [EBX]로 표현되어 있는 경우에는 EBX가 가리키는 값을 EAX에 대입한다.
MOV EAX, [EBP+10]의 경우 EBP의 주소에서 20만큼 증가한 주소지의 값을 EAX에 대입한다
MOV EAX, [EBP-10]의 경우 EBP의 주소에서 20만큼 감소한 주소지의 값을 EAX에 대입한다
[EBP+10]과 [EBP-10]의 경우에 함수 내부에 존재하는 명령이었다면 EBP를 기준으로 10 증가된 경우에는 파라미터로 넘어오는 값일 수 있고, 10 감소한 경우에는 함수 내부에서 쓰이는 지역변수일 수 있다.
LEA EAX, [EBP+10] 명령의 경우 EBP 주소에서 10만큼의 주소지를 더한 값이 아니라 EBP의 주소지 값에서 10을 더한 값을 EAX로 대입한다.



2. 전역변수 :

전역변수의 경우 data 섹션에 저장이 되고, 프로그램을 초기화하는 과정에서 세팅되거나 실행중에 변경이 될 수도 있다. 예를 들어 문자열이 참조되는 방법은

.data:0088A1A1  'This Program is powerful' , 0

data 섹션의 문자열이 이렇게 존재하면

.text:006A2A22 mov eax, 0x88A1A1h
.text:006A2A27 retn



3. 비교구문 cmp, test :

cmp 명령어는 주어지는 두 값을 뺄셈을 하여 처리하고, test 명령어는 주어지는 두 값을 논리적 AND 연산해서 처리하는 점이 다르며, 두 명령어가 조건 분기문을 결정하기 위해서 사용된다는 점은 같다.

cmp eax, ebx는 두 값을 뺴서 0이면 참(같은 값)이 되는 형태이다.

test 연산은 보통 호출된 함수들이 일반적으로 리턴값을 EAX 레지스터에 저장하는 것을 이용하여, 다음과 같이 사용된다.

CALL function
TEST eax, eax
JZ 주소지

함수를 call하고, 함수의 리턴값으로 EAX값이 세팅되면, 비교를 하는 것이다.

cmp가 영향을 미치는 플래그들은 ZF, OF, SF, CF이고 만약 ZF 플래그가 ZF=1로 세팅되었다면 주어진 두 값이 같았다는 것을 의미한다.
그 외에 같지 않았을 경우에는 OF, SF, CF를 이용해서 어느 쪽이 더 큰지를 알 수 있다.

test가 영향을 미치는 플래그는 SF, ZF, PF가 이고, test 명령어는 같은 피연산자를 가지게 되는데 그러므로 중요한것은 피연산자가 0이냐, 0이 아니냐하는 것이다. 0이면 ZF가 1로 세트되고, 0이 아니면 ZF가 0으로 세트된다.



4. 분기문의 사용 :

분기문은 일반적으로 점프 구문을 이야기하며, if문을 어떻게 사용하는지에 따라서 달라지는 어셈코드들을 살펴보겠다.

if 조건문은 if(rsult == 0)라면 다음과 같이 변경된다.

cmp dword ptr [ebp-4], 1
jne 00401234

cmp로 비교를 한 후에 점프 구문이 된다.


시언어 소스
if(변수==0){
          if문 내의 함수 호출
}
이후 동작

어셈블리어 변환
mov eax, 변수값
test eax, eax
jnz 이후 동작
if문 내의 함수 호출

if에서 사용되는 변수를 먼저 mov로 값을 대입하고, test로 비교(즉 eax값이 0이냐 0이 아니냐를 판단)를 한 다음에 JNZ를 이용하여 이후 동작을 할지 if문 내부의 함수를 호출할지를 결정한다. 이외에 if문은 if 다음에 else 구문이 여러 번 더 나타날 수 있다는 점을 고려해서 비슷한 방식으로 분석하면된다.



5. 반복문의 사용 :

같은 내용이 반복되어 처리되다가 반복문을 종료하는 비교문을 만나서 점프하여 반복문을 벗어나도록 구현 있다.

loop :
      mov al, [ecx]
      mov [edx], al
      inc ecx
      inc edx
      cmp al, 'c'
      jnz short loop

ECX와 EDX는 메모리상의 문자열을 지칭하는 포인터이고, ECX가 가리키는 문자열에서 EDX가 가리키는 문자열로 복사가 일어나고 있다다. 반복을 할 때마다 포인터가 둘 다 1씩 증가되고, 'c'문자열이 나타날 때까지 복사를 하는 것으로 생각할 수 있다.


for 반복문은 for(int i = 1 ; i<= param ; i++)이라면
mov dword ptr [ebp-8], 1        //  i 변수 = 1
jmp 004017FF
mov eax, dword ptr [ebp-8]    //  i 변수를 eax에 대입
add eax, 1                             //  eax + 1
mov dword ptr [ebp-8], eax    //  eax + 1을 i에 대입      
mov ecx, dword ptr [ebp-8]    //  ecx로 카운팅을 한다.
cmp ecx, dword ptr [ebp+8]    //  종료인지 체크
jg 00401812

어셈블리코드를 보면 i 변수의 값이 변경되고, 반복하면서 카운팅을 하는 것을 알 수 있다.
그리고 카운팅을 하는 변수가 반복 횟수를 초과했는지를 cmp로 검사해서 반복 구문을 빠져나온다.



6. 함수의 사용 :

함수는 시작하는 부분과 끝나는 부분을 파악할 수 있는 구분자가 있어서 확인이 어렵지 않다. 시작하는 부분의 코드를 보자.

push ebp
mov ebp, esp
sub esp, 20

EBP값은 스택에 넣어두고, 현재 ESP값을 EBP에 대입하고, 지역변수를 만들 공간을 확보하기 위해서 ESP값을 변경한다.
그리고 끝나는 부분은 호출이 끝나서 리턴되기 때문에 RET가 쓰이게 된다.

mov eax, -1
mov esp, ebp
pop ebp
ret

이와 같이 ESP는 원래 값으로 돌아가고, EBP는 저장되었던 값으로 복구된다. 그런 후에 RET를 이요하여 함수를 CALL했던 부분으로 다시 돌아간다. 그리고 eax에 -1을 넣어서 결과가 -1임을 표현해주기도 한다.


상황에 따른 어셈블리 명령어에 사용법에 대해 알아보았다.
어셈블리어에 익숙해지도록 쉬운 C소스를 어셈블리어로 바꾸는 연습을 하자.

'Language > _Assembly' 카테고리의 다른 글

함수 호출 과정  (0) 2010.03.06
어셈블리 디자인을 통한 컴퓨터 구조의 접근  (0) 2010.02.09
스택과 콜링컨벤션의 이해  (0) 2009.10.26
기초 어셈블리어.  (11) 2009.10.25