From bca2c74fb4b818334c60e95a813952938c4d0e7b Mon Sep 17 00:00:00 2001 From: patrick-g2 Date: Sat, 23 Jan 2021 20:28:17 +0100 Subject: [PATCH] BUG FIXES - Correct all known bugs - Fix all warnings reported by cppcheck - Fix all warnings reported by gcc 8.4 -Wall ENHANCEMENTS - Add option -u to do unbuffered output. - Flush outputs in order to not block pipe if stdout is redirected. - Add a slightly human friendlier output triggered by -H option - Print start time and end time in log (and console if -H is given) - Add time to log file name GUI - Add a simple, quick and dirty python/Tkinter tools to generate and/or run unhide-linux and unhide-tcp command. MISCELLANOUS - Adapt checkoneport() to bogus/broken text output of "recent" version of ss tool (modified end of line). --- .gitignore | 4 + NEWS | 22 ++ build_all.sh | 5 + changelog | 79 ++++++ ps | 4 + ss | 21 ++ ss-ref | 34 +++ tar_list.txt | 8 +- unhide-linux-bruteforce.c | 6 +- unhide-linux-compound.c | 74 ++++-- unhide-linux-procfs.c | 48 ++-- unhide-linux-syscall.c | 274 ++++++++++++--------- unhide-linux.c | 86 ++++--- unhide-linux.h | 7 +- unhide-output.c | 25 +- unhide-output.h | 4 +- unhide-posix.c | 59 ++--- unhide-tcp-simple-check.c | 499 -------------------------------------- unhide-tcp.c | 37 ++- unhide-tcp.h | 6 + unhideGui.py | 436 +++++++++++++++++++++++++++++++++ unhide_rb.c | 51 ++-- 22 files changed, 1038 insertions(+), 751 deletions(-) create mode 100644 .gitignore create mode 100755 build_all.sh create mode 100755 ps create mode 100755 ss create mode 100644 ss-ref delete mode 100644 unhide-tcp-simple-check.c create mode 100755 unhideGui.py diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..04d168d --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +unhide-linux +unhide-posix +unhide-tcp +unhide_rb diff --git a/NEWS b/NEWS index 29aae87..ce310dd 100644 --- a/NEWS +++ b/NEWS @@ -1,3 +1,25 @@ +Changes since 20130526 : +********************** + +BUG FIXES + - Correct all known bugs + - Fix all warnings reported by cppcheck + - Fix all warnings reported by gcc 8.4 -Wall + +ENHANCEMENTS + - Add option -u to do unbuffered output. + - Flush outputs in order to not block pipe if stdout is redirected. + - Add a slightly human friendlier output triggered by -H option + - Print start time and end time in log (and console if -H is given) + - Add time to log file name + +GUI + - Add a simple, quick and dirty python/Tkinter tools to generate and/or run unhide-linux and unhide-tcp command. + +MISCELLANOUS + - Adapt checkoneport() to bogus/broken text output of "recent" version of ss tool (modified end of line). + + Changes since 20121229 : ********************** diff --git a/build_all.sh b/build_all.sh new file mode 100755 index 0000000..825b12f --- /dev/null +++ b/build_all.sh @@ -0,0 +1,5 @@ +#! /bin/sh + gcc -Wall -O2 --static -pthread unhide-linux*.c unhide-output.c -o unhide-linux + gcc -Wall -O2 --static unhide_rb.c -o unhide_rb + gcc -Wall -O2 --static unhide-tcp.c unhide-tcp-fast.c unhide-output.c -o unhide-tcp + gcc -Wall -O2 --static unhide-posix.c -o unhide-posix diff --git a/changelog b/changelog index eca8707..4dc150a 100644 --- a/changelog +++ b/changelog @@ -1,3 +1,82 @@ +2021-01 + unhide-linux-procfs.c + - Suppress -Wformat-overflow warning by GCC >= 8.0 in function checkreaddir(). + + unhide-posix.c + - Correct warning about strcpy and strcat in main(). + + unhide_rb.c + - Add missing braces in get_suspicious_pids() + - Correct warning about strcpy and strcat in main(). + - increase size of scratch string to avoid warning. + + unhide-linux.c, unhide-linux.h + - Add option to get a slightly human friendlier output. + - Use it ! + + unhide-linux-compound.c, unhide-linux-output.c, unhide-tcp.c + - Use option for human friendlier output. + + unhide-tcp.h + - Add definition of boolean values. + +2020-01 + unhide-output.c + - Protect msgln() from buffer overflow. + + unhide-tcp.c + - adapt checkoneport() to broken text output of "recent" version of ss tool (modified end of line). + + +2019-11 + UnhideGui.py: + - Add a simple, quick and dirty python/Tkinter tools to generate and run unhide-linux and unhide-tcp command. + + unhide-linux.c + - Correct a fd leak in get_max_pid() [SF ticket #7]. + - flush stdout after usage message, in order to not block pipe if stdout is redirected. + - flush stdout after header display, for the same reason. + - add option to disable buffering of stdout for subprocesses pipe-opened by unhide. + + unhide-linux.h + - add option and macro to disable buffering of stdout for subprocesses pipe-opened by unhide. + - Translate (historical) spanish function names in english. + + unhide-output.c + - Flush stdout after display of string, in order to not block pipe if stdout is redirected. + - Add time to log name (as RKHunter run each test separately and overwrite previous log file). + - Print start and end times to stdout if log is enabled + + unhide-posix.c + - Test the return values of the two fopen() and correct a fd leak in checkps() + + unhide-linux-compound.c + - Add "Not found" message in case no hiden process is found in checkallquick() and checkallreverse(). + - Add a missing line feed in the first message of checkallquick(). + - Add a missing line feed in the first message of checkallreverse(). + + unhide-linux-syscall.c + - Manage unbuffering stdout option in checksysinfoX() routines. + - Translate (historical) spanish variable names in english. + + unhide-linux-bruteforce.c + - Translate (historical) spanish function names in english. + + unhide-tcp.c + - flush stdout after usage message, in order to not block pipe if stdout is redirected. + - flush stdout after header display, for the same reason. + - Correct message for used options for netstat option + + unhide-rb.c + - flush stdout after fprintf() and fputs(). + - don't call fclose() if fopen() failed in get_suspicious_pids(). + + tar_list.txt + - remove unhide-tcp-simple-check.c which was include by mistake. + + unhide-linux-compound.c, unhide-linux-procfs.c, unhide-linux-syscall.c, unhide-linux.c + - Correct ccpcheck warning + 2013-05-26 unhide-posix.c - Transform 'ret' in global variable to avoid warnings diff --git a/ps b/ps new file mode 100755 index 0000000..acb783f --- /dev/null +++ b/ps @@ -0,0 +1,4 @@ +#! /bin/bash + +/bin/ps "$@" +echo 65535 my_false_proc diff --git a/ss b/ss new file mode 100755 index 0000000..e4a0f68 --- /dev/null +++ b/ss @@ -0,0 +1,21 @@ +#!/bin/sh + +set -e + +# echo "Le 1er paramètre est : $1" >&2 +# echo "Le 2ème paramètre est : $2" >&2 +# echo "Le 3ème paramètre est : $3" >&2 +# echo "Le 4ème paramètre est : $4" >&2 + +if [ 0 -eq 1 ] +then + /usr/bin/netstat $@ | grep -v 631 + exit +elif [ "$4" != ":631" ] +then + # appelle le véritable ss + /sbin/ss $@ +else + echo "Le 4ème paramètre est : $4" >&2 +fi + diff --git a/ss-ref b/ss-ref new file mode 100644 index 0000000..9976bf7 --- /dev/null +++ b/ss-ref @@ -0,0 +1,34 @@ +#! /bin/bash + +# sanity.sh -- a growing testsuite for unhide. +# +# Copyright (C) 2010 Patrick Gouin. +# +# 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 . +# +# Original Author: Patrick Gouin + + + +# Sequence d'aqppel par unhide : ss -tan sport = :%d +# affiche les paramètres + +echo "Le 1er paramètre est : $1" >&2 +echo "Le 2ème paramètre est : $2" >&2 +echo "Le 3ème paramètre est : $3" >&2 +echo "Le 4ème paramètre est : $4" >&2 + +# appelle le véritable ss + +/sbin/ss $@ diff --git a/tar_list.txt b/tar_list.txt index 8661b50..68a6898 100644 --- a/tar_list.txt +++ b/tar_list.txt @@ -26,7 +26,11 @@ unhide-output.h unhide-posix.c unhide_rb.c unhide-tcp.c -unhide-tcp-simple-check.c unhide-tcp-fast.c unhide-tcp.h - +unhideGui.py +LICENSE +build_all.sh +ps +ss +ss-ref diff --git a/unhide-linux-bruteforce.c b/unhide-linux-bruteforce.c index 7eee9e8..f4788c9 100644 --- a/unhide-linux-bruteforce.c +++ b/unhide-linux-bruteforce.c @@ -49,7 +49,7 @@ along with this program. If not, see . * Minimalist thread function for brute test. * Set tid with the pid of the created thread. */ -void *funcionThread (void *parametro) +void *functionThread (void *parametro) { tid = (pid_t) syscall (SYS_gettid); @@ -163,7 +163,7 @@ void brute(void) pthread_t idHilo; int error; - error = pthread_create (&idHilo, NULL, funcionThread, NULL); + error = pthread_create (&idHilo, NULL, functionThread, NULL); if (error != 0) { die(unlog, "Error: Cannot create thread ! Exiting."); @@ -187,7 +187,7 @@ void brute(void) pthread_t idHilo; int error; - error = pthread_create (&idHilo, NULL, funcionThread, NULL); + error = pthread_create (&idHilo, NULL, functionThread, NULL); if (error != 0) { die(unlog, "Error: Cannot create thread ! Exiting."); diff --git a/unhide-linux-compound.c b/unhide-linux-compound.c index 795efe4..53458cd 100644 --- a/unhide-linux-compound.c +++ b/unhide-linux-compound.c @@ -57,19 +57,21 @@ void checkallquick(void) struct timespec tp; struct sched_param param; cpu_set_t mask; + int test_number = 0 ; int found=0; + int hidenflag = 0; int found_killbefore=0; int found_killafter=0; - char directory[100], *pathpt; + char directory[100]; struct stat buffer; - int statusproc, statusdir, backtodir ; + int statusproc, statusdir ; char curdir[PATH_MAX] ; DIR *dir_fd; - msgln(unlog, 0, "[*]Searching for Hidden processes through comparison of results of system calls, proc, dir and ps\n") ; + msgln(unlog, 0, "[*]Searching for Hidden processes through comparison of results of system calls, proc, dir and ps") ; // get the path where Unhide is ran from. - if (NULL == (pathpt = getcwd(curdir, PATH_MAX))) + if (NULL == getcwd(curdir, PATH_MAX)) { warnln(verbose, unlog, "Can't get current directory, test aborted.") ; return; @@ -89,58 +91,69 @@ void checkallquick(void) found=0; found_killbefore=0; found_killafter=0; + test_number = 0 ; errno=0; ret = kill(syspids, 0); if (errno == 0) found_killbefore=1; errno= 0 ; + test_number += 1 ; ret = getpriority(PRIO_PROCESS, syspids); if (errno == 0) found++; errno= 0 ; + test_number += 1 ; ret = getpgid(syspids); if (errno == 0) found++; errno= 0 ; + test_number += 1 ; ret = getsid(syspids); if (errno == 0) found++; errno= 0 ; + test_number += 1 ; ret = sched_getaffinity(syspids, sizeof(cpu_set_t), &mask); if (ret == 0) found++; errno= 0 ; + test_number += 1 ; ret = sched_getparam(syspids, ¶m); if (errno == 0) found++; errno= 0 ; + test_number += 1 ; ret = sched_getscheduler(syspids); if (errno == 0) found++; errno=0; + test_number += 1 ; ret = sched_rr_get_interval(syspids, &tp); if (errno == 0) found++; sprintf(&directory[6],"%d",syspids); + test_number += 1 ; statusproc = stat(directory, &buffer) ; if (statusproc == 0) { found++; } + test_number += 1 ; statusdir = chdir(directory) ; if (statusdir == 0) { found++; - if (-1 == (backtodir = chdir(curdir))) + if (-1 == chdir(curdir)) { warnln(verbose, unlog, "Can't go back to unhide directory, test aborted.") ; return; } } + test_number += 1 ; dir_fd = opendir(directory) ; if (NULL != dir_fd) { @@ -151,6 +164,7 @@ void checkallquick(void) // Avoid checkps call if nobody sees anything if ((0 != found) || (0 != found_killbefore)) { + test_number += 1 ; if(checkps(syspids,PS_PROC | PS_THREAD)) { found++; @@ -161,14 +175,18 @@ void checkallquick(void) ret = kill(syspids, 0); if (errno == 0) found_killafter=1; + // printf("Nb_test : %d\n", test_number); + // fflush(stdout) ; /* these should all agree, except if a process went or came in the middle */ if (found_killbefore == found_killafter) { if ( ! ((found_killbefore == 0 && found == 0) || - (found_killbefore == 1 && found == 11)) ) + (found_killbefore == 1 && found == test_number)) ) { printbadpid(syspids); + hidenflag = 1 ; + } } /* else: unreliable */ else @@ -177,6 +195,17 @@ void checkallquick(void) warnln(verbose, unlog, "syscall comparison test skipped for PID %d.", syspids) ; } } + if (humanfriendly == TRUE) + { + if (hidenflag == 0) + { + msgln(unlog, 0, "No hidden PID found\n") ; + } + else + { + msgln(unlog, 0, "") ; + } + } } /* @@ -191,9 +220,10 @@ void checkallreverse(void) struct timespec tp; struct sched_param param; cpu_set_t mask; - int not_seen=0; - int found_killbefore=0; - int found_killafter=0; + int not_seen = 0; + int hidenflag = 0; + int found_killbefore = 0; + int found_killafter = 0; FILE *fich_tmp; char command[50]; char read_line[1024]; @@ -201,11 +231,12 @@ void checkallreverse(void) int index; char directory[100]; struct stat buffer; - int statusproc, statusdir, backtodir; - char curdir[PATH_MAX], *pathpt ; + // int statusproc, statusdir, backtodir; + int statusproc, statusdir; + char curdir[PATH_MAX] ; DIR *dir_fd; - msgln(unlog, 0, "[*]Searching for Fake processes by verifying that all threads seen by ps are also seen by others\n") ; + msgln(unlog, 0, "[*]Searching for Fake processes by verifying that all threads seen by ps are also seen by others") ; sprintf(command,REVERSE) ; @@ -216,7 +247,7 @@ void checkallreverse(void) return; } // get the path where Unhide is ran from. - if (NULL == (pathpt = getcwd(curdir, PATH_MAX))) + if (NULL == getcwd(curdir, PATH_MAX)) { warnln(verbose, unlog, "Can't get current directory, test aborted") ; return; @@ -279,7 +310,7 @@ void checkallreverse(void) } else { - if (-1 == (backtodir = chdir(curdir))) + if (-1 == chdir(curdir)) { warnln(verbose, unlog, "Can't go back to unhide directory, test aborted") ; return; @@ -341,16 +372,18 @@ void checkallreverse(void) // printbadpid should NOT be used here : we are looking for faked process msgln(unlog, 0, "Found FAKE PID: %i\tCommand = %s not seen by %d sys fonc", syspids, curline, not_seen) ; found_HP = 1; + hidenflag = 1 ; } } } - else + else // even kill() doesn't see this process. { if (NULL == strstr(curline, REVERSE)) // avoid our spawned ps { // printbadpid should NOT be used here : we are looking for faked process msgln(unlog, 0, "Found FAKE PID: %i\tCommand = %s not seen by %d sys fonc", syspids, curline, not_seen + 2) ; found_HP = 1; + hidenflag = 1 ; } } } /* else: unreliable */ @@ -360,6 +393,17 @@ void checkallreverse(void) warnln(verbose, unlog, "reverse test skipped for PID %d", syspids) ; } } + if (humanfriendly == TRUE) + { + if (hidenflag == 0) + { + msgln(unlog, 0, "No FAKE PID found\n") ; + } + else + { + msgln(unlog, 0, "") ; + } + } if (fich_tmp != NULL) pclose(fich_tmp); diff --git a/unhide-linux-procfs.c b/unhide-linux-procfs.c index d2d7eaf..7768620 100644 --- a/unhide-linux-procfs.c +++ b/unhide-linux-procfs.c @@ -98,8 +98,8 @@ void checkchdir(void) { int procpids ; - int statusdir, backtodir; - char curdir[PATH_MAX], *pathpt ; + int statusdir; + char curdir[PATH_MAX] ; char directory[100] ; // char scratch[PATH_MAX] ; // DEBUG // int count = 0; //DEBUG @@ -107,7 +107,7 @@ void checkchdir(void) msgln(unlog, 0, "[*]Searching for Hidden processes through /proc chdir scanning\n") ; // get the path where Unhide is ran from. - if (NULL == (pathpt = getcwd(curdir, PATH_MAX))) + if (NULL == getcwd(curdir, PATH_MAX)) { warnln(verbose, unlog, "Can't get current directory, test aborted") ; return; @@ -137,8 +137,6 @@ void checkchdir(void) int found_tgid = FALSE; char line[128] ; char* tmp_pids = line; - char* end_pid; - char new_directory[100]; // printf("directory = '%s'\n", directory); // DEBUG // getcwd(scratch, PATH_MAX); // DEBUG @@ -163,6 +161,8 @@ void checkchdir(void) if (TRUE == found_tgid) { + char* end_pid; + tmp_pids = line + 5; while( ((*tmp_pids == ' ') || (*tmp_pids == '\t')) && (tmp_pids <= line+127)) { @@ -175,13 +175,15 @@ void checkchdir(void) end_pid++; } *end_pid = 0; // remove \n -// if the number of threads is < to about 40 % of the number of processes, -// the next "optimising" test actually produce a slower executable. -// if(procpids != atoi(tmp_pids)) + // if the number of threads is < to about 40 % of the number of processes, + // the next "optimising" test actually produce a slower executable. + // if(procpids != atoi(tmp_pids)) { // if the thread isn't the master thread (process) -// count++; // DEBUG + char new_directory[100]; + sprintf(new_directory,"/proc/%s/task/%d", tmp_pids, procpids) ; -// printf("new_dir = %s\n", new_directory); // DEBUG + // count++; // DEBUG + // printf("new_dir = %s\n", new_directory); // DEBUG statusdir = chdir(new_directory) ; if (statusdir != 0) { @@ -199,7 +201,7 @@ void checkchdir(void) } // unlock the proc directory so it can disappear if it's a transitory process - if (-1 == (backtodir = chdir(curdir))) + if (-1 == chdir(curdir)) { warnln(verbose, unlog, "Can't go back to unhide directory, test aborted") ; return; @@ -220,7 +222,7 @@ void checkchdir(void) printbadpid(procpids); } // go back to our path - if (-1 == (backtodir = chdir(curdir))) + if (-1 == chdir(curdir)) { warnln(verbose, unlog, "Can't go back to unhide directory, test aborted") ; return; @@ -266,9 +268,6 @@ void checkopendir(void) int found_tgid = FALSE; char line[128] ; char* tmp_pids = line; - char* end_pid; - char new_directory[100] ; - DIR* statdir; // printf("directory = '%s'\n", directory); // DEBUG // getcwd(scratch, PATH_MAX); // DEBUG @@ -294,6 +293,8 @@ void checkopendir(void) if (TRUE == found_tgid) { + char* end_pid; + tmp_pids = line + 5; while( ((*tmp_pids == ' ') || (*tmp_pids == '\t')) && (tmp_pids <= line+127)) { @@ -310,10 +311,13 @@ void checkopendir(void) // the next "optimising" test actually produce a slower executable. // if(procpids != atoi(tmp_pids)) { // if the thread isn't the master thread (process) -// count++; // DEBUG + char new_directory[100] ; + DIR* statdir; + sprintf(new_directory,"/proc/%s/task/%d", tmp_pids, procpids) ; -// printf("new_dir = %s\n", new_directory); // DEBUG -// errno = 0; + // count++; // DEBUG + // printf("new_dir = %s\n", new_directory); // DEBUG + // errno = 0; statdir = opendir(new_directory) ; if (NULL == statdir) { @@ -392,7 +396,15 @@ void checkreaddir(void) } // sprintf(currentproc, "%d", directory); + // Warning here as gcc can't know that directory (task number) contains far less than 94 char. +#ifdef __GNUC__ + #pragma GCC diagnostic push + #pragma GCC diagnostic ignored "-Wformat-overflow=" +#endif sprintf(&task[6], "%s/task", directory) ; +#ifdef __GNUC__ + #pragma GCC diagnostic pop +#endif // printf("task : %s", task) ; // DEBUG taskdir = opendir(task); if (NULL == taskdir) diff --git a/unhide-linux-syscall.c b/unhide-linux-syscall.c index 0493c77..d6d7bff 100644 --- a/unhide-linux-syscall.c +++ b/unhide-linux-syscall.c @@ -61,7 +61,7 @@ void checkgetpriority(void) int which = PRIO_PROCESS; // int ret; - errno= 0 ; + errno = 0 ; // avoid ourselves if (syspids == mypid) { @@ -79,7 +79,7 @@ void checkgetpriority(void) continue; } - errno=0; + errno = 0; ret = getpriority(which, syspids); if ( errno != 0) { @@ -101,7 +101,7 @@ void checkgetpgid(void) { // int ret; - errno= 0 ; + errno = 0 ; // avoid ourselves if (syspids == mypid) { @@ -119,7 +119,7 @@ void checkgetpgid(void) continue; } - errno=0; + errno = 0; ret = getpgid(syspids); if ( errno != 0 ) { @@ -137,11 +137,11 @@ void checkgetsid(void) int syspids ; msgln(unlog, 0, "[*]Searching for Hidden processes through getsid() scanning\n") ; - for ( syspids = 1; syspids <= maxpid; syspids = syspids +1 ) + for ( syspids = 1; syspids <= maxpid; syspids = syspids + 1 ) { // int ret; - errno= 0 ; + errno = 0 ; // avoid ourselves if (syspids == mypid) { @@ -157,7 +157,7 @@ void checkgetsid(void) { continue; } - errno=0; + errno = 0; ret = getsid(syspids); if ( errno != 0) { @@ -177,11 +177,11 @@ void checksched_getaffinity(void) cpu_set_t mask; msgln(unlog, 0, "[*]Searching for Hidden processes through sched_getaffinity() scanning\n") ; - for ( syspids = 1; syspids <= maxpid; syspids = syspids +1 ) + for ( syspids = 1; syspids <= maxpid; syspids = syspids + 1 ) { // int ret; - errno= 0 ; + errno = 0 ; // avoid ourselves if (syspids == mypid) { @@ -197,7 +197,7 @@ void checksched_getaffinity(void) { continue; } - errno=0; + errno = 0; ret = sched_getaffinity(syspids, sizeof(cpu_set_t), &mask); if (errno != 0) { @@ -217,11 +217,11 @@ void checksched_getparam(void) struct sched_param param; msgln(unlog, 0, "[*]Searching for Hidden processes through sched_getparam() scanning\n") ; - for ( syspids = 1; syspids <= maxpid; syspids = syspids +1 ) + for ( syspids = 1; syspids <= maxpid; syspids = syspids + 1 ) { // int ret; - errno= 0 ; + errno = 0 ; // avoid ourselves if (syspids == mypid) { continue; @@ -238,7 +238,7 @@ void checksched_getparam(void) continue; } - errno=0; + errno = 0; ret = sched_getparam(syspids, ¶m); if ( errno != 0) { @@ -256,11 +256,11 @@ void checksched_getscheduler(void) int syspids ; msgln(unlog, 0, "[*]Searching for Hidden processes through sched_getscheduler() scanning\n") ; - for ( syspids = 1; syspids <= maxpid; syspids = syspids +1 ) + for ( syspids = 1; syspids <= maxpid; syspids = syspids + 1 ) { // int ret; - errno= 0 ; + errno = 0 ; // avoid ourselves if (syspids == mypid) { continue; @@ -277,7 +277,7 @@ void checksched_getscheduler(void) continue; } - errno=0; + errno = 0; ret = sched_getscheduler(syspids); if ( errno != 0) { @@ -297,11 +297,11 @@ void checksched_rr_get_interval(void) struct timespec tp; msgln(unlog, 0, "[*]Searching for Hidden processes through sched_rr_get_interval() scanning\n") ; - for ( syspids = 1; syspids <= maxpid; syspids = syspids +1 ) + for ( syspids = 1; syspids <= maxpid; syspids = syspids + 1 ) { // int ret; - errno= 0 ; + errno = 0 ; // avoid ourselves if (syspids == mypid) { @@ -319,7 +319,7 @@ void checksched_rr_get_interval(void) continue; } - errno=0; + errno = 0; ret = sched_rr_get_interval(syspids, &tp); if ( errno != 0) { @@ -341,7 +341,7 @@ void checkkill(void) { // int ret; - errno= 0 ; + errno = 0 ; // avoid ourselves if (syspids == mypid) { @@ -359,7 +359,7 @@ void checkkill(void) continue; } - errno= 0 ; + errno = 0 ; ret = kill(syspids, 0); if ( errno != 0) { @@ -380,9 +380,9 @@ void checkallnoprocps(void) struct timespec tp; struct sched_param param; cpu_set_t mask; - int found=0; - int found_killbefore=0; - int found_killafter=0; + int found = 0; + int found_killbefore = 0; + int found_killafter = 0; msgln(unlog, 0, "[*]Searching for Hidden processes through comparison of results of system calls\n") ; for ( syspids = 1; syspids <= maxpid; syspids++ ) @@ -393,45 +393,45 @@ void checkallnoprocps(void) continue; } - found=0; - found_killbefore=0; - found_killafter=0; + found = 0; + found_killbefore = 0; + found_killafter = 0; - errno=0; + errno = 0; ret = kill(syspids, 0); - if (errno == 0) found_killbefore=1; + if (errno == 0) found_killbefore = 1; - errno= 0 ; + errno = 0 ; ret = getpriority(PRIO_PROCESS, syspids); if (errno == 0) found++; - errno= 0 ; + errno = 0 ; ret = getpgid(syspids); if (errno == 0) found++; - errno= 0 ; + errno = 0 ; ret = getsid(syspids); if (errno == 0) found++; - errno= 0 ; + errno = 0 ; ret = sched_getaffinity(syspids, sizeof(cpu_set_t), &mask); if (errno == 0) found++; - errno= 0 ; + errno = 0 ; ret = sched_getparam(syspids, ¶m); if (errno == 0) found++; - errno= 0 ; + errno = 0 ; ret = sched_getscheduler(syspids); if (errno == 0) found++; - errno=0; + errno = 0; ret = sched_rr_get_interval(syspids, &tp); if (errno == 0) found++; - errno=0; + errno = 0; ret = kill(syspids, 0); - if (errno == 0) found_killafter=1; + if (errno == 0) found_killafter = 1; /* these should all agree, except if a process went or came in the middle */ @@ -451,45 +451,62 @@ void checkallnoprocps(void) } } + +void genpscmd(char *cmd) +{ + if (unbufferedstdout == TRUE) + { + strcpy(cmd, NO_BUF_PIPE SYS_COMMAND) ; + } + else + { + strcpy(cmd, SYS_COMMAND) ; + } + printf("Commande : %s\n", cmd) ; +} + void checksysinfo(void) { struct sysinfo info; - int contador=0; - int resultado_antes=0; - int resultado_despues=0; - int resultado = 0; - int ocultos=0; + int procnumber = 0; + int initial_result = 0; + int final_result = 0; + int result = 0; char buffer[500]; + char command[60]; - FILE *fich_proceso ; + FILE *ps_fh ; - msgln(unlog, 0, "[*]Searching for Hidden processes through sysinfo() scanning\n") ; buffer[499] = '\0'; sysinfo(&info); - resultado = resultado_antes=info.procs; + result = initial_result = info.procs; + + genpscmd(command) ; - fich_proceso=popen (SYS_COMMAND, "r") ; - if (fich_proceso == NULL) + msgln(unlog, 0, "[*]Searching for Hidden processes through sysinfo() scanning (1st variant)\n") ; + + ps_fh = popen (command, "r") ; + if (ps_fh == NULL) { warnln(verbose, unlog, "Couldn't run command: %s, test aborted", SYS_COMMAND) ; return; } - while (NULL != fgets(buffer, 499, fich_proceso)) + while (NULL != fgets(buffer, 499, ps_fh)) { - contador++; + procnumber++; if(verbose) { sysinfo(&info); - if (resultado != info.procs) + if (result != info.procs) { - msgln(unlog, 1, "\tWARNING : info.procs changed during test : %d (was %d)",info.procs,resultado) ; - resultado = info.procs; + msgln(unlog, 1, "\tWARNING : info.procs changed during test : %d (was %d)",info.procs,result) ; + result = info.procs; } - if (verbose >=2) + if (verbose >= 2) { buffer[strlen(buffer)-1] = 0; // get rid of \n snprintf(scratch, 1000, "\"%s\"",buffer) ; @@ -497,24 +514,25 @@ void checksysinfo(void) } } } - pclose(fich_proceso); + pclose(ps_fh); sysinfo(&info); - resultado_despues=info.procs; + final_result = info.procs; if(verbose >= 1) { - if (resultado != resultado_despues) { - msgln(unlog, 1, "\tWARNING : info.procs changed during test : %d (was %d)",resultado_despues,resultado) ; + if (result != final_result) { + msgln(unlog, 1, "\tWARNING : info.procs changed during test : %d (was %d)",final_result,result) ; } } - if (resultado_antes == resultado_despues) /* otherwise intermittent activity.. */ + if (initial_result == final_result) /* otherwise intermittent activity.. */ { + int hidennumber = 0; // We add one as ps sees itself and not sysinfo. - ocultos=resultado_despues + 1 - contador ; + hidennumber = final_result + 1 - procnumber ; - if (ocultos != 0) { - msgln(unlog, 1, "%i HIDDEN Processes Found\tsysinfo.procs reports %d processes and ps sees %d processes",abs(ocultos), resultado_despues,contador-1) ; + if (hidennumber != 0) { + msgln(unlog, 1, "%i HIDDEN Processes Found\tsysinfo.procs reports %d processes and ps sees %d processes",abs(hidennumber), final_result,procnumber-1) ; found_HP = 1; } } @@ -536,41 +554,44 @@ void checksysinfo2() { struct sysinfo info; - int contador=0; - int resultado_antes=0; - int resultado_despues=0; - int resultado = 0; - int ocultos=0; + int procnumber = 0; + int initial_result = 0; + int final_result = 0; + int result = 0; char buffer[500]; + char command[60]; - FILE *fich_proceso ; + FILE *ps_fh ; - msgln(unlog, 0, "[*]Searching for Hidden processes through sysinfo() scanning\n") ; buffer[499] = '\0'; - fich_proceso=popen (SYS_COMMAND, "r") ; - if (fich_proceso == NULL) + genpscmd(command) ; + + msgln(unlog, 0, "[*]Searching for Hidden processes through sysinfo() scanning (2nd variant)\n") ; + + ps_fh = popen (command, "r") ; + if (ps_fh == NULL) { warnln(verbose, unlog, "Couldn't run command: %s, test aborted", SYS_COMMAND) ; return; } sysinfo(&info); - resultado = resultado_antes = info.procs; + result = initial_result = info.procs; - while (NULL != fgets(buffer, 499, fich_proceso)) + while (NULL != fgets(buffer, 499, ps_fh)) { - contador++; + procnumber++; if(verbose) { sysinfo(&info); // DEBUG - if (resultado != info.procs) + if (result != info.procs) { // DEBUG - msgln(unlog, 1, "\tWARNING : info.procs changed during test : %d (was %d)",info.procs,resultado) ; - resultado = info.procs; // DEBUG + msgln(unlog, 1, "\tWARNING : info.procs changed during test : %d (was %d)",info.procs,result) ; + result = info.procs; // DEBUG } - if (verbose >=2) + if (verbose >= 2) { buffer[strlen(buffer)-1] = 0; // get rid of \n snprintf(scratch, 1000, "\"%s\"",buffer) ; @@ -580,23 +601,25 @@ void checksysinfo2() } sysinfo(&info); - resultado_despues=info.procs; + final_result = info.procs; if(verbose >= 1) { - if (resultado != resultado_despues) + if (result != final_result) { - msgln(unlog, 1, "\tWARNING : info.procs changed during test : %d (was %d)", resultado_despues, resultado) ; + msgln(unlog, 1, "\tWARNING : info.procs changed during test : %d (was %d)", final_result, result) ; } } - pclose(fich_proceso); + pclose(ps_fh); - if (resultado_antes == resultado_despues) /* otherwise intermittent activity.. */ + if (initial_result == final_result) /* otherwise intermittent activity.. */ { - ocultos=resultado_despues - contador; - if (ocultos != 0) + int hidennumber = 0; + + hidennumber = final_result - procnumber; + if (hidennumber != 0) { - msgln(unlog, 1, "%i HIDDEN Processes Found\tsysinfo.procs reports %d processes and ps sees %d processes", abs(ocultos), resultado_despues,contador) ; + msgln(unlog, 1, "%i HIDDEN Processes Found\tsysinfo.procs reports %d processes and ps sees %d processes", abs(hidennumber), final_result,procnumber) ; found_HP = 1; } } @@ -617,39 +640,44 @@ void checksysinfo3() { struct sysinfo info; - int contador=0; - int resultado_antes=0; - int resultado_despues=0; - int ocultos=0; char buffer[500]; + char command[60]; - FILE *fich_proceso ; + FILE *ps_fh ; - msgln(unlog, 0, "[*]Searching for Hidden processes through sysinfo() scanning\n") ; buffer[499] = '\0'; - if (NULL != (fich_proceso=popen (SYS_COMMAND, "r"))) + genpscmd(command) ; + + msgln(unlog, 0, "[*]Searching for Hidden processes through sysinfo() scanning (3rd variant)\n") ; + + if (NULL != (ps_fh = popen (command, "r"))) { + int procnumber = 0; + int initial_result = 0; + int final_result = 0; sysinfo(&info); - resultado_antes = info.procs; + initial_result = info.procs; - while (NULL != fgets(buffer, 499, fich_proceso)) + while (NULL != fgets(buffer, 499, ps_fh)) { - contador++; + procnumber++; } sysinfo(&info); - resultado_despues=info.procs; - pclose(fich_proceso); + final_result = info.procs; + pclose(ps_fh); - if (resultado_antes == resultado_despues) /* otherwise intermittent activity.. */ + if (initial_result == final_result) /* otherwise intermittent activity.. */ { - ocultos=resultado_despues - contador; - if (ocultos != 0) + int hidennumber = 0; + + hidennumber = final_result - procnumber; + if (hidennumber != 0) { - msgln(unlog, 1, "%i HIDDEN Processes Found\tsysinfo.procs reports %d processes and ps sees %d processes", abs(ocultos), resultado_despues,contador) ; + msgln(unlog, 1, "%i HIDDEN Processes Found\tsysinfo.procs reports %d processes and ps sees %d processes", abs(hidennumber), final_result,procnumber) ; found_HP = 1; } } @@ -682,29 +710,35 @@ void checksysinfo4() { struct sysinfo info; - int contador=0; - int resultado_antes=0; - int resultado_despues=0; - int ocultos=0; // char buffer[500]; ssize_t read_size, avail ; char *buf_pt ; - int fd ; + char command[60]; + - FILE *fich_proceso ; + FILE *ps_fh ; - msgln(unlog, 0, "[*]Searching for Hidden processes through sysinfo() scanning\n") ; // buffer[499] = '\0'; buf_pt = big_buffer ; read_size = 0 ; avail = 32768*6 ; - if (NULL != (fich_proceso=popen (SYS_COMMAND, "r"))) + + genpscmd(command) ; + + msgln(unlog, 0, "[*]Searching for Hidden processes through sysinfo() scanning (4th variant)\n") ; + + if (NULL != (ps_fh = popen (command, "r"))) { - fd = fileno(fich_proceso) ; + int procnumber = 0; + int initial_result = 0; + int final_result = 0; + int fd ; + + fd = fileno(ps_fh) ; sysinfo(&info); - resultado_antes = info.procs; + initial_result = info.procs; while ((read_size = read(fd, buf_pt, avail))) { @@ -716,23 +750,25 @@ void checksysinfo4() *buf_pt = 0 ; sysinfo(&info); - resultado_despues=info.procs; - pclose(fich_proceso); + final_result = info.procs; + pclose(ps_fh); buf_pt = big_buffer ; while (*buf_pt) { if (*buf_pt == '\n') - contador++ ; + procnumber++ ; buf_pt++ ; } - if (resultado_antes == resultado_despues) /* otherwise intermittent activity.. */ + if (initial_result == final_result) /* otherwise intermittent activity.. */ { - ocultos=resultado_despues - contador; - if (ocultos != 0) + int hidennumber = 0; + + hidennumber = final_result - procnumber; + if (hidennumber != 0) { - msgln(unlog, 1, "%i HIDDEN Processes Found\tsysinfo.procs reports %d processes and ps sees %d processes", abs(ocultos), resultado_despues,contador) ; + msgln(unlog, 1, "%i HIDDEN Processes Found\tsysinfo.procs reports %d processes and ps sees %d processes", abs(hidennumber), final_result,procnumber) ; found_HP = 1; } } diff --git a/unhide-linux.c b/unhide-linux.c index 1aca7dd..c2aa860 100644 --- a/unhide-linux.c +++ b/unhide-linux.c @@ -65,10 +65,12 @@ int tid ; pid_t mypid ; // options -int verbose = 0; -int morecheck = FALSE; -int RTsys = FALSE; -int brutesimplecheck = TRUE; +int verbose = 0 ; +int morecheck = FALSE ; +int RTsys = FALSE ; +int brutesimplecheck = TRUE ; +int unbufferedstdout = FALSE ; +int humanfriendly = FALSE ; // Found hidden proccess flag int found_HP = 0; @@ -98,14 +100,10 @@ void get_max_pid(int* newmaxpid) if(!fd) { warnln(1, unlog, "Cannot read current maximum PID. Using default value %d", * newmaxpid) ; - return; } - - - if((fscanf(fd, "%d", &tmppid) != 1) || tmppid < 1) + else 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 { @@ -127,7 +125,6 @@ int checkps(int tmppid, int checks) char command[60]; - FILE *fich_tmp ; // printf("in --> checkps\n"); // DEBUG @@ -136,6 +133,8 @@ int checkps(int tmppid, int checks) if (PS_PROC == (checks & PS_PROC)) { + FILE *fich_tmp ; + sprintf(command,COMMAND,tmppid) ; fich_tmp=popen (command, "r") ; @@ -278,7 +277,7 @@ void printbadpid (int tmppid) struct stat buffer; FILE *cmdfile ; char cmdcont[1000], fmtstart[128]; - int cmdok = 0, cmdok2 = 0; + int cmdok = 0 ; found_HP = 1; sprintf(fmtstart,"Found HIDDEN PID: %i", tmppid) ; @@ -308,16 +307,17 @@ void printbadpid (int tmppid) } { // 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 + // printf("%s",cmd) ; //DEBUG + // printf("\tstatuscmd : %d\n",statuscmd) ; //DEBUG if (statuscmd == 0) { + ssize_t length ; + length = readlink(cmd, cmdcont, 1000) ; -// printf("\tLength : %0d\n",(int)length) ; //DEBUG + // printf("\tLength : %0d\n",(int)length) ; //DEBUG if (-1 != length) { cmdcont[length] = 0; // terminate the string @@ -343,11 +343,15 @@ void printbadpid (int tmppid) cmdfile=fopen (cmd, "r") ; if (cmdfile != NULL) { -// printf("\tCmdFile : %s\n",cmd) ; //DEBUG + int cmdok2 = 0 ; + + // printf("\tCmdFile : %s\n",cmd) ; //DEBUG while ((NULL != fgets (cmdcont, 1000, cmdfile)) && 0 == cmdok2) { - cmdok2++; -// printf("\tLastChar : %x\n",cmdcont[strlen(cmdcont)]) ; //DEBUG + // EXPLAIN-ME : why do we use a while and then read only one line ? + cmdok2++; + + // printf("\tLastChar : %x\n",cmdcont[strlen(cmdcont)]) ; //DEBUG if (cmdcont[strlen(cmdcont)-1] == '\n') { cmdcont[strlen(cmdcont)-1] = 0 ; // get rid of newline @@ -377,14 +381,15 @@ void printbadpid (int tmppid) // 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) { + FILE *fich_tmp ; + sprintf(cmd,"cat /proc/%i/environ | tr \"\\0\" \"\\n\" | grep -w 'USER'",tmppid) ; - // printf(cmd) ; + // printf(cmd) ; fich_tmp=popen (cmd, "r") ; if (fich_tmp == NULL) { @@ -403,7 +408,7 @@ void printbadpid (int tmppid) pclose(fich_tmp); sprintf(cmd,"cat /proc/%i/environ | tr \"\\0\" \"\\n\" | grep -w 'PWD'",tmppid) ; - // printf(cmd) ; + // printf(cmd) ; fich_tmp=popen (cmd, "r") ; if (fich_tmp == NULL) { @@ -444,6 +449,7 @@ void usage(char * command) 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(" -u inhibit stdout buffering of subprocesses (needs stdbuf command)\n\n"); printf("Test_list :\n"); printf(" Test_list is one or more of the following\n"); printf(" Standard tests :\n"); @@ -474,6 +480,7 @@ void usage(char * command) printf(" checksysinfo\n"); printf(" checksysinfo2\n"); printf(" checksysinfo3\n"); + fflush(stdout) ; } /* @@ -496,6 +503,7 @@ void parse_args(int argc, char **argv) {"verbose", no_argument, 0, 'v'}, {"help", no_argument, 0, 'h'}, {"version", no_argument, 0, 'V'}, + {"human-frienly", no_argument, 0, 'H'}, {0, 0, 0, 0} }; @@ -504,7 +512,7 @@ void parse_args(int argc, char **argv) /* getopt_long stores the option index here. */ int option_index = 0; - c = getopt_long (argc, argv, "dformhvV", + c = getopt_long (argc, argv, "dformhvVHu", long_options, &option_index); /* Detect the end of the options. */ @@ -526,7 +534,7 @@ void parse_args(int argc, char **argv) printf ("\n"); break ; case 'd' : - brutesimplecheck = FALSE; + brutesimplecheck = FALSE ; break ; case 'h' : usage(argv[0]) ; @@ -539,11 +547,15 @@ void parse_args(int argc, char **argv) logtofile = 1 ; break ; case 'm' : - morecheck = TRUE; - verbose = TRUE; + morecheck = TRUE ; + verbose = TRUE ; break ; case 'r' : - RTsys = TRUE; + RTsys = TRUE ; + break ; + + case 'u' : + unbufferedstdout = TRUE ; break ; case 'v' : verbose++ ; ; @@ -551,6 +563,9 @@ void parse_args(int argc, char **argv) case 'V' : exit (0) ; break ; + case 'H' : + humanfriendly = TRUE ; + break ; case '?' : // invalid option exit (2) ; break ; @@ -573,6 +588,8 @@ void parse_args(int argc, char **argv) strncat(used_options, "RTsys ", 1000-1-strlen(used_options)); if (logtofile) strncat(used_options, "logtofile ", 1000-1-strlen(used_options)); + if (unbufferedstdout) + strncat(used_options, "unbufferedstdout ", 1000-1-strlen(used_options)); // Process list of tests to do for (index = optind; index < argc; index++) @@ -712,6 +729,7 @@ void parse_args(int argc, char **argv) else { printf("Unknown argument\n") ; usage(argv[0]); exit(0); + fflush(stdout) ; } } @@ -723,7 +741,14 @@ int main (int argc, char *argv[]) { int i; + // try to unbufferd pipe : + // setvbuf(stdout, NULL, _IONBF, BUFSIZ); + // setvbuf(stdout, (char *)NULL, _IONBF, 0); + printf(header) ; + fflush(stdout) ; + // fflush(stdout) ; + if(getuid() != 0){ die(unlog, "You must be root to run %s !", argv[0]) ; } @@ -771,7 +796,7 @@ int i; if (logtofile == 1) { - unlog = init_log(logtofile, header, "unhide-linux") ; + unlog = init_log(logtofile, header, "unhide-linux", humanfriendly) ; } msgln(unlog, 0, used_options) ; @@ -789,7 +814,12 @@ int i; } if (logtofile == 1) { - close_log(unlog, "unhide-linux") ; + close_log(unlog, "unhide-linux", humanfriendly) ; + } + if (humanfriendly == TRUE) + { + puts("Done !\n") ; } + fflush(stdout) ; return found_HP; } diff --git a/unhide-linux.h b/unhide-linux.h index ece3f23..def4103 100644 --- a/unhide-linux.h +++ b/unhide-linux.h @@ -36,6 +36,9 @@ along with this program. If not, see . // an extra process/thread #define REVERSE "ps --no-header -eL o lwp,cmd" +// Avoid buffering stdout when piped. +#define NO_BUF_PIPE "stdbuf -i0 -o0 -e0 " + // Masks for the checks to do in checkps // ===================================== #define PS_PROC 0x00000001 @@ -104,6 +107,8 @@ extern int verbose ; extern int morecheck ; extern int RTsys ; extern int brutesimplecheck ; +extern int unbufferedstdout ; +extern int humanfriendly ; // Found hidden proccess flag extern int found_HP ; @@ -123,7 +128,7 @@ extern struct tab_test_t tab_test[MAX_TESTNUM]; // prototypes // ========== // unhide-linux-bruteforce.c -extern void *funcionThread (void *parametro) ; +extern void *functionThread (void *parametro) ; extern void brute(void) ; // unhide-linux.c diff --git a/unhide-output.c b/unhide-output.c index 284a5e6..e9702db 100644 --- a/unhide-output.c +++ b/unhide-output.c @@ -40,6 +40,8 @@ void vfmsg(FILE * unlog, FILE* fp, const char* fmt, va_list ap) vsnprintf(buf, BUFSIZ, fmt, ap); fputs(buf, fp); + fflush(fp) ; + // fputs(buf, stderr); if (NULL != unlog) fputs(buf, unlog); @@ -56,13 +58,14 @@ void msgln(FILE * unlog, int indent, const char* fmt, ...) if(1 == indent) { - strncpy(buf, "\t", BUFSIZ); - strncat(buf, fmt, BUFSIZ-strlen(buf)); + strncpy(buf, "\t", BUFSIZ-1); + strncat(buf, fmt, BUFSIZ-strlen(buf)-1); } else { - strncpy(buf, fmt, BUFSIZ); + strncpy(buf, fmt, BUFSIZ-1); } + buf[BUFSIZ-1] = 0 ; strncat(buf, "\n", BUFSIZ-1-strlen(buf)); va_start(ap, fmt); @@ -135,7 +138,7 @@ void die(FILE * unlog, const char* fmt, ...) /* * Initialize and write a header to the log file. */ -FILE *init_log(int logtofile, const char *header, const char *basename) +FILE *init_log(int logtofile, const char *header, const char *basename, int hfriend) { FILE *fh ; char filename[PATH_MAX] ; @@ -150,7 +153,7 @@ FILE *init_log(int logtofile, const char *header, const char *basename) scantime = time(NULL); tmPtr = localtime(&scantime); - sprintf(filename, "%s_%4d-%02d-%02d.%s", basename, tmPtr->tm_year+1900, tmPtr->tm_mon + 1, tmPtr->tm_mday, "log" ); + sprintf(filename, "%s_%4d-%02d-%02d_%02dh%02dm%02ds.%s", basename, tmPtr->tm_year+1900, tmPtr->tm_mon + 1, tmPtr->tm_mday, tmPtr->tm_hour, tmPtr->tm_min, tmPtr->tm_sec, "log" ); fh = fopen(filename, "w"); @@ -166,11 +169,16 @@ FILE *init_log(int logtofile, const char *header, const char *basename) strftime( cad, 80, "%H:%M:%S, %F", tmPtr ); fprintf(fh, "\n%s scan starting at: %s\n", basename, cad) ; + if (hfriend != 0) + { + printf("\n%s scan starting at: %s\n", basename, cad) ; + } + fflush(stdout) ; return(fh); } /* Write a footer and close the log file. */ -void close_log(FILE *fh, const char *basename) +void close_log(FILE *fh, const char *basename, int hfriend) { if (NULL == fh) @@ -187,6 +195,11 @@ void close_log(FILE *fh, const char *basename) strftime( cad, 80, "%H:%M:%S, %F", tmPtr ); fprintf(fh, "%s scan ending at: %s\n", basename, cad ); + if (hfriend != 0) + { + printf("%s scan ending at: %s\n", basename, cad ); + } + fflush(stdout) ; fclose(fh); } diff --git a/unhide-output.h b/unhide-output.h index b992c01..b8bc942 100644 --- a/unhide-output.h +++ b/unhide-output.h @@ -41,8 +41,8 @@ extern void warnln(int verbose, FILE * unlog, const char* fmt, ...) ; extern void die(FILE * unlog, const char* fmt, ...) ; // Initialize and write a header to the log file. -extern FILE *init_log(int logtofile, const char *header, const char *basename) ; +extern FILE *init_log(int logtofile, const char *header, const char *basename, int hfriend) ; // Write a footer and close a log file. -extern void close_log(FILE *fh, const char *basename) ; +extern void close_log(FILE *fh, const char *basename, int hfriend) ; diff --git a/unhide-posix.c b/unhide-posix.c index 471efdd..d08669d 100644 --- a/unhide-posix.c +++ b/unhide-posix.c @@ -79,53 +79,39 @@ void checkps(int tmppid) { FILE *fich_tmp ; - fich_tmp=popen (COMMAND, "r") ; - - - while (!feof(fich_tmp) && ok == 0) { - - fgets(pids, 30, fich_tmp); - - sprintf(compare,"%i\n",tmppid); - - if (strcmp(pids, compare) == 0) {ok = 1;} - - + if (NULL != (fich_tmp = popen (COMMAND, "r"))) + { + while (!feof(fich_tmp) && ok == 0) + { + fgets(pids, 30, fich_tmp); + sprintf(compare,"%i\n",tmppid); + if (strcmp(pids, compare) == 0) {ok = 1;} } - - pclose(fich_tmp); + pclose(fich_tmp); + } if ( ok == 0 ) { - int statuscmd ; char cmd[100] ; - struct stat buffer; printf ("Found HIDDEN PID: %i\n", tmppid) ; - sprintf(cmd,"/proc/%i/cmdline",tmppid); - statuscmd = stat(cmd, &buffer); - if (statuscmd == 0) { - FILE *cmdfile ; char cmdcont[1000]; - cmdfile=fopen (cmd, "r") ; - - - while (!feof (cmdfile)) { - - fgets (cmdcont, 1000, cmdfile); - printf ("Command: %s\n\n", cmdcont); - - } + if (NULL != (cmdfile = fopen (cmd, "r"))) + { + while (!feof (cmdfile)) { + fgets (cmdcont, 1000, cmdfile); + printf ("Command: %s\n\n", cmdcont); + } + fclose(cmdfile) ; + } } - } - } void checkproc() { @@ -231,12 +217,13 @@ void checkgetsid() { int main (int argc, char *argv[]) { - strncpy(scratch,"Unhide-posix 20130526\n", 1000) ; - strncat(scratch, "Copyright © 2013 Yago Jesus & Patrick Gouin\n", 1000); - strncat(scratch, "License GPLv3+ : GNU GPL version 3 or later\n", 1000); - strncat(scratch, "http://www.unhide-forensics.info\n\n", 1000); + strncpy(scratch,"Unhide-posix 20130526\n", sizeof(scratch)-1) ; + strncat(scratch, "Copyright © 2013 Yago Jesus & Patrick Gouin\n", sizeof(scratch)-strlen(scratch)-1); + strncat(scratch, "License GPLv3+ : GNU GPL version 3 or later\n", sizeof(scratch)-strlen(scratch)-1); + strncat(scratch, "http://www.unhide-forensics.info\n\n", sizeof(scratch)-strlen(scratch)-1); strncat(scratch, "NOTE : This is legacy version of unhide, it is intended\n\ - for systems using Linux < 2.6 or other UNIX systems\n\n", 1000); + for systems using Linux < 2.6 or other UNIX systems\n\n", sizeof(scratch)-strlen(scratch)-1); + scratch[999] = 0 ; fputs(scratch, stdout); diff --git a/unhide-tcp-simple-check.c b/unhide-tcp-simple-check.c deleted file mode 100644 index a6c11b1..0000000 --- a/unhide-tcp-simple-check.c +++ /dev/null @@ -1,499 +0,0 @@ -/* - http://www.unhide-forensics.info -*/ - -/* -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 . -*/ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "unhide-output.h" -#include "unhide-tcp.h" - -// header -const char header[] = - "Unhide-tcp 20121229\n" - "Copyright © 2012 Yago Jesus & Patrick Gouin\n" - "License GPLv3+ : GNU GPL version 3 or later\n" - "http://www.unhide-forensics.info\n"; - -// options -int verbose = 0; -int use_fuser = 0; -int use_lsof = 0; -int use_ss = 1; // use ss by default -int use_quick = 0; - -char checker[10] = "ss" ; - -// Temporary string for output -char scratch[1000]; - -// Temporary string for output -char used_options[1000] = ""; - -// For logging to file -int logtofile = 0; -FILE *unlog; - -// Global hidden port counter, used only to set the exit code of the program -int hidden_found; - -/* thx aramosf@unsec.net for the nice regexp! */ - -// Linux -char tcpcommand1[]= "netstat -tan | sed -e '/[\\.:][0-9]/!d' -e 's/.*[\\.:]\\([0-9]*\\) .*[\\.:].*/\\1/'" ; -char udpcommand1[]= "netstat -uan | sed -e '/[\\.:][0-9]/!d' -e 's/.*[\\.:]\\([0-9]*\\) .*[\\.:].*/\\1/'" ; - -// Alternative commands, needs iproute2 -char tcpcommand2[]= "ss -tan sport = :%d | sed -e '/[\\.:][0-9]/!d' -e 's/.*[\\.:]\\([0-9]*\\) .*[\\.:].*/\\1/'" ; -char udpcommand2[]= "ss -uan sport = :%d | sed -e '/[\\.:][0-9]/!d' -e 's/.*[\\.:]\\([0-9]*\\) .*[\\.:].*/\\1/'" ; - -// fuser commands -char fuserTCPcommand[]= "fuser -v -n tcp %d 2>&1" ; -char fuserUDPcommand[]= "fuser -v -n udp %d 2>&1" ; - -// lsof commands -char lsofTCPcommand[]= "lsof +c 0 -iTCP:%d" ; -char lsofUDPcommand[]= "lsof +c 0 -iUDP:%d" ; - -// OpenBSD -// char tcpcommand[]= "netstat -an -p tcp | sed -e '/[\\.:][0-9]/!d' -e 's/.*[\\.:]\\([0-9]*\\) .*[\\.:].*/\\1/'" ; -// char udpcommand[]= "netstat -an -p udp| sed -e '/[\\.:][0-9]/!d' -e 's/.*[\\.:]\\([0-9]*\\) .*[\\.:].*/\\1/'" ; - - -// Solaris -// char tcpcommand[]= "netstat -an -P tcp | sed -e '/[\\.:][0-9]/!d' -e 's/.*[\\.:]\\([0-9]*\\) .*[\\.:].*/\\1/'" ; -// char udpcommand[]= "netstat -an -P udp| sed -e '/[\\.:][0-9]/!d' -e 's/.*[\\.:]\\([0-9]*\\) .*[\\.:].*/\\1/'" ; - - - -/* - * Run a command to get more information about a port. - */ -static void print_info(const char *prog_name, const char *command_fmt, int port) -{ - - char buffer[1000]; - FILE* fp; - - sprintf(buffer, command_fmt, port); - fp = popen(buffer, "r") ; - - if (NULL == fp) - { - warnln(verbose, unlog, "Couldn't run command: %s", buffer) ; - return ; - } - - msgln(unlog, 1, "%s reports :", prog_name) ; - - while (NULL != fgets(buffer, 1000, fp)) - { - msgln(unlog, 1, buffer) ; - } - - pclose(fp); -} - -/* Print a port, optionally querying info about it via lsof or fuser. */ -void print_port(enum Proto proto, int port) -{ - msgln(unlog, 0, "\nFound Hidden port that not appears in %s: %i", checker, port) ; - if (1 == use_fuser) - { - if (TCP == proto) - { - print_info("fuser", fuserTCPcommand, port); - } - else - { - print_info("fuser", fuserUDPcommand, port); - } - } - if (1 == use_lsof) - { - if (TCP == proto) - { - print_info("lsof", lsofTCPcommand, port); - } - else - { - print_info("lsof", lsofUDPcommand, port); - } - } -} - - -/* - * Check if port is seen by netstat. - * - * If not, report it and optionnally run lsof and/or fuser - * to show more info. - */ -void checkoneport(int port, char command[], enum Proto proto) -{ - int ok = 0; - char ports[30]; - char compare[100]; - - FILE *fich_tmp ; - - if (NULL != (fich_tmp=popen (command, "r"))) - { - sprintf(compare,"%i\n",port); - while ((NULL != fgets(ports, 30, fich_tmp)) && ok == 0) { - if (strcmp(ports, compare) == 0) {ok = 1;} - } - pclose(fich_tmp); - } - else - { - die(unlog, "Couldn't execute command : %s while checking port %d", command, port) ; - } -// test -// ok = 0 ; - if ( ok == 0 ) - { - hidden_found++; - print_port(proto, port) ; - } -} - -/* - * Check all TCP ports one by one. - */ -static void print_hidden_TCP_ports_1_by_1(enum Proto proto) -{ - int i ; - char tcpcommand[512] ; - - hidden_found = 0 ; - for (i =1; i <= 65535; i++) - { - int socket_desc; - struct sockaddr_in address; - - if ( -1 != (socket_desc=socket(AF_INET,SOCK_STREAM,0))) - { - address.sin_family = AF_INET; - address.sin_addr.s_addr = INADDR_ANY; - address.sin_port = htons(i); - errno= 0 ; - if ( -1 != bind(socket_desc,(struct sockaddr *)&address,sizeof(address))) - { - listen(socket_desc,1); - if ( EADDRINUSE == errno ) // port is listened by another process - { - close(socket_desc); - if (1 == use_ss) - { - sprintf(tcpcommand, tcpcommand2, i) ; - } - else - { - strncpy(tcpcommand, tcpcommand1, 512) ; - } - checkoneport(i, tcpcommand, TCP); - } - else - { - close(socket_desc); - } - } - else - { - if (EADDRINUSE == errno) //port is in use by another process - { - close(socket_desc); - if (1 == use_ss) - { - sprintf(tcpcommand, tcpcommand2, i) ; - } - else - { - strncpy(tcpcommand, tcpcommand1, 512) ; - } - checkoneport(i, tcpcommand, TCP); - } - else - { - close(socket_desc); - warnln(verbose, unlog, "can't bind to socket while checking port %d", i) ; - } - } - } - else - { - warnln(verbose, unlog, "can't create socket while checking port %d/tcp", i) ; - } - } -} - -/* - * Check all UDP ports one by one. - */ -static void print_hidden_UDP_ports_1_by_1(enum Proto proto) -{ - int u ; - char udpcommand[512] ; - - hidden_found = 0 ; - - for (u = 1; u <= 65535; u++) - { - int socket_desc; - struct sockaddr_in address; - - if ( -1 != (socket_desc=socket(AF_INET,SOCK_DGRAM,0))) - { - address.sin_family = AF_INET; - address.sin_addr.s_addr = INADDR_ANY; - address.sin_port = htons(u); - errno= 0 ; - if ( 0 != bind(socket_desc,(struct sockaddr *)&address,sizeof(address))) - { - if ( EADDRINUSE == errno ) //port is in use by another process - { - close(socket_desc); - if (1 == use_ss) - { - sprintf(udpcommand, udpcommand2, u) ; - } - else - { - strncpy(udpcommand, udpcommand1, 512) ; - } - checkoneport(u, udpcommand, UDP); - } - else // other error - { - close(socket_desc); - warnln(verbose, unlog, "can't bind to socket while checking port %d", u) ; - } - } - else // port is available - { - close(socket_desc); - } - } - else - { - warnln(verbose, unlog, "can't create socket while checking port %d/udp", u) ; - } - } -} - - -/* - * Print usage. - */ -void usage(char * command) { - - printf("Usage: %s [options] \n\n", command); - printf("Options :\n"); - printf(" -V Show version and exit\n"); - printf(" -v verbose\n"); - printf(" -h display this help\n"); - printf(" -f show fuser output for hidden ports\n"); - printf(" -l show lsof output for hidden ports\n"); - printf(" -o log result into unhide-tcp.log file\n"); - printf(" -s use very quick version for server with lot of opened ports\n"); - printf(" -n use netstat instead of ss\n"); -} - -/* - * Parse command line arguments (exiting if requested by any option). - */ -void parse_args(int argc, char **argv) -{ - int c = 0; - - static struct option long_options[] = - { - /* These options set a flag. */ - {"verbose", no_argument, &verbose, 1}, - {"brief", no_argument, &verbose, 0}, - {"fuser", no_argument, &use_fuser, 1}, - {"lsof", no_argument, &use_lsof, 1}, - {"log", no_argument, &logtofile, 1}, - {"netstat", no_argument, &use_ss, 0}, - {"server", no_argument, &use_quick, 1}, - /* These options don't set a flag. - We distinguish them by their indices. */ - {"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, "Vvhflosn", - 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 'f' : - use_fuser = 1 ; - break ; - case 'h' : - usage(argv[0]) ; - exit (0) ; - break ; - case 'l' : - use_lsof = 1 ; - break; - case 'o' : - logtofile = 1 ; - break ; - case 's' : - use_quick = 1 ; - break ; - case 'n' : - use_ss = 0 ; - 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 (use_lsof) - strncat(used_options, "use_lsof ", 1000-1-strlen(used_options)); - if (use_fuser) - strncat(used_options, "use_fuser ", 1000-1-strlen(used_options)); - if (!use_ss) - strncat(used_options, "use_netscape ", 1000-1-strlen(used_options)); - if (use_quick) - strncat(used_options, "use_quick ", 1000-1-strlen(used_options)); - if (logtofile) - strncat(used_options, "logtofile ", 1000-1-strlen(used_options)); -} - -/* - * Look for TCP and UDP ports that are hidden to netstat. - * - * Returns 0 if none is found, 1 if there is some internal error, 4 if TCP - * hidden ports were found, 8 if UDP hidden ports were found or 12 (4 & 8) if - * both were found. - */ -int main(int argc, char **argv) -{ - int ret_code = 0; - - printf(header) ; - if(getuid() != 0){ - die(unlog, "You must be root to run %s !", argv[0]) ; - } - - parse_args(argc, argv) ; - - if (1 == logtofile) - { - unlog = init_log(logtofile, header, "unhide-tcp") ; - } - msgln(unlog, 0, used_options) ; - - if (1 == use_ss) - { - strncpy(checker, "ss", 10); - } - else - { - strncpy(checker, "netstat", 10); - } - - setpriority(PRIO_PROCESS,0,-20); /* reduce risk of race condition - may fail, dont care */ - - msgln(unlog, 0, "[*]Starting TCP checking") ; - if (1 == use_quick) - { - print_hidden_ports(TCP); - } - else - { - print_hidden_TCP_ports_1_by_1(TCP) ; - } - if (hidden_found) - { - ret_code += 4; - } - - msgln(unlog, 0, "[*]Starting UDP checking") ; - if (1 == use_quick) - { - print_hidden_ports(UDP); - } - else - { - print_hidden_UDP_ports_1_by_1(UDP) ; - } - if (hidden_found) - { - ret_code += 8; - } - - if (logtofile == 1) - { - close_log(unlog, "unhide-tcp") ; - } - return(ret_code); - -} - diff --git a/unhide-tcp.c b/unhide-tcp.c index 3d3cf19..7302b22 100644 --- a/unhide-tcp.c +++ b/unhide-tcp.c @@ -29,6 +29,7 @@ along with this program. If not, see . #include #include #include +#include #include "unhide-output.h" #include "unhide-tcp.h" @@ -44,6 +45,8 @@ const char header[] = int verbose = 0; int use_fuser = 0; int use_lsof = 0; +int humanfriendly = FALSE ; + #ifdef __linux__ int use_ss = 1; // on Linux use ss by default #else @@ -179,18 +182,30 @@ void print_port(enum Proto proto, int port) * If not, report it and optionnally run lsof and/or fuser * to show more info. */ +# define STR_PORT_LENGTH 6 // with ending null char int checkoneport(int port, char command[], enum Proto proto) { int ok = 0; - char ports[30]; - char compare[100]; FILE *fich_tmp ; if (NULL != (fich_tmp=popen (command, "r"))) { - sprintf(compare,"%i\n",port); - while ((NULL != fgets(ports, 30, fich_tmp)) && ok == 0) { + char ports[2 * STR_PORT_LENGTH]; // port number reported by ss or netstat + char compare[2 * STR_PORT_LENGTH]; // port number to verify. + char *port_p ; + + // sprintf(compare,"%i\n",port); + sprintf(compare,"%i",port); + // or itoa(i, compare, 10) ; + while ((NULL != fgets(ports, 2 * STR_PORT_LENGTH - 2, fich_tmp)) && ok == 0) { + ports[2 * STR_PORT_LENGTH - 1] = 0 ; // force string terminaison + port_p = ports + strlen(ports) ; + while ((port_p > ports) && !isdigit(*port_p)) + { + *port_p = 0 ; // replace all non numerical char by end of string ! + port_p--; + } if (strcmp(ports, compare) == 0) {ok = 1;} } pclose(fich_tmp); @@ -377,6 +392,7 @@ void usage(char * command) { printf(" -o log result into unhide-tcp.log file\n"); printf(" -s use very quick version for server with lot of opened ports\n"); printf(" -n use netstat instead of ss\n"); + fflush(stdout) ; } /* @@ -400,6 +416,7 @@ void parse_args(int argc, char **argv) We distinguish them by their indices. */ {"help", no_argument, 0, 'h'}, {"version", no_argument, 0, 'V'}, + {"version", no_argument, 0, 'H'}, {0, 0, 0, 0} }; @@ -408,7 +425,7 @@ void parse_args(int argc, char **argv) /* getopt_long stores the option index here. */ int option_index = 0; - c = getopt_long (argc, argv, "Vvhflosn", + c = getopt_long (argc, argv, "VvhHflosn", long_options, &option_index); /* Detect the end of the options. */ @@ -454,6 +471,9 @@ void parse_args(int argc, char **argv) case 'V' : exit (0) ; break ; + case 'H' : + humanfriendly = TRUE ; + break ; case '?' : // invalid option exit (2) ; break ; @@ -473,7 +493,7 @@ void parse_args(int argc, char **argv) if (use_fuser) strncat(used_options, "use_fuser ", 1000-1-strlen(used_options)); if (!use_ss) - strncat(used_options, "use_netscape ", 1000-1-strlen(used_options)); + strncat(used_options, "use_netstat ", 1000-1-strlen(used_options)); if (use_quick) strncat(used_options, "use_quick ", 1000-1-strlen(used_options)); if (logtofile) @@ -492,6 +512,7 @@ int main(int argc, char **argv) int ret_code = 0; printf(header) ; + fflush(stdout) ; if(getuid() != 0){ die(unlog, "You must be root to run %s !", argv[0]) ; @@ -501,7 +522,7 @@ int main(int argc, char **argv) if (1 == logtofile) { - unlog = init_log(logtofile, header, "unhide-tcp") ; + unlog = init_log(logtofile, header, "unhide-tcp", humanfriendly) ; } msgln(unlog, 0, used_options) ; @@ -546,7 +567,7 @@ int main(int argc, char **argv) if (logtofile == 1) { - close_log(unlog, "unhide-tcp") ; + close_log(unlog, "unhide-tcp", humanfriendly) ; } return(ret_code); diff --git a/unhide-tcp.h b/unhide-tcp.h index 250a1e3..54bdd1e 100644 --- a/unhide-tcp.h +++ b/unhide-tcp.h @@ -17,6 +17,12 @@ You should have received a copy of the GNU General Public License along with this program. If not, see . */ +// boolean values +// ============== +#define FALSE 0 +#define TRUE 1 + + /* * Globals diff --git a/unhideGui.py b/unhideGui.py new file mode 100755 index 0000000..c813ec2 --- /dev/null +++ b/unhideGui.py @@ -0,0 +1,436 @@ +#!/bin/python3 + +from tkinter import * +from tkinter.ttk import * +import os +import platform +import subprocess +import shlex +import time +import time +from ToolTip import * + +OutputWindow = None +OutputMsg = None + +ttDelay = 0.1 + +OptionList = [ ['-V', 'Version', 'Show version and exit'], + ['-v', 'Verbose', "Be verbose, display warning message (default : don't display). This option may be repeated more than once."], + ['-h', 'Help', 'Display help'], +# ['-m', 'more checks (available only with procfs, checkopendir & checkchdir commands)'], + ['-m', 'More checks', 'Do more checks. As of 2021-xx-xx version, this option has only effect for the procfs, procall, checkopendir and checkchdir tests.\nImplies -v'], + ['-r', 'Alternate sysinfo', 'Use alternate version of sysinfo check in standard tests'], + ['-f', 'Log result', 'Write a log file (unhide-linux.log) in the current directory.'], + ['-o', 'Log result', 'Write a log file (unhide-linux.log) in the current directory.'], + ['-d', 'Double check', 'Do a double check in brute test to avoid false positive.'], + ['-H', 'Human friendly', 'Output a slightlu human friendlier result'], + ] + +TcpOptionList = [ ['-h', 'Help', "Display help"], + ['--brief', 'Quiet', "Don't display warning messages"], + ['-f', 'fuser', "On Linux, display fuser output (if available). On FreeBSD displays the output of sockstat"], + ['-l', 'lsof', "Display lsof output (if available)"], + ['-n', 'netstat', "Use /bin/netstat instead of /sbin/ss."], + ['-s', 'Server', "Use a very quick strategy of scanning (for servers)."], + ['-o', 'Log', "Write a log file."], + ['-V', 'Version', "Show version and exit"], + ['-v', 'Verbose', "Be verbose, display warning message"], + ['-H', 'Human friendly', 'Output a slightlu human friendlier result'], + ] + + + +StandardTestsList = [ ['brute', "The brute technique consists of bruteforcing the all process IDs.\nThis technique is only available with version unhide-linux."], + ['proc', "The proc technique consists of comparing /proc with the output of /bin/ps."], + ['procall', "The procall technique combinates proc and procfs tests.\nThis technique is only available with version unhide-linux."], + ['procfs', "The procfs technique consists of comparing information gathered from /bin/ps with information gathered by walking in the procfs.\nWith -m option, this test makes more checks, see checkchdir test.\nThis technique is only available with version unhide-linux."], + ['quick', "The quick technique combines the proc, procfs and sys techniques in a quick way. It's about 20 times faster but may give more false positives.\nThis technique is only available with version unhide-linux."], + ['reverse', "The reverse technique consists of verifying that all threads seen by ps are also seen in procfs and by system calls. It is intended to verify that a rootkit has not killed a security tool (IDS or other) and make ps showing a fake process instead.\nThis technique is only available with version unhide-linux."], + ['sys', "The sys technique consists of comparing information gathered from /bin/ps with information gathered from system calls."], + ] + +ElementaryTestsList = [ [ 'checkRRgetinterval', "The checkRRgetinterval technique consists of comparing information gathered from /bin/ps with the result of call to the sched_rr_get_interval() system function.\nThis technique is only available with version unhide-linux."], + [ 'checkbrute', "The checkbrute technique consists of bruteforcing the all process IDs.\nThis technique is only available with version unhide-linux."], + [ 'checkchdir', "The checkchdir technique consists of comparing information gathered from /bin/ps with information gathered by making chdir() in the procfs.\nWith the -m option, it also verify that the thread appears in its 'leader process' threads list.\nThis technique is only available with version unhide-linux."], + [ 'checkgetaffinity', "The checkgetaffinity technique consists of comparing information gathered from /bin/ps with the result of call to the sched_getaffinity() system function.\nThis technique is only available with version unhide-linux."], + [ 'checkgetparam', "The checkgetparam technique consists of comparing information gathered from /bin/ps with the result of call to the sched_getparam() system function.\nThis technique is only available with version unhide-linux."], + [ 'checkgetpgid', "The checkgetpgid technique consists of comparing information gathered from /bin/ps with the result of call to the getpgid() system function.\nThis technique is only available with version unhide-linux."], + [ 'checkgetprio', "The checkgetprio technique consists of comparing information gathered from /bin/ps with the result of call to the getpriority() system function.\nThis technique is only available with version unhide-linux."], + [ 'checkgetsched', "The checkgetsched technique consists of comparing information gathered from /bin/ps with the result of call to the sched_getscheduler() system function.\nThis technique is only available with version unhide-linux."], + [ 'checkgetsid', "The checkgetsid technique consists of comparing information gathered from /bin/ps with the result of call to the getsid() system function.\nThis technique is only available with version unhide-linux."], + [ 'checkkill', "The checkkill technique consists of comparing information gathered from /bin/ps with the result of call to the kill() system function.\nNote : no process is really killed by this test.\This technique is only available with version unhide-linux."], + [ 'checknoprocps', "The checknoprocps technique consists of comparing the result of the call to each of the system functions. No comparison is done against /proc or the output of ps.\nThis technique is only available with version unhide-linux."], + [ 'checkopendir', "The checkopendir technique consists of comparing information gathered from /bin/ps with information gathered by making opendir() in the procfs.\nThis technique is only available with version unhide-linux."], + [ 'checkproc', "The checkproc technique consists of comparing /proc with the output of /bin/ps.\nThis technique is only available with version unhide-linux."], + [ 'checkquick', "The checkquick technique combines the proc, procfs and sys techniques in a quick way. It's about 20 times faster but may give more false positives.\nThis technique is only available with version unhide-linux."], + [ 'checkreaddir', "The checkreaddir technique consists of comparing information gathered from /bin/ps with information gathered by making readdir() in /proc and /proc/pid/task.\nThis technique is only available with version unhide-linux."], + [ 'checkreverse', "The checkreverse technique consists of verifying that all threads seen by ps are also seen in procfs and by system calls. It is intended to verify that a rootkit has not killed a security tool (IDS\n\nor other) and make ps showing a fake process instead.\nThis technique is only available with version unhide-linux."], + [ 'checksysinfo', "The checksysinfo technique consists of comparing the number of process seen by /bin/ps with information obtained from sysinfo() system call.\nThis technique is only available with version unhide-linux."], + [ 'checksysinfo2', "The checksysinfo2 technique is an alternate version of checksysinfo test. It might (or not) work better on kernel patched for RT, preempt or latency and with kernel that don't use the standard\nscheduler.\nIt's also invoked by standard tests when using the -r option\nThis technique is only available with version unhide-linux."], + [ 'checksysinfo3', "The checksysinfo2 technique is an alternate version of checksysinfo test. It might (or not) work better on kernel patched for RT, preempt or latency and with kernel that don't use the standard\nscheduler.\nIt's also invoked by standard tests when using the -r option\nThis technique is only available with version unhide-linux."], + ] + + + + +TestGroupList = { 'brute' : ['checkbrute'], + 'proc' : ['checkproc'], + 'procall' : ['checkchdir', 'checkopendir', 'checkproc', 'checkreaddir'], + 'procfs' : ['checkchdir', 'checkopendir', 'checkreaddir'], + 'quick' : ['checkquick'], + 'reverse' : ['checkreverse'], + 'sys' : ['checkRRgetinterval', 'checkgetaffinity', 'checkgetparam', 'checkgetpgid', 'checkgetprio', 'checkgetsched', 'checkgetsid', 'checkkill', 'checknoprocps'], + } + +# "Macros" +CHKB = 0 +VARB = 1 + +TNAME = 0 +TDESC = 1 + + +def CheckOpt(idx) : + GenCmd() + +def CheckTcpOpt(idx) : + GenTcpCmd() + +def CheckCtest(idx) : + ctest = CTestBut[idx][CHKB].cget('text') + ctest_state = CTestBut[idx][VARB].get() + if ctest_state == '1': + if ctest in TestGroupList : + print('Ctest in TestGroupList') + for etest in TestGroupList[ctest] : + for count, l in enumerate(ElementaryTestsList) : + if etest in l : + idx = count + break + ETestBut[idx][VARB].set(1) + else : + if ctest in TestGroupList : + for etest in TestGroupList[ctest] : + for count, l in enumerate(ElementaryTestsList) : + if etest in l : + idx = count + break + ETestBut[idx][VARB].set(0) + GenCmd() + + +def CheckEtest(idx) : + etest = ETestBut[idx][CHKB].cget('text') + etest_state = ETestBut[idx][VARB].get() + if etest_state == '0' : + for key in TestGroupList : + if etest in TestGroupList[key] : + for count, l in enumerate(StandardTestsList) : + if key in l : + idx_c = count + break + CTestBut[idx_c][VARB].set(0) + else : + for key in TestGroupList : + if etest in TestGroupList[key] : + nb = 0 + for test in TestGroupList[key] : + for count, l in enumerate(ElementaryTestsList) : + if test in l : + idx_e = count + break + if ETestBut[idx_e][VARB].get() == '1' : + nb += 1 + if nb == len(TestGroupList[key]) : + idx_c = StandardTestsList.index(key) + CTestBut[idx_c][VARB].set(1) + GenCmd() + + +def GenCommand() : + SelTab = ToolNote.index("current") + if SelTab == 0 : + GenCmd() + else : + GenTcpCmd() + +def TabEvent(event) : + GenCommand() + + +def GenCmd() : + Cmd = './unhide-linux ' + idx = 0 + for opt in OptionBut : + if opt[VARB].get() == '1' : + Cmd += OptionList[idx][0] + ' ' + idx += 1 + idx = 0 + etestlist = [] + for ctest in CTestBut : + if ctest[VARB].get() == '1' : + Cmd += StandardTestsList[idx][TNAME] + ' ' + etestlist += TestGroupList[StandardTestsList[idx][TNAME]] + idx += 1 + idx = 0 + for etest in ETestBut : + if etest[VARB].get() == '1' and ElementaryTestsList[idx][TNAME] not in etestlist: + Cmd += ElementaryTestsList[idx][TNAME] + ' ' + idx += 1 + Ucmd.set(Cmd) + CmdText.config(width = len(Cmd)) + +def GenTcpCmd() : + Cmd = './unhide-tcp ' + idx = 0 + for opt in TcpOptionBut : + if opt[VARB].get() == '1' : + Cmd += TcpOptionList[idx][0] + ' ' + idx += 1 + idx = 0 + Ucmd.set(Cmd) + CmdText.config(width = len(Cmd)) + + + +def CpyCmd() : + root.clipboard_clear() + root.clipboard_append(Ucmd.get()) + + +def RunCmd() : + # print(os.environ['COMSPEC']) + # Attention, sous Windows le module subprocess ne fonctionne que si la variable + # d'environnement COMSPEC ne contient qu'un seul élément (en l'occurence : + # "%SystemRoot%\system32\cmd.exe" + command = Ucmd.get() +'\n' + + p = subprocess.Popen(command, stdout=subprocess.PIPE, shell=True) + + ## Talk with unhide command i.e. read data from stdout and stderr. Store this info in tuple ## + (output, err) = p.communicate() + ## Wait for unhide to terminate. Get return returncode ## + p_status = p.wait() + print("Command output : \n", output.decode('utf-8')) + print("Command exit status/return code : ", p_status) + +def delOutput() : + global OutputWindow, OutputMsg + OutputWindow.destroy() + OutputWindow = None + OutputMsg = None + +def RunCmd_2() : + global OutputWindow, OutputMsg + + if platform.system() == 'Windows' : + UnhCommand = ["C:\\Windows\\System32\\HOSTNAME.EXE"] + UnhCommand = ["dir"] + else : + UnhCommand = shlex.split(Ucmd.get()) + + + # Do not wait till UCmd finish, start displaying output immediately # + if OutputWindow == None : + OutputWindow = Toplevel() + + rX = root.winfo_x() + rW = root.winfo_width() + rY = root.winfo_y() + rH = root.winfo_height() + # print('rx : {} - rw : {} - ry : {} - rh : {}'.format(rX, rW, rY, rH)) + + OutputWindow.geometry('+%d+%d' % (rX + (rW//3), rY + (rH//3))) + OutputWindow.title(Ucmd.get()) + OutputWindow.grid_rowconfigure(0, weight = 1) + OutputWindow.grid_columnconfigure(0, weight = 1) + # intercepte la fermeture de la fenêtre par la croix + OutputWindow.protocol("WM_DELETE_WINDOW", delOutput) + + + Line1 = Frame(OutputWindow) + # Le stickyness permet de resizer la frame avec la fenêtre : collé au 4 côtés. + Line1.grid(row = 0, column = 0, sticky = 'nsew') + Line1.grid_rowconfigure(0, weight = 1) + Line1.grid_columnconfigure(0, weight = 1) + + Line2 = Frame(OutputWindow) + Line2.grid(row = 1, column = 0, sticky = 'nw') + + OutputMsg = Text(Line1) + OutputMsg.grid(row = 0, column = 0, sticky = 'nsew') + + Outbutton = Button(Line2, text="Fermer", command = delOutput) + Outbutton.grid(row = 1, column = 0, sticky = 'nw') + + Clearbutton = Button(Line2, text="Effacer", command = lambda OutputMsg = OutputMsg : OutputMsg.delete(1.0, END)) + Clearbutton.grid(row = 1, column = 1, sticky = 'nw') + + S = Scrollbar(Line1) + # en ne collant pas à l'ouest, la barre conserve sa largeur quand la fenêtre grandit. + S.grid(row = 0, column = 1, sticky = 'nse') + + S.config(command = OutputMsg.yview) + OutputMsg.config(yscrollcommand = S.set) + OutputMsg.insert(END, 'Bientôt la sortie\n\n') + OutputWindow.update() + + p = subprocess.Popen(UnhCommand, universal_newlines = True, bufsize = 1 , stdout = subprocess.PIPE) + + outline = '' + while True : + outline = p.stdout.readline() + if outline == '' and p.poll() != None: + break + + if outline != '': + if platform.system() == 'Windows' : + OutputMsg.insert(END, str(outline,'cp850')) + outline = b'' + else : + OutputMsg.insert(END, outline) + outline = '' + OutputWindow.update() + OutputMsg.insert(END, '\n') + +# Construct TKinter Gui +root=Tk(className = "UnhideGUI") + +root.grid_columnconfigure(3, weight=1) + +# Frame permettant de contenir les tab des outils +ToolFrame = Frame(root, relief = RIDGE) +ToolFrame.grid(sticky = 'nw',row = 0, column = 0, columnspan = 4) + +# Notebook +ToolNote = Notebook(ToolFrame) +ToolNote.grid(sticky = 'nw',row = 0, column = 0, columnspan = 4) + +# Les deux frames des onglets +ProcessFrame = Frame(ToolNote, relief = RIDGE) +ProcessFrame.grid(sticky = 'nw',row = 0, column = 0, columnspan = 4) +TcpFrame = Frame(ToolNote, relief = RIDGE) +TcpFrame.grid(sticky = 'nw',row = 0, column = 0, columnspan = 4) + +# Onglet Unhide +OptionFrame = Labelframe(ProcessFrame, text = 'Options', relief = RIDGE) +OptionFrame.grid(sticky = 'nw',row = 0, column = 0) + + + +OptIdx = 0 +OptionBut = [] +for opt in OptionList : + # print(OptIdx, ' - ', opt) + tempo = StringVar() + OptCB = Checkbutton(OptionFrame, text = opt[1], variable = tempo, command = lambda OptIdx = OptIdx: CheckOpt(OptIdx)) + OptCBTT = ToolTip( OptCB, msg = opt[2], follow = 1, delay = ttDelay) + + OptionBut.append([OptCB, tempo, OptCBTT]) + del tempo + del OptCB + del OptCBTT + OptionBut[OptIdx][VARB].set(0) + OptionBut[OptIdx][CHKB].grid(sticky = 'w', row = OptIdx, column = 0) + OptIdx += 1 + +CTestFrame = Labelframe(ProcessFrame, text = 'Compound Tests', relief = RIDGE) +CTestFrame.grid(sticky = 'nw',row = 0, column = 1) + +CTestIdx = 0 +CTestBut = [] +for ctest in StandardTestsList : + # print(CTestIdx, ' - ', ctest) + tempo = StringVar() + # print('ctest[TNAME] :{}'.format(ctest[TNAME])) + TCB = Checkbutton(CTestFrame, text = ctest[TNAME], variable = tempo, command = lambda CTestIdx = CTestIdx: CheckCtest(CTestIdx)) + TCBTT = ToolTip( TCB, msg = ctest[TDESC], follow = 1, delay = ttDelay) + CTestBut.append([TCB, tempo, TCBTT]) + del tempo + del TCBTT + del TCB + CTestBut[CTestIdx][VARB].set(0) + CTestBut[CTestIdx][CHKB].grid(sticky = 'w', row = CTestIdx, column = 0) + CTestIdx += 1 + + +ETestFrame = Labelframe(ProcessFrame, text = 'Elementary Tests', relief = RIDGE) +ETestFrame.grid(sticky = 'nw',row = 0, column = 2) + +ETestIdx = 0 +ETestBut = [] +Ecol = 0 +Erow = 0 +NumEtest = len(ElementaryTestsList) +for etest in ElementaryTestsList : + # print(ETestIdx, ' - ', etest) + tempo = StringVar() + TCB = Checkbutton(ETestFrame, text = etest[TNAME], variable = tempo, command = lambda ETestIdx = ETestIdx: CheckEtest(ETestIdx)) + TCBTT = ToolTip( TCB, msg = etest[TDESC], follow = 1, delay = ttDelay) + ETestBut.append([TCB, tempo, TCBTT]) + del tempo + del TCBTT + del TCB + ETestBut[ETestIdx][VARB].set(0) + ETestBut[ETestIdx][CHKB].grid(sticky = 'w', row = Erow, column = Ecol) + ETestIdx += 1 + Erow += 1 + if ETestIdx == (NumEtest // 2) + 1 : + Ecol += 1 + Erow = 0 + +# Onglet Unhide-tcp +TcpOptionFrame = Labelframe(TcpFrame, text = 'Options', relief = RIDGE) +TcpOptionFrame.grid(sticky = 'nw',row = 0, column = 0) + +TcpOptIdx = 0 +TcpOptionBut = [] +for opt in TcpOptionList : + # print(TcpOptIdx, ' - ', opt) + tempo = StringVar() + OptCB = Checkbutton(TcpOptionFrame, text = opt[1], variable = tempo, command = lambda TcpOptIdx = TcpOptIdx: CheckTcpOpt(TcpOptIdx)) + OptCBTT = ToolTip( OptCB, msg = opt[2], follow = 1, delay = ttDelay) + TcpOptionBut.append([OptCB, tempo, OptCBTT]) + del tempo + del OptCB + del OptCBTT + TcpOptionBut[TcpOptIdx][CHKB].grid(sticky = 'w', row = TcpOptIdx, column = 0) + TcpOptIdx += 1 + +# Met les 2 onglets dans le noteBook +ToolNote.add(ProcessFrame, text = "Unhide-linux") +ToolNote.add(TcpFrame, text = "Unhide-tcp") +ToolNote.bind("<>", TabEvent) + +UCmdFrame = Labelframe(root, text = 'Command Unhide', relief = RIDGE) +UCmdFrame.grid(sticky = 'nw',row = 1, column = 0, columnspan = 4) +# ici il faut ajouter columnspan = 4 (=nb colonne + 1) pour que les frames au-dessus ne soit pas étendues inopinément. + +Ucmd = StringVar() +CmdText = Entry(UCmdFrame, textvariable = Ucmd, state="readonly") +CmdText.grid(sticky = 'w', row = 0, column = 0, columnspan = 4 ) + +#buttons +RunUCmd = Button(UCmdFrame, text="Run", command = RunCmd_2) +RunUCmd.grid(sticky = 'w', row = 1, column = 0) + +GenUCmd = Button(UCmdFrame, text="Generate", command = GenCommand) +GenUCmd.grid(sticky = 'w', row = 1, column = 1) + +CopyCmd = Button(UCmdFrame, text="Copy to ClipBoard", command = CpyCmd) + +CopyCmd.grid(sticky = 'w', row = 1, column = 2) + +UCmdFrame.grid_columnconfigure(3, weight=1) + + +screen_width = root.winfo_screenwidth() +screen_height = root.winfo_screenheight() +root.geometry('+%d+%d' % (screen_width/3, screen_height/3)) + + +root.update() + +root.mainloop() + diff --git a/unhide_rb.c b/unhide_rb.c index 76f846f..5da200d 100644 --- a/unhide_rb.c +++ b/unhide_rb.c @@ -53,7 +53,7 @@ along with this program. If not, see . int maxpid = 32768; // Temporary string for output -char scratch[1000] ; +char scratch[1500] ; char cmdcont[1000] ; unsigned int proc_parent_pids[65536] ; @@ -62,7 +62,7 @@ char *proc_tasks[65536]; char *ps_pids[65536]; char *messages_pids[65536]; char message[1000] ; -char description[1000] ; +char description[1100] ; int ps_count = 0 ; const char *pid_detectors[] = @@ -162,7 +162,16 @@ void setup(int phase) size_t length ; char myexe[512] ; + // Warning here as gcc can't know that directory (task number) contains far less than 94 char. +#ifdef __GNUC__ + #pragma GCC diagnostic push + #pragma GCC diagnostic ignored "-Wformat-overflow=" +#endif sprintf(myexe,"%s%s/exe",mypath,directory); +#ifdef __GNUC__ + #pragma GCC diagnostic pop +#endif + // printf("%s\n",myexe); length = readlink(myexe, cmdcont, 1000) ; @@ -276,15 +285,19 @@ int get_suspicious_pids(int pid_num) snprintf(scratch, 1000, "[*] Error: cannot get current maximum PID: %s\n", strerror(errno)); fputs(scratch, stdout); } - else if((fscanf(fd, "%d", &tmppid) != 1) || tmppid < 1) - { - snprintf(scratch, 1000, "[*] cannot get current maximum PID: Error parsing %s format\n", path); - fputs(scratch, stdout); - } else + else { - maxpid = tmppid; + if((fscanf(fd, "%d", &tmppid) != 1) || tmppid < 1) + { + snprintf(scratch, 1000, "[*] cannot get current maximum PID: Error parsing %s format\n", path); + fputs(scratch, stdout); + } + else + { + maxpid = tmppid; + } + fclose(fd) ; } - fclose(fd) ; pid_min = 1 ; pid_max = maxpid ; @@ -412,14 +425,18 @@ int get_suspicious_pids(int pid_num) if (FALSE == existence_consensus) { if (TRUE == pid_exists[index]) + { suspicious = TRUE ; break ; + } } else { if (FALSE == pid_exists[index]) + { suspicious = TRUE ; break ; + } } } @@ -483,17 +500,20 @@ int main (int argc, char *argv[]) int found_something = FALSE ; int phase1_ko = FALSE ; - strncpy(scratch,"Unhide_rb 20130526\n", 1000) ; + strncpy(scratch,"Unhide_rb 20130526\n", sizeof(scratch)-1) ; - strncat(scratch, "Copyright © 2013 Yago Jesus & Patrick Gouin\n", 1000); - strncat(scratch, "License GPLv3+ : GNU GPL version 3 or later\n", 1000); - strncat(scratch, "http://www.unhide-forensics.info\n\n", 1000); - strncat(scratch, "NOTE : This version of unhide_rb is for systems using Linux >= 2.6 \n\n", 1000); + strncat(scratch, "Copyright © 2013 Yago Jesus & Patrick Gouin\n", sizeof(scratch)-strlen(scratch)-1); + strncat(scratch, "License GPLv3+ : GNU GPL version 3 or later\n", sizeof(scratch)-strlen(scratch)-1); + strncat(scratch, "http://www.unhide-forensics.info\n\n", sizeof(scratch)-strlen(scratch)-1); + strncat(scratch, "NOTE : This version of unhide_rb is for systems using Linux >= 2.6 \n\n", sizeof(scratch)-strlen(scratch)-1); + scratch[999] = 0 ; fputs(scratch, stdout); + fflush(stdout) ; // printf(header) ; if(getuid() != 0){ printf("You must be root to run %s !\n", argv[0]) ; + fflush(stdout) ; } @@ -504,6 +524,7 @@ int main (int argc, char *argv[]) printf("Unhide_rb scan starting at: %s\n", cad ); */ puts ("Scanning for hidden processes...") ; + fflush(stdout) ; // initializing memory pointers for (i = 0 ; i < maxpid; i++) @@ -521,6 +542,7 @@ int main (int argc, char *argv[]) puts("ps and sysinfo() process count mismatch:\n") ; printf(" ps: %d processes\n", ps_count) ; printf(" sysinfo(): %d processes\n", info.procs) ; + fflush(stdout) ; } phase1_ko = get_suspicious_pids(-1) ; @@ -541,6 +563,7 @@ int main (int argc, char *argv[]) { found_something = TRUE ; puts(message) ; + fflush(stdout) ; } } }