Provided as compact DLL and LIB + C/C++ and .NET/C# samples provided.
In-process EXE host for Windows x64. Not another PE mapper. Official name WithExe — the Detours counterpart of withdll: contrary to Detour injecting a DLL into a new process: WithExe maps and hosts an EXE inside the original process.
With.dll loads the target with the OS LoadLibrary path and a few tricks so a Windows EXE can be mapped and run inside the caller. Imports, relocs, manifests, and SEC_IMAGE are the loader’s job — not a from-scratch mapper’s.
This is an instrumentation POC, in the same neighborhood as Detours. The two sample hosts follow the Detours samples/withdll skeleton (parse options, load helper DLLs, start) — Run instead of DetourCreateProcessWithDlls.
Binary mapper, tiny on purpose: With.dll is shipped in binary form — a tiny 2 KB DLL (SOURCE\bin\x64\With.dll, copied next to the hosts under REDISTRIBUTABLES\BIN\x64). There is no mapper source in this tree. Drop it in IDA if you are a specialist — 2 KB is not a secret. If you need customization, look below: change a sample host, or LoadLibrary a helper DLL before the guest. SOURCE\include\With.h is the API; SOURCE\lib\x64 has the import library if you link your own host. Rebuild the hosts if you want; do not rebuild the mapper from here.
NO WARRANTY: This is a proof of concept. Unsupported use of the Windows loader. Provided "AS IS" with no warranty. Extra behavior belongs in your host or a helper DLL, not in With.dll. Use, modify, and experiment at your own risk.
The sample hosts load one EXE. Helper DLLs come first (LoadLibrary); the first EXE is the guest; everything after that is that guest’s command line. The target EXE being mapped is simply a binary executable—it is not necessarily written in C++; as long as it is a valid Windows PE, the host will process it.
There is also no patch-to-load / patch-to-run story. WithExe does not rewrite the guest’s IAT, and it does not patch system exports to hook start, run, or exit. The subset of redirections it needs is done with the thread’s DRx hardware breakpoints and a vectored handler. The targeted EXE’s import table and system DLL bytes stay as the loader left them.
SOURCE\ WithExe\ native host WithExeClr\ CLR host include\With.h API lib\x64\Release|Debug With.lib bin\x64\With.dll one mapper (Debug and Release hosts copy this same file) REDISTRIBUTABLES\BIN\x64\Release|Debug With.dll prebuilt mapper (do not rebuild from this tree) WithExe.exe WithExeClr.exe (+ .dll / .deps.json / .runtimeconfig.json) WithExe.slnx
Hosts build into REDISTRIBUTABLES\BIN\x64\Release and Debug. With.dll is already there; a host rebuild must not delete it.
x64 only. Do not rebuild With.dll from this tree — use the prebuilt SOURCE\bin\x64\With.dll (copied next to the hosts on build).
WithExe.slnx in Visual Studio. Platform x64.WithExe (native) or WithExeClr (CLR).%SystemRoot%\System32\notepad.exe. Working directory is REDISTRIBUTABLES\BIN\x64\<config>, so With.dll sits beside the host. Change the arguments for another guest (full path unless the file sits next to the host).Command line.
MSBuild WithExe.slnx /restore /p:Configuration=Release /p:Platform=x64
Output: REDISTRIBUTABLES\BIN\x64\Release or Debug.
REDISTRIBUTABLES\BIN\x64\Release\WithExe.exe notepad.exe REDISTRIBUTABLES\BIN\x64\Release\WithExe.exe helper.dll C:\full\path\app.exe --flag REDISTRIBUTABLES\BIN\x64\Release\WithExe.exe -d C:\full\path\app.exe REDISTRIBUTABLES\BIN\x64\Release\WithExeClr.exe C:\full\path\app.exe
dlls / exe need a full path unless the file is next to that host EXE. Helpers (any number of DLLs) first, then one EXE, then that EXE’s arguments. A helper DLL is the place for your own detours. -d is host-only (DLL directory + cwd = guest folder).
A typical PE mapper VirtualAllocates, copies sections, walks imports, applies relocs, and fakes an entry. That is a second loader, always behind the OS, and the image is usually private memory rather than a real image section.
WithExe uses LoadLibrary on the EXE (the image is briefly treated as a DLL for the loader, then restored). Compared to mapping by hand:
SEC_IMAGE. The file is mapped as an image section, not a memcpy into anonymous pages. Section permissions, sharing, and “this is a PE” come from the kernel.PAGE_EXECUTE_READWRITE. Hand-rolled maps VirtualProtect the image to that flag so one region is writable and executable. AV scanners treat RWX as a malware sign. We never set it. Section rights come from SEC_IMAGE, not a RWX pass..pdata is registered the usual way.HMODULE. The guest is an LDR module, not a floating allocation you must keep secret from every API that wants a module handle.HMODULE, any functions exported by the target EXE can be resolved natively using GetProcAddress. This technique allows exporting of functions from the target EXE, treating it exactly the same as a loaded DLL.Run writes the PEB command line (CreateProcess-style). The guest sees its own module path.Detours samples/withdll starts the target with CreateProcess suspended, then injects a DLL into that new PID (WriteProcessMemory / CreateRemoteThread / LoadLibrary, or an entry-point patch). You instrument someone else’s process.
WithExe is the inverse: you write the host; Run maps the EXE into this process. Helper DLLs and Detours-style hooks run as your code in your address space. Same sample shape as withdll (options, helper DLLs, start) — not a remote inject.
This tree ships two sample hosts. Same job: LoadLibrary With.dll, call Run, map one guest EXE on this thread. They are templates, not a product.
SOURCE\WithExe\WithExe.cpp)__try { Run } — the host decides how to process SEH. A GUI host can run a secondary message pump; this sample just reports the exception.%SystemRoot%\System32\notepad.exe.SOURCE\WithExeClr\WithExeClr.cs)__try; a guest AV kills the process. Same guest via Properties\launchSettings.json (native debug engine).Command line is the same for both:
WithExe.exe [options] [helper.dll ...] guest.exe [guest args...]
UINT WINAPI Run(LPCWSTR exe, LPCWSTR cmdLine);
exe — full path of a PE without IMAGE_FILE_DLL, unless the file sits next to the host EXE. Not PATH.cmdLine — written into the PEB UNICODE CommandLine (Buffer + Length) and kernel32's GetCommandLineA cache. Same idea as CreateProcess lpCommandLine. The guest line is a substring of the host line, so both buffers already fit.__try { Run(...) }.x64 only. With.dll must sit next to the host (already true under REDISTRIBUTABLES\BIN). The host thread calls Run. The sample uses LoadLibrary / GetProcAddress; With.lib is there if you want to link your own host.