본문 바로가기

학교공부📚

시스템보안^^...공부기록 : 어셈블리어, pwntools 사용법, 시스템해킹 풀기 전 사전지식~~~~😇

아무것도 모르는데 시스템 보안 수업 듣고 후회하며 작성하는 공부 기록...! 

 

 

📚시스템보안을 위한 사전지식..?

1. 시스템의 구조 

로더의 입장에서 바라본 세그먼트!! bss를 데이터 영역이라고 볼 수 도 있음 일단 따로 씀

세그먼트는 코드, 데이터, bss, stack, heap 으로 나뉨

  • Text segment : 코드가 올라감 ㄹㅇ 코드
  • Data segment: 초기화 된 전역변수들
  • BSS : 초기화 되지않은 전역변수들
  • stack : 함수, 지역변수들 ***스택은 밑으로 자라남! (높은 주소에서 낮은 주소로 자란다는 뜻, 스택에서 가장 많은 취약점이 발견됨)
  • heap : 동적할당되는 변수들

32비트 컴퓨터에서(2^32-1을 2^64-1로 하면 64비트컴퓨터)

참고자료

 https://m.blog.naver.com/PostView.naver?isHttpsRedirect=true&blogId=thechizko&logNo=20129533828 

+ 교수님 강의

 

2.  레지스터의 종류 및 역할 정리

👀64bit 컴퓨터의 범용 레지스터 16개 

: rax, rbx, rcx, rdx, rsi, rdi, rsp, rbp, r8 ~ r15

r로 시작하는 레지스터는 64비트임. e로 시작하면 32비트 (rax -> eax)

+ 플래그 레지스터, rip(==pc 레지스터)

 

1. 범용 레지스터

  • rax : 산술/논리 연산, *함수의 리턴값 저장, 시스템 콜 함수 호출 시 rax에 시스템콜 주소를 담아둠
  • rbx : 메모리 주소 지정
  • rcx : 반복문에 많이 사용
  • rdx : 여분, rax와 같이 사용될 때가 많음

 

2. 인덱스 레지스터

  • rsi : 메모리 이동하거나 비교할 때 출발주소를 가리킴
  • rdi : 메모리 이동하거나 비교할 때 도착주소를 가리킴

3. 포인터 레지스터

  • rsp (stack pointer) : 스택의 가장 위를 가리킴
  • rbp (base pointer) : 스택의 가장 바닥

4. 플래그 레지스터

  • ZF (Zero Flag) : 연산 결과가 0이면 1
  • CF (Carry Flag) : 부호 없는 수의 연산 결과가 비트의 범위를 넘을 경우
  • SF(Sign Flag) : 연산결과가 음수
  • OF(Overflow Flag) : 부호 있는 수의 연산결과가 비트 범위를 넘을 때

이외에도 많음

 

5. 파라미터 전달 시 사용하는 레지스터 (리눅스 기준)

  • RDI : 첫번째 인자
  • RSI : 두번째 인자
  • RDX : 세번째 인자
  • RCX : 네번째 인자
  • R8 : 다섯번째 인자 
  • R9 : 여섯번째 인자
  • 그 이상은 스택 활용

 

6. 기타 등등

  •  rip : 앞으로 수행할 명령어의 주소 (program counter; pc 랑 같은 거인듯?)

 

 

[참고자료]

https://com24everyday.tistory.com/223

https://velog.io/@hidaehyunlee/libasm-%EC%96%B4%EC%85%88%EB%B8%94%EB%A6%AC-%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%A8-%EA%B5%AC%EC%A1%B0%EC%99%80-x64-%EB%A0%88%EC%A7%80%EC%8A%A4%ED%84%B0-%EC%9D%B4%ED%95%B4%ED%95%98%EA%B8%B0

https://yechoi.tistory.com/10

https://blog.sonagi.dev/os_day02/

+강의

 

3.  스택 프레임

1.

스택프레임은 함수의 호출과 함께 할당된다.

sp는 스택의 가장 위를 가리키는 값 (=rsp 레지스터)

bp는 스택의 가장 아래를 가리킴 (=rbp 레지스터)

2. 함수 프롤로그, 에필로그 과정

 

함수 시작 시 이전 함수의 sp값을 bp에 저장

rsp의 값을 빼주면서 스택 프레임의 크기를 늘려감(스택은 높은 주소 -> 낮은 주소로 자라기 때문에 rsp 값이 작아지면 스택이 커짐)

 

함수 끝나면 sp값에 bp 값을 넣음

rbp와 rsp 가 같은 위치에 있게 됨. pop rbp로 rbp에 sfp(stack frame pointer)값을 넣음

sfp는 이전 함수의 bp 값임

그 다음 pop rip로 다음에 실행해야 할 instruction 주소를 rip에 담음.

 

3. 함수 프롤로그 ~ 에필로그 과정 자세히 (어셈블리어 먼저 보고 오기!)

1
2
3
4
5

leave 는

mov rsp, rbp

pop rbp

 

ret 는 pop rip임

 

[참고자료]

dreamhack.io

https://hg2lee.tistory.com/entry/%EC%8B%9C%EC%8A%A4%ED%85%9C-Prologue-%ED%94%84%EB%A1%A4%EB%A1%9C%EA%B7%B8-%EA%B3%BC%EC%A0%95?category=1240865

http://www.tcpschool.com/c/c_memory_stackframe

4. plt와 got

https://hg2lee.tistory.com/entry/%EC%8B%9C%EC%8A%A4%ED%85%9C-PLT-%EC%99%80-GOT-%EC%B4%88%EA%B8%89?category=1240865 

 

[시스템] PLT 와 GOT [초급]

시스템 해킹(포너블)을 하다보면 PLT, GOT 어디선가 들어봤고, plt, got 라고 불리우는것들을 막 호출하고 변조하고 할겁니다. 이번에는 이러한 plt와 got가 뭔지에 대해 알아보도록 하겠습니다. 보통

hg2lee.tistory.com

 

📚어셈블리어

인텔문법 사용할 것!

 

👀리눅스에서 실행파일 인텔 문법 어셈블리어로 보는 법

objdump -d -M intel 실행파일이름

👀어셈블리어 기본 구조

opcode operand1 operand2

 

📍어셈블리어 알아보기

주석은 ; 임

 

✅ operand의 종류

1. 상수 : 숫자

2. 레지스터 : rax, rbx 등등

3. 메모리 : []로 둘러싸임. 앞에 크기지정자(TYPE PTR)가 추가 될 수 있음.

    TYPE : QWORD(8바이트), DWORD(4), WORD(2), BYTE(1)

 

✅ opcode의 종류

 

1. 데이터 transfer 명령어

push ; 스택에 값을 넣는다.
pop ; 스택에서 값을 빼냄

mov rdi rsi             ;rsi의 값을 rdi에 대입 
mov QWORD PTR[rdi], rsi ; rsi의 값을 rdi가 가리키는 주소에 대입 

lea rsi, [rbx+8*rcx]    ; rbx+8*rcx를 rsi에 대입

2. 조작 명령어

call ; 함수호출

ret ; call로 호출된 함수 종료, 그 다음 명령줄로 이동

nop ; 아무것도 안함

jmp addr; 분기 명령어 addr로 rip를 이동시킴

je addr; 직전에 비교한 두 피연산자가 같으면 점프   

jg addr; 직전에 비교한 두 피연산자 중 전자가 더 크면 점프

3. 논리 연산

; 비트연산자
and dst, src ;둘 다 1이면 1 아니면 0
or dst, src  ; 둘 중 하나만 1이면 1
xor dst, src ; 서로 다른 비트면 1 같으면 0 
not op       ; 비트 반전

4. 산술 연산

inc op; 인자의 값을 1 증가
dec op; 인자의 값을 1 감소
add dst, src; 인자2 값을 인자1에 더함
sub dst, src; 인자2 값을 인자1에서 뺌

cmp op1 op2; 인자 1과 인자 2 비교 op1 - op2로 대소 비교
; 0일경우 zf=1, 음수일 경우 sf = 1

test op1, op2 ; op1과 op2에 and 연산을 취함 연산의 결과를 op1에 저장하지는 않음


;test 예시, 0인지 확인하는데 자주 사용

xor rax, rax  ;rax가 0이됨
test rax, rax ; 연산결과 0이 나옴, zf=1

 

5. 프로시저, 스택

call addr ; addr에 위치한 프로시저 호출
; 프로시저를 호츨하기 위해 돌아와야 하는 return address를 push한 후 jmp addr함

leave    ; 스택 프레임 정리
; mov rsp, rbp
; pop rbp


ret ; return address로 반환
; pop rip

pop reg  ; 스택 최상단의 값을 reg에 담음
push val ; 스택 최상단에 쌓기

6. 시스템 콜

rax로 시스템 콜 요청 (syscall table을 통해 몇번인지 알 수 있음)
read는 0, write는 1, open은 2 등등~
execve는 0x3b

write(1, "Hello World", 11);

<registers>
rax = 0x1
rdi = 0x1 ; 첫번째 argument
rsi = hello world가 저장되어있는 주소 ; 두번째 argument
rdx = 0xb ; 세번째 argumnet

<code>
syscall

📚GDB 사용법

gdb기능 너무 많아.. 

https://dining-developer.tistory.com/13

 

gdb - 간단한 명령어/사용법/단축어 정리(cheat sheet)

GDB를 이용해 간간히 디버깅 하긴 했지만, 자주 사용하지 않아서 익숙하지 않았다. 앞으로 사용할 때마다 검색시간을 줄이기 위해 내가 사용하는/했던 gdb 명령어를 정리해놓기로 했다. 명령어를

dining-developer.tistory.com

https://bpsecblog.wordpress.com/2016/04/04/gdb_memory_2/

 

우리집에 GDB 있는데… 메모리 보고갈래?(2)

season 1. 우리집에 GDB 있는데… 메모리 보고 갈래?(2) Day #2. 애프터 신청 (너 gdb 사용법, 갖고싶다.. 너란 stack )   in09@ubuntu:~/bpsecblog/day2$ gcc -fno-stack-protector -o tomato tomato.c -fno-stack-protector 옵션을 포

bpsecblog.wordpress.com

 

1. 실행파일(elf파일) 분석하기!

 

- elf file의 헤더정보 출력

readelf -h 실행파일이름

Entry point addr(EP)은 진입점 : 프로그램 실행 시 이 주소부터 시작

 

2. gdb 명령어

set disassembly-flavor intel : 인텔문법으로 바꾸기

b 함수이름 : 함수에 breakpoint 걸기
b *주소 : 주소에 breakpoint 걸기

r (run) : 실행 argument 넣을 때는 r 옆에 쓰면 됨

disas main : main 함수 disassemble

- 한줄씩 실행
si : 한줄씩 실행 함수 만날 시 함수 내부로 들어감
ni : 함수내부로 안들어감

-특정 주소의 뭔가를 출력

x/원하는길이원하는format

format 예시
o : 8진법으로 보여줌
x : 16진법으로 보여줌
u : 10진법으로 보여줌
t : 2진법으로 보여줌

b : 1 byte 단위로
h : 2 byte 
w: 4 byte 
g : 8 byte 단위로 

i : 역어셈블된 명령어
c : 문자
s : 문자열

예시)
x/5i $rip : rip 부터 5줄 어셈블리어
x/s 0x주소 : 주소의 값을 문자열로
x/10gx $rsp : rsp부터 8바이트 단위로 10줄 총 80바이트를 16진수로 출력
-레지스터 출력
info reg : 레지스터 정보 출력
info reg rsi : rsi 출력

 

📚pwntools 사용법

1. 로컬 바이너리를 대상으로 익스플로잇 할 때

from pwn import *

p = process("./test")

2. send, recv

from pwn import *

p = process('./test')
p.send('A') # ./test에 'A'를 입력
p.sendline('A') # ./test에 'A'+'\n'을 입력
p.sendafter('hello','A') # ./test가 'hello'를 출력하면, 'A'를 입력
p.sendlineafter('hello','A') # ./test가 'hello'를 출력하면, 'A' + '\n'을 입력
data = p.recv(1024) #p가 출력하는 데이터를 최대 1024바이트까지 받아서 data에 저장
data = p.recvline() #p가 출력하는 데이터를 개행문자를 만날 때까지 받아서 data에 저장
data = p.recvn(5) #p가 출력하는 데이터를 5바이트만 받아서 data에 저장
data = p.recvuntil('hello') #p가 출력하는 데이터를 'hello'가 출력될 때까지 받아서 data에 저장
data = p.recvall() #p가 출력하는 데이터를 프로세스가 종료될 받아서 data에 저장

 

 

ㅎㅋㅎㅋ...

1.  메모리 보호 기법

- Stack Canary : 스택 버퍼 오버플로 취약점을 방지하기 위한 메모리 보호 기법 중 하나임

sfp(stack frame pointer) 버퍼 사이에 임의의 데이터를 넣음

많은 공격들은 아래 그림의 ret 값을 변경하려고 함. sfp 위에 canary값을 넣어놓고 버퍼오버플로를 통해 ret 값을 조작하려 하면 canary가 변경되어 경고가 뜸

 

ㅎㅋㅎㅋ...ㅎㅋ.ㅎㅋㅎ..