⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 win32.c

📁 cryptlib安全工具包
💻 C
📖 第 1 页 / 共 5 页
字号:
	   wondering what this call is doing here... the reason why it's 
	   necessary is because RegQueryValueEx() will hang indefinitely if the 
	   async driver bind is in progress.  The problem occurs in the dynamic 
	   loading and linking of driver DLL's, which work as follows:

		hDriver = LoadLibrary( DRIVERNAME );
		pFunction1 = ( TYPE_FUNC1 ) GetProcAddress( hDriver, NAME_FUNC1 );
		pFunction2 = ( TYPE_FUNC1 ) GetProcAddress( hDriver, NAME_FUNC2 );

	   If RegQueryValueEx() is called while the GetProcAddress()'s are in
	   progress, it will hang indefinitely.  This is probably due to some
	   synchronisation problem in the NT kernel where the GetProcAddress()
	   calls affect something like a module reference count or function
	   reference count while RegQueryValueEx() is trying to take a snapshot of
	   the statistics, which include the reference counts.  Because of this,
	   we have to wait until any async driver bind has completed before we can
	   call RegQueryValueEx() */
	if( !krnlWaitSemaphore( SEMAPHORE_DRIVERBIND ) )
		{
		/* The kernel is shutting down, bail out */
		return;
		}

	/* Get information from the system performance counters.  This can take a
	   few seconds to do.  In some environments the call to RegQueryValueEx()
	   can produce an access violation at some random time in the future, in
	   some cases adding a short delay after the following code block makes
	   the problem go away.  This problem is extremely difficult to
	   reproduce, I haven't been able to get it to occur despite running it
	   on a number of machines.  MS knowledge base article Q178887 covers
	   this type of problem, it's typically caused by an external driver or
	   other program that adds its own values under the
	   HKEY_PERFORMANCE_DATA key.  The NT kernel, via Advapi32.dll, calls the
	   required external module to map in the data inside an SEH try/except
	   block, so problems in the module's collect function don't pop up until
	   after it has finished, so the fault appears to occur in Advapi32.dll.
	   There may be problems in the NT kernel as well though, a low-level
	   memory checker indicated that ExpandEnvironmentStrings() in
	   Kernel32.dll, called an interminable number of calls down inside
	   RegQueryValueEx(), was overwriting memory (it wrote twice the
	   allocated size of a buffer to a buffer allocated by the NT kernel).
	   OTOH this could be coming from the external module calling back into
	   the kernel, which eventually causes the problem described above.

	   Possibly as an extension of the problem that the krnlWaitSemaphore()
	   call above works around, running two instances of cryptlib (e.g. two
	   applications that use it) under NT4 can result in one of them hanging
	   in the RegQueryValueEx() call.  This happens only under NT4 and is
	   hard to reproduce in any consistent manner.

	   One workaround that helps a bit is to read the registry as a remote
	   (rather than local) registry, it's possible that the use of a network
	   RPC call isolates the calling app from the problem in that whatever
	   service handles the RPC is taking the hit and not affecting the
	   calling app.  Since this would require another round of extensive
	   testing to verify and the NT native API call is working fine, we'll
	   stick with the native API call for now.

	   Some versions of NT4 had a problem where the amount of data returned
	   was mis-reported and would never settle down, because of this the code
	   below includes a safety-catch that bails out after 10 attempts have
	   been made, this results in no data being returned but at does ensure
	   that the thread will terminate.

	   In addition to these problems the code in RegQueryValueEx() that
	   estimates the amount of memory required to return the performance
	   counter information isn't very accurate (it's much worse than the
	   "slightly-inaccurate" level that the MS docs warn about, it's usually
	   wildly off) since it always returns a worst-case estimate which is
	   usually nowhere near the actual amount required.  For example it may
	   report that 128K of memory is required, but only return 64K of data.

	   Even worse than the registry-based performance counters is the
	   performance data helper (PDH) shim that tries to make the counters
	   look like the old Win16 API (which is also used by Win95).  Under NT
	   this can consume tens of MB of memory and huge amounts of CPU time
	   while it gathers its data, and even running once can still consume
	   about 1/2MB of memory.

	   "Windows NT is a thing of genuine beauty, if you're seriously into 
	    genuine ugliness.  It's like a woman with a history of insanity in 
		the family, only worse" -- Hans Chloride, "Why I Love Windows NT" */
	pPerfData = ( PPERF_DATA_BLOCK ) clAlloc( "slowPollNT", cbPerfData );
	while( pPerfData != NULL && iterations++ < 10 )
		{
		dwSize = cbPerfData;
		dwStatus = RegQueryValueEx( HKEY_PERFORMANCE_DATA, "Global", NULL,
									NULL, ( LPBYTE ) pPerfData, &dwSize );
		if( dwStatus == ERROR_SUCCESS )
			{
			if( !memcmp( pPerfData->Signature, L"PERF", 8 ) )
				{
				static const int quality = 100;

				setMessageData( &msgData, pPerfData, dwSize );
				status = krnlSendMessage( SYSTEM_OBJECT_HANDLE,
										  IMESSAGE_SETATTRIBUTE_S, &msgData,
										  CRYPT_IATTRIBUTE_ENTROPY );
				if( cryptStatusOK( status ) )
					krnlSendMessage( SYSTEM_OBJECT_HANDLE,
									 IMESSAGE_SETATTRIBUTE,
									 ( void * ) &quality,
									 CRYPT_IATTRIBUTE_ENTROPY_QUALITY );
				}
			clFree( "slowPollWinNT", pPerfData );
			pPerfData = NULL;
			}
		else
			{
			if( dwStatus == ERROR_MORE_DATA )
				{
				PPERF_DATA_BLOCK pTempPerfData;
				
				cbPerfData += PERFORMANCE_BUFFER_STEP;
				pTempPerfData = ( PPERF_DATA_BLOCK ) realloc( pPerfData, cbPerfData );
				if( pTempPerfData == NULL )
					{
					/* The realloc failed, free the original block and force 
					   a loop exit */
					clFree( "slowPollWinNT", pPerfData );
					pPerfData = NULL;
					}
				else
					pPerfData = pTempPerfData;
				}
			}
		}

	/* Although this isn't documented in the Win32 API docs, it's necessary to
	   explicitly close the HKEY_PERFORMANCE_DATA key after use (it's
	   implicitly opened on the first call to RegQueryValueEx()).  If this
	   isn't done then any system components that provide performance data
	   can't be removed or changed while the handle remains active */
	RegCloseKey( HKEY_PERFORMANCE_DATA );
	}

static void slowPollWinNT( void )
	{
	static BOOLEAN addedFixedItems = FALSE;
	static int isWorkstation = CRYPT_ERROR;
	MESSAGE_DATA msgData;
	HANDLE hDevice;
	LPBYTE lpBuffer;
	ULONG ulSize;
	DWORD dwType, dwSize, dwResult;
	void *buffer;
	int nDrive, noResults = 0, status;

	/* 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 + 8 ];
			dwSize = 32;

			isWorkstation = TRUE;
			if( RegQueryValueEx( hKey, "ProductType", 0, NULL, szValue,
								 &dwSize ) == 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 functions */
		pNtQuerySystemInformation = ( NTQUERYSYSTEMINFORMATION ) \
						GetProcAddress( hNTAPI, "NtQuerySystemInformation" );
		pNtQueryInformationProcess = ( NTQUERYINFORMATIONPROCESS ) \
						GetProcAddress( hNTAPI, "NtQueryInformationProcess" );
		if( pNtQuerySystemInformation == NULL || \
			pNtQueryInformationProcess == NULL )
			hNTAPI = NULL;
		pNtPowerInformation = ( NTPOWERINFORMATION ) \
						GetProcAddress( hNTAPI, "NtPowerInformation" );
		}
	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 < FAILSAFE_ITERATIONS_MED; nDrive++ )
		{
		BYTE diskPerformance[ 256 + 8 ];
		char szDevice[ 32 + 8 ];

		/* Check whether we can access this device */
		sprintf_s( szDevice, 32, "\\\\.\\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 were
		   traditionally disabled under NT, although in newer installs of 
		   Win2K and newer the physical disk object is enabled by default 
		   while the logical disk object is disabled by default 
		   ('diskperf -yv' turns on the counters for logical drives in this 
		   case, since they're already on for physical drives).
		   
		   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's 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, 256, &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 in registryPoll()) that it's too risky
	   to rely on it except as a fallback in emergencies.  Instead, we rely
	   mostly on the NT native API function NtQuerySystemInformation(), which
	   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 ) || \
		( buffer = clAlloc( "slowPollNT", PERFORMANCE_BUFFER_SIZE ) ) == NULL )
		{
		registryPoll();
		return;
		}

	/* Clear the buffer before use.  We have to do this because even though 
	   NtQuerySystemInformation() tells us that it's filled in ulSize bytes, 
	   it doesn't necessarily mean that it actually has provided that much 
	   data */
	memset( buffer, 0, PERFORMANCE_BUFFER_SIZE );

	/* 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).
	   This

⌨️ 快捷键说明

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