0

I tried to change syscall behavior to remote process with ptrace in Arm linux while the process is access write syscall

ptrace.c

#include <errno.h>
#include <sys/ptrace.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <asm/ptrace.h>
#include <sys/user.h>


void main(int argc, char**argv){


    int pid = atoi (argv[1]);

    int retval;
    int i = 0 ; 


    if((retval = ptrace(PTRACE_ATTACH, pid, NULL ,NULL)) ==-1) {
        printf("error PTRACE_ATTACH \n");
    }

    wait(NULL);

    struct user_regs attack_regs;
    while(1)
    {
        if((retval = ptrace(PTRACE_SYSCALL, pid, NULL ,NULL)) ==-1) {
            printf("error PTRACE_SYSCALL \n");
        }
        wait(NULL);

        if((retval = ptrace(PTRACE_GETREGS, pid, NULL ,&attack_regs)) ==-1) { 
            printf("error PTRACE_GETREGS \n");
            return;
        }
        printf("call : command = %lu , r0 = %lu , r1 = %lu , r2 = %lu \n",attack_regs.ARM_r7 , attack_regs.ARM_r0 , attack_regs.ARM_r1, attack_regs.ARM_r2);
        if(attack_regs.ARM_r7 ==4 ) //write syscall
        {

            i++;
            if(i % 3 ==0)
            {
                attack_regs.ARM_r2=2 ; //edit write count
                if((retval = ptrace(PTRACE_SETREGS, pid, NULL ,&attack_regs)) ==-1) { 
                    printf("error PTRACE_SETREGS \n");
                    return;
                }
            }
        }

        if((retval = ptrace(PTRACE_SYSCALL, pid, NULL ,NULL)) ==-1) {
            printf("error PTRACE_SYSCALL \n");
        }
        wait(NULL);

        if((retval = ptrace(PTRACE_GETREGS, pid, NULL ,&attack_regs)) ==-1) { 
            printf("error PTRACE_GETREGS \n");
            return;
        }



        printf("result : command = %lu , r0 = %lu , r1 = %lu , r2 = %lu \n",attack_regs.ARM_r7 , attack_regs.ARM_r0 , attack_regs.ARM_r1, attack_regs.ARM_r2);
    }

    if((retval = ptrace(PTRACE_DETACH, pid, NULL ,NULL)) ==-1) {
            printf("error PTRACE_DETACH\n");
    }

}

test.c

#include <stdio.h>
#include <unistd.h>

void main ()
{
    char * bu ="123456789";
    int i = 0;
    while (1)
    {
        sleep(1);
        write(1,bu+(i%10),1);
        i++;
    }
}

test.c print each one sec. only 1 char (1..2...3..) .

When I attach to test pid with ptrace , each 3 times of write syscall I change that it print 2 chars instead of 1 char (by edit r2 register attack_regs.ARM_r2=2 ; ), that working fine .

What I tried now is to edit the syscall number , that diffrent syscall will be called by edit r7 register, like attack_regs.ARM_r7=0x18 (getuid syscall) or another , but that not work.

Even I see that r7 is realy edit , the diffrent syscall not called.

Why is that ?

paramikoooo
  • 177
  • 2
  • 16
  • When you catch a syscall first time, it has already started. You cannot change this fact. It will continue and you will catch its exit. you cannot redirect it just by changing its number. You can do something like LD_PRELOAD or other tricks to replace write with something else in the executable. – Serge Feb 03 '20 at 14:44
  • 1
    @Serge this is not true in general, but indeed for older arm kernels. I would consider this a bug, which was fixed in newer kernels (>=3.16 I believe). – Ctx Feb 03 '20 at 14:52
  • @Serge @Ctx 1) `LD_PRELOAD` not help me because I want to do that for running process 2) So why I can edit register `r2` but can't edit register `r7` ? – paramikoooo Feb 04 '20 at 06:12

0 Answers0