"Fossies" - the Fresh Open Source Software archive 
Member "exepak-1.5/src/exepak.c" of archive exepak-1.5.tar.gz:
/* EXEPAK version 1.2
(c)1997 Adam Ierymenko
Copyright (C) 2004 - 2005 by Stefan Talpalaru <stefantalpalaru@yahoo.com>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License version 2 as published by
the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston,
MA 02111-1307 USA
This code is not pretty, but it works. :) */
#include <stdio.h>
#include <unistd.h>
#include <stdarg.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <fcntl.h>
#include <malloc.h>
#include <string.h>
#include <utime.h>
#include <time.h>
#include "../stub/__stub.h"
#include "../stub/stubsize.h"
#include <ucl/ucl.h>
#include <errno.h>
#include <linux/elf.h>
#include <sys/mman.h>
/* Prototypes */
char *tospaces(char *str);
char my_rename(char *oldname,char *newname);
char check_already_compressed(char *fname);
char create_sfx_binary(char *infile);
char decompress_sfx_binary(char *infile);
void bad_options(void);
/* Configuration options */
ucl_uint blocksize = (256*1024); // 256 Kb - seems reasonable
char testing = 0;
char extracting = 0;
char file_buffer[65536];
int stub_wsize;
int zbufsz = 0;
/* Cheezy little formatting function */
char *tospaces(char *str)
{
static char buf[512];
register int i = 0;
while(*(str+i))
buf[i++] = ' ';
buf[i] = '\0';
return buf;
}
/*
* Rename function which will work between two different filesystems.
* Returns nonzero if error, zero if successful.
*
* (I have /home and /tmp mounted on two different disks)
*/
char my_rename(char *oldname,char *newname)
{
FILE *infile,*outfile;
char buf[65536];
register int n;
errno = 0;
if (rename(oldname,newname))
{
if (errno == EXDEV)
{
if (!(outfile = fopen(newname,"w")))
return 1;
if (!(infile = fopen(oldname,"r")))
{
fclose(outfile);
return 1;
}
while((n = fread(&buf,1,sizeof(buf),infile)) > 0)
{
if (fwrite(&buf,1,n,outfile) != n)
{
fclose(outfile);
return 1;
}
}
fclose(infile);
fclose(outfile);
unlink(oldname);
}
else return 1;
}
return 0;
}
/*
* read_headers() reads the ELF header and the program header table,
* and checks to make sure that this is in fact a file that we should
* be munging.
*/
int read_headers(int fd)
{
Elf32_Ehdr elfhdr;
Elf32_Ehdr *elfhdr2 = (Elf32_Ehdr *)exepak_stub;
Elf32_Phdr *phdr = (Elf32_Phdr *)&exepak_stub[elfhdr2->e_phoff];
/* written stub size */
stub_wsize = phdr[1].p_offset + phdr[1].p_filesz;
if( read(fd, &elfhdr, sizeof(elfhdr)) != sizeof(elfhdr) )
return 0;
if( elfhdr.e_ident[EI_MAG0] != ELFMAG0
|| elfhdr.e_ident[EI_MAG1] != ELFMAG1
|| elfhdr.e_ident[EI_MAG2] != ELFMAG2
|| elfhdr.e_ident[EI_MAG3] != ELFMAG3
|| elfhdr.e_type != ET_EXEC )
return 0;
if( elfhdr.e_ehsize != sizeof(Elf32_Ehdr) )
return 0;
lseek(fd,0,SEEK_SET);
return 1;
}
int is_script(int fd)
{
char magic[2];
lseek(fd, 0, SEEK_SET);
if(read(fd, &magic, 2) != 2)
return 0;
if(memcmp(magic, "#!", 2)) // 0 if equal
return 0;
lseek(fd, 0, SEEK_SET);
return 1;
}
int append_stubfile(int fdcompressed,int fdout,char *stub,struct stat *infstat,unsigned int compressed_size)
{
Elf32_Ehdr *elfhdr = (Elf32_Ehdr *)stub;
Elf32_Phdr *phdr = (Elf32_Phdr *)&stub[elfhdr->e_phoff];
unsigned int mode;
void *map = NULL;
/* permission for decompression */
mode = infstat->st_mode;
/* paranoia check */
if( elfhdr->e_ident[EI_MAG0] != ELFMAG0
|| elfhdr->e_ident[EI_MAG1] != ELFMAG1
|| elfhdr->e_ident[EI_MAG2] != ELFMAG2
|| elfhdr->e_ident[EI_MAG3] != ELFMAG3
|| elfhdr->e_type != ET_EXEC )
return 0;
if( elfhdr->e_ehsize != sizeof(Elf32_Ehdr) )
return 0;
if( !elfhdr->e_phoff && elfhdr->e_phnum != 2 &&
elfhdr->e_phentsize != sizeof(Elf32_Phdr) )
return 0;
/*
* Remove references to the section header table if
* it was removed, and reduces program header table entries that
* included truncated bytes at the end of the file.
*/
elfhdr->e_shoff = 0;
elfhdr->e_shnum = 0;
elfhdr->e_shentsize = 0;
elfhdr->e_shstrndx = 0;
zbufsz = phdr[1].p_memsz - phdr[1].p_filesz - 8;
// printf("zbufsz = 0x%X\n", zbufsz);
/*
* Adjust the size of the data section. Add
* an integer at the end to break decompress loop.
*/
phdr[1].p_filesz += 8 + compressed_size;
phdr[1].p_memsz += 8 + compressed_size + sizeof(int);
/*
* Write the new ordered stub file at the begin
* to the compressed file and sets the new file size.
*/
// printf("the stub is %d bytes long\n", stub_wsize + 8);
if(write(fdout,stub,stub_wsize) != stub_wsize)
return 0;
if(write(fdout,&compressed_size,sizeof(int)) != sizeof(int))
return 0;
if(write(fdout,&mode,sizeof(mode)) != sizeof(mode))
return 0;
if((map = mmap(0, compressed_size, PROT_READ, MAP_SHARED, fdcompressed, 0)) == (void *)-1)
return 0;
if(write(fdout, map + compressed_size - zbufsz, zbufsz) !=
zbufsz) //the tail
return 0;
if(write(fdout, map, compressed_size - zbufsz) !=
(compressed_size - zbufsz)) //the head
return 0;
munmap(map, compressed_size);
return 1;
}
/*
* This function checks to see if a file is already compressed by looking
* for the marker string within the first 16k of the file (or less if the
* file is smaller). Returns nonzero if true.
*/
char check_already_compressed(char *fname)
{
FILE *inf;
char buf[16400];
int n;
register int i;
struct stat infstat;
if (stat(fname,&infstat))
return 0;
/* If it's not an executable, return 0 */
if ((!(infstat.st_mode & S_IXUSR))&&(!(infstat.st_mode & S_IXGRP)))
{
if (!(infstat.st_mode & S_IXOTH))
return 0;
}
if (S_ISDIR(infstat.st_mode))
return 0;
if (!(inf = fopen(fname,"r")))
return 0;
n = fread(&buf,1,16384,inf);
if (n > 0)
{
char exepak[] = {68, 87, 68, 79, 64, 74, 0};
char epk11[] = {68, 79, 74, 48, 48, 0};
/* make it say "EXEPAK" */
for (i = 0; i < 6; i++)
exepak[i]++;
/* and this one: "EPK11" */
for (i = 0; i < 5; i++)
epk11[i]++;
/* Get rid of nulls so we can use strstr() */
for(i=0;i<n;i++)
{
if (buf[i] == '\0')
buf[i] = (char)1;
}
buf[n+1] = '\0';
if (strstr(buf,exepak))
return 1;
if (strstr(buf,epk11))
return 2;
}
fclose(inf);
return 0;
}
/*
* Compresses an uncompressed executable.
* Returns nonzero if successful.
*/
char create_sfx_binary(char *infile)
{
unsigned int sizes[2]; /* Two 32-bit ints to write before each block */
ucl_bytep inbuf;
ucl_bytep outbuf;
ucl_bytep frombuf;
ucl_uint outsize;
int n;
FILE *inf,*of;
struct stat infstat;
char ofname[256];
char newfname[256];
int oldsize,newsize;
int progmeter = 0;
struct utimbuf utb;
char didsomething = 0;
int uncomp; //uncompressible
int fdin = -1;
sprintf(ofname,"/tmp/_comp%x%x",(unsigned int)time(NULL),getpid());
sprintf(newfname,"%s~",infile);
/* Allocate memory */
if ((!(inbuf = (ucl_bytep)malloc(blocksize))) ||
(!(outbuf = (ucl_bytep)malloc(blocksize / 8 + blocksize + 256))))
{
printf("%s: compress failed: out of memory!\n",infile);
return 0;
}
/* Stat the input file */
if (stat(infile,&infstat))
{
printf("%s: compress failed: can't stat\n",infile);
return 0;
}
/* Can't compress files we don't own unless we're super-user */
if ((getuid())&&(infstat.st_uid != getuid()))
{
printf("%s: compress failed: permission denied\n",infile);
return 0;
}
/* Check to make sure it's an executable file */
if ((!(infstat.st_mode & S_IXUSR))&&(!(infstat.st_mode & S_IXGRP)))
{
if (!(infstat.st_mode & S_IXOTH))
{
printf("%s: compress failed: not an executable file\n",infile);
return 0;
}
}
if (S_ISDIR(infstat.st_mode))
{
printf("%s: compress failed: %s is a directory\n",infile,infile);
return 0;
}
/* Open the input file */
if (!(inf = fopen(infile,"r")))
{
printf("%s: compress failed: %s\n",infile,strerror(errno));
return 0;
}
/* Check to make sure if it's ELF it's an executable (not a dll) */
if(! (read_headers(fileno(inf)) || is_script(fileno(inf))))
{
printf("%s: compress failed, invalid ELF binary or script.\n",infile);
return 0;
}
/* Open the output file */
if (!(of = fopen(ofname,"w+")))
{
printf("%s: compress failed: could not create temporary file\n",infile);
return 0;
}
printf("Compressing...");
fflush(stdout);
/* Copy and compress infile to outfile */
while((n = fread(inbuf,1,blocksize,inf)))
{
didsomething = 1;
uncomp = 0;
/* Compress the block of read data */
if (ucl_nrv2e_99_compress(inbuf, n, outbuf, &outsize, 0, 10, NULL, NULL) != UCL_E_OK)
{
printf("\r%s: compress failed: internal compression error\n",infile);
fclose(of);
unlink(ofname);
return 0;
}
/* Write the block size before the actual block */
if (outsize >= n) //uncompressible block, write it as it is
{
uncomp = 1;
outsize = n;
}
sizes[0] = outsize;
sizes[1] = n;
if (fwrite(&sizes,1,sizeof(sizes),of) != sizeof(sizes))
{
printf("\r%s: compress failed: error writing to temporary file: %s\n",infile,strerror(errno));
fclose(of);
unlink(ofname);
return 0;
}
/* Write the block of un/compressed data */
if (uncomp)
frombuf = inbuf;
else
frombuf = outbuf;
if (fwrite(frombuf,1,outsize,of) != outsize)
{
printf("\r%s: compress failed: error writing to temporary file: %s\n",infile,strerror(errno));
fclose(of);
unlink(ofname);
return 0;
}
printf("\r%s: read=%d/%d written=%d (%.02f:1)",infile,(progmeter += n),(int)infstat.st_size,(int)ftell(of),(((float)n) / ((float)outsize)));
fflush(stdout);
}
printf("\n");
if (!didsomething)
{
printf("\r%s: compress failed: read error\n",infile);
fclose(of);
unlink(ofname);
return 0;
}
oldsize = ftell(inf);
newsize = ftell(of);
/* Check to see if we got something smaller than before, if not we fail */
if (newsize + stub_wsize >= oldsize)
{
printf("\r%s: compress failed: no reduction in size was achieved\n",infile);
my_rename(newfname,infile);
fclose(of);
unlink(ofname);
return 0;
}
fclose(inf);
/* Reopen and write stub file to disk */
if ((fdin = open(infile,O_WRONLY|O_CREAT|O_TRUNC)) < 0 || !append_stubfile(fileno(of), fdin, exepak_stub, &infstat, newsize))
{
printf("\r%s: compress failed, could not replace original binary; %s\n",infile,strerror(errno));
my_rename(newfname,infile);
close(fdin);
fclose(of);
unlink(ofname);
return 0;
}
fclose(of);
unlink(ofname);
/* Set up permissions on new file */
chown(infile,infstat.st_uid,infstat.st_gid);
chmod(infile,infstat.st_mode);
utb.modtime = infstat.st_mtime;
utb.actime = infstat.st_atime;
utime(infile,&utb);
newsize += stub_wsize + 8;
printf("\r%s: initial=%d, compressed=%d (%.02f:1, %.02f%%)\n",infile,oldsize,newsize,(((float)oldsize) / ((float)newsize)), ((float)newsize) * 100 / ((float)oldsize));
return 1;
}
/*
* Decompresses a self-extracting binary, returns nonzero if successful.
* Writes infile~ as output file.
*/
char decompress_sfx_binary(char *infile)
{
FILE *of;
int npid,n;
char *args[3];
char ofname[256];
int pipes[2];
char readbuf[16384];
char *tmp1,*tmp2;
struct stat infstat;
char newfname[256];
int newsize;
struct utimbuf utb;
sprintf(ofname,"/tmp/_decomp%x%x",(unsigned int)time(NULL),getpid());
sprintf(newfname,"%s~",infile);
/* Stat the input file */
if (stat(infile,&infstat))
{
printf("%s: decompress failed: can't stat\n",infile);
return 0;
}
/* Can't decompress files we don't own unless we're super-user */
if ((getuid())&&(infstat.st_uid != getuid()))
{
printf("%s: decompress failed: permission denied\n",infile);
return 0;
}
/* Check to make sure it's an executable file */
if ((!(infstat.st_mode & S_IXUSR))&&(!(infstat.st_mode & S_IXGRP)))
{
if (!(infstat.st_mode & S_IXOTH))
{
printf("%s: decompress failed: not an executable file\n",infile);
return 0;
}
}
if (S_ISDIR(infstat.st_mode))
{
printf("%s: decompress failed: %s is a directory\n",infile,infile);
return 0;
}
/* Open output file */
if (!(of = fopen(ofname,"w+")))
{
printf("%s: decompress failed: could not create %s\n",infile,ofname);
return 0;
}
/* Create pipes */
if (pipe(pipes))
{
printf("%s: decompress failed: could not create pipes!\n",infile);
fclose(of);
unlink(ofname);
return 0;
}
/* Fork off and execute compressed binary in dump-decompressed-data mode */
if (!(npid = fork()))
{
close(STDOUT_FILENO);
dup2(pipes[1],STDOUT_FILENO);
dup2(pipes[1],STDERR_FILENO);
args[0] = infile;
args[1] = ">d";
args[2] = (char *)0;
execv(infile,args);
}
close(pipes[1]);
if (npid < 0)
{
printf("%s: decompress failed: could not fork!\n",infile);
close(pipes[0]);
fclose(of);
unlink(ofname);
return 0;
}
while((n = read(pipes[0],&readbuf,sizeof(readbuf))) > 0)
{
if (fwrite(&readbuf,1,n,of) != n)
{
printf("%s: decompress failed: write error\n",infile);
close(pipes[0]);
fclose(of);
unlink(ofname);
return 0;
}
}
close(pipes[0]);
/* Look to make sure it's a real exe and not an error from the
* self-decompressing executable */
newsize = ftell(of);
rewind(of);
if ((n = fread(&readbuf,1,128,of)) < 128)
{
printf("%s: decompress failed: write error\n",infile);
fclose(of);
unlink(ofname);
return 0;
}
readbuf[n+1] = '\0';
if (strstr(readbuf,"can't extract"))
{
if ((tmp1 = strchr(readbuf,'\n')))
{
if ((tmp2 = strchr(readbuf,':')))
{
*tmp1 = '\0';
printf("%s: decompress failed: extraction error%s\n",infile,tmp2);
}
}
else printf("%s: decompress failed: extraction error\n",infile);
fclose(of);
unlink(ofname);
return 0;
}
fclose(of);
unlink(infile);
if (my_rename(ofname,infile))
{
printf("%s: decompress failed: could not replace original binary\n",infile);
unlink(ofname);
my_rename(newfname,infile);
return 0;
}
/* Set up permissions on new file */
chown(infile,infstat.st_uid,infstat.st_gid);
chmod(infile,infstat.st_mode);
utb.modtime = infstat.st_mtime;
utb.actime = infstat.st_atime;
utime(infile,&utb);
printf("%s: decompress successful: compressed=%d, decompressed=%d\n",infile,(int)infstat.st_size,newsize);
return 1;
}
/* Called for bad or nonexistant command line options */
void bad_options(void)
{
printf("Usage: exepak [options] <files...>\n\
Options are:\n\
-b# - Sets compression block size in Kb (default:256)\n\
-t - Just test whether executables are compressed\n\
-d\n\
-x - Decompress a compressed executable\n");
exit(0);
}
int main(int argc,char *argv[])
{
int i;
char x;
FILE *tf;
struct stat infstat;
/* Initialize UCL compression library */
if (ucl_init() != UCL_E_OK)
{
printf("UCL compression library failed to initialize!\n");
exit(1);
}
/* Process command line options */
if (argc < 2)
bad_options();
for(i=1;i<argc;i++)
{
if (*argv[i] == '-')
{
switch(*(argv[i]+1))
{
case 'b':
blocksize = 1024 * atoi(argv[i]+2);
if (blocksize <= (1024 * 16))
{
printf("Minimum blocksize is 16k\n\n");
bad_options();
}
break;
case 't':
testing = 1;
break;
case 'x':
case 'd':
extracting = 1;
break;
default:
bad_options();
}
}
else
{
if (testing)
{
if (stat(argv[i],&infstat))
printf("%s: can't stat\n",argv[i]);
else
{
if ((!(infstat.st_mode & S_IXUSR))&&(!(infstat.st_mode & S_IXGRP)))
{
if (!(infstat.st_mode & S_IXOTH))
{
printf("%s: not an executable file\n",argv[i]);
}
}
else if (S_ISDIR(infstat.st_mode))
{
printf("%s: is a directory\n",argv[i]);
}
else if ((tf = fopen(argv[i],"r")))
{
fclose(tf);
if ((x = check_already_compressed(argv[i])))
printf("%s: compressed (%s)\n",argv[i],((x == 2) ? "1.1" : "1.0"));
else printf("%s: uncompressed\n",argv[i]);
}
else printf("%s: file not found or unreadable\n",argv[i]);
}
}
else if (extracting)
{
if ((x = check_already_compressed(argv[i])))
{
if (x == 2)
decompress_sfx_binary(argv[i]);
else printf("%s: decompress failed: this is an exepak 1.0 binary, run it\n%s with the command line \"^dc-<target filename>\" to decompress\n",argv[i],tospaces(argv[i]));
}
else printf("%s: decompress failed: not compressed\n",argv[i]);
}
else
{
if (!check_already_compressed(argv[i]))
create_sfx_binary(argv[i]);
else printf("%s: compress failed: already compressed\n",argv[i]);
}
}
waitpid(-1,(int *)0,WNOHANG);
}
exit(0);
}