리눅스, 디버거 프로그램 개발

리눅스, 디버거 프로그램 개발

우리는 PID나 프로그램 이름을 입력으로 취하고 PID로 gdb를 호출하는 디버거 프로그램을 구현하려고 합니다. 아래에는 두 개의 작은 프로그램이 작성되어 있는데 정확한 문제가 무엇인지 알 수 없습니다. PID를 통과한 후 결과에 따르면 5000개 이상의 명령어가 실행된 것으로 나타났습니다.

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;
}

위 프로그램은 다음 프로그램(예: 두 숫자의 합)으로 생성된 프로세스를 디버깅하는 데 사용되었습니다. 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);
}

먼저 ./test를 실행하고 pid를 확인합니다. 다음으로 ./Debug [pid]를 실행하겠습니다. 위의 실행 결과는 자식 프로세스가 5000개 이상의 명령어를 실행했다는 것을 보여주며, 항상 같은 명령어가 출력된다.

이를 수행할 수 있는 다른 방법이 있는지 알려주시고 다른 프로세스에서 데이터를 읽는 방법도 알려주십시오. 이 경우 "./test로 생성된 프로세스의 데이터(변수 값)를 어떻게 읽는가?"입니다.

답변1

사실 그것은 옳은 행동이다.

다음은 다음에서 인용되었습니다.여기:

대답은 흥미 롭습니다. 기본적으로 Linux의 gcc는 프로그램을 C 런타임 라이브러리에 동적으로 연결합니다. 이는 프로그램이 실행될 때 가장 먼저 실행되는 것 중 하나가 필요한 공유 라이브러리를 찾는 동적 라이브러리 로더라는 것을 의미합니다. 이는 꽤 많은 코드입니다. 기본 추적 프로그램은 주요 기능뿐만 아니라 전체 프로세스를 포함한 모든 명령을 살펴봅니다.

답변2

"./test로 생성된 프로세스의 데이터(변수 값)를 읽는 방법은 무엇입니까?".

DWARF 디버깅 형식을 살펴보고 싶을 수도 있습니다. 3부디버거 작동 방식아래 링크에서는 DWARF에 대해 간략하게 설명합니다. 기호를 해석하는 다른 방법이 있지만 GDB처럼 DWARF를 사용하는 것은 어떨까요? [편집: 더 간단한 프로그램에서 사용하기 위해 gdb 소스 코드에서 함수를 추출하는 것은 간단한 작업이 아닙니다.] 어쨌든 소스 코드를 사용할 수 있습니다. '를 확인하십시오.GDB가 기호 파일을 로드하는 방법'아래 링크, 링크가 그 곳을 가리킵니다. 세 번째 옵션은 자신의 함수를 사용하여 ELF 기호 테이블을 수동으로 구문 분석하는 것입니다. 더 추악하고 아마도 더 복잡한 경로일 수 있지만 dwarf가 제공하는 디버깅 기호에 의존하지 않습니다.

중단점의 경우 ptrace를 사용할 수 있으며트랩 = 메모리 & 0xffffff00 |해당 주소에 명령어를 저장하고 트랩에 부딪힌 후 명령어를 복원한 후 다음과 같이 한다.디버거 작동 방식설명하다. 0xcc 바이트는 opcode입니다.정수 3.

gdb가 이를 수행하는 방법을 알아보려면 다음 링크를 따르십시오. GDB가 기호 파일을 로드하는 방법

다음은 연결된 라이브러리 없이 어셈블러를 실행하는 유사한 추적기를 통해 이러한 5000개 이상의 단계가 어디에서 나오는지에 대한 힌트입니다.

;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"

제가 계산에 사용하는 프로그램은 비슷합니다.디버거 작동 방식)

#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;
}

a.out으로 컴파일하고 다음을 실행합니다.

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

그리고

#include <stdio.h>

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

총 141,690개의 명령어.

관련 정보