CTF SHOW逆向

想做一些逆向题目提升代码分析能力。

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

明显题目少了很多!

python 复制代码
from 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")
相关推荐
今天你TLE了吗41 分钟前
通过RocketMQ延时消息实现优惠券等业务MySQL当中定时自动过期
java·spring boot·后端·学习·rocketmq
被制作时长两年半的个人练习生1 小时前
如何调试llama.cpp及判断是否支持RVV
linux·服务器·llama
拉不动的猪1 小时前
前端JS脚本放在head与body是如何影响加载的以及优化策略
前端·javascript·面试
shykevin1 小时前
Actix-Web完整项目实战:博客 API
前端·数据库·oracle
烤麻辣烫1 小时前
黑马程序员苍穹外卖(新手)DAY12
java·开发语言·学习·spring·intellij-idea
BD_Marathon1 小时前
【IDEA】常用插件——3
android·java·intellij-idea
四谎真好看1 小时前
Linux 附录二,实验一
linux·运维·服务器·学习笔记
lichong9511 小时前
RelativeLayout 根布局里有一个子布局预期一直展示,但子布局RelativeLayout被 覆盖了
android·java·前端