
A DTrace on Windows Reimplementation
Steve's Tracer. Windows syscall 후크를 재구현한 DTrace입니다. 꼼수 없이 패치가드(PatchGuard)와 호환되는 SSDT 후크라고 생각하면 됩니다. DTrace 시스템 호출 API 자체가 이 추가 테이블을 지원하지 않으므로 SSSDT(win32k API)는 지원하지 않습니다. 모든 사용자 모드 SSDT syscall 외에도 Zw* 커널 API를 추적할 수 있습니다.
STrace용 새 플러그인을 개발 중이라면 이 README를 끝까지 읽어 주세요!
Windows의 DTrace는 여러 프로브 유형을 지원합니다. 여기에는 syscall, fbt, etw, profile 등이 포함됩니다. 이 재구현은 syscall 및 etw 프로브 유형만 재구현합니다. 다른 모든 프로브 유형은 이 프로젝트의 범위에서 완전히 제외되며 절대 지원되지 않습니다. 추가 프로브 유형을 추가하려면 프로젝트를 자유롭게 포크하세요. 범위가 syscall과 etw 프로브로만 한정된 이유는 다른 프로브 유형과 그들이 다루는 시스템의 복잡성 때문입니다.
이 재구현은 D 스크립팅 언어를 완전히 버립니다 (참고: 더 널리 알려진 현대 언어인 DLang이 아닙니다). 원래 DTrace 구현과 같은 커널 내 VM의 복잡성은 이 프로젝트에 적합하지 않습니다. 대신 이 구현은 DTrace Windows 커널 인터페이스에 연결하는 데 필요한 관련 C 콜백을 직접 노출합니다. 스크립트의 '핫 로딩'을 가능하게 하기 위해 원래 dtrace의 VM + 스크립팅 환경을 대체하는 DLL 기반 플러그인 시스템이 사용되었습니다. 이 플러그인 시스템은 보안 검사가 활성화되어 있지 않거나 외부 종속성이 없는 '일반' 사용자 모드 DLL을 받아 커널 주소 공간에 수동으로 매핑합니다. plugin dll에는 커널 syscall 콜백이 발생할 때 호출되는 익스포트가 있습니다. 커널 API는 DLL의 일반적인 임포트 테이블(IAT)을 통해 해석됩니다. 플러그인 DLL은 ntoskrnl.lib에 링크되며, 드라이버는 로드 시 이러한 API를 해석하여 플러그인 DLL 내에서 일반적인 방식으로 모든 시스템 API를 호출할 수 있게 합니다. 이 플러그인 시스템은 스크립트 인터프리터나 JIT가 아닌 네이티브 코드가 syscall ENTRY와 RETURN 사이에서 직접 실행되므로 성능이 우수합니다. 이 설계는 Microsoft가 제공하는 dtrace 구현에 비해 성능을 향상시킵니다.
후크 설정은 매우 간단합니다. API 이름으로 후크를 등록/등록 해제하는 루틴과 pre/post syscall 콜백이 제공됩니다. 콜백은 인자와 반환 값을 읽기 전용 값으로 접근할 수 있습니다. 반환 값은 리턴 프로브에서 스푸핑할 수 있고 인자는 엔트리 프로브에서 수정할 수 있지만, 원래 syscall을 일반적으로 대체하거나 '취소'할 수는 없습니다. 이 API는 관찰자 역할을 합니다. 그러나 (https://github.com/everdox/InfinityHook 및 https://github.com/everdox/InfinityHook/raw/master/resources/perf.png)에 문서화된 것과 유사한 설계상 실수(oversight)가 존재하며, 이를 통해 스택의 syscall 포인터를 교체할 수 있습니다. 이 실수를 이용하면 syscall을 완전히 대체하여 사용자가 제어하는 루틴에 대한 포인터로 바꿀 수 있습니다. 이벤트가 발생하고 후크 콜백이 실행되면 원하는 모든 작업을 수행할 수 있습니다. 콜백은 동기식입니다. 즉, 예를 들어 엔트리 콜백에서 while 루프에 머물면서 실행을 지연시키면 syscall 호출도 그 시간만큼 지연됩니다. 시스템 호출의 실행은 엔트리 콜백에서 반환된 직후, 리턴 콜백 진입 직전에 발생합니다. 이 시스템은 패치가드와 완전히 호환되지만, Microsoft가 불행히도 이러한 유형의 커널 확장을 NT 커널의 일부로 간주하여 루트 서명자가 Windows인지 검증하므로 DSE를 비활성화해야 합니다. 사용자 지정 커널 서명자를 활성화하는 것과 같은 트릭으로는 작동하지 않습니다. DSE는 커널 부팅 중에 정말로 꺼져 있어야 합니다.
ValidationFlags=IMGP_LATEST_MS_ROOT_REQUIRED | IMGP_WINDOWS_ROOT_REQUIRED | IMGP_MS_SIGNATURE_REQUIRED
Scenario=ImgSigningScenarioWindows
Rust 드라이버는 C++ 드라이버에서 사용하는 DLL 기반 플러그인 시스템을 버리고 대신 웹 어셈블리 인터프리터를 사용하여 Windows 커널에서 wasm 스크립트를 호스팅하려고 시도합니다. 이는 VM을 사용하는 원래 dtrace 구현에 더 가깝게 하기 위함이지만 DLang보다 더 나은 언어를 사용합니다. 이를 통해 샌드박싱 및 기타 좋은 이점이 제공됩니다. POC는 완성되어 작동하지만, 불행히도 WASMI로 WASM을 인터프리팅할 때의 성능 문제로 인해 사용할 수 없습니다. 이 대체 설계가 작동하려면 NT 커널과 호환되는 JIT 기반 wasm 엔진이 필요할 것입니다. 재미있는 참신함으로 포함된 것이며, 약간의 작업을 통해 다른 용도로 사용될 수도 있습니다.
이 프로젝트는 크게 C++ 및 Rust 구성 요소로 나뉩니다. C 드라이버는 기능이 완전하므로 우선적으로 사용해야 합니다. 높은 수준에서 프로젝트는 다음 구성 요소를 가집니다.
DDK와 함께 Visual Studio 설정
드라이버와 CLI를 빌드하고 파일을 스크립트와 같은 폴더로 이동한 다음 install 폴더의 powershell 스크립트를 관리자 권한으로 실행합니다:
./install_as_admin.ps1
재부팅한 다음 STrace 부팅 항목을 선택하고 이 화면에서 F8(엔터가 아님!)을 누릅니다:

그러면 이 화면이 나타나며, DSE를 비활성화한 부팅을 선택합니다:

부팅에 성공하면 CLI를 사용하여 플러그인 DLL을 로드/언로드하여 추적을 시작할 수 있습니다. 두 개의 예제 플러그인이 제공됩니다. 문제가 발생하면 아래 세부 사항을 자세히 읽어 보세요. 처음에는 재부팅을 한 번 더 해야 할 수 있으며, process hacker와 같은 도구를 사용하여 STrace 서비스를 부팅 시 자동 시작으로 수동 설정해야 할 수도 있습니다.
원래 DTrace 설치는 여기에 있습니다: https://techcommunity.microsoft.com/t5/windows-kernel-internals/dtrace-on-windows-20h1-updates/ba-p/1127929. 원래 DTrace는 Secure Boot 및 Virtualized Based Security가 구성되어 있어야 하지만, STrace의 경우에는 그렇지 않습니다. STrace는 이러한 기능을 요구하는 프로브 유형(FBT)을 구현하지 않기 때문입니다.
이 프로젝트는 설치 프로그램과 동일한 작업을 수행하지만 MSI 대신 간단한 powershell 스크립트를 사용합니다. 과정은 간단합니다. 먼저 apiset dll을 system32에 복사하여 커널 확장이 활성화되도록 합니다. 그런 다음 사용자 모드 통신을 위해 시스템 시작 시 드라이버 main이 실행되도록 드라이버 항목을 설치합니다. ApiSet dll은 디지털 서명되므로 요구 사항에 따라 원래 Microsoft apiset이 사용되었습니다. 이 파일은 바이너리 형식으로만 제공되며 확장 임포트가 구현 드라이버를 가리키는 것 외에는 로직이 없습니다: ext-ms-win-ntos-trace-l1-1-0 -> dtrace.sys. 이 메커니즘에 대한 자세한 내용은 https://www.geoffchappell.com/studies/windows/win32/apisetschema/index.htm (ApiSetSchemaExtensions)을 참조하세요.
STrace는 커널 초기화 중 매우 이른 시점에 로드되므로, 부팅 구성 데이터베이스(BCD)에서 Test Signing을 활성화하는 것만으로는 충분하지 않습니다. STrace가 성공적으로 로드되려면 드라이버 서명 강제(DSE)를 비활성화해야 하며, 이는 매 부팅마다 수동으로 수행해야 합니다. 이를 돕기 위해 설치 스크립트는 사용자가 선택할 수 있는 쉬운 부팅 메뉴 항목을 생성합니다. 재부팅 후에도 DSE를 영구적으로 비활성화할 수 있게 하는 BCD 플래그는 없습니다. 커널이 이를 명시적으로 금지합니다.
부팅 시 DSE를 비활성화하는 것을 잊으면 재시작 후 자동 복구(Automatic Repair) 메뉴를 만나게 됩니다. 다시 시도해도 해롭지 않습니다. 부팅이 계속 실패하면 STrace 드라이버를 drivers 폴더에서 수동으로 제거해야 합니다. 부팅 실패로 인해 Windows가 STrace 서비스의 자동 실행(autorun) 플래그를 비활성화할 수 있습니다*, 부팅 실패가 발생하면 언제든지 이 플래그를 자동 실행으로 다시 설정해야 할 수 있습니다.
자체 플러그인을 개발하려면 기존 플러그인 중 하나를 기본 프로젝트로 사용하는 것이 가장 좋습니다. Visual Studio 프로젝트에는 종속성이 없는 독립 실행형(free standing) 바이너리를 생성하기 위한 매우 구체적인 많은 설정이 지정되어 있습니다. 기본값이 아닌 설정이 너무 많아 나열할 수 없으므로 프로젝트 중 하나를 복사한 다음 코드를 수정하여 자체 로직을 추가하기만 하면 됩니다 (https://stackoverflow.com/questions/884255/visual-studio-copy-project). 유용한 플러그인을 만들었다면 PR을 제출해 주세요 ! 더 많은 플러그인이 만들어질수록 이 시스템은 모두에게 더 유용해집니다!