📄 win32.c
字号:
pSetupDiEnumDeviceInfo = ( SETUPDIENUMDEVICEINFO ) \
GetProcAddress( hSetupAPI, "SetupDiEnumDeviceInfo" );
pSetupDiGetClassDevs = ( SETUPDIGETCLASSDEVS ) \
GetProcAddress( hSetupAPI, "SetupDiGetClassDevsA" );
pSetupDiGetDeviceRegistryProperty = ( SETUPDIGETDEVICEREGISTRYPROPERTY ) \
GetProcAddress( hSetupAPI, "SetupDiGetDeviceRegistryPropertyA" );
if( pSetupDiDestroyDeviceInfoList == NULL || \
pSetupDiEnumDeviceInfo == NULL || pSetupDiGetClassDevs == NULL || \
pSetupDiGetDeviceRegistryProperty == NULL )
{
FreeLibrary( hSetupAPI );
return;
}
/* Get info on all PnP devices */
hDevInfo = pSetupDiGetClassDevs( NULL, NULL, NULL,
DIGCF_PRESENT | DIGCF_ALLCLASSES );
if( hDevInfo != INVALID_HANDLE_VALUE )
{
SP_DEVINFO_DATA devInfoData;
RANDOM_STATE randomState;
BYTE buffer[ RANDOM_BUFSIZE + 8 ];
BYTE pnpBuffer[ 512 + 8 ];
DWORD cbPnPBuffer;
int deviceCount, iterationCount, status;
/* Enumerate all PnP devices */
status = initRandomData( randomState, buffer, RANDOM_BUFSIZE );
if( cryptStatusError( status ) )
retIntError_Void();
memset( &devInfoData, 0, sizeof( devInfoData ) );
devInfoData.cbSize = sizeof( SP_DEVINFO_DATA );
for( deviceCount = 0, iterationCount = 0;
pSetupDiEnumDeviceInfo( hDevInfo, deviceCount,
&devInfoData ) && \
iterationCount < FAILSAFE_ITERATIONS_MAX;
deviceCount++, iterationCount++ )
{
if( pSetupDiGetDeviceRegistryProperty( hDevInfo, &devInfoData,
SPDRP_HARDWAREID, NULL,
pnpBuffer, 512, &cbPnPBuffer ) )
addRandomData( randomState, pnpBuffer, cbPnPBuffer );
}
ENSURES_V( iterationCount < FAILSAFE_ITERATIONS_MAX );
pSetupDiDestroyDeviceInfoList( hDevInfo );
endRandomData( randomState, 5 );
}
FreeLibrary( hSetupAPI );
}
/****************************************************************************
* *
* Fast Poll *
* *
****************************************************************************/
/* The shared Win32 fast poll routine */
void fastPoll( void )
{
static BOOLEAN addedFixedItems = FALSE;
FILETIME creationTime, exitTime, kernelTime, userTime;
DWORD minimumWorkingSetSize, maximumWorkingSetSize;
LARGE_INTEGER performanceCount;
MEMORYSTATUS memoryStatus;
HANDLE handle;
POINT point;
RANDOM_STATE randomState;
BYTE buffer[ RANDOM_BUFSIZE + 8 ];
int status;
if( krnlIsExiting() )
return;
status = initRandomData( randomState, buffer, RANDOM_BUFSIZE );
if( cryptStatusError( status ) )
retIntError_Void();
/* Get various basic pieces of system information: Handle of active
window, handle of window with mouse capture, handle of clipboard owner
handle of start of clpboard viewer list, pseudohandle of current
process, current process ID, pseudohandle of current thread, current
thread ID, handle of desktop window, handle of window with keyboard
focus, whether system queue has any events, cursor position for last
message, 1 ms time for last message, handle of window with clipboard
open, handle of process heap, handle of procs window station, types of
events in input queue, and milliseconds since Windows was started.
Since a HWND/HANDLE can be a 64-bit value on a 64-bit platform, we
have to use a mapping macro that discards the high 32 bits (which
presumably won't be of much interest anyway) */
addRandomHandle( randomState, GetActiveWindow() );
addRandomHandle( randomState, GetCapture() );
addRandomHandle( randomState, GetClipboardOwner() );
addRandomHandle( randomState, GetClipboardViewer() );
addRandomHandle( randomState, GetCurrentProcess() );
addRandomValue( randomState, GetCurrentProcessId() );
addRandomHandle( randomState, GetCurrentThread() );
addRandomValue( randomState, GetCurrentThreadId() );
addRandomHandle( randomState, GetDesktopWindow() );
addRandomHandle( randomState, GetFocus() );
addRandomValue( randomState, GetInputState() );
addRandomValue( randomState, GetMessagePos() );
addRandomValue( randomState, GetMessageTime() );
addRandomHandle( randomState, GetOpenClipboardWindow() );
addRandomHandle( randomState, GetProcessHeap() );
addRandomHandle( randomState, GetProcessWindowStation() );
addRandomValue( randomState, GetTickCount() );
if( krnlIsExiting() )
return;
/* Calling the following function can cause problems in some cases in
that a calling application eventually stops getting events from its
event loop, so we can't (safely) use it as an entropy source */
/* addRandomValue( randomState, GetQueueStatus( QS_ALLEVENTS ) ); */
/* Get multiword system information: Current caret position, current
mouse cursor position */
GetCaretPos( &point );
addRandomData( randomState, &point, sizeof( POINT ) );
GetCursorPos( &point );
addRandomData( randomState, &point, sizeof( POINT ) );
/* Get percent of memory in use, bytes of physical memory, bytes of free
physical memory, bytes in paging file, free bytes in paging file, user
bytes of address space, and free user bytes */
memoryStatus.dwLength = sizeof( MEMORYSTATUS );
GlobalMemoryStatus( &memoryStatus );
addRandomData( randomState, &memoryStatus, sizeof( MEMORYSTATUS ) );
/* Get thread and process creation time, exit time, time in kernel mode,
and time in user mode in 100ns intervals */
handle = GetCurrentThread();
GetThreadTimes( handle, &creationTime, &exitTime, &kernelTime, &userTime );
addRandomData( randomState, &creationTime, sizeof( FILETIME ) );
addRandomData( randomState, &exitTime, sizeof( FILETIME ) );
addRandomData( randomState, &kernelTime, sizeof( FILETIME ) );
addRandomData( randomState, &userTime, sizeof( FILETIME ) );
handle = GetCurrentProcess();
GetProcessTimes( handle, &creationTime, &exitTime, &kernelTime, &userTime );
addRandomData( randomState, &creationTime, sizeof( FILETIME ) );
addRandomData( randomState, &exitTime, sizeof( FILETIME ) );
addRandomData( randomState, &kernelTime, sizeof( FILETIME ) );
addRandomData( randomState, &userTime, sizeof( FILETIME ) );
/* Get the minimum and maximum working set size for the current process */
GetProcessWorkingSetSize( handle, &minimumWorkingSetSize,
&maximumWorkingSetSize );
addRandomValue( randomState, minimumWorkingSetSize );
addRandomValue( randomState, maximumWorkingSetSize );
/* The following are fixed for the lifetime of the process so we only
add them once */
if( !addedFixedItems )
{
STARTUPINFO startupInfo;
/* Get name of desktop, console window title, new window position and
size, window flags, and handles for stdin, stdout, and stderr */
startupInfo.cb = sizeof( STARTUPINFO );
GetStartupInfo( &startupInfo );
addRandomData( randomState, &startupInfo, sizeof( STARTUPINFO ) );
/* Remember that we've got the fixed info */
addedFixedItems = TRUE;
}
/* The performance of QPC varies depending on the architecture it's
running on and on the OS, the MS documentation is vague about the
details because it varies so much. Under Win9x/ME it reads the
1.193180 MHz PIC timer. Under NT/Win2K/XP it may or may not read the
64-bit TSC depending on the HAL and assorted other circumstances,
generally on machines with a uniprocessor HAL
KeQueryPerformanceCounter() uses a 3.579545MHz timer and on machines
with a multiprocessor or APIC HAL it uses the TSC (the exact time
source is controlled by the HalpUse8254 flag in the kernel). Since
Vista-era machines are generally running on multiprocessor HALs we
usually get the TSC under Vista.
This choice of time sources is somewhat peculiar because on a
multiprocessor machine it's theoretically possible to get completely
different TSC readings depending on which CPU you're currently
running on, while for uniprocessor machines it's not a problem.
However the kernel synchronises the TSCs across CPUs at boot time if
a multiprocessor HAL is used (it resets the TSC as part of its system
init) so this shouldn't really be a problem. Under WinCE it's
completely platform-dependant, if there's no hardware performance
counter available it uses the 1ms system timer.
Another feature of the TSC (although it doesn't really affect us
here) is that mobile CPUs will turn off the TSC when they idle,
newer Pentiums and Athlons with advanced power management/clock
throttling will change the rate of the counter when they clock-
throttle (to match the current CPU speed), and hyperthreading
Pentiums will turn it off when both threads are idle (this more or
less makes sense since the CPU will be in the halted state and not
executing any instructions to count). What makes this even more
exciting is that the resulting handling of the TSC is almost entirely
nondeterministic, the only thing that's guaranteed is that the
counter is monotonically incrementing. For example when a newer
processor with power-saving features enabled ramps down its core
clock the TSC rate is lowered to correspond to the clock rate
(assuming that the BIOS sets this up correctly, which isn't always
the case). However various events like cache probes can cause the
core to temporarily return to the original rate to process the
event before dropping back to the throttled rate, which can cause
TSC drift across multiple cores. Hardware-specific events like
AMDs STPCLK signalling ("shut 'er down ma, she's glowing red") can
reach different cores at different times and lead to ramping up
and down and different times and for different durations, leading
to further drift. Newer AMD CPUs fix this by providing drift-
invariant TSCs if the TscInvariant CPUID feature flag is set.
As a result, if we're on a system with multiple cores and it doesn't
have the AMD TscInvariant fix then the TSC skew can also be a source
of entropy. To some extent this is just timing the context switch
time across CPUs (which in itself is a source of some entropy), but
what's left is the TSC skew across cores. If this is if interest we
can do it with code something like the following. Note that this is
only sample code, there are various complications such as the fact
that another thread may change the process affinity mask between the
call to the initial GetProcessAffinityMask() and the final
SetThreadAffinityMask() to restore the original mask, even if we
insert another GetProcessAffinityMask() just before the
SetThreadAffinityMask() there's still a small race condition present
but no real way to avoid it without OS API-level support:
DWORD processAfinityMask, systemAffinityMask, mask = 0x10000;
// Get the available processors that the thread can be scheduled on
if( !GetProcessAffinityMask( GetCurrentProcess(), processAfinityMask,
systemAffinityMask ) )
return;
// Move the thread to each processor and read the TSC
while( mask != 0 )
{
mask >>= 1;
if( !( mask & processAfinityMask ) )
continue;
SetThreadAffinityMask( GetCurrentThread(), mask );
// RDTSC
}
// Restore the original thread affinity
SetThreadAffinityMask( GetCurrentThread(), processAfinityMask );
Finally, there's the HPET, but this is only supported in Vista and
it's not clear how to access it from user-level APIs rather than as
part of the multimedia subsystem.
To make things unambiguous, we call RDTSC directly if possible and
fall back to QPC in the highly unlikely situation where this isn't
present */
#ifndef NO_ASM
if( getSysVar( SYSVAR_HWCAP ) & HWCAP_FLAG_RDTSC )
{
unsigned long value;
__asm {
xor eax, eax
xor edx, edx /* Tell VC++ that EDX:EAX will be trashed */
rdtsc
mov [value], eax /* Ignore high 32 bits, which are > 1s res */
}
addRandomValue( randomState, value );
}
#endif /* NO_ASM */
else
{
if( QueryPerformanceCounter( &performanceCount ) )
addRandomData( randomState, &performanceCount,
sizeof( LARGE_INTEGER ) );
else
{
/* Millisecond accuracy at best... */
addRandomValue( randomState, GetTickCount() );
}
}
/* If there's a hardware RNG present, read data from it. We check that
the RNG is still present/enabled on each fetch since it could (at
least in theory) be disabled by the OS between fetches. We also read
the data into an explicitly dword-aligned buffer (which the standard
buffer should be anyway, but we make it explicit here just to be
safe). Note that we have to force alignment using a LONGLONG rather
than a #pragma pack, since chars don't need alignment it would have
no effect on the BYTE [] member */
#ifndef NO_ASM
if( getSysVar( SYSVAR_HWCAP ) & HWCAP_FLAG_XSTORE )
{
struct alignStruct {
LONGLONG dummy1; /* Force alignment of following member */
BYTE buffer[ 64 ];
};
struct alignStruct *rngBuffer = ( struct alignStruct * ) buffer;
void *bufPtr = rngBuffer->buffer; /* Get it into a form asm can handle */
int byteCount = 0;
__asm {
push es
xor ecx, ecx /* Tell VC++ that ECX will be trashed */
mov eax, 0xC0000001 /* Centaur extended feature flags */
cpuid
and edx, 01100b
cmp edx, 01100b /* Check for RNG present + enabled flags */
jne rngDisabled /* RNG was disabled after our initial check */
push ds
pop es
mov edi, bufPtr /* ES:EDI = buffer */
xor edx, edx /* Fetch 8 bytes */
xstore_rng
and eax, 011111b /* Get count of bytes returned */
jz rngDisabled /* Nothing read, exit */
mov [byteCount], eax
rngDisabled:
pop es
}
if( byteCount > 0 )
addRandomData( randomState, bufPtr, byteCount );
}
if( getSysVar( SYSVAR_HWCAP ) & HWCAP_FLAG_TRNG )
{
unsigned long value = 0;
__asm {
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -