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
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
i) Merge sections
ii) Use smaller DOS stub with MS link
http://www.asmcommunity.net/board/showthread.php?postid=112309.msg112309
Or just use FASM :grin: without linker... ;)
Or just use FASM :grin: without linker... ;)
nasm is much more flexible :)
Can you describe a little more about flexibility?
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. :)
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.
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.
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.
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
char *text = "Your operating system has been identified as... Windows ";
char *retp = strcat(text, ver);
Please tell me I didn't just see this...
Originally posted by death
Please tell me I didn't just see this...
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
And have a look at Jibz' wcrt, www.ibsensoftware.com
It's quite new, I didn't visit his page for a while. Tnx.
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.
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.
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.
Ok, I get the point. :grin:
What would be a good way to avoid those 3 problems?
What would be a good way to avoid those 3 problems?
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
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
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().
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.
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.
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.
pmpch,
Possibly, your small example worked fine but it's better to follow the PE specifications. :)
Possibly, your small example worked fine but it's better to follow the PE specifications. :)
@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).
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]
Here is my example of 432 bytes running on every version of windows. Find out the trick. :)