ARM 아키텍처를 사용하기 전 패키지 설치
sudo apt install build-essential
sudo apt install gcc-arm-linux-gnueabihf
sudo apt install libc6-armhf-cross
sudo apt install gdb gdb-multiarch
for MIPS
ARM 아키텍처를 이용하는 경우 설치하지 않아도 괜찮다.
sudo apt install gcc-mips-linux-gnu
sudo apt install libc6-mips-cross
플러그인(peda, pwndbg, gef)
git clone https://github.com/apogiatzis/gdb-peda-pwndbg-gef
git clone
명령으로 파일을 받은 다음 해당 디렉토리로 들어가 install.sh를 실행하면 메인 디렉토리에 peda-arm, pwndbg, gef 디렉토리가 생성된다. gef는 레지스터, 스택의 꼭대기, 디스어셈블된 코드, 스레드 등 정보를 시각화해준다.
QEMU 유저 모드
유저 모드에서는 다른 아키텍처를 위한 바이너리를 만들고, 컴파일하고, 디버그할 수 있다.
ARM 아키텍처용 hello world.c 파일을 컴파일 해보자.
#include <stdio.h>
int
main()
{
printf("Hello, qemu fans!\n");
return 0;
}
컴파일
arm-linux-gnueabihf-gcc -g -o hello-world-arm hello-world-arm.c
arm-linux-gnueabihf-gcc -static -g -o hello-world-arm-static hello-world-arm.c
file hello-world-arm
file hello-world-arm-static
-g
는 바이너리에 디버깅 정보를 포함하라는 옵션이고, -static
은 정적으로 링크하라는 옵션이다. 스태틱 링킹은 프로그램 실행 중 함수가 사용될 때 라이브러리를 불러오는 동적 링크(dynamic linking)과는 달리 프로그램 바이너리 파일 생성 시기에 라이브러리를 불러온다.
이제 컴파일된 프로그램을 qemu로 실행해보자.
qemu-arm hello-world-arm-static
qemu-arm hello-world-arm
qemu-arm -L /usr/arm-linux-gnueabihf/ hello-world-arm
정적 링킹된 파일은 바로 실행할 수 있지만, 동적 링킹된 경우 qemu가 해석할 수 없으므로 라이브러리를 지정해주어야 한다.
디버깅
먼저 QEMU로 바이너리를 실행시키고 디버거를 기다린다.
qemu-arm -L /usr/arm-linux-gnueabihf/ -g 1234 hello-world-arm
다음 다른 터미널을 열어서 gdb 디버거를 넣어준다.
gdb-multiarch -q -ex 'init-gef' -ex 'set architecture arm' -ex 'set solib-absolute-prefix /usr/arm-linux-gueabihf/'
위 명령을 통해 get가 열리면 아래 명령을 입력한다.
gef-remote --qemu-user --qemu-binary hello-world-arm localhost 1234
gef를 켠 터미널에서 탐색을 계속한다.
아래 명령으로 바이너리의 심볼을 로드한다.
file hello-world-arm
main 함수에 중단점을 잡고 계속 진행한다.
b main
c
디버깅을 계속하면서 바이너리가 실행되는 도중에 메모리와 레지스터가 어떻게 바뀌는지 확인할 수 있다.
QEMU 풀 시스템 모드
풀 시스템 모드(full-system mode)에서는 CPU, 플랫폼 칩셋, 버스 등을 포함한 머신을 에뮬레이트할 수 있다.
시스템 에뮬레이션은 -M
이나 --machine
옵션으로 설정할 수 있다. 예시는 다음과 같다.
qemu-system-arm -M raspi3b ...
qemu-system-x86_64 -M q35 ...
대상 아키텍처에 따라 부팅 과정은 다를 수 있다. x64에서 QEMU는 기본적으로 PC BIOS 펌웨어를 부팅한다. 이는 표준 절차를 사용해 부트로더에 제어권을 넘겨준다. 그러나 AArch64 리눅스 등 많은 다른 플랫폼은 초기화를 위해 펌웨어에 의존하지 않으며, 공급업체별 부팅 프르토콜을 따라야 한다.
QEMU에서 유저는 이런 경우 -kernel
이나 -initrd
옵션으로 리눅스 커널과 초기 RAM 디스크 이미지를 제공할 수 있다.
또한 KVM(Kernel-based Virtual Machine) 커널 모드와 함께 QEMU는 하드웨어 지원 가상화를 제공한다. 호스트와 대상 아키텍처가 일치하는 경우 대부분의 명령어를 에뮬레이션을 방지(aviod)하는 데 사용할 수 있다. -accel kvm
옵션으로 활성화할 수 있다.
예시는 다음과 같다.
qemu-system-arm -M raspi2b -m 1G -sd disk.img -serial stdio \
-kernel boot/kernel7.img -initrd boot/initramfs-linux.img \
-dtb boot/bcm2709-rpi-2-b.dtb \
-append 'root=/dev/mmcblk0p2 rw rootwait console=ttyAMA0,115200'
GDB 리모트 디버깅 옵션도 있다. -s
옵션은 TCP 1234번 포트에 GDB 리모트 디버깅을 사용한다는 의미이고, -S
옵션은 시작 시 CPU를 정지(freeze)한다는 의미이다.
리눅스 커널 이미지
일반적으로 압축 없는 빌트 바이너리(built binary without compression)은 vmlinux라 불린다. 이는 ELF 파일이고, 많은 디버거가 바로 식별할 수 있다. bzImage, zImage, vmlinuz는 원본 ELF 파일을 압축한 버전이다. bzip2 압축은 bzImage에,, gunzip은 zImage나 vmlinuz에 사용된다. 압축된 파일은 RAM에 압축해제할 때 압축되지 않은 데이터를 읽는 것보다 빠르다. 부팅 과정에는 초기 파일 시스템인 initrd 이미지도 필요하다.
참고자료
Antonio Nappa , Eduardo Blázquez - Fuzzing Against the Machine
'보안 > fuzzing' 카테고리의 다른 글
[하드웨어 해킹] 퍼징 기술 (0) | 2024.01.15 |
---|---|
[하드웨어 해킹] 퍼징과 분석 기술 - 심볼릭 실행 (0) | 2024.01.11 |
[하드웨어 해킹] QEMU (2) | 2024.01.09 |
[하드웨어 해킹] 에뮬레이션(emulation)이란? (1) | 2024.01.09 |
퍼징(fuzzing) 환경 구축하기 (1) | 2024.01.06 |