Logo WP

ReWithCrypto

EzMath TEA EzEnc What'sThis#

四道算法逆向题 这个分类就叫ReCrypto吧233

推荐阅读:【CTF Wiki】常见加密算法和编码识别

1. 简单数学#

ezmath

上过初中就会写

出题人:@dev1l

文件{:download="ezmath.exe"}

拖进DIE,使用PyInstaller打包的,解包

  1. PyInstxtractor

是一个单独脚本,python pyinstxtractor.py ezmath.exe 即可

解包出 ezmath.pyc

  1. 修改魔数

由于pyinstaller在打包可执行文件时会将.pyc文件头部的16字节magic head去掉,因此需要对需要反编译的.pyc文件进行预处理,将文件头部的16字节magic head补充完整。

55 0D 0D 0A 00 00 00 00 00 00 00 00 00 00 00 00 
改成
55 0D 0D 0A 00 00 00 00 70 79 69 30 01 01 00 00 
即可
  1. 安装和使用 Uncompyle6
pip install uncompyle6
uncompyle6 ezmath.pyc > ezmath.py
  1. 分析代码逻辑

看起来是一个乘法+求和的式子,但是很混乱,直接看不出什么

尝试整理式子

source = "sum([flag[23] for _ in range(flag[23])]) + sum([flag[12] for _ in range(flag[12])]) + sum([flag[1] for _ in range(flag[1])]) - sum([flag[24] for _ in range(222)]) + sum([flag[22] for _ in range(flag[22])]) + sum([flag[31] for _ in range(flag[31])]) + sum([flag[26] for _ in range(flag[26])]) - sum([flag[9] for _ in range(178)]) - sum([flag[29] for _ in range(232)]) + sum([flag[17] for _ in range(flag[17])]) - sum([flag[23] for _ in range(150)]) - sum([flag[6] for _ in range(226)]) - sum([flag[7] for _ in range(110)]) + sum([flag[19] for _ in range(flag[19])]) + sum([flag[2] for _ in range(flag[2])]) - sum([flag[0] for _ in range(176)]) + sum([flag[10] for _ in range(flag[10])]) - sum([flag[12] for _ in range(198)]) + sum([flag[24] for _ in range(flag[24])]) + sum([flag[9] for _ in range(flag[9])]) - sum([flag[3] for _ in range(168)]) + sum([flag[8] for _ in range(flag[8])]) - sum([flag[2] for _ in range(134)]) + sum([flag[14] for _ in range(flag[14])]) - sum([flag[13] for _ in range(170)]) + sum([flag[4] for _ in range(flag[4])]) - sum([flag[10] for _ in range(142)]) + sum([flag[27] for _ in range(flag[27])]) + sum([flag[15] for _ in range(flag[15])]) - sum([flag[15] for _ in range(224)]) + sum([flag[16] for _ in range(flag[16])]) - sum([flag[11] for _ in range(230)]) - sum([flag[1] for _ in range(178)]) + sum([flag[28] for _ in range(flag[28])]) - sum([flag[5] for _ in range(246)]) - sum([flag[17] for _ in range(168)]) + sum([flag[30] for _ in range(flag[30])]) - sum([flag[21] for _ in range(220)]) - sum([flag[22] for _ in range(212)]) - sum([flag[16] for _ in range(232)]) + sum([flag[25] for _ in range(flag[25])]) - sum([flag[4] for _ in range(140)]) - sum([flag[31] for _ in range(250)]) - sum([flag[28] for _ in range(150)]) + sum([flag[11] for _ in range(flag[11])]) + sum([flag[13] for _ in range(flag[13])]) - sum([flag[14] for _ in range(234)]) + sum([flag[7] for _ in range(flag[7])]) - sum([flag[8] for _ in range(174)]) + sum([flag[3] for _ in range(flag[3])]) - sum([flag[25] for _ in range(242)]) + sum([flag[29] for _ in range(flag[29])]) + sum([flag[5] for _ in range(flag[5])]) - sum([flag[30] for _ in range(142)]) - sum([flag[26] for _ in range(170)]) - sum([flag[19] for _ in range(176)]) + sum([flag[0] for _ in range(flag[0])]) - sum([flag[27] for _ in range(168)]) + sum([flag[20] for _ in range(flag[20])]) - sum([flag[20] for _ in range(212)]) + sum([flag[21] for _ in range(flag[21])]) + sum([flag[6] for _ in range(flag[6])]) + sum([flag[18] for _ in range(flag[18])]) - sum([flag[18] for _ in range(178)])"
# 去掉 sum 和 for
source = (source.replace("sum([", "")
    .replace(" for _ in range(", " * ") # 把 for 翻译成乘法
    .replace(")])", ""))

source = "+ " + source # 方便分组

ss = source.split(" ")
result: dict[int, list] = {}

for i in range(0, len(ss), 4):
    # e.g. + flag[23] * flag[23]
    index = int(ss[i + 1][5:-1]) # flag[23] 获取其中的数字
    if index not in result:
        result[index] = [] # 同个下标多个对应,用list存储
    
    result[index].append(' '.join(ss[i:i+4]))

for k in sorted(result.keys()):
    for s in result[k]:
        print(s)
- flag[0] * 176
+ flag[0] * flag[0]
+ flag[1] * flag[1]
- flag[1] * 178
+ flag[2] * flag[2]
- flag[2] * 134
- flag[3] * 168
+ flag[3] * flag[3]
+ flag[4] * flag[4]
- flag[4] * 140
- flag[5] * 246
+ flag[5] * flag[5]
- flag[6] * 226
+ flag[6] * flag[6]
- flag[7] * 110
+ flag[7] * flag[7]

观察一下,每个下标都是平方再加一个乘常数,然后再稍微联想一下

就是完全平方公式,所以把常数除以2即可

还真是上过初中就会写

# 简单改了下,前面的代码都一样
result: dict[int, int] = {}
for i in range(0, len(ss), 4):
    # e.g. + flag[23] * flag[23]
    index = int(ss[i + 1][5:-1]) # flag[23] 获取其中的数字
    if ss[i+3].find("flag") != -1: continue
    result[index] = int(ss[i+3])
r = ''
for k in sorted(result.keys()):
    r += chr(result[k] // 2)
print(r)

EXP2#

2024-04-22 更新

学习了z3之后,可以直接爆破

import z3
    
solver = z3.Solver()
flag = [z3.Int("i_%s" % (i+1)) for i in range(32)]
solver.add(flag[23] * flag[23] + flag[12] * flag[12] + flag[1] * flag[1] - flag[24] * 222 + flag[22] * flag[22] + flag[31] * flag[31] + flag[26] * flag[26] - flag[9] * 178 - flag[29] * 232 + flag[17] * flag[17] - flag[23] * 150 - flag[6] * 226 - flag[7] * 110 + flag[19] * flag[19] + flag[2] * flag[2] - flag[0] * 176 + flag[10] * flag[10] - flag[12] * 198 + flag[24] * flag[24] + flag[9] * flag[9] - flag[3] * 168 + flag[8] * flag[8] - flag[2] * 134 + flag[14] * flag[14] - flag[13] * 170 + flag[4] * flag[4] - flag[10] * 142 + flag[27] * flag[27] + flag[15] * flag[15] - flag[15] * 224 + flag[16] * flag[16] - flag[11] * 230 - flag[1] * 178 + flag[28] * flag[28] - flag[5] * 246 - flag[17] * 168 + flag[30] * flag[30] - flag[21] * 220 - flag[22] * 212 - flag[16] * 232 + flag[25] * flag[25] - flag[4] * 140 - flag[31] * 250 - flag[28] * 150 + flag[11] * flag[11] + flag[13] * flag[13] - flag[14] * 234 + flag[7] * flag[7] - flag[8] * 174 + flag[3] * flag[3] - flag[25] * 242 + flag[29] * flag[29] + flag[5] * flag[5] - flag[30] * 142 - flag[26] * 170 - flag[19] * 176 + flag[0] * flag[0] - flag[27] * 168 + flag[20] * flag[20] - flag[20] * 212 + flag[21] * flag[21] + flag[6] * flag[6] + flag[18] * flag[18] - flag[18] * 178 + 297412 == 0) # 添加约束
print(solver.check())
m = solver.model() # 求解
print(m)

2. 卡布奇诺#

给阿姨倒一杯卡布奇诺

到点了,该喝茶了。

文件{:download="Cappuccino.exe"}

算式很简单,就是 + - * / ^

捋清楚逻辑比较困难

看了下 CTF Reverse Wiki,原来这种算法就叫 TEA

TEA——微型加密算法(Tiny Encryption Algorithm,TEA)

不过是魔改过的,加了个异或操作

在 Tea 算法中其最主要的识别特征就是 拥有一个 magic number :0x9e3779b9 。

3. EzEnc#

文件{:download="ez_enc.exe"}

加密如下:

// Str是输入的值
char aImouto[] = 'IMouto';
for ( i = 0; i < (int)(j_strlen(Str) - 1); ++i )
    Str[i] = aImouto[i % 6] ^ (Str[i + 1] + (unsigned __int8)Str[i] % 20);

可知,最后一个元素是没有改变的,从后往前解密即可

逆向运算 s[i] ^ aImouto[i % 6] 可得到 (Str[i + 1] + (unsigned __int8)Str[i] % 20),减去已知的 Str[i + 1]

得到 (unsigned __int8)Str[i] % 20),取余的逆运算为,Str[i] = 20 * n + (Str[i] % 20)

有多个解,这里要个条件,0 <= Str[i - 1] % 20 < 20,多算一步即可

EXP:

enc = [0x27,0x24,0x17,0xb,0x50,0x3,0xc8,0xc,0x1f,0x17,0x36,0x55,0xcb,0x2d,0xe9,0x32,0xe,0x11,0x26,0x2,0xc,0x7,0xfc,0x27,0x3d,0x2d,0xed,0x35,0x59,0xeb,0x3c,0x3e,0xe4,0x7d]
length = len(enc)

key = 'IMouto'

for i in range(length - 2, -1, -1):
    for n_20 in range(20, 121, 20): # ASCII 32 ~ 126
        moded = (enc[i] ^ ord(key[i % 6])) - enc[i + 1]
        temp = n_20 + moded

        # 检验
        if 0 <= ((enc[i - 1] ^ ord(key[(i - 1) % 6])) - temp) < 20:
            enc[i] = temp
            break
        else:
            continue

print(''.join(chr(c) for c in enc))

注意:位运算优先级低于加减运算,需要加括号(踩了两次坑 orz

4. What's this#

文件{:download="whats_this"}

扔进DIE,发现是个 格式: Lua Bytecode (.LUAC)(v5.1)[LittleEndian]

使用 luadec51 反编译

常见语法:

  • value = value .. temp 追加字符串
  • string.char('A', 'b', 0x23) 连成字符串
  • string.byte(output, i, j) 获取字符串output的 i 到 j 的字符ASCII码列表
  • string.reverse(obfuscated_output) 逆序字符串
  • string.gsub(obfuscated_output, "g", "3") 替换字符串中的文本

EXP

enc = '==AeuFEcwxGPuJ0PBNzbC16ctFnPB5DPzI0bwx6bu9GQ2F1XOR1U'

enc = enc.replace("6", "W")
enc = enc.replace("H", "4")
enc = enc.replace("g", "3")
enc = enc[::-1]

print(enc)

import base64

obfuscated_output = base64.b64decode(enc).decode()
result = ''

for i in range(len(obfuscated_output)):
    result += chr(((ord(obfuscated_output[i]) - 3) ^ 8))
print(result)