본문 바로가기

개념정리/System Hacking

4-2. 어셈블리(Assembly) - 스택, 프로시저, 시스템콜

 

저번 시간에는 기계어와 어셈블리 기본구조, 산술연산, 논리연산, 비교, 분기를 공부하였다.

다시 공부하고 싶다면 밑에 링크를 참고하면 된다.

 

4-1. 어셈블리(Assembly)

1. 기계어 CPU가 직접 해독하고 실행할 수 있는 비트 단위로 쓰인 컴퓨터 언어이다. 0과 1로만 구성돼 있어서 단어 위주로 문장을 구사하는 사람으로서 이해하기 어렵다. 2. 어셈블리 언어 기계어

sungw00k.tistory.com

이어서 이번 시간에는 스택, 프로시저, 시스템콜에 관련된 어셈블리를 공부할 것이다. 

 


1. Opcode : 스택

스택을 조작할 수 있는 명령어로 1)push, 2)pop이 있다.

1) push

push val : val을 스택 최상단에 쌓는다. 

연산

rsp -= 8
[rsp] = val

 

2) pop

pop reg : 스택 최상단의 값을 꺼내서 reg에 대입한다.

연산

rsp += 8
reg = [rsp-8]

 

2. Opcode : 프로시저

프로시저는 특정 기능을 수행하는 코드 조각이다. 반복되는 연산을 프로시저 호출로 대체하여 코드 크기를 줄이며, 기능별로 코드 조각에 이름을 붙여 코드의 가독성을 높일 수 있다. 프로시저는 호출(Call)과 반환(Return)이 있다.

- 호출(Call) : 프로시저를 부르는 행위

- 반환(Return) : 프로시저에서 돌아오는 행위

프로시저를 호출할 때는 프로시저를 실행하고 나서 원래의 실행 흐름으로 돌아와야 하므로, call  다음의 명령어 주소(반환 주소)를 스택에 저장하고 프로시저로 rip를 이동시킨다. x64어셈블리언어는 프로시저의 호출과 반환을 위한 1)call, 2)leave, 3)ret 명령어가 있다.

1) call

call addr : addr에 위치한 프로시져 호출

연산

push return_address
jmp addr

 

2) leave

leave : 스택프레임 정리

연산

mov rsp, rbp
pop rbp

 

3) ret

ret : return address로 반환

연산

pop rip

 

3. Opcode : 시스템 콜

운영체제는 컴퓨터 자원의 효율적인 사용 및 사용자에게 편리한 경험을 제공하기 위해 내부적으로 복잡한 동작을 한다. 또한 하드웨어, 소프트웨어에 접근하여 이들을 제어할 수 있다.  그리고 해킹으로 부터 보호하기 위해 1)커널 모드와 2)유저 모드로 권한을 나눈다.

1) 커널 모드 

운영체제가 전체 시스템을 제어하기 위해 시스템 소프트웨어에 부여하는 권한이다. 파일 시스템, 네트워크 통신, 메모리 관리 등 사용자 모르게 커널 모드에서 진행된다. 커널 모드에서 시스템의 모든 부분을 제어하기 때문에 해커가 커널 모드까지 집입하게 되면 시스템은 무방비 상태가 된다. 

2) 유저 모드

운영체제가 사용자에게 부여하는 권한이다. 프로그래밍하거나 브라우저를 이용해 유튜브 시청등 유저 모드에서 이루어 진다. 유저 모드는 해킹이 발생해도 유저 모드 권한까지 밖에 획득하지 못하므로 커널의 권한을 보호할 수 있다.

 

시스템 콜(syscall)유저 모드에서 커널 모드의 시스템 소프트웨어에게 동작을 요청하기 위해 사용된다. 

1) syscall 

필요한 기능과 인자에 대한 정보를 레지스터로 전달하고 커널이 이를 읽어 요청을 처리한다. 

요청 : rax

인자 순서 : rdi - rsi - rdx - rcx - r8 - r9 - stack

 

2) x64 syscall 테이블

시스템 콜 테이블 일부이다. 엄청 많으므로 외울 수 없고 중요한 몇가지만 익숙해지면 된다.

syscall rax arg0 (rdi) arg1 (rsi) arg2 (rdx)
read 0x00 unsigned int fd char *buf size_t count
write 0x01 unsigned int fd const char *buf size_t count
open 0x02 const char *filename int flags umode_t mode
close 0x03 unsigned int fd    
mprotect 0x0a unsigned long start size_t len unsigned long prot
connect 0x2a int sockfd struct sockaddr * addr int addrlen
execve 0x3b const char *filename const char *const *argv const char *const *envp

 

<시스템 콜 예제>

[Register]
rax = 0x1   
rdi = 0x1   
rsi = 0x401000  
rdx = 0xb 

[Memory]
0x401000 | "Hello Wo"   
0x401008 | "rld"  

[Code]  
syscall

[result]
Hello World

 

<시스템 콜 예제 설명>

syscall table을 참고하여 rax가 0x01이면 커널에 write 시스템 콜을 요청한다. 그리고 인자 rdi = 0x01, rsi = 0x401000, rdx = 0xb 이므로 write(0x01, 0x401000, 0xb)를 수행한다. write 함수의 인자는 각각 출력 스트림, 출력 버퍼, 출력 길이이다. 0x01은 stdout, 0x401000은 Hello World, 길이는 0xb이다. 결과로 Hello World가 출력된다.

 

 


 

이번 시간에는 스택, 프로시저, 시스템콜에 관련된 어셈블리를 공부하였다. 

다음 시간에는 리눅스 대표적인 디버거 중 하나인 gdb 기능들과 사용법을 공부할 것이다. 

 

 

'개념정리 > System Hacking' 카테고리의 다른 글

6. pwntools  (0) 2022.08.01
5. gdb 디버거  (0) 2022.07.21
4-1. 어셈블리(Assembly) - 산술연산, 논리연산, 비교, 분기  (2) 2022.07.06
3. 리눅스 프로세스의 메모리 구조  (0) 2022.07.04
2. x86-64 아키텍처  (0) 2022.07.04