📄 l_linux.c
字号:
/* l_linux.c -- stub loader for Linux This file is part of the UPX executable compressor. Copyright (C) 1996-2004 Markus Franz Xaver Johannes Oberhumer Copyright (C) 1996-2004 Laszlo Molnar 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 <mfx@users.sourceforge.net> <ml1050@users.sourceforge.net> */#include "linux.hh"/*************************************************************************// configuration section**************************************************************************/// use malloc instead of the bss segement#define USE_MALLOC/*************************************************************************// file util**************************************************************************/#undef xread#undef xwrite#if 1//static int xread(int fd, void *buf, int count) __attribute__((__stdcall__));static int xread(int fd, void *buf, int count){ // note: we can assert(count > 0); do { int n = read(fd, buf, count); if (n == -EINTR) continue; if (n <= 0) break; buf += n; // gcc extension: add to void * count -= n; } while (count > 0); return count;}#else#define xread(fd,buf,count) ((count) - read(fd,buf,count))#endif#if 1static __inline__ int xwrite(int fd, const void *buf, int count){ // note: we can assert(count > 0); do { int n = write(fd, buf, count); if (n == -EINTR) continue; if (n <= 0) break; buf += n; // gcc extension: add to void * count -= n; } while (count > 0); return count;}#else#define xwrite(fd,buf,count) ((count) - write(fd,buf,count))#endif/*************************************************************************// util**************************************************************************/static char *upx_itoa(char *buf, unsigned long v){ char *p = buf; { unsigned long k = v; do { p++; k /= 10; } while (k > 0); } buf = p; *p = 0; { unsigned long k = v; do { *--p = '0' + k % 10; k /= 10; } while (k > 0); } return buf;}#if defined(__i386__)# define SET2(p, c0, c1) \ * (unsigned short *) (p) = ((c1)<<8 | c0)# define SET4(p, c0, c1, c2, c3) \ * (uint32_t *) (p) = ((c3)<<24 | (c2)<<16 | (c1)<<8 | c0)# define SET3(p, c0, c1, c2) \ SET4(p, c0, c1, c2, 0)#else# define SET2(p, c0, c1) \ (p)[0] = c0, (p)[1] = c1# define SET3(p, c0, c1, c2) \ (p)[0] = c0, (p)[1] = c1, (p)[2] = c2# define SET4(p, c0, c1, c2, c3) \ (p)[0] = c0, (p)[1] = c1, (p)[2] = c2, (p)[3] = c3#endif/*************************************************************************// UPX & NRV stuff**************************************************************************/// must be the same as in p_unix.cpp !#if !defined(USE_MALLOC)# define BLOCKSIZE (512*1024)#endif#if defined(__i386__)extern intnrv2b_decompress_asm_fast ( const nrv_byte *src, nrv_uint src_len, nrv_byte *dst, nrv_uint *dst_len );#define nrv2b_decompress nrv2b_decompress_asm_fastextern intnrv2d_decompress_asm_fast ( const nrv_byte *src, nrv_uint src_len, nrv_byte *dst, nrv_uint *dst_len );#define nrv2d_decompress nrv2d_decompress_asm_fastextern intnrv2e_decompress_asm_fast ( const nrv_byte *src, nrv_uint src_len, nrv_byte *dst, nrv_uint *dst_len );#define nrv2e_decompress nrv2e_decompress_asm_fast#endif /* __i386__ *//*************************************************************************// upx_main - called by our entry code//// This function is optimized for size.**************************************************************************/void upx_main(char *argv[], char *envp[]) __asm__("upx_main");void upx_main(char *argv[], char *envp[]){ // file descriptors int fdi, fdo; uint32_t header[3]; // header[0]: a random program id generated at compression time // header[1]: original (uncompressed) file size // header[2]: blocksize used for compression // for getpid() pid_t pid; // temporary file name (max 14 chars) static char tmpname_buf[] = "/tmp/upxAAAAAAAAAAA"; char *tmpname = tmpname_buf; char procself_buf[64]; char *procself; // decompression buffer#if defined(USE_MALLOC) unsigned char *buf; static int malloc_args[6] = { 0, UPX5, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0 };#else static unsigned char buf[BLOCKSIZE + OVERHEAD];#endif // // ----- Step 0: set /proc/self using /proc/<pid> ----- // //personality(PER_LINUX); pid = getpid(); SET4(procself_buf + 0, '/', 'p', 'r', 'o'); SET2(procself_buf + 4, 'c', '/'); procself = upx_itoa(procself_buf + 6, pid); *procself++ = '/'; // // ----- Step 1: prepare input file ----- // // Open the exe. SET3(procself, 'e', 'x', 'e'); fdi = open(procself_buf, O_RDONLY, 0);#if 1 // try /proc/<pid>/file for the sake of FreeBSD if (fdi < 0) { SET4(procself, 'f', 'i', 'l', 'e'); fdi = open(procself_buf, O_RDONLY, 0); }#endif#if 0 // Save some bytes of code - the lseek() below will fail anyway. if (fdi < 0) goto error1;#endif // Seek to start of compressed data. The offset is patched // by the compressor. if (lseek(fdi, UPX1, 0) < 0) goto error1; // Read header. if (xread(fdi, (void *)header, 12) != 0) goto error1; // Paranoia. Make sure this is actually our expected executable // by checking the random program id. (The id is both stored // in the header and patched into this stub.) if (header[0] != UPX2) goto error1; // // ----- Step 2: prepare temporary output file ----- // // Compute name of temporary output file in tmpname[]. // Protect against Denial-of-Service attacks. { char *p = tmpname_buf + sizeof(tmpname_buf) - 1; uint32_t r; // Compute the last 4 characters (20 bits) from getpid(). { unsigned k = 4; r = (uint32_t) pid; do { unsigned char d = r % 32; if (d >= 26) d += '0' - 'Z' - 1; *--p += d; r /= 32; } while (--k > 0); } // Provide 4 random bytes from our program id. r ^= header[0]; // Mix in 4 runtime random bytes. // Don't consume precious bytes from /dev/urandom. {#if 1 struct timeval tv; gettimeofday(&tv, 0); r ^= (uint32_t) tv.tv_sec; r ^= ((uint32_t) tv.tv_usec) << 12; // shift into high-bits#else // using adjtimex() may cause portability problems static struct timex tx; adjtimex(&tx); r ^= (uint32_t) tx.time.tv_sec; r ^= ((uint32_t) tx.time.tv_usec) << 12; // shift into high-bits r ^= (uint32_t) tx.errcnt;#endif } // Compute 7 more characters from the 32 random bits. { unsigned k = 7; do { unsigned char d = r % 32; if (d >= 26) d += '0' - 'Z' - 1; *--p += d; r /= 32; } while (--k > 0); } } // Just in case, remove the file. { int err = unlink(tmpname); if (err != -ENOENT && err != 0) goto error1; } // Create the temporary output file. fdo = open(tmpname, O_WRONLY | O_CREAT | O_EXCL, 0700);#if 0 // Save some bytes of code - the ftruncate() below will fail anyway. if (fdo < 0) goto error;#endif // Set expected file size. if (ftruncate(fdo, header[1]) != 0) goto error; // // ----- Step 3: setup memory ----- //#if defined(USE_MALLOC) buf = (unsigned char *) mmap(malloc_args); if ((unsigned long) buf >= (unsigned long) -4095) goto error;#else if (header[2] > BLOCKSIZE) goto error;#endif // // ----- Step 4: decompress blocks ----- // for (;;) { int32_t size[2]; // size[0]: uncompressed block size // size[1]: compressed block size // Note: if size[0] == size[1] then the block was not // compressible and is stored in its uncompressed form. int i; // Read and check block sizes. if (xread(fdi, (void *)size, 8) != 0) goto error; if (size[0] == 0) // uncompressed size 0 -> EOF { if (size[1] != UPX_MAGIC_LE32) // size[1] must be h->magic goto error; if (header[1] != 0) // all bytes must be written goto error; break; } if (size[1] <= 0) goto error; if (size[1] > size[0] || size[0] > (int32_t)header[2]) goto error; // Now we have: // assert(size[1] <= size[0]); // assert(size[0] > 0 && size[0] <= blocksize); // assert(size[1] > 0 && size[1] <= blocksize); // Read compressed block. i = header[2] + OVERHEAD - size[1]; if (xread(fdi, buf+i, size[1]) != 0) goto error; // Decompress block. if (size[1] < size[0]) { // in-place decompression nrv_uint out_len;#if defined(NRV2B) i = nrv2b_decompress(buf+i, size[1], buf, &out_len);#elif defined(NRV2D) i = nrv2d_decompress(buf+i, size[1], buf, &out_len);#elif defined(NRV2E) i = nrv2e_decompress(buf+i, size[1], buf, &out_len);#else# error#endif if (i != 0 || out_len != (nrv_uint)size[0]) goto error; // i == 0 now } // Write uncompressed block. if (xwrite(fdo, buf+i, size[0]) != 0) {// error exit is here in the middle to keep the jumps short. error: (void) unlink(tmpname); error1: // Note: the kernel will close all open files and // unmap any allocated memory. for (;;) (void) exit(127); } header[1] -= size[0]; } // // ----- Step 5: release resources ----- //#if defined(USE_MALLOC) munmap(buf, malloc_args[1]);#endif if (close(fdo) != 0) goto error; if (close(fdi) != 0) goto error; // // ----- Step 6: try to start program via /proc/self/fd/X ----- // // Many thanks to Andi Kleen <ak@muc.de> and // Jamie Lokier <nospam@cern.ch> for this nice idea. // Open the temp file. fdi = open(tmpname, O_RDONLY, 0); if (fdi < 0) goto error; // Compute name of temp fdi. SET3(procself, 'f', 'd', '/'); upx_itoa(procself + 3, fdi); // Check for working /proc/self/fd/X by accessing the // temp file again, now via temp fdi.#define err fdo err = access(procself_buf, R_OK | X_OK); if (err == UPX3) { // Now it's safe to unlink the temp file (as it is still open). unlink(tmpname); // Set the file close-on-exec. fcntl(fdi, F_SETFD, FD_CLOEXEC); // Execute the original program via /proc/self/fd/X. execve(procself_buf, argv, envp); // If we get here we've lost. }#undef err // The proc filesystem isn't working. No problem. close(fdi); // // ----- Step 7: start program in /tmp ----- // // Fork off a subprocess to clean up. // We have to do this double-fork trick to keep a zombie from // hanging around if the spawned original program doesn't check for // subprocesses (as well as to prevent the real program from getting // confused about this subprocess it shouldn't have). // Thanks to Adam Ierymenko <api@one.net> for this solution. if (fork() == 0) { if (fork() == 0) { // Sleep 3 seconds, then remove the temp file. static const struct timespec ts = { UPX4, 0 }; nanosleep(&ts, 0); unlink(tmpname); } exit(0); } // Wait for the first fork()'d process to die. waitpid(-1, (int *)0, 0); // Execute the original program. execve(tmpname, argv, envp); // // ----- Step 8: error exit ----- // // If we return from execve() there was an error. Give up. goto error;}/*vi:ts=4:et:nowrap*/
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -