1
0
mirror of https://github.com/veracrypt/VeraCrypt.git synced 2025-11-11 02:58:02 -06:00

Windows: Revert use PDH API to gather system entropy because of issues encountered by users

cf thread: https://sourceforge.net/p/veracrypt/discussion/general/thread/293d401a30

delays and sporadic crashes in some cases.
This commit is contained in:
Mounir IDRASSI
2025-02-02 13:51:33 +01:00
parent 498dff9013
commit 04e7d8c5ee

View File

@@ -20,8 +20,6 @@
#include "Crypto\rdrand.h"
#include <Strsafe.h>
#include <bcrypt.h>
#include <pdh.h>
#include <pdhmsg.h>
static unsigned __int8 buffer[RNG_POOL_SIZE];
static unsigned char *pRandPool = NULL;
@@ -84,45 +82,15 @@ DWORD ProcessedMouseEventsCounter = 0;
CRITICAL_SECTION critRandProt; /* The critical section */
BOOL volatile bThreadTerminate = FALSE; /* This variable is shared among thread's so its made volatile */
/* Network library handle for the SlowPoll function */
HANDLE hNetAPI32 = NULL;
// CryptoAPI
DWORD CryptoAPILastError = ERROR_SUCCESS;
typedef DWORD (WINAPI *RtlNtStatusToDosError_t)(NTSTATUS);
RtlNtStatusToDosError_t pRtlNtStatusToDosError = NULL;
static HMODULE hPdhLib = NULL;
typedef PDH_STATUS (WINAPI *PfnPdhOpenQueryW)(LPCWSTR, DWORD_PTR, PDH_HQUERY *);
typedef PDH_STATUS (WINAPI *PfnPdhAddCounterW)(PDH_HQUERY, LPCWSTR, DWORD_PTR, PDH_HCOUNTER *);
typedef PDH_STATUS (WINAPI *PfnPdhCollectQueryData)(PDH_HQUERY);
typedef PDH_STATUS (WINAPI *PfnPdhGetFormattedCounterValue)(PDH_HCOUNTER, DWORD, LPDWORD, PPDH_FMT_COUNTERVALUE);
typedef PDH_STATUS (WINAPI *PfnPdhCloseQuery)(PDH_HQUERY);
static PfnPdhOpenQueryW pfnPdhOpenQuery = NULL;
static PfnPdhAddCounterW pfnPdhAddCounter = NULL;
static PfnPdhCollectQueryData pfnPdhCollectQueryData = NULL;
static PfnPdhGetFormattedCounterValue pfnPdhGetFormattedCounterValue = NULL;
static PfnPdhCloseQuery pfnPdhCloseQuery = NULL;
static BOOL LoadPdhDll()
{
if (!hPdhLib)
{
hPdhLib = LoadLibraryExW(L"pdh.dll", NULL, LOAD_LIBRARY_SEARCH_SYSTEM32);
if (!hPdhLib)
return FALSE;
pfnPdhOpenQuery = (PfnPdhOpenQueryW) GetProcAddress(hPdhLib, "PdhOpenQueryW");
pfnPdhAddCounter = (PfnPdhAddCounterW) GetProcAddress(hPdhLib, "PdhAddCounterW");
pfnPdhCollectQueryData = (PfnPdhCollectQueryData) GetProcAddress(hPdhLib, "PdhCollectQueryData");
pfnPdhGetFormattedCounterValue = (PfnPdhGetFormattedCounterValue) GetProcAddress(hPdhLib, "PdhGetFormattedCounterValue");
pfnPdhCloseQuery = (PfnPdhCloseQuery) GetProcAddress(hPdhLib, "PdhCloseQuery");
}
return (pfnPdhOpenQuery && pfnPdhAddCounter && pfnPdhCollectQueryData &&
pfnPdhGetFormattedCounterValue && pfnPdhCloseQuery);
}
/* Init the random number generator, setup the hooks, and start the thread */
int RandinitWithCheck ( int* pAlreadyInitialized)
{
@@ -223,6 +191,12 @@ void RandStop (BOOL freePool)
if (PeriodicFastPollThreadHandle)
WaitForSingleObject (PeriodicFastPollThreadHandle, INFINITE);
if (hNetAPI32 != 0)
{
FreeLibrary (hNetAPI32);
hNetAPI32 = NULL;
}
hMouse = NULL;
hKeyboard = NULL;
@@ -666,192 +640,142 @@ static unsigned __stdcall PeriodicFastPollThreadProc (void *dummy)
}
}
/* Type definitions for function pointers to call NetAPI32 functions */
/* -------------------------------------------------------------------------------------
GetDiskStatistics: This function uses the Windows Performance Data Helper (PDH) API
to collect disk statistics. The function collects the number of disk reads and writes
per second for all physical disks. The function also collects high-resolution
timestamps before and after the PDH query. The function then adds the collected data
to the random pool.
The code waits a short random interval between the two PDH samples to ensures that
the performance counters have time to accumulate measurable changes and produce more
varied data.
-------------------------------------------------------------------------------------
typedef
DWORD (WINAPI * NETSTATISTICSGET) (LPWSTR szServer, LPWSTR szService,
DWORD dwLevel, DWORD dwOptions,
LPBYTE * lpBuffer);
typedef
DWORD (WINAPI * NETAPIBUFFERSIZE) (LPVOID lpBuffer, LPDWORD cbBuffer);
typedef
DWORD (WINAPI * NETAPIBUFFERFREE) (LPVOID lpBuffer);
*/
void GetDiskStatistics()
{
if (!LoadPdhDll())
return;
PDH_STATUS status;
PDH_HQUERY query = NULL;
PDH_HCOUNTER counterReads = NULL, counterWrites = NULL;
PDH_FMT_COUNTERVALUE counterValue;
DWORD dwType;
LONGLONG llReads = 0, llWrites = 0;
DWORDLONG tstampBefore = 0, tstampAfter = 0;
LARGE_INTEGER perfCounterBefore, perfCounterAfter;
NETSTATISTICSGET pNetStatisticsGet = NULL;
NETAPIBUFFERSIZE pNetApiBufferSize = NULL;
NETAPIBUFFERFREE pNetApiBufferFree = NULL;
// High-resolution timestamps
if (!QueryPerformanceCounter(&perfCounterBefore))
return;
tstampBefore = GetTickCount64();
// Open PDH query
status = pfnPdhOpenQuery(NULL, 0, &query);
if (status != ERROR_SUCCESS)
goto error;
// Add counters for disk reads and writes (all physical disks).
status = pfnPdhAddCounter(query, L"\\PhysicalDisk(*)\\Disk Reads/sec", 0, &counterReads);
if (status != ERROR_SUCCESS)
goto error;
status = pfnPdhAddCounter(query, L"\\PhysicalDisk(*)\\Disk Writes/sec", 0, &counterWrites);
if (status != ERROR_SUCCESS)
goto error;
// First sample
status = pfnPdhCollectQueryData(query);
if (status != ERROR_SUCCESS)
goto error;
// Wait a short random interval
Sleep(10 + (GetCurrentProcessId() % 40));
// Second sample
status = pfnPdhCollectQueryData(query);
if (status != ERROR_SUCCESS)
goto error;
// Format counters in PDH_FMT_LARGE
status = pfnPdhGetFormattedCounterValue(counterReads, PDH_FMT_LARGE, &dwType, &counterValue);
if (status == ERROR_SUCCESS)
llReads = counterValue.largeValue;
status = pfnPdhGetFormattedCounterValue(counterWrites, PDH_FMT_LARGE, &dwType, &counterValue);
if (status == ERROR_SUCCESS)
llWrites = counterValue.largeValue;
// Another high-resolution timestamp
if (!QueryPerformanceCounter(&perfCounterAfter))
goto error;
tstampAfter = GetTickCount64();
// Close PDH query
pfnPdhCloseQuery(query);
query = NULL;
// Collect results into the random pool
RandaddBuf(&llReads, sizeof(llReads));
RandaddBuf(&llWrites, sizeof(llWrites));
RandaddBuf(&tstampBefore, sizeof(tstampBefore));
RandaddBuf(&tstampAfter, sizeof(tstampAfter));
RandaddBuf(&perfCounterBefore.QuadPart, sizeof(perfCounterBefore.QuadPart));
RandaddBuf(&perfCounterAfter.QuadPart, sizeof(perfCounterAfter.QuadPart));
error:
if (query)
pfnPdhCloseQuery(query);
}
/* -------------------------------------------------------------------------------------
GetNetworkStatistics: This function uses the Windows Performance Data Helper (PDH) API
to collect network statistics. The function collects the number of bytes sent and
received per second for all network interfaces. The function also collects
high-resolution timestamps before and after the PDH query. The function then adds the
collected data to the random pool.
The code waits a short random interval between the two PDH samples to ensures that
the performance counters have time to accumulate measurable changes and produce more
varied data.
*/
void GetNetworkStatistics()
{
if (!LoadPdhDll())
return;
PDH_STATUS status;
PDH_HQUERY query = NULL;
PDH_HCOUNTER counterBytesSent = NULL, counterBytesReceived = NULL;
PDH_FMT_COUNTERVALUE counterValue;
DWORD dwType;
LONGLONG llBytesSent = 0, llBytesReceived = 0;
DWORDLONG tstampBefore = 0, tstampAfter = 0;
LARGE_INTEGER perfCounterBefore, perfCounterAfter;
// High-resolution timestamps
if (!QueryPerformanceCounter(&perfCounterBefore))
return;
tstampBefore = GetTickCount64();
// Open PDH query
status = pfnPdhOpenQuery(NULL, 0, &query);
if (status != ERROR_SUCCESS)
goto error;
// Add counters for network bytes sent and received
status = pfnPdhAddCounter(query, L"\\Network Interface(*)\\Bytes Sent/sec", 0, &counterBytesSent);
if (status != ERROR_SUCCESS)
goto error;
status = pfnPdhAddCounter(query, L"\\Network Interface(*)\\Bytes Received/sec", 0, &counterBytesReceived);
if (status != ERROR_SUCCESS)
goto error;
// First sample
status = pfnPdhCollectQueryData(query);
if (status != ERROR_SUCCESS)
goto error;
// Wait short, dynamic interval
Sleep(10 + (GetCurrentProcessId() % 40));
// Second sample
status = pfnPdhCollectQueryData(query);
if (status != ERROR_SUCCESS)
goto error;
// Format counters
status = pfnPdhGetFormattedCounterValue(counterBytesSent, PDH_FMT_LARGE, &dwType, &counterValue);
if (status == ERROR_SUCCESS)
llBytesSent = counterValue.largeValue;
status = pfnPdhGetFormattedCounterValue(counterBytesReceived, PDH_FMT_LARGE, &dwType, &counterValue);
if (status == ERROR_SUCCESS)
llBytesReceived = counterValue.largeValue;
if (!QueryPerformanceCounter(&perfCounterAfter))
goto error;
tstampAfter = GetTickCount64();
// Close PDH query
pfnPdhCloseQuery(query);
query = NULL;
// Collect results into our random pool
RandaddBuf(&llBytesSent, sizeof(llBytesSent));
RandaddBuf(&llBytesReceived, sizeof(llBytesReceived));
RandaddBuf(&tstampBefore, sizeof(tstampBefore));
RandaddBuf(&tstampAfter, sizeof(tstampAfter));
RandaddBuf(&perfCounterBefore.QuadPart, sizeof(perfCounterBefore.QuadPart));
RandaddBuf(&perfCounterAfter.QuadPart, sizeof(perfCounterAfter.QuadPart));
error:
if (query)
pfnPdhCloseQuery(query);
}
/* This is the slowpoll function which gathers up network/hard drive
performance data for the random pool */
BOOL SlowPoll (void)
{
static int isWorkstation = -1;
static int cbPerfData = 0x10000;
HANDLE hDevice;
LPBYTE lpBuffer;
DWORD dwSize, status;
LPWSTR lpszLanW, lpszLanS;
int nDrive;
NTSTATUS bStatus = 0;
// Gather disk stats via PDH
GetDiskStatistics();
/* Find out whether this is an NT server or workstation if necessary */
if (isWorkstation == -1)
{
HKEY hKey;
if (RegOpenKeyEx (HKEY_LOCAL_MACHINE,
L"SYSTEM\\CurrentControlSet\\Control\\ProductOptions",
0, KEY_READ, &hKey) == ERROR_SUCCESS)
{
wchar_t szValue[32];
dwSize = sizeof (szValue);
isWorkstation = TRUE;
status = RegQueryValueEx (hKey, L"ProductType", 0, NULL,
(LPBYTE) szValue, &dwSize);
if (status == ERROR_SUCCESS && _wcsicmp (szValue, L"WinNT"))
/* Note: There are (at least) three cases for
ProductType: WinNT = NT Workstation,
ServerNT = NT Server, LanmanNT = NT Server
acting as a Domain Controller */
isWorkstation = FALSE;
RegCloseKey (hKey);
}
}
/* Initialize the NetAPI32 function pointers if necessary */
if (hNetAPI32 == NULL)
{
/* Obtain a handle to the module containing the Lan Manager
functions */
wchar_t dllPath[MAX_PATH];
if (GetSystemDirectory (dllPath, MAX_PATH))
{
StringCchCatW(dllPath, ARRAYSIZE(dllPath), L"\\NETAPI32.DLL");
}
else
StringCchCopyW(dllPath, ARRAYSIZE(dllPath), L"C:\\Windows\\System32\\NETAPI32.DLL");
hNetAPI32 = LoadLibrary (dllPath);
if (hNetAPI32 != NULL)
{
/* Now get pointers to the functions */
pNetStatisticsGet = (NETSTATISTICSGET) GetProcAddress (hNetAPI32,
"NetStatisticsGet");
pNetApiBufferSize = (NETAPIBUFFERSIZE) GetProcAddress (hNetAPI32,
"NetApiBufferSize");
pNetApiBufferFree = (NETAPIBUFFERFREE) GetProcAddress (hNetAPI32,
"NetApiBufferFree");
/* Make sure we got valid pointers for every NetAPI32
function */
if (pNetStatisticsGet == NULL ||
pNetApiBufferSize == NULL ||
pNetApiBufferFree == NULL)
{
/* Free the library reference and reset the
static handle */
FreeLibrary (hNetAPI32);
hNetAPI32 = NULL;
}
}
}
/* Get network statistics. Note: Both NT Workstation and NT Server
by default will be running both the workstation and server
services. The heuristic below is probably useful though on the
assumption that the majority of the network traffic will be via
the appropriate service */
lpszLanW = (LPWSTR) WIDE ("LanmanWorkstation");
lpszLanS = (LPWSTR) WIDE ("LanmanServer");
if (hNetAPI32 &&
pNetStatisticsGet (NULL,
isWorkstation ? lpszLanW : lpszLanS,
0, 0, &lpBuffer) == 0)
{
pNetApiBufferSize (lpBuffer, &dwSize);
RandaddBuf ((unsigned char *) lpBuffer, dwSize);
pNetApiBufferFree (lpBuffer);
}
/* Get disk I/O statistics for all the hard drives */
for (nDrive = 0;; nDrive++)
{
DISK_PERFORMANCE diskPerformance;
wchar_t szDevice[24];
/* Check whether we can access this device */
StringCchPrintfW (szDevice, ARRAYSIZE(szDevice), L"\\\\.\\PhysicalDrive%d", nDrive);
hDevice = CreateFile (szDevice, 0, FILE_SHARE_READ | FILE_SHARE_WRITE,
NULL, OPEN_EXISTING, 0, NULL);
if (hDevice == INVALID_HANDLE_VALUE)
break;
/* Note: This only works if you have turned on the disk
performance counters with 'diskperf -y'. These counters
are off by default */
if (DeviceIoControl (hDevice, IOCTL_DISK_PERFORMANCE, NULL, 0,
&diskPerformance, sizeof (DISK_PERFORMANCE),
&dwSize, NULL))
{
RandaddBuf ((unsigned char *) &diskPerformance, dwSize);
}
CloseHandle (hDevice);
}
// Gather network stats via PDH
GetNetworkStatistics();
bStatus = BCryptGenRandom(NULL, buffer, sizeof(buffer), BCRYPT_USE_SYSTEM_PREFERRED_RNG);
if (NT_SUCCESS(bStatus))
@@ -890,8 +814,6 @@ BOOL SlowPoll (void)
#endif
burn(buffer, sizeof (buffer));
/* Mix the pool */
Randmix();
return TRUE;