mirror of
https://github.com/upx/upx
synced 2025-09-28 19:06:07 +08:00
Update because of age and evolution.
modified: elf-to-mem.txt
This commit is contained in:
parent
ed2633bf95
commit
67564513d2
|
@ -15,7 +15,7 @@ the value that is manipulated by system call 45 (__NR_brk in
|
||||||
there will be no execve() except for the execve() of the decompressor
|
there will be no execve() except for the execve() of the decompressor
|
||||||
program itself. So, the decompressor program (which contains the
|
program itself. So, the decompressor program (which contains the
|
||||||
compressed version of the original executable) must have the same
|
compressed version of the original executable) must have the same
|
||||||
brk() as the original executable. So, the second PT_LOAD
|
brk() as the original executable. So, the first PT_LOAD
|
||||||
ELF "segment" of the compressed program is used only to set the brk(0).
|
ELF "segment" of the compressed program is used only to set the brk(0).
|
||||||
See src/p_lx_elf.cpp, function PackLinuxElf32::generateElfHdr.
|
See src/p_lx_elf.cpp, function PackLinuxElf32::generateElfHdr.
|
||||||
All of the decompressor's code, and all of the compressed image
|
All of the decompressor's code, and all of the compressed image
|
||||||
|
@ -25,35 +25,27 @@ decompressor program.
|
||||||
The decompressor program stub is just under 2K bytes when linked.
|
The decompressor program stub is just under 2K bytes when linked.
|
||||||
After linking, the decompressor code is converted to an initialized
|
After linking, the decompressor code is converted to an initialized
|
||||||
array, and #included into the compilation of the compressor;
|
array, and #included into the compilation of the compressor;
|
||||||
see src/stub/l_le_n2b.h. To make self-contained compressed
|
see stub/i386-linux.elf-entry.h. To make self-contained compressed
|
||||||
executables even smaller, the compressor also compresses all but the
|
executables even smaller, the compressor also compresses all but the
|
||||||
startup and decompression subroutine of the decompressor itself,
|
startup and decompression subroutine of the decompressor itself,
|
||||||
saving a few hundred bytes. The startup code first decompresses the
|
saving a few hundred bytes. The startup code first decompresses the
|
||||||
rest of the decompressor, then jumps to it. A nonstandard linker
|
rest of the decompressor, then jumps to it. A nonstandard linker
|
||||||
script src/stub/l_lx_elf86.lds places both the .text and .data
|
script src/stub/src/i386-linux.elf-entry.lds arranges the SECTIONS
|
||||||
of the decompressor into the same PT_LOAD at 0x00401000. The
|
so that PackLinuxElf32x86::buildLoader() and buildLinuxLoader()
|
||||||
compressor includes the compressed bytes of the original executable
|
generate the desired stub code, which goes into PT_LOAD[1].
|
||||||
at the end of this first PT_LOAD.
|
|
||||||
|
|
||||||
At runtime, the decompressed stub lives at 0x00400000. In order for the
|
At runtime, the decompressed stub lives close beyond the brk().
|
||||||
decompressed stub to work properly at an address that is different
|
In order for the decompressed stub to work properly at an address
|
||||||
from its link-time address, the compiled code must contain no absolute
|
that is different from its link-time address, the compiled code must
|
||||||
addresses. So, the data items in l_lx_elf.c must be only parameters
|
contain no absolute addresses. So, the data items in stub code
|
||||||
and automatic (on-stack) local variables; no global data, no static data,
|
must be only parameters and automatic (on-stack) local variables;
|
||||||
and no string constants. Use "size l_le_n2b.o l_6e_n2b.o" to check
|
no global data, no static data, and no string constants. Also,
|
||||||
that both data and bss have length zero. Also, the '&' operator
|
the '&' operator may not be used to take the address of a function.
|
||||||
may not be used to take the address of a function.
|
|
||||||
|
|
||||||
The address 0x00400000 was chosen to be out of the way of the usual
|
|
||||||
load address 0x08048000, and to minimize fragmentation in kernel
|
|
||||||
page tables; one page of page tables covers 4 MiB. The address
|
|
||||||
0x00401000 was chosen as 1 page up from a 64 KiB boundary, to
|
|
||||||
make the startup code and its constants smaller.
|
|
||||||
|
|
||||||
Decompression of the executable begins by decompressing the Elf32_Ehdr
|
Decompression of the executable begins by decompressing the Elf32_Ehdr
|
||||||
and Elf32_Phdr, and then uses the Ehdr and Phdrs to control decompression
|
and Elf32_Phdr, and then uses those Ehdr and Phdrs to control decompression
|
||||||
of the PT_LOAD segments.
|
of the PT_LOAD segments. Subroutine do_xmap() of src/stub/src/
|
||||||
Subroutine do_xmap() of src/stub/l_lx_elf.c performs the
|
i386-linux.elf-main.c performs the
|
||||||
"virtual execve()" using the compressed data as source, and stores
|
"virtual execve()" using the compressed data as source, and stores
|
||||||
the decompressed bytes directly into the appropriate virtual addresses.
|
the decompressed bytes directly into the appropriate virtual addresses.
|
||||||
|
|
||||||
|
@ -74,22 +66,17 @@ expanding $ORIGIN in -rpath, or for application code that relies on
|
||||||
--unmap-all-pages to achieve that effect at run time. Upx-3.04
|
--unmap-all-pages to achieve that effect at run time. Upx-3.04
|
||||||
and previous versions did this by default with no option. However,
|
and previous versions did this by default with no option. However,
|
||||||
too much other software erroneously assumes that /proc/self/exe
|
too much other software erroneously assumes that /proc/self/exe
|
||||||
always exists.
|
always exists. upx-4.3.0 made /proc/self/exe optional so that
|
||||||
|
chroot() and related environments can work.
|
||||||
|
For Elf formats, UPX adds an environment variable named " " [three
|
||||||
|
spaces] which saves the results of readlink("/proc/self/exe",,)
|
||||||
|
If /proc/self/exe is ENOENT, then the variable has the same value
|
||||||
|
as its name "/proc/self/exe".
|
||||||
|
|
||||||
On arm*-linux-elf there is no good address at which to retain one
|
All of the above documentation refers to ET_EXEC main programs,
|
||||||
page of the compressed executable. Pages below the usual .p_vaddr
|
which always use the same virtual addresses. An ET_DYN executable
|
||||||
0x8000 (32KiB) are rejected by the kernel. Using a page above the
|
(main program or shared library) follows much the same scheme,
|
||||||
original uncompressed brk(0) would require placing the entire initial
|
re-using the address space that the kernel chose originally.
|
||||||
compressed program above uncompressed brk(0), which would significantly
|
|
||||||
increase the running brk(0); but too many programs break if brk(0)
|
|
||||||
moves. Thus on arm*-linux-elf the compressed executable begins
|
|
||||||
with 0x8000==.p_vaddr, all pages mapped by execve() that are also
|
|
||||||
occupied by decompressed bytes are removed before overwriting, and
|
|
||||||
/proc/self/exe becomes a "(deleted)" symlink. It might be possible
|
|
||||||
to preserve /proc/self/exe if the original uncompressed executable
|
|
||||||
were created with 0x9000==.p_vaddr (one page higher than the usual
|
|
||||||
0x8000) so that the compressed page mapped at 0x8000 would linger.
|
|
||||||
[This has not been tested.]
|
|
||||||
|
|
||||||
Linux stores the pathname argument that was specified to execve()
|
Linux stores the pathname argument that was specified to execve()
|
||||||
immediately after the '\0' which terminates the character string of the
|
immediately after the '\0' which terminates the character string of the
|
||||||
|
@ -99,9 +86,24 @@ records a pointer to that character string in Elf32_auxv[AT_EXECFN].
|
||||||
The pathname is not "bound" to the file as strongly as /proc/self/exe
|
The pathname is not "bound" to the file as strongly as /proc/self/exe
|
||||||
(the file may be changed without affecting the pathname), but the
|
(the file may be changed without affecting the pathname), but the
|
||||||
pathname does provide some information. The pathname may be relative
|
pathname does provide some information. The pathname may be relative
|
||||||
to the working directory, so look before any chdir().
|
to the working directory, so look before performing any chdir().
|
||||||
|
|
||||||
The Elf formats for Linux add an environment variable named " " [three
|
On any page, then SELinux in strictest enforcing mode prohibits
|
||||||
spaces] which saves the results of readlink("/proc/self/exe",,) before
|
simultaneous PROT_EXEC and PROT_WRITE, and also prohibits adding
|
||||||
the runtime stub unmaps all its pages. As of 2006-10-03 this works
|
PROT_EXEC if the kernel VMA struct (Virtual Memory Area struct)
|
||||||
for linux/elf386 and linux/ElfAMD.
|
that manages that page ever has had PROT_WRITE. This implies that
|
||||||
|
the only way to get PROT_EXEC is to map the page directly from a file.
|
||||||
|
Therefore, in late 2023 the various decompression stubs are being
|
||||||
|
rewritten to "bounce" the decompressed data through pages in a
|
||||||
|
memory-resident file created by the memfd_create() system call,
|
||||||
|
and subsequently mapped PROT_EXEC. Actual copying of the pages
|
||||||
|
can be avoided by careful sequence mmap() modes, but the overhead
|
||||||
|
of an additional system call is required.
|
||||||
|
|
||||||
|
The not-as-strict "targeted enforcing" mode of
|
||||||
|
SELinux seems not to demand this extra work, except for executables
|
||||||
|
that run with elevated privileges, such as various system daemons.
|
||||||
|
So "ordinary" user-mode apps can run in current "targeted enforcing"
|
||||||
|
mode. But because the actual runtime mode of SELinux is unknown
|
||||||
|
at compression time, then the memfd_create method should be used
|
||||||
|
all the time.
|
||||||
|
|
Loading…
Reference in New Issue
Block a user