From bd251c0dbd4034d9b03182136b1fa2c05aab27bc Mon Sep 17 00:00:00 2001 From: John Reiser Date: Thu, 28 Nov 2024 19:10:27 -0800 Subject: [PATCH] Stub for 64-bit shlib modified: stub/Makefile modified: stub/src/amd64-expand.S modified: stub/src/amd64-linux.elf-entry.S modified: stub/src/amd64-linux.elf-so_entry.S modified: stub/src/amd64-linux.elf-so_fold.S modified: stub/src/amd64-linux.elf-so_main.c modified: stub/src/arm64-expand.S modified: stub/src/upxfd_linux.c --- src/stub/Makefile | 31 +++++++-- src/stub/src/amd64-expand.S | 4 ++ src/stub/src/amd64-linux.elf-entry.S | 6 +- src/stub/src/amd64-linux.elf-so_entry.S | 18 ++++- src/stub/src/amd64-linux.elf-so_fold.S | 8 +++ src/stub/src/amd64-linux.elf-so_main.c | 93 +++++++++++-------------- src/stub/src/arm64-expand.S | 3 + src/stub/src/upxfd_linux.c | 2 +- 8 files changed, 99 insertions(+), 66 deletions(-) diff --git a/src/stub/Makefile b/src/stub/Makefile index 1cbe6efa..72c63002 100644 --- a/src/stub/Makefile +++ b/src/stub/Makefile @@ -421,11 +421,13 @@ tc.amd64-linux.elf.gcc += -fno-exceptions -fno-asynchronous-unwind-tables tc.amd64-linux.elf.gcc += -Wall -W -Wcast-align -Wcast-qual -Wstrict-prototypes -Wwrite-strings -Werror amd64-linux.elf-entry.h: $(srcdir)/src/$$T.S + @echo; echo TARGET=$@ PATH=$(PATH); echo $(call tc,gcc) -c -x assembler-with-cpp $< -o tmp/$T.bin $(call tc,f-embed_objinfo,tmp/$T.bin) $(call tc,bin2h) tmp/$T.bin $@ amd64-linux.elf-so_entry.h: $(srcdir)/src/$$T.S + @echo; echo TARGET=$@ PATH=$(PATH); echo $(call tc,gcc) -c -x assembler-with-cpp $< -o tmp/$T.bin $(call tc,f-embed_objinfo,tmp/$T.bin) $(call tc,bin2h) tmp/$T.bin $@ @@ -433,17 +435,32 @@ amd64-linux.elf-so_entry.h: $(srcdir)/src/$$T.S amd64-linux.elf-fold.h : $(srcdir)/src/$$T.lds \ tmp/$$T.o \ tmp/amd64-expand.o \ + tmp/amd64-linux.elf-upxfd_linux.o \ tmp/amd64-linux.elf-main2.o + @echo; echo TARGET=$@ PATH=$(PATH); echo # FIXME: multiarch-ld-2.18 creates a huge file here, so use 2.17 multiarch-ld-2.17 -r -T $(srcdir)/src/$T.lds -Map tmp/$T.map $(filter %.o,$^) -o tmp/$T.bin $(call tc,f-embed_objinfo_without_xstrip_keep_dot_text,tmp/$T.bin) $(call tc,bin2h) tmp/$T.bin $@ -amd64-linux.elf-so_fold.h : tmp/$$T.o tmp/amd64-linux.elf-so_main.o $(srcdir)/src/$$T.lds tmp/amd64-expand.o - multiarch-ld-2.17 -r -T $(srcdir)/src/$T.lds -Map tmp/$T.map $(filter %.o,$^) -o tmp/$T.bin +amd64-linux.elf-so_fold.h : $(srcdir)/src/$$T.lds \ + tmp/$$T.o \ + tmp/amd64-expand.o \ + tmp/amd64-linux.elf-upxfd_linux.o \ + tmp/amd64-linux.elf-so_main.o + @echo; echo TARGET=$@ PATH=$(PATH); echo +# FIXME: multiarch-ld-2.18 creates a huge file here, so use 2.17 + $(call tc,ld) -r -T $(srcdir)/src/$T.lds -Map tmp/$T.map $(filter %.o,$^) -o tmp/$T.bin +# multiarch-ld-2.17 -r -T $(srcdir)/src/$T.lds -Map tmp/$T.map $(filter %.o,$^) -o tmp/$T.bin + ls -l tmp/$T.bin $(call tc,f-embed_objinfo_without_xstrip,tmp/$T.bin) $(call tc,bin2h) tmp/$T.bin $@ +tmp/amd64-linux.elf-so_fold.o : $(srcdir)/src/$$T.S + $(call tc,gcc) -c $< -o tmp/$T.o + $(call tc,objcopy) -R .text $@ + $(call tc,f-objstrip,$@) + tmp/amd64-expand.o: $(srcdir)/src/$$T.S $(call tc,gcc) -c $< -o $@ @@ -451,11 +468,6 @@ tmp/amd64-linux.elf-fold.o : $(srcdir)/src/$$T.S $(call tc,gcc) -c $< -o $@ $(call tc,f-objstrip,$@) -tmp/amd64-linux.elf-so_fold.o : $(srcdir)/src/$$T.S - $(call tc,gcc) -c $< -o $@ - $(call tc,objcopy) -R .text $@ - $(call tc,f-objstrip,$@) - tmp/amd64-linux.elf-main.o : $(srcdir)/src/$$T.c $(call tc,gcc) -c -Os $< -o $@ $(call tc,f-objstrip,$@) @@ -834,6 +846,11 @@ tmp/arm64-expand.o: $(srcdir)/src/$$T.S $(call tc,gcc) -c $< -o $@ arm64-linux-objdump-2.25 -Dr $(tc_objdump_disasm_options) tmp/$T.o | $(RTRIM) > tmp/$T.o.disasm +tmp/amd64-linux.elf-upxfd_linux.o : $(srcdir)/src/upxfd_linux.c + $(call tc,gcc) -c -O $< -o $@ + $(call tc,objcopy) --rename-section .text=UMF_LINUX -R .comment -R .data -R .bss -R .note.GNU-stack $@ + $(call tc,objdump) -Dr $(tc_objdump_disasm_options) $@ | $(RTRIM) > $@.disasm + tmp/arm64-linux.elf-main2.o : $(srcdir)/src/$$T.c $(srcdir)/src/amd64-linux.elf-main2.c $(call tc,gcc) -c -Os $(srcdir)/src/arm64-linux.elf-main2.c -o $@ $(call tc,f-objstrip,$@) diff --git a/src/stub/src/amd64-expand.S b/src/stub/src/amd64-expand.S index fbb22236..0628b4fd 100644 --- a/src/stub/src/amd64-expand.S +++ b/src/stub/src/amd64-expand.S @@ -229,3 +229,7 @@ eof_n2e: movl %edi,(%rcx) // actual length used at dst XXX: 4GB sub %rsi,%rax // src -= eof; // return 0: good; else: bad ret + + .balign 4 +upx_mmap_and_fd: .globl upx_mmap_and_fd + // UMF_LINUX goes here diff --git a/src/stub/src/amd64-linux.elf-entry.S b/src/stub/src/amd64-linux.elf-entry.S index 7667dbac..dba9bf0d 100644 --- a/src/stub/src/amd64-linux.elf-entry.S +++ b/src/stub/src/amd64-linux.elf-entry.S @@ -129,9 +129,9 @@ D_PMASK= 0*NBPW #define arg2l esi #define arg3l edx // Create anonymous temporary file on mfd; like upxfd_create - push $'u'|('p'<<8)|('x'<<16)|(0<<24) // MATCH_22 - push %rsp; pop %arg1 // "upx" - mov $MFD_EXEC,%arg2l + push $'u'|('p'<<8)|('X'<<16)|(0<<24) // MATCH_22 + push %rsp; pop %arg1 // "upX" + push $MFD_EXEC; pop %arg2 0: // try memfd_create movl $__NR_memfd_create,%eax; syscall test %eax,%eax; jns ok_memfd // success diff --git a/src/stub/src/amd64-linux.elf-so_entry.S b/src/stub/src/amd64-linux.elf-so_entry.S index 26666d8e..096bbdbb 100644 --- a/src/stub/src/amd64-linux.elf-so_entry.S +++ b/src/stub/src/amd64-linux.elf-so_entry.S @@ -58,6 +58,7 @@ __NR_mmap= 9 // 64-bit mode only! /usr/include/asm/unistd_64.h __NR_mprotect= 10 __NR_munmap= 11 __NR_memfd_create= 0x13f // 319 + MFD_EXEC= 0x10 __NR_write= 1 __NR_close= 3 @@ -204,9 +205,20 @@ eof_n2b: pop %rbp // MATCH_45 - push $0; pop %arg2 - call 0f; .asciz "upx"; 0: pop %arg1 - push $__NR_memfd_create; call do_sys +#define arg2l esi +// Create anonymous temporary file on mfd; like upxfd_create + push $'u'|('p'<<8)|('X'<<16)|(0<<24) // MATCH_22 + push %rsp; pop %arg1 // "upX" + push $MFD_EXEC; pop %arg2 +0: // try memfd_create + movl $__NR_memfd_create,%eax; syscall + test %eax,%eax; jns ok_memfd // success + test %arg2l,%arg2l; jz no_memfd // memfd_create failed twice + xor %arg2l,%arg2l; jmp 0b // try again without MFD_EXEC +no_memfd: // so try /dev/shm + hlt // FIXME /dev/shm +ok_memfd: + pop %rcx // MATCH_22 discard "upx" push %rax; pop %arg1 // mfd push %rsp; pop %arg2 // buffer diff --git a/src/stub/src/amd64-linux.elf-so_fold.S b/src/stub/src/amd64-linux.elf-so_fold.S index 99f70275..886ab670 100644 --- a/src/stub/src/amd64-linux.elf-so_fold.S +++ b/src/stub/src/amd64-linux.elf-so_fold.S @@ -98,6 +98,7 @@ __NR_read= 0 __NR_write= 1 //__NR_open= 2 __NR_openat= 257 + FD_CWD= -100 __NR_close= 3 __NR_mmap= 9 @@ -149,6 +150,13 @@ sysgo: # NOTE: kernel demands 4th arg in %sys4, NOT %arg4 syscall; cmp $-4096,%rax; jb 0f; int3; 0: ret +open: .globl open + mov %arg3,%arg4 + mov %arg2,%arg3 + mov %arg1,%arg2 + mov $FD_CWD,%arg1 + jmp openat + Punmap: .globl Punmap // page-align the lo end mov %arg1,%rax; and $0xfff,%eax sub %rax,%arg1 diff --git a/src/stub/src/amd64-linux.elf-so_main.c b/src/stub/src/amd64-linux.elf-so_main.c index 5f7c33fb..4c78b6a6 100644 --- a/src/stub/src/amd64-linux.elf-so_main.c +++ b/src/stub/src/amd64-linux.elf-so_main.c @@ -40,8 +40,6 @@ extern size_t Pwrite(unsigned, void const *, size_t); extern void f_int3(int arg); -#define DEBUG 1 - #ifndef DEBUG //{ #define DEBUG 0 #endif //} @@ -384,10 +382,10 @@ make_hatch( ) >> ((pf & (PF_R|PF_W|PF_X))<<2) )) #undef PAGE_MASK -static unsigned long +static ElfW(Addr) get_page_mask(void) // the mask which KEEPS the page, discards the offset { - unsigned long rv = ~0xffful; // default to (PAGE_SIZE == 4KiB) + ElfW(Addr) rv = ~0xffful; // default to (PAGE_SIZE == 4KiB) int fd = openat(0, addr_string("/proc/self/auxv"), O_RDONLY, 0); if (0 <= fd) { ElfW(auxv_t) data[40]; @@ -454,16 +452,25 @@ fini_SELinux( } unsigned -prep_SELinux(unsigned size, char *ptr, unsigned len) // returns mfd +prep_SELinux(unsigned size, char *ptr, ElfW(Addr) page_mask) // returns mfd { // 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. - char *val = upx_mmap_and_fd(ptr, size, nullptr); - unsigned mfd = 0xfff & (unsigned)(long)val; + unsigned saved[65536/sizeof(unsigned)]; + char *page = (char *)(page_mask & (ElfW(Addr))ptr); + unsigned frag = (unsigned)(ptr - page); + if (frag) { + memcpy(saved, page, frag); + } + char *val = upx_mmap_and_fd(page, frag + size, nullptr); + unsigned mfd = 0xfff & (unsigned)(ElfW(Addr))val; val -= mfd; --mfd; - if (len) - Pwrite(mfd, ptr, len); // Save lo fragment of contents on first page. + if (val != page) { + my_bkpt((void const *)0x1262, val, page, ptr, frag); + } + if (frag) + write(mfd, saved, frag); // Save lo fragment of contents on page. return mfd; } @@ -491,7 +498,7 @@ upx_so_main( // returns &escape_hatch ElfW(Ehdr) *elf_tmp // scratch for ElfW(Ehdr) and ElfW(Phdrs) ) { - unsigned long const page_mask = get_page_mask(); + ElfW(Addr) const page_mask = get_page_mask(); char *const va_load = (char *)&so_info->off_reloc - so_info->off_reloc; So_info so_infc; // So_info Copy memcpy(&so_infc, so_info, sizeof(so_infc)); // before de-compression overwrites @@ -548,59 +555,41 @@ upx_so_main( // returns &escape_hatch for (; phdr < phdrN; ++phdr) if (phdr->p_type == PT_LOAD && !(phdr->p_flags & PF_W)) { - unsigned hi_offset = phdr->p_filesz + phdr->p_offset; - struct b_info al_bi; // for aligned data from binfo // Need un-aligned read of b_info to determine compression sizes. + struct b_info al_bi; // for aligned data from binfo x0.size = sizeof(struct b_info); xread(&x0, (char *)&al_bi, x0.size); // aligned binfo x0.buf -= sizeof(al_bi); // back up (the xread() was a peek) - x1.size = hi_offset - xct_off; - x1.buf = (void *)(hi_offset + base - al_bi.sz_unc); - x0.size = al_bi.sz_cpr; + x0.size = al_bi.sz_cpr; + x1.size = al_bi.sz_unc; + + DPRINTF("\\nphdr@%%p p_offset=%%p p_vaddr=%%p p_filesz=%%p p_memsz=%%p\\n", + phdr, phdr->p_offset, phdr->p_vaddr, phdr->p_filesz, phdr->p_memsz); + DPRINTF("x0=%%p x1=%%p\\n", &x0, &x1); if (!base) { base = (ElfW(Addr))va_load - phdr->p_vaddr; DPRINTF("base=%%p\\n", base); } - DPRINTF("phdr@%%p p_offset=%%p p_vaddr=%%p p_filesz=%%p p_memsz=%%p\\n", - phdr, phdr->p_offset, phdr->p_vaddr, phdr->p_filesz, phdr->p_memsz); - DPRINTF("x0=%%p x1=%%p\\n", &x0, &x1); - //my_bkpt((void const *)0x1230, phdr, &x0, &x1); - - unsigned frag = ~page_mask & (unsigned)(long)x1.buf; - unsigned mfd = 0; - if (!n_load) { // 1st PT_LOAD is special. - // Already ELF headers are in place, perhaps already followed - // by non-compressed loader tables below xct_off. - if (xct_off < hi_offset) { // 1st PT_LOAD also has compressed code, too - if (phdr->p_flags & PF_X) { - mfd = prep_SELinux(x1.size, x1.buf, frag); - } - else { - underlay(x1.size, x1.buf, page_mask); // also makes PROT_WRITE - } - unpackExtent(&x0, &x1); - if (!hatch && phdr->p_flags & PF_X) { - hatch = make_hatch(phdr, x1.buf, ~page_mask); - } - my_bkpt((void const *)0x1235, &x1); - fini_SELinux(x1.size, x1.buf, phdr, mfd, base); // FIXME: x1 changed! + x1.buf = (void *)(phdr->p_filesz + phdr->p_vaddr + base - x1.size); + if ((phdr->p_filesz + phdr->p_offset) <= xct_off) { // hi_offset <= xct_off + if (!n_load) { + ++n_load; + continue; // 1st PT_LOAD is non-compressed loader tables ONLY! } } - else { // 2nd and later PT_LOADs - x1.buf = (void *)(phdr->p_vaddr + base); - x1.size = phdr->p_filesz; - if (phdr->p_flags & PF_X) { - mfd = prep_SELinux(x1.size, x1.buf, frag); - } - else { - underlay(x1.size, x1.buf, page_mask); // also makes PROT_WRITE - } - unpackExtent(&x0, &x1); - if (!hatch && phdr->p_flags &PF_X) { - hatch = make_hatch(phdr, x1.buf, ~page_mask); - } - fini_SELinux(al_bi.sz_unc, (void *)(phdr->p_vaddr + base), phdr, mfd, base); + int mfd = 0; + if (phdr->p_flags & PF_X) { + mfd = prep_SELinux(x1.size, x1.buf, page_mask); + } + else { + underlay(x1.size, x1.buf, page_mask); // also makes PROT_WRITE + } + Extent xt = x1; + unpackExtent(&x0, &x1); + if (!hatch && phdr->p_flags & PF_X) { + hatch = make_hatch(phdr, x1.buf, ~page_mask); + fini_SELinux(xt.size, xt.buf, phdr, mfd, base); // FIXME: x1 was changed by unpackExtent! } ++n_load; } diff --git a/src/stub/src/arm64-expand.S b/src/stub/src/arm64-expand.S index 0c180e47..a4a34f9f 100644 --- a/src/stub/src/arm64-expand.S +++ b/src/stub/src/arm64-expand.S @@ -140,6 +140,9 @@ eof_lzma: .globl eof_lzma POP4(x2,x3, fp,lr) // MATCH_94 x2= orig_dst; x3= plen_dst ret +upx_mmap_and_fd: .globl upx_mmap_and_fd + // UMF_LINUX goes here + #define M_NRV2B_LE32 2 #define M_NRV2B_8 3 #define M_NRV2D_LE32 5 diff --git a/src/stub/src/upxfd_linux.c b/src/stub/src/upxfd_linux.c index c00be20e..a03006f8 100644 --- a/src/stub/src/upxfd_linux.c +++ b/src/stub/src/upxfd_linux.c @@ -1,4 +1,4 @@ -/* upxfd_create.c -- workaround for 32-bit Android +/* upxfd_create.c -- simplify upx_mmap_and_fd_linux for non-Android This file is part of the UPX executable compressor.