Create a super small ELF executable on the Linux platform

xiaoxiao2021-04-05  272

Create a super small ELF executable on the Linux platform

Author: breadbox

Original

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

: push% EBP

0x80483A1

: MOV% ESP,% EBP

0x80483A3

: MOV $ 0x2a,% EAX

0x80483A8

: jmp 0x80483b0

0x80483AA

: Lea 0x0 (% ESI),% ESI

0x80483B0

: Leave

0x80483b1

: RET

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


New Post(0)