neugierig.org: Tech Notes

submited by
Style Pass
2024-09-19 01:00:03

The "Windows emulator" half of retrowin32 is an implementation of the Windows API, which means it provides implementations of functions exposed by Windows. I've recently changed how calls from emulated code into these native implementations works and this post gives background on how and why.

Suppose we have some 32-bit x86 executable that thinks it's calling the former. retrowin32's machinery connects it through to the latter — even though the implementation might be running on 64-bit x86, a different CPU architecture, or even the web platform.

Before we get into the lower-level x86 details, you might first notice that those function prototypes don't even take the same arguments — nNumberOfBytesToWrite disappeared.

The dllexport annotation in the Rust code above causes a code generator to generate an interop function that knows how to read various Rust types from the stack. For example for WriteFile the code it generates looks something like the following, which maps the pointer+length pair into a Rust slice:

There's a bit more detail around how the return value makes it into the eax register and how the stack gets cleaned up, but that is most of it. With these in place, the remaining question is how to intercept when the executable is making a DLL call and to pass it through to this implementation.

Leave a Comment