BugChecker
소개
BugChecker는 Windows 11(및 Windows XP도 지원: XP부터 11까지, x86 및 x64 모두)을 위한 SoftICE 스타일의 커널 및 사용자 디버거입니다. BugChecker는 WinDbg나 KD처럼 디버깅 대상 시스템에 연결할 두 번째 머신이 필요하지 않습니다. 이 버전의 BugChecker(20년 전에 개발된 원본 버전과 달리)는 NTOSKRNL의 내부 문서화되지 않은 KD API를 활용합니다. KD API는 WinDbg/KD가 가상 메모리 읽기/쓰기, 레지스터 읽기/쓰기, 주소에 중단점 설정 등의 호출을 할 수 있게 해줍니다.
반면, 원래 BugChecker(SoftICE도 마찬가지)는 여러 커널 API(익스포트된 것과 프라이빗 모두)를 후킹하고, APIC를 제어하며, IPI를 전송하는 등 시스템을 "인수"했습니다. 이 접근 방식은 지원되는 모든 Windows 버전 및 하위 버전(함수 시그니처 수준)과 가능한 모든 하드웨어 구성에서 호환되어야 하므로 복잡성이 기하급수적으로 증가하고(시스템 안정성은 낮아집니다). 또한 20년 후에는 PatchGuard가 이 솔루션을 불가능하게 만듭니다.
반면에, 이 버전의 BugChecker는 커널에서 KdSendPacket 및 KdReceivePacket 호출을 가로채어 디버깅 대상 머신에 외부 커널 디버거를 실행하는 두 번째 시스템으로 스스로를 제시하지만, 실제로는 모든 것이 동일한 머신에서 발생합니다. 일반적으로 이는 KDCOM.DLL을 교체하고(Windows에서 KD API의 직렬 케이블 통신을 구현하는 모듈) 시스템을 커널 디버깅 모드로 시작하여 달성됩니다. 이 접근 방식(VirtualKD에서 영감을 받음)은 복잡성을 낮추고 안정성과 호환성(및 이식성, 예: ARM - 그리고 모듈성, 하위 수준 디버거 기능이 KdXxxPacket 뒤에 구현되어 사용자 정의 구현으로 대체될 수 있음)을 높입니다. 또한 부팅 시 커널 디버거가 존재하면(비록 "가짜"일지라도) Windows가 PatchGuard를 비활성화합니다.
현재 BugChecker는 입력에 PS/2 키보드가 필요하고 출력에 선형 프레임버퍼가 필요합니다. 최신 노트북의 내장 키보드는 여전히 PS/2인 경우가 많습니다.
기능
- Windows XP부터 Windows 11까지, x86 및 x64, SMP 커널을 지원합니다. x64에서 WOW64 프로세스를 지원합니다.
- QuickJS를 MSVC++로 포팅한 QuickJSPP가 통합되었습니다. QuickJS를 호출하기 전에 BugChecker는 FPU 상태를 저장하고(x86) 128KB 확장 스택으로 전환합니다.
- 명령어는 JS 표현식을 받습니다. 예를 들어, "U rip+rax*4" 및 "U MyJsFn(rax+2)"는 유효한 명령어입니다. 사용자 정의 함수는 스크립트 창에서 정의할 수 있습니다. CPU 레지스터는 BugChecker에 의해 자동으로 전역 범위 변수로 선언됩니다.
- PDB 심볼 파일을 지원합니다. PDB 파일을 수동으로 지정하거나 Symbol Loader가 심볼 서버에서 다운로드할 수 있습니다.
- JavaScript 코드는 다음 비동기 함수를 호출할 수 있습니다: WriteReg, ReadMem, WriteMem.
- 중단점에는 JS 조건을 설정할 수 있습니다: 조건이 0으로 평가되면 "breakin"이 발생하지 않습니다. 이를 통해 "Logpoints" 및 실행 흐름을 변경할 수 있는 중단점을 설정할 수 있습니다.
- 로그 창에는 커널 디버거로 전송된 메시지(예: DbgPrint 메시지)가 표시됩니다.
- 구문 강조 기능이 있는 JavaScript 창.
- 탭 키를 사용하면 몇 자리 숫자로 화면의 모든 16진수를 순환하거나 몇 문자로 해당 문자를 포함하는 모든 심볼을 순환할 수 있습니다.
- EASTL 및 C++20 코루틴으로 새 명령어를 쉽게 만들 수 있습니다. 풀 리퀘스트를 보내 주세요!
동영상 (Youtube)
VirtualBox 7.0.4 내 Windows 11 22H2에서 BugChecker 시연. 사용자 모드 스레드의 실행 흐름을 변경하는 JavaScript 중단점 조건이 작성됩니다.

매우 제한된 환경에서 실행되는 BugChecker: Raspberry Pi 4(4GB RAM), QEMU에서 Windows XP(512MB RAM)를 통해. SYSENTER 호출을 사용자 모드에서 커널로 모두 기록하는 데 중단점이 사용됩니다. 서비스 인덱스는 JavaScript 배열에 저장됩니다.

HP Pavilion Dv2000(PS/2 키보드가 있는 오래된 PC)의 베어 메탈에서 BugChecker 직접 실행. OS는 Windows 7 Home 32bit입니다.

설치 지침
소개
BugChecker를 설치하고 사용할 때 Secure Boot가 비활성화되어 있는지 확인하세요. 일반적으로 나중에 다시 활성화할 수 있습니다. VMware 또는 VirtualBox를 사용하는 경우 가상 머신 설정에서 Secure Boot를 비활성화할 수 있습니다.
Windows 8, 10 또는 11을 사용하는 경우 명령어 bcdedit /set "{current}" bootmenupolicy legacy를 사용하여 레거시 부팅 메뉴를 활성화하는 것도 고려하세요. 그러면 부팅 중 BugChecker 부팅 옵션을 선택하고 동시에 드라이버 서명 적용을 비활성화할 수 있어 더 원활한 환경을 제공합니다.
지침
첫 번째 단계는 Symbol Loader를 시작하는 것입니다:

필요한 경우 "Disable Display Drvs" 버튼을 클릭하여 디스플레이 드라이버를 비활성화하세요. Windows 장치 관리자에서도 동일한 작업을 수행할 수 있습니다. 디스플레이 드라이버가 비활성화되면 시스템 재부팅 후에도 비활성화 상태로 유지됩니다. BugChecker를 사용하지 않을 때 언제든지 다시 활성화할 수 있습니다.
BugChecker는 인터페이스를 그리기 위해 32비트-퍼-픽셀 형식의 선형 프레임버퍼가 필요합니다. 디스플레이 드라이버를 비활성화하면 Windows는 UI 그리기를 위한 하드웨어 가속을 해제하고 VGA 호환 모드로 대체됩니다. 베어 메탈 또는 VMware에서 실행하는 경우 디스플레이 드라이버를 비활성화해야 합니다. VirtualBox에서 실행하는 경우 디스플레이 드라이버를 비활성화하거나 아래 설명대로 BugChecker.dat에서 vm_screen 설정을 지정해야 합니다. QEMU에서 실행하는 경우 디스플레이 드라이버를 비활성화할 필요는 없지만 "-vga std" 디스플레이 장치를 지정해야 합니다.
VGA 호환 모드는 최대 화면 해상도를 제한할 수 있습니다. VMware는 최대 해상도 1152x864로 제한됩니다. "-vga std" 디스플레이 장치가 있는 QEMU는 이 제한이 없습니다.
흥미롭게도 BugChecker가 두 개 이상의 그래픽 카드가 있는 시스템에 설치된 경우, BugChecker UI를 표시할 화면에 연결된 카드 하나의 디스플레이 드라이버만 비활성화할 수 있습니다. 다른 카드(기본 디스플레이로 설정)는 OpenGL 및 DirectX 지원을 포함한 모든 2D 및 3D 가속 기능을 유지합니다(참고: VMware, Windows 11 및 DisplayLink 디스플레이에서 테스트 완료).
그런 다음 "Start Driver"를 클릭하고 "Auto Detect"를 클릭한 후 마지막으로 "Save"를 클릭합니다. "Auto Detect"는 프레임버퍼의 너비, 높이, 물리적 주소 및 스트라이드를 자동으로 결정할 수 있습니다. 그러나 이러한 설정을 수동으로 지정할 수도 있습니다(완료 후 "Save" 클릭하는 것을 잊지 마세요). "Stride"가 0이면 드라이버 시작 시 "Width" * 4로 자동 계산됩니다. "Address"(프레임버퍼의 물리적 주소)는 Windows 장치 관리자에서 디스플레이 장치의 "속성"을 클릭하고 "리소스" 탭에서 확인할 수 있습니다.
그런 다음 "KDCOM Hook Method" 섹션에서 "Callback"을 클릭하고 "Copy/Replace Kdcom"을 클릭한 후 시스템을 재부팅합니다.
이 설정 과정은 한 번만 수행하면 되며 필요한 경우 디스플레이 드라이버를 다시 활성화할 수 있습니다. 그러나 BugChecker를 사용할 때는 구성에 따라 디스플레이 드라이버를 다시 비활성화해야 합니다.
VirtualBox용 vm_screen 설정 (실험적)
BugChecker.dat의 vm_screen 설정을 사용하면 Symbol Loader에서 사전에 화면 해상도를 지정하거나 디스플레이 드라이버를 비활성화하지 않고 VirtualBox에서 BugChecker 디버거 UI를 열 수 있습니다.
아이디어는 가상 디스플레이 장치의 I/O 포트와 명령 버퍼에 직접 쓰여 현재 화면 해상도를 얻고 프레임버퍼의 업데이트를 하이퍼바이저에 알리는 것입니다.
이 솔루션은 X.org xf86-video-vmware 드라이버에서 영감을 받았습니다.
이 솔루션은 VirtualBox VM에서만 작동하며 BugChecker.dat 파일을 수동으로 편집해야 합니다:

- Symbol Loader에서 프레임버퍼의 너비와 높이를 가능한 최대 해상도(즉, 컴퓨터 화면의 크기)로 수동 설정합니다. 스트라이드는 0으로 설정합니다.
- BugChecker.dat 파일은 Symbol Loader에 의해 "C:\Windows\BugChecker"에 생성됩니다.
- vm_screen 설정은 "settings->framebuffer" 아래에 추가해야 합니다.
- 이 파일의 설정 계층 구조는 탭 문자(공백 아님)에 의해 결정됩니다.
- 설정 형식은 Command_Buffer_Start_Address (쉼표) Command_Buffer_End_Address (쉼표) I/O_Port_Base 입니다.
- 중요: VM 설정의 디스플레이 아래에서 그래픽 컨트롤러로 "VBoxSVGA"를 선택하고 "3D 가속 사용"을 해제하세요.
이 기능은 실험적입니다. 향후 이 설정은 Symbol Loader에 의해 자동으로 추가될 예정입니다.
구현된 명령어
명령어 이름과 구문은 원래 NT용 SoftICE의 것과 최대한 가깝게 선택되었습니다:
- ? javascript-expression: JavaScript 표현식을 평가합니다.
- ADDR eprocess: 프로세스 컨텍스트로 전환합니다(OS로 제어권 반환).
- BC list|*: 하나 이상의 중단점을 지웁니다.
- BD list|*: 하나 이상의 중단점을 비활성화합니다.
- BE list|*: 하나 이상의 중단점을 활성화합니다.
- BL (매개변수 없음): 모든 중단점을 나열합니다.
- BPX address [-t|-p|-kt thread|-kp process] [WHEN js-expression]: 실행 중단점을 설정합니다.
- CLS (매개변수 없음): 로그 창을 지웁니다.
- COLOR [normal bold reverse help line]|[reset]: 화면 색상을 표시, 설정 또는 재설정합니다.
- DB/DW/DD/DQ [address] [-l len-in-bytes]: 메모리를 8/16/32/64비트 값으로 표시합니다.
- EB/EW/ED/EQ address -v space-separated-values: 메모리를 8/16/32/64비트 값으로 편집합니다.
- KL EN|IT: 키보드 레이아웃을 설정합니다.
- LINES [rows-num]: 현재 표시 행 수를 표시하거나 설정합니다.
- MOD [-u|-s] [search-string]: 모듈 정보를 표시합니다.
- P [RET]: 한 프로그램 단계를 실행합니다.
- PAGEIN address: 메모리 페이지를 강제로 페이지 인합니다(OS로 제어권 반환).
- PROC [search-string]: 프로세스 정보를 표시합니다.
- R register-name -v value: 레지스터 값을 변경합니다.
- STACK [stack-ptr]: 반환 주소를 검색하기 위해 스택을 검색합니다.
- T (매개변수 없음): 한 명령어를 추적합니다.
- THREAD [-kt thread|-kp process]: 스레드 정보를 표시합니다.
- U address|DEST: 명령어를 디스어셈블합니다.
- VER (매개변수 없음): 버전 정보를 표시합니다.
- WD [window-size]: 디스어셈블러 창을 토글하거나 크기를 설정합니다.
- WIDTH [columns-num]: 현재 표시 열 수를 표시하거나 설정합니다.
- WR (매개변수 없음): 레지스터 창을 토글합니다.
- WS [window-size]: 스크립트 창을 토글하거나 크기를 설정합니다.
- X (매개변수 없음): BugChecker 화면에서 종료합니다.
빌드 지침
전제 조건
- Visual Studio 2019
- Windows Driver Kit 7.1.0
참고: WDK는 기본 위치(즉, BugChecker 소스가 저장된 드라이브의 X:\WinDDK)에 설치해야 합니다.
커널 드라이버 빌드에 대한 단계별 가이드는 여기에서 확인할 수 있습니다.
Visual Studio 프로젝트 설명
- BugChecker: 디버거의 전체가 구현된 BugChecker 커널 드라이버입니다. "Release|x86" 및 "Release|x64" 출력 파일이 최종 패키지에 포함됩니다. 초기화 중 드라이버는 "\SystemRoot\BugChecker\BugChecker.dat"에서 구성 파일을 로드하고(모든 심볼 파일도 이 디렉터리에 저장됨) 커널 공간에서 "KDCOM.dll"을 찾으려고 시도합니다. 찾으면 내보낸 함수 "KdSetBugCheckerCallbacks"를 호출하여 KdSendPacket 및 KdReceivePacket을 후킹합니다.
- SymLoader: Symbol Loader입니다. "Release|x86" 출력 파일만 최종 패키지에 포함됩니다. Symbol Loader는 BugChecker 구성을 변경하고("\SystemRoot\BugChecker\BugChecker.dat"에 기록), PDB 파일을 다운로드하며 사용자 지정 KDCOM.dll 모듈을 설치하는 데 사용됩니다.
- KDCOM: NTOSKRNL이 시스템 시작 시 로드하는 사용자 지정 KDCOM.dll 모듈입니다. 드라이버가 KdSendPacket 및 KdReceivePacket을 후킹하기 위해 호출하는 "KdSetBugCheckerCallbacks" 함수를 내보냅니다.
- pdb: Ghidra "pdb" 프로젝트입니다. 원래 버전은 PDB 파일의 내용을 xml 형식으로 표준 출력에 출력합니다. 코드가 수정되어 대신 BCS 파일을 생성합니다.
- NativeUtil: Symbol Loader는 Windows x64에서 WOW64 응용 프로그램이므로 아키텍처 네이티브 이미지에서 수행되어야 하는 API 호출(예: Device and Driver Installation API 호출)이 여기로 이동되었습니다.
- HttpToHttpsProxy: Windows XP에서 실행될 때 Symbol Loader의 인터넷 프록시 역할을 하는 ASP.NET Core 응용 프로그램입니다. XP는 TLS 지원이 구식이므로 Symbol Loader는 임의의 심볼 서버에서 파일을 다운로드할 수 없습니다. 이 응용 프로그램을 동일한 네트워크의 IIS에 배포한 후 Symbol Loader에서 서버 URL 앞에 "http://<YOUR_IIS_SERVER_IP>/HttpToHttpsProxy/"를 추가하여 Windows XP에서 심볼 서버에서 파일을 다운로드할 수 있습니다.
크레딧
- VirtualKD: BugChecker의 첫 번째 POC는 VirtualKD를 수정하여 구축되었습니다.
- BazisLib: Symbol Loader의 "Copy/Replace Kdcom + Add Boot Entry" 버튼 뒤의 코드는 VirtualKD에서 가져왔으며 BazisLib을 사용합니다.
- EASTL: 여기서 MSVC++ STL을 사용할 수 없습니다. EASTL은 훌륭한 대안입니다.
- Ghidra: BugChecker의 "pdb" 프로젝트는 Ghidra에서 가져왔습니다. BCS 파일을 생성하도록 수정되었습니다.
- Zydis: BugChecker의 디스어셈블러 창용입니다.
- QuickJSPP: QuickJS를 MSVC++로 포팅한 것으로, 커널 드라이버에 통합된 JavaScript 엔진용입니다.
- ReactOS: Windows KD 내부 유형 정의용입니다.
- SerenityOS: BugChecker 메모리 할당자가 사용하는 저수준 비트맵 조작 함수용입니다. (C/C++ 및 모든 유형의 저수준 프로그래밍에서 10년간 금욕한 후) Andreas의 동영상을 본 후 BugChecker를 시작했기 때문에 BugChecker에 SerenityOS의 작은 조각을 포함하고 싶었습니다.