diff --git a/NEWS b/NEWS
index ce310dd..90db331 100644
--- a/NEWS
+++ b/NEWS
@@ -1,3 +1,22 @@
+Changes since 20210124 :
+**********************
+
+BUG FIXES
+ - Correct two typo in english man pages
+ - Dirty hacks in unhide_rb to increase the max number of PID so it doesn't crash in 64 bits systems.
+
+ENHANCEMENTS
+ - In brute test, allocate PID tables on the heap instead of stack, as maxpid on 64 bits Linux may cause a stack overflow.
+ - unhide-linux and unhide-posix: set the default value of max_pid to 8388608.
+
+GUI
+ - N/A
+
+MISCELLANOUS
+ - Update README.txt (build instructions and some document layout)
+ - Clearly indicate in its display header of unhide_rb that it SHOULD NOT be used for serious work.
+
+
Changes since 20130526 :
**********************
diff --git a/README.txt b/README.txt
index ec7f5b7..ef112ee 100644
--- a/README.txt
+++ b/README.txt
@@ -27,15 +27,20 @@ Detecting hidden processes. Implements six main techniques
// ---------
It's a back port in C language of the ruby unhide.rb
-As the original unhide.rb, it is roughly equivalent to "unhide-linux quick reverse" :
+
+As the original unhide.rb, it is roughly equivalent to "unhide-linux quick reverse" but:
- it makes three tests less (kill, opendir and chdir),
-- it only run /bin/ps once at start and once for the double check,
-- also, its tests are less accurate (e.g.. testing return value instead of errno),
+- it only run /bin/ps once at start and once for the double check, this gives more false positives:
+ short live processes are seen as hidden.
+- also, its tests are less accurate (e.g. testing return value instead of errno),
+- it doesn't scale well when max_PID number increases,
- processes are only identified by their exe link (unhide-linux also use cmdline and
"sleeping kernel process" name),
- there's little protection against failures (failed fopen or popen by example),
- there's no logging capability.
-It is very quick, about 80 times quicker than "unhide-linux quick reverse"
+
+On 32 bits system (with max_PID = 2^16) It is about 80 times quicker than "unhide-linux quick reverse"
+On 64 bits system (with max_PID = 2^22) It is about 2 times quicker than "unhide-linux quick reverse"
// Unhide-TCP
// ----------
@@ -92,29 +97,33 @@ man/fr/unhide-tcp.8 -- French man page of unhide-tcp
// Compiling
// ---------
-Build requires
+ Build requires :
+ --------------
glibc-devel
glibc-static-devel
-Require
-- unhide-tcp under linux :
- iproute2
- net-tools (for netstat)
- lsof
- psmisc (for fuser)
-- unhide-tcp under freeBSD :
- sockstat
- lsof
- netstat
+ Requires :
+ --------
+ - unhide-tcp under linux :
+ iproute2
+ net-tools (for netstat)
+ lsof
+ psmisc (for fuser)
+ - unhide-tcp under freeBSD :
+ sockstat
+ lsof
+ netstat
-unhide-linux, unhide-posix, unhide_rb :
- procps
+ - unhide-linux, unhide-posix, unhide_rb :
+ procps
+
+IMPORTANT : Notes that, as a forensic tool, unhide is built statically as the host system libraries may be compromised.
If you ARE using a Linux kernel >= 2.6
- 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 -Wextra -O2 --static -pthread unhide-linux*.c unhide-output.c -o unhide-linux
+ gcc -Wall -Wextra -O2 --static unhide-tcp.c unhide-tcp-fast.c unhide-output.c -o unhide-tcp
+ gcc -Wall -Wextra -O2 --static unhide_rb.c -o unhide_rb
ln -s unhide unhide-linux
Else (Linux < 2.6, *BSD, Solaris and other Unice)
diff --git a/unhide-linux-bruteforce.c b/unhide-linux-bruteforce.c
index 9f72b6f..3ee297e 100644
--- a/unhide-linux-bruteforce.c
+++ b/unhide-linux-bruteforce.c
@@ -66,32 +66,64 @@ void *functionThread (void *parametro)
void brute(void)
{
int i=0;
- int allpids[maxpid] ;
- int allpids2[maxpid] ;
+ int* allpids;
+ int* allpids2;
int x;
int y;
int z;
msgln(unlog, 0, "[*]Starting scanning using brute force against PIDS with fork()\n") ;
- // PID under 301 are reserved for kernel
- for(x=0; x < 301; x++)
+ if ( NULL == (allpids = (int *)malloc(sizeof(int) * maxpid)))
{
- allpids[x] = 0 ;
- allpids2[x] = 0 ;
+ die(unlog, "Error: Cannot allocate pid arrays ! Exiting.");
}
- for(z=301; z < maxpid; z++)
+ if(FALSE == brutesimplecheck) // allocate second table
{
- allpids[z] = z ;
- allpids2[z] = z ;
+ if ( NULL == (allpids2 = (int *)malloc(sizeof(int) * maxpid)))
+ {
+ die(unlog, "Error: Cannot allocate pid arrays ! Exiting.");
+ }
}
+
+
+ if(FALSE == brutesimplecheck) // Init the two tables
+ {
+ // PID under 301 are reserved for kernel
+ for(x=0; x < 301; x++)
+ {
+ allpids[x] = 0 ;
+ allpids2[x] = 0 ;
+ }
+
+ for(z=301; z < maxpid; z++)
+ {
+ allpids[z] = z ;
+ allpids2[z] = z ;
+ }
+ }
+ else // Init only the first table
+ {
+ for(x=0; x < 301; x++)
+ {
+ allpids[x] = 0 ;
+ }
+
+ for(z=301; z < maxpid; z++)
+ {
+ allpids[z] = z ;
+ }
+ }
+
+ // printf("Maxpid : %06d\n", maxpid);
for (i=301; i < maxpid; i++)
{
int vpid;
int status;
+ // printf("Tested pid : %06d\r", i);
errno= 0 ;
if ((vpid = vfork()) == 0)
@@ -143,21 +175,36 @@ void brute(void)
msgln(unlog, 0, "[*]Starting scanning using brute force against PIDS with pthread functions\n") ;
- // PID under 301 are reserved for kernel
- for(x=0; x < 301; x++)
+ if(FALSE == brutesimplecheck) // Init the two tables
{
- allpids[x] = 0 ;
- allpids2[x] = 0 ;
+ // PID under 301 are reserved for kernel
+ for(x=0; x < 301; x++)
+ {
+ allpids[x] = 0 ;
+ allpids2[x] = 0 ;
+ }
+
+ for(z=301; z < maxpid; z++)
+ {
+ allpids[z] = z ;
+ allpids2[z] = z ;
+ }
}
-
-
- for(z=301; z < maxpid; z++)
+ else // Init only the first table
{
- allpids[z] = z ;
- allpids2[z] = z ;
+ for(x=0; x < 301; x++)
+ {
+ allpids[x] = 0 ;
+ }
+
+ for(z=301; z < maxpid; z++)
+ {
+ allpids[z] = z ;
+ }
}
+
for (i=301; i < maxpid ; i++)
{
void *status;
@@ -216,4 +263,12 @@ void brute(void)
}
}
}
+
+ if ( NULL != allpids)
+ free((void *)allpids) ;
+
+ if ( NULL != allpids2)
+ free((void *)allpids2) ;
+
+
}
diff --git a/unhide-linux.c b/unhide-linux.c
index 1962937..3d78805 100644
--- a/unhide-linux.c
+++ b/unhide-linux.c
@@ -51,14 +51,15 @@ along with this program. If not, see .
// header
const char header[] =
- "Unhide 20210124\n"
+ "Unhide 20211016\n"
"Copyright © 2010-2021 Yago Jesus & Patrick Gouin\n"
"License GPLv3+ : GNU GPL version 3 or later\n"
"http://www.unhide-forensics.info\n\n"
"NOTE : This version of unhide is for systems using Linux >= 2.6 \n\n";
// defauly sysctl kernel.pid_max
-int maxpid = 32768;
+# define MAX_PID 8388608
+int maxpid = MAX_PID;
// Threads id for sync
int tid ;
diff --git a/unhide-posix.c b/unhide-posix.c
index 7b5d572..6f1d191 100644
--- a/unhide-posix.c
+++ b/unhide-posix.c
@@ -65,7 +65,8 @@ along with this program. If not, see .
-int maxpid= 999999;
+# define MAX_PID 8388608
+int maxpid = MAX_PID;
// Temporary string for output
char scratch[1000];
@@ -219,7 +220,7 @@ void checkgetsid() {
int main (int argc, char *argv[]) {
- strncpy(scratch,"Unhide-posix 20210124\n", sizeof(scratch)-1) ;
+ strncpy(scratch,"Unhide-posix 20211016\n", sizeof(scratch)-1) ;
strncat(scratch, "Copyright © 2013-2021 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);
diff --git a/unhide-tcp.c b/unhide-tcp.c
index 9e873ae..7f193ac 100644
--- a/unhide-tcp.c
+++ b/unhide-tcp.c
@@ -38,7 +38,7 @@ along with this program. If not, see .
// header
const char header[] =
- "Unhide-tcp 20210124\n"
+ "Unhide-tcp 20211016\n"
"Copyright © 2013-2021 Yago Jesus & Patrick Gouin\n"
"License GPLv3+ : GNU GPL version 3 or later\n"
"http://www.unhide-forensics.info\n";
diff --git a/unhide_rb.c b/unhide_rb.c
index af0fa1d..369948c 100644
--- a/unhide_rb.c
+++ b/unhide_rb.c
@@ -52,17 +52,21 @@ along with this program. If not, see .
#define UNKNOWN -1
// sysctl kernel.pid_max
-int maxpid = 32768;
+# define MAX_PID 8388608
+int maxpid = MAX_PID;
// Temporary string for output
char scratch[1500] ;
char cmdcont[1000] ;
-unsigned int proc_parent_pids[65536] ;
+unsigned int proc_parent_pids[MAX_PID] ;
-char *proc_tasks[65536];
-char *ps_pids[65536];
-char *messages_pids[65536];
+// char *proc_tasks[65536];
+// char *ps_pids[65536];
+// char *messages_pids[65536];
+char *proc_tasks[MAX_PID];
+char *ps_pids[MAX_PID];
+char *messages_pids[MAX_PID];
char message[1000] ;
char description[1100] ;
int ps_count = 0 ;
@@ -91,6 +95,7 @@ enum n_detector
void setup(int phase)
{
+ // printf("in : %s\n", __func__) ;
// setup part of unhide.rb
// -----------------------
@@ -214,9 +219,11 @@ void setup(int phase)
myline[999] = 0;
if (myline[0] != '\n')
{
+ //printf("myline = --%s--\n", myline);
mypid = strtol(myline, NULL,10) ;
// printf("line+5 = --%s--\n", myline+5); // DEBUG
- if (NULL == strstr(myline+5,UNHIDE_RB))
+ // if (NULL == strstr(myline+5,UNHIDE_RB))
+ if (NULL == strstr(myline+7,UNHIDE_RB))
{
ps_count++ ;
if (2 == phase)
@@ -241,9 +248,11 @@ void setup(int phase)
my_end-- ;
}
// printf("line = --%s--\n", myline); // DEBUG
- ps_pids[mypid] = malloc(strlen(myline+5)+1) ;
+ //ps_pids[mypid] = malloc(strlen(myline+5)+1) ;
+ ps_pids[mypid] = malloc(strlen(myline+7)+1) ;
// printf("ps_pids[%d] = %p for %d bytes\n", mypid,ps_pids[mypid],strlen(myline+5)+1);
- strcpy(ps_pids[mypid],myline+5) ;
+ // strcpy(ps_pids[mypid],myline+5) ;
+ strcpy(ps_pids[mypid],myline+7) ;
// printf("pid = %d\t", mypid); // DEBUG
// printf("cmd = --%s--\n",ps_pids[mypid]);
}
@@ -264,6 +273,8 @@ void setup(int phase)
int get_suspicious_pids(int pid_num)
{
+ //printf("in : %s\n", __func__) ;
+
int pid_min, pid_max, my_pid ;
int pid_exists[N_SCHED_RR_GET_INTERVAL+1];
char mypath[50] ;
@@ -303,19 +314,23 @@ int get_suspicious_pids(int pid_num)
pid_min = 1 ;
pid_max = maxpid ;
+ printf("pid_max : %0d\n", pid_max) ;
+
}
else
pid_min = pid_max = pid_num ;
for (my_pid = pid_min ; my_pid <= pid_max; my_pid++)
{
-// printf("pid_min = %d, pid_max = %d, my_pid = %d\n", pid_min, pid_max, my_pid) ;
+ // printf("pid_min = %d, pid_max = %d, my_pid = %d\n", pid_min, pid_max, my_pid) ;
// ps
+ // printf("in : %s ps\n", __func__) ;
if (ps_pids[my_pid] != NULL)
pid_exists[N_PS] = TRUE ;
else
pid_exists[N_PS] = FALSE ;
// proc
+ // printf("in : %s proc\n", __func__) ;
sprintf(mypath,"/proc/%d",my_pid);
statuscmd = stat(mypath, &buffer) ;
if ((statuscmd == 0) && S_ISDIR(buffer.st_mode))
@@ -339,17 +354,20 @@ int get_suspicious_pids(int pid_num)
pid_exists[N_PROC] = FALSE ;
}
// proc/#/task
+ // printf("in : %s proc/#/task\n", __func__) ;
if (proc_tasks[my_pid] != NULL)
pid_exists[N_PROC_TASK] = TRUE ;
else
pid_exists[N_PROC_TASK] = FALSE ;
// proc_parent
+ // printf("in : %s proc_parent\n", __func__) ;
if (proc_parent_pids[my_pid] != 0)
pid_exists[N_PROC_PARENT] = TRUE ;
else
pid_exists[N_PROC_PARENT] = UNKNOWN ;
// getsid()
+ // printf("in : %s getsid\n", __func__) ;
if (-1 != getsid(my_pid))
{
pid_exists[N_GETSID] = TRUE ;
@@ -359,6 +377,7 @@ int get_suspicious_pids(int pid_num)
pid_exists[N_GETSID] = FALSE ;
}
// getpgid()
+ // printf("in : %s getpgid\n", __func__) ;
if (-1 != getpgid(my_pid))
{
pid_exists[N_GET_PGID] = TRUE ;
@@ -368,6 +387,7 @@ int get_suspicious_pids(int pid_num)
pid_exists[N_GET_PGID] = FALSE ;
}
// getpriority()
+ // printf("in : %s getpriority\n", __func__) ;
if (-1 != getpriority(PRIO_PROCESS, my_pid))
{
pid_exists[N_GETPRIORITY] = TRUE ;
@@ -377,6 +397,7 @@ int get_suspicious_pids(int pid_num)
pid_exists[N_GETPRIORITY] = FALSE ;
}
// sched_getparam()
+ // printf("in : %s sched_getparam\n", __func__) ;
if (-1 != sched_getparam(my_pid, ¶m))
{
pid_exists[N_SCHED_GETPARAM] = TRUE ;
@@ -386,6 +407,7 @@ int get_suspicious_pids(int pid_num)
pid_exists[N_SCHED_GETPARAM] = FALSE ;
}
// sched_getaffinity()
+ // printf("in : %s sched_getaffinity\n", __func__) ;
if (-1 != sched_getaffinity(my_pid, sizeof(cpu_set_t), &mask))
{
pid_exists[N_SCHED_GETAFFINITY] = TRUE ;
@@ -395,6 +417,7 @@ int get_suspicious_pids(int pid_num)
pid_exists[N_SCHED_GETAFFINITY] = FALSE ;
}
// sched_getscheduler()
+ // printf("in : %s sched_getscheduler\n", __func__) ;
if (-1 != sched_getscheduler(my_pid))
{
pid_exists[N_SCHED_GETSCHEDULER] = TRUE ;
@@ -404,6 +427,7 @@ int get_suspicious_pids(int pid_num)
pid_exists[N_SCHED_GETSCHEDULER] = FALSE ;
}
// sched_rr_get_interval()
+ // printf("in : %s sched_rr_get_interval\n", __func__) ;
if (-1 != sched_rr_get_interval(my_pid, &tp))
{
pid_exists[N_SCHED_RR_GET_INTERVAL] = TRUE ;
@@ -475,7 +499,7 @@ int get_suspicious_pids(int pid_num)
strcpy(description, proc_exe) ;
}
}
- sprintf(scratch, "\n %s %s%s", (pid_exists[index] ? "Seen by" : "Not seen by"), pid_detectors[index], description) ;
+ sprintf(scratch, "\n %s %s %s", (pid_exists[index] ? "Seen by" : "Not seen by"), pid_detectors[index], description) ;
strcat(message, scratch) ;
}
// puts(message) ; //DEBUG
@@ -502,13 +526,35 @@ int main (int argc, char *argv[])
int found_something = FALSE ;
int phase1_ko = FALSE ;
- strncpy(scratch,"Unhide_rb 20210124\n", sizeof(scratch)-1) ;
+ strncpy(scratch,"Unhide_rb 20211016\n", sizeof(scratch)-1) ;
strncat(scratch, "Copyright © 2013-2021 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 ;
+ strncat(scratch, "WARNING : \n", sizeof(scratch)-strlen(scratch)-1);
+ strncat(scratch, "TL;DR : This tool is a P.O.F. (proof of fake).\n", sizeof(scratch)-strlen(scratch)-1);
+ strncat(scratch, " DON'T USE IT for serious work.\n\n", sizeof(scratch)-strlen(scratch)-1);
+ strncat(scratch, " Back in time, the Dev of unhide.rb pretends that his tool, written in ruby\n", sizeof(scratch)-strlen(scratch)-1);
+ strncat(scratch, " do the same checks that unhide-linux, which is written in C, but is 10 times faster.\n", sizeof(scratch)-strlen(scratch)-1);
+ strncat(scratch, " This was evidently false:\n", sizeof(scratch)-strlen(scratch)-1);
+ strncat(scratch, " - unhide.rb makes less tests,\n", sizeof(scratch)-strlen(scratch)-1);
+ strncat(scratch, " - unhide.rb tests are less accurate,\n", sizeof(scratch)-strlen(scratch)-1);
+ strncat(scratch, " - unhide.rb displays only displays minimal information about hidden processes,\n", sizeof(scratch)-strlen(scratch)-1);
+ strncat(scratch, " - unhide.rb find lot of false positives when processes number is high,\n", sizeof(scratch)-strlen(scratch)-1);
+ strncat(scratch, " - unhide.rb find lot of false positives when there are short live processes,\n", sizeof(scratch)-strlen(scratch)-1);
+ strncat(scratch, " - unhide.rb don't log results of tests,\n", sizeof(scratch)-strlen(scratch)-1);
+ strncat(scratch, " - and so on.\n", sizeof(scratch)-strlen(scratch)-1);
+ strncat(scratch, " In order to verify assertion about speed, I backported unhide.rb to C language,\n", sizeof(scratch)-strlen(scratch)-1);
+ strncat(scratch, " in the more straight/dummy way:\n", sizeof(scratch)-strlen(scratch)-1);
+ strncat(scratch, " No optimisation, translation from line to line, exactly the same tests and treatments.\n", sizeof(scratch)-strlen(scratch)-1);
+ strncat(scratch, " The result is native unhide_rb.\n", sizeof(scratch)-strlen(scratch)-1);
+ strncat(scratch, " It is ONE THOUSAND times faster that the original ruby unhide.rb\n\n", sizeof(scratch)-strlen(scratch)-1);
+ strncat(scratch, " So, don't rely neither on unhide.rb nor on unhide_rb, they are just toys ! \n", sizeof(scratch)-strlen(scratch)-1);
+ strncat(scratch, " For a quick but quite accurate test, use the command 'unhide-linux quick reverse'\n\n", sizeof(scratch)-strlen(scratch)-1);
+
+
+ scratch[1499] = 0 ;
fputs(scratch, stdout);
fflush(stdout) ;
@@ -516,6 +562,7 @@ int main (int argc, char *argv[])
if(getuid() != 0){
printf("You must be root to run %s !\n", argv[0]) ;
fflush(stdout) ;
+ exit(1);
}
@@ -547,6 +594,7 @@ int main (int argc, char *argv[])
fflush(stdout) ;
}
+ puts ("Phase 1...") ;
phase1_ko = get_suspicious_pids(-1) ;
// re-initializing memory pointers (were used as boolean in setup() phase 1)
for (i = 0 ; i < maxpid; i++)
@@ -556,6 +604,7 @@ int main (int argc, char *argv[])
}
if (TRUE == phase1_ko)
{
+ puts ("Phase 2...") ;
setup(2);
for (i=1; i