想做一些逆向题目提升代码分析能力。
re2
通过运行程序,试图寻找提示

这两个if判断都是读取文件错误后的代码,所以暂时不需要看。(我们现在本地搞一个flag,txt)

看字符串充值成功,定位到这个函数:
一波异或找到充值密钥,看能不能不分析加密部分直接获得flag。

这样总提示错误,很苦恼。换一个思路,继续分析
看到这里,明显是RC4

准备解密。
参考了:你终于回来了(。・∀・)ノ
豆包给解密脚本即可
python
def rc4_decrypt(key, ciphertext):
# 初始化S盒
s = list(range(256))
j = 0
key_len = len(key)
# 密钥调度算法(KSA)
for i in range(256):
j = (j + s[i] + key[i % key_len]) % 256
s[i], s[j] = s[j], s[i] # 交换
# 伪随机生成算法(PRGA)
i = j = 0
plaintext = []
for byte in ciphertext:
i = (i + 1) % 256
j = (j + s[i]) % 256
s[i], s[j] = s[j], s[i] # 交换
t = (s[i] + s[j]) % 256
k = s[t]
plaintext.append(byte ^ k) # 异或解密
return bytes(plaintext)
if __name__ == "__main__":
# 密钥(保持不变)
key = b"[Warnning]Access_Unauthorized"
# 更新后的密文
ciphertext = [
195, 130, 163, 37, 246, 76, 54, 59, 89, 204,
196, 233, 241, 181, 50, 24, 177, 150, 174, 191,
8, 53
]
# 解密
plaintext = rc4_decrypt(key, ciphertext)
# 尝试解析(优先UTF-8,乱码则试GBK)
print("UTF-8解密结果:", plaintext.decode('utf-8', errors='replace'))
print("GBK解密结果:", plaintext.decode('gbk', errors='replace'))
re3
cpp
int __fastcall main(int argc, const char **argv, const char **envp)
{
size_t key_len; // rax
int v5; // [rsp+Ch] [rbp-134h] BYREF
unsigned int i; // [rsp+10h] [rbp-130h]
int v7; // [rsp+14h] [rbp-12Ch]
int v8; // [rsp+18h] [rbp-128h]
int v9; // [rsp+1Ch] [rbp-124h]
int v10; // [rsp+20h] [rbp-120h]
int v11; // [rsp+24h] [rbp-11Ch]
int v12; // [rsp+28h] [rbp-118h]
int v13; // [rsp+2Ch] [rbp-114h]
int v14; // [rsp+30h] [rbp-110h]
int v15; // [rsp+34h] [rbp-10Ch]
unsigned __int64 v16; // [rsp+38h] [rbp-108h]
int v17[8]; // [rsp+40h] [rbp-100h]
char key[5]; // [rsp+60h] [rbp-E0h] BYREF
char v19[107]; // [rsp+65h] [rbp-DBh] BYREF
char dest[104]; // [rsp+D0h] [rbp-70h] BYREF
unsigned __int64 v21; // [rsp+138h] [rbp-8h]
v21 = __readfsqword(0x28u);
v7 = 80;
v8 = 64227;
v9 = 226312059;
v10 = -1540056586;
v11 = 5;
v12 = 16;
v13 = 3833;
v5 = 0;
puts("plz input the key:");
__isoc99_scanf("%s", key);
key_len = strlen(key);
strncpy(dest, v19, key_len - 6);
dest[strlen(key) - 6] = 0;
__isoc99_sscanf(dest, "%x", &v5);
v17[0] = v7;
v17[1] = v8;
v17[2] = v9;
v17[3] = v10;
v17[4] = (v11 << 12) + v12;
v17[5] = v13;
v17[6] = v5;
v16 = 0LL;
for ( i = 0; i <= 6; ++i )
{
for ( v16 += v17[i]; v16 > 0xFFFF; v16 = v15 + v16 )
{
v14 = v16;
v15 = v16 >> 16;
}
}
if ( v16 == 0xFFFF )
puts("OK");
else
puts("Error");
return 0;
}
调整思路,既然题目说满足条件的最小解,那我们就直接想办法找到一个最小解就好了
看到【CTF Reverse】CTFShow re3 Writeup(反编译+位运算+暴力破解)-CSDN博客
有了灵感,可以利用暴力枚举找答案
cpp#include <stdio.h> #include <stdbool.h> // 引入bool类型定义(C99标准) #define _DWORD int // 改用默认__cdecl调用约定,兼容更多编译器 bool check(int a) { int v5; // [rsp+Ch] [rbp-134h] BYREF unsigned int i; // [rsp+10h] [rbp-130h] int v7; // [rsp+14h] [rbp-12Ch] int v8; // [rsp+18h] [rbp-128h] int v9; // [rsp+1Ch] [rbp-124h] int v10; // [rsp+20h] [rbp-120h] int v11; // [rsp+24h] [rbp-11Ch] int v12; // [rsp+28h] [rbp-118h] int v13; // [rsp+2Ch] [rbp-114h] unsigned __int16 v14; // 修正为unsigned __int16(存储低16位) unsigned int v15; // 存储高16位 unsigned __int64 v16; // [rsp+38h] [rbp-108h] _DWORD v17[8]; // [rsp+40h] [rbp-100h] // 冗余变量(s、v19、v21等)未使用,可保留但不影响逻辑 // 固定变量初始化(保持原数值不变) v7 = 80; v8 = 64227; v9 = 226312059; v10 = -1540056586; v11 = 5; v12 = 16; v13 = 3833; v5 = a; // 数组v17赋值(保持原逻辑不变) v17[0] = v7; v17[1] = v8; v17[2] = v9; v17[3] = v10; v17[4] = (v11 << 12) + v12; // 5<<12=0x5000,+16=0x5010 v17[5] = v13; v17[6] = v5; v16 = 0LL; for (i = 0; i <= 6; ++i) { // 第一步:累加当前v17[i]到v16 v16 += (unsigned int)v17[i]; // 强制转换避免符号扩展问题 // 第二步:循环计算"高16位 + 低16位",直到v16 ≤ 0xFFFF while (v16 > 0xFFFF) { v14 = (unsigned __int16)v16; // 取v16的低16位 v15 = (unsigned int)(v16 >> 16); // 取v16的高16位 v16 = v15 + v14; // 核心修正:高16位 + 低16位 } } // 判断最终结果是否等于0xFFFF return v16 == 0xFFFF; } int main() { // 遍历0~0xFFFF(共65536个值) for (int i = 0; i <= 0xFFFF; i++) { if (check(i)) { printf("符合条件的i(十六进制):%x\n", i); // 若想输出十进制,可加:printf("(十进制):%d\n", i); } } return 0; }
re4
经过动态调试后发现两个弹窗函数,分别对应正确和失败
然后定位到check函数
大概应该是每2字节比较一个。

看encode函数。首先是在a489xx这个编码表里面v3%26索引取值,然后/26。

萌新赛
数学不及格
似乎是给了一系列限制条件,然后让找到最终值

这里代表的是命令行运行程序带的参数,其中argc表示参数数量,f函数是计算斐波那契数列。我们可以利用z3_solve来求解
现在可以说缺少一个约束方程,结合f函数限定v4不大于200我们可以考虑使用暴力破解

受CTFshow Reverse 数学不及格 wp - Ethan(ˊ˘ˋ*) - 博客园的启发,我们可以用数学知识估计出大概的v4,,也就是题目式子相加刚好能约掉未知数,由于v4值小,可以忽略。
发现第58个是最贴切,得出f的值,进而推出所有

写出exp:
python
from z3 import *
from Crypto.Util.number import long_to_bytes
f = Int('f')
b = Int('b')
c = Int('c')
d = Int('d')
e = Int('e')
s=Solver()
s.add(f==591286729879)
s.add(f - b == 0x233F0E151C)
s.add(f - c == 0x1B45F81A32)
s.add(f - d == 0x244C071725)
s.add(b+c+d+e == 0x13A31412F8C)
if s.check() == sat:
_solve=s.model()
print(hex(_solve[f].as_long()),end='\n')
print(hex(_solve[b].as_long()),end='\n')
print(hex(_solve[c].as_long()),end='\n')
print(hex(_solve[d].as_long()),end='\n')
print(hex(58+25923))
#估算的代码
# sum=0x233F0E151C+0x1B45F81A32+0x244C071725+0x13A31412F8C
# f = sum//3
# print(f)
long =0x666c61677b6e65776265655f686572657d
print(long_to_bytes(long))
flag白给
UPX脱壳后发现代码很混乱,所以考虑动态调试/

利用OD查看字符串,随后锁定到成功了,观察附近汇编显然发现HackAv就是flag。
签退
明显是pyc反编译
先看这部分内容
通过对encode处理后的内容,按照大小写进行处理,返回一个字符串。
我耐着性子对代码大概的分析,可以看出其实有点类似base64!

那我们只要逆向一下rend的算法,就可以实现拿到base64解码后的东西了。
但是观察到ctfshow Reverse试水 -- 一些简单题-CTF对抗-看雪论坛-安全社区|非营利性质技术交流社区
的WP才发现可以一键解密出来,是先凯撒了再base64!我们也可以慢慢还原算法,我不这样写了。

内部赛
真的是签到
参考了REVERSE-PRACTICE-CTFSHOW-6_ctfshow 真的是签到-CSDN博客
ASpack壳,脱壳机脱掉
还有魔改UPX,ESP定律定位到输入函数,发现main函数起始部分

将EIP修改到对应位置,然后就可以dump再修复了

IDA代码如上,应该是只有C能让里面有的大数异或后输出字符。但是编码始终出不来中文,注意到必须用gb2312编码

看样子大数字前六位才是正确的
BJDCTF2020
encode
这是一个静态链接还加UPX的程序,自动脱壳脱掉
分析代码,还原函数名

大概将代码翻了一遍,估计那个没看出来的系统函数就是复制key的(也就是密钥)
大概思路是找到&flag[18],根据代码,它先RC4,再异或Flag{xxxx}字符串,最终base解密即可!
出题人的锅,解密的字符实际应该是:E8D8BD91871A010E560F53F4889682F961420AF2AB08FED7ACFD5E00
明显题目少了很多!
pythonfrom Crypto.Cipher import ARC4 # 密钥 key = b"Flag{This_a_Flag}" key_len = len(key) # 密文(16进制转二进制) cipher_hex = 'E8D8BD91871A010E560F53F4889682F961420AF2AB08FED7ACFD5E00' cipher_bin = bytes.fromhex(cipher_hex) # RC4解密 rc4 = ARC4.new(key) decrypted = rc4.decrypt(cipher_bin) # 逆向异或(得到Base64解码前的原始数据) xor_reversed = [] for i in range(len(decrypted)): xor_byte = decrypted[i] ^ key[i % key_len] xor_reversed.append(xor_byte) print("原始字符串:", bytes(xor_reversed).decode('utf-8'))
Easy
进去只有一个can you find me,找不到其他提示
找到一个ques函数疑似时打印内容,粘贴运行即可。

识别出来是HACKIT4FUN
2023愚人杯
easy_pyc
直接pyc反编译即可
# uncompyle6 version 3.8.0
# Python bytecode 2.7 (62211)
# Decompiled from: Python 3.8.10 (v3.8.10:3d8993a744, May 3 2021, 08:55:58)
# [Clang 6.0 (clang-600.0.57)]
# Embedded file name: enpyc.py
# Compiled at: 2023-03-29 18:30:23
print 'Welcome to CTFshow Re!'
print 'your flag is here!'
flag = ''
l = len(flag)
for i in range(l):
num = ((flag[i] + i) % 114514 + 114514) % 114514
code += chr(num)
code = map(ord, code)
for i in range(l - 4 + 1):
code[i] = code[i] ^ code[(i + 1)]
print code
code = ['\x16', '\x1d', '\x1e', '\x1a', '\x18', '\t', b'\xff', b'\xd0', ',', '\x03', '\x02', '\x14', '8', 'm', '\x01', 'C', 'D', b'\xbd', b'\xf7', '*', '\r', b'\xda', b'\xf9', '\x1c', '&', '5', "'", b'\xda', b'\xd4', b'\xd1', '\x0b', b'\xc7', b'\xc7', '\x1a', b'\x90', 'D', b'\xa1']
偷出脚本:ctfshow愚人杯 re easy_pyc wp_b鈥榎xda-CSDN博客
python
code = ['\x16', '\x1d', '\x1e', '\x1a', '\x18', '\t', '\xff', '\xd0', ',', '\x03', '\x02', '\x14', '8', 'm', '\x01', 'C', 'D', '\xbd', '\xf7', '*', '\r', '\xda', '\xf9', '\x1c', '&', '5', "'", '\xda', '\xd4', '\xd1', '\x0b', '\xc7', '\xc7', '\x1a', '\x90', 'D', '\xa1']
temp=list(map(ord,code)) #将code中的字符转换成ascii码值放入temp
#temp=[22, 29, 30, 26, 24, 9, 255, 208, 44, 3, 2, 20, 56, 109, 1, 67, 68, 189, 247, 42, 13, 218, 249, 28, 38, 53, 39, 218, 212, 209, 11, 199, 199, 26, 144, 68, 161]
#从倒数第四个元素开始,从后往前做异或,依然是下标i处元素与下标i+1处元素异或
for i in range(len(temp)-3)[::-1]: #这里写成for i in range(len(temp)-4,-1,-1)也可哟
temp[i]=temp[i]^temp[i+1]
#temp=[99, 117, 104, 118, 108, 116, 125, 130, 82, 126, 125, 127, 107, 83, 62, 63, 124, 56, 133, 114, 88, 85, 143, 118, 106, 76, 121, 94, 132, 80, 129, 138, 77, 138, 144, 68, 161]
for i in range(len(temp)):
print(chr(temp[i]-i),end='') #目测这里没有小于下标i的数所以不用关心模数运算(可以这么说吗?
#ctfshow{Just_F00l's_D@y_R3_Ch3ck-in!}
不能乱反编译,否则数据都是错误的,吃住亏了。卡了很长时间,导致WP也不想写了
easy_re
参考了CTFShow愚人杯RE - Tree_24 - 博客园

输入两个数字,经过一系列变换,最终直接在下面输出一段数据。
这里分析查看发现关键是拿出v13的值
v13和dict异或,获得v9的值,我们需要球的最终结果应该就是v13,因为v9、dict都是已知的。这两个数字都在299之内,我们可以由此写出爆破脚本(判断条件就是字符包含ZmxhZ,这是flag的base64)
看WP人家用open函数处理exe内部文件,这也是值得我学习的。

研究下加密矩阵,我们回头看看加密矩阵,输出他,看着是RGB数据
有参考了WP,是红色色调:CTFSHOW第三届愚人杯WP | 长亭百川云
python
from PIL import Image
# 读取re1.exe并提取指定范围数据(请确保文件路径正确)
try:
with open("./re1.exe", "rb") as f:
exe = f.read()
bins = exe[0x28A0:0x5A6E0]
except FileNotFoundError:
print("错误:未找到re1.exe文件,请检查路径")
exit()
# 按1字节/个提取数据(仅取每组4字节的第1个字节)
datas = []
for xx in range(0, len(bins), 4):
datas.append(ord(bins[xx: xx+1]))
# 创建300x300的图像,仅使用红色通道
newimg = Image.new('RGB', (300, 300))
for i in range(300):
for j in range(300):
idx = 300 * i + j
r = datas[idx] if idx < len(datas) else 0 # 数据不足时填充0
newimg.putpixel((i, j), (r, 0, 0))
# 保存图像
newimg.save('flag_red.png')
print("已生成红色调图像:flag_red.png")