Back
Featured image of post TSGCTF2021 and pbctf2021 RE writeups

TSGCTF2021 and pbctf2021 RE writeups

和队友两人一起打的两场比赛

TSGCTF2021

Beginners Rev

fork() 遍历了 32 位输入,每个输入都分别进行一次函数运算

  do
  {
    ++v2;
    if ( !fork() )
    {
      v2 = 0;
      v1 |= 1 << v3;
      v7 = open("/dev/null", 1);
      dup2(v7, 1);
    }
    ++v3;
  }
  while ( v3 != 5 );

每个函数运算都是将输入迭代好多次

  v4 = (unsigned __int8 *)&key + a2;
  v5 = v4[2];
  v6 = 1;
  while ( (unsigned int)(-1217102449 * v5 + 1217102449) > 0xB2927C )
  {
    ++v6;
    v5 += v4[2];
    if ( v6 == 367 )
    {
      v6 = -1;
      break;
    }
  }
  v7 = v4[4];
  v8 = v4[3] * ((a1 + *v4) % 367 * v4[1] % 367 * v6 % 367) % 367;

被retaddr坑了一会,后面才反应过来开头那个 if ( retaddr - (_BYTE *)check != 95 ) 的意思

解法就是先将while循环内的运算解出来,然后用z3求解就行

from z3 import *

key = [
    0x9E, 0xA5, 0x43, 0x3C, 0x3D, 0xE5, 0x50, 0x95, 0x29, 0xFB, 
    0x03, 0x34, 0xF6, 0x6D, 0xF7, 0x9A, 0x5E, 0x8A, 0x6F, 0x0F, 
    0xAE, 0x6A, 0x78, 0x41, 0x02, 0x46, 0x8B, 0xAE, 0xB6, 0x83, 
    0x09, 0x4F, 0x54, 0x74, 0x8D, 0xF4, 0x79, 0xD2, 0xFE, 0x2D, 
    0x78, 0x1B, 0x11, 0x57, 0xB7, 0x9F, 0x4E, 0xC4, 0x52, 0x9E, 
    0xF5, 0xFF, 0x56, 0x71, 0x3C, 0x1B, 0x60, 0x22, 0x9C, 0x56, 
    0xA7, 0xCF, 0x8E, 0x45, 0x16, 0x5C, 0xA5, 0xF4, 0x28, 0xA0, 
    0x30, 0x57, 0xA5, 0xB1, 0xC9, 0xC4, 0x86, 0x3E, 0xB8, 0x13, 
    0x44, 0x4D, 0xBF, 0x97, 0xE4, 0x06, 0x96, 0x07, 0x8B, 0x9F, 
    0x52, 0x12, 0x92, 0xC6, 0xC0, 0x8A, 0x69, 0xF5, 0xA5, 0x9D, 
    0xF3, 0x3B, 0xB6, 0x99, 0x86, 0xD9, 0x67, 0x32, 0xB1, 0xBF, 
    0xB8, 0x2E, 0x58, 0x55, 0xB0, 0x9C, 0x65, 0x9E, 0x9F, 0xE3, 
    0xF0, 0xBF, 0xCF, 0xCD, 0xDE, 0xFD, 0x34, 0x31, 0x78, 0x55, 
    0x6E, 0x01, 0x74, 0xD7, 0xA8, 0x26, 0xFF, 0xD6, 0xCC, 0x99, 
    0x51, 0xFB, 0xF6, 0xF4, 0x03, 0xFA, 0x61, 0xDF, 0x41, 0x98, 
    0x0D, 0xBD, 0xBF, 0x88, 0x44, 0x5E, 0x56, 0xD2, 0xA9, 0x00
]

time = [
    0x127, 0x0ee, 0x07e, 0x068, 0x169, 0x0f2, 0x04e, 0x0ea, 
    0x0bc, 0x0ae, 0x0f5, 0x078, 0x114, 0x10a, 0x13b, 0x0ff, 
    0x052, 0x07d, 0x0cd, 0x031, 0x0fb, 0x142, 0x034, 0x060, 
    0x0b8, 0x0c2, 0x12d, 0x0fb, 0x0f4, 0x161, 0x0cc, 0x0df, 
    0x11c, 0x0c1, 0x0b1, 0x0b6, 0x05b, 0x0bb, 0x162, 0x105, 
    0x034, 0x044, 0x06c, 0x087, 0x16d, 0x151, 0x050, 0x117, 
    0x05e, 0x127, 0x003, 0x09a, 0x12f, 0x00d, 0x068, 0x044, 
    0x041, 0x036, 0x028, 0x12f, 0x0bd, 0x148, 0x0a8, 0x0fa, 
    0x13d, 0x004, 0x0ee, 0x0b6, 0x09c, 0x027, 0x082, 0x087, 
    0x0ee, 0x08d, 0x02a, 0x117, 0x03f, 0x094, 0x002, 0x03a, 
    0x01b, 0x08f, 0x062, 0x0af, 0x042, 0x132, 0x073, 0x069, 
    0x12d, 0x151, 0x05e, 0x066, 0x112, 0x04c, 0x0d8, 0x07d, 
    0x007, 0x003, 0x0ee, 0x0b4, 0x125, 0x038, 0x0f4, 0x00c, 
    0x03f, 0x0fc, 0x136, 0x159, 0x08d, 0x062, 0x002, 0x008, 
    0x0ab, 0x05f, 0x10d, 0x028, 0x102, 0x127, 0x151, 0x10e, 
    0x01a, 0x062, 0x148, 0x06f, 0x11e, 0x0eb, 0x078, 0x00f, 
    0x034, 0x05f, 0x165, 0x001, 0x0c1, 0x10c, 0x08e, 0x01d, 
    0x09a, 0x163, 0x009, 0x00c, 0x091, 0x0ae, 0x114, 0x0b6, 
    0x0f5, 0x045, 0x08c, 0x04f, 0x060, 0x063, 0x071, 0x0a7, 
    0x062, 0x0c5, 0x01b, 0x052, 0x12f, 0x0bb, 0x123, -1,
]

enc = [
    185, 180, 193, 107, 325, 148, 254, 342,
    56, 160, 338, 143, 58, 289, 362, 248,
    319, 339, 92, 66, 248, 72, 21, 229,
    122, 206, 101, 235, 113, 100, 45, 262
]

for i in range(len(enc)):
    s = Solver()
    a1 = Int("a1")
    s.add(a1 >= 0)
    s.add(a1 <= 128)
    v6 = time[i + 2]
    v8 = key[i + 3] * ((a1 + key[i + 0]) % 367 * key[i + 1] % 367 * v6 % 367) % 367
    v9 = time[i + 4]
    v11 = time[i + 7]
    v12 = (95 + (((v9 * v8 % 367 + 367 - key[i + 5]) % 367 + 367 - key[i + 6]) % 367)) % 367
    v13 = key[i + 9] * ((key[i + 8] + v11 * v12 % 367) % 367)
    v15 = time[i + 10]
    v16 = v13 % 367
    v17 = v15 * v16
    v19 = time[i + 11]
    v21 = (key[i + 16] + (key[i + 15] + key[i + 14] * (((key[i + 12] + v17 % 367 * v19 % 367) % 367 + 367 - key[i + 13]) % 367) % 367) % 367) % 367
    v22 = time[i + 17]
    v24 = (key[i + 19] + key[i + 18] * (v21 * v22 % 367) % 367) % 367
    v25 = time[i + 20]
    v26 = key[i + 28] + key[i + 27] * (((key[i + 25] + (key[i + 24] + key[i + 22] * ((v24 * v25 % 367 + 367 - key[i + 21]) % 367) % 367 * key[i + 23] % 367) % 367) % 367 + 367 - key[i + 26]) % 367) % 367
    v28 = time[i + 30]
    v29 = 95 + v26 % 367 * key[i + 29] % 367 - 367 * ((95 + (v26 % 367 * key[i + 29] % 367)) / 367)
    v30 = (v29 * v28 % 367 + 367 - key[i + 31]) % 367 + 367 - key[i + 32]
    v32 = time[i + 33]
    v34 = key[i + 35] * ((key[i + 34] + v30 % 367 * v32 % 367) % 367) % 367 * key[i + 36] % 367
    v35 = time[i + 37]
    v37 = (key[i + 43] * (((((key[i + 38] * (v34 * v35 % 367) % 367 + 367 - key[i + 39]) % 367 + 367 - key[i + 40]) % 367 + 367 - key[i + 41]) % 367 + 367 - key[i + 42]) % 0x16F) % 0x16F + key[i + 44]) % 0x16F
    v38 = time[i + 45]
    v40 = (key[i + 46] + (v37 * v38) % 367) % 367
    v41 = time[i + 47]
    v43 = (key[i + 49] + (key[i + 48] + v40 * v41 % 367) % 367) % 367
    v44 = time[i + 50]
    v46 = key[i + 55] * ((key[i + 54] + ((key[i + 52] + (key[i + 51] + v43 * v44 % 367) % 367) % 367 + 367 - key[i + 53]) % 367) % 367) % 367
    v47 = time[i + 56]
    v49 = (v46 * v47 % 367 + 367 - key[i + 57]) % 367
    v50 = time[i + 58]
    v52 = ((95 + v49 * v50 % 367) % 367 + key[i + 59]) % 367
    v53 = time[i + 60]
    v54 = (key[i + 63] + key[i + 62] * ((key[i + 61] + v52 * v53 % 367) % 367) % 367) % 367 * key[i + 64] % 367 + 367 - key[i + 65]
    v56 = time[i + 67]
    v57 = v56 * ((key[i + 66] + v54 % 367) % 367)
    v59 = time[i + 68]
    v61 = (key[i + 69] * (v57 % 367 * v59 % 367) % 367 + 367 - key[i + 70]) % 367
    v62 = time[i + 71]
    v64 = (key[i + 73] * (key[i + 72] * (v61 * v62 % 367) % 367) % 367 + 367 - key[i + 74]) % 367
    v65 = time[i + 75]
    v67 = (v64 * v65 % 367 + 367 - key[i + 76]) % 367
    v68 = time[i + 77]
    v70 = ((key[i + 79] + (v67 * v68 % 367 + 367 - key[i + 78]) % 367) % 367 + 367 - key[i + 80]) % 367
    v71 = time[i + 81]
    v73 = key[i + 85] * (((key[i + 82] * (v70 * v71 % 367) % 367 + 367 - key[i + 83]) % 367 + 367 - key[i + 84]) % 367) % 367
    v74 = time[i + 86]
    v76 = (v73 * v74 % 367 + 367 - key[i + 87]) % 367
    v77 = time[i + 88]
    v78 = key[i + 90] * (((95 + v76 * v77 % 367) % 367 + 367 - key[i + 89]) % 367)
    v79 = time[i + 98]
    v82 = time[i + 99]
    v84 = ((v82 * ((v79 * (((((((v78 % 367 + 367 - key[i + 91]) % 367 * key[i + 92] % 367 + 367 - key[i + 93]) % 367 + 367 - key[i + 94]) % 367 + 367 - key[i + 95]) % 367 + 367 - key[i + 96]) % 0x16F + key[i + 97]) % 0x16F)) % 367) % 367 + 367 - key[i + 100]) % 367 + 367 - key[i + 101]) % 367 + 367 - key[i + 102]
    v85 = time[i + 108]
    v86 = 95 + ((((v84 % 367 + 367 - key[i + 103]) % 0x16F + key[i + 104]) % 0x16F + 367 - key[i + 105]) % 0x16F * key[i + 106] % 0x16F + 367 - key[i + 107]) % 0x16F - 367 * ((95 + (((((v84 % 367 + 367 - key[i + 103]) % 0x16F + key[i + 104]) % 0x16F + 367 - key[i + 105]) % 0x16F * key[i + 106] % 0x16F + 367 - key[i + 107]) % 0x16F)) / 367)
    v87 = key[i + 113] * (((((key[i + 109] + v86 * v85 % 367) % 367 + 367 - key[i + 110]) % 367 + 367 - key[i + 111]) % 367 + 367 - key[i + 112]) % 367)
    v89 = time[i + 114]
    v90 = v89 * (v87 % 367)
    v92 = time[i + 115]
    v93 = v90 % 367 * v92
    v95 = time[i + 117]
    v97 = (key[i + 118] + (key[i + 116] + v93 % 367) % 367 * v95 % 367) % 367
    v98 = time[i + 119]
    v100 = v97 * v98 % 367
    v101 = time[i + 120]
    v103 = (v100 * v101 % 367 + 367 - key[i + 121]) % 367
    v104 = time[i + 122]
    v105 = (key[i + 124] * ((key[i + 123] + v103 * v104 % 367) % 367) % 367 + 367 - key[i + 125]) % 367 * key[i + 126]
    v107 = time[i + 127]
    s.add(enc[i] == ((v105 % 367 * v107 % 367) + 95) % 367)
    if s.check() == sat:
        print (chr(s.model()[a1].as_long()), end = '')
    else:
        print ("unsat")
print ()

跑得倒是挺快,就是一行一行复制有点累…

natural flag processing

队友查看了out的参数,发现只有第314位为1,于是猜测只要满足第314位输出大于0,就通过验证了(在 forward() 函数里加一行就验证了)

先只输入 TSGCTF{ 测试了一下,然后惊奇地发现,每次运行完 forward() 后,都会有一位大于0,于是猜测错误的输入会使得大于0的位消失,且消失后无法再得到大于0,这样就可以爆破了,大于0的数字消失就剪掉

队友用dfs,我就用了bfs,主要就是在之前的代码上进行一些魔改(感觉这个更像misc啊)

import string

import torch
from torch import nn

FLAG_CHARS = string.ascii_letters + string.digits + "{}-"
CHARS = "^$" + FLAG_CHARS
def sanity_check(text):
    global FLAG_CHARS
    assert text[:7] == "TSGCTF{"
    assert text[-1:] == "}"
    assert all([t in FLAG_CHARS for t in text])

def embedding(text):
    global CHARS
    x = torch.zeros((len(text), len(CHARS)))
    for i, t in enumerate(text):
        x[i, CHARS.index(t)] = 1.0
    return x

class Model(nn.Module):
    def __init__(self, inpt, hidden):
        super().__init__()
        self.cell = nn.RNNCell(inpt, hidden)
        self.out = nn.Linear(hidden, 1)
    def forward(self, xs):
        count = 0
        h = None
        global baopo
        global cur
        # print (xs)
        # print ('-------------------------------')
        for x in xs[:-1]:
            count += 1
            h = self.cell(x, h)
        if (len((h > 0).nonzero()) != 0):
            print (cur)
            baopo.append(cur)
        x = xs[-1]
        h = self.cell(x, h)
        return self.out(h)

count = 0

def inference(model, text):
    model.eval()
    with torch.no_grad():
        x = embedding("^"+text+"$").unsqueeze(1)
        y = model(x)[0].sigmoid().cpu().item()
    return y


# baopo = ['mRNA-st4nDs-f0r-mANuaLLy-tun3d-RecurrEn7-N3uRAl-AutoM4toN}']
baopo = ['m']

while True:
    i = baopo.pop(0)
    for j in FLAG_CHARS:
        model = Model(len(CHARS), 520)
        model.load_state_dict(torch.load("model_final.pth"))
    # text = input("input flag:")
    # sanity_check(text)
    # for i in range(400):
        text = 'TSGCTF{' + i + j
        # print (i, j)
        cur = i + j
        res = inference(model, text)
        if res > 0.5:
            print("Congrats!")
            exit(0)
            # print (cur)
        # else:
            # print (res)
            # print("Wrong.")

爆破的时间有点久

*optimized

这道题的UPX特征被完全去除了,以至于比赛的时候只看出来了是个壳,但没发现是UPX壳(其实猜到了,但elf就有点懒了…)

只需要打开010,将所有的 tsg_ 修改为 UPX! 就行

脱壳后发现就是简单的数学约束,例如

_mm_movemask_epi8(
  _mm_cmpeq_epi8(
    _mm_slli_si128((__m128i)0x9569uLL, 8),
    _mm_slli_si128(
      (__m128i)(((0x2AF91 * (unsigned __int128)(0x5F50DDCA7B17LL * (unsigned __int64)v8)) >> 64) & 0x3FFFF),
        8))) == 0xFFFF

去官网查了一下函数的定义,就是要求

(__m128i)(((0x2AF91 * (unsigned __int128)(0x5F50DDCA7B17LL * (unsigned __int64)v8)) >> 64) & 0x3FFFF) == (__m128i)0x9569uLL

本来想用c或者汇编的,但128位一直会有一些问题,于是只好用python了…

for i in range(0, 0xffffffff):
    if (0x9569 == (((i * 0x5F50DDCA7B17 & 0xffffffffffffffff) * 0x2AF91) >> 64) & 0x3FFFF) and (0x26CF2 == (((i * 0x4DC4591DAC8F & 0xffffffffffffffff) * 0x34AB9) >> 64) & 0x3FFFF):
        print (f'v8 = {i}', hex(i))
    if (0x20468 == (((i * 0x4AE11552DF1A & 0xffffffffffffffff) * 0x36B39) >> 64) & 0x3FFFF) and (0x3787A == (((i * 0x46680B140EFF & 0xffffffffffffffff) * 0x3A2D3) >> 64) & 0x3FFFF):
        print (f'v9 = {i}', hex(i))
    if (i * 0x4D935BBD3E0 & 0xffffffffffffffff < 0x4D935BBD3E0) and (0x5563 == (((i * 0x66B9B431B9ED & 0xffffffffffffffff) * 0x27DF9) >> 64) & 0x3FFFF):
        print (f'v10 = {i}', hex(i))
    if (i * 0x1E5D2BE81C5 & 0xffffffffffffffff < 0x1E5D2BE81C5) and (0x133E7 == (((i * 0x448626500938 & 0xffffffffffffffff) * 0x3BC65) >> 64) & 0x3FFFF):
        print (f'v11 = {i}', hex(i))
    if (i & 0xffffff == 0):
        print (f'time: {hex(i)}')

跑亿会就行

$ ./optimized-unpacked
Enter password: 772928896 2204180909 4273479145 1334930147
TSGCTF{F457_m0dul0!}@

pbctf2021

cosmo

main函数在 sub_403066

要求长度是38,简单看了一下加密逻辑,发现是两个字符一起加密并验证,还想用pintools试试,结果发现根本跑不起来

于是又开始了快乐的手写爆破,不过这个比较简单,动调一下就知道只用了最下面的几行加密逻辑

#include <stdio.h>

long long qword_40C000[20] = {
    21233875ll,
    69468586ll,
    146735755ll,
    251265871ll,
    379651085ll,
    536872170ll,
    719455639ll,
    924911196ll,
    1158088491ll,
    1412368333ll,
    1695680674ll,
    2005272944ll,
    2341407284ll,
    2698316511ll,
    3076262773ll,
    3483634782ll,
    3913551105ll,
    72486322ll,
    548474478ll,
    0ll
};

long long encrypt(char *a, int len, int cur){
    int v5 = len;
    int res = 0;
    long long v3 = qword_40C000[cur - 1];
    int v6 = v3 >> 16;
    int v7 = v3 & 0xffff;
    while(v5){
        v7 += *a;
        v6 += v7;
        --v5;
        ++a;
    }
    if (v7 > 0xFFF0)
        v7 -= 0xFFF1;
    return v7 | ((v6 % 0xFFF1) << 16);
}

int test_cnt(int cnt){
    for (int i = 0x20; i < 0x80; i++){
        for (int j = 0x20; j < 0x80; j++){
            char a[] = {i, j};
            if (encrypt(a, 2, cnt) == qword_40C000[cnt]){
                printf("%c%c", i, j);
                return 0;
            }
        }
    }
}

int main(){
    printf("pbctf{");
    for (int i = 3; i < 19; i++)
        test_cnt(i);
    printf("\n");
    return 0;
}

除了找main函数之外也没有什么特别的

BinaryTree

这题逻辑还是比较清晰的,就是根据800bit的输入进行了800层SMC

每一层都会对寄存器进行一个加法,然后决定下一个SMC的结果,大概是

  jz Label
  mov r8, [rdi + 0x____]    ; 具体寄存器忘了,随便写了个,中间一堆nop省略了
  add r9, 0x__
Label:
  mov r8, [rdi + 0x____]
  add r9, 0x__
  jmp SMC

最后一层就是对寄存器进行cmp,小于等于某个值就行

一开始还想着出题人仁慈一点,贪心就是最小(想直接用idapython自动跑),结果直接gg

队友对每一层的可能性进行了遍历,发现每一层最多有16种可能

于是这题就成了有向无环图求单源最短路径

求解代码可以分成三个部分:从SMC结果提取关键数据,建图,跑SPFA

其中SPFA是抄的,路径输出稍微修改一下就行

#include <algorithm>
#include <vector>
#include <queue>
#include <iostream>
#include <cstdlib>
#include <cstdio>
#include <cmath>
#include <cstring>

using namespace std;

#define INF 0xfffffff
#define MAXN 16 * 800 + 5

struct Cur_smc{
    char bytecode[0x30];
    int depth;
    int len[2];
};
Cur_smc cur_smc_code[MAXN];

char smc_xor_code[0xFFFFF];

struct Edge{
    int to;
    int len;
    int path;
    Edge(int to, int len, int path): to(to), len(len), path(path) {}
};

vector <Edge> G[MAXN];
int dist[MAXN];

int FLAG_exist;

bool my_strncmp(char *a, char *b, int length){
    for (int i = 0; i < length; ++i){
        if (a[i] != b[i])
            return false;
    }
    return true;
}

int analysis_next(int cur, int direction){
    // direction == 0 for bellow, direction == 1 for above
    int i = 0;
    while (cur_smc_code[cur].bytecode[i] == '\x90'){
        ++i;
    } // pass "nop"
    int end_left = 0;
    unsigned int index_xor = 0;
    int START = 0;
    int END = 0;
    if (cur_smc_code[cur].bytecode[i] == '\x74') // always starts as "jz label1"
        end_left = cur_smc_code[cur].bytecode[++i] + (++i);
    if (direction == 1){        // if input bit is 1
        START = 0;
        END = end_left;
    } else if (direction == 0){  // if input bit is 0
        START = end_left;
        END = 0x20;
    }
    for (i = START; i < END; ++i){
        // read "add r9, xx"
        if (cur_smc_code[cur].bytecode[i] == '\x49' && cur_smc_code[cur].bytecode[i + 1] == '\x83' && cur_smc_code[cur].bytecode[i + 2] == '\xC1')
            cur_smc_code[cur].len[direction] = cur_smc_code[cur].bytecode[i + 3];
        // read next key to SMC
        if (cur_smc_code[cur].bytecode[i] == '\x48' && cur_smc_code[cur].bytecode[i + 1] == '\x8D' && cur_smc_code[cur].bytecode[i + 2] == '\x5F')
            index_xor = cur_smc_code[cur].bytecode[i + 3] & 0xff;
        if (cur_smc_code[cur].bytecode[i] == '\x48' && cur_smc_code[cur].bytecode[i + 1] == '\x8D' && cur_smc_code[cur].bytecode[i + 2] == '\x9F'){
            index_xor = cur_smc_code[cur].bytecode[i + 3] & 0xff;
            index_xor |= (cur_smc_code[cur].bytecode[i + 4] & 0xff) << 8;
            index_xor |= (cur_smc_code[cur].bytecode[i + 5] & 0xff) << 16;
            index_xor |= (cur_smc_code[cur].bytecode[i + 6] & 0xff) << 24;
        }
    }
    char next_smc[0x30];
    memset(next_smc, 0, sizeof(next_smc));
    for (i = 0; i < 0x20; i++){
        next_smc[i] = (char)(cur_smc_code[cur].bytecode[i] ^ smc_xor_code[index_xor + i]);
    }
    int next_depth = cur_smc_code[cur].depth + 1;
    for (i = (next_depth - 1) * 16; i < (next_depth) * 16; ++i){
        if (cur_smc_code[i].depth == 0){
            break;
        }
        if (my_strncmp(next_smc, cur_smc_code[i].bytecode, 0x20)){
            FLAG_exist = 0;
            return i;
        }
    }
    FLAG_exist = 1;
    for (int j = 0; j < 0x20; j++){
        cur_smc_code[i].bytecode[j] = next_smc[j];
    }
    cur_smc_code[i].depth = next_depth;
    if (next_depth == 801){
        FLAG_exist = 0;
    }
    return i;
}

void set_up_map(){
    queue <int> Q;
    Q.push(0);
    while (!Q.empty()){
        int tmp = Q.front();
        Q.pop();
        int left = analysis_next(tmp, 1);
        G[tmp].push_back(Edge(left, cur_smc_code[tmp].len[1], 1));
        if (FLAG_exist) Q.push(left);
        int right = analysis_next(tmp, 0);
        G[tmp].push_back(Edge(right, cur_smc_code[tmp].len[0], 0));
        if (FLAG_exist) Q.push(right);
    }
    return;
}

int Path[MAXN], flag[MAXN];
bool vis[MAXN];

void Spfa(int Start)
{
    dist[Start] = 0;
    queue<Edge> Q;
    Q.push(Edge(Start, 0, 0));

    while( !Q.empty() ) {
        Edge P = Q.front();
        Q.pop();
        vis[P.to] = true;
        int len = G[P.to].size();
        for(int i=0; i<len; i++) {
            Edge Pn = G[P.to][i];
            if(dist[Pn.to] > dist[P.to] + Pn.len) {
                dist[Pn.to] = dist[P.to] + Pn.len;
                Path[Pn.to] = P.to;
                flag[Pn.to] = Pn.path;
                if( !vis[Pn.to] ) {
                    Q.push(Pn);
                    vis[Pn.to] = true;
                }
            }
        }
    }
}

void PutPath(int Star,int End)
{
    if(Star == End)
        return ;
    PutPath(Star, Path[End]);
    printf("%d", flag[End]);
}

void Init()
{
    for(int i = 0; i < MAXN; i++) {
        G[i].clear();
        dist[i] = INF;
        vis[i] = false;
        Path[i] = i;
    }
    FILE *fp = fopen("smc_code.txt", "r");
    for (int i = 0; i < 0xC73E0; ++i){
        smc_xor_code[i] = fgetc(fp);
    }
}

int main(){
    Init();
    printf("\n");
    memcpy(cur_smc_code[0].bytecode, "\x90\x74\x0D\x48\x8D\x5F\x40\x90\x49\x83\xC1\x49\xEB\xC5\x90\x90\x48\x8D\x5F\x20\x90\x90\x90\x49\x83\xC1\x11\x90\x90\x90\xEB\xB3", 0x21);
    cur_smc_code[0].depth = 1;
    set_up_map();
    Spfa(0);
    printf("%x\n", dist[16 * 800]);
    PutPath(0, 16 * 800);
    return 0;
}

好久没有用c写这些东西了,一开始全是bug(

switchingitup

题目描述就说了是 python 3.10.0,一开始还以为没啥区别,结果发现pycdas只支持到了3.9,3.10连反汇编都出不来,好在pycdc的pull requests里有人提交了一份3.10的(现在已经更新了)差点考虑自己魔改pycdc了

3.10加了一些新东西,一开始不知道看得很痛苦,后来查到之后感觉就不难了

上一下目前手动反编译的结果

import dis

def test_func():
    @__import__('dataclasses').dataclass
    class a1:
        x: int
        y: str

    def key(v1):
        for e in v1:
            yield e ^ 1337
    
    def gene(a):
        for e in a:
            yield e
    
    a1 = a1()
    a3 = bytes(key(iter((1385, 1403, 1402, 1389, 1407))))
    a6 = __import__('hashlib').md5
    I = input("flag? ")
    a4 = lambda x: a6(x).hexdigest()
    a5 = list(I)
    a2 = {}
    a7 = -1
    while a5:
        match a5:
            case ['p', 'b', 'c', 't', 'f', '{', *R, '}']:
                a7 = a7 + 1
                a2 |= {0:112} if a7 or len(R) != 32 else {}
                a5 = list(gene(enumerate(R)))
            case a1(x, y):
                a7 = a7 + 1
                a2 |= {x:y} if x + 1 != a7 or a4(a3 * x)[x] != y else {}
            case _:
                a2 |= {1:125}
                break
    print('Correct' if not a2 else 'Nope')

print (dis.dis(test_func))

新的特性就是 match case 语句,感觉还是很实用的,学到了

目前还有几个地方不太知道怎么写的

一个是 a2 |= {} if () else {} 这里,题目里应该是把 a7=a7+1 放到了这一行里

还有一个是 GET_ITERFOR_ITER 在迭代部分是分开的,不太懂了

不过仿照这这个逻辑写有些问题

解决方案就是直接在 .pyc 文件里加一个输出,a4(a3 * x)[x] != y 给这个逻辑套上输出就行,改完后的pyasm文件长这样:

        276     LOAD_NAME               12: a2
        278     LOAD_NAME               13: a7
        280     LOAD_CONST              21: 1
        282     BINARY_ADD              
        284     DUP_TOP                 
        286     STORE_NAME              13: a7
        288     LOAD_NAME               18: x
        290     LOAD_CONST              21: 1
        292     BINARY_ADD              
        294     COMPARE_OP              3 (!=)
        296     POP_JUMP_IF_TRUE        159 (to 318)
                            --> LOAD_NAME 20: print
        298     LOAD_NAME               9: a4
        300     LOAD_NAME               4: a3
        302     LOAD_NAME               18: x
        304     BINARY_MULTIPLY         
        306     CALL_FUNCTION           1
        308     LOAD_NAME               18: x
        310     BINARY_SUBSCR           
                            --> CALL_FUNCTION 1
        312     LOAD_NAME               19: y
        314     COMPARE_OP              3 (!=)
        316     POP_JUMP_IF_FALSE       163 (to 326)

然后还有手动修改一下各个 jump 的目的地址,最后的二进制文件长这样

运行一下就有结果了

Built with Hugo
Theme Stack designed by Jimmy