Board logo

标题: [原创] 批处理技术内幕:随机数%RANDOM% [打印本页]

作者: Demon    时间: 2012-8-31 20:24     标题: 批处理技术内幕:随机数%RANDOM%

本帖最后由 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




欢迎光临 批处理之家 (http://bathome.net./) Powered by Discuz! 7.2