본문 바로가기
정보보안

정보보안2 5차시

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

printf 포맷스트링
%s 문자열
%c 문자1개
%f 실수 (double, float) ex) %0.2f => 12.12 소숫점 2자리까지 표기
%u 부호 없는 정수
%d 부호 있는 정수 (2진 보수 방식 등)
%x 16진수 소문자 표기
%X 16진수 대문자 표기

 

printf("|%20c|\n", 'A');

=> 20칸에 맞추어 문자 A를 출력

 

printf("|%10.2f|\n", 13.540000);

=> 10칸에 맞추고 소숫점은 2자리까지 출력. 13.54

 

signed와 unsigned

signed : 양수와 정수 모두 사용 가능 ( -128 ~ 127 까지)
unsigned : 양수만 사용 가능 ( 0 ~ 255까지. 음수를 넣으면 쓰레기값이 셋팅된다고 함)

 

 

복습) 어셈블러 종류
- GAS : 리눅스, 유닉스 환경
- NASM : x86 환경 아키텍처에서 사용되는 오픈소스
- MASM : 마이크로소프트사 개발, 윈도우 환경에서 주로 사용

 

인텔 문법 사용하여 컴파일

=> gcc -masm=intel ~

 

intel_syntax

 

변수에 %가 붙는다

 

(32bit) intel과 at&t 문법의 기본 구조 차이

 

vi 명령어

:w newName.txt

=> newName.txt로 새로운 파일을 만들어 저장한다.

:r !ls %t

k

shift + j

=> 커서 아랫쪽 줄에 현재 파일의 이름을 불러온다. k로 위로 올라간다. j로 밑줄을 끌어온다.

:qa

=> 모든 창 종료

:r sample.s

=> sample.s의 내용을 불러들인다.

 

PUSH 명령어

- att : pushl 피연산자

- intel : push 피연산자

- 피연산자를 스택에 저장한다

- 스택에 값이 push되면 ESP 값이 x86일 때 32bit(4byte) 감소한다. (스택은 높은 주소에서 시작하여 낮은 주소로 향하기 때문)

- 스택에 관련된 레지스터 (32bit)

1) EBP : 스택의 바닥이자 시작점 (가장 높은 주소이며 함수가 시작되면 종료 전까지 고정된다)

2) ESP : 현재의 스택 위치 (프로세스 실행중에 자료가 저장되면 계속 낮아진다)

3) EIP : 현재 실행중인 명령어의 주소를 저장하고 있다

 

+ at&t 문법에서는 레지스터 앞에 %가 붙고 즉시값에는 $가 붙는다.

(즉시값 : 어셈블리어 명령어에서 직접 사용하는 상수 값)

ex) %EBP, $1

 

(gdb) disp $esp

disp $ebp
1: $esp = (void *) 0xffffd448
2: $ebp = (void *) 0xffffd448

(gdb) ni
0x08048482 in main ()
1: $esp = (void *) 0xffffd444
2: $ebp = (void *) 0xffffd448

...

1: $esp = (void *) 0xffffd440
2: $ebp = (void *) 0xffffd448

...

1: $esp = (void *) 0xffffd44c
2: $ebp = (void *) 0x0

(함수 종료)

→ ebp는 고정이고 esp만 변경되었다

( disp 말고 i r $ebp $esp 로 출력해도 된다 )

 

(gdb) x/i $eip
=> 0x8048484 <main+7>:  push   $0x3

(gdb) ni

(gdb) x/i $eip
=> 0x8048486 <main+9>:  push   $0x4

→ 현재 실행중인 명령어($eip) 정보를 출력해준다. ni를 실행하면 계속 변동되는 걸 볼 수 있다.

 

4byte씩 증감되므로 16진수 표기로 0, 4, 8, c가 반복된다. ( 0 ~ f )

 

 

(gdb) ni         * 4번 반복하여 push 1 2 3 4 실행된 상태
(gdb) x/5xw $esp
0xffffd438:     0x00000004      0x00000003      0x00000002      0x00000001
0xffffd448:     0x00000000

→ $esp 레지스터에 1 2 3 4 push된 것이 보인다
(gdb) ni
(gdb) x/5xw $esp
0xffffd434:     0x00000005      0x00000004      0x00000003      0x00000002
0xffffd444:     0x00000001

→  한 번 더 ni 하자 push 5 되었고 $esp 레지스터에 메모리 주소가 보인다

 

(gdb) x/5xw $esp

→  x로 메모리 조사를 한다. 5개를 출력한다. x 16진수로, w word 4byte 단위로 $esp 레지스터 메모리를 조사함.

 

$ESP 레지스터를 기준으로 메모리를 조사한 이유

: 낮은 주소부터 보기 위해서

 

실습) $EBP 기준으로 메모리 조사

(gdb) x/5xw $ebp
0xffffd448:     0x00000000      0xf7e34f36      0x00000001      0xffffd4d4
0xffffd458:     0xffffd4dc

(gdb) x/5xw $ebp -20
0xffffd434:     0x00000005      0x00000004      0x00000003      0x00000002
0xffffd444:     0x00000001

→  -20을 해 준 이유는 4Byte * 5개를 보기 위해서이다. $EBP는 가장 높은 주소(시작점)이기 때문에, $EBP에서부터 메모리를 조사하면 프로그램의 시작점을 넘어가게 된다.

 

← x/5xw $ebp           → x/5xw $ebp -20

-------------------+-------+--+--+--+--+--+-----

             High   | EBP | 1 | 2 | 3 | 4 | 5                 | Low

-------------------+-------+--+--+--+--+--+-----

 

이 때 $EBP 0xffffd434의 34는 0x00000005의 어느 부분을 의미할까?

→  정답은 05 이다. 뒤에서부터 값이 채워지기 때문

( 가장 낮은 바이트LSB를 가장 낮은 메모리 주소에 저장하는 리틀 앤디안Little Endian 방식 )

 

x86 & x86_64 : 리틀 엔디안 방식 사용

ARM : 리틀 엔디안, 빅 엔디안 모두 지원. 리틀 엔디안을 더 일반적으로 사용한다

 

실습) 메모리에 값을 저장하고 주소를 확인하기 (32bit)

10진수 형식: set *(int *)메모리주소 = 값

16진수 형식: set *(int *)메모리주소 = "0x" + 값

 

(gdb) set *(int *)$ebp = 12345678

(gdb) x/5xw $ebp
0xffffd448:     0x12345678      0xf7e34f36      0x00000001      0xffffd4d4
0xffffd458:     0xffffd4dc

 

 

(gdb) x/1xb $ebp
0xffffd448:     0x78
(gdb) x/1xb $ebp+1
0xffffd449:     0x56
(gdb) x/1xb $ebp+2
0xffffd44a:     0x34
(gdb) x/1xb $ebp+3
0xffffd44b:     0x12

→  byte 단위(b)로 끊어서 표기하고 있다.

$ebp를 기준으로 +1 하면 0x12345678 에서 0x12345678 으로 이동

$ebp를 기준으로 +2 하면 0x12345678 에서 0x12345678 으로 이동

$ebp를 기준으로 +3 하면 0x12345678 에서 0x12345678 으로 이동

 

(gdb) x/1xb 0xffffd448
0xffffd448:     0x78

→ $ebp의 주소를 기준으로 메모리 조사를 해보면 78이 나온다.

 

(gdb) x/1xb $ebp+4
0xffffd44c:     0x36
(gdb) x/1xb $ebp+5
0xffffd44d:     0x4f
(gdb) x/1xb $ebp+6
0xffffd44e:     0xe3
(gdb) x/1xb $ebp+7
0xffffd44f:     0xf7

→ 메모리 주소 0x f7 e3 4f 36가 끝에서부터 두 칸씩 묶어(x16진수) 출력되고 있다.

 

* 0x12345678 출력을 기준으로 볼 때

(gdb) x/4xb $ebp
0xffffd448:     0x78    0x56    0x34    0x12
(gdb) x/4xw $ebp
0xffffd448:     0x12345678      0xf7e34f36      0x00000001      0xffffd4d4

→ byte (b) 단위로 보면 낮은주소(78)부터 높은주소(12)를 출력

→ word (w) 단위로 보면 높은주소(12)부터 낮은주소(78)를 출력 (한번에 출력하면 낮은주소가 뒤에 표기되기 때문)

 

MOV 명령어 : 데이터를 이동(복사) 하는 명령어

- att : movl [제1피연산자], [제2피연산자]

- intel : mov [제2피연산자], [제1피연산자]

- 제1피연산자의 값을 제2피연산자에 복사한다.

- 피연산자에는 레지스터, 값, 메모리가 올 수 있다.

단, 메모리를 메모리로 복사할 수는 없고, att에서는 제1피연산자에만, intel에서는 제2피연산자에만 값이 올 수 있다

movl $1, %ebp

→ $ebp는 스택의 시작점 주소를 담고 있으므로 Segmentation fault 에러가 발생

 

(알고보니 at&t 문법으로 작성된 코드를 intel로 컴파일하고 있었다... 참고할것)

 

(gdb) disas /r main 에서 발췌

=> 0x08048480 <+3>:     68 78 56 34 12  push   $0x12345678

(gdb) i r $eip
eip            0x8048480           0x8048480 <main+3>
(gdb) x/i $eip
=> 0x8048480 <main+3>:  push   $0x12345678

→ eip 레지스터에 0x12345678을 넣는 명령어를 실행하고 있다.

 

(gdb) i r $eax
eax            0xf7fbe248          -134487480

→ 원래 $eax 레지스터에는 쓰레기값이 들어가 있었다

(gdb) disas /r main

=> 0x08048487 <+10>:    b8 01 00 00 00  movl    $0x1,%eax

(gdb) i r $eax
eax            0x1                 1

→ 실행후 1이 셋팅되었음

 

 

AX(16bit) : AH(8bit) + AL(8bit)

EAX(32bit) : AX + AX

RAX(64bit) : EAX + EAX

RAX (64)
EAX (32) EAX (32)
AX (16) AX (16) AX (16) AX (16)
AH AL AH AL AH AL AH AL

 

AH

- AX 레지스터의 상위 8비트.  32 bit 환경에서 사용

AL

- AX 레지스터의 하위 8비트. 32 bit 환경에서 사용

AX

- EAX 레지스터의 하위 16비트. 32 bit 환경에서 사용

EAX

- RAX 레지스터의 하위 32비트. 32 bit 환경에서 사용

RAX (default)

- 64비트 레지스터, x86_64 아키텍처 환경에서 사용된다.

 

* movl이 아니라 movb를 사용하면 8비트 데이터 이동을 할 수 있다

movw 16비트

movl 32비트

movq 64비트

 

%esp 레지스터의 경우, 괄호로 감싸주지 않으면 Segmentation falut 에러가 발생한다.

 

(gdb) x/xw $esp
0xffffd444:     0x12345678

(gdb) disas /r main

=> 0x08048485 <+8>:     c6 04 24 01     movb   $0x1,(%esp)

(gdb) ni
1: $esp = (void *) 0xffffd444
2: $ebp = (void *) 0xffffd448

(gdb) x/xw $esp
0xffffd444:     0x12345601

→ 원래 78이었는데, $esp 메모리 주소에 01이 들어감 확인. 왜 끝에 들어갔냐면, 리틀 엔디안 방식을 따라 가장 낮은 주소부터 채워지기 때문이다.

 

(gdb) disas /r main

=>  0x08048489 <+12>:    66 c7 04 24 56 78       movw   $0x7856,(%esp)

(gdb) ni
1: $esp = (void *) 0xffffd444
2: $ebp = (void *) 0xffffd448
(gdb) x/xw $esp
0xffffd444:     0x12347856

→ 역시 $esp 메모리 주소에 7856이 들어감을 확인할 수 있다.

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

정보보안2 7차시  (0) 2024.05.25
정보보안2 6차시  (0) 2024.05.19
정보보안2 4차시  (0) 2024.05.12
정보보안2 3차시  (0) 2024.05.11
과제)  (0) 2024.05.09