mirror of
https://github.com/upx/upx.git
synced 2025-08-11 22:52:30 +08:00
SELinux for i386 shlib stub
modified: stub/src/i386-linux.elf-so_entry.S modified: stub/src/i386-linux.elf-so_fold.S modified: stub/src/i386-linux.elf-so_main.c modified: stub/src/include/linux.h
This commit is contained in:
@ -48,12 +48,14 @@ MAP_PRIVATE= 2
|
||||
MAP_FIXED= 0x10
|
||||
MAP_ANONYMOUS= 0x20
|
||||
|
||||
__NR_memfd_create= 0x164 // 356
|
||||
__NR_mmap= 90
|
||||
__NR_mprotect=125
|
||||
__NR_munmap= 91
|
||||
|
||||
__NR_write= 4
|
||||
__NR_close= 6
|
||||
__NR_exit= 1
|
||||
__NR_write= 4
|
||||
|
||||
PAGE_SHIFT= 12
|
||||
PAGE_MASK= (~0<<PAGE_SHIFT)
|
||||
@ -76,7 +78,7 @@ M_NRV2E_LE32=8
|
||||
// .long offset({l_info; p_info; b_info; compressed data})
|
||||
section ELFMAINX
|
||||
_start: // C-called: %esp: ret_addr,argc,argv,envp; must save %ebx,%esi,%edi,%ebp
|
||||
nop // DEBUG i386 so_entry.S
|
||||
nop // int3 // DEBUG i386 so_entry.S
|
||||
pusha // MATCH_03
|
||||
call L70 // MATCH_08 push $&getbit
|
||||
L70ret:
|
||||
@ -102,25 +104,20 @@ refill:
|
||||
adc bits,bits // LSB= 1 (CarryIn); CarryOut= next bit
|
||||
ret // infrequent (1/32)
|
||||
|
||||
#define foldi %esi
|
||||
#define old_sp %ebp
|
||||
L20:
|
||||
pop %ebx // MATCH_09 &fold_info
|
||||
cmpw $M_NRV2B_LE32|(0<<8),b_method(%ebx); je 0f; hlt; 0: // check method and filter bytes
|
||||
pop foldi // MATCH_09 &fold_info
|
||||
cmpw $M_NRV2B_LE32|(0<<8),b_method(foldi); je 0f; hlt; 0: // check method and filter bytes
|
||||
lea _start - 4*NBPW - L70ret(%edx),%ecx // &so_info
|
||||
|
||||
push %ecx // MATCH_14 &so_info
|
||||
push %ebx // MATCH_15 old_mmap destroys
|
||||
mov /*sz_unc*/(foldi),%eax; push %eax // MATCH_15 F_LENU
|
||||
mov %esp,old_sp
|
||||
#define F_LENU 0*NBPW
|
||||
|
||||
// Get pages for unfolded code
|
||||
push $0 // arg6 offset
|
||||
push $-1 // arg5 fd
|
||||
push $MAP_PRIVATE|MAP_ANONYMOUS // arg4 modes
|
||||
push $PROT_READ|PROT_WRITE // arg3 prot
|
||||
push /*sz_unc*/(%ebx) // arg2 dstlen
|
||||
push $0 // arg1 addr (kernel chooses)
|
||||
call old_mmap
|
||||
pop %ebx // MATCH_15 &fold_info
|
||||
push /*sz_unc*/(%ebx) // MATCH_10 len unfolded cold
|
||||
push %eax // MATCH_04 ptr unfolded_code
|
||||
sub %eax,%esp // alloca
|
||||
and $-2*NBPW,%esp // align stack
|
||||
|
||||
// This is nrv2b_d32, inlined and optimized for small space (about 160 bytes).
|
||||
// The task is to de-compress the folded pieces for shared library init:
|
||||
@ -132,15 +129,19 @@ L20:
|
||||
// and compressability of C-coded de-compressors for Lzma and Zstd
|
||||
// in contrast to the simple and small assembly-coded NRV.
|
||||
|
||||
mov %eax,dst // &unfolded_code
|
||||
mov sz_cpr(%ebx),lsrc
|
||||
lea sz_b_info(%ebx),src
|
||||
mov %esp,dst // &unfolded_code
|
||||
push old_sp // MATCH_16
|
||||
mov sz_cpr(foldi),lsrc
|
||||
lea sz_b_info(foldi),src // foldi dead
|
||||
decompress: // inlined: (uchar const *src, uint len, uchar *dst /*, u32 &ldst, uint method */)
|
||||
add src,lsrc; push lsrc // MATCH_05 &input_eof
|
||||
|
||||
//%esp:
|
||||
// MATCH_05 &input_eof
|
||||
// MATCH_04 ptr unfolded_code
|
||||
// MATCH_16 old_sp
|
||||
// space for de-compressed code
|
||||
|
||||
//old_sp:
|
||||
// MATCH_10 len unfolded_code
|
||||
// MATCH_14 &so_info
|
||||
// MATCH_03 pusha regs {%edi,%esi,%ebp,%esp,%ebx,%edx,%ecx,%eax}
|
||||
@ -188,14 +189,35 @@ gotlen_n2b:
|
||||
eof_n2b:
|
||||
pop %ecx // MATCH_05 &input_eof
|
||||
cmp %ecx,%esi; je 0f; hlt; 0: // test for ending in correct place
|
||||
pop old_sp // MATCH_16
|
||||
|
||||
pop %ebx // MATCH_04 arg1 ptr unfolded_code
|
||||
pop %ecx // MATCH_10 arg2 len unfoded_code
|
||||
push %ecx // MATCH_12 len unfolded code
|
||||
push %ebx // MATCH_13 ptr unfolded code
|
||||
push $PROT_EXEC|PROT_READ; pop %edx // arg3
|
||||
push $__NR_mprotect; pop %eax; int $0x80
|
||||
xor %ecx,%ecx // zero
|
||||
call 0f; .asciz "upx"; 0: pop %ebx
|
||||
push $__NR_memfd_create; pop %eax; int $0x80
|
||||
#define mfd %edi
|
||||
mov %eax,mfd
|
||||
|
||||
mov F_LENU(old_sp),%edx
|
||||
mov %esp,%ecx
|
||||
mov %edi,%ebx
|
||||
push $__NR_write; pop %eax; int $0x80
|
||||
mov old_sp,%esp // de-alloca
|
||||
|
||||
push $0 // arg6
|
||||
push mfd // arg5
|
||||
push $MAP_PRIVATE // arg4
|
||||
push $PROT_READ|PROT_EXEC // arg3
|
||||
push 4*NBPW(%esp) // arg2 F_LENU
|
||||
push $0 // arg1
|
||||
mov %esp,%ebx
|
||||
push $__NR_mmap; pop %eax; int $0x80; add $6*NBPW,%esp
|
||||
|
||||
push %eax // MATCH_13 ptr unfolded_code
|
||||
mov mfd,%ebx
|
||||
push $__NR_close; pop %eax; int $0x80
|
||||
|
||||
pop %eax; push %eax
|
||||
jmp *%eax
|
||||
// %esp:
|
||||
// MATCH_13 ptr unfolded_code; for escape hatch
|
||||
// MATCH_12 len unfolded code; for escape hatch
|
||||
@ -206,8 +228,6 @@ eof_n2b:
|
||||
// MATCH_01 argv
|
||||
// MATCH_07 envp
|
||||
|
||||
pop %eax; push %eax // MATCH_13 ptr unfolded code
|
||||
jmp *%eax // enter C code
|
||||
|
||||
old_mmap: // oldmmap: ebx -> 6 arguments; remove arguments on return
|
||||
lea 4(%esp),%ebx
|
||||
|
@ -1,3 +1,5 @@
|
||||
#include "MAX_ELF_HDR.S"
|
||||
|
||||
#define section .section
|
||||
NBPW= 4
|
||||
|
||||
@ -23,10 +25,11 @@ NBPW= 4
|
||||
fold:
|
||||
mov %esp,%eax
|
||||
mov 2*NBPW(%eax),%ecx // &so_info
|
||||
sub $MAX_ELF_HDR_32,%esp; push %esp // &elf_tmp
|
||||
add $(3+8+1)*NBPW,%eax; push %eax // &{argc,argv,envp}
|
||||
push %ecx // &so_info
|
||||
call upx_so_main // (&so_info, &{argc, argv, envp}); returns &escape_hatch
|
||||
add $2*NBPW,%esp // remove args
|
||||
call upx_so_main // (&so_info, &{argc, argv, envp}, &elf_tmp); returns &escape_hatch
|
||||
add $MAX_ELF_HDR_32 + 3*NBPW,%esp // remove args
|
||||
mov %eax,%ebp // save &escape_hatch
|
||||
|
||||
pop %ebx // MATCH_13 ptr unfolded code
|
||||
@ -106,6 +109,7 @@ __NR_write= 4
|
||||
__NR_open= 5
|
||||
__NR_close= 6
|
||||
|
||||
__NR_memfd_create= 0x164 // 356
|
||||
__NR_mmap= 90
|
||||
__NR_mprotect=125
|
||||
__NR_munmap= 91
|
||||
@ -164,6 +168,9 @@ Pprotect: .globl Pprotect
|
||||
mov 1*NBPW(%esp),%ebx // restore reg
|
||||
ret
|
||||
|
||||
memfd_create: .globl memfd_create
|
||||
int3
|
||||
push $__NR_memfd_create; 5: jmp 5f
|
||||
mprotect: .globl mprotect
|
||||
mov %ebx,%eax; and $-1+ (1<<12),%eax
|
||||
sub %eax,%ebx
|
||||
@ -171,6 +178,8 @@ mprotect: .globl mprotect
|
||||
push $ __NR_mprotect; 5: jmp 5f
|
||||
exit: .globl exit
|
||||
push $ __NR_exit; 5: jmp 5f
|
||||
close: .globl close
|
||||
push $__NR_close; 5: jmp 5f
|
||||
munmap: .globl munmap
|
||||
push $ __NR_munmap; 5: jmp 5f
|
||||
write: .globl write
|
||||
|
@ -107,9 +107,9 @@ ssize_t write(int, void const *, size_t);
|
||||
static int dprintf(char const *fmt, ...); // forward
|
||||
#endif //}
|
||||
|
||||
#ifdef __arm__ /*{*/
|
||||
#ifdef __arm__ //{
|
||||
extern unsigned div10(unsigned);
|
||||
#endif /*}*/
|
||||
#endif //}
|
||||
|
||||
#if DEBUG //{
|
||||
void dprint8(
|
||||
@ -241,99 +241,100 @@ ERR_LAB
|
||||
}
|
||||
}
|
||||
|
||||
#if defined(__i386__) /*{*/
|
||||
#if defined(__i386__) //}{
|
||||
#define addr_string(string) ({ \
|
||||
char const *str; \
|
||||
asm("call 0f; .asciz \"" string "\"; 0: pop %0" \
|
||||
/*out*/ : "=r"(str) ); \
|
||||
str; \
|
||||
})
|
||||
#elif defined(__arm__) //}{
|
||||
#define addr_string(string) ({ \
|
||||
char const *str; \
|
||||
asm("bl 0f; .string \"" string "\"; .balign 4; 0: mov %0,lr" \
|
||||
/*out*/ : "=r"(str) \
|
||||
/* in*/ : \
|
||||
/*und*/ : "lr"); \
|
||||
str; \
|
||||
})
|
||||
#else //}{
|
||||
error;
|
||||
#endif //}
|
||||
|
||||
#if defined(__i386__) //
|
||||
// Create (or find) an escape hatch to use when munmapping ourselves the stub.
|
||||
// Called by do_xmap to create it; remembered in AT_NULL.d_val
|
||||
static void *
|
||||
make_hatch_i386(Elf32_Phdr const *const phdr, ptrdiff_t reloc)
|
||||
static char *
|
||||
make_hatch_i386(
|
||||
Elf32_Phdr const *const phdr,
|
||||
char *base,
|
||||
char *next_unc,
|
||||
char *mfd_addr,
|
||||
unsigned frag_mask
|
||||
)
|
||||
{
|
||||
unsigned xprot = 0;
|
||||
unsigned *hatch = 0;
|
||||
DPRINTF("make_hatch %%p %%x %%x\\n",phdr,reloc,0);
|
||||
char *hatch = 0;
|
||||
DPRINTF("make_hatch %%p %%p %%p %%p %%x\\n", phdr, base, next_unc, mfd_addr, frag_mask);
|
||||
if (phdr->p_type==PT_LOAD && phdr->p_flags & PF_X) {
|
||||
// The format of the 'if' is
|
||||
// if ( ( (hatch = loc1), test_loc1 )
|
||||
// || ( (hatch = loc2), test_loc2 ) ) {
|
||||
// action
|
||||
// }
|
||||
// which uses the comma to save bytes when test_locj involves locj
|
||||
// and the action is the same when either test succeeds.
|
||||
|
||||
if (
|
||||
// Try page fragmentation just beyond .text .
|
||||
( (hatch = (void *)(phdr->p_memsz + phdr->p_vaddr + reloc)),
|
||||
( phdr->p_memsz==phdr->p_filesz // don't pollute potential .bss
|
||||
&& 4<=(~PAGE_MASK & -(int)hatch) ) ) // space left on page
|
||||
// Try Elf32_Ehdr.e_ident[12..15] . warning: 'const' cast away
|
||||
|| ( (hatch = (void *)(&((Elf32_Ehdr *)phdr->p_vaddr + reloc)->e_ident[12])),
|
||||
(phdr->p_offset==0) )
|
||||
// Allocate and use a new page.
|
||||
|| ( xprot = 1, hatch = mmap(0, PAGE_SIZE, PROT_WRITE|PROT_READ,
|
||||
MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) )
|
||||
) {
|
||||
// Omitting 'const' saves repeated literal in gcc.
|
||||
unsigned /*const*/ escape = 0xc36180cd; // "int $0x80; popa; ret"
|
||||
// Don't store into read-only page if value is already there.
|
||||
if (* (volatile unsigned*) hatch != escape) {
|
||||
* hatch = escape;
|
||||
}
|
||||
if (xprot) {
|
||||
Pprotect(hatch, 1*sizeof(unsigned), PROT_EXEC|PROT_READ);
|
||||
}
|
||||
DPRINTF(" hatch at %%p\\n", hatch);
|
||||
next_unc += phdr->p_memsz - phdr->p_filesz; // Skip over local .bss
|
||||
frag_mask &= -(long)next_unc; // bytes left on pge
|
||||
unsigned /*const*/ escape = 0xc36180cd; // "int $0x80; popa; ret"
|
||||
if (4 <= frag_mask) {
|
||||
hatch = next_unc;
|
||||
*(long *)&hatch[0] = escape;
|
||||
hatch += phdr->p_vaddr + (base - mfd_addr); // relocate to eventual address
|
||||
}
|
||||
else {
|
||||
hatch = 0;
|
||||
else { // Does not fit at hi end of .text, so must use a new page "permanently"
|
||||
int mfd = memfd_create(addr_string("upx"), 0); // the directory entry
|
||||
write(mfd, &escape, 4);
|
||||
mfd_addr = mmap(0, 4, PROT_READ|PROT_EXEC, MAP_PRIVATE, mfd, 0);
|
||||
close(mfd);
|
||||
hatch = mfd_addr;
|
||||
}
|
||||
}
|
||||
DPRINTF("hatch=%%p\\n", hatch);
|
||||
return hatch;
|
||||
}
|
||||
#elif defined(__arm__) /*}{*/
|
||||
extern unsigned get_sys_munmap(void);
|
||||
|
||||
static void *
|
||||
make_hatch_arm(
|
||||
make_hatch_arm32(
|
||||
Elf32_Phdr const *const phdr,
|
||||
ptrdiff_t reloc
|
||||
char *base,
|
||||
char *next_unc,
|
||||
char *mfd_addr,
|
||||
unsigned frag_mask
|
||||
)
|
||||
{
|
||||
unsigned const sys_munmap = get_sys_munmap();
|
||||
unsigned xprot = 0;
|
||||
unsigned code[2] = {
|
||||
sys_munmap, // syscall __NR_unmap
|
||||
0xe8bd80ff, // ldmia sp!,{r0,r1,r2,r3,r4,r5,r6,r7,pc}
|
||||
};
|
||||
unsigned *hatch = 0;
|
||||
DPRINTF("make_hatch %%p %%x %%x\\n",phdr,reloc,sys_munmap);
|
||||
if (phdr->p_type==PT_LOAD && phdr->p_flags & PF_X) {
|
||||
// The format of the 'if' is
|
||||
// if ( ( (hatch = loc1), test_loc1 )
|
||||
// || ( (hatch = loc2), test_loc2 ) ) {
|
||||
// action
|
||||
// }
|
||||
// which uses the comma to save bytes when test_locj involves locj
|
||||
// and the action is the same when either test succeeds.
|
||||
DPRINTF("make_hatch %%p %%p %%p %%p %%x\\n", phdr, base, next_unc, mfd_addr, frag_mask);
|
||||
|
||||
if (
|
||||
// Try page fragmentation just beyond .text .
|
||||
( (hatch = (void *)(~3u & (3+ phdr->p_memsz + phdr->p_vaddr + reloc))),
|
||||
( phdr->p_memsz==phdr->p_filesz // don't pollute potential .bss
|
||||
&& (2*4)<=(~PAGE_MASK & -(int)hatch) ) ) // space left on page
|
||||
// Try Elf32_Ehdr.e_ident[8..15] . warning: 'const' cast away
|
||||
|| ( (hatch = (void *)(&((Elf32_Ehdr *)phdr->p_vaddr + reloc)->e_ident[8])),
|
||||
(phdr->p_offset==0) )
|
||||
// Allocate and use a new page.
|
||||
// Linux on ARM wants PROT_EXEC or else __clear_cache does not work?
|
||||
|| ( xprot = 1, hatch = mmap(0, PAGE_SIZE, PROT_EXEC|PROT_WRITE|PROT_READ,
|
||||
MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) )
|
||||
) {
|
||||
hatch[0] = sys_munmap; // syscall __NR_unmap
|
||||
hatch[1] = 0xe8bd80ff; // ldmia sp!,{r0,r1,r2,r3,r4,r5,r6,r7,pc}
|
||||
__clear_cache(&hatch[0], &hatch[2]); // ? needed before Pprotect()
|
||||
if (xprot) {
|
||||
Pprotect(hatch, 2*sizeof(unsigned), PROT_EXEC|PROT_READ);
|
||||
}
|
||||
if (phdr->p_type==PT_LOAD && phdr->p_flags & PF_X) {
|
||||
next_unc += phdr->p_memsz - phdr->p_filesz; // Skip over local .bss
|
||||
frag_mask &= -(long)next_unc; // bytes left on pge
|
||||
if (2*4 <= frag_mask) {
|
||||
hatch = (unsigned *)(void *)next_unc;
|
||||
hatch[0]= code[0];
|
||||
hatch[1]= code[1];
|
||||
char *t = (phdr->p_vaddr + base) + ((char *)hatch - mfd_addr);
|
||||
__clear_cache(&hatch[0], &hatch[2]);
|
||||
hatch = (unsigned *)(void *)t;
|
||||
}
|
||||
else {
|
||||
hatch = 0;
|
||||
else { // Does not fit at hi end of .text, so must use a new page "permanently"
|
||||
int mfd = memfd_create(addr_string("upx"), 0); // the directory entry
|
||||
write(mfd, &code, 2*4);
|
||||
mfd_addr = mmap(0, 2*4, PROT_READ|PROT_EXEC, MAP_PRIVATE, mfd, 0);
|
||||
close(mfd);
|
||||
hatch = (unsigned *)(void *)mfd_addr;
|
||||
}
|
||||
}
|
||||
DPRINTF("hatch=%%p\\n", hatch);
|
||||
return hatch;
|
||||
}
|
||||
#elif defined(__mips__) /*}{*/
|
||||
@ -424,8 +425,14 @@ make_hatch_ppc32(
|
||||
|
||||
#define nullptr (void *)0
|
||||
|
||||
#if 0 //{
|
||||
unsigned long
|
||||
#if defined(__i386__) //{
|
||||
unsigned
|
||||
get_PAGE_MASK(void)
|
||||
{
|
||||
return ~0xFFF;
|
||||
}
|
||||
#else //}{
|
||||
unsigned
|
||||
get_PAGE_MASK(void) // the mask which KEEPS the page address
|
||||
{
|
||||
int fd = open("/proc/self/auxv", O_RDONLY, 0);
|
||||
@ -461,8 +468,18 @@ extern void
|
||||
underlay(unsigned size, char *ptr, unsigned len, unsigned p_flags);
|
||||
#endif //}
|
||||
|
||||
extern int ftruncate(int fd, size_t length);
|
||||
|
||||
// Exchange the bits with values 4 (PF_R, PROT_EXEC) and 1 (PF_X, PROT_READ)
|
||||
// Use table lookup into a PIC-string that pre-computes the result.
|
||||
unsigned PF_to_PROT(Elf32_Phdr const *phdr)
|
||||
{
|
||||
return 7& addr_string("@\x04\x02\x06\x01\x05\x03\x07")
|
||||
[phdr->p_flags & (PF_R|PF_W|PF_X)];
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
int argc;
|
||||
long argc;
|
||||
char **argv;
|
||||
char **envp;
|
||||
} So_args;
|
||||
@ -481,22 +498,23 @@ typedef struct {
|
||||
void *
|
||||
upx_so_main( // returns &escape_hatch
|
||||
So_info *so_info,
|
||||
So_args *so_args
|
||||
So_args *so_args,
|
||||
Elf32_Ehdr *elf_tmp // scratch for Elf32_Ehdr and Elf32_Phdrs
|
||||
)
|
||||
{
|
||||
unsigned long const page_mask = get_PAGE_MASK();
|
||||
char *const va_load = (char *)&so_info->off_reloc - so_info->off_reloc;
|
||||
Elf32_Phdr const *phdr = (Elf32_Phdr *)(1+ (Elf32_Ehdr *)(void *)va_load);
|
||||
Elf32_Addr const base = (Elf32_Addr)va_load - phdr->p_vaddr;
|
||||
while (phdr->p_type != PT_LOAD) ++phdr; // skip PT_PHDR if any
|
||||
So_info so_infc; // So_info Copy
|
||||
memcpy(&so_infc, so_info, sizeof(so_infc)); // before de-compression overwrites
|
||||
unsigned const xct_off = so_infc.off_xct_off;
|
||||
(void)xct_off; // use even if not DEBUG
|
||||
unsigned const xct_off = so_infc.off_xct_off; (void)xct_off;
|
||||
|
||||
char *const cpr_ptr = so_infc.off_info + va_load;
|
||||
char *const cpr_ptr = so_info->off_info + va_load;
|
||||
unsigned const cpr_len = (char *)so_info - cpr_ptr;
|
||||
DPRINTF("upx_so_main@%%p va_load=%%p base=%%p cpr_ptr=%%p cpr_len=%%x xct_off=%%x\\n",
|
||||
upx_so_main, va_load, base, cpr_ptr, cpr_len, xct_off);
|
||||
typedef void (*Dt_init)(int argc, char *argv[], char *envp[]);
|
||||
Dt_init const dt_init = (Dt_init)(void *)(so_info->off_user_DT_INIT + va_load);
|
||||
DPRINTF("upx_so_main va_load=%%p so_infc=%%p cpr_ptr=%%p cpr_len=%%x xct_off=%%x\\n",
|
||||
va_load, &so_infc, cpr_ptr, cpr_len, xct_off);
|
||||
// DO NOT USE *so_info AFTER THIS!! It gets overwritten.
|
||||
|
||||
// Copy compressed data before de-compression overwrites it.
|
||||
char *const sideaddr = mmap(nullptr, cpr_len, PROT_WRITE|PROT_READ,
|
||||
@ -524,11 +542,28 @@ upx_so_main( // returns &escape_hatch
|
||||
// Pprotect(,, PF_TO_PROT(.p_flags));
|
||||
|
||||
// Get the uncompressed Elf32_Ehdr and Elf32_Phdr
|
||||
// The first b_info is aligned, so direct access of fields is OK.
|
||||
Extent x1 = {binfo->sz_unc, va_load}; // destination
|
||||
Pprotect(va_load, binfo->sz_unc, PROT_WRITE|PROT_READ);
|
||||
// The first b_info is aligned, so direct access to fields is OK.
|
||||
Extent x1 = {binfo->sz_unc, (char *)elf_tmp}; // destination
|
||||
Extent x0 = {binfo->sz_cpr + sizeof(*binfo), (char *)binfo}; // source
|
||||
unpackExtent(&x0, &x1); // de-compress Elf headers; x0.buf is updated
|
||||
unpackExtent(&x0, &x1); // de-compress _Ehdr and _Phdrs; x0.buf is updated
|
||||
|
||||
Elf32_Phdr const *phdr = (Elf32_Phdr *)(1+ elf_tmp);
|
||||
Elf32_Phdr const *const phdrN = &phdr[elf_tmp->e_phnum];
|
||||
while (phdr->p_type != PT_LOAD) ++phdr; // skip PT_PHDR if any
|
||||
Elf32_Addr const base = (Elf32_Addr)va_load - phdr->p_vaddr;
|
||||
DPRINTF("base=%%p\\n", base);
|
||||
|
||||
if (phdr->p_flags & PF_X) {
|
||||
int mfd = memfd_create(addr_string("upx"), 0);
|
||||
unsigned mfd_len = 0ul - page_mask;
|
||||
write(mfd, elf_tmp, binfo->sz_unc); // de-compressed Elf_Ehdr and Elf_Phdrs
|
||||
write(mfd, binfo->sz_unc + va_load, mfd_len - binfo->sz_unc); // rest of 1st page
|
||||
|
||||
munmap(va_load, mfd_len); // make SELinux forget any previous protection
|
||||
Elf32_Addr va_mfd = (Elf32_Addr)mmap(va_load, mfd_len, PF_to_PROT(phdr),
|
||||
MAP_FIXED|MAP_PRIVATE|MAP_ANONYMOUS, mfd, 0); (void)va_mfd;
|
||||
close(mfd);
|
||||
}
|
||||
|
||||
// Process each read-only PT_LOAD.
|
||||
// A read+write PT_LOAD might be relocated by rtld before de-compression,
|
||||
@ -536,8 +571,7 @@ upx_so_main( // returns &escape_hatch
|
||||
struct b_info al_bi; // for aligned data from binfo
|
||||
void *hatch = nullptr;
|
||||
|
||||
unsigned n_phdr = ((Elf32_Ehdr *)(void *)va_load)->e_phnum;
|
||||
for (; n_phdr > 0; --n_phdr, ++phdr)
|
||||
for (; phdr < phdrN; ++phdr)
|
||||
if ( phdr->p_type == PT_LOAD && !(phdr->p_flags & PF_W)) {
|
||||
DPRINTF("phdr@%%p p_offset=%%p p_vaddr=%%p p_filesz=%%p p_memsz=%%p binfo=%%p\\n",
|
||||
phdr, phdr->p_offset, phdr->p_vaddr, phdr->p_filesz, phdr->p_memsz, x0.buf);
|
||||
@ -556,38 +590,69 @@ upx_so_main( // returns &escape_hatch
|
||||
al_bi.sz_unc, al_bi.sz_cpr, *(unsigned *)(void *)&al_bi.b_method);
|
||||
|
||||
// Using .p_memsz implicitly handles .bss via MAP_ANONYMOUS.
|
||||
// Omit any not-compressed prefix (below xct_off)
|
||||
// Omit any non-tcompressed prefix (below xct_off)
|
||||
x1.buf = (char *)(phdr->p_vaddr + pfx + base);
|
||||
x1.size = phdr->p_memsz - pfx;
|
||||
|
||||
pfx = (phdr->p_vaddr + pfx) & ~PAGE_MASK; // lo fragment on page
|
||||
pfx = (phdr->p_vaddr + pfx) & ~page_mask; // lo fragment on page
|
||||
x1.buf -= pfx;
|
||||
x1.size += pfx;
|
||||
DPRINTF("mmap(%%p %%p) xct_off=%%x pfx=%%x\\n", x1.buf, x1.size, xct_off, pfx);
|
||||
underlay(x1.size, x1.buf, pfx, phdr->p_flags);
|
||||
DPRINTF("phdr(%%p %%p) xct_off=%%x pfx=%%x\\n", x1.buf, x1.size, xct_off, pfx);
|
||||
|
||||
int mfd = 0;
|
||||
char *mfd_addr = 0;
|
||||
if (phdr->p_flags & PF_X) { // SELinux
|
||||
// Cannot set PROT_EXEC except via mmap() into a region (Linux "vma")
|
||||
// that has never had PROT_WRITE. So use a Linux-only "memory file"
|
||||
// to hold the contents.
|
||||
mfd = memfd_create(addr_string("upx"), 0); // the directory entry
|
||||
ftruncate(mfd, x1.size); // Allocate the pages in the file.
|
||||
write(mfd, x1.buf, pfx); // Save lo fragment of contents on first page.
|
||||
mfd_addr = mmap(0, x1.size, PROT_READ|PROT_WRITE, MAP_SHARED, mfd, 0);
|
||||
DPRINTF("mfd_addr= %%p\\n", mfd_addr); // Now "somewhere" in RAM,
|
||||
// and ready to receive de-compressed bytes, and remember them in the
|
||||
// file (MAP_SHARED) so that they can be mmap() according to *phdr.
|
||||
|
||||
// Must keep the original compressed data until now, in order to
|
||||
// prevent the mmap(0, ...) from stealing that address range.
|
||||
munmap(x1.buf, x1.size); // Discard original page frames in RAM.
|
||||
x1.buf = mfd_addr; // will add pfx soon
|
||||
}
|
||||
else {
|
||||
underlay(x1.size, x1.buf, pfx, phdr->p_flags); // also makes PROT_WRITE
|
||||
}
|
||||
|
||||
x1.buf += pfx;
|
||||
x1.size = al_bi.sz_unc;
|
||||
x0.size = al_bi.sz_cpr + sizeof(struct b_info);
|
||||
DPRINTF("befor unpack x0=(%%p %%p x1=(%%p %%p)\\n", x0.size, x0.buf, x1.size, x1.buf);
|
||||
unpackExtent(&x0, &x1); // updates x0 and x1
|
||||
DPRINTF("after unpack x0=(%%p %%p x1=(%%p %%p)\\n", x0.size, x0.buf, x1.size, x1.buf);
|
||||
|
||||
if (!hatch && phdr->p_flags & PF_X) {
|
||||
//#define PAGE_MASK ~0xFFFull
|
||||
#if defined(__arm__) //{
|
||||
hatch = make_hatch_arm(phdr, base);
|
||||
#elif defined(__powerpc__) //}{
|
||||
hatch = make_hatch_ppc(phdr, base, ~PAGE_MASK);
|
||||
#elif defined(__i386__) //}{
|
||||
hatch = make_hatch_i386(phdr, base);
|
||||
#if defined(__i386__) //{
|
||||
hatch = make_hatch_i386(phdr, (char *)base, x1.buf, mfd_addr, ~page_mask);
|
||||
#elif defined(__arm__) //}{
|
||||
hatch = make_hatch_arm32(phdr, (char *)base, x1.buf, mfd_addr, ~page_mask);
|
||||
|
||||
#elif defined(__powerpc32__) //}{
|
||||
hatch = make_hatch_ppc32(phdr, (char *)base, x1.buf, mfd_addr, ~page_mask);
|
||||
#endif //}
|
||||
}
|
||||
DPRINTF("mprotect %%p (%%p %%p %%x)\\n",
|
||||
phdr, (char *)(phdr->p_vaddr + base), phdr->p_memsz, PF_TO_PROT(phdr->p_flags));
|
||||
Pprotect( (char *)(phdr->p_vaddr + base), phdr->p_memsz, PF_TO_PROT(phdr->p_flags));
|
||||
|
||||
if (phdr->p_flags & PF_X) { // SELinux
|
||||
// Map the contents of mfd as per *phdr.
|
||||
DPRINTF("mfd mmap addr=%%p len=%%p\\n", (phdr->p_vaddr + base + pfx), al_bi.sz_unc);
|
||||
munmap(mfd_addr, pfx + al_bi.sz_unc); // Discard RW mapping; mfd has the bytes
|
||||
mmap((char *)(phdr->p_vaddr + base + pfx), al_bi.sz_unc, PF_to_PROT(phdr),
|
||||
MAP_FIXED|MAP_PRIVATE, mfd, 0);
|
||||
close(mfd);
|
||||
}
|
||||
else { // easy
|
||||
Pprotect( (char *)(phdr->p_vaddr + base), phdr->p_memsz, PF_to_PROT(phdr));
|
||||
}
|
||||
}
|
||||
|
||||
typedef void (*Dt_init)(int argc, char *argv[], char *envp[]);
|
||||
Dt_init dt_init = (Dt_init)(void *)(so_infc.off_user_DT_INIT + va_load);
|
||||
munmap(sideaddr, cpr_len);
|
||||
DPRINTF("calling user DT_INIT %%p\\n", dt_init);
|
||||
dt_init(so_args->argc, so_args->argv, so_args->envp);
|
||||
|
@ -184,6 +184,7 @@ struct timespec {
|
||||
#define __NR_adjtimex 124
|
||||
#define __NR_mprotect 125
|
||||
#define __NR_nanosleep 162
|
||||
#define __NR_memfd_create 356 /*0x164*/
|
||||
|
||||
#undef _syscall0
|
||||
#undef _syscall1
|
||||
@ -353,6 +354,7 @@ static inline _syscall2(int,ftruncate,int,fd,size_t,len)
|
||||
static inline _syscall0(pid_t,getpid)
|
||||
static inline _syscall2(int,gettimeofday,struct timeval *,tv,void *,tz)
|
||||
static inline _syscall3(off_t,lseek,int,fd,off_t,offset,int,whence)
|
||||
static inline _syscall2(int,memfd_create,char const *,name,unsigned,flags);
|
||||
static inline _syscall3(int,mprotect,void *,addr,size_t,len,int,prot)
|
||||
static inline _syscall2(int,munmap,void *,start,size_t,length)
|
||||
static inline _syscall2(int,nanosleep,const struct timespec *,rqtp,struct timespec *,rmtp)
|
||||
@ -542,6 +544,7 @@ static ssize_t write(int fd, void const *buf, size_t len)
|
||||
void *brk(void *);
|
||||
int close(int);
|
||||
void exit(int) __attribute__((__noreturn__,__nothrow__));
|
||||
int memfd_create(char const *, unsigned);
|
||||
void *mmap(void *, size_t, int, int, int, off_t);
|
||||
int munmap(void *, size_t);
|
||||
int mprotect(void const *, size_t, int);
|
||||
|
Reference in New Issue
Block a user