Killing with ptrace
I found myself wanting to kill a process under Linux, but have it
exit with a zero return code, so that its parent thought that it had
no error. Impossible using kill
directly, but possible
with ptrace
. The use case was stopping a component of
the Debian/Ubuntu install system such that it would not be rerun as
a failure.
The idea is to use ptrace
to attach to the process, to
stop the process, alter the next instruction pointed to by the
instruction pointer to syscall
, and to set the
registers to indicate a function of 60 (exit), with a return code,
held in %rdi, of zero. The traced process is then resumed.
The code will run as root, or will run against one's own processes
if /proc/sys/kernel/yama/ptrace_scope
is zero, which it
is not on most modern Linux distributions. It assumes x86_64, and
therefore that the op code for syscall is 0x0f05, and that storage
is little-endian, so that a word of any length set to 0x050f will be
written to memory as 0x0f, followed by 0x05, probably followed by
several bytes of zeroes.
The code can be considered a very basic demonstration of
what ptrace
is capable of.
#include<stdio.h> #include<stdlib.h> #include<sys/ptrace.h> #include<sys/types.h> #include<sys/wait.h> #include<sys/user.h> #include<errno.h> int main(int argc, char * argv[]){ pid_t pid; long err; int status; struct user_regs_struct regs; pid=atoi(argv[1]); err=ptrace(PTRACE_ATTACH,pid,NULL,NULL); if (err) {perror(NULL); exit(1);} fprintf(stderr,"Successfully attached\n"); waitpid(pid,&status,WUNTRACED); fprintf(stderr,"Wait over\n"); ptrace(PTRACE_GETREGS, pid, NULL, ®s); if (err) {perror(NULL); exit(1);} fprintf(stderr,"Registers fetched\n"); regs.rax=60; regs.rdi=0; ptrace(PTRACE_SETREGS, pid, NULL, ®s); if (err) {perror(NULL); exit(1);} fprintf(stderr,"Registers set\n"); ptrace(PTRACE_POKETEXT, pid, (void*)regs.rip, (void*)0x050f); ptrace(PTRACE_DETACH, pid, NULL, NULL); fprintf(stderr,"Target resumes\n"); exit(0); }
To demonstrate its use, try typing
sleep 100; echo $?
in one window, and in another find the PID of the sleep command, and
kill it. If killed with simply kill
, the response in the first
window will be
Terminated 143
whereas if killed with this trick, the response will be simply
0
showing that the sleep exited with no error. (Note that the error
code on exiting due to a signal is
128 + signal number. Here the signal number is 15,
being SIGTERM, the default signal sent by kill
, so the
return code is 143.)