我应该如何解码这个 data/these 字符串

How should i decode this data/these strings

我目前正在尝试解决旧的 python CTF 挑战,提供了服务器的脚本,想法是将正确的数据发送到此服务器,

#!/usr/bin/env python3
# from dis import dis
import socketserver
import types


class RequestHandler(socketserver.BaseRequestHandler):

    def handle(self):
        self.request.sendall(b'PyDRM Proof of Concept version 0.7\n')
        self.request.sendall(
            b'Submit the secret password to retrieve the flag:\n')
        user_input_bytes = self.request.recv(4096).strip()
        user_input = user_input_bytes.decode('utf-8', 'ignore')
        if validate_password(user_input):
            self.request.sendall(read_flag())
        else:
            self.request.sendall(b'Invalid password\n')


class RequestServer(socketserver.ThreadingMixIn, socketserver.TCPServer):
    pass


def read_flag():
    with open('flag.txt', 'rb') as fh:
        return fh.read()


def generate_validation_function():
    code_obj = types.CodeType(
        1,
        0,
        5,
        32,
        67,
        b'd\x01\x00d\x02\x00d\x03\x00d\x04\x00d\x05\x00d\x06\x00d\x05\x00d\x07'
        b'\x00d\x08\x00d\x05\x00d\t\x00d\x08\x00d\n\x00d\x01\x00d\x07\x00d\x07'
        b'\x00d\x01\x00d\x0b\x00d\x08\x00d\x07\x00d\x0c\x00d\r\x00d\x0e\x00d'
        b'\x08\x00d\x05\x00d\x0f\x00d\x03\x00d\x04\x00d\x05\x00d\x06\x00d\x05'
        b'\x00d\x07\x00g \x00}\x01\x00g\x00\x00}\x02\x00x+\x00|\x01\x00D]#\x00'
        b'}\x03\x00|\x02\x00j\x00\x00t\x01\x00t\x02\x00|\x03\x00\x83\x01\x00d'
        b'\x10\x00\x18\x83\x01\x00\x83\x01\x00\x01qs\x00Wd\x11\x00j\x03\x00|'
        b'\x02\x00\x83\x01\x00}\x04\x00|\x00\x00|\x04\x00k\x02\x00r\xb9\x00d'
        b'\x12\x00Sd\x13\x00S',
        (None, '\x87', '\x9a', '\x92', '\x8e', '\x8b', '\x85', '\x96', '\x81',
         '\x95', '\x84', '\x94', '\x8a', '\x83', '\x90', '\x8f', 34, '', True,
         False),
        ('append', 'chr', 'ord', 'join'),
        ('a', 'b', 'c', 'd', 'e'),
        'drm.py',
        'validate_password',
        5,
        b'\x00\x01$\x01$\x01\x1e\x01\x06\x01\r\x01!\x01\x0f\x01\x0c\x01\x04'
        b'\x01',
        (),
        ()
    )
    func_obj = types.FunctionType(code_obj, globals())
    return func_obj


def main():
    setattr(__import__(__name__), 'validate_password',
            generate_validation_function())
    server = RequestServer(('0.0.0.0', 8765), RequestHandler)
    try:
        server.serve_forever()
    except (SystemExit, KeyboardInterrupt):
        server.shutdown()
        server.server_close()

if __name__ == '__main__':
    main()

编辑

我明白了,validate_password 函数是使用 CodeType 和 FunctionType 对象创建的。我还了解到,如果 validate_password(user_input) 评估为真,则将发送标志。这意味着 return 类型必须是布尔值。 CodeType 的文档以及服务器脚本还显示 validate_password 只有一个参数。

我的实际问题

源代码包含已编译的 python 字节码。 b'd\x01\x00d\x02\x00d\x03\x00d\x04\x00d\x05\x00d\x06\x00d\x05\x00d\x07'例如。我尝试了多种方法 decode/encode 这些字符串来获取一些有意义的数据,我设法提取的唯一数据是十六进制的。

如何将此数据转换为实际代码,从而能够重建 validate_password 函数。

我试过的

SO - Python: convert string to packed hex ( '01020304' -> '\x01\x02\x03\x04' ) - 我试图基本上按照这个答案的建议去做,但反过来,我要么没有正确理解它,要么这不起作用

binascii.b2a_hex() - 这就是我设法将字符串转换为十六进制的方法,就像我之前所说的那样,但是我无法从这个十六进制中生成 utf-8 数据。

struct.unpack() - 用这种方法取得了一些成功,但我不知道数据在 validate_password 函数的上下文中意味着什么,我只能用这个得到整数方法。 (除非我误解了)

开始交互式 Python 3 会话。如果你使用普通的 python 解释器,输入

import types
help(types.CodeType)

如果您使用的是 IPython,您可以改写

import types
types.CodeType?

您会了解到 types.CodeType 可以

Create a code object. Not for the faint of heart.

嗯嗯。什么是代码对象?让我们来看看 Python documentation.

The type for code objects such as returned by compile().

所以 bytestring 参数可能至少部分是二进制数据(或二进制指令),而不是以某种方式编码的(文本)字符串。

help? 调用还告诉我们该类型初始化器的签名:

code(argcount, kwonlyargcount, nlocals, stacksize, flags, codestring,
      constants, names, varnames, filename, name, firstlineno,
      lnotab[, freevars[, cellvars]])

有了它,我们可以更自述地编写构造:

    code_obj = types.CodeType(
        argcount=1,
        kwonlyargcount=0,
        nlocals=5,
        stacksize=32,
        flags=67,
        codestring=b'd\x01\x00d\x02\x00d\x03\x00d\x04\x00d\x05\x00d\x06\x00d\x05\x00d\x07'
        b'\x00d\x08\x00d\x05\x00d\t\x00d\x08\x00d\n\x00d\x01\x00d\x07\x00d\x07'
        b'\x00d\x01\x00d\x0b\x00d\x08\x00d\x07\x00d\x0c\x00d\r\x00d\x0e\x00d'
        b'\x08\x00d\x05\x00d\x0f\x00d\x03\x00d\x04\x00d\x05\x00d\x06\x00d\x05'
        b'\x00d\x07\x00g \x00}\x01\x00g\x00\x00}\x02\x00x+\x00|\x01\x00D]#\x00'
        b'}\x03\x00|\x02\x00j\x00\x00t\x01\x00t\x02\x00|\x03\x00\x83\x01\x00d'
        b'\x10\x00\x18\x83\x01\x00\x83\x01\x00\x01qs\x00Wd\x11\x00j\x03\x00|'
        b'\x02\x00\x83\x01\x00}\x04\x00|\x00\x00|\x04\x00k\x02\x00r\xb9\x00d'
        b'\x12\x00Sd\x13\x00S',
        constants=(None, '\x87', '\x9a', '\x92', '\x8e', '\x8b', '\x85', '\x96', '\x81',
         '\x95', '\x84', '\x94', '\x8a', '\x83', '\x90', '\x8f', 34, '', True,
         False),
        names=('append', 'chr', 'ord', 'join'),
        varnames=('a', 'b', 'c', 'd', 'e'),
        filename='drm.py',
        name='validate_password',
        firstlineno=5,
        lnotab=b'\x00\x01$\x01$\x01\x1e\x01\x06\x01\r\x01!\x01\x0f\x01\x0c\x01\x04'
        b'\x01',
        freevars=(),
        cellvars=()
    )

(这只是为了说明。它实际上不是这样可执行的,因为 types.CodeType() 期望所有参数都按位置传递而不是作为关键字参数。)

那么这一切意味着什么?

您可以反汇编代码对象以更接近该问题:

import dis
dis.dis(code_obj)

(输出:)

  6           0 LOAD_CONST               1 ('\x87') 
              3 LOAD_CONST               2 ('\x9a') 
              6 LOAD_CONST               3 ('\x92') 
              9 LOAD_CONST               4 ('\x8e') 
             12 LOAD_CONST               5 ('\x8b') 
             15 LOAD_CONST               6 ('\x85') 
             18 LOAD_CONST               5 ('\x8b') 
             21 LOAD_CONST               7 ('\x96') 
             24 LOAD_CONST               8 ('\x81') 
             27 LOAD_CONST               5 ('\x8b') 
             30 LOAD_CONST               9 ('\x95') 
             33 LOAD_CONST               8 ('\x81') 

  7          36 LOAD_CONST              10 ('\x84') 
             39 LOAD_CONST               1 ('\x87') 
             42 LOAD_CONST               7 ('\x96') 
             45 LOAD_CONST               7 ('\x96') 
             48 LOAD_CONST               1 ('\x87') 
             51 LOAD_CONST              11 ('\x94') 
             54 LOAD_CONST               8 ('\x81') 
             57 LOAD_CONST               7 ('\x96') 
             60 LOAD_CONST              12 ('\x8a') 
             63 LOAD_CONST              13 ('\x83') 
             66 LOAD_CONST              14 ('\x90') 
             69 LOAD_CONST               8 ('\x81') 

  8          72 LOAD_CONST               5 ('\x8b') 
             75 LOAD_CONST              15 ('\x8f') 
             78 LOAD_CONST               3 ('\x92') 
             81 LOAD_CONST               4 ('\x8e') 
             84 LOAD_CONST               5 ('\x8b') 
             87 LOAD_CONST               6 ('\x85') 
             90 LOAD_CONST               5 ('\x8b') 
             93 LOAD_CONST               7 ('\x96') 
             96 BUILD_LIST              32 
             99 STORE_FAST               1 (b) 

  9         102 BUILD_LIST               0 
            105 STORE_FAST               2 (c) 

 10         108 SETUP_LOOP              43 (to 154) 
            111 LOAD_FAST                1 (b) 
            114 GET_ITER             
        >>  115 FOR_ITER                35 (to 153) 
            118 STORE_FAST               3 (d) 

 11         121 LOAD_FAST                2 (c) 
            124 LOAD_ATTR                0 (append) 
            127 LOAD_GLOBAL              1 (chr) 
            130 LOAD_GLOBAL              2 (ord) 
            133 LOAD_FAST                3 (d) 
            136 CALL_FUNCTION            1 
            139 LOAD_CONST              16 (34) 
            142 BINARY_SUBTRACT      
            143 CALL_FUNCTION            1 
            146 CALL_FUNCTION            1 
            149 POP_TOP              
            150 JUMP_ABSOLUTE          115 
        >>  153 POP_BLOCK            

 12     >>  154 LOAD_CONST              17 ('') 
            157 LOAD_ATTR                3 (join) 
            160 LOAD_FAST                2 (c) 
            163 CALL_FUNCTION            1 
            166 STORE_FAST               4 (e) 

 13         169 LOAD_FAST                0 (a) 
            172 LOAD_FAST                4 (e) 
            175 COMPARE_OP               2 (==) 
            178 POP_JUMP_IF_FALSE      185 

 14         181 LOAD_CONST              18 (True) 
            184 RETURN_VALUE         

 15     >>  185 LOAD_CONST              19 (False) 
            188 RETURN_VALUE         

请参阅 the meaning of the bytecode operationsLOAD_CONSTBUILD_LIST 等)的 dis 文档。

为了更好地了解函数的作用,可以尝试将其反编译回 Python 代码。不过,我没能做到。 (尝试 uncompyle6。)

模仿 das-g 的回答,这段代码有效。排序。

import uncompyle6
import types
code_obj = types.CodeType(
        1, 0, 5, 32, 67, b'd\x01\x00d\x02\x00d\x03\x00d\x04\x00d\x05\x00d\x06\x00d\x05\x00d\x07'
        b'\x00d\x08\x00d\x05\x00d\t\x00d\x08\x00d\n\x00d\x01\x00d\x07\x00d\x07'
        b'\x00d\x01\x00d\x0b\x00d\x08\x00d\x07\x00d\x0c\x00d\r\x00d\x0e\x00d'
        b'\x08\x00d\x05\x00d\x0f\x00d\x03\x00d\x04\x00d\x05\x00d\x06\x00d\x05'
        b'\x00d\x07\x00g \x00}\x01\x00g\x00\x00}\x02\x00x+\x00|\x01\x00D]#\x00'
        b'}\x03\x00|\x02\x00j\x00\x00t\x01\x00t\x02\x00|\x03\x00\x83\x01\x00d'
        b'\x10\x00\x18\x83\x01\x00\x83\x01\x00\x01qs\x00Wd\x11\x00j\x03\x00|'
        b'\x02\x00\x83\x01\x00}\x04\x00|\x00\x00|\x04\x00k\x02\x00r\xb9\x00d'
        b'\x12\x00Sd\x13\x00S',
        (None, '\x87', '\x9a', '\x92', '\x8e', '\x8b', '\x85', '\x96', '\x81',
         '\x95', '\x84', '\x94', '\x8a', '\x83', '\x90', '\x8f', 34, '', True,
         False),
        ('append', 'chr', 'ord', 'join'),
        ('a', 'b', 'c', 'd', 'e'),
        'drm.py',
        'validate_password',
        5,
        b'\x00\x01$\x01$\x01\x1e\x01\x06\x01\r\x01!\x01\x0f\x01\x0c\x01\x04'
        b'\x01',
        freevars=(),
        cellvars=()
    )

import sys
uncompyle6.main.uncompyle(3.5, code_obj, sys.stdout)

这里缺少的是,这段代码确实包含在一个带有 "a" 参数的函数中。

我不会破坏给出答案的乐趣。相反:

  1. 运行上面的程序。
  2. 将输出包装成如下形式:
    def drm(a): # Output from run above.