1. 程式人生 > >[2018看雪]

[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個“數”一個輪迴