Skip to content

Commit 78d4b00

Browse files
committed
[ADVAPI32] Rewrite SystemFunction036
- Use AES-CTR from cryptlib - Reseed regularly from ksecdd
1 parent e48ea78 commit 78d4b00

2 files changed

Lines changed: 134 additions & 58 deletions

File tree

dll/win32/advapi32/CMakeLists.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,6 @@ if(DLL_EXPORT_VERSION GREATER_EQUAL 0x600)
6666
target_link_libraries(advapi32 etwtrace)
6767
endif()
6868
add_delay_importlibs(advapi32 secur32)
69-
add_importlibs(advapi32 advapi32_vista rpcrt4 kernel32 ntdll)
69+
add_importlibs(advapi32 advapi32_vista rpcrt4 kernel32 ntdll ntdll_vista)
7070
add_pch(advapi32 advapi32.h "${PCH_SKIP_SOURCE}")
7171
add_cd_file(TARGET advapi32 DESTINATION reactos/system32 FOR all)

dll/win32/advapi32/misc/sysfunc.c

Lines changed: 133 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
#include <md4.h>
1717
#include <md5.h>
1818
#include <rc4.h>
19+
#include <aes-ctr.h>
1920

2021
static const unsigned char CRYPT_LMhash_Magic[8] =
2122
{ 'K', 'G', 'S', '!', '@', '#', '$', '%' };
@@ -585,63 +586,6 @@ BOOL WINAPI SystemFunction035(LPCSTR lpszDllFilePath)
585586
return TRUE;
586587
}
587588

588-
/******************************************************************************
589-
* SystemFunction036 (ADVAPI32.@)
590-
*
591-
* MSDN documents this function as RtlGenRandom and declares it in ntsecapi.h
592-
*
593-
* PARAMS
594-
* pbBuffer [O] Pointer to memory to receive random bytes.
595-
* dwLen [I] Number of random bytes to fetch.
596-
*
597-
* RETURNS
598-
* Always TRUE in my tests
599-
*/
600-
BOOLEAN
601-
WINAPI
602-
SystemFunction036(PVOID pbBuffer, ULONG dwLen)
603-
{
604-
////////////////////////////////////////////////////////////////
605-
//////////////////// B I G W A R N I N G !!! ////////////////
606-
// This function will output numbers based on the tick count. //
607-
// It will NOT OUTPUT CRYPTOGRAPHIC-SAFE RANDOM NUMBERS !!! //
608-
////////////////////////////////////////////////////////////////
609-
610-
DWORD dwSeed;
611-
PBYTE pBuffer;
612-
ULONG uPseudoRandom;
613-
LARGE_INTEGER time;
614-
static ULONG uCounter = 17;
615-
616-
if(!pbBuffer || !dwLen)
617-
{
618-
/* This function always returns TRUE, even if invalid parameters were passed. (verified under WinXP SP2) */
619-
return TRUE;
620-
}
621-
622-
/* Get the first seed from the performance counter */
623-
QueryPerformanceCounter(&time);
624-
dwSeed = time.LowPart ^ time.HighPart ^ RtlUlongByteSwap(uCounter++);
625-
626-
/* We will access the buffer bytewise */
627-
pBuffer = (PBYTE)pbBuffer;
628-
629-
do
630-
{
631-
/* Use the pseudo random number generator RtlRandom, which outputs a 4-byte value and a new seed */
632-
uPseudoRandom = RtlRandom(&dwSeed);
633-
634-
do
635-
{
636-
/* Get each byte from the pseudo random number and store it in the buffer */
637-
*pBuffer = (BYTE)(uPseudoRandom >> 8 * (dwLen % 3) & 0xFF);
638-
++pBuffer;
639-
} while(--dwLen % 3);
640-
} while(dwLen);
641-
642-
return TRUE;
643-
}
644-
645589
HANDLE KsecDeviceHandle;
646590

647591
static
@@ -729,6 +673,138 @@ KsecDeviceIoControl(
729673
return Status;
730674
}
731675

676+
/* Reseed every 256 KB generated */
677+
#define MAX_RNG_RESEED_INTERVAL (256 * 1024)
678+
679+
/* Generate a maximum of 1KB on each iteration */
680+
#define MAX_RNG_CHUNK_SIZE 1024
681+
682+
/* Initialize the counter for instant reseed */
683+
static volatile LONG RngBytesGeneratedSinceReseed = MAX_RNG_RESEED_INTERVAL + 1;
684+
685+
static AES_CTR_CTX RngContext;
686+
static RTL_SRWLOCK RngReseedLock;
687+
688+
_Requires_lock_not_held_(RngReseedLock)
689+
static
690+
NTSTATUS
691+
RngReseed(VOID)
692+
{
693+
BYTE Seed[32];
694+
NTSTATUS Status;
695+
696+
/* Acquire the reseed lock exclusively */
697+
RtlAcquireSRWLockExclusive(&RngReseedLock);
698+
699+
/* Check if another thread already reseeded the PRNG and at least half is left */
700+
if (RngBytesGeneratedSinceReseed < (MAX_RNG_RESEED_INTERVAL / 2))
701+
{
702+
Status = STATUS_SUCCESS;
703+
goto Exit;
704+
}
705+
706+
/* Get new seed data from ksecdd */
707+
Status = KsecDeviceIoControl(IOCTL_KSEC_RANDOM_FILL_BUFFER, NULL, 0, Seed, sizeof(Seed));
708+
if (!NT_SUCCESS(Status))
709+
{
710+
//ERR("Failed to get random data from ksecdd for reseeding the PRNG!\n");
711+
goto Exit;
712+
}
713+
714+
/* Reseed the PRNG */
715+
AES_CTR_Init(&RngContext, Seed, sizeof(Seed));
716+
717+
/* Reset the reseed counter */
718+
InterlockedExchange(&RngBytesGeneratedSinceReseed, 0);
719+
720+
Status = STATUS_SUCCESS;
721+
722+
Exit:
723+
/* Release the reseed lock */
724+
RtlReleaseSRWLockExclusive(&RngReseedLock);
725+
726+
/* Erase the seed data */
727+
RtlSecureZeroMemory(Seed, sizeof(Seed));
728+
729+
return Status;
730+
}
731+
732+
_Requires_shared_lock_held_(RngReseedLock)
733+
static
734+
BOOLEAN
735+
RngGenerateChunk(PVOID Buffer, ULONG Length)
736+
{
737+
ASSERT(Length > 0);
738+
ASSERT(Length <= MAX_RNG_CHUNK_SIZE);
739+
740+
/* Make sure we don't exceed the maximum number of bytes per reseed */
741+
ULONG Count = InterlockedAdd(&RngBytesGeneratedSinceReseed, Length);
742+
if (Count >= MAX_RNG_RESEED_INTERVAL)
743+
{
744+
/* Release the shared lock before acquiring the exclusive lock for reseeding */
745+
RtlReleaseSRWLockShared(&RngReseedLock);
746+
747+
/* We have used up the maximum, reseed the RNG */
748+
if (!RngReseed())
749+
{
750+
return FALSE;
751+
}
752+
753+
/* Reacquire the shared lock after reseeding */
754+
RtlAcquireSRWLockShared(&RngReseedLock);
755+
}
756+
757+
/* Generate the random data */
758+
AES_CTR_GenRandom(&RngContext, Buffer, Length);
759+
760+
return TRUE;
761+
}
762+
763+
764+
/******************************************************************************
765+
* SystemFunction036 (ADVAPI32.@)
766+
*
767+
* MSDN documents this function as RtlGenRandom and declares it in ntsecapi.h
768+
*
769+
* PARAMS
770+
* pbBuffer [O] Pointer to memory to receive random bytes.
771+
* dwLen [I] Number of random bytes to fetch.
772+
*
773+
* RETURNS
774+
* Always TRUE in my tests
775+
*/
776+
BOOLEAN
777+
WINAPI
778+
SystemFunction036(PVOID pvBuffer, ULONG dwLen)
779+
{
780+
if ((pvBuffer == NULL) || (dwLen == 0))
781+
{
782+
return FALSE;
783+
}
784+
785+
/* Acquire the reseed lock shared, so we can safely access the RNG state */
786+
RtlAcquireSRWLockShared(&RngReseedLock);
787+
788+
/* Generate chunks of bytes until the buffer is filled */
789+
ULONG cjRemaining = dwLen;
790+
while (cjRemaining > 0)
791+
{
792+
ULONG cjChunkSize = min(cjRemaining, MAX_RNG_CHUNK_SIZE);
793+
if (!RngGenerateChunk(pvBuffer, cjChunkSize))
794+
{
795+
return FALSE;
796+
}
797+
798+
pvBuffer = (PBYTE)pvBuffer + cjChunkSize;
799+
cjRemaining -= cjChunkSize;
800+
}
801+
802+
/* Release the shared lock */
803+
RtlReleaseSRWLockShared(&RngReseedLock);
804+
805+
return TRUE;
806+
}
807+
732808
/*
733809
These functions have nearly identical prototypes to CryptProtectMemory and CryptUnprotectMemory,
734810
in crypt32.dll.

0 commit comments

Comments
 (0)