feat: add unit buffer and no banner options

This commit is contained in:
2025-11-15 23:35:54 +08:00
parent 896d004baf
commit d605965b36
3 changed files with 105 additions and 9 deletions

View File

@@ -74,7 +74,7 @@ Feel free to open an issue if you have any questions, suggestions, or problems.
## Todo (PR Welcome!)
- [ ] Documentation (Do not accept PR about this)
- [ ] Regenerate pyc for other backend decompilers (discussion in GH-24, GH-30)
- [ ] Regenerate pyc for other backend decompilers (discussion in [GH-24](https://github.com/Lil-House/Pyarmor-Static-Unpack-1shot/issues/24), [GH-30](https://github.com/Lil-House/Pyarmor-Static-Unpack-1shot/issues/30))
- [ ] BCC Mode native part analysis tool
- [ ] Verify support for different obfuscating options
- [ ] Verify support for pyarmor_runtime executable on different platforms

View File

@@ -76,14 +76,23 @@ async def run_pycdc_async(
exe_path: str,
seq_file_path: str,
path_for_log: str,
*,
unit_buf: bool = False,
no_banner: bool = False,
show_all: bool = False,
show_err_opcode: bool = False,
show_warn_stack: bool = False,
):
logger = logging.getLogger("shot")
try:
options = []
if unit_buf:
options.append("--unitbuf")
if no_banner:
options.append("--no-banner")
process = await asyncio.create_subprocess_exec(
exe_path,
*options,
seq_file_path,
stdout=asyncio.subprocess.PIPE,
stderr=asyncio.subprocess.PIPE,
@@ -97,6 +106,21 @@ async def run_pycdc_async(
logger.warning(f"PYCDC: {line} ({path_for_log})")
for line in stderr_lines:
if not unit_buf and line.startswith("Segfault caught"):
# retry with --unitbuf
await run_pycdc_async(
exe_path,
seq_file_path,
path_for_log,
unit_buf=True,
no_banner=no_banner,
show_all=show_all,
show_err_opcode=show_err_opcode,
show_warn_stack=show_warn_stack,
)
# do not log anything because it will be logged in the retried call
return
if line.startswith(
(
"Warning: Stack history is empty",
@@ -115,6 +139,7 @@ async def run_pycdc_async(
"Unsupported argument",
"Unsupported Node type",
"Unsupported node type",
"Segfault caught",
)
): # annoying wont-fix errors
if show_all:
@@ -238,9 +263,10 @@ async def decrypt_process_async(
exe_path,
seq_file_path,
relative_path,
args.show_all,
args.show_err_opcode,
args.show_warn_stack,
no_banner=args.no_banner,
show_all=args.show_all,
show_err_opcode=args.show_err_opcode,
show_warn_stack=args.show_warn_stack,
)
except Exception as e:
@@ -370,6 +396,11 @@ def parse_args():
help="path to the pyarmor-1shot executable to use",
type=str,
)
parser.add_argument(
"--no-banner",
help="do not show banner in console and output files",
action="store_true",
)
return parser.parse_args()
@@ -381,7 +412,8 @@ def main():
)
logger = logging.getLogger("shot")
print(rf"""{Fore.CYAN}
if not args.no_banner:
print(rf"""{Fore.CYAN}
____ ____
( __ ) ( __ )
| |~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~| |

View File

@@ -1,3 +1,10 @@
#include <signal.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
/** I want to use functions in pycdas.cpp directly, but not moving them to
* another file, to sync with upstream in the future easily.
*/
@@ -7,12 +14,44 @@
#include "ASTree.h"
const char* VERSION = "v0.2.1";
const char* VERSION = "v0.2.1+";
#ifdef __cpp_lib_fstream_native_handle
static int g_dc_fd = -1;
static int g_das_fd = -1;
static void segv_handler(int sig) {
const char msg[] = "Segfault caught. Best-effort fsync.\n";
// Only use async-signal-safe functions
write(STDERR_FILENO, msg, sizeof(msg)-1);
if (g_das_fd != -1) fsync(g_das_fd);
if (g_dc_fd != -1) fsync(g_dc_fd);
_Exit(128 + sig);
}
#else
static void segv_handler(int sig) {
const char msg[] = "Segfault caught.\n";
write(STDERR_FILENO, msg, sizeof(msg)-1);
_Exit(128 + sig);
}
#endif
struct SegvInstall {
SegvInstall() {
struct sigaction sa{};
sa.sa_handler = segv_handler;
sigemptyset(&sa.sa_mask);
sa.sa_flags = SA_RESTART;
sigaction(SIGSEGV, &sa, nullptr);
}
} segv_install_guard;
int main(int argc, char* argv[])
{
const char* infile = nullptr;
unsigned disasm_flags = 0;
bool unitbuf = false;
bool banner = true;
std::ofstream dc_out_file;
std::ofstream das_out_file;
@@ -26,13 +65,23 @@ int main(int argc, char* argv[])
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(" --unitbuf Set output streams to be unbuffered\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], "--unitbuf") == 0) {
unitbuf = true;
} 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 {
} 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;
}
}
@@ -50,17 +99,28 @@ int main(int argc, char* argv[])
}
dc_out_file.open(prefix_name + ".cdc.py", std::ios_base::out);
if (unitbuf) {
dc_out_file.setf(std::ios::unitbuf);
}
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 (unitbuf) {
das_out_file.setf(std::ios::unitbuf);
}
if (das_out_file.fail()) {
fprintf(stderr, "Error opening file '%s' for writing\n", (prefix_name + ".das").c_str());
return 1;
}
#ifdef __cpp_lib_fstream_native_handle
g_dc_fd = dc_out_file.native_handle();
g_das_fd = das_out_file.native_handle();
#endif
PycModule mod;
try {
mod.loadFromOneshotSequenceFile(infile);
@@ -79,7 +139,7 @@ int main(int argc, char* argv[])
const char* disp_prefix = strrchr(prefix_name.c_str(), PATHSEP);
disp_prefix = (disp_prefix == NULL) ? prefix_name.c_str() : disp_prefix + 1;
formatted_print(
banner && formatted_print(
das_out_file,
R"(# File: %s (Python %d.%d)
# Disassembly generated by Pyarmor-Static-Unpack-1shot (%s), powered by pycdas
@@ -111,13 +171,15 @@ int main(int argc, char* argv[])
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();
formatted_print(
banner && formatted_print(
dc_out_file,
R"(# File: %s (Python %d.%d)
# Source generated by Pyarmor-Static-Unpack-1shot (%s), powered by Decompyle++ (pycdc)
@@ -136,6 +198,8 @@ int main(int argc, char* argv[])
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;
}