Welcome to hackvshack.net Forum!
Download Free HvH CS2/CS:GO Cheats, CFG, LUA/JS Scripts, and More!
Register

C++ Function Custom Model Changer

Tonyha7

Rookie HvHer
User ID:
163005
Messages:
16
Reactions:
10
Badges:
12
REP:
−0/0+
Level:
88
Inspired by a lua for a famous cheat. Have fun!🄵

C++:
Expand Collapse Copy
#include <windows.h>
#include <string>
#include <vector>
#include <filesystem>
#include <thread>
#include <iostream>
#include <cstdarg>
#include <d3d11.h>
#include <dxgi.h>
#include "imgui/imgui.h"
#include "imgui/backends/imgui_impl_win32.h"
#include "imgui/backends/imgui_impl_dx11.h"
#include "minhook/include/MinHook.h"
#pragma comment(lib, "d3d11.lib")
namespace fs = std::filesystem;
// --- Globals ---
bool g_ShowMenu = false;
bool g_NeedSetModel = false;
int g_SelectedIdx = 0;
std::vector<std::pair<std::string, std::string>> g_Models;
uintptr_t client_base = 0;
uintptr_t resourcesystem_base = 0;
uintptr_t tier0_base = 0;
void* (*fnSetModel)(void*, const char*) = nullptr;
void* (*fnPrecache)(void*, void*, const char*) = nullptr;
void* IRS = nullptr;
// --- Logging ---
void Log(const char* fmt, ...) {
    va_list args;
    va_start(args, fmt);
    printf("[FutaModels] ");
    vprintf(fmt, args);
    printf("\n");
    va_end(args);
}
// --- CBufferString ---
struct CBufferString {
    int m_nLength;
    int m_nAllocatedSize;
    union {
        char* m_pString;
        char m_szString[8];
    };
   
    CBufferString() : m_nLength(0), m_nAllocatedSize(0x80000000 | 0x40000000 | 8) {
        m_pString = nullptr;
    }
   
    static const char* (__fastcall* fnInsert)(void* ecx, int, const char*, int, bool);
   
    void Insert(int index, const char* str, int length, bool b1) {
        if (fnInsert) fnInsert(this, index, str, length, b1);
    }
};
const char* (__fastcall* CBufferString::fnInsert)(void*, int, const char*, int, bool) = nullptr;
// --- Utility ---
uintptr_t FindPattern(const char* moduleName, const char* pattern) {
    HMODULE hModule = GetModuleHandleA(moduleName);
    if (!hModule) {
        Log("Failed to get handle for %s", moduleName);
        return 0;
    }
   
    auto pattern_to_byte = [](const char* pattern) {
        auto bytes = std::vector<int>{};
        auto start = const_cast<char*>(pattern);
        auto end = const_cast<char*>(pattern) + strlen(pattern);
        for (auto current = start; current < end; ++current) {
            if (*current == '?') {
                ++current;
                if (*current == '?') ++current;
                bytes.push_back(-1);
            } else {
                bytes.push_back(strtoul(current, &current, 16));
            }
        }
        return bytes;
    };
    auto dosHeader = (PIMAGE_DOS_HEADER)hModule;
    auto ntHeaders = (PIMAGE_NT_HEADERS)((std::uint8_t*)hModule + dosHeader->e_lfanew);
    auto sizeOfImage = ntHeaders->OptionalHeader.SizeOfImage;
    auto patternBytes = pattern_to_byte(pattern);
    auto scanBytes = reinterpret_cast<std::uint8_t*>(hModule);
    auto s = patternBytes.size();
    auto d = patternBytes.data();
    for (auto i = 0ul; i < sizeOfImage - s; ++i) {
        bool found = true;
        for (auto j = 0ul; j < s; ++j) {
            if (scanBytes[i + j] != d[j] && d[j] != -1) {
                found = false;
                break;
            }
        }
        if (found) return reinterpret_cast<uintptr_t>(&scanBytes[i]);
    }
    Log("Pattern not found in %s", moduleName);
    return 0;
}
void* GetInterface(const char* moduleName, const char* interfaceName) {
    typedef void* (*CreateInterfaceFn)(const char*, int*);
    CreateInterfaceFn CreateInterface = (CreateInterfaceFn)GetProcAddress(GetModuleHandleA(moduleName), "CreateInterface");
    if (!CreateInterface) return nullptr;
    return CreateInterface(interfaceName, nullptr);
}
// --- Engine Interaction ---
void* GetLocalPlayerInstance() {
    static uintptr_t dwLocalPlayerPawn = 0x233F698; // <--- CHANGE THIS IF THE GAME UPDATED
   
    if (!client_base) return nullptr;
   
    uintptr_t localPawn = *(uintptr_t*)(client_base + dwLocalPlayerPawn);
    if (!localPawn) return nullptr;
   
    return (void*)localPawn;
}
void PrecacheResource(const std::string& path) {
    if (!fnPrecache || !IRS) return;
    CBufferString names;
    names.Insert(0, path.c_str(), -1, false);
    fnPrecache(IRS, &names, "");
}
void ChangeModelNow() {
    if (g_SelectedIdx <= 0 || g_SelectedIdx >= g_Models.size()) return;
    std::string path = g_Models[g_SelectedIdx].second;
    if (path.empty()) return;
    void* pawn = GetLocalPlayerInstance();
    if (!pawn || !fnSetModel) {
        if (!pawn) Log("Local Player Pawn not found. Can't set model.");
        if (!fnSetModel) Log("SetModel function not hooked properly.");
        return;
    }
    Log("Applying model: %s", path.c_str());
    PrecacheResource(path);
    fnSetModel(pawn, path.c_str());
    g_NeedSetModel = false;
}
// --- Hooks ---
typedef void(__thiscall* FrameStageNotify_t)(void*, int);
FrameStageNotify_t oFrameStageNotify = nullptr;
void __fastcall hkFrameStageNotify(void* ecx, int stage) {
    if (stage == 6 /* FRAME_RENDER_END */ && g_NeedSetModel) {
        ChangeModelNow();
    }
    return oFrameStageNotify(ecx, stage);
}

typedef HRESULT(__stdcall* Present_t)(IDXGISwapChain*, UINT, UINT);
Present_t oPresent = nullptr;
HWND window = nullptr;
ID3D11Device* pDevice = nullptr;
ID3D11DeviceContext* pContext = nullptr;
ID3D11RenderTargetView* mainRenderTargetView = nullptr;
WNDPROC oWndProc;
extern LRESULT ImGui_ImplWin32_WndProcHandler(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);
LRESULT __stdcall WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) {
    if (msg == WM_KEYUP && wParam == VK_HOME) {
        g_ShowMenu = !g_ShowMenu;
        if (!g_ShowMenu && ImGui::GetCurrentContext()) {
            ImGuiIO& io = ImGui::GetIO();
            memset(io.MouseDown, 0, sizeof(io.MouseDown));
           
            io.ClearInputKeys();
            io.KeyCtrl = io.KeyShift = io.KeyAlt = io.KeySuper = false;
        }
        return 1;
    }
   
    if (g_ShowMenu) {
        ImGui_ImplWin32_WndProcHandler(hWnd, msg, wParam, lParam);
       
        if (msg >= WM_MOUSEFIRST && msg <= WM_MOUSELAST) return 1;
        if (msg >= WM_KEYFIRST && msg <= WM_KEYLAST) return 1;
        if (msg == 0x00FF) return 1;
    }
   
    return CallWindowProc(oWndProc, hWnd, msg, wParam, lParam);
}
HRESULT __stdcall hkPresent(IDXGISwapChain* pSwapChain, UINT SyncInterval, UINT Flags) {
    static bool init = false;
    if (!init) {
        if (SUCCEEDED(pSwapChain->GetDevice(__uuidof(ID3D11Device), (void**)&pDevice))) {
            pDevice->GetImmediateContext(&pContext);
            DXGI_SWAP_CHAIN_DESC sd;
            pSwapChain->GetDesc(&sd);
            window = sd.OutputWindow;
           
            ID3D11Texture2D* pBackBuffer;
            pSwapChain->GetBuffer(0, __uuidof(ID3D11Texture2D), (LPVOID*)&pBackBuffer);
            pDevice->CreateRenderTargetView(pBackBuffer, NULL, &mainRenderTargetView);
            pBackBuffer->Release();
           
            oWndProc = (WNDPROC)SetWindowLongPtr(window, GWLP_WNDPROC, (LONG_PTR)WndProc);
           
            ImGui::CreateContext();
            ImGuiIO& io = ImGui::GetIO();
            io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard;
           
            ImFontConfig font_cfg;
            font_cfg.FontDataOwnedByAtlas = false;
            io.Fonts->AddFontFromFileTTF("c:\\windows\\fonts\\msyh.ttc", 16.0f, &font_cfg, io.Fonts->GetGlyphRangesChineseFull());
            ImGui_ImplWin32_Init(window);
            ImGui_ImplDX11_Init(pDevice, pContext);
            init = true;
            Log("ImGui and DX11 Initialized.");
        }
    }
   
    if (init && g_ShowMenu) {
        ImGui_ImplDX11_NewFrame();
        ImGui_ImplWin32_NewFrame();
        ImGui::NewFrame();
       
        ImGui::Begin(u8"FutaModels");
        if (ImGui::BeginCombo(u8"CHOOSE A MODEL", g_Models[g_SelectedIdx].first.c_str())) {
            for (int i = 0; i < g_Models.size(); i++) {
                bool is_selected = (g_SelectedIdx == i);
                if (ImGui::Selectable(g_Models[i].first.c_str(), is_selected)) {
                    g_SelectedIdx = i;
                    if (i != 0) {
                        g_NeedSetModel = true;
                        Log("Model Selected: %s", g_Models[i].first.c_str());
                    } else {
                        Log("Model Changer Disabled");
                    }
                }
                if (is_selected) ImGui::SetItemDefaultFocus();
            }
            ImGui::EndCombo();
        }
        if (ImGui::Button(u8"REFRESH MODELS")) {
            void ScanModels();
            ScanModels();
        }
        ImGui::End();
       
        ImGui::Render();
        pContext->OMSetRenderTargets(1, &mainRenderTargetView, NULL);
        ImGui_ImplDX11_RenderDrawData(ImGui::GetDrawData());
    }
    return oPresent(pSwapChain, SyncInterval, Flags);
}
// --- Initialization ---
void ScanModels() {
    g_Models.clear();
    g_Models.push_back({u8"[ OFF ]", ""});
   
    char cwd[MAX_PATH];
    GetCurrentDirectoryA(MAX_PATH, cwd);
    std::string root = cwd;
   
    auto pos = root.find("bin\\win64");
    if (pos != std::string::npos) {
        root.replace(pos, 9, "csgo\\characters\\models");
    }
   
    Log("Scanning models in root: %s", root.c_str());
   
    int scannedCount = 0;
    if (fs::exists(root)) {
        for (auto& p : fs::recursive_directory_iterator(root)) {
            if (p.path().extension() == ".vmdl_c") {
                std::string full = p.path().string();
                std::string rel = full.substr(full.find("characters\\"));
                std::replace(rel.begin(), rel.end(), '\\', '/');
                rel = rel.substr(0, rel.find(".vmdl_c")) + ".vmdl";
               
                std::string fname = p.path().stem().string();
                g_Models.push_back({fname, rel});
                scannedCount++;
            }
        }
    }
    Log("Scan completed. Found %d custom models.", scannedCount);
}
bool InitDXGIHook() {
    WNDCLASSEXA wc = { sizeof(WNDCLASSEXA), CS_CLASSDC, DefWindowProcA, 0L, 0L, GetModuleHandleA(NULL), NULL, NULL, NULL, NULL, "DXGI_Dummy", NULL };
    RegisterClassExA(&wc);
    HWND hWnd = CreateWindowA("DXGI_Dummy", "DXGI Dummy Window", WS_OVERLAPPEDWINDOW, 100, 100, 100, 100, NULL, NULL, wc.hInstance, NULL);
    DXGI_SWAP_CHAIN_DESC sd;
    ZeroMemory(&sd, sizeof(sd));
    sd.BufferCount = 1;
    sd.BufferDesc.Width = 2;
    sd.BufferDesc.Height = 2;
    sd.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
    sd.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
    sd.OutputWindow = hWnd;
    sd.SampleDesc.Count = 1;
    sd.Windowed = TRUE;
    D3D_FEATURE_LEVEL featureLevel;
    const D3D_FEATURE_LEVEL featureLevelArray[2] = { D3D_FEATURE_LEVEL_11_0, D3D_FEATURE_LEVEL_10_0, };
    IDXGISwapChain* swapChain = nullptr;
    ID3D11Device* dummyDevice = nullptr;
    ID3D11DeviceContext* dummyContext = nullptr;
    HRESULT res = D3D11CreateDeviceAndSwapChain(NULL, D3D_DRIVER_TYPE_HARDWARE, NULL, 0, featureLevelArray, 2, D3D11_SDK_VERSION, &sd, &swapChain, &dummyDevice, &featureLevel, &dummyContext);
   
    if (FAILED(res)) {
        DestroyWindow(hWnd);
        UnregisterClassA(wc.lpszClassName, wc.hInstance);
        Log("Failed to create dummy D3D11 Device and SwapChain.");
        return false;
    }
    void** pVTable = *reinterpret_cast<void***>(swapChain);
    void* pPresent = pVTable[8];
    MH_CreateHook(pPresent, &hkPresent, (LPVOID*)&oPresent);
    MH_EnableHook(pPresent);
    Log("DXGI Present Hooked locally.");
    swapChain->Release();
    dummyDevice->Release();
    dummyContext->Release();
    DestroyWindow(hWnd);
    UnregisterClassA(wc.lpszClassName, wc.hInstance);
    return true;
}
DWORD WINAPI MainThread(LPVOID lpReserved) {
    AllocConsole();
    FILE* fDummy;
    freopen_s(&fDummy, "CONOUT$", "w", stdout);
    Log("DLL Attached. Initializing...");
    // Wait for modules
    while (!(client_base = (uintptr_t)GetModuleHandleA("client.dll"))) Sleep(100);
    while (!(resourcesystem_base = (uintptr_t)GetModuleHandleA("resourcesystem.dll"))) Sleep(100);
    while (!(tier0_base = (uintptr_t)GetModuleHandleA("tier0.dll"))) Sleep(100);
    Log("Found Engine Modules");
    fnSetModel = (void*(*)(void*, const char*))FindPattern("client.dll", "40 53 48 83 EC 20 48 8B D9 4C 8B C2 48 8B 0D ?? ?? ?? ?? 48 8D 54 24");
    if (fnSetModel) Log("Found fnSetModel");
    uintptr_t ci_addr = FindPattern("resourcesystem.dll", "4C 8B 0D ?? ?? ?? ?? 4C 8B D2 4C 8B D9");
    if (ci_addr) {
        typedef void* (*CreateInterfaceFn)(const char*, int*);
        IRS = ((CreateInterfaceFn)ci_addr)("ResourceSystem013", nullptr);
        if (IRS) Log("Found ResourceSystem");
    }
   
    uintptr_t bload_addr = FindPattern("resourcesystem.dll", "40 53 55 57 48 81 EC 80 00 00 00 48 8B 01 49 8B E8 48 8B FA");
    fnPrecache = (void*(*)(void*, void*, const char*))bload_addr;
    if (fnPrecache) Log("Found fnPrecache");
   
    CBufferString::fnInsert = (const char*(__fastcall*)(void*, int, const char*, int, bool))GetProcAddress((HMODULE)tier0_base, "?Insert@CBufferString@@QEAAPEBDHPEBDH_N@Z");
   
    ScanModels();
   
    MH_Initialize();
   
    void* Source2Client = GetInterface("client.dll", "Source2Client002");
    if (Source2Client) {
        void** vtable = *(void***)Source2Client;
        MH_CreateHook(vtable[36], &hkFrameStageNotify, (LPVOID*)&oFrameStageNotify);
        MH_EnableHook(vtable[36]);
        Log("FrameStageNotify Hooked.");
    }
   
    InitDXGIHook();
    Log("Initialization Complete. Press HOME to show/hide menu.");
    return 1;
}
BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved) {
    if (ul_reason_for_call == DLL_PROCESS_ATTACH) {
        DisableThreadLibraryCalls(hModule);
        CreateThread(nullptr, 0, MainThread, nullptr, 0, nullptr);
    }
    return TRUE;
}
 
Last edited by a moderator:
Thank you for sharing. By the way for anyone wanting this, I have reversed the arm changer as well. Arms chams are implemented separately by hooking SceneSystem’s GeneratePrimitives. After the original render call, objects with designer name cs2_hudmodel_arms are identified, and both mesh material pointers are replaced with a runtime created VMAT through VMaterialSystem2_001. Arms use only the normal visible pass. Style, RGBA color, and enabled state changed settings rebuild the material under a unique name to bypass material caching.
 
Thank you for sharing. By the way for anyone wanting this, I have reversed the arm changer as well. Arms chams are implemented separately by hooking SceneSystem’s GeneratePrimitives. After the original render call, objects with designer name cs2_hudmodel_arms are identified, and both mesh material pointers are replaced with a runtime created VMAT through VMaterialSystem2_001. Arms use only the normal visible pass. Style, RGBA color, and enabled state changed settings rebuild the material under a unique name to bypass material caching.
this is so much better than how i rendered hand models as entity props bc i couldn't find much info about how to do it, i finally got it working after like a bit
 

Who has read this thread (Total: 1) in last 1 hours View details