글

Inside the CPU Part 2: RTL — Hardware CPU (TCG가 흉내 냈던 동작은 실제 CPU 회로에서는 어디에 존재하는가?)

Inside the CPU Part 1: QEMU TCG — Software CPU (소프트웨어로 CPU instruction을 어떻게 구현하는가?)

kprobes를 이용한 커널 instrumentation

 kprobes 에 대하여 kprobes는 linux kernel에 내장된 디버깅 API이며 프로세서의 디버그 기능을 기반으로 한다. linux kernel 소스코드에는 kprobes.txt 문서가 있다. 현재 probe 에는 3가지 종류가 있다. kprobe: 커널의 거의 모든 명령어에 삽입할 수 있다. jprobe: 커널 함수의 진입점에 삽입되며 함수의 인자에 편리하게 접근할 수 있게 해준다. kretprobe: 지정한 함수가 반환될 때 동작한다. 이 정의를 보면 kprobes 인터페이스를 이용해 커널을 여러 흥미로운 방법으로 계측할 수 있다는 것을 상상할 수 있다. 보안을 강화하는 방향으로도 반대로 약화하는 방향으로도 사용할 수 있다.  kprobes 가 커널 자체에서 제공되는 인터페이스라는 점은 장점이지만 뒤에서 살펴볼 여러 제한도 존재한다. 즉, kprobes는 커널의 특정 부분을 패치하는 데 사용할 수 있지만 모든 것을 처리할 수 있는 것은 아니다. 그럼 언제, 어디서 커널의 동작을 수정할 수 있는지 살펴보자. 경우에 따라 다른 패치 방법과 함께 사용해야 할 수도 있다. 몇가지 기본적인 사실은 먼저 등록된 kprobe는 /sys/kernel/debug/kprobes/list에서 확인할 수 있다. /sys/kerenl/debug/kprobes/enabled 파일에 0 또는 1을 기록하면 kprobes를 비활성화하거나 활성화할 수 있다. kprobes 소스코드는 다음 위치에 있다. /usr/src/linux/kernel/kprobes.c /usr/src/linux/arch/x86/kernel/kprobes.c jprobes와 kretprobes는 전적으로 kprobes를 기반으로 구현되어 있어 kprobes를 비활성화하면 kretprobe와 jprobe 역시 동작하지 않는다. jprobe 예제 probe를 등록하거나 해제하는 여러 함수가 제공된다. 예를 들어 sys_mprotect 호출을 관찰하고 어떤 인자가 전달되는지 확인하고 싶다고 ...

VM escape - QEMU의 메모리를 읽는 두 가지 방법

악성코드가 VM을 탈출하여 호스트 머신에서 코드를 실행할 수 있을까? KVM(Kernel-based Virtual Machine) 은 사용자 공간 프로그램에 완전 가상화 인프라를 제공하는 커널 모듈이다. 이를 통해 수정되지 않은 Linux 또는 Windows 이미지를 실행하는 여러 가상 머신을 구동할 수 있다. KVM 의 사용자 공간 구성요소는 메인라인 QEMU(Quick Emulator)에 포함되어있으며 특히 device emulation을 담당한다. 개발 환경 여기서 타겟으로 잡은 취약점은 이미 오래전 패치되었기 때문에 QEMU 소스에서 해당 취약점 수정 직전의 커밋으로 checkout 해야한다. QEMU 메모리 배치 게스트에 할당된 물리 메모리는 실제로 QEMU의 가상 주소 공간 내에서 mmap 된 private 영역이다. 게스트의 물리 메모리를 할당할 때 PROT_EXEC 플래그가 활성화되지 않는다는 점에 주목하는 것이 중요하다. 게스트 프로세스 +--------------------+ 가상 주소 공간 | | +--------------------+ | | \__ 페이지 테이블 \__ \ \ | | 게스트 커널 +----+--------------------+----------------+ 게스트 물리 메모리 | | | | +----+--------------------+--------...

ELF 구조부터 로딩까지

ELF format ELF(Executable and Linking Format)는 실행 가능한 코드를 위한 표준화된 파일 형식이다. ELF는 주로 실행 파일(ET_EXEC)이나 공유 라이브러리(ET_DYN) 에 사용된다. 현재 거의 모든 현대적인 Unix 계열 운영체제는 ELF 의 이식성, 표준화된 기능, 처음부터 깔끔하게 설계된 구조 덕분에 ELF 형식을 지원한다. ELF 표준의 실제 버전은 1.2 이다. ELF 표준을 다루는 여러 문서가 있으며 자세한 내용은 Tool Interface Standard, Executeable and Linking Format, Version 1.2 를 참고하면 된다. ELF 바이너리 형식은 다음 두가지 요구사항을 모두 만족하도록 설계되었다. linker : 일반적으로 컴파일 과정에서 사용된다. loader : 일반적으로 프로그램 실행 시점에서 사용된다. 이 두가지 요구사항을 만족하기 위해 ELF 바이너리 파일 내부의 데이터를 설명하는 서로 구별되는 두가지 인터페이스가 포함되었다. 이 두 인터페이스는 서로 의존하지 않는다. ELF headers ELF 파일은 최소한 ELF header를 반드시 포함해야 한다. ELF 헤더에는 바이너리 파일의 내용을 어떻게 해석해야 하는지에 대한 정보와 바이너리를 설명하는 다른 구조체들이 파일의 어디에 위치하는지에 대한 정보가 들어있다. ELF 헤더는 파일 내부의 offset 0에서 시작하며 다음과 같은 형식을 가진다. #define EI_NIDENT (16) typedef struct { unsigned char e_ident[EI_NIDENT]; Elf32_Half e_type; Elf32_Half e_machine; Elf32_Word e_version; Elf32_Addr e_entry; Elf32_Off e_phoff; Elf32_Off e_shoff; } Elf32_Ehdr; [e_ident] e...