Reverse
T0p_Gear
程序加壳了,用upx脱壳一下
程序有三个check
第一个是明文匹配
第二个是从文件中解密字符串
第三个是将输入进行加密,和指定字符串进行比较
flag = ""
for c in [0x35, 0x61, 0x36, 0x62, 0x62, 0x32, 0x39, 0x63][::-1]:
flag += chr(c)
flag += '-'
flag += 'a6c30091'
flag += '-'
for idx, c in enumerate([ord(t) for t in list('00l>>=<:'[::-1] + 'mm?kj<l:'[::-1])]):
temp = (c + 10) ^ (idx + 3)
if temp == 70:
flag += 'F'
else:
flag += chr(c ^ 8)
print(flag)
easy_maze
一个迷宫,很简单
jkkjjhjjkjjkkkuukukkuuhhhuukkkk
Magia
裸解方程题,让输入满足以下等式即可
for ( i = 31; v8 < i; --i )
{
if ( (input[i] ^ input[v8]) != *(&v62 + v8) )
{
printf_0("No_need_to_scare");
return 0;
}
if ( (input[i] & input[v8]) != *(&v46 + v8) )
{
printf_0("No_need_to_scare");
return 0;
}
if ( (input[v8] & 0xF) != *(&v14 + v8) || (input[i] & 0xF) != *(&v14 + i) )
{
printf_0("No_need_to_scare");
return 0;
}
++v8;
}
用 z3 建立 BitVec 变量(位运算只能用bitvec),然后添加所有等式
原题目hints提示说flag中含有下划线,所以我在方程中多加了几个特判,否则解出来的flag有问题,会解出一些不可见字符
from z3 import *
v46 = [0x4c, 0x65, 0x60, 0x72, 0x64, 0x49, 0x70, 0x63, 0x6c, 0x45, 0x53, 0x61, 0x4e, 0x64, 0x48, 0x61]
v62 = [0x33, 0x0, 0x15, 0x9, 0xb, 0x36, 0x6, 0xc, 0x2, 0x3a, 0x2c, 0x8, 0x31, 0xb, 0x37, 0xc]
v14 = [0xe, 0x5, 0x0, 0xb, 0xd, 0x9, 0x2, 0x3, 0xc, 0x5, 0xf, 0x1, 0xe, 0x4, 0xf, 0xd, 0x1, 0x8,
0xf, 0xf, 0x9, 0x3, 0xf, 0xe, 0xf, 0x4, 0xf, 0x6, 0x2, 0x5, 0x5, 0xd]
ans = [BitVec(f'v{i}', 8) for i in range(32)]
s = Solver()
s.add(ans[0] == ord('N'))
s.add(ans[1] == ord('e'))
s.add(ans[2] == ord('p'))
s.add(ans[3] == ord('{'))
s.add(ans[31] == ord('}'))
for i in range(32):
s.add(Or(ans[i] == ord('{'), ans[i] == ord('}'), ans[i] == ord('_'),
And(ans[i] >= ord('a'), ans[i] <= ord('z')), ans[i] == ord('N')))
i = 31
v8 = 0
while v8 < i:
s.add(ans[i] ^ ans[v8] == v62[v8])
s.add(ans[i] & ans[v8] == v46[v8])
s.add(ans[v8] & 0xf == v14[v8])
s.add(ans[i] & 0xf == v14[i])
v8 += 1
i -= 1
if s.check() == sat:
print("good!")
m = s.model()
for idx, each in enumerate(ans):
#print(each, end=",")
print(chr(m[each].as_long()), end="")
else:
print("boom!")
接下来需要一点心灵感应,输入神秘字符串之后程序会再跑一段解密代码,解密出真正的flag,动态调试才能看到
8b272473500a451286ab225413f1debd
521
c++ 逆向,需要有一些动态调试技巧
主逻辑:
其中 encrypt_0 中能找到这个函数:
__int64 __fastcall encrypt(__int64 a1, unsigned int a2, __int64 a3, unsigned int a4)
{
__int64 result; // rax
unsigned int v5; // eax
unsigned int v6; // [rsp+1Ch] [rbp-Ch]
int v7; // [rsp+20h] [rbp-8h]
unsigned int i; // [rsp+24h] [rbp-4h]
sub_55C13086819A(a3, a4);
v6 = 0;
v7 = 0;
for ( i = 0; ; ++i )
{
result = i;
if ( a2 <= i )
break;
v6 = (unsigned __int8)(((unsigned int)((signed int)(v6 + 1) >> 31) >> 24) + v6 + 1)
- ((unsigned int)((signed int)(v6 + 1) >> 31) >> 24);
v5 = (unsigned int)((v7 + byte_55C130A6A260[v6]) >> 31) >> 24;
v7 = (unsigned __int8)(v5 + v7 + byte_55C130A6A260[v6]) - v5;
swap((char *)&byte_55C130A6A260[v6], (char *)&byte_55C130A6A260[v7]);
*(_BYTE *)((signed int)i + a1) ^= byte_55C130A6A260[(unsigned __int8)(byte_55C130A6A260[v6] + byte_55C130A6A260[v7])];
}
return result;
}
a1 就是刚刚输入的字符串,可以看到,程序只是对它进行了异或操作,其中加密时利用到了一个固定字符串
这个操作就利用固定密钥对称加密字符串,因此我们只需要把输入换成密码,就能得到flag了,利用idapython patch一下即可:
# 在字符串加密之前运行
# 将 addr 换成你之前输入的字符串的地址
addr = 0x000055C1325964C0
enc = [128, 89, 35, 53, 34, 115, 141, 26, 81, 93, 48, 232, 87, 38, 246, 7, 198, 146, 94, 220, 131, 31, 118, 146, 37, 15, 101, 251, 46, 77, 107, 69, 3, 135, 233, 159, 34]
for i in range(37):
patch_byte(addr+i, enc[i])
pycharm
参考资料:
http://chumen77.xyz/2020/06/27/DASCTF%E5%AE%89%E6%81%92%E6%9C%88%E8%B5%9B(6th)/#pyCharm-pyc%E6%96%87%E4%BB%B6%E6%81%A2%E5%A4%8D
https://www.52pojie.cn/thread-912103-1-1.html
https://www.cnblogs.com/blili/p/11799483.html (pyc文件结构分析,重点关注code_block的位置)
这道题给了一个pyc,能够正常运行,但是使用 uncompyle6 反编译却出错了:
这时候有两种方法:修复pyc,或者直接翻译pyc。
我们通过以下的命令分别读取 pyc 的文件头、常量表、变量名表、字节码表:
Python 2.7.12 (default, Apr 15 2020, 17:07:12)
[GCC 5.4.0 20160609] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> import dis,marshal
>>> f = open('a.pyc')
>>> f.read(4)
'\x03\xf3\r\n'
>>> f.read(4)
'jv\xe7^'
>>> code = marshal.load(f)
>>> code.co_consts
(-1, None, 'YamaNalaZaTacaxaZaDahajaYamaIa0aNaDaUa3aYajaUawaNaWaNajaMajaUawaNWI3M2NhMGM=', 'Are u ready?', 0, 32, 'a', '', 'great!waht u input is the flag u wanna get.', 'pity!')
>>> code.co_varnames
()
>>> code.co_names
('base64', 'a', 'raw_input', 'flag', 'b64encode', 'c', 'list', 'd', 'range', 'i', 'join', 'ohh')
>>> code.co_code
"q\x03\x00q\x00\x06d\xffd\x00\x00d\x01\x00l\x00\x00Z\x00\x00d\x02\x00Z\x01\x00e\x02\x00d\x03\x00\x83\x01\x00Z\x03\x00e\x00\x00j\x04\x00e\x03\x00\x83\x01\x00Z\x05\x00e\x06\x00e\x05\x00\x83\x01\x00Z\x07\x00x'\x00e\x08\x00d\x04\x00d\x05\x00\x83\x02\x00D]\x16\x00Z\t\x00e\x07\x00e\t\x00c\x02\x00\x19d\x06\x007\x03<qI\x00Wd\x07\x00j\n\x00e\x07\x00\x83\x01\x00Z\x0b\x00e\x0b\x00e\x01\x00k\x02\x00r\x86\x00d\x08\x00GHn\x05\x00d\t\x00GHd\x01\x00S"
>>> dis.dis(code.co_code)
0 JUMP_ABSOLUTE 3
>> 3 JUMP_ABSOLUTE 1536
6 LOAD_CONST 25855 (25855)
9 STOP_CODE
10 STOP_CODE
11 LOAD_CONST 1 (1)
14 IMPORT_NAME 0 (0)
17 STORE_NAME 0 (0)
20 LOAD_CONST 2 (2)
23 STORE_NAME 1 (1)
26 LOAD_NAME 2 (2)
29 LOAD_CONST 3 (3)
32 CALL_FUNCTION 1
35 STORE_NAME 3 (3)
38 LOAD_NAME 0 (0)
41 LOAD_ATTR 4 (4)
44 LOAD_NAME 3 (3)
47 CALL_FUNCTION 1
50 STORE_NAME 5 (5)
53 LOAD_NAME 6 (6)
56 LOAD_NAME 5 (5)
59 CALL_FUNCTION 1
62 STORE_NAME 7 (7)
65 SETUP_LOOP 39 (to 107)
68 LOAD_NAME 8 (8)
71 LOAD_CONST 4 (4)
74 LOAD_CONST 5 (5)
77 CALL_FUNCTION 2
80 GET_ITER
81 FOR_ITER 22 (to 106)
84 STORE_NAME 9 (9)
87 LOAD_NAME 7 (7)
90 LOAD_NAME 9 (9)
93 DUP_TOPX 2
96 BINARY_SUBSCR
97 LOAD_CONST 6 (6)
100 INPLACE_ADD
101 ROT_THREE
102 STORE_SUBSCR
103 JUMP_ABSOLUTE 73
>> 106 POP_BLOCK
>> 107 LOAD_CONST 7 (7)
110 LOAD_ATTR 10 (10)
113 LOAD_NAME 7 (7)
116 CALL_FUNCTION 1
119 STORE_NAME 11 (11)
122 LOAD_NAME 11 (11)
125 LOAD_NAME 1 (1)
128 COMPARE_OP 2 (==)
131 POP_JUMP_IF_FALSE 134
>> 134 LOAD_CONST 8 (8)
137 PRINT_ITEM
138 PRINT_NEWLINE
139 JUMP_FORWARD 5 (to 147)
142 LOAD_CONST 9 (9)
145 PRINT_ITEM
146 PRINT_NEWLINE
>> 147 LOAD_CONST 1 (1)
150 RETURN_VALUE
>>>
可以发现,前五条语句明显出现了问题:
0 JUMP_ABSOLUTE 3
>> 3 JUMP_ABSOLUTE 1536
6 LOAD_CONST 25855 (25855)
9 STOP_CODE
10 STOP_CODE
11 LOAD_CONST 1 (1)
程序不可能一下子jmp到1536,所以这个地方明显是混淆代码,程序能够执行而 decompyle6 却不能反编译,只要我们读懂这个地方的逻辑并且修改字节码,就能顺利反编译。
0x64 操作为LOAD_CONST,用法举例:LOAD_CONST 1 HEX: 640100
0x71 操作为JUMP_ABSOLUTE,用法举例:JUMP_ABSOLUTE 14 HEX: 710e00
0x65 操作为LOAD_NAME,用法举例:LOAD_NAME 1 HEX: 650100
...
python的字节码都是以 opcode + arg 的形式呈现的。
根据上方的字节码对照表我们能够猜出整个程序逻辑是从 0x1e 开始的。 '/x97/x00/x00/x00' 则是字节码的长度。
0x26 至 0x28,本来程序本意是要从表中读取常量(Load_Const 0),但程序因为混淆代码的关系翻译有误。我们只需要去掉这之前的代码即可。
即:去掉0x1e-0x25 共8个字节
字节码表长度初始值为 0x97(大端序)
0x97 - 8 = 0x8f
with open('a.pyc','r') as f:
dt = f.read()
# 其他pyc也可用类似的方法进行去混淆
data = dt[:0x1a]+'\x8f\x00'+dt[0x1c:0x1e] + dt[0x26:]
with open('out.pyc', 'w') as ff:
ff.write(data)
接下来就是 uncompyle6 一把梭
# uncompyle6 version 3.6.2
# Python bytecode 2.7 (62211)
# Decompiled from: Python 2.7.12 (default, Apr 15 2020, 17:07:12)
# [GCC 5.4.0 20160609]
# Embedded file name: pyCharm.py
# Compiled at: 2020-06-15 21:23:54
import base64
a = 'YamaNalaZaTacaxaZaDahajaYamaIa0aNaDaUa3aYajaUawaNaWaNajaMajaUawaNWI3M2NhMGM='
flag = raw_input('Are u ready?')
c = base64.b64encode(flag)
d = list(c)
for i in range(0, 32):
d[i] += 'a'
ohh = ('').join(d)
if ohh == a:
print 'great!waht u input is the flag u wanna get.'
else:
print 'pity!'
# okay decompiling out.pyc
程序逻辑十分简单,去掉字符串中的'a'并且base64解码即可。
brainbreaker
题目中有反调,patch一下即可
main函数不能直接f5,也需要patch一下修一下栈才能继续,不过翻译出来的伪代码有点问题。
就是一个简单的线性变换题。
本来可以z3一把梭的,不过不知道为啥解出来结果不太对,先放着,以后解决。
from z3 import *
a1 = [BitVec('flag%d' % i,9) for i in range(100)]
s = Solver()
a1.reverse()
result = [0xFA,0x2B,0x94,0xEA,0x63,0xA8,0xC4,0x13,0x84,0xE7,0xA7,0xDA,0xD4,0x2D,0xAE,0xBE]
opcode = "****--:~+******^.~+*****:*+++++^.-:///--*^.:///---^.,:~-----^>,:~-/++++++++++++^>,:~+****--^>+*******://++++++++++^:,^>-:///^:,^>+******://-^:,^>-/+://+++++++^:,^>-/----:,^>-/+:/+++++^:,^>-:++****+^:,^>-/+:~+**++^:,^>+****:*^:,^>-:++****++++^:,^>-/---:,^>+*****://+^:,^>-/+:///+^:,^>,"
ax = 0
bx = 0
fin=[0 for i in range(100)]
fin[0] = 1
for i in opcode:
o = ord(i)
if o == 60:
ax -= 1
# print "dec ax","ax = ",ax
if o == 62:
ax += 1
# print "inc ax","ax = ",ax
if o == 46:
continue
# print "put %c fin[{}]".format(ax)
# print chr(fin[ax])
if o == 44:
fin[ax] = a1.pop()
# print "get %2x fin[{}]".format(ax)
# break
if o == 43:
# print "inc fin[{}]".format(ax)
fin[ax] += 1
if o == 45:
# print "dec fin[{}]".format(ax)
fin[ax] -= 1
if o == 42:
# print "double fin[{}]".format(ax)
fin[ax] *= 2
if o == 47:
# print "shr fin[{}]".format(ax)
fin[ax] >>= 1
if o == 94:
# print "xor fin[{}],bx".format(ax)
fin[ax] ^= bx
if o == 58:
# print "mov bx,fin[{}]".format(ax)
bx = fin[ax]
if o == 126:
# print "check ax == 32;mov fin[{}],0".format(ax)
if ax == 32:
break
fin[ax] = 0
fin[ax] &= 0xff
ax &= 0xff
bx &= 0xff
for i in range(len(result)):
s.add(result[i] == fin[i])
print(s.check())
answer = s.model()
flag = "".join([hex(answer[i].as_long())[2:] for i in a1])
print(flag)
print(answer)