1 /*
2           http://sourceforge.net/projects/unhide/
3 */
4 
5 /*
6 This program is free software: you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation, either version 3 of the License, or
9 (at your option) any later version.
10 
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14 GNU General Public License for more details.
15 
16 You should have received a copy of the GNU General Public License
17 along with this program.  If not, see <http://www.gnu.org/licenses/>.
18 */
19 
20 // Needed for unistd.h to declare getpgid() and others
21 #define _XOPEN_SOURCE 500
22 
23 // Needed for sched.h to declare sched_getaffinity()
24 #define _GNU_SOURCE
25 
26 #include <stdio.h>
27 #include <string.h>
28 #include <sys/stat.h>
29 #include <unistd.h>
30 #include <wait.h>
31 #include <sys/resource.h>
32 #include <errno.h>
33 #include <dirent.h>
34 #include <sched.h>
35 #include <sys/types.h>
36 #include <signal.h>
37 #include <stdlib.h>
38 #include <sys/sysinfo.h>
39 #include <fcntl.h>
40 #include <pthread.h>
41 #include <sys/syscall.h>
42 #include <ctype.h>
43 #include <time.h>
44 #include <getopt.h>
45 
46 #include "unhide-output.h"
47 #include "unhide-linux.h"
48 
49 
50 // header
51 const char header[] =
52    "Unhide 20121229\n"
53    "Copyright © 2012 Yago Jesus & Patrick Gouin\n"
54    "License GPLv3+ : GNU GPL version 3 or later\n"
55    "http://www.unhide-forensics.info\n\n"
56    "NOTE : This version of unhide is for systems using Linux >= 2.6 \n\n";
57 
58 // defauly sysctl kernel.pid_max
59 int maxpid = 32768;
60 
61 // Threads id for sync
62 int tid ;
63 
64 // our own PID
65 pid_t mypid ;
66 
67 // options
68 int verbose = 0;
69 int morecheck = FALSE;
70 int RTsys = FALSE;
71 int brutesimplecheck = TRUE;
72 
73 // Found hidden proccess flag
74 int found_HP = 0;
75 
76 // For logging to file
77 int logtofile;
78 FILE *unlog;
79 
80 // Temporary string for output
81 char used_options[1000];
82 
83 // Temporary string for output
84 char scratch[1000];
85 
86 // table of test to perform
87 struct tab_test_t tab_test[MAX_TESTNUM];
88 
89 
90 /*
91  *  Get the maximum number of process on this system.
92  */
get_max_pid(int * newmaxpid)93 void get_max_pid(int* newmaxpid)
94 {
95    char path[]= "/proc/sys/kernel/pid_max";
96    pid_t tmppid = 0;
97    FILE* fd= fopen(path,"r");
98    if(!fd)
99    {
100       warnln(1, unlog, "Cannot read current maximum PID. Using default value %d", * newmaxpid) ;
101       return;
102    }
103 
104 
105    if((fscanf(fd, "%d", &tmppid) != 1) || tmppid < 1)
106    {
107       msgln(unlog, 0, "Warning : Cannot get current maximum PID, error parsing %s format. Using default value %d", path, * newmaxpid) ;
108       return;
109    }
110    else
111    {
112       *newmaxpid = tmppid;
113    }
114    fclose(fd) ;
115 }
116 
117 /*
118  *  Verify if ps see a given pid.
119  */
checkps(int tmppid,int checks)120 int checkps(int tmppid, int checks)
121 {
122 
123    int ok = 0;
124    char pids[30];
125 
126    char compare[100];
127    char command[60];
128 
129 
130    FILE *fich_tmp ;
131 
132 // printf("in --> checkps\n");   // DEBUG
133 
134 // The compare string is the same for all test
135    sprintf(compare,"%i\n",tmppid);
136 
137    if (PS_PROC == (checks & PS_PROC))
138    {
139       sprintf(command,COMMAND,tmppid) ;
140 
141       fich_tmp=popen (command, "r") ;
142       if (fich_tmp == NULL)
143       {
144          warnln(verbose, unlog, "Couldn't run command: %s while ps checking pid %d", command, tmppid) ;
145          return(0);
146       }
147 
148       {
149          char* tmp_pids = pids;
150 
151          if (NULL != fgets(pids, 30, fich_tmp))
152          {
153             pids[29] = 0;
154 
155 //          printf("pids = %s\n", pids);   // DEBUG
156             while( *tmp_pids == ' ' && tmp_pids <= pids+29)
157             {
158                tmp_pids++;
159             }
160 
161             if (strncmp(tmp_pids, compare, 30) == 0) {ok = 1;}
162          }
163       }
164 
165       if (NULL != fich_tmp)
166          pclose(fich_tmp);
167 
168       if (1 == ok) return(ok) ;   // pid is found, no need to go further
169    }
170 
171    if (PS_THREAD == (checks & PS_THREAD))
172    {
173       FILE *fich_thread ;
174 
175       fich_thread=popen (THREADS, "r") ;
176       if (NULL == fich_thread)
177       {
178          warnln(verbose, unlog, "Couldn't run command: %s while ps checking pid %d", THREADS, tmppid) ;
179          return(0);
180       }
181 
182       while ((NULL != fgets(pids, 30, fich_thread)) && ok == 0)
183       {
184          char* tmp_pids = pids;
185 
186          pids[29] = 0;
187 
188          while( *tmp_pids == ' ' && tmp_pids <= pids+29)
189          {
190             tmp_pids++;
191          }
192 
193          if (strncmp(tmp_pids, compare, 30) == 0) {ok = 1;}
194       }
195       if (fich_thread != NULL)
196          pclose(fich_thread);
197 
198       if (1 == ok) return(ok) ;   // thread is found, no need to go further
199    }
200 
201    if (PS_MORE == (checks & PS_MORE))
202    {
203 
204       FILE *fich_session ;
205 
206       sprintf(command,SESSION,tmppid) ;
207 
208       fich_session=popen (command, "r") ;
209       if (fich_session == NULL)
210       {
211          warnln(verbose, unlog, "Couldn't run command: %s while ps checking pid %d", command, tmppid) ;
212          return(0);
213       }
214 
215 
216       while ((NULL != fgets(pids, 30, fich_session)) && ok == 0)
217       {
218          char* tmp_pids = pids;
219 
220          pids[29] = 0;
221 
222          while( *tmp_pids == ' ' && tmp_pids <= pids+29)
223          {
224             tmp_pids++;
225          }
226 
227          if (strncmp(tmp_pids, compare, 30) == 0)
228          {
229             ok = 1;
230          }
231       }
232 
233       pclose(fich_session);
234 
235       if (1 == ok)
236          return(ok) ;   // session is found, no need to go further
237 
238       FILE *fich_pgid ;
239 
240       fich_pgid=popen (PGID, "r") ;
241       if (NULL == fich_pgid)
242       {
243          warnln(verbose, unlog, "Couldn't run command: %s while ps checking pid %d", PGID, tmppid) ;
244          return(0);
245       }
246 
247       while ((NULL != fgets(pids, 30, fich_pgid)) && ok == 0)
248       {
249          char* tmp_pids = pids;
250 
251          pids[29] = 0;
252 
253          while( *tmp_pids == ' ' && tmp_pids <= pids+29)
254          {
255             tmp_pids++;
256          }
257 
258          if (strncmp(tmp_pids, compare, 30) == 0)
259          {
260             ok = 1;
261          }
262       }
263 
264       pclose(fich_pgid);
265 
266    }
267    return ok;
268 }
269 
270 /*
271  *  Display hidden process and possibly some information on it.
272  */
printbadpid(int tmppid)273 void printbadpid (int tmppid)
274 {
275 
276    int statuscmd ;
277    char cmd[100] ;
278    struct stat buffer;
279    FILE *cmdfile ;
280    char cmdcont[1000], fmtstart[128];
281    int cmdok = 0, cmdok2 = 0;
282 
283    found_HP = 1;
284    sprintf(fmtstart,"Found HIDDEN PID: %i", tmppid) ;
285    msgln(unlog, 0, "%s", fmtstart) ;
286 
287    sprintf(cmd,"/proc/%i/cmdline",tmppid);
288 
289    statuscmd = stat(cmd, &buffer);
290 // statuscmd = 0 ;  // DEBUG
291 
292    if (statuscmd == 0)
293    {
294       cmdfile=fopen (cmd, "r") ;
295       if (cmdfile != NULL)
296       {
297          while ((NULL != fgets (cmdcont, 1000, cmdfile)) && 0 == cmdok)
298          {
299             cmdok++ ;
300             msgln(unlog, 0, "\tCmdline: \"%s\"", cmdcont) ;
301          }
302          fclose(cmdfile);
303       }
304    }
305    if (0 == cmdok)
306    {
307       msgln(unlog, 0, "\tCmdline: \"<none>\"") ;
308    }
309 
310    {  // try to readlink the exe
311       ssize_t length ;
312 
313       sprintf(cmd,"/proc/%i/exe",tmppid);
314       statuscmd = lstat(cmd, &buffer);
315 //    printf("%s",cmd) ; //DEBUG
316 //      printf("\tstatuscmd : %d\n",statuscmd) ; //DEBUG
317       if (statuscmd == 0)
318       {
319          length = readlink(cmd, cmdcont, 1000) ;
320 //         printf("\tLength : %0d\n",(int)length) ; //DEBUG
321          if (-1 != length)
322          {
323             cmdcont[length] = 0;   // terminate the string
324             cmdok++;
325             msgln(unlog, 0, "\tExecutable: \"%s\"", cmdcont) ;
326          }
327          else
328          {
329             msgln(unlog, 0, "\tExecutable: \"<nonexistant>\"") ;
330 
331          }
332       }
333       else
334       {
335          msgln(unlog, 0, "\tExecutable: \"<no link>\"") ;
336       }
337    }
338    {       // read internal command name
339       sprintf(cmd,"/proc/%i/comm",tmppid);
340       statuscmd = stat(cmd, &buffer);
341       if (statuscmd == 0)
342       {
343          cmdfile=fopen (cmd, "r") ;
344          if (cmdfile != NULL)
345          {
346 //       printf("\tCmdFile : %s\n",cmd) ; //DEBUG
347             while ((NULL != fgets (cmdcont, 1000, cmdfile)) && 0 == cmdok2)
348             {
349                cmdok2++;
350 //               printf("\tLastChar : %x\n",cmdcont[strlen(cmdcont)]) ; //DEBUG
351                if (cmdcont[strlen(cmdcont)-1] == '\n')
352                {
353                   cmdcont[strlen(cmdcont)-1] = 0 ;  // get rid of newline
354                }
355                if (0 == cmdok) // it is a kthreed : add brackets
356                {
357                   msgln(unlog, 0, "\tCommand: \"[%s]\"", cmdcont) ;
358                }
359                else
360                {
361                   msgln(unlog, 0, "\tCommand: \"%s\"", cmdcont) ;
362                }
363 
364             }
365             fclose(cmdfile);
366          }
367          else
368          {
369             msgln(unlog, 0, "\tCommand: \"can't read file\"") ;
370          }
371       }
372       else
373       {
374          msgln(unlog, 0, "\t\"<none>  ... maybe a transitory process\"") ;
375       }
376    }
377    // try to print some useful info about the hidden process
378    // does not work well for kernel processes/threads and deamons
379    {
380       FILE *fich_tmp ;
381 
382       sprintf(cmd,"/proc/%i/environ",tmppid);
383       statuscmd = stat(cmd, &buffer);
384       if (statuscmd == 0)
385       {
386          sprintf(cmd,"cat /proc/%i/environ | tr \"\\0\" \"\\n\" | grep -w 'USER'",tmppid) ;
387    //      printf(cmd) ;
388          fich_tmp=popen (cmd, "r") ;
389          if (fich_tmp == NULL)
390          {
391             warnln(verbose, unlog, "\tCouldn't read USER for pid %d", tmppid) ;
392          }
393 
394          if (NULL != fgets(cmdcont, 30, fich_tmp))
395          {
396             cmdcont[strlen(cmdcont)-1] = 0 ;  // get rid of newline
397             msgln(unlog, 0, "\t$%s", cmdcont) ;
398          }
399          else
400          {
401             msgln(unlog, 0, "\t$USER=<undefined>", cmdcont) ;
402          }
403          pclose(fich_tmp);
404 
405          sprintf(cmd,"cat /proc/%i/environ | tr \"\\0\" \"\\n\" | grep -w 'PWD'",tmppid) ;
406    //      printf(cmd) ;
407          fich_tmp=popen (cmd, "r") ;
408          if (fich_tmp == NULL)
409          {
410             warnln(verbose, unlog, "\tCouldn't read PWD for pid %d", tmppid) ;
411          }
412 
413          if (NULL != fgets(cmdcont, 30, fich_tmp))
414          {
415             cmdcont[strlen(cmdcont)-1] = 0 ;  // get rid of newline
416             msgln(unlog, 0, "\t$%s", cmdcont) ;
417          }
418          else
419          {
420             msgln(unlog, 0, "\t$PWD=<undefined>", cmdcont) ;
421          }
422          pclose(fich_tmp);
423 
424    //      printf("Done !\n");
425       }
426    }
427    printf("\n");
428 }
429 
430 
431 /*
432  *  Display short help
433  */
usage(char * command)434 void usage(char * command)
435 {
436 
437    printf("Usage: %s [options] test_list\n\n", command);
438    printf("Option :\n");
439    printf("   -V          Show version and exit\n");
440    printf("   -v          verbose\n");
441    printf("   -h          display this help\n");
442    printf("   -m          more checks (available only with procfs, checkopendir & checkchdir commands)\n");
443    printf("   -r          use alternate sysinfo test in meta-test\n");
444    printf("   -f          log result into unhide-linux.log file\n");
445    printf("   -o          same as '-f'\n");
446    printf("   -d          do a double check in brute test\n");
447    printf("Test_list :\n");
448    printf("   Test_list is one or more of the following\n");
449    printf("   Standard tests :\n");
450    printf("      brute\n");
451    printf("      proc\n");
452    printf("      procall\n");
453    printf("      procfs\n");
454    printf("      quick\n");
455    printf("      reverse\n");
456    printf("      sys\n");
457    printf("   Elementary tests :\n");
458    printf("      checkbrute\n");
459    printf("      checkchdir\n");
460    printf("      checkgetaffinity\n");
461    printf("      checkgetparam\n");
462    printf("      checkgetpgid\n");
463    printf("      checkgetprio\n");
464    printf("      checkRRgetinterval\n");
465    printf("      checkgetsched\n");
466    printf("      checkgetsid\n");
467    printf("      checkkill\n");
468    printf("      checknoprocps\n");
469    printf("      checkopendir\n");
470    printf("      checkproc\n");
471    printf("      checkquick\n");
472    printf("      checkreaddir\n");
473    printf("      checkreverse\n");
474    printf("      checksysinfo\n");
475    printf("      checksysinfo2\n");
476    printf("      checksysinfo3\n");
477 }
478 
479 /*
480  * Parse command line arguments (exiting if requested by any option).
481  */
parse_args(int argc,char ** argv)482 void parse_args(int argc, char **argv)
483 {
484    int c = 0;
485    int index = 0;
486 
487    static struct option long_options[] =
488    {
489    /* These options set a flag. */
490       {"brute-doublecheck",  no_argument,      &brutesimplecheck,   0},
491       {"alt-sysinfo",        no_argument,      &RTsys,              1},
492       {"log",                no_argument,      &logtofile,          1},
493       /* These options don't set a flag.
494          We distinguish them by their indices. */
495       {"morecheck",          no_argument,      0,                 'm'},
496       {"verbose",            no_argument,      0,                 'v'},
497       {"help",               no_argument,      0,                 'h'},
498       {"version",            no_argument,      0,                 'V'},
499       {0, 0, 0, 0}
500    };
501 
502    for(;;)  // until there's no more option
503    {
504       /* getopt_long stores the option index here. */
505       int option_index = 0;
506 
507       c = getopt_long (argc, argv, "dformhvV",
508                         long_options, &option_index);
509 
510       /* Detect the end of the options. */
511       if (c == -1)
512          break;
513 
514       switch(c)
515       {
516       case 0 :   // flag long options
517          if (long_options[option_index].flag != 0) //if this option set a flag
518          {
519             break;  // nothing to do
520          }
521          printf ("option %s", long_options[option_index].name);
522          if (optarg) // if there's an argument
523          {
524             printf (" with arg %s", optarg);
525          }
526          printf ("\n");
527          break ;
528       case 'd' :
529          brutesimplecheck = FALSE;
530          break ;
531       case 'h' :
532          usage(argv[0]) ;
533          exit (0) ;
534          break ;
535       case 'f' :
536          logtofile = 1;
537          break;
538       case 'o' :
539          logtofile = 1 ;
540          break ;
541       case 'm' :
542          morecheck = TRUE;
543          verbose = TRUE;
544          break ;
545       case 'r' :
546          RTsys = TRUE;
547          break ;
548       case 'v' :
549          verbose++ ; ;
550          break ;
551       case 'V' :
552          exit (0) ;
553          break ;
554       case '?' :     // invalid option
555          exit (2) ;
556          break ;
557       default :      // something very nasty happened
558          exit(-1) ;
559          break ;
560       }
561 
562    }
563 
564    // generate options string for logging
565    strncpy(used_options, "Used options: ", 1000);
566    if (verbose)
567       strncat(used_options, "verbose ", 1000-1-strlen(used_options));
568    if (!brutesimplecheck)
569       strncat(used_options, "brutesimplecheck ", 1000-1-strlen(used_options));
570    if (morecheck)
571       strncat(used_options, "morecheck ", 1000-1-strlen(used_options));
572    if (RTsys)
573       strncat(used_options, "RTsys ", 1000-1-strlen(used_options));
574    if (logtofile)
575       strncat(used_options, "logtofile ", 1000-1-strlen(used_options));
576 
577    // Process list of tests to do
578    for (index = optind; index < argc; index++)
579    {
580       if ((strcmp(argv[index], "proc") == 0) ||
581                (strcmp(argv[index], "checkproc") == 0))
582       {
583          tab_test[TST_PROC].todo = TRUE;
584       }
585       else if (strcmp(argv[index], "procfs") == 0)
586       {
587          tab_test[TST_CHDIR].todo = TRUE;
588          tab_test[TST_OPENDIR].todo = TRUE;
589          tab_test[TST_READDIR].todo = TRUE;
590       }
591       else if (strcmp(argv[index], "procall") == 0)
592       {
593          tab_test[TST_PROC].todo = TRUE;
594          tab_test[TST_CHDIR].todo = TRUE;
595          tab_test[TST_OPENDIR].todo = TRUE;
596          tab_test[TST_READDIR].todo = TRUE;
597       }
598       else if (strcmp(argv[index], "sys") == 0)
599       {
600          tab_test[TST_KILL].todo = TRUE;
601          tab_test[TST_NOPROCPS].todo = TRUE;
602          tab_test[TST_GETPRIO].todo = TRUE;
603          tab_test[TST_GETPGID].todo = TRUE;
604          tab_test[TST_GETSID].todo = TRUE;
605          tab_test[TST_GETAFF].todo = TRUE;
606          tab_test[TST_GETPARM].todo = TRUE;
607          tab_test[TST_GETSCHED].todo = TRUE;
608          tab_test[TST_RR_INT].todo = TRUE;
609 /* Remove sysinfo test from sys compound test as it give FP in some case
610          if (TRUE == RTsys)
611          {
612             tab_test[TST_SYS_INFO2].todo = TRUE;
613          }
614          else
615          {
616             tab_test[TST_SYS_INFO].todo = TRUE;
617          }
618 */
619       }
620       else if (strcmp(argv[index], "quick") == 0)
621       {
622          tab_test[TST_QUICKONLY].todo = TRUE;
623 /* Remove sysinfo test from quick compound test as it give FP in some case
624          if (TRUE == RTsys)
625          {
626             tab_test[TST_SYS_INFO2].todo = TRUE;
627          }
628          else
629          {
630             tab_test[TST_SYS_INFO].todo = TRUE;
631          }
632 */
633       }
634       else if ((strcmp(argv[index], "brute") == 0) ||
635                (strcmp(argv[index], "checkbrute") == 0))
636       {
637          tab_test[TST_BRUTE].todo = TRUE;
638       }
639       else if ((strcmp(argv[index], "reverse") == 0) ||
640                (strcmp(argv[index], "checkreverse") == 0))
641       {
642          tab_test[TST_REVERSE].todo = TRUE;
643       }
644       else if (strcmp(argv[index], "opendir") == 0)
645       {
646          tab_test[TST_OPENDIR].todo = TRUE;
647       }
648       else if (strcmp(argv[index], "checkquick") == 0)
649       {
650          tab_test[TST_QUICKONLY].todo = TRUE;
651       }
652       else if (strcmp(argv[index], "checksysinfo") == 0)
653       {
654          tab_test[TST_SYS_INFO].todo = TRUE;
655       }
656       else if (strcmp(argv[index], "checksysinfo2") == 0)
657       {
658          tab_test[TST_SYS_INFO2].todo = TRUE;
659       }
660       else if (strcmp(argv[index], "checksysinfo3") == 0)
661       {
662          tab_test[TST_SYS_INFO3].todo = TRUE;
663       }
664       else if (strcmp(argv[index], "checkchdir") == 0)
665       {
666          tab_test[TST_CHDIR].todo = TRUE;
667       }
668       else if (strcmp(argv[index], "checkreaddir") == 0)
669       {
670          tab_test[TST_READDIR].todo = TRUE;
671       }
672       else if (strcmp(argv[index], "checkopendir") == 0)
673       {
674          tab_test[TST_OPENDIR].todo = TRUE;
675       }
676       else if (strcmp(argv[index], "checkkill") == 0)
677       {
678          tab_test[TST_KILL].todo = TRUE;
679       }
680       else if (strcmp(argv[index], "checknoprocps") == 0)
681       {
682          tab_test[TST_NOPROCPS].todo = TRUE;
683       }
684       else if (strcmp(argv[index], "checkgetprio") == 0)
685       {
686          tab_test[TST_GETPRIO].todo = TRUE;
687       }
688       else if (strcmp(argv[index], "checkgetpgid") == 0)
689       {
690          tab_test[TST_GETPGID].todo = TRUE;
691       }
692       else if (strcmp(argv[index], "checkgetsid") == 0)
693       {
694          tab_test[TST_GETSID].todo = TRUE;
695       }
696       else if (strcmp(argv[index], "checkgetaffinity") == 0)
697       {
698          tab_test[TST_GETAFF].todo = TRUE;
699       }
700       else if (strcmp(argv[index], "checkgetparam") == 0)
701       {
702          tab_test[TST_GETPARM].todo = TRUE;
703       }
704       else if (strcmp(argv[index], "checkgetsched") == 0)
705       {
706          tab_test[TST_GETSCHED].todo = TRUE;
707       }
708       else if (strcmp(argv[index], "checkRRgetinterval") == 0)
709       {
710          tab_test[TST_RR_INT].todo = TRUE;
711       }
712       else
713       {
714          printf("Unknown argument\n") ; usage(argv[0]); exit(0);
715       }
716    }
717 
718 
719 }
720 
721 
main(int argc,char * argv[])722 int main (int argc, char *argv[])
723 {
724 int i;
725 
726    printf(header) ;
727    if(getuid() != 0){
728       die(unlog, "You must be root to run %s !", argv[0]) ;
729    }
730 
731    // Initialize the table of test to perform.
732    // ---------------------------------------
733    for (i=0 ; i<MAX_TESTNUM ; i++) {
734       tab_test[i].todo = FALSE;
735       tab_test[i].func = NULL;
736    }
737    tab_test[TST_PROC].func = checkproc;
738    tab_test[TST_CHDIR].func = checkchdir;
739    tab_test[TST_OPENDIR].func = checkopendir;
740    tab_test[TST_READDIR].func = checkreaddir;
741    tab_test[TST_GETPRIO].func = checkgetpriority;
742    tab_test[TST_GETPGID].func = checkgetpgid;
743    tab_test[TST_GETSID].func = checkgetsid;
744    tab_test[TST_GETAFF].func = checksched_getaffinity;
745    tab_test[TST_GETPARM].func = checksched_getparam;
746    tab_test[TST_GETSCHED].func = checksched_getscheduler;
747    tab_test[TST_RR_INT].func = checksched_rr_get_interval;
748    tab_test[TST_KILL].func = checkkill;
749    tab_test[TST_NOPROCPS].func = checkallnoprocps;
750    tab_test[TST_BRUTE].func = brute;
751    tab_test[TST_REVERSE].func = checkallreverse;
752    tab_test[TST_QUICKONLY].func = checkallquick;
753    tab_test[TST_SYS_INFO].func = checksysinfo;
754    tab_test[TST_SYS_INFO2].func = checksysinfo2;
755    tab_test[TST_SYS_INFO3].func = checksysinfo3;
756 
757 
758    // get the number max of processes on the system.
759    // ---------------------------------------------
760    get_max_pid(&maxpid);
761 
762    // analyze command line args
763    // -------------------------
764    if(argc < 2)
765    {
766       usage(argv[0]);
767       exit (1);
768    }
769    used_options[0] = 0 ;
770    parse_args(argc, argv) ;
771 
772    if (logtofile == 1)
773    {
774       unlog = init_log(logtofile, header, "unhide-linux") ;
775    }
776    msgln(unlog, 0, used_options) ;
777 
778    setpriority(PRIO_PROCESS,0,-20);  /* reduce risk from intermittent processes - may fail, dont care */
779 
780    mypid = getpid();
781 
782    // Execute required tests.
783    // ----------------------
784    for (i=0 ; i<MAX_TESTNUM ; i++) {
785       if ((tab_test[i].todo == TRUE) && (tab_test[i].func != NULL))
786       {
787          tab_test[i].func();
788       }
789    }
790 
791    if (logtofile == 1) {
792       close_log(unlog, "unhide-linux") ;
793    }
794    return found_HP;
795 }
796