From d956eafda8b91384537fa4a167f2e9ebb600915f Mon Sep 17 00:00:00 2001 From: Stanislav Motylkov Date: Sun, 29 Jul 2018 23:50:15 +0300 Subject: [PATCH] [KERNEL32] Implement System Firmware functions - Implement EnumSystemFirmwareTables - Implement GetSystemFirmwareTable These functions currently using registry workaround and can be improved later. CORE-12105 --- dll/win32/kernel32/client/sysinfo.c | 282 +++++++++++++++++++++++++++- 1 file changed, 274 insertions(+), 8 deletions(-) diff --git a/dll/win32/kernel32/client/sysinfo.c b/dll/win32/kernel32/client/sysinfo.c index f57717a33d7..92474d050a7 100644 --- a/dll/win32/kernel32/client/sysinfo.c +++ b/dll/win32/kernel32/client/sysinfo.c @@ -7,6 +7,7 @@ * Christoph von Wittich * Thomas Weidenmueller * Gunnar Andre Dalsnes + * Stanislav Motylkov */ /* INCLUDES *******************************************************************/ @@ -74,6 +75,82 @@ GetSystemInfoInternal(IN PSYSTEM_BASIC_INFORMATION BasicInfo, } } +UINT +WINAPI +GetRawSMBiosTableFromRegistry(OUT PVOID pData) +{ + static const LPCWSTR RegistryKey = L"\\Registry\\Machine\\System\\CurrentControlSet\\Services\\mssmbios\\Data"; + static const LPCWSTR ValueNameStr = L"SMBiosData"; + + PKEY_VALUE_PARTIAL_INFORMATION KeyInfo = NULL; + OBJECT_ATTRIBUTES ObjectAttributes; + UNICODE_STRING KeyName; + UNICODE_STRING ValueName; + HANDLE KeyHandle; + ULONG KeyInfoSize; + ULONG ReturnSize = 0; + NTSTATUS Status; + + RtlInitUnicodeString(&KeyName, RegistryKey); + InitializeObjectAttributes(&ObjectAttributes, + &KeyName, + OBJ_CASE_INSENSITIVE, + NULL, + NULL); + + Status = NtOpenKey(&KeyHandle, + KEY_READ, + &ObjectAttributes); + if (!NT_SUCCESS(Status)) + { + return 0; + } + + // 256 KiB is more than enough for raw SMBIOS dump + KeyInfoSize = sizeof(KEY_VALUE_PARTIAL_INFORMATION) + 256 * 1024; + KeyInfo = RtlAllocateHeap(RtlGetProcessHeap(), 0, KeyInfoSize); + if (KeyInfo == NULL) + { + NtClose(KeyHandle); + goto cleanup; + } + + RtlInitUnicodeString(&ValueName, ValueNameStr); + + Status = NtQueryValueKey(KeyHandle, + &ValueName, + KeyValuePartialInformation, + KeyInfo, + KeyInfoSize, + &ReturnSize); + + NtClose(KeyHandle); + ReturnSize = 0; + + if (!NT_SUCCESS(Status)) + { + goto cleanup; + } + + if (KeyInfo->Type != REG_BINARY) + { + goto cleanup; + } + + ReturnSize = KeyInfo->DataLength; + if (pData) + { + RtlCopyMemory(pData, KeyInfo->Data, KeyInfo->DataLength); + } + +cleanup: + if (KeyInfo) + { + RtlFreeHeap(RtlGetProcessHeap(), 0, KeyInfo); + } + return ReturnSize; +} + /* PUBLIC FUNCTIONS ***********************************************************/ /* @@ -423,8 +500,32 @@ SetFirmwareEnvironmentVariableA(IN LPCSTR lpName, return 0; } -/* - * @unimplemented +/** + * @name EnumSystemFirmwareTables + * @implemented + * + * Obtains firmware table identifiers. + * https://msdn.microsoft.com/en-us/library/windows/desktop/ms724259(v=vs.85).aspx + * + * @param FirmwareTableProviderSignature + * Can be either ACPI, FIRM, or RSMB. + * + * @param pFirmwareTableBuffer + * Pointer to the output buffer, can be NULL. + * + * @param BufferSize + * Size of the output buffer. + * + * @return + * Actual size of the data in case of success, 0 otherwise. + * + * @remarks + * Data would be written to buffer only if the specified size is + * larger or equal to the actual size, in the other case Last Error + * value would be set to ERROR_INSUFFICIENT_BUFFER. + * In case of incorrect provider signature, Last Error value would be + * set to ERROR_INVALID_FUNCTION. + * */ UINT WINAPI @@ -432,12 +533,104 @@ EnumSystemFirmwareTables(IN DWORD FirmwareTableProviderSignature, OUT PVOID pFirmwareTableBuffer, IN DWORD BufferSize) { - STUB; - return 0; + UINT uSize = 0; + UINT uCount = 0; + DWORD dwError = ERROR_SUCCESS; + PDWORD pBuffer = NULL; + + switch (FirmwareTableProviderSignature) + { + case 'ACPI': + { + /* FIXME: Not implemented yet */ + dwError = ERROR_CALL_NOT_IMPLEMENTED; + break; + } + case 'FIRM': + { + /* FIXME: Not implemented yet */ + dwError = ERROR_CALL_NOT_IMPLEMENTED; + break; + } + case 'RSMB': + { + if (GetRawSMBiosTableFromRegistry(NULL) > 0) + { + uCount = 1; + uSize = uCount * sizeof(DWORD); + pBuffer = RtlAllocateHeap(RtlGetProcessHeap(), 0, uSize); + if (!pBuffer) + { + SetLastError(ERROR_OUTOFMEMORY); + return 0; + } + *pBuffer = 0; + } + break; + } + default: + { + dwError = ERROR_INVALID_FUNCTION; + } + } + + if (dwError == ERROR_SUCCESS) + { + if (uSize > 0 && BufferSize >= uSize) + { + /* Write to buffer */ + if (pFirmwareTableBuffer) + { + RtlMoveMemory(pFirmwareTableBuffer, pBuffer, uSize); + } + } + else if (BufferSize < uSize) + { + dwError = ERROR_INSUFFICIENT_BUFFER; + } + } + + if (pBuffer) + { + RtlFreeHeap(RtlGetProcessHeap(), 0, pBuffer); + } + SetLastError(dwError); + return uSize; } -/* - * @unimplemented +/** + * @name GetSystemFirmwareTable + * @implemented + * + * Obtains the firmware table data. + * https://msdn.microsoft.com/en-us/library/windows/desktop/ms724379(v=vs.85).aspx + * + * @param FirmwareTableProviderSignature + * Can be either ACPI, FIRM, or RSMB. + * + * @param FirmwareTableID + * Correct table identifier. + * + * @param pFirmwareTableBuffer + * Pointer to the output buffer, can be NULL. + * + * @param BufferSize + * Size of the output buffer. + * + * @return + * Actual size of the data in case of success, 0 otherwise. + * + * @remarks + * Data would be written to buffer only if the specified size is + * larger or equal to the actual size, in the other case Last Error + * value would be set to ERROR_INSUFFICIENT_BUFFER. + * In case of incorrect provider signature, Last Error value would be + * set to ERROR_INVALID_FUNCTION. + * Also Last Error value becomes ERROR_NOT_FOUND if incorrect + * table identifier was specified along with ACPI provider, and + * ERROR_INVALID_PARAMETER along with FIRM provider. The RSMB provider + * accepts any table identifier. + * */ UINT WINAPI @@ -446,8 +639,81 @@ GetSystemFirmwareTable(IN DWORD FirmwareTableProviderSignature, OUT PVOID pFirmwareTableBuffer, IN DWORD BufferSize) { - STUB; - return 0; + /* This function currently obtains data using a hack (registry workaround) + which is located here: drivers/input/i8042prt/hwhacks.c + i8042StoreSMBiosTables is responsible for writing SMBIOS table into registry + + Should be implemented correctly using NtQuerySystemInformation + along with SystemFirmwareTableInformation class + + Reference: https://github.com/hfiref0x/VMDE/blob/master/src/vmde/sup.c + */ + + UINT uSize = 0; + DWORD dwError = ERROR_SUCCESS; + PVOID pBuffer = NULL; + + switch (FirmwareTableProviderSignature) + { + case 'ACPI': + { + /* FIXME: Not implemented yet */ + dwError = ERROR_CALL_NOT_IMPLEMENTED; + break; + } + case 'FIRM': + { + /* FIXME: Not implemented yet */ + dwError = ERROR_CALL_NOT_IMPLEMENTED; + break; + } + case 'RSMB': + { + uSize = GetRawSMBiosTableFromRegistry(NULL); + if (uSize > 0) + { + pBuffer = RtlAllocateHeap(RtlGetProcessHeap(), 0, uSize); + if (!pBuffer) + { + SetLastError(ERROR_OUTOFMEMORY); + return 0; + } + GetRawSMBiosTableFromRegistry(pBuffer); + } + else + { + dwError = ERROR_NOT_FOUND; + } + break; + } + default: + { + dwError = ERROR_INVALID_FUNCTION; + } + } + + if (dwError == ERROR_SUCCESS) + { + if (uSize > 0 && BufferSize >= uSize) + { + /* Write to buffer */ + if (pFirmwareTableBuffer) + { + RtlMoveMemory(pFirmwareTableBuffer, pBuffer, uSize); + } + } + else if (BufferSize < uSize) + { + dwError = ERROR_INSUFFICIENT_BUFFER; + } + } + + if (pBuffer) + { + RtlFreeHeap(RtlGetProcessHeap(), 0, pBuffer); + } + SetLastError(dwError); + return uSize; } /*