[ 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