I can't figure out why the function returns an "No such process" error message every time I run it, but simply using the same instructions inline produces the required output.
#include <sys/ptrace.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/user.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
void getregs(pid_t proc, struct user_regs_struct *regs);
int main() {
pid_t proc = fork();
if(proc == 0) {
if(ptrace(PTRACE_TRACEME, 0, NULL, NULL) == -1) {
perror("traceme");
exit(0);
}
if(execl("child", "child", NULL) == -1) {
perror("execl");
exit(0);
}
} else {
wait(&proc);
struct user_regs_struct regs;
ptrace(PTRACE_GETREGS, proc, NULL, ®s);
printf("eax: %08x\n", (unsigned int)regs.eax);
getregs(proc, ®s);
ptrace(PTRACE_CONT, proc, NULL, NULL);
}
return 0;
}
void getregs(pid_t proc, struct user_regs_struct *regs) {
if(ptrace(PTRACE_GETREGS, proc, NULL, ®s) == -1) {
perror("GETREGS");
exit(1);
}
printf("eax: %08x\n", (unsigned int)regs->eax);
}
When I run this I get
~$ ./tracer
eax: 0000002f
GETREGS: No such process
I don't get why getregs() returns that error. It's almost like it is outside scope of something?
Also, something a little unrelated: EAX is always set to 0000002f no matter what process I try to execl(). Is this natural? I don't know if i'm forking the child process properly or not. Would I need to make a new question on SO for this?
You hit this error because you are modifying the value of the process identifier (PID) contained in the variable proc by passing its address to the wait(2) syscall.
The wait syscall will change the value of proc with the return status of your child process upon its termination. So when you reference your child process in ptrace using proc, its value will likely be invalid and referencing no existing processes.
And as #lornix noticed, make sure that you pass the right pointer to ptrace in the getregs function.
void getregs(pid_t proc, struct user_regs_struct *regs) {
if(ptrace(PTRACE_GETREGS, proc, NULL, ®s) == -1) {
You need to dereference regs in the ptrace call. (remove & in this case)
if(ptrace(PTRACE_GETREGS, proc, NULL, regs) == -1) {
you're calling getregs with the ADDRESS of regs, so getregs' regs is not a structure like in the main code, it's a pointer to a structure.
EDIT: figured it out
You're using/reassigning proc in the wait call, shouldn't do that. The parameter to wait is a status value, not the pid of a particular child. Wait waits for any child, see waitpid for a pid specific wait.
Try:
int wait_status;
wait(&wait_status);
in place of the current wait function call.
Both your ptrace calls are behaving the same way. The difference is that you're ignoring the return value of the inline one, whereas the one in the function is checked.
The EAX value is a red herring: the structure is not initialized because the PTRACE_GETREGS failed.
The wait function does not take a process ID. It waits for some process to terminate and puts its status into the integer value that is passed in by pointer.
You want waitpid (if you want to wait for a specific child process). The simple function wait is useful when you know there is only one:
int status;
if (wait(&status)) { ... }
Related
I am currently recode the Strace command.
I understand the goal of this command and I can catch some syscalls from an exectuable file.
My question is : Why I don't catch the "write" syscall ?
this is my code :
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/ptrace.h>
#include <sys/user.h>
#include <wait.h>
int main(int argc, char* argv[]) {
int status;
pid_t pid;
struct user_regs_struct regs;
int counter = 0;
int in_call =0;
switch(pid = fork()) {
case -1:
perror("fork");
exit(1);
case 0:
ptrace(PTRACE_TRACEME, 0, NULL, NULL);
execvp(argv[1], argv + 1);
break;
default:
wait(&status);
while (status == 1407) {
ptrace(PTRACE_GETREGS, pid, NULL, ®s);
if(!in_call) {
printf("SystemCall %lld called with %lld, %lld, %lld\n",regs.orig_rax,
regs.rbx, regs.rcx, regs.rdx);
in_call=1;
counter ++;
}
else
in_call = 0;
ptrace(PTRACE_SYSEMU, pid, NULL, NULL);
wait(&status);
}
}
printf("Total Number of System Calls = %d\n", counter);
return 0;
}
This is the output using my program :
./strace ./my_program
SystemCall 59 called with 0, 0, 0
SystemCall 60 called with 0, 4198437, 5
Total Number of System Calls = 2
59 represents the execve syscall.
60 represents the exit syscall.
This is the output using the real strace :
strace ./my_program
execve("./my_program", ["./bin_asm_write"], 0x7ffd2929ae70 /* 67 vars */) = 0
write(1, "Toto\n", 5Toto
) = 5
exit(0) = ?
+++ exited with 0 +++
As you can see, my program don't catch the write syscall.
I don't understrand why, do you have any idea ?
Thank You for your answer.
Your while loop is set up rather strangely -- you have this in_call flag that you toggle back and forth between 0 and 1, and you only print the system call when it is 0. The net result is that while you catch every system call, you only print every other system call. So when you catch the write call, the flag is 1 and you don't print anything.
Another oddness is that you're using PTRACE_SYSEMU rather than PTRACE_SYSCALL. SYSEMU is intended for emulating system calls, so the system call won't actually run at all (it will be skipped); normally your ptracing program would do whatever the systme call is supposed to do itself and then call PTRACE_SETREGS to set the tracee's registers with the appropriate return values before calling PTRACE_SYSEMU again to run to the next system call.
Your in_call flagging would make more sense if you were actually using PTRACE_SYSCALL, as that will stop twice for each syscall -- once on entry to the syscall and a second time when the call returns. However, it will also stop for signals, so you need to be decoding the status to see if a signal has occurred or not.
While reading up and learning about signals, I found a program, that uses signals in a specific way. I tried understand it, but I am not sure, how all the parts of the code interact with another.
Below is the above mentioned code and I added comments, where I have difficulties:
#include <dirent.h>
#include <errno.h>
#include <fcntl.h>
#include <netinet/in.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#define CP 5
static volatile int curprocs =0; ;
static void die() {
exit(EXIT_FAILURE);
}
static void chldhandler() {
int e = errno;
// Why do we use waitpid here? What does it do?
while(waitpid(-1, NULL, WNOHANG) > 0) {
curprocs--;
}
errno = e;
}
void do_work() {
time_t t;
srand((unsigned) time(&t));
sleep(5+ rand() % 4);
}
int main() {
struct sigaction sa = {
.sa_handler = chldhandler,
.sa_flags = SA_RESTART,
};
sigemptyset(&sa.sa_mask);
if (sigaction(SIGCHLD, &sa, NULL) == -1) {
exit(-1);
}
while(1) {
sigset_t chld, empty;
sigemptyset(&empty);
sigemptyset(&chld);
sigaddset(&chld, SIGCHLD);
// What do the following lines of code do??
sigprocmask(SIG_BLOCK, &chld, NULL);
while (curprocs >= CP) { // cap for the number of child processes
sigsuspend(&empty);
}
curprocs++;
sigprocmask(SIG_UNBLOCK, &chld, NULL);
pid_t p = fork();
if (p == -1) {
return -1;
}
if (p == 0) {
// code for the child processes to execute
do_work();
die();
} else {
// Parent process does nothing
}
}
return 0;
}
Obviously above program is intended to have a max amount of 42 child processes doing work. Whenever we want to have a new child process, we use fork, and adjust curprocs.
Whenever a child process finishes, chldhandler() is called and curprocs is adjusted as well.
However I don't understand the interplay of the two sigproc_mask, sigsuspend, waitpid and our two signalsets chld, empty.
Can someone explain what these lines do or why they are used the way they are?
sigprocmask(SIG_BLOCK, &chld, NULL); blocks SIGCHLD so that you can be sure that while you do while (curprocs >= 42) the SIGCHLD handler won't interrupt the code, changing curprocs in the middle of the check.
sigsuspends atomically unblocks it and waits for a SIGCHLD (any signal really, since your passing an empty mask), atomically reblocking it when it returns.
The waitpid(-1,/*...*/) in the handler reaps the status of any (that's what the -1 means) child that has a status change (typically termination notification) pending so that the data the kernel associates with the status change can be freed. The second argument would be where the status change info would go but since you passed NULL, the info will simply be dropped. WNOHANG means don't wait if there aren't any more status change notifications.
Since the handler is run in response to SIGCHLD, there should be at least one status change notification, but there could be more because SIGCHLDs can coalesce (it's also possible there isn't any — if you called waitpid from normal context while SIGCHLD was blocked). That's why the handler loops. The WNOHANG is important because without it, after all the status change notifications have been reaped, the waitpid call would block your process until a new notification arrived.
My question is: after finishing the execution of the new process image, the function
execl()
would return the execution to the caller process or to the father process?
When using one of the exec family of functions, you do not expect the function to return at all. The program counter begins at the first instruction of the binary image that replaced the calling process.
From the Darwin man page:
If any of the exec() functions returns, an error will have occurred.
The return value is -1, and the global variable errno will be set to
indicate the error.
There was a comment asking about the following, but it was deleted:
If you are in a child process, and execl succeeds, then the child process is replaced by the new binary. If it fails, then control returns to that child process (the caller). There's no strict relationship between fork and exec, if that's what you're asking. If you are in a child process, and exec fails, then you have a "forked" child process, which is a copy of the original parent process. At this point you probably want to print some error message and exit from the child process.
If you want to know why it failed, you can use the following pattern:
if (execl(...)) {
perror(NULL);
exit(errno);
}
For example, try running this program, the error message will indicate how to fix the program:
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main (const int argc, char * const argv[]) {
if (execl("ls", "ls", "-la", NULL)) {
perror(NULL);
exit(errno);
}
return 0;
}
The solution, use execlp instead of execl in this case.
I was going through an article here and was trying out the code snippet I have copied out below :-
#include <sys/ptrace.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <linux/user.h> /* For constants
ORIG_EAX etc */
int main()
{ pid_t child;
long orig_eax;
child = fork();
if(child == 0) {
ptrace(PTRACE_TRACEME, 0, NULL, NULL);
execl("/bin/ls", "ls", NULL);
}
else {
wait(NULL);
orig_eax = ptrace(PTRACE_PEEKUSER,
child, 4 * ORIG_EAX,
NULL);
printf("The child made a "
"system call %ld\n", orig_eax);
ptrace(PTRACE_CONT, child, NULL, NULL);
}
return 0;
}
I have a doubt regarding what ORIG_EAX is exactly and why 4*ORIG_EAX is passed onto the ptrace call. I initially assumed that ORIG_EAX, EBX, ECX etc would be the offsets into a particular structure where the values of the registers would be stored.
So I decided to print the value of ORIG_EAX just after the wait by using printf("origeax = %ld\n", ORIG_EAX);. The value was 11. So, my earlier assumption regarding the offsets was wrong.
I understand that the wait call is terminated when the child has a state change(in this case, issues a system call) and that ORIG_EAX would contain the system call number.
However, why is ORIG_EAX * 4 passed onto the ptrace call?
The parameter is an offset into the user_regs_struct. Note that each of these is an unsigned long, so to get the 11th entry (orig_eax) the offset in bytes is 44, (provided you're on an x86 machine of course).
I would like to create a file whose descriptor would have some customizable behavior. In particular, I'd like to create a file descriptor, which, when written to, would prefix every line, with name of the process and pid (and maybe time), but I can imagine it can be useful to do other things.
I don't want to alter the writing program - for one thing, I want it to work for all programs on my system, even shell/perl/etc. scripts, and it would be impractical if not impossible to change the source code of everything.
Note that pipes wouldn't do in this case, because when the writing process fork()s, the newly created child shares the fd and is indistinguishable from its parent by the reading end of the pipe.
There are approaches which would do, but I think they are rather clumsy:
Create a kernel module that will create such fds. For example, you could open some /dev/customfd and then instruct the module to do some transformation etc. or send data to userspace or socket etc.
Use LD_PRELOAD that will override the fd manipulation functions and do these kinds of things on the "special" fd.
However, both of these approaches are quite laborious, so I would like to know if there is a better way, or any infrastructure (like off-the-shelf libraries) that would help.
I'd prefer a solution which doesn't involve kernel changes, but I'm ready to accept them if necessary.
Just an idea: would FUSE be an answer?
You have a lot of options , as you mentioned using the LD_PRELOAD wrapping the write()/read() functions is a good approach.
I recommend you to use unix ptrace(2) to caught the desired system call and pass the arguments to your own function.
Example :
#include <sys/ptrace.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <linux/user.h>
#include <sys/syscall.h> /* For SYS_write etc */
int main()
{ pid_t child;
long orig_eax, eax;
long params[3];
int status;
int insyscall = 0;
child = fork();
if(child == 0) {
ptrace(PTRACE_TRACEME, 0, NULL, NULL);
execl("/bin/ls", "ls", NULL);
}
else {
while(1) {
wait(&status);
if(WIFEXITED(status))
break;
orig_eax = ptrace(PTRACE_PEEKUSER,
child, 4 * ORIG_EAX, NULL);
if(orig_eax == SYS_write) {
if(insyscall == 0) {
/* Syscall entry */
insyscall = 1;
params[0] = ptrace(PTRACE_PEEKUSER,
child, 4 * EBX,
NULL);
params[1] = ptrace(PTRACE_PEEKUSER,
child, 4 * ECX,
NULL);
params[2] = ptrace(PTRACE_PEEKUSER,
child, 4 * EDX,
NULL);
printf("Write called with "
"%ld, %ld, %ld\n",
params[0], params[1],
params[2]);
}
else { /* Syscall exit */
eax = ptrace(PTRACE_PEEKUSER,
child, 4 * EAX, NULL);
printf("Write returned "
"with %ld\n", eax);
insyscall = 0;
}
}
ptrace(PTRACE_SYSCALL,
child, NULL, NULL);
}
}
return 0;
}