Начинающий
- Статус
- Оффлайн
- Регистрация
- 5 Май 2025
- Сообщения
- 41
- Реакции
- 0
Ну вот и вторая часть!
Выкладываю сразу, чтобы потом ко мне претензий не было. (есть у меня чувство что мне и 2-ой части не хватит :cry:)
апдейт: хватило, это финал.
апдейт 2: сделал v2 с учетом ошибок.
апдйет 3: хотел обновить лоудер и симейк листы, не вышло. больше 40.000 символов. апдейт 4: появилось параноидальное чувство что в моем лоудере бесконечные дыры в защите.
Сам loader.cpp, encryptor.cpp, CMakeLists.txt и тд тп:
P.S: Код абсолютно нихуя не идеален, не несу ответственность за седину которая появится на вашей голове, после попытки запуска этой залупы.
Ну усе, жду пиздец в комментариях

Выкладываю сразу, чтобы потом ко мне претензий не было. (есть у меня чувство что мне и 2-ой части не хватит :cry:)
апдейт: хватило, это финал.
апдейт 2: сделал v2 с учетом ошибок.
апдйет 3: хотел обновить лоудер и симейк листы, не вышло. больше 40.000 символов. апдейт 4: появилось параноидальное чувство что в моем лоудере бесконечные дыры в защите.
Сам loader.cpp, encryptor.cpp, CMakeLists.txt и тд тп:
toolchain-file:
set(CMAKE_SYSTEM_NAME Linux)
set(CMAKE_C_COMPILER "/opt/ollvm/bin/clang")
set(CMAKE_CXX_COMPILER "/opt/ollvm/bin/clang++")
set(CMAKE_CXX_FLAGS_RELEASE
"-O2 -flto -fvisibility=hidden -fuse-ld=lld \
-mllvm -fla \
-mllvm -bcf -mllvm -bcf_prob=100 \
-mllvm -sub -mllvm -sub_loop=3 \
-mllvm -strcry")
hooks.hpp:
#pragma once
#include <cstdint>
#ifdef _WIN32
# include <windows.h>
# include <winternl.h>
#else
# include <dlfcn.h>
# include <sys/mman.h>
# include <unistd.h>
#endif
inline void kill_jfr_hook() {
#ifdef _WIN32
if (HMODULE jvm = ::GetModuleHandleA("jvm.dll")) {
unsigned char* base = reinterpret_cast<unsigned char*>(jvm);
const char sig[] = "should_post_class_file_load_hook";
for (size_t off = 0; off < 0x02000000; ++off) {
if (::memcmp(base + off, sig, sizeof(sig) - 1) == 0) {
volatile uint8_t* flag = base + off - 4;
DWORD old;
::VirtualProtect((LPVOID)flag, 1, PAGE_READWRITE, &old);
*flag = 0;
::VirtualProtect((LPVOID)flag, 1, old, &old);
break;
}
}
}
#else
void* self = dlopen(nullptr, RTLD_NOW);
void* sym = dlsym(self, "should_post_class_file_load_hook");
if (!sym) sym = dlsym(self, "_ZN22ClassFileLoadHookImpl24should_post_hookE");
if (sym) {
size_t page = reinterpret_cast<size_t>(sym) & ~0xFFF;
mprotect((void*)page, 4096, PROT_READ | PROT_WRITE | PROT_EXEC);
[I](volatile uint8_t[/I])sym = 0;
mprotect((void*)page, 4096, PROT_READ | PROT_EXEC);
}
#endif
}
inline void kill_jvmti_hook_all() {
#ifdef _WIN32
if (HMODULE jvm = ::GetModuleHandleA("jvm.dll")) {
auto patchSym = [&](const char* name) {
void* sym = ::GetProcAddress(jvm, name);
if (sym) {
DWORD old;
::VirtualProtect(sym, sizeof(void*), PAGE_READWRITE, &old);
[I](void*[/I])sym = nullptr;
::VirtualProtect(sym, sizeof(void*), old, &old);
}
};
patchSym("JvmtiExport_ClassFileLoadHook");
patchSym("JvmtiExport_ClassPrepareHook");
patchSym("JvmtiExport_RetransformClassesHook");
patchSym("JvmtiExport_DefineClassHook");
patchSym("JvmtiExport_VMStartHook");
patchSym("JvmtiExport_VMInitHook");
}
#else
void* self = dlopen(nullptr, RTLD_NOW);
auto patchSym = [&](const char* name) {
void** sym = reinterpret_cast<void**>(dlsym(self, name));
if (sym) {
size_t page = reinterpret_cast<size_t>(sym) & ~0xFFF;
mprotect((void*)page, 4096, PROT_READ | PROT_WRITE | PROT_EXEC);
*sym = nullptr;
mprotect((void*)page, 4096, PROT_READ | PROT_EXEC);
}
};
patchSym("_ZN11JvmtiExport24ClassFileLoadHookE");
patchSym("_ZN11JvmtiExport23ClassPrepareHookE");
patchSym("_ZN11JvmtiExport24RetransformClassesHookE");
patchSym("_ZN11JvmtiExport25DefineClassHookE");
patchSym("_ZN11JvmtiExport11VMStartHookE");
patchSym("_ZN11JvmtiExport13VMInitHookE");
#endif
}
#ifdef _WIN32
using NtPV = NTSTATUS (NTAPI*)(HANDLE, PVOID*, PSIZE_T, ULONG, PULONG);
static NtPV realNtProtect = reinterpret_cast<NtPV>(
::GetProcAddress(::GetModuleHandleA("ntdll.dll"),
"NtProtectVirtualMemory"));
static NTSTATUS NTAPI noWriteNtPV(HANDLE proc,
PVOID* baseAddr,
PSIZE_T size,
ULONG newProtect,
PULONG oldProtect)
{
if (newProtect & (PAGE_READWRITE|PAGE_WRITECOPY))
return STATUS_ACCESS_DENIED;
return realNtProtect(proc, baseAddr, size, newProtect, oldProtect);
}
inline void patch_syscall_protect() {
HMODULE mod = ::GetModuleHandleA("ntdll.dll");
if (!mod) return;
BYTE* func = reinterpret_cast<BYTE*>(
::GetProcAddress(mod, "NtProtectVirtualMemory"));
if (!func) return;
unsigned char detour[12];
detour[0] = 0x48; detour[1] = 0xB8;
uint64_t addr = reinterpret_cast<uint64_t>(&noWriteNtPV);
std::memcpy(detour + 2, &addr, sizeof(addr));
detour[10] = 0xFF; detour[11] = 0xE0;
DWORD old;
::VirtualProtect(func, sizeof(detour),
PAGE_EXECUTE_READWRITE, &old);
std::memcpy(func, detour, sizeof(detour));
::VirtualProtect(func, sizeof(detour), old, &old);
}
#else
extern void patchGOT_mprotect();
inline void patch_syscall_protect() {
patchGOT_mprotect();
}
#endif
C++:
#pragma once
#include <cstdint>
#include <cstddef>
inline constexpr std::uint8_t LOADER_SIG[64] = {
0x7d,0x0a,0x6b,0xed,0x31,0x92,0x75,0xca,0x55,0xbe,0x03,0x9e,0x5a,0x2f,0x4b,0x38,
0x87,0x6e,0x4f,0x11,0xfa,0x29,0x05,0xdd,0xec,0x44,0x7e,0x70,0x63,0x89,0xa3,0xdd,
0xd8,0x8e,0xff,0x1c,0x5b,0x29,0x50,0x05,0x45,0xf4,0xaa,0x5f,0x5e,0x0d,0x95,0xd7,
0x8f,0x16,0x02,0xad,0x34,0x14,0xfa,0x88,0x91,0x7a,0xf3,0x4e,0x8c,0xa2,0xca,0x19
};
inline constexpr std::uint8_t SERVER_SECRET[32] = {
0x58,0xb7,0x9c,0xa4,0x51,0xd3,0x7e,0xaa,0x9f,0x60,0x1e,0x2b,0x73,0xdf,0x44,0x8e,
0xa9,0x0d,0xbc,0x4f,0xee,0x83,0x92,0xcd,0x11,0x56,0x21,0x97,0xbb,0x09,0xe5,0x66
};
#ifndef STRKEY
#define STRKEY 0xA3B1C2D3E4F58716ULL
#endif
loader.cpp:
#include "common.hpp"
#include "hooks.hpp"
#include <array>
#include <chrono>
#include <cstdint>
#include <cstring>
#include <filesystem>
#include <fstream>
#include <immintrin.h>
#include <iostream>
#include <random>
#include <span>
#include <string>
#include <thread>
#include <unordered_map>
#include <vector>
#include <curl/curl.h>
#include <cjson/cJSON.h>
#include <openssl/crypto.h>
#include <openssl/evp.h>
#include <openssl/hmac.h>
#include <openssl/rand.h>
#include <openssl/sha.h>
#include <argon2.h>
#include <jni.h>
#ifdef _WIN32
# include <windows.h>
# include <winternl.h>
# include <intrin.h>
# include <psapi.h>
BOOL WINAPI DllMain(HINSTANCE, DWORD fdwReason, LPVOID) {
if (fdwReason == DLL_PROCESS_ATTACH) {
patch_syscall_protect();
}
return TRUE;
}
#pragma comment(linker, "/INCLUDE:patch_syscall_protect")
#else
# include <link.h>
# include <elf.h>
# include <unistd.h>
# include <sys/mman.h>
# include <sys/ptrace.h>
# include <sys/stat.h>
# include <dlfcn.h>
# include <fstream>
# include <string>
[B]attribute[/B]((constructor))
static void early_init() {
patch_syscall_protect();
}
static void patchGOT_mprotect() {
dl_iterate_phdr(+[](struct dl_phdr_info* info, size_t, void*) -> int {
ElfW(Dyn)* dyn = nullptr;
for (int i = 0; i < info->dlpi_phnum; ++i) {
if (info->dlpi_phdr[i].p_type == PT_DYNAMIC) {
dyn = (ElfW(Dyn)*)(info->dlpi_addr + info->dlpi_phdr[i].p_vaddr);
break;
}
}
if (!dyn) return 0;
ElfW(Rela)* rela = nullptr;
size_t rela_count = 0;
bool use_rela = false;
ElfW(Sym)* symtab = nullptr;
const char* strtab = nullptr;
for (ElfW(Dyn)* d = dyn; d->d_tag; ++d) {
switch (d->d_tag) {
case DT_JMPREL:
rela = (ElfW(Rela)*)(info->dlpi_addr + d->d_un.d_ptr);
break;
case DT_PLTRELSZ:
rela_count = d->d_un.d_val / sizeof(ElfW(Rela));
break;
case DT_PLTREL:
use_rela = (d->d_un.d_val == DT_RELA);
break;
case DT_SYMTAB:
symtab = (ElfW(Sym)*)(info->dlpi_addr + d->d_un.d_ptr);
break;
case DT_STRTAB:
strtab = (const char*)(info->dlpi_addr + d->d_un.d_ptr);
break;
}
}
if (!use_rela || !rela || !symtab || !strtab) return 0;
long page_size = sysconf(_SC_PAGESIZE);
for (size_t i = 0; i < rela_count; ++i) {
auto& r = rela[i];
size_t sym_idx = ELF64_R_SYM(r.r_info);
const char* name = strtab + symtab[sym_idx].st_name;
if (strcmp(name, "mprotect") == 0) {
void** got_entry = (void**)(info->dlpi_addr + r.r_offset);
uintptr_t page = (uintptr_t)got_entry & ~(page_size - 1);
mprotect((void*)page, page_size, PROT_READ | PROT_WRITE | PROT_EXEC);
[I]got_entry = (void[/I])noWriteMP;
mprotect((void*)page, page_size, PROT_READ | PROT_EXEC);
break;
}
}
return 0;
}, nullptr);
}
#endif
#ifdef _WIN32
static void protectModuleExeOnly(HMODULE mod) {
MODULEINFO mi;
if (GetModuleInformation(GetCurrentProcess(), mod, &mi, sizeof(mi))) {
DWORD old;
VirtualProtect(mi.lpBaseOfDll, mi.SizeOfImage,
PAGE_EXECUTE_READ, &old);
}
}
#else
static void protectModuleExeOnly(const char* moduleName) {
std::ifstream maps("/proc/self/maps");
std::string line;
while (std::getline(maps, line)) {
if (moduleName && line.find(moduleName) == std::string::npos)
continue;
unsigned long start, end;
char perms[5];
if (sscanf(line.c_str(), "%lx-%lx %4s", &start, &end, perms) != 3)
continue;
if (perms[0]=='r' && perms[2]=='x')
mprotect((void*)start, end - start, PROT_READ|PROT_EXEC);
}
}
#endif
#define CFG_KDF_MEM_KIB 131072
#define CFG_KDF_ITERS 4
#define CFG_KDF_PARALLEL 4
#define WD_FAST_MS 50
#define WD_SLOW_MS 700
#define TSC_THRESHOLD_MS 550
enum class Err { OK, HWID, DEC, CLASS, JVM, DBG, OOM, SIG };
struct Logger {
static inline std::ofstream f;
static void init() { f.open("loader.log", std::ios::app); }
static void log(Err e, const std::string& m) {
if (!f.is_open()) return;
std::string s = m;
if (s.size() > 64)
for (size_t i = 0; i + 63 < s.size(); ++i)
if (s.substr(i,64).find_first_not_of("0123456789abcdef")==std::string::npos)
s.replace(i,64,"[REDACTED]");
f << '['<<int(e)<<"] "<<s<<'\n'; f.flush();
}
};
#ifndef SILENT_RELEASE
# define LOG(c,m) Logger::log(c,m)
#else
# define LOG(c,m) ((void)0)
#endif
class SBuf {
uint8_t* p; size_t n;
public:
explicit SBuf(size_t sz):n(sz){
#ifdef _WIN32
p=(uint8_t*)VirtualAlloc(nullptr,sz+8192,MEM_COMMIT|MEM_RESERVE,PAGE_READWRITE);
VirtualProtect(p,4096,PAGE_NOACCESS,nullptr);
VirtualProtect(p+4096+sz,4096,PAGE_NOACCESS,nullptr);
p+=4096; VirtualLock(p,sz);
#else
p=(uint8_t*)mmap(nullptr,sz+8192,PROT_READ|PROT_WRITE,MAP_PRIVATE|MAP_ANONYMOUS,-1,0);
mprotect(p,4096,PROT_NONE);
mprotect(p+4096+sz,4096,PROT_NONE);
p+=4096; mlock(p,sz);
#endif
if(!p) throw std::bad_alloc();
}
~SBuf(){
OPENSSL_cleanse(p,n);
#ifdef _WIN32
VirtualFree(p-4096,0,MEM_RELEASE);
#else
munmap(p-4096,n+8192);
#endif
}
uint8_t* data() { return p; }
const uint8_t* data() const { return p; }
size_t size() const { return n; }
std::span<const uint8_t> span() const { return {p,n}; }
};
[[gnu::always_inline]]
static inline size_t wr(void* c,size_t s,size_t nm,std::string* out){
out->append((char*)c, s*nm);
return s*nm;
}
static std::string post(const std::string& url,const std::string& b){
CURL* h=curl_easy_init(); std::string r;
curl_easy_setopt(h, CURLOPT_URL, url.c_str());
curl_easy_setopt(h, CURLOPT_POSTFIELDS, b.c_str());
curl_easy_setopt(h, CURLOPT_WRITEFUNCTION, wr);
curl_easy_setopt(h, CURLOPT_WRITEDATA, &r);
curl_easy_setopt(h, CURLOPT_SSL_VERIFYPEER, 1L);
if(curl_easy_perform(h)!=CURLE_OK){
curl_easy_cleanup(h);
throw std::runtime_error("HTTP");
}
curl_easy_cleanup(h);
return r;
}
static std::string hwid(){
std::string s;
#ifdef _WIN32
char vol[MAX_PATH+1]; DWORD sn{};
GetVolumeInformationA("C:\\",vol,MAX_PATH,&sn,nullptr,nullptr,nullptr,0);
s+=std::to_string(sn);
ULONG len=GetSystemFirmwareTable('RSMB',0,nullptr,0);
if(len){
std::vector<uint8_t> buf(len);
if(GetSystemFirmwareTable('RSMB',0,buf.data(),len))
s.append((char*)buf.data(),buf.size());
}
#else
struct stat st{}; if(stat("/dev/sda",&st)==0) s+=std::to_string(st.st_dev);
std::ifstream brd("/sys/devices/virtual/dmi/id/board_serial");
std::string ln;
if(brd&&std::getline(brd,ln)) s+=ln;
#endif
unsigned int ci[4]{};
#ifdef _MSC_VER
__cpuid((int*)ci,1);
#else
asm volatile("cpuid":"=a"(ci[0]),"=b"(ci[1]),"=c"(ci[2]),"=d"(ci[3]):"a"(1));
#endif
s+=std::to_string(ci[0]);
SBuf h(SHA256_DIGEST_LENGTH);
SHA256((uint8_t*)s.data(),s.size(),h.data());
char tmp[3]; std::string out;
for(auto v:h.span()){ snprintf(tmp,3,"%02x",v); out+=tmp; }
return out;
}
static bool hwBpPresent(){
#ifdef _WIN32
CONTEXT c{}; c.ContextFlags=CONTEXT_DEBUG_REGISTERS;
if(!GetThreadContext(GetCurrentThread(),&c)) return false;
return c.Dr0||c.Dr1||c.Dr2||c.Dr3;
#else
return false;
#endif
}
static bool isDbg(){
#ifdef _WIN32
PPEB peb=(PPEB)__readgsqword(0x60);
if(peb->BeingDebugged||peb->NtGlobalFlag&0x70) return true;
#endif
int a,b,c,d; __cpuid(1,a,b,c,d);
if(c&(1<<31)) return true;
if(hwBpPresent()) return true;
unsigned long long t1=__rdtsc();
for(volatile int i=0;i<100000;++i);
unsigned int aux; unsigned long long t2=__rdtscp(&aux);
return ((t2-t1)/2900000.0*1000.0)>TSC_THRESHOLD_MS;
}
static bool isValidClassFile(std::span<const uint8_t> d){
if(d.size()<4) return false;
uint32_t m=(d[0]<<24)|(d[1]<<16)|(d[2]<<8)|d[3];
return m==0xCAFEBABE||m==0xDEADBEEF;
}
static SBuf kdf(const std::string& hw,std::span<const uint8_t> salt,const std::string& fn){
SBuf key(32); std::array<uint8_t,32> master{};
int rc=argon2id_hash_raw(CFG_KDF_ITERS,CFG_KDF_MEM_KIB,CFG_KDF_PARALLEL,
hw.data(),hw.size(),salt.data(),salt.size(),
master.data(),master.size());
if(rc!=ARGON2_OK) throw std::runtime_error("argon2");
unsigned int l=0;
HMAC(EVP_sha256(),master.data(),master.size(),
(uint8_t*)fn.data(),fn.size(),
key.data(),&l);
OPENSSL_cleanse(master.data(),master.size());
return key;
}
using CtxUP = std::unique_ptr<EVP_CIPHER_CTX,decltype(&EVP_CIPHER_CTX_free)>;
static std::vector<uint8_t> aesDec(std::span<const uint8_t> ct,
std::span<const uint8_t> k,
const std::array<uint8_t,12>& iv,
const std::array<uint8_t,16>& tag){
CtxUP ctx(EVP_CIPHER_CTX_new(),EVP_CIPHER_CTX_free);
SBuf pt(ct.size()); int n=0,tot=0;
if(!EVP_DecryptInit_ex(ctx.get(),EVP_aes_256_gcm(),nullptr,nullptr,nullptr)||
!EVP_DecryptInit_ex(ctx.get(),nullptr,nullptr,k.data(),iv.data())||
!EVP_DecryptUpdate(ctx.get(),pt.data(),&n,ct.data(),ct.size()))
throw std::runtime_error("dec");
tot+=n;
EVP_CIPHER_CTX_ctrl(ctx.get(),EVP_CTRL_GCM_SET_TAG,16,(void*)tag.data());
if(EVP_DecryptFinal_ex(ctx.get(),pt.data()+tot,&n)<=0)
throw std::runtime_error("auth");
tot+=n;
return {pt.data(),pt.data()+tot};
}
// JIT-only decrypt & load, immediate cleanse
static void jLoad(JNIEnv* env,
std::span<const uint8_t> ct,
const std::array<uint8_t,12>& iv,
const std::array<uint8_t,16>& tag,
const SBuf& key,
const std::string& name)
{
auto plain = aesDec(ct, key.span(), iv, tag);
jbyteArray ba = env->NewByteArray((jsize)plain.size());
env->SetByteArrayRegion(ba, 0, (jsize)plain.size(), (jbyte*)plain.data());
jclass cl = env->FindClass("com/example/CustomClassLoader");
jmethodID m = env->GetStaticMethodID(
cl, "loadClass", "([BLjava/lang/String;)Ljava/lang/Class;");
jstring jn = env->NewStringUTF(name.c_str());
env->CallStaticObjectMethod(cl, m, ba, jn);
OPENSSL_cleanse(plain.data(), plain.size());
}
namespace EncryptedRemapTable {
struct BadTable:std::runtime_error{ using std::runtime_error::runtime_error; };
static std::unordered_map<std::string,std::string> decrypt(const std::string& hw) {
std::string body = "{\"hwid\":\""+hw+"\"}";
std::string raw = post("https://your-server.com/remap-table", body);
cJSON* root = cJSON_Parse(raw.c_str());
if(!root) throw BadTable("json");
cJSON* tbl = cJSON_GetObjectItem(root, "table");
if(!cJSON_IsString(tbl)){ cJSON_Delete(root); throw BadTable("no-field"); }
std::string blob = b64dec(tbl->valuestring);
cJSON_Delete(root);
if(blob.size()<24+16+32) throw BadTable("len");
const uint8_t* nonce = (uint8_t*)blob.data();
const uint8_t* tag = nonce + blob.size() - 32;
size_t ctLen = blob.size() - 24 - 32;
const uint8_t* ct = nonce + 24;
const uint8_t* hmac = tag + 16;
uint8_t calc[32];
HMAC(EVP_sha256(), SERVER_SECRET, 32,
nonce, 24+16,
calc, nullptr);
if(CRYPTO_memcmp(calc, hmac, 16)!=0) throw BadTable("hmac");
SBuf k(32);
{
EVP_PKEY_CTX* kctx = EVP_PKEY_CTX_new_id(EVP_PKEY_HKDF,nullptr);
EVP_PKEY_CTX_set_hkdf_md(kctx, EVP_sha256());
EVP_PKEY_CTX_set1_hkdf_salt(kctx,"remap-v1",8);
EVP_PKEY_CTX_set1_hkdf_key(kctx,SERVER_SECRET,32);
EVP_PKEY_CTX_add1_hkdf_info(kctx,hw.data(),hw.size());
size_t len=32;
EVP_PKEY_derive(kctx, k.data(), &len);
EVP_PKEY_CTX_free(kctx);
}
CtxUP ctx(EVP_CIPHER_CTX_new(),EVP_CIPHER_CTX_free);
SBuf plain(ctLen); int n=0;
if(!EVP_DecryptInit_ex(ctx.get(),EVP_chacha20_poly1305(),nullptr,nullptr,nullptr)||
!EVP_DecryptInit_ex(ctx.get(),nullptr,nullptr,k.data(),nonce)||
!EVP_DecryptUpdate(ctx.get(),plain.data(),&n,ct,ctLen))
throw BadTable("dec");
EVP_CIPHER_CTX_ctrl(ctx.get(),EVP_CTRL_AEAD_SET_TAG,16,(void*)tag);
if(EVP_DecryptFinal_ex(ctx.get(),plain.data()+n,&n)<=0)
throw BadTable("auth");
std::unordered_map<std::string,std::string> out;
size_t i=0;
while(i<ctLen){
if(plain.data()[i++]!=1) break;
uint16_t kl=(plain.data()[i++]<<8)|plain.data()[i++];
uint16_t vl=(plain.data()[i++]<<8)|plain.data()[i++];
if(i+kl+vl>ctLen) break;
std::string k_s((char*)plain.data()+i,kl); i+=kl;
std::string v_s((char*)plain.data()+i,vl); i+=vl;
out.emplace(std::move(k_s),std::move(v_s));
}
if(out.empty()) throw BadTable("parse");
return out;
}
}
namespace wd {
static inline volatile bool run = true;
static inline SBuf* keyPtr = nullptr;
static std::array<uint8_t,16> origJfr, origJvmti;
static void snapshotHooks(){
memcpy(origJfr.data(), (void*)&kill_jfr_hook, origJfr.size());
memcpy(origJvmti.data(), (void*)&kill_jvmti_hook_all, origJvmti.size());
}
#ifdef _WIN32
[[gnu::noinline]] void kill(){
if(keyPtr) OPENSSL_cleanse(keyPtr->data(), keyPtr->size());
::TerminateProcess(::GetCurrentProcess(), 13);
}
#else
[[gnu::noinline]] void kill(){
if(keyPtr) OPENSSL_cleanse(keyPtr->data(), keyPtr->size());
::quick_exit(13);
}
#endif
[[gnu::noinline]] void fast(){
int strikes=0;
while(run){
if(memcmp((void*)&kill_jfr_hook, origJfr.data(), origJfr.size())!=0) kill();
if(memcmp((void*)&kill_jvmti_hook_all,origJvmti.data(),origJvmti.size())!=0) kill();
if(isDbg()){ if(++strikes>2) kill(); } else strikes=0;
std::this_thread::sleep_for(std::chrono::milliseconds(WD_FAST_MS));
}
}
[[gnu::noinline]] void slow(const uint8_t* golden, size_t len){
SBuf ref(SHA256_DIGEST_LENGTH);
SHA256(golden, len, ref.data());
int miss=0;
while(run){
SBuf cur(SHA256_DIGEST_LENGTH);
SHA256(golden, len, cur.data());
if(CRYPTO_memcmp(cur.data(), ref.data(), ref.size())!=0){
if(++miss>1) kill();
} else miss=0;
std::this_thread::sleep_for(std::chrono::milliseconds(WD_SLOW_MS));
}
}
}
int main(){
Logger::init();
curl_global_init(CURL_GLOBAL_ALL);
kill_jfr_hook();
kill_jvmti_hook_all();
patch_syscall_protect();
wd::snapshotHooks();
#ifdef _WIN32
protectModuleExeOnly(::GetModuleHandleA("jvm.dll"));
protectModuleExeOnly(::GetModuleHandle(nullptr));
patchIAT_VirtualProtect();
#else
protectModuleExeOnly("libjvm.so");
protectModuleExeOnly(nullptr);
patchGOT_mprotect();
#endif
if (getenv("LD_PRELOAD") || getenv("LD_LIBRARY_PATH")
#ifdef _WIN32
|| GetEnvironmentVariableA("AppInit_DLLs",nullptr,0)>0
#endif
) {
return LOG(Err::DBG, "injection env");
}
#ifdef [B]linux[/B]
if (access("/usr/local/lib/frida", F_OK)==0 ||
(getenv("LD_PRELOAD") && strstr(getenv("LD_PRELOAD"), "frida")))
return LOG(Err::DBG, "frida detected");
#endif
#ifdef _WIN32
if (GetSystemMetrics(SM_REMOTESESSION))
return LOG(Err::DBG, "remote session");
#endif
if (isDbg()) {
LOG(Err::DBG, "early");
return 2;
}
std::string hw = hwid();
std::ifstream f("encrypted_mod.class", std::ios::binary);
if (!f) { LOG(Err::CLASS, "open"); return 3; }
std::vector<uint8_t> buf((std::istreambuf_iterator<char>(f)), {});
if (buf.size() < 44) { LOG(Err::CLASS, "trunc"); return 4; }
SBuf salt(16);
std::copy(buf.data(), buf.data()+16, salt.data());
std::array<uint8_t,12> iv;
std::copy(buf.data()+16, buf.data()+28, iv.begin());
std::array<uint8_t,16> tag;
std::copy(buf.data()+28, buf.data()+44, tag.begin());
SBuf key = kdf(hw, salt.span(), "encrypted_mod.class");
wd::keyPtr = &key;
JavaVM* vm = nullptr; JNIEnv* env = nullptr; jsize cnt=0;
JNI_GetCreatedJavaVMs(&vm, 1, &cnt);
if (cnt == 0) {
JavaVMInitArgs args{}; args.version = JNI_VERSION_1_8;
JavaVMOption opt{ (char*)"-Djava.class.path=." };
args.nOptions = 1; args.options = &opt;
if (JNI_CreateJavaVM(&vm, (void**)&env, &args) != JNI_OK) {
LOG(Err::JVM, "spawn");
return 6;
}
} else {
vm->AttachCurrentThread((void**)&env, nullptr);
}
auto* table = const_cast<JNINativeInterface*>(*env);
table->DefineClass = nullptr;
table->FindClass = nullptr;
table->ReleaseByteArrayElements = nullptr;
table->NewDirectByteBuffer = nullptr;
auto remap = EncryptedRemapTable::decrypt(hw);
jLoad(env,
{ buf.data() + 44, buf.size() - 44 },
iv, tag,
key,
remap.at("a.b.c"));
std::cout << "Loaded OK\n";
std::thread wdFast(wd::fast);
std::thread wdSlow(wd::slow,
buf.data()+44, 64);
wd::run = false;
wdFast.join();
wdSlow.join();
curl_global_cleanup();
return 0;
}
encryptor.cpp:
#include "common.hpp"
#include <array>
#include <cstdint>
#include <cstring>
#include <filesystem>
#include <fstream>
#include <span>
#include <string>
#include <vector>
#include <openssl/evp.h>
#include <openssl/hmac.h>
#include <openssl/rand.h>
#include <openssl/sha.h>
#include <openssl/crypto.h>
#include <argon2.h>
#ifdef _WIN32
#include <windows.h>
#else
#include <sys/stat.h>
#include <unistd.h>
#endif
namespace {
enum class ECode { SUCCESS, OOM, READ_FAIL, WRITE_FAIL, CRYPTO_FAIL };
struct Logger {
static std::ofstream f;
static void init() { f.open("encryptor.log", std::ios::app); }
static void log(ECode c, const std::string& m){
if (f.is_open()) { f << '['<<int(c)<<"] "<< m <<'\n'; f.flush(); }
}
}; std::ofstream Logger::f;
class SBuf {
std::uint8_t* p; size_t n;
public:
explicit SBuf(size_t n_) : n(n_)
{
#ifdef _WIN32
p=(uint8_t*)VirtualAlloc(nullptr,n+8192,MEM_COMMIT|MEM_RESERVE,PAGE_READWRITE);
VirtualProtect(p,4096,PAGE_NOACCESS,nullptr);
VirtualProtect(p+4096+n,4096,PAGE_NOACCESS,nullptr);
p+=4096;
#else
p=(uint8_t*)mmap(nullptr,n+8192,PROT_READ|PROT_WRITE,MAP_PRIVATE|MAP_ANONYMOUS,-1,0);
mprotect(p,4096,PROT_NONE);
mprotect(p+4096+n,4096,PROT_NONE);
p+=4096;
#endif
if(!p) throw std::bad_alloc{};
#ifdef _WIN32
VirtualLock(p,n);
#else
mlock(p,n);
#endif
}
~SBuf(){
if(!p) return;
OPENSSL_cleanse(p,n);
#ifdef _WIN32
VirtualProtect(p-4096,n+8192,PAGE_NOACCESS,nullptr);
VirtualFree(p-4096,0,MEM_RELEASE);
#else
mprotect(p-4096,n+8192,PROT_NONE);
munmap(p-4096,n+8192);
#endif
}
uint8_t* data(){return p;}
const uint8_t* data()const{return p;}
size_t size()const{return n;}
std::span<const uint8_t> span()const{return {p,n};}
};
std::string hwid()
{
std::string s="";
#ifdef _WIN32
DWORD sn; char vol[MAX_PATH+1];
GetVolumeInformationA("C:\\",vol,MAX_PATH,&sn,nullptr,nullptr,nullptr,0);
s+=std::to_string(sn);
#else
struct stat st{}; if(!stat("/dev/sda",&st)) s+=std::to_string(st.st_dev);
#endif
unsigned int cpu[4];
#if defined([B]i386[/B])||defined([B]x86_64[/B])
#ifdef _WIN32
__cpuid(cpu,1);
#else
asm volatile("cpuid":"=a"(cpu[0]):"a"(1):"ebx","ecx","edx");
#endif
s+=std::to_string(cpu[0]);
#endif
SBuf h(SHA256_DIGEST_LENGTH);
SHA256((const uint8_t*)s.data(),s.size(),h.data());
char hex[3]; std::string out;
for(auto b: h.span()){ snprintf(hex,3,"%02x",b); out+=hex; }
return out;
}
SBuf deriveKey(const std::string& id, std::span<const uint8_t> salt)
{
SBuf k(32);
int r=argon2id_hash_raw(3,64*1024,2,id.data(),id.size(),
salt.data(),salt.size(),k.data(),32);
if(r!=ARGON2_OK) throw std::runtime_error("argon2");
return k;
}
using EVPctx = std::unique_ptr<EVP_CIPHER_CTX,
decltype(&EVP_CIPHER_CTX_free)>;
std::vector<uint8_t> aesGcmEncrypt(std::span<const uint8_t> plain,
std::span<const uint8_t> key,
std::span<const uint8_t,12> iv,
uint8_t* tag16)
{
EVPctx ctx{EVP_CIPHER_CTX_new(),EVP_CIPHER_CTX_free};
int len=0,tot=0;
std::vector<uint8_t> ct(plain.size());
if(!EVP_EncryptInit_ex(ctx.get(),EVP_aes_256_gcm(),nullptr,nullptr,nullptr)||
!EVP_EncryptInit_ex(ctx.get(),nullptr,nullptr,key.data(),iv.data())||
!EVP_EncryptUpdate(ctx.get(),ct.data(),&len,plain.data(),plain.size()))
throw std::runtime_error("enc");
tot=len;
if(!EVP_EncryptFinal_ex(ctx.get(),ct.data()+len,&len))
throw std::runtime_error("enc fin");
tot+=len;
if(!EVP_CIPHER_CTX_ctrl(ctx.get(),EVP_CTRL_GCM_GET_TAG,16,tag16))
throw std::runtime_error("tag");
ct.resize(tot);
return ct;
}
}
int main()
{
Logger::init();
try{
const std::string id = hwid();
std::ifstream in("HelloWorld.class",std::ios::binary);
if(!in) throw std::runtime_error("open HelloWorld.class");
const std::vector<uint8_t> cls((std::istreambuf_iterator<char>(in)),{});
SBuf salt(16); RAND_bytes(salt.data(),16);
std::array<uint8_t,12> iv; RAND_bytes(iv.data(),12);
SBuf master = deriveKey(id,salt.span());
uuid_t uu; uuid_generate(uu);
uint8_t fileKeyRaw[32];
HMAC(EVP_sha256(),master.data(),32,uu,16,fileKeyRaw,nullptr);
SBuf fileKey(32); std::memcpy(fileKey.data(),fileKeyRaw,32);
OPENSSL_cleanse(fileKeyRaw,32);
uint8_t tag[16];
auto enc = aesGcmEncrypt(cls,fileKey.span(),iv,tag);
HMAC_CTX* hctx=HMAC_CTX_new();
HMAC_Init_ex(hctx,SERVER_SECRET,sizeof SERVER_SECRET,EVP_sha256(),nullptr);
HMAC_Update(hctx,salt.data(),16);
HMAC_Update(hctx,iv.data(),12);
HMAC_Update(hctx,tag,16);
HMAC_Update(hctx,enc.data(),enc.size());
uint8_t auth[32]; unsigned int alen;
HMAC_Final(hctx,auth,&alen);
HMAC_CTX_free(hctx);
std::ofstream out("encrypted_mod.class",std::ios::binary);
out.write((const char*)salt.data(),16);
out.write((const char*)iv.data(),12);
out.write((const char*)tag,16);
out.write((const char*)enc.data(),enc.size());
out.write((const char*)auth,16); // трим до 16
std::cout<<"Encrypted ✓ ("<<enc.size()<<" bytes)\n";
return 0;
}catch(const std::exception& e){
Logger::log(ECode::CRYPTO_FAIL,e.what());
std::cerr<<"err: "<<e.what()<<'\n';
return 1;
}
}
CMakeLists.txt:
cmake_minimum_required(VERSION 3.22)
project(SecureLoader LANGUAGES CXX)
set(CMAKE_CXX_STANDARD 20)
set(CMAKE_POSITION_INDEPENDENT_CODE ON)
find_package(OpenSSL 3 REQUIRED)
find_package(Argon2 REQUIRED)
find_package(cJSON REQUIRED)
find_package(CURL REQUIRED)
find_package(Java 11 REQUIRED)
find_package(JNI REQUIRED)
add_executable(encryptor encryptor.cpp common.hpp)
target_link_libraries(encryptor PRIVATE
OpenSSL::Crypto
Argon2::argon2
)
add_executable(loader loader.cpp common.hpp)
target_include_directories(loader PRIVATE ${JNI_INCLUDE_DIRS})
target_link_libraries(loader PRIVATE
OpenSSL::Crypto CURL::libcurl cJSON::cJSON ${JNI_LIBRARIES}
)
if(WIN32)
target_link_libraries(loader PRIVATE ncrypt)
else()
find_package(Threads REQUIRED)
target_link_libraries(loader PRIVATE Threads::Threads)
endif()
if(NOT DEFINED OBF_OFF)
include(cmake/ollvm17.cmake)
endif()
CustomClassLoader.java:
package com.example;
public class CustomClassLoader extends ClassLoader {
public Class<?> load(byte[] bytes, String name) {
return defineClass(name, bytes, 0, bytes.length);
}
public static Class<?> loadClass(byte[] bytes, String name)
throws ClassNotFoundException {
return new CustomClassLoader().load(bytes, name);
}
}
server.py:
from fastapi import FastAPI, HTTPException
from pydantic import BaseModel
import os, time, base64, hashlib, hmac, secrets
from cryptography.hazmat.primitives import hashes, serialization
from cryptography.hazmat.primitives.asymmetric import ed25519
from cryptography.hazmat.primitives.kdf.hkdf import HKDF
from cryptography.hazmat.primitives.ciphers import Cipher, algorithms
SECRET_PATH = "/run/secrets/server_secret"
with open(SECRET_PATH, "rb") as f:
SERVER_SECRET = f.read()
priv_key = ed25519.Ed25519PrivateKey.generate() # демо; в проде — HSM
app = FastAPI()
class HWIDRequest(BaseModel): hwid: str
class LicenseRequest(BaseModel): hwid: str; license: str
def sign(d: bytes) -> bytes: return priv_key.sign(d)
@app.post("/register")
def register(r: HWIDRequest):
ts = str(int(time.time()))
payload = f"{r.hwid}:{ts}".encode()
mac = hmac.new(SERVER_SECRET, payload, hashlib.sha256).hexdigest()[:32]
full = payload+b":"+mac.encode()
lic = base64.b64encode(full+sign(full)).decode()
return {"license": lic}
def check(lic: str, hw: str):
raw = base64.b64decode(lic); sig, body = raw[-64:], raw[:-64]
priv_key.public_key().verify(sig, body)
h,s = body.decode().split(":",1)
if h!=hw or int(s)<time.time()-300:
raise ValueError
expected = hmac.new(SERVER_SECRET, f"{h}:{s}".encode(),
hashlib.sha256).hexdigest()[:32]
if expected!=body.decode().split(":")[2]:
raise ValueError
@app.post("/session-key")
def session(r: LicenseRequest):
try: check(r.license,r.hwid)
except: raise HTTPException(status_code=403)
key = secrets.token_bytes(32)
nonce = secrets.token_bytes(12)
cipher= Cipher(algorithms.ChaCha20(SERVER_SECRET,nonce),mode=None)
enc = cipher.encryptor().update(key)
tag = hmac.new(SERVER_SECRET, nonce+enc, hashlib.sha256).digest()[:16]
blob = base64.b64encode(nonce+enc+tag).decode()
return {"key": blob}
@app.post("/remap-table")
def table(r: LicenseRequest):
try: check(r.license,r.hwid)
except: raise HTTPException(status_code=403)
tbl = b"{'a.b.c':'com.example.MyModClass','x.y.z':'com.example.CoreMod'}"
hkdf = HKDF(algorithm=hashes.SHA256(),length=32,
salt=b"remap-table-v1",info=b"remap-table")
tkey = hkdf.derive(r.hwid.encode())
nonce= secrets.token_bytes(12)
cipher= Cipher(algorithms.ChaCha20(tkey,nonce),mode=None)
enc = cipher.encryptor().update(tbl)
tag = hmac.new(tkey, nonce+enc, hashlib.sha256).digest()[:16]
blob = base64.b64encode(nonce+enc+tag).decode()
return {"table": blob}
@app.post("/public-key")
def pub():
return {"public_key": base64.b64encode(
priv_key.public_key().public_bytes(
encoding=serialization.Encoding.Raw,
format=serialization.PublicFormat.Raw)
).decode()}
Dockerfile:
FROM python:3.10-slim
RUN pip install fastapi uvicorn cryptography
COPY server.py /app/server.py
WORKDIR /app
CMD ["uvicorn", "server:app", "--host", "0.0.0.0", "--port", "8000"]
docker-compose.yml:
version: "3.9"
services:
api:
build: ./server
ports: ["8000:8000"]
secrets: ["server_secret"]
secrets:
server_secret:
file: ./server_secret.bin
P.S: Код абсолютно нихуя не идеален, не несу ответственность за седину которая появится на вашей голове, после попытки запуска этой залупы.
Ну усе, жду пиздец в комментариях


Последнее редактирование: