MCPcopy Create free account
hub / github.com/Chemiculs/qengine

github.com/Chemiculs/qengine @1.1.1.3

Chat with this repo
repository ↗ · DeepWiki ↗ · release 1.1.1.3 ↗ · + Follow
4,019 symbols 9,181 edges 182 files 1,446 documented · 36% updated 9mo ago1.1.1.3 · 2024-07-25★ 4423 open issues

Browse by type

Functions 3,256 Types & classes 763
What it actually does AI analysis from the code graph — generated when you open this
loading…
README

qengine

                                            qengine

qengine is a Header-Only, Highly Configurable, Compiler-Independent, and largely inlined Binary Obfuscation Toolkit designed for C++ Standard 17 (or higher) Applications for Microsoft Windows, offering ease of use in your projects, while making your output code extremely difficult to understand, especially for classic disassemblers like IDA.

If you are interested in security testing qengine, or downloading further example usage of qengine, please refer to the Research and Development Repository which contains official template projects for these purposes:

qengine Research & Development Branch


What is qengine?


qengine is a polymorphic engine (meaning an engine that takes multiple forms/permutations) created for the Microsoft Windows operating system, designed to make reverse engineering significantly harder.

This project aims to make binaries appear as unique and unrecognizable as possible at each independent execution.

  • qengine is well tested (considering we are a small team) - I currently am unaware of any bugs for LLVM / CLANG, MSVC, and Intel compiler targets for both x86 and x64 release builds.

  • This will NOT prevent static disk signatures of your executables - however, it will make the task of understanding your code from a classic disassembler such as IDA VERY difficult if used properly, and will prevent memory-dump / memory-scan-based signature detections of your binary.

  • This library is fully inlined, employing a minimalist design and maximum performance + reliability, function inlining allows qengine to hide the actual code you are executing behind a wall of cryptographic instructions and protected memory regions

qengine is very lightweight and from my personal benchmarks, incurs a ~1.70% average performance ( for qxx_ types ) loss vs. standard library / primitive types on modern CPU's, likewise you will retain most of your application's original performance ( on average ) while simultaneously generating thousands or even millions of junk instructions dilluting your meaningful compiled codebase from the eyes of Reverse-Engineer's.


How does qengine work?


qengine creates obfuscated compiler output by compelling function inlining through a number of means including function modifiers and high inlining depth.

Function inlining is a compiler feature which allows the compiler to expand / copy the contents of a called function, directly into the calling function.

Below is a basic diagram outlining the difference between standard symbolic function compilation, and inlined compilation :

symbolicvsinline

qengine creates instruction-bloated and heavily-inlined wrappers around commonly used primitive and extended datatypes, which create a meaningful and simple way to bring about obfuscation of your code without need for appending unrelated protections or using third-party post-compilation obfuscators.

For a better example, take the below code using a qengine string type, as opposed to a standard library string type:

#include <iostream>

#include <qengine/engine/qengine.hpp>

using namespace qengine;

int main(){

    qtype_enc::e_string MyString("Hello World!");

    std::cout << MyString.get() << std::endl;

    return 0;
}

This code, presuming the compiler manages to comply with qengine's inlining reuests, eactually expands to the following:

#include <iostream>

#include <qengine/engine/qengine.hpp>

using namespace qengine;

int main(){

    [-- qtype_enc::e_string MyString("Hello World!"); --]   EXPAND ---->
    {
        MyString.ctor()                                 <------ Inlined ->
        MyString.set("Hello World!")                            <------ Inlined ->
        qengine::polyc::algorithm( MyString.std_string() )              <------ Inlined ->
        qengine::polyc::register_polyc_pointer( MyString.std_string() )         <------ Inlined ->
        qengine::polyc::internal_do_algo_subroutine( MyString.std_string() )        <------ Inlined
    }

    std::cout << MyString.get() << std::endl;   EXPAND ---->
    {
        MyString.get()                                  <------ Inlined ->
        qengine::polyc::algorithm( MyString.std_string() )              <------ Inlined
        std::cout << MyString.std_string() << std::endl;
        qengine::polyc::algorithm( MyString.std_string() )              <------ Inlined

    }

    return 0;
}

Keep in mind that the above example is only from basic initialization of one local qengine::string variable and one get() accessor invokation -

Each get() and set() accessor call is compelled inline, and each math operation or manipulation of qengine::type variables calls get() and / or set() accessor, seeing as these are rather large functions to begin with, the compiler output can go as far as crashing modern disassemblers.


Is qengine Malicious?


This is a perspective which i have been approached with several times to date and will likely wind up being subject to criticism of eventually (should this project continue to thrive and advance, ofcourse).

I will say that at the moment, while this engine is interesting and powerful, it is not really currently much more effective than many other ( Virtualized ) obfuscation software(s) with VMProtect and Themida likely out-doing qengine in terms of protection of original code by a decent margin.

qengine employ's it's own methods of mutation and similar to other Obfuscation / Virtualization software(s), albeit much less aggressively and entirely optionally, currently only in very specific manners as well which could probably be constrained to a heuristic trait detection eventually.

This project does however, if it has the potential which i believe it may, this could end up becoming something capable of significantly polluting the software ecosystem for a good while - whether this ends up happening or not only time will tell.


Features


  • Runtime stack ( locals will be manipulated directly on the stack through compiler-generated junk code, and will be encrypted with a dynamic key and algorithm decided at each execution )

  • Runtime heap polymorphism ( polymorphic heap allocations are supported )

  • Thorough control-flow obfuscation ( depending on the compiler used and amount of library types used, the IDA control-flow graph will be extremely difficult to read and in many cases fail pseudo-code generation )

  • Cumbersome conditional branching ( extended memory check control flow branching e.g. create indirection for checking valuable information such as product keys etc. )

  • .text / executable section Polymorphism ( .text section dumps will appear different at each runtime which would hypothetically prevent basic static .text dump signature scans by AV's / AC's etc. )

  • PE header wipe/mutation ( headers will be wiped or appear differently at each runtime, in memory )

  • Dynamic / Runtime imports ( hide imports from disk PE image import table )


Polymorphic Encryption Algorithm (polyc)


The backbone to this project has been it's aggressively-inlined polyc encryption algorithm.

While the algorithm has been strong and reliable, before the most recent update it really couldn't be truly labeled 'polymorphic' except in the sense that it generates its own table data and keys at runtime.

The polyc algorithm has been updated to support encrypted function calls to differing encryption subroutines, encapsulating it's xor pass.

polyc holds a global pointer table which is managed by the qxx_type objects - this table registers or retrieves a pointer entry every time you call the algorithm. This pointer descripts which subroutine pointer must be decrypted, called, and encrypted again.

Below is a diagram of how the polyc algorithm currently works, please bear with my bad MSPAINT artwork:

polyc diagram


Setup / Usage


** NOTE: This setup option only works out of the box targetting the MSVC v143 compiler WITH the "Runtime Library" Option set to the default " Multi-threaded DLL (/MD) " build target.

if you wish to target another compiler or Runtime Library version, you MUST first compile ASMJIT and Capstone from their source(s), with the according compiler settings from your target project applied and then replace the library files output with the according target output filename(s) in the /qengine/engine/extern/ folder :

UPDATE: If you are using llvm / clang, there is an alternative llvm / clang compatible build of the static libraries located in the /src/qengine/extern/clang_alternate_libs, set this as your library directory with llvm / clang projects.


asmjit32.lib    //  32-bit release static library build for asmjit

asmjit64.lib    //  32-bit release static library build for asmjit

asmjit_d32.lib  //  32-bit release static library build for asmjit

asmjit_d64.lib  //  32-bit release static library build for asmjit

capstone32.lib  //  32-bit release static library build for capstone

capstone64.lib  //  64-bit release static library build for capstone

IF you are simply using MSVC Compiler v143 or higher, you will NOT need to worry about the above step.

  • Download the repository as a zip file, and extract the /src/qengine folder to your project's main / root directory

  • goto /qengine/extern/ and unzip "asmjit_libs.zip" - make sure all the files within are extracted to this directory

  • Include the qengine header file contained in /qengine/engine/

  • Add /qengine/extern/ to additional library directories (for linking)


Demonstration of Control-Flow Obfuscation


  • "Hello, World!" application BEFORE Polymorphic type -

IDA view of hello world C++ program before polymorphic engine


  • "Hello, World!" application AFTER Polymorphic type - (The control flow chart might be hard to see, but there are 1,000++ sub-routines in the compiled binary)

IDA view of hello world C++ program after polymorphic engine


Compiler-specific Settings and Output


LLVM

Core symbols most depended-on inside this repo

Shape

Method 2,329
Function 927
Class 523
Enum 240

Languages

C++100%

Modules by API surface

src/qengine/extern/asmjit/core/support.h295 symbols
src/qengine/extern/asmjit/core/func.h209 symbols
src/qengine/extern/asmjit/core/operand.h198 symbols
src/qengine/extern/asmjit/core/radefs_p.h193 symbols
src/qengine/extern/asmjit/core/builder.h165 symbols
src/qengine/extern/asmjit/core/rapass_p.h131 symbols
src/qengine/extern/asmjit/x86/x86operand.h114 symbols
src/qengine/extern/asmjit/core/codeholder.h104 symbols
src/qengine/extern/asmjit/x86/x86instdb.h102 symbols
src/qengine/extern/asmjit/arm/a64instdb_p.h95 symbols
src/qengine/extern/asmjit/arm/armoperand.h88 symbols
src/qengine/extern/asmjit/core/jitallocator.cpp86 symbols

For agents

$ claude mcp add qengine \
  -- python -m otcore.mcp_server <graph>

⬇ download graph artifact

Ask about this repo answers extend the page