1 shellcode
(1) 정의
- 프로세서 맞춤형 기계어 명령어들의 집합
- 메모리 안에서 실행됨
- 어셈블리 언어를 사용해 생성됨
- 생성절차 : C 프로그램 > 어셈블리 프로그램 > 쉘코드
(2-1) Helloworld.asm 실습
- 소스코드
section .data ; Data Segment
msg dd "Hello, World!", 0x0a ; String and newline character
section .text ;Text segment
global _start ;Default entry point for ELF linking
_start: ; _start label
;SYSCALL: write(1, msg, 14)
mov eax, 4 ; Put 4 into eax, since write is syscall #4.
mov ebx, 1 ; Put 1 into ebx, since stdout is 1.
mov ecx, msg ; Put the address of the string into ecx.
mov edx, 14 ; Put 14 into edx, since out string is 14 bytes.
int 0x80 ; Call the kernel to make the system call happen
;SYSCALL: exit(0)
mov eax, 1 ; Put 1 into eax, since exit is syscall #1.
mov ebx, 0 ; Exit with success.
int 0x80 ; Do the syscall.
nasm -f elf helloworld.asm // 오브젝트 파일로 어셈블
ld helloworld.o // 링커프로그램 사용하여 a.out 실행 바이너리 생성
(2-2) 문제점
- 메모리에 데이터 세그먼트 선언해야 함
- Self-conained되지 않음, 메모리 안에서 독립적으로 선언된 것이 아닌 오브젝트 파일 링킹을 사용함
- 위치 의존적인 코드, Start Point를 가져야만 실행될 수 있음
(3-1) Helloworld1.s 실습
- 소스코드
BITS 32 ; Tell nasm this is 32-bit code.
call mark_below ; Call below the string to instructions
db "Hello, World!", 0x0a, 0x0d ; with newline and carriage return bytes.
mark_below:
; ssize_t write(int fd, const void *buf, size_t count);
pop ecx ; Pop the return address (string ptr) into ecx.
mov eax, 4 ; Write syscall #4
mov ebx, 1 ; STDOUT file descriptor
mov edx, 15 ; Length of the string
int 0x80 ; Do syscall: write(1, string, 15)
; void _exit(int status);
mov eax, 1 ; Exit syscall #1
mov ebx, 0 ; Status = 0
int 0x80 ; Do syscall: exit(0)
- call 명령어 실행 시 1)실행 전에 "hello, world"의 문자열이 저장된 주소를 스텍에 저장하고 2)mark_below로 이동
- 3) pop으로 스텍에 저장한 주소를 꺼내서 이동 // ecx에도 100이 저장되어 있음
- 0x0a, 0x0d를 함께 사용하면 엔터키를 누른 효과 나타남
nasm helloworld1.s // 오브젝트 파일로 어셈블
1) hexdump -C helloworld1 // helloworld1 실행파일의 값을 hexDump로 확인
2) ndisasm -b32 helloworld1 // 어셈블리와 기계어 코드를 모두 확인할 수 있음
(3-2) 문제점
- call 명령어 기계어 코드에 Null byte가 포함된 것을 확인할 수 있음 > strcpy()는 nullbyte를 만나면 더이상 복사 안함
- 불완전하고 정상적으로 동작하지 못하는 쉘코드가 메모리에서 생성됨
- Self-conained이며 위치에 독립적인 코드이지만 Nullbyte 문제점을 가짐
(4-1) Helloworld2.s 실습
- call 명령어에서 nullbyte를 제거하기 위한 접근법
- jump는 무조건 레이블로 이동하고 call은 다음 주소를 스택에 넣고 이동함
- 소스코드
BITS 32 ; Tell nasm this is 32-bit code.
jmp short one ; Jump down to a call at the end.
two:
; ssize_t write(int fd, const void *buf, size_t count);
pop ecx ; Pop the return address (string ptr) into ecx.
mov eax, 4 ; Write syscall #4
mov ebx, 1 ; STDOUT file descriptor
mov edx, 15 ; Length of the string
int 0x80 ; Do syscall: write(1, string, 14)
; void _exit(int status);
mov eax, 1 ; Exit syscall #1
mov ebx, 0 ; Status = 0
int 0x80 ; Do syscall: exit(0)
one:
call two ; Call back upwards to avoid null bytes
db "Hello, World!", 0x0a, 0x0d ; with newline and carriage return bytes
\
- jmp 를 사용해서 원하는 위치로 이동하고(+) call로 역이동 함(-)
- 2의 보수 접근법에 의해 0x00이 0xff로 변함