Linux, desenvolvimento do programa Debugger

3

Estamos tentando implementar o programa de depuração que utilizará PID ou nome do programa como entrada e chamar o gdb usando PID. Abaixo estão dois pequenos programas são escritos, incapaz de descobrir qual é o problema exato aqui ... depois de passar PID, ele mostra 5000 + instruções são executadas como resultado.

Debug.c

#include <stdio.h>
#include <stdarg.h>
#include <stdlib.h>
#include <signal.h>
#include <syscall.h>
#include <sys/ptrace.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/reg.h>
#include <sys/user.h>
#include <unistd.h>
#include <errno.h>


/* Print a message to stdout, prefixed by the process ID
*/
void procmsg(const char* format, ...)
{
    va_list ap;
    fprintf(stdout, "[%d] ", getpid());
    va_start(ap, format);
    vfprintf(stdout, format, ap);
    va_end(ap);
}


void run_target(const char* programname)
{
    procmsg("target started. will run '%s'\n", programname);

    /* Allow tracing of this process */
    if (ptrace(PTRACE_TRACEME, 0, 0, 0) < 0) {
        perror("ptrace");
        return;
    }

    /* Replace this process's image with the given program */
    execl(programname, programname, 0);
}


void run_debugger(pid_t child_pid)
{
    int wait_status;
    unsigned icounter = 0;
    procmsg("debugger started\n");

    /* Wait for child to stop on its first instruction */
    wait(&wait_status);

    while (WIFSTOPPED(wait_status)) {
        icounter++;
        struct user_regs_struct regs;
        ptrace(PTRACE_GETREGS, child_pid, 0, &regs);
        unsigned instr = ptrace(PTRACE_PEEKTEXT, child_pid, regs.eip, 0);

        procmsg("icounter = %u.  EIP = 0x%08x.  instr = 0x%08x\n",
                    icounter, regs.eip, instr);

        /* Make the child execute another instruction */
        if (ptrace(PTRACE_SINGLESTEP, child_pid, 0, 0) < 0) {
            perror("ptrace");
            return;
        }

        /* Wait for child to stop on its next instruction */
        wait(&wait_status);
    }

    procmsg("the child executed %u instructions\n", icounter);
}


int main(int argc, char** argv)
{
    pid_t child_pid_attach;

    if (argc < 2) {
        fprintf(stderr, "Expected a program name as argument\n");
        return -1;
    }
sscanf(argv[1],"%d",&child_pid_attach);

//Attaching to running process
if(ptrace(PTRACE_ATTACH,child_pid_attach,0,0)<)
{
perror("ptrace");
return;
}
else
{
printf("%d",child_pid_attach);
}
    if (child_pid_attach== 0)
        run_target(argv[1]);
    else if (child_pid_attach > 0)
        run_debugger(child_pid_attach);
    else {
        perror("fork");
        return -1;
    }

ptrace(PTRACE_DETACH,child_pid_attach,0,0);
    return 0;
}

O programa acima foi usado para depurar o processo que é criado pelo programa a seguir (ou seja, a soma de dois números). test.c

#include<stdio.h>
void main()
{
int a, b, c;
scanf("%d", &a);
scanf("%d", &b);
printf("\n Sum of Two Numbers is:");
c=a+b;
printf("%d",c);
}

Primeiro, estamos executando o ./test e, em seguida, verificando o seu pid. Como próximo passo, estamos executando o ./Debug [pid]. Como resultado da execução acima, ele está exibindo que o processo Child executou mais de 5000 instruções e está imprimindo as mesmas instruções o tempo todo.

Por favor, deixe-me saber se existe alguma outra maneira de fazê-lo e deixe-me saber como ler os dados de outro processo. Neste caso "Como posso ler os dados (valores das variáveis) do processo criado por ./test?".

    
por user29997 09.01.2013 / 15:05

2 respostas

2

Na verdade, esse é o comportamento correto.

Abaixo está uma citação de aqui :

The answer is very interesting. By default, gcc on Linux links programs to the C runtime libraries dynamically. What this means is that one of the first things that runs when any program is executed is the dynamic library loader that looks for the required shared libraries. This is quite a lot of code – and remember that our basic tracer here looks at each and every instruction, not of just the main function, but of the whole process.

    
por 27.10.2013 / 03:31
0

"How can I read the data (values of variables) of process that is created by ./test?".

Você pode querer olhar para o formato de depuração DWARF. A parte 3 do link How Debuggers Work abaixo discute o DWARF brevemente. Existem outras maneiras de resolver símbolos, mas por que não fazer como o GDB e usar o DWARF? [edit: Não é uma tarefa trivial extrair funções do código-fonte do gdb para usá-las em programas mais simples]. O código-fonte está disponível de qualquer maneira, verifique o link ' Como o GDB carrega os arquivos de símbolo ' abaixo, onde os links apontam diretamente para ele. A terceira opção seria analisar manualmente as tabelas de símbolos ELF com suas próprias funções. É mais feio e é provavelmente a estrada mais envolvida, mas não depende dos símbolos de depuração que o anão fornece.

Quanto aos pontos de interrupção, você pode configurá-los usando ptrace e trap = memory & 0xffffff00 | 0xcc depois de salvar a instrução no endereço e recuperar a instrução depois que o trap é atingido, como How Debuggers Work descreve. O byte 0xcc é opcode int 3.

Para ver como o gdb faz isso, este é o link para você: Como o GDB Carrega os arquivos de símbolos

O seguinte é apenas para sugerir de onde vêm essas 5000+ etapas executando um programa de montagem sem bibliotecas vinculadas por meio de um rastreador semelhante:

;hello.asm

section .text
    global _start

_start:
    mov edx,5
    mov ecx,msg
    mov ebx,1
    mov eax,4
    int 0x80

    mov eax,1
    int 0x80

msg:
    db "Hello"

O programa que eu usei para contar é similar (de Como funcionam os depuradores )

#include <sys/ptrace.h>
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>

#include <sys/wait.h>

void run_debugger(pid_t child_pid)
{
    int wait_status;
    unsigned icounter = 0;
    printf("debugger started\n");

    /* Wait for child to stop on its first instruction */
    wait(&wait_status);

    while (WIFSTOPPED(wait_status)) {
        icounter++;
        /* Make the child execute another instruction */
        if (ptrace(PTRACE_SINGLESTEP, child_pid, 0, 0) < 0) {
            perror("ptrace");
            return;
        }

        /* Wait for child to stop on its next instruction */
        wait(&wait_status);
    }

    printf("\nthe child executed %u instructions\n", icounter);
}


void run_target(const char* programname)
{
    printf("target started. will run '%s'\n", programname);

    /* Allow tracing of this process */
    if (ptrace(PTRACE_TRACEME, 0, 0, 0) < 0) {
        perror("ptrace");
        return;
    }

    /* Replace this process's image with the given program */
    execl(programname, programname, NULL);
}

int main(int argc, char** argv)
{
    pid_t child_pid;

    if (argc < 2) {
        fprintf(stderr, "Expected a program name as argument\n");
        return -1;
    }

    child_pid = fork();
    if (child_pid == 0)
        run_target(argv[1]);
    else if (child_pid > 0)
        run_debugger(child_pid);
    else {
        perror("fork");
        return -1;
    }

    return 0;
}

Compilado como a.out e executado:

$ ./a.out helloasm
debugger started
target started. will run 'helloasm'
Hello
the child executed 7 instructions

vs.

#include <stdio.h>

int main()
{
        printf("Hello World\n");
        return 0;
}

Que totaliza 141 690 instruções.

    
por 28.06.2017 / 22:46