Nakido's Blog
website·Indexed 31 days ago
https://nakido.meA technical blog featuring guides on Windows bootkit creation using the Windows Platform Binary Table (WPBT), patching WDK 22H2 for legacy project support, customizing Hexo, and automating synchronization from Overleaf to GitHub using GitHub Actions.
What's inside Nakido
- The Windows Platform Binary Table (WPBT) is an ACPI table used by OEMs to run a program every time Windows 8 or later boots. For a developer or security researcher, this provides a non-invasive method to execute custom code during the boot sequence without needing to hook RuntimeServices, patch the bootloader, or modify the MBR. The payload is executed by the Session Manager (SMSS) after the kernel has started but before the Win32 subsystem, CSRSS, or Winlogon have initialized.
Remove deprecation warnings for ExAllocatePool and ExAllocatePoolWithTag
In WDK 10.0.22621, functions like
ExAllocatePoolare marked as deprecated. To remove these warnings, manually edit the WDM header file.File path:
C:\Program Files (x86)\Windows Kits\10\Include\10.0.22621.0\km\wdm.hAction: Locate the definition of the function (e.g.,
ExAllocatePool) and delete the__declspec(deprecated(ExAllocatePool))attribute and the subsequent__drv_preferredFunction(...)call.Define and inject a Windows Platform Binary Table (WPBT) via UEFI
To achieve early code execution in Windows, a WPBT can be injected into the ACPI tables using a UEFI DXE module. This involves defining theWINDOWS_PLATFORM_BINARY_TABLEstructure, locating theEFI_ACPI_TABLE_PROTOCOL, allocating memory for the payload, and installing the table into the system.#include <Include/IndustryStandard/Acpi.h> #pragma pack(push, 1) typedef struct { EFI_ACPI_DESCRIPTION_HEADER Header; unsigned long HandoffMemSize; EFI_PHYSICAL_ADDRESS HandoffMemLoc; unsigned char ContentLayout; unsigned char ContentType; unsigned short ArgLength; } WINDOWS_PLATFORM_BINARY_TABLE; #pragma pack(pop) // 1. Define the WPBT instance WINDOWS_PLATFORM_BINARY_TABLE windowsPlatformBinaryTable = { { SIGNATURE_32('W', 'P', 'B', 'T'), sizeof(WINDOWS_PLATFORM_BINARY_TABLE), EFI_ACPI_5_0_FIRMWARE_PERFORMANCE_DATA_TABLE_REVISION, 0x00, // Checksum {'N', 'A', 'K', 'I', 'D'}, 0x01, 0x01, SIGNATURE_32('N', 'A', 'K', 'I'), 0x01, }, sizeof(hexData), (EFI_PHYSICAL_ADDRESS)NULL, 1, 1, 0 }; // 2. Locate the ACPI Table Protocol EFI_ACPI_TABLE_PROTOCOL *EfiAcpiTableProtocolIf; EFI_STATUS status = gBS->LocateProtocol(&gEfiAcpiTableProtocolGuid, NULL, &EfiAcpiTableProtocolIf); // 3. Allocate memory for payload and copy data EFI_PHYSICAL_ADDRESS wpbtBinAddress; status = gBS->AllocatePages(AllocateAnyPages, EfiACPIReclaimMemory, sizeof(hexData) / 4096 + 1, &wpbtBinAddress); if (!EFI_ERROR(status)) { gBS->CopyMem((void *)wpbtBinAddress, hexData, sizeof(hexData)); windowsPlatformBinaryTable.HandoffMemLoc = wpbtBinAddress; // Calculate checksum windowsPlatformBinaryTable.Header.Checksum = CalculateCheckSum8( (UINT8 *)&windowsPlatformBinaryTable, windowsPlatformBinaryTable.Header.Length ); } // 4. Install the table EfiAcpiTableProtocolIf->InstallAcpiTable( EfiAcpiTableProtocolIf, (void *)&windowsPlatformBinaryTable, windowsPlatformBinaryTable.Header.Length, &wpbtKey );Compile and test a WPBT-based bootkit
The deployment process requires a chain of signed binaries. Because Windows loaders block unsigned drivers and WPBT programs on 64-bit systems, you must either use valid code signatures or disable Driver Signature Enforcement at boot.
Compilation Workflow:
- Compile and sign the Payload Driver $\rightarrow$ convert to a
chararray $\rightarrow$ embed in the payload'shexData. - Compile and sign the payload $\rightarrow$ embed into the
AcpiWpbtInjectorDxemodule. - Use EDK2 to compile
AcpiWpbtInjectorDxeinto an EFI module or OVMF image.
Testing can be performed using QEMU by passing the
-pflashflag pointing to the compiled OVMF image.- Compile and sign the Payload Driver $\rightarrow$ convert to a
Generate ntdll.lib for Native Application linking
The Windows SDK does not providentdll.lib. To link against the NT Native API, you must generate a library file fromntdll.dllusing a tool likepexports(from MinGW) and the MSVClibtool.# Export symbols from ntdll.dll to a def file pexports ntdll.dll > ntdll.def # Create the .lib file from the .def file lib.exe /def:ntdll.def /out:ntdll.libDevelop a Windows Native Application for WPBT payload
A WPBT payload must be a 'Native Application' (Subsystem 1). Because it runs before the Win32 subsystem is initialized, it cannot use standard Win32 libraries (like kernel32.dll) or the UCRT/MSVCRT. It can only use the Windows NT Native API (syscalls).
Key constraints:
- No Win32 API access.
- No standard C/C++ runtime libraries.
- Debugging requires a WinDbg dual-machine kernel debugging environment or using
NtDisplayStringfor basic logging. - The entry point must be specified as
NtProcessStartup.
Enable 32-bit and Windows 7 to 8.1 build targets in WDK 22H2
Visual Studio 2022 and WDK 10.0.22621 (22H2) restrict several older build targets. These restrictions are implemented in the MSBuild scripts rather than the headers or libraries. To bypass these checks and support 32-bit or Windows 7/8.1 targets using the v14.3 toolset, patch the
WindowsDriver.Common.targetsfile.File path:
C:\Program Files (x86)\Windows Kits\10\build\10.0.22621.0\WindowsDriver.Common.targets(adjust if using a custom installation path).Action: Search for the string
'$(DDKPlatform)' == 'x86' or '$(DDKPlatform)' == 'ARM')and remove the conditional logic that validatesPlatformandTargetVersion.Customize Hexo page elements using non-invasive CSS
To customize Hexo theme elements without modifying the core theme files, add custom CSS to the theme configuration. Use CSS selectors to target specific elements and apply!importantto override existing styles of the same priority./* Hide specific sidebar elements */ #sidebar #sidebar-menus .sidebar-site-data { display: none !important; } /* Customize footer colors for light and dark themes */ [data-theme='light'] #footer { background-color: #72bdfc; } [data-theme='dark'] #footer { background-color: transparent; }Set up GitHub Action to sync Overleaf to GitHub
Create a workflow file in.github/workflows/to automate the retrieval of files from Overleaf. This workflow uses thesubhamx/overleaf_sync_with_gitaction to fetch the latest project version andpeter-evans/create-pull-requestto propose changes via a Pull Request if differences are detected. The workflow is configured for manual trigger viaworkflow_dispatchbut can be modified for scheduled runs.name: overleaf2git on: workflow_dispatch: permissions: contents: write pull-requests: write jobs: main: runs-on: ubuntu-latest steps: - name: Fetch the latest version from overleaf server uses: subhamx/overleaf_sync_with_git@main with: OVERLEAF_PROJECT_ID: ${{ secrets.OVERLEAF_PROJECT_ID }} OVERLEAF_COOKIE: ${{ secrets.OVERLEAF_COOKIE }} - name: Get GMT+8 time id: gmt8-time run: | GMT8_TIME=$(date -u -d '+8 hours' +'%Y-%m-%d %H:%M:%S') echo "time=$GMT8_TIME" >> $GITHUB_OUTPUT - uses: actions/checkout@v2 with: path: repo/ - name: Update workspace run: | cd repo/ cp -r ../artifacts/* ./ git config user.name "github-actions[bot]" git config user.email "github-actions[bot]@users.noreply.github.com" git add . if [[ $(git diff HEAD --stat) == '' ]]; then echo 'Working tree is clean' else git commit -m "sync from overleaf" fi - name: Create Pull Request uses: peter-evans/create-pull-request@v7 with: path: repo/ title: 'sync from overleaf at ${{ steps.gmt8-time.outputs.time }}'Implement a Windows Native Application entry point for bootkits
Native applications loaded by SMSS (Session Manager Subsystem) do not use the standard CRT entry point. They require a specific signature:void NtProcessStartup(PPEB Argument). ThePPEB(Process Environment Block) pointer is used to retrieve system versions, environment variables, and loader parameters. Because the loader does not create a heap for native processes, the heap must be initialized manually usingRtlCreateHeap.void NtProcessStartup(PPEB Argument) { PVOID heap; RTL_HEAP_PARAMETERS heapParams = {0}; heapParams.Length = sizeof(RTL_HEAP_PARAMETERS); heap = RtlCreateHeap(HEAP_GROWABLE, NULL, 0x100000, 0x1000, NULL, &heapParams); // ... payload logic ... }Synchronize Overleaf project changes to GitHub
The synchronization process is unidirectional (Overleaf $\rightarrow$ GitHub).
- Initial Setup: If this is the first sync or if you have made changes directly in Git, you must manually upload the files from the GitHub repository to Overleaf first to avoid overwriting work.
- Execution: Navigate to the Actions tab in your GitHub repository, select the workflow (e.g.,
overleaf2git), and click Run workflow. - Result: If changes are detected in Overleaf, the action will automatically create a Pull Request containing the latest updates.
Configure an EDK2 INF file for ACPI Table Injection
To inject an ACPI table (such as WPBT) before the OS bootstrap, a UEFI module should be developed in the DXE (Driver Execution Environment). The module must be defined as aDXE_RUNTIME_DRIVERto ensure the payload memory is preserved after the OS bootstrap. If integrating into the OVMF firmware, the module can be defined directly withinOvmfPkgusing an.infdeclaration file.[Defines] INF_VERSION = 0x00010005 BASE_NAME = AcpiWpbtInjectorDxe # Generate a unique GUID to avoid conflicts FILE_GUID = B709BBEF-0862-4170-81A5-D5B316AD88A3 # Use DXE_RUNTIME_DRIVER so memory is preserved after OS Bootstrap MODULE_TYPE = DXE_RUNTIME_DRIVER VERSION_STRING = 0.1 ENTRY_POINT = DxeMain UNLOAD_IMAGE = DxeUnload [Sources] main.c [Packages] MdePkg/MdePkg.dec MdeModulePkg/MdeModulePkg.dec [LibraryClasses] UefiLib UefiDriverEntryPoint UefiBootServicesTableLib UefiRuntimeServicesTableLib DebugLib BaseLib [Depex] gEfiSimpleTextOutProtocolGuid AND gEfiLoadedImageProtocolGuid AND gEfiVariableArchProtocolGuid AND gEfiVariableWriteArchProtocolGuid AND gEfiResetArchProtocolGuid AND gEfiBdsArchProtocolGuid AND gEfiRuntimeArchProtocolGuid [Protocols] gEfiAcpiTableProtocolGuid