关于Linux的伪随机数分析

rand函数

函数说明:
rand()会返回一随机数值,范围在0至RAND_MAX 间。在调用此函数产生随机数前,必须先利用srand()设好随机数种子,如果未设随机数种子,rand()在调用时会自动设随机数种子为1,关于随机数种子请参考srand()
返回值:
返回0至RAND_MAX之间的随机数值,RAND_MAX定义在stdlib.h,其值为2147483647

srand函数

函数说明:
srand()用来设置rand()产生随机数时的随机数种子。参数seed必须是个整数,通常可以利用geypid()或time(0)的返回值来当做seed。如果每次seed都设相同值,rand()所产生的随机数值每次就会一样。

Linux随机数分析

最一般的随机数过程,即srand->rand的过程,目的是根据获得的随机数逆向出种子

unsafe_state:

146 static int32_t randtbl[DEG_3 + 1] =
147   {
148     TYPE_3,
149 
150     -1726662223, 379960547, 1735697613, 1040273694, 1313901226,
151     1627687941, -179304937, -2073333483, 1780058412, -1989503057,
152     -615974602, 344556628, 939512070, -1249116260, 1507946756,
153     -812545463, 154635395, 1388815473, -1926676823, 525320961,
154     -1009028674, 968117788, -123449607, 1284210865, 435012392,
155     -2017506339, -911064859, -370259173, 1132637927, 1398500161,
156     -205601318,
157   };
160 static struct random_data unsafe_state =
161   {
162 /* FPTR and RPTR are two pointers into the state info, a front and a rear
163    pointer.  These two pointers are always rand_sep places apart, as they
164    cycle through the state information.  (Yes, this does mean we could get
165    away with just one pointer, but the code for random is more efficient
166    this way).  The pointers are left positioned as they would be from the call:
167         initstate(1, randtbl, 128);
168    (The position of the rear pointer, rptr, is really 0 (as explained above
169    in the initialization of randtbl) because the state table pointer is set
170    to point to randtbl[1] (as explained below).)  */
171 
172     .fptr = &randtbl[SEP_3 + 1], // 前指针  
173     .rptr = &randtbl[1], // 后指针
174 
175 /* The following things are the pointer to the state information table,
176    the type of the current generator, the degree of the current polynomial
177    being used, and the separation between the two pointers.
178    Note that for efficiency of random, we remember the first location of
179    the state information, not the zeroth.  Hence it is valid to access
180    state[-1], which is used to store the type of the R.N.G.
181    Also, we remember the last location, since this is more efficient than
182    indexing every time to find the address of the last element to see if
183    the front and rear pointers have wrapped.  */
184 
185     .state = &randtbl[1], // randtbl为基础的表,state会在srand设置种子后对这个表进行转换,并将生成的随机数循环存在这里
186 
187     .rand_type = TYPE_3, // 3
188     .rand_deg = DEG_3, // state的大小为31,因为state是从randtbl[1]开始算起,randtbl[0]始终为TYPE_3
189     .rand_sep = SEP_3, // 3
190 
191     .end_ptr = &randtbl[sizeof (randtbl) / sizeof (randtbl[0])] // randtbl终结的位置
192 };

srand函数:

srand的具体实现就是__srandom_r,其中参数 seed为种子,buf就是上面的unsafe_state。
这里主要是以seed为基础,记为state[0],实现state[i] = (16807 * state[i - 1]) % 2147483647。

161 __srandom_r (unsigned int seed, struct random_data *buf) 
162 {
163   int type;
164   int32_t *state;
165   long int i;
166   int32_t word;
167   int32_t *dst;
168   int kc;
169 
170   if (buf == NULL)
171     goto fail;
172   type = buf->rand_type; // type默认为3
173   if ((unsigned int) type >= MAX_TYPES)
174     goto fail;
175 
176   state = buf->state; // state默认从randtbl[1]开始
177   /* We must make sure the seed is not 0.  Take arbitrarily 1 in this case.  */
178   if (seed == 0) // seed为0也改为1
179     seed = 1;
180   state[0] = seed; // state[0]设置为seed,默认是TYPE_3
181   if (type == TYPE_0)
182     goto done;
183   // 从state[1]开始,实现state[i] = (16807 * state[i - 1]) % 2147483647
184   dst = state;  // dst表示state[i]
185   word = seed; // word表示state[i-1]
186   kc = buf->rand_deg; // kc=31,表示table的大小
187   for (i = 1; i < kc; ++i) //一共30个
188     {
189       /* This does:
190            state[i] = (16807 * state[i - 1]) % 2147483647;
191          but avoids overflowing 31 bits.  */
192       long int hi = word / 127773;
193       long int lo = word % 127773;
194       word = 16807 * lo - 2836 * hi;
195       if (word < 0)
196         word += 2147483647;
197       *++dst = word;
198     }
199 
200   buf->fptr = &state[buf->rand_sep]; // fptr从state[3]开始
201   buf->rptr = &state[0]; // rptr从state[0]开始
202   kc *= 10; // 310
203   while (--kc >= 0) // 通过__random_r生成310个随机数
204     {
205       int32_t discard;
206       (void) __random_r (buf, &discard);
207     }
208 
209  done:
210   return 0;
211 
212  fail:
213   return -1;
214 }

rand函数:

rand的具体实现就是__random_r,参数buf依然是上面的unsafe_state,result就是我们返回的随机数。

352 int
353 __random_r (struct random_data *buf, int32_t *result)
354 {
355   int32_t *state;
356 
357   if (buf == NULL || result == NULL)
358     goto fail;
359 
360   state = buf->state;
361 
362   if (buf->rand_type == TYPE_0)
363     {
364       int32_t val = state[0];
365       val = ((state[0] * 1103515245) + 12345) & 0x7fffffff;
366       state[0] = val;
367       *result = val;
368     }
369   else // 一般type=3,所以走下面这一段
370     {
371       int32_t *fptr = buf->fptr; 
372       int32_t *rptr = buf->rptr;
373       int32_t *end_ptr = buf->end_ptr;
374       int32_t val;
375 
376       val = *fptr += *rptr;
377       /* Chucking least random bit.  */
378       *result = (val >> 1) & 0x7fffffff;
379       ++fptr;
380       if (fptr >= end_ptr)
381         {
382           fptr = state;
383           ++rptr;
384         }
385       else
386         {
387           ++rptr;
388           if (rptr >= end_ptr)
389             rptr = state;
390         }
391       buf->fptr = fptr;
392       buf->rptr = rptr;
393     }
394   return 0;
395 
396  fail:  
397   __set_errno (EINVAL);
398   return -1;
399 }

伪代码:

通过上面的分析,已经可以根据跟定的seed获得随机数,以及对应的state了:

#include          
#define MAX 1000
#define seed 1
#int main() {
    int r[MAX],i,j;
    int state[31];
    state[0] = seed;
    for (i=1; i<31; i++){
            state[i] = (16807LL * state[i-1]) % 2147483647;
            if (state[i] < 0) {
                state[i] += 2147483647;
            }
        }
    for (i=31; i<341;i++){
        state[(i+3)%31] = (state[(i+3)%31]+state[i%31]);
    }
    for (i=341;i>1)&0x7fffffff;
        printf("%d : %x\n",i-340,r[i-340]);
    }
    return 0;
}

代码简化:

#include 
#define MAX 1000
#define seed 1
int main() {
    int r[MAX],i;
    r[0] = seed;
    for (i=1; i<31; i++){
            r[i] = (16807LL * r[i-1])%0x7fffffff;
            if (r[i] < 0) {
                r[i] += 0x7fffffff;
            }
        }
    for (i=31; i<34; i++){
        r[i] = r[i-31];
    }
    for (i=34; i<344;i++){
        r[i] = r[i-31] + r[i-3];
    }
    for (i=344;i<350;i++){
        r[i] = r[i-31] + r[i-3];
        printf("%d %#x\n", i-343, (r[i]>>1)&0x7fffffff);
    }
    return 0;
}

预测:
可以看到上面存在&0x7fffffff,并且还存在除以2,所以要能成功预测的情况是r[i-31]和r[i-3]都没有&0x7fffffff,这样就是r[i] = ((r[i-31]<<1)+(r[i-3]<<1))
对应的随机数还是(r[i]>>1)&0x7fffffff),但是实际上还能更简单,由于我们获得的是随机数,那么就是这样的:

rand[i] = (r[i]>>1)&0x7fffffff
r[i] = ((r[i-31]<<1)+(r[i-3]<<1))
rand[i-3] = (r[i-3]>>1)&0x7fffffff
rand[i-31] = (r[i-31]>>1)&0x7fffffff
综上:
rand[i] = (((r[i-31]<<1)+(r[i-3]<<1))>>1)&0x7fffffff 推出
rand[i] = (rand[i-3]+rand[i-31])&0x7fffffff
但是由于存在除以2的原因,所以可能会存在1的误差

你可能感兴趣的:(关于Linux的伪随机数分析)