exe32_js — Windows 32-bit JavaScript simulator

· updated

A pure-JavaScript port of the Ruby exe32_rb project — runs entirely in your browser, no server, no WASM, no toolchain. It parses real Windows 32-bit (PE32) EXE files, decodes and executes a substantial i386 instruction subset, resolves IAT imports against stubbed Win32 DLLs (kernel32, user32, msvcrt), and — uniquely — visualises the engine’s own class invocations and data flow live as the program runs.

The default boot image is an interactive Snake game built from a real hand-assembled PE32 binary: the binary is a tight infinite x86 loop calling kernel32!WriteConsoleA on a shared memory buffer at 0x00500000; the game state, AI, and keyboard input live in JS and write the next frame into that buffer on every tick. You can also drag-drop or upload your own .exe.

Open fullscreen ↗

Lineage

exe32_js is the in-browser JavaScript port of exe32_rb, a Ruby Windows-32 EXE emulator. The class taxonomy mirrors the Ruby parent so the engine reads the same shape:

Loader/  DosHeader · NtHeaders · FileHeader · OptionalHeader32 ·
         SectionHeader · ImportDescriptor · DataDirectory · ImageLoader
Memory/  VirtualSpace · Region · Heap · (Reader/Writer inlined)
CPU/     Registers · Decoder · Executor · ModRM/Operand (inlined)
Win32/   ApiDispatch · Handle · Kernel32 · User32 · Msvcrt
Process/ Process · PEB · TEB · CallStack · DynamicLinker · ConsoleOutput

…and the per-field offsets / x86 opcode handlers / flag semantics are verified against Microsoft’s PE/COFF spec and Intel SDM Vol. 2 directly.

The advantage of the JS port: zero install. Open the page, the simulator boots a real Windows EXE in the browser tab.

Five views

ViewShows
OverviewImage summary + Imports/IAT + the running program’s console (full-height when Snake is loaded) + API call log + current instruction + registers — single dashboard
PEFull IMAGE_* header tree (DOS, NT, FileHeader, OptionalHeader32, all 16 DataDirectories, every section, imports per DLL) + raw file hex
ExecuteDisassembly walking forward from EIP + Registers/Flags + Stack with ESP/EBP markers
MemoryVirtual memory region map (Headers / .text / Stack / Heap / PEB / TEB / API_Stubs / SnakeBuffer) + hex view that jumps to any address
ClassesLive class-invocation lattice (every engine class as a node, edges pulse on caller→callee) + scrolling method call sequence

Default app: Snake

The simulator boots into an interactive Snake game. Auto-play is on by default; the AI is a greedy chase (move toward food, avoid walls and self).

KeyAction
PToggle keyboard control ↔ auto-play
W A S D / arrow keysSteer (when keyboard mode is on)
EnterRestart after game over
[Restart] buttonSame, clickable

The Snake “binary” is a real PE32 with a single import (kernel32GetStdHandle, WriteConsoleA, ExitProcess) and ~26 bytes of x86 code in a tight infinite loop:

push -11              ; STD_OUTPUT_HANDLE
call [GetStdHandle]
mov  ebx, eax         ; save handle
.loop:
  push 0
  push 0
  mov  eax, [0x00500000]   ; length
  push eax
  mov  eax, 0x00500008     ; buffer
  push eax
  push ebx
  call [WriteConsoleA]
  jmp  .loop                ; never exits

JS owns the game logic and writes the next ASCII frame (with an ANSI \x1b[2J\x1b[H clear prefix) to 0x00500000 + 8 on every tick. The emulator’s ConsoleOutput.append recognises the clear sequence and wipes its buffer, so the panel becomes a real animated terminal.

Other controls

KeyAction
spaceRun / pause
sStep one instruction
shift+sStep ×10
rReset (restarts current image)
vCycle views
Upload .exe buttonPick a PE32 file from disk
Drag-drop a .exe anywhereSame as upload
Hello World buttonLoad the alternative embedded demo (MessageBoxA + ExitProcess)

Trace example

When the simulator steps a single instruction, the live trace looks like:

Executor.step → Decoder.decode → VirtualSpace.read8 (×3)
              → Executor.readOp → VirtualSpace.read32
              → Executor.writeOp → VirtualSpace.write32
              → ApiDispatch.maybeDispatch
                  → Kernel32.WriteConsoleA → ConsoleOutput.append

…and each arrow lights up on the Classes view.

What’s implemented

Limitations (v1)

How it works under the hood

The simulator is a single HTML file with embedded CSS and JavaScript — no build, no server, no WASM, no toolchain. Everything runs in the browser tab. The engine is split into clean class layers (see “Lineage” above); the visualizer subscribes by reading window.proc and rendering each frame from the live state.

The class-invocation instrumentation works by wrapping every method in every engine class’s prototype at boot with a small shim that records a (caller, callee, method) event into a sliding buffer. The Classes view reads that buffer to draw the lattice and the live sequence ribbon. Toggle to Classes and step through the embedded Hello World (or pause Snake and step) to see the structure emerge.

← systems