[2018看雪]
在之前插播一個數學知識:
一個在素數p下的有限域:GF(p) = {0,1,2,3,……,p-1},加法運算是模p加,乘法運算是模p乘
數學上可以證明對於在GF(p)中的每一個元素都存在加法逆元和乘法逆元,因此是可以做四則運算的
加法 = 加法,減法 = 加上 加法逆元,乘法 = 乘法,除法 = 乘以 乘法逆元
在有限域裡,會出現特殊的“迴圈”的現象:
任意一個數x屬於GF(p),一直加上某個數y,加p次後一定會回到x
舉例如下:令p = 7,x = 3,y = 4
GF(p) = {0,1,2,3,4,5,6},當x = 3,y = 4時,我們產生的數列會是3,0,4,1,5,2,6,3,……(7次一定返回)
知道這個數學特點,理解之後的官方wp比較好懂
貼幾個連結:
自己在比賽的時候根據題目提示讀明白了題,但是沒想明白怎麼做,賽後來複現
main中邏輯很簡單,進入401050中,一堆賦值先不管,接下來是判斷輸入合法性(是否為字母加數字)
接下來這個迴圈理解很精髓,看圖:
根據題目提示,每個人都會在其他兩個人的幫助下去上樓,意思是說,(i,j,k)這個變化是有規律的,而且迴圈次數告訴我們,確實走了這麼多層,之後進行的是正確性檢查
所以,之前的賦值我們要去搞明白上樓該怎麼理解
OD中下斷,發現是在堆疊裡賦值,觀察之後發現連續四個DWORD之中只會出現3個1
16 * 16的表格,每行3個1,即3個有用的資料
然後回頭看byte_40FEF0,這個陣列大小為 262144 = 64 * 64 * 64,翻一翻資料,值為0 - 0x40
即(i,j,k)座標控制字元在[0,0x3F]內變換
根據這樣的規則變換20次,然後check一下,且中間有某個值會是正確性的顯示,知道有限域的數學結論,所以可以大膽猜想,既然輸入可以得到flag,flag在相同變換規則下,也可以得到輸入
變換規則相同,資料會是一個迴圈,只是什麼時候會出現相同資料我們不知道(也就是有限域中資料的個數不定)
#coding:utf-8 p = open('./Escape.exe','rb') s = p.read() p.close() change_table = [] #offset = fef0 - e4f0, just find data in Winhex for i in s[0xe4f0:0xe4f0 + 262144]: change_table.append(ord(i)) #print change_table[0:0x40] result = [] #for i in s[0xfee0 - 0x1a00:0xfee0 -0x1a00 + 16]: for i in s[0xe4e0:0xe4e0 + 16]: result.append(ord(i)) #print result def reverse(num): i = num / (64 * 64) j = (num / 64) % 64 k = num % 64 return [i,j,k] def realnum(i,j,k): return 64 * 64 * i + 64 * j + k def con(n): ch = 'abcdefghijklmnopqrstuvwxyz+-ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789' data = [] for i in range(len(n)): for j in range(len(ch)): if n[i] == ch[j]: data.append(j) break return data def recon(n): s = '' ch = 'abcdefghijklmnopqrstuvwxyz+-ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789' for i in n: s += ch[i] return s def encrypt(n): data=[] data.append(change_table[realnum(n[0x0],n[0x1],n[0x2])]) data.append(change_table[realnum(n[0x3],n[0x4],n[0x0])]) data.append(change_table[realnum(n[0x5],n[0x6],n[0x0])]) data.append(change_table[realnum(n[0x5],n[0x7],n[0x1])]) data.append(change_table[realnum(n[0x4],n[0x6],n[0x1])]) data.append(change_table[realnum(n[0x8],n[0x2],n[0x3])]) data.append(change_table[realnum(n[0x9],n[0x2],n[0x4])]) data.append(change_table[realnum(n[0x7],n[0xa],n[0x3])]) data.append(change_table[realnum(n[0x8],n[0xc],n[0x5])]) data.append(change_table[realnum(n[0xb],n[0xd],n[0x6])]) data.append(change_table[realnum(n[0xc],n[0xd],n[0x7])]) data.append(change_table[realnum(n[0xb],n[0xe],n[0x9])]) data.append(change_table[realnum(n[0xe],n[0x8],n[0xa])]) data.append(change_table[realnum(n[0xf],n[0x9],n[0xa])]) data.append(change_table[realnum(n[0xf],n[0xb],n[0xc])]) data.append(change_table[realnum(n[0xf],n[0xd],n[0xe])]) return data tmp = result for i in range(0x500): print tmp,recon(tmp) tmp = encrypt(tmp)
學到了幾種姿勢:怎麼扣資料,怎麼爆破把
把跑出來的結果放到一個txt中,然後搜尋下~~
根據題目意思,運算20層,從449層倒數算到430層,就可以看到需要的flag
可以看到這個變換是448個“數”一個輪迴