2023春秋杯春季赛-Re方向WP(共6解4)
sum

直接查看这个代码,首先看到Solver求解器,属于是一种数学游戏。
进一步分析可以发现(char *)&matrix;代码使用了这个矩阵作为地图.根据for循环遍历该图的逻辑可以发现这个其实是一个二维数组。矩阵如下:
[5, 3, 0, 0, 7, 0, 0, 0, 0]
[6, 0, 0, 1, 9, 5, 0, 0, 0]
[0, 9, 8, 0, 0, 0, 0, 6, 0]
[8, 0, 0, 0, 6, 0, 0, 0, 3]
[4, 0, 0, 8, 0, 3, 0, 0, 1]
[7, 0, 0, 0, 2, 0, 0, 0, 6]
[0, 6, 0, 0, 0, 0, 2, 8, 0]
[0, 0, 0, 4, 1, 9, 0, 0, 5]
[0, 0, 0, 0, 8, 0, 0, 7, 9]
数独游戏
查看flag的构造方式是v5的值进行md5而v5的值是矩阵每个地方的值加起来而不是我们需要填入的值,那么这个题就答案就为(1-9)*9答案为405取md5即可
Poisoned_tea_CHELL
这个题我们看到反编界面很奇怪字符串都无法交叉引用
不过在寻找函数的时候我们发现了tea,和比较逻辑
__int64 sub_597A()
{
__int64 result; // rax
int v1; // [rsp+8h] [rbp-468h]
int i; // [rsp+Ch] [rbp-464h]
int j; // [rsp+10h] [rbp-460h]
int v4; // [rsp+14h] [rbp-45Ch] BYREF
int v5; // [rsp+18h] [rbp-458h]
int v6; // [rsp+1Ch] [rbp-454h]
int v7[8]; // [rsp+20h] [rbp-450h] BYREF
int v8; // [rsp+40h] [rbp-430h]
int v9; // [rsp+44h] [rbp-42Ch]
int v10; // [rsp+48h] [rbp-428h]
int v11; // [rsp+4Ch] [rbp-424h]
int v12; // [rsp+50h] [rbp-420h]
int v13[258]; // [rsp+60h] [rbp-410h] BYREF
unsigned __int64 v14; // [rsp+468h] [rbp-8h]
v14 = __readfsqword(0x28u);
v1 = 1;
v7[0] = 5;
v7[1] = 2;
v7[2] = MEMORY[0x8464];
v7[3] = MEMORY[0x8454];
v7[4] = 0;
memset(v13, 0, 0x400uLL);
sub_5524(nullsub_1);
sub_5554(byte_645B);
sub_5594(byte_6469, v13);
sub_5574();
v4 = 0;
v5 = 0;
v6 = 0;
for ( i = 0; v13[i]; i += 2 )
{
v4 = v13[i];
v5 = v13[i + 1];
sub_5847(MEMORY[0x8474], &v4, v7);
v13[i] = v4;
v13[i + 1] = v5;
}
v8 = 0;
v9 = 0;
v10 = 0;
v11 = 0;
v12 = 0;
for ( j = 0; v13[j]; j += 2 )
{
v8 = *(_DWORD *)(4LL * j + 0x8624);
v9 = *(_DWORD *)(4LL * (j + 1) + 0x8624);
v10 = v13[j];
v11 = v13[j + 1];
if ( v8 != v10 || v9 != v11 )
{
v1 = 0;
break;
}
}
if ( v1 )
sub_5524(&dword_646C);
else
sub_5524(byte_6483);
result = 0LL;
if ( v14 != __readfsqword(0x28u) )
return sub_5544();
return result;
}
审计代码,发现并没有出现密文和key。因此我们需要动态调试来拿到正常的密文和密钥,但是正常的远程调试无法调试这个题,估计是做了反调,因此我们使用进程附加调试,发现可以调试。调试过程这里不说了,比较长,慢慢找就能找到
然后我做的时候忘记存迭代轮数了因此需要爆破一下,脚本如下
#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cmath>
#include<map>
#include<vector>
#include<queue>
#include<stack>
#include<set>
#include<string>
#include<cstring>
#include<list>
#include<stdlib.h>
using namespace std;
typedef int status;
typedef int selemtype;
/*
_ooOoo_
o8888888o
88" . "88
(| -_- |)
O\ = /O
____/`---'\____
.' \\| |// `.
/ \\||| : |||// \
/ _||||| -:- |||||- \
| | \\\ - /// | |
| \_| ''\---/'' | |
\ .-\__ `-` ___/-. /
___`. .' /--.--\ `. . __
."" '< `.___\_<|>_/___.' >'"".
| | : `- \`.;`\ _ /`;.`/ - ` : | |
\ \ `-. \_ __\ /__ _/ .-` / /
======`-.____`-.___\_____/___.-`____.-'======
`=---='
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
佛祖保佑 永不宕机 永无BUG
*/
unsigned int enc[20] =
{
0xECFDA301,0x61BECDF5,0xB89E6C7D,0xCE36DC68,0x4B6E539E,0x642EB504,0x54F9D33C,0x6D06E365,0xEA873D53,0xA4618507,0xD7B18E30,0xC45B4042
};
unsigned int enc2[20] =
{
0xECFDA301,0x61BECDF5,0xB89E6C7D,0xCE36DC68,0x4B6E539E,0x642EB504,0x54F9D33C,0x6D06E365,0xEA873D53,0xA4618507,0xD7B18E30,0xC45B4042
};
__int64 __fastcall sub_7F1E806D5847(int a1, unsigned int *a2, unsigned int *a3)
{
__int64 result; // rax
int i; // [rsp+24h] [rbp-14h]
unsigned int v5; // [rsp+28h] [rbp-10h]
unsigned int v6; // [rsp+2Ch] [rbp-Ch]
unsigned int v7; // [rsp+30h] [rbp-8h]
v5 = *a2;
v6 = a2[1];
v7 = 0;
for(int i = 0 ; i < a1 ; i ++ )
{
v7 -= 1091584273;
}
for ( i = 0; i < a1; ++i )
{
v6 -= (v5 + ((v5 >> 5) ^ (16 * v5))) ^ (*(((v7 >> 11) & 3) + a3) + v7);
v7 += 1091584273;
v5 -= (v6 + ((v6 >> 5) ^ (16 * v6))) ^ (*((v7 & 3) + a3) + v7);
}
*a2 = v5;
result = v6;
a2[1] = v6;
return result;
}
unsigned int key[4]={
5,2,9,7
};
int main ()
{
for(int i = 0 ; i < 12 ; i ++ )
{
printf("0x%X,",enc[i]);
}
for(int j = 0 ; j < 108 ; j ++ )
{
int ok = 0;
for(int i = 0 ; i < 12 ; i ++ )
{
enc[i] = enc2[i];
}
for(int i = 0 ; i < 12 ; i+= 2)
{
if(!ok)
{
cout<<j<<" :";
ok=1;
}
sub_7F1E806D5847(j,enc+i,key);
}
//if(enc[12]&0xff == '}')
printf("%s\n",enc);
}
}
BWBA
这题的加密逻辑非常的简单明了
__int64 __fastcall encrypt(__int64 a1, __int64 a2)
{
double *v2; // rax
double *v3; // rax
double v5; // [rsp+8h] [rbp-38h]
double v6; // [rsp+8h] [rbp-38h]
double v7; // [rsp+8h] [rbp-38h]
char v8; // [rsp+23h] [rbp-1Dh] BYREF
int v9; // [rsp+24h] [rbp-1Ch]
int j; // [rsp+28h] [rbp-18h]
int i; // [rsp+2Ch] [rbp-14h]
v9 = std::vector<int>::size(a2);
std::allocator<double>::allocator(&v8);
std::vector<double>::vector<int>(a1, (unsigned int)v9, 0LL, &v8);
std::allocator<double>::~allocator(&v8);
for ( i = 0; i < v9; ++i )
{
for ( j = 0; j < v9; ++j )
{
v5 = (double)*(int *)std::vector<int>::operator[](a2, j);
v6 = cos(((double)j + 0.5) * (3.141592653589793 * (double)i) / (double)v9) * v5;
v2 = (double *)std::vector<double>::operator[](a1, i);
*v2 = *v2 + v6;
}
if ( i )
v7 = sqrt(2.0 / (double)v9);
else
v7 = sqrt(1.0 / (double)v9);
v3 = (double *)std::vector<double>::operator[](a1, i);
*v3 = *v3 * v7;
}
return a1;
}
一开始以为是傅里叶变换,后面发现是离散余弦变换,这里直接上公式

我们这里需要使用的是IDCT公式
不过这里需要注意的是精度会出现偏差,因此我们需要四舍五入取值
#include <iostream>
#include <vector>
#include <cmath>
std::vector<double> decrypt(const std::vector<double>& input)
{
int v9 = input.size();
std::vector<double> result(v9, 0.0);
for (int i = 0; i < v9; ++i)
{
for (int j = 0; j < v9; ++j)
{
double v7 = (j == 0) ? sqrt(1.0 / v9) : sqrt(2.0 / v9);
double v5 = input[j];
double v6 = cos((i + 0.5) * (3.141592653589793 * j) / v9) * v5 * v7;
result[i] += v6;
}
// 四舍五入
result[i] = round(result[i]);
}
return result;
}
int main()
{
std::vector<double> input = {370.75, 234.362, -58.0834, 59.8212, 88.8221, -30.2406, 21.8316, 49.9781, -33.5259, 2.69675, 43.5386, -30.2925, -28.0754, 27.593, -2.53962, -27.1883, -5.60777, -0.263937, 6.80326, 8.03022, -6.34681, -0.89506, -6.80685, -13.6088, 27.0958, 29.8439, -21.7688, -20.6925, -13.2155, -37.0994, 2.23679, 37.6699, -3.5, 9.85188, 57.2806, 13.5715, -20.7184, 8.6816, 3.59369, -4.5302, 4.22203, -28.8166, -23.695, 31.2268, 6.58823, -39.9966, -20.7877, -19.7624, -22.031, 16.3285, 2.07557, -26.2521, 16.1914, 18.3976, -26.9295, 3.03769, 41.0412, 20.2598, 14.991, 6.99392, -22.3752, -7.24466, 8.96299, -10.4874};
std::vector<double> decrypted = decrypt(input);
for (const auto& value : decrypted)
{
std::cout <<(char) value;
}
std::cout << std::endl;
return 0;
}
Pytrans
这题就是Linux下python使用pyinstall打包的程序,我们直接解包就行了。
使用uncompyle6反编译run.py
# uncompyle6 version 3.9.0
# Python bytecode version base 3.8.0 (3413)
# Decompiled from: Python 3.10.4 (tags/v3.10.4:9d38120, Mar 23 2022, 23:13:41) [MSC v.1929 64 bit (AMD64)]
# Embedded file name: run.py
import base64, zlib, ctypes
try:
mylib = ctypes.cdll.LoadLibrary('./mylib.so')
except:
print('file no exit!')
else:
a = []
try:
sstr = input("Please enter the 10 digits and ending with '\\n': ").split(' ')
if len(sstr) == 10:
for i in sstr:
a.append(int(i))
mylib.check.argtypes = (
ctypes.POINTER(ctypes.c_int), ctypes.c_int)
mylib.check.restype = ctypes.c_char_p
scrambled_code_string = mylib.check((ctypes.c_int * len(a))(*a), len(a))
try:
decoded_data = base64.b64decode(scrambled_code_string)
uncompressed_data = zlib.decompress(decoded_data)
exec(__import__('marshal').loads(uncompressed_data))
except:
print('Incorrect input caused decryption failure!')
except:
pass
# okay decompiling .\run.pyc
分析发现,程序加载了一个mylib.so然后传入了输入的是个数字作为判断,调用的是check函数
函数内容如下:
void *__fastcall check(_DWORD *a1)
{
void *v2; // [rsp+18h] [rbp-28h]
char v3[24]; // [rsp+20h] [rbp-20h] BYREF
unsigned __int64 v4; // [rsp+38h] [rbp-8h]
v4 = __readfsqword(0x28u);
sub_1239();
if ( -27 * a1[7]
+ -11 * a1[6]
+ 16 * a1[5]
+ *a1
+ 2 * a1[1]
- a1[2]
+ 8 * a1[3]
- 14 * a1[4]
+ 26 * a1[8]
+ 17 * a1[9] != 14462
|| -30 * a1[8] + 13 * a1[5] + a1[3] + a1[1] + 2 * *a1 - 15 * a1[4] - 24 * a1[6] + 16 * a1[7] + 36 * a1[9] != -2591
|| 16 * a1[6]
+ -21 * a1[5]
+ 7 * a1[3]
+ 3 * a1[1]
- *a1
- a1[2]
+ 12 * a1[4]
- 23 * a1[7]
+ 25 * a1[8]
- 18 * a1[9] != 2517
|| -6 * a1[6] + 2 * a1[2] - a1[1] + 2 * a1[5] + 9 * a1[7] + 2 * a1[8] - 5 * a1[9] != 203
|| -5 * a1[8] + 6 * a1[7] + 3 * a1[1] - a1[3] - a1[5] + a1[6] + 5 * a1[9] != 3547
|| -9 * a1[8] + a1[4] + a1[2] + a1[7] - 5 * a1[9] != -7609
|| 2 * a1[5] + -a1[3] - a1[4] + a1[8] + 6 * a1[9] != 4884
|| a1[6] - a1[7] + 2 * a1[8] != 1618
|| a1[4] - a1[6] + 2 * a1[9] != 1096
|| a1[8] + a1[4] + a1[3] + a1[2] + a1[1] + *a1 - a1[5] - a1[6] - a1[7] - a1[9] != 711
|| 2 * (2 * a1[4] + a1[3]) + 5 * a1[5] != 7151 )
{
return 0LL;
}
v3[0] = 0;
v3[1] = 0;
v3[2] = 0;
v3[3] = 0;
v3[4] = 0;
v3[5] = 0;
v3[6] = 0;
v3[7] = 0;
v3[8] = 0;
v3[9] = 0;
v3[10] = 0;
v3[11] = 0;
v3[12] = 0;
v3[13] = 0;
v3[14] = 0;
v3[15] = a1[4] % 255;
v2 = malloc(0x4F0uLL);
sub_27E4(&unk_62C0, v2, v3);
return v2;
}
是个z3方程,我们直接解z3
from z3 import *
a1 = [BitVec("num[%d]" % i, 32) for i in range(13)]
solver = Solver()
# 添加方程组中的每个不等式约束条件
solver.add(
-27 * a1[7]
+ -11 * a1[6]
+ 16 * a1[5]
+ a1[0]
+ 2 * a1[1]
- a1[2]
+ 8 * a1[3]
- 14 * a1[4]
+ 26 * a1[8]
+ 17 * a1[9] == 14462
)
solver.add(
-30 * a1[8] + 13 * a1[5] + a1[3] + a1[1] + 2 * a1[0] - 15 * a1[4] - 24 * a1[6] + 16 * a1[7] + 36 * a1[9] == -2591
)
solver.add(
16 * a1[6]
+ -21 * a1[5]
+ 7 * a1[3]
+ 3 * a1[1]
- a1[0]
- a1[2]
+ 12 * a1[4]
- 23 * a1[7]
+ 25 * a1[8]
- 18 * a1[9] == 2517
)
solver.add(
-6 * a1[6] + 2 * a1[2] - a1[1] + 2 * a1[5] + 9 * a1[7] + 2 * a1[8] - 5 * a1[9] == 203
)
solver.add(
-5 * a1[8] + 6 * a1[7] + 3 * a1[1] - a1[3] - a1[5] + a1[6] + 5 * a1[9] == 3547
)
solver.add(
-9 * a1[8] + a1[4] + a1[2] + a1[7] - 5 * a1[9] == -7609
)
solver.add(
2 * a1[5] + -a1[3] - a1[4] + a1[8] + 6 * a1[9] == 4884
)
solver.add(
a1[6] - a1[7] + 2 * a1[8] == 1618
)
solver.add(
a1[4] - a1[6] + 2 * a1[9] == 1096
)
solver.add(
a1[8] + a1[4] + a1[3] + a1[2] + a1[1] + a1[0] - a1[5] - a1[6] - a1[7] - a1[9] == 711
)
solver.add(
2 * (2 * a1[4] + a1[3]) + 5 * a1[5] == 7151
)
print(solver.check())
for i in a1:
print(solver.model()[i], end=" ")
#511 112 821 949 517 637 897 575 648 738
解出来之后发现后面程序还有内容,调用了sub_27E4(&unk_62C0, v2, v3);该函数,利用该函数的返回值然后一系列操作使用exec加载了序列化代码。由于sub_27E4(&unk_62C0, v2, v3);函数涉及到的加密非常的繁杂,因此我们自己写一个loader就可以加载出他的返回值
# -*- coding: utf-8 -*-
import base64, zlib, ctypes, marshal
import dis
try:
mylib = ctypes.cdll.LoadLibrary('./mylib.so')
except:
print('file no exit!')
else:
a = []
try:
sstr = "511 112 821 949 517 637 897 575 648 738".split(' ')
if len(sstr) == 10:
for i in sstr:
a.append(int(i))
mylib.check.argtypes = (
ctypes.POINTER(ctypes.c_int), ctypes.c_int)
mylib.check.restype = ctypes.c_char_p
scrambled_code_string = mylib.check((ctypes.c_int * len(a))(*a), len(a))
try:
decoded_data = base64.b64decode(scrambled_code_string)
uncompressed_data = zlib.decompress(decoded_data)
code_obj = marshal.loads(uncompressed_data)
dis.disassemble(code_obj)
exec(code_obj)
except:
print('Incorrect input caused decryption failure!')
except:
pass
输出的是字节码,字节码比较长,这里简明主要逻辑,逻辑为把你输入的10个数字转化为二进制作为10*10的地图。
然后把你经过的路径在footprint的表里查找就是flag
