高效伪随机数序列生成方案
- 解决(不重复的生成上亿伪随机数),并且不需要记录和比对生成过的随机数
前言
- 前段时间开发过一个快速端口扫描的模块,需求是要对大批量主机进行端口扫描。端口扫描技术已经是被无数人玩过的了,快速端口扫描主要的技术就是syn扫描,比较成熟。但在大批量扫描的时候,为了降低对目标安全设备的警报(常见的防火墙会通过连接并发数,端口的连接顺序,连接间隔时间来做警报),一般会选择随机的端口扫描。不过因为syn的特性,所以几个目标随机扫描基本没什么意义(发包太快),但是如果是大批量次的扫描,随机就会有意义。如有10个B段要扫描,每个B段的发包数量就是255*255*65535=4261413375(非常大,一般不会傻傻的扫描那么大),每次都随机的从10个段中选择一个段,然后从这个段中随机的选择一个目标,因为扫描的基数大,所以对每个目标重复扫描的间隔时间就会相对比较长,从而减少安全设备的警告
- 正常情况,大多数人第一反应想到的是洗牌算法,或每次保存生成的随机数,第二次对生成的随机数进行去重查找,如果已经生成过,就继续生成,直到不重复。这些随机数序列生成算法在小范围内是非常高效的,但要生成上亿的随机数序列,并且每次生成的随机数都不重复,使用这些算法就会非常消耗内存和时间,基本不可接受。前期的做法是通过对一个大范围的段拆分为无数个小范围的段,然后一次对10或20个小范围的段进行随机扫描,这样对内存和时间的消耗勉强可以接受,但拆分的过程也是很消耗时间和空间的,整体效率和效果不高
- 注意:因为IP段其实就是一段连续的无符号整数(在主机字节序的情况下),如一个B段(119.41.0.0-119.41.255.255)要扫描全端口(65535),他的扫描范围就是1-4261413375,只要从1-4261413375范围中随机选择一个数,就可以通过这个数计算出对应的IP与端口,所以只要有1-4261413375范围的随机数序列,就可以高效的进行随机扫描(前提是算法时空效率高)
- 通过一段时间的查找,终于找到自己满意的算法:http://www.cnblogs.com/Geometry/archive/2011/01/25/1944582.html,这个算法的主要思路是通过加密与随机的相通性来实现。如将一个数加密为另一个数,这与随机算法达到的效果很相似,那么如果将1-4261413375中的每一个数都进行加密,就可以得到一个随机的序列。另外随机数的变化是通过初始种子,加密的变化是通过密钥,那么看起来通过改造一个加密算法来实现随机效果,是可以行的。
- http://www.cnblogs.com/Geometry/archive/2011/01/25/1944582.html中是通过RSA算法来实现伪随机数序列生成,并且此加密算法的特性已经保证每次产生的随机数都是不重复的(有重复还咋解密),最主要的是,在生成1-N范围内的随机数时,生成N次,即可全覆盖1-N(N太大就需要使用大数运算),整体效果还是非常好。
RSA算法简介
RSA加密解密的基本原理
- 选取随机的初始大素数:(p,q),注意:p与q不相等
- 计算模数N:(N = p * q)
- 计算欧拉φ(n):(φ(n) = (p - 1) * (q - 1))
- 计算公钥指数e:(1 < e < φ(n) 且 gcd(e,φ(n)) = 1),即e与φ(n)互质,一般选择的e都是比较小的数,主要是为了提升计算速度,毕竟大数运算是很消耗时间的
- 计算私钥指数d:(e * d) % φ(n) = 1,即d是e的模反元素
- 组合密钥:(n,e)组成公钥,(n,d)组成私钥
- 加密:假设A是明文,C是密文,(C = A^e % N),即A的e次幂模N等于C。注意:如果e过大,很容易产生计算漏出,另外使用模幂运算,可有效减少计算漏洞并加速计算
- 解密:(A = C^d % N),即C的d次幂模N等于A
RSA安全性
- RSA的安全性主要体现在大数N分解难度,通过上面可看出e与d是密钥的关键,在知道了e与N的情况下,要计算出d就需要对N分解来得出p和q,目前公布的对N分解最高的二进制位数是768位。
RSA算法生成随机序列的难点
- 利用RSA来做随机数序列生成的难点也是分解N,但这个N往往是非常小的(相对于加密的安全性来说),因为我们不需要考虑安全性,只需要考虑随机数序列的范围,所以也不是特别麻烦。另外我们只能得到一个随机数范围的参数如1-4261413375,此时Na=4261413375(假设Na为用户输入的随机数序列范围参数),需要对Na分解来得到p,q,然后重新计算出RSA的N。
RSA算法应用于随机数序列生成的基本原理
- **用户输入随机数序列范围:**Na,如Na = 4261413375,即生成1-4261413375范围的随机数序列
- 分解Na:由于Na是一个用户输入的自然数,而分解质因数是针对合数,另外分解后的p与q是质数并且不相等,并且大多数据情况分解出来的因数会是多个,所以需要循环累加分解,直到找到合适的p和q。通过测试对亿大小的数分解还是很快速的
- 得出p和q后,其它步骤与RSA算法加密流程一样。注意:随机数生成只需要使用到RSA加密部分,所以只需要计算出e即可
- 注意:如果在同一范围内,想要每次生成的随机数序列都不一样,可以改变e来实现
具体C++代码实现
- 这里没有使用大数运算,在使用的时候需要注意。经过测试在1亿的范围内生成随机数序列是没问题的,如果有需要提高范围的,加入大数运算也不难,主要是在模幂运算的时候使用大数即可
- 注意:如果n过小,就不能使用此算法生成随机序列,如n<=6的时候,因为无法计算有效的e,所以无法生成无序的随机数序列
- 如果代码有什么问题或有更好的方法请告知
- radom_integer_iterator.h 文件
#ifndef RADOM_INTEGER_ITERATOR_H_H
#define RADOM_INTEGER_ITERATOR_H_H
#include <vector>
using std::vector;
#include <stdlib.h>
#include <time.h>
#define KOOK_MAX_RADOM_INTEGER_N 100000000
namespace kook {
typedef struct _RSAPQ {
unsigned long long rsap;
unsigned long long rsaq;
_RSAPQ() :rsap(0), rsaq(0) {};
}rsapq;
class radom_integer_iterator {
public:
radom_integer_iterator() : n(0), rn(0), re(0), i(1) {}
radom_integer_iterator(unsigned long n);
static vector<unsigned long long> decompose_prime_factor(unsigned long long x);
static bool is_prime_number(unsigned long long x);
static unsigned long long mul_mod(unsigned long long a, unsigned long long b, unsigned long long n);
static unsigned long long pow_mod(unsigned long long base, unsigned long long po