pwnable.kr---blackjack

pwnable.kr—blackjack

解题思路

万万没想到这个题突破点在这里,我本想不看别人的writeup 独立做出这个题目的,但还是败给了自己,加油!

1.先在服务器玩一下这个游戏,然后去看游戏的源代码;是一个不难理解的21点游戏

2.查看游戏的源代码,了解游戏的实现

3.关键代码所在:line721—line734

int betting() //Asks user amount to bet
{
 printf("\n\nEnter Bet: $");
 scanf("%d", &bet);

 if (bet > cash) //If player tries to bet more money than player has
 {
        printf("\nYou cannot bet more money than you have.");
        printf("\nEnter Bet: ");
        scanf("%d", &bet);
        return bet;
 }
 else return bet;
} // End Function

这个函数是游戏的过程中,保证玩家压的赌注是小于他所拥有的本钱的。可以看到,如果bet(赌注)>cash(玩家此时的本钱),那么要求玩家重新输入。但是!!!玩家再次输入的时候,没有循环判断!!!直接将第二次输入的bet值return了。那么我们就可以将押金压上1000,000,然后赢一次就赢了1000,000,然后就可以得到flag了。(当然输了也没关系啊,大不了从头再来)

so easy………………………………………………………………..

我遇到的问题

1.我的第一个思路

首先我在看游戏源码的时候,感觉这个游戏的规则很乱,就怀疑会不会存在一些逻辑漏洞,我可以根据这个判断逻辑更好的赢钱。

游戏的大致逻辑如下:

判断玩家==21:玩家赢

判断玩家>21:玩家输

判断玩家\<21&&玩家选择Hit:玩家加上随机数;dealer加上随机数;接着如果dealer==21,玩家输;如果dealer>21,则玩家赢,这个时候其实玩家也可能>21了,只不过先判断了dealer输;

判断玩家\<21&&玩家选择Stay:这个时候相当于玩家半等死;不断执行dealer()(当然dealer函数中表示如果total>17就不再add了);这时先比较玩家和dealer的大小,然后再判断dealer是否超过了21…..(这个游戏规则);

然而并没有什么卵用…………..只是想吐槽一下这个游戏规则,虽然这个规则在看似不合理中却也还是比较合理的,玩家和dealer都有占便宜的机会…….

2.我的第二个思路:

玩了很久的这个游戏,我突然想到这里用到的是随机数,于是着重看了源代码中的srand()和rand(),并试图从这上边找到突破点。

令我开心的是,我写了下边一段代码来寻找伪随机数中的问题:

#include<time.h>
#include<stdio.h>
void func();
int main()
{
    int i=0;
    func();
    printf("simple seed:\n");
    srand((unsigned)time(NULL));
    for(i=0;i<200;i++)
    {
        printf("%d ",(rand()%13)+1);
    }
    printf("\n");
    for(i=0;i<20;i++)
    {
        if(i%4==0)
        {
            func();
        }
    }
    return 0;
}
void func()
{
    int i=0;
    printf("func random:\n");
    srand((unsigned)time(NULL));
    for(i=0;i<200;i++)
    {
        printf("%d ",(rand()%13+1));
    }
    printf("\n");
}

我发现,当我多次重复下边这部分时,产生相同的伪随机数

srand((unsigned)time(NULL));
rand()%13+1;

于是我开心的以为这个题目中,如果我能预知双方下一张牌是什么的话,1000,000当然不在话下,可以一夜暴富了。

但是我对游戏代码进行输出测试的时候,发现并不是我想象的那样啊。它们为什么产生的伪随机数列不同呢?可是我的测试代码却相同呢?于是查阅资料并且重新对我的代码进行修改测试后得到这样结论:

srand((unsigned)time(NULL))是取当前时间作为时间种子的,但是cpu执行一段简单的代码或者一个for循环的时间太短了,以至于time(NULL)获得的当前时间是一样的,所以才会产生相同的伪随机数列。然而,由于游戏当中,玩家的操作以及选择都需要时间,所以每次的时间种子都是不一样的。out

我在测试代码中加了几个sleep(),发现事实确实如此….经过一段时间后,它们不再产生相同的随机数列了。

收获

1.sleep()的用法:执行挂起一段时间;在VC中使用头文件(windows.h),sleep的首字母大写,参数单位是毫秒;在gcc编译器中,linux环境下(unistd.h),首字母不大写,参数单位是秒。

2.常见的srand()的用法:srand((unsigned)time(NULL));srand(time(0))(前一种的缩写);srand((int)getpid())(使用当前程序的ID来作为初始化种子,因此在同一个程序中这个种子是固定的)

3.别打我……英语中spade(黑桃)heart(桃心)club(梅花)diamond(方块)

4.system(“cls”)和system(“pause”):system()作用是调用系统命令;声明与stdlib.h,形式为int system(char* cmd),执行cmd中的命令;cmd中”cls”的作用清除屏幕的输出;”pause”是暂停。

疑惑

1.在asktitile函数中玩家输入N,程序有友好的退出界面;但是在askover中的N,程序闪退,没有输出期望的内容?

2.发现如果不输入bet值的话,在enter bet:后边直接输入H或S,赌注是我上一次的赌注,而且程序是可以继续运行的?

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值