逆向 PYC - Self Modifying (Byte)Code
先将hash_函数处的指令全部替换成nop指令也就是"90 00",再运行pycdc即可看到main函数代码。发现main函数确实存在调用stuff函数进行代码自修改。
可以看出除main函数以外的其他函数应该都被加密了,需要调用stuff函数解密。我选择的处理思路是通过脚本主动调用stuff函数解密函数后,读取内存中的字节码写回到pyc文件中。
import chall import ctypes with open('output.txt', 'wb') as f: chall.stuff(chall.hash_) buffer = ctypes.cast(id(chall.hash_.__code__.co_code) + 32, ctypes.POINTER(ctypes.c_char)) for i in range(len(chall.hash_.__code__.co_code)): f.write(buffer[i]) chall.stuff(chall.flag) buffer = ctypes.cast(id(chall.flag.__code__.co_code) + 32, ctypes.POINTER(ctypes.c_char)) for i in range(len(chall.flag.__code__.co_code)): f.write(buffer[i]) chall.stuff(chall.key_check) buffer = ctypes.cast(id(chall.key_check.__code__.co_code) + 32, ctypes.POINTER(ctypes.c_char)) for i in range(len(chall.key_check.__code__.co_code)): f.write(buffer[i])
使用pycdc反编译pyc文件,可以得到代码逻辑,如下:
# Source Generated with Decompyle++ # File: chall.pyc (Python 3.10) import ctypes def hash_(x): x ^= x >> 33 x *= 0xFF51AFD7ED558CCD x &= 0xFFFFFFFFFFFFFFFF x ^= x >> 33 x *= 0xC4CEB9FE1A85EC53 x &= 0xFFFFFFFFFFFFFFFF x ^= x >> 33 return x def key_check(buffer): __ = True ___ = not __ ____ = len _____ = sum ______ = all _______ = ord if ____(buffer) * 209715 != 3774870: return ___ None(hash_) p1 = buffer[:6] p2 = buffer[6:12] p3 = buffer[12:] if hash_(int.from_bytes(p1, 'big')) != 0x40F930C735AC9F85: return ___ if None(int.from_bytes(p2, 'big')) != 0x4E69109B310B3595: return ___ if None(int.from_bytes(p3, 'big')) != 0x14F1662D6E00E686: return ___ def flag(key): f = b'7+Cn\x04;u\x0f\nn\rYFU:4HC\r\tVf#\x1dA\x06%\x01\x05S\x11\x1b' o = [] for i, c in enumerate(f): o.append(c ^ key[i % len(key)]) return bytes(o).decode() def stuff(object): buffer = ctypes.cast(id(object.__code__.co_code) + 32, ctypes.POINTER(ctypes.c_char)) for i in range(len(object.__code__.co_code)): buffer[i] = buffer[i][0] ^ 0xDDBBAAFF12E0100 >> (i % 8) * 8 & 255 if __name__ == '__main__': b = input('Key: ').encode() stuff(key_check) if key_check(b): stuff(flag) print('GG use this flag to validate the challenge : {}'.format(flag(b))) return None None(":'(") return None
这题需要爆破hash值,但是无法使用hashcat来爆破,因为该算法不是完整的Murmur3 Hash。我这里写了个线程池爆破脚本,如下:
from concurrent.futures import ThreadPoolExecutor def hash_(x): x ^= x >> 33 x *= 0xFF51AFD7ED558CCD x &= 0xFFFFFFFFFFFFFFFF x ^= x >> 33 x *= 0xC4CEB9FE1A85EC53 x &= 0xFFFFFFFFFFFFFFFF x ^= x >> 33 return x def action(x): value = hash_(int.from_bytes(x, 'big')) # print("hash:", "".join(chr(i) for i in x), hex(value)) if value == 0x40F930C735AC9F85: print("".join(chr(i) for i in x)) exit() return max_thread_num = 500 executor = ThreadPoolExecutor(max_workers=max_thread_num) charset = '''()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~''' threadsList = [] for k1 in charset: for k2 in charset: for k3 in charset: for k4 in charset: for k5 in charset: for k6 in charset: executor.submit(action, [ord(k1), ord( k2), ord(k3), ord(k4), ord(k5), ord(k6)])
爆破出18个字节的key后,与密文异或即可得到flag。