mov rsi, rsp: 스택포인터 즉 rsp의 현재 값을 rsi 레지스터로 복사 → buf 인자 설정을 위한 준비 단계
sub rsi, 0x30: rsp에서 0x30(48바이트)를 빼고 그 결과를 rsi에 저장. 이는 buf의 주소를 스택 상의 현재 위치로부터 48바이트 위로 설정 → 스택의 이 부분을 읽기 버퍼로 사용 가능
mov rdx, 0x30: rdx 레지스터에 0x30(48바이트)을 저장. → 이 값은 count 인자로, 읽은 바이트 수 표시
mov rax, 0x0: rax 레지스터에 0을 저장 → read 시스템 콜 번호는 0을 의미
syscall: 실제 시스템 콜을 실행
✅ fd란? 파일 서술자 (File Descriptor, fd)는 유닉스 계열의 운영체제에서 파일에 접근하는 소프트웨어에 제공하는 가상의 접근 제어자를 의미한다. 프로세스마다 고유의 서술자 테이블을 갖고 있으며, 그 안에 여러 파일 서술자를 저장한다. 서술자 각각은 번호로 구별되는데, 일반적으로 0번은 일반 입력(Standard Input, STDIN), 1번은 일반 출력(Standard Output, STDOUT), 2번은 일반 오류(Standard Error, STDERR)에 할당되어 있으며, 이들은 프로세스를 터미널과 연결해준다. 그래서 우리는 키보드 입력을 통해 프로세스에 입력을 전달하고, 출력을 터미널로 받아볼 수 있다.
mov rdi, 1: rdi 레지스터에 1을 저장. write 시스템 콜의 첫 번째 인자인 파일 디스크립터(fd)로, 표준 출력(stdout)을 나타내는데 사용됨. 파일 디스크립터 1은 표준 출력을 의미
mov rax, 0x1: rax 레지스터에 1을 저장. rax는 시스템 콜 번호를 저장하는데 사용되며 1은 write의 시스템 콜 번호
(2) orw 셸코드 컴파일 및 실행
윈도우는 PE, 리눅스는 ELF와 같이 운영체제는 실행 가능한 파일의 형식(ELF, Executable and Linkable Format)을 규정하고 있다. ELF는 크게 헤더와 코드 그리고 기타 데이터로 구성되어 있는데 헤더에는 실행에 필요한 여러 정보가 적혀있고, 코드에는 CPU가 이해할 수 있는 기계어 코드가 적혀있다.
▶ 현재 작성한 orw 셸코드는 아스키로 작성된 어셈블리 코드이므로 gcc 컴파일을 통해 ELF 형식으로 변형해야 함.
✅ 기본 구조를 갖춘 스켈레톤 코드 예제
// File name: sh-skeleton.c
// Compile Option: gcc -o sh-skeleton sh-skeleton.c -masm=intel
__asm__(
".global run_sh\n"
"run_sh:\n"
"Input your shellcode here.\n"
"Each line of your shellcode should be\n"
"seperated by '\n'\n"
"xor rdi, rdi # rdi = 0\n"
"mov rax, 0x3c # rax = sys_exit\n"
"syscall # exit(0)");
void run_sh();
int main() { run_sh(); }
1. orw.c 파일 생성
2. "/tmp/flag" 파일 생성
3. orw.c 컴파일 및 실행
(3) orw 셸코드 디버깅
1. orw를 gdb로 열고, run_sh()에 브레이크 포인트 설정
2. run 명령어로 run_sh()의 시작 부분까지 코드 실행
3. 시스템 콜들의 구현 여부 확인
3-1) int fd = open(“/tmp/flag”, O_RDONLY, NULL)
- 첫번째 syscall이 위치한 run_sh+29 브레이크 포인트를 설정한 후 실행하여, 해당 시점에 syscall에 들어가는 인자를 확인
- ni 명령어로 syscall을 실행하고 나면, open 시스템 콜을 수행한 결과로 /tmp/flag의 fd(3)가 rax에 저장
3-2) read(fd, buf, 0x30)
- 두 번째 syscall이 위치한 run_sh+55에 브레이크 포인트를 설정하고 실행한 후 인자 확인
- ni 명령어로 syscall을 실행
- REGISTERS 부분의 RSI를 통해서 파일의 내용이 0x7fffffffdf68에 저장되었음을 이미 알 수 있지만, x/s 명령어로도 확인
execve 셸코드
✅ execve 셸코드란?
임의의 프로그램을 실행하는 셸코드로, 이를 이용하면 서버의 셸을 획득할 수 있다. 다른 언급없이 셸코드라고 하면 execve 셸코드를 의미하는 경우가 많다.
(1) execve 셸코드 작성
✅ execve("bin/sh", null, null)
execve 셸코드는 execve 시스템 콜만으로 구성된다.
📌 execve 시스템 콜 설명 ▶ argv는 실행파일에 넘겨줄 인자, envp는 환경변수를 의미