defcon-quals 2023 crackme.tscript.dso wp

2023-05-31 06:00:23

隊友找到的引擎TorqueGameEngines/Torque3D (github.com)

將dso檔案放到data/ExampleModule目錄下,編輯ExampleModule.tscript檔案

function ExampleModule::onCreate(%this)
{
    trace(true);
    exec("./crackme");
    __main("aaaaaaaa");
    quit();
}

然後點選主目錄下的Torque3D-debug.bat就可以在生成的console.log檔案裡看到輸出,發現使用的是getc獲取輸入,putc輸出。

下載原始碼,在Engine\source\console\console.cpp中找到執行dso檔案的位置

另外在CodeBlock這個類裡面看到了一個dumpInstructions函數可以dump指令於是在code->read後面加上一個code->dumpInstructions語句來檢視指令碼,結果發現其他函數都有輸出,只有我們的dso檔案沒有輸出,麻,看程式碼。

發現造成沒有輸出的原因是codeSize為0;

再看看read函數發現果然讀取檔案後沒有改變codeSize,有點小坑。

在這裡加上一句改寫codeSize。

這樣就可以輸出dso檔案位元組碼了,由於位元組碼過長這裡就不放了。

首先查詢getc被呼叫的位置,發現在getc後面有一些OP_CMPNE指令,猜測這裡是比較的地方,檢視exec函數裡實現OP_CMPNE的位置,發現是用doFloatMathOperation函數處理的,加上一句把比較的資料列印出來。

發現在IP=4125的地方有一個比較97與102的cmpne,97就是a的ascii碼,102是f的ascii碼,再看一下列印出的位元組碼發現結構很類似,首先getc然後再getc的地址+101處比較,然後比較處+18再次getc,這樣就可以該寫程式碼來獲取flag,更改處如下


然後再log中發現了

看來不是簡單的比較,接著看後面的位元組碼

簡單講一下指令的作用,其他指令基本一看就知道幹嘛的。

OP_LOAD_LOCAL_VAR_UINT stk=+1 reg=4  --> push reg[4];
OP_SAVE_LOCAL_VAR_UINT stk=0 reg=7   --> mov reg[7], stack[sp];
OP_POP_STK                           --> sp--

發現後面的處理其實跟前面差不多隻是把cmpne變成了sub,偏移變成了103,再修改一下程式碼得到第二部分的flag

接著看後面的指令,重點關注呼叫了doIntOperation和doFloatMathOperation的指令

而後面每兩個getc之間的指令就變得特別多了達到了幾萬條,有限時間內肉眼看是基本上看不出來了,開始亂猜大法。

發現一個可疑的指令後面load了一個108,查詢規律,發現應該是這個,偏移為距getc+61處,然後發現其實上面的也可以在這裡獲取到。

獲取到第三部分flag

flag{vmprotect?_where_we_re_going_we_ll_need_protecti

再看下後面的指令,發現先將所有的資料全加起來跟減去1327

新增以下程式碼仔細觀察資料操作指令


將輸入改為flag{vmprotect?_where_we_re_going_we_ll_need_protecti0123456789abcdefghijklmnopq}輸出為

再往後發現每次從輸入中去除一個然後累加再減去以下資料然後判斷等不等於0,這裡的16776909等是因為signed型別的負數與0xffffff與得到結果,所以是個解方程問題,z3一把梭

[1327, 1394, 1332, 1347, 1372, 1360, 1394, 1365, 1333, 1347, 1326, 1338, 1391, 1347, 1324, 1333]
from z3 import *
sub = [1327, 1394, 1332, 1347, 1372, 1360, 1394, 1365, 1333, 1347, 1326, 1338, 1391, 1347, 1324, 1333]
s = Solver()

flag = Ints("flag%d" %i for i in range(16))
for i in range(16):
    s.add(flag[i] >0)
    s.add(flag[i] <128)
exceptIndex = 14
for i in range(16):
    result = 0
    exceptIndex = (exceptIndex + 1) % 16
    for j in range(16):
        if j != exceptIndex:
            result += flag[j]
    s.add(result == sub[i])

if s.check() == sat:
    print("find")
    m = s.model()
    f = ""
    for i in range(16):
        f += chr(m[flag[i]].as_long())
    print(f)

得到flag

flag{vmprotect?_where_we_re_going_we_ll_need_protecti0n_FR0Mm_th3_vms}

只要跟蹤每個getc後面的指令就可以分析清楚整個流程。