본문 바로가기
정보보안

정보보안2 8차시

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

gdb 할 때 기본적인 disp 항목

disp $esp
disp $ebp
disp $eip
disp /i $eip

disp /4xw $esp

--

 

실습) 어셈블리 코드를 보고 c언어로 변경하기

 

어셈블리어만으로는 return이 덧셈연산인지 뺄셈연산인지 알 수 없지 않나 생각했는데

확인해보니 다른 점이 있었다

 

func1의 return이

a-b일때)    sub

mov    0x8(%ebp),%eax

sub    0xc(%ebp),%eax

a+b일때)   mov와 add

mov    0x8(%ebp),%edx
mov    0xc(%ebp),%eax
add    %edx,%eax

 

#include <stdio.h>
int func1(int a, int b);
int main(){
	int a = 2;
	int b = 1;
	int c;
	c = func1(a, b);
	printf("result: %d\n", c);
	return 0;
}
int func1(int a, int b){
	printf("a: %d, b: %d\n", a, b);
	return a-b;
}
// 아래는 a+b
#include <stdio.h>
int func1(int a, int b);
int main(){
	int a = 1;
	int b = 2;
	int c;
	c = func1(a, b);
	printf("result: %d\n", c);
	return 0;
}
int func1(int a, int b){
	printf("a: %d, b: %d\n", a, b);
	return a+b;
}

 

func1

   0x080484ec <+3>:     pushl  0xc(%ebp)
   0x080484ef <+6>:     pushl  0x8(%ebp)

=> main과 달리 pushl 해주는데 3바이트 떨어진 곳과 2바이트 떨어진 곳이다.

 

메모리 구조

(                     main                         )    (                                func1        ~ ~ ~

main ebp    int a=2     int b=1    int c    b값    a값   복귀주소  func1 ebp = func1 esp

     458            454         450      44c    448    444       440                      43c ( 여기에 main ebp 주소 저장 )

=> 448 444에 a와 b값이 거꾸로 들어간다는 점을 인지해야한다

 

func1 ebp    b값   a값   문자열   

      43c       438    434    430

=> push로 다시 b값과 a값 문자열 들어감

 

 

main ebp : 0xffffd458 (main 시작!)

(gdb) x/4xw $ebp -8
0xffffd450:     0x00000002      0x00000001      0x00000000      0xf7e34f36

=> 조사해보면 ($ebp -8)에 b값인 1, ($ebp -4)에 2이 있다.

 

func ebp : 0xffffd43c (func1 시작!)

(gdb) p ($ebp+12)
$1 = (void *) 0xffffd448
(gdb) x/s 0xffffd448
0xffffd448:     "\001"

=> 현재 ebp에서 12바이트 떨어진 곳은 448이고 1이 들어있다.
(gdb) p ($ebp+8)
$2 = (void *) 0xffffd444
(gdb) x/s 0xffffd444
0xffffd444:     "\002"

=> 현재 ebp에서 8바이트 떨어진 곳은 444이고 2가 들어있다.

 

즉, pushl 2와 pushl 1이라는 뜻

 

0x080484f2 <+9>:     push   $0x80485a8

=> 이것은 printf를 위한 복귀주소

 

call   0x8048350 <printf@plt>

add    $0xc,%esp

=> printf 콜 이후, add로 printf를 위해 할당했던 3바이트 자리를 다시 복구함

 

mov    0x8(%ebp),%eax

sub    0xc(%ebp),%eax

=> eax 레지스터에 a값인 2 넣고, 거기에 sub로 b값인 1빼면 끝.

 

leave

과정에서 ebp가 갖고 있던 main ebp 주소를 esp 레지스터로 옮기고

pop을 통해 ebp가 esp 레지스터 값을 전달받아

ebp는 main ebp위치로 이동

pop 되었으므로 esp도 한 바이트 높아졌다

ret

과정에서 eip는 esp가 갖고 있던 복귀주소를 pop으로 가져오고

esp는 한 바이트 높아지며(func1 호출 이후에 실행할 명령어 위치로)

jmp로 eip 레지스터가 call func1 다음줄로 복귀함

 

add    $0x8,%esp

=> 다시 돌아온 메인에서 func1 하느라 할당했던 2바이트 esp 복귀

mov    %eax,-0xc(%ebp)

=> mov로 %eax의 값을 %ebp -12 (int c)위치에 저장한다 (func1 return 값)

그 후 pushl로 %ebp -12값과 복귀주소를 저장한후(복귀주소는 printf 안으로 들어갔을때만 확인 가능)

(gdb) x/4xw $esp
0xffffd444:     0x0804859c      0x00000001      0x00000001      0x00000001

                         문자열            %ebp -12값

(gdb) x/s 0x0804859c
0x804859c:      "result: %d\n"

=> 0x0804859c에 문자열이 들어가 있다

 

add    $0x8,%esp

=> printf 종료 후에 다시 esp 복원하고

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

 

mov    $0x0,%eax

leave

ret

=> mov로 eax에 return 0 셋팅후 끝

 

 

함수의 스택 프레임SF

함수를 호출할 때 스택에서 생성되는 구조. 실행상태를 저장하고 관리하는 역할

함수가 종료할 때 제거된다

- 인수

- 리턴주소

- 이전 프레임 포인터 : caller(이전 함수의 호출자)

- 지역 변수

 

(gdb) i r $pc
pc             0x80484b9           0x80484b9 <main>
=> eip 레지스터와 같다

 

콜링 컨벤션

- 함수를 호출할 때 인수들의 전달 방법과 함수가 반환될 때 스택을 정리하는 규칙

- cdecl, stdcall, fastcall, 64비트 환경에서의 x86_64 system v 등등 ...

 

주요 콜링 컨벤션

1) cdecl : 주로 c언어에서 사용되고 x86시스템에서 많이 씀

인수 전달 : 인수는 오른쪽에서 왼쪽 순서로 스택에 푸시

스택 정리 : 호출자caller가 스택 정리

( rocky8 에서 이 방식을 사용 )

 

int a, int b, int c;

printf("문자열", c);

일 때,

rocky8 에서는 3개의 변수공간만 잡는다.

printf call 전에 printf용 인수 공간을 2개 선언한 뒤, 호출이 종료되면 add로 2개 공간을 다시 거둬들인다

 

centos7 에서는

미리 printf에서 쓸 인수 2개의 공간을 추가로 셈하여 변수 3개 + 인수 2개 = 총 5개의 인수공간을 잡는다

printf가 종료되어도 스택을 정리하지 않는다

 

2) stdcall : 주로 윈도우 API에서 사용

인수 전달 : 오른쪽에서 왼쪽으로 스택에 푸시

스택 정리 : 호출된 함수callee가 스택을 정리한다

 

3) fastcall : 함수 호출시 성능을 높이기 위해 사용

인수 전달 : 첫 번째와 두 번째 인수는 레지스터 ECX, EDX로 전달되고 나머지는 스택에 푸시

스택정리 : 호출된 함수callee가 스택을 정리

 

4) 64비트 콜링 컨벤션 : 윈도우 64비트 환경에서 사용

인수 전달 : 첫 4개 정수 인수는 레지스터 (RCX, RDX, R8, R9)로 전달. 나머지는 스택에 푸시

단, 부동 소수점 ㄷ인수는 XMM0-XMM3 레지스터로 전달.

스택정리 : 호출된 함수 callee가 스택 정리

 

 

과제) C언어 소스를 gdb 어셈블리어로 분석하기

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

정보보안3 2차시  (0) 2024.06.02
정보보안3 1차시  (0) 2024.06.01
정보보안2 7차시  (0) 2024.05.25
정보보안2 6차시  (0) 2024.05.19
정보보안2 5차시  (0) 2024.05.18