I just realised today that the "interceptor function" doesn't
necessarily need to end with a jump to the original function.
[...]
In our MITM DLL file, when either "OpenSocket" or "CloseSocket" is
called, we want to lock a mutex, then call the original function, then
unlock the mutex, then return. This scenario is a little more
complicated than what I described in my original post because the
"interceptor function" won't end with a jump to the original function
(instead it will end with a jump back to the caller).
[...]
In C++26, if we were to have "interceptor functions", then the above
assembler would become:
std::mutex m;
auto OpenSocket(auto) interceptor
{
WriteLog( "Function called 'OpenSocket'");
m.lock();
auto const h = LoadLibraryA("transport.dll.original");
auto const pf = (void(*)(void))GetProcAddress(h, "OpenSocket");
goto pf;
m.unlock();
goto return;
}
I'm 99% certain that this technique will work on every CPU with every
calling convention. Even when supernumerary arguments are pushed onto
the stack, still the last thing pushed onto the stack is the return
address.
This only works as long as your locals (ASDVs) don't interfere. In fact, if you were to use std::lock_guard<std::mutex> there, it would probably be overwritten on the stack. This is because, on most architectures, parameters and locals (ASDVs) use the same stack. Now, you might say this should work:
std::mutex m;
auto OpenSocket(auto&&... args) /* interceptor */
{
WriteLog( "Function called 'OpenSocket'");
std::lock_guard<std::mutex> guard(m);
auto const h = LoadLibraryA("transport.dll.original");
auto const pf = (void(*)(void))GetProcAddress(h, "OpenSocket");
return pf(std::forward<decltype(args)>(args)...);
}
You simply need to ask for this to go to the DLL, without any further instantiations. That's a change, that might or might not be doable - note that, due to locals, there's a stack frame of the interceptor that exists in a stack-based implementation while calling pf but before returning from the interceptor.
But note, this needs moves (or any other kind of forwarding, which might or might not be detectable in the code of the DLL being loaded). If you don't want locals of the interceptor to interfere, then you can't have locals (that cannot be overwritten), and that's a serious limitation.
Also, it's not apparent whether the goto pf in this mail is supposed to mean the same thing as in the original mail or not. Originally, you expected it to replace the current function; now, you expect it like a call, where we can return to.
Thanks,
-lorro