mirror of
https://github.com/stormshield/HiveSwarming
synced 2026-06-08 17:37:30 +00:00
@@ -1,3 +1,5 @@
|
||||
src/.vs
|
||||
src/*.vcxproj.user
|
||||
src/*.aps
|
||||
src/intermediate
|
||||
src/output
|
||||
|
||||
+16
-13
@@ -1,13 +1,16 @@
|
||||
Version 1.0 - 2020-02-10
|
||||
* Initial release
|
||||
|
||||
Version 1.1 - 2020-02-11
|
||||
* Support newline (\n) characters in key names
|
||||
* Support newline (\n) characters in value names
|
||||
* Support newline (\n) characters in string value contents
|
||||
|
||||
Version 1.2 - 2022-02-28
|
||||
* Support registry symbolic links
|
||||
* Accept continuation lines with any amount of leading spaces
|
||||
for values stored as hexadecimal
|
||||
* Accept multiple newlines after preamble
|
||||
Version 1.3 - 2023-06-15
|
||||
* Narrow desired rights when loading hive
|
||||
|
||||
Version 1.2 - 2022-02-28
|
||||
* Support registry symbolic links
|
||||
* Accept continuation lines with any amount of leading spaces
|
||||
for values stored as hexadecimal
|
||||
* Accept multiple newlines after preamble
|
||||
|
||||
Version 1.1 - 2020-02-11
|
||||
* Support newline (\n) characters in key names
|
||||
* Support newline (\n) characters in value names
|
||||
* Support newline (\n) characters in string value contents
|
||||
|
||||
Version 1.0 - 2020-02-10
|
||||
* Initial release
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
Copyright 2022 Stormshield
|
||||
Copyright 2023 Stormshield
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
(C) 2022 Stormshield
|
||||
(C) 2023 Stormshield
|
||||
|
||||
HiveSwarming - Conversions between registry hive and registry export formats
|
||||
without a need for special privileges.
|
||||
|
||||
+74
-74
@@ -1,74 +1,74 @@
|
||||
// (C) Stormshield 2022
|
||||
// Licensed under the Apache license, version 2.0
|
||||
// See LICENSE.txt for details
|
||||
|
||||
#include <windows.h>
|
||||
#include "Constants.h"
|
||||
#include "CommonFunctions.h"
|
||||
#include <vector>
|
||||
#include <iostream>
|
||||
#include <iomanip>
|
||||
|
||||
void DeleteHiveLogFiles
|
||||
(
|
||||
_In_ const std::wstring &HiveFilePath
|
||||
)
|
||||
{
|
||||
for (auto& Ext : Constants::Hives::LogFileExtensions)
|
||||
{
|
||||
std::wstring LogFilePath = HiveFilePath + Ext;
|
||||
DWORD Attributes = GetFileAttributesW(LogFilePath.c_str());
|
||||
if (Attributes != INVALID_FILE_ATTRIBUTES)
|
||||
{
|
||||
Attributes &= ~FILE_ATTRIBUTE_HIDDEN;
|
||||
Attributes &= ~FILE_ATTRIBUTE_SYSTEM;
|
||||
SetFileAttributesW(LogFilePath.c_str(), Attributes);
|
||||
DeleteFileW(LogFilePath.c_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ReportError
|
||||
(
|
||||
_In_ const HRESULT ErrorCode,
|
||||
_In_ const std::wstring& Context
|
||||
)
|
||||
{
|
||||
LPWSTR MessageBuffer = NULL;
|
||||
DWORD FmtResult = FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM |
|
||||
FORMAT_MESSAGE_IGNORE_INSERTS | FORMAT_MESSAGE_MAX_WIDTH_MASK,
|
||||
NULL, ErrorCode, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
|
||||
(LPWSTR)&MessageBuffer, 0, NULL);
|
||||
|
||||
if (!Context.empty())
|
||||
{
|
||||
std::wcerr << Context << L":" << std::endl;
|
||||
}
|
||||
|
||||
if (FmtResult == 0)
|
||||
{
|
||||
std::wcerr << L"ERROR " << std::hex << std::setw(8) << ErrorCode << L" (could not format message)";
|
||||
}
|
||||
else
|
||||
{
|
||||
std::wcerr << L"ERROR " << std::hex << std::setw(8) << ErrorCode << L" (" << MessageBuffer << L")";
|
||||
LocalFree((PVOID)MessageBuffer);
|
||||
MessageBuffer = NULL;
|
||||
}
|
||||
|
||||
std::wcerr << std::endl << std::endl;
|
||||
}
|
||||
|
||||
VOID GlobalStringSubstitute(
|
||||
_Inout_ std::wstring& String,
|
||||
_In_ const std::wstring& Pattern,
|
||||
_In_ const std::wstring& Replacement
|
||||
)
|
||||
{
|
||||
size_t Position = String.find(Pattern);
|
||||
while (Position != String.npos)
|
||||
{
|
||||
String.replace(Position, Pattern.length(), Replacement);
|
||||
Position = String.find(Pattern, Position + Replacement.length());
|
||||
}
|
||||
}
|
||||
// (C) Stormshield 2023
|
||||
// Licensed under the Apache license, version 2.0
|
||||
// See LICENSE.txt for details
|
||||
|
||||
#include <windows.h>
|
||||
#include "Constants.h"
|
||||
#include "CommonFunctions.h"
|
||||
#include <vector>
|
||||
#include <iostream>
|
||||
#include <iomanip>
|
||||
|
||||
void DeleteHiveLogFiles
|
||||
(
|
||||
_In_ const std::wstring &HiveFilePath
|
||||
)
|
||||
{
|
||||
for (auto& Ext : Constants::Hives::LogFileExtensions)
|
||||
{
|
||||
std::wstring LogFilePath = HiveFilePath + Ext;
|
||||
DWORD Attributes = GetFileAttributesW(LogFilePath.c_str());
|
||||
if (Attributes != INVALID_FILE_ATTRIBUTES)
|
||||
{
|
||||
Attributes &= ~FILE_ATTRIBUTE_HIDDEN;
|
||||
Attributes &= ~FILE_ATTRIBUTE_SYSTEM;
|
||||
SetFileAttributesW(LogFilePath.c_str(), Attributes);
|
||||
DeleteFileW(LogFilePath.c_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ReportError
|
||||
(
|
||||
_In_ const HRESULT ErrorCode,
|
||||
_In_ const std::wstring& Context
|
||||
)
|
||||
{
|
||||
LPWSTR MessageBuffer = NULL;
|
||||
DWORD FmtResult = FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM |
|
||||
FORMAT_MESSAGE_IGNORE_INSERTS | FORMAT_MESSAGE_MAX_WIDTH_MASK,
|
||||
NULL, ErrorCode, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
|
||||
(LPWSTR)&MessageBuffer, 0, NULL);
|
||||
|
||||
if (!Context.empty())
|
||||
{
|
||||
std::wcerr << Context << L":" << std::endl;
|
||||
}
|
||||
|
||||
if (FmtResult == 0)
|
||||
{
|
||||
std::wcerr << L"ERROR " << std::hex << std::setw(8) << ErrorCode << L" (could not format message)";
|
||||
}
|
||||
else
|
||||
{
|
||||
std::wcerr << L"ERROR " << std::hex << std::setw(8) << ErrorCode << L" (" << MessageBuffer << L")";
|
||||
LocalFree((PVOID)MessageBuffer);
|
||||
MessageBuffer = NULL;
|
||||
}
|
||||
|
||||
std::wcerr << std::endl << std::endl;
|
||||
}
|
||||
|
||||
VOID GlobalStringSubstitute(
|
||||
_Inout_ std::wstring& String,
|
||||
_In_ const std::wstring& Pattern,
|
||||
_In_ const std::wstring& Replacement
|
||||
)
|
||||
{
|
||||
size_t Position = String.find(Pattern);
|
||||
while (Position != String.npos)
|
||||
{
|
||||
String.replace(Position, Pattern.length(), Replacement);
|
||||
Position = String.find(Pattern, Position + Replacement.length());
|
||||
}
|
||||
}
|
||||
|
||||
+32
-32
@@ -1,32 +1,32 @@
|
||||
// (C) Stormshield 2022
|
||||
// Licensed under the Apache license, version 2.0
|
||||
// See LICENSE.txt for details
|
||||
|
||||
#pragma once
|
||||
#include <string>
|
||||
|
||||
/// @brief Delete .LOG1 and .LOG2 system files that were created when loading an application hive
|
||||
/// @param[in] HiveFilePath Path to the hive file
|
||||
void DeleteHiveLogFiles
|
||||
(
|
||||
_In_ const std::wstring &HiveFilePath
|
||||
);
|
||||
|
||||
/// @brief Report an error
|
||||
/// @param[in] ErrorCode HRESULT value
|
||||
/// @param[in] Context Optional context
|
||||
void ReportError
|
||||
(
|
||||
_In_ const HRESULT ErrorCode,
|
||||
_In_ const std::wstring& Context = std::wstring{}
|
||||
);
|
||||
|
||||
/// @brief Perform global replacement of substring in a std::wstring
|
||||
/// @param[in,out] String String on which to perform substitutions
|
||||
/// @param[in] Pattern Substring that will be replaced by #Replacement in #String
|
||||
/// @param[in] Replacement What is inserted in place of #Substring in #String
|
||||
VOID GlobalStringSubstitute(
|
||||
_Inout_ std::wstring& String,
|
||||
_In_ const std::wstring& Pattern,
|
||||
_In_ const std::wstring& Replacement
|
||||
);
|
||||
// (C) Stormshield 2023
|
||||
// Licensed under the Apache license, version 2.0
|
||||
// See LICENSE.txt for details
|
||||
|
||||
#pragma once
|
||||
#include <string>
|
||||
|
||||
/// @brief Delete .LOG1 and .LOG2 system files that were created when loading an application hive
|
||||
/// @param[in] HiveFilePath Path to the hive file
|
||||
void DeleteHiveLogFiles
|
||||
(
|
||||
_In_ const std::wstring &HiveFilePath
|
||||
);
|
||||
|
||||
/// @brief Report an error
|
||||
/// @param[in] ErrorCode HRESULT value
|
||||
/// @param[in] Context Optional context
|
||||
void ReportError
|
||||
(
|
||||
_In_ const HRESULT ErrorCode,
|
||||
_In_ const std::wstring& Context = std::wstring{}
|
||||
);
|
||||
|
||||
/// @brief Perform global replacement of substring in a std::wstring
|
||||
/// @param[in,out] String String on which to perform substitutions
|
||||
/// @param[in] Pattern Substring that will be replaced by #Replacement in #String
|
||||
/// @param[in] Replacement What is inserted in place of #Substring in #String
|
||||
VOID GlobalStringSubstitute(
|
||||
_Inout_ std::wstring& String,
|
||||
_In_ const std::wstring& Pattern,
|
||||
_In_ const std::wstring& Replacement
|
||||
);
|
||||
|
||||
+93
-93
@@ -1,93 +1,93 @@
|
||||
// (C) Stormshield 2022
|
||||
// Licensed under the Apache license, version 2.0
|
||||
// See LICENSE.txt for details
|
||||
|
||||
#pragma once
|
||||
#include <windows.h>
|
||||
#include <vector>
|
||||
#include <string>
|
||||
|
||||
/// Program constants
|
||||
namespace Constants {
|
||||
|
||||
/// Command-line constants
|
||||
namespace Program {
|
||||
/// Switch for converting a hive to a .reg file
|
||||
static const std::wstring HiveToRegFileSwitch { L"--hive-to-reg-file" };
|
||||
|
||||
/// Switch for converting a .reg file to a hive
|
||||
static const std::wstring RegFileToHiveSwitch { L"--reg-file-to-hive" };
|
||||
};
|
||||
|
||||
/// Program defaults
|
||||
namespace Defaults {
|
||||
/// Default Root registry path in generated .reg files
|
||||
static const std::wstring ExportKeyPath { L"(HiveRoot)" };
|
||||
};
|
||||
|
||||
/// Hive-specific constants
|
||||
namespace Hives {
|
||||
/// Extensions of extra files that are generated when manipulating registry hives
|
||||
static const std::vector<std::wstring> LogFileExtensions{ L".LOG1", L".LOG2" };
|
||||
|
||||
/// Special value storing the destination of a symbolic link
|
||||
static const std::wstring SymbolicLinkValue { L"SymbolicLinkValue" };
|
||||
};
|
||||
|
||||
/// .reg file-specific constants
|
||||
namespace RegFiles {
|
||||
/// New lines used in .reg files
|
||||
static const std::wstring NewLines { L"\r\n" };
|
||||
|
||||
/// Preamble found in .reg files
|
||||
static const std::wstring Preamble { L"\ufeff" // Byte Order Mark
|
||||
L"Windows Registry Editor Version 5.00\r\n"
|
||||
L"\r\n" };
|
||||
|
||||
/// Character used at start of line beginning a registry key path
|
||||
static const WCHAR KeyOpening { L'[' };
|
||||
|
||||
/// Character delimiting parent key from its child in a registry path
|
||||
static const WCHAR PathSeparator { L'\\' };
|
||||
|
||||
/// Character used at end of line closing a registry key path
|
||||
static const WCHAR KeyClosing { L']' };
|
||||
|
||||
/// Character used for default registry values
|
||||
static const WCHAR DefaultValue { L'@' };
|
||||
|
||||
/// Character used for separating value declaration from its content
|
||||
static const WCHAR ValueNameSeparator { L'=' };
|
||||
|
||||
/// Sequence used for describing a DWORD rendition
|
||||
static const std::wstring DwordPrefix { L"dword:" };
|
||||
|
||||
/// Sequence used for describing a hexadecimal rendition
|
||||
static const std::wstring HexPrefix { L"hex" };
|
||||
|
||||
/// Character used when beginning the value type for a hexadecimal rendition
|
||||
static const WCHAR HexTypeSpecOpening { L'(' };
|
||||
|
||||
/// Character used when ending the value type for a hexadecimal rendition
|
||||
static const WCHAR HexTypeSpecClosing { L')' };
|
||||
|
||||
/// Character at the beginning of the hexadecimal rendition
|
||||
static const WCHAR HexSuffix { L':' };
|
||||
|
||||
/// Byte separator in hexadecimal renditions
|
||||
static const WCHAR HexByteSeparator { L',' };
|
||||
|
||||
/// Desired maximal line length when dumping hexadecimal rendition of registry values
|
||||
static const SIZE_T HexWrappingLimit = 80u;
|
||||
|
||||
/// Count of leading spaces when wrapping a hexadecimal rendition
|
||||
static const SIZE_T HexNewLineLeadingSpaces = 2u;
|
||||
|
||||
/// Space character at the beginning of a continuation line when wrapping hex values
|
||||
static const WCHAR LeadingSpace { L' '};
|
||||
|
||||
/// Sequence used when continuing a hexadecimal rendition on a new line
|
||||
static const std::wstring HexByteNewLine { L"\\\r\n" };
|
||||
};
|
||||
};
|
||||
|
||||
// (C) Stormshield 2023
|
||||
// Licensed under the Apache license, version 2.0
|
||||
// See LICENSE.txt for details
|
||||
|
||||
#pragma once
|
||||
#include <windows.h>
|
||||
#include <vector>
|
||||
#include <string>
|
||||
|
||||
/// Program constants
|
||||
namespace Constants {
|
||||
|
||||
/// Command-line constants
|
||||
namespace Program {
|
||||
/// Switch for converting a hive to a .reg file
|
||||
static const std::wstring HiveToRegFileSwitch { L"--hive-to-reg-file" };
|
||||
|
||||
/// Switch for converting a .reg file to a hive
|
||||
static const std::wstring RegFileToHiveSwitch { L"--reg-file-to-hive" };
|
||||
};
|
||||
|
||||
/// Program defaults
|
||||
namespace Defaults {
|
||||
/// Default Root registry path in generated .reg files
|
||||
static const std::wstring ExportKeyPath { L"(HiveRoot)" };
|
||||
};
|
||||
|
||||
/// Hive-specific constants
|
||||
namespace Hives {
|
||||
/// Extensions of extra files that are generated when manipulating registry hives
|
||||
static const std::vector<std::wstring> LogFileExtensions{ L".LOG1", L".LOG2" };
|
||||
|
||||
/// Special value storing the destination of a symbolic link
|
||||
static const std::wstring SymbolicLinkValue { L"SymbolicLinkValue" };
|
||||
};
|
||||
|
||||
/// .reg file-specific constants
|
||||
namespace RegFiles {
|
||||
/// New lines used in .reg files
|
||||
static const std::wstring NewLines { L"\r\n" };
|
||||
|
||||
/// Preamble found in .reg files
|
||||
static const std::wstring Preamble { L"\ufeff" // Byte Order Mark
|
||||
L"Windows Registry Editor Version 5.00\r\n"
|
||||
L"\r\n" };
|
||||
|
||||
/// Character used at start of line beginning a registry key path
|
||||
static const WCHAR KeyOpening { L'[' };
|
||||
|
||||
/// Character delimiting parent key from its child in a registry path
|
||||
static const WCHAR PathSeparator { L'\\' };
|
||||
|
||||
/// Character used at end of line closing a registry key path
|
||||
static const WCHAR KeyClosing { L']' };
|
||||
|
||||
/// Character used for default registry values
|
||||
static const WCHAR DefaultValue { L'@' };
|
||||
|
||||
/// Character used for separating value declaration from its content
|
||||
static const WCHAR ValueNameSeparator { L'=' };
|
||||
|
||||
/// Sequence used for describing a DWORD rendition
|
||||
static const std::wstring DwordPrefix { L"dword:" };
|
||||
|
||||
/// Sequence used for describing a hexadecimal rendition
|
||||
static const std::wstring HexPrefix { L"hex" };
|
||||
|
||||
/// Character used when beginning the value type for a hexadecimal rendition
|
||||
static const WCHAR HexTypeSpecOpening { L'(' };
|
||||
|
||||
/// Character used when ending the value type for a hexadecimal rendition
|
||||
static const WCHAR HexTypeSpecClosing { L')' };
|
||||
|
||||
/// Character at the beginning of the hexadecimal rendition
|
||||
static const WCHAR HexSuffix { L':' };
|
||||
|
||||
/// Byte separator in hexadecimal renditions
|
||||
static const WCHAR HexByteSeparator { L',' };
|
||||
|
||||
/// Desired maximal line length when dumping hexadecimal rendition of registry values
|
||||
static const SIZE_T HexWrappingLimit = 80u;
|
||||
|
||||
/// Count of leading spaces when wrapping a hexadecimal rendition
|
||||
static const SIZE_T HexNewLineLeadingSpaces = 2u;
|
||||
|
||||
/// Space character at the beginning of a continuation line when wrapping hex values
|
||||
static const WCHAR LeadingSpace { L' '};
|
||||
|
||||
/// Sequence used when continuing a hexadecimal rendition on a new line
|
||||
static const std::wstring HexByteNewLine { L"\\\r\n" };
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
+81
-81
@@ -1,82 +1,82 @@
|
||||
// (C) Stormshield 2022
|
||||
// Licensed under the Apache license, version 2.0
|
||||
// See LICENSE.txt for details
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Windows.h>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
/// Internal representation of a registry value.
|
||||
struct RegistryValue {
|
||||
/// Name of the registry value. May not contain null character
|
||||
std::wstring Name;
|
||||
|
||||
/// Type of the registry value. Usually a small number.
|
||||
DWORD Type;
|
||||
|
||||
/// Binary representation of the underlying data as a byte buffer.
|
||||
std::vector<BYTE> BinaryValue;
|
||||
};
|
||||
|
||||
/// Internal representation of a registry key.
|
||||
struct RegistryKey {
|
||||
/// Name of the registry key. May contain any character except backslash
|
||||
std::wstring Name;
|
||||
|
||||
/// Container of subkeys
|
||||
std::vector<RegistryKey> Subkeys;
|
||||
|
||||
/// Container of values
|
||||
std::vector<RegistryValue> Values;
|
||||
};
|
||||
|
||||
/// @brief Create an internal representation of a registry key from a registry hive (binary) file
|
||||
/// @param[in] HiveFilePath Path to the registry hive
|
||||
/// @param[in] RootName Path to the root key for export
|
||||
/// @param[out] RegKey Internal structure
|
||||
/// @return HRESULT semantics
|
||||
/// @note For the moment, system files that are created (.LOG1, .LOG2) are not cleaned up.
|
||||
_Must_inspect_result_
|
||||
HRESULT HiveToInternal
|
||||
(
|
||||
_In_ const std::wstring &HiveFilePath,
|
||||
_In_ const std::wstring &RootName,
|
||||
_Out_ RegistryKey& RegKey
|
||||
);
|
||||
|
||||
/// @brief Create a .reg file from the internal representation of a registry key
|
||||
/// @param[in] RegKey Representation of the registry key
|
||||
/// @param[in] OutputFilePath Path of the desired output file
|
||||
/// @return HRESULT semantics
|
||||
/// @note #OutputFilePath is overwritten if it already exists
|
||||
_Must_inspect_result_
|
||||
HRESULT InternalToRegfile
|
||||
(
|
||||
_In_ const RegistryKey& RegKey,
|
||||
_In_ const std::wstring &OutputFilePath
|
||||
);
|
||||
|
||||
/// @brief Create a hive file from the internal representation of a registry key
|
||||
/// @param[in] RegKey Representation of the registry key
|
||||
/// @param[in] OutputFilePath Path of the desired output file
|
||||
/// @return HRESULT semantics
|
||||
/// @note #OutputFilePath is overwritten if it already exists
|
||||
_Must_inspect_result_
|
||||
HRESULT InternalToHive
|
||||
(
|
||||
_In_ const RegistryKey& RegKey,
|
||||
_In_ const std::wstring &OutputFilePath
|
||||
);
|
||||
|
||||
/// @brief Create an internal representation of a registry key from a registry .reg (text) file
|
||||
/// @param[in] RegFilePath Path to the registry .reg file
|
||||
/// @param[out] RegKey Internal structure
|
||||
/// @return HRESULT semantics
|
||||
_Must_inspect_result_
|
||||
HRESULT RegfileToInternal
|
||||
(
|
||||
_In_ const std::wstring& RegFilePath,
|
||||
_Out_ RegistryKey& RegKey
|
||||
// (C) Stormshield 2023
|
||||
// Licensed under the Apache license, version 2.0
|
||||
// See LICENSE.txt for details
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Windows.h>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
/// Internal representation of a registry value.
|
||||
struct RegistryValue {
|
||||
/// Name of the registry value. May not contain null character
|
||||
std::wstring Name;
|
||||
|
||||
/// Type of the registry value. Usually a small number.
|
||||
DWORD Type;
|
||||
|
||||
/// Binary representation of the underlying data as a byte buffer.
|
||||
std::vector<BYTE> BinaryValue;
|
||||
};
|
||||
|
||||
/// Internal representation of a registry key.
|
||||
struct RegistryKey {
|
||||
/// Name of the registry key. May contain any character except backslash
|
||||
std::wstring Name;
|
||||
|
||||
/// Container of subkeys
|
||||
std::vector<RegistryKey> Subkeys;
|
||||
|
||||
/// Container of values
|
||||
std::vector<RegistryValue> Values;
|
||||
};
|
||||
|
||||
/// @brief Create an internal representation of a registry key from a registry hive (binary) file
|
||||
/// @param[in] HiveFilePath Path to the registry hive
|
||||
/// @param[in] RootName Path to the root key for export
|
||||
/// @param[out] RegKey Internal structure
|
||||
/// @return HRESULT semantics
|
||||
/// @note For the moment, system files that are created (.LOG1, .LOG2) are not cleaned up.
|
||||
_Must_inspect_result_
|
||||
HRESULT HiveToInternal
|
||||
(
|
||||
_In_ const std::wstring &HiveFilePath,
|
||||
_In_ const std::wstring &RootName,
|
||||
_Out_ RegistryKey& RegKey
|
||||
);
|
||||
|
||||
/// @brief Create a .reg file from the internal representation of a registry key
|
||||
/// @param[in] RegKey Representation of the registry key
|
||||
/// @param[in] OutputFilePath Path of the desired output file
|
||||
/// @return HRESULT semantics
|
||||
/// @note #OutputFilePath is overwritten if it already exists
|
||||
_Must_inspect_result_
|
||||
HRESULT InternalToRegfile
|
||||
(
|
||||
_In_ const RegistryKey& RegKey,
|
||||
_In_ const std::wstring &OutputFilePath
|
||||
);
|
||||
|
||||
/// @brief Create a hive file from the internal representation of a registry key
|
||||
/// @param[in] RegKey Representation of the registry key
|
||||
/// @param[in] OutputFilePath Path of the desired output file
|
||||
/// @return HRESULT semantics
|
||||
/// @note #OutputFilePath is overwritten if it already exists
|
||||
_Must_inspect_result_
|
||||
HRESULT InternalToHive
|
||||
(
|
||||
_In_ const RegistryKey& RegKey,
|
||||
_In_ const std::wstring &OutputFilePath
|
||||
);
|
||||
|
||||
/// @brief Create an internal representation of a registry key from a registry .reg (text) file
|
||||
/// @param[in] RegFilePath Path to the registry .reg file
|
||||
/// @param[out] RegKey Internal structure
|
||||
/// @return HRESULT semantics
|
||||
_Must_inspect_result_
|
||||
HRESULT RegfileToInternal
|
||||
(
|
||||
_In_ const std::wstring& RegFilePath,
|
||||
_Out_ RegistryKey& RegKey
|
||||
);
|
||||
+100
-100
@@ -1,101 +1,101 @@
|
||||
// (C) Stormshield 2022
|
||||
// Licensed under the Apache license, version 2.0
|
||||
// See LICENSE.txt for details
|
||||
|
||||
#include <windows.h>
|
||||
#include <iostream>
|
||||
#include "Conversions.h"
|
||||
#include "CommonFunctions.h"
|
||||
#include "Constants.h"
|
||||
|
||||
/// @brief Program entry point
|
||||
/// @param[in] Argc Command line token count, including program name
|
||||
/// @param[in] Argv Tokens of the command line (#Argc valid entries)
|
||||
/// @retval EXIT_SUCCESS Program execution successful
|
||||
/// @retval EXIT_FAILURE Program execution failed
|
||||
int wmain(
|
||||
INT CONST Argc,
|
||||
LPCWSTR CONST* CONST Argv
|
||||
)
|
||||
{
|
||||
HRESULT Result = E_FAIL;
|
||||
|
||||
RegistryKey InternalStruct;
|
||||
|
||||
auto Usage = [&]()
|
||||
{
|
||||
std::wcerr << L"Usage: " << std::endl <<
|
||||
L"\t" << Argv[0] << L" " << Constants::Program::HiveToRegFileSwitch << L" <HiveFile> <RegFile>" << std::endl <<
|
||||
L"\t" << Argv[0] << L" " << Constants::Program::RegFileToHiveSwitch << L" <RegFile> <HiveFile>" << std::endl <<
|
||||
std::endl;
|
||||
};
|
||||
|
||||
if (Argc <= 1)
|
||||
{
|
||||
Usage();
|
||||
Result = S_OK;
|
||||
goto Cleanup;
|
||||
}
|
||||
|
||||
if (Constants::Program::HiveToRegFileSwitch == Argv[1])
|
||||
{
|
||||
if (Argc != 4)
|
||||
{
|
||||
Usage();
|
||||
Result = E_INVALIDARG;
|
||||
goto Cleanup;
|
||||
}
|
||||
|
||||
const std::wstring HivePath { Argv[2] };
|
||||
const std::wstring RegPath { Argv[3] };
|
||||
|
||||
Result = HiveToInternal(HivePath, Constants::Defaults::ExportKeyPath, InternalStruct);
|
||||
if (FAILED(Result))
|
||||
{
|
||||
goto Cleanup;
|
||||
}
|
||||
|
||||
Result = InternalToRegfile(InternalStruct, RegPath);
|
||||
if (FAILED(Result))
|
||||
{
|
||||
goto Cleanup;
|
||||
}
|
||||
}
|
||||
else if (Constants::Program::RegFileToHiveSwitch == Argv[1])
|
||||
{
|
||||
if (Argc != 4)
|
||||
{
|
||||
Usage();
|
||||
Result = E_INVALIDARG;
|
||||
goto Cleanup;
|
||||
}
|
||||
|
||||
const std::wstring RegPath { Argv[2] };
|
||||
const std::wstring HivePath { Argv[3] };
|
||||
|
||||
Result = RegfileToInternal(RegPath, InternalStruct);
|
||||
if (FAILED(Result))
|
||||
{
|
||||
ReportError(Result, L"Serializing registry file" + RegPath);
|
||||
goto Cleanup;
|
||||
}
|
||||
|
||||
Result = InternalToHive(InternalStruct, HivePath);
|
||||
if (FAILED(Result))
|
||||
{
|
||||
ReportError(Result, L"Writing hive file " + HivePath);
|
||||
goto Cleanup;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Usage();
|
||||
Result = E_INVALIDARG;
|
||||
goto Cleanup;
|
||||
}
|
||||
|
||||
Result = S_OK;
|
||||
|
||||
Cleanup:
|
||||
return FAILED(Result) ? EXIT_FAILURE : EXIT_SUCCESS;
|
||||
// (C) Stormshield 2023
|
||||
// Licensed under the Apache license, version 2.0
|
||||
// See LICENSE.txt for details
|
||||
|
||||
#include <windows.h>
|
||||
#include <iostream>
|
||||
#include "Conversions.h"
|
||||
#include "CommonFunctions.h"
|
||||
#include "Constants.h"
|
||||
|
||||
/// @brief Program entry point
|
||||
/// @param[in] Argc Command line token count, including program name
|
||||
/// @param[in] Argv Tokens of the command line (#Argc valid entries)
|
||||
/// @retval EXIT_SUCCESS Program execution successful
|
||||
/// @retval EXIT_FAILURE Program execution failed
|
||||
int wmain(
|
||||
INT CONST Argc,
|
||||
LPCWSTR CONST* CONST Argv
|
||||
)
|
||||
{
|
||||
HRESULT Result = E_FAIL;
|
||||
|
||||
RegistryKey InternalStruct;
|
||||
|
||||
auto Usage = [&]()
|
||||
{
|
||||
std::wcerr << L"Usage: " << std::endl <<
|
||||
L"\t" << Argv[0] << L" " << Constants::Program::HiveToRegFileSwitch << L" <HiveFile> <RegFile>" << std::endl <<
|
||||
L"\t" << Argv[0] << L" " << Constants::Program::RegFileToHiveSwitch << L" <RegFile> <HiveFile>" << std::endl <<
|
||||
std::endl;
|
||||
};
|
||||
|
||||
if (Argc <= 1)
|
||||
{
|
||||
Usage();
|
||||
Result = S_OK;
|
||||
goto Cleanup;
|
||||
}
|
||||
|
||||
if (Constants::Program::HiveToRegFileSwitch == Argv[1])
|
||||
{
|
||||
if (Argc != 4)
|
||||
{
|
||||
Usage();
|
||||
Result = E_INVALIDARG;
|
||||
goto Cleanup;
|
||||
}
|
||||
|
||||
const std::wstring HivePath { Argv[2] };
|
||||
const std::wstring RegPath { Argv[3] };
|
||||
|
||||
Result = HiveToInternal(HivePath, Constants::Defaults::ExportKeyPath, InternalStruct);
|
||||
if (FAILED(Result))
|
||||
{
|
||||
goto Cleanup;
|
||||
}
|
||||
|
||||
Result = InternalToRegfile(InternalStruct, RegPath);
|
||||
if (FAILED(Result))
|
||||
{
|
||||
goto Cleanup;
|
||||
}
|
||||
}
|
||||
else if (Constants::Program::RegFileToHiveSwitch == Argv[1])
|
||||
{
|
||||
if (Argc != 4)
|
||||
{
|
||||
Usage();
|
||||
Result = E_INVALIDARG;
|
||||
goto Cleanup;
|
||||
}
|
||||
|
||||
const std::wstring RegPath { Argv[2] };
|
||||
const std::wstring HivePath { Argv[3] };
|
||||
|
||||
Result = RegfileToInternal(RegPath, InternalStruct);
|
||||
if (FAILED(Result))
|
||||
{
|
||||
ReportError(Result, L"Serializing registry file" + RegPath);
|
||||
goto Cleanup;
|
||||
}
|
||||
|
||||
Result = InternalToHive(InternalStruct, HivePath);
|
||||
if (FAILED(Result))
|
||||
{
|
||||
ReportError(Result, L"Writing hive file " + HivePath);
|
||||
goto Cleanup;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Usage();
|
||||
Result = E_INVALIDARG;
|
||||
goto Cleanup;
|
||||
}
|
||||
|
||||
Result = S_OK;
|
||||
|
||||
Cleanup:
|
||||
return FAILED(Result) ? EXIT_FAILURE : EXIT_SUCCESS;
|
||||
}
|
||||
+110
-110
@@ -1,110 +1,110 @@
|
||||
// Microsoft Visual C++ generated resource script.
|
||||
//
|
||||
#include "resource.h"
|
||||
|
||||
#define APSTUDIO_READONLY_SYMBOLS
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Generated from the TEXTINCLUDE 2 resource.
|
||||
//
|
||||
#include "winres.h"
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
#undef APSTUDIO_READONLY_SYMBOLS
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
// English (United States) resources
|
||||
|
||||
#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)
|
||||
LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
|
||||
#pragma code_page(1252)
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Version
|
||||
//
|
||||
|
||||
VS_VERSION_INFO VERSIONINFO
|
||||
FILEVERSION 1,2,0,0
|
||||
PRODUCTVERSION 1,2,0,0
|
||||
FILEFLAGSMASK 0x3fL
|
||||
#ifdef _DEBUG
|
||||
FILEFLAGS 0x1L
|
||||
#else
|
||||
FILEFLAGS 0x0L
|
||||
#endif
|
||||
FILEOS 0x40004L
|
||||
FILETYPE 0x1L
|
||||
FILESUBTYPE 0x0L
|
||||
BEGIN
|
||||
BLOCK "StringFileInfo"
|
||||
BEGIN
|
||||
BLOCK "040904b0"
|
||||
BEGIN
|
||||
VALUE "CompanyName", "Stormshield SAS"
|
||||
VALUE "FileDescription", "Conversions between Registry export format and Registry hive"
|
||||
VALUE "FileVersion", "1.2.0.0"
|
||||
VALUE "InternalName", "HiveSwarming.exe"
|
||||
VALUE "LegalCopyright", "Copyright (C) 2022 Stormshield SAS"
|
||||
VALUE "OriginalFilename", "HiveSwarming.exe"
|
||||
VALUE "ProductName", "HiveSwarming"
|
||||
VALUE "ProductVersion", "1.2.0.0"
|
||||
END
|
||||
END
|
||||
BLOCK "VarFileInfo"
|
||||
BEGIN
|
||||
VALUE "Translation", 0x409, 1200
|
||||
END
|
||||
END
|
||||
|
||||
#endif // English (United States) resources
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
// French (France) resources
|
||||
|
||||
#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_FRA)
|
||||
LANGUAGE LANG_FRENCH, SUBLANG_FRENCH
|
||||
#pragma code_page(1252)
|
||||
|
||||
#ifdef APSTUDIO_INVOKED
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// TEXTINCLUDE
|
||||
//
|
||||
|
||||
1 TEXTINCLUDE
|
||||
BEGIN
|
||||
"resource.h\0"
|
||||
END
|
||||
|
||||
2 TEXTINCLUDE
|
||||
BEGIN
|
||||
"#include ""winres.h""\r\n"
|
||||
"\0"
|
||||
END
|
||||
|
||||
3 TEXTINCLUDE
|
||||
BEGIN
|
||||
"\r\n"
|
||||
"\0"
|
||||
END
|
||||
|
||||
#endif // APSTUDIO_INVOKED
|
||||
|
||||
#endif // French (France) resources
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
|
||||
#ifndef APSTUDIO_INVOKED
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Generated from the TEXTINCLUDE 3 resource.
|
||||
//
|
||||
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
#endif // not APSTUDIO_INVOKED
|
||||
|
||||
// Microsoft Visual C++ generated resource script.
|
||||
//
|
||||
#include "resource.h"
|
||||
|
||||
#define APSTUDIO_READONLY_SYMBOLS
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Generated from the TEXTINCLUDE 2 resource.
|
||||
//
|
||||
#include "winres.h"
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
#undef APSTUDIO_READONLY_SYMBOLS
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
// English (United States) resources
|
||||
|
||||
#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)
|
||||
LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
|
||||
#pragma code_page(1252)
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Version
|
||||
//
|
||||
|
||||
VS_VERSION_INFO VERSIONINFO
|
||||
FILEVERSION 1,3,0,0
|
||||
PRODUCTVERSION 1,3,0,0
|
||||
FILEFLAGSMASK 0x3fL
|
||||
#ifdef _DEBUG
|
||||
FILEFLAGS 0x1L
|
||||
#else
|
||||
FILEFLAGS 0x0L
|
||||
#endif
|
||||
FILEOS 0x40004L
|
||||
FILETYPE 0x1L
|
||||
FILESUBTYPE 0x0L
|
||||
BEGIN
|
||||
BLOCK "StringFileInfo"
|
||||
BEGIN
|
||||
BLOCK "040904b0"
|
||||
BEGIN
|
||||
VALUE "CompanyName", "Stormshield SAS"
|
||||
VALUE "FileDescription", "Conversions between Registry export format and Registry hive"
|
||||
VALUE "FileVersion", "1.3.0.0"
|
||||
VALUE "InternalName", "HiveSwarming.exe"
|
||||
VALUE "LegalCopyright", "Copyright (C) 2022 Stormshield SAS"
|
||||
VALUE "OriginalFilename", "HiveSwarming.exe"
|
||||
VALUE "ProductName", "HiveSwarming"
|
||||
VALUE "ProductVersion", "1.3.0.0"
|
||||
END
|
||||
END
|
||||
BLOCK "VarFileInfo"
|
||||
BEGIN
|
||||
VALUE "Translation", 0x409, 1200
|
||||
END
|
||||
END
|
||||
|
||||
#endif // English (United States) resources
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
// French (France) resources
|
||||
|
||||
#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_FRA)
|
||||
LANGUAGE LANG_FRENCH, SUBLANG_FRENCH
|
||||
#pragma code_page(1252)
|
||||
|
||||
#ifdef APSTUDIO_INVOKED
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// TEXTINCLUDE
|
||||
//
|
||||
|
||||
1 TEXTINCLUDE
|
||||
BEGIN
|
||||
"resource.h\0"
|
||||
END
|
||||
|
||||
2 TEXTINCLUDE
|
||||
BEGIN
|
||||
"#include ""winres.h""\r\n"
|
||||
"\0"
|
||||
END
|
||||
|
||||
3 TEXTINCLUDE
|
||||
BEGIN
|
||||
"\r\n"
|
||||
"\0"
|
||||
END
|
||||
|
||||
#endif // APSTUDIO_INVOKED
|
||||
|
||||
#endif // French (France) resources
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
|
||||
#ifndef APSTUDIO_INVOKED
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Generated from the TEXTINCLUDE 3 resource.
|
||||
//
|
||||
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
#endif // not APSTUDIO_INVOKED
|
||||
|
||||
|
||||
+188
-188
@@ -1,189 +1,189 @@
|
||||
// (C) Stormshield 2022
|
||||
// Licensed under the Apache license, version 2.0
|
||||
// See LICENSE.txt for details
|
||||
|
||||
#include "Conversions.h"
|
||||
#include "CommonFunctions.h"
|
||||
#include <sstream>
|
||||
|
||||
/// @brief Create an internal representation of a registry key from the handle to a registry key
|
||||
/// @param[in] Hkey the registry key
|
||||
/// @param[in] KeyName Name of the key #Hkey is a handle to
|
||||
/// @param[out] RegKey Representation of the key
|
||||
/// @return HRESULT semantics
|
||||
_Must_inspect_result_
|
||||
static HRESULT HkeyToInternal
|
||||
(
|
||||
_In_ const HKEY Hkey,
|
||||
_In_ const std::wstring& KeyName,
|
||||
_Out_ RegistryKey& RegKey
|
||||
)
|
||||
{
|
||||
HRESULT Result = E_FAIL;
|
||||
DWORD SubkeyCount = 0;
|
||||
DWORD MaximalSubKeyLength = 0;
|
||||
DWORD ValueCount = 0;
|
||||
DWORD MaximalValueNameLength = 0;
|
||||
DWORD MaximalValueLength = 0;
|
||||
LPWSTR SubKeyNameBuffer = NULL;
|
||||
LPWSTR ValueNameBuffer = NULL;
|
||||
PBYTE ValueBuffer = NULL;
|
||||
|
||||
if (Hkey == NULL)
|
||||
{
|
||||
ReportError(E_HANDLE, L"Null HKEY");
|
||||
return E_HANDLE;
|
||||
}
|
||||
|
||||
Result = HRESULT_FROM_WIN32(RegQueryInfoKeyW(Hkey, NULL, 0, NULL, &SubkeyCount, &MaximalSubKeyLength,
|
||||
NULL, &ValueCount, &MaximalValueNameLength, &MaximalValueLength, NULL, NULL));
|
||||
if (FAILED(Result))
|
||||
{
|
||||
ReportError(Result, L"Getting information on HKEY - Current key name: " + KeyName);
|
||||
goto Cleanup;
|
||||
}
|
||||
|
||||
SubKeyNameBuffer = (LPWSTR)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, (1 + MaximalSubKeyLength) * sizeof(WCHAR));
|
||||
if (SubKeyNameBuffer == NULL)
|
||||
{
|
||||
Result = HRESULT_FROM_WIN32(GetLastError());
|
||||
ReportError(Result, L"Allocating space for subkey names - Current key name: " + KeyName);
|
||||
goto Cleanup;
|
||||
}
|
||||
|
||||
ValueNameBuffer = (LPWSTR)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, (1 + MaximalValueNameLength) * sizeof(WCHAR));
|
||||
if (ValueNameBuffer == NULL)
|
||||
{
|
||||
Result = HRESULT_FROM_WIN32(GetLastError());
|
||||
ReportError(Result, L"Allocating space for value names - Current key name: " + KeyName);
|
||||
goto Cleanup;
|
||||
}
|
||||
|
||||
ValueBuffer = (PBYTE)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, MaximalValueLength);
|
||||
if (ValueBuffer == NULL)
|
||||
{
|
||||
Result = HRESULT_FROM_WIN32(GetLastError());
|
||||
ReportError(Result, L"Allocating space for values - Current key name: " + KeyName);
|
||||
goto Cleanup;
|
||||
}
|
||||
|
||||
RegKey.Name = KeyName;
|
||||
|
||||
for (DWORD ValueIndex = 0; ValueIndex < ValueCount; ++ValueIndex)
|
||||
{
|
||||
DWORD ValueNameLength = MaximalValueNameLength + 1;
|
||||
DWORD ValueType = 0;
|
||||
DWORD DataLength = MaximalValueLength + 1;
|
||||
Result = HRESULT_FROM_WIN32(RegEnumValueW(Hkey, ValueIndex, ValueNameBuffer, &ValueNameLength,
|
||||
NULL, &ValueType, ValueBuffer, &DataLength));
|
||||
if (FAILED(Result))
|
||||
{
|
||||
std::wostringstream ErrorMessageStream;
|
||||
ErrorMessageStream << L"Getting value at index " << ValueIndex << L" - Current key name: " << KeyName;
|
||||
ReportError(Result, ErrorMessageStream.str());
|
||||
goto Cleanup;
|
||||
}
|
||||
|
||||
RegistryValue NewValue;
|
||||
NewValue.Name.assign(ValueNameBuffer, ValueNameLength);
|
||||
NewValue.Type = ValueType;
|
||||
NewValue.BinaryValue.assign(ValueBuffer, ValueBuffer + DataLength);
|
||||
|
||||
RegKey.Values.emplace_back(std::move(NewValue));
|
||||
}
|
||||
|
||||
for (DWORD SubkeyIndex = 0; SubkeyIndex < SubkeyCount; ++SubkeyIndex)
|
||||
{
|
||||
HKEY HSubkey = NULL;
|
||||
DWORD SubkeyNameLength = MaximalSubKeyLength + 1;
|
||||
|
||||
Result = HRESULT_FROM_WIN32(RegEnumKeyExW(Hkey, SubkeyIndex, SubKeyNameBuffer, &SubkeyNameLength, NULL, NULL, NULL, NULL));
|
||||
if (FAILED(Result))
|
||||
{
|
||||
std::wostringstream ErrorMessageStream;
|
||||
ErrorMessageStream << L"Getting subkey name at index " << SubkeyIndex << L" - Current key name: " << KeyName;
|
||||
ReportError(Result, ErrorMessageStream.str());
|
||||
goto Cleanup;
|
||||
}
|
||||
|
||||
Result = HRESULT_FROM_WIN32(RegOpenKeyExW(Hkey, SubKeyNameBuffer, REG_OPTION_OPEN_LINK, KEY_READ, &HSubkey));
|
||||
if (FAILED(Result)) {
|
||||
Result = HRESULT_FROM_WIN32(RegOpenKeyExW(Hkey, SubKeyNameBuffer, 0, KEY_READ, &HSubkey));
|
||||
}
|
||||
if (FAILED(Result))
|
||||
{
|
||||
std::wostringstream ErrorMessageStream;
|
||||
ErrorMessageStream << L"Opening subkey named " << SubKeyNameBuffer << L" at index " << SubkeyIndex << L" - Current key name: " << KeyName;
|
||||
ReportError(Result, ErrorMessageStream.str());
|
||||
goto Cleanup;
|
||||
}
|
||||
|
||||
std::wstring SubkeyName;
|
||||
SubkeyName.assign(SubKeyNameBuffer, SubkeyNameLength);
|
||||
|
||||
RegistryKey NewKey;
|
||||
Result = HkeyToInternal(HSubkey, SubkeyName, NewKey);
|
||||
if (FAILED(Result))
|
||||
{
|
||||
std::wostringstream ErrorMessageStream;
|
||||
ErrorMessageStream << L"Getting contents of subkey named " << SubkeyName << L" - Current key name: " << KeyName;
|
||||
ReportError(Result, ErrorMessageStream.str());
|
||||
goto Cleanup;
|
||||
}
|
||||
RegKey.Subkeys.emplace_back(std::move(NewKey));
|
||||
|
||||
RegCloseKey(HSubkey);
|
||||
}
|
||||
Cleanup:
|
||||
if (SubKeyNameBuffer)
|
||||
{
|
||||
HeapFree(GetProcessHeap(), 0, (LPVOID)SubKeyNameBuffer);
|
||||
SubKeyNameBuffer = NULL;
|
||||
}
|
||||
if (ValueNameBuffer)
|
||||
{
|
||||
HeapFree(GetProcessHeap(), 0, (LPVOID)ValueNameBuffer);
|
||||
ValueNameBuffer = NULL;
|
||||
}
|
||||
if (ValueBuffer)
|
||||
{
|
||||
HeapFree(GetProcessHeap(), 0, (LPVOID)ValueBuffer);
|
||||
ValueBuffer = NULL;
|
||||
}
|
||||
|
||||
return Result;
|
||||
}
|
||||
|
||||
// non-static function: documented in header.
|
||||
_Must_inspect_result_
|
||||
HRESULT HiveToInternal
|
||||
(
|
||||
_In_ const std::wstring& HiveFilePath,
|
||||
_In_ const std::wstring& RootName,
|
||||
_Out_ RegistryKey& RegKey
|
||||
)
|
||||
{
|
||||
HRESULT Result = E_FAIL;
|
||||
HKEY HiveKey = NULL;
|
||||
|
||||
Result = HRESULT_FROM_WIN32(RegLoadAppKeyW(HiveFilePath.c_str(), &HiveKey, KEY_ALL_ACCESS, REG_PROCESS_APPKEY, 0));
|
||||
if (FAILED(Result))
|
||||
{
|
||||
ReportError(Result, L"Loading hive file " + HiveFilePath);
|
||||
goto Cleanup;
|
||||
}
|
||||
|
||||
Result = HkeyToInternal(HiveKey, RootName, RegKey);
|
||||
|
||||
Cleanup:
|
||||
if (HiveKey != NULL)
|
||||
{
|
||||
RegCloseKey(HiveKey);
|
||||
HiveKey = NULL;
|
||||
|
||||
DeleteHiveLogFiles(HiveFilePath);
|
||||
}
|
||||
|
||||
|
||||
return Result;
|
||||
// (C) Stormshield 2023
|
||||
// Licensed under the Apache license, version 2.0
|
||||
// See LICENSE.txt for details
|
||||
|
||||
#include "Conversions.h"
|
||||
#include "CommonFunctions.h"
|
||||
#include <sstream>
|
||||
|
||||
/// @brief Create an internal representation of a registry key from the handle to a registry key
|
||||
/// @param[in] Hkey the registry key
|
||||
/// @param[in] KeyName Name of the key #Hkey is a handle to
|
||||
/// @param[out] RegKey Representation of the key
|
||||
/// @return HRESULT semantics
|
||||
_Must_inspect_result_
|
||||
static HRESULT HkeyToInternal
|
||||
(
|
||||
_In_ const HKEY Hkey,
|
||||
_In_ const std::wstring& KeyName,
|
||||
_Out_ RegistryKey& RegKey
|
||||
)
|
||||
{
|
||||
HRESULT Result = E_FAIL;
|
||||
DWORD SubkeyCount = 0;
|
||||
DWORD MaximalSubKeyLength = 0;
|
||||
DWORD ValueCount = 0;
|
||||
DWORD MaximalValueNameLength = 0;
|
||||
DWORD MaximalValueLength = 0;
|
||||
LPWSTR SubKeyNameBuffer = NULL;
|
||||
LPWSTR ValueNameBuffer = NULL;
|
||||
PBYTE ValueBuffer = NULL;
|
||||
|
||||
if (Hkey == NULL)
|
||||
{
|
||||
ReportError(E_HANDLE, L"Null HKEY");
|
||||
return E_HANDLE;
|
||||
}
|
||||
|
||||
Result = HRESULT_FROM_WIN32(RegQueryInfoKeyW(Hkey, NULL, 0, NULL, &SubkeyCount, &MaximalSubKeyLength,
|
||||
NULL, &ValueCount, &MaximalValueNameLength, &MaximalValueLength, NULL, NULL));
|
||||
if (FAILED(Result))
|
||||
{
|
||||
ReportError(Result, L"Getting information on HKEY - Current key name: " + KeyName);
|
||||
goto Cleanup;
|
||||
}
|
||||
|
||||
SubKeyNameBuffer = (LPWSTR)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, (1 + MaximalSubKeyLength) * sizeof(WCHAR));
|
||||
if (SubKeyNameBuffer == NULL)
|
||||
{
|
||||
Result = HRESULT_FROM_WIN32(GetLastError());
|
||||
ReportError(Result, L"Allocating space for subkey names - Current key name: " + KeyName);
|
||||
goto Cleanup;
|
||||
}
|
||||
|
||||
ValueNameBuffer = (LPWSTR)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, (1 + MaximalValueNameLength) * sizeof(WCHAR));
|
||||
if (ValueNameBuffer == NULL)
|
||||
{
|
||||
Result = HRESULT_FROM_WIN32(GetLastError());
|
||||
ReportError(Result, L"Allocating space for value names - Current key name: " + KeyName);
|
||||
goto Cleanup;
|
||||
}
|
||||
|
||||
ValueBuffer = (PBYTE)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, MaximalValueLength);
|
||||
if (ValueBuffer == NULL)
|
||||
{
|
||||
Result = HRESULT_FROM_WIN32(GetLastError());
|
||||
ReportError(Result, L"Allocating space for values - Current key name: " + KeyName);
|
||||
goto Cleanup;
|
||||
}
|
||||
|
||||
RegKey.Name = KeyName;
|
||||
|
||||
for (DWORD ValueIndex = 0; ValueIndex < ValueCount; ++ValueIndex)
|
||||
{
|
||||
DWORD ValueNameLength = MaximalValueNameLength + 1;
|
||||
DWORD ValueType = 0;
|
||||
DWORD DataLength = MaximalValueLength + 1;
|
||||
Result = HRESULT_FROM_WIN32(RegEnumValueW(Hkey, ValueIndex, ValueNameBuffer, &ValueNameLength,
|
||||
NULL, &ValueType, ValueBuffer, &DataLength));
|
||||
if (FAILED(Result))
|
||||
{
|
||||
std::wostringstream ErrorMessageStream;
|
||||
ErrorMessageStream << L"Getting value at index " << ValueIndex << L" - Current key name: " << KeyName;
|
||||
ReportError(Result, ErrorMessageStream.str());
|
||||
goto Cleanup;
|
||||
}
|
||||
|
||||
RegistryValue NewValue;
|
||||
NewValue.Name.assign(ValueNameBuffer, ValueNameLength);
|
||||
NewValue.Type = ValueType;
|
||||
NewValue.BinaryValue.assign(ValueBuffer, ValueBuffer + DataLength);
|
||||
|
||||
RegKey.Values.emplace_back(std::move(NewValue));
|
||||
}
|
||||
|
||||
for (DWORD SubkeyIndex = 0; SubkeyIndex < SubkeyCount; ++SubkeyIndex)
|
||||
{
|
||||
HKEY HSubkey = NULL;
|
||||
DWORD SubkeyNameLength = MaximalSubKeyLength + 1;
|
||||
|
||||
Result = HRESULT_FROM_WIN32(RegEnumKeyExW(Hkey, SubkeyIndex, SubKeyNameBuffer, &SubkeyNameLength, NULL, NULL, NULL, NULL));
|
||||
if (FAILED(Result))
|
||||
{
|
||||
std::wostringstream ErrorMessageStream;
|
||||
ErrorMessageStream << L"Getting subkey name at index " << SubkeyIndex << L" - Current key name: " << KeyName;
|
||||
ReportError(Result, ErrorMessageStream.str());
|
||||
goto Cleanup;
|
||||
}
|
||||
|
||||
Result = HRESULT_FROM_WIN32(RegOpenKeyExW(Hkey, SubKeyNameBuffer, REG_OPTION_OPEN_LINK, KEY_READ, &HSubkey));
|
||||
if (FAILED(Result)) {
|
||||
Result = HRESULT_FROM_WIN32(RegOpenKeyExW(Hkey, SubKeyNameBuffer, 0, KEY_READ, &HSubkey));
|
||||
}
|
||||
if (FAILED(Result))
|
||||
{
|
||||
std::wostringstream ErrorMessageStream;
|
||||
ErrorMessageStream << L"Opening subkey named " << SubKeyNameBuffer << L" at index " << SubkeyIndex << L" - Current key name: " << KeyName;
|
||||
ReportError(Result, ErrorMessageStream.str());
|
||||
goto Cleanup;
|
||||
}
|
||||
|
||||
std::wstring SubkeyName;
|
||||
SubkeyName.assign(SubKeyNameBuffer, SubkeyNameLength);
|
||||
|
||||
RegistryKey NewKey;
|
||||
Result = HkeyToInternal(HSubkey, SubkeyName, NewKey);
|
||||
if (FAILED(Result))
|
||||
{
|
||||
std::wostringstream ErrorMessageStream;
|
||||
ErrorMessageStream << L"Getting contents of subkey named " << SubkeyName << L" - Current key name: " << KeyName;
|
||||
ReportError(Result, ErrorMessageStream.str());
|
||||
goto Cleanup;
|
||||
}
|
||||
RegKey.Subkeys.emplace_back(std::move(NewKey));
|
||||
|
||||
RegCloseKey(HSubkey);
|
||||
}
|
||||
Cleanup:
|
||||
if (SubKeyNameBuffer)
|
||||
{
|
||||
HeapFree(GetProcessHeap(), 0, (LPVOID)SubKeyNameBuffer);
|
||||
SubKeyNameBuffer = NULL;
|
||||
}
|
||||
if (ValueNameBuffer)
|
||||
{
|
||||
HeapFree(GetProcessHeap(), 0, (LPVOID)ValueNameBuffer);
|
||||
ValueNameBuffer = NULL;
|
||||
}
|
||||
if (ValueBuffer)
|
||||
{
|
||||
HeapFree(GetProcessHeap(), 0, (LPVOID)ValueBuffer);
|
||||
ValueBuffer = NULL;
|
||||
}
|
||||
|
||||
return Result;
|
||||
}
|
||||
|
||||
// non-static function: documented in header.
|
||||
_Must_inspect_result_
|
||||
HRESULT HiveToInternal
|
||||
(
|
||||
_In_ const std::wstring& HiveFilePath,
|
||||
_In_ const std::wstring& RootName,
|
||||
_Out_ RegistryKey& RegKey
|
||||
)
|
||||
{
|
||||
HRESULT Result = E_FAIL;
|
||||
HKEY HiveKey = NULL;
|
||||
|
||||
Result = HRESULT_FROM_WIN32(RegLoadAppKeyW(HiveFilePath.c_str(), &HiveKey, KEY_READ, REG_PROCESS_APPKEY, 0));
|
||||
if (FAILED(Result))
|
||||
{
|
||||
ReportError(Result, L"Loading hive file " + HiveFilePath);
|
||||
goto Cleanup;
|
||||
}
|
||||
|
||||
Result = HkeyToInternal(HiveKey, RootName, RegKey);
|
||||
|
||||
Cleanup:
|
||||
if (HiveKey != NULL)
|
||||
{
|
||||
RegCloseKey(HiveKey);
|
||||
HiveKey = NULL;
|
||||
|
||||
DeleteHiveLogFiles(HiveFilePath);
|
||||
}
|
||||
|
||||
|
||||
return Result;
|
||||
}
|
||||
+138
-138
@@ -1,139 +1,139 @@
|
||||
// (C) Stormshield 2022
|
||||
// Licensed under the Apache license, version 2.0
|
||||
// See LICENSE.txt for details
|
||||
|
||||
#include "Conversions.h"
|
||||
#include "CommonFunctions.h"
|
||||
#include <sstream>
|
||||
#include <iomanip>
|
||||
#include "Constants.h"
|
||||
|
||||
/// @brief Fill an empty HKEY with internal structures representing a registry key and dependencies
|
||||
/// @param[in] RegKey Registry key to dump into the HKEY
|
||||
/// @param[in] KeyHandle Handle to the key
|
||||
/// @return HRESULT semantics
|
||||
_Must_inspect_result_
|
||||
static HRESULT InternalToHkey(
|
||||
_In_ const RegistryKey& RegKey,
|
||||
_In_ const HKEY KeyHandle
|
||||
)
|
||||
{
|
||||
HRESULT Result = E_FAIL;
|
||||
HKEY SubkeyHandle = NULL;
|
||||
|
||||
if (KeyHandle == NULL)
|
||||
{
|
||||
ReportError(E_HANDLE, L"Invalid Parameter");
|
||||
return E_HANDLE;
|
||||
}
|
||||
|
||||
for (auto &Value : RegKey.Values)
|
||||
{
|
||||
if (Value.BinaryValue.size() > MAXDWORD)
|
||||
{
|
||||
ReportError(E_UNEXPECTED, L"Binary value is too long (name: " + Value.Name + L")");
|
||||
Result = E_UNEXPECTED;
|
||||
goto Cleanup;
|
||||
}
|
||||
Result = HRESULT_FROM_WIN32(RegSetValueExW(KeyHandle, Value.Name.c_str(), 0, Value.Type,
|
||||
Value.BinaryValue.data(), static_cast<DWORD>(Value.BinaryValue.size())));
|
||||
if (FAILED(Result))
|
||||
{
|
||||
ReportError(Result, L"Could not set value " + Value.Name + L" of key " + RegKey.Name);
|
||||
goto Cleanup;
|
||||
}
|
||||
}
|
||||
|
||||
for (auto &Key : RegKey.Subkeys)
|
||||
{
|
||||
if (SubkeyHandle != NULL)
|
||||
{
|
||||
RegCloseKey(SubkeyHandle);
|
||||
SubkeyHandle = NULL;
|
||||
}
|
||||
|
||||
if (Key.Values.size() == 1 && Key.Subkeys.size() == 0 && Key.Values[0].Type == REG_LINK && Key.Values[0].Name == Constants::Hives::SymbolicLinkValue)
|
||||
{
|
||||
Result = HRESULT_FROM_WIN32(RegCreateKeyExW(KeyHandle, Key.Name.c_str(), 0, NULL, REG_OPTION_NON_VOLATILE | REG_OPTION_CREATE_LINK,
|
||||
KEY_ALL_ACCESS, NULL, &SubkeyHandle, NULL));
|
||||
}
|
||||
else
|
||||
{
|
||||
Result = HRESULT_FROM_WIN32(RegCreateKeyExW(KeyHandle, Key.Name.c_str(), 0, NULL, REG_OPTION_NON_VOLATILE,
|
||||
KEY_ALL_ACCESS, NULL, &SubkeyHandle, NULL));
|
||||
}
|
||||
|
||||
if (FAILED(Result))
|
||||
{
|
||||
ReportError(Result, L"Could not create subkey " + Key.Name + L" of key " + RegKey.Name);
|
||||
goto Cleanup;
|
||||
}
|
||||
|
||||
Result = InternalToHkey(Key, SubkeyHandle);
|
||||
|
||||
if (FAILED(Result))
|
||||
{
|
||||
ReportError(Result, L"Could not render subkey " + Key.Name + L" of key " + RegKey.Name);
|
||||
goto Cleanup;
|
||||
}
|
||||
}
|
||||
|
||||
Result = S_OK;
|
||||
Cleanup:
|
||||
if (SubkeyHandle != NULL)
|
||||
{
|
||||
RegCloseKey(SubkeyHandle);
|
||||
SubkeyHandle = NULL;
|
||||
}
|
||||
|
||||
return Result;
|
||||
}
|
||||
|
||||
|
||||
// non-static function: documented in header.
|
||||
_Must_inspect_result_
|
||||
HRESULT InternalToHive
|
||||
(
|
||||
_In_ const RegistryKey& RegKey,
|
||||
_In_ const std::wstring& OutputFilePath
|
||||
)
|
||||
{
|
||||
HRESULT Result = E_FAIL;
|
||||
HKEY HiveKey = NULL;
|
||||
|
||||
if (!DeleteFileW(OutputFilePath.c_str()))
|
||||
{
|
||||
if (GetLastError() != ERROR_FILE_NOT_FOUND)
|
||||
{
|
||||
Result = HRESULT_FROM_WIN32(GetLastError());
|
||||
ReportError(Result, L"Could not delete hive file " + OutputFilePath);
|
||||
goto Cleanup;
|
||||
}
|
||||
}
|
||||
|
||||
Result = HRESULT_FROM_WIN32(RegLoadAppKeyW(OutputFilePath.c_str(), &HiveKey, KEY_ALL_ACCESS, REG_PROCESS_APPKEY, 0));
|
||||
if (FAILED(Result))
|
||||
{
|
||||
ReportError(Result, L"Could not create empty hive file " + OutputFilePath);
|
||||
goto Cleanup;
|
||||
}
|
||||
|
||||
Result = InternalToHkey(RegKey, HiveKey);
|
||||
if (FAILED(Result))
|
||||
{
|
||||
ReportError(Result, L"Could not render internal structure to hive");
|
||||
}
|
||||
|
||||
Result = S_OK;
|
||||
|
||||
Cleanup:
|
||||
if (HiveKey != NULL)
|
||||
{
|
||||
RegCloseKey(HiveKey);
|
||||
HiveKey = NULL;
|
||||
|
||||
DeleteHiveLogFiles(OutputFilePath);
|
||||
}
|
||||
|
||||
return Result;
|
||||
// (C) Stormshield 2023
|
||||
// Licensed under the Apache license, version 2.0
|
||||
// See LICENSE.txt for details
|
||||
|
||||
#include "Conversions.h"
|
||||
#include "CommonFunctions.h"
|
||||
#include <sstream>
|
||||
#include <iomanip>
|
||||
#include "Constants.h"
|
||||
|
||||
/// @brief Fill an empty HKEY with internal structures representing a registry key and dependencies
|
||||
/// @param[in] RegKey Registry key to dump into the HKEY
|
||||
/// @param[in] KeyHandle Handle to the key
|
||||
/// @return HRESULT semantics
|
||||
_Must_inspect_result_
|
||||
static HRESULT InternalToHkey(
|
||||
_In_ const RegistryKey& RegKey,
|
||||
_In_ const HKEY KeyHandle
|
||||
)
|
||||
{
|
||||
HRESULT Result = E_FAIL;
|
||||
HKEY SubkeyHandle = NULL;
|
||||
|
||||
if (KeyHandle == NULL)
|
||||
{
|
||||
ReportError(E_HANDLE, L"Invalid Parameter");
|
||||
return E_HANDLE;
|
||||
}
|
||||
|
||||
for (auto &Value : RegKey.Values)
|
||||
{
|
||||
if (Value.BinaryValue.size() > MAXDWORD)
|
||||
{
|
||||
ReportError(E_UNEXPECTED, L"Binary value is too long (name: " + Value.Name + L")");
|
||||
Result = E_UNEXPECTED;
|
||||
goto Cleanup;
|
||||
}
|
||||
Result = HRESULT_FROM_WIN32(RegSetValueExW(KeyHandle, Value.Name.c_str(), 0, Value.Type,
|
||||
Value.BinaryValue.data(), static_cast<DWORD>(Value.BinaryValue.size())));
|
||||
if (FAILED(Result))
|
||||
{
|
||||
ReportError(Result, L"Could not set value " + Value.Name + L" of key " + RegKey.Name);
|
||||
goto Cleanup;
|
||||
}
|
||||
}
|
||||
|
||||
for (auto &Key : RegKey.Subkeys)
|
||||
{
|
||||
if (SubkeyHandle != NULL)
|
||||
{
|
||||
RegCloseKey(SubkeyHandle);
|
||||
SubkeyHandle = NULL;
|
||||
}
|
||||
|
||||
if (Key.Values.size() == 1 && Key.Subkeys.size() == 0 && Key.Values[0].Type == REG_LINK && Key.Values[0].Name == Constants::Hives::SymbolicLinkValue)
|
||||
{
|
||||
Result = HRESULT_FROM_WIN32(RegCreateKeyExW(KeyHandle, Key.Name.c_str(), 0, NULL, REG_OPTION_NON_VOLATILE | REG_OPTION_CREATE_LINK,
|
||||
KEY_ALL_ACCESS, NULL, &SubkeyHandle, NULL));
|
||||
}
|
||||
else
|
||||
{
|
||||
Result = HRESULT_FROM_WIN32(RegCreateKeyExW(KeyHandle, Key.Name.c_str(), 0, NULL, REG_OPTION_NON_VOLATILE,
|
||||
KEY_ALL_ACCESS, NULL, &SubkeyHandle, NULL));
|
||||
}
|
||||
|
||||
if (FAILED(Result))
|
||||
{
|
||||
ReportError(Result, L"Could not create subkey " + Key.Name + L" of key " + RegKey.Name);
|
||||
goto Cleanup;
|
||||
}
|
||||
|
||||
Result = InternalToHkey(Key, SubkeyHandle);
|
||||
|
||||
if (FAILED(Result))
|
||||
{
|
||||
ReportError(Result, L"Could not render subkey " + Key.Name + L" of key " + RegKey.Name);
|
||||
goto Cleanup;
|
||||
}
|
||||
}
|
||||
|
||||
Result = S_OK;
|
||||
Cleanup:
|
||||
if (SubkeyHandle != NULL)
|
||||
{
|
||||
RegCloseKey(SubkeyHandle);
|
||||
SubkeyHandle = NULL;
|
||||
}
|
||||
|
||||
return Result;
|
||||
}
|
||||
|
||||
|
||||
// non-static function: documented in header.
|
||||
_Must_inspect_result_
|
||||
HRESULT InternalToHive
|
||||
(
|
||||
_In_ const RegistryKey& RegKey,
|
||||
_In_ const std::wstring& OutputFilePath
|
||||
)
|
||||
{
|
||||
HRESULT Result = E_FAIL;
|
||||
HKEY HiveKey = NULL;
|
||||
|
||||
if (!DeleteFileW(OutputFilePath.c_str()))
|
||||
{
|
||||
if (GetLastError() != ERROR_FILE_NOT_FOUND)
|
||||
{
|
||||
Result = HRESULT_FROM_WIN32(GetLastError());
|
||||
ReportError(Result, L"Could not delete hive file " + OutputFilePath);
|
||||
goto Cleanup;
|
||||
}
|
||||
}
|
||||
|
||||
Result = HRESULT_FROM_WIN32(RegLoadAppKeyW(OutputFilePath.c_str(), &HiveKey, KEY_ALL_ACCESS, REG_PROCESS_APPKEY, 0));
|
||||
if (FAILED(Result))
|
||||
{
|
||||
ReportError(Result, L"Could not create empty hive file " + OutputFilePath);
|
||||
goto Cleanup;
|
||||
}
|
||||
|
||||
Result = InternalToHkey(RegKey, HiveKey);
|
||||
if (FAILED(Result))
|
||||
{
|
||||
ReportError(Result, L"Could not render internal structure to hive");
|
||||
}
|
||||
|
||||
Result = S_OK;
|
||||
|
||||
Cleanup:
|
||||
if (HiveKey != NULL)
|
||||
{
|
||||
RegCloseKey(HiveKey);
|
||||
HiveKey = NULL;
|
||||
|
||||
DeleteHiveLogFiles(OutputFilePath);
|
||||
}
|
||||
|
||||
return Result;
|
||||
}
|
||||
+396
-396
@@ -1,397 +1,397 @@
|
||||
// (C) Stormshield 2022
|
||||
// Licensed under the Apache license, version 2.0
|
||||
// See LICENSE.txt for details
|
||||
|
||||
#include "Constants.h"
|
||||
#include "Conversions.h"
|
||||
#include "CommonFunctions.h"
|
||||
#include <sstream>
|
||||
#include <iomanip>
|
||||
|
||||
/// @brief Append binary contents of a std::wstring to an opened file
|
||||
/// @param[in] OutFileHandle Handle to the file
|
||||
/// @param[in] String Input string
|
||||
/// @note The string buffer is dumped to the file “as is”.
|
||||
/// Null characters may be appended to the file if the string buffer includes some of them.
|
||||
_Must_inspect_result_
|
||||
static HRESULT WriteStringBufferToFile
|
||||
(
|
||||
_In_ const HANDLE OutFileHandle,
|
||||
_In_ const std::wstring String
|
||||
)
|
||||
{
|
||||
HRESULT Result = E_FAIL;
|
||||
|
||||
if (String.length() >= MAXDWORD / sizeof(decltype(String)::traits_type::char_type))
|
||||
{
|
||||
Result = HRESULT_FROM_WIN32(ERROR_ARITHMETIC_OVERFLOW);
|
||||
ReportError(Result, L"Writing data that is too large");
|
||||
return Result;
|
||||
}
|
||||
|
||||
if (OutFileHandle == NULL || OutFileHandle == INVALID_HANDLE_VALUE)
|
||||
{
|
||||
ReportError(E_HANDLE, L"Invalid parameter");
|
||||
return E_HANDLE;
|
||||
}
|
||||
|
||||
{
|
||||
const DWORD BytesToWrite = static_cast<DWORD>(String.length() * sizeof(decltype(String)::traits_type::char_type));
|
||||
DWORD BytesWritten = 0;
|
||||
|
||||
if (!WriteFile(OutFileHandle, (LPCVOID)String.c_str(), BytesToWrite, &BytesWritten, NULL))
|
||||
{
|
||||
Result = HRESULT_FROM_WIN32(GetLastError());
|
||||
ReportError(Result, L"Could not write to output file");
|
||||
return Result;
|
||||
}
|
||||
if (BytesWritten != BytesToWrite)
|
||||
{
|
||||
Result = E_UNEXPECTED;
|
||||
ReportError(Result, L"Bytes not fully written to file");
|
||||
return Result;
|
||||
}
|
||||
}
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
/// @brief Render the contents of a registry value in a .reg file
|
||||
/// @param[in] OutFileHandle Handle to the file
|
||||
/// @param[in] FirstLineSizeSoFar How many characters have already been written on the line when dumping
|
||||
/// the name of the value and the equal sign.
|
||||
/// This is used for mimicking .reg format line breaks before lines over 80 characters
|
||||
/// @param[in] RegValue Representation of the registry value
|
||||
/// @return HRESULT semantics
|
||||
_Must_inspect_result_
|
||||
static HRESULT RenderBinaryValue
|
||||
(
|
||||
_In_ const HANDLE OutFileHandle,
|
||||
_In_ const SIZE_T FirstLineSizeSoFar,
|
||||
_In_ const RegistryValue& RegValue
|
||||
)
|
||||
{
|
||||
HRESULT Result = E_FAIL;
|
||||
SIZE_T CurLineSizeSoFar = FirstLineSizeSoFar;
|
||||
if (OutFileHandle == NULL || OutFileHandle == INVALID_HANDLE_VALUE)
|
||||
{
|
||||
return E_HANDLE;
|
||||
}
|
||||
|
||||
std::wostringstream BinaryRenditionStream;
|
||||
BinaryRenditionStream << Constants::RegFiles::HexPrefix;
|
||||
if (RegValue.Type != REG_BINARY)
|
||||
{
|
||||
BinaryRenditionStream << Constants::RegFiles::HexTypeSpecOpening << std::hex << RegValue.Type << Constants::RegFiles::HexTypeSpecClosing;
|
||||
}
|
||||
BinaryRenditionStream << Constants::RegFiles::HexSuffix;
|
||||
|
||||
CurLineSizeSoFar += BinaryRenditionStream.str().length();
|
||||
|
||||
for (std::vector<BYTE>::const_iterator CurByteIt = RegValue.BinaryValue.cbegin(); CurByteIt != RegValue.BinaryValue.cend(); ++CurByteIt)
|
||||
{
|
||||
BinaryRenditionStream << std::hex << std::setfill(L'0') << std::setw(2) << *CurByteIt;
|
||||
CurLineSizeSoFar += 2;
|
||||
if (CurByteIt + 1 != RegValue.BinaryValue.cend())
|
||||
{
|
||||
BinaryRenditionStream << Constants::RegFiles::HexByteSeparator;
|
||||
CurLineSizeSoFar += 1;
|
||||
if (CurLineSizeSoFar > Constants::RegFiles::HexWrappingLimit - 4)
|
||||
{
|
||||
// adding "xx,\" would go over 80 characters, reg export typically breaks line here.
|
||||
BinaryRenditionStream << Constants::RegFiles::HexByteNewLine;
|
||||
for (SIZE_T SpaceCount = 0; SpaceCount < Constants::RegFiles::HexNewLineLeadingSpaces; ++SpaceCount)
|
||||
{
|
||||
BinaryRenditionStream << Constants::RegFiles::LeadingSpace;
|
||||
}
|
||||
CurLineSizeSoFar = Constants::RegFiles::HexNewLineLeadingSpaces;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
BinaryRenditionStream << Constants::RegFiles::NewLines;
|
||||
|
||||
Result = WriteStringBufferToFile(OutFileHandle, BinaryRenditionStream.str());
|
||||
if (FAILED(Result))
|
||||
{
|
||||
ReportError(Result, L"Could not write to output file");
|
||||
return Result;
|
||||
}
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
/// @brief Render the contents of a REG_DWORD registry value in a .reg file
|
||||
/// @param[in] OutFileHandle Handle to the file
|
||||
/// @param[in] FirstLineSizeSoFar Use for fallback to #RenderBinaryValue
|
||||
/// @param[in] RegValue Representation of the registry value
|
||||
/// @return HRESULT semantics
|
||||
_Must_inspect_result_
|
||||
static HRESULT RenderDwordValue
|
||||
(
|
||||
_In_ const HANDLE OutFileHandle,
|
||||
_In_ const SIZE_T FirstLineSizeSoFar,
|
||||
_In_ const RegistryValue& RegValue
|
||||
)
|
||||
{
|
||||
if (OutFileHandle == NULL || OutFileHandle == INVALID_HANDLE_VALUE)
|
||||
{
|
||||
ReportError(E_HANDLE, L"Invalid parameter");
|
||||
return E_HANDLE;
|
||||
}
|
||||
|
||||
if (RegValue.Type != REG_DWORD)
|
||||
{
|
||||
ReportError(E_HANDLE, L"Invalid parameter");
|
||||
return E_INVALIDARG;
|
||||
}
|
||||
|
||||
if (RegValue.BinaryValue.size() != sizeof(DWORD))
|
||||
{
|
||||
return RenderBinaryValue(OutFileHandle, FirstLineSizeSoFar, RegValue);
|
||||
}
|
||||
|
||||
std::wostringstream DwordRenditionStream;
|
||||
DwordRenditionStream << L"dword:";
|
||||
DwordRenditionStream << std::hex << std::setw(8) << std::setfill(L'0') << *(DWORD*)(RegValue.BinaryValue.data());
|
||||
DwordRenditionStream << Constants::RegFiles::NewLines;
|
||||
|
||||
return WriteStringBufferToFile(OutFileHandle, DwordRenditionStream.str());
|
||||
|
||||
}
|
||||
|
||||
/// @brief Render the contents of a REG_SZ registry value in a .reg file
|
||||
/// @param[in] OutFileHandle Handle to the file
|
||||
/// @param[in] FirstLineSizeSoFar Use for fallback to #RenderBinaryValue
|
||||
/// @param[in] RegValue Representation of the registry value
|
||||
/// @return HRESULT semantics
|
||||
/// @note This falls back to binary rendition if the REG_SZ does not meet requirements such as:
|
||||
/// - REG_SZ values should be terminated by a null character
|
||||
/// - REG_SZ values may not contain other null characters
|
||||
/// - REG_SZ value sizes should be a multiple of 2 as they store WCHAR-based values.
|
||||
_Must_inspect_result_
|
||||
static HRESULT RenderStringValue
|
||||
(
|
||||
_In_ const HANDLE OutFileHandle,
|
||||
_In_ const SIZE_T FirstLineSizeSoFar,
|
||||
_In_ const RegistryValue& RegValue
|
||||
)
|
||||
{
|
||||
std::wstring WstringValue;
|
||||
if (OutFileHandle == NULL || OutFileHandle == INVALID_HANDLE_VALUE)
|
||||
{
|
||||
ReportError(E_HANDLE, L"Invalid parameter");
|
||||
return E_HANDLE;
|
||||
}
|
||||
|
||||
if (RegValue.Type != REG_SZ)
|
||||
{
|
||||
ReportError(E_HANDLE, L"Invalid parameter");
|
||||
return E_INVALIDARG;
|
||||
}
|
||||
|
||||
if (RegValue.BinaryValue.size() % sizeof(WCHAR) != 0)
|
||||
{
|
||||
return RenderBinaryValue(OutFileHandle, FirstLineSizeSoFar, RegValue);
|
||||
}
|
||||
|
||||
WstringValue.assign((PWCHAR) RegValue.BinaryValue.data(), RegValue.BinaryValue.size() / sizeof(WCHAR));
|
||||
if (WstringValue.empty())
|
||||
{
|
||||
return RenderBinaryValue(OutFileHandle, FirstLineSizeSoFar, RegValue);
|
||||
}
|
||||
|
||||
if ( WstringValue.empty()
|
||||
|| WstringValue.back() != L'\0'
|
||||
|| std::count(WstringValue.cbegin(), WstringValue.cend() - 1, L'\0') != 0
|
||||
)
|
||||
{
|
||||
return RenderBinaryValue(OutFileHandle, FirstLineSizeSoFar, RegValue);
|
||||
}
|
||||
|
||||
// Now we can render the value as REG_SZ: Remove null character
|
||||
WstringValue.pop_back();
|
||||
|
||||
GlobalStringSubstitute(WstringValue, L"\\", L"\\\\");
|
||||
GlobalStringSubstitute(WstringValue, L"\"", L"\\\"");
|
||||
GlobalStringSubstitute(WstringValue, L"\n", L"\r\n");
|
||||
|
||||
std::wostringstream StringRenditionStream;
|
||||
StringRenditionStream << L"\"" << WstringValue << L"\"" << Constants::RegFiles::NewLines;
|
||||
return WriteStringBufferToFile(OutFileHandle, StringRenditionStream.str());
|
||||
}
|
||||
|
||||
/// @brief Render a registry value and its contents in a .reg file
|
||||
/// @param[in] OutFileHandle Handle to the file
|
||||
/// @param[in] RegValue Representation of the registry value
|
||||
/// @return HRESULT semantics
|
||||
_Must_inspect_result_
|
||||
HRESULT RenderRegistryValue
|
||||
(
|
||||
_In_ const HANDLE OutFileHandle,
|
||||
_In_ const RegistryValue& RegValue
|
||||
)
|
||||
{
|
||||
HRESULT Result = E_FAIL;
|
||||
SIZE_T FirstLineSizeSoFar = 0;
|
||||
if (OutFileHandle == NULL || OutFileHandle == INVALID_HANDLE_VALUE)
|
||||
{
|
||||
ReportError(E_HANDLE, L"Invalid parameter");
|
||||
return E_HANDLE;
|
||||
}
|
||||
|
||||
std::wstring EscapedName = RegValue.Name;
|
||||
GlobalStringSubstitute(EscapedName, L"\\", L"\\\\");
|
||||
GlobalStringSubstitute(EscapedName, L"\"", L"\\\"");
|
||||
GlobalStringSubstitute(EscapedName, L"\n", L"\r\n");
|
||||
std::wostringstream Str;
|
||||
if (EscapedName.empty())
|
||||
{
|
||||
Str << Constants::RegFiles::DefaultValue << Constants::RegFiles::ValueNameSeparator;
|
||||
}
|
||||
else
|
||||
{
|
||||
Str << L"\"" << EscapedName << L"\"=";
|
||||
}
|
||||
Result = WriteStringBufferToFile(OutFileHandle, Str.str());
|
||||
if (FAILED(Result))
|
||||
{
|
||||
ReportError(Result, L"Could not write data to file");
|
||||
return Result;
|
||||
}
|
||||
|
||||
FirstLineSizeSoFar += Str.str().length();
|
||||
|
||||
if (RegValue.Type == REG_DWORD)
|
||||
{
|
||||
return RenderDwordValue(OutFileHandle, FirstLineSizeSoFar, RegValue);
|
||||
}
|
||||
if (RegValue.Type == REG_SZ)
|
||||
{
|
||||
return RenderStringValue(OutFileHandle, FirstLineSizeSoFar, RegValue);
|
||||
}
|
||||
return RenderBinaryValue(OutFileHandle, FirstLineSizeSoFar, RegValue);
|
||||
}
|
||||
|
||||
/// @brief Render a registry key and its values and subkeys in a .reg file
|
||||
/// @param[in] OutFileHandle Handle to the file
|
||||
/// @param[in] RegKey Representation of the registry key
|
||||
/// @param[in] PathSoFar Path to this key
|
||||
/// @return HRESULT semantics
|
||||
_Must_inspect_result_
|
||||
HRESULT RenderRegistryKey
|
||||
(
|
||||
_In_ const HANDLE OutFileHandle,
|
||||
_In_ const RegistryKey& RegKey,
|
||||
_In_ const std::wstring &PathSoFar
|
||||
)
|
||||
{
|
||||
HRESULT Result = E_FAIL;
|
||||
if (OutFileHandle == NULL || OutFileHandle == INVALID_HANDLE_VALUE)
|
||||
{
|
||||
ReportError(E_HANDLE, L"Invalid parameter");
|
||||
return E_HANDLE;
|
||||
}
|
||||
|
||||
std::wstring NewPath;
|
||||
if (PathSoFar.empty())
|
||||
{
|
||||
NewPath = RegKey.Name;
|
||||
}
|
||||
else
|
||||
{
|
||||
NewPath = PathSoFar + Constants::RegFiles::PathSeparator + RegKey.Name;
|
||||
}
|
||||
|
||||
std::wstring EscapedPath { NewPath };
|
||||
GlobalStringSubstitute(EscapedPath, L"\n", L"\r\n");
|
||||
|
||||
{
|
||||
std::wostringstream KeySpecStream;
|
||||
KeySpecStream << Constants::RegFiles::KeyOpening << EscapedPath << Constants::RegFiles::KeyClosing << Constants::RegFiles::NewLines;
|
||||
|
||||
Result = WriteStringBufferToFile(OutFileHandle, KeySpecStream.str());
|
||||
if (FAILED(Result))
|
||||
{
|
||||
ReportError(E_HANDLE, L"Could not write to output file");
|
||||
return Result;
|
||||
}
|
||||
}
|
||||
|
||||
for (const RegistryValue &Value : RegKey.Values)
|
||||
{
|
||||
Result = RenderRegistryValue(OutFileHandle, Value);
|
||||
if (FAILED(Result))
|
||||
{
|
||||
ReportError(E_HANDLE, L"Could not render registry value" + Value.Name);
|
||||
return Result;
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
Result = WriteStringBufferToFile(OutFileHandle, Constants::RegFiles::NewLines );
|
||||
if (FAILED(Result))
|
||||
{
|
||||
ReportError(E_HANDLE, L"Could not write to output file");
|
||||
return Result;
|
||||
}
|
||||
}
|
||||
|
||||
for (const RegistryKey &Key : RegKey.Subkeys)
|
||||
{
|
||||
Result = RenderRegistryKey(OutFileHandle, Key, NewPath);
|
||||
if (FAILED(Result))
|
||||
{
|
||||
ReportError(E_HANDLE, L"Could not render registry key" + Key.Name);
|
||||
return Result;
|
||||
}
|
||||
}
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
// non-static function: documented in header.
|
||||
_Must_inspect_result_
|
||||
HRESULT InternalToRegfile
|
||||
(
|
||||
_In_ const RegistryKey& RegKey,
|
||||
_In_ const std::wstring& OutputFilePath
|
||||
)
|
||||
{
|
||||
HRESULT Result = E_FAIL;
|
||||
HANDLE OutFileHandle = INVALID_HANDLE_VALUE;
|
||||
|
||||
OutFileHandle = CreateFileW(OutputFilePath.c_str(), GENERIC_WRITE, FILE_SHARE_READ, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
|
||||
if (OutFileHandle == INVALID_HANDLE_VALUE)
|
||||
{
|
||||
Result = HRESULT_FROM_WIN32(GetLastError());
|
||||
ReportError(Result, L"Could not open file " + OutputFilePath + L" for writing");
|
||||
goto Cleanup;
|
||||
}
|
||||
|
||||
{
|
||||
Result = WriteStringBufferToFile(OutFileHandle, Constants::RegFiles::Preamble);
|
||||
if (FAILED(Result))
|
||||
{
|
||||
ReportError(Result, L"Could not write to output file");
|
||||
goto Cleanup;
|
||||
}
|
||||
}
|
||||
|
||||
Result = RenderRegistryKey(OutFileHandle, RegKey, std::wstring{});
|
||||
if (FAILED(Result))
|
||||
{
|
||||
ReportError(Result, L"Could not render registry key");
|
||||
goto Cleanup;
|
||||
}
|
||||
|
||||
Result = S_OK;
|
||||
|
||||
Cleanup:
|
||||
if (OutFileHandle != INVALID_HANDLE_VALUE)
|
||||
{
|
||||
CloseHandle(OutFileHandle);
|
||||
OutFileHandle = INVALID_HANDLE_VALUE;
|
||||
}
|
||||
|
||||
return Result;
|
||||
// (C) Stormshield 2023
|
||||
// Licensed under the Apache license, version 2.0
|
||||
// See LICENSE.txt for details
|
||||
|
||||
#include "Constants.h"
|
||||
#include "Conversions.h"
|
||||
#include "CommonFunctions.h"
|
||||
#include <sstream>
|
||||
#include <iomanip>
|
||||
|
||||
/// @brief Append binary contents of a std::wstring to an opened file
|
||||
/// @param[in] OutFileHandle Handle to the file
|
||||
/// @param[in] String Input string
|
||||
/// @note The string buffer is dumped to the file “as is”.
|
||||
/// Null characters may be appended to the file if the string buffer includes some of them.
|
||||
_Must_inspect_result_
|
||||
static HRESULT WriteStringBufferToFile
|
||||
(
|
||||
_In_ const HANDLE OutFileHandle,
|
||||
_In_ const std::wstring String
|
||||
)
|
||||
{
|
||||
HRESULT Result = E_FAIL;
|
||||
|
||||
if (String.length() >= MAXDWORD / sizeof(decltype(String)::traits_type::char_type))
|
||||
{
|
||||
Result = HRESULT_FROM_WIN32(ERROR_ARITHMETIC_OVERFLOW);
|
||||
ReportError(Result, L"Writing data that is too large");
|
||||
return Result;
|
||||
}
|
||||
|
||||
if (OutFileHandle == NULL || OutFileHandle == INVALID_HANDLE_VALUE)
|
||||
{
|
||||
ReportError(E_HANDLE, L"Invalid parameter");
|
||||
return E_HANDLE;
|
||||
}
|
||||
|
||||
{
|
||||
const DWORD BytesToWrite = static_cast<DWORD>(String.length() * sizeof(decltype(String)::traits_type::char_type));
|
||||
DWORD BytesWritten = 0;
|
||||
|
||||
if (!WriteFile(OutFileHandle, (LPCVOID)String.c_str(), BytesToWrite, &BytesWritten, NULL))
|
||||
{
|
||||
Result = HRESULT_FROM_WIN32(GetLastError());
|
||||
ReportError(Result, L"Could not write to output file");
|
||||
return Result;
|
||||
}
|
||||
if (BytesWritten != BytesToWrite)
|
||||
{
|
||||
Result = E_UNEXPECTED;
|
||||
ReportError(Result, L"Bytes not fully written to file");
|
||||
return Result;
|
||||
}
|
||||
}
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
/// @brief Render the contents of a registry value in a .reg file
|
||||
/// @param[in] OutFileHandle Handle to the file
|
||||
/// @param[in] FirstLineSizeSoFar How many characters have already been written on the line when dumping
|
||||
/// the name of the value and the equal sign.
|
||||
/// This is used for mimicking .reg format line breaks before lines over 80 characters
|
||||
/// @param[in] RegValue Representation of the registry value
|
||||
/// @return HRESULT semantics
|
||||
_Must_inspect_result_
|
||||
static HRESULT RenderBinaryValue
|
||||
(
|
||||
_In_ const HANDLE OutFileHandle,
|
||||
_In_ const SIZE_T FirstLineSizeSoFar,
|
||||
_In_ const RegistryValue& RegValue
|
||||
)
|
||||
{
|
||||
HRESULT Result = E_FAIL;
|
||||
SIZE_T CurLineSizeSoFar = FirstLineSizeSoFar;
|
||||
if (OutFileHandle == NULL || OutFileHandle == INVALID_HANDLE_VALUE)
|
||||
{
|
||||
return E_HANDLE;
|
||||
}
|
||||
|
||||
std::wostringstream BinaryRenditionStream;
|
||||
BinaryRenditionStream << Constants::RegFiles::HexPrefix;
|
||||
if (RegValue.Type != REG_BINARY)
|
||||
{
|
||||
BinaryRenditionStream << Constants::RegFiles::HexTypeSpecOpening << std::hex << RegValue.Type << Constants::RegFiles::HexTypeSpecClosing;
|
||||
}
|
||||
BinaryRenditionStream << Constants::RegFiles::HexSuffix;
|
||||
|
||||
CurLineSizeSoFar += BinaryRenditionStream.str().length();
|
||||
|
||||
for (std::vector<BYTE>::const_iterator CurByteIt = RegValue.BinaryValue.cbegin(); CurByteIt != RegValue.BinaryValue.cend(); ++CurByteIt)
|
||||
{
|
||||
BinaryRenditionStream << std::hex << std::setfill(L'0') << std::setw(2) << *CurByteIt;
|
||||
CurLineSizeSoFar += 2;
|
||||
if (CurByteIt + 1 != RegValue.BinaryValue.cend())
|
||||
{
|
||||
BinaryRenditionStream << Constants::RegFiles::HexByteSeparator;
|
||||
CurLineSizeSoFar += 1;
|
||||
if (CurLineSizeSoFar > Constants::RegFiles::HexWrappingLimit - 4)
|
||||
{
|
||||
// adding "xx,\" would go over 80 characters, reg export typically breaks line here.
|
||||
BinaryRenditionStream << Constants::RegFiles::HexByteNewLine;
|
||||
for (SIZE_T SpaceCount = 0; SpaceCount < Constants::RegFiles::HexNewLineLeadingSpaces; ++SpaceCount)
|
||||
{
|
||||
BinaryRenditionStream << Constants::RegFiles::LeadingSpace;
|
||||
}
|
||||
CurLineSizeSoFar = Constants::RegFiles::HexNewLineLeadingSpaces;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
BinaryRenditionStream << Constants::RegFiles::NewLines;
|
||||
|
||||
Result = WriteStringBufferToFile(OutFileHandle, BinaryRenditionStream.str());
|
||||
if (FAILED(Result))
|
||||
{
|
||||
ReportError(Result, L"Could not write to output file");
|
||||
return Result;
|
||||
}
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
/// @brief Render the contents of a REG_DWORD registry value in a .reg file
|
||||
/// @param[in] OutFileHandle Handle to the file
|
||||
/// @param[in] FirstLineSizeSoFar Use for fallback to #RenderBinaryValue
|
||||
/// @param[in] RegValue Representation of the registry value
|
||||
/// @return HRESULT semantics
|
||||
_Must_inspect_result_
|
||||
static HRESULT RenderDwordValue
|
||||
(
|
||||
_In_ const HANDLE OutFileHandle,
|
||||
_In_ const SIZE_T FirstLineSizeSoFar,
|
||||
_In_ const RegistryValue& RegValue
|
||||
)
|
||||
{
|
||||
if (OutFileHandle == NULL || OutFileHandle == INVALID_HANDLE_VALUE)
|
||||
{
|
||||
ReportError(E_HANDLE, L"Invalid parameter");
|
||||
return E_HANDLE;
|
||||
}
|
||||
|
||||
if (RegValue.Type != REG_DWORD)
|
||||
{
|
||||
ReportError(E_HANDLE, L"Invalid parameter");
|
||||
return E_INVALIDARG;
|
||||
}
|
||||
|
||||
if (RegValue.BinaryValue.size() != sizeof(DWORD))
|
||||
{
|
||||
return RenderBinaryValue(OutFileHandle, FirstLineSizeSoFar, RegValue);
|
||||
}
|
||||
|
||||
std::wostringstream DwordRenditionStream;
|
||||
DwordRenditionStream << L"dword:";
|
||||
DwordRenditionStream << std::hex << std::setw(8) << std::setfill(L'0') << *(DWORD*)(RegValue.BinaryValue.data());
|
||||
DwordRenditionStream << Constants::RegFiles::NewLines;
|
||||
|
||||
return WriteStringBufferToFile(OutFileHandle, DwordRenditionStream.str());
|
||||
|
||||
}
|
||||
|
||||
/// @brief Render the contents of a REG_SZ registry value in a .reg file
|
||||
/// @param[in] OutFileHandle Handle to the file
|
||||
/// @param[in] FirstLineSizeSoFar Use for fallback to #RenderBinaryValue
|
||||
/// @param[in] RegValue Representation of the registry value
|
||||
/// @return HRESULT semantics
|
||||
/// @note This falls back to binary rendition if the REG_SZ does not meet requirements such as:
|
||||
/// - REG_SZ values should be terminated by a null character
|
||||
/// - REG_SZ values may not contain other null characters
|
||||
/// - REG_SZ value sizes should be a multiple of 2 as they store WCHAR-based values.
|
||||
_Must_inspect_result_
|
||||
static HRESULT RenderStringValue
|
||||
(
|
||||
_In_ const HANDLE OutFileHandle,
|
||||
_In_ const SIZE_T FirstLineSizeSoFar,
|
||||
_In_ const RegistryValue& RegValue
|
||||
)
|
||||
{
|
||||
std::wstring WstringValue;
|
||||
if (OutFileHandle == NULL || OutFileHandle == INVALID_HANDLE_VALUE)
|
||||
{
|
||||
ReportError(E_HANDLE, L"Invalid parameter");
|
||||
return E_HANDLE;
|
||||
}
|
||||
|
||||
if (RegValue.Type != REG_SZ)
|
||||
{
|
||||
ReportError(E_HANDLE, L"Invalid parameter");
|
||||
return E_INVALIDARG;
|
||||
}
|
||||
|
||||
if (RegValue.BinaryValue.size() % sizeof(WCHAR) != 0)
|
||||
{
|
||||
return RenderBinaryValue(OutFileHandle, FirstLineSizeSoFar, RegValue);
|
||||
}
|
||||
|
||||
WstringValue.assign((PWCHAR) RegValue.BinaryValue.data(), RegValue.BinaryValue.size() / sizeof(WCHAR));
|
||||
if (WstringValue.empty())
|
||||
{
|
||||
return RenderBinaryValue(OutFileHandle, FirstLineSizeSoFar, RegValue);
|
||||
}
|
||||
|
||||
if ( WstringValue.empty()
|
||||
|| WstringValue.back() != L'\0'
|
||||
|| std::count(WstringValue.cbegin(), WstringValue.cend() - 1, L'\0') != 0
|
||||
)
|
||||
{
|
||||
return RenderBinaryValue(OutFileHandle, FirstLineSizeSoFar, RegValue);
|
||||
}
|
||||
|
||||
// Now we can render the value as REG_SZ: Remove null character
|
||||
WstringValue.pop_back();
|
||||
|
||||
GlobalStringSubstitute(WstringValue, L"\\", L"\\\\");
|
||||
GlobalStringSubstitute(WstringValue, L"\"", L"\\\"");
|
||||
GlobalStringSubstitute(WstringValue, L"\n", L"\r\n");
|
||||
|
||||
std::wostringstream StringRenditionStream;
|
||||
StringRenditionStream << L"\"" << WstringValue << L"\"" << Constants::RegFiles::NewLines;
|
||||
return WriteStringBufferToFile(OutFileHandle, StringRenditionStream.str());
|
||||
}
|
||||
|
||||
/// @brief Render a registry value and its contents in a .reg file
|
||||
/// @param[in] OutFileHandle Handle to the file
|
||||
/// @param[in] RegValue Representation of the registry value
|
||||
/// @return HRESULT semantics
|
||||
_Must_inspect_result_
|
||||
HRESULT RenderRegistryValue
|
||||
(
|
||||
_In_ const HANDLE OutFileHandle,
|
||||
_In_ const RegistryValue& RegValue
|
||||
)
|
||||
{
|
||||
HRESULT Result = E_FAIL;
|
||||
SIZE_T FirstLineSizeSoFar = 0;
|
||||
if (OutFileHandle == NULL || OutFileHandle == INVALID_HANDLE_VALUE)
|
||||
{
|
||||
ReportError(E_HANDLE, L"Invalid parameter");
|
||||
return E_HANDLE;
|
||||
}
|
||||
|
||||
std::wstring EscapedName = RegValue.Name;
|
||||
GlobalStringSubstitute(EscapedName, L"\\", L"\\\\");
|
||||
GlobalStringSubstitute(EscapedName, L"\"", L"\\\"");
|
||||
GlobalStringSubstitute(EscapedName, L"\n", L"\r\n");
|
||||
std::wostringstream Str;
|
||||
if (EscapedName.empty())
|
||||
{
|
||||
Str << Constants::RegFiles::DefaultValue << Constants::RegFiles::ValueNameSeparator;
|
||||
}
|
||||
else
|
||||
{
|
||||
Str << L"\"" << EscapedName << L"\"=";
|
||||
}
|
||||
Result = WriteStringBufferToFile(OutFileHandle, Str.str());
|
||||
if (FAILED(Result))
|
||||
{
|
||||
ReportError(Result, L"Could not write data to file");
|
||||
return Result;
|
||||
}
|
||||
|
||||
FirstLineSizeSoFar += Str.str().length();
|
||||
|
||||
if (RegValue.Type == REG_DWORD)
|
||||
{
|
||||
return RenderDwordValue(OutFileHandle, FirstLineSizeSoFar, RegValue);
|
||||
}
|
||||
if (RegValue.Type == REG_SZ)
|
||||
{
|
||||
return RenderStringValue(OutFileHandle, FirstLineSizeSoFar, RegValue);
|
||||
}
|
||||
return RenderBinaryValue(OutFileHandle, FirstLineSizeSoFar, RegValue);
|
||||
}
|
||||
|
||||
/// @brief Render a registry key and its values and subkeys in a .reg file
|
||||
/// @param[in] OutFileHandle Handle to the file
|
||||
/// @param[in] RegKey Representation of the registry key
|
||||
/// @param[in] PathSoFar Path to this key
|
||||
/// @return HRESULT semantics
|
||||
_Must_inspect_result_
|
||||
HRESULT RenderRegistryKey
|
||||
(
|
||||
_In_ const HANDLE OutFileHandle,
|
||||
_In_ const RegistryKey& RegKey,
|
||||
_In_ const std::wstring &PathSoFar
|
||||
)
|
||||
{
|
||||
HRESULT Result = E_FAIL;
|
||||
if (OutFileHandle == NULL || OutFileHandle == INVALID_HANDLE_VALUE)
|
||||
{
|
||||
ReportError(E_HANDLE, L"Invalid parameter");
|
||||
return E_HANDLE;
|
||||
}
|
||||
|
||||
std::wstring NewPath;
|
||||
if (PathSoFar.empty())
|
||||
{
|
||||
NewPath = RegKey.Name;
|
||||
}
|
||||
else
|
||||
{
|
||||
NewPath = PathSoFar + Constants::RegFiles::PathSeparator + RegKey.Name;
|
||||
}
|
||||
|
||||
std::wstring EscapedPath { NewPath };
|
||||
GlobalStringSubstitute(EscapedPath, L"\n", L"\r\n");
|
||||
|
||||
{
|
||||
std::wostringstream KeySpecStream;
|
||||
KeySpecStream << Constants::RegFiles::KeyOpening << EscapedPath << Constants::RegFiles::KeyClosing << Constants::RegFiles::NewLines;
|
||||
|
||||
Result = WriteStringBufferToFile(OutFileHandle, KeySpecStream.str());
|
||||
if (FAILED(Result))
|
||||
{
|
||||
ReportError(E_HANDLE, L"Could not write to output file");
|
||||
return Result;
|
||||
}
|
||||
}
|
||||
|
||||
for (const RegistryValue &Value : RegKey.Values)
|
||||
{
|
||||
Result = RenderRegistryValue(OutFileHandle, Value);
|
||||
if (FAILED(Result))
|
||||
{
|
||||
ReportError(E_HANDLE, L"Could not render registry value" + Value.Name);
|
||||
return Result;
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
Result = WriteStringBufferToFile(OutFileHandle, Constants::RegFiles::NewLines );
|
||||
if (FAILED(Result))
|
||||
{
|
||||
ReportError(E_HANDLE, L"Could not write to output file");
|
||||
return Result;
|
||||
}
|
||||
}
|
||||
|
||||
for (const RegistryKey &Key : RegKey.Subkeys)
|
||||
{
|
||||
Result = RenderRegistryKey(OutFileHandle, Key, NewPath);
|
||||
if (FAILED(Result))
|
||||
{
|
||||
ReportError(E_HANDLE, L"Could not render registry key" + Key.Name);
|
||||
return Result;
|
||||
}
|
||||
}
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
// non-static function: documented in header.
|
||||
_Must_inspect_result_
|
||||
HRESULT InternalToRegfile
|
||||
(
|
||||
_In_ const RegistryKey& RegKey,
|
||||
_In_ const std::wstring& OutputFilePath
|
||||
)
|
||||
{
|
||||
HRESULT Result = E_FAIL;
|
||||
HANDLE OutFileHandle = INVALID_HANDLE_VALUE;
|
||||
|
||||
OutFileHandle = CreateFileW(OutputFilePath.c_str(), GENERIC_WRITE, FILE_SHARE_READ, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
|
||||
if (OutFileHandle == INVALID_HANDLE_VALUE)
|
||||
{
|
||||
Result = HRESULT_FROM_WIN32(GetLastError());
|
||||
ReportError(Result, L"Could not open file " + OutputFilePath + L" for writing");
|
||||
goto Cleanup;
|
||||
}
|
||||
|
||||
{
|
||||
Result = WriteStringBufferToFile(OutFileHandle, Constants::RegFiles::Preamble);
|
||||
if (FAILED(Result))
|
||||
{
|
||||
ReportError(Result, L"Could not write to output file");
|
||||
goto Cleanup;
|
||||
}
|
||||
}
|
||||
|
||||
Result = RenderRegistryKey(OutFileHandle, RegKey, std::wstring{});
|
||||
if (FAILED(Result))
|
||||
{
|
||||
ReportError(Result, L"Could not render registry key");
|
||||
goto Cleanup;
|
||||
}
|
||||
|
||||
Result = S_OK;
|
||||
|
||||
Cleanup:
|
||||
if (OutFileHandle != INVALID_HANDLE_VALUE)
|
||||
{
|
||||
CloseHandle(OutFileHandle);
|
||||
OutFileHandle = INVALID_HANDLE_VALUE;
|
||||
}
|
||||
|
||||
return Result;
|
||||
}
|
||||
+549
-549
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user