Alternative to %RANDOM% in Windows batch files

So, it turns out, the %RANDOM% environment variable isn’t random. It’s very much time based. On my XP box, it appears to generate a number that changes every second. What’s worse is that it’s very much deterministic — in most cases, the generated number will be 13 greater than the last number generated.

Not that I was using it for anything crypto-related, you understand, but the time-based thing does present a problem if you are using it in Scheduled Tasks. Particularly if you are using it to create unique, random temporary file names in two Scheduled Tasks that run at the same time.

Of course, this isn’t news. It’s an old new thing. But it was new to me and caused me to screw something up that I shouldn’t have and so on and so forth

Instead, we should pull random data from the system entropy, akin to *NIX’s /dev/random device. After doing some digging, it appears that what *NIX ‘stores’ in /dev/random, Windows ‘stores’ in the registry, a binary Seed value in the HKEY_LOCAL_MACHINE\Software\Microsoft\Cryptography\RNG key. Of course it does.

We can get to that in Windows batch files, with the REG.EXE command-line utility, usually found in the C:\Windows\system32 folder):

"%SYSTEMROOT%\system32\reg.exe" query HKLM\Software\Microsoft\Cryptography\RNG /v Seed

That gets us some version info and the binary data, in some nicely-formatted table, e.g…

! REG.EXE VERSION 3.0

HKEY_LOCAL_MACHINE\software\microsoft\cryptography\rng
    Seed        REG_BINARY      CC577B9D245DB08D34EC20C75B80CDF36822ACFB13F3FAD640F426948F5A05F1E9C956A8DB58F565CC6A1D2A947F93B38DA425D02D49BF30622FD8E7CFDBF63366A2357361C76ABA68720A04F28E7C4D

…which we can parse with FOR /f and convert to a similar 0-32767, like so:

FOR /f "usebackq tokens=3 skip=4" %%A IN (`"%SYSTEMROOT%\system32\reg.exe" query HKLM\Software\Microsoft\Cryptography\RNG /v Seed`) DO SET RND=%%A
SET /a RND=0x%RND:~0,4% %% 32768

The environment variable %RND% now contains a random number.

If you need a large quantity of random numbers, you may be concerned with repeated calls depleting the system entropy pool. This is a possibility, of course, but may be mitigated by keeping the last retrieved entropy data in an environment variable, extracting the first two bytes when you need to, removing those bytes from the data and refilling the entropy variable when it is empty. Something like this should do the trick

:get_random
IF NOT DEFINED ENTROPY FOR /f "usebackq tokens=3 skip=4" %%A IN (`"%SYSTEMROOT%\system32\reg.exe" query HKLM\Software\Microsoft\Cryptography\RNG /v Seed`) DO SET ENTROPY=%%A
SET /a RND=0x%ENTROPY:~0,4% %% 32768
SET ENTROPY=%ENTROPY:~4%
GOTO :EOF

Of course, this doesn’t seem to work on Windows Server 2008 R2 (surprise, surprise), as the HKLM...RNG\Seed value does not seem to exist in the registry (or at least cannot be read by REG.EXE), so I expect that means it doesn’t work in Vista or Win7, either. The closest alternative is the Seed value in the HKLM\System\RNG key, but that does not seem to update after reading, so is next to useless for this purpose.

The only alternative I have in Windows Server 2008 R2 is to invoke PowerShell (which is installed by default on our builds) to get a similar binary value, instead of REG.EXE. While you could instantiate an RNGCryptoServiceProvider object and call GetBytes on a suitably large byte array, it may just be sufficient to invoke Guid.NewGuid one or more times, instead (remembering to escape parentheses properly):

FOR /f "usebackq" %%A IN (`"%SYSTEMROOT%\System32\WindowsPowerShell\v1.0\powershell.exe" -Command [System.Guid]::NewGuid^(^).ToString^('N'^)`) DO SET RND=%%A

That said, if you have PowerShell, you might want to consider doing your script in that instead, if you can get past its awful, awful syntax…

Advertisements

One Response to “Alternative to %RANDOM% in Windows batch files”

  1. alektant Says:

    Four years later and this post is still helping people out. Thanks for the info and code!

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s


%d bloggers like this: