# 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
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/