Skip to content

Commit 3d98fc8

Browse files
authored
Merge pull request #57 from bytebutcher/develop
Custom Code Script
2 parents 5869d4d + a68c314 commit 3d98fc8

File tree

137 files changed

+3911
-522
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

137 files changed

+3911
-522
lines changed

README.md

Lines changed: 34 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -6,27 +6,27 @@
66

77
# Decoder++
88

9-
An extensible application for penetration testers and software developers to decode/encode data into various formats.
9+
Decoder++ is an extensible application designed for penetration testers, software developers,
10+
and anyone in between looking to effortlessly decode and encode data across various formats.
11+
It includes a wide range of preinstalled scripts and codecs, smart decoding and format identification,
12+
and supports both graphical user interface (GUI) and command-line interface (CLI) operations.
1013

11-
## Setup
14+
## Quick Start
1215

13-
To install ```Decoder++``` simply use ```pip```:
16+
Get up and running with Decoder++ in just a few steps:
1417

1518
```bash
1619
# Install using pip (latest:qt6)
1720
pip3 install decoder-plus-plus[qt6]
1821

19-
# Install using pip (backport:qt5)
22+
# Or, for a qt5 backport:
2023
pip3 install decoder-plus-plus[qt5]
21-
```
2224

23-
Note, that this will install ```Decoder++``` and the most common plugins.
24-
In order to install and use all available plugins the package ```decoder-plus-plus[extras]``` needs to be installed:
25-
```
25+
# To leverage all features and plugins:
2626
pip3 install decoder-plus-plus[extras]
2727
```
2828

29-
Please refer to the [Installation Guide](docs/INSTALL.md) for more information regarding individual setups.
29+
For a detailed installation guide, including platform-specific instructions, see the [Installation Guide](docs/INSTALL.md).
3030

3131
## Overview
3232

@@ -55,26 +55,32 @@ $ dpp -e base64 -h sha1 "Hello, world!"
5555
e52d74c6d046c390345ae4343406b99587f2af0d
5656
```
5757

58-
### Features
59-
60-
* User Interfaces:
61-
* Graphical User Interface
62-
* Command Line Interface
63-
* Preinstalled Scripts and Codecs:
64-
* **Encode/Decode:** Base16, Base32, Base45, Base64, Base64 (URL-safe), Binary, Gzip, Hex, Html, JWT, HTTP64, Octal, Url, Url+, Zlib
65-
* **Hashing:** Adler-32, Apache-Md5, CRC32, FreeBSD-NT, Keccak224, Keccak256, Keccak384, Keccak512, LM, Md2, Md4,
66-
Md5, NT, PHPass, RipeMd160, Sha1, Sha3 224, Sha3 256, Sha3 384, Sha3 512, Sha224, Sha256, Sha348, Sha512,
67-
Sun Md5
68-
* **Scripts:** CSS-Minify, Caesar, Extract URLs, Filter-Lines, Identify File Format, Identify Hash Format, JS-Beautifier, JS-to-XML, JQ, JSONify, JSONPath, HTML-Beautifier, Little/Big-Endian Transform, Reformat Text, Remove Newlines, Remove Whitespaces, Search and Replace, Split and Rejoin, Unescape/Escape String, XPath
69-
* Format-Identification
70-
* Smart-Decode
71-
* Plugin System
72-
* Load & Save Current Session
73-
* Supported Platforms:
74-
* Windows
75-
* Linux
76-
* MAC
58+
### Codecs and Scripts
59+
60+
```Decoder++``` allows you to choose from a variety of codecs and scripts:
61+
62+
* **Encode/Decode:**
63+
- Base16, Base32, Base45, Base64, Base64 (URL-safe)
64+
- Binary, Gzip, Hex, Html, JWT, HTTP64
65+
- Octal, Url, Url+, Zlib
66+
* **Hashing:**
67+
- Adler-32, Apache-Md5, CRC32, FreeBSD-NT
68+
- Keccak224, Keccak256, Keccak384, Keccak512
69+
- LM, Md2, Md4, Md5, NT, PHPass
70+
- RipeMd160, Sha1, Sha3 224, Sha3 256, Sha3 384, Sha3 512
71+
- Sha224, Sha256, Sha348, Sha512, Sun Md5
72+
* **Scripts:**
73+
- Caesar, CSS-Minify, Custom Code, Extract URLs, Filter-Lines
74+
- Identify File Format, Identify Hash Format, JS-Beautifier, JS-to-XML, JQ
75+
- JSONify, JSONPath, HTML-Beautifier
76+
- Little/Big-Endian Transform, Reformat Text, Remove Newlines, Remove Whitespaces
77+
- Search and Replace, Split and Rejoin, Unescape/Escape String, XPath
78+
79+
80+
In cases where you require a bit more flexibility ```Decoder++``` allows you to process your data with
81+
custom scripts by using the ```Custom Code``` script:
7782

83+
![Decoder++ Screenshot](https://raw.githubusercontent.com/bytebutcher/decoder-plus-plus/master/images/dpp-custom-code-script.png)
7884

7985
## Advanced Usage
8086

data/dpp.png

8.45 KB
Loading

docs/INSTALL.md

Lines changed: 18 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -2,43 +2,46 @@
22

33
The following sections provide information about how Decoder++ can be installed on various Systems.
44

5-
## Ubuntu
5+
## via pip (Ubuntu)
66

77
### Qt6
88
```bash
99
apt-get update
10-
apt-get install -y python3 python3-pip git \
10+
apt-get install -y python3 python3-pip \
1111
libqt6core6 libqt6network6 libqt6openglwidgets6 libqt6widgets6 qt6-qpa-plugins \
1212
libgl1 libxcb-xinerama0
13-
git clone https://github.com/bytebutcher/decoder-plus-plus/
14-
cd decoder-plus-plus
15-
pip3 install --upgrade pip && pip3 install decoder-plus-plus[qt6] decoder-plus-plus[extras]
13+
pip3 install decoder-plus-plus[qt6]
1614
```
1715

1816
### Qt5
17+
1918
```bash
2019
apt-get update
21-
apt-get install -y python3 python3-pip git qt5-default libgl1 libxcb-xinerama0
22-
git clone https://github.com/bytebutcher/decoder-plus-plus/
23-
cd decoder-plus-plus
24-
pip3 install --upgrade pip && pip3 install decoder-plus-plus[qt5] decoder-plus-plus[extras]
20+
apt-get install -y python3 python3-pip qt5-default libgl1 libxcb-xinerama0
21+
pip3 install decoder-plus-plus[qt5]
2522
```
2623

27-
## Git
24+
## via git (Ubuntu)
2825

2926
### Qt6
3027
```bash
31-
git clone https://github.com/bytebutcher/decoder-plus-plus
32-
pip3 install decoder-plus-plus/.[qt6] decoder-plus-plus/.[extras]
28+
apt-get update
29+
apt-get install -y python3 python3-pip git \
30+
libqt6core6 libqt6network6 libqt6openglwidgets6 libqt6widgets6 qt6-qpa-plugins \
31+
libgl1 libxcb-xinerama0
32+
git clone https://github.com/bytebutcher/decoder-plus-plus/
33+
pip3 install --upgrade pip && pip3 install ./decoder-plus-plus[qt6] ./decoder-plus-plus[extras]
3334
```
3435

3536
### Qt5
3637
```bash
37-
git clone https://github.com/bytebutcher/decoder-plus-plus
38-
pip3 install decoder-plus-plus/.[qt5] decoder-plus-plus/.[extras]
38+
apt-get update
39+
apt-get install -y python3 python3-pip git qt5-default libgl1 libxcb-xinerama0
40+
git clone https://github.com/bytebutcher/decoder-plus-plus/
41+
pip3 install --upgrade pip && pip3 install ./decoder-plus-plus[qt5] ./decoder-plus-plus[extras]
3942
```
4043

41-
## Docker container
44+
## via Docker
4245

4346
### Qt6
4447
```bash

docs/plugin-config-class-overview.drawio

Lines changed: 1 addition & 1 deletion
Large diffs are not rendered by default.
276 KB
Loading

dpp/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
# along with this program. If not, see <http://www.gnu.org/licenses/>.
1717

1818
__name__ = 'decoder-plus-plus'
19-
__version__ = '1.5.0'
19+
__version__ = '1.6.0'
2020
__author__ = 'bytebutcher'
2121

2222
import os

dpp/core/context.py

Lines changed: 6 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ class Shortcut:
6767
TAB_CLOSE = "tab_close"
6868
TAB_SELECT_ = "tab_select_{}"
6969
TAB_SELECT_1 = "tab_select_1"
70-
TAB_SELECT_2= "tab_select_2"
70+
TAB_SELECT_2 = "tab_select_2"
7171
TAB_SELECT_3 = "tab_select_3"
7272
TAB_SELECT_4 = "tab_select_4"
7373
TAB_SELECT_5 = "tab_select_5"
@@ -92,14 +92,13 @@ class Shortcut:
9292
SHOW_KEYBOARD_SHORTCUTS = "show_keyboard_shortcuts"
9393
SHOW_ABOUT = "show_about"
9494

95-
def __init__(self, app_id, app_path, namespace):
95+
def __init__(self, app_id, app_path):
9696
super(__class__, self).__init__()
9797
self._app_id = app_id
9898
self._app_path = app_path
99-
self._namespace = namespace
10099
self._trace_mode = False
101100
self._debug_mode = self.config.isDebugModeEnabled()
102-
self._logger = logger.init_logger(self.getLogLevel())
101+
self._logger = logger.getLogger(name='dpp', level=self.getLogLevel())
103102
self._listener = Listener(self)
104103
self._plugins = None
105104
self._shortcuts = {}
@@ -117,7 +116,7 @@ def config(self):
117116
@property
118117
def logger(self) -> logging.Logger:
119118
""" Returns the standard logger for this application. """
120-
return logging.getLogger()
119+
return self._logger
121120

122121
def getAppName(self) -> str:
123122
""" Returns the name of the application. """
@@ -157,15 +156,15 @@ def setDebugMode(self, status: bool, temporary=False):
157156
self.config.setDebugMode(status)
158157
self._debug_mode = status
159158
self._trace_mode = False
160-
logger.set_level(logging.DEBUG if status else logging.INFO)
159+
self._logger.setLevel(logging.DEBUG if status else logging.INFO)
161160
status_string = "enabled" if status else "disabled"
162161
self._logger.info("Debug Mode: {} {}".format(status_string, " (temporary) " if temporary else ""))
163162

164163
def setTraceMode(self, status: bool):
165164
""" Enables/Disables debug mode. """
166165
self._trace_mode = status
167166
self._debug_mode = False
168-
logger.set_level(logging.TRACE if status else logging.INFO)
167+
self._logger.setLevel(logging.TRACE if status else logging.INFO)
169168
status_string = 'enabled' if status else 'disabled'
170169
self._logger.info(f'Trace Mode: {status_string}')
171170

@@ -261,10 +260,6 @@ def getShortcutById(self, id: str):
261260
def getPluginByName(self, name: str, type: str) -> AbstractPlugin:
262261
return self.plugins().plugin(name, type)
263262

264-
def saveAsFile(self, filename: str, content: str):
265-
with open(filename, "w") as f:
266-
f.write(content)
267-
268263
def __deepcopy__(self, memo):
269264
""" There shall be only one. """
270265
return self

dpp/core/icons.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@
2222

2323
from dpp import app_path
2424

25-
logger = logging.getLogger()
25+
logger = logging.getLogger(__name__)
2626

2727

2828
class Icon:

dpp/core/listener.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ class Listener:
6767
selectedFrameChanged = Signal(str, str, str) # tab_id, frame_id, input_text
6868

6969
# Signals that the text inside the codec frame changed (e.g. to update the hex view)
70-
textChanged = Signal(str, str, str, bool) # tab_id, frame_id, input_text, interactive
70+
textChanged = Signal(str, str, str) # tab_id, frame_id, input_text
7171

7272
# Signals that the text selection inside the codec frame changed (e.g. to update the hex view)
7373
textSelectionChanged = Signal(str, str, str) # tab_id, frame_id, input_text
@@ -83,8 +83,8 @@ def __init__(self, context: 'core.context.Context'):
8383
self.selectedFrameChanged.connect(lambda tab_id, frame_id, input_text:
8484
context.logger.trace(
8585
"selectedFrameChanged({}, {}, {})".format(tab_id, frame_id, input_text)))
86-
self.textChanged.connect(lambda tab_id, frame_id, input_text, interactive:
87-
context.logger.trace("textChanged({}, {}, {}, {})".format(tab_id, frame_id, input_text, interactive)))
86+
self.textChanged.connect(lambda tab_id, frame_id, input_text:
87+
context.logger.trace("textChanged({}, {}, {})".format(tab_id, frame_id, input_text)))
8888
self.textSelectionChanged.connect(lambda tab_id, frame_id, input_text:
8989
context.logger.trace("textSelectionChanged({}, {}, {})".format(tab_id, frame_id, input_text)))
9090
self.textSubmitted.connect(lambda tab_id, frame_id, input_text:

dpp/core/logger.py

Lines changed: 83 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -16,45 +16,105 @@
1616
# along with this program. If not, see <http://www.gnu.org/licenses/>.
1717
import logging
1818
import sys
19-
from functools import partial
20-
from typing import Callable
2119

22-
logging.TRACE = logging.DEBUG - 5
2320

24-
console_logger = None
21+
level_trace = logging.DEBUG - 5
22+
23+
24+
def logmethod(name=None, level=level_trace, prefix_callback=None):
25+
""" Decorator for logging method calls. """
26+
if not name:
27+
name = __name__
28+
def decorator(method):
29+
def wrapper(self, *args, **kwargs):
30+
prefix = ''
31+
if prefix_callback:
32+
prefix = prefix_callback(self)
33+
logging.getLogger(name=name).log(
34+
level=level,
35+
msg=f'{prefix}{method.__name__}({args, kwargs})'
36+
)
37+
#print(f'{level}: {logger_message}')
38+
result = method(self, *args, **kwargs)
39+
if result is not None:
40+
logging.getLogger(name=name).log(
41+
level=level,
42+
msg=f'{prefix}{method.__name__}({args, kwargs}) returns {result}'
43+
)
44+
return result
45+
46+
return wrapper
47+
48+
return decorator
2549

2650

2751
def _get_log_format(level: int) -> str:
2852
""" Returns the log format for the specified log level. """
29-
if level == logging.DEBUG or level == logging.TRACE:
53+
if level == logging.DEBUG or level == level_trace:
3054
return f'%(asctime)s: %(levelname)7s: %(filename)s:%(lineno)s:%(funcName)s: %(msg)s'
3155
else:
3256
return f'%(asctime)s: %(levelname)7s: %(msg)s'
3357

3458

35-
def _init_log_trace(logger: logging.Logger) -> Callable:
36-
""" Add custom log level TRACE. Do not define if it already exists. """
37-
if logging.getLevelName(logging.TRACE) == f'Level {logging.TRACE}':
38-
logging.addLevelName(logging.TRACE, 'TRACE')
39-
return lambda msg, *args, **kwargs: logger.log(logging.TRACE, msg, *args, **kwargs)
59+
def addLoggingLevel(levelName, levelNum, methodName=None):
60+
"""
61+
Comprehensively adds a new logging level to the `logging` module and the
62+
currently configured logging class.
63+
64+
`levelName` becomes an attribute of the `logging` module with the value
65+
`levelNum`. `methodName` becomes a convenience method for both `logging`
66+
itself and the class returned by `logging.getLoggerClass()` (usually just
67+
`logging.Logger`). If `methodName` is not specified, `levelName.lower()` is
68+
used.
69+
70+
To avoid accidental clobberings of existing attributes, this method will
71+
raise an `AttributeError` if the level name is already an attribute of the
72+
`logging` module or if the method name is already present
73+
74+
Example
75+
-------
76+
>>> addLoggingLevel('TRACE', logging.DEBUG - 5)
77+
>>> logging.getLogger(__name__).setLevel("TRACE")
78+
>>> logging.getLogger(__name__).trace('that worked')
79+
>>> logging.trace('so did this')
80+
>>> logging.TRACE
81+
5
82+
83+
"""
84+
if not methodName:
85+
methodName = levelName.lower()
4086

87+
if hasattr(logging, levelName):
88+
raise AttributeError('{} already defined in logging module'.format(levelName))
89+
if hasattr(logging, methodName):
90+
raise AttributeError('{} already defined in logging module'.format(methodName))
91+
if hasattr(logging.getLoggerClass(), methodName):
92+
raise AttributeError('{} already defined in logger class'.format(methodName))
4193

42-
def init_logger(level: int) -> logging.Logger:
94+
# This method was inspired by the answers to Stack Overflow post
95+
# http://stackoverflow.com/q/2183233/2988730, especially
96+
# http://stackoverflow.com/a/13638084/2988730
97+
def logForLevel(self, msg, *args, **kwargs):
98+
if self.isEnabledFor(levelNum):
99+
self._log(levelNum, msg, args, **kwargs)
100+
def logToRoot(msg, *args, **kwargs):
101+
logging.log(levelNum, msg, *args, **kwargs)
102+
103+
logging.addLevelName(levelNum, levelName)
104+
setattr(logging, levelName, levelNum)
105+
setattr(logging.getLoggerClass(), methodName, logForLevel)
106+
setattr(logging, methodName, logToRoot)
107+
108+
109+
def getLogger(name: str, level: int) -> logging.Logger:
43110
""" Initializes a logging.Logger with the specified level. """
44-
logger = logging.getLogger()
111+
logger = logging.getLogger(name)
45112
logger.setLevel(level)
46-
logger.trace = _init_log_trace(logger)
47-
48-
global console_logger
113+
try:
114+
addLoggingLevel('TRACE', level_trace)
115+
except AttributeError as err:
116+
logger.debug(f'Failed to add TRACE logging level: {err}')
49117
console_logger = logging.StreamHandler(sys.stderr)
50118
console_logger.setFormatter(logging.Formatter(_get_log_format(level)))
51119
logger.addHandler(console_logger)
52120
return logger
53-
54-
55-
def set_level(level: int):
56-
""" Sets the specified level for the standard logger. """
57-
logger = logging.getLogger()
58-
logger.setLevel(level)
59-
global console_logger
60-
console_logger.setFormatter(logging.Formatter(_get_log_format(level)))

0 commit comments

Comments
 (0)