3. 함수

-함수(function)이라는 개념이 없다.

-스택 메모리의 이해


// sub routine

// procedure -> 절차(x)


int sum( int a, int b)


{

return a+b

}


int main()

{

int result =0;

result =sum(10,20);

printf("sum:%d\n",result);


return 0;

}



지역 변수는 함수가 호출될때 생성되고 끝나면 메모리에서 없어진다

스택메모리를 이용해서 함수를 표시한다.



1. 실행중인 프로세스의 전체 메모리 구조

-basic3.c

#include <stdio.h>


int main()

{


        printf("hello, world\n");

        sleep(100000);


        return 0;

}


실행중일때 메모리 구성이 어떻게 되는지 확인해보면

1.ctrl + z (중지가 아니라 백그라운드로 넘긴다)

2.jobs


3.ps -ef

  1)프로세스 아이디를 확인한다 802번


4.cd /proc/

  1) 802번 디렉토리로 접속하면 정보를 확인할 수 있다


5.cd 802

  1).maps <-실행파일 메모리 공간을 확인할 수 있다.


 




low <-------------------------------------------------------------------> high

0x00000000~0x08048000             사용불가

0x08048000~0x08049000             text segment  (4k) 실행 권한이 r-xp

0x08049000~0x0804aFFF             data segment (.data, .bss, heap 메모리 영역이 포함되어 있음) (4k) 실행권한이 rw-p

                                      ----------- 데이터메모리영역 (a.out)


0x0804aFFF~0x40000000 예약된 메모리 (heap)을 위한


0x40000000~0x40013000 lib

0x40013000~0x40014000

                                        -----------  공유라이브러리 


0x4001c000~0x40109000 libc

0x40109000~0x4010d000

                                        -----------  공유라이브러리

 

0x4010d000~0xbfffe000 예약된 메모리 (stack)을 위한


0xbfffe000~0xbfffffff                  stack(static) 메모리 (지역, 환경변수, 함수가 들어있음)


c0000000 부터는 커널이 사용 (사용할 수 없음)


! heap메모리는 할당할수록 크기가 커지지만 커널은 할당할수록 stack쪽으로 내려온다.


! 중간비어있는 부분은 예약된 메모리


! stack메모리는 함수가 실행될때 크기를 잡으므로 그때그때 필요한만큼 할당해서 쓰므로

  모든 메모리에서 스택을 쓸 경우 stack에서 가져다가 쓴다. (rwx) 모두가 공통으로 사용하는 메모리이다.

  이부분에서 취약점이 발생하는데 stack은 모든 권한을 가지고있다.


메모리 보호기법은 스택의 실행 권한을 뺐는것이다.


stack

LIFO(last in first out)

주소가 낮아지면 낮아질수록 사용량이 많다

push로 스택을 쌓을수 있다.

한번 푸쉬할때 마다 기본적으로 4byte 씩 정렬된다.

pop 로 스택을 가져올수 있다.(맨위에 있는것만 pop할 수 있다)

가장 윗쪽에 있는 부분을 top  위치는 push와 pop에 의해 유동적으로 변한다.



esp 레지스터가 stack pionter <- top의 주소를 나타낸다 유동적으로 계속 변한다. 정렬단위 4 byte

ebp 레지스터 base pointer 한번 설정되면 값이 바뀌지 않는 레지스터(기준점을 잡을때 사용)

스택상에서 주소를 값으로 갖는다.


stack.asm


extern printf



segment         .data

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



segment         .text

global  main


main:

        push    esp

        push    prompt_hex

        call    printf


        push    esp

        push    prompt_hex

        call    printf



결과값 

esp:0xbffffb3c

esp:0xbffffb34

8바이트 차이가 나는데 


push esp , push prompt_hex  두번 push를 하니까 4byte씩 이동한다.


l0xbffffb34l

l    4byte l push prompt_hex              

l    4byte l push esp

l0xbffffb3c  l

l          l push prompt_hex

l         l push esp

l         l      

l0xbfffffff    l



[실습 ] -jmp 를 이용해서 함수 구현


int result =0;

int a,b;



int sum( )

{

return a+b

}


int main()

{

result =sum(10,20);

printf("sum:%d\n",result);


return 0;

}


extern printf


segment         .data

prompt  db      'sum:%d',10,00



segment         .bss

result  resd    1

a       resd    1

b       resd    1



segment         .text

global main



sum:

        mov     eax,    dword [a]

        add     eax,    dword [b]

        mov     dword [result],eax

        jmp     return


main:

        mov     dword [a],      10 ;명령어의 주소가 eip에 들어가 있다. (cpu가 참조)

        mov     dword [b],      20 ;어떤 명령어가 실행할지는 eip를 보고 정한다.

        jmp     sum ;


         call     sum

return:

        push    dword [result] ;jmp sum 을 실행할때 eip 값이 여기 있는데 이걸 stack에 저장해서 사용한다

        push    prompt

        call    printf


이렇게 표현하면 프로그램이 엉망이 되기때문에 쓰면 안된다.






[실습 ] -stack 함수 구현



extern printf


segment         .data

prompt  db      'sum:%d',10,00



segment         .bss

result  resd    1

a       resd    1

b       resd    1



segment         .text

global main



sum:

        mov     eax,    dword [a]

        add     eax,    dword [b]

        mov     dword [result],eax

        ;pop      eip

         ret

main:

        mov     dword [a],      10 ;명령어의 주소가 eip에 들어가 있다. (cpu가 참조)

        mov     dword [b],      20 ;어떤 명령어가 실행할지는 eip를 보고 정한다.

        ;push    eip

        ;jmp     sum

        call     sum


        push    dword [result] ;jmp sum 을 실행할때 eip 값이 여기 있는데 이걸 stack에 저장해서 사용한다

        push    prompt

        call    printf


eip가 다음 수행할 명령어의 주소를 저장해두는 곳인데  만약 jmp sum 하기전에 eip 값을

스택에 저장하면 eip에는 push dword [result] 의 주소가 들어가 있다

jmp sum을 하고나서는 값을 더해주고 다시 push dword [result]로 와야하는데 이때 레이블을 안쓰고도

eip 주소를 이용해서 pop 해주면 돌아올수 있다.


nasm 에서는 eip를 못써서


push eip +jmp sum = call sum

pop eip =ret


이런식으로 사용한다.



Stack 메모리를 static 메모리 라고도 한다.



[실습 ] -stack 이용해서 함수 구현(지역변수 사용)


int sum(int a,int b )

{

return a+b

}


int main()

{

int result =0;

result =sum(10,20);

printf("sum:%d\n",result);


return 0;

}


extern printf


segment         .data

prompt  db      'sum:%d',10,00



segment         .bss



segment         .text

global main



sum:

        push    ebp

        mov     ebp,    esp


        mov     ebx,    dword [ebp+8]

        add     ebx,    dword [ebp+12]


        mov     eax,    ebx


        ;function epilogue

        ;mov    esp,    ebp

        ;pop    ebp

        leave

        ret


main:

        ;function prologue

        push    ebp

        mov     ebp,    esp

        sub     esp,    4


        push    20

        push    10

        call    sum

        add     esp,    8


        mov     dword [ebp-4],eax


        push    dword [ebp-4]

        push    prompt

        call    printf


        ;mov    esp,    ebp

        ;pop    ebp

        leave

        ret




1. main 

 push    ebp

 mov     ebp,    esp

 sub     esp,    4


main에서 ebp를 push하고

ebp에 esp를 push 하고

esp-4 를 해서 지역변수 result 값이 들어가는 위치를 확보한다


그림으로 표시해보면 

 

result  //esp -4를 해서 위치를 확보한다.

push ebp //esp 위치  

push eip  //main 호출하면 설정된다



2. push    20

   push    10

   call    sum

 

 push eip //push eip

 push 10 //push 10

 push 20 //push 20

 result

 push ebp

 push eip



call sum 의 의미는 push eip +jmp sum 이다



3.jmp sum 에 의해서 

   push    ebp

        mov     ebp,    esp


        mov     ebx,    dword [ebp+8]

        add     ebx,    dword [ebp+12]


        mov     eax,    ebx


sum 함수의 기준점을 설정해주기 위해서

push ebp

mov ebp, esp 를 해준다



 push ebp  <- esp  설정 

 push eip //push eip

 push 10 //push 10

 push 20 //push 20

 result

 push ebp

 push eip




4. mov     ebx,    dword [ebp+8]

   add     ebx,    dword [ebp+12]

   

   mov     eax,    ebx


   ;function epilogue

   ;mov    esp,    ebp

   ;pop    ebp

    leave

    ret



기준점 [ebp+8](push 10)  ebx에 대입해준다

add   ebx + [ebp+12](push 20)  ebx 에 30이 저장된다

mov eax,ebx eax에 30을 대입해준다. 

eax는 따로 지정안하면 리턴값을 저장하는 용도로 사용한다.

이제 함수 epilogue 하면서 main 함수로 돌아 간다










[ebp +12]

ㅡㅡㅡㅡㅡ

[ebp+8]

ㅡㅡㅡㅡㅡ

Saved ebp

ㅡㅡㅡㅡㅡ

Saved eip

ㅡㅡㅡㅡㅡ

10

ㅡㅡㅡㅡㅡ

20

ㅡㅡㅡㅡㅡ

result   <-[ebp -4]

ㅡㅡㅡㅡㅡ 

Saved ebp  <-ebp 

ㅡㅡㅡㅡㅡ

Saved eip    

ㅡㅡㅡㅡㅡ


1.Saved eip는 이미 실행된 상태에서 main 함수가 실행될것이다

2.push ebp

  mov  ebp,esp

  ebp의 값을 






ebp 라는 기준점을 세워둔다 

ebp를 우선 가지고와서 사용하고 나중에는 원래있던 곳으로 보내야하니까 ebp를 우선 저장해놓고

ebp를 기준으로 4바이트씩 이동해서 

함수 끝에 eax는 return을 의미한다


꼭 회수는 해야한다.











+ Recent posts