伪随机数的爆破–1
1 简介
关于伪随机数生成器的相关知识,参考解密随机数生成器。
Java.util.Random采用的是线性同余法生成伪随机数,种子是48位的,而Random.nextInt返回的是32位数字:next(32)。如下代码,直接是nextSeed右移16位的结果。
protected int next(int bits) { long oldseed, nextseed; AtomicLong seed = this.seed; do { oldseed = seed.get(); nextseed = (oldseed * multiplier + addend) & mask; } while (!seed.compareAndSet(oldseed, nextseed)); return (int)(nextseed >>> (48 - bits)); }
因此,只要知道两个连续的nextInt值,便可以通过暴力猜解丢失的前18位来获得种子。
2 具体实现
参考cracking random number generators的实现:
;; 常数定义和算法都取自java.util.Random类 (def multiplier 0x5DEECE66D) (def addend 0xB) (def mask (dec (bit-shift-left 1 48))) (defn next-seed [seed] (bit-and (+ (unchecked-multiply seed multiplier) addend) mask)) (defn next-int [seed] (-> (next-seed seed) (unsigned-bit-shift-right 16) unchecked-int)) (defn brute-seed "只要有两个连续的randInt值就可以非常快的爆破出种子" [v1 v2] (loop [i 0] (when (< i 65536) (let [seed (+ i (* v1 65536)) test-v (next-int seed)] (if (= test-v v2) seed (recur (inc i))))))) ;;; 以下为测试代码 (def rnd (java.util.Random.)) (def rnd-v1 (.nextInt rnd)) (def rnd-v2 (.nextInt rnd)) rnd-v1 ;; => -1680039525 rnd-v2 ;; => -1809533376 (def seed (brute-seed rnd-v1 rnd-v2)) seed ;; => -110103070270234 ;; 获得下一个随机值 (-> (next-seed seed) next-int) ;; => 796622690 ;; 与生成的随机值比较,可以看到正确的获取到了下一个Int值 (.nextInt rnd) ;; => 796622690 ;; 再测试下下个 (-> (next-seed seed) next-seed next-int) ;; => -52712848 (.nextInt rnd) ;; => -52712848
3 结尾
猜测216还是很快的.
但对于nextInt(int bound)的调用,由于信息损失较多,就不大可能直接猜解出来,要爆破248速度还没那么快。下一篇是关于php mt_rand的讲解。
Created: 2019-03-16 周六 21:31