記錄一次菜的摳腳的學(xué)習(xí)過程
題目是一個(gè)bin文件,首先通過file命令看一下文件格式
$ file blend/main.bin
blend/main.bin: DOS/MBR boot sector
拖到IDA Pro 可以識(shí)別其中的匯編,但需要快捷鍵p創(chuàng)建函數(shù)才可以使用空格,靜態(tài)可能看不太明白,因?yàn)樗臀覀兂R姷某绦虿惶粯?,函?shù)調(diào)用都是通過軟件斷點(diǎn)形式實(shí)現(xiàn),動(dòng)態(tài)調(diào)一下就會(huì)明白程序流程。
程序的調(diào)試可以使用qemu
$ qemu-system-i386 -s -drive format=raw,file=./main.bin
程序就可以執(zhí)行起來,可以通過gdb進(jìn)行調(diào)試,程序默認(rèn)加載地址為0x7c00,所以下端點(diǎn)的時(shí)候地址為偏移地址+0x7c00。由于是16為匯編所以gdb的插件可能會(huì)有bug,只是pwndbg是這樣,建議使用原生gdb,gdb調(diào)試流程如下
$ gdb
GNU gdb (Ubuntu 7.11.1-0ubuntu1~16.5) 7.11.1
Copyright (C) 2016 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law. Type "show copying"
and "show warranty" for details.
This GDB was configured as "x86_64-linux-gnu".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>.
Find the GDB manual and other documentation resources online at:
<http://www.gnu.org/software/gdb/documentation/>.
For help, type "help".
Type "apropos word" to search for commands related to "word".
(gdb) set architecture i8086
warning: A handler for the OS ABI "GNU/Linux" is not built into this configuration
of GDB. Attempting to continue with the default i8086 settings.
The target architecture is assumed to be i8086
(gdb) set disassembly-flavor intel
(gdb) target remote:1234
注意要執(zhí)行這句set architecture i8086,否則匯編也會(huì)出錯(cuò)。其他就沒什么區(qū)別了,正常調(diào)試即可。通過調(diào)試可知其實(shí)程序一直會(huì)在一個(gè)循環(huán)里運(yùn)行,默認(rèn)走綠色分支。

當(dāng)輸入夠20個(gè)字符會(huì)進(jìn)入紅色的分支,輸入保存在0x1234處,這一分支也就是對(duì)輸入flag進(jìn)行驗(yàn)證的地方,也是需要我們重點(diǎn)關(guān)注的部分。程序首先會(huì)對(duì)前4字節(jié)進(jìn)行判斷,靜態(tài)可知前4字節(jié)為flag。緊接著對(duì)0x1238處的輸入進(jìn)行驗(yàn)證,匯編為SSE浮點(diǎn)匯編,沒接觸過的可能有些難度,多用Google搜索,看幾個(gè)例子相信你對(duì)每句匯編就理解了。程序首先會(huì)對(duì)你的輸入進(jìn)行一個(gè)亂序處理,實(shí)現(xiàn)這一操作的是pshufd指令,如果可以動(dòng)態(tài)調(diào)試其實(shí)可以不用理細(xì)節(jié),可以直接看執(zhí)行完的xmm0寄存器。緊接著和內(nèi)存中的數(shù)進(jìn)行相與
0xffffffffffffff00 0xffffffffffffff00
0xffffffffffff00ff 0xffffffffffff00ff
0xffffffffff00ffff 0xffffffffff00ffff
0xffffffff00ffffff 0xffffffff00ffffff
0xffffff00ffffffff 0xffffff00ffffffff
0xffff00ffffffffff 0xffff00ffffffffff
0xff00ffffffffffff 0xff00ffffffffffff
0x00ffffffffffffff 0x00ffffffffffffff
寫出來應(yīng)該可以看明白相與之后的結(jié)果了吧。相與之后與xmm5做一個(gè)絕對(duì)差求和的運(yùn)算,將最終的結(jié)果與程序的數(shù)據(jù)進(jìn)行比較
0x03110304
0x02d902cd
0x02d402db
0x02c402e2
0x02ce02e2
0x02d802ed
0x02dc02e8
0x02dd02f6
注意這三句匯編其實(shí)它的效果是進(jìn)行了一次高低位交換

如果相等繼續(xù)下次循環(huán),一共要循環(huán)8次,每次循環(huán)xmm0不變,變得只有xmm5,xmm5其實(shí)就是上一次循環(huán)的結(jié)果。所以整個(gè)算法邏輯就是這樣,如果可以動(dòng)態(tài)調(diào)試是很好理解的,最初沒找到合適的調(diào)試方法,靜態(tài)看有點(diǎn)吃力。
要解整個(gè)題很難逆推回去,因?yàn)樗惴ú皇且粋€(gè)可逆算法。所以自然想到暴力破解,當(dāng)然暴力破解也有技巧,可以借助z3,可以提高效率。直接上最后的python腳本(python3執(zhí)行)
from z3 import *
s = Solver()
a = Int('a')
b = Int('b')
c = Int('c')
d = Int('d')
e = Int('e')
f = Int('f')
g = Int('g')
h = Int('h')
i = Int('i')
j = Int('j')
k = Int('k')
l = Int('l')
m = Int('m')
n = Int('n')
o = Int('o')
p = Int('p')
s.add(a < 127)
s.add(b < 127)
s.add(c < 127)
s.add(d < 127)
s.add(e < 127)
s.add(f < 127)
s.add(g < 127)
s.add(h < 127)
s.add(i < 127)
s.add(j < 127)
s.add(k < 127)
s.add(l < 127)
s.add(m < 127)
s.add(n < 127)
s.add(o < 127)
s.add(p < 127)
s.add(a > 32)
s.add(b > 32)
s.add(c > 32)
s.add(d > 32)
s.add(e > 32)
s.add(f > 32)
s.add(g > 32)
s.add(h > 32)
s.add(i > 32)
s.add(j > 32)
s.add(k > 32)
s.add(l > 32)
s.add(m > 32)
s.add(n > 32)
s.add(o > 32)
s.add(p > 32)
def abs(x):
return If(x >= 0,x,-x)
s.add(abs(d-0x22)+abs(c-0xf)+abs(b-0x2)+abs(a-0xc8)+abs(h-0x83)+abs(g-0xfb)+abs(f-0xe0)+abs(0-0x83) ==0x304)
s.add(abs(p-0xc0)+abs(o-0x20)+abs(n-0xf)+abs(m-0x10)+abs(l-0xcd)+abs(k-0x00)+abs(j-0x13)+abs(0-0xb8) ==0x311)
s.add(abs(d-0x0)+abs(c-0x0)+abs(b-0x0)+abs(a-0x0)+abs(h-0x0)+abs(g-0x0)+abs(0-0x3)+abs(e-0x4) ==0x2cd)
s.add(abs(p-0x0)+abs(o-0x0)+abs(n-0x00)+abs(m-0x0)+abs(l-0x0)+abs(k-0x00)+abs(0-0x3)+abs(i-0x11) ==0x2d9)
s.add(abs(d-0x0)+abs(c-0x0)+abs(b-0x0)+abs(a-0x0)+abs(h-0x0)+abs(0-0x0)+abs(f-0x2)+abs(e-0xcd) ==0x2db)
s.add(abs(p-0x0)+abs(o-0x0)+abs(n-0x0)+abs(m-0x0)+abs(l-0x0)+abs(0-0x00)+abs(j-0x2)+abs(i-0xd9) ==0x2d4)
s.add(abs(d-0x0)+abs(c-0x0)+abs(b-0x0)+abs(a-0x0)+abs(0-0x0)+abs(g-0x0)+abs(f-0x2)+abs(e-0xdb) ==0x2e2)
s.add(abs(p-0x0)+abs(o-0x0)+abs(n-0x0)+abs(m-0x0)+abs(0-0x0)+abs(k-0x00)+abs(j-0x2)+abs(i-0xd4) ==0x2c4)
s.add(abs(d-0x0)+abs(c-0x0)+abs(b-0x0)+abs(0-0x0)+abs(h-0x0)+abs(g-0x0)+abs(f-0x2)+abs(e-0xe2) ==0x2e2)
s.add(abs(p-0x0)+abs(o-0x0)+abs(n-0x0)+abs(0-0x0)+abs(l-0x0)+abs(k-0x00)+abs(j-0x2)+abs(i-0xc4) ==0x2ce)
s.add(abs(d-0x0)+abs(c-0x0)+abs(0-0x0)+abs(a-0x0)+abs(h-0x0)+abs(g-0x0)+abs(f-0x2)+abs(e-0xe2) ==0x2ed)
s.add(abs(p-0x0)+abs(o-0x0)+abs(0-0x0)+abs(m-0x0)+abs(l-0x0)+abs(k-0x00)+abs(j-0x2)+abs(i-0xce) ==0x2d8)
s.add(abs(d-0x0)+abs(0-0x0)+abs(b-0x0)+abs(a-0x0)+abs(h-0x0)+abs(g-0x0)+abs(f-0x2)+abs(e-0xed) ==0x2e8)
s.add(abs(p-0x0)+abs(0-0x0)+abs(n-0x0)+abs(m-0x0)+abs(l-0x0)+abs(k-0x00)+abs(j-0x2)+abs(i-0xd8) ==0x2dc)
s.add(abs(0-0x0)+abs(c-0x0)+abs(b-0x0)+abs(a-0x0)+abs(h-0x0)+abs(g-0x0)+abs(f-0x2)+abs(e-0xe8) ==0x2f6)
s.add(abs(0-0x0)+abs(o-0x0)+abs(n-0x0)+abs(m-0x0)+abs(l-0x0)+abs(k-0x00)+abs(j-0x2)+abs(i-0xdc) ==0x2dd)
if s.check() == sat:
model = s.model()
answer = [model[a],model[b],model[c],model[d],model[e],model[f],model[g],model[h], model[i],model[j],model[k],model[l],model[m],model[n],model[o],model[p],0]
flag = ""
answer_str = ""
for i in range(len(answer)-1):
answer_str += chr(int(str(answer[i])))
print(answer_str)
else:
print('unsat')