Dr. Call
Syscall tampering using a Language-specific handler and the Trap flag
Project Files
How to run
Dr. Call is packaged as a @config.spec file with a simple base loader:
cpl loader.spec /path/to/file.dll out.bin @drcall.spec
@drcall.spec has expectations of its patients:
- The program output must be PIC with fixptrs set
- The entry point must be go()
- The base program must meet Crystal Palace's requirements to generate stack unwinding data (e.g., frame pointer is present in fixbss/dfr functions)
NOTES
Dr. Call is a syscall parameter tampering exercise. The idea is to call a highly-likely to be hooked function with false parameters that are less likely to attract attention. And, right before control is passed to the kernel (via a syscall instruction), we swap in our real arguments.
The above feat is accomplished with the CPU Trap flag. This flag, which we can set from userland, puts the CPU into single step mode:
When in single step mode, the CPU fires EXCEPTION_SINGLE_STEP for the next instruction. It's up to the exception handler to determine whether or not to re-arm this flag. One way to catch these exceptions is to register a Vectored Exception Handler in the process. And, there's prior work (see Conversation) that acts on this idea with a VEH. There's another way though (that's what this example is about).
Crystal Palace has the option to generate stack unwinding meta-information (.pdata/.xdata) for its x64 programs. In doing this, it also has the option to specify a Language-specific exception handler (LSH) for its functions. When an exception fires, Windows gives VEHs first dibs. If no VEH acts on the exception, Windows then unwinds the call stack with the aid of stack unwinding meta-info made available to the OS. And, if there's a LSH associated with the frame, Windows calls it, and gives that handler a chance to swallow the exception. If it does, yay. The process is done. Otherwise, the search continues.
To make the above work, drcall.spec takes over the entry point of our base loader (setup.c), and immediately calls RtlAddFunctionTable to register the stack unwinding meta-info with the operating system. drcall.spec also uses catch "drcall_go" "DrCall_Handler" to setup the LSH for our program entry point.
The LSH is called a language specific handler, because it's not something our languages let us play with directly. Rather, our compiler bakes a LSH into our programs and its job is to support the language's exception handling semantics. The LSH is a powerful primitive and it has abilities that are similar to (but not fully on par with) a Vectored Exception Handler. LSH has some constraints that a VEH doesn't have too. For example, if we tamper with our call stack return address--Windows won't be able to find our LSH and that'll break the whole process.
Here's an example LSH handler to respond to EXCEPTION_SINGLE_STEP and re-arm the Trap flag (if desired).
Putting the above together, Dr. Call is a pretty simple implementation:
hooks.c implements Crystal Palace hooks for VirtualAlloc and VirtualProtect. drcall.spec uses attach to apply those hooks to a base program. These hooks call the Dr. Call API to configure the real arguments for the NTDLL functions VirtualAlloc and VirtualProtect will eventually call. Once arguments are configured, the hook enables the Trap flag, and calls the intended Win32 API with less suspicious arguments.
Here's the VirtualAlloc hook:
drcall.c contains a LSH similar to the one demonstrated above. When it sees an instruction that's not a SYSCALL, it re-arms the Trap flag. When it sees a SYSCALL, it consults a global array of arguments and determines which ones it needs to update. It makes the update and (crucially) it doesn't re-arm the Trap flag. The program continues execution and the kernel sees our real arguments. One weakness of this implementation is that it does not validate the syscall it encountered is the one we intended to tamper arguments for.
That's the Dr. Call process in a nutshell. I chose this problem as something small and self-contained that demonstrates the Language-specific Handler in Crystal Palace. LSHs are a cool primitive and this is just one possibility with them.
Performance warning
One of the downsides to the Trap flag is firing an exception for each instruction is a lot of overhead. Beware, when you call a Win32 API or use a global variable in an exception handler from PIC, Crystal Palace will call helper functions to make these language features work. Having these execute within a single step trap handler is an incredible amount of overhead.
In drcall.c I took care to trigger on a hard-coded condition first (e.g., the SYSCALL instruction). This way I'm eating the overhead of processing and single-stepping through my fixbss and dfr helpers once.
Conversation
- TamperingSyscalls (2022) by rad9800 introduces using a VEH to tamper with syscall parameters and to recover syscall system service numbers (SSNs). Code is at https://github.com/rad9800/TamperingSyscalls.
- Feeding EDR False Telemetry (2022) by rad9800 describes the possibilities and why of syscall tampering.
- TrapFlagForSyscalling: Bypass user-land hooks by syscall tampering via the Trap Flag (2025) by MalDev Academy shows how to use the Trap flag to tamper with syscall parameters, recover SSNs, and force NTDLL functions to make other syscalls for us.
- Early Exception Handling (2025) by kr0tt surveys VEH abuse, the drawbacks (e.g., why more exception catching means are good), and introduces a method to catch exceptions by overwriting the Wow64PrepareForException pointer in NTDLL. A lot of background information here too.
License
This project is licensed under the BSD License.