1/*
2;  i386-linux.shlib-init.S -- Linux shared library init & decompressor (Elf binary)
3;
4;  This file is part of the UPX executable compressor.
5;
6;  Copyright (C) 1996-2020 Markus Franz Xaver Johannes Oberhumer
7;  Copyright (C) 1996-2020 Laszlo Molnar
8;  Copyright (C) 2000-2020 John F. Reiser
9;  All Rights Reserved.
10;
11;  UPX and the UCL library are free software; you can redistribute them
12;  and/or modify them under the terms of the GNU General Public License as
13;  published by the Free Software Foundation; either version 2 of
14;  the License, or (at your option) any later version.
15;
16;  This program is distributed in the hope that it will be useful,
17;  but WITHOUT ANY WARRANTY; without even the implied warranty of
18;  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19;  GNU General Public License for more details.
20;
21;  You should have received a copy of the GNU General Public License
22;  along with this program; see the file COPYING.
23;  If not, write to the Free Software Foundation, Inc.,
24;  59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
25;
26;  Markus F.X.J. Oberhumer              Laszlo Molnar
27;  <markus@oberhumer.com>               <ezerotven+github@gmail.com>
28;
29;  John F. Reiser
30;  <jreiser@users.sourceforge.net>
31;
32*/
33
34#include "arch/i386/macros.S"
35
36
37/*************************************************************************
38// program entry point
39// see glibc/sysdeps/i386/elf/start.S
40**************************************************************************/
41
42section LEXEC000
43//  .long .  // compress-time virtual address (detect runtime relocation)
44//  .long user DT_INIT
45//  .long &escape_hatch
46//  .long &{p_info; b_info; compressed data}
47_start: .globl _start
48  ////    int3
49        push eax; pusha
50        mov ebp,esp
51o_uinit= 8*4  // beyond saved registers
52//o_reloc= 6*4  // saved ecx
53
54        call main  // push address of decompress subroutine
55decompress:
56
57// /*************************************************************************
58// // C callable decompressor
59// **************************************************************************/
60
61// /* Offsets to parameters, allowing for {pusha + call} */
62#define         O_INP   (8*4 +1*4)
63#define         O_INS   (8*4 +2*4)
64#define         O_OUTP  (8*4 +3*4)
65#define         O_OUTS  (8*4 +4*4)
66#define         O_PARAM (8*4 +5*4)
67
68#define         INP     dword ptr [esp+O_INP]
69#define         INS     dword ptr [esp+O_INS]
70#define         OUTP    dword ptr [esp+O_OUTP]
71#define         OUTS    dword ptr [esp+O_OUTS]
72#define         PARM    dword ptr [esp+O_PARAM]
73
74section LEXEC009
75        //;  empty section for commonality with l_lx_exec86.asm
76section LEXEC010
77                pusha
78                // cld
79
80                mov     esi, INP
81                mov     edi, OUTP
82
83                or      ebp, -1
84//;;             align   8
85
86#include "arch/i386/nrv2b_d32.S"
87#include "arch/i386/nrv2d_d32.S"
88#include "arch/i386/nrv2e_d32.S"
89#include "arch/i386/lzma_d.S"
90                cjt32 0
91
92section LEXEC015
93                // eax is 0 from decompressor code
94                //xor     eax, eax               ; return code
95
96// check compressed size
97                mov     edx, INP
98                add     edx, INS
99                cmp     esi, edx
100                jz      .ok
101                dec     eax
102.ok:
103
104// write back the uncompressed size
105                sub     edi, OUTP
106                mov     edx, OUTS
107                mov     [edx], edi
108
109                mov [7*4 + esp], eax
110                popa
111                ret
112
113                ctojr32
114                ctok32  edi, dl
115section LEXEC017
116                popa
117                ret
118
119section LEXEC020
120
121#define PAGE_SIZE ( 1<<12)
122PAGE_MASK= -PAGE_SIZE  // AND clears the offset within page
123
124#define MAP_FIXED     0x10
125#define MAP_PRIVATE   0x02
126#define MAP_ANONYMOUS 0x20
127#define PROT_READ      1
128#define PROT_WRITE     2
129#define PROT_EXEC      4
130#define __NR_mmap     90
131#define __NR_munmap   91
132#define __NR_mprotect 125
133#define szElf32_Ehdr 0x34
134#define p_memsz  5*4
135sz_p_info = 3*4
136sz_l_info = 3*4
137sz_b_info=3*4
138  sz_unc= 0
139  sz_cpr= 4
140  b_method= 8
141
142#define __NR_write 4
143#define __NR_exit  1
144
145#define pushsbli .byte 0x6a,  /* push sign-extended byte to long immediate*/
146
147msg_SELinux:
148        pushsbli L71 - L70
149        pop edx  // length
150        call L71
151L70:
152        .ascii "PROT_EXEC|PROT_WRITE failed.\n"
153L71:
154        pop ecx  // message text
155        push 2  // fd stderr
156        pop ebx
157        push __NR_write; pop eax; int 0x80
158die:
159        mov bl, 127  // only low 7 bits matter!
160        push __NR_exit; pop eax; int 0x80
161
162main:
163//  1. allocate temporary pages
164//  2. copy to temporary pages:
165//       fragment of page below dst; compressed src;
166//       decompress+unfilter; supervise
167//  3. mmap destination pages for decompressed data
168//  4. create escape hatch
169//  5. jump to temporary pages
170//  6. uncompress
171//  7. unfilter
172//  8. mprotect decompressed pages
173//  9  setup args for unmap of temp pages
174// 10. jump to escape hatch
175// 11. unmap temporary pages
176// 12. goto user DT_INIT
177
178        pop edx  // &decompress
179
180        lea esi,[edx + _start - decompress - 4*4]
181               mov ecx,esi
182        lodsd; sub ecx,eax; //mov [ebp+o_reloc],ecx
183        lodsd; add eax,ecx; mov [ebp+o_uinit],eax  // reloc DT_INIT  for step 12
184        lodsd; add eax,ecx; push eax  // reloc &hatch   for step 10
185p_hatch= -1*4
186        lodsd; add eax,ecx; xchg eax,edi  // &l_info; also destination for decompress
187        lea esi,[edi + sz_l_info + sz_p_info]  // &b_info
188
189        push eax; push eax  // param space: munmap temp pages  step 9
190p_unmap= -3*4
191
192        lodsd; lodsd; add esi,eax; lodsd  // skip unpack helper block
193
194        lodsd  // eax=dstlen
195        mov ebx,edi
196        and ebx,~PAGE_MASK  // ebx= fragment
197        add eax,ebx  // extend to page-aligned
198        sub edi,ebx
199        push eax; push edi  // params: mprotect restored pages  step 8
200p_mprot= -5*4
201        sub eax,ebx  // restore
202        add edi,ebx
203
204        movzx ecx, byte ptr [esi+b_method-4+1]; push ecx  // ftid
205        movzx ecx, byte ptr [esi+b_method-4+2]; push ecx  // cto8
206        push eax; mov ecx,esp  // dstlen  also for unfilter  step 7
207        push edi  // dst                 param for unfilter  step 7
208p_unflt= -9*4
209        push edx  // &decompress
210o_uncpr= -10*4
211
212        lodsd; xchg eax,edx  // edx= srclen
213        lodsd; push eax  // method,filter,cto,junk
214        push ecx  // &dstlen
215        push edi  // dst
216        push edx  // srclen
217        push esi  // src;  arglist ready for decompress  step 6
218p_uncpr= -15*4
219
220        mov eax,[ebp+o_uncpr]  // &decompress
221        add edx,[eax-4]  // l_d_cpr + l_f_unc
222
223        call L220
224supervise:
225        // Allocate pages for result of decompressing.
226        // These replace the compressed source and the following hole.
227        push 0; push 0
228        push MAP_PRIVATE|MAP_ANONYMOUS|MAP_FIXED
229        push PROT_READ|PROT_WRITE
230        push [ebp+p_mprot+4]  // dstlen
231        push [ebp+p_mprot]  // dst
232        mov ecx,ebx  // save fragment
233        mov ebx,esp
234        push __NR_mmap; pop eax; int 0x80
235                cmp eax,[ebx]; je 0f; hlt; 0:
236        add esp,6*4
237
238        // Restore fragment of page below dst
239        xchg eax,edi
240        mov esi,[ebp+p_unmap]
241        add ecx,3; shr ecx,2  // FIXME: is this safe?
242        rep movsd
243
244        call [ebp+o_uncpr]  // decompress
245// decompression can overrun dst by 3 bytes on i386; construct hatch now
246        pop eax; pop eax  // discard src, srclen
247        pop eax  // dst
248        pop ecx  // &dstlen
249        pop edx  // discard method,filter,cto,junk
250        add eax,[ecx]  // dst += dstlen
251        mov dword ptr [eax],0xc36180cd  // "int 0x80; popa; ret"
252        mov [esp + p_hatch - o_uncpr],eax  // hatch at end of .text
253//o_uncpr
254        pop eax  // &decompress
255//p_unflt
256        cmp word ptr [esp+3*4],0; je 0f  // 0==ftid ==> no filter
257        add eax,2; call eax  // unfilter {i386 f_unf==(2+f_unc)}
2580:
259        add esp,4*4
260//p_mprot
261        pop ebx  // dst including fragment
262        pop ecx  // dstlen
263        push PROT_READ|PROT_EXEC; pop edx
264        push __NR_mprotect; pop eax; int 0x80
265//p_unmap
266        pop ebx  // &temp pages
267        pop ecx  // length
268        push __NR_munmap; pop eax
269//p_hatch
270        ret  // goto escape hatch
271//hatch:
272        int 0x80  // munmap temporary pages
273        popa
274        ret  // goto user DT_INIT
275
276L220:
277        pop esi  // &supervise
278        add edx,[esi-4]  // total length to allocate
279        add edx,ebx  // include fragment
280
281        // Allocate pages to hold temporary copy.
282        push 0; push 0
283        push MAP_PRIVATE|MAP_ANONYMOUS
284        push PROT_READ|PROT_WRITE|PROT_EXEC
285        push edx  // length with fragment
286        push 0  // addr
287        mov ecx,ebx  // save fragment
288        mov ebx,esp  // & vec
289        push __NR_mmap; pop eax; int 0x80
290                cmp eax,PAGE_MASK; jb 0f; hlt; 0:
291        add esp,6*4
292        mov ebx,ecx  // save fragment
293
294        mov [ebp+p_unmap  ],eax  // addr
295        mov [ebp+p_unmap+4],edx  // length with fragment
296        xchg eax,edi  // edi= dst
297        xchg eax,esi  // eax= &supervise
298//p_uncpr
299        mov esi,[ebp+p_mprot]
300        add ecx,3; shr ecx,2  // FIXME: is this safe?
301        rep movsd  // copy the fragment
302
303        pop esi  // &src data (after fragment)
304                pop ecx; push ecx  // length
305        push edi  // &copied data (after fragment)
306        add ecx,3; shr ecx,2
307        rep movsd  // copy compressed data
308
309        mov esi,[ebp+o_uncpr]
310        mov     [ebp+o_uncpr],edi
311        mov ecx,[esi-4]
312        rep movsb
313
314//o_super
315        xchg eax,esi  // esi= &supervise
316        push edi  // &copied
317        mov ecx,[esi-4]
318        rep movsb
319
320        ret  // goto copied supervise:
321
322// empty sections for commonality with non-shlib
323section LUNMP000
324section LUNMP001
325section LEXEC025
326section LEXECDYN
327
328/* vim:set ts=8 sw=8 et: */
329