Welcome to the first post of my new series, "Shelved Projects". In this series, I dig into the digital attic to share projects that were technically sophisticated, challenging, and highly functional, but were ultimately archived or put on the shelf.
Today, we're talking about Windows Automata (WA)—an enterprise-grade, sandboxed UI automation framework designed to orchestrate Windows applications and browsers securely and robustly.
The Vision: Safe and Isolated Desktop Automation
When you run automated agents—especially those executing dynamic, complex, or third-party workflows—you expose the host system to potential risks. An automation script or automated application could delete critical files, corrupt registry settings, or access private directories.
I wanted a framework that could:
- Traverse and interact with native Windows desktop applications programmatically.
- Isolate the execution completely, ensuring that the target applications under automation could never modify the host file system or write to registry keys.
To achieve this, I combined a C# UI Automation server with a custom-built user-mode sandbox injector (wuias_shield.dll). This sandbox intercepted and redirected all file and registry modifications on the fly, creating a lightweight, isolated workspace for every automated run.
Here is what the architecture looked like at a high level:
The Engineering Challenge: Hooking Chrome Without Triggering Security Crashes
The hardest part of this project wasn't mapping the UI tree or writing the JSON-RPC pipe handler. It was API Hooking inside modern web browsers.
To sandbox an application (like Chrome) in user-mode, you have to intercept system calls like NtCreateFile and NtCreateKey.
Normally, developers use Inline Hooking (also known as a Detour or Trampoline). This involves overwriting the first few bytes of the target function's machine code in memory with a JMP instruction pointing to your hooked function.
However, modern browsers implement Arbitrary Code Guard (ACG) and other Code Integrity mitigations. ACG prevents pages from being marked as writable and executable at the same time (PAGE_EXECUTE_READWRITE). If your DLL tries to write to the assembly code section of ntdll.dll or kernel32.dll to install a trampoline, Chrome will immediately crash with a status violation, or the OS container will reboot.
The Solution: Import Address Table (IAT) Hooking
To bypass ACG, we abandoned inline hooking and built an Import Address Table (IAT) Hooking Engine.
How does IAT Hooking work conceptually?
When a compiled PE (Portable Executable) binary calls a function from an external DLL (like ntdll.dll), it doesn't call the function address directly. Instead:
- The binary points to a special lookup table called the Import Address Table (IAT).
- The IAT is filled with pointers to the actual function addresses, resolved by the OS loader at runtime.
- Crucially, the IAT resides in a writable data section (
.rdata) rather than the read-only, execute-only code section (.text).
Instead of rewriting assembly instructions in the code segment, IAT Hooking simply changes the function pointer inside this table to point to our custom hooked function. Because the table is writable, Chrome's Arbitrary Code Guard (ACG) doesn't flag it, and the application remains perfectly stable.
[Normal Flow]
App Code ---> Call [IAT Pointer] ---> Original API (ntdll.dll)
[Hooked Flow]
App Code ---> Call [IAT Pointer] ---> Hooked API (wuias_shield.dll) ---> Original API (ntdll.dll)
|
(Swapped Pointer)
Traversing Modules via the PEB (Process Environment Block)
To apply this hook to every module loaded by Chrome, I had to find them all. I couldn't use standard Windows APIs like CreateToolhelp32Snapshot because they can be blocked in hardened sandboxes, or trigger deadlocks in suspended threads.
By reading the PEB pointer directly from CPU registers (specifically the GS register on x64 Windows systems) using assembly intrinsics, my injected DLL could traverse this linked list manually in memory. This allowed the hook injector to locate the base address of every loaded module and patch its IAT table without calling standard Windows APIs that could be intercepted by security policies or trigger loader-lock deadlocks.Technical Deep Dive: The PEB Structure
The Process Environment Block (PEB) is an internal, undocumented (or semi-documented) Windows data structure containing metadata about the active process. One of its key fields, Ldr, points to a loader data structure (PEB_LDR_DATA) which maintains double-linked lists of all loaded modules (such as InMemoryOrderModuleList).
Dynamic Hook Propagation
What happens when Chrome dynamically loads a new DLL while running? If my hooking engine only ran once at startup, the newly loaded DLL would bypass the sandbox entirely.
To solve this, I hooked LdrLoadDll (the internal NT API that handles library loading). Every time the application loads a new module:
- My hook intercepts the load request.
- I let the original OS loader map the new DLL into memory.
- Before returning control, I trigger my IAT patching engine to scan the newly loaded module and install my hooks.
This dynamic propagation ensures that no matter when a DLL is loaded, it is instantly bound to the sandbox rules.
Sandboxing Files and Registry Concept: Copy-on-Write (CoW)
Once the hooks were securely installed, I implemented Copy-on-Write (CoW) Redirection for both the filesystem and registry:
1. Filesystem Virtualization
Every time the application calls NtCreateFile or NtOpenFile with write access, my hook intercepts the path:
-
Write Requests: If Chrome tries to write to a path (e.g.,
C:\ImportantData\file.txt), I catch the write, create a mirrored directory structure inside my safe<sandbox_root>\redirect\<session_id>\files\, and swap the target path pointer to point to the sandbox. - Read Requests: If the application tries to read a file, I check if a modified copy exists in my sandbox. If it does, we serve the sandboxed version. If it doesn't, we let the read pass through to the real filesystem.
2. Registry Redirection
Windows apps write configuration parameters directly to the Registry. To prevent host contamination, I redirected registry writes to an isolated subkey under the user's registry hive:
- Writes to
HKCU\Software\...orHKLM\Software\...were transparently mapped toHKCU\Software\WUIAS_Sandbox\<session_id>\HKCUor\HKLM.
This meant that the automated browser believed it was modifying standard system settings, while the host OS remained completely untouched.
Why Windows Automata was Shelved
If the project worked so well, why did it end up on the shelf?
The answer is simple: it was succeeded by my next big project. Windows Automata successfully laid the essential engineering foundation and validated the core user-mode sandboxing paradigms I needed. However, my focus and development resources were soon transitioned to build Talent by UnitBuilds (TUB)—which is the subject of Shelved Projects #2.
Windows Automata served its purpose perfectly as a proof of concept, and its architecture directly paved the way for TUB.
Lessons Learned
Building Windows Automata taught me a massive amount about:
- Windows Internals: Walking undocumented OS structures like the PEB and patching portable executables directly in memory.
- Low-Level Synchronization: Dealing with thread-local variables and recursion guards to prevent stack overflows (where a hooked function recursively calls something that triggers the hook again).
- The Trade-offs of User-Mode Virtualization: While user-mode sandboxing is incredibly lightweight, it requires a lot of low-level maintenance compared to kernel-level virtualization or hardware-isolated virtual machines.
Windows Automata was a wild engineering ride. The source code is now archived, but the design patterns—walking the PEB, patching the IAT, and using named pipes for low-overhead telemetry—remain incredibly useful patterns in system programming.
Let me know in the comments: Should I revive this project or keep it shelved?

Top comments (11)
@xulingfeng that was fast 😂
Red badges are my kryptonite. The moment that little red number appears in the top-right corner, my mouse just... clicks. I can't help it.🤣
Haha, same
Actually I don't know about what you are talking about but this projects is real
I can briefly give you short explanation and provide some information if you are open to this
Sure, I read up on your post, what's the flow from agreeing to assignment?
Hi! Your experience caught my attention. I'm currently helping an American development team connect with experienced developers for long-term remote project collaborations. We're looking for committed professionals who are ready to grow with an international team and work on real projects. If this sounds like something you'd be interested in, reply to this comment or send me a message, and I'll gladly provide more information.
Sorry, I was replying to UnitBuilds. Got my threads crossed.
This is a gate, and a clean one. Intercepting NtCreateFile / NtCreateKey, redirecting writes copy-on-write while reads pass through, is the same shape I keep landing on from the agent side block what mutates, let what only observes through, leave the host untouched. You did it at the syscall layer instead of the tool layer. The part I’d underline for anyone building safety layers: hooking LdrLoadDll so a newly loaded DLL gets patched before it runs. That’s the failure mode most people miss. A gate that doesn’t propagate to surfaces that appear after startup isn’t a gate, it’s a gate with a hole, and the dangerous call walks straight through the new module.You and I went back and forth on “gating is too slow” before. This is the other half of that conversation. Revive it, or at least keep the LdrLoadDll propagation pattern alive somewhere.
Some comments may only be visible to logged-in visitors. Sign in to view all comments.