// Tinyload v7.1, MIT license, https://github.com/iamsopotatoe-coder/TinyLoad/ #include #include #include #include #include #include #include #include #include #include using Bytes = std::vector; // stub wrappers static FARPROC g_Real_GetModuleHandleA; static FARPROC g_Real_GetProcAddress; static FARPROC g_Real_ExitProcess; static FARPROC g_Real_VirtualAlloc; static HMODULE WINAPI Stub_GetModuleHandleA(LPCSTR n) { return ((decltype(&GetModuleHandleA))g_Real_GetModuleHandleA)(n); } static FARPROC WINAPI Stub_GetProcAddress(HMODULE m, LPCSTR n) { return ((decltype(&GetProcAddress))g_Real_GetProcAddress)(m, n); } static void WINAPI Stub_ExitProcess(UINT c) { ((decltype(&ExitProcess))g_Real_ExitProcess)(c); } static LPVOID WINAPI Stub_VirtualAlloc(LPVOID a, SIZE_T s, DWORD t, DWORD p) { return ((decltype(&VirtualAlloc))g_Real_VirtualAlloc)(a, s, t, p); } // You will see encrypted strings trougout this code, they are XOR'ed to avoid them being plaintext. decrypted by sdec2() using a positional XOR cipher (buf[i] = enc[i] ^ (key + i)). // Keys are stored per string in StubHook struct StubHook { const BYTE* dll; uint8_t dllLen; const BYTE* name; uint8_t nameLen; uint8_t key; FARPROC* realStore; FARPROC wrapper; }; static const BYTE _ed_k32[] = {0x3A,0x37,0x21,0x3A,0x30,0x3A,0x64,0x6A,0x77,0x3E,0x37,0x30}; static const BYTE _ed_gmha[] = {0x70,0x5D,0x4D,0x77,0x54,0x58,0x48,0x52,0x5A,0x08,0x20,0x2C,0x27,0x28,0x20,0x07}; static const BYTE _ed_gpa[] = {0x06,0x27,0x37,0x14,0x37,0x29,0x24,0x09,0x2D,0x2E,0x39,0x29,0x3E,0x3D}; static const BYTE _ed_ep[] = {0x66,0x5C,0x4C,0x52,0x77,0x5A,0x46,0x49,0x4E,0x5F,0x5E}; static const BYTE _ed_va[] = {0x4F,0x73,0x69,0x68,0x68,0x7F,0x73,0x61,0x4D,0x4E,0x4C,0x47}; static const BYTE _ed_sig[] = {0x02,0x1E,0x16,0x00,0x16,0x1F,0x6A,0x6D}; static StubHook g_hooks[] = { {_ed_k32,12, _ed_gmha,16, 0x37, &g_Real_GetModuleHandleA, (FARPROC)Stub_GetModuleHandleA}, {_ed_k32,12, _ed_gpa, 14, 0x41, &g_Real_GetProcAddress, (FARPROC)Stub_GetProcAddress}, {_ed_k32,12, _ed_ep, 11, 0x23, &g_Real_ExitProcess, (FARPROC)Stub_ExitProcess}, {_ed_k32,12, _ed_va, 12, 0x19, &g_Real_VirtualAlloc, (FARPROC)Stub_VirtualAlloc}, }; static int g_hookCount = sizeof(g_hooks) / sizeof(g_hooks[0]); // pre-decrypted static char _pd_sig[9]; static char _pd_k32[13]; // noise table struct NoiseEnt { const BYTE* enc; size_t n; uint8_t k; }; static NoiseEnt g_noise[] = { {_ed_sig, 8, 0x56}, {_ed_k32, 12, 0x37}, {_ed_gmha, 16, 0x37}, {_ed_gpa, 14, 0x41}, {_ed_ep, 11, 0x23}, {_ed_va, 12, 0x19}, }; // xor decrypt static char* sdec2(char* buf, const BYTE* enc, size_t n, uint8_t k) { for (size_t i = 0; i < n; i++) { buf[i] = enc[i] ^ (uint8_t)(k + i); } buf[n] = 0; return buf; } static void initStrings() { sdec2(_pd_sig, _ed_sig, 8, 0x56); sdec2(_pd_k32, _ed_k32, 12, 0x37); } __attribute__((noinline)) __attribute__((used)) static void noiseDecrypt() { volatile char junk[64]; int n = sizeof(g_noise)/sizeof(g_noise[0]); int idx = (int)(((uintptr_t)&junk * 2654435761u) >> 5) % n; sdec2((char*)junk, g_noise[idx].enc, g_noise[idx].n, g_noise[idx].k); } //dual thread key recombination struct PxTR_Context { DWORD k[4]; // [0,1] from Thread A, [2,3] from Thread B HANDLE fenceEvent; BYTE* target; SIZE_T length; }; #define XXTEA_DELTA 0x9E3779B9 #define XXTEA_MX (((z>>5^y<<2)+(y>>3^z<<4))^((sum^y)+(ctx->k[(p&3)^e]^z))) static void xxteaDecrypt(PxTR_Context* ctx, DWORD* v, int n) { DWORD y = v[0], z, sum = (DWORD)((6 + 52/n) * (uint64_t)XXTEA_DELTA); unsigned p, rounds = 6 + 52/n, e; do { e = (sum >> 2) & 3; for (p = n - 1; p > 0; p--) { z = v[p - 1]; y = v[p] -= XXTEA_MX; } z = v[n - 1]; y = v[0] -= XXTEA_MX; sum -= XXTEA_DELTA; } while (--rounds); } #undef XXTEA_MX // Encrypt key is passed as explicit array #define XXTEA_MX (((z>>5^y<<2)+(y>>3^z<<4))^((sum^y)+(key[(p&3)^e]^z))) static void xxteaEncrypt(DWORD* v, int n, const DWORD key[4]) { DWORD y, z, sum = 0; unsigned p, rounds = 6 + 52/n, e; z = v[n - 1]; do { sum += XXTEA_DELTA; e = (sum >> 2) & 3; for (p = 0; p < n - 1; p++) { y = v[p + 1]; z = v[p] += XXTEA_MX; } y = v[0]; z = v[n - 1] += XXTEA_MX; } while (--rounds); } #undef XXTEA_MX #undef XXTEA_DELTA // Encoded key words __attribute__((section(".pxkey"))) static volatile struct { DWORD guard1; DWORD k0, k1, k2, k3; DWORD guard2; } _pxBlock = { 0xC0DE1337, 0xFEEDF00D, 0xCAFEBABE, 0xDEADBEEF, 0x8BADF00D, 0xB007DEAD }; //james bond reference :D static DWORD _pxRead(int i) { volatile DWORD _keep = _pxBlock.guard2; return ((&_pxBlock.k0)[i]) ^ (_keep & 0); } static DWORD WINAPI PxTR_ThreadA(LPVOID p) { PxTR_Context* ctx = (PxTR_Context*)p; // decode Thread A's key halves DWORD k0 = _pxRead(0) ^ 0xFEEDF00D; DWORD k1 = _pxRead(1) ^ 0xCAFEBABE; WaitForSingleObject(ctx->fenceEvent, INFINITE); // recombine transiently ctx->k[0] = k0; ctx->k[1] = k1; // fall back to XOR for short/remainder SIZE_T nWords = ctx->length / 4; if (nWords >= 2) { xxteaDecrypt(ctx, (DWORD*)ctx->target, (int)nWords); } else if (ctx->length > 0) { DWORD tailKey = k0 ^ k1 ^ ctx->k[2] ^ ctx->k[3]; for (size_t i = 0; i < ctx->length; i++) ctx->target[i] ^= (BYTE)(tailKey >> ((i & 3) * 8)); } // scramble all key material _pxBlock.k0 = 0; _pxBlock.k1 = 0; ctx->k[0] = 0; ctx->k[1] = 0; ctx->k[2] = 0; ctx->k[3] = 0; return 0; } static DWORD WINAPI PxTR_ThreadB(LPVOID p) { PxTR_Context* ctx = (PxTR_Context*)p; ctx->k[2] = _pxRead(2) ^ 0xDEADBEEF; ctx->k[3] = _pxRead(3) ^ 0x8BADF00D; _pxBlock.k2 = 0; _pxBlock.k3 = 0; SetEvent(ctx->fenceEvent); return 0; } __attribute__((noinline)) bool isDebugged() { // PEB checks (x64) BYTE* peb = (BYTE*)__readgsqword(0x60); if (peb) { if (*(peb + 2)) return true; // BeingDebugged if (*(DWORD*)(peb + 0xBC) & 0x70) return true; // NtGlobalFlag } if (IsDebuggerPresent()) return true; BOOL remote = FALSE; CheckRemoteDebuggerPresent(GetCurrentProcess(), &remote); if (remote) return true; return false; } enum { HLT_I, NOP_I, LDI_I, MOV_I, ADD_I, SUB_I, MUL_I, XOR_I, AND_I, OR_I, SHL_I, SHR_I, ROL_I, ROR_I, NOT_I, ADDI_I, XORI_I, ANDI_I, MULI_I, ROLI_I, RORI_I, LDB_I, STB_I, CMP_I, JMP_I, JNZ_I, CALL_I, RET_I, NUM_OPS }; #pragma pack(push, 1) struct Tail { char sig[8]; DWORD origSz; DWORD packSz; BYTE flags; uint64_t dispOff[NUM_OPS]; BYTE subtables[4][8]; DWORD vmCodeSz; uint64_t dispKey; // canary uint32_t canaryOff[8]; BYTE canaryExp[8]; BYTE canaryCnt; // chunk splitting uint32_t chunkOff[4]; uint32_t chunkSz[4]; uint8_t chunkOrder[4]; uint8_t chunkCnt; }; #pragma pack(pop) #define TAIL_SERIALIZED_SZ (sizeof(Tail) + TF_COUNT * 3) // Tail field table for shuffled serialization enum { TF_SIG, TF_ORIGSZ, TF_PACKSZ, TF_FLAGS, TF_DISPOFF, TF_SUBTABLES, TF_VMCODESZ, TF_DISPKEY, TF_CANARYOFF, TF_CANARYEXP, TF_CANARYCNT, TF_CHUNKOFF, TF_CHUNKSZ, TF_CHUNKORDER, TF_CHUNKCNT, TF_COUNT }; struct TailField { uint8_t id; uint16_t off; uint16_t sz; }; static const TailField g_tailFields[TF_COUNT] = { {TF_SIG, offsetof(Tail,sig), sizeof(((Tail*)0)->sig)}, {TF_ORIGSZ, offsetof(Tail,origSz), sizeof(((Tail*)0)->origSz)}, {TF_PACKSZ, offsetof(Tail,packSz), sizeof(((Tail*)0)->packSz)}, {TF_FLAGS, offsetof(Tail,flags), sizeof(((Tail*)0)->flags)}, {TF_DISPOFF, offsetof(Tail,dispOff), sizeof(((Tail*)0)->dispOff)}, {TF_SUBTABLES, offsetof(Tail,subtables), sizeof(((Tail*)0)->subtables)}, {TF_VMCODESZ, offsetof(Tail,vmCodeSz), sizeof(((Tail*)0)->vmCodeSz)}, {TF_DISPKEY, offsetof(Tail,dispKey), sizeof(((Tail*)0)->dispKey)}, {TF_CANARYOFF, offsetof(Tail,canaryOff), sizeof(((Tail*)0)->canaryOff)}, {TF_CANARYEXP, offsetof(Tail,canaryExp), sizeof(((Tail*)0)->canaryExp)}, {TF_CANARYCNT, offsetof(Tail,canaryCnt), sizeof(((Tail*)0)->canaryCnt)}, {TF_CHUNKOFF, offsetof(Tail,chunkOff), sizeof(((Tail*)0)->chunkOff)}, {TF_CHUNKSZ, offsetof(Tail,chunkSz), sizeof(((Tail*)0)->chunkSz)}, {TF_CHUNKORDER, offsetof(Tail,chunkOrder), sizeof(((Tail*)0)->chunkOrder)}, {TF_CHUNKCNT, offsetof(Tail,chunkCnt), sizeof(((Tail*)0)->chunkCnt)}, }; static Bytes serializeTail(const Tail& t, uint32_t seed) { Bytes out; out.reserve(512); int order[TF_COUNT]; for (int i = 0; i < TF_COUNT; i++) order[i] = i; // Fisher Yates uint32_t s = seed; for (int i = TF_COUNT - 1; i > 0; i--) { s = s * 1103515245 + 12345; int j = (int)(((uint64_t)s * (i + 1)) >> 32); int tmp = order[i]; order[i] = order[j]; order[j] = tmp; } for (int fi = 0; fi < TF_COUNT; fi++) { const auto& f = g_tailFields[order[fi]]; out.push_back(f.id); out.push_back(f.sz & 0xFF); out.push_back((f.sz >> 8) & 0xFF); out.insert(out.end(), (BYTE*)&t + f.off, (BYTE*)&t + f.off + f.sz); } return out; } static bool parseTail(const Bytes& data, size_t pos, Tail& t, size_t endPos) { memset(&t, 0, sizeof(t)); int fieldsFound = 0; while (pos + 3 <= endPos && fieldsFound < TF_COUNT) { uint8_t id = data[pos]; uint16_t sz = data[pos + 1] | ((uint16_t)data[pos + 2] << 8); pos += 3; if (pos + sz > endPos) return false; if (id >= TF_COUNT) return false; const auto& f = g_tailFields[id]; if (sz != f.sz) return false; memcpy((BYTE*)&t + f.off, &data[pos], sz); pos += sz; fieldsFound++; } return fieldsFound == TF_COUNT; } // per-subtable keys static void xorOpmap(BYTE* sub, const Tail& t, const BYTE* vmCode, const BYTE* pay) { uint32_t baseH = 0x811C9DC5u; auto feedBase = [&](uint8_t b) { baseH ^= b; baseH *= 0x01000193u; }; for (int i = 0; i < 4; i++) { feedBase((uint8_t)(t.origSz >> (i * 8))); feedBase((uint8_t)(t.packSz >> (i * 8))); feedBase((uint8_t)(t.vmCodeSz >> (i * 8))); } for (int tbl = 0; tbl < 4; tbl++) { uint32_t h = baseH; auto feed = [&](uint8_t b) { h ^= b; h *= 0x01000193u; }; feed((uint8_t)tbl); DWORD vmLim = t.vmCodeSz < 32 ? t.vmCodeSz : 32; DWORD payLim = t.packSz < 32 ? t.packSz : 32; DWORD vmOff = vmLim ? (tbl * 8) % vmLim : 0; DWORD payOff = payLim ? (tbl * 8) % payLim : 0; // Lets clean this up with braces, ok slavik? if (vmCode) { for (DWORD i = 0; i < 8 && (vmOff + i) < vmLim; i++) { feed(vmCode[vmOff + i]); } } else { for (int i = 0; i < 8; i++) { feed((uint8_t)(tbl ^ i)); } } if (pay) { for (DWORD i = 0; i < 8 && (payOff + i) < payLim; i++) { feed(pay[payOff + i]); } } else for (int i = 0; i < 8; i++) { feed((uint8_t)(~tbl ^ i)); } for (int i = 0; i < 8; i++) { feed((uint8_t)i); sub[tbl * 8 + i] ^= (uint8_t)((h >> 24) ^ (h >> 16) ^ (h >> 8) ^ h); } } } // dead code __attribute__((used)) static DWORD dead_crc32(const BYTE* d, size_t n) { DWORD c = 0xFFFFFFFF; for (size_t i = 0; i < n; i++) { c ^= d[i]; for (int j = 0; j < 8; j++) c = (c >> 1) ^ (0xEDB88320 & -(c & 1)); } return ~c; } __attribute__((used)) static bool dead_checkBP() { // alt debug check BYTE* peb = (BYTE*)__readgsqword(0x60); return peb && *(peb + 2); } __attribute__((used)) static void dead_scramble(char* b, size_t n, DWORD s) { for (size_t i = 0; i < n; i++) { s = s * 1103515245 + 12345; b[i] ^= (char)(s >> 16); } } __attribute__((used)) static int dead_strlen_safe(const char* s, int max) { int i = 0; while (s && i < max && s[i]) { i++; } return i; } __attribute__((used)) static bool dead_isPe(const BYTE* d, size_t n) { if (n < 64) { return false; } if (d[0] != 'M' || d[1] != 'Z') { return false; } DWORD pe = *(DWORD*)(d + 0x3C); return pe < n && *(DWORD*)(d + pe) == 0x00004550; } Bytes loadFile(const std::string& p) { std::ifstream f(p, std::ios::binary | std::ios::ate); if (!f) { return {}; } Bytes b((size_t)f.tellg()); f.seekg(0); f.read((char*)b.data(), b.size()); return b; } bool saveFile(const std::string& p, const Bytes& d) { std::ofstream f(p, std::ios::binary); f.write((char*)d.data(), d.size()); return f.good(); } static void writeVarInt(Bytes& out, int v) { while (v >= 0x80) { out.push_back((BYTE)(v | 0x80)); v >>= 7; } out.push_back((BYTE)v); } static int readVarInt(const Bytes& in, size_t& p) { unsigned v = 0; int shift = 0; while (p < in.size() && shift < 28) { BYTE b = in[p++]; v |= (unsigned)(b & 0x7F) << shift; if (!(b & 0x80)) { break; } shift += 7; } return (int)v; } Bytes lzPack(const Bytes& in) { if (in.empty()) { return {0, 0, 0, 0}; } const int WINDOW = 0xFFFF; const int MAXCHAIN = 4096; const int MAXMATCH = 258; const int MINMATCH = 3; const int HSIZE = 1 << 16; std::vector head(HSIZE, -1); std::vector prev(in.size(), -1); auto hash4 = [&](size_t p) -> int { if (p + 2 >= in.size()) { return 0; } unsigned h = in[p]; h = (h * 0x1000193u) ^ in[p+1]; h = (h * 0x1000193u) ^ in[p+2]; if (p + 3 < in.size()) { h = (h * 0x1000193u) ^ in[p + 3]; } return h & (HSIZE - 1); }; auto insert = [&](size_t p) { if (p + 2 >= in.size()) { return; } int h = hash4(p); prev[p] = head[h]; head[h] = (int)p; }; auto findMatch = [&](size_t p, int& ml, int& md) { ml = 0; md = 0; if (p + MINMATCH > in.size()) return; int h = hash4(p); int cur = head[h]; int lo = std::max(0, (int)p - WINDOW); int cap = std::min(MAXMATCH, (int)(in.size() - p)); for (int c = 0; c < MAXCHAIN && cur >= lo; c++) { if (ml > 0 && ((size_t)(cur + ml) >= in.size() || (size_t)(p + ml) >= in.size())) { ml = 0; } if (in[cur] == in[p] && in[cur + ml] == in[p + ml]) { int l = 1; while (l < cap && in[cur + l] == in[p + l]) { l++; } if (l > ml) { ml = l; md = (int)(p - cur); if (l >= cap) { return; } } } cur = prev[cur]; if (cur < 0) { return; } } }; struct Tok { bool match; BYTE lit; int dist, len; }; std::vector toks; toks.reserve(in.size() / 2); size_t pos = 0; while (pos < in.size()) { int ml, md; findMatch(pos, ml, md); if (ml >= MINMATCH) { int bestOff = 0, bestLen = ml, bestDist = md; for (int la = 1; la <= 2 && pos + la + MINMATCH <= in.size(); la++) { int ml2, md2; findMatch(pos + la, ml2, md2); if (ml2 > bestLen + la) { bestOff = la; bestLen = ml2; bestDist = md2; } } for (int j = 0; j < bestOff; j++) { toks.push_back({false, in[pos], 0, 0}); insert(pos); pos++; } toks.push_back({true, 0, bestDist, bestLen}); for (int j = 0; j < bestLen; j++) { insert(pos + j); } pos += bestLen; } else { insert(pos); toks.push_back({false, in[pos], 0, 0}); pos++; } } Bytes out; DWORD sz = (DWORD)in.size(); for (int i = 0; i < 4; i++) out.push_back((sz >> (i * 8)) & 0xFF); size_t ti = 0; while (ti < toks.size()) { BYTE flag = 0; size_t fp = out.size(); out.push_back(0); for (int bit = 0; bit < 8 && ti < toks.size(); bit++, ti++) { auto& t = toks[ti]; if (t.match) { flag |= (1 << bit); writeVarInt(out, t.dist); writeVarInt(out, t.len - MINMATCH); } else { out.push_back(t.lit); } } out[fp] = flag; } return out; } Bytes lzUnpack(const Bytes& in) { if (in.size() < 4) return {}; DWORD sz = in[0] | (in[1] << 8) | (in[2] << 16) | (in[3] << 24); Bytes out; out.reserve(sz); size_t p = 4; while (p < in.size() && out.size() < sz) { BYTE flag = in[p++]; for (int bit = 0; bit < 8 && p < in.size() && out.size() < sz; bit++) { if (flag & (1 << bit)) { if (p >= in.size()) break; int dist = readVarInt(in, p); int len = readVarInt(in, p) + 3; if (dist <= 0 || (size_t)dist > out.size()) return {}; size_t src = out.size() - dist; for (int i = 0; i < len; i++) { out.push_back(out[src + i]); } } else { out.push_back(in[p++]); } } } if (out.size() < sz) return {}; return out; } // packer reads live offsets via this __attribute__((used)) static intptr_t g_off[NUM_OPS]; __attribute__((used)) static intptr_t* g_vmOffPtr = g_off; void vmRun(BYTE* data, uint64_t dataSz, const BYTE* code, size_t codesz, const BYTE* dec, const uint64_t* dispOff, uint64_t dispKey) { // r0=data, r1=sz, r2=i, r3/4=key, r5=const, r6-8=tmp uint64_t r[9] = {}; r[0] = (uint64_t)(uintptr_t)data; r[1] = dataSz; size_t ip = 0; // populate offset table each call if (dispKey) { for (int i = 0; i < NUM_OPS; i++) { g_off[i] = (intptr_t)(dispOff[i] ^ dispKey); } } else { g_off[0] = (intptr_t)(uintptr_t)&&hlt_l - (intptr_t)(uintptr_t)&&hlt_l; g_off[1] = (intptr_t)(uintptr_t)&&nop_l - (intptr_t)(uintptr_t)&&hlt_l; g_off[2] = (intptr_t)(uintptr_t)&&ldi_l - (intptr_t)(uintptr_t)&&hlt_l; g_off[3] = (intptr_t)(uintptr_t)&&mov_l - (intptr_t)(uintptr_t)&&hlt_l; g_off[4] = (intptr_t)(uintptr_t)&&add_l - (intptr_t)(uintptr_t)&&hlt_l; g_off[5] = (intptr_t)(uintptr_t)&&sub_l - (intptr_t)(uintptr_t)&&hlt_l; g_off[6] = (intptr_t)(uintptr_t)&&mul_l - (intptr_t)(uintptr_t)&&hlt_l; g_off[7] = (intptr_t)(uintptr_t)&&xor_l - (intptr_t)(uintptr_t)&&hlt_l; g_off[8] = (intptr_t)(uintptr_t)&&and_l - (intptr_t)(uintptr_t)&&hlt_l; g_off[9] = (intptr_t)(uintptr_t)&&or_l - (intptr_t)(uintptr_t)&&hlt_l; g_off[10] = (intptr_t)(uintptr_t)&&shl_l - (intptr_t)(uintptr_t)&&hlt_l; g_off[11] = (intptr_t)(uintptr_t)&&shr_l - (intptr_t)(uintptr_t)&&hlt_l; g_off[12] = (intptr_t)(uintptr_t)&&rol_l - (intptr_t)(uintptr_t)&&hlt_l; g_off[13] = (intptr_t)(uintptr_t)&&ror_l - (intptr_t)(uintptr_t)&&hlt_l; g_off[14] = (intptr_t)(uintptr_t)&¬_l - (intptr_t)(uintptr_t)&&hlt_l; g_off[15] = (intptr_t)(uintptr_t)&&addi_l - (intptr_t)(uintptr_t)&&hlt_l; g_off[16] = (intptr_t)(uintptr_t)&&xori_l - (intptr_t)(uintptr_t)&&hlt_l; g_off[17] = (intptr_t)(uintptr_t)&&andi_l - (intptr_t)(uintptr_t)&&hlt_l; g_off[18] = (intptr_t)(uintptr_t)&&muli_l - (intptr_t)(uintptr_t)&&hlt_l; g_off[19] = (intptr_t)(uintptr_t)&&roli_l - (intptr_t)(uintptr_t)&&hlt_l; g_off[20] = (intptr_t)(uintptr_t)&&rori_l - (intptr_t)(uintptr_t)&&hlt_l; g_off[21] = (intptr_t)(uintptr_t)&&ldb_l - (intptr_t)(uintptr_t)&&hlt_l; g_off[22] = (intptr_t)(uintptr_t)&&stb_l - (intptr_t)(uintptr_t)&&hlt_l; g_off[23] = (intptr_t)(uintptr_t)&&cmp_l - (intptr_t)(uintptr_t)&&hlt_l; g_off[24] = (intptr_t)(uintptr_t)&&jmp_l - (intptr_t)(uintptr_t)&&hlt_l; g_off[25] = (intptr_t)(uintptr_t)&&jnz_l - (intptr_t)(uintptr_t)&&hlt_l; g_off[26] = (intptr_t)(uintptr_t)&&call_l - (intptr_t)(uintptr_t)&&hlt_l; g_off[27] = (intptr_t)(uintptr_t)&&ret_l - (intptr_t)(uintptr_t)&&hlt_l; } // build dispatch from offsets void* dispatch[NUM_OPS]; { intptr_t base = (intptr_t)(uintptr_t)&&hlt_l; for (int i = 0; i < NUM_OPS; i++) { dispatch[i] = (void*)(base + g_off[i]); } } dispatch:; if (ip >= codesz) return; { static size_t _nc = 0; if ((++_nc & 63) == 0) noiseDecrypt(); uint8_t b = code[ip++]; uint8_t op = dec[((b >> 6) & 3) * 8 + (b & 7)]; if (op >= NUM_OPS) return; goto *dispatch[op]; } hlt_l: return; nop_l: goto dispatch; ldi_l: { if (ip >= codesz) goto dispatch; uint8_t reg = code[ip++]; if (reg >= 9) return; ip += 8; if (ip > codesz) goto dispatch; uint64_t v = 0; for (int i = 0; i < 8; i++) { v |= (uint64_t)code[ip - 8 + i] << (i*8); } r[reg] = v; } goto dispatch; mov_l: { if (ip + 1 >= codesz) goto dispatch; uint8_t d = code[ip++], s = code[ip++]; if (d >= 9 || s >= 9) return; r[d] = r[s]; } goto dispatch; add_l: { if (ip + 1 >= codesz) goto dispatch; uint8_t d = code[ip++], s = code[ip++]; if (d >= 9 || s >= 9) return; r[d] += r[s]; } goto dispatch; sub_l: { if (ip + 1 >= codesz) goto dispatch; uint8_t d = code[ip++], s = code[ip++]; if (d >= 9 || s >= 9) return; r[d] -= r[s]; } goto dispatch; mul_l: { if (ip + 1 >= codesz) goto dispatch; uint8_t d = code[ip++], s = code[ip++]; if (d >= 9 || s >= 9) return; r[d] *= r[s]; } goto dispatch; xor_l: { if (ip + 1 >= codesz) goto dispatch; uint8_t d = code[ip++], s = code[ip++]; if (d >= 9 || s >= 9) return; r[d] ^= r[s]; } goto dispatch; and_l: { if (ip + 1 >= codesz) goto dispatch; uint8_t d = code[ip++], s = code[ip++]; if (d >= 9 || s >= 9) return; r[d] &= r[s]; } goto dispatch; or_l: { if (ip + 1 >= codesz) goto dispatch; uint8_t d = code[ip++], s = code[ip++]; if (d >= 9 || s >= 9) return; r[d] |= r[s]; } goto dispatch; shl_l: { if (ip + 1 >= codesz) goto dispatch; uint8_t reg = code[ip++], n = code[ip++] & 63; if (reg >= 9) return; r[reg] <<= n; } goto dispatch; shr_l: { if (ip + 1 >= codesz) goto dispatch; uint8_t reg = code[ip++], n = code[ip++] & 63; if (reg >= 9) return; r[reg] >>= n; } goto dispatch; rol_l: { if (ip + 1 >= codesz) goto dispatch; uint8_t reg = code[ip++], n = code[ip++] & 63; if (reg >= 9) return; if (n) r[reg] = (r[reg] << n) | (r[reg] >> (64 - n)); } goto dispatch; ror_l: { if (ip + 1 >= codesz) goto dispatch; uint8_t reg = code[ip++], n = code[ip++] & 63; if (reg >= 9) return; if (n) r[reg] = (r[reg] >> n) | (r[reg] << (64 - n)); } goto dispatch; not_l: { if (ip >= codesz) goto dispatch; uint8_t reg = code[ip++]; if (reg >= 9) return; r[reg] = ~r[reg]; } goto dispatch; addi_l: { if (ip >= codesz) goto dispatch; uint8_t reg = code[ip++]; if (reg >= 9) return; ip += 8; if (ip > codesz) goto dispatch; uint64_t v = 0; for (int i = 0; i < 8; i++) v |= (uint64_t)code[ip - 8 + i] << (i*8); r[reg] += v; } goto dispatch; xori_l: { if (ip >= codesz) goto dispatch; uint8_t reg = code[ip++]; if (reg >= 9) return; ip += 8; if (ip > codesz) goto dispatch; uint64_t v = 0; for (int i = 0; i < 8; i++) v |= (uint64_t)code[ip - 8 + i] << (i*8); r[reg] ^= v; } goto dispatch; andi_l: { if (ip >= codesz) goto dispatch; uint8_t reg = code[ip++]; if (reg >= 9) return; ip += 8; if (ip > codesz) goto dispatch; uint64_t v = 0; for (int i = 0; i < 8; i++) v |= (uint64_t)code[ip - 8 + i] << (i*8); r[reg] &= v; } goto dispatch; muli_l: { if (ip >= codesz) goto dispatch; uint8_t reg = code[ip++]; if (reg >= 9) return; ip += 8; if (ip > codesz) goto dispatch; uint64_t v = 0; for (int i = 0; i < 8; i++) v |= (uint64_t)code[ip - 8 + i] << (i*8); r[reg] *= v; } goto dispatch; roli_l: { if (ip + 1 >= codesz) goto dispatch; uint8_t reg = code[ip++], n = code[ip++] & 63; if (reg >= 9) return; if (n) r[reg] = (r[reg] << n) | (r[reg] >> (64 - n)); } goto dispatch; rori_l: { if (ip + 1 >= codesz) goto dispatch; uint8_t reg = code[ip++], n = code[ip++] & 63; if (reg >= 9) return; if (n) r[reg] = (r[reg] >> n) | (r[reg] << (64 - n)); } goto dispatch; ldb_l: { if (ip + 2 >= codesz) goto dispatch; uint8_t d = code[ip++], b = code[ip++], idx = code[ip++]; if (d >= 9 || b >= 9 || idx >= 9) return; if (r[idx] >= r[1]) return; r[d] = ((BYTE*)(uintptr_t)r[b])[r[idx]]; } goto dispatch; stb_l: { if (ip + 2 >= codesz) goto dispatch; uint8_t b = code[ip++], idx = code[ip++], s = code[ip++]; if (b >= 9 || idx >= 9 || s >= 9) return; if (r[idx] >= r[1]) return; ((BYTE*)(uintptr_t)r[b])[r[idx]] = (BYTE)r[s]; } goto dispatch; cmp_l: { if (ip + 2 >= codesz) goto dispatch; uint8_t d = code[ip++], a = code[ip++], b2 = code[ip++]; if (d >= 9 || a >= 9 || b2 >= 9) return; r[d] = r[a] < r[b2] ? 1 : 0; } goto dispatch; jmp_l: { if (ip + 4 > codesz) goto dispatch; int32_t off = 0; memcpy(&off, &code[ip], 4); ip = (size_t)((int64_t)(ip + 4) + off); } goto dispatch; jnz_l: { if (ip >= codesz) goto dispatch; uint8_t reg = code[ip++]; if (reg >= 9) return; if (ip + 4 > codesz) goto dispatch; int32_t off = 0; memcpy(&off, &code[ip], 4); ip += 4; if (r[reg]) ip = (size_t)((int64_t)ip + off); } goto dispatch; call_l: { if (ip + 4 > codesz) goto dispatch; int32_t off = 0; memcpy(&off, &code[ip], 4); r[7] = ip + 4; ip = (size_t)((int64_t)(ip + 4) + off); } goto dispatch; ret_l: { ip = (size_t)r[7]; } goto dispatch; } static void eOp(Bytes& bc, const BYTE* enc, int op) { bc.push_back(enc[op]); } static void eR(Bytes& bc, uint8_t r) { bc.push_back(r); } static void e64(Bytes& bc, uint64_t v) { for (int i = 0; i < 8; i++) bc.push_back((v >> (i*8)) & 0xFF); } static void e32(Bytes& bc, int32_t v) { uint32_t u = (uint32_t)v; for (int i = 0; i < 4; i++) bc.push_back((u >> (i*8)) & 0xFF); } Bytes makeVmProgram(const BYTE* enc, uint64_t key1, uint64_t key2, const uint32_t* canOff, const BYTE* canExp, BYTE canCnt) { Bytes bc; eOp(bc,enc,LDI_I); eR(bc,2); e64(bc,0); eOp(bc,enc,LDI_I); eR(bc,3); e64(bc,key1); eOp(bc,enc,LDI_I); eR(bc,4); e64(bc,key2); eOp(bc,enc,LDI_I); eR(bc,5); e64(bc,0x9E3779B97F4A7C15ull); // φ eOp(bc,enc,NOP_I); // junk eOp(bc,enc,MOV_I); eR(bc,6); eR(bc,6); // junk // coffee before cream eOp(bc,enc,LDI_I); eR(bc,6); e64(bc,0xCAFE); eOp(bc,enc,LDI_I); eR(bc,7); e64(bc,0xCAFF); eOp(bc,enc,CMP_I); eR(bc,8); eR(bc,6); eR(bc,7); eOp(bc,enc,JNZ_I); eR(bc,8); int opqPatch = (int)bc.size(); e32(bc, 0); // Jumps over if true eOp(bc,enc,HLT_I); // Trap if jumped int opqEnd = (int)bc.size(); { int32_t off = opqEnd - (opqPatch + 4); memcpy(&bc[opqPatch], &off, 4); } // xor self -> 0 eOp(bc,enc,LDI_I); eR(bc,6); e64(bc,0xDEADBEEFCAFEBABEull); eOp(bc,enc,XOR_I); eR(bc,6); eR(bc,6); eOp(bc,enc,LDI_I); eR(bc,7); e64(bc,1); eOp(bc,enc,CMP_I); eR(bc,8); eR(bc,6); eR(bc,7); eOp(bc,enc,JNZ_I); eR(bc,8); int opq2Patch = (int)bc.size(); e32(bc, 0); eOp(bc,enc,HLT_I); int opq2End = (int)bc.size(); { int32_t off = opq2End - (opq2Patch + 4); memcpy(&bc[opq2Patch], &off, 4); } // 0x1337 < 0xBEEF eOp(bc,enc,LDI_I); eR(bc,6); e64(bc,0x1337); eOp(bc,enc,LDI_I); eR(bc,7); e64(bc,0xBEEF); eOp(bc,enc,CMP_I); eR(bc,8); eR(bc,6); eR(bc,7); eOp(bc,enc,JNZ_I); eR(bc,8); int opq3Patch = (int)bc.size(); e32(bc, 0); eOp(bc,enc,HLT_I); int opq3End = (int)bc.size(); { int32_t off = opq3End - (opq3Patch + 4); memcpy(&bc[opq3Patch], &off, 4); } // opaque eOp(bc,enc,MOV_I); eR(bc,6); eR(bc,3); eOp(bc,enc,XOR_I); eR(bc,6); eR(bc,3); // r6 = 0 eOp(bc,enc,CMP_I); eR(bc,6); eR(bc,6); eR(bc,5); // r6 = (0 < 1) = 1 eOp(bc,enc,JNZ_I); eR(bc,6); int opq5Patch = (int)bc.size(); e32(bc,0); eOp(bc,enc,HLT_I); // trap if zero (never) int opq5End = (int)bc.size(); { int32_t o = opq5End - (opq5Patch + 4); memcpy(&bc[opq5Patch], &o, 4); } // opaque eOp(bc,enc,ANDI_I); eR(bc,8); e64(bc,1); // r8 = (junk) & 1 eOp(bc,enc,CMP_I); eR(bc,6); eR(bc,5); eR(bc,8); // r6 = (1 < r8), always 0 eOp(bc,enc,JNZ_I); eR(bc,6); // never jumps int opq6Patch = (int)bc.size(); e32(bc,0); { // dead block eOp(bc,enc,ROL_I); eR(bc,8); eR(bc,8); eOp(bc,enc,MULI_I); eR(bc,8); e64(bc,0x517CC1B7); eOp(bc,enc,NOP_I); } int opq6End = (int)bc.size(); { int32_t o = opq6End - (opq6Patch + 4); memcpy(&bc[opq6Patch], &o, 4); } int loopPos = (int)bc.size(); eOp(bc,enc,CMP_I); eR(bc,7); eR(bc,2); eR(bc,1); eOp(bc,enc,JNZ_I); eR(bc,7); int jnzPatch = (int)bc.size(); e32(bc, 0); // JMP to canary block (patched later) eOp(bc,enc,JMP_I); int exitPatch = (int)bc.size(); e32(bc, 0); int bodyPos = (int)bc.size(); { int32_t off = bodyPos - (jnzPatch + 4); memcpy(&bc[jnzPatch], &off, 4); } // 0 < ~0 eOp(bc,enc,LDI_I); eR(bc,6); e64(bc,0); eOp(bc,enc,LDI_I); eR(bc,7); e64(bc,0xFFFFFFFFFFFFFFFFull); eOp(bc,enc,CMP_I); eR(bc,8); eR(bc,6); eR(bc,7); eOp(bc,enc,JNZ_I); eR(bc,8); int opq4Patch = (int)bc.size(); e32(bc, 0); eOp(bc,enc,HLT_I); int opq4End = (int)bc.size(); { int32_t off = opq4End - (opq4Patch + 4); memcpy(&bc[opq4Patch], &off, 4); } // keystream eOp(bc,enc,MOV_I); eR(bc,6); eR(bc,3); eOp(bc,enc,XOR_I); eR(bc,6); eR(bc,4); eOp(bc,enc,ADD_I); eR(bc,6); eR(bc,5); eOp(bc,enc,ANDI_I); eR(bc,6); e64(bc,0xFF); eOp(bc,enc,NOP_I); // junk eOp(bc,enc,LDB_I); eR(bc,8); eR(bc,0); eR(bc,2); eOp(bc,enc,XOR_I); eR(bc,8); eR(bc,6); eOp(bc,enc,NOT_I); eR(bc,8); eOp(bc,enc,STB_I); eR(bc,0); eR(bc,2); eR(bc,8); eOp(bc,enc,MOV_I); eR(bc,8); eR(bc,8); // junk // key mixing eOp(bc,enc,MOV_I); eR(bc,6); eR(bc,3); eOp(bc,enc,ROL_I); eR(bc,6); eR(bc,11); eOp(bc,enc,XOR_I); eR(bc,6); eR(bc,4); eOp(bc,enc,MOV_I); eR(bc,7); eR(bc,6); eOp(bc,enc,ROL_I); eR(bc,8); eR(bc,0); // junk eOp(bc,enc,MOV_I); eR(bc,6); eR(bc,4); eOp(bc,enc,ROR_I); eR(bc,6); eR(bc,19); eOp(bc,enc,ADD_I); eR(bc,6); eR(bc,3); eOp(bc,enc,ADD_I); eR(bc,6); eR(bc,5); eOp(bc,enc,MOV_I); eR(bc,4); eR(bc,6); eOp(bc,enc,MULI_I); eR(bc,5); e64(bc,0x9E3779B97F4A7C15ull); eOp(bc,enc,XOR_I); eR(bc,5); eR(bc,7); eOp(bc,enc,ADDI_I); eR(bc,6); e64(bc,0); // junk eOp(bc,enc,MOV_I); eR(bc,3); eR(bc,7); eOp(bc,enc,ADDI_I); eR(bc,2); e64(bc,1); eOp(bc,enc,JMP_I); int32_t back = loopPos - ((int)bc.size() + 4); e32(bc, back); // patch exit jump to here int canaryStart = (int)bc.size(); { int32_t off = canaryStart - (exitPatch + 4); memcpy(&bc[exitPatch], &off, 4); } // canary if (canCnt > 0) { int n = canCnt < 8 ? canCnt : 8; uint32_t minOff = canOff[0]; for (int c = 1; c < n; c++) { if (canOff[c] < minOff) { minOff = canOff[c]; } } // r6=mask, r7=prev_actual, r5=const_1 eOp(bc,enc,LDI_I); eR(bc,6); e64(bc,0); // mask=0 eOp(bc,enc,LDI_I); eR(bc,7); e64(bc,0); // prev=0 eOp(bc,enc,LDI_I); eR(bc,5); e64(bc,1); // const 1 for (int c = 0; c < n; c++) { eOp(bc,enc,LDI_I); eR(bc,2); e64(bc, canOff[c]); eOp(bc,enc,LDB_I); eR(bc,8); eR(bc,0); eR(bc,2); // r8=actual eOp(bc,enc,LDI_I); eR(bc,3); e64(bc, canExp[c]); eOp(bc,enc,XOR_I); eR(bc,3); eR(bc,7); // r3=canExp^prev eOp(bc,enc,MOV_I); eR(bc,4); eR(bc,8); eOp(bc,enc,XOR_I); eR(bc,4); eR(bc,3); // r4=actual^expected eOp(bc,enc,CMP_I); eR(bc,4); eR(bc,4); eR(bc,5); // r4=(diff<1) eOp(bc,enc,JNZ_I); eR(bc,4); int okPatch = (int)bc.size(); e32(bc,0); eOp(bc,enc,LDI_I); eR(bc,2); e64(bc, (uint64_t)1 << c); eOp(bc,enc,OR_I); eR(bc,6); eR(bc,2); // mask|=bit int okEnd = (int)bc.size(); { int32_t o = okEnd - (okPatch + 4); memcpy(&bc[okPatch], &o, 4); } eOp(bc,enc,MOV_I); eR(bc,7); eR(bc,8); // prev=actual } // apply mask from minOff eOp(bc,enc,LDI_I); eR(bc,2); e64(bc, minOff); int applyLoop = (int)bc.size(); eOp(bc,enc,CMP_I); eR(bc,3); eR(bc,2); eR(bc,1); // r3=(j> 53)) ^ k2; uint64_t nk2 = ((k2 >> 19) | (k2 << 45)) + k1 + k3; k1 = nk1; k2 = nk2; k3 = (k3 * 0x9E3779B97F4A7C15ull) ^ k1; } } // pe loader stages typedef int (*PEStageFn)(); #define FLAG_VEH 4 #define FLAG_CHUNK 8 static struct { const Bytes* data; void* base; IMAGE_DOS_HEADER* dos; IMAGE_NT_HEADERS64* nt; size_t delta; bool veh; } g_pe; static int sp_hdr() { if (g_pe.data->size() < sizeof(IMAGE_DOS_HEADER)) return -2; g_pe.dos = (IMAGE_DOS_HEADER*)g_pe.data->data(); if (g_pe.dos->e_magic != IMAGE_DOS_SIGNATURE) return -2; if (g_pe.dos->e_lfanew <= 0 || (DWORD)g_pe.dos->e_lfanew > g_pe.data->size() - sizeof(IMAGE_NT_HEADERS64)) return -2; g_pe.nt = (IMAGE_NT_HEADERS64*)(g_pe.data->data() + g_pe.dos->e_lfanew); if (g_pe.nt->Signature != IMAGE_NT_SIGNATURE) return -2; if (g_pe.nt->FileHeader.Machine != IMAGE_FILE_MACHINE_AMD64) return -2; if (g_pe.nt->FileHeader.NumberOfSections > 96 || g_pe.nt->FileHeader.NumberOfSections == 0) return -2; DWORD sio = g_pe.nt->OptionalHeader.SizeOfImage; if (sio > 0x40000000 || sio == 0 || sio < g_pe.nt->OptionalHeader.SizeOfHeaders) return -2; if (g_pe.nt->OptionalHeader.SizeOfHeaders > g_pe.data->size()) return -2; return 1; } static int sp_map() { g_pe.base = VirtualAlloc(NULL, g_pe.nt->OptionalHeader.SizeOfImage, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE); if (!g_pe.base) return -2; size_t hdrSize = g_pe.nt->OptionalHeader.SizeOfHeaders; if (hdrSize > g_pe.data->size()) { hdrSize = g_pe.data->size(); } memcpy(g_pe.base, g_pe.data->data(), hdrSize); IMAGE_SECTION_HEADER* sect = IMAGE_FIRST_SECTION(g_pe.nt); int ns = g_pe.nt->FileHeader.NumberOfSections; for (int i = 0; i < ns; i++) { if (sect[i].SizeOfRawData == 0) continue; DWORD srcOff = sect[i].PointerToRawData; DWORD dstOff = sect[i].VirtualAddress; DWORD sz = sect[i].SizeOfRawData; if (srcOff + sz < srcOff) continue; // overflow if (dstOff + sz < dstOff) continue; if (srcOff + sz > g_pe.data->size()) continue; if (dstOff + sz > g_pe.nt->OptionalHeader.SizeOfImage) continue; memcpy((BYTE*)g_pe.base + dstOff, g_pe.data->data() + srcOff, sz); } return 2; } static int sp_reloc() { g_pe.delta = (size_t)g_pe.base - g_pe.nt->OptionalHeader.ImageBase; if (g_pe.delta != 0) { auto* relDir = &g_pe.nt->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC]; if (relDir->Size > 0 && relDir->VirtualAddress < g_pe.nt->OptionalHeader.SizeOfImage) { auto* rel = (IMAGE_BASE_RELOCATION*)((BYTE*)g_pe.base + relDir->VirtualAddress); BYTE* relEnd = (BYTE*)g_pe.base + relDir->VirtualAddress + relDir->Size; while ((BYTE*)rel + sizeof(IMAGE_BASE_RELOCATION) <= relEnd && rel->VirtualAddress > 0) { if (rel->SizeOfBlock < sizeof(IMAGE_BASE_RELOCATION)) break; DWORD count = (rel->SizeOfBlock - sizeof(IMAGE_BASE_RELOCATION)) / sizeof(WORD); WORD* list = (WORD*)(rel + 1); for (DWORD i = 0; i < count; i++) { WORD type = list[i] >> 12; if (type == IMAGE_REL_BASED_DIR64) { size_t target = (size_t)rel->VirtualAddress + (list[i] & 0xFFF); if (target + sizeof(size_t) <= g_pe.nt->OptionalHeader.SizeOfImage) { size_t* p = (size_t*)((BYTE*)g_pe.base + target); *p += g_pe.delta; } } else if (type != IMAGE_REL_BASED_ABSOLUTE) { return -2; } } rel = (IMAGE_BASE_RELOCATION*)((BYTE*)rel + rel->SizeOfBlock); } } } return 3; } static int sp_import() { auto* impDir = &g_pe.nt->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT]; DWORD imgSz = g_pe.nt->OptionalHeader.SizeOfImage; if (impDir->Size > 0 && impDir->VirtualAddress < imgSz) { auto* imp = (IMAGE_IMPORT_DESCRIPTOR*)((BYTE*)g_pe.base + impDir->VirtualAddress); BYTE* impEnd = (BYTE*)g_pe.base + impDir->VirtualAddress + impDir->Size; int dllCount = 0; while ((BYTE*)(imp + 1) <= impEnd && imp->Name && dllCount < 256) { dllCount++; if (imp->Name >= imgSz) { imp++; continue; } HMODULE mod = LoadLibraryA((char*)((BYTE*)g_pe.base + imp->Name)); if (mod) { if (imp->FirstThunk >= imgSz) { imp++; continue; } auto* thunk = (IMAGE_THUNK_DATA64*)((BYTE*)g_pe.base + imp->FirstThunk); DWORD oth = imp->OriginalFirstThunk ? imp->OriginalFirstThunk : imp->FirstThunk; if (oth >= imgSz) { imp++; continue; } auto* orig = (IMAGE_THUNK_DATA64*)((BYTE*)g_pe.base + oth); int impLimit = 10000; while (orig->u1.AddressOfData && impLimit-- > 0) { if (IMAGE_SNAP_BY_ORDINAL64(orig->u1.Ordinal)) { thunk->u1.Function = (size_t)GetProcAddress(mod, (char*)(orig->u1.Ordinal & 0xFFFF)); } else { auto* name = (IMAGE_IMPORT_BY_NAME*)((BYTE*)g_pe.base + orig->u1.AddressOfData); FARPROC real = GetProcAddress(mod, name->Name); const char* dllName = (const char*)((BYTE*)g_pe.base + imp->Name); FARPROC hook = nullptr; char dbuf[32], nbuf[64]; for (int h = 0; h < g_hookCount; h++) { sdec2(dbuf, g_hooks[h].dll, g_hooks[h].dllLen, g_hooks[h].key); sdec2(nbuf, g_hooks[h].name, g_hooks[h].nameLen, g_hooks[h].key); if (!_stricmp(dllName, dbuf) && !strcmp(name->Name, nbuf)) { *g_hooks[h].realStore = real; hook = g_hooks[h].wrapper; break; } } thunk->u1.Function = hook ? (size_t)hook : (size_t)real; } thunk++; orig++; } if (impLimit <= 0 && orig->u1.AddressOfData) return -2; imp->OriginalFirstThunk = 0; imp->Name = 0; } imp++; } g_pe.nt->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress = 0; g_pe.nt->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].Size = 0; } return 4; } static int sp_go() { using EntryPoint = void(WINAPI*)(); EntryPoint entry = (EntryPoint)((BYTE*)g_pe.base + g_pe.nt->OptionalHeader.AddressOfEntryPoint); entry(); return -1; } // VEH static CRITICAL_SECTION g_vehLock; static uintptr_t g_vehBase; static size_t g_vehSize; static uint64_t g_vehKey; static HANDLE g_vehWatchdog; static volatile LONG g_vehStop; static PVOID g_vehHandle; struct VehPage { uintptr_t addr; DWORD lastTick; }; static VehPage g_vehCache[256]; static int g_vehCacheCnt; struct PageRange { DWORD pgStart; DWORD pgEnd; DWORD origProt; }; static PageRange g_vehProt[96]; static int g_vehProtCnt; static DWORD sectToProt(DWORD chars) { // map section characteristics to PAGE_* constant bool ex = (chars & 0x20000000) != 0; bool rd = (chars & 0x40000000) != 0; bool wr = (chars & 0x80000000) != 0; if (ex && rd && wr) return PAGE_EXECUTE_READWRITE; if (ex && rd) return PAGE_EXECUTE_READ; if (ex && wr) return PAGE_EXECUTE_READWRITE; if (ex) return PAGE_EXECUTE; if (rd && wr) return PAGE_READWRITE; if (rd) return PAGE_READONLY; if (wr) return PAGE_READWRITE; return PAGE_READONLY; } static DWORD lookupOrigProt(uintptr_t pg) { DWORD num = (DWORD)((pg - g_vehBase) >> 12); for (int i = 0; i < g_vehProtCnt; i++) { if (num >= g_vehProt[i].pgStart && num <= g_vehProt[i].pgEnd) { return g_vehProt[i].origProt; } } return PAGE_EXECUTE_READWRITE; // fallback } static void vehPageXor(BYTE* p, size_t sz, uint64_t k, uintptr_t addr) { uint64_t s = k ^ (addr >> 12); for (size_t i = 0; i < sz; i++) { s = s * 0x9E3779B97F4A7C15ull + 0x517CC1B727220A95ull; p[i] ^= (BYTE)(s >> 32); } } static DWORD WINAPI vehWatchdogProc(LPVOID) { while (!g_vehStop) { Sleep(50); EnterCriticalSection(&g_vehLock); DWORD now = GetTickCount(); for (int i = 0; i < g_vehCacheCnt; ) { if (now - g_vehCache[i].lastTick > 200) { uintptr_t pg = g_vehCache[i].addr; DWORD old; // fence: stop all access, any faulting thread blocks in VEH on our CS VirtualProtect((LPVOID)pg, 0x1000, PAGE_NOACCESS, &old); VirtualProtect((LPVOID)pg, 0x1000, PAGE_READWRITE, &old); vehPageXor((BYTE*)pg, 0x1000, g_vehKey, pg); VirtualProtect((LPVOID)pg, 0x1000, PAGE_NOACCESS, &old); g_vehCache[i] = g_vehCache[--g_vehCacheCnt]; } else { i++; } } LeaveCriticalSection(&g_vehLock); } return 0; } __attribute__((used)) static LONG CALLBACK vehHandler(EXCEPTION_POINTERS* ex) { if (ex->ExceptionRecord->ExceptionCode != EXCEPTION_ACCESS_VIOLATION) { return EXCEPTION_CONTINUE_SEARCH; } uintptr_t fault = (uintptr_t)ex->ExceptionRecord->ExceptionInformation[1]; if (fault < g_vehBase || fault >= g_vehBase + g_vehSize) { return EXCEPTION_CONTINUE_SEARCH; } uintptr_t pg = fault & ~(uintptr_t)0xFFF; EnterCriticalSection(&g_vehLock); // check if already decrypted (race) for (int i = 0; i < g_vehCacheCnt; i++) { if (g_vehCache[i].addr == pg) { g_vehCache[i].lastTick = GetTickCount(); LeaveCriticalSection(&g_vehLock); return EXCEPTION_CONTINUE_EXECUTION; } } DWORD old; VirtualProtect((LPVOID)pg, 0x1000, PAGE_READWRITE, &old); vehPageXor((BYTE*)pg, 0x1000, g_vehKey, pg); VirtualProtect((LPVOID)pg, 0x1000, lookupOrigProt(pg), &old); if (g_vehCacheCnt < 256) { g_vehCache[g_vehCacheCnt].addr = pg; g_vehCache[g_vehCacheCnt].lastTick = GetTickCount(); g_vehCacheCnt++; } else { // evict oldest int oldest = 0; for (int i = 1; i < 256; i++) { if (g_vehCache[i].lastTick < g_vehCache[oldest].lastTick) { oldest = i; } } uintptr_t evict = g_vehCache[oldest].addr; // fence: stop all access, VEH blocked on our CS VirtualProtect((LPVOID)evict, 0x1000, PAGE_NOACCESS, &old); VirtualProtect((LPVOID)evict, 0x1000, PAGE_READWRITE, &old); vehPageXor((BYTE*)evict, 0x1000, g_vehKey, evict); VirtualProtect((LPVOID)evict, 0x1000, PAGE_NOACCESS, &old); g_vehCache[oldest].addr = pg; g_vehCache[oldest].lastTick = GetTickCount(); } LeaveCriticalSection(&g_vehLock); return EXCEPTION_CONTINUE_EXECUTION; } static void vehCleanup() { g_vehStop = 1; if (g_vehWatchdog) { WaitForSingleObject(g_vehWatchdog, 2000); CloseHandle(g_vehWatchdog); g_vehWatchdog = NULL; } RemoveVectoredExceptionHandler(g_vehHandle); DeleteCriticalSection(&g_vehLock); } static int sp_veh() { if (!g_pe.veh) return 5; g_vehBase = (uintptr_t)g_pe.base; g_vehSize = (g_pe.nt->OptionalHeader.SizeOfImage + 0xFFF) & ~0xFFF; std::mt19937_64 rng(GetTickCount64() ^ (uint64_t)(uintptr_t)&rng); g_vehKey = rng(); InitializeCriticalSection(&g_vehLock); DWORD hdrEnd = (g_pe.nt->OptionalHeader.SizeOfHeaders + 0xFFF) & ~0xFFF; g_vehProtCnt = 0; IMAGE_SECTION_HEADER* sect = IMAGE_FIRST_SECTION(g_pe.nt); int ns = g_pe.nt->FileHeader.NumberOfSections; // std::clamp here if (ns < 0) ns = 0; if (ns > 96) ns = 96; for (int i = 0; i < ns; i++) { if (sect[i].SizeOfRawData == 0) continue; DWORD va = sect[i].VirtualAddress; DWORD sz = sect[i].SizeOfRawData; if (va >= g_vehSize) continue; if (va + sz < va || va + sz > g_vehSize) { sz = g_vehSize - va; } DWORD end = (va + sz + 0xFFF) & ~0xFFF; if (end < va) continue; // record protection for this section DWORD prot = sectToProt(sect[i].Characteristics); DWORD ps = va >> 12; DWORD pe = (end - 1) >> 12; if (g_vehProtCnt < 96) { g_vehProt[g_vehProtCnt].pgStart = ps; g_vehProt[g_vehProtCnt].pgEnd = pe; g_vehProt[g_vehProtCnt].origProt = prot; g_vehProtCnt++; } for (DWORD off = va & ~0xFFF; off < end; off += 0x1000) { if (off < hdrEnd) continue; // skip header overlap BYTE* p = (BYTE*)g_pe.base + off; DWORD old; VirtualProtect(p, 0x1000, PAGE_READWRITE, &old); vehPageXor(p, 0x1000, g_vehKey, (uintptr_t)p); VirtualProtect(p, 0x1000, PAGE_NOACCESS, &old); } } g_vehHandle = AddVectoredExceptionHandler(1, vehHandler); g_vehStop = 0; g_vehWatchdog = CreateThread(NULL, 0, vehWatchdogProc, NULL, 0, NULL); return 5; } bool runInMem(const Bytes& data, bool veh) { static PEStageFn stages[] = { sp_hdr, sp_map, sp_reloc, sp_import, sp_veh, sp_go }; g_pe.data = &data; g_pe.base = nullptr; g_pe.veh = veh; int stage = 0; while (stage >= 0) { if (stage >= (int)(sizeof(stages)/sizeof(stages[0]))) return false; noiseDecrypt(); stage = stages[stage](); } if (veh) vehCleanup(); return stage == -1; } // stage dispatch typedef int (*StageFn)(); static struct { char self[MAX_PATH]; Bytes blob; DWORD off; Tail* t; BYTE* vmCodePtr; BYTE* payPtr; Bytes pay; } g_st; static int s_chk() { initStrings(); if (isDebugged()) return -2; noiseDecrypt(); // TODO: direct syscall for NtQueryInformationProcess to bypass IAT hooks GetModuleFileNameA(NULL, g_st.self, MAX_PATH); return 1; } static int s_ld() { noiseDecrypt(); g_st.blob = loadFile(g_st.self); if (g_st.blob.size() < TAIL_SERIALIZED_SZ + 4) return -2; uint64_t sk = 0x9E3779B97F4A7C15ull; for (size_t i = 0x1000; i < g_st.blob.size() && i < 0x2000; i++) { sk = (sk ^ g_st.blob[i]) * 0x9E3779B97F4A7C15ull; } g_st.off = *(DWORD*)&g_st.blob[g_st.blob.size() - 4] ^ (DWORD)(sk & 0xFFFFFFFF); if (g_st.off + TAIL_SERIALIZED_SZ > g_st.blob.size()) return -2; return 2; } static int s_prs() { noiseDecrypt(); static Tail _ts; if (!parseTail(g_st.blob, g_st.off, _ts, g_st.off + TAIL_SERIALIZED_SZ)) return -2; g_st.t = &_ts; // derive key from stub (.text, skip headers) uint64_t stubKey = 0x9E3779B97F4A7C15ull; for (size_t i = 0x1000; i < g_st.blob.size() && i < 0x2000; i++) { stubKey = (stubKey ^ g_st.blob[i]) * 0x9E3779B97F4A7C15ull; } // de XOR metadata fields g_st.t->origSz ^= (DWORD)(stubKey & 0xFFFFFFFF); g_st.t->packSz ^= (DWORD)((stubKey >> 32) & 0xFFFFFFFF); g_st.t->flags ^= (BYTE)((stubKey >> 16) & 0xFF); // de XOR dispKey, canary, vmCodeSz uint64_t realDispKey = g_st.t->dispKey ^ stubKey; g_st.t->vmCodeSz ^= (DWORD)((stubKey >> 8) & 0xFFFFFFFF); g_st.t->canaryCnt ^= (BYTE)(stubKey & 0xFF); for (int si = 0; si < (int)sizeof(g_st.t->canaryOff); si++) { ((BYTE*)g_st.t->canaryOff)[si] ^= (BYTE)(stubKey >> ((si*7)&63)); } for (int si = 0; si < (int)sizeof(g_st.t->canaryExp); si++) { g_st.t->canaryExp[si] ^= (BYTE)(stubKey >> ((si*11)&63)); } // de XOR sig with stubKey char sigBuf[8]; memcpy(sigBuf, g_st.t->sig, 8); for (int si = 0; si < 8; si++) { sigBuf[si] ^= (BYTE)(stubKey >> (si*8)); } if (memcmp(sigBuf, _pd_sig, 8)) return -2; // restore dispKey g_st.t->dispKey = realDispKey; Bytes interleaved; if (g_st.t->flags & FLAG_CHUNK) { // decrypt chunk fields int nChunks = g_st.t->chunkCnt ^ (BYTE)((stubKey >> 24) & 0xFF); if (nChunks < 1 || nChunks > 4) return -2; for (int si = 0; si < nChunks; si++) { g_st.t->chunkOff[si] ^= (DWORD)(stubKey >> ((si*11)&63)); } for (int si = 0; si < nChunks; si++) { g_st.t->chunkSz[si] ^= (DWORD)(stubKey >> ((si*17)&63)); } for (int si = 0; si < nChunks; si++) { g_st.t->chunkOrder[si] ^= (BYTE)(stubKey >> ((si*13)&63)); } // dual thread { BYTE* overlayStart = g_st.blob.data() + g_st.off + TAIL_SERIALIZED_SZ; SIZE_T overlayLen = (SIZE_T)(g_st.blob.size() - 4 - (g_st.off + TAIL_SERIALIZED_SZ)); PxTR_Context pxCtx; pxCtx.target = overlayStart; pxCtx.length = overlayLen; pxCtx.fenceEvent = CreateEventA(NULL, FALSE, FALSE, NULL); if (pxCtx.fenceEvent) { HANDLE hA = CreateThread(NULL, 0, PxTR_ThreadA, &pxCtx, 0, NULL); HANDLE hB = CreateThread(NULL, 0, PxTR_ThreadB, &pxCtx, 0, NULL); if (hA && hB) { WaitForSingleObject(hA, INFINITE); WaitForSingleObject(hB, INFINITE); } if (hA) CloseHandle(hA); if (hB) CloseHandle(hB); CloseHandle(pxCtx.fenceEvent); } } // read chunks from file Bytes chunks[4]; for (int ci = 0; ci < nChunks; ci++) { DWORD off = g_st.t->chunkOff[ci]; DWORD sz = g_st.t->chunkSz[ci]; if (!sz) continue; if (off < g_st.off + TAIL_SERIALIZED_SZ || off + sz > g_st.blob.size() - 4) return -2; chunks[ci].assign(g_st.blob.begin() + off, g_st.blob.begin() + off + sz); } // reassemble in logical order for (int logical = 0; logical < nChunks; logical++) { int phys = -1; for (int j = 0; j < nChunks; j++) { if (g_st.t->chunkOrder[j] == logical) { phys = j; break; } } if (phys < 0) return -2; if (chunks[phys].empty()) continue; interleaved.insert(interleaved.end(), chunks[phys].begin(), chunks[phys].end()); } } else { // old format size_t overlayEnd = g_st.blob.size() - 4; for (size_t i = g_st.off + TAIL_SERIALIZED_SZ; i < overlayEnd; i++) { interleaved.push_back(g_st.blob[i]); } } // de interleave { Bytes deint; for (size_t i = 0, fi = 0; i < interleaved.size(); i++, fi++) { deint.push_back(interleaved[i]); if ((fi % 3) == 2 && i + 1 < interleaved.size()) i++; } interleaved = std::move(deint); } // verify sizes if (interleaved.size() != (size_t)g_st.t->vmCodeSz + (size_t)g_st.t->packSz) return -2; // store in g_st.blob so pointers remain valid // reserialize tail into blob prefix Bytes serTail = serializeTail(*g_st.t, 0); // seed=0 -> identity order g_st.blob.resize(g_st.off + serTail.size()); memcpy(&g_st.blob[g_st.off], serTail.data(), serTail.size()); g_st.blob.insert(g_st.blob.end(), interleaved.begin(), interleaved.end()); if (!parseTail(g_st.blob, g_st.off, _ts, g_st.off + serTail.size())) return -2; g_st.t = &_ts; g_st.vmCodePtr = g_st.blob.data() + g_st.off + serTail.size(); // decrypt VM bytecode for (size_t vi = 0; vi < g_st.t->vmCodeSz; vi++) { g_st.vmCodePtr[vi] ^= (BYTE)(stubKey >> ((vi*3)&63)); } g_st.payPtr = g_st.vmCodePtr + g_st.t->vmCodeSz; g_st.pay = Bytes(g_st.payPtr, g_st.payPtr + g_st.t->packSz); return 3; } static int s_vm() { noiseDecrypt(); if (g_st.t->flags & 2) { xorOpmap((BYTE*)g_st.t->subtables, *g_st.t, g_st.vmCodePtr, g_st.payPtr); vmRun(g_st.pay.data(), g_st.pay.size(), g_st.vmCodePtr, g_st.t->vmCodeSz, (const BYTE*)g_st.t->subtables, g_st.t->dispOff, g_st.t->dispKey); } return 4; } static int s_dc() { noiseDecrypt(); if (g_st.t->flags & 1) { g_st.pay = lzUnpack(g_st.pay); if (g_st.pay.empty()) return -2; } return 5; } static int s_ex() { noiseDecrypt(); return runInMem(g_st.pay, !!(g_st.t->flags & FLAG_VEH)) ? -1 : -2; } bool tryRun() { static StageFn stages[] = { s_chk, s_ld, s_prs, s_vm, s_dc, s_ex }; int stage = 0; while (stage >= 0) { if (stage >= (int)(sizeof(stages)/sizeof(stages[0]))) return false; stage = stages[stage](); } return stage == -1; } struct ResCtx { HANDLE dst; }; BOOL CALLBACK resCbk(HMODULE mod, LPCSTR type, LPSTR name, LONG_PTR ctx) { ResCtx* c = (ResCtx*)ctx; HRSRC res = FindResourceA(mod, name, type); if (!res) return TRUE; HGLOBAL glob = LoadResource(mod, res); UpdateResourceA(c->dst, type, name, MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL), LockResource(glob), SizeofResource(mod, res)); return TRUE; } BOOL CALLBACK resTypeCbk(HMODULE mod, LPSTR type, LONG_PTR ctx) { EnumResourceNamesA(mod, type, resCbk, ctx); return TRUE; } void cloneRes(const std::string& src, const std::string& dst) { // clone all resources HMODULE mod = LoadLibraryExA(src.c_str(), NULL, LOAD_LIBRARY_AS_DATAFILE); if (!mod) return; HANDLE h = BeginUpdateResourceA(dst.c_str(), FALSE); if (h) { ResCtx c = {h}; EnumResourceTypesA(mod, resTypeCbk, (LONG_PTR)&c); EndUpdateResourceA(h, FALSE); } FreeLibrary(mod); } void scrambleSections(Bytes& data) { // evade packer sigs if (data.size() < sizeof(IMAGE_DOS_HEADER)) return; IMAGE_DOS_HEADER* dos = (IMAGE_DOS_HEADER*)data.data(); if (dos->e_magic != IMAGE_DOS_SIGNATURE) return; IMAGE_NT_HEADERS64* nt = (IMAGE_NT_HEADERS64*)(data.data() + dos->e_lfanew); if (nt->Signature != IMAGE_NT_SIGNATURE) return; IMAGE_SECTION_HEADER* sect = IMAGE_FIRST_SECTION(nt); const char* names[] = {".text", ".data", ".rdata", ".bss", ".idata"}; for (int i = 0; i < nt->FileHeader.NumberOfSections; i++) { memset(sect[i].Name, 0, 8); strncpy((char*)sect[i].Name, names[i % 5], 8); } } bool pack(const std::string& in, const std::string& out, bool vm, bool comp, bool veh) { Bytes orig = loadFile(in); if (orig.size() < 2 || orig[0] != 'M' || orig[1] != 'Z') { printf("error: '%s' is not a valid PE file\n", in.c_str()); return false; } printf("input: %zu bytes\n", orig.size()); // validate PE header bounds { DWORD peOff = *(DWORD*)(orig.data() + 0x3C); if (peOff + sizeof(IMAGE_NT_HEADERS64) > orig.size()) { printf("error: corrupted PE header\n"); return false; } auto* nth = (IMAGE_NT_HEADERS64*)(orig.data() + peOff); if (nth->Signature != IMAGE_NT_SIGNATURE) { printf("error: invalid PE signature\n"); return false; } if (nth->FileHeader.NumberOfSections == 0 || nth->FileHeader.NumberOfSections > 96) { printf("error: bad section count\n"); return false; } } // arch check if (orig.size() > 0x3C + 4) { DWORD peOff = *(DWORD*)(orig.data() + 0x3C); if (peOff + 6 <= orig.size()) { WORD machine = *(WORD*)(orig.data() + peOff + 4); if (machine == 0x014C) { printf("error: 32-bit PE not supported\n"); return false; } if (machine != 0x8664) { printf("error: unsupported machine type 0x%04X\n", machine); return false; } } } BYTE flags = 0; Bytes pay = orig; if (comp) { flags |= 1; Bytes packed = lzPack(pay); printf("compressed: %zu -> %zu bytes (%d%%)\n", pay.size(), packed.size(), (int)(100.0 * packed.size() / orig.size())); pay = packed; } BYTE opmap_enc[NUM_OPS] = {}; BYTE subtables[4][8]; Bytes vmCode; uint32_t canOff[8] = {}; BYTE canExp[8] = {}; BYTE canCnt = 0; if (vm) { flags |= 2; std::mt19937 rng((uint32_t)GetTickCount() ^ (uint32_t)(uintptr_t)&rng); // fill subtables with sentinel for (int t = 0; t < 4; t++) { for (int i = 0; i < 8; i++) { subtables[t][i] = 0xFF; } } // scatter 27 ops across 32 slots struct Slot { int t, i; }; Slot slots[32]; int si = 0; for (int t = 0; t < 4; t++) { for (int i = 0; i < 8; i++) { slots[si++] = {t, i}; } } std::shuffle(slots, slots + 32, rng); for (int op = 0; op < NUM_OPS; op++) { subtables[slots[op].t][slots[op].i] = (BYTE)op; } // build enc mapping: real op -> (table<<6)|index for (int t = 0; t < 4; t++) { for (int i = 0; i < 8; i++) { BYTE v = subtables[t][i]; if (v < NUM_OPS) { opmap_enc[v] = (t << 6) | i; } } } LARGE_INTEGER qpc; QueryPerformanceCounter(&qpc); std::mt19937_64 rng2(GetTickCount64() ^ (uint64_t)(uintptr_t)&rng2 ^ qpc.QuadPart); uint64_t key1 = rng2(), key2 = rng2(); // canary corridor { uint32_t nCan = 8; if (pay.size() < nCan * 2) nCan = (uint32_t)pay.size() > 0 ? 1 : 0; if (nCan > 0) { std::mt19937 crng((uint32_t)(key1 ^ (uint64_t)(uintptr_t)&pay)); uint32_t skipHdr = pay.size() > 0x400 ? 0x400 : 0; uint32_t usable = (uint32_t)pay.size() - skipHdr; uint32_t step = usable / (nCan + 1); for (uint32_t c = 0; c < nCan; c++) { uint32_t base = skipHdr + step * (c + 1); uint32_t off = base + (crng() % (step > 0 ? step : 1)); if (off >= pay.size()) { off = (uint32_t)pay.size() - 1; } canOff[c] = off; } std::shuffle(canOff, canOff + nCan, crng); BYTE prev = 0; for (uint32_t c = 0; c < nCan; c++) { BYTE actual = pay[canOff[c]]; canExp[c] = actual ^ prev; prev = actual; } } canCnt = (BYTE)nCan; } vmCode = makeVmProgram(opmap_enc, key1, key2, canOff, canExp, canCnt); vmEncryptPayload(pay, key1, key2); printf("vm encrypted: custom ISA, %zu bytes of bytecode\n", vmCode.size()); } if (veh) { flags |= FLAG_VEH; } char self[MAX_PATH]; GetModuleFileNameA(NULL, self, MAX_PATH); Bytes stub = loadFile(self); if (stub.empty()) { printf("error: cannot read stub from self\n"); return false; } // derive key from stub for overlay encryption (skip headers: use .text) uint64_t stubKey = 0x9E3779B97F4A7C15ull; for (size_t i = 0x1000; i < stub.size() && i < 0x2000; i++) { stubKey = (stubKey ^ stub[i]) * 0x9E3779B97F4A7C15ull; } // encrypt dispatch offsets uint64_t dispKey = 0; uint64_t encOff[NUM_OPS] = {}; { std::mt19937_64 rng3(GetTickCount64() ^ (uint64_t)(uintptr_t)&stub); dispKey = rng3(); // prime vmRun to populate g_vmOffPtr with live label offsets { BYTE d = 0; uint64_t z[28] = {}; vmRun(&d, 0, &d, 0, &d, z, 0); } if (g_vmOffPtr) { for (int j = 0; j < NUM_OPS; j++) { encOff[j] = (uint64_t)g_vmOffPtr[j] ^ dispKey; } } } if (!saveFile(out, stub)) return false; // cloneRes disabled: LoadLibraryExA intermittently locks output file Bytes result = loadFile(out); Tail t; memcpy(t.sig, _pd_sig, 8); t.origSz = (DWORD)orig.size(); t.packSz = (DWORD)pay.size(); t.flags = flags; t.vmCodeSz = (DWORD)vmCode.size(); t.dispKey = dispKey; memcpy(t.dispOff, encOff, sizeof(encOff)); memcpy(t.subtables, subtables, sizeof(subtables)); t.canaryCnt = canCnt; memcpy(t.canaryOff, canOff, sizeof(canOff)); memcpy(t.canaryExp, canExp, sizeof(canExp)); // hide signature with stubKey for (int si = 0; si < 8; si++) t.sig[si] ^= (BYTE)(stubKey >> (si*8)); // scramble subtables xorOpmap((BYTE*)t.subtables, t, vmCode.empty() ? nullptr : vmCode.data(), pay.data()); t.flags |= FLAG_CHUNK; // set before encrypt // encrypt sensitive Tail fields with stub-derived key t.origSz ^= (DWORD)(stubKey & 0xFFFFFFFF); t.packSz ^= (DWORD)((stubKey >> 32) & 0xFFFFFFFF); t.flags ^= (BYTE)((stubKey >> 16) & 0xFF); t.dispKey ^= stubKey; for (int si = 0; si < (int)sizeof(t.canaryOff); si++) ((BYTE*)t.canaryOff)[si] ^= (BYTE)(stubKey >> ((si*7)&63)); for (int si = 0; si < (int)sizeof(t.canaryExp); si++) t.canaryExp[si] ^= (BYTE)(stubKey >> ((si*11)&63)); t.canaryCnt ^= (BYTE)(stubKey & 0xFF); t.vmCodeSz ^= (DWORD)((stubKey >> 8) & 0xFFFFFFFF); // encrypt VM bytecode for (size_t vi = 0; vi < vmCode.size(); vi++) { vmCode[vi] ^= (BYTE)(stubKey >> ((vi*3)&63)); } DWORD tailOff = 0; // build overlay { Bytes payload; if (!vmCode.empty()) { payload.insert(payload.end(), vmCode.begin(), vmCode.end()); } payload.insert(payload.end(), pay.begin(), pay.end()); // interleave payload Bytes interleaved; interleaved.reserve(payload.size() + payload.size() / 3 + 4); for (size_t i = 0; i < payload.size(); i++) { interleaved.push_back(payload[i]); if ((i % 3) == 2) { interleaved.push_back(0x00); } } t.chunkCnt = 4; size_t chunkBase = interleaved.size() / t.chunkCnt; size_t chunkRem = interleaved.size() % t.chunkCnt; uint32_t rawOff[4] = {}, rawSz[4] = {}; uint8_t rawOrder[4] = {0, 1, 2, 3}; std::mt19937 crng((uint32_t)(stubKey ^ result.size())); std::shuffle(rawOrder, rawOrder + 4, crng); // write Tail placeholder tailOff = (DWORD)result.size(); size_t tailPos = result.size(); result.resize(result.size() + TAIL_SERIALIZED_SZ); for (int ci = 0; ci < 4; ci++) { size_t gapSz = 128 + (crng() % 513); for (size_t j = 0; j < gapSz; j++) { result.push_back((BYTE)(crng() & 0xFF)); } rawOff[ci] = (DWORD)result.size(); uint8_t logical = rawOrder[ci]; size_t start = logical * chunkBase + (logical < chunkRem ? logical : chunkRem); size_t end = start + chunkBase + (logical < chunkRem ? 1 : 0); if (start >= interleaved.size()) { rawSz[ci] = 0; continue; } if (end > interleaved.size()) end = interleaved.size(); rawSz[ci] = (DWORD)(end - start); result.insert(result.end(), interleaved.begin() + start, interleaved.begin() + end); } for (int ci = 0; ci < 4; ci++) { t.chunkOff[ci] = rawOff[ci]; t.chunkSz[ci] = rawSz[ci]; t.chunkOrder[ci] = rawOrder[ci]; } // generate split 128 bit key and XXTEA encrypt overlay chunks { std::mt19937_64 pxRng(GetTickCount64() ^ (uint64_t)(uintptr_t)&result ^ stubKey); DWORD pxKey[4] = { (DWORD)pxRng(), (DWORD)pxRng(), (DWORD)pxRng(), (DWORD)pxRng() }; size_t overlayStart = tailPos + TAIL_SERIALIZED_SZ; size_t overlayLen = result.size() - overlayStart; size_t nWords = overlayLen / 4; if (nWords >= 2) { xxteaEncrypt((DWORD*)&result[overlayStart], (int)nWords, pxKey); } // XOR remainder (0-3 bytes) DWORD tailKey = pxKey[0] ^ pxKey[1] ^ pxKey[2] ^ pxKey[3]; for (size_t i = nWords * 4; i < overlayLen; i++) { result[overlayStart + i] ^= (BYTE)(tailKey >> ((i & 3) * 8)); } // Patch 4 encoded key words into stub guard block DWORD enc[4] = { pxKey[0] ^ 0xFEEDF00D, pxKey[1] ^ 0xCAFEBABE, pxKey[2] ^ 0xDEADBEEF, pxKey[3] ^ 0x8BADF00D }; DWORD guard1 = 0xC0DE1337, guard2 = 0xB007DEAD; for (size_t i = 0; i + 24 <= tailPos; i++) { if (*(DWORD*)&result[i] == guard1 && *(DWORD*)&result[i + 20] == guard2) { for (int j = 0; j < 4; j++) *(DWORD*)&result[i + 4 + j * 4] = enc[j]; break; } } } // encrypt chunk fields for (int si = 0; si < 4; si++) { t.chunkOff[si] ^= (DWORD)(stubKey >> ((si*11)&63)); } for (int si = 0; si < 4; si++) { t.chunkSz[si] ^= (DWORD)(stubKey >> ((si*17)&63)); } for (int si = 0; si < 4; si++) { t.chunkOrder[si] ^= (BYTE)(stubKey >> ((si*13)&63)); } t.chunkCnt ^= (BYTE)((stubKey >> 24) & 0xFF); Bytes serTail = serializeTail(t, (uint32_t)(stubKey ^ result.size())); memcpy(&result[tailPos], serTail.data(), serTail.size()); } // encrypt tail offset DWORD encTailOff = tailOff ^ (DWORD)(stubKey & 0xFFFFFFFF); result.push_back(encTailOff & 0xFF); result.push_back((encTailOff >> 8) & 0xFF); result.push_back((encTailOff >> 16) & 0xFF); result.push_back((encTailOff >> 24) & 0xFF); scrambleSections(result); if (!saveFile(out, result)) return false; printf("-> %s (%zu bytes)\n", out.c_str(), result.size()); return true; } int main(int argc, char* argv[]) { if (tryRun()) return 0; std::string in, out; bool vm = false, comp = false, veh = false; for (int i = 1; i < argc; i++) { std::string a = argv[i]; if (a == "--i" && i + 1 < argc) { in = argv[++i]; if (in.size() < 4 || _stricmp(in.c_str() + in.size() - 4, ".exe")) in += ".exe"; } else if (a == "--o" && i + 1 < argc) { out = argv[++i]; if (out.size() < 4 || _stricmp(out.c_str() + out.size() - 4, ".exe")) out += ".exe"; } else if (a == "--vm") vm = true; else if (a == "--c") comp = true; else if (a == "--veh") veh = true; } if (in.empty()) { // help strings XOR'ed static const BYTE _eh[] = { 0x09,0x37,0x31,0x19,0x2D,0x0D,0x02,0x00,0x45,0x10,0x50,0x46,0x59,0x4A,0x46,0x4C, 0x3D,0x2B,0x4F,0x00,0x10,0x11,0x18,0x11,0x07,0x59,0x14,0x0A,0x00,0x0A,0x0F,0x19, 0x0F,0x73,0x75,0xF5,0xF2,0xE3,0xE4,0xE1,0xBF,0xA6,0xD3,0xE1,0xE7,0xF3,0xC7,0xE3, 0xEC,0xEA,0xA1,0xF5,0xE9,0xF7,0xB3,0xB9,0xB8,0xFF,0xB7,0xA4,0xFF,0xF3,0xF7,0xF9, 0xA3,0xBE,0xC4,0xCF,0xD1,0xD6,0xCA,0xCB,0xCB,0xD5,0xFA,0xA5,0xA3,0x8A,0x8B,0x81, 0x80,0xC7,0x8F,0x8C,0xD7,0xDB,0xDF,0xD1,0x8B,0x96,0x97,0x98,0x99,0xD3,0xD5,0xCC, 0xC8,0xCA,0x9F,0xA5,0xB9,0xA7,0xE3,0xB0,0xAA,0xE6,0xB7,0xA9,0xAA,0xA1,0xC6,0xC6, 0xED,0xEE,0xE2,0xFD,0xBE,0xF2,0xEF,0xB2,0xBC,0xBA,0xB2,0xE6,0xF9,0xFA,0xFB,0xFC, 0xB2,0xAB,0xAB,0x90,0x94,0x96,0xC3,0x94,0x84,0x92,0x8F,0xC8,0xC1,0x8E,0x8E,0x8A, 0x8C,0x9B,0x83,0x84,0xCB,0xD2,0x9A,0x9A,0x85,0x83,0x83,0xA7,0x89,0x9B,0x98,0x97, 0x98,0x9A,0xD1,0x65,0x79,0x67,0x2A,0x09,0x0F,0x26,0x27,0x25,0x24,0x7C,0x66,0x2C, 0x2D,0x2E,0x2F,0x30,0x31,0x32,0x33,0x34,0x35,0x40,0x5A,0x38,0x7C,0x74,0x78,0x6E, 0x64,0x6E,0x6B,0x49,0x4E,0x4C,0x2E,0x2E,0x05,0x06,0x0A,0x05,0x4A,0x0A,0x0B,0x0C, 0x0D,0x0E,0x0F,0x10,0x11,0x12,0x13,0x14,0x79,0x6C,0x00,0x0F,0x19,0x59,0x54,0x51, 0x4D,0x4C,0x5A,0x33,0x32,0x2B,0x2C,0x2A,0x48,0x4C,0x67,0x68,0x64,0x67,0x3D,0x29, 0x25,0x6E,0x6F,0x70,0x71,0x72,0x73,0x74,0x75,0x76,0x01,0x1D,0x11,0x7A,0x2B,0x3D, 0x3A,0x3B,0x72,0x06,0x00,0x17,0x0F,0x10,0x45,0x02,0x02,0x0B,0x1B,0x13,0x1B,0x18, 0x04,0x01,0x01,0x7D,0x7B }; char buf[512]; uint8_t k = 0x5D; for (int i = 0; i < (int)sizeof(_eh); i++) { buf[i] = _eh[i] ^ (uint8_t)(k + i); } buf[sizeof(_eh)] = 0; puts(buf); return 1; } if (out.empty()) { auto d = in.rfind('.'); out = d != std::string::npos ? in.substr(0, d) + "_packed" + in.substr(d) : in + "_packed.exe"; } if (!vm && !comp && !veh) { static const BYTE _en[] = { 0x48,0x21,0x33,0x2C,0x2A,0x2A,0x22,0x7C,0x67,0x26,0x26,0x6A,0x2D,0x20,0x2C,0x29, 0x3C,0x70,0x22,0x22,0x36,0x37,0x3C,0x30,0x3E,0x3D,0x3D,0x57,0x51 }; char b2[32]; uint8_t k2 = 0x3F; for (int i = 0; i < (int)sizeof(_en); i++) { b2[i] = _en[i] ^ (uint8_t)(k2 + i); } b2[sizeof(_en)] = 0; puts(b2); return 1; } return pack(in, out, vm, comp, veh) ? 0 : 1; }