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을 의미한다
꼭 회수는 해야한다.
'시스템 해킹' 카테고리의 다른 글
[시스템 해킹] main 함수의 인자 (0) | 2018.01.27 |
---|---|
[시스템 해킹] 배열 지역 변수로 구현하기 (0) | 2018.01.27 |
[시스템 해킹] 어셈블리 반복문 (0) | 2018.01.18 |
[시스템 해킹] 어셈블리 형변환, 분기문 (0) | 2018.01.17 |
[시스템 해킹] 어셈블리 사칙연산 (0) | 2018.01.16 |