win32.c

来自「cryptlib是功能强大的安全工具集。允许开发人员快速在自己的软件中集成加密和」· C语言 代码 · 共 1,332 行 · 第 1/4 页

C
1,332
字号

	/* Take a snapshot of everything we can get to that's currently in the
	   system */
	hSnapshot = pCreateToolhelp32Snapshot( TH32CS_SNAPALL, 0 );
	if( !hSnapshot )
		return;

	/* Walk through the local heap.  We have to be careful to not spend
	   excessive amounts of time on this if we're linked into a large
	   application with a great many heaps and/or heap blocks, since the
	   heap-traversal functions are rather slow.  Fortunately this is
	   quite rare under Win95/98, since it implies a large/long-running
	   server app that would be run under NT/Win2K/XP rather than Win95
	   (the performance of the mapped ToolHelp32 helper functions under
	   these OSes is even worse than under Win95, fortunately we don't
	   have to use them there).

	   Ideally in order to prevent excessive delays we'd count the number
	   of heaps and ensure that no_heaps * no_heap_blocks doesn't exceed
	   some maximum value, however this requires two passes of (slow) heap
	   traversal rather than one, which doesn't help the situation much.
	   To provide at least some protection, we limit the total number of
	   heaps and heap entries traversed, although this leads to slightly
	   suboptimal performance if we have a small number of deep heaps
	   rather than the current large number of shallow heaps.

	   There is however a second consideration that needs to be taken into
	   account when doing this, which is that the heap-management functions
	   aren't completely thread-safe, so that under (very rare) conditions
	   of heavy allocation/deallocation this can cause problems when calling
	   HeapNext().  By limiting the amount of time that we spend in each
	   heap, we can reduce our exposure somewhat */
	hl32.dwSize = sizeof( HEAPLIST32 );
	if( pHeap32ListFirst( hSnapshot, &hl32 ) )
		do
			{
			HEAPENTRY32 he32;
			int entryCount = 0;

			/* First add the information from the basic Heaplist32
			   structure */
			if( krnlIsExiting() )
				{
				CloseHandle( hSnapshot );
				return;
				}
			addRandomData( randomState, &hl32, sizeof( HEAPLIST32 ) );

			/* Now walk through the heap blocks getting information
			   on each of them */
			he32.dwSize = sizeof( HEAPENTRY32 );
			if( pHeap32First( &he32, hl32.th32ProcessID, hl32.th32HeapID ) )
				do
					{
					if( krnlIsExiting() )
						{
						CloseHandle( hSnapshot );
						return;
						}
					addRandomData( randomState, &he32,
								   sizeof( HEAPENTRY32 ) );
					}
				while( entryCount++ < 20 && pHeap32Next( &he32 ) );
			}
		while( listCount++ < 20 && pHeap32ListNext( hSnapshot, &hl32 ) );

	/* Walk through all processes */
	pe32.dwSize = sizeof( PROCESSENTRY32 );
	if( pProcess32First( hSnapshot, &pe32 ) )
		do
			{
			if( krnlIsExiting() )
				{
				CloseHandle( hSnapshot );
				return;
				}
			addRandomData( randomState, &pe32, sizeof( PROCESSENTRY32 ) );
			}
		while( pProcess32Next( hSnapshot, &pe32 ) );

	/* Walk through all threads */
	te32.dwSize = sizeof( THREADENTRY32 );
	if( pThread32First( hSnapshot, &te32 ) )
		do
			{
			if( krnlIsExiting() )
				{
				CloseHandle( hSnapshot );
				return;
				}
			addRandomData( randomState, &te32, sizeof( THREADENTRY32 ) );
			}
	while( pThread32Next( hSnapshot, &te32 ) );

	/* Walk through all modules associated with the process */
	me32.dwSize = sizeof( MODULEENTRY32 );
	if( pModule32First( hSnapshot, &me32 ) )
		do
			{
			if( krnlIsExiting() )
				{
				CloseHandle( hSnapshot );
				return;
				}
			addRandomData( randomState, &me32, sizeof( MODULEENTRY32 ) );
			}
	while( pModule32Next( hSnapshot, &me32 ) );

	/* Clean up the snapshot */
	CloseHandle( hSnapshot );
	if( krnlIsExiting() )
		return;

	/* Flush any remaining data through */
	endRandomData( randomState, 100 );
	}

/* Perform a thread-safe slow poll for Windows 95 */

unsigned __stdcall threadSafeSlowPollWin95( void *dummy )
	{
	UNUSED( dummy );

	slowPollWin95();
	_endthreadex( 0 );
	return( 0 );
	}

/* Type definitions for function pointers to call NetAPI32 functions */

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 );

/* Type definitions for functions to call native NT functions */

typedef DWORD ( WINAPI *NTQUERYSYSTEMINFO )( DWORD dwType, DWORD dwData,
											 DWORD dwMaxSize, DWORD dwDataSize );

/* Global function pointers. These are necessary because the functions need to
   be dynamically linked since only the WinNT kernel currently contains them.
   Explicitly linking to them will make the program unloadable under Win95 */

static NETSTATISTICSGET pNetStatisticsGet = NULL;
static NETAPIBUFFERSIZE pNetApiBufferSize = NULL;
static NETAPIBUFFERFREE pNetApiBufferFree = NULL;
static NTQUERYSYSTEMINFO pNtQuerySystemInfo = NULL;

/* When we query the performance counters, we allocate an initial buffer and
   then reallocate it as required until RegQueryValueEx() stops returning
   ERROR_MORE_DATA.  The following values define the initial buffer size and
   step size by which the buffer is increased */

#define PERFORMANCE_BUFFER_SIZE		65536	/* Start at 64K */
#define PERFORMANCE_BUFFER_STEP		16384	/* Step by 16K */

static void slowPollWinNT( void )
	{
	static BOOLEAN addedFixedItems = FALSE;
	static int isWorkstation = CRYPT_ERROR;
	static int cbPerfData = PERFORMANCE_BUFFER_SIZE;
	RESOURCE_DATA msgData;
	PPERF_DATA_BLOCK pPerfData;
	HANDLE hDevice;
	LPBYTE lpBuffer;
	DWORD dwSize, status;
	int nDrive, iterations = 0;

	/* Find out whether this is an NT server or workstation if necessary */
	if( isWorkstation == CRYPT_ERROR )
		{
		HKEY hKey;

		if( RegOpenKeyEx( HKEY_LOCAL_MACHINE,
						  "SYSTEM\\CurrentControlSet\\Control\\ProductOptions",
						  0, KEY_READ, &hKey ) == ERROR_SUCCESS )
			{
			BYTE szValue[ 32 ];
			dwSize = sizeof( szValue );

			isWorkstation = TRUE;
			status = RegQueryValueEx( hKey, "ProductType", 0, NULL,
									  szValue, &dwSize );
			if( status == ERROR_SUCCESS && stricmp( szValue, "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 );
			}
		}

	/* The following are fixed for the lifetime of the process so we only
	   add them once */
	if( !addedFixedItems )
		{
		readPnPData();
		addedFixedItems = TRUE;
		}

	/* Initialize the NetAPI32 function pointers if necessary */
	if( hNetAPI32 == NULL )
		{
		/* Obtain a handle to the module containing the Lan Manager functions */
		if( ( hNetAPI32 = LoadLibrary( "NetAPI32.dll" ) ) != 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;
				}
			}
		}

	/* Initialize the NT kernel native API function pointers if necessary */
	if( hNTAPI == NULL && \
		( hNTAPI = GetModuleHandle( "NTDll.dll" ) ) != NULL )
		{
		/* Get a pointer to the NT native information query function */
		pNtQuerySystemInfo = ( NTQUERYSYSTEMINFO ) GetProcAddress( hNTAPI,
												"NtQuerySystemInformation" );
		if( pNtQuerySystemInfo == NULL )
			hNTAPI = NULL;
		}
	if( krnlIsExiting() )
		return;

	/* 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. In
	   any case the network statistics return almost no randomness */
	if( hNetAPI32 != NULL &&
		pNetStatisticsGet( NULL,
						   isWorkstation ? L"LanmanWorkstation" : L"LanmanServer",
						   0, 0, &lpBuffer ) == 0 )
		{
		pNetApiBufferSize( lpBuffer, &dwSize );
		setMessageData( &msgData, lpBuffer, dwSize );
		krnlSendMessage( SYSTEM_OBJECT_HANDLE, IMESSAGE_SETATTRIBUTE_S,
						 &msgData, CRYPT_IATTRIBUTE_ENTROPY );
		pNetApiBufferFree( lpBuffer );
		}

	/* Get disk I/O statistics for all the hard drives */
	for( nDrive = 0;; nDrive++ )
		{
		BYTE diskPerformance[ 256 ];
		char szDevice[ 24 ];

		/* Check whether we can access this device */
		sprintf( szDevice, "\\\\.\\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 the user has turned on the disk
		   performance counters with 'diskperf -y'.  These counters are
		   usually disabled, although they appear to be enabled in newer
		   installs of Win2K and XP.  In addition using the documented
		   DISK_PERFORMANCE data structure to contain the returned data
		   returns ERROR_INSUFFICIENT_BUFFER (which is wrong) and doesn't
		   change dwSize (which is also wrong), so we pass in a larger
		   buffer and pre-set dwSize to a safe value.  Finally, there is a
		   bug in pre-SP4 Win2K in which enabling diskperf, installing a
		   file system filter driver, and then disabling diskperf, causes
		   diskperf to corrupt the registry key HKEY_LOCAL_MACHINE\SYSTEM\
		   CurrentControlSet\Control\Class\{71A27CDD-812A-11D0-BEC7-
		   08002BE2092F}\Upper Filters, resulting in a Stop 0x7B bugcheck */
		dwSize = sizeof( diskPerformance );
		if( DeviceIoControl( hDevice, IOCTL_DISK_PERFORMANCE, NULL, 0,
							 &diskPerformance, sizeof( diskPerformance ),
							 &dwSize, NULL ) )
			{
			if( krnlIsExiting() )
				{
				CloseHandle( hDevice );
				return;
				}
			setMessageData( &msgData, &diskPerformance, dwSize );
			krnlSendMessage( SYSTEM_OBJECT_HANDLE, IMESSAGE_SETATTRIBUTE_S,
							 &msgData, CRYPT_IATTRIBUTE_ENTROPY );
			}
		CloseHandle( hDevice );
		}
	if( krnlIsExiting() )
		return;

	/* In theory we should be using the Win32 performance query API to obtain
	   unpredictable data from the system, however this is so unreliable (see
	   the multiple sets of comments further down) that it's too risky to
	   rely on it except as a fallback in emergencies.  Instead, we rely
	   mostly on an NT native API function that has the dual advantages that
	   it doesn't have as many (known) problems as the Win32 equivalent, and
	   that it doesn't access the data indirectly via pseudo-registry keys,
	   which means that it's much faster.  Note that the Win32 equivalent
	   actually works almost all of the time, the problem is that on one or
	   two systems it can fail in strange ways that are never the same and
	   can't be reproduced on any other system, which is why we use the
	   native API here.  Microsoft officially documented this function in
	   early 2003, so it'll be fairly safe to use */
	if( hNTAPI != NULL )
		{
		void *buffer = clAlloc( "slowPollNT", PERFORMANCE_BUFFER_SIZE );

		if( buffer != NULL )
			{
			DWORD dwSize = PERFORMANCE_BUFFER_SIZE, dwType;
			int noResults = 0;

			/* Scan the first 64 possible information types (we don't bother
			   with increasing the buffer size as we do with the Win32 version
			   of the performance data read, we may miss a few classes but
			   it's no big deal).  In addition the returned size value for
			   some classes is wrong (e.g. 23 and 24 return a size of 0) so we
			   miss a few more things, but again it's no big deal.  This scan
			   typically yields around 20 pieces of data, there's nothing in

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?