【UNCTF】逆向WriteUp以及出题笔记

本文详细介绍了多个CTF逆向挑战的WriteUp,包括re_checkin、babapy、easyMaze等,涉及动态调试、字符串对比、解码算法等内容。同时,文章还分享了出题者对于CTFilter和原神等题目的设计思路,强调了解题过程中的关键点和技巧。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >


WriteUp

re_checkin

IDA打开,搜索字符串

查看交叉引用,定位到主函数

可以看到直接比较字符串

由于Str2在IDA中没有出现明文值,因此直接x64dbg打开,主函数下断,即可看到flag

反编译

这道题…有意思???

正规解题步骤参照下面的babypy,不过这道题据说运行就有flag?!

babypy

ExeinfoPe打开,发现是pyinstaller打包的,用网上的脚本解包

将解包出来的struct的前16字节添加到babypy文件头部,改名为babypy.pyc,用uncompyle6反编译,得到.py文件

import os, libnum, binascii
flag = 'unctf{*******************}'
x = libnum.s2n(flag)

def gen(x):
    y = abs(x)
    while 1:
        if y > 0:
            yield y % 2
            y = y >> 1
    else:
        if x == 0:
            yield 0


l = [i for i in gen(x)]
l.reverse()
f = '%d' * len(l) % tuple(l)
a = binascii.b2a_hex(f.encode())
b = int(a, 16)
c = hex(b)[2:]
print(c)
os.system('pause')

再结合tip.txt,很容易写出逆向算法

flag = '313131303130313031313031313130303131303030313130313131303130303031313030313130303131313130313130313031303130303031313031303030303130303030303030313131303130303031303131313131303131303130303130313131303031313031303131313131303131313030313030313130303130313031313030303031303031313030303130303131303030313031313131303031303130313131313130313130303031313030313130303030303031313030303030303131303030313031313131313031'
n = 0
for i in flag:
    if i == '1':
        n = (n << 1) + 1
    elif i == '0':
        n = n << 1
s = libnum.n2s(n)
print(s)

easyMaze

IDA打开,标准的迷宫题

检查输入格式

输入wasd控制方向,Dst处是迷宫地图

根据交叉引用定位

迷宫是10x10的,这样看着不直观,而且是反的,倒过来换成每行10个稍微直观了一点

Oo00oD00SD
0oooo0Dooo
o0D0oD0o00
ooooo00o00
oD0D0ooooo
o00o0o0o0o
oDoooooDDD
o00o00oooo
oD0D0000oD
oooooooooD

很容易看出来走出迷宫的路径:dsdddssaaaassssssddddddddwwaawawwddwwwdw

ICU

IDA打开,明文对比字符串

看似好像是先处理一遍然后依次对单个字符进行处理,动态调试看看情况

输入123456下断查看第一次处理的结果,6位变8位,看起来像base64,IDA搜索字符串可以看到base64的变表

单步即可看到第二次处理的结果

最终解密脚本

import base64

table = 'UyOPef2ghvwx3ABdT7856QSijuCDFGst0LKER4ZabckHIJMNnopqrlmz1VWXY9+/'
origin = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'
t = 'HSWEH2vXHmRtGZRJvSmKviwtviv4Ga5rD25Mvl:u6ewBUKg9'
f = ''
odd = True
for i in t:
    if odd:
        f += chr(ord(i) - 1)
    else:
        f += i
    odd = not odd
flag = f.translate(str.maketrans(table, origin))
print(base64.b64decode(flag))

ezRust

通过试错可以发现需要2个额外的命令行参数

x64dbg打开,给2个假的命令行参数“1234”和“5678”,一路F8,遇到Error输出就F7,最后进入下面的函数

这一行会把第一个命令行和“YLBNB”入参,“1234”的情况下执行完call后rax等于0

尝试把第一个命令行换成“YLBNB”,rax等于1

返回0会直接输出Error,返回1会进行第二次判断,同理得到第二个命令行为“RUSTPROGRAMING”

运行直接出flag(IDA也能找到对应的地方)

base_on_rust

(做题环境忘了保存,口头说一下做法)

这题我是动调出来的,但是我想站在上帝视角教大家一个简单的办法

程序需要一个额外的命令行输入,我们假设输入123456,会发现它会输出一段base64,将其解码,发现疑似base32,再解,发现是313233343536,正好是输入的字符的ascii码的十六进制拼起来

IDA的F5里面很多JUMPOUT,在汇编窗口手动修一下(没有解析的代码C一下,C不行的话就nop,再U整个函数,再P),可以看到疑似比较的操作,把待比较的数据dump下来,解码一下,跑脚本出flag

s = ''
f = '756E6374667B6261736536345F6261736533325F6261736531365F656E636F64655F5F5F7D'
i = 0
while i < len(f):
    c = f[i:i + 2]
    s += chr(int(c, 16))
    i += 2
print(s)

Trap

IDA打开,发现输入的flag s1和dest各留一份,在sub_400CBE后s1与s2比较

跟进函数内部,异或后会开启线程

这个程序有很多地方不能正常反编译,手动修一下汇编就好了

比如说sub_400C13,一开始F5后看不懂,修好以后发现是个简单递归

跟进另一个函数,这个函数里面本来是有反调试的,被我nop掉了

对s1 s2的算法都很简单,很容易求出输入的是 941463c8-2bcb-

运行的时候输入正确的flag会异或解密整个动态链接库文件,然后写入文件并调用jo_enc函数对接下来的输入进行检查

我的Ubuntu20不知道为什么调用动态库会失败,于是在/tmp中找到文件直接静态分析


也是进行运算操作后和固定值比较,逆向脚本还算好写

cipher = [1668, 1646, 1856, 4118, 1899, 1752, 640, 2000, 4412, 1835, 820, 984, 968, 1189, 4353, 1646, 4348, 4561, 1564,
          1566, 5596, 1525]
input1 = ''
input2 = '941463c8-2bcb-'
a = [i * 2 for i in range(128)]
b = [i * 2 + 1 for i in range(128)]
c = [16 * ord(input2[m % 14]) ^ cipher[m] for m in range(22)]
for d in c:
    for n in range(128):
        j = 0
        if not n % 2:
            for i in range(0, n, 2):
                j += a[i]
        else:
            for i in range(0, n, 2):
                j += b[i]
        if j == d:
            input1 += chr(n)
print('unctf{' + input2 + input1 + '}')

ezre

IDA打开,发现有混淆,根据字符串定位到主函数,flag有18位

整个函数充满着混淆后的junk代码,自己调一调就能明白这些代码什么意思

前面写了用来对比的数据,此处就是check的代码

2张截图之间的160多行代码就是具体的算法了,下面给出化简后的代码

v1 = v45[0];
v42 = 0;
v2 = 9;
while (1)
{
   
    v3 = v45[v2];
    if (v1 + v3 < 128)
    {
   
        v43[v42] = 128 - v3;
        v43[v42 + 9] = 2 * v3 + v1 - 128;
    }
    else
    {
   
        if (v1 + v3 == 128)
        {
   
            v43[v42] = v1;
            v43[v42 + 9] = v3;
        }
        else
        {
   
            v43[v42] = 2 * v1 + v3 - 128;
            v43[v42 + 9] = 128 - v1;
        }
    }
    if (++v42 == 9)
        break;
    v1 = v45[v42];
    v2 = v42 + 9;
}

用python写一个爆破脚本跑出flag

def chk(a, b):
    x = a + b
    if x < 128:
        y = 128 - b
        a, b = y, x - y
    elif x == 128:
        a = a
        b = b
    else:
        y = 128 - b
        a, b = x - y, y
    return a, b

flag = [141, 99, 177, 201, 161, 205, 177, 177, 203, 51, 62, 33, 19, 31, 12, 24, 33, 23]
flag_pre = ''
flag_post = ''
for i in range(0, 9):
    for j in range(0, 256):
        for k in range(0, 256):
            if chk(j, k) == (flag[i], flag[i + 9]):
                flag_pre += chr(k)
                flag_post += chr(j)
print(flag_pre + flag_post)

ezvm

IDA打开,就是一个VM

一开始程序对VM进行了初始化

根据初始化代码,自建一个结构体

分析每条指令作用的时候有几个地方需要注意


将opcode提取出来,并写出伪代码

-9 -15 6 3 -15 7 0 * -15 5 0 -8 -15 2 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值