0

I am trying to do a simple thing (just for learning), I wish to intercept clock_gettime on 64 bit linux, read the output and modify it so to return a flase date/time to the tracee (/bin/date).

What I do is:

   ptrace(PTRACE_GETREGS, pid, NULL, &regs);
   if(regs.orig_rax==228){  // this is the clock_gettime syscall number in 64 bit linux
     unsigned long p1=ptrace(PTRACE_PEEKDATA, pid, regs.rcx, NULL); // rcx is ARG1
     printf("ARG1: 0x%lx\n",p1);
   }

Now if I understood correctly (clearly not) regs.rcx should point to a timespec structure, so I should read the first long int of that structure which is the time in seconds (unixtime). But I read 0.

Also, the printf is invoked twice, once entering the syscall and the second time exiting it. So ok it's normal is 0 when entering but it should not be whene exiting. Infact strace shows it correctly:

strace 2>&1 date|grep CLOCK

clock_gettime(CLOCK_REALTIME, {tv_sec=1583960872, tv_nsec=403163000}) = 0

How can I do the same?

Zibri
  • 9,096
  • 3
  • 52
  • 44
  • Note that clock_gettime is special and runs purely in user-space (code and scale factors in the VDSO page), normally not using a `syscall` instruction to enter the kernel. On my system (Arch Linux), `strace date` doesn't include any clock_gettime system calls, but apparently yours does. Anyway, how are you deciding when to do `PTRACE_GETREGS`? And why do you think RCX is the first arg? x86-64 System V uses RDI as the first arg in its calling convention for function and system calls. `syscall` itself uses RCX to save RIP and isn't a syscall arg. (And it's the 4th function arg) – Peter Cordes Mar 11 '20 at 21:17
  • I read here and there.. I am trying to figure out this... onviously my program is wrong otherwise it would work :D – Zibri Mar 11 '20 at 22:21
  • Well have a look at https://blog.packagecloud.io/eng/2016/04/05/the-definitive-guide-to-linux-system-calls/, and also single-step into `clock_gettime` with GDB. And see [What are the calling conventions for UNIX & Linux system calls on i386 and x86-64](https://stackoverflow.com/q/2535989) for the function and system calling conventions on Linux. (Not Windows where RCX *is* the first arg-passing register for function calls; maybe that's what you read?) – Peter Cordes Mar 11 '20 at 22:26
  • found it.. the reg is rsi – Zibri Mar 11 '20 at 22:29
  • Ok, so you are doing this at a breakpoint on a call to glibc's clock_gettime or something. And yes, it's the 2nd arg, not 1st: `int clock_gettime(clockid_t clk_id, struct timespec *tp);` – Peter Cordes Mar 11 '20 at 22:40
  • yep.. I did a stupid program.. it intecepts gettime and returns (for now) a fixed different time/date... faketime uses LD_PRELOAD and I never found an implementation using ptrace so I wrote it :D – Zibri Mar 11 '20 at 23:02

1 Answers1

0

found the error.. wrong register.. it was RSI

 ptrace(PTRACE_GETREGS, pid, NULL, &regs);
   if(regs.orig_rax==228){  // this is the clock_gettime syscall number in 64 bit linux
     unsigned long p1=ptrace(PTRACE_PEEKDATA, pid, regs.rsi, NULL); // rsi is ARG2
     printf("ARG2: 0x%lx\n",p1);
   }

for x86_64:

#define SYSCALL_ENTRY ((long)RET == -ENOSYS)
#define REGS_STRUCT struct user_regs_struct

#define SYSCALL (regs.orig_rax)
#define ARG1 (regs.rdi)
#define ARG2 (regs.rsi)
#define ARG3 (regs.rdx)
#define ARG4 (regs.r10)
#define ARG5 (regs.r8)
#define ARG6 (regs.r9)
#define RET (regs.rax)
Zibri
  • 9,096
  • 3
  • 52
  • 44