[ gdb 로 스택 구조 확인 ]
2012. 2. 5. 22:44ㆍ개발/서버
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int check_authentication(char *password)
{
int auth_flag = 0;
char password_buffer[16];
strcpy(password_buffer,password);
if(strcmp(password_buffer,"brillig") == 0)
auth_flag = 1;
if(strcmp(password_buffer,"outgrabe") == 0)
auth_flag = 1;
return auth_flag;
}
int main(int argc, char *argv[])
{
if(argc <2)
{
printf("Using : %s <Password>\n",argv[0]);
exit(0);
}
if(check_authentication(argv[1]))
{
printf("\n------------------------------\n");
printf("Password True!!\n");
printf("--------------------------------\n");
}
else
{
printf("\n Password False!!\n");
}
return 0;
}
------------------------------------------------------------------------------------------
------------------------------------------------------------------------------------------
[root@localhost buffer]# gcc auth_overflow.c -o auth_overflow.exe
[root@localhost buffer]# ./auth_overflow.exe test
Password False!!
[root@localhost buffer]# ./auth_overflow.exe brillig
------------------------------
Password True!!
--------------------------------
[root@localhost buffer]# ./auth_overflow.exe outgrabe
------------------------------
Password True!!
--------------------------------
[root@localhost buffer]#
[root@localhost buffer]# ./auth_overflow.exe AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
------------------------------
Password True!!
--------------------------------
[root@localhost buffer]#
------------------------------------------------------------------------------------------
------------------------------------------------------------------------------------------
[root@localhost buffer]# gcc auth_overflow.c -g -o auth_overflow.exe
strcpy(password_buffer,password); <-- 여기에 브레이크 포인트를 건다.
return auth_flag <--- 여기에 브레이크 포인트를 건다.
그리고 나서 아래와 같이 실행한다.
(gdb) run AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
(gdb) x/s password_buffer
0xbffff380:
(gdb) x/x &auth_flag
0xbffff39c: 0x00000000
(gdb) print 0x0xbffff39c - 0xbffff380
Invalid number "0x0xbffff39c".
(gdb) print 0xbffff39c - 0xbffff380
$1 = 28
(gdb) x/16xw password_buffer
0xbffff380: 0xbffff3d0 0x4000be03 0x40015bd4 0x40016380
0xbffff390: 0x00000001 0x00000000 0x42015481 0x00000000
0xbffff3a0: 0x42130ef8 0x42130a14 0xbffff3c8 0x0804846b
0xbffff3b0: 0xbffffc33 0x4000c660 0xbffff3c8 0x080484c6
(gdb) continue
Continuing.
Breakpoint 2, check_authentication (password=0xbffffc33 'A' <repeats 32 times>)
at auth_overflow.c:17
17 return auth_flag;
(gdb) x/s password_buffer
0xbffff380: 'A' <repeats 32 times>
(gdb) x/x &auth_flag
0xbffff39c: 0x41414141
(gdb) x/16xw password_buffer
0xbffff380: 0x41414141 0x41414141 0x41414141 0x41414141
0xbffff390: 0x41414141 0x41414141 0x41414141 0x41414141
0xbffff3a0: 0x42130e00 0x42130a14 0xbffff3c8 0x0804846b
0xbffff3b0: 0xbffffc33 0x4000c660 0xbffff3c8 0x080484c6
(gdb) continue
Continuing.
------------------------------
Password True!!
--------------------------------
Program exited normally.
(gdb)
------------------------------------------------------------------------------------------
오버플로우 후에 check_authentication 함수가 0 대신
(gdb) x/x &auth_flag
0xbffff39c: 0x41414141
값을 리턴한다 그래서 if 구문에서 0이 아닌 값은 모두 인증되는 것으로 처리했으므로 프로그램은 접근 허용 쪽
구문으로 실행된다. 이 예제에서는 덮어써진 auth_flag 변수가 바로 실행 제어 포인트
execution control point 다
하지만 이 프로그램은 변수의 메모리 위치에 영향을 받는 조작된 만들어진 예제이다.
만약
int check_authentication(char *password)
{
int auth_flag = 0;
char password_buffer[16];
위와 같은 코드를
int check_authentication(char *password)
{
char password_buffer[16];
int auth_flag = 0;
이렇게 위치만 바꾼다면 스택 구조가 바뀌어서 password_buffer 변수 복사를 할때
auth_flag 변수에 영향을 미칠수가 없다.
하지만 C 코드에서는 볼 수 없는 다른 실행 제어 포인트가 존재한다. 실행 제어 포인트는
바로 모든 스택 변수들 다음에 위치하고 있다. 그래서 쉽게 덮어 써질 수 있다.
이 메모리는 모든 프로그램의 실행에서 반드시 필요하고, 그래서 모든 프로그램에 존재한다.
그러다 덮어써지면 프로그램 충돌이 발생한다.
------------------------------------------------------------------------------------
함수 호출시 스택의 구조를 gdb 디버깅을 이용해서 확인해보자..
대상 프로그램은 아래와 같다.
------------------------------------------------------------------------------------
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int check_authentication(char *password)
{
char password_buffer[16];
int auth_flag = 0;
strcpy(password_buffer,password);
if(strcmp(password_buffer,"brillig") == 0)
auth_flag = 1;
if(strcmp(password_buffer,"outgrabe") == 0)
auth_flag = 1;
return auth_flag;
}
int main(int argc, char *argv[])
{
if(argc <2)
{
printf("Using : %s <Password>\n",argv[0]);
exit(0);
}
if(check_authentication(argv[1]))
{
printf("\n------------------------------\n");
printf("Password True!!\n");
printf("--------------------------------\n");
}
else
{
printf("\n Password False!!\n");
}
return 0;
}
------------------------------------------------------------------------------------
------------------------------------------------------------------------------------
(gdb) list 1
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <string.h>
4
5 int check_authentication(char *password)
6 {
7 char password_buffer[16];
8 int auth_flag = 0;
9
10 strcpy(password_buffer,password);
(gdb)
11
12 if(strcmp(password_buffer,"brillig") == 0)
13 auth_flag = 1;
14 if(strcmp(password_buffer,"outgrabe") == 0)
15 auth_flag = 1;
16
17 return auth_flag;
18 }
19
20 int main(int argc, char *argv[])
(gdb)
21 {
22 if(argc <2)
23 {
24 printf("Using : %s <Password>\n",argv[0]);
25 exit(0);
26 }
27
28 if(check_authentication(argv[1]))
29 {
30 printf("\n------------------------------\n");
(gdb) b 28
Breakpoint 1 at 0x804845b: file auth_overflow2.c, line 28.
(gdb) b 10
Breakpoint 2 at 0x80483d1: file auth_overflow2.c, line 10.
(gdb) b 17
Breakpoint 3 at 0x8048421: file auth_overflow2.c, line 17.
(gdb)
(gdb) run AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
Starting program: /home/haking/buffer/auth_overflow2.exe AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
Breakpoint 1, main (argc=2, argv=0xbfffecf4) at auth_overflow2.c:28
28 if(check_authentication(argv[1]))
(gdb) i r esp
esp 0xbfffeca0 0xbfffeca0
(gdb) x/32xw $esp
0xbfffeca0: 0x42130a14 0x40015360 0xbfffecc8 0x42015574
0xbfffecb0: 0x00000002 0xbfffecf4 0xbfffed00 0x4001582c
0xbfffecc0: 0x00000002 0x08048314 0x00000000 0x08048335
0xbfffecd0: 0x08048426 0x00000002 0xbfffecf4 0x080484bc
0xbfffece0: 0x080484ec 0x4000c660 0xbfffecec 0x00000000
0xbfffecf0: 0x00000002 0xbffffbef 0xbffffc16 0x00000000
0xbfffed00: 0xbffffc35 0xbffffc4d 0xbffffc6c 0xbffffc77
0xbfffed10: 0xbffffc87 0xbffffc95 0xbffffca5 0xbffffcbb
------------------------------------------------------------------------------------
첫 번째 중지점은 main()에서 check_authentication()을 호출하기 바로 전이다.
이 시점에서 스택 포인터 레지스터(ESP)는 0xbfffeca0 이고 스택의 맨 위를 가리킨다.
이 부분은 main() 함수의 스택 프레임이다. 다음 중지점까지 계속해서
check_authentication() 안으로 들어가면 , ESP가 이제는 스택에 푸시된
check_authentication()의 스택 프레임을 위한 메모리 공간 만큼 작아진다.
------------------------------------------------------------------------------------
(gdb) c
Continuing.
Breakpoint 2, check_authentication (password=0xbffffc16 'A' <repeats 30 times>) at auth_overflow2.c:10
10 strcpy(password_buffer,password);
(gdb) i r esp
esp 0xbfffec60 0xbfffec60
(gdb) x/32xw $esp
0xbfffec60: 0xbfffecb0 0x4000be03 0x40015bd4 0x00000000
0xbfffec70: 0x00000001 0x00000000 0x42015481 0x08048342
0xbfffec80: 0x42130ef8 0x42130a14 0xbfffeca8 0x0804846b
0xbfffec90: 0xbffffc16 0x4000c660 0xbfffeca8 0x080484c6
0xbfffeca0: 0x42130a14 0x40015360 0xbfffecc8 0x42015574
0xbfffecb0: 0x00000002 0xbfffecf4 0xbfffed00 0x4001582c
0xbfffecc0: 0x00000002 0x08048314 0x00000000 0x08048335
0xbfffecd0: 0x08048426 0x00000002 0xbfffecf4 0x080484bc
(gdb) p 0xbfffeca0 - 0xbfffec60
$1 = 64
(gdb) x/s password_buffer
0xbfffec70: "\001"
(gdb) x/x &auth_flag
0xbfffec6c: 0x00000000
(gdb)
------------------------------------------------------------------------------------
check_authentication() 에서 설정된 두 번째 중지점까지 계속하면 함수가 호출 될때
스택 프레임이 스택에 푸시된다.
(gdb) c
Continuing.
Breakpoint 3, check_authentication (password=0xbffffc16 'A' <repeats 30 times>) at auth_overflow2.c:17
17 return auth_flag;
(gdb) x/s password_buffer
0xbfffec70: 'A' <repeats 30 times>
(gdb) x/32xb password_buffer
0xbfffec70: 0x41 0x41 0x41 0x41 0x41 0x41 0x41 0x41
0xbfffec78: 0x41 0x41 0x41 0x41 0x41 0x41 0x41 0x41
0xbfffec80: 0x41 0x41 0x41 0x41 0x41 0x41 0x41 0x41
0xbfffec88: 0x41 0x41 0x41 0x41 0x41 0x41 0x00 0x08
(gdb)
(gdb) i r ebp
ebp 0xbfffec88 0xbfffec88
(gdb) c
Continuing.
Program received signal SIGSEGV, Segmentation fault.
0x08004141 in ?? ()
(gdb)
------------------------------------------------------------------------------------
여기서 ebp가 0xbfffec88 값을 갖고 있다.. 0xbfffec88 +4 부터 4바이트가 바로
리턴어드레이스인데 리턴 어드레스의 2바이트가 0x4141 로 덮어씌워진것을 볼 수 있다.
그래서 해당 함수 호출 후 돌아갈때 세그먼테이션 폴트 가 뜬다. (segmentation fault)
-----------------------------------------------------------------------------
'개발 > 서버' 카테고리의 다른 글
프로그래밍 - gdb , objdump (0) | 2012.02.05 |
---|---|
네트워킹 - 작은 웹서버 , 웹서버 원리 제작 (0) | 2012.02.05 |
프로세스 (0) | 2012.02.05 |
사용자 계정 관리 (0) | 2012.02.05 |
[su 권한]특정 유저 또는 그룹에만 사용권한 (0) | 2012.02.05 |