
Empaquetador y cifrador de PE x64 de Windows de código abierto. Comprime y cifra ejecutables con una máquina virtual personalizada en un stub autoextraíble.
TinyLoad es un cifrador/empaquetador PE para ejecutables x64 que empaqueta un exe de entrada con distintas capas de protección para evitar que sea sometido a ingeniería inversa. Es un único archivo .cpp y no tiene dependencias externas.
TinyLoad añade tu payload a una copia de sí mismo. Cuando el exe empaquetado se ejecuta, extrae el payload, lo descifra y lo ejecuta directamente en memoria sin escribir jamás el original en disco. Cada vez que empaquetas algo, los opcodes de la VM se cambian aleatoriamente y se colocan en 4 subtablas con claves independientes, de modo que no hay dos builds iguales.
flowchart TD
start["pack(in, out, vm, comp, veh, noconsole)"] --> load["loadFile(in) → orig"]
load --> pe{"¿MZ + PE00 + Machine 0x8664 + NumberOfSections en 1..96?"}
pe -->|fallo| err["printf error / return false"]
pe -->|ok| init["pay = orig flags = 0"]
init --> cdec{"¿--c ?"}
cdec -->|sí| lz["flags |= 1<br/>lzPack: WINDOW=0xFFFF MAXCHAIN=4096<br/>MAXMATCH=258 MINMATCH=3 HSIZE=65536<br/>hash4 = plegado tipo FNV * 0x1000193<br/>lookahead perezoso la = 1,2<br/>out = uint32le origSz + bytes de flag de 8 tokens<br/>coincidencia → varint dist, varint len-3"]
cdec -->|no| vmq
lz --> vmq{"¿--vm ?"}
vmq -->|sí| scatter["flags |= 2<br/>Fisher-Yates 32 slots, dispersar NUM_OPS=28<br/>en subtablas 4x8, las no usadas quedan 0xFF<br/>opmap_enc op = tbl shl 6 OR idx"]
scatter --> canary["corredor canario n=8, omitir primeros 0x400 de pay<br/>canExp c = pay en off XOR prev"]
canary --> mkvm["makeVmProgram opmap_enc, key1, key2, canary<br/>key1,key2 ← mt19937_64 TickCount64 XOR QPC<br/>predicados opacos + bucle de flujo + máscara canaria"]
mkvm --> venc["vmEncryptPayload estado de 192 bits<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 -->|sí| vflag["flags |= FLAG_VEH 4"]
vehq -->|no| stub
vflag --> stub["stub = loadFile GetModuleFileNameA self<br/>saveFile out, stub como imagen anfitriona"]
stub --> skey["stubKey = 0x9E3779B97F4A7C15<br/>para i en 0x1000 .. min n, 0x2000:<br/>stubKey = (stubKey XOR stub i) * phi"]
skey --> disp["inicializar vmRun dispKey=0 → rellenar g_off<br/>dispKey ← rng<br/>encOff j = uint64(g_off j) XOR dispKey"]
disp --> tail["Cola: sig TINYLD60, origSz, packSz, flags,<br/>dispOff x28, subtablas 4x8, vmCodeSz,<br/>dispKey, canary*, chunk*,<br/>payHash = SHA-256 orig primeros 8B"]
tail --> hide["sig i XOR= stubKey shr 8i<br/>xorOpmap FNV-1a 0x811C9DC5 / 0x01000193<br/>alimenta origSz, packSz, vmCodeSz + primeros 32B<br/>de vmCode y pay, por tabla<br/>flags |= FLAG_CHUNK 8"]
hide --> tenc["Codificar con XOR los campos de Tail con fragmentos de stubKey<br/>vmCode vi XOR= stubKey shr ((vi*3) and 63)"]
tenc --> ovl["payload = vmCode || pay<br/>intercalar: insertar 0x00 después de cada 3 bytes"]
ovl --> chunks["dividir en 4 chunks, barajar el orden físico<br/>placeholder de Tail TAIL_SERIALIZED_SZ = 419<br/>antes de cada chunk: 128..640 bytes basura RNG"]
chunks --> tea["xxteaEncrypt bytes después de Tail<br/>DELTA=0x9E3779B9 rondas=6+52/n<br/>resto XOR pxKey0 xor pxKey1 xor pxKey2 xor pxKey3<br/>parchear k i XOR FEEDF00D / CAFEBABE /<br/>DEADBEEF / 8BADF00D en _pxBlock<br/>entre C0DE1337 y B007DEAD"]
tea --> ser["XOR chunkOff / chunkSz / chunkOrder / chunkCnt<br/>serializeTail: 16 registros TLV id + u16le sz + bytes<br/>orden Fisher-Yates, LCG 1103515245+12345<br/>semilla = stubKey XOR filesize<br/>añadir DWORD LE: tailOff XOR stubKey low 32"]
ser --> fin{"¿--noconsole ?"}
fin -->|sí| gui["OptionalHeader.Subsystem = IMAGE_SUBSYSTEM_WINDOWS_GUI"]
fin -->|no| scr
gui --> scr["scrambleSections: nombres en ciclo<br/>.text .data .rdata .bss .idata"]
scr --> done["saveFile out, result"]Consigue un binario precompilado desde releases o compílalo tú mismo.
Necesitas MinGW (g++). Solo ejecuta:
g++ -o TinyLoad.exe TinyLoad.cpp -static -O2 -s
o usa build.bat.
TinyLoad.exe --i <input> [--o <output>] [--vm] [--c] [--veh]
Necesitas al menos 1 de --vm, --c o --veh.
Compresión LZ77 personalizada con coincidencia por cadena hash y una ventana deslizante de 64KB. La compresión se ejecuta primero y luego el cifrado VM se aplica encima, de modo que cualquier patrón en los datos comprimidos también queda oculto.
Máquina virtual personalizada de 28 opcodes que se ejecuta dentro del stub. Los opcodes se colocan aleatoriamente en 4 subtablas de 8 cada una y cada subtabla se cifra con XOR usando una clave distinta derivada de los datos del payload. Descifrar 1 subtabla revela como máximo 8 opcodes de 28. El cifrado en sí es un cifrado de flujo de 128 bits que usa mezcla de claves rotl y rotr ejecutada íntegramente dentro del intérprete de la VM. El payload se cifra con XXTEA.
Con --veh habilitado, todas las páginas de las secciones PE se mapean como PAGE_NOACCESS. Cuando el programa intenta acceder a una página, se dispara una excepción; un manejador de excepciones vectorizado descifra solo esa página y establece la protección correcta. Un hilo vigilante se ejecuta en segundo plano y vuelve a cifrar cualquier página que no se haya tocado en 200ms. En cualquier momento dado, la mayor parte de tu programa sigue cifrada en memoria, por lo que los volcados de memoria solo capturan lo que se accedió recientemente.
Las 4 APIs más críticas (GetModuleHandleA, GetProcAddress, ExitProcess, VirtualAlloc) se redirigen a través de funciones envoltorio dentro del stub. Después de cargar el payload, todo el directorio de importaciones se pone a cero, de modo que los volcados de memoria no pueden reconstruir la tabla de importaciones.
MIT
| flag | qué hace |
|---|
--i <archivo> | exe de entrada a empaquetar |
--o <archivo> | ruta de salida (por defecto: input_packed.exe) |
--vm | cifrado VM |
--c | compresión LZ77 |
--veh | descifrado por fallo de página VEH |
--noconsole | subsistema GUI |