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_ITER
和 FOR_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
的目的地址,最后的二进制文件长这样
运行一下就有结果了