79453389

Date: 2025-02-20 05:06:33
Score: 0.5
Natty:
Report link

Firstly, do NOT directly use types.CodeType and use pyobject.Code instead, as constructing types.CodeType is too complex and not compatible across multiple Python versions.
The pyobject library, which can be installed via pip install pyobject, provides a high-level wrapper for code objects.

After testing it on Python 3.11, the output is:

E:\Git-repositories\Github-publish\pyc-zipper>py311
Python 3.11.8 (tags/v3.11.8:db85d51, Feb  6 2024, 22:03:32) [MSC v.1937 64 bit (AMD64)] on win32
Type "help", "copyright", "credits" or "license" for more information.
>>> import random
>>> from pyobject import Code
>>> def f(a):return a+1
...
>>> c=Code(f.__code__)
>>> new_co_code = bytes([random.randint(0, 255) for _ in range(24)])
>>> new_c=c.copy()
>>> new_c.co_code=new_co_code # pyobject.Code is mutable
>>> new_code=new_c.to_code()
>>> new_code.co_code == new_co_code
False
>>> c.co_code
b'\x97\x00|\x00d\x01z\x00\x00\x00S\x00'
>>> new_code.co_code
b'\x90ik^\x00\x00\x00\x00\xa6\xec\x00\x00x\x8adR=\xeb\x00\xe7\x00\x8cT\x90'

As I tried to disassembly it:

>>> c.dis()
  1           0 EXTENDED_ARG           105
Traceback (most recent call last):
  ...
IndexError: tuple index out of range

>>> new_co_code=bytearray(new_co_code) # convert to bytearray
>>> new_co_code[1]=new_co_code[3]=0 # modify bytes at 1 and 3 to 0 to observe them clearly
>>> new_co_code=bytes(new_co_code) # convert it back to bytes
>>> new_co_code
b'"\x00k\x00d0Ef\x8f\xec\xf9\xc8x\x8adR=\xeb\xe7\xe7\xc8\x8cT\x90'
>>> c.co_code=new_co_code
>>> c.to_code().co_code
b'\x90\x00k\x00\x00\x00\x00\x00\xa6\xec\x00\x00x\x8adR=\xeb\x00\xe7\x00\x8cT\x90'
>>> c.dis()
  1           0 EXTENDED_ARG             0
              2 COMPARE_OP               0 (<)
              ...

So I guess that Python 3.11 automatically adds EXTENDED_ARG opcode that extends the original one-byte argument when some irregular bytecodes are detected.

>>> new_co_code=bytearray(new_co_code)
>>> new_co_code[0]=ord('d')
>>> new_co_code=bytes(new_co_code)
>>> c.co_code=new_co_code
>>> c.to_code().co_code
b'd\x00k\x00\x00\x00\x00\x00\xa6\xec\x00\x00x\x8adR=\xeb\x00\xe7\x00\x8cT\x90'
>>> c.dis()
  1           0 LOAD_CONST               0 (None)
              2 COMPARE_OP               0 (<)
              8 PRECALL                236
             12 COPY                   138
Traceback (most recent call last):
  ...
IndexError: tuple index out of range

The first opcode is set to LOAD_CONST, and the EXTENDED_ARG is not added automatically.

Furthermore:

>>> new_co_code=bytearray(new_co_code)
>>> new_co_code[0]=0x90 # directly set to EXTENDED_ARG
>>> new_co_code=bytes(new_co_code)
>>> c.co_code=new_co_code
>>> c.to_code().co_code
b'\x90\x00k\x00\x00\x00\x00\x00\xa6\xec\x00\x00x\x8adR=\xeb\x00\xe7\x00\x8cT\x90'
>>> c.dis()
  1           0 EXTENDED_ARG             0
              2 COMPARE_OP               0 (<)
              8 PRECALL                236
              ...

It indicates that some other opcodes, including COMPARE_OP will be modified automaticlly.

Reasons:
  • Contains signature (1):
  • Long answer (-1):
  • Has code block (-0.5):
  • Low reputation (1):
Posted by: qfcy