
Packer e crypter PE open source per Windows x64. Comprime e crittografa gli eseguibili tramite una macchina virtuale personalizzata, racchiudendoli in uno stub autoestraente.
TinyLoad è un crypter/packer PE per eseguibili x64: comprime un exe in input con diversi livelli di protezione per impedirne il reverse engineering. È un singolo file .cpp e non ha dipendenze esterne.
TinyLoad aggiunge il tuo payload a una copia di se stesso. Quando l'exe compresso viene eseguito, estrae il payload, lo decripta e lo esegue direttamente in memoria senza mai scrivere l'originale su disco. Ogni volta che comprimi qualcosa, gli opcode della VM vengono modificati casualmente e inseriti in 4 sottotabelle con chiavi indipendenti, così non esistono due build identiche.
flowchart TD
start["pack(in, out, vm, comp, veh, noconsole)"] --> load["loadFile(in) → orig"]
load --> pe{"MZ + PE00 + Machine 0x8664 + NumberOfSections in 1..96?"}
pe -->|fail| err["printf error / return false"]
pe -->|ok| init["pay = orig flags = 0"]
init --> cdec{"--c ?"}
cdec -->|yes| lz["flags |= 1<br/>lzPack: WINDOW=0xFFFF MAXCHAIN=4096<br/>MAXMATCH=258 MINMATCH=3 HSIZE=65536<br/>hash4 = FNV-ish fold * 0x1000193<br/>lazy lookahead la = 1,2<br/>out = uint32le origSz + 8-token flag bytes<br/>match → varint dist, varint len-3"]
cdec -->|no| vmq
lz --> vmq{"--vm ?"}
vmq -->|yes| scatter["flags |= 2<br/>Fisher-Yates 32 slots, scatter NUM_OPS=28<br/>into subtables 4x8, unused stay 0xFF<br/>opmap_enc op = tbl shl 6 OR idx"]
scatter --> canary["canary corridor n=8, skip first 0x400 of pay<br/>canExp c = pay at off XOR prev"]
canary --> mkvm["makeVmProgram opmap_enc, key1, key2, canary<br/>key1,key2 ← mt19937_64 TickCount64 XOR QPC<br/>opaque predicates + stream loop + canary mask"]
mkvm --> venc["vmEncryptPayload 192-bit state<br/>k3 = 0x9E3779B97F4A7C15<br/>ks = uint8((k1 XOR k2) + k3)<br/>b = NOT(b XOR ks)<br/>k1 = rotl64(k1,11) XOR k2<br/>k2 = rotr64(k2,19) + k1 + k3<br/>k3 = k3 * phi XOR k1"]
vmq -->|no| vehq
venc --> vehq{"--veh ?"}
vehq -->|yes| vflag["flags |= FLAG_VEH 4"]
vehq -->|no| stub
vflag --> stub["stub = loadFile GetModuleFileNameA self<br/>saveFile out, stub as host image"]
stub --> skey["stubKey = 0x9E3779B97F4A7C15<br/>for i in 0x1000 .. min n, 0x2000:<br/>stubKey = (stubKey XOR stub i) * phi"]
skey --> disp["prime vmRun dispKey=0 → fill g_off<br/>dispKey ← rng<br/>encOff j = uint64(g_off j) XOR dispKey"]
disp --> tail["Tail: sig TINYLD60, origSz, packSz, flags,<br/>dispOff x28, subtables 4x8, vmCodeSz,<br/>dispKey, canary*, chunk*,<br/>payHash = SHA-256 orig first 8B"]
tail --> hide["sig i XOR= stubKey shr 8i<br/>xorOpmap FNV-1a 0x811C9DC5 / 0x01000193<br/>feeds origSz, packSz, vmCodeSz + first 32B<br/>of vmCode and pay, per-table<br/>flags |= FLAG_CHUNK 8"]
hide --> tenc["XOR-encode Tail fields with stubKey slices<br/>vmCode vi XOR= stubKey shr ((vi*3) and 63)"]
tenc --> ovl["payload = vmCode || pay<br/>interleave: insert 0x00 after every 3 bytes"]
ovl --> chunks["split into 4 chunks, shuffle physical order<br/>Tail placeholder TAIL_SERIALIZED_SZ = 419<br/>before each chunk: 128..640 RNG junk"]
chunks --> tea["xxteaEncrypt bytes after Tail<br/>DELTA=0x9E3779B9 rounds=6+52/n<br/>remainder XOR pxKey0 xor pxKey1 xor pxKey2 xor pxKey3<br/>patch k i XOR FEEDF00D / CAFEBABE /<br/>DEADBEEF / 8BADF00D into _pxBlock<br/>between C0DE1337 and B007DEAD"]
tea --> ser["XOR chunkOff / chunkSz / chunkOrder / chunkCnt<br/>serializeTail: 16 TLV records id + u16le sz + bytes<br/>Fisher-Yates order, LCG 1103515245+12345<br/>seed = stubKey XOR filesize<br/>append DWORD LE: tailOff XOR stubKey low 32"]
ser --> fin{"--noconsole ?"}
fin -->|yes| gui["OptionalHeader.Subsystem = IMAGE_SUBSYSTEM_WINDOWS_GUI"]
fin -->|no| scr
gui --> scr["scrambleSections: cycle names<br/>.text .data .rdata .bss .idata"]
scr --> done["saveFile out, result"]Prendi un binario precompilato dalle release oppure compilalo tu stesso.
Ti serve MinGW (g++). Basta eseguire:
g++ -o TinyLoad.exe TinyLoad.cpp -static -O2 -s
oppure usa build.bat.
TinyLoad.exe --i <input> [--o <output>] [--vm] [--c] [--veh]
Devi usare almeno uno tra --vm, --c o --veh.
Compressione LZ77 personalizzata con matching tramite hash chain e una finestra scorrevole da 64KB. La compressione viene eseguita per prima, poi la crittografia VM viene applicata sopra, così anche eventuali pattern nei dati compressi vengono nascosti.
Macchina virtuale personalizzata con 28 opcode che viene eseguita all'interno dello stub. Gli opcode vengono posizionati casualmente in 4 sottotabelle da 8 ciascuna e ogni sottotabella viene crittografata con XOR usando una chiave diversa derivata dai dati del payload. Decifrare 1 sottotabella rivela al massimo 8 opcode su 28. La cifratura stessa è un cifrario a flusso a 128 bit che usa rotl e rotr per il mixing delle chiavi, eseguito interamente dentro l'interprete della VM. Il payload viene crittografato con XXTEA.
Con --veh abilitato, tutte le pagine delle sezioni PE vengono mappate come PAGE_NOACCESS. Quando il programma tenta di accedere a una pagina, viene generata un'eccezione; un gestore di eccezioni vettoriale decripta solo quella singola pagina e imposta la protezione corretta. Un thread watchdog gira in background e ri-crittografa qualsiasi pagina che non è stata toccata per 200ms. In ogni momento la maggior parte del tuo programma è ancora crittografata in memoria, quindi i memory dump catturano solo ciò che è stato acceduto di recente.
Le 4 API più critiche (GetModuleHandleA, GetProcAddress, ExitProcess, VirtualAlloc) vengono reindirizzate tramite funzioni wrapper all'interno dello stub. Dopo che il payload è stato caricato, l'intera directory delle importazioni viene azzerata, così i memory dump non possono ricostruire la tabella delle importazioni.
MIT
| flag | cosa fa |
|---|
--i <file> | exe di input da comprimere |
--o <file> | percorso di output (default: input_packed.exe) |
--vm | crittografia VM |
--c | compressione LZ77 |
--veh | decrittografia VEH tramite page fault |
--noconsole | sottosistema GUI |