CCGT is a post-build string protection tool for Windows x64 Binaries.
It scans a compiled executable for eligible strings, encrypts them & writes a compact metadata table into a dedicated PE section (.ccgtr). At runtime, a lightweight header-only runtime (ccgt_runtime.h) decrypts those protected regions back into plaintext before the program’s code uses them.
This approach is designed to reduce the exposure of sensitive strings in static analysis workflows (e.g., strings.exe, YARA, bulk IOC extraction, and casual RE).
CCGT is a two-part system:
-
CCGT CLI (post-build patcher) A command-line tool that:
- scans an EXE for candidate strings
- encrypts those bytes directly inside the file
- writes a metadata table (regions + key material) into a dedicated section
-
CCGT Runtime (in-binary decrypt shim) A header-only runtime (
ccgt_runtime.h) that:- defines the metadata section (
.ccgtr) - runs automatically during process initialization
- decrypts encrypted regions in-memory using the metadata
- defines the metadata section (
-
Protecting sensitive identifiers embedded as string literals:
- API endpoints, URLs, tokens, keys, auth strings
- internal routes, feature flags, debug markers
- error messages revealing architecture or detection logic
-
Making static triage harder:
strings.exe,floss, naive regex harvesting- simple signature rules targeting plain
.rdatastrings
-
Scan the PE file
- locate candidate ASCII strings and their file offsets
- validate length and null termination
-
Verify candidates are safe to patch
- must belong to
.rdataor.data - must not overlap PE directories (imports, resources, IAT, relocations, etc.)
- must not be inside the
.ccgtrmeta section - must be within file bounds and outside headers
- must belong to
-
Encrypt in-place
-
algorithm: ChaCha20-Poly1305 (AEAD)
-
key: per-binary master key (32 bytes)
-
nonce: derived per region from
(seed, RVA) -
AAD: region metadata
(RVA, length, seed) -
result:
- the original bytes at each string location are replaced with ciphertext bytes
- a per-region Poly1305 authentication tag is generated
-
-
Write metadata
- regions list:
{ RVA, length, seed, tag }for each encrypted block - key material: stored as
frag ^ maskcomponents in.ccgtr - metadata is written into the
.ccgtrsection embedded into the target binary
- regions list:
On process start (before main()), the runtime:
-
Locates its own module base.
-
Reads
.ccgtrmetadata (g_meta). -
Reconstructs the master key from
frag ^ mask. -
For each region:
-
computes address from
base + RVA -
flips protection to RW via
VirtualProtect -
verifies and decrypts using ChaCha20-Poly1305
- authentication covers ciphertext and region metadata
-
if authentication fails, the region is left encrypted
-
restores original protection
-
Result: string call sites remain valid and the program runs normally, but the on-disk binary no longer contains plaintext strings and unauthorized modification of protected regions is detected at runtime.
- Toolchain: MSVC
- Language: C++20
- Platform: x64
Build ccgt as a normal console app.
In the project you want to protect:
- Add
/include/*hto your include path (all header files) - Include
/include/ccgt_runtime.hit in one translation unit (recommended: your main file)
Example:
#include "ccgt_runtime.h"
#include <iostream>
int main() {
std::cout << "Hello from protected binary\n";
return 0;
}This causes the protected binary to contain a .ccgtr section with g_meta.
From the output directory containing your built executable:
.\ccgt.exe .\<ExeToProtect>.exeCheck that .ccgtr exists:
dumpbin /headers .\prototype.exe
dumpbin /section:.ccgtr .\prototype.exeConfirm plaintext strings are reduced:
strings.exe .\prototype.exe | findstr /i "example.com"usage:
ccgt <binary.exe> [--scan] [--dry-run] [-v] [--min N] [--max N] [--no-backup]
notes:
- default action is to PATCH (encrypt strings + write meta)
- --scan prints findings only
-
--scanScan only, print candidate strings and offsets. No patching occurs. -
--dry-runPerform all checks and plan encryption, but do not write changes. -
-v/--verbosePrint detailed logs (skips, decisions, region selections). -
--min N/--max NClamp eligible string lengths. -
--no-backupDisable.bakcreation.
Your target binary does no contain the .ccgtr section.
Fix:
- Ensure all header files from
/include/are included in your project - Ensure
/include/ccgt_runtime.hin at least one translation unit that is linked - Ensure the compiler/linker didn’t discard it
The .ccgtr section exists but is not large enough.
Fix:
- Do not shrink meta capacity
- Ensure
Metais allocated exactly as provided in the runtime header
Likely causes:
- encrypting bytes that are not safe to modify
- accidental overlap with structured PE regions
- runtime decrypt not running early enough
Fix:
- run with
--verboseand inspect skip reasons - tighten selection rules: require null termination, raise
min_len - validate runtime auto-init is linked (CRT initialization section present)
CCGT is intended for legitimate protection and defensive hardening. It is not a guarantee against reverse engineering or runtime analysis. It is designed to raise the cost of static extraction and reduce accidental exposure of sensitive strings in deployed binaries.