dp.exe — KiTTY Password Decryptor

📚 Table of Contents
- Feature Overview
- Architecture
- Usage
- Crypto Engine
- Session Scanner
- Export
- UI and Dark Mode
- Module Map
- Source Tree
- Build System
- System Requirements
Feature Overview
This is a standalone Win32 utility written in modern C++23 that provides comprehensive KiTTY password decryption capabilities:
Core Capabilities
- Password decryption — KiTTY custom base64 + Fisher-Yates scramble seeded by ciphertext prefix; two key derivation modes tried automatically
- Session scanning — Recursive directory walker (max depth 4) searches for
Sessionsfolders; percent-decodes filenames; skipsDefault Settings - GUI — Win32 dialog, sortable/filterable
SysListView32, double-click / right-click clipboard copy - CLI — Headless single-password decrypt; two argument orderings; attaches to parent console for pipeline use
- Export CSV — RFC 4180, semicolon-separated, double-quote escaping, UTF-8 no BOM
- Export JSON — Pretty-printed JSON array, full Unicode escape for control characters, UTF-8 no BOM
- Dark mode — Auto-detects Windows dark/light theme; MICA backdrop on Win11 (build ≥ 22000); rounded corners via DWM; reacts to
WM_SETTINGCHANGElive - DPI — Per-Monitor DPI v2 (manifest);
Segoe UI Variableon Win11,Segoe UIfallback; font scaled viaGetDpiForWindow - No dependencies — Pure Win32; no external DLLs; no VC++ Redistributable; statically linked CRT (
/MT)
Design Priorities
- Zero external dependencies — pure Win32 API surface
- Modern C++23 architecture with RAII and
std::format - Deterministic behavior with fail-fast crypto fallback
- Native Windows UX with dark mode, MICA, and DPI awareness
Architecture
Layered Structure
wmain (dp.cpp)
↓
dp::app::Run (App.cpp)
├─ CLI path ──► dp::core::DecryptPassword ──► KittyDecryptBase64
│ └─ KiTTY base64 engine
└─ GUI path ──► dp::ui::MainDialog::Show
├─ OnScan ──► dp::core::LoadSessionsFromRoot
│ ├─ ScanDir (recursive walker)
│ ├─ ReadKeyValues (kv parser)
│ └─ BuildSession ──► DecryptPassword
├─ OnExportCsv ──► dp::core::ExportCsv
├─ OnExportJson ──► dp::core::ExportJson
└─ ApplyTheme ──► DWM + uxtheme dark-mode stack
Namespace Map
| Namespace | Directory | Responsibility |
|---|---|---|
dp::app |
src/app/ |
CLI/GUI dispatch, COM init, console attach |
dp::core |
src/core/ |
Crypto, scan, export, string utilities |
dp::ui |
src/ui/ |
Win32 dialog, dark mode, ListView |
dp::ui::dark |
src/ui/DarkMode.h |
Undocumented uxtheme wrappers |
Usage
GUI Mode
Launch dp.exe with no arguments. The main dialog opens:
- Click Browse… to select a root folder (e.g. your KiTTY Portable directory or
%APPDATA%\KiTTY). - Click Scan — the tool recursively finds all
Sessionssubdirectories and decrypts every stored password. - Use the Filter box to search across session name, host, user, terminal type and password simultaneously (case-insensitive).
- Click any column header to sort; click again to reverse.
- Double-click a row or right-click → Copy selected to copy tab-separated fields to the clipboard.
- Export CSV / Export JSON — save the currently shown (filtered) set via a standard Save dialog.
Rows with a failed decrypt are rendered in red. The status bar shows total / shown / decrypted counts.
CLI Mode
dp.exe <password> <host> <term> [0|1]
dp.exe <0|1> <host> <term> <password>
Two argument orderings are accepted — mode flag first or password first. The mode flag is optional; when omitted the engine tries both key derivation modes automatically.
Example (from build.ps1 smoke test):
dp.exe 1633bGXSgBnT4I wesmar.wp.pl xterm 0
Expected output: bc107!+
On success the plaintext is written to stdout followed by a newline and the process exits with code 0. On failure ERR:DECRYPT is printed and the exit code is 1. When launched from a terminal the process auto-attaches to the parent console and sets UTF-8 output.
Help:
dp.exe --help
dp.exe -h
dp.exe /?
Crypto Engine
KiTTY stores passwords as a custom base64-encoded ciphertext. The decryption algorithm implemented in KittyBcrypt.cpp works as follows:
Base Alphabet
A shuffled 64-character set distinct from standard Base64:
AZERTYUIOPQSDFGHJKLMWXCVBNazertyuiopqsdfghjklmwxcvbn0123456789+/
Key Derivation
Two modes, both attempted automatically:
| Mode | Key |
|---|---|
| 0 (default) | host + term + "KiTTY" |
| 1 (portable) | "KiTTY" (password-only variant) |
Decryption Steps
[mermaid]
flowchart LR
A[Ciphertext] --> B[Seed Alphabet
≤5 bytes]
B --> C[Advance Past
Seed Prefix]
C --> D{Marker Byte?}
D -->|Yes| E[Accumulate Offset
Re-scramble Alphabet]
D -->|No| F[Locate in Alphabet]
E --> G[Output
offset + index]
F --> G
G --> H{Every pattern.size()?}
H -->|Yes| I[Key Re-scramble]
H -->|No| J{More Bytes?}
I --> J
J -->|Yes| D
J -->|No| K[Plaintext Output]
1. **Seed the alphabet** with the first ≤ 5 bytes of the ciphertext (Fisher-Yates scramble driven by ciphertext bytes).
2. **Advance past** the 5-byte seed prefix.
3. **For each remaining ciphertext byte:**
- Detect **marker bytes** (equal to the last character of the current alphabet)
- Each marker accumulates an offset and re-scrambles the alphabet using the key
4. **Locate the non-marker byte** in the current alphabet; output `offset + index` as the plaintext byte.
5. **Every `pattern.size()` decoded bytes** trigger another key-driven re-scramble.
6. **Skip silently:** embedded newlines and characters absent from the current alphabet.
`DecryptPassword` tries mode 0 first; falls back to mode 1 if mode 0 yields no output.
---
## Session Scanner {#session-scanner}
`Scan.cpp` — `LoadSessionsFromRoot(root, out)`
### Scanning Process
[mermaid]
flowchart TD
A[Root Directory] --> B[Recursive Walk<br/>Max Depth 4]
B --> C{Directory =<br/>'Sessions'?}
C -->|No| B
C -->|Yes| D[Read All Files<br/>in Sessions]
D --> E[Parse Key-Value<br/>KeyName\Value\n]
E --> F[Percent-Decode<br/>Filename]
F --> G{Default Settings?}
G -->|Yes| H[Skip]
G -->|No| I[Extract Fields]
I --> J[Decrypt Password]
J --> K[Build Session Struct]
K --> L[Add to Output]
Scanner Characteristics
- Recursively walks subdirectories up to depth 4
- When a directory named
Sessions(case-insensitive) is found, all files inside it are treated as session files - Subdirectories of
Sessionsare not descended - Each session file is parsed as
KeyName\Value\nkey-value pairs (KiTTY's native storage format) - The filename is percent-decoded (
%XX→ byte) and widened to UTF-16 for the session name Default Settings(and its percent-encoded form) is silently skipped
Fields Extracted Per Session
| Field | Registry key |
|---|---|
| Session name | filename (percent-decoded) |
| Host | HostName |
| User | UserName |
| Terminal | TerminalType (default: xterm) |
| Port | PortNumber |
| Encrypted password | Password |
- Files are read in a single
ReadFilecall (capped at 64 MiB) - Zero-copy
string_viewslicing is used for line parsing
Export
Both exporters build the entire output in a std::string buffer before writing in a single fwrite call (RAII unique_ptr<FILE>).
CSV Export (ExportCsv)
- Semicolon-separated, UTF-8 no BOM
- RFC 4180 double-quote escaping
- Columns:
Session;Host;User;Port;Term;Password;Source
JSON Export (ExportJson)
- Pretty-printed JSON array, one object per line
std::formatused for field serialisation- All control characters (
< 0x20) emitted as\uXXXX - Special characters (
",\,\n,\r,\t) escaped per JSON spec
UI and Dark Mode
MainDialog is a single modal Win32 dialog (DIALOGEX, resource IDD_MAIN). All Windows API surface uses Unicode (W variants). The dialog owns one SysListView32 with full-row select, double-buffer, and grid lines.
Dark Mode Detection
Reads HKCU\...\Themes\Personalize\AppsUseLightTheme. Theme is applied on WM_INITDIALOG and refreshed live on WM_SETTINGCHANGE / ImmersiveColorSet.
DWM Attributes Applied at Runtime
| Attribute | Effect |
|---|---|
DWMWA_USE_IMMERSIVE_DARK_MODE (20) |
Dark title bar |
DWMWA_SYSTEMBACKDROP_TYPE (38) = DWMSBT_MAINWINDOW |
MICA backdrop (Win11 only) |
DWMWA_WINDOW_CORNER_PREFERENCE (33) = DWMWCP_ROUND |
Rounded window corners |
Undocumented uxtheme.dll Ordinals
Loaded dynamically; all calls are no-ops on older Windows:
| Ordinal | Function | Purpose |
|---|---|---|
| 133 | AllowDarkModeForWindow |
Opts a specific HWND into dark rendering |
| 135 | SetPreferredAppMode(1) |
Allows dark menus and scrollbars process-wide |
| 136 | FlushMenuThemes |
Forces menu renderer to pick up dark preference |
| 137 | RefreshImmersiveColorPolicyState |
Tells DWM color system to re-read user preference |
ListView Styling
SetWindowTheme(list_, L"DarkMode_Explorer", nullptr) applied to the SysListView32 and its header in dark mode. The NM_CUSTOMDRAW handler:
- Colours failed-decrypt rows red
- Highlights filtered rows with a tinted background
Font
Segoe UI Variable (Win11) / Segoe UI (Win10), height scaled with GetDpiForWindow and MulDiv(9, dpi, 72), CLEARTYPE_QUALITY.
Module Map (for Maintainers)
| File | Purpose |
|---|---|
src/dp.cpp |
wmain — Unicode entry point |
src/app/App.cpp |
Run() — argument parsing, CLI decrypt, GUI bootstrap |
src/app/App.h |
Run() declaration |
src/core/Crypto.cpp |
DecryptPassword — tries both key derivation modes |
src/core/Crypto.h |
Public decrypt API |
src/core/KittyBcrypt.cpp |
KittyDecryptBase64 — full KiTTY nbcrypt algorithm |
src/core/KittyBcrypt.h |
Decrypt function declaration |
src/core/Scan.cpp |
LoadSessionsFromRoot — walker, parser, session builder |
src/core/Scan.h |
Scanner API |
src/core/Export.cpp |
ExportCsv, ExportJson |
src/core/Export.h |
Export API |
src/core/Session.h |
Session struct (name, host, user, term, port, password, source) |
src/core/StringUtil.cpp |
WidenUtf8Fallback, NarrowUtf8, BaseName, PercentDecode, IEquals, ContainsInsensitive |
src/core/StringUtil.h |
String utility declarations |
src/ui/MainDialog.cpp |
Win32 dialog — all message handling, theming, ListView, export |
src/ui/MainDialog.h |
MainDialog class declaration |
src/ui/DarkMode.h |
Inline dark-mode wrappers over undocumented uxtheme ordinals |
src/WinCommon.h |
Windows targeting macros, lean headers |
src/Resource.h |
Dialog and control resource IDs |
src/LinkerDeps.cpp |
#pragma comment(lib, …) for comctl32, dwmapi, ole32, shell32, uxtheme |
src/dp.manifest |
Per-Monitor DPI v2, long-path aware, CC v6, Win10/11 compat |
src/dp.rc |
Dialog template and version resource |
dp.vcxproj |
MSBuild project — Debug/Release × Win32/x64, C++23, /MT release |
build.ps1 |
PowerShell build script — clean, build both arches, strip intermediates, reproducible timestamps |
Source Tree (Local Repo)
Current local layout:
dp/
├─ src/
│ ├─ dp.cpp wmain
│ ├─ WinCommon.h Windows targeting + lean headers
│ ├─ Resource.h Control and dialog IDs
│ ├─ LinkerDeps.cpp Pragma-lib dependencies
│ ├─ dp.manifest DPI v2 / CC v6 / Win11 compat manifest
│ ├─ dp.rc Dialog template + version resource
│ ├─ app/
│ │ ├─ App.cpp CLI/GUI dispatch
│ │ └─ App.h
│ ├─ core/
│ │ ├─ Session.h Session data struct
│ │ ├─ Crypto.cpp/h DecryptPassword (dual-mode key derivation)
│ │ ├─ KittyBcrypt.cpp/h KittyDecryptBase64 (nbcrypt engine)
│ │ ├─ Scan.cpp/h Session directory walker and parser
│ │ ├─ Export.cpp/h CSV and JSON export
│ │ └─ StringUtil.cpp/h UTF-8/wide conversions, percent-decode, search
│ └─ ui/
│ ├─ DarkMode.h Uxtheme ordinal wrappers (header-only)
│ ├─ MainDialog.cpp Full Win32 dialog implementation
│ └─ MainDialog.h
├─ dp.vcxproj MSBuild project
├─ dp.vcxproj.filters Solution Explorer filter layout
├─ dp.slnx Solution file
└─ build.ps1 Build + clean + smoke test script
Build output:
bin/
├─ x86/dp.exe
└─ x64/dp.exe
Build System
Requirements: Visual Studio 2026 with C++ Desktop workload (MSVC v145 toolset).
.\build.ps1
The script performs the following steps:
- Locate VS 2026 via
vswhere.exe - Kill any running
dp.exefrom the repo directory (avoids file-lock on clean) - Remove
bin\andobj\directories - Build Release|Win32 and Release|x64 via MSBuild
- Strip all intermediates (
obj\and everything inbin\exceptdp.exe) - Stamp both binaries with a reproducible timestamp of
2030-01-01
Compiler Flags (Release)
| Flag | Purpose |
|---|---|
/std:c++latest |
C++23 features (std::format, std::ranges, designated initialisers) |
/utf-8 |
Source and execution charset = UTF-8 |
/W4 /permissive- /sdl |
High warning level, strict conformance, security checks |
/Zc:__cplusplus /Zc:preprocessor |
Correct __cplusplus value, conformant preprocessor |
/MT |
Static CRT — no VC++ Redistributable on target |
/O2 /GL /LTCG |
Max speed, whole-program optimisation, link-time code generation |
/BREPRO |
Reproducible builds |
Linker Entry Point
wmainCRTStartup — Unicode wide-char wmain; no narrow-string codec risk for path arguments.
Smoke Test
Printed by build.ps1 on completion:
cd test
..\bin\x86\dp.exe 1633bGXSgBnT4I wesmar.wp.pl xterm 0
# Expected: bc107!+
System Requirements
| Component | Requirement |
|---|---|
| Operating System | Windows 10 or later (Windows 11 recommended for MICA and rounded corners) |
| Architecture | x64 (bin\x64\dp.exe) and x86 (bin\x86\dp.exe) |
| Compiler (build) | Visual Studio 2026, MSVC v145 toolset, C++23 |
| Runtime dependencies | None — static CRT (/MT), pure Win32; no VC++ Redistributable required |
| Windows APIs | comctl32, dwmapi, ole32 (IFileDialog), shell32, uxtheme |
dp.exe v1.0.0 — KiTTY Password Decryptor — Modern C++23 Win32 implementation.