본문 바로가기
보안과정/참고

변수의 메모리 배치 확인과 GDB 사용법

by Luuii 2017. 11. 30.

사용시스템 : HackMe

ID : level9

PASS : apple


○ 변수의 메모리 배치

> 변수의 메모리 배치를 확인하기 위해서 프로그램을 만들어 보자


$ cd tmp

$ vi distance.c

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
#include <stdio.h>
 
int main()
{
    char AA;
    char strAA[1];
    char strBB[2];
    char strCC[3];
    char strDD[5];
    char strEE[9];
    char strFF[17];
 
    printf("AA's address: 0x%x, sizeof: 0x%x\n"&AA, sizeof(AA));
            printf("strAA[1]'s address: 0x%x, sizeof: 0x%x, distance: 0x%x\n",
 strAA, sizeof(strAA), &AA - strAA);
            printf("strBB[2]'s address: 0x%x, sizeof: 0x%x, distance: 0x%x\n",
 strBB, sizeof(strBB), strAA - strBB);
            printf("strCC[3]'s address: 0x%x, sizeof: 0x%x, distance: 0x%x\n",
 strCC, sizeof(strCC), strBB - strCC);
            printf("strDD[5]'s address: 0x%x, sizeof: 0x%x, distance: 0x%x\n"
 strDD, sizeof(strDD), strCC - strDD);
            printf(" strEE[9]'s address: 0x%x, sizeof: 0x%x, distance: 0x%x\n",
 strEE, sizeof(strEE), strDD - strEE);
            printf("strFF[17]'s address: 0x%x, sizeof: 0x%x, distance: 0x%x\n",
 strFF, sizeof(strFF), strEE - strFF);
 
    return 0;
}
cs


> printf("strAA[1]'s address: 0x%x, sizeof: 0x%x, distance: 0x%x\n", strAA, sizeof(strAA), &AA - strAA);


왜 이렇게 만들었느냐?

(원리)

메인으로 이렇게 해놓으면 저장공간, 메모리 공간이 확보가 된다.

함수 내에서 실행되는 변수는 스택에 저장된다. 메인이라는 함수에 들어있으니까 스택에 들어간다.

제일 처음에 SFP라는 공간이 하나있다. dbp를 푸쉬하는 값이다.

esp와 ebp의 위치를 동일하게 한다고 했다.

그 뒤에 RET(4byte), 그 뒤에 argv(4byte), 그 뒤에 argv(4byte)의 포인터, 그뒤에 ENV(4byte)에 대한 포인터가 들어가 있다.


 . . . . 

 strFF

strEE 

strDD 

strCC 

strBB 

strAA 

AA 

SFP 

RET 

argv 

 *argv

*ENV 


이제 SFP 앞에 순차적으로 변수들이 들어간다.

먼저 AA 들어가고, strAA[1], 그 앞에 strBB[1] . . . . 이렇게 순차적으로 들어간다.


함수의 인자 값은 거꾸로 집어넣고 순차적으로 뽑지만 함수 내에 있는 지역변수들은 우리가 생각하는 순차적으로 저장한다.

AA 와 SFP 사이에는 더미공간이 있을 수도 없을 수도 있다.

AA와 strAA 사이에는 더미공간이 있을 수도 없을 수도 있다.

그것을 정확히 알아야한다.


그럼 그것을 어떻게 아느냐.

AA: 높은주소 strAA:낮은 주소 둘이 뺀다. 그럼 크기라는게 나온다.

주소2-주소1=거리

sizeof(strAA[1]) 확인

두 개의 값이 같으면 더미공간이 없는거다.

얘랑 얘랑 빼서 10이 나왔는데

strAA 크기가 9면 더미공간이 1바이트라는 것이다.


strBB[2]는 [0],[1]로 구성되어있다. 배열은 더미공간이 없지만 얘랑 다른 것들은 더미공간이 있을 수 있다.


예전에는 이런 더미공간이 없었다. 그래서 성능이 떨어졌다. 그래서 최근에 최적화라는 단계를 거친다.

중간중간에 자기가 바이너리 코드를 넣기도 하고 안 넣기도 한다.

CPU가 읽어 들일 때 읽이 좋은 형태로 중간에 NULL처리를 하는 것이다. 성능 개선을 위해 컴파일러가 알아서 자기가 배치를 시켜버린다.


꼭 더미공간의 유무를 확인해야한다.

높은 주소에서 낮은 주소를 뺀 것과 뒤에 있는 것의 sizeof의 크기를 비교한다.

 

첫 번째 변수와 두 번째 변수의 주소를 빼고

두 번째 변수의 크기와 동일하면 더미공간이 없는 것이고

차이가 있으면 더미공간이 있는 것이다.



$ gcc -o distance distance.c

$ distance.c


메모리주소

변수명 

변수의 크기 

메모리공간 

 0xbfffe6ee

strAA[1] 

0x1(1 bytes) 

 0x1(1 bytes) 

 0xbfffe6ec

strBB[2] 

0x2(2 bytes)

0x2(2 bytes) 

 0xbfffe6d0

strCC[3]

0x3(3 bytes)

 0x1c(28 byte)

 0xbfffe6c0

strDD[5]

0x5(5 bytes)

 0x10(16 bytes)

 0xbfffe6b0

strEE[9]

0x9(9 bytes)

 0x10(16 bytes)

 0xbfffe690

strFF[17]

0x11(17 bytes)

0x20(32 bytes) 

> 실제 메모리 공간은 2의 배수 형태로 운영체제와 컴파일러마다 최고의 성능을 낼 수 있도록 기본 원칙을 어기지 않는 범위 안에서 약간의 변형을 가한다.

> 변수와 변수 사이에는 더미공간이 존재할 수 있다.




2. gdb 사용법

○ 분석 방법의 종류

> 정적분석

> 동적분석


gdb 사용법

명령어 

설명

 gdb <파일이름>

 지정된 파일을 gdb로 열기 

 list 

 gcc 컴파일시 -ggdb 옵션을 지정한 경우 소스 확인가능 

 disassemble 주소/ 함수명 

 지정된 함수를 디스어셈블해 실행 

 run 

 지정된 파일을 실행 

 continue 

 브레이크 걸린 상태에서 계속 진행 

 break 주소/함수명 

 주소나 함수에 브레이크 포인터 걸기 

 x/32x 주소 

 주소에서 32개를 16진수로 출력(x/32s 는 문자열) 

 info registers 

 레지스터의 값을 출력 

 nexti 

 함수 내부로 들어가지 않고 한 라인 실행 

 stepi 

 함수 내부로 들어가면서 한 라인 실행 

 help 

 도움말 출력 

 backtrace 

프로그램 실행의 스택 추적 결과 출력 

 quit 

 gdb 종료 


$ cd ~/tmp

$ vi bof.c

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
 
main()
{
        char buf2[10];
        char buf[10];
 
        printf("It can be overflow : ");
        fgets(buf, 40, stdin);
 
        if(strncmp(buf2, "go"2== 0)
        {
                printf("Good Skill!\n");
                setreuid(30103010);
                system("/bin/bash");
        }
}
cs


$ gcc -ggdb -o bof bof.c

> -g : 타겟이 되는 파일 안에다가 아스키 코드를 숫자로 전환해서 집어넣는다. 프로그래밍 중간에 디버깅 용도로 매우 용이하다.


$ gdb

(gdb) file /home/level9/tmp/bof

(gdb) help


(gdb) help data


(gdb) list 1,20(포함된 코드를 볼 때 기본적으로는 10줄을 보여주도록 되어있다.)


(gdb) disassemble main

> 어디에다가 브레이크 포인터를 걸어야 하느냐가 가장 중요하다.

1. 모든 라인 : 아주 자세하게. 한줄 한줄이 무슨 뜻인지 알아보고 싶을 때

2. 모든 함수

1,2 의 방법은 효율적이지 못하다.


점검구문

1. 조건 구문

> 모의해커에게 매우 중요

2. 함수를 호출하는 구문

> 개발자들이 개발을 할 때 코드를 정리하는 작업을 한다. 어떤 기능은 함수로 만들어져 있을 가능성이 크다. 패스워드를 함수로 만든다거나.

3. 입력 값이 있는 전후 부분

> 내가 유일하게 바꿀 수 있는 부분은 입력 값 부분이다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
Dump of assembler code for function main:
0x08048420 <main+0>:    push   %ebp
0x08048421 <main+1>:    mov    %esp,%ebp
0x08048423 <main+3>:    sub    $0x28,%esp
0x08048426 <main+6>:    and    $0xfffffff0,%esp
0x08048429 <main+9>:    mov    $0x0,%eax
0x0804842e <main+14>:   sub    %eax,%esp
0x08048430 <main+16>:   sub    $0xc,%esp
0x08048433 <main+19>:   push   $0x8048554
0x08048438 <main+24>:   call   0x8048350 <printf>
0x0804843d <main+29>:   add    $0x10,%esp
0x08048440 <main+32>:   sub    $0x4,%esp
0x08048443 <main+35>:   pushl  0x8049698
0x08048449 <main+41>:   push   $0x28
0x0804844b <main+43>:   lea    0xffffffd8(%ebp),%eax
0x0804844e <main+46>:   push   %eax
0x0804844f <main+47>:   call   0x8048320 <fgets>
0x08048454 <main+52>:   add    $0x10,%esp
0x08048457 <main+55>:   sub    $0x4,%esp
0x0804845a <main+58>:   push   $0x2
0x0804845c <main+60>:   push   $0x804856a
0x08048461 <main+65>:   lea    0xffffffe8(%ebp),%eax
0x08048464 <main+68>:   push   %eax
---Type <return> to continue, or q <return> to quit---
0x08048465 <main+69>:   call   0x8048330 <strncmp>
0x0804846a <main+74>:   add    $0x10,%esp
0x0804846d <main+77>:   test   %eax,%eax
0x0804846f <main+79>:   jne    0x80484a6 <main+134>
0x08048471 <main+81>:   sub    $0xc,%esp
0x08048474 <main+84>:   push   $0x804856d
0x08048479 <main+89>:   call   0x8048350 <printf>
0x0804847e <main+94>:   add    $0x10,%esp
0x08048481 <main+97>:   sub    $0x8,%esp
0x08048484 <main+100>:  push   $0xbc2
0x08048489 <main+105>:  push   $0xbc2
0x0804848e <main+110>:  call   0x8048360 <setreuid>
0x08048493 <main+115>:  add    $0x10,%esp
0x08048496 <main+118>:  sub    $0xc,%esp
0x08048499 <main+121>:  push   $0x804857a
0x0804849e <main+126>:  call   0x8048310 <system>
0x080484a3 <main+131>:  add    $0x10,%esp
0x080484a6 <main+134>:  leave
0x080484a7 <main+135>:  ret
End of assembler dump.
 
cs



(gdb) x/s buf

> x/x : 뽑아서 16진수로 보여줘

> x/d : 뽑아서 10진수로 보여줘

> x/s : 스트링으로 보여줘

(gdb) x/x buf

>> x/1s, x/2s, x/3s 이런식으로도 사용할 수 있다.


(gdb) nexti

> 다음다음 다음 다음 넘어가 보고 싶다. 다름 브레이크 포인트가 아니라.

(gdb) stepi

> 현재 메인구문에 있고 stepi는 혹시나 다음 줄에 함수가 있으면 그 함수로 가서 한줄 씩 읽는다.

(gdb) backtrace 

> 지금 어느 부분에 있는지 어떤 라이브러리를 참조해서 했는지.















































































반응형

'보안과정 > 참고' 카테고리의 다른 글

SAN(Storage Area Network)  (0) 2017.12.29
공유 메모리 관련 함수에 대하여  (0) 2017.11.30
아스키 코드표  (0) 2017.11.29
SIGNAL에 대하여  (0) 2017.11.29
ln 명령어  (0) 2017.11.29