0

I was looking at the glibc implementation of ptrace, I understand what's going on until +44(canary setup), but I don't understand why we put the adress rsp+70 in rax. Isn't this adress owned by the calling function, since we only allocated 0x68 bytes (with sub rsp, 0x68)so it is one qword past the saved return adress ? And what do we do with it anyways ?

    0x00007f60a9dade00 <+0>:     sub    $0x68,%rsp
   0x00007f60a9dade04 <+4>:     lea    -0x1(%rdi),%r8d
   0x00007f60a9dade08 <+8>:     mov    %rsi,0x38(%rsp)
   0x00007f60a9dade0d <+13>:    lea    0x8(%rsp),%r10
   0x00007f60a9dade12 <+18>:    mov    %rdx,0x40(%rsp)
   0x00007f60a9dade17 <+23>:    mov    %rcx,0x48(%rsp)
   0x00007f60a9dade1c <+28>:    mov    %fs:0x28,%rax
   0x00007f60a9dade25 <+37>:    mov    %rax,0x28(%rsp)
   0x00007f60a9dade2a <+42>:    xor    %eax,%eax
   0x00007f60a9dade2c <+44>:    lea    0x70(%rsp),%rax
   0x00007f60a9dade31 <+49>:    cmp    $0x3,%r8d
   0x00007f60a9dade35 <+53>:    movl   $0x8,0x10(%rsp)
   0x00007f60a9dade3d <+61>:    mov    %rax,0x18(%rsp)
   0x00007f60a9dade42 <+66>:    lea    0x30(%rsp),%rax
   0x00007f60a9dade47 <+71>:    mov    0x8(%rax),%esi
   0x00007f60a9dade4a <+74>:    cmovae %rcx,%r10
   0x00007f60a9dade4e <+78>:    mov    %rax,0x20(%rsp)
   0x00007f60a9dade53 <+83>:    mov    $0x65,%eax
   0x00007f60a9dade58 <+88>:    syscall 
   0x00007f60a9dade5a <+90>:    cmp    $0xfffffffffffff000,%rax
   0x00007f60a9dade60 <+96>:    ja     0x7f60a9dadea0 <ptrace+160>
   0x00007f60a9dade62 <+98>:    test   %rax,%rax
   0x00007f60a9dade65 <+101>:   js     0x7f60a9dade6d <ptrace+109>
   0x00007f60a9dade67 <+103>:   cmp    $0x2,%r8d
   0x00007f60a9dade6b <+107>:   jbe    0x7f60a9dade88 <ptrace+136>
   0x00007f60a9dade6d <+109>:   mov    0x28(%rsp),%rcx
   0x00007f60a9dade72 <+114>:   sub    %fs:0x28,%rcx
   0x00007f60a9dade7b <+123>:   jne    0x7f60a9dadeb5 <ptrace+181>
   0x00007f60a9dade7d <+125>:   add    $0x68,%rsp
   0x00007f60a9dade81 <+129>:   ret    
   0x00007f60a9dade82 <+130>:   nopw   0x0(%rax,%rax,1)
   0x00007f60a9dade88 <+136>:   mov    0xc7fb9(%rip),%rax        # 0x7f60a9e75e48
   0x00007f60a9dade8f <+143>:   movl   $0x0,%fs:(%rax)
   0x00007f60a9dade96 <+150>:   mov    0x8(%rsp),%rax
   0x00007f60a9dade9b <+155>:   jmp    0x7f60a9dade6d <ptrace+109>
   0x00007f60a9dade9d <+157>:   nopl   (%rax)
   0x00007f60a9dadea0 <+160>:   mov    0xc7fa1(%rip),%rdx        # 0x7f60a9e75e48
   0x00007f60a9dadea7 <+167>:   neg    %eax
   0x00007f60a9dadea9 <+169>:   mov    %eax,%fs:(%rdx)
   0x00007f60a9dadeac <+172>:   mov    $0xffffffffffffffff,%rax
   0x00007f60a9dadeb3 <+179>:   jmp    0x7f60a9dade6d <ptrace+109>
   0x00007f60a9dadeb5 <+181>:   call   0x7f60a9dc4d10 <__stack_chk_fail>
Jester
  • 56,577
  • 4
  • 81
  • 125
Aaa Bbb
  • 627
  • 4
  • 12
  • 1
    It's accessing extra arguments on the stack. – Jester Dec 14 '21 at 19:49
  • 1
    @Jester: But Linux glibc `ptrace(enum __ptrace_request request, pid_t pid, void *addr, void *data);` only takes 4 int/pointer args, so they all fit in regs. You're right that `lea 0x70(%rsp)` is getting a pointer to the stack slot above the return address, though. But that's not the only weird thing: there's an `xor %eax,%eax` right before LEA writes RAX. A compiler would never do that. – Peter Cordes Dec 14 '21 at 20:48
  • 1
    This might be something to do with "C library/kernel differences" https://man7.org/linux/man-pages/man2/ptrace.2.html#NOTES where `PTRACE_PEEKUSER` and others apparently have a different kernel API? At least that explains why the function's non-trivial, but I don't see how it explains the LEA and copying that pointer around. Maybe there's more to it that the man page doesn't document, or some hand-written asm that would also explain the useless `xor`-zeroing. – Peter Cordes Dec 14 '21 at 20:49
  • 3
    The function in glibc is varargs, looks like at least some of this mess comes from `va_` macros. I believe `va_start` stores the start address of any additional stack arguments in a local variable so that `va_arg` could find them if needed._ – Jester Dec 14 '21 at 23:08
  • 1
    The useless `xor` is presumably a security measure to stop leaking the stack canary into the function. – Jester Dec 14 '21 at 23:29
  • 1
    @Jester: Oh right, derp, yeah that's right after storing the stack canary, and GCC has a missed optimization where it still xor-zeroes even if the next instruction unconditionally writes EAX. Anyway, yeah, glibc source code declares it as variadic, so it does make sense that the compiler is taking a pointer to the stack-arg area: https://code.woboq.org/userspace/glibc/sysdeps/unix/sysv/linux/ptrace.c.html. I guess so it matches the headers, and the header is variadic so it can be used with fewer args. So that's another missed opt; it doesn't see that it's always reading 3 vargs – Peter Cordes Dec 15 '21 at 04:13
  • 1
    Would be a duplicate of [How are variable arguments implemented in gcc?](https://stackoverflow.com/q/12371450) except that the answers there only cover a stack-args calling convention, or worse incorrectly claim that variadic functions change the calling convention. – Peter Cordes Dec 15 '21 at 04:19
  • I see, thank you for looking at it. I thought it was actually using the value in some way I didn't spot. My goal was just to write a ptrace shellcode to detect the presence of a debugger, as a security exercice, so I looked at how glibc implements it – Aaa Bbb Dec 15 '21 at 05:10

0 Answers0