From fbb63b353498dfa2ee3d134879c1b6c06bacadea Mon Sep 17 00:00:00 2001 From: robert-yates Date: Sat, 11 Jan 2025 16:34:51 +0100 Subject: [PATCH] add optional apiset dump tool --- .github/workflows/build.yml | 2 +- README.md | 2 +- src/CMakeLists.txt | 1 + src/tools/CMakeLists.txt | 4 + src/tools/dump-apiset/CMakeLists.txt | 17 ++++ src/tools/dump-apiset/dump-apiset.cpp | 118 ++++++++++++++++++++++++++ src/{ => tools}/grab-registry.bat | 0 7 files changed, 142 insertions(+), 2 deletions(-) create mode 100644 src/tools/CMakeLists.txt create mode 100644 src/tools/dump-apiset/CMakeLists.txt create mode 100644 src/tools/dump-apiset/dump-apiset.cpp rename src/{ => tools}/grab-registry.bat (100%) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index c89248fd..22422081 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -36,7 +36,7 @@ jobs: uses: actions/checkout@v4 - name: Dump Registry - run: src/grab-registry.bat + run: src/tools/grab-registry.bat - name: Upload Artifacts uses: actions/upload-artifact@v4 diff --git a/README.md b/README.md index e2a94c99..8a8cd688 100644 --- a/README.md +++ b/README.md @@ -87,7 +87,7 @@ cmake --workflow --preset=release ## Dumping the Registry The emulator needs a registry dump to run, otherwise it will print `Bad hive file` errors. -You can create one by running the src/grab-registry.bat script as administrator. +You can create one by running the src/tools/grab-registry.bat script as administrator. This will create a `registry` folder that needs to be placed in the working directory of the emulator. ## Running Tests diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index ff79e18f..70bef540 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1,4 +1,5 @@ add_subdirectory(common) +add_subdirectory(tools) add_subdirectory(emulator) add_subdirectory(unicorn-emulator) add_subdirectory(windows-emulator) diff --git a/src/tools/CMakeLists.txt b/src/tools/CMakeLists.txt new file mode 100644 index 00000000..d6d59c3a --- /dev/null +++ b/src/tools/CMakeLists.txt @@ -0,0 +1,4 @@ +if(WIN32) + add_subdirectory(dump-apiset) +endif() + diff --git a/src/tools/dump-apiset/CMakeLists.txt b/src/tools/dump-apiset/CMakeLists.txt new file mode 100644 index 00000000..9a3bf180 --- /dev/null +++ b/src/tools/dump-apiset/CMakeLists.txt @@ -0,0 +1,17 @@ +file(GLOB_RECURSE SRC_FILES CONFIGURE_DEPENDS + *.cpp + *.hpp + *.rc +) + +list(SORT SRC_FILES) + +add_executable(dump-apiset ${SRC_FILES}) + +momo_assign_source_group(${SRC_FILES}) + +target_link_libraries(dump-apiset PRIVATE + emulator-common +) + +momo_strip_target(dump-apiset) diff --git a/src/tools/dump-apiset/dump-apiset.cpp b/src/tools/dump-apiset/dump-apiset.cpp new file mode 100644 index 00000000..e4f3f33e --- /dev/null +++ b/src/tools/dump-apiset/dump-apiset.cpp @@ -0,0 +1,118 @@ +#include +#include "platform/platform.hpp" +#include "utils/compression.hpp" +#include "utils/io.hpp" +#include +#include + +void print_apiset(PAPI_SET_NAMESPACE apiSetMap); +void create_header_file(const std::vector& data); + +__forceinline PVOID GetCurrentProcessPeb() +{ +#ifdef _WIN64 + return (PVOID)__readgsqword(0x60); +#else + return (PVOID)__readfsdword(0x30); +#endif +} + +int main() +{ + printf("Dump API-SET\n"); + printf("------------\n\n"); + + const auto peb = (PPEB64)GetCurrentProcessPeb(); + const auto apiSetMap = (PAPI_SET_NAMESPACE)(peb->ApiSetMap); + + printf("APISET: 0x%p\n", apiSetMap); + printf("Version: %d\n", apiSetMap->Version); + printf("Size: %08X\n", apiSetMap->Size); + printf("Flags: %08X\n", apiSetMap->Flags); + printf("Count: %d\n", apiSetMap->Count); + printf("EntryOffset: %08X\n", apiSetMap->EntryOffset); + printf("HashOffset: %08X\n", apiSetMap->HashOffset); + printf("HashFactor: %08X\n", apiSetMap->HashFactor); + // print_apiset(apiSetMap); + + // Compress the API-SET binary blob + const auto* dataPtr = reinterpret_cast(apiSetMap); + std::vector buffer(dataPtr, dataPtr + apiSetMap->Size); + auto compressed = utils::compression::zlib::compress(buffer); + if (compressed.empty()) + { + printf("Failed to compress API-SET\n"); + return 1; + } + + // Dump the API-SET binary blob to disk + utils::io::write_file("api-set.bin", compressed, false); + printf("\nWrote API-SET to api-set.bin\n"); + // create_header_file(compressed); + + return 0; +} + +void print_apiset(PAPI_SET_NAMESPACE apiSetMap) +{ + for (ULONG i = 0; i < apiSetMap->Count; i++) + { + auto entry = (PAPI_SET_NAMESPACE_ENTRY)((ULONG_PTR)apiSetMap + apiSetMap->EntryOffset + + i * sizeof(API_SET_NAMESPACE_ENTRY)); + // printf(" Flags: %08X\n", entry->Flags); + // printf(" NameOffset: %08X\n", entry->NameOffset); + // printf(" NameLength: %08X\n", entry->NameLength); + // printf(" HashedLength: %08X\n", entry->HashedLength); + // printf(" ValueOffset: %08X\n", entry->ValueOffset); + // printf(" ValueCount: %08X\n", entry->ValueCount); + + std::wstring name((wchar_t*)((ULONG_PTR)apiSetMap + entry->NameOffset), entry->NameLength / sizeof(wchar_t)); + printf("-----------\n[%05d]: Contract Name: %ls\n", i, name.data()); + + for (ULONG x = 0; x < entry->ValueCount; x++) + { + auto value = + (PAPI_SET_VALUE_ENTRY)((ULONG_PTR)apiSetMap + entry->ValueOffset + x * sizeof(API_SET_VALUE_ENTRY)); + // printf(" Value %d\n", x); + // printf(" Flags: %08X\n", value->Flags); + // printf(" NameOffset: %08X\n", value->NameOffset); + // printf(" NameLength: %08X\n", value->NameLength); + // printf(" ValueOffset: %08X\n", value->ValueOffset); + // printf(" ValueLength: %08X\n", value->ValueLength); + + std::wstring hostName((wchar_t*)((ULONG_PTR)apiSetMap + value->NameOffset), + value->NameLength / sizeof(wchar_t)); + std::wstring altName((wchar_t*)((ULONG_PTR)apiSetMap + value->ValueOffset), + value->ValueLength / sizeof(wchar_t)); + printf(" HostName: %ls - AltName: %ls\n", hostName.empty() ? L"" : hostName.data(), + altName.empty() ? L"" : altName.data()); + } + } +} + +// Internal +void create_header_file(const std::vector& data) +{ + FILE* output; + fopen_s(&output, "api-set.h", "w"); + if (!output) + { + printf("Failed to create output file\n"); + return; + } + + fprintf(output, "#pragma once\n\n"); + fprintf(output, "#include \n\n"); + fprintf(output, "const uint8_t api_set_blob[] = {\n"); + for (ULONG i = 0; i < data.size(); i++) + { + fprintf(output, "0x%02X, ", data[i]); + if (i % 16 == 15) + { + fprintf(output, "\n"); + } + } + + fprintf(output, "};\n"); + fclose(output); +} \ No newline at end of file diff --git a/src/grab-registry.bat b/src/tools/grab-registry.bat similarity index 100% rename from src/grab-registry.bat rename to src/tools/grab-registry.bat