Skip to content
Open
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .envrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export VIRTUAL_ENV=.venv
layout python3
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,7 @@ dist
build
MANIFEST
TODO

__pycache__
.ruff*
xortool_out
1 change: 1 addition & 0 deletions .python-version
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
3.13.2
106 changes: 54 additions & 52 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,75 +1,79 @@
xortool.py
====================
# xortool.py

A tool to do some xor analysis:

- guess the key length (based on count of equal chars)
- guess the key (base on knowledge of most frequent char)
- guess the key length (based on count of equal chars)
- guess the key (base on knowledge of most frequent char)

**Notice:** xortool is now only running on Python 3. The old Python 2 version is accessible at the `py2` branch. The **pip** package has been updated.
**Notice:** xortool is now only running on Python 3. (And Update with `rye` project support and maximum support for Python 3.9-3.13+)

## Installation

```bash
$ pip3 install xortool
pip3 install xortool
```

For development or building this repository, [poetry](https://python-poetry.org/) is needed.


```bash
poetry build
pip install dist/xortool*.whl
```

Usage
---------------------
## Usage

```text
$ xortool --help

Usage: xortool [OPTIONS] [FILENAME]

A tool to do some xor analysis:
- guess the key length (based on count of equal chars)
- guess the key (base on knowledge of most frequent char)

╭─ Arguments ───────────────────────────────────────────────────────────────────────────────╮
│ filename [FILENAME] Input file (or stdin if omitted) │
╰───────────────────────────────────────────────────────────────────────────────────────────╯
╭─ Options ─────────────────────────────────────────────────────────────────────────────────╮
│ --hex -x Input is hex-encoded str │
│ --key-length -l INTEGER Length of the key [default: None] │
│ --max-keylen -m INTEGER Maximum key length to probe [default: 65] │
│ --char -c TEXT Most frequent char (one char or hex code) │
│ [default: None] │
│ --brute-chars -b Brute force all possible most frequent chars │
│ --brute-printable -o Same as -b but will only check printable chars │
│ --filter-output -f Filter outputs based on the charset │
│ --text-charset -t TEXT Target text character set │
│ [default: │
│ 0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOP… │
│ ] │
│ --known-plaintext -p TEXT Use known plaintext for decoding [default: None] │
│ --version Show version and exit │
│ --help Show this message and exit. │
╰───────────────────────────────────────────────────────────────────────────────────────────╯

```
xortool
A tool to do some xor analysis:
- guess the key length (based on count of equal chars)
- guess the key (base on knowledge of most frequent char)

Usage:
xortool [-x] [-m MAX-LEN] [-f] [-t CHARSET] [FILE]
xortool [-x] [-l LEN] [-c CHAR | -b | -o] [-f] [-t CHARSET] [-p PLAIN] [FILE]
xortool [-x] [-m MAX-LEN| -l LEN] [-c CHAR | -b | -o] [-f] [-t CHARSET] [-p PLAIN] [FILE]
xortool [-h | --help]
xortool --version

Options:
-x --hex input is hex-encoded str
-l LEN, --key-length=LEN length of the key
-m MAX-LEN, --max-keylen=MAX-LEN maximum key length to probe [default: 65]
-c CHAR, --char=CHAR most frequent char (one char or hex code)
-b --brute-chars brute force all possible most frequent chars
-o --brute-printable same as -b but will only check printable chars
-f --filter-output filter outputs based on the charset
-t CHARSET --text-charset=CHARSET target text character set [default: printable]
-p PLAIN --known-plaintext=PLAIN use known plaintext for decoding
-h --help show this help

Notes:
Text character set:
* Pre-defined sets: printable, base32, base64
* Custom sets:
- a: lowercase chars
- A: uppercase chars
- 1: digits
- !: special chars
- *: printable chars
Text character set:

- Pre-defined sets: printable, base32, base64
- Custom sets:
- a: lowercase chars
- A: uppercase chars
- 1: digits
- !: special chars
- \*: printable chars

Examples:
xortool file.bin
xortool -l 11 -c 20 file.bin
xortool -x -c ' ' file.hex
xortool -b -f -l 23 -t base64 message.enc
xortool -b -p "xctf{" message.enc
```

Example 1
---------------------
- `xortool file.bin`
- `xortool -l 11 -c 20 file.bin`
- `xortool -x -c ' ' file.hex`
- `xortool -b -f -l 23 -t base64 message.enc`
- `xortool -b -p "xctf{" message.enc`

## Example 1

```bash
# xor is xortool/xortool-xor
Expand Down Expand Up @@ -149,8 +153,7 @@ So, if automated decryption fails, you can calibrate:
- (`-l`) selected length to see some interesting keys
- (`-c`) the most frequent char to produce right plaintext

Example 2
---------------------
## Example 2

We are given a message in encoded in Base64 and XORed with an unknown key.

Expand Down Expand Up @@ -188,8 +191,7 @@ See files filename-key.csv, filename-char_used-perc_valid.csv

By filtering the outputs on the character set of Base64, we directly keep the only solution.

Information
---------------------
## Information

Author: hellman

Expand Down
66 changes: 0 additions & 66 deletions poetry.lock

This file was deleted.

34 changes: 20 additions & 14 deletions pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
[tool.poetry]
[project]
name = "xortool"
version = "1.0.2"
version = "1.1.0"
description = "A tool to analyze multi-byte xor cipher"
authors = ["hellman"]
authors = [{ name = "DawnMagnet", email = "[email protected]" }]
license = "MIT"
readme = "README.md"
keywords = ["xor", "xortool", "cryptanalysis"]
Expand All @@ -11,22 +11,28 @@ classifiers = [
'Intended Audience :: Science/Research',
'Topic :: Security :: Cryptography',
]
requires-python = ">=3.6,<4.0"
dependencies = [
"typer>=0.15.2",
"rich>=14.0.0",
]

[tool.poetry.scripts]
[project.scripts]
xortool = 'xortool.tool_main:main'
xortool-xor = 'xortool.tool_xor:main'

[tool.poetry.urls]
[project.urls]
homepage = "http://github.com/hellman/xortool"

[tool.poetry.dependencies]
python = ">=3.6,<4.0"
docopt = "^0.6.2"
[build-system]
requires = ["pdm-backend"]
build-backend = "pdm.backend"

[tool.poetry.dev-dependencies]
docopt = "^0.6.2"
importlib_metadata = "^4.8"
[tool.rye]
managed = true
dev-dependencies = []
# dev-dependencies = ["importlib_metadata ^4.8"]

[build-system]
requires = ["poetry-core>=1.0.0a5"]
build-backend = "poetry.core.masonry.api"
# [build-system]
# requires = ["rye>=0.21.1"]
# build-backend = "rye.masonry.api"
29 changes: 29 additions & 0 deletions requirements-dev.lock
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
# generated by rye
# use `rye lock` or `rye sync` to update this lockfile
#
# last locked with the following flags:
# pre: false
# features: []
# all-features: false
# with-sources: false
# generate-hashes: false
# universal: false

-e file:.
click==8.1.8
# via typer
markdown-it-py==3.0.0
# via rich
mdurl==0.1.2
# via markdown-it-py
pygments==2.19.1
# via rich
rich==14.0.0
# via typer
# via xortool
shellingham==1.5.4
# via typer
typer==0.15.2
# via xortool
typing-extensions==4.13.2
# via typer
29 changes: 29 additions & 0 deletions requirements.lock
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
# generated by rye
# use `rye lock` or `rye sync` to update this lockfile
#
# last locked with the following flags:
# pre: false
# features: []
# all-features: false
# with-sources: false
# generate-hashes: false
# universal: false

-e file:.
click==8.1.8
# via typer
markdown-it-py==3.0.0
# via rich
mdurl==0.1.2
# via markdown-it-py
pygments==2.19.1
# via rich
rich==14.0.0
# via typer
# via xortool
shellingham==1.5.4
# via typer
typer==0.15.2
# via xortool
typing-extensions==4.13.2
# via typer
4 changes: 2 additions & 2 deletions xortool/__init__.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
from importlib_metadata import version
from importlib.metadata import version

__version__ = version(__package__)
__version__ = version(__package__ or "xortool")
26 changes: 0 additions & 26 deletions xortool/args.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,3 @@
from docopt import docopt

from xortool.charset import get_charset


class ArgError(Exception):
pass

Expand All @@ -29,24 +24,3 @@ def parse_int(i):
if i is None:
return None
return int(i)


def parse_parameters(doc, version):
p = docopt(doc, version=version)
p = {k.lstrip("-"): v for k, v in p.items()}
try:
return {
"brute_chars": bool(p["brute-chars"]),
"brute_printable": bool(p["brute-printable"]),
"filename": p["FILE"] if p["FILE"] else "-", # stdin by default
"filter_output": bool(p["filter-output"]),
"frequency_spread": 0, # to be removed
"input_is_hex": bool(p["hex"]),
"known_key_length": parse_int(p["key-length"]),
"max_key_length": parse_int(p["max-keylen"]),
"most_frequent_char": parse_char(p["char"]),
"text_charset": get_charset(p["text-charset"]),
"known_plain": p["known-plaintext"].encode() if p["known-plaintext"] else False,
}
except ValueError as err:
raise ArgError(str(err))
Loading