2019.8.4雷火筆試第五題 黑客行動(98%)

前言

萬萬沒想到,遊戲的筆試居然出了道逆向的題來作壓軸,要不是這道題,我原以爲這輩子都不會碰這些逆向的工具=。=

題目

輸入0到1的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)=ax4bx3+cx2+dx+eF(x) = ax^4-bx^3 + cx^2+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.55555, 我們的**寄存器st(0)**變爲:22.42810821068


0.55555a=22.428108210680.55555a =22.42810821068
我們可以手動算出aa的值, 約40左右的一個常數,但是這樣太麻煩了,

並且因爲float在寄存器中的存儲並不精確(0.55555都變成了0.555549979…),我們無法知道做除法的時候該保留幾位才合適,因此這種方法並不合適。

慢!

回想一下上面的式子F(x)=ax4bx3+cx2+dx+eF(x) = ax^4-bx^3 + cx^2+dx+e如果我們輸入x爲1,那麼在調試的時候,ax=aax = a
我們直接可以從寄存器的窗口中讀取a,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)應該就會顯示aa的值了

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

在這裏插入圖片描述
可以看到a的值爲40.371,與上面輸入0.55555時預估的40左右差不多, 說明此方法可行!
在這裏插入圖片描述
同理,我們可以得到剩餘b,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.0521
那麼
F(x)=40.371x436.819x3+0.378x2+0.3855x+3.0521F(x) =40.371 x^4-36.819x^3 + 0.378x^2+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,可惜沒機會再次提交了。
在這裏插入圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章