deaced 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (65) hide show
  1. deaced-0.1.0/CHANGELOG.md +50 -0
  2. deaced-0.1.0/CONTRIBUTING.md +31 -0
  3. deaced-0.1.0/LICENSE +22 -0
  4. deaced-0.1.0/MANIFEST.in +11 -0
  5. deaced-0.1.0/NOTICE +43 -0
  6. deaced-0.1.0/PKG-INFO +151 -0
  7. deaced-0.1.0/README.md +112 -0
  8. deaced-0.1.0/pyproject.toml +71 -0
  9. deaced-0.1.0/setup.cfg +4 -0
  10. deaced-0.1.0/src/deaced/__init__.py +49 -0
  11. deaced-0.1.0/src/deaced/__main__.py +6 -0
  12. deaced-0.1.0/src/deaced/cli.py +94 -0
  13. deaced-0.1.0/src/deaced/errors.py +27 -0
  14. deaced-0.1.0/src/deaced/jfloat.py +86 -0
  15. deaced-0.1.0/src/deaced/model.py +209 -0
  16. deaced-0.1.0/src/deaced/mutf8.py +70 -0
  17. deaced-0.1.0/src/deaced/parser.py +699 -0
  18. deaced-0.1.0/src/deaced/py.typed +0 -0
  19. deaced-0.1.0/src/deaced/reader.py +63 -0
  20. deaced-0.1.0/src/deaced/render/__init__.py +17 -0
  21. deaced-0.1.0/src/deaced/render/_safe.py +18 -0
  22. deaced-0.1.0/src/deaced/render/json.py +151 -0
  23. deaced-0.1.0/src/deaced/render/pretty.py +137 -0
  24. deaced-0.1.0/src/deaced/render/text.py +430 -0
  25. deaced-0.1.0/src/deaced/tags.py +49 -0
  26. deaced-0.1.0/src/deaced.egg-info/PKG-INFO +151 -0
  27. deaced-0.1.0/src/deaced.egg-info/SOURCES.txt +63 -0
  28. deaced-0.1.0/src/deaced.egg-info/dependency_links.txt +1 -0
  29. deaced-0.1.0/src/deaced.egg-info/entry_points.txt +2 -0
  30. deaced-0.1.0/src/deaced.egg-info/requires.txt +9 -0
  31. deaced-0.1.0/src/deaced.egg-info/top_level.txt +1 -0
  32. deaced-0.1.0/tests/data/arraylist.golden +42 -0
  33. deaced-0.1.0/tests/data/arraylist.ser +0 -0
  34. deaced-0.1.0/tests/data/arrays.golden +174 -0
  35. deaced-0.1.0/tests/data/arrays.ser +0 -0
  36. deaced-0.1.0/tests/data/classobj.golden +18 -0
  37. deaced-0.1.0/tests/data/classobj.ser +0 -0
  38. deaced-0.1.0/tests/data/enumv.golden +63 -0
  39. deaced-0.1.0/tests/data/enumv.ser +0 -0
  40. deaced-0.1.0/tests/data/floats.golden +134 -0
  41. deaced-0.1.0/tests/data/floats.ser +0 -0
  42. deaced-0.1.0/tests/data/inherit.golden +59 -0
  43. deaced-0.1.0/tests/data/inherit.ser +0 -0
  44. deaced-0.1.0/tests/data/longstring.golden +8 -0
  45. deaced-0.1.0/tests/data/longstring.ser +0 -0
  46. deaced-0.1.0/tests/data/primarrays.golden +212 -0
  47. deaced-0.1.0/tests/data/primarrays.ser +0 -0
  48. deaced-0.1.0/tests/data/prims.golden +94 -0
  49. deaced-0.1.0/tests/data/prims.ser +0 -0
  50. deaced-0.1.0/tests/data/proxy.golden +62 -0
  51. deaced-0.1.0/tests/data/proxy.ser +0 -0
  52. deaced-0.1.0/tests/data/refs.golden +30 -0
  53. deaced-0.1.0/tests/data/refs.ser +0 -0
  54. deaced-0.1.0/tests/test_cli.py +95 -0
  55. deaced-0.1.0/tests/test_dump.py +97 -0
  56. deaced-0.1.0/tests/test_golden.py +28 -0
  57. deaced-0.1.0/tests/test_jfloat.py +73 -0
  58. deaced-0.1.0/tests/test_mutf8.py +62 -0
  59. deaced-0.1.0/tests/test_protocol.py +233 -0
  60. deaced-0.1.0/tests/test_reader.py +41 -0
  61. deaced-0.1.0/tests/test_render.py +133 -0
  62. deaced-0.1.0/tests/test_robustness.py +139 -0
  63. deaced-0.1.0/tools/GenerateFixtures.java +157 -0
  64. deaced-0.1.0/tools/README.md +24 -0
  65. deaced-0.1.0/tools/regen_goldens.sh +35 -0
@@ -0,0 +1,50 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project are documented here. The format is based on
4
+ [Keep a Changelog](https://keepachangelog.com/), and this project adheres to
5
+ [Semantic Versioning](https://semver.org/).
6
+
7
+ ## [0.1.0] - 2026-06-27
8
+
9
+ First public release.
10
+
11
+ ### Added
12
+ - Python port of [NickstaDB/SerializationDumper](https://github.com/NickstaDB/SerializationDumper)
13
+ as a dependency-free library + CLI.
14
+ - Public API: `dump(data, *, format="text"|"json"|"pretty", offsets=False)` and
15
+ `parse(data) -> Stream`, which returns a typed semantic AST (nodes mirror
16
+ protocol entities; back-references are resolved against a handle table).
17
+ - Text dump byte-for-byte compatible with the patched upstream jar; `--offsets`
18
+ prefixes each line with its byte offset.
19
+ - JSON renderer (structured, machine-readable) and pretty renderer (compact data
20
+ tree); CLI `deaced -r/-f/-x [-o FILE] [-F text|json|pretty] [--offsets]`, stdin.
21
+ - Layered architecture: `reader` (byte reader), `tags` (`TC`/`SC` enums),
22
+ `parser` (stream → AST), `model` (the AST), `render/` (visitors), with
23
+ dedicated exceptions that carry the byte offset.
24
+
25
+ ### Fixed / improved (vs upstream)
26
+ - `(long)`/`(double)` integer-shift bug: bytes are widened before shifting, so
27
+ `0xFFFFFFFFFFFFFFFF` reads as `-1` (not `-4294967297`).
28
+ - `TC_LONGSTRING` accepted as an object-field value; `TC_BLOCKDATA` /
29
+ `TC_BLOCKDATALONG` accepted as array-field values.
30
+ - Strings decoded as Java modified UTF-8 (`U+0000` as `C0 80`, supplementary
31
+ characters as surrogate pairs); the original decoded byte-by-byte.
32
+ - `TC_RESET` (`0x79`) and `TC_EXCEPTION` (`0x7b`) are parsed; upstream aborts on
33
+ both. `TC_RESET` restarts handle numbering as Java does.
34
+ - `float`/`double` rendered with Java's exact `Double.toString` /
35
+ `Float.toString` notation (e.g. `1.0E7`, `1.5E-10`), not Python's `repr`.
36
+ - `long` field type code is labelled `Long - J` (the JVM type code, JVMS 4.3.2);
37
+ upstream's `Long - L` is a typo (the dumped hex byte `0x4a` is already `J`).
38
+
39
+ ### Hardened
40
+ - Output is always valid UTF-8: lone UTF-16 surrogates render as `?` (matching
41
+ the jar); previously such a value could crash the CLI on write.
42
+ - Structurally-invalid streams (negative array sizes, field/interface counts, or
43
+ block-data/string lengths) are rejected with a clear `SerDumpError` instead of
44
+ a misleading dump or a huge allocation attempt.
45
+ - Deeply nested streams parse with an enlarged worker-thread stack and raise a
46
+ clean error past a generous depth limit, instead of crashing the interpreter.
47
+ - Parse errors distinguish end-of-stream (`EOF`) from a literal `0x00` tag.
48
+ - `--offsets` is validated as a text-format-only option.
49
+
50
+ [0.1.0]: https://github.com/gmarav/DeACED/releases/tag/v0.1.0
@@ -0,0 +1,31 @@
1
+ # Contributing to DeACED
2
+
3
+ Thanks for your interest!
4
+
5
+ ## Setup
6
+
7
+ ```bash
8
+ pip install -e ".[dev]"
9
+ pre-commit install
10
+ ```
11
+
12
+ ## Before opening a PR
13
+
14
+ ```bash
15
+ ruff check .
16
+ ruff format --check .
17
+ mypy
18
+ pytest -q
19
+ ```
20
+
21
+ ## Guidelines
22
+
23
+ - Keep the core dependency-free (standard library only).
24
+ - All code, comments and commit messages in English.
25
+ - Add a test for every fix or new feature. Fixtures must be small and synthetic
26
+ (no third-party data) — the repo is public.
27
+ - Preserve the text dump format unless intentionally changing it (it is covered by
28
+ a golden test); discuss format changes in an issue first.
29
+ - Golden fixtures live in `tests/data`; regenerate them with `tools/regen_goldens.sh`
30
+ (needs a JDK and the patched jar -- see `tools/README.md`).
31
+ - Update `CHANGELOG.md` and, when behaviour diverges from upstream, `NOTICE`.
deaced-0.1.0/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2017 Nicky Bloor (original SerializationDumper, https://github.com/NickstaDB/SerializationDumper)
4
+ Copyright (c) 2026 Alexander Gmar (Python port, https://github.com/gmarav/DeACED)
5
+
6
+ Permission is hereby granted, free of charge, to any person obtaining a copy
7
+ of this software and associated documentation files (the "Software"), to deal
8
+ in the Software without restriction, including without limitation the rights
9
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10
+ copies of the Software, and to permit persons to whom the Software is
11
+ furnished to do so, subject to the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be included in all
14
+ copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22
+ SOFTWARE.
@@ -0,0 +1,11 @@
1
+ # Source distribution contents. The wheel ships only the package (src/deaced);
2
+ # the sdist additionally carries the tests + their fixtures and the dev tools so
3
+ # it is self-contained and auditable, and `pytest` works from an unpacked sdist.
4
+ include LICENSE
5
+ include NOTICE
6
+ include README.md
7
+ include CHANGELOG.md
8
+ include CONTRIBUTING.md
9
+ recursive-include tests *.py
10
+ recursive-include tests/data *.ser *.golden
11
+ recursive-include tools *.java *.sh *.md
deaced-0.1.0/NOTICE ADDED
@@ -0,0 +1,43 @@
1
+ DeACED
2
+ ======
3
+
4
+ DeACED is a Python port of SerializationDumper by Nicky Bloor.
5
+
6
+ Original work: SerializationDumper
7
+ Author: Nicky Bloor (@NickstaDB)
8
+ Source: https://github.com/NickstaDB/SerializationDumper
9
+ License: MIT (Copyright (c) 2017 Nicky Bloor)
10
+
11
+ Python port: Alexander Gmar (https://github.com/gmarav/DeACED)
12
+ Port license: MIT (Copyright (c) 2026 Alexander Gmar)
13
+
14
+ The output format (the text dump) mirrors the original so existing dumps remain
15
+ comparable. This port additionally fixes and extends the original:
16
+
17
+ * (long)/(double) fields: the original shifts byte operands as 32-bit ints
18
+ (b1 << 56 == b1 << 24) and sign-extends int masks, so 0xFFFFFFFFFFFFFFFF
19
+ was read as -4294967297 instead of -1 and some doubles were corrupted.
20
+ DeACED widens each byte to long before shifting (correct values).
21
+ * TC_LONGSTRING (0x7c) accepted as an object-field value.
22
+ * TC_BLOCKDATA (0x77) / TC_BLOCKDATALONG (0x7a) accepted as array-field values.
23
+ * Strings decoded as Java modified UTF-8 (the encoding serialization actually
24
+ uses): U+0000 as C0 80 and supplementary characters as surrogate pairs are
25
+ decoded correctly. The original decoded byte-by-byte, corrupting non-ASCII.
26
+ * TC_RESET (0x79) and TC_EXCEPTION (0x7b) are parsed; the original aborts on
27
+ both with "Illegal content element type".
28
+ * float/double values are rendered with Java's exact Double.toString /
29
+ Float.toString rules (shortest round-trip digits; decimal vs. scientific by
30
+ magnitude). This matches Java 19+ except for the smallest subnormals (a small
31
+ cluster near Double/Float.MIN_VALUE), where Java itself does not emit the
32
+ shortest digits; DeACED's output still round-trips to the identical value.
33
+ * The field type code for long is labelled "Long - J" (the JVM type code, per
34
+ JVMS 4.3.2). The original prints "Long - L", which is a typo -- the dumped
35
+ hex byte, 0x4a, is already ASCII 'J'.
36
+ * Lone UTF-16 surrogates in string/char values are rendered as '?', so output
37
+ is always valid UTF-8 (the original could emit an unencodable character).
38
+ * Structurally-invalid streams (negative array sizes, field/interface counts,
39
+ or block-data/string lengths) are rejected with a clear error rather than
40
+ silently producing a misleading partial dump.
41
+ * Optional byte-offset annotation mode.
42
+
43
+ The "rebuild" (-b) mode of the original is not (yet) ported.
deaced-0.1.0/PKG-INFO ADDED
@@ -0,0 +1,151 @@
1
+ Metadata-Version: 2.4
2
+ Name: deaced
3
+ Version: 0.1.0
4
+ Summary: Dump and inspect Java Object Serialization (0xAC 0xED) streams and RMI packets in human-readable form.
5
+ Author: Alexander Gmar
6
+ License-Expression: MIT
7
+ Project-URL: Homepage, https://github.com/gmarav/DeACED
8
+ Project-URL: Repository, https://github.com/gmarav/DeACED
9
+ Project-URL: Documentation, https://github.com/gmarav/DeACED#readme
10
+ Project-URL: Issues, https://github.com/gmarav/DeACED/issues
11
+ Project-URL: Changelog, https://github.com/gmarav/DeACED/blob/main/CHANGELOG.md
12
+ Keywords: java,serialization,deserialization,rmi,security,aced,dump
13
+ Classifier: Development Status :: 4 - Beta
14
+ Classifier: Intended Audience :: Developers
15
+ Classifier: Environment :: Console
16
+ Classifier: Topic :: Security
17
+ Classifier: Topic :: Software Development :: Disassemblers
18
+ Classifier: Topic :: Utilities
19
+ Classifier: Programming Language :: Python :: 3
20
+ Classifier: Programming Language :: Python :: 3 :: Only
21
+ Classifier: Programming Language :: Python :: 3.10
22
+ Classifier: Programming Language :: Python :: 3.11
23
+ Classifier: Programming Language :: Python :: 3.12
24
+ Classifier: Programming Language :: Python :: 3.13
25
+ Classifier: Programming Language :: Python :: 3.14
26
+ Requires-Python: >=3.10
27
+ Description-Content-Type: text/markdown
28
+ License-File: LICENSE
29
+ License-File: NOTICE
30
+ Provides-Extra: dev
31
+ Requires-Dist: pytest>=7; extra == "dev"
32
+ Requires-Dist: pytest-cov>=4; extra == "dev"
33
+ Requires-Dist: ruff>=0.4; extra == "dev"
34
+ Requires-Dist: mypy>=1.8; extra == "dev"
35
+ Requires-Dist: build>=1; extra == "dev"
36
+ Requires-Dist: twine>=5; extra == "dev"
37
+ Requires-Dist: pre-commit>=3; extra == "dev"
38
+ Dynamic: license-file
39
+
40
+ # DeACED
41
+
42
+ [![CI](https://github.com/gmarav/DeACED/actions/workflows/ci.yml/badge.svg)](https://github.com/gmarav/DeACED/actions/workflows/ci.yml)
43
+ [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](LICENSE)
44
+ ![Python](https://img.shields.io/badge/python-3.10%E2%80%933.14-blue)
45
+
46
+ > Dump and inspect **Java Object Serialization** (`0xAC 0xED`) streams — and Java RMI
47
+ > packet contents — in a clear, human-readable, hierarchical form.
48
+
49
+ `DeACED` (a nod to the `AC ED` stream magic, and to *de*-serialization) is a small,
50
+ **dependency-free** Python library and CLI. It is a port of
51
+ [SerializationDumper](https://github.com/NickstaDB/SerializationDumper) by Nicky Bloor,
52
+ with several correctness fixes (see [NOTICE](NOTICE)).
53
+
54
+ ## Install
55
+
56
+ ```bash
57
+ pip install deaced # once published
58
+ # or from source:
59
+ pip install .
60
+ ```
61
+
62
+ ## CLI
63
+
64
+ ```bash
65
+ deaced -r dump.bin # raw serialized file
66
+ cat dump.bin | deaced -r - # from stdin
67
+ deaced -x aced0005740004414243... # hex on the command line
68
+ deaced -f hexdump.txt # file of hex-ascii bytes
69
+ deaced -r dump.bin --offsets # prefix each line with '@<byte-offset>|'
70
+ deaced -r dump.bin -o out.txt # write to a file
71
+ deaced -r dump.bin -F json # structured, machine-readable JSON
72
+ deaced -r dump.bin -F pretty # compact, human-readable data tree
73
+ ```
74
+
75
+ Example:
76
+
77
+ ```text
78
+ $ deaced -x aced0005740004414243447071007e0000
79
+
80
+ STREAM_MAGIC - 0xac ed
81
+ STREAM_VERSION - 0x00 05
82
+ Contents
83
+ TC_STRING - 0x74
84
+ newHandle 0x00 7e 00 00
85
+ Length - 4 - 0x00 04
86
+ Value - ABCD - 0x41424344
87
+ TC_NULL - 0x70
88
+ TC_REFERENCE - 0x71
89
+ Handle - 8257536 - 0x00 7e 00 00
90
+ ```
91
+
92
+ ## Library
93
+
94
+ ```python
95
+ from deaced import dump, parse
96
+
97
+ data = open("dump.bin", "rb").read()
98
+
99
+ print(dump(data)) # text dump (default)
100
+ print(dump(data, offsets=True)) # with '@<offset>|' prefixes
101
+ print(dump(data, format="json")) # structured JSON
102
+ print(dump(data, format="pretty")) # compact human-readable tree
103
+
104
+ tree = parse(data) # the semantic AST (deaced.model.Stream)
105
+ ```
106
+
107
+ ## Why a port?
108
+
109
+ Removes the JVM dependency (the original is a Java jar), runs anywhere Python does,
110
+ integrates into Python tooling, and fixes real bugs in the original — most notably
111
+ the `(long)`/`(double)` integer-shift bug (`0xFFFFFFFFFFFFFFFF` was shown as
112
+ `-4294967297` instead of `-1`). See [NOTICE](NOTICE) for the full list.
113
+
114
+ ## Safety
115
+
116
+ DeACED is built to inspect **untrusted, attacker-controlled** serialization
117
+ streams (that is the usual reason to dump one), so:
118
+
119
+ - **It never deserializes Java objects.** DeACED only reads and decodes the byte
120
+ stream into a descriptive tree — it does not load classes, instantiate objects,
121
+ or invoke `readObject`. The classic Java deserialization gadget chains cannot
122
+ fire through it.
123
+ - **It fails cleanly on malformed input.** Truncated or structurally-invalid
124
+ streams raise a `SerDumpError` carrying the byte offset; negative
125
+ lengths/counts are rejected rather than driving huge allocations; deeply nested
126
+ streams raise an error instead of crashing the interpreter.
127
+
128
+ Resource use is still proportional to input size — apply your own limits when
129
+ dumping data straight off the network. See [SECURITY.md](SECURITY.md).
130
+
131
+ ## Development
132
+
133
+ ```bash
134
+ pip install -e ".[dev]"
135
+ pytest # tests (tiny synthetic fixtures)
136
+ ruff check .
137
+ ruff format --check .
138
+ mypy
139
+ ```
140
+
141
+ ## Contributing
142
+
143
+ Contributions welcome — see [CONTRIBUTING](CONTRIBUTING.md). The stream "rebuild"
144
+ (`-b`) mode of the original is intentionally not ported (see [NOTICE](NOTICE)).
145
+
146
+ ## Credits & license
147
+
148
+ Python port by **Alexander Gmar** ([@gmarav](https://github.com/gmarav)) —
149
+ [github.com/gmarav/DeACED](https://github.com/gmarav/DeACED).
150
+ Based on **SerializationDumper** by **Nicky Bloor** ([@NickstaDB](https://github.com/NickstaDB)).
151
+ Licensed under the [MIT License](LICENSE); port © 2026 Alexander Gmar, original © 2017 Nicky Bloor.
deaced-0.1.0/README.md ADDED
@@ -0,0 +1,112 @@
1
+ # DeACED
2
+
3
+ [![CI](https://github.com/gmarav/DeACED/actions/workflows/ci.yml/badge.svg)](https://github.com/gmarav/DeACED/actions/workflows/ci.yml)
4
+ [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](LICENSE)
5
+ ![Python](https://img.shields.io/badge/python-3.10%E2%80%933.14-blue)
6
+
7
+ > Dump and inspect **Java Object Serialization** (`0xAC 0xED`) streams — and Java RMI
8
+ > packet contents — in a clear, human-readable, hierarchical form.
9
+
10
+ `DeACED` (a nod to the `AC ED` stream magic, and to *de*-serialization) is a small,
11
+ **dependency-free** Python library and CLI. It is a port of
12
+ [SerializationDumper](https://github.com/NickstaDB/SerializationDumper) by Nicky Bloor,
13
+ with several correctness fixes (see [NOTICE](NOTICE)).
14
+
15
+ ## Install
16
+
17
+ ```bash
18
+ pip install deaced # once published
19
+ # or from source:
20
+ pip install .
21
+ ```
22
+
23
+ ## CLI
24
+
25
+ ```bash
26
+ deaced -r dump.bin # raw serialized file
27
+ cat dump.bin | deaced -r - # from stdin
28
+ deaced -x aced0005740004414243... # hex on the command line
29
+ deaced -f hexdump.txt # file of hex-ascii bytes
30
+ deaced -r dump.bin --offsets # prefix each line with '@<byte-offset>|'
31
+ deaced -r dump.bin -o out.txt # write to a file
32
+ deaced -r dump.bin -F json # structured, machine-readable JSON
33
+ deaced -r dump.bin -F pretty # compact, human-readable data tree
34
+ ```
35
+
36
+ Example:
37
+
38
+ ```text
39
+ $ deaced -x aced0005740004414243447071007e0000
40
+
41
+ STREAM_MAGIC - 0xac ed
42
+ STREAM_VERSION - 0x00 05
43
+ Contents
44
+ TC_STRING - 0x74
45
+ newHandle 0x00 7e 00 00
46
+ Length - 4 - 0x00 04
47
+ Value - ABCD - 0x41424344
48
+ TC_NULL - 0x70
49
+ TC_REFERENCE - 0x71
50
+ Handle - 8257536 - 0x00 7e 00 00
51
+ ```
52
+
53
+ ## Library
54
+
55
+ ```python
56
+ from deaced import dump, parse
57
+
58
+ data = open("dump.bin", "rb").read()
59
+
60
+ print(dump(data)) # text dump (default)
61
+ print(dump(data, offsets=True)) # with '@<offset>|' prefixes
62
+ print(dump(data, format="json")) # structured JSON
63
+ print(dump(data, format="pretty")) # compact human-readable tree
64
+
65
+ tree = parse(data) # the semantic AST (deaced.model.Stream)
66
+ ```
67
+
68
+ ## Why a port?
69
+
70
+ Removes the JVM dependency (the original is a Java jar), runs anywhere Python does,
71
+ integrates into Python tooling, and fixes real bugs in the original — most notably
72
+ the `(long)`/`(double)` integer-shift bug (`0xFFFFFFFFFFFFFFFF` was shown as
73
+ `-4294967297` instead of `-1`). See [NOTICE](NOTICE) for the full list.
74
+
75
+ ## Safety
76
+
77
+ DeACED is built to inspect **untrusted, attacker-controlled** serialization
78
+ streams (that is the usual reason to dump one), so:
79
+
80
+ - **It never deserializes Java objects.** DeACED only reads and decodes the byte
81
+ stream into a descriptive tree — it does not load classes, instantiate objects,
82
+ or invoke `readObject`. The classic Java deserialization gadget chains cannot
83
+ fire through it.
84
+ - **It fails cleanly on malformed input.** Truncated or structurally-invalid
85
+ streams raise a `SerDumpError` carrying the byte offset; negative
86
+ lengths/counts are rejected rather than driving huge allocations; deeply nested
87
+ streams raise an error instead of crashing the interpreter.
88
+
89
+ Resource use is still proportional to input size — apply your own limits when
90
+ dumping data straight off the network. See [SECURITY.md](SECURITY.md).
91
+
92
+ ## Development
93
+
94
+ ```bash
95
+ pip install -e ".[dev]"
96
+ pytest # tests (tiny synthetic fixtures)
97
+ ruff check .
98
+ ruff format --check .
99
+ mypy
100
+ ```
101
+
102
+ ## Contributing
103
+
104
+ Contributions welcome — see [CONTRIBUTING](CONTRIBUTING.md). The stream "rebuild"
105
+ (`-b`) mode of the original is intentionally not ported (see [NOTICE](NOTICE)).
106
+
107
+ ## Credits & license
108
+
109
+ Python port by **Alexander Gmar** ([@gmarav](https://github.com/gmarav)) —
110
+ [github.com/gmarav/DeACED](https://github.com/gmarav/DeACED).
111
+ Based on **SerializationDumper** by **Nicky Bloor** ([@NickstaDB](https://github.com/NickstaDB)).
112
+ Licensed under the [MIT License](LICENSE); port © 2026 Alexander Gmar, original © 2017 Nicky Bloor.
@@ -0,0 +1,71 @@
1
+ [build-system]
2
+ requires = ["setuptools>=77"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "deaced"
7
+ version = "0.1.0"
8
+ description = "Dump and inspect Java Object Serialization (0xAC 0xED) streams and RMI packets in human-readable form."
9
+ readme = "README.md"
10
+ requires-python = ">=3.10"
11
+ license = "MIT"
12
+ license-files = ["LICENSE", "NOTICE"]
13
+ authors = [{ name = "Alexander Gmar" }]
14
+ keywords = ["java", "serialization", "deserialization", "rmi", "security", "aced", "dump"]
15
+ classifiers = [
16
+ "Development Status :: 4 - Beta",
17
+ "Intended Audience :: Developers",
18
+ "Environment :: Console",
19
+ "Topic :: Security",
20
+ "Topic :: Software Development :: Disassemblers",
21
+ "Topic :: Utilities",
22
+ "Programming Language :: Python :: 3",
23
+ "Programming Language :: Python :: 3 :: Only",
24
+ "Programming Language :: Python :: 3.10",
25
+ "Programming Language :: Python :: 3.11",
26
+ "Programming Language :: Python :: 3.12",
27
+ "Programming Language :: Python :: 3.13",
28
+ "Programming Language :: Python :: 3.14",
29
+ ]
30
+
31
+ [project.urls]
32
+ Homepage = "https://github.com/gmarav/DeACED"
33
+ Repository = "https://github.com/gmarav/DeACED"
34
+ Documentation = "https://github.com/gmarav/DeACED#readme"
35
+ Issues = "https://github.com/gmarav/DeACED/issues"
36
+ Changelog = "https://github.com/gmarav/DeACED/blob/main/CHANGELOG.md"
37
+
38
+ [project.scripts]
39
+ deaced = "deaced.cli:main"
40
+
41
+ [project.optional-dependencies]
42
+ dev = ["pytest>=7", "pytest-cov>=4", "ruff>=0.4", "mypy>=1.8", "build>=1", "twine>=5", "pre-commit>=3"]
43
+
44
+ [tool.setuptools.packages.find]
45
+ where = ["src"]
46
+
47
+ [tool.setuptools.package-data]
48
+ deaced = ["py.typed"]
49
+
50
+ [tool.ruff]
51
+ line-length = 100
52
+ target-version = "py310"
53
+
54
+ [tool.ruff.lint]
55
+ select = ["E", "F", "I", "UP", "B"]
56
+
57
+ [tool.mypy]
58
+ python_version = "3.10"
59
+ strict = true
60
+ files = ["src/deaced"]
61
+
62
+ [tool.pytest.ini_options]
63
+ testpaths = ["tests"]
64
+ pythonpath = ["src"]
65
+
66
+ [tool.coverage.run]
67
+ source = ["deaced"]
68
+
69
+ [tool.coverage.report]
70
+ show_missing = true
71
+ fail_under = 90
deaced-0.1.0/setup.cfg ADDED
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,49 @@
1
+ """DeACED - dump and inspect Java Object Serialization (``0xAC 0xED``) streams.
2
+
3
+ A small, dependency-free library and CLI that turns a Java serialization stream
4
+ (and Java RMI packet contents) into a human-readable, hierarchical text dump.
5
+
6
+ This is a Python port of SerializationDumper by Nicky Bloor (MIT); see NOTICE.
7
+ """
8
+
9
+ from __future__ import annotations
10
+
11
+ from .model import Stream
12
+ from .parser import Parser, parse, run_deep
13
+ from .render import render_json, render_pretty, render_text
14
+
15
+ __all__ = ["dump", "parse", "__version__"]
16
+ __version__ = "0.1.0"
17
+
18
+ _FORMATS = ("text", "json", "pretty")
19
+
20
+
21
+ def dump(data: bytes, *, format: str = "text", offsets: bool = False) -> str:
22
+ """Parse ``data`` and render it.
23
+
24
+ Args:
25
+ data: The raw serialization stream.
26
+ format: Output format -- ``"text"`` (the default hierarchical dump,
27
+ byte-for-byte compatible with the patched upstream jar), ``"json"``
28
+ (a structured machine-readable view), or ``"pretty"`` (a compact
29
+ human-readable data tree).
30
+ offsets: When true (``text`` only), prefix every line with
31
+ ``@<byte-offset>|``.
32
+
33
+ Returns:
34
+ The rendered output as a single string.
35
+ """
36
+ if format not in _FORMATS:
37
+ raise ValueError(f"unknown format: {format!r}")
38
+ if offsets and format != "text":
39
+ raise ValueError("offsets are only supported for the 'text' format")
40
+
41
+ def render() -> str:
42
+ node: Stream = Parser(data).parse()
43
+ if format == "text":
44
+ return render_text(node, offsets=offsets)
45
+ if format == "json":
46
+ return render_json(node)
47
+ return render_pretty(node)
48
+
49
+ return run_deep(render)
@@ -0,0 +1,6 @@
1
+ """Enable ``python -m deaced``."""
2
+
3
+ from .cli import main
4
+
5
+ if __name__ == "__main__": # pragma: no cover
6
+ raise SystemExit(main())
@@ -0,0 +1,94 @@
1
+ """Command-line interface for DeACED."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import argparse
6
+ import sys
7
+
8
+ from . import __version__, dump
9
+
10
+
11
+ def _read_input(args: argparse.Namespace) -> bytes:
12
+ if args.hex is not None:
13
+ return bytes.fromhex("".join(args.hex.split()))
14
+ if args.hex_file is not None:
15
+ with open(args.hex_file, "rb") as f:
16
+ txt = f.read().decode("latin-1")
17
+ return bytes.fromhex("".join(c for c in txt if c in "0123456789abcdefABCDEF"))
18
+ # raw bytes ('-' = stdin)
19
+ if args.raw == "-":
20
+ return sys.stdin.buffer.read()
21
+ with open(args.raw, "rb") as f:
22
+ return f.read()
23
+
24
+
25
+ def main(argv: list[str] | None = None) -> int:
26
+ p = argparse.ArgumentParser(
27
+ prog="deaced",
28
+ description="Dump and inspect Java Object Serialization (0xAC 0xED) streams "
29
+ "and RMI packets in human-readable form.",
30
+ formatter_class=argparse.RawDescriptionHelpFormatter,
31
+ epilog=(
32
+ "examples:\n"
33
+ " deaced -r dump.bin dump a raw serialized file\n"
34
+ " cat dump.bin | deaced -r - read from stdin\n"
35
+ " deaced -x aced0005740004414243... decode hex from the command line\n"
36
+ " deaced -f hexdump.txt read a file of hex-ascii bytes\n"
37
+ " deaced -r dump.bin -F json emit structured JSON\n"
38
+ " deaced -r dump.bin -F pretty emit a compact data tree\n"
39
+ " deaced -r dump.bin --offsets annotate each line with its byte offset\n"
40
+ " deaced -r dump.bin -o out.txt write the dump to a file"
41
+ ),
42
+ )
43
+ src = p.add_mutually_exclusive_group(required=True)
44
+ src.add_argument(
45
+ "-r", "--raw", metavar="FILE", help="raw binary serialization file ('-' for stdin)"
46
+ )
47
+ src.add_argument(
48
+ "-f", "--hex-file", metavar="FILE", dest="hex_file", help="file of hex-ascii bytes"
49
+ )
50
+ src.add_argument("-x", "--hex", metavar="HEX", help="hex-ascii bytes on the command line")
51
+ p.add_argument("-o", "--output", metavar="FILE", help="write the dump to FILE (default stdout)")
52
+ p.add_argument(
53
+ "-F",
54
+ "--format",
55
+ choices=["text", "json", "pretty"],
56
+ default="text",
57
+ help="output format (default: text)",
58
+ )
59
+ p.add_argument(
60
+ "--offsets",
61
+ action="store_true",
62
+ help="prefix each line with '@<byte-offset>|' (text format only)",
63
+ )
64
+ p.add_argument("-V", "--version", action="version", version=f"deaced {__version__}")
65
+ args = p.parse_args(argv)
66
+
67
+ if args.offsets and args.format != "text":
68
+ p.error("--offsets is only valid with -F text")
69
+
70
+ try:
71
+ data = _read_input(args)
72
+ except (OSError, ValueError) as e:
73
+ print(f"deaced: cannot read input: {e}", file=sys.stderr)
74
+ return 2
75
+
76
+ try:
77
+ text = dump(data, format=args.format, offsets=args.offsets)
78
+ except Exception as e: # parser errors carry an offset in the message
79
+ print(f"deaced: parse error: {e}", file=sys.stderr)
80
+ return 1
81
+
82
+ if args.output:
83
+ with open(args.output, "w", encoding="utf-8") as f:
84
+ f.write(text)
85
+ else:
86
+ reconfigure = getattr(sys.stdout, "reconfigure", None)
87
+ if reconfigure is not None:
88
+ reconfigure(encoding="utf-8")
89
+ sys.stdout.write(text)
90
+ return 0
91
+
92
+
93
+ if __name__ == "__main__": # pragma: no cover
94
+ raise SystemExit(main())