Created
April 15, 2022 13:57
-
-
Save betamaoIS/d38ec4a398f97d7a6b1ad8828eef0b4d to your computer and use it in GitHub Desktop.
修复pyc/pyo的行号表,需要pyc/o文件,反编译的py文件,这个代码是python26写的,高版本CodeType有变化需要移植下...
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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