Recoding Strace, why I can't catch the "write syscall ?" - c

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, &regs);
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.

Related

How to re-develop the sys-function "system()" cleanly?

I have to develop my own C function system. To do that, I use the call-system fork to create a child process and it has to execute the command given to system, calling exec.
What I've wrote seems to work fine (it compiles and executes without any error).
The problem concerns my function system's return (called mySystem in my code). For example, in my child process, if I give an nonexistent shell to exec (the latter returns -1), my child process stops with an exit code of -1 because I told it to do that. But : my parent process, which retrieves this exit code thanks to a wait(&status), returns... 255 and not -1 !
I don't understand why. I paid attention to use the macro WEXISTATUS in my mySystem's return.
Could you help me to know why my parent process doesn't return -1 (the exit code of its child process) please ? Thank you in advance.
My src (there are a lot of comments) :
#include <stdio.h>
#include <unistd.h>
#include <sys/wait.h>
#include <stdlib.h>
pid_t pid;
int mySystem(char*);
int main(int argc, char* argv[]) {
int result = mySystem("ls");
fprintf(stdout, "%i", result);
return 0;
}
int mySystem(char* command) {
pid = fork();
if(pid == -1) {
perror("Fork");
return -1; // An error occurred => return -1
} else if (pid == 0) { // The child process will do the following
execl("BLABLABLABLA MAUVAIS SHELL BLABLABLAA", "sh", "-c", command, NULL); // If this call doesn't fail, the following lines are not read
perror("Exec"); // If (and only if) execl couldn't be called (bad shell's path, etc.)...
exit(-1); // ..., we stop the child process and this one has an exit code equaled to -1
}
/*
* NOW, the child process ended because... :
* 1. Either because of our "exit(-1)" after the "perror" (our source-code)
* 2. OR because of an "exit(-1") of the command passed into the execl (source-code of the execl's command)
* 3. OR because of the "exit(0)" of the command passed into the execl (source-code of the execl's command)
*/
// The parent process will execute the following lines (child process ended)
int status = -1;
if(wait(&status) == -1) { // We store into the var 'status' the exit code of the child process : -1 or 0
perror("Wait"); // Note that because we have only one process child, we don't need to do : while(wait(&status) > 0) {;;}
return -1;
}
return WEXITSTATUS(status); // Our function mySystem returns this exit code
}
Look at the following picture:
Where each of the 2 blocks is 8 bits (so total 16 bits).
Now you pass exit(-1) which in binary using 8 bits is: 11111111 (the two-complement) that's why you get 255 using WEXITSTATUS(status).
Another example to be clear: let's suppose to call exit(-6), -6 in binary two-complement is 11111010 that correspond to 250, if you make your program run you will see 250 printed on stdout.

Process fork isn't executing desired code

So I'm trying to execute this code given to me by my professor. It's dead simple. It forks, checks to see if the forking works properly, then executes another bit of code in a separate file.
For some reason, on my OS X 10.9.5 machine, it's failing to execute the second bit of code. Here are both of the programs:
exercise.c
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>
int main() {
pid_t child = fork();
if ((int)child < 0) {
fprintf(stderr, "fork error!\n");
exit(0);
} else if ((int)child > 0) {
int status;
(void) waitpid(child, &status, 0);
if (WIFEXITED(status)) {
printf("child %d exited normally and returned %d\n",
child, WEXITSTATUS(status));
} else if (WIFSIGNALED(status)) {
printf("\nchild %d was killed by signal number %d and %s leave a core dump\n",
child, WTERMSIG(status), (WCOREDUMP(status) ? "did" : "didn't"));
} else {
printf("child %d is dead and I don't know why!\n", child);
}
} else {
char *argv[] = { "./getcode" };
execve(argv[0], argv, NULL);
}
return 0;
}
And getcode.c
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdlib.h>
int main() {
int rc = 256;
printf("I am child process %d\n", getpid());
while ((rc > 255) || (rc < 0)) {
printf("please type an integer from 0 to 255: ");
scanf("%d", &rc);
}
exit(rc);
}
I compile both with the commands:
gcc -Wall -pedantic-errors exercise.c -o exercise
and
gcc -Wall -pedantic-errors getcode.c -o getcode
Unfortunately, the only thing I get back from the child process is a return code of 0
./exercise
child 903 exited normally and returned 0
I'm baffled. Can anyone help?
EDIT: Okay, so I included perror("execve") as requested, and it returns execve: Bad address. So how can I fix that?
EDIT2: All right. I fixed it. I've changed the bit of the above code to include this:
char *argv[] = { "./getcode",NULL };
execve(argv[0], argv, NULL);
Null termination fixes the argv issues.
You need to terminate argv with a NULL element. From the execve man page:
Both argv and envp must be terminated by a NULL pointer.
Also it is not clear that NULL is valid for the envp argument. The Linux man page says
On Linux, argv can be specified as NULL, which has the same effect as specifying this argument as a pointer to a list containing a single NULL pointer. Do not take advantage of this misfeature! It is nonstandard and nonportable: on most other UNIX systems doing this will result in an error (EFAULT).
Possibly specifying envp as NULL is similarly nonstandard. Use execv not execve if you don't need to specify an environment.
You should check the return value of execve. And use errno to determine the cause. Eg., use perror("execve") It may be complaining.
You're not checking the result of the execve call, so I suspect it's failing, and the child process is reaching the return 0 at the end of main.

Why does ptrace not recognize the function?

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, &regs);
printf("eax: %08x\n", (unsigned int)regs.eax);
getregs(proc, &regs);
ptrace(PTRACE_CONT, proc, NULL, NULL);
}
return 0;
}
void getregs(pid_t proc, struct user_regs_struct *regs) {
if(ptrace(PTRACE_GETREGS, proc, NULL, &regs) == -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, &regs) == -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)) { ... }

(ORIG_EAX*4) in ptrace calls

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).

How to play with ptrace on x86-64?

I'm following the tutorial here, and modified a little for x86-64(basically replace eax to rax,etc) so that it compiles:
#include <sys/ptrace.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <sys/user.h>
#include <sys/reg.h>
#include <unistd.h>
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_RAX,
NULL);
printf("The child made a "
"system call %ld\n", orig_eax);
ptrace(PTRACE_CONT, child, NULL, NULL);
}
return 0;
}
But it doesn't actually work as expected, it always says:
The child made a system call -1
What's wrong in the code?
ptrace returns -1 with errno EIO because what you're trying to read is not correctly aligned. Taken from ptrace manpage:
PTRACE_PEEKUSER
Reads a word at offset addr in the child's USER area, which
holds the registers and other information about the process (see
<sys/user.h>). The word is returned as the result of the
ptrace() call. Typically the offset must be word-aligned,
though this might vary by architecture. See NOTES. (data is
ignored.)
In my 64-bits system, 4 * ORIG_RAX is not 8-byte-aligned. Try with values such 0 or 8 and it should work.
In 64 bit = 8 * ORIG_RAX
8 = sizeof(long)

Resources