사용시스템 : 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(3010, 3010); 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 |