// Tinyload v5.0, 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); } // API strings are XOR-encrypted to avoid them appearing in plaintext // in the packed binary's string table. decrypted at runtime by sdec2(). // key is per-string, rolling XOR: buf[i] = enc[i] ^ (key + i) // // _ed_k32 = "kernel32.dll" // _ed_gmha = "GetModuleHandleA" // _ed_gpa = "GetProcAddress" // _ed_ep = "ExitProcess" // _ed_va = "VirtualAlloc" // _ed_sig = "TINYLD50" 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,0x69,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]); // 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; } bool isDebugged() { // go away windbg if (IsDebuggerPresent()) return true; BOOL remote = FALSE; CheckRemoteDebuggerPresent(GetCurrentProcess(), &remote); if (remote) return true; return false; } #pragma pack(push, 1) struct Tail { char sig[8]; DWORD origSz; DWORD packSz; BYTE flags; BYTE opmap[32]; DWORD vmCodeSz; }; #pragma pack(pop) 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 }; // per-file opmap key static void xorOpmap(BYTE* opmap, const Tail& t, const BYTE* vmCode, const BYTE* pay) { uint32_t h = 0x811C9DC5u; auto feed = [&](uint8_t b) { h ^= b; h *= 0x01000193u; }; for (int i = 0; i < 4; i++) { feed((uint8_t)(t.origSz >> (i * 8))); feed((uint8_t)(t.packSz >> (i * 8))); feed((uint8_t)(t.vmCodeSz >> (i * 8))); } DWORD vmLim = t.vmCodeSz < 32 ? t.vmCodeSz : 32; if (vmCode) for (DWORD i = 0; i < vmLim; i++) feed(vmCode[i]); DWORD payLim = t.packSz < 32 ? t.packSz : 32; if (pay) for (DWORD i = 0; i < payLim; i++) feed(pay[i]); for (int i = 0; i < NUM_OPS; i++) { feed((uint8_t)i); opmap[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(); } Bytes lzPack(const Bytes& in) { if (in.empty()) return {0, 0, 0, 0}; const int WINDOW = 0x10000; const int MAXCHAIN = 512; 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 + 3 >= in.size()) { if (p + 2 >= in.size()) return 0; return ((in[p] * 0x1000193u) ^ (in[p+1] * 0x100) ^ in[p+2]) & (HSIZE - 1); } unsigned h = in[p]; h = (h * 0x1000193u) ^ in[p+1]; h = (h * 0x1000193u) ^ in[p+2]; 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 (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) { insert(pos); // lazy matching: try 1 byte ahead, pick the better match if (pos + 1 + MINMATCH <= in.size()) { int ml2, md2; findMatch(pos + 1, ml2, md2); if (ml2 > ml + 1) { toks.push_back({false, in[pos], 0, 0}); insert(pos + 1); pos++; ml = ml2; md = md2; } } toks.push_back({true, 0, md, ml}); for (int j = 0; j < ml; j++) insert(pos + j); pos += ml; } 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); out.push_back(t.dist & 0xFF); out.push_back((t.dist >> 8) & 0xFF); out.push_back((BYTE)(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 + 2 >= in.size()) break; int dist = in[p] | (in[p + 1] << 8); int len = (int)in[p + 2] + 3; p += 3; if (dist <= 0 || (size_t)dist > out.size()) break; 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++]); } } } return out; } void vmRun(BYTE* data, uint64_t dataSz, const BYTE* code, size_t codesz, const BYTE* dec) { // 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; while (ip < codesz) { uint8_t op = dec[code[ip++]]; if (ip > codesz) break; switch (op) { case HLT_I: return; case NOP_I: break; case LDI_I: { if (ip >= codesz) break; uint8_t reg = code[ip++]; if (reg >= 9) return; ip += 8; if (ip > codesz) break; uint64_t v = 0; for (int i = 0; i < 8; i++) v |= (uint64_t)code[ip - 8 + i] << (i*8); r[reg] = v; break; } case MOV_I: { if (ip + 1 >= codesz) break; uint8_t d = code[ip++], s = code[ip++]; if (d >= 9 || s >= 9) return; r[d] = r[s]; break; } case ADD_I: { if (ip + 1 >= codesz) break; uint8_t d = code[ip++], s = code[ip++]; if (d >= 9 || s >= 9) return; r[d] += r[s]; break; } case SUB_I: { if (ip + 1 >= codesz) break; uint8_t d = code[ip++], s = code[ip++]; if (d >= 9 || s >= 9) return; r[d] -= r[s]; break; } case MUL_I: { if (ip + 1 >= codesz) break; uint8_t d = code[ip++], s = code[ip++]; if (d >= 9 || s >= 9) return; r[d] *= r[s]; break; } case XOR_I: { if (ip + 1 >= codesz) break; uint8_t d = code[ip++], s = code[ip++]; if (d >= 9 || s >= 9) return; r[d] ^= r[s]; break; } case AND_I: { if (ip + 1 >= codesz) break; uint8_t d = code[ip++], s = code[ip++]; if (d >= 9 || s >= 9) return; r[d] &= r[s]; break; } case OR_I: { if (ip + 1 >= codesz) break; uint8_t d = code[ip++], s = code[ip++]; if (d >= 9 || s >= 9) return; r[d] |= r[s]; break; } case SHL_I: { if (ip + 1 >= codesz) break; uint8_t reg = code[ip++], n = code[ip++] & 63; if (reg >= 9) return; r[reg] <<= n; break; } case SHR_I: { if (ip + 1 >= codesz) break; uint8_t reg = code[ip++], n = code[ip++] & 63; if (reg >= 9) return; r[reg] >>= n; break; } case ROL_I: { if (ip + 1 >= codesz) break; uint8_t reg = code[ip++], n = code[ip++] & 63; if (reg >= 9) return; if (n) r[reg] = (r[reg] << n) | (r[reg] >> (64 - n)); break; } case ROR_I: { if (ip + 1 >= codesz) break; uint8_t reg = code[ip++], n = code[ip++] & 63; if (reg >= 9) return; if (n) r[reg] = (r[reg] >> n) | (r[reg] << (64 - n)); break; } case NOT_I: { if (ip >= codesz) break; uint8_t reg = code[ip++]; if (reg >= 9) return; r[reg] = ~r[reg]; break; } case ADDI_I: { if (ip >= codesz) break; uint8_t reg = code[ip++]; if (reg >= 9) return; ip += 8; if (ip > codesz) break; uint64_t v = 0; for (int i = 0; i < 8; i++) v |= (uint64_t)code[ip - 8 + i] << (i*8); r[reg] += v; break; } case XORI_I: { if (ip >= codesz) break; uint8_t reg = code[ip++]; if (reg >= 9) return; ip += 8; if (ip > codesz) break; uint64_t v = 0; for (int i = 0; i < 8; i++) v |= (uint64_t)code[ip - 8 + i] << (i*8); r[reg] ^= v; break; } case ANDI_I: { if (ip >= codesz) break; uint8_t reg = code[ip++]; if (reg >= 9) return; ip += 8; if (ip > codesz) break; uint64_t v = 0; for (int i = 0; i < 8; i++) v |= (uint64_t)code[ip - 8 + i] << (i*8); r[reg] &= v; break; } case MULI_I: { if (ip >= codesz) break; uint8_t reg = code[ip++]; if (reg >= 9) return; ip += 8; if (ip > codesz) break; uint64_t v = 0; for (int i = 0; i < 8; i++) v |= (uint64_t)code[ip - 8 + i] << (i*8); r[reg] *= v; break; } case ROLI_I: { if (ip + 1 >= codesz) break; uint8_t reg = code[ip++], n = code[ip++] & 63; if (reg >= 9) return; if (n) r[reg] = (r[reg] << n) | (r[reg] >> (64 - n)); break; } case RORI_I: { if (ip + 1 >= codesz) break; uint8_t reg = code[ip++], n = code[ip++] & 63; if (reg >= 9) return; if (n) r[reg] = (r[reg] >> n) | (r[reg] << (64 - n)); break; } case LDB_I: { if (ip + 2 >= codesz) break; uint8_t d = code[ip++], b = code[ip++], idx = code[ip++]; if (d >= 9 || b >= 9 || idx >= 9) return; r[d] = ((BYTE*)(uintptr_t)r[b])[r[idx]]; break; } case STB_I: { if (ip + 2 >= codesz) break; uint8_t b = code[ip++], idx = code[ip++], s = code[ip++]; if (b >= 9 || idx >= 9 || s >= 9) return; ((BYTE*)(uintptr_t)r[b])[r[idx]] = (BYTE)r[s]; break; } case CMP_I: { if (ip + 2 >= codesz) break; 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; break; } case JMP_I: { if (ip + 4 > codesz) break; int32_t off = 0; memcpy(&off, &code[ip], 4); ip = (size_t)((int64_t)(ip + 4) + off); break; } case JNZ_I: { if (ip >= codesz) break; uint8_t reg = code[ip++]; if (reg >= 9) return; if (ip + 4 > codesz) break; int32_t off = 0; memcpy(&off, &code[ip], 4); ip += 4; if (r[reg]) ip = (size_t)((int64_t)ip + off); break; } case CALL_I: { if (ip + 4 > codesz) break; int32_t off = 0; memcpy(&off, &code[ip], 4); r[7] = ip + 4; ip = (size_t)((int64_t)(ip + 4) + off); break; } case RET_I: { ip = (size_t)r[7]; break; } } } } 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) { 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); } 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); eOp(bc,enc,HLT_I); 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); return bc; } void vmEncryptPayload(Bytes& pay, uint64_t k1, uint64_t k2) { // stream cipher: xor + NOT (cuz 1 layer is boring) uint64_t k3 = 0x9E3779B97F4A7C15ull; for (size_t i = 0; i < pay.size(); i++) { uint8_t b = pay[i]; uint8_t ks = (uint8_t)(((k1 ^ k2) + k3) & 0xFF); b ^= ks; b = ~b; pay[i] = b; uint64_t nk1 = ((k1 << 11) | (k1 >> 53)) ^ k2; uint64_t nk2 = ((k2 >> 19) | (k2 << 45)) + k1 + k3; k1 = nk1; k2 = nk2; k3 = (k3 * 0x9E3779B97F4A7C15ull) ^ k1; } } bool runInMem(const Bytes& data) { // manual PE loader if (data.size() < sizeof(IMAGE_DOS_HEADER)) return false; IMAGE_DOS_HEADER* dos = (IMAGE_DOS_HEADER*)data.data(); if (dos->e_magic != IMAGE_DOS_SIGNATURE) return false; IMAGE_NT_HEADERS64* nt = (IMAGE_NT_HEADERS64*)(data.data() + dos->e_lfanew); if (nt->Signature != IMAGE_NT_SIGNATURE) return false; void* base = VirtualAlloc(NULL, nt->OptionalHeader.SizeOfImage, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE); if (!base) return false; memcpy(base, data.data(), nt->OptionalHeader.SizeOfHeaders); IMAGE_SECTION_HEADER* sect = IMAGE_FIRST_SECTION(nt); for (int i = 0; i < nt->FileHeader.NumberOfSections; i++) { if (sect[i].SizeOfRawData > 0) memcpy((BYTE*)base + sect[i].VirtualAddress, data.data() + sect[i].PointerToRawData, sect[i].SizeOfRawData); } size_t delta = (size_t)base - nt->OptionalHeader.ImageBase; if (delta != 0) { auto* relDir = &nt->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC]; if (relDir->Size > 0) { auto* rel = (IMAGE_BASE_RELOCATION*)((BYTE*)base + relDir->VirtualAddress); while (rel->VirtualAddress > 0) { DWORD count = (rel->SizeOfBlock - sizeof(IMAGE_BASE_RELOCATION)) / sizeof(WORD); WORD* list = (WORD*)(rel + 1); for (DWORD i = 0; i < count; i++) { if ((list[i] >> 12) == IMAGE_REL_BASED_DIR64) { size_t* p = (size_t*)((BYTE*)base + rel->VirtualAddress + (list[i] & 0xFFF)); *p += delta; } } rel = (IMAGE_BASE_RELOCATION*)((BYTE*)rel + rel->SizeOfBlock); } } } auto* impDir = &nt->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT]; if (impDir->Size > 0) { auto* imp = (IMAGE_IMPORT_DESCRIPTOR*)((BYTE*)base + impDir->VirtualAddress); while (imp->Name) { HMODULE mod = LoadLibraryA((char*)((BYTE*)base + imp->Name)); if (mod) { auto* thunk = (IMAGE_THUNK_DATA64*)((BYTE*)base + imp->FirstThunk); auto* orig = (IMAGE_THUNK_DATA64*)((BYTE*)base + (imp->OriginalFirstThunk ? imp->OriginalFirstThunk : imp->FirstThunk)); while (orig->u1.AddressOfData) { 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*)base + orig->u1.AddressOfData); FARPROC real = GetProcAddress(mod, name->Name); // hook match const char* dllName = (const char*)((BYTE*)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++; } } // kill recovery imp->OriginalFirstThunk = 0; imp->Name = 0; imp++; } nt->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress = 0; nt->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].Size = 0; } using EntryPoint = void(WINAPI*)(); EntryPoint entry = (EntryPoint)((BYTE*)base + nt->OptionalHeader.AddressOfEntryPoint); entry(); return true; } bool tryRun() { // self-extract: find tail, decrypt, run if (isDebugged()) return false; char self[MAX_PATH]; GetModuleFileNameA(NULL, self, MAX_PATH); Bytes blob = loadFile(self); if (blob.size() < sizeof(Tail) + 4) return false; // tail offset -> vmCode -> payload DWORD off = *(DWORD*)&blob[blob.size() - 4]; if (off + sizeof(Tail) > blob.size()) return false; Tail* t = (Tail*)&blob[off]; char sigbuf[9]; if (memcmp(t->sig, sdec2(sigbuf, _ed_sig, 8, 0x56), 8)) return false; if ((uint64_t)off + sizeof(Tail) + (uint64_t)t->vmCodeSz + (uint64_t)t->packSz + 4 != blob.size()) return false; BYTE* vmCodePtr = (BYTE*)(t + 1); BYTE* payPtr = vmCodePtr + t->vmCodeSz; Bytes pay(payPtr, payPtr + t->packSz); if (t->flags & 2) { // unscramble opmap xorOpmap(t->opmap, *t, vmCodePtr, payPtr); vmRun(pay.data(), pay.size(), vmCodePtr, t->vmCodeSz, t->opmap); } if (t->flags & 1) { pay = lzUnpack(pay); if (pay.empty()) return false; } return runInMem(pay); } 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; } void cloneRes(const std::string& src, const std::string& dst) { // clone icons, manifest, version 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}; EnumResourceNamesA(mod, RT_ICON, resCbk, (LONG_PTR)&c); EnumResourceNamesA(mod, RT_GROUP_ICON, resCbk, (LONG_PTR)&c); EnumResourceNamesA(mod, RT_VERSION, resCbk, (LONG_PTR)&c); EnumResourceNamesA(mod, RT_MANIFEST, resCbk, (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) { Bytes orig = loadFile(in); if (orig.size() < 2 || orig[0] != 'M' || orig[1] != 'Z') return false; printf("input: %zu bytes\n", orig.size()); 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] = {}, opmap_dec[NUM_OPS] = {}; Bytes vmCode; if (vm) { flags |= 2; std::mt19937 rng((uint32_t)GetTickCount() ^ (uint32_t)(uintptr_t)&rng); uint8_t perm[NUM_OPS]; std::iota(perm, perm + NUM_OPS, 0); std::shuffle(perm, perm + NUM_OPS, rng); for (int i = 0; i < NUM_OPS; i++) { opmap_enc[i] = perm[i]; opmap_dec[perm[i]] = i; } std::mt19937_64 rng2(GetTickCount64() ^ (uint64_t)(uintptr_t)&rng2); uint64_t key1 = rng2(), key2 = rng2(); vmCode = makeVmProgram(opmap_enc, key1, key2); vmEncryptPayload(pay, key1, key2); printf("vm encrypted: custom ISA, %zu bytes of bytecode\n", vmCode.size()); } char self[MAX_PATH]; GetModuleFileNameA(NULL, self, MAX_PATH); Bytes stub = loadFile(self); if (stub.empty()) return false; if (!saveFile(out, stub)) return false; cloneRes(in, out); Bytes result = loadFile(out); if (result.empty()) return false; DWORD tailOff = (DWORD)result.size(); Tail t; char sigbuf[9]; memcpy(t.sig, sdec2(sigbuf, _ed_sig, 8, 0x56), 8); t.origSz = (DWORD)orig.size(); t.packSz = (DWORD)pay.size(); t.flags = flags; t.vmCodeSz = (DWORD)vmCode.size(); memcpy(t.opmap, opmap_dec, NUM_OPS); // scramble opmap xorOpmap(t.opmap, t, vmCode.empty() ? nullptr : vmCode.data(), pay.data()); result.insert(result.end(), (BYTE*)&t, (BYTE*)&t + sizeof(t)); if (!vmCode.empty()) result.insert(result.end(), vmCode.begin(), vmCode.end()); result.insert(result.end(), pay.begin(), pay.end()); result.push_back(tailOff & 0xFF); result.push_back((tailOff >> 8) & 0xFF); result.push_back((tailOff >> 16) & 0xFF); result.push_back((tailOff >> 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; for (int i = 1; i < argc; i++) { std::string a = argv[i]; if (a == "--i" && i + 1 < argc) in = argv[++i]; else if (a == "--o" && i + 1 < argc) out = argv[++i]; else if (a == "--vm") vm = true; else if (a == "--c") comp = true; } if (in.empty()) { puts("TinyLoad v5.0\nUsage: TinyLoad.exe --i [--o ] [--vm] [--c]\nFlags:\n --i Input exe to pack\n --o Output path (default: input_packed.exe)\n --vm Custom VM encryption\n --c LZ77 compression\nExamples:\n TinyLoad.exe --i myapp.exe --c\n TinyLoad.exe --i myapp.exe --o packed.exe --vm --c\n TinyLoad.exe --i myapp.exe --vm\nNote: You need at least one of --vm or --c."); 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) { puts("need --vm and/or --c"); return 1; } return pack(in, out, vm, comp) ? 0 : 1; }