# Shrinking Windows Visual C Applications Starting with Visual Studio 2019. File -> New -> Project -> C++ -> Console App -> Create Right click the project name -> Properties -> C/C++ -> Advanced -> Compile As -> Compile as C Code /TC Copy and paste hello world ``` #include <stdio.h> int main() { printf("Hello World"); return 0; } ``` Select Release from the drop down in the menu bar above Select x86 from the drop down in the menu bar above Build -> Build Solution Which gives me a 9,216 byte binary. # We can do better Right the project name -> Properties -> - C/C++ -> Optimization -> Optimization Maximum Optimization (Favor Size) (/O1) - C/C++ -> Optimization -> Enable Intrinsic Functions -> No - C/C++ -> Optimization -> Favor Size or Speed -> Favor small code (/Os) - C/C++ -> General -> Debug Information Format -> None - C/C++ -> General -> Code Generation -> Security Check -> Diable Security Check - Linker -> Advanced -> Image Has Safe Exception Handlers -> No Recompile the app and we are at... 9,216 bytes... same size, that's weird. ## TCC Tiny C Compiler can generate smaller binaries ``` tcc.exe hello.c ``` And we have a 2048 byte binary that doesn't need a redistributable to execute. TCC is written by [Fabrice Ballard](https://bellard.org/) he also wrote QEMU and FFMPEG. He has some other cool projects jslinux too, crazy smart guy. ## Visual Studio - https://www.solomonsklash.io/smaller-c-payloads-on-windows.html - https://old.reddit.com/r/winternals/comments/79h59/under_the_hood_reduce_exe_and_dll_size_with/ - https://www.youtube.com/watch?v=5tg_TbURMy0 - https://www.mvps.org/user32/nocrt.html These show there's some easy black magic we can do to shrink down our binary. We just need a copy of msvcrt.lib from Visual C++ 6.0, just open the iso in 7z and look in `VC98\LIB\` Copy that to the directory where the c file is - Linker -> Input -> Additional Dependenices -> Add full path to msvcrt.lib; - Linker -> Manifest File -> No - Linker -> Manifest File -> Enable UAC -> No - Linker -> Debugging -> General Debug -> No - Linker -> Advanced -> Entry Point -> go - Linker -> Advanced -> Randomized Base Address -> No - Linker -> Advanced -> DEP -> No - Linker -> Advanced -> Fixed Base Address -> Yes - C/C++ -> Code Generation -> Runtime Library -> /MT Rename main() to go() and compile When I attempt to compile I get: ``` unresolved external symbol __imp____acrt_iob_func ``` https://social.msdn.microsoft.com/Forums/en-US/073eda9b-214e-47e6-8071-f41bfa3a43fb/migrating-from-vs2010-to-vs-2017-getting-error-iobfunc-identifier-not-found?forum=vclanguage Which says this is not possible to fix this and to use an older visual studio This says otherwise: https://stackoverflow.com/questions/30412951/unresolved-external-symbol-imp-fprintf-and-imp-iob-func-sdl2 The top answers didn't work, but what did was adding this line before your include ``` #define _NO_CRT_STDIO_INLINE ``` And it compiles to a 1,536 byte binary and executes without visual c redistributable Parsing command line arguments is a different story... https://old.reddit.com/r/C_Programming/comments/iofcdo/linking_against_windowssystem32msvcrtdll_with_cli/ https://stackoverflow.com/questions/291424/canonical-way-to-parse-the-command-line-into-arguments-in-plain-c-windows-api/42048224#42048224 However, this didn't play nice when compiling as CPP. Further googling and I found I was able to copy in CommandLineToArgvA from: http://alter.org.ua/docs/win/args/ ``` int go() { printf("Hello World"); int argc; LPSTR* argv; argv = CommandLineToArgvA(GetCommandLineA(), &argc); printf("\nArgc:%d\n", argc); printf("Argv: "); for (int i = 0; i < argc; i++) { printf("%s ", argv[i]); } return 0; } ``` And we can generate a 2048 byte binary that can handle arguments ## TCC Tcc has a much easier time creating smaller binaries with command line arguments assuming all the code is C and not CPP https://gilesbathgate.com/2009/04/07/creating-tiny-windows-executables-with-tcc/ ``` ..\tcc.exe -impdef ws2_32.dll ..\tcc.exe fpipe.c ws2_32.def ..\lib\user32.def ``` 14,336 bytes (tcc) vs 12,800 bytes (vc) ## Note 1 When I wasn't using a simple hello world, I ran into issues where `#define _NO_CRT_STDIO_INLINE` didn't work and I still got unresolved printf and iob symbols. I'm not sure what causes this sometimes. I ended up just compiling using visual studio 2008. ## Note 2 If you ever need to create a .LIB file from a .DLL you can use tcc to generate a .DEF file from a .DLL ``` tcc.exe -impdef file.dll ``` This can also be done manually with dumpbin and awk (see video above) or this [example](https://stackoverflow.com/questions/9946322/how-to-generate-an-import-library-lib-file-from-a-dll) Then using a visual studio cmd prompt, use `lib` to generate a .LIB from a .DEF ``` lib /def:file.def /out:file.lib /machine:x86 ``` I would recommend using the vc6 lib's over generating your own though. ## Fun fact 268 bytes seems to be smallest pe binary you can make with windows 10. https://github.com/pts/pts-tinype https://github.com/rcx/tinyPE http://www.phreedom.org/research/tinype/