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