/** I want to use functions in pycdas.cpp directly, but not moving them to * another file, to sync with upstream in the future easily. */ #define main pycdas_main # include "pycdas.cpp" #undef main #include "ASTree.h" const char* VERSION = "v0.2.1+"; int main(int argc, char* argv[]) { const char* infile = nullptr; unsigned disasm_flags = 0; bool banner = true; std::ofstream dc_out_file; std::ofstream das_out_file; for (int arg = 1; arg < argc; ++arg) { if (strcmp(argv[arg], "--pycode-extra") == 0) { disasm_flags |= Pyc::DISASM_PYCODE_VERBOSE; } else if (strcmp(argv[arg], "--show-caches") == 0) { disasm_flags |= Pyc::DISASM_SHOW_CACHES; } else if (strcmp(argv[arg], "--help") == 0 || strcmp(argv[arg], "-h") == 0) { fprintf(stderr, "Usage: %s [options] input.1shot.seq\n\n", argv[0]); fputs("Options:\n", stderr); fputs(" --pycode-extra Show extra fields in PyCode object dumps\n", stderr); fputs(" --show-caches Don't suprress CACHE instructions in Python 3.11+ disassembly\n", stderr); fputs(" --no-banner Don't output banner\n", stderr); fputs(" --help Show this help text and then exit\n", stderr); return 0; } else if (strcmp(argv[arg], "--no-banner") == 0) { banner = false; } else if (argv[arg][0] == '-') { fprintf(stderr, "Error: Unrecognized argument %s\n", argv[arg]); return 1; } else if (!infile) { infile = argv[arg]; } else { fprintf(stderr, "Error: Only one input file allowed, got %s and %s\n", infile, argv[arg]); return 1; } } if (!infile) { fputs("No input file specified\n", stderr); return 1; } std::string prefix_name; const char *prefix_name_pos = strstr(infile, ".1shot.seq"); if (prefix_name_pos == NULL) { prefix_name = infile; } else { prefix_name = std::string(infile, prefix_name_pos - infile + 6); } dc_out_file.open(prefix_name + ".cdc.py", std::ios_base::out); if (dc_out_file.fail()) { fprintf(stderr, "Error opening file '%s' for writing\n", (prefix_name + ".cdc.py").c_str()); return 1; } das_out_file.open(prefix_name + ".das", std::ios_base::out); if (das_out_file.fail()) { fprintf(stderr, "Error opening file '%s' for writing\n", (prefix_name + ".das").c_str()); return 1; } PycModule mod; try { mod.loadFromOneshotSequenceFile(infile); } catch (std::exception &ex) { fprintf(stderr, "Error disassembling %s: %s\n", infile, ex.what()); return 1; } if (!mod.isValid()) { fprintf(stderr, "Could not load file %s\n", infile); return 1; } const char* dispname = strrchr(infile, PATHSEP); dispname = (dispname == NULL) ? infile : dispname + 1; const char* disp_prefix = strrchr(prefix_name.c_str(), PATHSEP); disp_prefix = (disp_prefix == NULL) ? prefix_name.c_str() : disp_prefix + 1; banner && formatted_print( das_out_file, R"(# File: %s (Python %d.%d) # Disassembly generated by Pyarmor-Static-Unpack-1shot (%s), powered by pycdas # ================================ # Pyarmor notes: # - Pyarmor bytecode and code objects match standard Python, but special calls to Pyarmor runtime functions exist. # - Calls on strings are not mistakes but markers, which are processed by Pyarmor at runtime. # # Decompilation guidance (without runtime): # 1. Ignore encrypted bytes after `#`; use only the string before `#`. # 2. Remove `"__pyarmor_enter_xxx__"(b"...")` and `"__pyarmor_leave_xxx__"(b"...")` (prologue/epilogue). # 3. `"__pyarmor_assert_xxx__"(A)` is not a real assert statement. # - If `A` is a name or readable string: replace with `A`. # - If `A` is `(X, "Y")`: replace with `X.Y`. # - If `A` is `(X, "Y", Z)`: replace with `X.Y = Z`. # - Otherwise: choose the most reasonable replacement. # 4. `"__pyarmor_bcc_xxx__"(...)` indicates native code; function body is not available. Add a comment. # ================================ )", dispname, mod.majorVer(), mod.minorVer(), VERSION ); try { output_object(mod.code().try_cast(), &mod, 0, disasm_flags, das_out_file); } catch (std::exception& ex) { fprintf(stderr, "Error disassembling %s: %s\n", infile, ex.what()); das_out_file.flush(); das_out_file.close(); return 1; } das_out_file.flush(); das_out_file.close(); banner && formatted_print( dc_out_file, R"(# File: %s (Python %d.%d) # Source generated by Pyarmor-Static-Unpack-1shot (%s), powered by Decompyle++ (pycdc) # Note: Decompiled code can be incomplete and incorrect. # Please also check the correct and complete disassembly file: %s.das )", dispname, mod.majorVer(), mod.minorVer(), VERSION, disp_prefix ); try { decompyle(mod.code(), &mod, dc_out_file); } catch (std::exception& ex) { fprintf(stderr, "Error decompyling %s: %s\n", infile, ex.what()); dc_out_file.flush(); dc_out_file.close(); return 1; } dc_out_file.flush(); dc_out_file.close(); return 0; }