Skip to content

Instantly share code, notes, and snippets.

@maple3142
Last active May 30, 2022 03:33

Revisions

  1. maple3142 revised this gist May 30, 2022. 1 changed file with 1 addition and 0 deletions.
    1 change: 1 addition & 0 deletions xx.sh
    Original file line number Diff line number Diff line change
    @@ -1,4 +1,5 @@
    # use xx.py to build the opcode mapping
    # need to compile https://github.com/python/cpython/tree/702e0da000bf28aa20cb7f3893b575d977506495 at ./cpython-702e0da000bf28aa20cb7f3893b575d977506495
    rm -rf __pycache__;PYTHONHOME=`pwd` ./bin/python -c 'import hello' ; mv __pycache__/hello.cpython-312.pyc patched_hello.pyc
    rm -rf __pycache__; ./cpython-702e0da000bf28aa20cb7f3893b575d977506495/python -c 'import hello' ; mv __pycache__/hello.cpython-312.pyc original_hello.pyc
    ./cpython-702e0da000bf28aa20cb7f3893b575d977506495/python xx.py
  2. maple3142 revised this gist May 30, 2022. 1 changed file with 1 addition and 4 deletions.
    5 changes: 1 addition & 4 deletions rev.py
    Original file line number Diff line number Diff line change
    @@ -143,10 +143,7 @@ def walk(code):
    mask = consts[bc[j + 16 + 1]]
    # assert opname[bc[j + 22]] == "LOAD_CONST"
    value = consts[bc[j + 22 + 1]]
    # if mask in [8,64,128]:
    # value ^= mask
    if idx <= 0:
    print(f"{j} flag[{idx}] & {mask} == {value}")
    print(f"{j} flag[{idx}] & {mask} == {value}")
    if mask == value:
    flag[idx] |= mask
    print(flag)
  3. maple3142 revised this gist May 30, 2022. 1 changed file with 1 addition and 0 deletions.
    1 change: 1 addition & 0 deletions hello.py
    Original file line number Diff line number Diff line change
    @@ -1,5 +1,6 @@
    # A Python 3 script trying to in include every bytecode in Python 3.12, but it actually misses these bytecode:
    # {'LOAD_ASSERTION_ERROR', 'CHECK_EG_MATCH', 'UNPACK_EX', 'POP_JUMP_BACKWARD_IF_NONE', 'DELETE_DEREF', 'POP_JUMP_FORWARD_IF_NOT_NONE', 'EXTENDED_ARG', 'PRINT_EXPR', 'LOAD_CLASSDEREF', 'MAP_ADD', 'POP_JUMP_BACKWARD_IF_NOT_NONE', 'JUMP_IF_FALSE_OR_POP', 'IS_OP', 'LIST_TO_TUPLE', 'JUMP_IF_TRUE_OR_POP', 'PREP_RERAISE_STAR'}
    # the idea comes from https://github.com/kholia/dedrop
    def main():

    (lambda:1)()
  4. maple3142 created this gist May 30, 2022.
    430 changes: 430 additions & 0 deletions hello.py
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,430 @@
    # A Python 3 script trying to in include every bytecode in Python 3.12, but it actually misses these bytecode:
    # {'LOAD_ASSERTION_ERROR', 'CHECK_EG_MATCH', 'UNPACK_EX', 'POP_JUMP_BACKWARD_IF_NONE', 'DELETE_DEREF', 'POP_JUMP_FORWARD_IF_NOT_NONE', 'EXTENDED_ARG', 'PRINT_EXPR', 'LOAD_CLASSDEREF', 'MAP_ADD', 'POP_JUMP_BACKWARD_IF_NOT_NONE', 'JUMP_IF_FALSE_OR_POP', 'IS_OP', 'LIST_TO_TUPLE', 'JUMP_IF_TRUE_OR_POP', 'PREP_RERAISE_STAR'}
    def main():

    (lambda:1)()

    zzz = 1
    del zzz

    a = 1; b = 2
    (a, b) = (b, a)

    a = 1
    (a, a, a) = (a, a, a)

    {'a':1}

    x = list(range(6))
    x[2:4] += 'abc'

    a = 1
    a = +a

    a = 1
    a = -a

    a = 1
    a = not a

    a = 1

    a = 1
    a = ~a

    a = [i*i for i in (1,2)]

    a = 2
    a = a ** 2

    a = 2
    a = a * 2

    a = 2
    a = a / 2

    a = 2
    a = a % 2

    a = 2
    a = a + 2

    a = 2
    a = a - 2

    a = [1]
    a[0]

    a = 2
    a = a // 2

    a = 2
    a = a / 2
    a = a // 2

    a = 1
    a //= 10.2

    a = 1
    a /= 2
    a //= 2
    b = 2
    a //= b
    a /= b

    a = [1,2,3]
    a = a[:]


    a = [1,2,3]
    a = a[1:]

    a = [1,2,3]
    a = a[:2]

    a = [1,2,3]
    a = a[1:2]

    a = [1,2,3]
    a[:] = [1,2,3]

    a = [1,2,3]
    a[1:] = [2,3]

    a = [1,2,3]
    a[:2] = [1,2]

    a = [1,2,3]
    a[1:2] = [2]

    a = [1,2,3]
    del a[:]

    a = [1,2,3]
    del a[1:]

    a = [1,2,3]
    del a[:2]


    a = [1,2,3]
    del a[1:2]

    a = 1
    a += 1

    a = 1
    a -= 1

    a = 1
    a *= 1

    a = 1
    a /= 1

    a = 1
    a %= 1

    a = [0, 1]
    a[0] = 1

    a = [1]
    del a[0]

    a = 1
    a = a << 1

    a = 1
    a = a >> 1

    a = 1
    a = a & 1

    a = 1
    a = a ^ 1

    a = 1
    a = a | 1

    a = 1
    a **= 1

    for a in (1,2): pass

    print("hello world!")

    print()

    def fv(a,b): pass
    av = (1,2)
    fv(*av)

    def fkv(a,b,c): pass
    akv = {"b":1,"c":2}
    b = (3,)
    fkv(*b, **akv)

    def fk(a,b): pass
    ak = {"a":1,"b":2}
    fk(**ak)


    import sys
    print("hello world", end=' ', file=sys.stdout)

    import sys
    print(file=sys.stdout)
    del sys.api_version

    zzz = 89
    del zzz


    a = 1
    a <<= 1

    a = 1
    a >>= 1

    a = 1
    a &= 1

    a = 1
    a ^= 1

    a = 1
    a |= 1

    for a in (1,2): break

    try:
    with open("1.txt") as f:
    print(f.read())
    except:
    pass

    class a: pass

    # empty file

    exec("print('hello world')", globals(), locals())

    frozenset({1, 2, 3})

    for a in (1,2): break

    try:
    a = 1
    except ValueError:
    a = 2
    finally:
    a = 3

    class a: pass

    a = 1

    a = 1
    del a

    (a, b) = "ab"

    for i in (1,2): pass

    a = 0
    b = [0]
    b[a] += 1

    a = 1

    a = 1
    a = a

    a = 1;
    a = (a, a)

    [1,2,3]

    {"a":1,"b":2}

    [].sort()

    a = 1 == 2

    a = 2+3+4
    "@"*4
    a="abc" + "def"
    a = 3**4
    a = 13//4

    a //= 2

    from dis import opmap

    if 1 == 2: pass
    else: pass

    if 1 == 2: pass
    else: pass

    if not(1 == 2): pass
    else: pass

    for i in (1,2): pass

    for x in (1,2):
    try: continue
    except: pass

    while 0 > 1: pass

    try:
    a = 1
    except ValueError:
    a = 2
    finally:
    a = 3

    try:
    a = 1
    except ValueError:
    a = 2
    finally:
    a = 3

    a = [1,2,3,4]
    b = a[::-1]

    xyz = 0

    def lolcats():
    global xyz
    pass

    raise ValueError


    def fc():
    a = 1
    zyx = set()
    zyx.add("hello")
    zyx.add("abc")
    zyx.remove("hello")
    def g():
    return a + 1
    return g()

    print(fc())


    def f(): pass
    f()


    def f1():
    from sys import environ
    a = 1
    a = a

    def f2():
    a = 1
    a = a

    def f3():
    a = 1
    del a


    import sys
    sys.stderr = sys.stdout

    import sys
    # del sys.stderr

    l1 = 0
    def lolx():
    global l1
    l1 = 1

    l2 = 0
    def loly():
    global l2
    del l2
    def f():
    a = 3
    b = 5
    def g():
    return a + b
    f()

    mylist = [1, 1, 1, 2, 2, 3]
    z = {x for x in mylist if mylist.count(x) >= 2}
    a = {x for x in 'abracadabra' if x not in 'abc'}
    set([20, 0])

    lolx()
    loly()

    def foo():
    print('hello')
    yield 1
    print('world')
    yield 2

    a = foo()
    print(next(a))
    print(next(a))


    def myfunc(alist: list):
    return len(alist)

    def peko(*args, **kwargs):
    yield from f()
    1
    2
    3
    if True:
    return 1
    1
    2
    3
    pass

    async def f():
    await f()
    async for x in f():
    pass

    async def g():
    await g()
    yield 1
    async with x:
    pass

    class A:
    n: int
    a = 1
    match a:
    case 1: print(1)
    case []: pass
    case A(): pass
    case {1:a}: pass

    peko(*[123])
    peko(**{'a':1})
    peko(*[123], **{'a':1})
    (x for x in [])
    dt={}
    dt|={'1':123}
    f'1{2}3'
    {**{1:2}}
    tuple([1,2,3])
    del a
    from os import *
    peko(*[1,2,3,4,5,6,7,8,9])
    assert True
    class A:
    pass

    main()
    158 changes: 158 additions & 0 deletions rev.py
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,158 @@
    # fix the opcode from mapping, and attempt to solve for the flag
    # but it turns out the bytecode also patches python to make `bytes([ord('F'), ...])` behavior different
    # so it is necessary to guess or reverse how it works
    # out team got the flag by guessing
    import dis
    import marshal
    from dis import opname

    fmap = {
    151: 151,
    100: 108,
    132: 133,
    90: 98,
    2: 12,
    101: 103,
    166: 166,
    0: 10,
    171: 172,
    1: 11,
    108: 100,
    106: 106,
    95: 95,
    97: 99,
    102: 101,
    71: 70,
    120: 120,
    107: 104,
    114: 111,
    110: 115,
    32: 30,
    30: 33,
    152: 152,
    129: 129,
    92: 96,
    31: 32,
    33: 31,
    99: 97,
    103: 107,
    142: 142,
    105: 102,
    164: 163,
    68: 71,
    122: 122,
    155: 155,
    157: 157,
    165: 162,
    162: 0,
    91: 91,
    84: 84,
    9: 15,
    83: 86,
    125: 125,
    126: 126,
    124: 124,
    116: 116,
    133: 131,
    25: 35,
    60: 61,
    10: 0,
    11: 1,
    12: 2,
    15: 9,
    61: 60,
    93: 93,
    140: 140,
    156: 156,
    172: 171,
    96: 92,
    53: 53,
    160: 160,
    35: 25,
    49: 0,
    115: 112,
    119: 119,
    89: 87,
    104: 109,
    163: 164,
    36: 49,
    109: 105,
    176: 176,
    130: 130,
    145: 145,
    135: 137,
    138: 138,
    136: 136,
    149: 146,
    137: 139,
    98: 90,
    175: 175,
    146: 147,
    118: 118,
    75: 82,
    86: 83,
    69: 69,
    123: 0,
    134: 128,
    131: 0,
    50: 37,
    51: 51,
    54: 54,
    87: 89,
    52: 52,
    85: 85,
    }
    imap = {v: k for k, v in fmap.items()}


    with open("chall.pyc", "rb") as f:
    header = f.read(16)
    code = marshal.load(f)


    def walk(code):
    new_code = bytes([v if i & 1 else imap[v] for i, v in enumerate(code.co_code)])
    new_consts = tuple(
    [c if type(c) != type(code) else walk(c) for c in code.co_consts]
    )
    return code.replace(co_consts=new_consts, co_code=new_code)


    new_code = walk(code)
    # dis.dis(new_code)
    with open("new_chall.pyc", "wb") as f:
    f.write(header)
    marshal.dump(new_code, f)
    # dis.dis(new_code)
    check_flag = new_code.co_consts[1]
    bc = check_flag.co_code
    consts = check_flag.co_consts
    base = 48
    chk = 84 - 48
    flag = bytearray(59)
    for i in range(1000):
    j = base + chk * i
    if j + 4 >= len(bc):
    break
    # dis.dis(bc[j:j+chk])
    # assert opname[bc[j + 4]] == "LOAD_CONST"
    idx = consts[bc[j + 4 + 1]]
    # assert opname[bc[j + 18]] == "BINARY_OP"
    assert bc[j + 18 + 1] == 1
    # assert opname[bc[j + 16]] == "LOAD_CONST"
    mask = consts[bc[j + 16 + 1]]
    # assert opname[bc[j + 22]] == "LOAD_CONST"
    value = consts[bc[j + 22 + 1]]
    # if mask in [8,64,128]:
    # value ^= mask
    if idx <= 0:
    print(f"{j} flag[{idx}] & {mask} == {value}")
    if mask == value:
    flag[idx] |= mask
    print(flag)
    # bytearray(b'\x8e\x86\x8d\x95\xbb\xaa\xb9\xb2\xc8\xb3\xba\xc5\xaf\xc3\xc8\xc7\xc5\xb9\xcf\xe3\xe3\xe6\xd3\xd3\xd1\xe4\xe1\xcd\xe3\xf2\xed\xfa\xe0\xe9\xea\xddC0EH\xe7\xf3\x0f\xed\x06\x17\x02\xf5_Z^\x13`\x16\x15fhj)')

    from new_chall import check_flag

    print(check_flag(flag))

    35 changes: 35 additions & 0 deletions xx.py
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,35 @@
    # compare the bytecode difference to build a mapping from original opcode and patched opcode
    # it is actually not correct as it assumes every instruction is 2 bytes, which is untrue is later python versions
    import dis
    import marshal

    # rm -rf __pycache__;PYTHONHOME=`pwd` ./bin/python -c 'import hello' ; xxd __pycache__/hello.cpython-312.pyc && mv __pycache__/hello.cpython-312.pyc patched_hello.pyc
    # rm -rf __pycache__; ./cpython-702e0da000bf28aa20cb7f3893b575d977506495/python -c 'import hello' ; xxd __pycache__/hello.cpython-312.pyc && mv __pycache__/hello.cpython-312.pyc original_hello.pyc


    fp=open('patched_hello.pyc','rb')
    fo=open('original_hello.pyc','rb')
    fp.seek(16)
    fo.seek(16)
    orig = marshal.load(fo)
    patched = marshal.load(fp)
    dt = {}
    dt2 = {}
    def walk(orig, patched):
    print(orig.co_name, len(orig.co_code), len(patched.co_code))
    for i in range(0, len(orig.co_code), 2):
    o = dis.opname[orig.co_code[i]]
    p = dis.opname[patched.co_code[i]]
    if o in dt:
    if dt[o] != p:
    print('NEQ',o, dt[o], p)
    dt[o] = p
    dt2[orig.co_code[i]] = patched.co_code[i]
    for x, y in zip(orig.co_consts, patched.co_consts):
    if type(x) == type(orig) and type(y) == type(patched):
    walk(x,y)
    walk(orig, patched)
    print(dt)
    print(len(dt.keys()))
    print(set(dis.opmap.keys()) - set(dt.keys()))
    print(dt2)
    4 changes: 4 additions & 0 deletions xx.sh
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,4 @@
    # use xx.py to build the opcode mapping
    rm -rf __pycache__;PYTHONHOME=`pwd` ./bin/python -c 'import hello' ; mv __pycache__/hello.cpython-312.pyc patched_hello.pyc
    rm -rf __pycache__; ./cpython-702e0da000bf28aa20cb7f3893b575d977506495/python -c 'import hello' ; mv __pycache__/hello.cpython-312.pyc original_hello.pyc
    ./cpython-702e0da000bf28aa20cb7f3893b575d977506495/python xx.py