By: Other Guy
Abstract: Think you can't tweak with your project after it's compiled? Check out these useful techniques for adding custom data to your EXE in Win32 and Linux. By Daniel Polistchuck.
The code for this article is available at CodeCentral.
Find it here
We developers are used to treating the executable code generated by our
development tools as "black boxes." Nevertheless, there are times
when the ability to write and read custom data to and from executable files
would be really useful. Some interesting solutions can make use of the technique
described in this article, including:
Each operating system defines its own executable-file internal format. We
will be dealing here with Linux, which uses the industry-standard ELF
(Executable and Linking Format) format and with Win32 operating systems that
implement the Microsoft PE (Portable Executable) standard. Although the two
formats differ in their inner structures, each can be seen as a
self-contained, internally-referenced "file system." Both formats
define a header that is divided into several parts. Some of these parts (the
Section Header in PE and Program Header in ELF) work much like file allocation
tables in FAT file systems: They describe the internal
offsets to chunks of data and code in the executable file.
When a user or shell activates a file that is recognized as an
executable file in Linux or Win32, the "loader" springs into action. The
loader is responsible for loading the file from the disk, mapping it to memory,
interpreting the file internal structures (headers and such), and starting the
execution of the executable initial entry-point.
Take a look at the picture above. Bearing in mind that the
executable file works like a self-containing file system that is
read from its beginning to its end, it is safe to assume that whatever is put beyond
the end of the executable file won't compromise the executable
information. So the best place to put custom data is after the EOF of the original executable
The Win32 and Linux Loaders happily ignore our CUSTOM DATA chunk.
Yes, that's correct. We can write anything past the EOF of an
executable file in Linux and Win32. We are free to
define any kind of structure and deal with it in our code any way we want to.
So let's add a footer to our executable file that will include some
//our ExeBuffer signature
ExeBufSig = 'EB1.0';
//the Footer for our executable format
TExeBufFooter = record
OriginalSize : Integer;
Sig : Array[0..4] of char;
Linux uses POSIX file semantics, which lets us modify, delete, and
rename a file that is already in use and not locked. But Win32 doesn't let us
do that kind of stuff! It's all very complicated, but the bottom line is that Linux lets us write self-modifying
executables, but Win32 doesn't.
To deal with that situation, we must write a second executable
(the Writer), that will be responsible for writing custom data into the
"client" (Reader) executable.
The code that is responsible for writing the data is simple:
procedure SetExeData (ExeName : String; ExeBuf : TExeBuf);
F : File;
BufSz,OrigSz : Integer;
Footer : TExeBufFooter;
//obtaining the original file size
OrigSz := FileSize(F);
//go to the EOF of the file
//Writing our custom data beyond the EOF
BufSz := Length(ExeBuf);
//Writing our footer
Footer.OriginalSize := OrigSz;
Footer.Sig := ExeBufSig;
Simple, right? We treat the executable like any other file.
Everything we need to read the data is in the TExeBufFooter
record located in the end of the executable. The exact location of the the
footer is FileSize - SizeOf(TExeBufFooter). Upon reading the footer from the
executable, we can obtain the original file size by accessing its OriginalSize
field. So we can implement GetExeData like this:
procedure GetExeData (ExeName : String; var ExeBuf : TExeBuf);
F : File;
CurrSz, BufSize : Integer;
OldFileMode : Integer;
Footer : TExeBufFooter;
//Saving the old FileMode
OldFileMode := FileMode;
//Setting the FileMode to ReadOnly
FileMode := 0;
//Getting the current file size
CurrSz := FileSize (F);
//Seeking to the footer position
//and reading it
Seek (F,CurrSz-SizeOf (Footer));
//if there's no signature, boom!
//no data in this executable!
if Footer.Sig <> ExeBufSig then
raise EExeBuf.Create ('No Data in EXE!');
//calculating the buffer size that was written
//to this executable file
//seek and read!
//returning to the previous saved
FileMode := OldFileMode;
This code may seem a little complicated at first glance, but the only real
complication is the part where we calculate the buffer size (BufSize).
In fact, it's very simple: We subtract from the current file size the
original size and the footer size. Algorithms don't get much simpler than that.
Now we throw in a couple of helper functions to convert
TExeBuf to and from Strings:
procedure StringToExeBuf (const S : String; var ExeBuf : TExeBuf);
function ExeBufToString (const ExeBuf : TExeBuf) : String;
And that's it!
Daniel is the IT Director of QualTech IT and is eagerly waiting for the December 19
LOTR Release. He can be reached through e-mail at firstname.lastname@example.org.
Download Delphi 10 now!
Webinars on demand!
More social media choices:
Delphi on Google+
@RADTools on Twitter
Server Response from: ETNASC03