If you are developing a C++ application without use of standard library (be it a driver, shellcode, or just a tiny DLL), you might have noticed that this also means that you don't get to use the standard formatted output function. This is a post about getting around that through use of WinAPI.
The idea
For this we'll need
- wvsprintfA
for formatted output.
As far as I can tell, this group of functions is the only formatted output implementation that already resides in some Windows DLL and doesn't involve including a handful of code - GetStdHandle to get a handle to standard output device/CONOUT$.
- WriteFile to actually write data to it.
The code
Firstly, you would need to include user32.lib and kernel32.lib.
Generally you should be able to do so via
#pragma comment(lib, "user32.lib") #pragma comment(lib, "kernel32.lib")
but in Visual Studio I could not get these to load in correct order, so I ended up adding them to Properties ➜ Linker ➜ Input ➜ Additional Dependencies instead.
With above in mind, the actual function is as straightforward as it gets:
void my_printf(const char* pszFormat, ...) { char buf[1024]; va_list argList; va_start(argList, pszFormat); wvsprintfA(buf, pszFormat, argList); va_end(argList); DWORD done; WriteFile(GetStdHandle(STD_OUTPUT_HANDLE), buf, strlen(buf), &done, NULL); }
And compiles much like you'd expect:
(output from Ghidra; variable names edited for clarity)
Caveats
- As per MSDN description, wvsprintfA supports buffers up to 1024 bytes, and will truncate data past that length.
- MSDN advices to use "safe string" functions instead
(all of which depend on standard library)
and to see "Security Considerations", but there is no such section to be found.
With concerns about buffer overflow out of the window, this might mean that the function is not thread-safe.
That is all!