240 lines
7.1 KiB
ArmAsm
240 lines
7.1 KiB
ArmAsm
/* amd64-linux.elf-fold.S -- linkage to C code to process Elf binary
|
|
*
|
|
* This file is part of the UPX executable compressor.
|
|
*
|
|
* Copyright (C) 2000-2024 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
|
|
* <markus@oberhumer.com> <ezerotven+github@gmail.com>
|
|
*
|
|
* John F. Reiser
|
|
* <jreiser@users.sourceforge.net>
|
|
*/
|
|
|
|
NBPW= 8
|
|
#include "arch/amd64/macros.S"
|
|
#include "arch/amd64/regs.h"
|
|
|
|
PATH_MAX= 4096 // /usr/include/linux/limits.h
|
|
PAGE_SHIFT= 12 // /usr/include/sys/user.h
|
|
PAGE_SIZE= 1 << PAGE_SHIFT
|
|
PAGE_MASK= ~0 << PAGE_SHIFT
|
|
|
|
sz_b_info= 12
|
|
sz_unc= 0
|
|
sz_cpr= 4
|
|
|
|
sz_l_info= 12
|
|
sz_p_info= 12
|
|
|
|
MAP_PRIVATE= 0x02
|
|
MAP_FIXED= 0x10
|
|
|
|
PROT_READ= 0x1
|
|
|
|
O_RDONLY= 0
|
|
|
|
OVERHEAD=2048
|
|
|
|
/* 64-bit mode only! */
|
|
__NR_read= 0
|
|
__NR_write= 1
|
|
__NR_open= 2
|
|
__NR_close= 3
|
|
|
|
__NR_mmap= 9
|
|
__NR_mprotect= 10
|
|
__NR_munmap= 11
|
|
__NR_brk= 12
|
|
|
|
__NR_exit= 60
|
|
__NR_readlink= 89
|
|
|
|
// IN: [ADRX,+LENX): compressed data; [ADRU,+LENU): expanded fold (w/ upx_main)
|
|
// %rbx= 4+ &O_BINFO; %rbp= f_exp; %r14= ADRX; %r15= LENX;
|
|
// rsp/ elfaddr,%fd,ADRU,LENU,rdx,%entry, argc,argv,0,envp,0,auxv,0,strings
|
|
|
|
fold_begin:
|
|
//// int3 // DEBUG only
|
|
call L90
|
|
.asciz "/proc/self/exe"; .byte 0 // 16 bytes in all
|
|
#include "arch/amd64/bxx.S"
|
|
L90:
|
|
pop %rdx // "/proc/self/eax"
|
|
push %r14 // ADRX
|
|
push %r15 // LENX
|
|
lea 16(%rdx),%rax; push %rax # &amdbxx: f_unf
|
|
|
|
unmap_all_pages= (1<<1)
|
|
is_ptinterp= (1<<0)
|
|
push %rsp; pop %rsi
|
|
testb $unmap_all_pages,-4(%rbx); jnz 0f; sub $PATH_MAX,%rsp; 0:
|
|
push %rsp; pop %rdi
|
|
push $10; pop %rcx; rep movsq # f_unf,LENX,ADRX,elfaddr,%fd,ADRU,LENU,rdx,%entry,argc
|
|
xor %eax,%eax
|
|
|
|
0:
|
|
cmpq %rax,(%rsi); movsq; jne 0b # move past argv
|
|
movq %rdi,%r14 # remember &new_env[0]
|
|
testb $unmap_all_pages,-4(%rbx); jnz 0f; stosq; 0: # space for new_env[0]
|
|
|
|
0:
|
|
cmpq %rax,(%rsi); movsq; jne 0b # move past env
|
|
mov %rdi,%r12 # &new Elf64_auxv
|
|
|
|
push %rsi; pop %arg4 # &old_auxv in case no_env_pse
|
|
0:
|
|
cmpq %rax,(%rsi); movsq; movsq; jne 0b # move past auxv
|
|
mov %rdi,%r15 # beyond auxv
|
|
mov %rsi,%r13 # beginning of strings
|
|
sub %r15,%r12 # -length of auxv
|
|
|
|
testb $unmap_all_pages,-4(%rbx); jz env_pse
|
|
mov %rdi,%r14; jmp no_env_pse # beyond auxv
|
|
env_pse:
|
|
push %rdx; pop %arg1 # "/proc/self/exe"
|
|
push $O_RDONLY; pop %arg2
|
|
call open; mov %rax,4*NBPW(%rsp) # fd
|
|
movl $-1+ PATH_MAX,%arg3l # buflen
|
|
mov %r15,%arg2 # buffer (beyond auxv)
|
|
// %arg1 persists from open()
|
|
push $ __NR_readlink; pop %rax; syscall; testl %eax,%eax; jns 0f
|
|
// readlink() failed. Set the result equal to the argument.
|
|
push %arg1; pop %arg2 # result= "/proc/self/exe"
|
|
push $15; pop %rax # 1+ strlen( )
|
|
0:
|
|
xchg %eax,%ecx # %ecx= byte count
|
|
std
|
|
lea -1(%r13),%rdi # dst last byte
|
|
movb $0,%al; stosb # terminate
|
|
lea -1(%arg2,%rcx),%rsi # src last byte
|
|
rep movsb # slide up
|
|
sub $3,%rdi; movl $('='<<24)|(' '<<16)|(' '<<8)|(' '<<0),(%rdi) # env var name
|
|
mov %rdi,(%r14) # new_env[0]
|
|
and $~0<<3,%rdi # align
|
|
mov %r15,%rcx
|
|
sub %rsp,%rcx # byte count
|
|
|
|
mov %ecx,%eax
|
|
xor %edi,%eax
|
|
xor $NBPW,%eax
|
|
and $NBPW,%eax
|
|
sub %rax,%rdi # keep 16-byte alignment of %rsp
|
|
|
|
mov %rdi,%r14 # end of new auxv
|
|
sub $NBPW,%rdi # &last qword of new auxv
|
|
lea -NBPW(%r15),%rsi # &last qword of old auxv
|
|
shr $3,%rcx; rep movsq
|
|
lea NBPW(%rdi),%rsp
|
|
cld
|
|
|
|
lea (%r14,%r12),%arg4 # &new Elf64_auxv
|
|
no_env_pse:
|
|
movl -4(%rbx),%eax; and $is_ptinterp,%eax; add %rax,%arg4
|
|
pop %arg6 # f_unf
|
|
pop %arg2 # LENX
|
|
pop %arg1 # ADRX
|
|
|
|
pop %rax # elfaddr
|
|
subq $ OVERHEAD,%rsp
|
|
movq %rsp,%arg3 # &ELf64_Ehdr temporary space
|
|
push %rax; mov %rax,%r13 # elfaddr 7th arg
|
|
|
|
movq %rbp,%arg5 # &decompress: f_expand
|
|
call upx_main # Out: %rax= entry
|
|
/* entry= upx_main(b_info *arg1, total_size arg2, Elf64_Ehdr *arg3,
|
|
Elf32_Auxv_t *arg4, f_decompr arg5, f_unf arg6,
|
|
Elf64_Addr elfaddr )
|
|
*/
|
|
// rsp/ elfaddr,{OVERHEAD},fd,ADRU,LENU,rdx,%entry, argc,argv,0,envp,0,auxv,0,strings
|
|
addq $1*NBPW+OVERHEAD,%rsp # also discard elfaddr
|
|
movq %rax,4*NBPW(%rsp) # entry
|
|
pop %rbx # fd
|
|
|
|
sz_Ehdr= 8*NBPW
|
|
e_type= 16
|
|
ET_EXEC= 2
|
|
sz_Phdr= 7*NBPW
|
|
p_memsz= 5*NBPW
|
|
// Discard pages of compressed data (includes [ADRX,+LENX) )
|
|
movq p_memsz+sz_Phdr+sz_Ehdr(%r13),%arg2 # Phdr[C_TEXT= 1].p_memsz
|
|
//cmpw $ET_EXEC, e_type(%r13); jne 1f
|
|
movq %r13,%arg1; call brk // also sets the brk
|
|
1:
|
|
movq %r13,%arg1; call munmap # discard C_TEXT compressed data
|
|
|
|
// Map 1 page of /proc/self/exe so that the symlink does not disappear.
|
|
test %ebx,%ebx; js no_pse_map
|
|
subq %arg6,%arg6 # 0 offset
|
|
mov %ebx,%arg5l # fd
|
|
push $MAP_PRIVATE; pop %arg4
|
|
push $PROT_READ; pop %arg3
|
|
mov $PAGE_SIZE,%arg2l
|
|
subl %arg1l,%arg1l # 0
|
|
call mmap
|
|
|
|
mov %ebx,%edi # fd
|
|
call close
|
|
no_pse_map:
|
|
pop %arg1 # ADRU: unfolded upx_main etc.
|
|
pop %arg2 # LENU
|
|
push $__NR_munmap; pop %rax
|
|
jmp *-NBPW(%r14) # goto: syscall; pop %rdx; ret
|
|
|
|
mmap: .globl mmap
|
|
movb $ __NR_mmap,%al
|
|
movq %arg4,%sys4
|
|
sysgo: # NOTE: kernel demands 4th arg in %sys4, NOT %arg4
|
|
movzbl %al,%eax
|
|
syscall
|
|
cmpq $ PAGE_MASK,%rax; jc no_fail
|
|
orq $~0,%rax # failure; IGNORE errno
|
|
no_fail:
|
|
ret
|
|
|
|
Pprotect: .globl Pprotect
|
|
movl $~PAGE_MASK,%ecx // frag mask
|
|
and %edi,%ecx // frag
|
|
sub %rcx,%rdi
|
|
add %rcx,%rsi
|
|
jmp mprotect
|
|
|
|
exit: .globl exit
|
|
movb $ __NR_exit,%al; 5: jmp 5f
|
|
brk: .globl brk
|
|
movb $ __NR_brk,%al; 5: jmp 5f
|
|
close: .globl close
|
|
movb $ __NR_close,%al; 5: jmp 5f
|
|
open: .globl open
|
|
movb $ __NR_open,%al; 5: jmp 5f
|
|
munmap: .globl munmap
|
|
movb $ __NR_munmap,%al; 5: jmp 5f
|
|
mprotect: .globl mprotect
|
|
movb $ __NR_mprotect,%al; 5: jmp 5f
|
|
write: .globl write
|
|
mov $__NR_write,%al; 5: jmp 5f
|
|
read: .globl read
|
|
movb $ __NR_read,%al; 5: jmp sysgo
|
|
|
|
my_bkpt: .globl my_bkpt
|
|
int3 // my_bkpt
|
|
ret
|
|
|
|
/* vim:set ts=8 sw=8 et: */
|