From c3f9db22c17dbfece05d619de84084cc99d8d94f Mon Sep 17 00:00:00 2001 From: Lil-Ran Date: Wed, 5 Mar 2025 18:08:18 +0800 Subject: [PATCH] fix: py3.10 limited support --- ASTree.cpp | 61 +++++++++++++++++++++++++++++++++++++------------ helpers/shot.py | 33 +++++++++++++++++++++----- 2 files changed, 74 insertions(+), 20 deletions(-) diff --git a/ASTree.cpp b/ASTree.cpp index bd12b85..2d84e35 100644 --- a/ASTree.cpp +++ b/ASTree.cpp @@ -121,22 +121,18 @@ PycRef PyarmorMixStrDecrypt(const std::string &inputString, PycModule * void CallOrPyarmorBuiltins(FastStack &stack, PycRef &curblock, PycModule *mod) { - PycRef top = stack.top(); - if (top->type() != ASTNode::NODE_CALL) + if (stack.empty() || stack.top()->type() != ASTNode::NODE_CALL) return; - PycRef call = top.cast(); + PycRef call = stack.top().cast(); if (call->func().type() != ASTNode::NODE_OBJECT) return; - PycRef func_name_obj = call->func().cast()->object(); - if (func_name_obj.try_cast() == nullptr) + PycRef func_name = call->func().cast()->object().try_cast(); + if (func_name == nullptr || !func_name->startsWith("__pyarmor_")) return; - if (!func_name_obj.cast()->startsWith("__pyarmor_")) - return; - - const std::string& name = func_name_obj.cast()->strValue(); + const std::string& name = func_name->strValue(); if (name.find("__pyarmor_assert_") == std::string::npos) { PycRef new_str = new PycString(PycString::TYPE_UNICODE); @@ -153,10 +149,10 @@ void CallOrPyarmorBuiltins(FastStack &stack, PycRef &curblock, PycModu const auto& param = call->pparams().front(); if (param.type() == ASTNode::NODE_OBJECT) { - PycRef obj = param.cast()->object(); - if (obj.try_cast() == nullptr) + PycRef obj = param.cast()->object().try_cast(); + if (obj == nullptr) return; - PycRef new_node = PyarmorMixStrDecrypt(obj.cast()->strValue(), mod); + PycRef new_node = PyarmorMixStrDecrypt(obj->strValue(), mod); stack.pop(); stack.push(new_node); // result of __pyarmor_assert__(b'something') @@ -655,7 +651,9 @@ PycRef BuildFromCode(PycRef code, PycModule* mod) stack.push(new ASTCall(func, pparamList, kwparamList)); + // BEGIN ONESHOT TEMPORARY PATCH CallOrPyarmorBuiltins(stack, curblock, mod); + // END ONESHOT PATCH } break; case Pyc::CALL_FUNCTION_VAR_A: @@ -684,7 +682,9 @@ PycRef BuildFromCode(PycRef code, PycModule* mod) call.cast()->setVar(var); stack.push(call); + // BEGIN ONESHOT TEMPORARY PATCH CallOrPyarmorBuiltins(stack, curblock, mod); + // END ONESHOT PATCH } break; case Pyc::CALL_FUNCTION_KW_A: @@ -713,7 +713,9 @@ PycRef BuildFromCode(PycRef code, PycModule* mod) call.cast()->setKW(kw); stack.push(call); + // BEGIN ONESHOT TEMPORARY PATCH CallOrPyarmorBuiltins(stack, curblock, mod); + // END ONESHOT PATCH } break; case Pyc::CALL_FUNCTION_VAR_KW_A: @@ -745,9 +747,12 @@ PycRef BuildFromCode(PycRef code, PycModule* mod) call.cast()->setVar(var); stack.push(call); + // BEGIN ONESHOT TEMPORARY PATCH CallOrPyarmorBuiltins(stack, curblock, mod); + // END ONESHOT PATCH } break; + // BEGIN ONESHOT TEMPORARY PATCH case Pyc::CALL_FUNCTION_EX_A: { PycRef kw; @@ -770,6 +775,7 @@ PycRef BuildFromCode(PycRef code, PycModule* mod) CallOrPyarmorBuiltins(stack, curblock, mod); } break; + // END ONESHOT PATCH case Pyc::CALL_METHOD_A: { ASTCall::pparam_t pparamList; @@ -797,7 +803,9 @@ PycRef BuildFromCode(PycRef code, PycModule* mod) stack.pop(); stack.push(new ASTCall(func, pparamList, ASTCall::kwparam_t())); + // BEGIN ONESHOT TEMPORARY PATCH CallOrPyarmorBuiltins(stack, curblock, mod); + // END ONESHOT PATCH } break; case Pyc::CONTINUE_LOOP_A: @@ -1472,7 +1480,9 @@ PycRef BuildFromCode(PycRef code, PycModule* mod) bool push = true; do { - // A polyfill for Pyarmor which jumps forward on top level scope + // BEGIN ONESHOT TEMPORARY PATCH + // This implementation is probably wrong + // Pyarmor jumps forward on top level scope auto &top = blocks.top(); if (top == defblock) { pos += offs; @@ -1481,6 +1491,7 @@ PycRef BuildFromCode(PycRef code, PycModule* mod) } break; } + // END ONESHOT PATCH blocks.pop(); @@ -2648,8 +2659,24 @@ PycRef BuildFromCode(PycRef code, PycModule* mod) stack.push(next_tup); } break; + // BEGIN ONESHOT TEMPORARY PATCH + // These opcodes are not implemented + case Pyc::JUMP_IF_NOT_EXC_MATCH_A: + { + PycRef ex_type = stack.top(); + stack.pop(); + PycRef cur_ex = stack.top(); + stack.pop(); + } + case Pyc::RERAISE: + case Pyc::RERAISE_A: + break; + // END ONESHOT PATCH default: - fprintf(stderr, "Unsupported opcode: %s (%d) at %s\n", Pyc::OpcodeName(opcode), opcode, code->qualName()->value()); + fprintf(stderr, "Unsupported opcode: %s (%d) at %s\n", + Pyc::OpcodeName(opcode), + opcode, + mod->verCompare(3, 11) >= 0 ? code->qualName()->value() : code->name()->value()); cleanBuild = false; return new ASTNodeList(defblock->nodes()); } @@ -3073,6 +3100,12 @@ void print_src(PycRef node, PycModule* mod, std::ostream& pyc_output) case ASTNode::NODE_BLOCK: { PycRef blk = node.cast(); + + // BEGIN ONESHOT TEMPORARY PATCH + if (blk->blktype() == ASTBlock::BLK_MAIN) + break; + // END ONESHOT PATCH + if (blk->blktype() == ASTBlock::BLK_ELSE && blk->size() == 0) break; diff --git a/helpers/shot.py b/helpers/shot.py index 275927b..eba85b7 100644 --- a/helpers/shot.py +++ b/helpers/shot.py @@ -62,12 +62,23 @@ def decrypt_process(runtimes: Dict[str, RuntimeInfo], sequences: List[Tuple[str, stdout = sp.stdout.decode().splitlines() stderr = sp.stderr.decode().splitlines() for line in stdout: - logger.warning(f'STDOUT {line} ({path})') + logger.warning(f'PYCDC: {line} ({path})') for line in stderr: - if line.startswith('Warning'): - logger.warning(f'STDERR {line} ({path})') - elif not line.startswith('Unsupported opcode:') or args.show_err_opcode: - logger.error(f'STDERR {line} ({path})') + if line.startswith(( + 'Warning: Stack history is empty', + 'Warning: Stack history is not empty!', + 'Warning: block stack is not empty!', + )): + if args.show_warn_stack or args.show_all: + logger.warning(f'PYCDC: {line} ({path})') + elif line.startswith('Unsupported opcode:'): + if args.show_err_opcode or args.show_all: + logger.error(f'PYCDC: {line} ({path})') + else: + logger.error(f'PYCDC: {line} ({path})') + if sp.returncode != 0: + logger.warning(f'PYCDC returned {sp.returncode} ({path})') + continue except Exception as e: logger.error(f'Decrypt failed: {e} ({path})') continue @@ -98,9 +109,19 @@ def parse_args(): help='save data found in source files as-is', action='store_true', ) + parser.add_argument( + '--show-all', + help='show all pycdc errors and warnings', + action='store_true', + ) parser.add_argument( '--show-err-opcode', - help='show pycdc unsupported opcode error', + help='show pycdc unsupported opcode errors', + action='store_true', + ) + parser.add_argument( + '--show-warn-stack', + help='show pycdc stack related warnings', action='store_true', ) return parser.parse_args()