unhide-20121229/0000775000175000017500000000000012077357015013435 5ustar icehouseicehouseunhide-20121229/unhide-linux.c0000644000175000017500000005134712067501177016221 0ustar icehouseicehouse/* http://sourceforge.net/projects/unhide/ */ /* This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 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. If not, see . */ // Needed for unistd.h to declare getpgid() and others #define _XOPEN_SOURCE 500 // Needed for sched.h to declare sched_getaffinity() #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "unhide-output.h" #include "unhide-linux.h" // header const char header[] = "Unhide 20121229\n" "Copyright © 2012 Yago Jesus & Patrick Gouin\n" "License GPLv3+ : GNU GPL version 3 or later\n" "http://www.unhide-forensics.info\n\n" "NOTE : This version of unhide is for systems using Linux >= 2.6 \n\n"; // defauly sysctl kernel.pid_max int maxpid = 32768; // Threads id for sync int tid ; // our own PID pid_t mypid ; // options int verbose = 0; int morecheck = FALSE; int RTsys = FALSE; int brutesimplecheck = TRUE; // Found hidden proccess flag int found_HP = 0; // For logging to file int logtofile; FILE *unlog; // Temporary string for output char used_options[1000]; // Temporary string for output char scratch[1000]; // table of test to perform struct tab_test_t tab_test[MAX_TESTNUM]; /* * Get the maximum number of process on this system. */ void get_max_pid(int* newmaxpid) { char path[]= "/proc/sys/kernel/pid_max"; pid_t tmppid = 0; FILE* fd= fopen(path,"r"); if(!fd) { warnln(1, unlog, "Cannot read current maximum PID. Using default value %d", * newmaxpid) ; return; } if((fscanf(fd, "%d", &tmppid) != 1) || tmppid < 1) { msgln(unlog, 0, "Warning : Cannot get current maximum PID, error parsing %s format. Using default value %d", path, * newmaxpid) ; return; } else { *newmaxpid = tmppid; } fclose(fd) ; } /* * Verify if ps see a given pid. */ int checkps(int tmppid, int checks) { int ok = 0; char pids[30]; char compare[100]; char command[60]; FILE *fich_tmp ; // printf("in --> checkps\n"); // DEBUG // The compare string is the same for all test sprintf(compare,"%i\n",tmppid); if (PS_PROC == (checks & PS_PROC)) { sprintf(command,COMMAND,tmppid) ; fich_tmp=popen (command, "r") ; if (fich_tmp == NULL) { warnln(verbose, unlog, "Couldn't run command: %s while ps checking pid %d", command, tmppid) ; return(0); } { char* tmp_pids = pids; if (NULL != fgets(pids, 30, fich_tmp)) { pids[29] = 0; // printf("pids = %s\n", pids); // DEBUG while( *tmp_pids == ' ' && tmp_pids <= pids+29) { tmp_pids++; } if (strncmp(tmp_pids, compare, 30) == 0) {ok = 1;} } } if (NULL != fich_tmp) pclose(fich_tmp); if (1 == ok) return(ok) ; // pid is found, no need to go further } if (PS_THREAD == (checks & PS_THREAD)) { FILE *fich_thread ; fich_thread=popen (THREADS, "r") ; if (NULL == fich_thread) { warnln(verbose, unlog, "Couldn't run command: %s while ps checking pid %d", THREADS, tmppid) ; return(0); } while ((NULL != fgets(pids, 30, fich_thread)) && ok == 0) { char* tmp_pids = pids; pids[29] = 0; while( *tmp_pids == ' ' && tmp_pids <= pids+29) { tmp_pids++; } if (strncmp(tmp_pids, compare, 30) == 0) {ok = 1;} } if (fich_thread != NULL) pclose(fich_thread); if (1 == ok) return(ok) ; // thread is found, no need to go further } if (PS_MORE == (checks & PS_MORE)) { FILE *fich_session ; sprintf(command,SESSION,tmppid) ; fich_session=popen (command, "r") ; if (fich_session == NULL) { warnln(verbose, unlog, "Couldn't run command: %s while ps checking pid %d", command, tmppid) ; return(0); } while ((NULL != fgets(pids, 30, fich_session)) && ok == 0) { char* tmp_pids = pids; pids[29] = 0; while( *tmp_pids == ' ' && tmp_pids <= pids+29) { tmp_pids++; } if (strncmp(tmp_pids, compare, 30) == 0) { ok = 1; } } pclose(fich_session); if (1 == ok) return(ok) ; // session is found, no need to go further FILE *fich_pgid ; fich_pgid=popen (PGID, "r") ; if (NULL == fich_pgid) { warnln(verbose, unlog, "Couldn't run command: %s while ps checking pid %d", PGID, tmppid) ; return(0); } while ((NULL != fgets(pids, 30, fich_pgid)) && ok == 0) { char* tmp_pids = pids; pids[29] = 0; while( *tmp_pids == ' ' && tmp_pids <= pids+29) { tmp_pids++; } if (strncmp(tmp_pids, compare, 30) == 0) { ok = 1; } } pclose(fich_pgid); } return ok; } /* * Display hidden process and possibly some information on it. */ void printbadpid (int tmppid) { int statuscmd ; char cmd[100] ; struct stat buffer; FILE *cmdfile ; char cmdcont[1000], fmtstart[128]; int cmdok = 0, cmdok2 = 0; found_HP = 1; sprintf(fmtstart,"Found HIDDEN PID: %i", tmppid) ; msgln(unlog, 0, "%s", fmtstart) ; sprintf(cmd,"/proc/%i/cmdline",tmppid); statuscmd = stat(cmd, &buffer); // statuscmd = 0 ; // DEBUG if (statuscmd == 0) { cmdfile=fopen (cmd, "r") ; if (cmdfile != NULL) { while ((NULL != fgets (cmdcont, 1000, cmdfile)) && 0 == cmdok) { cmdok++ ; msgln(unlog, 0, "\tCmdline: \"%s\"", cmdcont) ; } fclose(cmdfile); } } if (0 == cmdok) { msgln(unlog, 0, "\tCmdline: \"\"") ; } { // try to readlink the exe ssize_t length ; sprintf(cmd,"/proc/%i/exe",tmppid); statuscmd = lstat(cmd, &buffer); // printf("%s",cmd) ; //DEBUG // printf("\tstatuscmd : %d\n",statuscmd) ; //DEBUG if (statuscmd == 0) { length = readlink(cmd, cmdcont, 1000) ; // printf("\tLength : %0d\n",(int)length) ; //DEBUG if (-1 != length) { cmdcont[length] = 0; // terminate the string cmdok++; msgln(unlog, 0, "\tExecutable: \"%s\"", cmdcont) ; } else { msgln(unlog, 0, "\tExecutable: \"\"") ; } } else { msgln(unlog, 0, "\tExecutable: \"\"") ; } } { // read internal command name sprintf(cmd,"/proc/%i/comm",tmppid); statuscmd = stat(cmd, &buffer); if (statuscmd == 0) { cmdfile=fopen (cmd, "r") ; if (cmdfile != NULL) { // printf("\tCmdFile : %s\n",cmd) ; //DEBUG while ((NULL != fgets (cmdcont, 1000, cmdfile)) && 0 == cmdok2) { cmdok2++; // printf("\tLastChar : %x\n",cmdcont[strlen(cmdcont)]) ; //DEBUG if (cmdcont[strlen(cmdcont)-1] == '\n') { cmdcont[strlen(cmdcont)-1] = 0 ; // get rid of newline } if (0 == cmdok) // it is a kthreed : add brackets { msgln(unlog, 0, "\tCommand: \"[%s]\"", cmdcont) ; } else { msgln(unlog, 0, "\tCommand: \"%s\"", cmdcont) ; } } fclose(cmdfile); } else { msgln(unlog, 0, "\tCommand: \"can't read file\"") ; } } else { msgln(unlog, 0, "\t\" ... maybe a transitory process\"") ; } } // try to print some useful info about the hidden process // does not work well for kernel processes/threads and deamons { FILE *fich_tmp ; sprintf(cmd,"/proc/%i/environ",tmppid); statuscmd = stat(cmd, &buffer); if (statuscmd == 0) { sprintf(cmd,"cat /proc/%i/environ | tr \"\\0\" \"\\n\" | grep -w 'USER'",tmppid) ; // printf(cmd) ; fich_tmp=popen (cmd, "r") ; if (fich_tmp == NULL) { warnln(verbose, unlog, "\tCouldn't read USER for pid %d", tmppid) ; } if (NULL != fgets(cmdcont, 30, fich_tmp)) { cmdcont[strlen(cmdcont)-1] = 0 ; // get rid of newline msgln(unlog, 0, "\t$%s", cmdcont) ; } else { msgln(unlog, 0, "\t$USER=", cmdcont) ; } pclose(fich_tmp); sprintf(cmd,"cat /proc/%i/environ | tr \"\\0\" \"\\n\" | grep -w 'PWD'",tmppid) ; // printf(cmd) ; fich_tmp=popen (cmd, "r") ; if (fich_tmp == NULL) { warnln(verbose, unlog, "\tCouldn't read PWD for pid %d", tmppid) ; } if (NULL != fgets(cmdcont, 30, fich_tmp)) { cmdcont[strlen(cmdcont)-1] = 0 ; // get rid of newline msgln(unlog, 0, "\t$%s", cmdcont) ; } else { msgln(unlog, 0, "\t$PWD=", cmdcont) ; } pclose(fich_tmp); // printf("Done !\n"); } } printf("\n"); } /* * Display short help */ void usage(char * command) { printf("Usage: %s [options] test_list\n\n", command); printf("Option :\n"); printf(" -V Show version and exit\n"); printf(" -v verbose\n"); printf(" -h display this help\n"); printf(" -m more checks (available only with procfs, checkopendir & checkchdir commands)\n"); printf(" -r use alternate sysinfo test in meta-test\n"); printf(" -f log result into unhide-linux.log file\n"); printf(" -o same as '-f'\n"); printf(" -d do a double check in brute test\n"); printf("Test_list :\n"); printf(" Test_list is one or more of the following\n"); printf(" Standard tests :\n"); printf(" brute\n"); printf(" proc\n"); printf(" procall\n"); printf(" procfs\n"); printf(" quick\n"); printf(" reverse\n"); printf(" sys\n"); printf(" Elementary tests :\n"); printf(" checkbrute\n"); printf(" checkchdir\n"); printf(" checkgetaffinity\n"); printf(" checkgetparam\n"); printf(" checkgetpgid\n"); printf(" checkgetprio\n"); printf(" checkRRgetinterval\n"); printf(" checkgetsched\n"); printf(" checkgetsid\n"); printf(" checkkill\n"); printf(" checknoprocps\n"); printf(" checkopendir\n"); printf(" checkproc\n"); printf(" checkquick\n"); printf(" checkreaddir\n"); printf(" checkreverse\n"); printf(" checksysinfo\n"); printf(" checksysinfo2\n"); printf(" checksysinfo3\n"); } /* * Parse command line arguments (exiting if requested by any option). */ void parse_args(int argc, char **argv) { int c = 0; int index = 0; static struct option long_options[] = { /* These options set a flag. */ {"brute-doublecheck", no_argument, &brutesimplecheck, 0}, {"alt-sysinfo", no_argument, &RTsys, 1}, {"log", no_argument, &logtofile, 1}, /* These options don't set a flag. We distinguish them by their indices. */ {"morecheck", no_argument, 0, 'm'}, {"verbose", no_argument, 0, 'v'}, {"help", no_argument, 0, 'h'}, {"version", no_argument, 0, 'V'}, {0, 0, 0, 0} }; for(;;) // until there's no more option { /* getopt_long stores the option index here. */ int option_index = 0; c = getopt_long (argc, argv, "dformhvV", long_options, &option_index); /* Detect the end of the options. */ if (c == -1) break; switch(c) { case 0 : // flag long options if (long_options[option_index].flag != 0) //if this option set a flag { break; // nothing to do } printf ("option %s", long_options[option_index].name); if (optarg) // if there's an argument { printf (" with arg %s", optarg); } printf ("\n"); break ; case 'd' : brutesimplecheck = FALSE; break ; case 'h' : usage(argv[0]) ; exit (0) ; break ; case 'f' : logtofile = 1; break; case 'o' : logtofile = 1 ; break ; case 'm' : morecheck = TRUE; verbose = TRUE; break ; case 'r' : RTsys = TRUE; break ; case 'v' : verbose++ ; ; break ; case 'V' : exit (0) ; break ; case '?' : // invalid option exit (2) ; break ; default : // something very nasty happened exit(-1) ; break ; } } // generate options string for logging strncpy(used_options, "Used options: ", 1000); if (verbose) strncat(used_options, "verbose ", 1000-1-strlen(used_options)); if (!brutesimplecheck) strncat(used_options, "brutesimplecheck ", 1000-1-strlen(used_options)); if (morecheck) strncat(used_options, "morecheck ", 1000-1-strlen(used_options)); if (RTsys) strncat(used_options, "RTsys ", 1000-1-strlen(used_options)); if (logtofile) strncat(used_options, "logtofile ", 1000-1-strlen(used_options)); // Process list of tests to do for (index = optind; index < argc; index++) { if ((strcmp(argv[index], "proc") == 0) || (strcmp(argv[index], "checkproc") == 0)) { tab_test[TST_PROC].todo = TRUE; } else if (strcmp(argv[index], "procfs") == 0) { tab_test[TST_CHDIR].todo = TRUE; tab_test[TST_OPENDIR].todo = TRUE; tab_test[TST_READDIR].todo = TRUE; } else if (strcmp(argv[index], "procall") == 0) { tab_test[TST_PROC].todo = TRUE; tab_test[TST_CHDIR].todo = TRUE; tab_test[TST_OPENDIR].todo = TRUE; tab_test[TST_READDIR].todo = TRUE; } else if (strcmp(argv[index], "sys") == 0) { tab_test[TST_KILL].todo = TRUE; tab_test[TST_NOPROCPS].todo = TRUE; tab_test[TST_GETPRIO].todo = TRUE; tab_test[TST_GETPGID].todo = TRUE; tab_test[TST_GETSID].todo = TRUE; tab_test[TST_GETAFF].todo = TRUE; tab_test[TST_GETPARM].todo = TRUE; tab_test[TST_GETSCHED].todo = TRUE; tab_test[TST_RR_INT].todo = TRUE; /* Remove sysinfo test from sys compound test as it give FP in some case if (TRUE == RTsys) { tab_test[TST_SYS_INFO2].todo = TRUE; } else { tab_test[TST_SYS_INFO].todo = TRUE; } */ } else if (strcmp(argv[index], "quick") == 0) { tab_test[TST_QUICKONLY].todo = TRUE; /* Remove sysinfo test from quick compound test as it give FP in some case if (TRUE == RTsys) { tab_test[TST_SYS_INFO2].todo = TRUE; } else { tab_test[TST_SYS_INFO].todo = TRUE; } */ } else if ((strcmp(argv[index], "brute") == 0) || (strcmp(argv[index], "checkbrute") == 0)) { tab_test[TST_BRUTE].todo = TRUE; } else if ((strcmp(argv[index], "reverse") == 0) || (strcmp(argv[index], "checkreverse") == 0)) { tab_test[TST_REVERSE].todo = TRUE; } else if (strcmp(argv[index], "opendir") == 0) { tab_test[TST_OPENDIR].todo = TRUE; } else if (strcmp(argv[index], "checkquick") == 0) { tab_test[TST_QUICKONLY].todo = TRUE; } else if (strcmp(argv[index], "checksysinfo") == 0) { tab_test[TST_SYS_INFO].todo = TRUE; } else if (strcmp(argv[index], "checksysinfo2") == 0) { tab_test[TST_SYS_INFO2].todo = TRUE; } else if (strcmp(argv[index], "checksysinfo3") == 0) { tab_test[TST_SYS_INFO3].todo = TRUE; } else if (strcmp(argv[index], "checkchdir") == 0) { tab_test[TST_CHDIR].todo = TRUE; } else if (strcmp(argv[index], "checkreaddir") == 0) { tab_test[TST_READDIR].todo = TRUE; } else if (strcmp(argv[index], "checkopendir") == 0) { tab_test[TST_OPENDIR].todo = TRUE; } else if (strcmp(argv[index], "checkkill") == 0) { tab_test[TST_KILL].todo = TRUE; } else if (strcmp(argv[index], "checknoprocps") == 0) { tab_test[TST_NOPROCPS].todo = TRUE; } else if (strcmp(argv[index], "checkgetprio") == 0) { tab_test[TST_GETPRIO].todo = TRUE; } else if (strcmp(argv[index], "checkgetpgid") == 0) { tab_test[TST_GETPGID].todo = TRUE; } else if (strcmp(argv[index], "checkgetsid") == 0) { tab_test[TST_GETSID].todo = TRUE; } else if (strcmp(argv[index], "checkgetaffinity") == 0) { tab_test[TST_GETAFF].todo = TRUE; } else if (strcmp(argv[index], "checkgetparam") == 0) { tab_test[TST_GETPARM].todo = TRUE; } else if (strcmp(argv[index], "checkgetsched") == 0) { tab_test[TST_GETSCHED].todo = TRUE; } else if (strcmp(argv[index], "checkRRgetinterval") == 0) { tab_test[TST_RR_INT].todo = TRUE; } else { printf("Unknown argument\n") ; usage(argv[0]); exit(0); } } } int main (int argc, char *argv[]) { int i; printf(header) ; if(getuid() != 0){ die(unlog, "You must be root to run %s !", argv[0]) ; } // Initialize the table of test to perform. // --------------------------------------- for (i=0 ; i