Create a super small ELF executable on the Linux platform
Author: breadbox
Squiring translation: Alert7
Home:
http://www.xfocus.org/
Time: 2001-9-4
-------------------------------------------------- ------------------------------
Preface:
Sometimes, the size of the document is very important, from this article, it also explores the work inside the ELF file format.
The situation with Linux operating system. This article shows us how to construct an ultra-small ELF executable.
These Example given in the article are on Linux running in the Intel 386 system. Other system systems may also be the same
The effect, but I don't feel sure.
Our assembly code is written by NASM, and its style is similar to the X86 compilation style.
NASM software is free, you can get it from below
http://www.web-sites.co.uk/nasm/
-------------------------------------------------- ------------------------------
See a small program example below, the only thing it does is to return a value into the operating system.
UNIX systems typically returns 0 and 1, here we use 42 as return values.
[alert7 @ redhat] # set -o noclobber && cat> tiny.c << EOF
/ * tiny.c * /
INT main (void) {Return 42;}
EOF
[alert7 @ redhat] # gcc -wall tiny.c
[Alert7 @ redhat] # ./a.out; echo $?
42
Use GDB to see, this program is very simple.
[Alert7 @ redhat] # gdb a.out -q
(GDB) disass main
Dump of assembler code for function main:
0x80483A0
0x80483A1
0x80483A3
0x80483A8
0x80483AA
0x80483B0
0x80483b1
See how big
[alert7 @ redhat] # wc -c a.out
11648 a.out
At 3998 on the original author's machine, it becomes 11648 on my RH 2.2.14-5.0, so big, we need
Make it smaller.
[alert7 @ redhat] # gcc -wall -s tiny.c
[Alert7 @ redhat] # ./a.out; echo $?
42
[alert7 @ redhat] # wc -c a.out
2960 a.out
Now become 2960, small.
gcc -wall -s tiny.c is actually equivalent to
gcc -wall tiny.c
Strip a.out Abandon all marking
[alert7 @ redhat] # wc -c a.out
11648 a.out
[alert7 @ redhat] # Strip a.out
[alert7 @ redhat] # wc -c a.out
2960 a.out
Next, let's optimize.
[alert7 @ redhat] # gcc -wall -s -o3 tiny.c
[alert7 @ redhat] # wc -c a.out
2944 A.out
We see that it is more difficult to reduce the size of the optimization instructions than the above small 16 bytes.
Unfortunately, the C program will add some additional code when compiling, so we will write the program with assembly.
As the previous program, we need to return to code 42, we only need to set EAX to 42. Program
The return status is stored in Eax, and the assembly code from the above DISASS MAIN should also be known.
[alert7 @ redhat] # set -o noclobber && cat> tiny.asm << EOF
Tiny.asm
BITS 32
Global Main
Section .text
MAIN:
MOV EAX, 42
RET
EOF
Compile and test
[Alert7 @ redhat] # Nasm -f Elf Tiny.asm
[alert7 @ redhat] # gcc -wall -s tiny.o
[Alert7 @ redhat] # ./a.out; echo $?
42
Now look at what is different from the assembly code, look at its size
[alert7 @ redhat] # wc -c a.out
2892 a.out
This reduces (2944-2892) 52 bytes. However, as long as we use the main () interface, there will be many additional code.
Linker will also add one to the OS interface. In fact, call main (). So how do we come down?
Code.
When the LINKER defaults is the label _start.gcc coupled, it automatically includes a _start routine, setting Argc and Argv,
...., last call main ().
So let's take a look, can you skip this, define the _start routine.
[alert7 @ redhat] # set -o noclobber && cat> tiny.asm << EOF
Tiny.asm
BITS 32
Global _Start
Section .text
_Start:
MOV EAX, 42
RET
EOF
[Alert7 @ redhat] # Nasm -f Elf Tiny.asm
[alert7 @ redhat] # gcc -wall -s tiny.o
Tiny.O: in function `_start ':
Tiny.o (.text 0x0): Multiple Definition of `_Start '
/usr/lib/crt1.o (.Text 0x0): first defined here
/usr/lib/crt1.o: in function `_start ':
/usr/lib/crt1.o(.Text 0x18): undefined reference to `main '
Collect2: ld returned 1 exit status
How to do before compiling?
GCC has a compilation option - NostartFiles
-NostartFiles
When Linking, the standard startup file is not used. But is usually used.
What we want is this, then come:
[Alert7 @ redhat] # Nasm -f Elf Tiny.asm
[alert7 @ redhat] # gcc -wall -s -nostartfiles tiny.o
[Alert7 @ redhat] # ./a.out; echo $?
Segmentation Fault (Core Dumped)
139
GCC has no error, but the program core dump, what happened?
Wrong is wrong to see _start as a function of a C, then try back from it. In fact it is not a function at all.
It is just a label, which is a program entry point used by Linker. When the program is running, it is directly called.
If we come and see, you will see the variable value at the top of the stack is 1, it is really not like an address. In fact, in
The position of the stack is the Argc variable of our program, followed by an argv array, contains NULL elements, and next is an ENVP environment variable.
So, that is not an address.
Therefore, _start wants to exit, call the exit () function.
In fact, we actually call the _exit () function, because the extra thing to do too much is too much, because we jump
The LIB library starts code, so we can also skip the Shutdown code for the lib library.
Ok, let's try again. Call the _exit () function, its unique parameter is a shaping. So we need a number of push to
In the stack, then call _exit ().
(It should be defined like this: exTern _exit)
[alert7 @ redhat] # set -o noclobber && cat> tiny.asm << EOF
Tiny.asm
BITS 32
Extern _exit
Global _Start
Section .text
_Start:
Push DWORD 42
Call_exit
EOF
[Alert7 @ redhat] # Nasm -f Elf Tiny.asm
[alert7 @ redhat] # gcc -wall -s -nostartfiles tiny.o
[Alert7 @ redhat] # ./a.out; echo $?
42
Yeah ~~, success, come and see how big
[alert7 @ redhat] # wc -c a.out
1312 A.out
Nice good, it reduces to half, :) Do you have other GCC options we are interested in?
There is a very interesting option in -nostartfiles:
-nostdlib
In line, the standard lib and startup file are not used. Those things need to be assigned to themselves
Linker.
This is worth studying:
[alert7 @ redhat] # gcc -wall -s -nostdlib tiny.o
Tiny.O: in function `_start ':
Tiny.o (.text 0x6): undefined reason to `_exit '
Collect2: ld returned 1 exit status
_exit () is a library function, but add -nostdlib can't be used, so we must handle itself, first, you must know how to create a system call under Linux.
-------------------------------------------------- ------------------------------
Like other operating systems, Linux provides basic services to programs through system calls.
This includes opening files, reading and writing file handles, and so on ...
The Linux system call interface has only one directive: int 0x80. All system calls are through the interface.
In order to make a system call, EAX should include a number (which system call is displayed), other registers
Save the parameters.
If the system calls uses a parameter, the parameters are in EBX;
If you use two parameters, then in EBX, ECX
If you use three, four, five parameters, then use EBX, ECX, ESI
When returns from the system call, EAX will contain a return value.
If the error occurs, EAX will be a negative value, and its absolute value represents the type of error.
Different system calls are listed in /usr/include/asm/unistd.h.
Quickly look at the exit system call number 1. It has only one parameter, which will return to the parent process, the value will
Put in EBX.
Ok, now I can start working :)
[alert7 @ redhat] # set -o noclobber && cat> tiny.asm << EOF
Tiny.asm
BITS 32
Global _Start
Section .text
_Start:
MOV Eax, 1
MOV EBX, 42
INT 0x80
EOF
[Alert7 @ redhat] # Nasm -f Elf Tiny.asm
[alert7 @ redhat] # gcc -wall -s -nostdlib tiny.o
[Alert7 @ redhat] # ./a.out; echo $?
42
Look at the size
[alert7 @ redhat] # wc -c a.out
416 a.out
It's really tiny now, huh, can you still be smaller?
How to use shorter instructions?
Take a look at the two assembly code:
00000000 B801000000 MOV EAX, 1
00000005 BB2A000000 MOV EBX, 42
0000000A CD80 INT 0x80
00000000 31C0 XOR EAX, EAX
00000002 40 Inc EAX
00000003 B32A MOV BL, 42
00000005 CD80 INT 0x80
It is obvious that it is equivalent to the equivalent, but the following is a 5 byte more than one.
Using GCC probably no longer decreased, let's use Linker - LD
[alert7 @ redhat] # set -o noclobber && cat> tiny.asm << EOF
Tiny.asm
BITS 32
Global _Start
Section .text
_Start:
XOR EAX, EAX
Inc Eaxmov BL, 42
INT 0x80
EOF
[Alert7 @ redhat] # Nasm -f Elf Tiny.asm
[alert7 @ redhat] # ld -s tiny.o
[alert7 @ redhat] # wc -c a.out
412 A.out
A small 4 bytes should be 5 bytes, but the additional bytes are used to consider aligned.
Will it reach the limit? Can you smaller?
HM. Our program code is now only 7 bytes long. Is there a 405-byte extra load? They are all
What?
Use ObjDump to see the contents of the file:
[Alert7 @ redhat] # Objdump -x a.out | Less
a.out: no symbols
A.out: File Format ELF32-I386
a.out
Architecture: I386, Flags 0x00000102:
EXEC_P, D_PAGED
START Address 0x08048080
Program HEADER:
Load off 0x00000000 VADDR 0x08048000 Paddr 0x08048000 align 2 ** 12
Filesz 0x00000087 MEMSZ 0x00000087 Flags R-X
SECTIONS:
IDX Name Size VMA LMA File Off Algn
0.text 00000007 08048080 08048080 00000080 2 ** 4
Contents, Alloc, Load, Readonly, CODE
1.BSS 00000001 08049087 08049087 00000087 2 ** 0
Contents
2.comment 000001c 00000000 00000000 00000088 2 ** 0
Contents, Readonly
[Translator Note: There are more .BSS section on my machine, I think it may be related to the LD version. So in my system
Demonstrate has always been larger than the original author :(
It seems that you want to think smaller, or you can consider looking for a low version of compilation :)
]
As above, the complete .Text section is 7 bytes, just as we just said.
But there are other festivals, such as ".comment", who arranges it? The ".comment" section is 28 bytes.
We don't know what the .comment festival is something, but can say boldly, it is not necessary.
The .Comment section is 000087 (16) in the file offset
Let's take a look at what is
[Alert7 @ redhat] # Objdump -s a.out
A.out: File Format ELF32-I386
Contents of section .text:
8048080 31c040b3 2acd80 1. @. * ..
CONTENTS OF Section .BSS:
8049087 00.
Contents of section.comment:
0000 00546865 204e6574 77696465 20417373.The NetWide ASS
2E393800 EMBLER 0.98 in 0010622030.
Oh, it is a message from NASM, maybe we should use GAS ....... If we:
[alert7 @ redhat] # set -o noclobber && cat> tiny.s << EOF
.globl _Start
.Text
_Start:
XORL% EAX,% EAX
INCL% EAX
MOVB $ 42,% BL
INT $ 0x80
EOF
[alert7 @ redhat] # gcc -s -nostdlib tiny.s
[Alert7 @ redhat] # ./a.out; echo $?
42
[alert7 @ redhat] # wc -c a.out
368 a.out
[Translator Note: There is no change in the size here, but in my system, it has become 368.
(The same as the author's machine), it is small than the front.
]
Use Objdump again, there will be some differences:
SECTIONS:
IDX Name Size VMA LMA File Off Algn
0 .Text 00000007 08048074 08048074 00000074 2 ** 2
Contents, Alloc, Load, Readonly, CODE
1.DATA 00000000 0804907C 0804907C 0000007C 2 ** 2
Contents, Alloc, Load, Data
2.BSS 00000000 0807907C 0804907C 0000007C 2 ** 2
Alloc
There is no COMMNET section, but there are more than two useless sections, used to store data that does not exist. And those festivals are still 0 lengths.
They make the file size bigger.
So they are all useless, how do we come down?
We need to prepare some knowledge of ELF file format. Although I have also translated "ELF file format",
in
Http://www.xfocus.org/ can be found, but the translation is very rubbish, and it has already been distinguished,
So I still recommend everyone to read the English original document, and it is highly recommended.
-------------------------------------------------- ------------------------------
ELF file format English document download address:
ftp://tsx.mit.edu/pub/linux/packages/gcc/elf.doc.tar.gz.
or
http://www.muppetlabs.com/~breadbox/software/elf.txt.
Basic, we need to know the following knowledge:
Each ELF file is started with an Elf Header structure. This structure is 52 bytes long and contains one
The information section, which describes the contents of the file. For example, the first 16 bytes contain a "identifier", it
The magic number of the ELF file is included, but the byte label indicates that is 32-bit or 64-bit, small or lower, and the like.
Other information included in Elf Header, for example: target system; if the ELF file is executable or Object
Document is still a shared library; the start address of the program; Program Header Table and Section Header Table
The offset in the file.
Both tables can be anywhere in the document, but they often followed directly behind the Elf Header, and later appearing in the end of the file may be close to the end. The two forms have a phase test, which is for the composition of the identity file. but,
Section Header Table is more concerned about where the identification is in different parts of the program, however, Program
Header table describes where and how to reprint those parts to memory.
Simply put, section header table is used by compiler and connector (Linker), Program
Header Table is used by the program loader (Loader). For the Object file, Program HEADER TALBE is
Optionally, it has never been seen. Similarly, for executable, section header TABLE
Also optional, but it always exists in an executable file.
Therefore, for our programs, the seciton header table is completely useless, and those Sections will not
Apoth affecting the program memory.
So, how do you get rid of them?
We must construct the program's Elf Header.
You can also see the ELF document and /usr/include/linux/Elf.h get information, an empty ELF executable should
Like the following:
BITS 32
ORG 0x08048000
EHDR:; ELF32_EHDR
DB 0x7F, "ELF", 1, 1, 1; E_IDENT
Times 9 DB 0
DW 2; E_TYPE
DW 3; E_MACHINE
DD 1; E_VERSION
DD _START; E_ENTRY
DD PHDR - $$; E_PHOFF
DD 0; E_SHOFF
DD 0; E_FLAGS
DW EHDRSIZE; E_EHSIZE
DW phdrsize; e_phennsize
DW 1; e_phnum
DW 0; E_SHENTSIZE
DW 0; E_SHNUM
DW 0; E_SHSTRNDX
EHDRSIZE EQU $ - EHDR
PHDR:; ELF32_PHDR
DD 1; p_type
DD 0; p_offsetdd $$; p_vaddr
DD $$; p_paddr
DD filesize; p_filesz
DD FileSize; P_Memsz
DD 5; p_flags
DD 0x1000; p_align
PHDRSIZE EQU $ - PHDR
_Start:
Your Program Here
FileSize Equ $ - $$
This image contains an Elf Header, no section header table, a program header table contains
An entry. The portal indicator loader loads the full file to memory (generally contains your own Elf Header and
Program Header Table) Start address 0x08048000 (this is where the default address loaded), and
Start executing _Start code, _start is tightly followed by the Program Header Table. No .data paragraph, no .bss segment
No .comment segment.
Ok, now our program turns this:
[alert7 @ redhat] # cat tiny.asm
Tiny.asm
ORG 0x08048000
EHDR:; ELF32_EHDR
DB 0x7F, "ELF", 1, 1, 1; E_IDENT
Times 9 DB 0
DW 2; E_TYPE
DW 3; E_MACHINE
DD 1; E_VERSION
DD _START; E_ENTRY
DD PHDR - $$; E_PHOFF
DD 0; E_SHOFF
DD 0; E_FLAGS
DW EHDRSIZE; E_EHSIZE
DW phdrsize; e_phennsize
DW 1; e_phnum
DW 0; E_SHENTSIZE
DW 0; E_SHNUM
DW 0; E_SHSTRNDX
EHDRSIZE EQU $ - EHDR
P_type
DD 0; p_offset
DD $$; p_vaddr
DD $$; p_paddr
DD filesize; p_filesz
DD FileSize; P_Memsz
DD 5; p_flags
DD 0x1000; p_align
PHDRSIZE EQU $ - PHDR
_Start:
MOV BL, 42
XOR EAX, EAX
INC EAX
INT 0x80
FileSize Equ $ - $$
[Alert7 @ redhat] # nasm -f bin -o a.out tiny.asm
[alert7 @ redhat] # chmod x a.out
[Alert7 @ redhat] # ./a.out; echo $?
42
Take a look at the size:
[alert7 @ redhat] # wc -c a.out
93 a.out
It's really a miracle, only 93 bytes size.
If we understand each byte in the executable, we may still be smaller, maybe it is very limit :)
-------------------------------------------------- ------------------------------
You may have noticed:
1) ELF files Allow to be positioned anywhere (except Elf Header, it must be placed in the beginning of the file),
And they can overlap.
2) In fact, some fields have not been used yet.
In the identification file field, there are 9 bytes of 0, and our code is only 7 bytes long, so we try to put the code in
In the last 9 bytes of the identification file field, there are 2 remaining. ....
[alert7 @ redhat] # cat tiny.asm
Tiny.asm
BITS 32
ORG 0x08048000
EHDR:; ELF32_EHDR
DB 0x7F, "ELF"; E_IDENT
DB 1, 1, 1, 0
_Start: MOV BL, 42
XOR EAX, EAX
INC EAX
INT 0x80
DB 0
DW 2; E_TYPE
DW 3; E_MACHINE
DD 1; E_VERSION
DD _START; E_ENTRY
DD PHDR - $$; E_PHOFFDD 0; E_SHOFF
DD 0; E_FLAGS
DW EHDRSIZE; E_EHSIZE
DW phdrsize; e_phennsize
DW 1; e_phnum
DW 0; E_SHENTSIZE
DW 0; E_SHNUM
DW 0; E_SHSTRNDX
EHDRSIZE EQU $ - EHDR
PHDR:; ELF32_PHDR
DD 1; p_type
DD 0; p_offset
DD $$; p_vaddr
DD $$; p_paddr
DD filesize; p_filesz
DD FileSize; P_Memsz
DD 5; p_flags
DD 0x1000; p_align
PHDRSIZE EQU $ - PHDR
FileSize Equ $ - $$
[Alert7 @ redhat] # nasm -f bin -o a.out tiny.asm
[alert7 @ redhat] # chmod x a.out
[Alert7 @ redhat] # ./a.out; echo $?
42
[alert7 @ redhat] # wc -c a.out
84 A.out
Now our program has only one Elf Header and a Program Header Table entry, in order to load and run the program,
These are our necessary. So now we can't decrease! unless....
We have a part of Elf Header and Program Header Table or an overlap, is it possible?
The answer is of course, pay attention to our procedure, you will notice that the last 8 bytes of Elf Header and Program HEADER TABLE
The first 8 bytes are the same, so ...
[alert7 @ redhat] # cat tiny.asm
Tiny.asm
BITS 32
ORG 0x08048000
EHDR:
DB 0x7F, "ELF"; E_IDENT
DB 1, 1, 1, 0
_Start: MOV BL, 42
XOR EAX, EAX
INC EAX
INT 0x80db 0
DW 2; E_TYPE
DW 3; E_MACHINE
DD 1; E_VERSION
DD _START; E_ENTRY
DD PHDR - $$; E_PHOFF
DD 0; E_SHOFF
DD 0; E_FLAGS
DW EHDRSIZE; E_EHSIZE
DW phdrsize; e_phennsize
PHDR: DD 1; E_PHNUM; P_TYPE
E_SHENTSIZE
DD 0; E_SHNUM; P_OFFSET
E_SHSTRNDX
EHDRSIZE EQU $ - EHDR
DD $$; p_vaddr
DD $$; p_paddr
DD filesize; p_filesz
DD FileSize; P_Memsz
DD 5; p_flags
DD 0x1000; p_align
PHDRSIZE EQU $ - PHDR
FileSize Equ $ - $$
[Alert7 @ redhat] # nasm -f bin -o a.out tiny.asm
[alert7 @ redhat] # chmod x a.out
[Alert7 @ redhat] # ./a.out; echo $?
42
[alert7 @ redhat] # wc -c a.out
76 a.out
It is no longer able to overlap the two structures, because the bytes of the two structures are not the same.
However, we can then construct these two structures so that they have more the same part.
How much is Linux? For example, will it check the E_MACHINE field?
In fact, it is still surprising that some fields are actually ignored silently.
So: What is the most important thing in Elf HEADER? The top four bytes of course are of course, it contains one
Magic number, otherwise Linux will not continue to handle it. The other three bytes of the E_IDENT field are not checked, that means
We have no less than 12 consecutive bytes we can set to any value. E_TYPE must be set to 2 (used to indicate
Is an executable file), E_MACHINE must be 3. Just like the version number in E_IDENT, E_VERSION is completely
ignore. (This can be understood because there is only one version of the ELF standard). E_ENTRY is of course set to correct
The value because it points to the beginning of the program. There is no doubt that E_PHOFF should be the correct offset in the Program Header Table in the file, and E_PHNUM is the correct port number included in the Program HEADER TABLE. However, e_flags
There is no use of the current Intel system, so we should be able to reuse. E_EHSIZE to check Elf Header
The desired size, but Linux ignores it. E_PHENTSIZE The same confirmation of the Program Header Table entrance
size. But only this field is checked only in the 2.2 series kernel after 2.2.17. Earlier than 2.2 and 2.4.0
The kernel is ignored.
How is the Program Header Table?
P_TYPE must be 1 (ie pt_load), indicating that this is a loadable segment. P_offset is the file offset that starts the load.
Similarly, p_vaddr is the correct load address. Note: We didn't require it to load it to 0x08048000.
The available addresses are 0-0X80000000, and the page is equal to. The document says P_Paddr is ignored, so this field is even more
use. P_filesz indicates how many bytes are loaded from the file to memory, and P_MEMSZ indicates how much memory is required.
Therefore, their values should be related. P_flags indicates what permissions given to the memory segment. Can set read, write, execute,
Other bits may also be set, but we only need minimum permissions. Finally, P_Align gives alignment requirements. This field is mainly
Using the code-independent code that is unrelated to the location, the executable file will be ignored by Linux to ignore this field.
According to the analysis, we can see some necessary fields, some useless fields, so we can overlap more
Word number.
[alert7 @ redhat] # cat tiny.asm
Tiny.asm ◆
BITS 32
ORG 0x00200000
DB 0x7F, "ELF"; E_IDENT
DB 1, 1, 1, 0
_Start:
MOV BL, 42
XOR EAX, EAX
INC EAX
INT 0x80
DB 0
DW 2; E_TYPE
DW 3; E_MACHINE
DD 1; E_VERSION
DD _START; E_ENTRY
DD PHDR - $$; E_PHOFF
PHDR: DD 1; E_SHOFF; P_TYPE
DD 0; e_flags; p_offset
DD $$; E_EHSIZE; P_VADDR
E_PHENTSIZE
DW 1; E_PHNUM; P_PADDR
DW 0; E_SHENTSIZE
DD FileSize; E_SHNUM; P_FILESZ
E_SHSTRNDX
DD filesize; p_memszdd 5; p_flags
DD 0x1000; p_align
FileSize Equ $ - $$
As you can see, the first 12 bytes of Program Header Table overlap in the last 12 bytes of Elf Header.
Quite anastomosis. There are only two parts in Elf Header to repeat. First, the e_phnum field, corresponds to p_paddr
It will be ignored. The second is the E_PHENTSIZE field, which is consistent with the first two bytes of p_vaddr, for this,
Using non-standard loading addresses 0x00200000, then the two bytes in front are 0x0020.
[Alert7 @ redhat] # nasm -f bin -o a.out tiny.asm
[alert7 @ redhat] # chmod x a.out
[Alert7 @ redhat] # ./a.out; echo $?
42
[alert7 @ redhat] # wc -c a.out
64 a.out
Well, now the size is 64 bytes.
If we make the Program Header Table in Elf Header, then, huh, you can smaller,
But do you do this?
Yes, it is possible. Enable the Program Header Table from the fourth byte, carefully construct the executable ELF file.
We have noticed:
The first P_MEMSZ pointed out how much memory allocated for the memory segment. Obviously, it must be as large as P_Filesz,
Of course, it is nothing to do.
Second, executable can discard from the p_flags field, Linux will set it for us. Why will this work?
The author said that he didn't know, but guess the reason for this paragraph due to the entrance pointer?
[★ Translator Note:
But I know that Linux does not set the executable bit in the p_flags field for us, you can work.
Just because the intel system does not have the implementation of protection, this reason is that some people have
It is necessary to design a similar stack that cannot be running kernel patches.
]
[alert7 @ redhat] # cat tiny.asm
Tiny.asm
BITS 32
ORG 0x00001000
DB 0x7F, "ELF"; E_IDENT
DD 1; p_type
DD 0; p_offset
DD $$; p_vaddr
DW 2; E_TYPE; P_PADDR
DW 3; E_MACHINE
DD filesize; e_version; p_filesz
DD _START; E_ENTRY; P_MEMSZ
DD 4; E_PHOFF; p_flags_start:
MOV BL, 42; E_SHOFF; P_ALIGN
XOR EAX, EAX
Inc Eax; E_FLAGS
INT 0x80
DB 0
DW 0x34; E_EHSIZE
DW 0x20; E_PHENTSIZE
DW 1; e_phnum
DW 0; E_SHENTSIZE
DW 0; E_SHNUM
DW 0; E_SHSTRNDX
FileSize Equ $ - $$
The p_flags field changes from 5 to 4, which is also the value of the E_PHOFF field, which gives the Program Header Table in the file.
The offset. The code is placed in the end of the E_SHOFF to the E_FLAGS.
Note that the loading address is changed. Just to keep the value of E_ENTRY to a relatively appropriate value, it is just
It is also the value of p_mensz.
[Alert7 @ redhat] # nasm -f bin -o a.out tiny.asm
[alert7 @ redhat] # chmod x a.out
[Alert7 @ redhat] # ./a.out; echo $?
42
[alert7 @ redhat] # wc -c a.out
52 a.out
Now, the program code itself and the Program Header Table are completely embedded in Elf Header, and our executable is now and
Elf header is as large. And you can run normally.
Finally, we can't help but ask, have you reached the minimum limit? After all, we need a complete Elf Header, otherwise
Linux does not give us an opportunity to run.
Is it really ?
Wrong, we can also use the last inspirable kid technology.
If the file size does not have the entire Elf Header, Linux will still run it. And filled those less bytes as
0. We have no less than 7 0 in the file, you can discard it.
[alert7 @ redhat] # cat tiny.asm
Tiny.asm
BITS 32
ORG 0x00001000
DB 0x7F, "ELF"; E_IDENT
DD 1; p_type
DD 0; p_offset
DD $$; p_vaddr
DW 2; E_TYPE; P_PADDR
DW 3; E_MACHINE
DD filesize; e_version; p_filesz
DD _START; E_ENTRY; P_MEMSZ
DD 4; E_PHOFF; p_flags_start:
MOV BL, 42; E_SHOFF; P_ALIGN
XOR EAX, EAX
Inc Eax; E_FLAGS
INT 0x80
DB 0
DW 0x34; E_EHSIZE
DW 0x20; E_PHENTSIZE
DB 1; E_PHNUM
E_SHENTSIZE
E_SHNUM
E_SHSTRNDX
FileSize Equ $ - $$
[Alert7 @ redhat] # nasm -f bin -o a.out tiny.asm
[alert7 @ redhat] # chmod x a.out
[Alert7 @ redhat] # ./a.out; echo $?
42
[alert7 @ redhat] # wc -c a.out
45 a.out
Discussion here, a minimum size of an ELF executable is 45 bytes, we were forced to terminate our discussion.
-------------------------------------------------- ------------------------------
A 45-byte size file is smaller than 1/8 of the minimum executable file created with a standard tool, compared with pure C code
The 1/50 created is also small.
Half of the ELF field variable in this article violates the standard ELF specification,
The procedure in the above programs will make Readelf Core DUMP
[alert7 @ redhat] # iedelf -a a.out
Elf header:
MAGIC: 7F 45 4C 46 01 01 01 00 B3 2A 31 C0 40 CD 80 00
Class: ELF32
Data: 2's Complement, Little Endian
Version: 1 (Current)
OS / ABI: UNIX - SYSTEM V
ABI VERSION: 179
TYPE: EXEC (Executable File)
Machine: intel 80386
Version: 0x1
Entry Point Address: 0x200008
Start of Program Headers: 32 (Bytes Into file)
Start of Section Headers: 1 (bytes Into file)
Flags: 0x0
Size of this header: 0 (bytes)
Size of Program Headers: 32 (Bytes)
Number of Program Headers: 1
Size of Section Headers: 0 (bytes) Number of Section Headers: 64
Section header string Table Index: 0
Readelf: Error: Unable to read in 0 bytes of section headers
Program HEADER:
Type Offset Virtdr Physaddr FileSiz Memsiz Flg Align
LOAD 0x000000 0x00200000 0x00000001 0x00040 0x00040 R e 0x1000
There is no dynamic segment in this file.
Segmentation Fault (Core Dumped)
Oh, there is a cute Core Dumped.
[Alert7 @ redhat] # ls -l / usr / bin / readelf
-RWXR-XR-x 1 root root 132368 Feb 5 2000 / USR / BIN / READELF
:( Don't bring S bit, too lazy to see where it is out.
This ultra-small ELF file created is indeed a fault, even Objdump can't dump them.
[Alert7 @ redhat] # Objdump -a a.out
Objdump: a.out: File Format Not Recognized