diff --git a/sdk/tools/CMakeLists.txt b/sdk/tools/CMakeLists.txt index f7f4fd2cccebd..2728efbefc1f4 100644 --- a/sdk/tools/CMakeLists.txt +++ b/sdk/tools/CMakeLists.txt @@ -29,6 +29,7 @@ add_host_tool(utf16le utf16le/utf16le.cpp) add_subdirectory(asmpp) add_subdirectory(cabman) +add_subdirectory(chkreg) add_subdirectory(fatten) add_subdirectory(hhpcomp) add_subdirectory(hpp) diff --git a/sdk/tools/chkreg/CMakeLists.txt b/sdk/tools/chkreg/CMakeLists.txt new file mode 100644 index 0000000000000..ee3d609773f42 --- /dev/null +++ b/sdk/tools/chkreg/CMakeLists.txt @@ -0,0 +1,17 @@ + +list(APPEND SOURCE + chkreg.c + hivinit.c + hivchk.c + hivio.c + hivmem.c + rtl.c) + +add_host_tool(chkreg ${SOURCE}) +target_include_directories(chkreg PRIVATE ${REACTOS_SOURCE_DIR}/sdk/lib/rtl) +target_compile_definitions(chkreg PRIVATE CHKREG_HOST) +if(NOT MSVC) + target_compile_options(chkreg PRIVATE "-fshort-wchar") +endif() + +target_link_libraries(chkreg PRIVATE host_includes unicode cmlibhost) diff --git a/sdk/tools/chkreg/chkreg.c b/sdk/tools/chkreg/chkreg.c new file mode 100644 index 0000000000000..63cbb3750b53d --- /dev/null +++ b/sdk/tools/chkreg/chkreg.c @@ -0,0 +1,132 @@ +/* + * PROJECT: ReactOS Tools + * LICENSE: GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later) + * PURPOSE: Check Registry Utility + * COPYRIGHT: Copyright 2023 George Bișoc + */ + +/* INCLUDES *****************************************************************/ + +#include "chkreg.h" + +/* FUNCTIONS ****************************************************************/ + +static +void +ChkRegShowUsage( + void) +{ + printf("Usage: chkreg /h /a /r /v HIVE_FILE LOG_FILE\n\n" + " /a HIVE_FILE - Analyze a registry hive.\n" + " /r HIVE_FILE LOG_FILE - Recover a damaged hive with a log (currently it's not implemented yet).\n" + " /h - Show help.\n" + " /v - Verbose mode (currently it's not implemented yet).\n"); +} + +static +void +ChkRegBuildFilePath( + IN OUT char *Destination, + IN char *Source) +{ + INT i; + + i = 0; + while (Source[i] != 0) + { +#ifdef _WIN32 + if (Source[i] == '/') + { + Destination[i] = '\\'; + } +#else + if (Source[i] == '\\') + { + Destination[i] = '/'; + } +#endif + else + { + Destination[i] = Source[i]; + } + + i++; + } + + Destination[i] = 0; +} + +int +main(int argc, char *argv[]) +{ + BOOLEAN Success; + INT i; + BOOLEAN AnalyzeHive = FALSE; + CHAR HiveName[260] = ""; + + /* Display the usage description if the user only typed the program name */ + if (argc < 2) + { + ChkRegShowUsage(); + return -1; + } + + /* Parse the arguments from the command line */ + for (i = 1; i < argc && *argv[i] == '/'; i++) + { + /* Display the usage description */ + if (argv[i][1] == 'h' && argv[i][2] == 0) + { + ChkRegShowUsage(); + return -1; + } + + /* The user wants to analyze its registry hive */ + if (argv[i][1] == 'a') + { + /* Grab the hive name */ + AnalyzeHive = TRUE; + ChkRegBuildFilePath(HiveName, argv[i] + 3); + break; + } + /* The user wants to repair its hive with a log */ + else if (argv[i][1] == 'r') + { + printf("Repair mode with a hive log is not implemented yet!\n"); + return -1; + } + /* The user wants verbose mode whilst analyzing its hive */ + else if (argv[i][1] == 'v') + { + printf("Verbose mode is not implemented yet!\n"); + return -1; + } + else + { + fprintf(stderr, "Unrecognized command option: %s\n", argv[i]); + return -1; + } + } + + /* The user asked to analyze its hive, ensure he submitted a hive file name */ + if (AnalyzeHive) + { + if (!*HiveName) + { + fprintf(stderr, "The hive name is missing!\n"); + return -1; + } + + /* Analyze it now */ + Success = ChkRegAnalyzeHive(HiveName); + if (!Success) + { + fprintf(stderr, "Hive analization finished, the hive has damaged parts!\n"); + return -1; + } + } + + return 0; +} + +/* EOF */ diff --git a/sdk/tools/chkreg/chkreg.h b/sdk/tools/chkreg/chkreg.h new file mode 100644 index 0000000000000..956470ecc96e0 --- /dev/null +++ b/sdk/tools/chkreg/chkreg.h @@ -0,0 +1,102 @@ +/* + * PROJECT: ReactOS Tools + * LICENSE: GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later) + * PURPOSE: Check Registry Utility main header + * COPYRIGHT: Copyright 2023 George Bișoc + */ + +#pragma once + +/* INCLUDES *****************************************************************/ + +#include +#include +#include + +#include + +#define CMLIB_HOST +#include + +/* GLOBALS *******************************************************************/ + +extern BOOLEAN FixBrokenHive; + +/* DEFINES *******************************************************************/ + +// Definitions copied from +// We only want to include host headers, so we define them manually +#define STATUS_SUCCESS ((NTSTATUS)0x00000000) +#define STATUS_UNSUCCESSFUL ((NTSTATUS)0xC0000001) +#define STATUS_NO_MEMORY ((NTSTATUS)0xC0000017) +#define STATUS_REGISTRY_CORRUPT ((NTSTATUS)0xC000014C) + +/* FUNCTION PROTOTYPES *******************************************************/ + +/* hivchk.c */ +BOOLEAN +ChkRegAnalyzeHive( + IN PCSTR HiveName); + +/* hivio.c */ +BOOLEAN +NTAPI +CmpFileRead( + IN PHHIVE RegistryHive, + IN ULONG FileType, + IN PULONG FileOffset, + OUT PVOID Buffer, + IN SIZE_T BufferLength); + +BOOLEAN +NTAPI +CmpFileWrite( + IN PHHIVE RegistryHive, + IN ULONG FileType, + IN PULONG FileOffset, + IN PVOID Buffer, + IN SIZE_T BufferLength); + +BOOLEAN +NTAPI +CmpFileSetSize( + IN PHHIVE RegistryHive, + IN ULONG FileType, + IN ULONG FileSize, + IN ULONG OldFileSize); + +BOOLEAN +NTAPI +CmpFileFlush( + IN PHHIVE RegistryHive, + IN ULONG FileType, + PLARGE_INTEGER FileOffset, + ULONG Length); + +BOOLEAN +ChkRegOpenHive( + IN PCSTR HiveName, + IN BOOLEAN WriteToHive, + OUT PVOID *HiveData); + +/* hivmem.c */ +PVOID +NTAPI +CmpAllocate( + IN SIZE_T Size, + IN BOOLEAN Paged, + IN ULONG Tag); + +VOID +NTAPI +CmpFree( + IN PVOID Ptr, + IN ULONG Quota); + +/* hivinit.c */ +NTSTATUS +ChkRegInitializeHive( + IN OUT PHHIVE RegistryHive, + IN PVOID HiveData); + +/* EOF */ diff --git a/sdk/tools/chkreg/hivchk.c b/sdk/tools/chkreg/hivchk.c new file mode 100644 index 0000000000000..cddce92cd87fe --- /dev/null +++ b/sdk/tools/chkreg/hivchk.c @@ -0,0 +1,198 @@ +/* + * PROJECT: ReactOS Tools + * LICENSE: GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later) + * PURPOSE: Check Registry Utility hive check analysis code + * COPYRIGHT: Copyright 2023 George Bișoc + */ + +/* INCLUDES *****************************************************************/ + +#include "chkreg.h" + +/* GLOBALS ******************************************************************/ + +BOOLEAN FixBrokenHive = FALSE; + +/* DEFINES *****************************************************************/ + +#define CMHIVE_TAG 'iHmC' +#define GET_HHIVE(CmHive) (&((CmHive)->Hive)) + +/* FUNCTIONS ****************************************************************/ + +static +BOOLEAN +ChkRegAnalyzeHiveHeader( + IN PHBASE_BLOCK BaseBlock) +{ + ULONG CheckSum; + + /* A corrupt hive header block signature is fatal */ + if (BaseBlock->Signature != HV_HBLOCK_SIGNATURE) + { + printf("The registry hive header has a corrupt block signature and it could not be repaired!\n"); + return FALSE; + } + + /* We do not support any other hives other than what we actually support */ + if (BaseBlock->Major != HSYS_MAJOR || + BaseBlock->Minor < HSYS_MINOR) + { + printf("The registry hive header has unsupported versions (Major 0x%x - Minor 0x%x)!\n", BaseBlock->Major, BaseBlock->Minor); + return FALSE; + } + + /* This hive has to be a primary hive otherwise this is wrong */ + if (BaseBlock->Type != HFILE_TYPE_PRIMARY) + { + printf("The registry hive header has a different file type (Type 0x%x, Expected 0x%x)!\n", BaseBlock->Type, HFILE_TYPE_PRIMARY); + return FALSE; + } + + /* This hive must be within a memory format otherwise this is fatal */ + if (BaseBlock->Format != HBASE_FORMAT_MEMORY) + { + printf("The registry hive header has an invalid base format and it could not be repaired!\n"); + return FALSE; + } + + /* This hive must have a sane cluster size */ + if (BaseBlock->Cluster != 1) + { + printf("The registry hive header has an invalid cluster size (Cluster 0x%x)!\n", BaseBlock->Cluster); + return FALSE; + } + + /* Check the integrity of primary and secondary sequences, fix them if necessary */ + if (BaseBlock->Sequence1 != BaseBlock->Sequence2) + { + if (!FixBrokenHive) + { + printf("The registry hive header has mismatching sequences (Sequence1 0x%x - Sequence2 0x%x)!\n", BaseBlock->Sequence1, BaseBlock->Sequence2); + return FALSE; + } + + BaseBlock->Sequence2 = BaseBlock->Sequence1; + } + + /* Check the hive checksum, fix it if necessary */ + CheckSum = HvpHiveHeaderChecksum(BaseBlock); + if (BaseBlock->CheckSum != CheckSum) + { + if (!FixBrokenHive) + { + printf("The registry hive header has invalid checksum (BaseBlock->CheckSum 0x%x - CheckSum 0x%x)!\n", BaseBlock->CheckSum, CheckSum); + return FALSE; + } + + BaseBlock->CheckSum = CheckSum; + } + + return TRUE; +} + +BOOLEAN +ChkRegAnalyzeHive( + IN PCSTR HiveName) +{ + CM_CHECK_REGISTRY_STATUS CmStatusCode; + NTSTATUS Status; + BOOLEAN Success; + INT Confirm; + PVOID HiveData; + PCMHIVE CmHive; + + /* Ask the user whether he wants its hive fixed during analyzation */ + printf("\n" + "The ReactOS Check Registry Utility is about to analyze %s. By default any damaged registry data is left unfixed!\n" + "Do you want it fixed? [Y/N]\n\n", HiveName); + Confirm = getchar(); + if (Confirm == 'Y' || Confirm == 'y') + { + /* Cache the request */ + FixBrokenHive = TRUE; + } + + /* Open the hive */ + Success = ChkRegOpenHive(HiveName, + FixBrokenHive, + &HiveData); + if (!Success) + { + printf("Failed to open the registry hive!\n"); + return FALSE; + } + + /* Analyze the registry header block */ + printf("Analyzing the registry header...\n"); + Success = ChkRegAnalyzeHiveHeader((PHBASE_BLOCK)HiveData); + if (!Success) + { + printf("The registry hive header has corrupt data!\n"); + CmpFree(HiveData, 0); + return FALSE; + } + + /* Now initialize the hive so that we can continue with further analyzation */ + CmHive = CmpAllocate(sizeof(CMHIVE), + TRUE, + CMHIVE_TAG); + if (CmHive == NULL) + { + printf("Failed to allocate memory for CM hive!\n"); + return FALSE; + } + + /* Initialize it */ + Status = ChkRegInitializeHive(GET_HHIVE(CmHive), + HiveData); + if (!NT_SUCCESS(Status)) + { + printf("Failed to initialize hive (Status 0x%lx)!\n", Status); + HvFree(GET_HHIVE(CmHive)); + CmpFree(CmHive, 0); + CmpFree(HiveData, 0); + return FALSE; + } + + /* Analyze the registry hive bins and underlying cells */ + printf("Analyzing the registry hive bins and cells...\n"); + CmStatusCode = HvValidateHive(GET_HHIVE(CmHive)); + if (!CM_CHECK_REGISTRY_SUCCESS(CmStatusCode)) + { + printf("The registry hive has corrupt bins or cells (status code %lu)!\n", CmStatusCode); + HvFree(GET_HHIVE(CmHive)); + CmpFree(CmHive, 0); + CmpFree(HiveData, 0); + return FALSE; + } + + /* Analyze the rest of the hive */ + printf("Analyzing the registry keys...\n"); + CmStatusCode = CmCheckRegistry(CmHive, + (FixBrokenHive == TRUE) ? CM_CHECK_REGISTRY_PURGE_VOLATILES | CM_CHECK_REGISTRY_FIX_HIVE : CM_CHECK_REGISTRY_PURGE_VOLATILES); + if (!CM_CHECK_REGISTRY_SUCCESS(CmStatusCode)) + { + printf("The registry hive has corrupt data (status code %lu)!\n", CmStatusCode); + HvFree(GET_HHIVE(CmHive)); + CmpFree(CmHive, 0); + CmpFree(HiveData, 0); + return FALSE; + } + + if (FixBrokenHive) + { + printf("Hive analyzation done. Any potential damaged registry data that were found are fixed!\n"); + } + else + { + printf("Hive analyzation done.\n"); + } + + HvFree(GET_HHIVE(CmHive)); + CmpFree(CmHive, 0); + CmpFree(HiveData, 0); + return TRUE; +} + +/* EOF */ diff --git a/sdk/tools/chkreg/hivinit.c b/sdk/tools/chkreg/hivinit.c new file mode 100644 index 0000000000000..9ab5148d648b8 --- /dev/null +++ b/sdk/tools/chkreg/hivinit.c @@ -0,0 +1,42 @@ +/* + * PROJECT: ReactOS Tools + * LICENSE: GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later) + * PURPOSE: Check Registry Utility I/O hive initialization code + * COPYRIGHT: Copyright 2023 George Bișoc + */ + +/* INCLUDES *****************************************************************/ + +#include "chkreg.h" + +/* FUNCTIONS ****************************************************************/ + +NTSTATUS +ChkRegInitializeHive( + IN OUT PHHIVE RegistryHive, + IN PVOID HiveData) +{ + NTSTATUS Status; + + Status = HvInitialize(RegistryHive, + HINIT_MEMORY, + HIVE_NOLAZYFLUSH, + HFILE_TYPE_PRIMARY, + HiveData, + CmpAllocate, + CmpFree, + CmpFileSetSize, + CmpFileWrite, + CmpFileRead, + CmpFileFlush, + 1, + NULL); + if (!NT_SUCCESS(Status)) + { + return Status; + } + + return STATUS_SUCCESS; +} + +/* EOF */ diff --git a/sdk/tools/chkreg/hivio.c b/sdk/tools/chkreg/hivio.c new file mode 100644 index 0000000000000..bc0e643e825d3 --- /dev/null +++ b/sdk/tools/chkreg/hivio.c @@ -0,0 +1,130 @@ +/* + * PROJECT: ReactOS Tools + * LICENSE: GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later) + * PURPOSE: Check Registry Utility I/O hive operation routines + * COPYRIGHT: Copyright 2023 George Bișoc + */ + +/* INCLUDES *****************************************************************/ + +#include "chkreg.h" + +/* DEFINES *****************************************************************/ + +#define HIVE_DATA_TAG 'aDiH' + +/* FUNCTIONS ****************************************************************/ + +BOOLEAN +NTAPI +CmpFileRead( + IN PHHIVE RegistryHive, + IN ULONG FileType, + IN PULONG FileOffset, + OUT PVOID Buffer, + IN SIZE_T BufferLength) +{ + PCMHIVE CmHive = (PCMHIVE)RegistryHive; + FILE *File = CmHive->FileHandles[HFILE_TYPE_PRIMARY]; + if (fseek(File, *FileOffset, SEEK_SET) != 0) + { + return FALSE; + } + + return (fread(Buffer, 1, BufferLength, File) == BufferLength); +} + +BOOLEAN +NTAPI +CmpFileWrite( + IN PHHIVE RegistryHive, + IN ULONG FileType, + IN PULONG FileOffset, + IN PVOID Buffer, + IN SIZE_T BufferLength) +{ + PCMHIVE CmHive = (PCMHIVE)RegistryHive; + FILE *File = CmHive->FileHandles[HFILE_TYPE_PRIMARY]; + if (fseek(File, *FileOffset, SEEK_SET) != 0) + { + return FALSE; + } + + return (fwrite(Buffer, 1, BufferLength, File) == BufferLength); +} + +BOOLEAN +NTAPI +CmpFileSetSize( + IN PHHIVE RegistryHive, + IN ULONG FileType, + IN ULONG FileSize, + IN ULONG OldFileSize) +{ + return TRUE; +} + +BOOLEAN +NTAPI +CmpFileFlush( + IN PHHIVE RegistryHive, + IN ULONG FileType, + PLARGE_INTEGER FileOffset, + ULONG Length) +{ + PCMHIVE CmHive = (PCMHIVE)RegistryHive; + FILE *File = CmHive->FileHandles[HFILE_TYPE_PRIMARY]; + return (fflush(File) == 0); +} + +BOOLEAN +ChkRegOpenHive( + IN PCSTR HiveName, + IN BOOLEAN WriteToHive, + OUT PVOID *HiveData) +{ + FILE *HiveHandle; + INT FileSize; + PVOID Buffer; + + HiveHandle = fopen(HiveName, (WriteToHive == TRUE) ? "rb+" : "rb"); + if (HiveHandle == NULL) + { + return FALSE; + } + + if (fseek(HiveHandle, 0, SEEK_END) != 0) + { + fclose(HiveHandle); + return FALSE; + } + + FileSize = ftell(HiveHandle); + if (fseek(HiveHandle, 0, SEEK_SET) != 0) + { + fclose(HiveHandle); + return FALSE; + } + + Buffer = CmpAllocate(sizeof(CMHIVE) + FileSize, + TRUE, + HIVE_DATA_TAG); + if (Buffer == NULL) + { + fclose(HiveHandle); + return FALSE; + } + + if (fread(Buffer, 1, FileSize, HiveHandle) != FileSize) + { + CmpFree(Buffer, 0); + fclose(HiveHandle); + return FALSE; + } + + *HiveData = Buffer; + fclose(HiveHandle); + return TRUE; +} + +/* EOF */ diff --git a/sdk/tools/chkreg/hivmem.c b/sdk/tools/chkreg/hivmem.c new file mode 100644 index 0000000000000..aff78626c24a0 --- /dev/null +++ b/sdk/tools/chkreg/hivmem.c @@ -0,0 +1,44 @@ +/* + * PROJECT: ReactOS Tools + * LICENSE: GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later) + * PURPOSE: Check Registry Utility I/O hive memory operation routines + * COPYRIGHT: Copyright 2023 George Bișoc + */ + +/* INCLUDES *****************************************************************/ + +#include "chkreg.h" + +/* FUNCTIONS ****************************************************************/ + +PVOID +NTAPI +CmpAllocate( + IN SIZE_T Size, + IN BOOLEAN Paged, + IN ULONG Tag) +{ + PVOID Buffer; + + Buffer = (PVOID)malloc((size_t)Size); + if (Buffer == NULL) + { + return NULL; + } + + return Buffer; +} + +VOID +NTAPI +CmpFree( + IN PVOID Ptr, + IN ULONG Quota) +{ + if (Ptr != NULL) + { + free(Ptr); + } +} + +/* EOF */ diff --git a/sdk/tools/chkreg/rtl.c b/sdk/tools/chkreg/rtl.c new file mode 100644 index 0000000000000..955ca3c16dfec --- /dev/null +++ b/sdk/tools/chkreg/rtl.c @@ -0,0 +1,119 @@ +/* + * PROJECT: ReactOS Tools + * LICENSE: GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later) + * PURPOSE: Check Registry Utility I/O Run-Time routines + * COPYRIGHT: Copyright 2023 George Bișoc + */ + +/* INCLUDES *****************************************************************/ + +#include "chkreg.h" + +/* FUNCTIONS ****************************************************************/ + +VOID +NTAPI +RtlInitializeBitMap( + IN PRTL_BITMAP BitMapHeader, + IN PULONG BitMapBuffer, + IN ULONG SizeOfBitMap) +{ + BitMapHeader->SizeOfBitMap = SizeOfBitMap; + BitMapHeader->Buffer = BitMapBuffer; +} + +VOID +NTAPI +RtlSetAllBits( + IN PRTL_BITMAP BitMapHeader) +{ + return; +} + +VOID +NTAPI +RtlClearAllBits( + IN PRTL_BITMAP BitMapHeader) +{ + return; +} + +ULONG +NTAPI +RtlFindSetBits( + IN PRTL_BITMAP BitMapHeader, + IN ULONG NumberToFind, + IN ULONG HintIndex) +{ + return 0; +} + +VOID +NTAPI +RtlSetBits( + IN PRTL_BITMAP BitMapHeader, + IN ULONG StartingIndex, + IN ULONG NumberToSet) +{ + return; +} + +LONG +NTAPI +RtlCompareUnicodeString( + IN PCUNICODE_STRING String1, + IN PCUNICODE_STRING String2, + IN BOOLEAN CaseInSensitive) +{ + USHORT i; + WCHAR c1, c2; + + for (i = 0; i <= String1->Length / sizeof(WCHAR) && i <= String2->Length / sizeof(WCHAR); i++) + { + if (CaseInSensitive) + { + c1 = RtlUpcaseUnicodeChar(String1->Buffer[i]); + c2 = RtlUpcaseUnicodeChar(String2->Buffer[i]); + } + else + { + c1 = String1->Buffer[i]; + c2 = String2->Buffer[i]; + } + + if (c1 < c2) + return -1; + else if (c1 > c2) + return 1; + } + + return 0; +} + +WCHAR +NTAPI +RtlUpcaseUnicodeChar( + IN WCHAR Source) +{ + USHORT Offset; + + if (Source < 'a') + return Source; + + if (Source <= 'z') + return (Source - ('a' - 'A')); + + Offset = 0; + + return Source + (SHORT)Offset; +} + +VOID +NTAPI +KeQuerySystemTime( + OUT PLARGE_INTEGER CurrentTime) +{ + CurrentTime->QuadPart = 0; +} + +/* EOF */