[완료]초간단 strace 구현시 에러가...

songaal의 이미지

부모프로세스가 자식프로세스를 트레이스하면서 open 시스템콜이 있을때 파일경로를 프린트하는 코드입니다.
strace프로그램의 초간단 버전으로 생각하셔도 되구요.
실행시키면 while문을 돌다가 중간에 세그먼트에러가 나네요.
처음 몇줄은 path가 출력되는걸로 보아 EBX *4위치에 경로가 저장되는게 맞는거 같은데
어떤 경우에는 세그먼트에러가 나니 참 난감합니다.
제가 사용법을 잘못알고 있는건가요. 아니면 체크해야할 조건이 더 있는건가요.
strace소스를 보고 연구해보고 있는데 소스파악이 너무 힘드네요..
QNA에 답변이 달리는거 보면 쉬운질문들에만 답이 달리는 것같은데
그냥 보고 지나가지 마시고 아시는분들 코멘트좀 부탁드립니다.

 while(1) {
          wait(&status);
          if(WIFEXITED(status))
              break;
          orig_eax = ptrace(PTRACE_PEEKUSER,  child, 4 * ORIG_EAX, NULL);
 
          if(orig_eax == SYS_open) {
             if(insyscall == 0) {
                /* Syscall entry */
                insyscall = 1;
                params[0] = ptrace(PTRACE_PEEKUSER, child, 4 * EBX, NULL);
                printf("path = %s\n",param[0]);
              }
          }
}
songaal의 이미지

## OPEN called with 1233527, 0, 0
path = /etc/ld.so.cache
Open returned with 3
## OPEN called with -1208691295, 0, -1076367628
세그멘테이션 오류

## OPEN called with.. 부분은 제가 추가로 더 넣어본것입니다. 숫자차례로 eax,ebx,ecx를 출력했습니다.
bootmeta의 이미지

쉬운 질문에 답변이 달릴 확률이 높은 것은 그만큼 번거롭지 않기 때문입니다.

특정 프로그램의 일부라면 실행 가능한 최소 test 코드라도 올려주셔야 댓글이 달릴 확률이 높아집니다.
아주 흥미로운 내용이 아니라면 직접 test 코드까지 짜서 돌릴 확률은 낮아지죠.
특히 low level 문제는 test 코드를 직접 제공하지 않으면 error 발생 재현 역시 보장할 수 없습니다.

ps) compiler, 실행환경 정보 제공 역시 필수

songaal의 이미지

사실 저 밑에 소스코드를 포함해서 질문을 한게 있는데 답변이 달리지 않아서
소스코드가 부담되서 답변을 안하시나 생각했습니다.

실행환경은 centos5 커널 2.6.18의 32bit환경에서 gcc로 컴파일했습니다.

이질문에 테스트를 하고 안하시고는 선택의 문제이지만, 혹시 다들 모르시는게 아닌가...해서 실망도 좀 됩니다...

#include <sys/ptrace.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <linux/user.h>
#include <sys/syscall.h>   /* For SYS_write etc */
#include <sys/reg.h>
#include <stdio.h>
#include <stdlib.h>
#include <syscall.h>
 
 
int main()
{   pid_t child;
    long orig_eax, eax;
    long params[3];
    int status;
    int insyscall = 0;
    child = fork();
    if(child == 0) {
        ptrace(PTRACE_TRACEME, 0, NULL, NULL);
        execl("/bin/ls", "ls", NULL);
    }
    else {
       while(1) {
          wait(&status);
          if(WIFEXITED(status))
              break;
          orig_eax = ptrace(PTRACE_PEEKUSER,
                     child, 4 * ORIG_EAX, NULL);
          if(orig_eax == SYS_open) {
             if(insyscall == 0) {
                /* Syscall entry */
                insyscall = 1;
                params[0] = ptrace(PTRACE_PEEKUSER,
                                   child, 4 * EBX,
                                   NULL);
                params[1] = ptrace(PTRACE_PEEKUSER,
                                   child, 4 * ECX,
                                   NULL);
                params[2] = ptrace(PTRACE_PEEKUSER,
                                   child, 4 * EDX,
                                   NULL);
                printf("## OPEN called with "
                       "%ld, %ld, %ld\n",
                       params[0], params[1],
                       params[2]);
		printf("path = %s\n",param[0]); //***************에러발생부분*****************//
              }
              else { /* Syscall exit */
                eax = ptrace(PTRACE_PEEKUSER, child, 4 * EAX, NULL);
                printf("Open returned with %ld\n", eax);
                insyscall = 0;
             }
 
          }//if(orig_eax == SYS_open) 
 
          ptrace(PTRACE_SYSCALL, child, NULL, NULL);
        }//while(1)
    }
    return 0;
}
bootmeta의 이미지

linux/user.h 제거 /* 제 시스템엔 없어서 제거 */

printf("path = %s\n",param[0]); /* %s 대신 %ld로 수정, param[0]대신 params[0] */

compile 경고, 에러만 보면 바로 해결이 가능했을 텐데요.

warning: format ‘%s’ expects type ‘char *’, but argument 2 has type ‘long int’

ps) test 환경
Linux 2.6.31-14-generic #48-Ubuntu SMP Fri Oct 16 14:04:26 UTC 2009 i686 GNU/Linux
gcc (Ubuntu 4.4.1-4ubuntu8) 4.4.1

songaal의 이미지

파일을 복사해서 넣는 도중에 실수한거구요.

해당 warning을 없애려면 (char *)로 캐스팅하면 될텐데, 제 컴에서 컴파일할땐 warning이 안났습니다.

printf("path = %s\n",(char *)params[0]);

로 수정을 하고 컴파일을 한다고 치구요...(혹시 이걸 해결이라고 하시면 제가 난감....시작도 안한건데...)

혹시 실행했을때 파일경로가 에러없이 잘 찍히시나요?

파일경로가 찍는게 이 프로그램의 목표인데요...%ld로 찍는 정수값이 아니구요.. %ld포맷으로 찍으면

잘 찍힌다는건 그 윗줄

 printf("## OPEN called with %ld, %ld, %ld\n", params[0], params[1], params[2]);

이 잘 출력되는것만 봐도 알수있구요...

그냥 해당 printf 라인 하나를 주석처리하면 프로그램 에러없이 잘돕니다.

파일경로를 찍으려니 에러가 나고 있습니다.

bootmeta의 이미지

즉 params[] 배열의 주소는 child의 register, 주소값이지 parent에서 의미있는 값이 아닙니다.
만일 parent에서 child 의 실제 data를 뽑아내려면 PTRACE_PEEKDATA를 이용, 현 parent 프로그램으로 data복사를 해야합니다.

http://www.linuxjournal.com/article/6100 (작업 코드를 보니 아마...) 문서를 참조하셨다면, Doing Funny Things 부분을 보시면 됩니다.

하도 오래 전에 본 코드라 기억이 가물 가물...

songaal의 이미지

Quote:
즉 params[] 배열의 주소는 child의 register, 주소값이지 parent에서 의미있는 값이 아닙니다.

고맙습니다. 미궁에 빠져있었는데 이 말이 실마리가 되었습니다.^^

문서를 보면서 Doing Funny Things부분을 좀더 고려했으면 아이디어가 떠올랐을텐데 왜 못봤는지 제자신이 안타깝네요..

실제로 PTRACE_PEEKDATA를 이용해서 데이터를 뽑아보니

SYS_open의 경우 (4 * EBX) 위치(정확히 말하면 이 위치의 값이 가리키는 child 프로세스의 데이터)에 파일명이 기록되어 있었고 (4 * ECX)에 O_RDONLY등의 옵션값이 들어있습니다.

이 두개의 데이터만 가지고도 제가 원하는 정도의 값을 뽑아낼수 있었습니다.

이 문제로 며칠을 고생했기때문에 이쯤에서 그만 파고 들고 싶기는한데..

각 read, open, write등의 SYSCALL호출시 레지스터의 값이 무엇을 가리키는지를 서술해 놓은 문서가 어디 없을까요...

Intel® 64 and IA-32 Architectures Software Developer’s Manual 5개를 pdf로 받아서 훓어봤는데 그런 내용은 없는것 같더구요...

http://www.linuxjournal.com/article/6100 글을 작성한 글쓴이는 어디서 이런걸 알았는지 궁금하네요.

bootmeta의 이미지

http://www.linuxjournal.com/article/6100 Basics 영역을 보시면 간단하게나마 설명이 되있습니다.
intel 386(32bit)에서 linux sys call의 경우, sys call번호는 %eax, function argument들은 순서대로 %ebx, %ecx, %edx, %esi, %edi에 넣습니다.
만일 헤깔린다 싶으면 간단히 write같은 syscall만 호출하는 아주 단순한 c code 작성 후, assembly로 dump 해보면 확인 가능합니다.

http://www.linuxjournal.com/article/6100 Basics 내 code 참조

write(2, "Hello", 5)

movl   $4, %eax        ;; sys call number
movl   $2, %ebx        ;; 1번째 인자
movl   $hello,%ecx     ;; 2번째 인자
movl   $5, %edx        ;; 3번째 인자
int    $0x80           ;; linux에서 inttrrupt 80은 linux sys call 호출 의미

ps)
intel manual은 cpu manual이니, linux system call은 안나오겠죠.
미리 정의된 system call 번호를 보려면 asm/unistd_32.h(64bit라면 asm/unistd_64.h)
code 내 header 파일로 사용하려면 linux/unistd.h를 포함

http://www.linuxjournal.com/article/6100 에서 간단히 code만 참조하지 마시고, 정독해 보시면 도움 됩니다.

gdb등에서 어떻게 breakpoint등을 구현하는지 보시려면
http://www.linuxjournal.com/article/6210 part2 추천

songaal의 이미지

레지스터에 어떻게 셋팅이 되고 있는지 알수가 있겠군요.

한번 테스트 해봤는데

int main(){
write(2, "Hello", 5);
open( "abcdefg", 0 );
return 4;
}

	.file	"t1.c"
	.section	.rodata
.LC0:
	.string	"Hello"
.LC1:
	.string	"abcdefg"
	.text
.globl main
	.type	main, @function
main:
	pushl	%ebp
	movl	%esp, %ebp
	andl	$-16, %esp
	subl	$16, %esp
	movl	$5, 8(%esp)
	movl	$.LC0, 4(%esp)
	movl	$2, (%esp)
	call	write
	movl	$0, 4(%esp)
	movl	$.LC1, (%esp)
	call	open
	movl	$4, %eax
	leave
	ret
	.size	main, .-main
	.ident	"GCC: (Ubuntu 4.4.1-4ubuntu8) 4.4.1"
	.section	.note.GNU-stack,"",@progbits

로 나오네요.
esp로 사용되어서 ebx에 셋팅되는지 ecx에 되는지 잘 알수는 없지만 그래도 이정도로 만족해야죠..

bootmeta님 귀한시간 답변해주셔서 감사드립니다.(__)

이이의 이미지

그냥 보고 지나쳐야징

댓글 달기

Filtered HTML

  • 텍스트에 BBCode 태그를 사용할 수 있습니다. URL은 자동으로 링크 됩니다.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>
  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.

BBCode

  • 텍스트에 BBCode 태그를 사용할 수 있습니다. URL은 자동으로 링크 됩니다.
  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param>
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.

Textile

  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • You can use Textile markup to format text.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>

Markdown

  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • Quick Tips:
    • Two or more spaces at a line's end = Line break
    • Double returns = Paragraph
    • *Single asterisks* or _single underscores_ = Emphasis
    • **Double** or __double__ = Strong
    • This is [a link](http://the.link.example.com "The optional title text")
    For complete details on the Markdown syntax, see the Markdown documentation and Markdown Extra documentation for tables, footnotes, and more.
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>

Plain text

  • HTML 태그를 사용할 수 없습니다.
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.
  • 줄과 단락은 자동으로 분리됩니다.
댓글 첨부 파일
이 댓글에 이미지나 파일을 업로드 합니다.
파일 크기는 8 MB보다 작아야 합니다.
허용할 파일 형식: txt pdf doc xls gif jpg jpeg mp3 png rar zip.
CAPTCHA
이것은 자동으로 스팸을 올리는 것을 막기 위해서 제공됩니다.