From 568b15d56afd3af397d2343d832f728c23227c9d Mon Sep 17 00:00:00 2001 From: Guillaume Valadon Date: Wed, 1 Oct 2025 21:13:34 +0200 Subject: [PATCH 1/3] Object saving/loading removed --- doc/scapy/usage.rst | 35 ----------------------------------- scapy/utils.py | 38 +++----------------------------------- test/regression.uts | 17 ----------------- 3 files changed, 3 insertions(+), 87 deletions(-) diff --git a/doc/scapy/usage.rst b/doc/scapy/usage.rst index 1202d9cc400..ed3184f9494 100644 --- a/doc/scapy/usage.rst +++ b/doc/scapy/usage.rst @@ -991,41 +991,6 @@ We can reimport the produced binary string by selecting the appropriate first la \x0b\x0c\r\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e \x1f !"#$%&\'()*+,-./01234567' |>>>> -Base64 -^^^^^^ - -Using the ``export_object()`` function, Scapy can export a base64 encoded Python data structure representing a packet:: - - >>> pkt - >>> - >>> export_object(pkt) - eNplVwd4FNcRPt2dTqdTQ0JUUYwN+CgS0gkJONFEs5WxFDB+CdiI8+pupVl0d7uzRUiYtcEGG4ST - OD1OnB6nN6c4cXrvwQmk2U5xA9tgO70XMm+1rA78qdzbfTP/lDfzz7tD4WwmU1C0YiaT2Gqjaiao - bMlhCrsUSYrYoKbmcxZFXSpPiohlZikm6ltb063ZdGpNOjWQ7mhPt62hChHJWTbFvb0O/u1MD2bT - WZXXVCmi9pihUqI3FHdEQslriiVfWFTVT9VYpog6Q7fsjG0qRWtQNwsW1fRTrUg4xZxq5pUx1aS6 - ... - -The output above can be reimported back into Scapy using ``import_object()``:: - - >>> new_pkt = import_object() - eNplVwd4FNcRPt2dTqdTQ0JUUYwN+CgS0gkJONFEs5WxFDB+CdiI8+pupVl0d7uzRUiYtcEGG4ST - OD1OnB6nN6c4cXrvwQmk2U5xA9tgO70XMm+1rA78qdzbfTP/lDfzz7tD4WwmU1C0YiaT2Gqjaiao - bMlhCrsUSYrYoKbmcxZFXSpPiohlZikm6ltb063ZdGpNOjWQ7mhPt62hChHJWTbFvb0O/u1MD2bT - WZXXVCmi9pihUqI3FHdEQslriiVfWFTVT9VYpog6Q7fsjG0qRWtQNwsW1fRTrUg4xZxq5pUx1aS6 - ... - >>> new_pkt - >>> - Sessions ^^^^^^^^ diff --git a/scapy/utils.py b/scapy/utils.py index 320349243c6..3b0f88b0d10 100644 --- a/scapy/utils.py +++ b/scapy/utils.py @@ -15,7 +15,6 @@ import argparse import array -import base64 import collections import decimal import difflib @@ -25,7 +24,6 @@ import locale import math import os -import pickle import random import re import shutil @@ -1221,39 +1219,9 @@ def __repr__(self): return "<%s>" % self.__dict__.get("name", self.__name__) -################### -# Object saving # -################### - - -def export_object(obj): - # type: (Any) -> None - import zlib - print(base64.b64encode(zlib.compress(pickle.dumps(obj, 2), 9)).decode()) - - -def import_object(obj=None): - # type: (Optional[str]) -> Any - import zlib - if obj is None: - obj = sys.stdin.read() - return pickle.loads(zlib.decompress(base64.b64decode(obj.strip()))) - - -def save_object(fname, obj): - # type: (str, Any) -> None - """Pickle a Python object""" - - fd = gzip.open(fname, "wb") - pickle.dump(obj, fd) - fd.close() - - -def load_object(fname): - # type: (str) -> Any - """unpickle a Python object""" - return pickle.load(gzip.open(fname, "rb")) - +################## +# Corrupt data # +################## @conf.commands.register def corrupt_bytes(data, p=0.01, n=None): diff --git a/test/regression.uts b/test/regression.uts index fd0fa61352c..4d6f6ab478c 100644 --- a/test/regression.uts +++ b/test/regression.uts @@ -993,17 +993,6 @@ zerofree_randstring(4) in [b"\xd2\x12\xe4\x5b", b'\xd3\x8b\x13\x12'] = Test strand function assert strand(b"AC", b"BC") == b'@C' -= Test export_object and import_object functions -from unittest import mock -def test_export_import_object(): - with ContextManagerCaptureOutput() as cmco: - export_object(2807) - result_export_object = cmco.get_output(eval_bytes=True) - assert result_export_object.startswith("eNprYPL9zqUHAAdrAf8=") - assert import_object(result_export_object) == 2807 - -test_export_import_object() - = Test tex_escape function tex_escape("$#_") == "\\$\\#\\_" @@ -1024,12 +1013,6 @@ assert sane(corrupt_bytes("ABCDE", n=3)) in ["A.8D4", ".2.DE"] assert corrupt_bits("ABCDE") in [b"EBCDE", b"ABCDG"] assert sane(corrupt_bits("ABCDE", n=3)) in ["AF.EE", "QB.TE"] -= Test save_object and load_object functions -import tempfile -fd, fname = tempfile.mkstemp() -save_object(fname, 2807) -assert load_object(fname) == 2807 - = Test whois function ~ netaccess From e762b8aed9a20f3e9ea1b980174b20609a06d46d Mon Sep 17 00:00:00 2001 From: Guillaume Valadon Date: Wed, 1 Oct 2025 21:22:18 +0200 Subject: [PATCH 2/3] Cache files extension --- scapy/data.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scapy/data.py b/scapy/data.py index a1a8e3c8316..35c1cd9ebd6 100644 --- a/scapy/data.py +++ b/scapy/data.py @@ -302,7 +302,7 @@ def scapy_data_cache(name): if SCAPY_CACHE_FOLDER is None: # Cannot cache. return lambda x: x - cachepath = SCAPY_CACHE_FOLDER / name + cachepath = SCAPY_CACHE_FOLDER / (name + ".pickle") def _cached_loader(func, name=name): # type: (DecoratorCallable, str) -> DecoratorCallable From ecf94bd35c4b3a832871dc090c12c579f9d36639 Mon Sep 17 00:00:00 2001 From: Guillaume Valadon Date: Wed, 1 Oct 2025 21:58:56 +0200 Subject: [PATCH 3/3] Sessions removed --- doc/scapy.1 | 8 +-- doc/scapy/usage.rst | 18 ----- scapy/main.py | 168 +++----------------------------------------- test/regression.uts | 67 +----------------- 4 files changed, 10 insertions(+), 251 deletions(-) diff --git a/doc/scapy.1 b/doc/scapy.1 index 395871a8849..4811055d459 100644 --- a/doc/scapy.1 +++ b/doc/scapy.1 @@ -17,10 +17,7 @@ arping, tcpdump, tshark, p0f, ... .PP \fBScapy\fP uses the Python interpreter as a command board. That means that you can use directly Python language (assign variables, use loops, -define functions, etc.) If you give a file a parameter when you run -\fBScapy\fP, your session (variables, functions, instances, ...) will be saved -when you leave the interpreter and restored the next time you launch -\fBScapy\fP. +define functions, etc.) .PP The idea is simple. Those kinds of tools do two things : sending packets and receiving answers. That's what \fBScapy\fP does : you define a set of @@ -48,9 +45,6 @@ header-less mode, also reduces verbosity. \fB\-d\fR increase log verbosity. Can be used many times. .TP -\fB\-s\fR FILE -use FILE to save/load session values (variables, functions, instances, ...) -.TP \fB\-p\fR PRESTART_FILE use PRESTART_FILE instead of $HOME/.config/scapy/prestart.py as pre-startup file .TP diff --git a/doc/scapy/usage.rst b/doc/scapy/usage.rst index ed3184f9494..9bb678a0849 100644 --- a/doc/scapy/usage.rst +++ b/doc/scapy/usage.rst @@ -991,24 +991,6 @@ We can reimport the produced binary string by selecting the appropriate first la \x0b\x0c\r\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e \x1f !"#$%&\'()*+,-./01234567' |>>>> -Sessions -^^^^^^^^ - -At last Scapy is capable of saving all session variables using the ``save_session()`` function: - ->>> dir() -['__builtins__', 'conf', 'new_pkt', 'pkt', 'pkt_export', 'pkt_hex', 'pkt_raw', 'pkts'] ->>> save_session("session.scapy") - -Next time you start Scapy you can load the previous saved session using the ``load_session()`` command:: - - >>> dir() - ['__builtins__', 'conf'] - >>> load_session("session.scapy") - >>> dir() - ['__builtins__', 'conf', 'new_pkt', 'pkt', 'pkt_export', 'pkt_hex', 'pkt_raw', 'pkts'] - - Making tables ------------- diff --git a/scapy/main.py b/scapy/main.py index 663d5308b8c..0290860773d 100644 --- a/scapy/main.py +++ b/scapy/main.py @@ -12,16 +12,13 @@ import code import getopt import glob -import gzip import importlib import io import logging import os import pathlib -import pickle import shutil import sys -import types import warnings from itertools import zip_longest @@ -260,7 +257,7 @@ def _validate_local(k): def _usage(): # type: () -> None print( - "Usage: scapy.py [-s sessionfile] [-c new_startup_file] " + "Usage: scapy.py [-c new_startup_file] " "[-p new_prestart_file] [-C] [-P] [-H]\n" "Args:\n" "\t-H: header-less start\n" @@ -494,116 +491,8 @@ def _scapy_exts(): return res -def save_session(fname="", session=None, pickleProto=-1): - # type: (str, Optional[Dict[str, Any]], int) -> None - """Save current Scapy session to the file specified in the fname arg. - - params: - - fname: file to save the scapy session in - - session: scapy session to use. If None, the console one will be used - - pickleProto: pickle proto version (default: -1 = latest)""" - from scapy import utils - from scapy.config import conf, ConfClass - if not fname: - fname = conf.session - if not fname: - conf.session = fname = utils.get_temp_file(keep=True) - log_interactive.info("Saving session into [%s]", fname) - - if not session: - if conf.interactive_shell in ["ipython", "ptipython"]: - from IPython import get_ipython - session = get_ipython().user_ns - else: - session = builtins.__dict__["scapy_session"] - - if not session: - log_interactive.error("No session found ?!") - return - - ignore = session.get("_scpybuiltins", []) - hard_ignore = ["scapy_session", "In", "Out", "open"] - to_be_saved = session.copy() - - for k in list(to_be_saved): - i = to_be_saved[k] - if k[0] == "_": - del to_be_saved[k] - elif hasattr(i, "__module__") and i.__module__.startswith("IPython"): - del to_be_saved[k] - elif isinstance(i, ConfClass): - del to_be_saved[k] - elif k in ignore or k in hard_ignore: - del to_be_saved[k] - elif isinstance(i, (type, types.ModuleType, types.FunctionType)): - if k[0] != "_": - log_interactive.warning("[%s] (%s) can't be saved.", k, type(i)) - del to_be_saved[k] - else: - try: - pickle.dumps(i) - except Exception: - log_interactive.warning("[%s] (%s) can't be saved.", k, type(i)) - - try: - os.rename(fname, fname + ".bak") - except OSError: - pass - - f = gzip.open(fname, "wb") - pickle.dump(to_be_saved, f, pickleProto) - f.close() - - -def load_session(fname=None): - # type: (Optional[Union[str, None]]) -> None - """Load current Scapy session from the file specified in the fname arg. - This will erase any existing session. - - params: - - fname: file to load the scapy session from""" - from scapy.config import conf - if fname is None: - fname = conf.session - try: - s = pickle.load(gzip.open(fname, "rb")) - except IOError: - try: - s = pickle.load(open(fname, "rb")) - except IOError: - # Raise "No such file exception" - raise - - scapy_session = builtins.__dict__["scapy_session"] - s.update({k: scapy_session[k] for k in scapy_session["_scpybuiltins"]}) - scapy_session.clear() - scapy_session.update(s) - update_ipython_session(scapy_session) - - log_loading.info("Loaded session [%s]", fname) - - -def update_session(fname=None): - # type: (Optional[Union[str, None]]) -> None - """Update current Scapy session from the file specified in the fname arg. - - params: - - fname: file to load the scapy session from""" - from scapy.config import conf - if fname is None: - fname = conf.session - try: - s = pickle.load(gzip.open(fname, "rb")) - except IOError: - s = pickle.load(open(fname, "rb")) - scapy_session = builtins.__dict__["scapy_session"] - scapy_session.update(s) - update_ipython_session(scapy_session) - - @overload -def init_session(session_name, # type: Optional[Union[str, None]] - mydict, # type: Optional[Union[Dict[str, Any], None]] +def init_session(mydict, # type: Optional[Union[Dict[str, Any], None]] ret, # type: Literal[True] ): # type: (...) -> Dict[str, Any] @@ -611,21 +500,18 @@ def init_session(session_name, # type: Optional[Union[str, None]] @overload -def init_session(session_name, # type: Optional[Union[str, None]] - mydict=None, # type: Optional[Union[Dict[str, Any], None]] +def init_session(mydict=None, # type: Optional[Union[Dict[str, Any], None]] ret=False, # type: Literal[False] ): # type: (...) -> None pass -def init_session(session_name, # type: Optional[Union[str, None]] - mydict=None, # type: Optional[Union[Dict[str, Any], None]] +def init_session(mydict=None, # type: Optional[Union[Dict[str, Any], None]] ret=False, # type: bool ): # type: (...) -> Union[Dict[str, Any], None] from scapy.config import conf - SESSION = {} # type: Optional[Dict[str, Any]] # Load Scapy scapy_builtins = _scapy_builtins() @@ -633,39 +519,7 @@ def init_session(session_name, # type: Optional[Union[str, None]] # Load exts scapy_builtins.update(_scapy_exts()) - if session_name: - try: - os.stat(session_name) - except OSError: - log_loading.info("New session [%s]", session_name) - else: - try: - try: - SESSION = pickle.load(gzip.open(session_name, "rb")) - except IOError: - SESSION = pickle.load(open(session_name, "rb")) - log_loading.info("Using existing session [%s]", session_name) - except ValueError: - msg = "Error opening Python3 pickled session on Python2 [%s]" - log_loading.error(msg, session_name) - except EOFError: - log_loading.error("Error opening session [%s]", session_name) - except AttributeError: - log_loading.error("Error opening session [%s]. " - "Attribute missing", session_name) - - if SESSION: - if "conf" in SESSION: - conf.configure(SESSION["conf"]) - conf.session = session_name - SESSION["conf"] = conf - else: - conf.session = session_name - else: - conf.session = session_name - SESSION = {"conf": conf} - else: - SESSION = {"conf": conf} + SESSION = {"conf": conf} # type: Dict[str, Any] SESSION.update(scapy_builtins) SESSION["_scpybuiltins"] = scapy_builtins.keys() @@ -678,6 +532,7 @@ def init_session(session_name, # type: Optional[Union[str, None]] return SESSION return None + ################ # Main # ################ @@ -810,8 +665,6 @@ def interact(mydict=None, STARTUP_FILE = DEFAULT_STARTUP_FILE PRESTART_FILE = DEFAULT_PRESTART_FILE - session_name = None - if argv is None: argv = sys.argv @@ -824,8 +677,6 @@ def interact(mydict=None, conf.fancy_banner = False conf.verb = 1 conf.logLevel = logging.WARNING - elif opt == "-s": - session_name = param elif opt == "-c": STARTUP_FILE = param elif opt == "-C": @@ -857,7 +708,7 @@ def interact(mydict=None, default=DEFAULT_PRESTART, ) - SESSION = init_session(session_name, mydict=mydict, ret=True) + SESSION = init_session(mydict=mydict, ret=True) if STARTUP_FILE: _read_config_file( @@ -1090,13 +941,10 @@ def ptpython_configure(repl): ) # Start Python elif conf.interactive_shell == "python": - code.interact(banner=banner_text, local=SESSION) + code.interact(banner=banner_text) else: raise ValueError("Invalid conf.interactive_shell") - if conf.session: - save_session(conf.session, SESSION) - if __name__ == "__main__": interact() diff --git a/test/regression.uts b/test/regression.uts index 4d6f6ab478c..f6cf13094ca 100644 --- a/test/regression.uts +++ b/test/regression.uts @@ -625,72 +625,6 @@ try: except SystemExit: assert True -= Session test - -import builtins - -# This is automatic when using the console -def get_var(var): - return builtins.__dict__["scapy_session"][var] - -def set_var(var, value): - builtins.__dict__["scapy_session"][var] = value - -def del_var(var): - del builtins.__dict__["scapy_session"][var] - -init_session(None, {"init_value": 123}) -set_var("test_value", "8.8.8.8") # test_value = "8.8.8.8" -save_session() -del_var("test_value") -load_session() -update_session() -assert get_var("test_value") == "8.8.8.8" #test_value == "8.8.8.8" -assert get_var("init_value") == 123 - -= Session test with fname - -session_name = tempfile.mktemp() -init_session(session_name) -set_var("test_value", IP(dst="192.168.0.1")) # test_value = IP(dst="192.168.0.1") -save_session(fname="%s.dat" % session_name) -del_var("test_value") - -set_var("z", True) #z = True -load_session(fname="%s.dat" % session_name) -try: - get_var("z") - assert False -except: - pass - -set_var("z", False) #z = False -update_session(fname="%s.dat" % session_name) -assert get_var("test_value").dst == "192.168.0.1" #test_value.dst == "192.168.0.1" -assert not get_var("z") - -= Clear session files - -os.remove("%s.dat" % session_name) - -= Test temporary file creation -~ ci_only - -scapy_delete_temp_files() - -tmpfile = get_temp_file(autoext=".ut") -tmpfile -if WINDOWS: - assert "scapy" in tmpfile and "AppData\\Local\\Temp" in tmpfile -else: - import platform - BYPASS_TMP = platform.python_implementation().lower() == "pypy" or DARWIN - assert "scapy" in tmpfile and (BYPASS_TMP == True or "/tmp/" in tmpfile) - -assert conf.temp_files[0].endswith(".ut") -scapy_delete_temp_files() -assert len(conf.temp_files) == 0 - = Emulate interact() ~ interact @@ -2181,6 +2115,7 @@ p.show() = Variable creations from io import BytesIO +import base64 pcapfile = BytesIO(b'\xd4\xc3\xb2\xa1\x02\x00\x04\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff\x00\x00e\x00\x00\x00\xcf\xc5\xacVo*\n\x00(\x00\x00\x00(\x00\x00\x00E\x00\x00(\x00\x01\x00\x00@\x06|\xcd\x7f\x00\x00\x01\x7f\x00\x00\x01\x00\x14\x00P\x00\x00\x00\x00\x00\x00\x00\x00P\x02 \x00\x91|\x00\x00\xcf\xc5\xacV_-\n\x00\x1c\x00\x00\x00\x1c\x00\x00\x00E\x00\x00\x1c\x00\x01\x00\x00@\x11|\xce\x7f\x00\x00\x01\x7f\x00\x00\x01\x005\x005\x00\x08\x01r\xcf\xc5\xacV\xf90\n\x00\x1c\x00\x00\x00\x1c\x00\x00\x00E\x00\x00\x1c\x00\x01\x00\x00@\x01|\xde\x7f\x00\x00\x01\x7f\x00\x00\x01\x08\x00\xf7\xff\x00\x00\x00\x00') pcapngfile = BytesIO(b'\n\r\r\n\\\x00\x00\x00M<+\x1a\x01\x00\x00\x00\xff\xff\xff\xff\xff\xff\xff\xff\x01\x00,\x00File created by merging: \nFile1: test.pcap \n\x04\x00\x08\x00mergecap\x00\x00\x00\x00\\\x00\x00\x00\x01\x00\x00\x00\\\x00\x00\x00e\x00\x00\x00\xff\xff\x00\x00\x02\x006\x00Unknown/not available in original file format(libpcap)\x00\x00\t\x00\x01\x00\x06\x00\x00\x00\x00\x00\x00\x00\\\x00\x00\x00\x06\x00\x00\x00H\x00\x00\x00\x00\x00\x00\x00\x8d*\x05\x00/\xfc[\xcd(\x00\x00\x00(\x00\x00\x00E\x00\x00(\x00\x01\x00\x00@\x06|\xcd\x7f\x00\x00\x01\x7f\x00\x00\x01\x00\x14\x00P\x00\x00\x00\x00\x00\x00\x00\x00P\x02 \x00\x91|\x00\x00H\x00\x00\x00\x06\x00\x00\x00<\x00\x00\x00\x00\x00\x00\x00\x8d*\x05\x00\x1f\xff[\xcd\x1c\x00\x00\x00\x1c\x00\x00\x00E\x00\x00\x1c\x00\x01\x00\x00@\x11|\xce\x7f\x00\x00\x01\x7f\x00\x00\x01\x005\x005\x00\x08\x01r<\x00\x00\x00\x06\x00\x00\x00<\x00\x00\x00\x00\x00\x00\x00\x8d*\x05\x00\xb9\x02\\\xcd\x1c\x00\x00\x00\x1c\x00\x00\x00E\x00\x00\x1c\x00\x01\x00\x00@\x01|\xde\x7f\x00\x00\x01\x7f\x00\x00\x01\x08\x00\xf7\xff\x00\x00\x00\x00<\x00\x00\x00') pcapnanofile = BytesIO(b"M<\xb2\xa1\x02\x00\x04\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff\x00\x00e\x00\x00\x00\xcf\xc5\xacV\xc9\xc1\xb5'(\x00\x00\x00(\x00\x00\x00E\x00\x00(\x00\x01\x00\x00@\x06|\xcd\x7f\x00\x00\x01\x7f\x00\x00\x01\x00\x14\x00P\x00\x00\x00\x00\x00\x00\x00\x00P\x02 \x00\x91|\x00\x00\xcf\xc5\xacV-;\xc1'\x1c\x00\x00\x00\x1c\x00\x00\x00E\x00\x00\x1c\x00\x01\x00\x00@\x11|\xce\x7f\x00\x00\x01\x7f\x00\x00\x01\x005\x005\x00\x08\x01r\xcf\xc5\xacV\x9aL\xcf'\x1c\x00\x00\x00\x1c\x00\x00\x00E\x00\x00\x1c\x00\x01\x00\x00@\x01|\xde\x7f\x00\x00\x01\x7f\x00\x00\x01\x08\x00\xf7\xff\x00\x00\x00\x00")