逆向 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。

 

posted @ 2024-08-21 23:51  An2i  阅读(43)  评论(0编辑  收藏  举报