[9] 어셈블리 스택 메모리를 이용한 간단한 덧셈 ( leave, ret, call )

2017. 10. 27. 20:01SystemHacking/Assembly


C언어 코드를 어셈블리코드로 표현하자


[ C언어 코드 ]


 int sum( int a, int b ){

    int sum = 0 ;

    sum = a + b ;

    return sum ;

 }


 int main(){

   int a = 10;

   int b = 20;

   int ret = 0;

 

   ret = sum(a,b);

   printf("Sum : %d\n",ret );

   return 0 ;

 }  



[ 어셈블리 코드 ] 

extern printf


section .data

prompt_hex:     db      '0x%08x',10,00

prompt_int:     db      '%d',10,00

prompt_sum:     db      'Sum is : %d',10,00

section .text

global  main

sum:

push    ebp

mov     ebp,    esp             ; function prologue

sub     esp,    4                  ; 지역변수 공간 확보 , 스택공간을 활용안하니까 현재 함수에서는 없어도 된다 


mov     eax,    dword [ebp+8]        ; eax = 20          

add      eax,    dword [ebp+12]      ; eax = eax + 10


; function epilogue

leave                                         ; mov    esp,    ebp           할당해줬던 공간 회수 

                                                       ; pop    ebp                     ebp <= saved ebp

ret                                            ; pop eip                         다음 실행 할 명령어의 주소를 꺼내온다

                                                        ; sum 을 호출하기 전의 ebp를 얻고, 순서대로 명령어가 실행된다

main:

push    ebp

mov     ebp,    esp

sub     esp,    12                    ; function prologue 지역변수 공간을 12byte 확보한다


mov     dword [ebp-4],  10      ; a = 10

mov     dword [ebp-8],  20      ; b = 20

mov     dword [ebp-12], 0       ; ret = 0      스택의 지역변수 공간에 변수들 저장한다


push    dword [ebp-8]

push    dword [ebp-4]           ; 저장해 두었던 변수들을 스택에 push, pop으로 변수들을 꺼내서 사용할거다


call    sum                  ; push eip + jmp sum , 원래 다음 실행할 명령어의 주소를 push 하고, 이동한다


mov     dword [ebp-12], eax     ; ret = sum  , eax레지스터에는 sum레이블에서 계산한 값이 들어있다

push    dword [ebp-12]

push    prompt_sum

call    printf                  ; printf("sum is: %d \n",ret );

xor     eax,    eax             ; return 0

       

        ; epilogue

leave                           ; mov esp, ebp   할당했던 지역변수 공간을 회수한다

                                         ;  pop ebp         기존의 ebp위치로 돌아온다

ret


 



[ 스택 메모리 구조 ] - 그림을 참고해서 코드를 보면 이해하기 쉽다

 스택 메모리 모습

 어셈블리 코드

 [ sum ] saved ebp 

 [sum 함수]  push ebp

  return address ( saved eip )

 [main 함수] call sum ( = push eip + jmp sum )

 [ main ] 10

 [main 함수] push dword [ebp-4]

 [ main ] 20

 [main 함수] push dword [ebp-8]

 [ main ] ebp-12

 [main 함수] mov dword [ebp-12] ,20

 [ main ] ebp-8

 [main 함수] mov dword [ebp-8] ,10

 [ main ] ebp-4

 [main 함수] mov dword [ebp-4] ,0

 [ main ] saved ebp

 [main 함수] push ebp



# 스택 메모리는 함수가 실행될 때 변수가 저장될 공간을 확보해서 고정된 크기를 가지게 된다 ( function prologue sub esp,12 부분 )

# 스택 메모리는 4byte씩 메모리가 쌓이게 된다

# push    스택 메모리에 데이터를 집어 넣는다

# pop    스택 메모리에서 꺼내온다 ( pop ebx => esp가 가리키고 있는 스택 ( 즉, 제일 위에 있는 데이터 ) 을 가지고 와서 ebx에 저장한다 )



# leave와 ret은 함수가 끝나면 항상 있어야 하는 함수의 에필로그 부분이다

# 어셈블리에서는 함수라는 개념이 없다. 즉, sum( 인자1, 인자2 ) => x, 직접 코드를 작성하고 컴파일러가 그 코드를 읽도록 이동시켜야한다


[ leave 명령어 ]

mov    esp,    ebp              ; sub esp,12 로 지역변수에게 할당했던 공간을 회수해준다

pop    ebp                        ;  저장해두었던 함수 실행 전의 ebp(base pointer) 를 가져와 ebp를 원래상태로 돌려준다 


[ ret 명령어 ]

pop eip                            ; eip 레지스터는 다음 실행할 명령어의 주소를 저장하는 레지스터이다

                   ; call 명령어를 사용할 때 push eip 되었던 스택을 가져오는 명령어이다

    

[ call 명령어 ]

push    eip                        ; 다음 실행할 명령어를 스택에 넣어준다 .( 쉽게 말하면, 함수 호출 call 명령 바로 아래 코드 ! )

jmp    [ 레이블 ]                 ; 실행시킬 함수의 주소로 이동한다