記錄一次菜的摳腳的學習過程
題目是一個bin文件,首先通過file命令看一下文件格式
$ file blend/main.bin
blend/main.bin: DOS/MBR boot sector
拖到IDA Pro 可以識別其中的匯編梦鉴,但需要快捷鍵p創(chuàng)建函數(shù)才可以使用空格揭保,靜態(tài)可能看不太明白,因為它和我們常見的程序不太一樣存筏,函數(shù)調(diào)用都是通過軟件斷點形式實現(xiàn)味榛,動態(tài)調(diào)一下就會明白程序流程。
程序的調(diào)試可以使用qemu
$ qemu-system-i386 -s -drive format=raw,file=./main.bin
程序就可以執(zhí)行起來藕溅,可以通過gdb進行調(diào)試继榆,程序默認加載地址為0x7c00,所以下端點的時候地址為偏移地址+0x7c00集币。由于是16為匯編所以gdb的插件可能會有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,否則匯編也會出錯跨细。其他就沒什么區(qū)別了冀惭,正常調(diào)試即可。通過調(diào)試可知其實程序一直會在一個循環(huán)里運行散休,默認走綠色分支戚丸。
當輸入夠20個字符會進入紅色的分支,輸入保存在0x1234處猴鲫,這一分支也就是對輸入flag進行驗證的地方谣殊,也是需要我們重點關注的部分。程序首先會對前4字節(jié)進行判斷宜狐,靜態(tài)可知前4字節(jié)為flag蛇捌。緊接著對0x1238處的輸入進行驗證,匯編為SSE浮點匯編俭驮,沒接觸過的可能有些難度春贸,多用Google搜索,看幾個例子相信你對每句匯編就理解了萍恕。程序首先會對你的輸入進行一個亂序處理,實現(xiàn)這一操作的是pshufd指令允粤,如果可以動態(tài)調(diào)試其實可以不用理細節(jié)翼岁,可以直接看執(zhí)行完的xmm0寄存器琅坡。緊接著和內(nèi)存中的數(shù)進行相與
0xffffffffffffff00 0xffffffffffffff00
0xffffffffffff00ff 0xffffffffffff00ff
0xffffffffff00ffff 0xffffffffff00ffff
0xffffffff00ffffff 0xffffffff00ffffff
0xffffff00ffffffff 0xffffff00ffffffff
0xffff00ffffffffff 0xffff00ffffffffff
0xff00ffffffffffff 0xff00ffffffffffff
0x00ffffffffffffff 0x00ffffffffffffff
寫出來應該可以看明白相與之后的結果了吧飘庄。相與之后與xmm5做一個絕對差求和的運算购撼,將最終的結果與程序的數(shù)據(jù)進行比較
0x03110304
0x02d902cd
0x02d402db
0x02c402e2
0x02ce02e2
0x02d802ed
0x02dc02e8
0x02dd02f6
注意這三句匯編其實它的效果是進行了一次高低位交換
如果相等繼續(xù)下次循環(huán)迂求,一共要循環(huán)8次,每次循環(huán)xmm0不變揩局,變得只有xmm5凌盯,xmm5其實就是上一次循環(huán)的結果。所以整個算法邏輯就是這樣驰怎,如果可以動態(tài)調(diào)試是很好理解的县忌,最初沒找到合適的調(diào)試方法,靜態(tài)看有點吃力症杏。
要解整個題很難逆推回去,因為算法不是一個可逆算法穴豫。所以自然想到暴力破解,當然暴力破解也有技巧绩郎,可以借助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')