Skip to content

Instantly share code, notes, and snippets.

@betamaoIS
Created April 15, 2022 13:57
Show Gist options
  • Save betamaoIS/d38ec4a398f97d7a6b1ad8828eef0b4d to your computer and use it in GitHub Desktop.
Save betamaoIS/d38ec4a398f97d7a6b1ad8828eef0b4d to your computer and use it in GitHub Desktop.
修复pyc/pyo的行号表,需要pyc/o文件,反编译的py文件,这个代码是python26写的,高版本CodeType有变化需要移植下...
import marshal
import logging
import os
import sys
import re
from types import CodeType
from zipfile import ZipFile
logging.basicConfig(
level=logging.DEBUG,
format='%(asctime)s [%(levelname)s] %(message)s',
datefmt='%m-%d %H:%M'
)
logger = logging.getLogger()
def traversal_co(co_obj, parent_name=''):
if not parent_name:
parent_name = co_obj.co_name # first traversal
name_code = {parent_name: co_obj}
else:
name_code = {}
for const in co_obj.co_consts:
if isinstance(const, CodeType):
if const.co_name != '<lambda>': # dont cache lambda func
full_name = parent_name + '.' + const.co_name
name_code[full_name] = const
name_code.update(traversal_co(const, full_name))
return name_code
def load_src_lineno(src_path):
with open(src_path, 'U') as f:
data = f.read()
co = compile(data, src_path, 'exec', dont_inherit=True)
return traversal_co(co, '')
def fix_co(pyc_co, current_name, src_co_map):
code_consts = list(pyc_co.co_consts)
if not current_name:
current_name = pyc_co.co_name
for i, const in enumerate(code_consts):
if isinstance(const, CodeType):
fullname = current_name + '.' + const.co_name
code_consts[i] = fix_co(const, fullname, src_co_map)
src_co = src_co_map.get(current_name)
if not src_co:
if not current_name.endswith('<lambda>'):
logger.warn('%s not found in src, use origin co...' % current_name)
# else:
# return pyc_co
src_co = pyc_co
# if 'gui' in current_name:
# logger.debug('%s old %d new %d' % (current_name, pyc_co.co_firstlineno, src_co.co_firstlineno))
# !!!! only for python2
new_code = CodeType(
pyc_co.co_argcount, pyc_co.co_nlocals, pyc_co.co_stacksize, pyc_co.co_flags,
pyc_co.co_code, tuple(code_consts), pyc_co.co_names,
pyc_co.co_varnames, src_co.co_filename, pyc_co.co_name,
src_co.co_firstlineno, src_co.co_lnotab or '\1\1', pyc_co.co_freevars, pyc_co.co_cellvars
)
# for have not lnotab, use (1, 1) avoid str ref...
return new_code
def fixup(pyc_bytes, src_bytes, src_path):
pyc_co = marshal.loads(pyc_bytes[8:])
co = compile(src_bytes, src_path, 'exec', dont_inherit=True)
src_map_no = traversal_co(co, '')
new_pyc_co = fix_co(pyc_co, '', src_map_no)
return pyc_bytes[:8] + marshal.dumps(new_pyc_co)
# def fixup_file(pyc_path, src_path, out_path):
# with open(pyc_path, 'rb') as f:
# data = f.read()
# pyc_co = marshal.loads(data[8:])
# src_map_no = load_src_lineno(src_path)
# new_pyc_co = fix_co(pyc_co, '', src_map_no)
# try:
# os.makedirs(out_path)
# except Exception:
# ...
# with open(out_path, 'wb') as f:
# f.write(data[:8])
# f.write(marshal.dumps(new_pyc_co))
# fixup(r'C:\Users\betamao\Desktop\src\gui.pyc', r'C:\Users\betamao\Desktop\src\guix.py')
def fixup_ipoe(in_ipoe, unzip_ipoe_dir, fixed_ipoe):
in_ipoe_zp = ZipFile(in_ipoe)
out_ipoe_zp = ZipFile(fixed_ipoe, mode='w')
for one_file in in_ipoe_zp.filelist:
file_content = in_ipoe_zp.read(one_file.filename)
src_path = os.path.join(unzip_ipoe_dir, one_file.filename[:-1])
if one_file.filename.endswith(('.pyo', '.pyc')) and os.path.exists(src_path) and len(
file_content) > 32: # only process ...
try:
with open(src_path, 'U') as f:
src_data = f.read()
if ':' == src_path[1]:
src_path = '/' + src_path[0] + src_path[2:].replace('\\', '/') # for *nux path
file_content = fixup(file_content, src_data, src_path)
except Exception as e:
logger.error('fix %s::%s -> %s' % (in_ipoe, one_file.filename, e))
out_ipoe_zp.writestr(one_file.filename, file_content)
out_ipoe_zp.close()
def makedirs(dir_):
try:
os.makedirs(dir_)
except WindowsError as e:
if e.errno == 17:
return
raise
def main():
if len(sys.argv) > 3:
decrypted_ipoe_dir, project_dir, fixed_ipoe_dir = sys.argv[0:3]
else:
decrypted_ipoe_dir = r'C:\Users\betamao\Desktop\decrypted_ipoe'
project_dir = r'C:\Users\betamao\Desktop\proj'
fixed_ipoe_dir = r'C:\Users\betamao\Desktop\fixed_ipoe'
logger.debug(locals())
makedirs(fixed_ipoe_dir)
for fn in os.listdir(decrypted_ipoe_dir):
in_ipoe = os.path.join(decrypted_ipoe_dir, fn)
if not in_ipoe.endswith('.ipoe') or os.path.isdir(in_ipoe):
continue
unzip_ipoe_dir = os.path.join(project_dir, fn)
fixed_ipoe = os.path.join(fixed_ipoe_dir, fn)
fixup_ipoe(in_ipoe, unzip_ipoe_dir, fixed_ipoe)
if __name__ == '__main__':
main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment