• Я зарабатываю 100 000 RUB / месяц на этом сайте!

    А знаешь как? Я всего-лишь публикую (создаю темы), а админ мне платит. Трачу деньги на мороженое, робуксы и сервера в Minecraft. А ещё на паль из Китая. 

    Хочешь так же? Пиши и узнавай условия: https://t.me/alex_redact
    Реклама: https://t.me/yougame_official

C++ [2/2] Mini SecureLoader v2 + Encryptor — комплект для шифрования и загрузки Java‑классов через C++/JNI

Начинающий
Начинающий
Статус
Оффлайн
Регистрация
5 Май 2025
Сообщения
41
Реакции
0
Ну вот и вторая часть!

Выкладываю сразу, чтобы потом ко мне претензий не было. (есть у меня чувство что мне и 2-ой части не хватит :cry:)
апдейт: хватило, это финал.
апдейт 2: сделал v2 с учетом ошибок.
апдйет 3: хотел обновить лоудер и симейк листы, не вышло. больше 40.000 символов. апдейт 4: появилось параноидальное чувство что в моем лоудере бесконечные дыры в защите.
Сам loader.cpp, encryptor.cpp, CMakeLists.txt и тд тп:

toolchain-file:
Expand Collapse Copy
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:
Expand Collapse Copy
#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++:
Expand Collapse Copy
#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:
Expand Collapse Copy
#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:
Expand Collapse Copy
#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:
Expand Collapse Copy
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:
Expand Collapse Copy
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:
Expand Collapse Copy
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:
Expand Collapse Copy
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:
Expand Collapse Copy
version: "3.9"

services:
  api:
    build: ./server
    ports: ["8000:8000"]
    secrets: ["server_secret"]

secrets:
  server_secret:
    file: ./server_secret.bin

P.S: Код абсолютно нихуя не идеален, не несу ответственность за седину которая появится на вашей голове, после попытки запуска этой залупы.

Ну усе, жду пиздец в комментариях :roflanPominki: :roflanEbalo:
 
Последнее редактирование:
Пожалуйста, авторизуйтесь для просмотра ссылки.

прикольная защита, жалко что через две секунды кряк
 
жвмти аттач я не резал специально, чтобы было легче тестить. в релизе атач выключен, а класс грузится вайт боксом и байты затираются, так что ClassDumper отвалится.
 
Ну вот и вторая часть!

Выкладываю сразу, чтобы потом ко мне претензий не было. (есть у меня чувство что и мне 2-ой части не хватит :cry:)
апдейт: хватило, это финал

Сам loader.cpp, encryptor.cpp, CMakeLists.txt и тд тп:

C++:
Expand Collapse Copy
#pragma once
#include <cstdint>
#include <cstddef>

/*
   ==============================================================
       common.hpp ― гайд по пользованию
   ==============================================================

   ►  LOADER_SIG
      64‑байтная Ed25519‑подпись неизмененного бинаря SecureLoaderExe.
      ▪‑ Сгенерируйте hash:   sha256sum SecureLoaderExe  > hash.bin
      ▪‑ Подпишите ключом:    openssl dgst -sign priv.pem -binary hash.bin > sig.bin
      ▪‑ Преобразуйте:       xxd -i sig.bin  (и вставьте вывод сюда).

   ►  SERVER_SECRET
      32‑байтный “серверный секрет”, который сервер использует как root‑key
      для HMAC и ChaCha-силинга сессионного AES.
      В бою он хранится в HSM / TPM и на клиент не попадает, но
      для офлайн‑демо мы жёстко вшиваем копию, чтобы всё расшифровывалось.

   ►  STRKEY
      64‑битный константный ключ, которым compile‑time шаблон encryptString<>
      запутывает строковые литералы в бинаре.
      Любое 64‑битное значение подойдёт – меняйте на вкус
 
*/

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:
Expand Collapse Copy
#include "common.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>
#   pragma comment(lib,"ntdll.lib")
#else
#   include <fcntl.h>
#   include <sys/mman.h>
#   include <sys/ptrace.h>
#   include <sys/stat.h>
#   include <sys/syscall.h>
#   include <unistd.h>
#endif

#define CFG_KDF_MEM_KIB      65536
#define CFG_KDF_ITERS        3
#define CFG_KDF_PARALLEL     2
#define CFG_WATCHDOG_MS      200
#define CFG_TSC_LIMIT        600

enum class Err { OK, HWID, DEC, CLASS, JVM, DBG, OOM, SIZE, SIG, TPM };
#ifndef SILENT_RELEASE
#   define LOG(x,msg) Logger::log(x,msg)
#else
#   define LOG(x,msg) (void)0
#endif
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();
    }
};


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
        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};}
};

namespace cx {
    constexpr uint8_t rotl(uint8_t x,int r){return (x<<r)|(x>>(8-r));}
    template<size_t N>
    consteval std::array<uint8_t,N> aesMask(){
        std::array<uint8_t,N> m{};
        uint8_t k=0xA5;
        for(size_t i=0;i<N;++i){ k=rotl(k,3)^0x1F; m[i]=k; }
        return m;
    }
    template<size_t N>
    consteval std::array<uint8_t,N> enc(const char(&s)[N]){
        auto m=aesMask<N>();
        std::array<uint8_t,N> r{};
        for(size_t i=0;i<N;++i) r[i]=uint8_t(s[i])^m[i];
        return r;
    }
    template<size_t N>
    std::string dec(const std::array<uint8_t,N>& e){
        auto m=aesMask<N>();
        std::string s; s.reserve(N-1);
        for(size_t i=0;i<N-1;++i) s.push_back(char(e[i]^m[i]));
        return s;
    }
}
#define ENCSTR(x) ([](){ \
    static constexpr auto _e = cx::enc(x); \
    return cx::dec(_e); }())

std::string b64dec(const std::string& in){
    if(in.empty()) throw std::runtime_error("b64");
    SBuf out((in.size()*3/4)+4);
    EVP_ENCODE_CTX* c=EVP_ENCODE_CTX_new(); int n=0,tot=0;
    EVP_DecodeInit(c);
    if(EVP_DecodeUpdate(c,out.data(),&n,(uint8_t*)in.data(),in.size())<0)
        {EVP_ENCODE_CTX_free(c); throw std::runtime_error("b64u");}
    tot+=n;
    if(EVP_DecodeFinal(c,out.data()+tot,&n)<=0)
        {EVP_ENCODE_CTX_free(c); throw std::runtime_error("b64f");}
    tot+=n; EVP_ENCODE_CTX_free(c);
    return { (char*)out.data(), size_t(tot) };
}

size_t wr(void* c,size_t s,size_t n,std::string* u){u->append((char*)c,s*n);return s*n;}
std::string post(const std::string& url,const std::string& body){
    CURL* h=curl_easy_init(); std::string r;
    curl_easy_setopt(h,CURLOPT_URL,url.c_str());
    curl_easy_setopt(h,CURLOPT_POSTFIELDS,body.c_str());
    curl_easy_setopt(h,CURLOPT_WRITEFUNCTION,wr);
    curl_easy_setopt(h,CURLOPT_WRITEDATA,&r);
    curl_easy_setopt(h,CURLOPT_SSL_VERIFYPEER,1L);
    curl_easy_setopt(h,CURLOPT_CAINFO,"/etc/ssl/certs/ca-certificates.crt");
    if(curl_easy_perform(h)!=CURLE_OK){curl_easy_cleanup(h); throw std::runtime_error("HTTP");}
    curl_easy_cleanup(h); return r;
}

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);
    PBYTE pBuf=nullptr; ULONG len=0;
    if(GetSystemFirmwareTable('RSMB',0,nullptr,0)==0){
        len=GetLastError();
        pBuf=new BYTE[len];
        GetSystemFirmwareTable('RSMB',0,pBuf,len);
        s.append((char*)pBuf,len); delete[] pBuf;
    }
#else
    struct stat st{}; if(stat("/dev/sda",&st)==0) s+=std::to_string(st.st_dev);
    std::ifstream b("/sys/devices/virtual/dmi/id/board_serial"); std::string l;
    if(b && std::getline(b,l)) s+=l;
#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 buf[3]; std::string out;
    for(auto v:h.span()){ snprintf(buf,3,"%02x",v); out+=buf; }
    return out;
}


SBuf kdf(const std::string& hw,std::span<const uint8_t> salt,const std::string& fname){
    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 len=0;
    HMAC(EVP_sha256(),master.data(),master.size(),
         (uint8_t*)fname.data(),fname.size(),key.data(),&len);
    OPENSSL_cleanse(master.data(),master.size());
    return key;
}

using CtxUP=std::unique_ptr<EVP_CIPHER_CTX,decltype(&EVP_CIPHER_CTX_free)>;
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("decUp");
    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};
}

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
}
bool isDbg(){
#ifdef _WIN32
    PPEB peb=(PPEB)__readgsqword(0x60);
    if(peb->BeingDebugged) return true;
    if(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)/ (double) 2900000 * 1000) > CFG_TSC_LIMIT;
}


std::unordered_map<std::string,std::string> tlvRemap(const std::span<const uint8_t> blob){
    std::unordered_map<std::string,std::string> r;
    size_t i=0;
    while(i<blob.size()){
        uint8_t type=blob[i++];
        if(type!=1) break;
        uint16_t l1=blob[i++]<<8 | blob[i++];
        uint16_t l2=blob[i++]<<8 | blob[i++];
        if(i+l1+l2>blob.size()) break;
        std::string k((char*)&blob[i],l1); i+=l1;
        std::string v((char*)&blob[i],l2); i+=l2;
        r[k]=v;
    }
    return r.empty()?std::unordered_map<std::string,std::string>{
        {"a.b.c","com.example.MyModClass"},
        {"x.y.z","com.example.CoreMod"}}:r;
}

void jLoad(JNIEnv* env,
           std::span<const uint8_t> cls,
           const std::string& n)
{
    jbyteArray ba=env->NewByteArray(cls.size());
    env->SetByteArrayRegion(ba,0,cls.size(),(jbyte*)cls.data());

    jclass cl=env->FindClass("com/example/CustomClassLoader");
    jmethodID m=env->GetStaticMethodID(cl,"loadClass","([BLjava/lang/String;)Ljava/lang/Class;");
    jstring jn=env->NewStringUTF(n.c_str());
    env->CallStaticObjectMethod(cl,m,ba,jn);
}


int main(){
    using namespace std::chrono;
    Logger::init(); curl_global_init(CURL_GLOBAL_ALL);

    enum State{S_INIT,S_READ,S_KDF,S_DEC,S_JVM,S_RUN,S_BAD,S_DONE};
    State st=S_INIT;
    std::string hw; SBuf key(0);
    std::vector<uint8_t> plain;
    JavaVM* vm=nullptr; JNIEnv* env=nullptr;
    bool wdRun=true; std::thread wd(watchdogThread,std::ref(wdRun));

    auto go=[&](auto&&self,State s)->int{
        switch(s){
        case S_INIT:{
            hw=hwid(); if(isDbg()){st=S_BAD;break;}
            st=S_READ; break;
        }
        case S_READ:{
            std::ifstream f("encrypted_mod.class",std::ios::binary);
            if(!f){LOG(Err::CLASS,"file"); st=S_BAD; break;}
            std::vector<uint8_t> buf((std::istreambuf_iterator<char>(f)),{});
            if(buf.size()<44){st=S_BAD;break;}
            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());
            key=kdf(hw,salt.span(),"encrypted_mod.class");
            plain=aesDec({buf.data()+44,buf.size()-44},key.span(),iv,tag);
            st=S_DEC; break;
        }
        case S_DEC:{
            if(!isValidClassFile(plain)){st=S_BAD;break;}
            st=S_JVM; break;
        }
        case S_JVM:{
            jsize nVMs=0; JNI_GetCreatedJavaVMs(&vm,1,&nVMs);
            if(nVMs==0){
                JavaVMInitArgs a{}; a.version=JNI_VERSION_1_8;
                JavaVMOption o{(char*)"-Djava.class.path=."};
                a.nOptions=1; a.options=&o;
                if(JNI_CreateJavaVM(&vm,(void**)&env,&a)!=JNI_OK){st=S_BAD;break;}
            }else vm->AttachCurrentThread((void**)&env,nullptr);
            st=S_RUN; break;
        }
        case S_RUN:{
            auto tbl=EncryptedRemapTable::decrypt(hw);
            std::string cls=tbl["a.b.c"];
            jLoad(env,plain,cls);
            std::cout<<"Loaded OK\n";
            st=S_DONE; break;
        }
        case S_BAD:{
            LOG(Err::DEC,"abort"); wdRun=false; wd.join();
            curl_global_cleanup(); return 1;
        }
        case S_DONE:{
            wdRun=false; wd.join(); curl_global_cleanup(); return 0;
        }}
        return self(self,st);
    };
    return go(go,st);
}

encryptor.cpp:
Expand Collapse Copy
#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:
Expand Collapse Copy
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()

CustomClassLoader.java:
Expand Collapse Copy
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:
Expand Collapse Copy
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:
Expand Collapse Copy
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:
Expand Collapse Copy
version: "3.9"

services:
  api:
    build: ./server
    ports: ["8000:8000"]
    secrets: ["server_secret"]

secrets:
  server_secret:
    file: ./server_secret.bin

P.S: Код абсолютно нихуя не идеален, не несу ответственность за седину которая появится на вашей голове, после попытки запуска этой залупы.

Ну усе, жду пиздец в комментариях :roflanPominki: :roflanEbalo:
похоже на говно защиту
 
шляпа какаято
 
жвмти аттач я не резал специально, чтобы было легче тестить. в релизе атач выключен, а класс грузится вайт боксом и байты затираются, так что ClassDumper отвалится.
атач выключил крутой теперь? хук можно поставить, а дальше сам думай куда и как его фиксить
 
так это изи фиксится, тупо кодом на коленке за 3 секунды:
C++:
Expand Collapse Copy
#ifdef _WIN32
#include <windows.h>
void kill_class_hook() {
    HMODULE jvm = GetModuleHandleA("jvm.dll");
    if(!jvm) return;
    unsigned char* base = (unsigned char*)jvm;
    for(size_t off = 0; off < 0x1000000; ++off) {
        if(memcmp(base+off, "should_post_class_file_load_hook", 32)==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;
        }
    }
}

+ твой супер дупер дампер ловит только если JvmtiExport::should_post_class_file_load_hook
остается тру. В релизе я глушу его сразу после JNI_CreateJavaVM() мне просто вот эту хуйню туда нужно запихнутл
 
мертвое полушарие gpt кодеров. я ведь вообще не про этот хук имел ввиду
 
умнейший, я патчил конкретно жфр,тк для хотспот 17+ это самый быстрый способ полнсотью выключить весь jvmti‑стрим до того, как верификатор вообще тронет байткод.
если ты имеешь в виду агент дефайнкласс‑хук, то да -его надо резать отдельно, но ты даже не указал что имеешь ввиду :roflanBuldiga:

тот фикс только жфр ветку затрагивает а не агенты
это моя 2 тема, и если знаешь где конкретно ошибки или дыры , напиши я не против, я по фикшу то что надо
почему
 
Последнее редактирование:
насрано удаляй
 
я обновил, и пропатчил там теперь не жфр ни агентом ни захукаешься. а ты про какой хук то вообще
 
gpt закрой, сурцы jvm открой, может дойдёт
так короче, я понял какой хук ты имеешь ввиду, но у меня щас котел не варит, я тебе с утра пропаченную версию выкачу :roflanPominki:
 
как я уже говорил, я влепил kill_jfr_hook() и kill_jvmti_hook() до создания жвмки ( JNI_CreateJavaVM()), так что твой “мега кряк” даже не просачивается + прописан RX-only для jvm.dll и своего EXE, теперь никакой virtual protect(.., PAGE_READWRITE) не сработает, живем с кайфом. + еще добавил интегритик-чек, мой watchdog каждые 50 мс сверяет первые 16 байт твоих киллеров и бум бум шакалака – процесс умирает при малейшей попытке или каком либо, даже малюсеньком изменении. обсирай меня дальше, мне же на пользу, спасибо! :seemsgood: :roflanBuldiga:
 
IMG_8492.png
 
я чай пить буду
 
Назад
Сверху Снизу