2017-05-22 11 views
-1

그래서이 연습 문제로 어려움을 겪었습니다. 내가 선택한 임의의 주어진 리눅스 명령 (I.E.ls 또는 cd)에 의해 만들어진 시스템 호출을 가져 와서 .txt 파일로 나열하고 그 고유 ID를 그 옆에 나열해야한다.시스템 호출 ID를 가져와 .txt 파일 (LINUX)에 저장하십시오.

지금까지 여기에 내가 가진 무엇 : 리눅스 쉘에서 실행될 때

strace -o filename.txt ls 

나에게 ls 명령의 모든 시스템 호출을 포함하는 "파일 이름 .txt"파일을 제공이. 이제 내 C 스크립트 :

#include <stdio.h> 
#include <stdlib.h> 

int main(){ 
    system("strace -o filename.txt ls"); 
    return 0; 
} 

이 이전 코드와 동일해야하지만, 코드가 성공적으로 컴파일하지만 그것은 나에게 아무것도 반환 아니에요. 어떻게 고쳐서 ID를 얻는거야? 나는 "stdlib"라이브러리를 사용하고 있습니다. 왜냐하면 제 조사에서 시스템 호출 ID와 관련이 있지만 발견 방법을 알지 못했기 때문입니다. 기본적으로 내가 만든 파일을 읽고 각 시스템에 ID를 호출하도록해야합니다.

+0

자신의 프로그램에서 셸 명령을 실행하면 어떤 점이 있습니까? C에서 그렇게 할 것을 요청 받았다면 [man ptrace] (http://man7.org/linux/man-pages/man2/ptrace.2.html) –

+0

을보아야합니다. 1) C는 없습니다. 스크립팅 언어. 2) @FelixPalmen이 옳다. 그것은 말이되지 않는다. – Olaf

답변

0

strace 유틸리티에는 syscall 번호를 인쇄 할 수있는 옵션이 없기 때문에 연습 문제는 ptrace() 기능을 사용하여 해결할 수 있습니다.

기술적으로, 당신은 다시 콜 번호에 매핑 콜 이름에 사용되는, syscall-number syscall-name 라인의 숫자를 생성하는

printf '#include <sys/syscall.h>\n' | gcc -dD -E - | awk '$1 == "#define" { m[$2] = $3 } END { for (name in m) if (name ~ /^SYS_/) { v = name; while (v in m) v = m[v]; sub(/^SYS_/, "", name); printf "%s %s\n", v, name } }' 

같은 것을 사용할 수 있지만,이 바보 및 오류가 발생하기 쉬운 것입니다. 어리석은 이유는 ptrace()을 사용할 수 있기 때문에 strace 유틸리티를 사용하는 것보다 훨씬 더 많은 제어 기능을 제공하고 위와 같은 "똑똑한 해킹"을 사용하면 어떻게하는지 배우는 것을 피할 수 있다는 것을 의미합니다. 내 의견으로는 자기가 패배하는 것이므로 완전히 어리 석다. 오류가 발생하기 쉽습니다. 설치된 헤더가 실행중인 아키텍처와 일치한다는 보장이 전혀 없기 때문입니다. 이는 -m32-m64 컴파일러 옵션을 사용하여 32 비트 및 64 비트 아키텍처간에 전환 할 수있는 다중 아키텍처에서 특히 문제가됩니다. 일반적으로 시스템 호출 번호는 완전히 다릅니다.

기본적으로 프로그램을 수행해야합니다

  1. fork() 자식 프로세스. 자식 프로세스에서

    :

    1. ptrace(PTRACE_TRACEME, (pid_t)0, (void *)0, (void *)0)

    2. 선택적으로 호출하여 prctl(PR_SET_DUMPABLE, 1L)

    3. 만들기 부모 프로세스에게 추적을 호출하여 ptracing 사용은 추적 옵션을 설정합니다. 예를 들어, 최소한 clone(), fork() 및 exec() 계열의 시스템 호출을 수신하도록 ptrace(PTRACE_SETOPTIONS, getpid(), PTRACE_O_TRACECLONE | PTRACE_O_TRACEEXEC | PTRACE_O_TRACEEXIT | PTRACE_O_TRACEFORK)으로 전화하십시오.

      PTRACE_O_TRACEEXEC 옵션을 설정하지 않은 경우 예를 들어를 사용하여이 시점에서 하위 프로세스를 중지해야합니다. raise(SIGSTOP);이므로 부모 프로세스가이 하위 추적을 시작할 수 있습니다.

    4. 예를 들면 다음과 같습니다. execv().특히, 첫 번째 명령 줄 매개 변수가 실행할 명령이고 선택적으로 옵션 뒤에 오는 경우 execvp(argv[1], argv + 1);을 사용할 수 있습니다.

      위의 경우 PTRACE_O_TRACEEXEC 옵션을 설정하면 커널이 새 바이너리를 실행하기 직전에 자식 프로세스를 자동 일시 중지합니다.

      exec가 실패하면 자식 프로세스가 종료됩니다. 나는 자식 프로세스에서 이벤트를 잡으려고, 루프에서 waitpid(childpid, &status, WUNTRACED | WCONTINUED를 사용하여 부모 프로세스에서 종료 상태를 127

  2. 을 반환, exit(127);를 사용하려면.

    매우 첫 번째 이벤트는 초기 일시 중지, 즉 WIFSTOPPED(status)이 참이어야합니다. (그렇지 않으면, 다른 문제가 발생했습니다.) waitpid(childpid, &status, WUNTRACED | WCONTINUED)는 반환 할 수 있습니다 이유를 세 가지 세 가지 이유가있다

  3. :

    • 아이가 종료 (WIFEXITED(status) 사실이 될 것이다)

      . 이것은 분명히 추적을 끝내고 상위 추적 프로그램 프로세스를 종료해야합니다.

    • 자식이 실행을 다시 시작할 때 (WIFCONTINUED(status)이 참일 때).

      부모가이 신호를받을 때까지 PTRACE_SYSCALL, PTRACE_SYSEMU, PTRACE_CONT 등의 명령으로 인해 실제로 하위 프로세스가 계속 진행되었다고 추측 할 수 없습니다. 즉, 자식 프로세스에 ptrace() 명령을 실행할 수 없으며 규칙적인 방식으로 명령을 수행 할 수 있습니다. ptrace() 기능은 비동기식이며 호출은 즉시 반환됩니다. 하위 프로세스가 명령에주의했음을 알기 위해 WIFCONTINUED(status) 이벤트 유형의 경우 waitpid()이 필요합니다.

    • 자식 프로세스가 syscall을 실행하려고하기 때문에 커널이 자식을 중지했을 때 (SIGTRAP). (부모에서 WIFSTOPPED(status) 사실이 될 것입니다.)

  4. 자식 프로세스가이 시스템 콜을 실행하는 것입니다 때문에 중단됩니다 때마다, 당신의 자식 프로세스의 CPU 레지스터 상태를 얻기 위해 ptrace(PTRACE_GETREGS, childpid, (void *)0, &regs)를 사용할 필요가 syscall 실행 지점.

    regs은 으로 정의 된 struct user입니다. 인텔/AMD 아키텍처, regs.regs.eax (32 비트) 또는 regs.regs.rax (64 비트)의 경우 <sys/syscall.h>에 정의 된 시스템 콜 번호 (SYS_foo가 포함되어 있습니다.

    당신은 그 콜을 실행하기 위해 커널에게 ptrace(PTRACE_SYSCALL, childpid, (void *)0, (void *)0)를 호출해야 그리고 waitpid() 다시했던 것을 알리는 WIFCONTINUED(status) 이벤트에 대한 대기.

    시스템 콜이 완료되면 당신이 원하는 경우., 당신은 PTRACE_GETREGS을 사용할 수 있습니다 발생합니다 waitpid()에서 다음 WIFSTOPPED(status) 형 이벤트가 다시 regs.regs.eax 또는 regs.regs.rax, 검사 할 syscall 반환 값을 포함하고, Intel/AMD에서는, 오류가 발생하면 음수 errno 값 (즉, -EACCES, -EINVAL 또는 이와 유사한 것.)

    다음 syscall까지 커널을 계속 실행하도록 커널에 알리기 위해 ptrace(PTRACE_SYSCALL, childpid, (void *)0, (void *)0)에 전화해야합니다. 나는 개인적으로 본 가장 있지만 온라인 위의 사항 중 일부를 보여주는 꽤 많은 사례가 있습니다

는 오류 검사에 매우 느슨하고, 때때로 WIFCONTINUED(status)waitpid() 이벤트를 확인 생략합니다. StackOverflow에서 개별 스레드를 중지하고 계속하는 방법을 자세히 설명하는 답변도 작성했습니다. 이 기술을 매우 강력한 사용자 정의 디버깅 도구로 사용할 수 있으므로 기존 코드를 복사하여 붙여 넣기보다는 연습에 활용할 수 있도록 시설을 익히는 것이 좋습니다.