LINUX.ORG.RU

Перенаправление операций ввода-вывода через ptrace.


0

0

В общем, имеется некая бинарная программа. И очень она любит лазить в /tmp, а нужно чтобы это было /home/murr/tmp (прав root на соответствующей машине нет). Переопределять через LD_PRELOAD функции не хочется, да и к тому же ряд функций вроде tmpnam вызывают внутренние функции glibc при обращении к файловой системе, а сами не weak (впрочем, тут есть еще где поковыряться).

Имеется некий код, написанный на скорую руку, который через PTRACE_SYSCALL пытается отлавливать все обращения и заменять путь, но он, к сожалению, не работает :( По всей видимости не удается переопределить указатель на новый путь.

Если у кого есть комментарии по коду или реализации самой задачи - пишите.

Заранее спасибо.

★★

Сам код

#include <stdio.h>
#include <unistd.h>
#include <signal.h>
#include <sys/ptrace.h>
#include <linux/user.h>
#include <syscall.h>
 
#define DEBUG
 
#define MAX_PATH 4096
 
int rc;
int stage = 1;
char * old_prefix, * new_prefix;
 
int saved_stack[MAX_PATH/4];
int  saved_size;
int unhook_data;
 
void save_stack (int num_longs) {
    int i;
     
    for (i=0; i<num_longs; i++)
        saved_stack[i] = ptrace (PTRACE_PEEKDATA, rc, 0xc0000000 - (i+1)*4, 0);
 
    saved_size = num_longs;
}
 
void copy_to_stack (int data[], int num_longs) {
    int i;
     
    for (i=0; i<num_longs; i++)
        ptrace (PTRACE_POKEDATA, rc, 0xc0000000 - (i+1)*4, data[num_longs-i-1]);}
 
void restore_stack (int num_longs) {
    int i;
     
    for (i=0; i<num_longs; i++)
        ptrace (PTRACE_POKEDATA, rc, 0xc0000000 - (i+1)*4, saved_stack[i]);
}
 
void open_hook (char ** str) {
    char path[MAX_PATH];
    char new_path[MAX_PATH];
    int i = 0;
    int tmp;
 
    for (;;) {
        tmp = ptrace (PTRACE_PEEKDATA, rc, *str + 4*i, 0);
        ((int *)path)[i] = tmp;
        if (!path[4*i] || !path[4*i+1] || !path[4*i+2] || !path[4*i+3]) break;
#ifdef DEBUG
        fprintf (stderr, "%x\n", tmp);
#endif
        i++;
    }
     
#ifdef DEBUG
    fprintf (stderr, "open_hook: opening file '%s' (old_prefix='%s' new_prefix='%s')\n", path, old_prefix, new_prefix);
#endif
 
    if (strstr (path, old_prefix) == path) {
        sprintf (new_path, "%s%s", new_prefix, path+strlen(old_prefix));
#ifdef DEBUG
        fprintf (stderr, "changing path to %s\n", new_path);
#endif
        save_stack ((strlen(new_path)+3)/4);
        copy_to_stack ((int *)new_path, (strlen(new_path)+3)/4);
    }
    *str = (char *)(0xc0000000-((strlen(new_path)+3)/4)*4);
}
 
void open_unhook (void) {
    restore_stack (saved_size);
}
 
void mysigact (int unused, siginfo_t * si, void * v) {
    struct user_regs_struct regs;
    int lrc;
 
#ifdef DEBUG
    fprintf (stderr, "si_signo=%d si_errno=%d si_code=%d \n", si->si_signo, si->si_errno, si->si_code);
#endif
 
    if ((si->si_signo == SIGCLD) && (si->si_code == CLD_EXITED)) exit (0);
 
    if (stage == 1) {
        /* Entering system call */
        stage = 2;
        ptrace (PTRACE_GETREGS, rc, 0, &regs);
 
#ifdef DEBUG
        fprintf (stderr, "entering system call %d (%x)\n", regs.orig_eax, regs.ebx);
#endif
 
        unhook_data = 0;
        switch (regs.orig_eax) {
            case __NR_open : open_hook ((char **)&regs.ebx);
                             unhook_data = __NR_open;
        }
        lrc = ptrace (PTRACE_SETREGS, rc, 0, &regs);
#ifdef DEBUG
        fprintf (stderr, "PTRACE_SETREGS returned %d (ebx=%x)\n", lrc, regs.ebx);
#endif
        ptrace (PTRACE_SYSCALL, rc, 0, 0);
   }
   else {
        /* Exiting system call */
#ifdef DEBUG
        fprintf (stderr, "exiting system call\n");
#endif
        switch (unhook_data) {
            case __NR_open : open_unhook ();
        }
        unhook_data = 0; // Just to be safe
        stage = 1;
        ptrace (PTRACE_SYSCALL, rc, 0, 0);
   }
}
 
extern char **environ;
 
int main (int argc, char *argv[]) {
    char * prog_name;
    char * new_arg[] = {argv[1], 0};
 
    sigset_t set;
     
    if (argc != 4) {
        // ...
        fprintf (stderr, "usage: trace program old_prefix new_prefix\n");
        return 0;
    }
 
    prog_name  = argv [1];
    old_prefix = argv [2];
    new_prefix = argv [3];
 
    rc = fork ();
    if (rc == -1) {
        fprintf (stderr, ".failed to fork!\n");
        return  0;
    }
    if (rc > 0) {
        sigemptyset (&set);
        sigaction (SIGCLD, &(const struct sigaction){.sa_sigaction = mysigact, .sa_mask = set, .sa_flags = SA_SIGINFO}, NULL);
    }
 
    if (rc == 0) {
        ptrace (PTRACE_TRACEME, 0, 0, 0);
        execve (prog_name, new_arg, environ);
    }
 
    for (;;) pause;
}

Murr ★★
() автор топика

Сам код

#include <stdio.h>
#include <unistd.h>
#include <signal.h>
#include <sys/ptrace.h>
#include <linux/user.h>
#include <syscall.h>
 
#define STACK_LIMIT 0xC0000000
 
#define DEBUG
 
#define MAX_PATH 4096
 
int rc;
int stage = 1;
char * old_prefix, * new_prefix;
 
int saved_stack[MAX_PATH/4];
int saved_size;
int unhook_data;
 
void save_stack (int num_longs) {
    int i;
     
    for (i=0; i<num_longs; i++)
        saved_stack[i] = ptrace (PTRACE_PEEKDATA, rc, STACK_LIMIT - (i+1)*4, 0); 
    saved_size = num_longs;
}
 
void copy_to_stack (int data[], int num_longs) {
    int i;
     
    for (i=0; i<num_longs; i++)
        ptrace (PTRACE_POKEDATA, rc, STACK_LIMIT - (i+1)*4, data[num_longs-i-1]);
}
 
void restore_stack (int num_longs) {
    int i;
     
    for (i=0; i<num_longs; i++)
        ptrace (PTRACE_POKEDATA, rc, STACK_LIMIT - (i+1)*4, saved_stack[i]);
}
 
void open_hook (char ** str) {
    char path[MAX_PATH];
    char new_path[MAX_PATH];
    int i = 0;
    int tmp;
 
    for (;;) {
        tmp = ptrace (PTRACE_PEEKDATA, rc, *str + 4*i, 0);
        ((int *)path)[i] = tmp;
        if (!path[4*i] || !path[4*i+1] || !path[4*i+2] || !path[4*i+3]) break;
#ifdef DEBUG
        fprintf (stderr, "%x\n", tmp);
#endif
        i++;
    }
     
#ifdef DEBUG
    fprintf (stderr, "open_hook: opening file '%s' (old_prefix='%s' new_prefix='%s')\n", path, old_prefix, new_prefix);
#endif
 
    if (strstr (path, old_prefix) == path) {
        sprintf (new_path, "%s%s", new_prefix, path+strlen(old_prefix));
#ifdef DEBUG
        fprintf (stderr, "changing path to %s\n", new_path);
#endif
        save_stack ((strlen(new_path)+3)/4);
        copy_to_stack ((int *)new_path, (strlen(new_path)+3)/4);
        *str = (char *)(STACK_LIMIT-((strlen(new_path)+3)/4)*4);
        unhook_data = __NR_open;
    }
}
 
void open_unhook (void) {
    restore_stack (saved_size);
}
 
void mysigact (int unused, siginfo_t * si, void * v) {
    struct user_regs_struct regs;
    int lrc;
 
#ifdef DEBUG
    fprintf (stderr, "si_signo=%d si_errno=%d si_code=%d si_status=%d\n", si->si_signo, si->si_errno, si->si_code, si->si_status);
#endif
 
    /* AP: First, check if child has exited */
    if (si->si_code == CLD_EXITED ||
        si->si_code == CLD_KILLED ||
        si->si_code == CLD_DUMPED) exit (0);
 
    /* AP: If not exited then it's a signal or a system
            call notification. Check for signal first. */
    if (si->si_status != SIGTRAP) {
        /* AP: The child received some signal, just deliver it and return */
        ptrace (PTRACE_SYSCALL, rc, 0, si->si_status);
        return;
    }
 
    /* AP: So, we've got the system call ... */
    if (stage == 1) {
        /* Entering system call */
        stage = 2;
        ptrace (PTRACE_GETREGS, rc, 0, &regs);
 
#ifdef DEBUG
        fprintf (stderr, "entering system call %d (%x)\n", regs.orig_eax, regs.ebx);
#endif
 
        unhook_data = 0;
        switch (regs.orig_eax) {
            case __NR_open : open_hook ((char **)&regs.ebx);
 
        }
        lrc = ptrace (PTRACE_SETREGS, rc, 0, &regs);
#ifdef DEBUG
        fprintf (stderr, "PTRACE_SETREGS returned %d (ebx=%x)\n", lrc, regs.ebx);
#endif
        ptrace (PTRACE_SYSCALL, rc, 0, 0);
   }
   else {
        /* Exiting system call */
#ifdef DEBUG
        fprintf (stderr, "exiting system call\n");
#endif
        switch (unhook_data) {
            case __NR_open : open_unhook ();
        }
        unhook_data = 0; // Just to be safe
        stage = 1;
        ptrace (PTRACE_SYSCALL, rc, 0, 0);
   }
}
 
extern char **environ;
 
int main (int argc, char *argv[]) {
    char * prog_name;
    char * new_arg[] = {argv[1], 0};
 
    sigset_t set;
     
    if (argc != 4) {
        // ...
        fprintf (stderr, "usage: trace program old_prefix new_prefix\n");
        return 0;
    }
 
    prog_name  = argv [1];
    old_prefix = argv [2];
    new_prefix = argv [3];
 
    rc = fork ();
    if (rc == -1) {
        fprintf (stderr, ".failed to fork!\n");
        return  0;
    }
    if (rc > 0) {
        sigemptyset (&set);
        sigaction (SIGCLD, &(const struct sigaction){.sa_sigaction = mysigact, .sa_mask = set, .sa_flags = SA_SIGINFO}, NULL);
    }
 
    if (rc == 0) {
        ptrace (PTRACE_TRACEME, 0, 0, 0);
        execve (prog_name, new_arg, environ);
    }
 
    for (;;) pause;
}

Murr ★★
() автор топика

>В общем, имеется некая бинарная программа. И очень она любит лазить в /tmp,

А порыться в бинарнике на предмет строки /tmp ну и поправить на что нибудь типа ~/xy ?

sS ★★★★★
()

Старый дедовский метод:

дизассемблируешь прогу, вырезаешь основной код,смотришь по какому смещению лежат ascii строки и подправляешь /tmp на /_твой_tmp_, затем везде в листинге ассемблерном выправляешь смещения с учетом твоих изменений (лучше метки использовать), компилишь заново.
Я понимаю, это рутина, но тоже вариант.

Если есть рут и можно модули грузить: пишешь драйвер, который перехватывает sys_open и т.д и вместо /tmp/... вызывает реальный sys_open с твоими путями.

Выбирай, что быстрей - 1-ое или 2-ое !!!???

anonymous
()
Ответ на: комментарий от anonymous

темный ты человек, сказали же, что имен в бинарнике нет, их генерит функция из libc. А перехватывать лучше open из libc, через LD_PRELOAD, потому что прав рута нет (см. исходный пост)

grustnoe ★★
()
Ответ на: комментарий от anonymous

anonymous: модуль написать было бы просто сказочно, но прав на той машине для его загрузки нет.

Реально там такая ситуация: есть машина в штатах, есть раздел JFS, смонтированный на /tmp, он регулярно вылетает (все процессы, туда читающие/пишущие, просто умирают в состоянии D(deep sleep, он же TASK_UNINTERRUPTIBLE)). Похоже, что сыпется RAID, хотя реакция JFS на это дело тоже довольно нездоровая... Администратора мы попинали, но ситуация повторяется с завидной регулярностью, а работать надо (ну и спортивный интерес - как же это можно сделать).

grustnoe: а для LD_PRELOAD init грузится после всех? Извиняюсь, за глупый вопрос, конечно.

Murr ★★
() автор топика
Ответ на: комментарий от Murr

RE:

Свой код я таки довел до рабочего состояния (там постоянно путался сигнал входа и выхода из системного вызова и плюс ко всему не писался на стек завершающий 0 для нового имени).

Вот код, если кому-то интересно:

Murr ★★
() автор топика
Ответ на: RE: от Murr

RE:

#include <stdio.h>
#include <errno.h>
#include <unistd.h>
#include <signal.h>
#include <sys/ptrace.h>
#include <linux/user.h>
#include <syscall.h>
#include <string.h>
 
#define STACK_LIMIT 0xC0000000
 
#define DEBUG
 
#define MAX_PATH 4096
 
int rc;
int stage = 1;
char * old_prefix, * new_prefix;
 
int saved_stack[MAX_PATH/4];
int saved_size;
int unhook_data;
 
void save_stack (int num_longs) {
    int i;
     
    for (i=0; i<num_longs; i++)
        saved_stack[i] = ptrace (PTRACE_PEEKDATA, rc, STACK_LIMIT - (i+1)*4, 0); 
    saved_size = num_longs;
}
 
void copy_to_stack (int data[], int num_longs) {
    int i;
     
    for (i=0; i<num_longs; i++)
        ptrace (PTRACE_POKEDATA, rc, STACK_LIMIT - (i+1)*4, data[num_longs-i-1]);
}
 
void restore_stack (int num_longs) {
    int i;
     
    for (i=0; i<num_longs; i++)
        ptrace (PTRACE_POKEDATA, rc, STACK_LIMIT - (i+1)*4, saved_stack[i]);
}
 
void open_hook (char ** str) {
    char path[MAX_PATH];
    char new_path[MAX_PATH];
    int i = 0, len;
    int tmp;
 
    for (;;) {
        tmp = ptrace (PTRACE_PEEKDATA, rc, *str + 4*i, 0);
        ((int *)path)[i] = tmp;
        if (!path[4*i] || !path[4*i+1] || !path[4*i+2] || !path[4*i+3]) break;
#ifdef DEBUG
        fprintf (stderr, "%x\n", tmp);
#endif
        i++;
    }
     
#ifdef DEBUG
    fprintf (stderr, "open_hook: opening file '%s' (old_prefix='%s' new_prefix='%s')\n", path, old_prefix, new_prefix);
#endif
 
    if (strstr (path, old_prefix) == path) {
        sprintf (new_path, "%s%s", new_prefix, path+strlen(old_prefix));
#ifdef DEBUG
        fprintf (stderr, "changing path to %s\n", new_path);
#endif
        len = strlen(new_path)+1; // AP: Save the whole string with a trailing zero
        save_stack ((len+3)/4);
        copy_to_stack ((int *)new_path, (len+3)/4);
        *str = (char *)(STACK_LIMIT-((len+3)/4)*4);
        unhook_data = __NR_open;
    }
}
 
void open_unhook (void) {
    restore_stack (saved_size);
}
 
void mysigact (int unused, siginfo_t * si, void * v) {
    struct user_regs_struct regs;
    int lrc;
 
#ifdef DEBUG
    fprintf (stderr, "si_signo=%d si_errno=%d si_code=%d si_status=%d\n", si->si_signo, si->si_errno, si->si_code, si->si_status);
#endif
 
    /* AP: First, check if child has exited */
    if (si->si_code == CLD_EXITED ||
        si->si_code == CLD_KILLED ||
        si->si_code == CLD_DUMPED) exit (0);
 
    /* AP: If not exited then it's a signal or a system
            call notification. Check for signal first. */
    if (si->si_status != SIGTRAP) {
        /* AP: The child received some signal, just deliver it and return */
        ptrace (PTRACE_SYSCALL, rc, 0, si->si_status);
        return;
    }
 
    /* AP: So, we've got the system call ... */
    if (stage == 1) {
        /* Entering system call */
        stage = 2;
 
        ptrace (PTRACE_GETREGS, rc, 0, &regs);
 
        if (regs.eax != -ENOSYS) {
            fprintf (stderr, ".our heuristics is bad - looks like we're exiting
system call\n");
            goto syscall_exit;
        }
 
        syscall_enter:
#ifdef DEBUG
        fprintf (stderr, "entering system call %d (%x)\n", regs.orig_eax, regs.ebx);
#endif
 
        unhook_data = 0;
        switch (regs.orig_eax) {
            case __NR_open : open_hook ((char **)&regs.ebx);
        }
        lrc = ptrace (PTRACE_SETREGS, rc, 0, &regs);
#ifdef DEBUG
        fprintf (stderr, "PTRACE_SETREGS returned %d (ebx=%x)\n", lrc, regs.ebx);
#endif
        ptrace (PTRACE_SYSCALL, rc, 0, 0);
   }
   else {
        /* Exiting system call */
        ptrace (PTRACE_GETREGS, rc, 0, &regs);
 
        if (regs.eax == -ENOSYS) {
            fprintf (stderr, ".our heuristics is bad - looks like we're entering system call\n");
            goto syscall_enter;
        }
 
        syscall_exit:
#ifdef DEBUG
        fprintf (stderr, "exiting system call\n");
#endif
        switch (unhook_data) {
            case __NR_open : open_unhook ();
        }
        unhook_data = 0; // Just to be safe
        stage = 1;
        ptrace (PTRACE_SYSCALL, rc, 0, 0);
   }
}
 
extern char **environ;
 
int main (int argc, char *argv[]) {
    char * prog_name;
    char * new_arg[] = {argv[1], 0};
 
    sigset_t set;
     
    if (argc != 4) {
        // ...
        fprintf (stderr, "usage: trace program old_prefix new_prefix\n");
        return 0;
    }
 
    prog_name  = argv [1];
    old_prefix = argv [2];
    new_prefix = argv [3];
 
    rc = fork ();
    if (rc == -1) {
        fprintf (stderr, ".failed to fork!\n");
        return  0;
    }
    if (rc > 0) {
        struct sigaction sa;
 
        sigemptyset (&set);
        sa.sa_sigaction = mysigact;
        sa.sa_mask = set;
        sa.sa_flags = SA_SIGINFO;
 
        sigaction (SIGCLD, &sa, NULL);
    }
 
    if (rc == 0) {
        ptrace (PTRACE_TRACEME, 0, 0, 0);
        execve (prog_name, new_arg, environ);
    }
 
    for (;;) pause;
}

Murr ★★
() автор топика
Ответ на: комментарий от grustnoe

Я насчет функции инициализации библиотеки, прописанной в LD_PRELOAD.

Murr ★★
() автор топика
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.