Reverse engineering the AirStrike 3D game series
My nostalgic journey into reverse engineering AirStrike 3D - the first PC game that captured my imagination as a kid. This repository contains tools and research for understanding the game's internals.
AirStrike 3D is a helicopter shoot-em-up series developed by DivoGames (Nizhny Novgorod, Russia) and published through Alawar Entertainment. The engine and all three franchise titles were built by a two-person team.
| Name | Role | Links |
|---|---|---|
| Anton Petrov | Engine architect, CTO & co-founder | |
| Dmitry Zakharov | Co-founder | โ |
Both names are embedded as string literals ({Anton Petrov}, {Dmitry Zakharov}) in the Gulf Thunder executable's credits data. Petrov describes the engine on LinkedIn as "my first game engine featuring a custom scripting language and hardware-accelerated 3D graphics โ powered three titles in the Air Strike 3D franchise".
After DivoGames, Petrov became CTO at Game Insight (2012โ2019, Nizhny Novgorod department), then co-founded Colossi Games in Cyprus (2020โpresent).
Before DivoGames was officially founded (~2004), the initial AirStrike chapters were developed under a group called Deaddybear. Community research on r/airstrike3d found that Deaddybear's earlier game Treasure Mole used a nearly identical .pak archive format โ confirming shared codebase ancestry. Deaddybear also released Bomberman vs Digger (2002).
| Year | Title | Engine Version |
|---|---|---|
| 2002 | AirStrike 3D: Operation W.A.T. | v1.x (OpenGL, Deaddybear era) |
| 2004 | AirStrike 2 | v2.06 (OpenGL 1.1, MSVC 7.0) |
| 2005โ2007 | AirStrike II: Gulf Thunder | v2.71 (Direct3D 8, MSVC 8.0) |
Custom C++ engine with no third-party framework. Uses Quake-style subsystem prefixes:
| Subsystem | Prefix | Examples |
|---|---|---|
| Game logic | G_ |
G_LoadBin, G_LoadLevelList |
| Renderer | R_ |
R_LoadModel, R_RegisterModel, R_RegisterShadow |
| Sound | S_ |
S_Init, S_RegisterSound |
| Window | MW_ |
MW_CreateWindow |
| Version | API | Compiler | Compile timestamp | Rich header |
|---|---|---|---|---|
v2.06 (as3d2.exe) |
OpenGL 1.1 (opengl32.dll, glu32.dll) |
MSVC 7.0 (.NET 2002/2003) | 2004-05-15 10:12:58 UTC |
โ |
v2.71 (Gulf.exe) |
Direct3D 8 (d3d8.dll) |
MSVC 8.0 (VS2005) | 2007-05-15 13:49:28 UTC |
โ |
- BASS โ Audio library. 3D positional audio, EAX effects, MO3/tracker module playback.
- libjpeg โ
Copyright (C) 1996, Thomas G. Lane(found in Gulf exe strings). - zlib + libpng โ PNG texture support.
- Custom scripting language โ Confirmed by Petrov on LinkedIn, no public documentation survived.
| Format | Extension | Description |
|---|---|---|
| Archives | .apk |
Custom encrypted containers (XOR, 1024-byte key table). Not Android APK. |
| Models | .mdl |
Custom 3D format with version checks (R_LoadModel: Illegal model version.) |
| Textures | .tga |
Standard Targa. Organized in gfx/, menu/, tiles/ dirs. |
| Levels | maps/levels.txt |
Plaintext level list (encrypted inside .apk) |
| Audio | .mo3 |
Tracker modules via BASS library |
| Config | config.ini |
Plaintext, stored alongside the executable |
MSVC RTTI type descriptors found in the Gulf binary (e.g. .?AVIntroPageDivoGames@@), confirming C++ with virtual inheritance and RTTI enabled. Divo Master string suggests an internal tool or debug mode.
The v2.06 executable (as3d2.exe, 199,680 bytes) is packed with ASProtect 1.0 by Alexey Solodovnikov.
| Indicator | Value | Meaning |
|---|---|---|
| Entry point | .data section (0x1DB3001) |
Packer stub, not original code |
| EP signature | 60 E8 01 00 00 00 |
PUSHAD + CALL +1 โ textbook ASProtect 1.0 |
| Section flags | All 0xC0000040 (RWX) |
Packer rewrites all section attributes |
.text entropy |
8.00 (maximum) | Fully encrypted/compressed |
| Visible IAT | 3 imports: GetProcAddress, GetModuleHandleA, LoadLibraryA |
Real IAT resolved at runtime |
| Compression | aPLib (LZ77 variant) | See scripts/static_exe_unpacker.py |
| Hashes | MD5: 1ba6f0187c43d07587e5212f1cb14190 |
SHA256: bc68bf37...81fb1a |
- Section wiping โ Original section names erased, all flags set to
0xC0000040. Two.datastubs appended. - aPLib decompression โ Compressed
.textstored in oversized.data(VirtSize 30 MB, RawSize 4 KB). - OEP byte stealing โ First bytes of Original Entry Point executed inside the stub before jumping to
OEP+N. - IAT redirection โ Import calls routed through ASProtect memory; executes first instructions of real API in-place, then jumps mid-body.
- Anti-debug โ
IsDebuggerPresent(), RDTSC timing, SEH breakpoint detection, debugger driverCreateFile()probes. - Checksums โ Code integrity verification to detect runtime patching.
- Anti-disasm โ Junk bytes after
CALLinstructions break linear-sweep disassemblers (W32DASM, SOURCER); IDA handles fine.
Gulf Thunder ships completely unprotected: EP in .text, entropy 6.83, full IAT, developer credits and error strings plainly readable. Much better target for engine analysis.
- r/airstrike3d โ community research & modding
- Ithamar's APK scripts โ updated extraction with text decryption
- QindieGL โ OpenGL-to-D3D wrapper for running on modern Windows
- PCGamingWiki: AirStrike 2 โ compatibility fixes
- xakep.ru: ASProtect taming โ technical packer analysis (Russian)
- ASProtect homepage โ Alexey Solodovnikov's official site
# Extract game assets from encrypted .apk archives
python extract_apk.py pak0.apk # Extracts all files
python pack_apk.py extracted_dir/ new.apk # Repack modified assetspython mdl_obj_converter.py some_file.mdl
python mdl_obj_converter.py some_file.objpython decrypt_save.py decrypt game.bin -o decrypted.bin# Convert MO3 tracker modules to standard audio
sudo dnf install libopenmpt openmpt123
openmpt123 --render file.mo3 --output file.wav# Best TGA texture viewer for Linux
# https://github.com/bluescan/tacentview
tacentview texture.tga# Fix OpenGL extension issues for old games
MESA_EXTENSION_MAX_YEAR=2003 %command%Add this to the game's launch options in Steam.
- Archive Format: Custom encrypted APK containers (not Android APK)
- Executable: ASProtect v1.0 packed (detected via YARA rules)
- Assets: TGA textures, MDL 3D models, MO3 audio modules
- Encryption: XOR cipher with 1024-byte key table
- Clone this repository
- Extract game assets:
python extract_pak.py /path/to/pak0.apk - Browse extracted files in the created directory
- Convert audio files as needed
- download llvm-mingw from https://github.com/mstorsjo/llvm-mingw/releases:
llvm-mingw-YYYYMMDD-ucrt-ubuntu-20.04-x86_64.tar.xzfor win10+ (ucrt)llvm-mingw-YYYYMMDD-msvcrt-ubuntu-20.04-x86_64.tar.xzfor win7+ (legacy crt)
-
extract to repository root in dir
llvm-mingw -
Run
cmake --preset llvm-mingw-i686
cmake --build --preset llvm-mingw-i686note: preset uses
jobs=1due to lld linker deadlock on parallel linking in mingw context
๐ Since the project uses ASProtect 1.0, I decided on Linux using a simple debugger to just walk until we get some kind of loop. The game seems to unpack itself creating some thread, so even the debugger detaches at some moment in ntdll magic ๐ช, so we need just to pause at any moment and get the address of the desired function (loop).
๐ฏ The next step is using x64dbg with DumpEx pluginโdump with the address of main loop function. And that's all!
๐ Stats:
- ๐ฆ Game weights: 31.2 MB
- ๐ In Ghidra project I've marked some of the interesting places:
- ๐ฎ Loading models
- ๐พ Working with saves
- ๐ง Core game mechanics
๐ Usage:
Just clone and open with Ghidraโthe project is ready to explore yourself!
Maybe some time someone will reverse it completely ๐ ๐ฆโก
Educational and preservation purposes only. Respect original copyrights.
MIT - Because knowledge should be free, just like the joy of playing games.
To that old PC that could barely run the game but somehow made it magical anyway.


