Hi Gary,
Thanks for taking the time to look it up and reply. The process in question was my solution for a class assignment. We should find a way to prevent an attacker from attaching to our running process through the ptrace system call. BTW, ptrace is what gdb uses to debug programs. The strategy I used works if the attacker doesn't have root privileges, but it seems to have the side effect of "demonizing" the process on Darwin... It worked without problems on Linux which was what the assignment required. Of course, I used my PowerBook G3 running OS X 10.3.9 to demonstrate my strategy in class. Hence I found that after sending a SIGKILL to my process, it would "stop running" but would still be "there".
My strategy relied on the fact that a process can be ptraced by only one other process at a time; The init process cannot be ptraced; orphaned processes are adopted by init; and a process can request to its parent to ptrace it.
I am posting the code here in case anyone wants to play with it.
<pre>
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/ptrace.h>
#include <unistd.h>
#include <errno.h>
#include <signal.h>
int main(void)
{
pid_t p;
printf("Starting parent...\n");
if ((p = fork()) == -1) /* an error has occurred */
{
perror(NULL);
exit(0);
}
if (p > 0) /* This is the parent process */
{
exit(0);
}
if (p == 0) /* This is the child */
{
while(getppid() != 1); /* spinlock until parent exits */
printf("This is the child running...\n");
printf("Calling ptrace(PTRACE_TRACEME). "
"The parent is not expecting it\n");
/* ptrace failed */
if (ptrace(PT
TRACEME, NULL, NULL, NULL) == -1)
{
perror("TRACEME failed");
fprintf(stderr, "exiting\n");
exit(1);
}
else
{
printf("TRACEME worked!\n");
printf("parent prosses is %d\n", getppid());
printf("this process is %d\n", getpid());
int i = 0;
while(1)
{
printf("count %d: ppid(%d), pid(%d)\n",
i++, getppid(), getpid());
sleep(1);
}
}
}
return 0;
}
</pre>
The kill -KILL <pid> makes the process stop its output. But doesn't really get rid of the process.
Here is the code that attempts to attach to a process:
<pre>
#include <stdio.h>
#include <stdlib.h>
#include <sys/ptrace.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
int main(int argc, char *argv[])
{
int pid;
int status;
if (argc < 2)
{
printf("usage: %s <pid>\n", argv[0]);
exit(1);
}
if ((pid=atoi(argv[1])) <= 0)
{
printf("Invalid pid number: %s\n", argv[1]);
exit(1);
}
printf("attach pid: %d\n", getpid());
if (ptrace(PT_ATTACH, pid, NULL, NULL) == -1)
{
perror("\n\n
**** Attach failed");
}
else
{
printf("Waiting for the signal that we've attached\n");
waitpid(pid,&status,0);
printf("Attached to pid [%d], "
"hit enter to continue...\n", pid);
getchar();
if (ptrace(PT_DETACH, pid, NULL, NULL) == -1)
{
perror("\n\n
**** Detach failed");
}
}
return 0;
}
</pre>
The man pages for ptrace states that a process should not call ptrace(PT
TRACEME) if the parent is not expecting it. However, it didn't have any side effects on Linux.
Anyway, I would expect that sending a SIGKILL to a precess would terminate it, unless you don't have privileges over the process. I guess that's not the case on Darwin. Somehow the SIGKILL is being traped alogn the way even tough the man page for signal states that SIGKILL and SIGSTOP cannot be caught, ignored or generate an interrupt. Or something else is going on with that process.
I just wonder what are the implications of not being able to kill a process which you have ownership.
Antonello
PS: I just tested and if the call ptrace(PT
TRACEME) is removed the process can be killed.