; fold_elf86.asm -- linkage to C code to process Elf binary ; ; This file is part of the UPX executable compressor. ; ; Copyright (C) 2000-2004 John F. Reiser ; All Rights Reserved. ; ; UPX and the UCL library are free software; you can redistribute them ; and/or modify them under the terms of the GNU General Public License as ; published by the Free Software Foundation; either version 2 of ; the License, or (at your option) any later version. ; ; This program is distributed in the hope that it will be useful, ; but WITHOUT ANY WARRANTY; without even the implied warranty of ; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ; GNU General Public License for more details. ; ; You should have received a copy of the GNU General Public License ; along with this program; see the file COPYING. ; If not, write to the Free Software Foundation, Inc., ; 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. ; ; Markus F.X.J. Oberhumer Laszlo Molnar ; ; ; John F. Reiser ; ; BITS 32 SECTION .text CPU 386 %define PAGE_SIZE ( 1<<12) %define szElf32_Ehdr 0x34 %define szElf32_Phdr 8*4 %define e_type 16 %define e_entry (16 + 2*2 + 4) %define p_memsz 5*4 %define szb_info 12 %define szl_info 12 %define szp_info 12 %define a_type 0 %define a_val 4 %define sz_auxv 8 %define __NR_munmap 91 ;; control just falls through, after this part and compiled C code ;; are uncompressed. fold_begin: ; enter: %ebx= &Elf32_Ehdr of this program ; patchLoader will modify to be ; dword sz_uncompressed, sz_compressed ; byte compressed_data... ; ld-linux.so.2 depends on AT_PHDR and AT_ENTRY, for instance. ; Move argc,argv,envp down to make room for Elf_auxv table. ; Linux kernel 2.4.2 and earlier give only AT_HWCAP and AT_PLATFORM ; because we have no PT_INTERP. Linux kernel 2.4.5 (and later?) ; give not quite everything. It is simpler and smaller code for us ; to generate a "complete" table where Elf_auxv[k -1].a_type = k. ; On second thought, that wastes a lot of stack space (the entire kernel ; auxv, plus those slots that remain empty anyway). So try for minimal ; space on stack, without too much code, by doing it serially. %define AT_NULL 0 %define AT_IGNORE 1 %define AT_PHDR 3 %define AT_PHENT 4 %define AT_PHNUM 5 %define AT_PAGESZ 6 %define AT_ENTRY 9 %define ET_DYN 3 sub ecx, ecx mov edx, (1<>8 ; %ecx= PAGE_SIZE add ebx, [p_memsz + szElf32_Ehdr + ebx] add ebx, ecx push ebx ; dynbase L53: sub esi, eax ; length of compressed data mov ebx, [ eax] ; length of uncompressed ELF headers mov ecx, [4+ eax] ; length of compressed ELF headers add ecx, byte szb_info pusha ; (AT_table, sz_cpr, f_expand, &tmp_ehdr, {sz_unc, &tmp}, {sz_cpr, &b1st_info} ) inc edi ; swap with above 'pusha' to inhibit auxv_up for PT_INTERP EXTERN upx_main call upx_main ; returns entry address add esp, byte 8*4 ; remove 8 params from pusha pop ecx ; dynbase pop ebx ; &Elf32_Ehdr of this stub add esp, dword MAX_ELF_HDR + OVERHEAD ; un-alloca push eax ; save entry address dec edi ; auxv table sub eax,eax ; 0, also AT_NULL db 0x3c ; "cmpb al, byte ..." like "jmp 1+L60" but 1 byte shorter L60: scasd ; a_un.a_val etc. scasd ; a_type jne L60 ; not AT_NULL ; edi now points at [AT_NULL]a_un.a_ptr which contains result of make_hatch() ; _dl_start and company (ld-linux.so.2) once assumed that it had virgin stack, ; and did not initialize all its stack local variables to zero. ; See bug libc/1165 at http://bugs.gnu.org/cgi-bin/gnatsweb.pl ; Found 1999-06-16 glibc-2.1.1 ; Fixed 1999-12-29 glibc-2.1.2 ; ;%define N_STKCLR (0x100 + MAX_ELF_HDR + OVERHEAD)/4 ;%define N_STKCLR 8 ; lea edi, [esp - 4*N_STKCLR] ; pusha ; values will be zeroed ; mov esi,esp ; save ; mov esp,edi ; Linux does not grow stack below esp ; mov ecx, N_STKCLR ; ; xor eax,eax ; eax already 0 from L60 ; rep stosd ; mov esp,esi ; restore push eax push eax push eax push eax push eax push eax push eax push eax ; 32 bytes of zeroes now on stack sub ecx, ebx ; length to unmap mov al, __NR_munmap ; eax was 0 from L60 jmp [edi] ; unmap ourselves via escape hatch, then goto entry ; called twice: ; 1st with esi==edi, ecx=0, edx= bitmap of slots needed: just update edx. ; 2nd with esi!=edi, ecx= slot_count: move, then append AT_IGNORE slots ; entry: esi= src = &argc; edi= dst; ecx= # slots wanted; edx= bits wanted ; exit: edi= &auxtab; edx= bits still needed do_auxv: ; cld L10: ; move argc+argv lodsd stosd test eax,eax jne L10 L20: ; move envp lodsd stosd test eax,eax jne L20 push edi ; return value L30: ; process auxv lodsd ; a_type stosd cmp eax, byte 32 jae L32 ; prevent aliasing by 'btr' when 32<=a_type btr edx, eax ; no longer need a slot of type eax [Carry only] L32: test eax, eax ; AT_NULL ? lodsd stosd jnz L30 ; a_type != AT_NULL sub edi, byte 8 ; backup to AT_NULL add ecx, ecx ; two words per auxv inc eax ; convert 0 to AT_IGNORE rep stosd ; allocate and fill dec eax ; convert AT_IGNORE to AT_NULL stosd ; re-terminate with AT_NULL stosd pop edi ; &auxtab ret %define __NR_mmap 90 global mmap mmap: push ebx lea ebx, [2*4 + esp] push byte __NR_mmap pop eax int 0x80 pop ebx ret ; vi:ts=8:et:nowrap