Tutorial OpenGL BlendBox and Custom Textures

Hexui Undetected CSGO Cheats PUBG Accounts


Trump Tier Donator
Dank Tier Donator
Oct 19, 2018
How long you been coding/hacking?
2 / 1.5 years
Coding Language
C++ 17
I will be explaining the gist of some of these functions and how to use them but I cannot cure retardation, even my own, so do the GHB first.
Sorry if this tutorial is a bit ass as I'm shit at explaining things.

Part 1: BlendBox
Assault Cube uses opengl intermediate mode to render a lot of things, which means they have opengl functions defined somewhere in their code. By using a de compiler, we can find the functions and search for cross-references. I cross-referenced glVertex2f to find some basic draw functions.

One of the functions I stumbled upon was BlendBox, which is a multi purpose box drawing function. The function is essentially all opengl so basic knowledge will allow you to fully reverse it. The function parameters are (int x1, int y1, int x2, int y2, bool border, int texture, float* color).

Even though in the source code -1 is used to denote no texture, it absolutely fucks up the entire screen so always just pass in 0 for no texture instead. An issue that will occur is that the border will always be gray due to being hard coded. I the assembly there is this call to glColor3f, which uses float stack manipulation.

To make a long story of googling short we just need to NOP the bytes that handle the float stack and pointer, so the border uses the color we assigned earlier. If you want independent colors you would need to write the assembly into the else part of the (0 < texture) utilizing it as a code cave.
typedef void* (__cdecl* tBlendBox) (int x1, int y1, int x2, int y2, bool border, int texture, float* color);

tBlendBox BlendBox = reinterpret_cast<tBlendBox>(Offsets::moduleBase + 0x004b30);

void BlendBoxColor(int x1, int y1, int x2, int y2, bool border, int tex = -1, float* c = NULL)
    //noping the float stack manipulation
    mem::Nop(reinterpret_cast<BYTE*>(Offsets::moduleBase + 0x004e30), 26);
    AC::BlendBox(x1, y1, x2, y2, border, tex, c);
    mem::Patch(reinterpret_cast<BYTE*>(Offsets::moduleBase + 0x004e30), reinterpret_cast<BYTE*>(const_cast<char*>("\xd9\x05\xc0\xe2\x4e\x00\x83\xec\x0c\xd9\x54\x24\x08\xd9\x54\x24\x04\xd9\x1c\x24\xff\x15\x90\xa2\x4d\x00")), 26);

Part 2: Texture loading
Texture loading is a multi step process that includes many functions within Assault Cube. So to make matters easy were simply going to find the first or "top" function, which calls everything else. This function is simply called TextureLoad, which creates a new texture object if you're loading a new texture or returns a pointer to the already loaded one. The parameters of the function are simply (const char* name, int clamp, float scale), and we only really care about the name. Due to compiler optimizations the name is passed through the ecx register so I've provided a wrapper function that Rake helped me out with a while back. The texture is stored in a struct which I copy and pasted from the source code cause I'm a lazy fuck who didn't want to bother reversing it.
struct ac_Texture
    char* name;
    int xs, ys, bpp, clamp;
    float scale;
    bool canreduce;
    unsigned int id;

typedef unsigned int** (__cdecl* tTextureLoad) (int unused, int clamp, const char* filePath);

tTextureLoad TextureLoad = reinterpret_cast<tTextureLoad>(Offsets::moduleBase + 0x01E480);

ac_Texture* GL::LoadImageToTexture(const char* name, int clamp, float scale)
    ac_Texture* tex{ new ac_Texture() };
    DWORD string{ reinterpret_cast<DWORD>(name) };
        push 0;
        push scale;
        push clamp;
        xor bl, bl;
        mov eax, string;
        call GL::AC::TextureLoad;
        add esp, 0xc;
        mov[tex], eax;
    return tex;
With this we can now load in existing textures or our own custom ones.
*Disclaimer, while it can load custom textures, those textures must be placed in the same folder path as the real textures. I haven't figured out how assault cube handles the file path you pass into it, so the path of least resistance is to simply dump your own textures into the assault cube texture folder. While messy, it works and I haven't tested it with file path that don't originate at the assault cube directory.

Part 3: Global Textures
There are two ways that Assault Cube stores textures internally. The first and simplest way they are stored is simply as global variables. A lot of the base textures for Assault Cube like blood, muzzle flash, loading screens, and icons. Essentially most things in the packages\misc directory. To change them all you need to do is find the address of the global, which I recommend just cross referencing TextureLoad, and create a pointer to it and overwrite the texture with your own. There are also some which are stored in a global array, like the sky box. It's the same process except it's now an array.
ac_Texture** skyTextures = reinterpret_cast<ac_Texture**>(Offsets::moduleBase + 0x109d40);

static ac_Texture* obamaTex{ GL::LoadImageToTexture("packages/misc/obama.jpg") };
*Offsets::skyTextures[0] = *obamaTex;
*Offsets::skyTextures[1] = *obamaTex;
*Offsets::skyTextures[2] = *obamaTex;
*Offsets::skyTextures[3] = *obamaTex;
*Offsets::skyTextures[4] = *obamaTex;
*Offsets::skyTextures[5] = *obamaTex;
Part 4: Model and Map Textures
This is the other way that Assault Cube stores textures, and it's a lot more annoying. Textures are stored as member variables of classes or structs which are dynamically loaded into an array or HashSet and dynamically manipulated through said medium. So we can't just overwrite a few globals and call it a day. my solution for this problem was to simply hook the rendering functions that handle drawing the models. I found these by playing spot the difference between the source code and de compiled mess I had, so not exactly a whole lot of reverse engineering going around. For models we can just change the texture parameter as it goes through our hook. Note that the texture id has to be negative because it's stored that way for some reason. In the code the texture is negated before being loaded, so I simply made my texture negative to cancel it out.

For map models I took the path of least resistance, simply noping the glBindTextures call in the RenderStrips function, hooking it and binding my own texture beforehand. With this you should be able to finally make Obama Cube... mostly.
typedef void* (__cdecl* tRenderEnt) (char* name, float param_2, int texture, int* param_4, float rotation, float param_6, float param_7, float param_8, float param_9, int* param_10, float param_11);

tRenderEnt RenderEnt;

void* __cdecl hRenderEnt(char* name, float param_2, int texture, int* param_4, float rotation, float param_6, float param_7, float param_8, float param_9, int* param_10, float param_11);

typedef void* (__cdecl* tRenderStrips) ();

tRenderStrips RenderStrips;

void* __cdecl hRenderStrips();

tRenderEnt RenderEnt = reinterpret_cast<tRenderEnt>(Offsets::moduleBase + 0x014350);

void* __cdecl hRenderEnt(char* name, float param_2, int texture, int* param_4, float rotation, float param_6, float param_7, float param_8, float param_9, int* param_10, float param_11)
    static ac_Texture* obamaTex{ GL::LoadImageToTexture("packages/misc/obama.jpg") };
    if (name)
        int tmp{ static_cast<int>(obamaTex->id) };
        texture = -tmp;

    return RenderEnt(name, param_2, texture, param_4, rotation, param_6, param_7, param_8, param_9, param_10, param_11);

tRenderStrips RenderStrips = reinterpret_cast<tRenderStrips>(Offsets::moduleBase + 0x01260);

void* __cdecl hRenderStrips()
    //nop the glBindTexture call
    mem::Nop(reinterpret_cast<BYTE*>(Offsets::moduleBase + 0x12f8), 12);
    static ac_Texture* obamaTex{ GL::LoadImageToTexture("packages/misc/obama.jpg") };
    glBindTexture(GL_TEXTURE2D, obamaTex->id);
    return RenderStrips();

Part 5: Fargo and stuff

I didn't cover how to change the texture of the weapons as that seems to be an independent function and I'm lazy. However my guess is that it would be quite similar to the RenderStrips function. So you can probably find it in the gl_drawframe function at 0x407010. I've also included the source of my GHB project Fargo. I been working on it off and on for a while as I worked through the GHB and was going to release it but I got sidetracked by textures. Parts of the code are quite proprietary because of that and theres random debugging or testing shit in all the wrong places. For that reason I won't include a dll or post it on github. It uses Imgui for the menu and nlohmann json to manage configs. In order to compile it you will need to provide the path of config.json to the config class and delete the GL::LoadImageToTexture calls which are littered throughout the code, as they reference images I put in the assault cube directory. The function where you need to change the filepath is called Hack::LoadConfig() line 296 in hack.cpp. However due remember that it's mostly for reference and if it doesn't compile it doesn't compile. If you don't care about learning at all and just want an Assault Cube hack I recommend OBDR's.

VirusTotal (0/61)
If I have missed something just tell me.


You can download 0 Attachments


I'm not your friend
Jan 21, 2014
I love this, every game needs an obama hack, thank you for sharing!


Biggest paster
Dank Tier VIP
Trump Tier Donator
Feb 19, 2018
This is amazing. Really detailed and well written tutorial that’s easy to follow. Thank you for sharing.

Similar threads

Community Mods