前言
萬萬沒想到,遊戲的筆試居然出了道逆向的題來作壓軸,要不是這道題,我原以爲這輩子都不會碰這些逆向的工具=。=
題目
輸入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)的大致結構:
其中的常數a, b, c, d, e應該就是下面這幾個了
//數據段中的常數
ds:[1080EA8]
ds:[1080E98]
ds:[1080E68]
ds:[1080E78]
ds:[1080E88]
在動態一步步調試的時候, 每一步的計算結果會實時反映在寄存器st(0)的變化中,如下:
在執行完下面這句:
fmul st(0),dword ptr ds:[1080EA8]
也就是算完, 我們的**寄存器st(0)**變爲:22.42810821068…
即
我們可以手動算出的值, 約40左右的一個常數,但是這樣太麻煩了,
並且因爲float在寄存器中的存儲並不精確(0.55555都變成了0.555549979…),我們無法知道做除法的時候該保留幾位才合適,因此這種方法並不合適。
慢!
回想一下上面的式子如果我們輸入x爲1,那麼在調試的時候,
我們直接可以從寄存器的窗口中讀取的值,豈不是會變得很簡單!
但是,1是非法輸入,在進到f(x)函數前便會退出,因此我們需要把1變成合法輸入,然後進入f(x)
爆破
重新Reload程序,在窗口中輸入1,運行到
我們修改反彙編代碼,把jne改成jmp,直接跳到f(x)函數:
st(0)寄存器的值爲1,說明跳轉成功!
再執行下面一條語句,寄存器st(0)應該就會顯示的值了
fmul st(0),dword ptr ds:[1080EA8]
可以看到a的值爲40.371,與上面輸入0.55555時預估的40左右差不多, 說明此方法可行!
同理,我們可以得到剩餘的值,這裏不再贅述。
五個常數值分別爲:
那麼
編寫代碼
下面的代碼寫好後,滿以爲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,可惜沒機會再次提交了。