2019.8.4雷火笔试第五题 黑客行动(98%)

前言

万万没想到,游戏的笔试居然出了道逆向的题来作压轴,要不是这道题,我原以为这辈子都不会碰这些逆向的工具=。=

题目

给出二进制文件,功能是输入0到1的x,输出f(x),输入输出均是浮点数。

自己实现F(x),可以和二进制文件输出同样的结果。

二进制文件在http://59.111.13.242/leihuo_2019_guess.zip下载

思路

先跑几遍guess_windows.exe

在这里插入图片描述

分析

可以看出,当且仅当输入为0~1之间的数时,会进行运算,当输大于等于1或者小于0时,输出error并退出

用x64dbg加载guess_windows.exe

不停的下断点找到主函数入口:
在这里插入图片描述
我们手动运行,步进到第一个红框处,输入测试数据:0.55555
由于输入的是合法数据,因此我们可以跳转到计算f(x)的函数
在这里插入图片描述

其中,我们可以看到f(x)函数应该是这句:

call guess_windows_FC8761

我们跟进去看看:
在这里插入图片描述

//数据段中的常数
ds:[1080EA8] 
ds:[1080E98]
ds:[1080E68]
ds:[1080E78]
ds:[1080E88]
...

上图中的fmul, fld fsubp等都是浮点数运算指令,其中的数据段数据ds:[xxxxxx]的应该就是隐藏在程序中的常数,理论上我们到相应内存地址里去找相应的数据万事大吉了。

但问题是内存中的数据是16进制,转成十进制还好说,转浮点数就有点费劲了,不急,我们慢慢调试来看:

我们先来看执行完下面这句后:

fld st(0), dword ptr ss:[ebp+8]

在这里插入图片描述
寄存器st(0)载入了0.55555,即我们输入的x,

那么看来

dword ptr ss:[ebp+8]//这个就是我们输入x的地址

回到计算f(x)的反汇编的核心代码,我们不难猜想f(x)的大致结构:
F(x)=ax4−bx3+cx2+dx+eF(x) = ax^4-bx^3 + cx^2+dx+eF(x)=ax4bx3+cx2+dx+e
其中的常数a, b, c, d, e应该就是下面这几个了

//数据段中的常数
ds:[1080EA8] 
ds:[1080E98]
ds:[1080E68]
ds:[1080E78]
ds:[1080E88]

在动态一步步调试的时候, 每一步的计算结果会实时反映在寄存器st(0)的变化中,如下:

在执行完下面这句:

fmul st(0),dword ptr ds:[1080EA8] 

在这里插入图片描述

也就是算完ax,其中x=0.55555ax, 其中x=0.55555ax,x=0.55555, 我们的**寄存器st(0)**变为:22.42810821068


0.55555a=22.428108210680.55555a =22.42810821068 0.55555a=22.42810821068
我们可以手动算出aaa的值, 约40左右的一个常数,但是这样太麻烦了,

并且因为float在寄存器中的存储并不精确(0.55555都变成了0.555549979…),我们无法知道做除法的时候该保留几位才合适,因此这种方法并不合适。

慢!

回想一下上面的式子F(x)=ax4−bx3+cx2+dx+eF(x) = ax^4-bx^3 + cx^2+dx+eF(x)=ax4bx3+cx2+dx+e如果我们输入x为1,那么在调试的时候,ax=aax = aax=a
我们直接可以从寄存器的窗口中读取a,b,c,d,ea, b, c, d, ea,b,c,d,e的值,岂不是会变得很简单!

但是,1是非法输入,在进到f(x)函数前便会退出,因此我们需要把1变成合法输入,然后进入f(x)

爆破

重新Reload程序,在窗口中输入1,运行到
在这里插入图片描述
我们修改反汇编代码,把jne改成jmp,直接跳到f(x)函数:
在这里插入图片描述
st(0)寄存器的值为1,说明跳转成功!
在这里插入图片描述
再执行下面一条语句,寄存器st(0)应该就会显示aaa的值了

fmul st(0),dword ptr ds:[1080EA8] 

在这里插入图片描述
可以看到a的值为40.371,与上面输入0.55555时预估的40左右差不多, 说明此方法可行!
在这里插入图片描述
同理,我们可以得到剩余b,c,d,eb, c, d, eb,c,d,e的值,这里不再赘述。

五个常数值分别为:
a=40.371,b=36.819,c=0.378,d=0.3855,e=3.0521a=40.371, b =36.819,c=0.378, d=0.3855, e=3.0521a=40.371,b=36.819,c=0.378,d=0.3855,e=3.0521
那么
F(x)=40.371x4−36.819x3+0.378x2+0.3855x+3.0521F(x) =40.371 x^4-36.819x^3 + 0.378x^2+0.3855x+3.0521 F(x)=40.371x436.819x3+0.378x2+0.3855x+3.0521

编写代码

下面的代码写好后,满以为100%AC,输入测试样例:0.268044

#include <stdio.h>
#include <math.h>
float f(float x)
{
    float a = 40.371, b = 36.819, c = 0.378, d = 0.3855, e = 3.0521;
    
    return a*pow(x, 4) - b*pow(x, 3) + c * pow(x, 2) + d * x + e;
    
}

int main()
{
    float x = 0;
    scanf("%f", &x);
    if (x <0.0 || x >=1.0)
    {
        printf("error\n");
        return 0;
    }
    printf("%0.6f", f(x));
    return 0;
}


在这里插入图片描述
在这里插入图片描述
齐活儿~~

提交
emmm…居然不是100%AC, 只有98%能过

后记

在交完卷后又反思了一下,试了0.999999(如下图),我的程序输出7.367545, 提供的程序输出7.367547,最后一位不一样
在这里插入图片描述
在这里插入图片描述
猜想计算f(x)的时候用float精度不够,于是把f(x)中的float全换成double

double f(float x)
{
    double a = 40.371, b = 36.819, c = 0.378, d = 0.3855, e = 3.0521;
    
    return a*pow(x, 4) - b*pow(x, 3) + c * pow(x, 2) + d * x + e;
    
}

测试用例:0.999999,输出变成:7.367547,可惜没机会再次提交了。
在这里插入图片描述

评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值