- 帖子
- 766
- 积分
- 1451
- 技术
- 117
- 捐助
- 0
- 注册时间
- 2010-5-12
|
本帖最后由 Demon 于 2012-8-31 20:27 编辑
标题: 批处理技术内幕:随机数%RANDOM%
作者: Demon
链接: http://demon.tw/reverse/cmd-internal-random.html
版权: 本博客的所有文章,都遵守“署名-非商业性使用-相同方式共享 2.5 中国大陆”协议条款。
在批处理中,如果命令扩展被启用,那么有几个动态环境变量可以被扩展,其中之一就是随机数%RANDOM%,%RANDOM%会被扩展到0和32767之间的任意十进制数字。这是常识,地球人都知道,我们讲点一般人不知道的。
说到随机数,学过C语言的朋友马上会想到rand函数,rand是C标准库函数,用来生成伪随机数,而%RANDOM%恰好也是用rand函数来实现的,也许CMD本身就是用C语言写的吧。
在调用rand函数之前,一般要先调用srand函数,用来初始化随机数种子,为了防止随机数每次重复常常使用系统时间来初始化,即使用time函数来获得系统时间,这在C程序中可以说是固定写法了。
#include <time.h>
#include <stdio.h>
#include <stdlib.h>
int main()
{
int i;
srand(time(NULL));
for (i = 0; i < 10; i++) {
printf("%d\n", rand());
}
return 0;
}
CMD在初始化时也是这么做的,以time函数的返回值来调用srand函数初始化种子。
rand函数生成的是伪随机数,而不是真正的随机数,其算法可以在Visual C++中找到。
void __cdecl srand (
unsigned int seed
)
{
_getptd()->_holdrand = (unsigned long)seed;
}
int __cdecl rand (
void
)
{
_ptiddata ptd = _getptd();
return( ((ptd->_holdrand = ptd->_holdrand * 214013L
+ 2531011L) >> 16) & 0x7fff );
}
分析上面的代码可知,如果知道调用srand函数的时间,那么就可以知道每次调用rand函数生成的随机数是多少。也就是说,如果知道CMD的启动时间(从CMD启动到CMD调用srand的时间通常不会超过1秒),那么就可以推算出%RANDOM%的值是多少。
写一个批处理来演示一下:
@echo off
setlocal enabledelayedexpansion
set /a x = !random!
echo !x! & echo;
call :time t
for /l %%i in (%t%, -1, 0) do (
set /a "y = ((%%i * 214013 + 2531011) >> 16) & 0x7fff"
if !y! equ !x! (
echo %%i
call :srand %%i
call :rand z
echo !z! & echo;
for /l %%j in (1, 1, 10) do (
call :rand z
echo !z! !random!
)
)
echo;
pause & exit
)
:time
setlocal
for /f "skip=1 tokens=1-9" %%a in ('wmic path win32_utctime ^| findstr .') do set /a m=%%e+9,m%%=12,y=%%i-m/10,t=365*y+y/4-y/100+y/400+(m*306+5)/10+%%a-719469,t=t*86400+%%c*3600+%%d*60+%%g
endlocal & set %1=%t% & goto :eof
:srand
set /a _holdrand = %1
goto :eof
:rand
set /a _holdrand = _holdrand * 214013 + 2531011
set /a "%1 = (_holdrand >> 16) & 0x7fff"
goto :eof |
|