본문 바로가기
정보보안

정보보안2 6차시

by 뭔가해보겠습니다 2024. 5. 19.

(gdb) show disable-randomization

=> randomization 설정 확인. on이면 메모리 주소가 바뀌지 않음 / off는 계속 메모리 주소가 바뀜

 

POP 명령어

- at&t : popl 피연산자

- intel :  pop 피연산자

- 스택의 $ESP가 가리키는 곳의 값을 피연산자에 저장한다

- 스택에서 값을 뺄 때 사용하는 명령어

- 스택에 값이 pop되면 $ESP 값이 x86 기준으로 32bit 증가 (스택은 높은 주소에서 시작하므로)

 

자꾸 헷갈리지만, gdb로 $esp의 위치가 아니라 값을 확인할 때는 disp가 아니라

(gdb) x/4xw $esp
0xffffd43c:     0x00000002      0x00000001      0x12345678      0x00000000

이런식으로 메모리 주소를 조사해야 한다

 

(gdb) ni

(gdb) x/4xw $esp
0xffffd440:     0x00000001      0x12345678      0x00000000      0xf7e34f36

(gdb) disp ~

1: $esp = (void *) 0xffffd440
2: $eax = 3
3: $ebx = 2
4: $ecx = 1679037307
5: $edx = -11148
6: $eip = (void (*)()) 0x804848d <main+16>

→ 2가 사라진 것을 볼 수 있다. 왜냐면 pop 명령어로 $esp의 값을 빼서 $ebx에 넣어주었기 때문

$esp의 주소도 0xffffd43c에서 0xffffd440으로 높아졌다

 

+ $edx = 305419896

=> 0x12345678를 10진수로 표현한 숫자임 ( p/x $edx 하면 16진수로 출력할 수 있음 )

 

$esp의 주소가 0xffffd440일 때, 이는 영역 중 가장 낮은 주소를 의미한다.

실제 $esp의 영역은 0xffffd440 ~ 0xffffd443 이다. (4Byte)

주소

0xffffd440:     0x00000001       0x12345678       0x00000000

                         43 42 41 40         47 46 45 44       4b 4a 49 48

이런식으로 1Byte씩 두 자리를 할당하여 주소가 할당됨

 

pop 해도  x/4xw $esp - 16 이런식으로 메모리조사를 해보면 다 남아있다

메모리에 저장된 값은 초기화되지 않음 (종료 전까지)

단, pop 되었으므로 쓸모없는 값이 되어 접근할 수 없게 된다

(gdb) x/4xw $esp -16
0xffffd43c:     0x00000002      0x00000001      0x12345678      0x00000000

 

뒷자리 변화에 유의

pushl (높은 주소 -> 낮은 주소)

44c -> 448 -> 444 -> 440 -> 43c -> 438 -> 434 ...

popl (낮은 주소 -> 높은 주소)

434 -> 438 -> 43c -> 440 -> 444 -> 448 -> 44c ... 

 

(gdb) disp/i $eip

=> eip를 그냥 disp 하면 명령어를 볼 수 없으므로, /i 옵션을 추가하여 명령어를 함께 추적한다.

(gdb) ni
0x0804848c in main ()
1: $esp = (void *) 0xffffd43c
2: x/i $eip
=> 0x804848c <main+15>: pop    %ebx

 

(gdb) x/4xw $ebp
0xffffd448:     0x00000000      0xf7e34f36      0x00000001      0xffffd4d4

                                       |                      └ return address

                                      └ 프로그램 시작점

 

스택프레임 구성
- 매개변수Parameter 호출 함수가 전달한 인자 값
- 지역변수Local Variables 함수 내에 선언된 변수
- 리턴 주소Return Address 함수 실행 종료 후 실행할 명령문 주소
- 리턴 값Return Value 호출 함수에세 돌려줄 값

 

스택 프레임은 함수가 호출되기 이전의 상태를 기록하는 역할을 하고,

함수가 리턴된 이후 호출한 함수의 이전 상태를 복원한다.
(함수를 호출하면 스택 메모리에 스택 프레임이 push되고, 함수가 종료되면 스택 메모리에 스택 프레임을 pop한다)

스택 프레임이 스택 메모리에 push되면, 다른 모든 스택 프레임이 비활성상태(접근X)가 된다.
스택 메모리의 top에 존재하는 하나의 스택 프레임만 활성화되기 때문.
스택 프레임의 크기는 컴파일 시점에 결정된다.
(함수에서 초기화되지 않은 변수에 들어간 쓰레기값의 정체는 무엇인가?
바로 직전에 비활성화pop된 스택프레임에 있던 값이다.)

 

(gdb) shell

- gdb를 잠깐 놔두고 shell로 빠져나갈 수 있는 명령어. exit하면 다시 gdb로 돌아온다.

 

# python3 -c 'print(hex(0xffffd448 - 16))'

=> python을 쉘에서 바로 사용함. 해당 내용은 -16한 값을 hex 16진수값으로 print하는 것.

(시스템 오버플로우 시킬 때 많이 사용됨)

 

 

ADD 명령어

- at&t : addl 1피연산자, 2피연산자

- intel : add 2피연산자, 1피연산자

- 1피연산자와 2피연산자를 더하여 2피연산자에 저장함

 

실습)

   0x08048480 <+3>:     push   $0x1
   0x08048482 <+5>:     push   $0x2
   0x08048484 <+7>:     addl   $0x1,(%esp)
   0x08048488 <+11>:    addl   $0x1,0x4(%esp)

   0x0804848d <+16>:    mov    $0x0,%eax

=> pushl 두 번, addl 두 번

 

첫 번째 addl을 보면

$1, (%esp)

%esp의 위치에 피연산자인 1을 더한다

 

(gdb) x/4xw $esp
0xffffd440:     0x00000002      0x00000001      0x00000000      0xf7e34f36

(gdb) ni

(gdb) x/4xw $esp
0xffffd440:     0x00000003      0x00000002      0x00000000      0xf7e34f36

 

%esp의 위치엔 2가 있었는데, 1이 더해져서 3이 된 것이 확인된다.

 

두 번째 addl을 보면

$1, 4(%esp)

이것은 %esp의 위치에서 +4한 위치를 의미함

그래서, %esp+4의 위치에 1피연산자인 1을 더한다.

 

(gdb) x/4xw $esp
0xffffd440:     0x00000003      0x00000001      0x00000000      0xf7e34f36

(gdb) ni

(gdb) x/4xw $esp
0xffffd440:     0x00000003      0x00000002      0x00000000      0xf7e34f36

 

%esp+4의 위치엔 1이 있었는데, 1이 더해져서 2가 된 것이 확인된다.

 

pushl을 두 번 했으므로,

첫 번째 addl을 실행할 때 $esp의 위치는 $ebp -8과 같다.

두 번째 addl을 실행할 때 $esp+4의 위치는 $ebp -4와 같다.

(양수는 +기호 생략하고 음수의 경우는 -8(%ebp)로 표기한다)

ebp는 프로그램의 시작점이자 가장 높은 주소이고

함수 명령어를 실행할 때마다 esp는 +4씩 낮아졌기 때문이다

 

ebp    ebp-4  ebp-8

54 ---- 50 ---- 4c ---- 48

          esp+8 esp+4  esp

                 push    push

 

 

 

실습2)

pushl 3번, addl 3번

 

(gdb) x/4xw $esp
0xffffd43c:     0x00000003      0x00000002      0x00000001      0x00000000

(gdb) ni

(gdb) x/4xw $esp
0xffffd440:     0x00000002      0x00000001      0x00000000      0xf7e34f36

(gdb) x/4xw $esp-4
0xffffd43c:     0x00000003      0x00000002      0x00000001      0x00000000

→ pushl 1 2 3 들어간 상태에서 addl 실행. $esp에서 전부 보이지 않고, -4 해야 보인다.

 

(gdb) ni

(gdb) x/4xw $esp-8
0xffffd43c:     0x00000003      0x00000002      0x00000001      0x00000000

(gdb) ni

(gdb) x/4xw $esp -12
0xffffd43c:     0x00000003      0x00000002      0x00000001      0x00000000

→ 3 2 1 전부 출력하려면 -12까지 가야한다

 

실습1과의 차이점은 괄호를 사용하지 않고 %esp에 직접 값을 넣어주었다는 것이다

$esp의 주소값을 따라가면

시작 0xffffd448 => pushl

=> 0xffffd444 => pushl

=> 0xffffd440 => pushl

=> 0xffffd43c => addl

=> 0xffffd440 => addl

=> 0xffffd444 => addl

=> 0xffffd448 종료

→ 주소가 낮아졌다가, 시작점으로 높아지는(돌아오는) 것을 확인할 수 있다.

 

* 실습1에서는 $esp의 위치가 변하지 않았다.

실습2에서는 $esp의 위치가 변했다.

 

 

 

SUB 명령어

- at&t : addl 1피연산자, 2피연산자

- intel : add 2피연산자, 1피연산자

- 1피연산자는 뺄 값

- 2피연산자는 결과가 저장될 위치이다

- 1피연산자와 2피연산자를 더하여 2피연산자에 저장함

 

 

(gdb) ni
1: x/i $eip
=> 0x804848a <main+13>: sub    $0x5,%eax
2: $esp = (void *) 0xffffd448
3: $eax = 7
4: $ebx = 8
(gdb) ni
1: x/i $eip
=> 0x804848d <main+16>: sub    $0x3,%ebx
2: $esp = (void *) 0xffffd448
3: $eax = 2
4: $ebx = 8

→ 7과 8이 들어간 상태에서, sub    $0x5,%eax 실행된 결과 eax의 값이 2가 되었음

 

(gdb) ni
1: x/i $eip
=> 0x8048490 <main+19>: mov    $0x0,%eax
2: $esp = (void *) 0xffffd448
3: $eax = 2
4: $ebx = 5

sub    $0x3,%ebx 실행된 결과 ebx의 값이 2가 되었음

 

 

0x080484b0 <+3>:     sub    $0x8,%esp

→ 변수 2개를 선언하기 위해 공간을 만들었다 (int니까 4byte 2개)

확인해보니 %ebp 0xffffd458 / %esp 0xffffd450 로 8만큼 %esp가 낮아짐

 

0x080484b3 <+6>:     movl   $0x1,-0x4(%ebp)

0x080484ba <+13>:    movl   $0x2,-0x8(%ebp)

→ $ebp 기준으로 -4 위치에 1 대입, -8 위치에 2 대입 (  ex) int a = 1, b = 2)

 

0x080484c1 <+20>:    pushl  -0x8(%ebp)
0x080484c4 <+23>:    pushl  -0x4(%ebp)

0x080484c7 <+26>:    push   $0x804856c

→ printf 함수의 세번째(b), 두번째(a), 첫번째(문자열) 인수를 스택에 저장한다.

(  ex) printf("%d, %d", a, b); )

 

0x080484cc <+31>:    call   0x8048350 <printf@plt>

→ printf 함수 호출. 이 때 printf함수 실행하고 종료되면 돌아올 RET(Return address)를 스택에 저장함

 

0x080484d1 <+36>:    add    $0xc,%esp

→  스택 복원

 

0x080484d4 <+39>:    mov    $0x0,%eax

→ 종료

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

정보보안2 8차시  (0) 2024.05.26
정보보안2 7차시  (0) 2024.05.25
정보보안2 5차시  (0) 2024.05.18
정보보안2 4차시  (0) 2024.05.12
정보보안2 3차시  (0) 2024.05.11