I have a 1.5K 'Hello World!' program (MASM), how could I get it smaller, without hex editing etc. With pure, code, assembler options, linker options etc. Keeping it an EXE. I want to use the same/similar methods for larger projects.
.386

.MODEL flat, stdcall
OPTION casemap :none

INCLUDE windows.inc

INCLUDE user32.inc
INCLUDE kernel32.inc

INCLUDELIB user32.lib
INCLUDELIB kernel32.lib

.CONST
Caption db "Hello World!", 0

.CODE

Start:
INVOKE MessageBox, NULL, addr Caption, NULL, NULL
INVOKE ExitProcess, 0
END Start
Posted on 2004-05-28 02:21:23 by SubEvil
Typical methods to reduce EXE size:

i) Merge sections
ii) Use smaller DOS stub with MS link

http://www.asmcommunity.net/board/showthread.php?postid=112309.msg112309
Posted on 2004-05-28 05:03:40 by Vortex
Or just use FASM :grin: without linker... ;)
Posted on 2004-05-28 14:28:44 by Tommy

Or just use FASM :grin: without linker... ;)


nasm is much more flexible :)
Posted on 2004-06-07 00:41:37 by Drocon
Can you describe a little more about flexibility?
Posted on 2004-06-07 04:47:44 by Vortex

Can you describe a little more about flexibility?


i guess no reserved symbols, or bunk restrictions, it's just WYSIWYG sort of thing.
not to say FASM is bad or anything, maybe i'm just not used to it. :)
Posted on 2004-06-07 12:48:06 by Drocon
Even in VC6 you can easily do 1-2 KB Exe Files, if you set the right compiler options. I've never been able to get comparably small files with other compiles, such as GCC, Borland or when using MFC (= the devil). And code bloat is one thing I really hate. (Another hint: search for libctiny.lib)

This is a (surely not perfect) example of a 1 KB exe files done with VC6.




#include <windows.h>
#include <string.h> // strcat()

#pragma comment(linker,"/ignore:4078")
#pragma comment(linker,"/merge:.text=.data")
#pragma comment(linker,"/merge:.rdata=.data")
#pragma comment(linker,"/filealign:512")
#pragma comment(linker,"/ENTRY:entry")

void entry()
{

OSVERSIONINFO lpVInfo;
lpVInfo.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);

GetVersionEx(&lpVInfo);

char *ver;

if(lpVInfo.dwPlatformId == VER_PLATFORM_WIN32_NT)

if(lpVInfo.dwMajorVersion < 5)
ver = "NT";
else
{
if(lpVInfo.dwMinorVersion < 1)
ver = "2000";
else if(lpVInfo.dwMinorVersion < 2)
ver = "XP";
else
ver = ".NET Server";
}

else

if(!lpVInfo.dwMinorVersion)
ver = "95";
else if(lpVInfo.dwMinorVersion == 90)
ver = "ME";
else
ver = "98";

// <insert better code here>

MessageBox(NULL, text, "Info:", MB_ICONINFORMATION);
ExitProcess(0);

}




You can get your hello world prog down to 552 bytes, if you stuff the code / string data into the pe header or you could get somewhere around 600, if you put .code and .data into 1 section starting at 200h. Just do the whole header in NASM.


Hope this helped



edit: fodder: Corrected.
Posted on 2004-06-07 17:49:38 by pmpch
Yay, another world's smallest messagebox... anyway, you shouldn't call your entrypoint "main" when you don't use regular libc, as it's bad karma to have "void main" - use "entry" or whatever. And have a look at Jibz' wcrt, www.ibsensoftware.com
Posted on 2004-06-07 18:06:52 by f0dder


char *text = "Your operating system has been identified as... Windows ";
char *retp = strcat(text, ver);

Please tell me I didn't just see this...
Posted on 2004-06-07 19:18:32 by death
Originally posted by death


char *text = "Your operating system has been identified as... Windows ";
char *retp = strcat(text, ver);

Please tell me I didn't just see this...


You did, what's wrong with it?
_CRTIMP char * __cdecl strcat(char *, const char *);


Originally posted by f0dder
And have a look at Jibz' wcrt, www.ibsensoftware.com


It's quite new, I didn't visit his page for a while. Tnx.
Posted on 2004-06-07 20:23:16 by pmpch
You did, what's wrong with it?

The main thing that's wrong with it, is that strcat just appends one string to another, which means the target string should be large enough for both. The string text points to is not, so the call to strcat will overwrite whatever data is after the string.

The second, somewhat more subtle, problem is that modifying a string constant is undefined in C. A string like the one text points to can be placed in the const segment, which could result in an access violation. Also string pooling could give you some interesting problems if your did something like that in a larger program.
Posted on 2004-06-08 02:30:57 by Jibz
Jibz is of course correct, and there is a third problem with it.
The CRT startup code is there for a reason, and if it is not being run, don't expect the CRT functions to work. Just because strcat() worked doesn't mean it will work in the future, and other functions certainly won't work now.
Posted on 2004-06-08 05:25:11 by death
Ok, I get the point. :grin:
What would be a good way to avoid those 3 problems?
Posted on 2004-06-08 10:28:52 by pmpch
pmpch,

You wrote:

>You can get your hello world prog down to 552 bytes

Don't expect that this prog will run on every version of windows
Posted on 2004-06-08 11:36:12 by Vortex

What would be a good way to avoid those 3 problems?

Allocate an array big enough to have both 'text' and 'ver' strings concatenated, use lstrcpy()/lstrcat().
Posted on 2004-06-08 13:25:12 by death

pmpch,
You wrote:
>You can get your hello world prog down to 552 bytes
Don't expect that this prog will run on every version of windows


It has run on every system so far, including Dos, Win95, 98, Me, 2000, Xp (SP0+1). I haven't tested it on NT. It has worked on 20 computers.
Is there a reason it shouldn't work? Alignment 512/1024 should work fine, and most of the header is unused. However, feel free to prove otherwise.
Posted on 2004-06-08 15:57:16 by pmpch
attached is an 753 byte uptime utility, in nasm, via a more traditional technique.
a messagebox alone can be as little as 550ish bytes. i've seen other exes merging portions of the PE as space for code/data. i believe a win32 pe hack for small exes is possible.
Posted on 2004-06-09 00:19:29 by Drocon
pmpch,

Possibly, your small example worked fine but it's better to follow the PE specifications. :)
Posted on 2004-06-09 04:55:11 by Vortex
@Vortex: Of course for any distrubuted/commercial exe it is good to follow the PE specs.
The files will most likely need 32k on disk anyway :grin:

If the specs are ignored you can get down to

552 - all versions
2xx - NT & 2000 (could possibly be lower)
157 - XP (with 1 correct import, doubt theres enough room at 133 for this)
97 - XP (only version specific XP, direct func call, massive overlays, but fun :alright: )

These, as Vortex said, are unsafe, but can be used for demos and stuff like that. (Not commercial)


Here the NASM source for the 552 byte prog (1 import).



[size=9]IMAGEBASE EQU 400000h
SECTIONALIGN EQU 4096 ; 4096/512 works for every
FILEALIGN EQU 512 ; 32-bit Windows

UNUSED EQU 0


; ####################################################
; H E A D E R
; ####################################################


; +++++++++++++
DOS_HEADER:
; +++++++++++++

e_magic DW 'MZ' ; [@00h]

e_cblp DB 0 ; [@02h]
DB 0
e_cp DB 1 ; total number of 512-byte pages
DB 0 ; (need only for dosstub)
e_crlc DB 0
DB 0
e_cparhdr DB 0
DB 0
e_minalloc DB 0
DB 0
e_maxalloc DB 0
DB 0
e_ss DB 0
DB 0


e_sp DB 0 ; initial SP, (for dosstub) [@10h]
DB 0
e_csum DB 0
DB 0
e_ip DB 0
DB 0
e_cs DB 0
DB 0
e_lfarlc DB 0
DB 0
e_ovno DB 0
DB 0
e_res DB 0
DB 0
DB 0
DB 0

e_ddosentry push cs ; DB 0Eh DOSSTUB entrypoint
pop ds ; DB 1Fh
mov dx, word string ; DB BAh
; DB ??h
; DB ??h
mov ah, 9 ; DB B4h
; DB 09h
int 21h ; DB CDh
; DB 21h
mov ax, 4C01h ; DB B8h
; DB 01h
; DB 4Ch
int 21h ; DB CDh
; DB 21h [@2Dh]
strr DB 0
DB 0
DB 0 ; [@30h]
DB 0
DB 0
DB 0
DB 0
DB 0
DB 0 ; [@36h]
DB 0
DB 0
DB 0
DB 0
DB 0
e_lfanew DD 40h ; points to NT_SIGNATURE [@3Ch]





; ++++++++++++++
FILE_HEADER:
; ++++++++++++++

NT_SIGNATURE: DB 'PE',0,0 ; [@FILE_HEADER + 00h]

Machine DB 4Ch ; [@FH+04h]
DB 1
NumberOfSections DB 1
DB 0
TimeDateStamp DB UNUSED ; ==> 12 bytes [@FH+08h]
DB UNUSED
DB UNUSED
DB UNUSED
PointerToSymbolTable DB UNUSED
DB UNUSED
DB UNUSED
DB UNUSED

NumberOfSymbols DB UNUSED ; [@FH+10h]
DB UNUSED
DB UNUSED
DB UNUSED
SizeOfOptionalHeader DB 0E0h ; const DW = 224 [@FH+14h]
DB 0
Characteristics DB 2 ; const DW = 2 [@FH+16h]
DB 0 ; 0 or 1 works



; ++++++++++++++++++
OPTIONAL_HEADER:
; ++++++++++++++++++

Magic DB 0Bh ; constant [@FH+18h]
DB 1 ;
MajorLinkerVersion DB UNUSED ; ==> 14 bytes [@FH+1Ah]
MinorLinkerVersion DB UNUSED
SizeOfCode DB UNUSED
DB UNUSED
DB UNUSED
DB UNUSED

SizeOfInitializedData DB UNUSED ; [@FH+20h]
DB UNUSED
DB UNUSED
DB UNUSED
SizeOfUninitializedData DB UNUSED
DB UNUSED
DB UNUSED
DB UNUSED
AddressOfEntryPoint DD CODE_ENTRY ; ENTRYPOINT FIXED! [@FILE_HEADER + 28h]
BaseOfCode DB UNUSED ; ==> 8 bytes [@FH+2Ch]
DB UNUSED
DB UNUSED
DB UNUSED

BaseOfData DB UNUSED ; [@FH+30h]
DB UNUSED
DB UNUSED
DB UNUSED
ImageBase DD IMAGEBASE ; IMAGEBASE [@FILE_HEADER + 34h]
SectionAlignment DD SECTIONALIGN ; SECTIONALIGN [@FILE_HEADER + 38h]
FileAlignment DD FILEALIGN ; FILEALIGN [@FILE_HEADER + 3Ch]

MajorOperatingSystemVer DB 4 ; ==> 8 bytes [@FH+40h]
DB UNUSED
MinorOperatingSystemVer DB UNUSED
DB UNUSED
MajorImageVersion DB UNUSED
DB UNUSED
MinorImageVersion DB UNUSED
DB UNUSED
MajorSubsystemVersion DB 4 ; constant = 4 [@FH+48h]
DB 0
MinorSubsystemVersion DB UNUSED ; ==> 6 bytes [@FH+4Ah]
DB UNUSED
Win32VersionValue DB UNUSED
DB UNUSED
DB UNUSED
DB UNUSED

SizeOfImage DD SECTIONALIGN + SECTION_END - SECTION_START ; [@FILE_HEADER + 50h]
SizeOfHeaders DD FILEALIGN ; [@FILE_HEADER + 54h]
CheckSum DB 0 ; ==> 4 bytes [@FH+58h]
DB UNUSED
DB UNUSED
DB UNUSED
Subsystem DB 2 ; gui(2), console(3) [@FH+5Ch]
DB 0
DllCharacteristics DB UNUSED ; ==> 2 bytes [@FH+5Eh]
DB UNUSED

SizeOfStackReserve DB 0 ; [@FILE_HEADER + 60h]
DB 0
DB 10
DB 0
SizeOfStackCommit DB 0 ; [@FILE_HEADER + 64h]
DB 10
DB 0
DB 0
SizeOfHeapReserve DB 0 ; [@FILE_HEADER + 68h]
DB 0
DB 10
DB 0
SizeOfHeapCommit DB 0 ; [@FILE_HEADER + 6Ch]
DB 10
DB 0
DB 0

LoaderFlags DB UNUSED ; ==> 4 bytes [@FH+70h]
DB UNUSED
DB UNUSED
DB UNUSED
NumberOfRvaAndSizes DB 2 ; const DD = 2 [@FH+74h]
DB 0
DB 0
DB 0


; +++++++++++++++++
DATA_DIRECTORY:
; +++++++++++++++++

DIRECTORY_ENTRY_EXPORT DB UNUSED ; ==> 8 bytes [@FH+78h]
DB UNUSED
DB UNUSED
DB UNUSED
DB UNUSED
DB UNUSED
DB UNUSED
DB UNUSED

DIRECTORY_ENTRY_IMPORT DD SECTIONALIGN + IMPORT_DESCRIPTOR - SECTION_START ; [@FILE_HEADER + 80h]
DD (IMPORT_DESCRIPTOR_END + 4) - IMPORT_DESCRIPTOR ; size = 24h
DIRECTORY_ENTRY_RESOURCE DB 0 ; const DD = 0 [@FH+88h]
DB 0
DB 0
DB 0
DB UNUSED ; ==> 52 bytes [@FH+8Ch]
DB UNUSED
DB UNUSED
DB UNUSED

DIRECTORY_ENTRY_EXCEPTION DB UNUSED ; [@FH+90h]
DB UNUSED
DB UNUSED
DB UNUSED
DB UNUSED
DB UNUSED
DB UNUSED
DB UNUSED
DIRECTORY_ENTRY_SECURITY DB UNUSED
DB UNUSED
DB UNUSED
DB UNUSED
DB UNUSED
DB UNUSED
DB UNUSED
DB UNUSED

DIRECTORY_ENTRY_BASERELOC DB UNUSED ; [@FH+A0h]
DB UNUSED
DB UNUSED
DB UNUSED
DB UNUSED
DB UNUSED
DB UNUSED
DB UNUSED
DIRECTORY_ENTRY_DEBUG DB UNUSED
DB UNUSED
DB UNUSED
DB UNUSED
DB UNUSED
DB UNUSED
DB UNUSED
DB UNUSED

DIRECTORY_ENTRY_COPYRIGHT DB UNUSED ; [@FH+B0h]
DB UNUSED
DB UNUSED
DB UNUSED
DB UNUSED
DB UNUSED
DB UNUSED
DB UNUSED
DIRECTORY_ENTRY_GLOBALPTR DB UNUSED
DB UNUSED
DB UNUSED
DB UNUSED
DB UNUSED
DB UNUSED
DB UNUSED
DB UNUSED

DIRECTORY_ENTRY_TLS DB 0 ; const DD = 0 [@FH+C0h]
DB 0
DB 0
DB 0
DB UNUSED ; ==> 52 bytes [@FH+C4h]
DB UNUSED
DB UNUSED
DB UNUSED
DIRECTORY_ENTRY_LOAD_CONFIG DB UNUSED
DB UNUSED
DB UNUSED
DB UNUSED
DB UNUSED
DB UNUSED
DB UNUSED
DB UNUSED

DIRECTORY_ENTRY_BOUND_IMPORT DB UNUSED ; [@FH+D0h]
DB UNUSED
DB UNUSED
DB UNUSED
DB UNUSED
DB UNUSED
DB UNUSED
DB UNUSED
DIRECTORY_ENTRY_IAT DB UNUSED
DB UNUSED
DB UNUSED
DB UNUSED
DB UNUSED
DB UNUSED
DB UNUSED
DB UNUSED

DIRECTORY_ENTRY_14 DB UNUSED ; [@FH+E0h]
DB UNUSED
DB UNUSED
DB UNUSED
DB UNUSED
DB UNUSED
DB UNUSED
DB UNUSED
DIRECTORY_ENTRY_15 DB UNUSED
DB UNUSED
DB UNUSED
DB UNUSED
DB UNUSED
DB UNUSED
DB UNUSED
DB UNUSED

DIRECTORY_ENTRY_16 DB UNUSED ; [@FH+F0h]
DB UNUSED
DB UNUSED
DB UNUSED
DB UNUSED
DB UNUSED
DB UNUSED
DB UNUSED

; +++++++++++++++++
SECTION_HEADER:
; +++++++++++++++++

SectionName DB UNUSED ; ==> 8 bytes [@FH+F8h]
DB UNUSED
DB UNUSED
DB UNUSED
DB UNUSED
DB UNUSED
DB UNUSED
DB UNUSED
VirtualSize DB 0 ; anything < SECTIONALIGN [@FILE_HEADER + 100h]
DB 0
DB 0
DB 0
VirtualAddress DD SECTIONALIGN ; RVA to section (in RAM)
SizeOfRawData DD SECTION_END-SECTION_START ; Size of section
PointerToRawData DD SECTION_START ; Offset to section (in File)

PointerToRelocations DB UNUSED ; ==> 12 bytes [@FH+110h]
DB UNUSED
DB UNUSED
DB UNUSED
PointerToLinenumbers DB UNUSED
DB UNUSED
DB UNUSED
DB UNUSED
NumberOfRelocations DB UNUSED
DB UNUSED
NumberOfLinenumbers DB UNUSED
DB UNUSED
SectionCharacteristics DB 0
DB 0
DB 0
DB 0E0h ; section executable, readable, writable


; +++++++++++++
CODE_ENTRY:
; +++++++++++++

[BITS 32]

mov eax,IMAGEBASE + title
cdq ; clears EDX
push 1040h
push eax
sub al, title - string
push eax
push edx
call dword [IMAGEBASE+SECTIONALIGN+(__imp__MessageBoxA-SECTION_START)]
retn

; +++++++
DATA:
; +++++++

string: DB 'PE/EXE File - 552 bytes',10,13,'(Dos/Win95/98/ME/NT/2000/Xp)'
DB 10,13,'CoDE: pmpch',10,13,10,13,'Copyright (c) 2oo4',0,'$'
title: DB ' trY ME!',0

IMPORT_BY_NAME: DW UNUSED ;(ordinal)
DB 'MessageBoxA',0

DLLNAME: DB 'User32.dll',0



ALIGN FILEALIGN,DB 0 ; 0-padding to FILEALIGN






; ####################################################
; S E C T I O N
; ####################################################


; ++++++++++++++++
SECTION_START: ; [@FILEALIGN + 00h]
; ++++++++++++++++



; +++++++++++++++
FIRST_THUNK:
; +++++++++++++++

__imp__MessageBoxA: DD IMPORT_BY_NAME ; RVA
;DD 0 ; terminator

; ++++++++++++++++++++
IMPORT_DESCRIPTOR:
; ++++++++++++++++++++
OriginalFirstThunk_1 DD 0 ; ...is terminator for __imp__MessageBoxA
; at the same time ;)

TimeDateStamp_1 DB UNUSED
DB UNUSED
DB UNUSED
DB UNUSED

ForwarderChain_1 DB UNUSED
DB UNUSED
DB UNUSED
DB UNUSED
Name_1 DD DLLNAME ; RVA to DLL name
FirstThunk_1 DD SECTIONALIGN + FIRST_THUNK - SECTION_START ; RVA to 1st thunk

IMP_DES_TERMINATOR:
tOriginalFirstThunk DB UNUSED
DB UNUSED
DB UNUSED
DB UNUSED
tTimeDateStamp DB UNUSED
DB UNUSED
DB UNUSED
DB UNUSED
tForwarderChain DB UNUSED
DB UNUSED
DB UNUSED
DB UNUSED
tName DB 0 ; const DD = 0
DB 0
DB 0
DB 0
;tFirstThunk DB 0 ; STRIPPED
; DB 0 ; STRIPPED
; DB 0 ; STRIPPED
; DB 0 ; STRIPPED

; ++++++++++++++++++++++++
IMPORT_DESCRIPTOR_END:
; ++++++++++++++++++++++++

; ATTETION: add +4 to DIRECTORY_ENTRY_IMPORT[size]



; ++++++++++++++
SECTION_END:
; ++++++++++++++


; ####################################################
; ####################################################[/SIZE]
Posted on 2004-06-09 07:30:45 by pmpch
Here is my example of 432 bytes running on every version of windows. Find out the trick. :)
Posted on 2004-06-09 11:57:59 by Vortex