
WHVP와 Bochs로 구축된 퍼징을 위한 하이퍼바이저
안녕하세요! applepie에 오신 것을 환영합니다! 이 도구는 퍼징, 인트로스펙션, 버그 찾기를 위해 설계되었습니다! 최신 Windows 버전에 포함된 Windows Hypervisor Platform API를 사용하는 하이퍼바이저입니다(특히 Windows 10 17763에서 개발 및 테스트되었습니다). Bochs는 심층 인트로스펙션과 장치 에뮬레이션을 제공하는 데 사용됩니다.
Windows Hypervisor Platform API(WHVP)는 Hyper-V의 하이퍼바이저 기능에 접근하기 위한 API 집합입니다. 이 API를 사용하면 특수 드라이버나 권한 없이도 사용자 공간에서 가상 머신을 쉽게 구현할 수 있습니다.
이 프로젝트는 빠르게 발전하고 있습니다. 새로운 기능이 문서화되기 전에 트윗할 가능성이 높습니다.
저는 프로젝트에 물리적인 것을 두는 것을 좋아합니다:
이 도구는 보안 연구 중 퍼징과 인트로스펙션을 위해 설계되었습니다. 하이퍼바이저를 사용하면 커널 또는 사용자 공간 등 모든 대상을 대상으로 일반적인 퍼징 기술을 적용할 수 있습니다. 이 환경을 통해 대상의 소스 코드 없이도 전체 시스템을 퍼징할 수 있습니다. 하이퍼바이저 수준에서 코드 커버리지를 수집할 수 있으며, 필요한 경우 Bochs 에뮬레이션을 사용하여 에뮬레이션 환경에서 임의의 인트로스펙션을 수행할 수 있습니다. 이 커버리지 정보는 퍼즈 케이스의 효과를 파악하는 데 사용할 수 있습니다. 커버리지 증가를 유발한 퍼즈 케이스는 흥미로운 케이스로 저장할 수 있습니다. 이 입력은 나중에 재사용되며 새로운 변형을 기반으로 구축될 수 있습니다.
스냅샷 퍼징이 이 도구의 주요 용도입니다. 특정 상태에 있는 시스템의 스냅샷을 찍어 저장합니다. 이 스냅샷은 나중에 퍼징을 위해 로드될 수 있으며, 퍼즈 케이스를 주입한 후 재개됩니다. VM을 매우 저렴하게 재설정할 수 있기 때문에 자주 재설정할 수 있습니다. Word가 부팅하는 데 5초가 걸리지만 파일을 읽는 순간에 스냅샷을 찍을 수 있다면, 퍼즈 케이스를 입력과 관련된 부분으로만 줄일 수 있습니다. 이를 통해 소스 코드에 접근할 필요 없이 매우 타이트한 퍼징 루프를 만들 수 있습니다. VM은 완전히 분리된 시스템이므로 많은 수를 병렬로 실행하여 모든 코어로 확장할 수 있습니다.
현재 이 도구는 코드 커버리지 수집, Windows용 동적 심볼 다운로드, Windows 대상의 심볼/모듈 파싱만 지원합니다. 퍼징 지원은 곧 추가될 예정입니다.
이전에 커버리지, 퍼징, 빠른 재설정 등 거의 모든 기능을 구현해 본 적이 있습니다. 따라서 이 프로젝트는 퍼징 준비가 꽤 빨리 될 것으로 예상합니다. 물론 산만해지지 않는다면요 :D
1월 말까지 커버리지(완료!), 피드백, 모듈 목록(완료!), 프로세스 목록, 빠른 재설정, 심볼 지원(완료!)을 목표로 하고 있습니다. 그러면 매우 강력한 퍼저가 될 것입니다.
주요 지원 대상은 최신 Windows 10입니다. Windows 대상은 심볼 저장소에서 심볼을 다운로드할 수 있습니다. 이를 통해 Windows 대상에서 즉시 심볼릭 커버리지를 얻을 수 있습니다. 그러나 코드는 Linux 깨우침(enlightenment)도 쉽게 추가할 수 있도록 작성되었습니다.
깨우침 없이도 부팅되는 모든 OS는 여전히 퍼징이 가능하며 기본 커버리지를 수집할 수 있습니다.
OS 지원 문제를 보고하기 전에, 표준 사전 빌드 Bochs(하이퍼바이저 없음)로 부팅하여 하이퍼바이저/Bochs 변경 사항에 문제가 있는지 확인해 주세요. Bochs는 일반적으로 사용되지 않으며 Linux 부팅과 같은 일반적인 작업에서도 자주 중대한 버그가 발생할 수 있습니다. 특히 OS에 Spectre/Meltdown 완화를 위한 CPUID/MSR 사용의 급격한 내부 변경 사항이 적용되면서 더욱 그렇습니다.
GitHub의 이슈 페이지에서 이슈 목록을 확인하세요. 이미 몇 가지를 등록해 두었습니다. 이 중 일부는 퍼징 개발을 시작하기 전에 신속히 해결해야 합니다.
빌드하려면 몇 가지가 필요합니다:
Visual Studio 2017을 설치하고 업데이트되었는지 확인하세요. 여기서는 최첨단 API, 헤더 및 라이브러리를 사용하고 있습니다.
저는 cl.exe 버전: Microsoft (R) C/C++ Optimizing Compiler Version 19.16.27025.1 for x64을 사용했습니다.
SDK 버전: 10.0.17763.0
https://rustup.rs/ 를 통해 Rust를 설치하세요. 저는 rustc 1.32.0-nightly (b3af09205 2018-12-04)를 사용했습니다.
이 프로젝트는 64비트만 지원하므로 x86_64-pc-windows-msvc 툴체인을 설치했는지 확인하세요.
cargo가 PATH에 있는지 확인하세요. 기본적으로 되어 있어야 합니다.
https://www.python.org/ 에서 Python을 다운로드하고 python 명령을 호출할 수 있도록 PATH에 추가하세요.
64비트 Cygwin(https://www.cygwin.com/setup-x86_64.exe)을 C:\cygwin64에 설치하세요. Cygwin을 설치할 때 autoconf 및 make 패키지를 설치했는지 확인하세요.
"Windows 기능 켜기/끄기"로 가서 "Hyper-V" 및 "Windows 하이퍼바이저 플랫폼" 옆의 확인란을 선택하세요. 물론 컴퓨터가 Hyper-V를 지원해야 합니다.
이 설치 과정 가이드는 다음 환경에서 확인되었습니다:
Windows 10 빌드 17763 클린 설치
rustc 1.33.0-nightly (8e2063d02 2019-01-07)
Microsoft (R) C/C++ Optimizing Compiler Version 19.16.27025.1 for x64
Visual Studio Community 2017 버전 15.9.4
applepie 커밋 `f84c084feb487e2e7f31f9052a4ab0addd2c4cf9`
Python 3.7.2 x64
git 버전 2.20.1.windows.1



autoconf 패키지)make 패키지)git clone https://github.com/gamozolabs/applepie 로 applepie 체크아웃python build.py 실행
초기 빌드 프로세스는 약 2분 정도 소요될 수 있으며, 최신 기계에서는 20~30초일 가능성이 높습니다.
이 프로젝트의 루트 디렉토리에서 python build.py를 실행하기만 하면 됩니다. 환경의 적합성을 확인하고 모든 것이 "그냥 작동"해야 합니다.
python build.py clean을 실행하여 Bochs 및 Rust 바이너리를 정리합니다.
python build.py deepclean을 실행하여 모든 Bochs 및 Rust 바이너리를 완전히 제거하고 Bochs의 모든 구성을 제거합니다. Bochs를 어떤 방식으로든 재구성한 경우 이 명령을 사용하세요.
Bochs 설정 방법에 대한 설명서를 읽어보세요. sync=none, ips=1000000, 현재 단일 프로세서만 지원 등의 몇 가지 요구 사항이 있습니다. 이는 코드 자체에서 강제되어 실수로 문제를 일으키지 않도록 합니다.
포함된 bochservisor_test\bochsrc.bxrc 및 bochservisor_test_real\bochsrc.bxrc 설정을 예제로 사용하세요. bochservisor_test_real이 참고할 최신 설정일 가능성이 높습니다.
Windows 대상에는 모듈 목록 깨우침(enlightenment)이 있어 실행 중인 컨텍스트의 모든 모듈 목록을 볼 수 있습니다. 이를 통해 명령어 주소를 모듈 + 오프셋으로 변환할 수 있습니다. 이 모듈 + 오프셋은 ASLR 상태가 변경될 때 퍼즈 케이스 간 커버리지 정보를 유지하는 데 도움이 됩니다. 또한 IDA와 같은 도구에서 모듈을 색칠하여 어떤 코드가 실행되었는지 시각적으로 볼 수 있습니다.
Windows 대상의 경우 _NT_SYMBOL_PATH와 symchk를 사용하여 심볼 저장소에서 동적으로 심볼이 다운로드됩니다. 경로에 symchk가 없으면 자동으로 실패합니다. 심볼이 있으면 사람이 읽을 수 있는 멋진 커버리지 버전을 저장하여 볼 수 있습니다. 또한 프라이빗 심볼을 사용하면 커버리지를 소스:라인으로 변환하여 소스 코드를 색칠할 수 있습니다.
음, 실제로 테스트는 없지만 bochservisor_test라는 작은 OS가 있어 하이퍼바이저와 함께 모든 것이 부팅되는지 확인합니다.
그리고 bochservisor_test_real은 Windows/Linux와 같은 작업에 사용하는 설정입니다. 아마 가장 자주 업데이트될 것입니다.
이 코드베이스는 Bochs에 약간의 코드를 추가하여 CPU 컨텍스트, 게스트 물리 메모리에서 실제 백업 메모리로의 매핑, 장치 및 CPU 상태 스테핑 등에 대한 모듈식 액세스를 허용합니다.
주요 코드는 bochservisor Rust 프로젝트의 lib.rs에 있습니다.
Bochs의 주요 CPU 루프에서 LoadLibrary()를 호출하여 bochservisor DLL을 로드합니다. 이 DLL은 호출될 Rust CPU 루프인 하나의 루틴을 내보냅니다.
Bochs는 이 bochs_cpu_loop 루틴에 Bochs에서 정보를 가져오고 장치 및 CPU 상태를 스텝핑하기 위한 함수 포인터가 포함된 구조체를 전달합니다.
MMIO 또는 I/O가 발생하면 하이퍼바이저는 메모리 오류 또는 I/O 명령 오류로 종료됩니다. WHVP는 에뮬레이션 API를 제공하지만 실제로는 부족하고 충분하지 않습니다.
대신 이미 있는 Bochs를 사용하여 몇 가지 명령을 스텝핑합니다. 하이퍼바이저 CPU 상태를 Bochs와 동기화 상태로 유지함으로써 언제든지 하이퍼바이저와 에뮬레이션 간에 동적으로 전환할 수 있습니다(적어도 가능해야 합니다).
즉, 전체 하이퍼바이저 상태는 항상 Bochs와 동기화되므로 Bochs 스냅샷이 정상적으로 작동하며 하이퍼바이저 없이도 부팅될 수 있습니다(스냅샷 정보에 저장해야 할 일부 CPUID 상태 제외).
MMIO 또는 I/O가 발생할 때 하나만 에뮬레이션하는 대신 에뮬레이션 하에서 일정 수의 명령을 실행합니다. 하이퍼바이저 진입 및 종료의 API 비용과 유사한 MMIO 작업이 인접할 가능성 때문에 몇 가지 명령을 스텝핑합니다. 이를 통해 API 오버헤드를 줄이고 VMEXIT 빈도를 낮출 수 있습니다. 이는 조정 가능한 숫자이며 코드베이스에 있는 값은 그럴 만한 이유가 있을 것입니다.
인터럽트는 매우 흥미로운 방식으로 처리합니다. 하이퍼바이저에 전달되도록 인터럽트를 예약하는 대신 모든 인터럽트를 Bochs 에뮬레이션 자체에서 처리합니다. 물론 하이퍼바이저 내에서 발생하는 예외 등은 Bochs에서 처리되지 않습니다.
이렇게 하면 WHVP가 지원하지 않는 기능(예: SMM용 SMI)도 제공됩니다. Bochs의 BIOS는 기본적으로 SMM을 사용하며 SMI 지원 없이는 사용자 지정 BIOS를 구축해야 합니다. 저는 이 프로젝트의 첫 번째 버전에서 그렇게 했습니다... 추천하지 않습니다.
이 프로젝트는 퍼징을 위해 설계되었지만, 너무 새롭기 때문에(며칠밖에 안 됨) 아직 이러한 기능이 전혀 없습니다.
가장 먼저 추가될 몇 가지 기능:
Bochs 장치 관련 작업을 실시간으로 한 스레드에서 루프로 실행하고, 다른 스레드에서 하이퍼바이저를 실행할 수 있습니다. 비동기 이벤트는 IPC를 통해 전달되며 실행이 게스트에 있을 때 장치를 업데이트할 수 있습니다.
현재 모든 작업이 하나의 스레드에서 발생하므로 하이퍼바이저가 주기적으로 종료되어 장치를 스텝핑할 수 있는지 확인해야 합니다. 마치 자체 스케줄러를 작성한 것과 같습니다.
이렇게 하면 속도가 빨라질 수 있지만 복잡성이 증가하고 경합 문제가 발생할 가능성이 있습니다. 이것이 실제로 구현될지는 알 수 없습니다.
코드 커버리지를 수집하기 위해 어떤 방법을 사용할지 아직 확실하지 않지만, 적어도 몇 가지 옵션이 있을 것입니다. 정확한 방법에서 빠른 방법까지 다양합니다. 이러한 모든 커버리지 메커니즘은 시스템 수준이며 대상의 소스나 심볼이 필요하지 않습니다.
OS 구조를 파싱하여 프로세스 목록, 모듈 목록 등의 기본 정보를 얻습니다. 그런 다음 이를 사용하여 PDB를 쿼리하고 심볼 정보를 얻습니다.
의미 있는 방식으로 크래시를 보고합니다. 이상적으로는 미니덤프가 WinDbg에서 로드 및 처리될 수 있어 좋을 것입니다. DMP는 물리적 메모리와 프로세서 컨텍스트일 뿐이며 이미 가지고 있으므로 상당히 쉬울 수 있습니다.
역사적으로 성공적이었던 재미있는 버그 근본 원인 분석 기술이 있습니다. 이 기법을 여기로 가져올 계획입니다.
더티 페이지를 추적하고 수정된 것만 복원함으로써 VM을 매우 빠르게 재설정할 수 있어야 합니다. 이를 통해 시스템 대상의 모든 코어에서 최대 속도로 퍼징할 수 있습니다. falkervisor에서 이미 했던 것과 유사하므로 이미 구상 및 설계되었습니다. 여기로 포팅하기만 하면 됩니다.
MMIO 또는 I/O가 발생할 때 실행을 취소하는 매우 빠른 퍼징입니다. 이를 통해 모든 CPU 시간을 하이퍼바이저에서 보내고 에뮬레이션 시간이 필요 없습니다. 퍼즈 케이스 중 디스크 I/O와 같은 것을 지원하지 않는 단점이 있지만 좋은 방법입니다.
이 프로젝트의 핵심 개념 중 일부는 Bochs에 대한 최소한의 수정입니다. 이를 통해 이 저장소의 Bochs 부분을 최신 상태로 유지할 수 있습니다.
목표는 가능한 많은 코드를 Rust 및 DLL로 이동하여 시스템을 훨씬 더 모듈화하고 안전하게 만드는 것입니다. 이를 통해 하이퍼바이저 자체에서 어리석은 손상 버그가 발생하여 잘못된 퍼즈 결과가 발생할 가능성을 줄일 수 있기를 바랍니다.
현재 하이퍼바이저는 DLL이며 FFI API가 변경되지 않는 한 Bochs를 변경하지 않고도 교체할 수 있습니다.
Bochs 자체에 대한 추가 변경 사항은 명확하게 문서화되어야 하며, 곧 Bochs 업데이트 시 포팅 및 재평가해야 할 변경 사항을 추적하기 위한 문서를 만들 예정입니다.