写在前面
这把被nep的xdm带飞了


但是自己还是拿了一些分,感觉还可以
但是自己并不会其它方向,因此这次其实是本人的RE wp
Baby Artist
一开始不知道这玩意是什么,但是看左上角像控制流图,扔进必应识图一搜,是Piet。
这个语言的介绍以及相关编辑器放这了。简单来讲,这玩意就是个简化汇编,但是做了亿些艺术化处理,每条指令的颜色决定了下一个色块的指令,push相关的值由上一个色块的大小决定。

本质上,一整个程序就是一堆算式和比对的集合,而由于Piet没有cmp,判断是通过下一个字符乘以((上一次计算的结果与上一个字符的差)取反)的值。由于基底还是栈,先进后出,所以把这玩意放进去跑,能跑出后半段,但是前半段会进入一段 意⭐义⭐不⭐明 的循环,所以得手操。
各位可以品鉴一下其中一小段:

not
push 10
push 5
push 2
* (get 2 * 5 = 10)
* (get 10 * 10 = 100)
push 5
- (get 100 - 5 = 95)
- (get -95 which means this letter must be "_")
not
*
# not(a[i]-(2*5*10-5))=1
push 2
push 5
push 11
* (get 55)
* (get 110)
-
not
*
# not(a[i]-2*5*11)=1
如上,我们可以硬推flag:
WACON2023{fUn_Dr4W1ng!__w4nN4_try?}
ps:实际上外网有个叫做repiet的工具,可助各位一臂之力,望周知
Adult Artist
这玩意看上去比较恐怖,因为它的特征完美符合REpsych生成的控制流图。如下是我经过改色之后产生的图片:

然而有一个更加令人痛心的事实:这个程序除了main就只有这个被弄成画的函数。更加让人男泵的是,这玩意因为太大,你f5不起作用(各种意义上,不论是它没法反汇编还是就算能反汇编你也看不懂)
不过,老逆向人的直觉会让你从汇编上面找线索。我们看它的开头有101个switch case:

我们随便点一个试试,你会发现它的逻辑都是对着eax寄存器嗯干:

最后汇总的时候,我们可以明显地看到eax实际上存着你输入的某位,而过程是将被对应位的case处理过的你的输入和预存储的东西进行一次cmp。

那思路很明显了,第一次运行自动patch过cmp,然后读dword里的东西,接着第二次运行再一个一个case的读汇编指令,然后自动化逆过程,一位一位地从dword还原输入,然后从控制台输入过case,最后拿到flag。(虽然实际上从代码表现来看,它是四位四位处理的)
但是,赛场上我并没有写出代码来,所以这个过程还是交给外网dalao来整活(摘自sahuang的题解):
patch部分
data = open("masterpiece", "rb").read()
bad = bytes.fromhex("2ec4e27196849a0c800e08")
nops = bytes([0x90] * len(bad))
jmpbad = bytes.fromhex("e900000000")
# these jmps should be jumping 11 bytes ahead
jmpgood = bytes.fromhex("e90b000000")
data = data.replace(bad, nops).replace(jmpbad, jmpgood)
target = 0x08049206
target = target.to_bytes(4, "little")
assert data.count(target) == 1
start = data.find(target)
xdata = data[:start]
for i in range(101):
jmpptr = data[start + i * 4:start + (i + 1) * 4]
jmpptr = int.from_bytes(jmpptr, "little")
jmpptr += 11 # skip nops
jmpptr = jmpptr.to_bytes(4, "little")
xdata += jmpptr
xdata += data[start + 404:]
open("masterpiece_patched", "wb").write(xdata)
还原算法部分
from pwn import *
from capstone import *
sbox = [101, 242, 170, 79, 1, 137, 147, 58, 194, 8, 28, 195, 59, 63, 123, 111, 56, 254, 219, 122, 144, 127, 95, 81, 75, 61, 23, 104, 128, 155, 41, 196, 136, 121, 245, 202, 40, 15, 6, 42, 112, 68, 103, 47, 35, 139, 185, 116, 120, 11, 114, 106, 141, 55, 30, 253, 175, 74, 80, 86, 161, 32, 255, 235, 94, 163, 183, 34, 248, 221, 110, 109, 89, 198, 209, 54, 226, 25, 93, 169, 229, 20, 131, 83, 208, 156, 187, 135, 52, 88, 165, 38, 143, 164, 172, 186, 213, 118, 124, 115, 26, 87, 78, 159, 36, 181, 151, 10, 39, 92, 227, 191, 91, 19, 126, 67, 4, 50, 31, 0, 22, 2, 97, 162, 217, 173, 145, 193, 212, 239, 51, 133, 224, 49, 243, 71, 24, 236, 244, 72, 180, 69, 60, 134, 228, 158, 119, 18, 203, 108, 132, 190, 201, 167, 53, 168, 152, 238, 218, 250, 231, 160, 199, 189, 76, 140, 252, 174, 12, 249, 65, 57, 77, 105, 146, 197, 37, 82, 7, 66, 206, 44, 13, 215, 99, 176, 210, 43, 150, 148, 184, 166, 64, 142, 17, 188, 96, 154, 130, 223, 33, 216, 125, 90, 45, 113, 27, 70, 29, 232, 205, 233, 84, 237, 129, 207, 222, 138, 85, 220, 21, 102, 178, 192, 234, 62, 149, 16, 214, 107, 5, 177, 3, 200, 153, 157, 179, 230, 171, 251, 246, 225, 204, 241, 73, 9, 211, 247, 182, 14, 100, 240, 98, 117, 46, 48]
inv_sbox = [sbox.index(i) for i in range(256)]
finales = [1294921289, 3556600223, 1209339435, 1851550862, 3576661803, 935542824, 890675233, 1466443951, 2899239426, 2509770635, 3811890241, 4189575110, 3407890776, 151811947, 2021481915, 2224322179, 937543787, 2656386893, 2430690788, 937170305, 1595242648, 1313689251, 3449380580, 3981305360, 1805462865, 1000700706, 1394137676, 3037656969, 2213052412, 621078233, 44681021, 3125866984, 2270629699, 81231002, 732808204, 1962199736, 500787935, 3021495503, 343658160, 2011362800, 3051343062, 2649999813, 479102860, 1517017809, 2535887555, 2767036793, 864456536, 3221496586, 1236923337, 2535989912, 750098417, 2125817032, 3181958189, 1206391945, 911571295, 3648355814, 1356734492, 3557631056, 2749801664, 1533604227, 1218930141, 3796472889, 2869013044, 2909236950, 770707252, 1775872345, 1668747571, 164579730, 3194457712, 309450627, 725278243, 2120079896, 225892262, 2486260442, 2483139025, 3603672921, 4265882258, 3540118190, 2883355033, 1214344214, 708738895, 1637597950, 4066117530, 843823022, 1243164200, 1354787218, 1808234221, 2545499367, 2573345003, 423063696, 4155282283, 1510805520, 3005416590, 3521755546, 1409845455, 487735804, 3385604609, 1807103354, 2376904603, 2343410443]
# max bits > 0 == width of the value in bits (e.g., int_16 -> 16)
# Rotate left: 0b1001 --> 0b0011
rol = lambda val, r_bits, max_bits: \
(val << r_bits%max_bits) & (2**max_bits-1) | \
((val & (2**max_bits-1)) >> (max_bits-(r_bits%max_bits)))
# Rotate right: 0b1001 --> 0b1100
ror = lambda val, r_bits, max_bits: \
((val & (2**max_bits-1)) >> r_bits%max_bits) | \
(val << (max_bits-(r_bits%max_bits)) & (2**max_bits-1))
def xor(a, b):
return (a ^ b) & 0xffffffff
def nott(a):
return (~a & 0xffffffff)
def sub(a, b):
return (a - b) & 0xffffffff
def add(a, b):
return (a + b) & 0xffffffff
def bswap(a):
a3 = (a >> 24) & 0xff
a2 = (a >> 16) & 0xff
a1 = (a >> 8) & 0xff
a0 = (a >> 0) & 0xff
return (a0 << 24) | (a1 << 16) | (a2 << 8) | a3
# {'inc', 'jmp', 'xor', 'bswap', 'sub', 'dec', 'not', 'nop', 'rol', 'mov', 'add', 'ror'}
msg = b""
f = open("./masterpiece_patched", "rb").read()
blocks = []
start = 0x1062
for _ in range(101):
jmptableptr = int.from_bytes(f[start:start+4], "little")
print(hex(jmptableptr))
start += 4
blocks.append(jmptableptr - 0x8048000)
for ind, s in enumerate(blocks[:-1]):
CODE = f[s:blocks[ind+1]] # idk get block
ddd = []
md = Cs(CS_ARCH_X86, CS_MODE_32)
for i in md.disasm(CODE, 0x1000):
ddd.append((i.address, i.mnemonic, i.op_str))
ddd = ddd[::-1]
fff = finales[ind]
al = 0
ah = 0
cl = 0
i = 0
while (i < len(ddd)):
opp = ddd[i][1]
sss = ddd[i][2]
if opp == 'nop' or opp == 'jmp':
i += 1
continue
if opp == 'bswap':
fff = bswap(fff)
elif opp == 'add':
vvv = int(sss[sss.index(',') + 1:], 16)
fff = sub(fff, vvv)
elif opp == 'sub':
vvv = int(sss[sss.index(',') + 1:], 16)
fff = add(fff, vvv)
elif opp == 'ror':
vvv = int(sss[sss.index(',') + 1:], 16)
fff = rol(fff, vvv, 32)
elif opp == 'rol':
vvv = int(sss[sss.index(',') + 1:], 16)
fff = ror(fff, vvv, 32)
elif opp == 'xor':
vvv = int(sss[sss.index(',') + 1:], 16)
fff = xor(fff, vvv)
elif opp == 'not':
fff = nott(fff)
elif opp == 'inc':
fff = fff - 1
elif opp == 'dec':
fff = fff + 1
elif opp == 'mov':
if sss.startswith('cl, al'):
fff = (fff >> 8 << 8) | cl
elif sss.startswith('cl, ah'):
fff = (fff ^ (ah << 8)) | (cl << 8)
elif sss.startswith('al, byte'):
al = fff & 0xff
cl = inv_sbox[al]
elif sss.startswith('ah, byte'):
ah = (fff >> 8) & 0xff
cl = inv_sbox[ah]
else:
print(ddd[i])
break
# print(hex(ddd[i][0]), ddd[i], hex(fff))
i += 1
msg += int.to_bytes(fff, 4, "little")
print(msg)