From the man page:
PTRACE_GET_SYSCALL_INFO (since Linux 5.3)
Retrieve information about the system call that caused the stop. The information is placed into the buffer pointed by the data argument, which should be a pointer to a buffer of type struct ptrace_syscall_info. The addr argument contains the size of the buffer pointed to by the data argument (i.e., sizeof(struct ptrace_syscall_info)). The return value contains the number of bytes available to be written by the kernel. If the size of the data to be written by the kernel exceeds the size specified by the addr argument, the output data is truncated.
>cat /proc/version
Linux version 5.3.0-kali2-amd64 (devel@kali.org) (gcc version 9.2.1 20191109 (Debian 9.2.1-19)) #1 SMP Debian 5.3.9-3kali1 (2019-11-20)
>
Now, I have this small program I've written, consisting of two files.
helpers.h
#ifndef HELPERS
#define HELPERS
//helpers.h
#include <errno.h>
#include <sys/ptrace.h>
#include <sys/types.h>
const char* op_to_string(__u8 op)
{
switch (op) {
case (PTRACE_SYSCALL_INFO_ENTRY):
return "SYSCALL_INFO_ENTRY";
break;
case (PTRACE_SYSCALL_INFO_EXIT):
return "SYSCALL_INFO_EXIT";
break;
case (PTRACE_SYSCALL_INFO_SECCOMP):
return "SYSCALL_INFO_SECCOMP";
break;
default:
fprintf(stderr, "op-to-string: Invalid op code");
}
}
void err_wrap(const int ret, const int success, const char *msg)
{
if (ret != success)
{
perror(msg);
}
}
void print_regs(const struct user_regs_struct regs) {
printf ("r15: %16llx %30s\n", regs.r15, "general purpose registers");
printf ("r14: %16llx\n", regs.r14);
printf ("r13: %16llx\n", regs.r13);
printf ("r12: %16llx\n", regs.r12);
printf ("rbp: %16llx\n", regs.rbp);
printf ("rbx: %16llx\n", regs.rbx);
printf ("r11: %16llx\n", regs.r11);
printf ("r10: %16llx\n", regs.r10);
printf ("r9: %16llx %s\n", regs.r9, "6.");
printf ("r8: %16llx %s\n", regs.r8, "5.");
printf ("rax: %16llx\n", regs.rax);
printf ("rcx: %16llx %s\n", regs.rcx, "4.");
printf ("rdx: %16llx %s\n", regs.rdx, "3.");
printf ("rsi: %16llx %s\n", regs.rsi, "2.");
printf ("rdi: %16llx %30s\n", regs.rdi, "1. function/syscall argument"); // aka "parameter registers"
printf ("orig_rax:%16llx\n", regs.orig_rax);
printf ("rip: %16llx %30s\n", regs.rip, "instruction pointer");
printf ("cs: %16llx\n", regs.cs);
printf ("eflags: %16llx\n", regs.eflags);
printf ("rsp: %16llx %30s\n", regs.rsp, " Stack Pointer (current location in stack)");
printf ("ss: %16llx\n", regs.ss);
printf ("fs_base: %16llx\n", regs.fs_base);
printf ("gs_base: %16llx\n", regs.gs_base);
printf ("ds: %16llx\n", regs.ds);
printf ("es: %16llx\n", regs.es);
printf ("fs: %16llx\n", regs.fs);
printf ("gs: %16llx\n", regs.gs);
}
void print_syscall_info(struct ptrace_syscall_info sys_info) {
__u8 op = sys_info.op;
printf("Type of system call stop: %20s (%x)\n", op_to_string(op), op);
printf("Arch: %16llx\n", sys_info.arch);
}
#endif
tracetest.c
#include <stdio.h>
#include <sys/wait.h>
#include <signal.h>
#include <unistd.h>
#include <sys/user.h>
#include <syscall.h>
#include "helpers.h"
// long ptrace(enum __ptrace_request request, pid_t pid,
// void *addr, void *data);
void child_code();
void parent_code(pid_t pid);
int main(const int argc, char *argv[])
{
pid_t pid;
switch (pid = fork())
{
case -1:
perror("fork");
break;
case 0:
child_code();
break;
default: //parent code
parent_code(pid);
}
return 0;
}
void parent_code(pid_t pid)
{
printf("Parent code\n");
int status;
if (wait(&status) == -1)
{
perror("parent wait one");
}
printf("Finished waiting\n");
printf("PID wait status is: %llx\n", status);
printf("My PID is %d\n", getpid());
err_wrap(
ptrace(PTRACE_SETOPTIONS, pid, 0, PTRACE_O_EXITKILL),
0, "ptrace-set-options");
struct user_regs_struct regs;
struct ptrace_syscall_info syscall_info;
while(1)
{
err_wrap(ptrace(PTRACE_SYSCALL, pid, 0, 0), 0, "ptrace-syscall first"); //Stop at next syscall.
if (wait(&status) == -1)
{
perror("parent wait one");
}
printf("syscall-entry-stop\n");
err_wrap(
ptrace(PTRACE_GET_SYSCALL_INFO, pid, sizeof(struct ptrace_syscall_info), &syscall_info),
0, "ptrace-get-syscall-info");
err_wrap(
ptrace(PTRACE_GETREGS, pid, 0, ®s),
0,
"ptrace-getregs");
print_regs(regs);
print_syscall_info(syscall_info);
}
}
void child_code() {
printf("Child code\n");
printf("Parent is: %d\n", getppid());
err_wrap(ptrace(PTRACE_TRACEME, 0,0,0), 0, "ptrace-traceme");
err_wrap(raise(SIGSTOP), 0, "raise");
unsigned int pleb = 0xffbbcde8;
int x = 0;
printf("Hello WoRld\n");
printf("Pleb is: %llx\n", pleb);
printf("x is: %d\n", x);
}
All things considered, my code should run normally without errors, but it seems like the PTRACE_GET_SYSCALL_INFO stuff isn't actually included, as gcc is complaining:
>gcc -g -o tracetest tracetest.c
In file included from tracetest.c:10:
helpers.h:9:26: error: unknown type name ‘__u8’
9 | const char* op_to_string(__u8 op)
| ^~~~
helpers.h:72:32: warning: ‘struct ptrace_syscall_info’ declared inside parameter list will not be visible outside of this definition or declaration
72 | void print_syscall_info(struct ptrace_syscall_info sys_info) {
| ^~~~~~~~~~~~~~~~~~~
helpers.h:72:52: error: parameter 1 (‘sys_info’) has incomplete type
72 | void print_syscall_info(struct ptrace_syscall_info sys_info) {
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~^~~~~~~~
helpers.h: In function ‘print_syscall_info’:
helpers.h:73:2: error: unknown type name ‘__u8’
73 | __u8 op = sys_info.op;
| ^~~~
helpers.h:74:54: warning: implicit declaration of function ‘op_to_string’ [-Wimplicit-function-declaration]
74 | printf("Type of system call stop: %20s (%x)\n", op_to_string(op), op);
| ^~~~~~~~~~~~
tracetest.c: In function ‘parent_code’:
tracetest.c:55:29: error: storage size of ‘syscall_info’ isn’t known
55 | struct ptrace_syscall_info syscall_info;
| ^~~~~~~~~~~~
tracetest.c:66:11: error: ‘PTRACE_GET_SYSCALL_INFO’ undeclared (first use in this function); did you mean ‘PTRACE_GETSIGINFO’?
66 | ptrace(PTRACE_GET_SYSCALL_INFO, pid, sizeof(struct ptrace_syscall_info), &syscall_info),
| ^~~~~~~~~~~~~~~~~~~~~~~
| PTRACE_GETSIGINFO
tracetest.c:66:11: note: each undeclared identifier is reported only once for each function it appears in
tracetest.c:66:48: error: invalid application of ‘sizeof’ to incomplete type ‘struct ptrace_syscall_info’
66 | ptrace(PTRACE_GET_SYSCALL_INFO, pid, sizeof(struct ptrace_syscall_info), &syscall_info),
| ^~~~~~
However, if I #include <linux/ptrace.h>
, then I don't get these errors, but instead it says implicit declaration of function ptrace.h, which to be honest I think isn't very good either.
What am I supposed to be including to make this function as intended? I also included sys/types.h
, but I'm still getting errors about __u8
being an unknown type name, but it's a type used in the ptrace.c source code, so it shouldn't be throwing any errors.
The top of the ptrace man page says:
#include <sys/ptrace.h>
long ptrace(enum __ptrace_request request, pid_t pid,
void *addr, void *data);`
So I would have thought this single include would be enough, but it's not working.
Thanks in advance.