From 369c5bb5dbe43d0e0f39df37f4917556bfa77c1f Mon Sep 17 00:00:00 2001 From: Philippe Teuwen Date: Wed, 14 Aug 2024 13:06:21 +0200 Subject: [PATCH] Added native output grabbing for Python and Lua: less hacky than output_grabber.py, should work on ProxSpace as well --- CHANGELOG.md | 1 + .../02b_run_test_py_grabber.sh | 3 - .../testembedded.lua | 11 +- .../testembedded.py | 8 + .../testembedded_grab.py | 13 -- client/experimental_lib/example_lua/test.lua | 11 +- .../example_py/02run_test_grab_interactive.sh | 3 - client/experimental_lib/example_py/test.py | 8 + .../experimental_lib/example_py/test_grab.py | 13 -- client/include/pm3.h | 5 +- client/pyscripts/fm11rf08s_recovery.py | 81 +++----- client/pyscripts/output_grabber.py | 81 -------- client/pyscripts/pm3.py | 20 +- client/src/pm3.c | 18 +- client/src/pm3.i | 10 +- client/src/pm3_luawrap.c | 34 +++- client/src/pm3_pywrap.c | 184 +++++++++++++++++- client/src/proxmark3.c | 4 +- client/src/ui.c | 62 +++++- client/src/ui.h | 1 + client/src/util.c | 4 +- client/src/util.h | 8 + doc/commands.json | 2 +- 23 files changed, 387 insertions(+), 198 deletions(-) delete mode 100755 client/experimental_client_with_swig/02b_run_test_py_grabber.sh delete mode 100755 client/experimental_client_with_swig/testembedded_grab.py delete mode 100755 client/experimental_lib/example_py/02run_test_grab_interactive.sh delete mode 100755 client/experimental_lib/example_py/test_grab.py delete mode 100644 client/pyscripts/output_grabber.py diff --git a/CHANGELOG.md b/CHANGELOG.md index df99d61b0..14e3a63a3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ All notable changes to this project will be documented in this file. This project uses the changelog in accordance with [keepchangelog](http://keepachangelog.com/). Please use this to write notable changes, which is not the same as git commit log... ## [unreleased][unreleased] +- Added native output grabbing for Python and Lua: less hacky than `output_grabber.py`, should work on ProxSpace as well (@doegox) - Changed `hf mf chk/fchk`: added option `--no-default` to skip loading the usual ~61 hardcoded keys (@doegox) - Fixed `hf mf wipe` to detect properly write errors (@doegox) - Fixed `hf mf fchk` which was leaving the RF field on when interrupted by keyboard (@doegox) diff --git a/client/experimental_client_with_swig/02b_run_test_py_grabber.sh b/client/experimental_client_with_swig/02b_run_test_py_grabber.sh deleted file mode 100755 index 8aed5b8bb..000000000 --- a/client/experimental_client_with_swig/02b_run_test_py_grabber.sh +++ /dev/null @@ -1,3 +0,0 @@ -#!/bin/bash - -../../pm3 -c "script run testembedded_grab.py" -i diff --git a/client/experimental_client_with_swig/testembedded.lua b/client/experimental_client_with_swig/testembedded.lua index 469bfff7a..e4ad8311e 100755 --- a/client/experimental_client_with_swig/testembedded.lua +++ b/client/experimental_client_with_swig/testembedded.lua @@ -1,4 +1,13 @@ local pm3 = require("pm3") p=pm3.pm3() + p:console("hw status") -print(p.name) +p:console("hw version") +for line in p.grabbed_output:gmatch("[^\r\n]+") do + if line:find("Unique ID") or line:find("uC:") then + print(line) + end +end + +print("Device:", p.name) +p:console("Rem passthru remark! :coffee:", true) diff --git a/client/experimental_client_with_swig/testembedded.py b/client/experimental_client_with_swig/testembedded.py index 12ae8b741..6dbff0403 100755 --- a/client/experimental_client_with_swig/testembedded.py +++ b/client/experimental_client_with_swig/testembedded.py @@ -2,5 +2,13 @@ import pm3 p=pm3.pm3() + p.console("hw status") +p.console("hw version") +for line in p.grabbed_output.split('\n'): + if "Unique ID" in line: + print(line) + if "uC:" in line: + print(line) print("Device:", p.name) +p.console("Rem passthru remark! :coffee:", True) diff --git a/client/experimental_client_with_swig/testembedded_grab.py b/client/experimental_client_with_swig/testembedded_grab.py deleted file mode 100755 index fda910fb1..000000000 --- a/client/experimental_client_with_swig/testembedded_grab.py +++ /dev/null @@ -1,13 +0,0 @@ -#!/usr/bin/env python3 - -import pm3 -from output_grabber import OutputGrabber - -out = OutputGrabber() -p=pm3.pm3() -print("Device:", p.name) -with out: - p.console("hw status") -for line in out.captured_output.split('\n'): - if "Unique ID" in line: - print(line) diff --git a/client/experimental_lib/example_lua/test.lua b/client/experimental_lib/example_lua/test.lua index be12f1d5b..e8f7ad735 100755 --- a/client/experimental_lib/example_lua/test.lua +++ b/client/experimental_lib/example_lua/test.lua @@ -2,5 +2,14 @@ local pm3 = require("pm3") p=pm3.pm3("/dev/ttyACM0") + p:console("hw status") -print(p.name) +p:console("hw version") +for line in p.grabbed_output:gmatch("[^\r\n]+") do + if line:find("Unique ID") or line:find("uC:") then + print(line) + end +end + +print("Device:", p.name) +p:console("Rem passthru remark! :coffee:", true) diff --git a/client/experimental_lib/example_py/02run_test_grab_interactive.sh b/client/experimental_lib/example_py/02run_test_grab_interactive.sh deleted file mode 100755 index 5cfa7a038..000000000 --- a/client/experimental_lib/example_py/02run_test_grab_interactive.sh +++ /dev/null @@ -1,3 +0,0 @@ -#!/bin/bash - -PYTHONPATH=../../pyscripts ipython3 -i ./test_grab.py diff --git a/client/experimental_lib/example_py/test.py b/client/experimental_lib/example_py/test.py index 39cee5301..7d9b4580a 100755 --- a/client/experimental_lib/example_py/test.py +++ b/client/experimental_lib/example_py/test.py @@ -2,5 +2,13 @@ import pm3 p=pm3.pm3("/dev/ttyACM0") + p.console("hw status") +p.console("hw version") +for line in p.grabbed_output.split('\n'): + if "Unique ID" in line: + print(line) + if "uC:" in line: + print(line) print("Device:", p.name) +p.console("Rem passthru remark! :coffee:", True) diff --git a/client/experimental_lib/example_py/test_grab.py b/client/experimental_lib/example_py/test_grab.py deleted file mode 100755 index f13773afe..000000000 --- a/client/experimental_lib/example_py/test_grab.py +++ /dev/null @@ -1,13 +0,0 @@ -#!/usr/bin/env python3 - -import pm3 -from output_grabber import OutputGrabber - -out = OutputGrabber() -p=pm3.pm3("/dev/ttyACM0") -print("Device:", p.name) -with out: - p.console("hw status") -for line in out.captured_output.split('\n'): - if "Unique ID" in line: - print(line) diff --git a/client/include/pm3.h b/client/include/pm3.h index 4cb348a9b..b19fe7174 100644 --- a/client/include/pm3.h +++ b/client/include/pm3.h @@ -16,10 +16,13 @@ #ifndef LIBPM3_H #define LIBPM3_H +#include + typedef struct pm3_device pm3; pm3 *pm3_open(const char *port); -int pm3_console(pm3 *dev, const char *cmd); +int pm3_console(pm3 *dev, const char *cmd, bool passthru); +const char *pm3_grabbed_output_get(pm3 *dev); const char *pm3_name_get(pm3 *dev); void pm3_close(pm3 *dev); pm3 *pm3_get_current_dev(void); diff --git a/client/pyscripts/fm11rf08s_recovery.py b/client/pyscripts/fm11rf08s_recovery.py index 76837b321..0be9112b5 100755 --- a/client/pyscripts/fm11rf08s_recovery.py +++ b/client/pyscripts/fm11rf08s_recovery.py @@ -19,7 +19,6 @@ import time import subprocess import argparse import pm3 -from output_grabber import OutputGrabber # optional color support try: # pip install ansicolors @@ -49,14 +48,6 @@ for tool, bin in tools.items(): if not os.path.isfile(bin): if os.path.isfile(bin + ".exe"): tools[tool] = bin + ".exe" - - print(f"Native Windows/ProxSpace detected.") - print(f"Currently, our Python output grabbing does not work properly on this environment.") - print(f"Use WSL or Linux.") - # cf https://stackoverflow.com/questions/52373180/python-on-windows-handle-invalid-when-redirecting-stdout-writing-to-file - # for ref : https://docs.python.org/3/c-api/init_config.html#c.PyConfig.legacy_windows_stdio - exit() - else: print(f"Cannot find {bin}, abort!") exit() @@ -69,27 +60,24 @@ parser.add_argument('-d', '--debug', action='store_true', help='Enable debug mod args = parser.parse_args() start_time = time.time() -out = OutputGrabber() p = pm3.pm3() restore_color = False -with out: - p.console("prefs get color") - p.console("prefs set color --off") -for line in out.captured_output.split('\n'): +p.console("prefs get color") +p.console("prefs set color --off") +for line in p.grabbed_output.split('\n'): if "ansi" in line: restore_color = True -with out: - p.console("hf 14a read") +p.console("hf 14a read") uid = None -for line in out.captured_output.split('\n'): +for line in p.grabbed_output.split('\n'): if "UID:" in line: uid = int(line[10:].replace(' ', ''), 16) if uid is None: print("Card not found") if restore_color: - with out: - p.console("prefs set color --ansi") + p.console("prefs set color --ansi") + _ = p.grabbed_output exit() print("UID: " + color(f"{uid:08X}", fg="green")) @@ -102,9 +90,8 @@ def print_key(sec, key_type, key): found_keys = [["", ""] for _ in range(NUM_SECTORS)] if not args.no_init_check: print("Checking default keys...") - with out: - p.console("hf mf fchk") - for line in out.captured_output.split('\n'): + p.console("hf mf fchk") + for line in p.grabbed_output.split('\n'): if "[+] 0" in line: res = [x.strip() for x in line.split('|')] sec = int(res[0][4:]) @@ -119,18 +106,17 @@ nt = [["", ""] for _ in range(NUM_SECTORS)] nt_enc = [["", ""] for _ in range(NUM_SECTORS)] par_err = [["", ""] for _ in range(NUM_SECTORS)] print("Getting nonces...") -with out: - for sec in range(NUM_SECTORS): - blk = sec * 4 - if found_keys[sec][0] == "" or found_keys[sec][1] == "": - # Even if one key already found, we'll need both nt - for key_type in [0, 1]: - cmd = f"hf mf isen -n1 --blk {blk} -c {key_type+4} --key {BACKDOOR_RF08S}" - p.console(cmd) - cmd += f" --c2 {key_type}" - p.console(cmd) +for sec in range(NUM_SECTORS): + blk = sec * 4 + if found_keys[sec][0] == "" or found_keys[sec][1] == "": + # Even if one key already found, we'll need both nt + for key_type in [0, 1]: + cmd = f"hf mf isen -n1 --blk {blk} -c {key_type+4} --key {BACKDOOR_RF08S}" + p.console(cmd) + cmd += f" --c2 {key_type}" + p.console(cmd) print("Processing traces...") -for line in out.captured_output.split('\n'): +for line in p.grabbed_output.split('\n'): if "nested cmd: 64" in line or "nested cmd: 65" in line: sec = int(line[24:26], 16)//4 key_type = int(line[21:23], 16) - 0x64 @@ -232,9 +218,8 @@ for sec in range(NUM_SECTORS): cmd = f"hf mf fchk --blk {sec * 4} -{kt} -f {dic} --no-default" if args.debug: print(cmd) - with out: - p.console(cmd) - for line in out.captured_output.split('\n'): + p.console(cmd) + for line in p.grabbed_output.split('\n'): if "aborted via keyboard" in line: abort = True if "found:" in line: @@ -259,9 +244,8 @@ for sec in range(NUM_SECTORS): cmd = f"hf mf fchk --blk {sec * 4} -{kt} -f {dic} --no-default" if args.debug: print(cmd) - with out: - p.console(cmd) - for line in out.captured_output.split('\n'): + p.console(cmd) + for line in p.grabbed_output.split('\n'): if "aborted via keyboard" in line: abort = True if "found:" in line: @@ -281,9 +265,8 @@ for sec in range(NUM_SECTORS): cmd = f"hf mf fchk --blk {sec * 4} -{kt} -f {dic} --no-default" if args.debug: print(cmd) - with out: - p.console(cmd) - for line in out.captured_output.split('\n'): + p.console(cmd) + for line in p.grabbed_output.split('\n'): if "aborted via keyboard" in line: abort = True if "found:" in line: @@ -324,9 +307,8 @@ for sec in range(NUM_SECTORS): cmd += f" -k {k}" if args.debug: print(cmd) - with out: - p.console(cmd) - for line in out.captured_output.split('\n'): + p.console(cmd) + for line in p.grabbed_output.split('\n'): if "aborted via keyboard" in line: abort = True if "found:" in line: @@ -338,8 +320,8 @@ for sec in range(NUM_SECTORS): if abort: break if restore_color: - with out: - p.console("prefs set color --ansi") + p.console("prefs set color --ansi") + _ = p.grabbed_output if abort: print("Brute-forcing phase aborted via keyboard!") @@ -389,10 +371,7 @@ else: cmd = f"hf mf fchk -f keys_{uid:08x}.dic --no-default --dump" if args.debug: print(cmd) - with out: - p.console(cmd) - for line in out.captured_output.split('\n'): - print(line) + p.console(cmd, passthru = True) elapsed_time = time.time() - start_time minutes = int(elapsed_time // 60) diff --git a/client/pyscripts/output_grabber.py b/client/pyscripts/output_grabber.py deleted file mode 100644 index 3ea17cf3f..000000000 --- a/client/pyscripts/output_grabber.py +++ /dev/null @@ -1,81 +0,0 @@ -import os -import sys -import threading -import time - -# From https://stackoverflow.com/a/29834357 -class OutputGrabber(object): - """ - Class used to grab standard output or another stream. - """ - escape_char = "\b" - - def __init__(self, stream=None, threaded=False): - self.origstream = stream - self.threaded = threaded - if self.origstream is None: - self.origstream = sys.stdout - self.origstreamfd = self.origstream.fileno() - self.captured_output = "" - - def __enter__(self): - self.start() - return self - - def __exit__(self, type, value, traceback): - self.stop() - - def start(self): - """ - Start capturing the stream data. - """ - self.captured_output = "" - # Create a pipe so the stream can be captured: - self.pipe_out, self.pipe_in = os.pipe() - # Save a copy of the stream: - self.streamfd = os.dup(self.origstreamfd) - # Replace the original stream with our write pipe: - os.dup2(self.pipe_in, self.origstreamfd) - if self.threaded: - # Start thread that will read the stream: - self.workerThread = threading.Thread(target=self.readOutput) - self.workerThread.start() - # Make sure that the thread is running and os.read() has executed: - time.sleep(0.01) - - def stop(self): - """ - Stop capturing the stream data and save the text in `captured_output`. - """ - # Print the escape character to make the readOutput method stop: - self.origstream.write(self.escape_char) - # Flush the stream to make sure all our data goes in before - # the escape character: - self.origstream.flush() - if self.threaded: - # wait until the thread finishes so we are sure that - # we have until the last character: - self.workerThread.join() - else: - self.readOutput() - # Close the pipe: - os.close(self.pipe_in) - os.close(self.pipe_out) - # Restore the original stream: - os.dup2(self.streamfd, self.origstreamfd) - # Close the duplicate stream: - os.close(self.streamfd) - - def readOutput(self): - """ - Read the stream data (one byte at a time) - and save the text in `captured_output`. - """ - while True: - char = os.read(self.pipe_out,1).decode(self.origstream.encoding, errors='replace') - if not char or self.escape_char in char: - break - self.captured_output += char - -if __name__ == "__main__": - print("This is a library, don't use it as a script") diff --git a/client/pyscripts/pm3.py b/client/pyscripts/pm3.py index 7f5a91445..ead49777a 100644 --- a/client/pyscripts/pm3.py +++ b/client/pyscripts/pm3.py @@ -1,13 +1,10 @@ -# This file was automatically generated by SWIG (http://www.swig.org). -# Version 4.0.2 +# This file was automatically generated by SWIG (https://www.swig.org). +# Version 4.2.1 # -# Do not make changes to this file unless you know what you are doing--modify +# Do not make changes to this file unless you know what you are doing - modify # the SWIG interface file instead. from sys import version_info as _swig_python_version_info -if _swig_python_version_info < (2, 7, 0): - raise RuntimeError("Python 2.7 or later required") - # Import the low-level C/C++ module if __package__ or "." in __name__: from . import _pm3 @@ -29,10 +26,10 @@ def _swig_repr(self): def _swig_setattr_nondynamic_instance_variable(set): def set_instance_attr(self, name, value): - if name == "thisown": - self.this.own(value) - elif name == "this": + if name == "this": set(self, name, value) + elif name == "thisown": + self.this.own(value) elif hasattr(self, name) and isinstance(getattr(type(self), name), property): set(self, name, value) else: @@ -69,9 +66,10 @@ class pm3(object): _pm3.pm3_swiginit(self, _pm3.new_pm3(*args)) __swig_destroy__ = _pm3.delete_pm3 - def console(self, cmd): - return _pm3.pm3_console(self, cmd) + def console(self, cmd, passthru=False): + return _pm3.pm3_console(self, cmd, passthru) name = property(_pm3.pm3_name_get) + grabbed_output = property(_pm3.pm3_grabbed_output_get) # Register pm3 in _pm3: _pm3.pm3_swigregister(pm3) diff --git a/client/src/pm3.c b/client/src/pm3.c index 7639ecc6d..b921d37b3 100644 --- a/client/src/pm3.c +++ b/client/src/pm3.c @@ -53,18 +53,32 @@ void pm3_close(pm3_device_t *dev) { msleep(100); // Make sure command is sent before killing client CloseProxmark(dev); } + free_grabber(); } -int pm3_console(pm3_device_t *dev, const char *cmd) { +int pm3_console(pm3_device_t *dev, const char *cmd, bool passthru) { // For now, there is no real device context: (void) dev; - return CommandReceived(cmd); + uint8_t prev_printAndLog = g_printAndLog; + if (! passthru) { + g_printAndLog |= PRINTANDLOG_GRAB; + g_printAndLog &= ~PRINTANDLOG_PRINT; + } + int ret = CommandReceived(cmd); + g_printAndLog = prev_printAndLog; + return ret; } const char *pm3_name_get(pm3_device_t *dev) { return dev->g_conn->serial_port_name; } +const char *pm3_grabbed_output_get(pm3_device_t *dev) { + char *tmp = g_grabbed_output.ptr; + g_grabbed_output.idx = 0; + return tmp; +} + pm3_device_t *pm3_get_current_dev(void) { return g_session.current_device; } diff --git a/client/src/pm3.i b/client/src/pm3.i index 1c9a91ed2..810f5eb22 100644 --- a/client/src/pm3.i +++ b/client/src/pm3.i @@ -8,6 +8,13 @@ /* Strip "pm3_" from API functions for SWIG */ %rename("%(strip:[pm3_])s") ""; %feature("immutable","1") pm3_current_dev; + +#ifdef PYWRAP + #include + %typemap(default) bool passthru { + $1 = Py_False; + } +#endif typedef struct { %extend { pm3() { @@ -30,8 +37,9 @@ typedef struct { pm3_close($self); } } - int console(char *cmd); + int console(char *cmd, bool passthru = false); char const * const name; + char const * const grabbed_output; } } pm3; //%nodefaultctor device; diff --git a/client/src/pm3_luawrap.c b/client/src/pm3_luawrap.c index b744305b6..c880d09fd 100644 --- a/client/src/pm3_luawrap.c +++ b/client/src/pm3_luawrap.c @@ -2768,18 +2768,23 @@ static int _wrap_pm3_console(lua_State *L) { int SWIG_arg = 0; pm3 *arg1 = (pm3 *) 0 ; char *arg2 = (char *) 0 ; + bool arg3 = (bool) false ; int result; - SWIG_check_num_args("pm3::console", 2, 2) + SWIG_check_num_args("pm3::console", 2, 3) if (!SWIG_isptrtype(L, 1)) SWIG_fail_arg("pm3::console", 1, "pm3 *"); if (!SWIG_lua_isnilstring(L, 2)) SWIG_fail_arg("pm3::console", 2, "char *"); + if (lua_gettop(L) >= 3 && !lua_isboolean(L, 3)) SWIG_fail_arg("pm3::console", 3, "bool"); if (!SWIG_IsOK(SWIG_ConvertPtr(L, 1, (void **)&arg1, SWIGTYPE_p_pm3, 0))) { SWIG_fail_ptr("pm3_console", 1, SWIGTYPE_p_pm3); } arg2 = (char *)lua_tostring(L, 2); - result = (int)pm3_console(arg1, arg2); + if (lua_gettop(L) >= 3) { + arg3 = (lua_toboolean(L, 3) != 0); + } + result = (int)pm3_console(arg1, arg2, arg3); lua_pushnumber(L, (lua_Number) result); SWIG_arg++; return SWIG_arg; @@ -2815,6 +2820,30 @@ fail: } +static int _wrap_pm3_grabbed_output_get(lua_State *L) { + int SWIG_arg = 0; + pm3 *arg1 = (pm3 *) 0 ; + char *result = 0 ; + + SWIG_check_num_args("pm3::grabbed_output", 1, 1) + if (!SWIG_isptrtype(L, 1)) SWIG_fail_arg("pm3::grabbed_output", 1, "pm3 *"); + + if (!SWIG_IsOK(SWIG_ConvertPtr(L, 1, (void **)&arg1, SWIGTYPE_p_pm3, 0))) { + SWIG_fail_ptr("pm3_grabbed_output_get", 1, SWIGTYPE_p_pm3); + } + + result = (char *)pm3_grabbed_output_get(arg1); + lua_pushstring(L, (const char *)result); + SWIG_arg++; + return SWIG_arg; + +fail: + SWIGUNUSED; + lua_error(L); + return 0; +} + + static void swig_delete_pm3(void *obj) { pm3 *arg1 = (pm3 *) obj; delete_pm3(arg1); @@ -2829,6 +2858,7 @@ static int _proxy__wrap_new_pm3(lua_State *L) { } static swig_lua_attribute swig_pm3_attributes[] = { { "name", _wrap_pm3_name_get, SWIG_Lua_set_immutable }, + { "grabbed_output", _wrap_pm3_grabbed_output_get, SWIG_Lua_set_immutable }, {0, 0, 0} }; static swig_lua_method swig_pm3_methods[] = { diff --git a/client/src/pm3_pywrap.c b/client/src/pm3_pywrap.c index 06e68747a..93a26e2d0 100644 --- a/client/src/pm3_pywrap.c +++ b/client/src/pm3_pywrap.c @@ -3270,6 +3270,149 @@ SWIGINTERN void delete_pm3(pm3 *self) { } } +SWIGINTERN int +SWIG_AsVal_double(PyObject *obj, double *val) { + int res = SWIG_TypeError; + if (PyFloat_Check(obj)) { + if (val) *val = PyFloat_AsDouble(obj); + return SWIG_OK; +#if PY_VERSION_HEX < 0x03000000 + } else if (PyInt_Check(obj)) { + if (val) *val = (double) PyInt_AsLong(obj); + return SWIG_OK; +#endif + } else if (PyLong_Check(obj)) { + double v = PyLong_AsDouble(obj); + if (!PyErr_Occurred()) { + if (val) *val = v; + return SWIG_OK; + } else { + PyErr_Clear(); + } + } +#ifdef SWIG_PYTHON_CAST_MODE + { + int dispatch = 0; + double d = PyFloat_AsDouble(obj); + if (!PyErr_Occurred()) { + if (val) *val = d; + return SWIG_AddCast(SWIG_OK); + } else { + PyErr_Clear(); + } + if (!dispatch) { + long v = PyLong_AsLong(obj); + if (!PyErr_Occurred()) { + if (val) *val = v; + return SWIG_AddCast(SWIG_AddCast(SWIG_OK)); + } else { + PyErr_Clear(); + } + } + } +#endif + return res; +} + + +#include + + +#include + + +SWIGINTERNINLINE int +SWIG_CanCastAsInteger(double *d, double min, double max) { + double x = *d; + if ((min <= x && x <= max)) { + double fx, cx, rd; + errno = 0; + fx = floor(x); + cx = ceil(x); + rd = ((x - fx) < 0.5) ? fx : cx; /* simple rint */ + if ((errno == EDOM) || (errno == ERANGE)) { + errno = 0; + } else { + double summ, reps, diff; + if (rd < x) { + diff = x - rd; + } else if (rd > x) { + diff = rd - x; + } else { + return 1; + } + summ = rd + x; + reps = diff / summ; + if (reps < 8 * DBL_EPSILON) { + *d = rd; + return 1; + } + } + } + return 0; +} + + +SWIGINTERN int +SWIG_AsVal_long(PyObject *obj, long *val) { +#if PY_VERSION_HEX < 0x03000000 + if (PyInt_Check(obj)) { + if (val) *val = PyInt_AsLong(obj); + return SWIG_OK; + } else +#endif + if (PyLong_Check(obj)) { + long v = PyLong_AsLong(obj); + if (!PyErr_Occurred()) { + if (val) *val = v; + return SWIG_OK; + } else { + PyErr_Clear(); + return SWIG_OverflowError; + } + } +#ifdef SWIG_PYTHON_CAST_MODE + { + int dispatch = 0; + long v = PyInt_AsLong(obj); + if (!PyErr_Occurred()) { + if (val) *val = v; + return SWIG_AddCast(SWIG_OK); + } else { + PyErr_Clear(); + } + if (!dispatch) { + double d; + int res = SWIG_AddCast(SWIG_AsVal_double(obj, &d)); + // Largest double not larger than LONG_MAX (not portably calculated easily) + // Note that double(LONG_MAX) is stored in a double rounded up by one (for 64-bit long) + // 0x7ffffffffffffc00LL == (int64_t)std::nextafter(double(__uint128_t(LONG_MAX)+1), double(0)) + const double long_max = sizeof(long) == 8 ? 0x7ffffffffffffc00LL : LONG_MAX; + // No equivalent needed for 64-bit double(LONG_MIN) is exactly LONG_MIN + if (SWIG_IsOK(res) && SWIG_CanCastAsInteger(&d, LONG_MIN, long_max)) { + if (val) *val = (long)(d); + return res; + } + } + } +#endif + return SWIG_TypeError; +} + + +SWIGINTERN int +SWIG_AsVal_bool(PyObject *obj, bool *val) { + int r; + if (!PyBool_Check(obj)) + return SWIG_ERROR; + r = PyObject_IsTrue(obj); + if (r == -1) + return SWIG_ERROR; + if (val) *val = r ? true : false; + return SWIG_OK; +} + + SWIGINTERNINLINE PyObject * SWIG_From_int(int value) { return PyInt_FromLong((long) value); @@ -3403,16 +3546,19 @@ SWIGINTERN PyObject *_wrap_pm3_console(PyObject *self, PyObject *args) { PyObject *resultobj = 0; pm3 *arg1 = (pm3 *) 0 ; char *arg2 = (char *) 0 ; + bool arg3 = (bool) false ; void *argp1 = 0 ; int res1 = 0 ; int res2 ; char *buf2 = 0 ; int alloc2 = 0 ; - PyObject *swig_obj[2] ; + bool val3 ; + int ecode3 = 0 ; + PyObject *swig_obj[3] ; int result; (void)self; - if (!SWIG_Python_UnpackTuple(args, "pm3_console", 2, 2, swig_obj)) SWIG_fail; + if (!SWIG_Python_UnpackTuple(args, "pm3_console", 2, 3, swig_obj)) SWIG_fail; res1 = SWIG_ConvertPtr(swig_obj[0], &argp1, SWIGTYPE_p_pm3, 0 | 0); if (!SWIG_IsOK(res1)) { SWIG_exception_fail(SWIG_ArgError(res1), "in method '" "pm3_console" "', argument " "1"" of type '" "pm3 *""'"); @@ -3423,7 +3569,14 @@ SWIGINTERN PyObject *_wrap_pm3_console(PyObject *self, PyObject *args) { SWIG_exception_fail(SWIG_ArgError(res2), "in method '" "pm3_console" "', argument " "2"" of type '" "char *""'"); } arg2 = (char *)(buf2); - result = (int)pm3_console(arg1, arg2); + if (swig_obj[2]) { + ecode3 = SWIG_AsVal_bool(swig_obj[2], &val3); + if (!SWIG_IsOK(ecode3)) { + SWIG_exception_fail(SWIG_ArgError(ecode3), "in method '" "pm3_console" "', argument " "3"" of type '" "bool""'"); + } + arg3 = (bool)(val3); + } + result = (int)pm3_console(arg1, arg2, arg3); resultobj = SWIG_From_int((int)(result)); if (alloc2 == SWIG_NEWOBJ) free((char *)buf2); return resultobj; @@ -3457,6 +3610,30 @@ fail: } +SWIGINTERN PyObject *_wrap_pm3_grabbed_output_get(PyObject *self, PyObject *args) { + PyObject *resultobj = 0; + pm3 *arg1 = (pm3 *) 0 ; + void *argp1 = 0 ; + int res1 = 0 ; + PyObject *swig_obj[1] ; + char *result = 0 ; + + (void)self; + if (!args) SWIG_fail; + swig_obj[0] = args; + res1 = SWIG_ConvertPtr(swig_obj[0], &argp1, SWIGTYPE_p_pm3, 0 | 0); + if (!SWIG_IsOK(res1)) { + SWIG_exception_fail(SWIG_ArgError(res1), "in method '" "pm3_grabbed_output_get" "', argument " "1"" of type '" "pm3 *""'"); + } + arg1 = (pm3 *)(argp1); + result = (char *)pm3_grabbed_output_get(arg1); + resultobj = SWIG_FromCharPtr((const char *)result); + return resultobj; +fail: + return NULL; +} + + SWIGINTERN PyObject *pm3_swigregister(PyObject *SWIGUNUSEDPARM(self), PyObject *args) { PyObject *obj; if (!SWIG_Python_UnpackTuple(args, "swigregister", 1, 1, &obj)) return NULL; @@ -3473,6 +3650,7 @@ static PyMethodDef SwigMethods[] = { { "delete_pm3", _wrap_delete_pm3, METH_O, NULL}, { "pm3_console", _wrap_pm3_console, METH_VARARGS, NULL}, { "pm3_name_get", _wrap_pm3_name_get, METH_O, NULL}, + { "pm3_grabbed_output_get", _wrap_pm3_grabbed_output_get, METH_O, NULL}, { "pm3_swigregister", pm3_swigregister, METH_O, NULL}, { "pm3_swiginit", pm3_swiginit, METH_VARARGS, NULL}, { NULL, NULL, 0, NULL } diff --git a/client/src/proxmark3.c b/client/src/proxmark3.c index 54b05670a..9ebf01a1e 100644 --- a/client/src/proxmark3.c +++ b/client/src/proxmark3.c @@ -553,7 +553,7 @@ check_script: if (cmd[0] != '\0') { uint8_t old_printAndLog = g_printAndLog; if (!printprompt) { - g_printAndLog &= PRINTANDLOG_LOG; + g_printAndLog &= ~PRINTANDLOG_PRINT; } char prompt[PROXPROMPT_MAX_SIZE] = {0}; prompt_compose(prompt, sizeof(prompt), prompt_ctx, prompt_dev, prompt_net, true); @@ -1462,6 +1462,8 @@ int main(int argc, char *argv[]) { preferences_save(); } + free_grabber(); + return mainret; } #endif //LIBPM3 diff --git a/client/src/ui.c b/client/src/ui.c index bfe705d80..b682dee49 100644 --- a/client/src/ui.c +++ b/client/src/ui.c @@ -168,6 +168,33 @@ int searchHomeFilePath(char **foundpath, const char *subdir, const char *filenam return PM3_SUCCESS; } +void free_grabber(void) { + free(g_grabbed_output.ptr); + g_grabbed_output.ptr = NULL; + g_grabbed_output.size = 0; + g_grabbed_output.idx = 0; +} + +static void fill_grabber(const char *string) { + if (g_grabbed_output.ptr == NULL || g_grabbed_output.size - g_grabbed_output.idx < MAX_PRINT_BUFFER) { + char *tmp = realloc(g_grabbed_output.ptr, g_grabbed_output.size + MAX_PRINT_BUFFER); + if (tmp == NULL) { + // We leave current g_grabbed_output untouched + PrintAndLogEx(ERR, "Out of memory error in fill_grabber()"); + return; + } + g_grabbed_output.ptr = tmp; + g_grabbed_output.size += MAX_PRINT_BUFFER; + } + int len = snprintf(g_grabbed_output.ptr + g_grabbed_output.idx, MAX_PRINT_BUFFER, "%s", string); + if (len < 0 || len > MAX_PRINT_BUFFER) { + // We leave current g_grabbed_output_len untouched + PrintAndLogEx(ERR, "snprintf error in fill_grabber()"); + return; + } + g_grabbed_output.idx += len; +} + void PrintAndLogOptions(const char *str[][2], size_t size, size_t space) { char buff[2000] = "Options:\n"; char format[2000] = ""; @@ -299,12 +326,15 @@ void PrintAndLogEx(logLevel_t level, const char *fmt, ...) { } else { snprintf(buffer2, sizeof(buffer2), "%s%s", prefix, buffer); if (level == INPLACE) { - char buffer3[sizeof(buffer2)] = {0}; - char buffer4[sizeof(buffer2)] = {0}; - memcpy_filter_ansi(buffer3, buffer2, sizeof(buffer2), !g_session.supports_colors); - memcpy_filter_emoji(buffer4, buffer3, sizeof(buffer3), g_session.emoji_mode); - fprintf(stream, "\r%s", buffer4); - fflush(stream); + // ignore INPLACE if rest of output is grabbed + if (!(g_printAndLog & PRINTANDLOG_GRAB)) { + char buffer3[sizeof(buffer2)] = {0}; + char buffer4[sizeof(buffer2)] = {0}; + memcpy_filter_ansi(buffer3, buffer2, sizeof(buffer2), !g_session.supports_colors); + memcpy_filter_emoji(buffer4, buffer3, sizeof(buffer3), g_session.emoji_mode); + fprintf(stream, "\r%s", buffer4); + fflush(stream); + } } else { fPrintAndLog(stream, "%s", buffer2); } @@ -401,18 +431,32 @@ static void fPrintAndLog(FILE *stream, const char *fmt, ...) { } #endif - if ((g_printAndLog & PRINTANDLOG_LOG) && logging && logfile) { + if (((g_printAndLog & PRINTANDLOG_LOG) && logging && logfile) || + (g_printAndLog & PRINTANDLOG_GRAB)) { memcpy_filter_emoji(buffer3, buffer2, sizeof(buffer2), EMO_ALTTEXT); - if (filter_ansi) { // already done + if (!filter_ansi) { + memcpy_filter_ansi(buffer, buffer3, sizeof(buffer3), true); + } + } + if ((g_printAndLog & PRINTANDLOG_LOG) && logging && logfile) { + if (filter_ansi) { fprintf(logfile, "%s", buffer3); } else { - memcpy_filter_ansi(buffer, buffer3, sizeof(buffer3), true); fprintf(logfile, "%s", buffer); } if (linefeed) fprintf(logfile, "\n"); fflush(logfile); } + if (g_printAndLog & PRINTANDLOG_GRAB) { + if (filter_ansi) { + fill_grabber(buffer3); + } else { + fill_grabber(buffer); + } + if (linefeed) + fill_grabber("\n"); + } if (flushAfterWrite) fflush(stdout); diff --git a/client/src/ui.h b/client/src/ui.h index 033f8b9a3..09a107ba4 100644 --- a/client/src/ui.h +++ b/client/src/ui.h @@ -80,6 +80,7 @@ bool GetFlushAfterWrite(void); void memcpy_filter_ansi(void *dest, const void *src, size_t n, bool filter); void memcpy_filter_rlmarkers(void *dest, const void *src, size_t n); void memcpy_filter_emoji(void *dest, const void *src, size_t n, emojiMode_t mode); +void free_grabber(void); int searchHomeFilePath(char **foundpath, const char *subdir, const char *filename, bool create_home); diff --git a/client/src/util.c b/client/src/util.c index b923b3020..bc6fbf3b4 100644 --- a/client/src/util.c +++ b/client/src/util.c @@ -36,8 +36,10 @@ #define UTIL_BUFFER_SIZE_SPRINT 8196 // global client debug variable uint8_t g_debugMode = 0; -// global client disable logging variable +// global client enable/disable printing/logging/grabbing variable uint8_t g_printAndLog = PRINTANDLOG_PRINT | PRINTANDLOG_LOG; +// global pointer to grabbed output +grabbed_output g_grabbed_output = {NULL, 0, 0}; // global client tell if a pending prompt is present bool g_pendingPrompt = false; // global CPU core count override diff --git a/client/src/util.h b/client/src/util.h index eedd409f8..86c7a4f9c 100644 --- a/client/src/util.h +++ b/client/src/util.h @@ -34,8 +34,16 @@ extern uint8_t g_printAndLog; extern bool g_pendingPrompt; extern int g_numCPUs; +typedef struct { + char *ptr; + size_t size; + size_t idx; +} grabbed_output; +extern grabbed_output g_grabbed_output; + #define PRINTANDLOG_PRINT 1 #define PRINTANDLOG_LOG 2 +#define PRINTANDLOG_GRAB 4 // Return error #define PM3_RET_ERR(err, ...) { \ diff --git a/doc/commands.json b/doc/commands.json index 95f7279c9..bcee83649 100644 --- a/doc/commands.json +++ b/doc/commands.json @@ -12813,6 +12813,6 @@ "metadata": { "commands_extracted": 740, "extracted_by": "PM3Help2JSON v1.00", - "extracted_on": "2024-08-14T08:56:40" + "extracted_on": "2024-08-14T11:49:06" } }