在之前插播一个数学知识:
一个在素数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比较好懂
贴几个链接:
https://bbs.pediy.com/thread-229393.htm
https://bbs.pediy.com/thread-229412.htm
自己在比赛的时候根据题目提示读明白了题,但是没想明白怎么做,赛后来复现
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个“数”一个轮回