因为utumno0不可读, 开始以为用strace可以获取一些信息
最后只是调用write,并没什么用(从这里可以知道库函数puts最后是调用write来实现的)
可以发现可执行文件在最后调用puts函数
这需要实现自己的puts函数替换库函数
自己实现的puts函数如下:
puts的参数
next main eip
ebp
ebx
0x24B
调用printf("%x-%x-%x-%x-%x-%x-%x-%x-%x-%x-%x-%x-");时
栈顶是指向"%x-%x-%x-%x-%x-%x-%x-%x-%x-%x-%x-%x-"的指针
10个%x把0x20B + ebx + ebp打印出来
最后两个%x把eip和puts的参数打印出来
为什么我们要把最后两个%x打印出来的地址之间的字符打印出来
倒数第二个%x打印出来的还是代码段的数据,
倒数第一个%x打印出来的是只读数据段的数据
那么如果utumno1的登陆密码是类似"aathaeyiew"这样初始化的,那么肯定存储在只读数据段中
那么理论上把整个只读数据段dump出就可能找到密码
程序是在结尾调用puts("Read me! :P"); 按照先后顺序,编译器很可能把"Read me! :P"放在只读数据段的最尾端
那么在代码段地址 和 只读数据段最尾端地址之间可以获取到所有只读数据段的数据
最后只是调用write,并没什么用(从这里可以知道库函数puts最后是调用write来实现的)
最后利用xocopy把可执行代码dump出来
gcc xocopy.c -o xocopy -m32
./xocopy /utumno/utumno0
./xocopy -a 0x08049000 /utumno/utumno0
objdump -D utumno0.out
(Disassembly of section .rodata:)
080484b8 <.rodata>:
80484b8: 03 00 add (%eax),%eax
80484ba: 00 00 add %al,(%eax)
80484bc: 01 00 add %eax,(%eax)
80484be: 02 00 add (%eax),%al
"aathaeyiew"
80484c0: 61 popa
80484c1: 61 popa
80484c2: 74 68 je 804852c <__libc_start_main@plt+0x23c>
80484c4: 61 popa
80484c5: 65 gs
80484c6: 79 69 jns 8048531 <__libc_start_main@plt+0x241>
80484c8: 65 gs
80484c9: 77 00 ja 80484cb <__libc_start_main@plt+0x1db>
"Read me! :P"
80484cb: 52 push %edx
80484cc: 65 gs
80484cd: 61 popa
80484ce: 64 20 6d 65 and %ch,%fs:0x65(%ebp)
80484d2: 21 20 and %esp,(%eax)
80484d4: 3a 50 00 cmp 0x0(%eax),%dl
到这里应该算就结束了,下面再介绍一种方法来解这一题
可以发现可执行文件在最后调用puts函数
这需要实现自己的puts函数替换库函数
自己实现的puts函数如下:
#define _GNU_SOURCE
#include <stdio.h>
#include <stdint.h>
#include <dlfcn.h>
int puts(const char *s)
{
int i;
char *pt;
// Variable to store the original puts function. just incase we need it.
static void* (*my_puts)(const char*s) = NULL;
if (!my_puts){
// Store the original puts function.
my_puts = dlsym(RTLD_NEXT, "puts");
}
// Start looking at stack addresses
printf("%x-%x-%x-%x-%x-%x-%x-%x-%x-%x-%x-%x-");
// Display our hooked
printf("Hooked-%s", s);
// Run through the memory space.
for( i = 0x804841a; i < 0x80484cb; i++) {
pt = i;
printf("%c", *pt);
printf("");
}
return 0;
}
gcc -m32 -fPIC -c hookputs.c
ld -shared -m elf_i386 -o hookputs.so hookputs.o -ldl
objdump -d hookputs.so
Disassembly of section .text:
00000240 <puts>:
240: 55 push %ebp
241: 89 e5 mov %esp,%ebp
243: 53 push %ebx
244: 83 ec 24 sub $0x24,%esp
那么进入puts时的栈环境是
puts的参数
next main eip
ebp
ebx
0x24B
调用printf("%x-%x-%x-%x-%x-%x-%x-%x-%x-%x-%x-%x-");时
栈顶是指向"%x-%x-%x-%x-%x-%x-%x-%x-%x-%x-%x-%x-"的指针
10个%x把0x20B + ebx + ebp打印出来
最后两个%x把eip和puts的参数打印出来
为什么我们要把最后两个%x打印出来的地址之间的字符打印出来
倒数第二个%x打印出来的还是代码段的数据,
倒数第一个%x打印出来的是只读数据段的数据
那么如果utumno1的登陆密码是类似"aathaeyiew"这样初始化的,那么肯定存储在只读数据段中
那么理论上把整个只读数据段dump出就可能找到密码
程序是在结尾调用puts("Read me! :P"); 按照先后顺序,编译器很可能把"Read me! :P"放在只读数据段的最尾端
那么在代码段地址 和 只读数据段最尾端地址之间可以获取到所有只读数据段的数据
root@today:~/Desktop/utumno/utumno0# ssh utumno0@178.79.134.250
utumno0@178.79.134.250's password: utumno0
utumno0@melinda:~$ cd /tmp/utumno0
utumno0@melinda:/tmp/utumno0$ gcc -m32 -fPIC -c hookputs.c
utumno0@melinda:/tmp/utumno0$ ld -shared -m elf_i386 -o hookputs.so hookputs.o -ldl
utumno0@melinda:/tmp/utumno0$ LD_PRELOAD="./hookputs.so" ltrace /utumno/utumno0
ERROR: ld.so: object './hookputs.so' from LD_PRELOAD cannot be preloaded (wrong ELF class: ELFCLASS32): ignored.
failed to initialize process 24891: No such file or directory
couldn't open program '/utumno/utumno0': No such file or directory
utumno0@melinda:/tmp/utumno0$ f7fd52c0-0-0-ffffd5d8-f7ff0500-ffffd604-f7fd5210-1-f7fc9000-ffffd5d8-804841a-80484cb---
ffffd674-ffffd67c-f7e5510d-f7fc93c4-f7ffd000-804843b-80484c0Hooked-Read me! :Pffffffb8(�) 00() 00() 00() 00()
ffffffc9(�) ffffffc3(�) 66(f) ffffff90(�) 66(f) ffffff90(�) 66(f) ffffff90(�) 66(f) ffffff90(�) 66(f) ffffff90(�)
66(f) ffffff90(�) 66(f) ffffff90(�) ffffff90(�) 55(U) 57(W) 31(1) ffffffff(�) 56(V) 53(S) ffffffe8(�) fffffff5(�)
fffffffe(�) ffffffff(�) ffffffff(�) ffffff81(�) ffffffc3(�) 71(q) 12() 00() 00() ffffff83(�) ffffffec(�) 1c() ffffff8b(�)
6c(l) 24($) 30(0) ffffff8d(�) ffffffb3(�) 0c(...) ffffffff(�) ffffffff(�) ffffffff(�) ffffffe8(�) 3d(=) fffffffe(�)
ffffffff(�) ffffffff(�) ffffff8d(�) ffffff83(�) 08) ffffffff(�) ffffffff(�) ffffffff(�) 29()) ffffffc6(�) ffffffc1(�)
fffffffe(�) 02() ffffff85(�) fffffff6(�) 74(t) 27(') ffffff8d(�) ffffffb6(�) 00() 00() 00() 00() ffffff8b(�) 44(D)
24($) 38(8) ffffff89(�) 2c(,) 24($) ffffff89(�) 44(D) 24($) 08) ffffff8b(�) 44(D) 24($) 34(4) ffffff89(�) 44(D) 24($) 04()
ffffffff(�) ffffff94(�) ffffffbb(�) 08) ffffffff(�) ffffffff(�) ffffffff(�) ffffff83(�) ffffffc7(�) 01() 39(9) fffffff7(�)
75() ffffff90(�) ffffff90(�) ffffff90(�) ffffff90(�) ffffff90(�) ffffff90(�) ffffff90(�) ffffff90(�) ffffff90(�) ffffff90(�)
ffffff90(�) ffffff90(�) ffffff90(�) fffffff3(�) ffffffc3(�) 00() 00() 53(S) ffffff83(�) ffffffec(�) 08) ffffffe8(�) ffffff83(�)
fffffffe(�) ffffffff(�) ffffffff(�) ffffff81(�) ffffffc3(�) ffffffff(�) 11() 00() 00() ffffff83(�) ffffffc4(�) 08) 5b([)
ffffffc3(�) 03() 00() 00() 00() 01() 00() 02() 00() 61(a) 61(a) 74(t) 68(h) 61(a) 65(e) 79(y) 69(i) 65(e) 77(w) 00()
xocopy.c
/* xocopy - Program for copying an executable with execute but no read perms.
* Copyright (C) 2002, 2003 Dion Mendel.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
/*
* A simple program to obtain a readable copy of an executable which has
* execute, but no read permission. It works by executing the process
* - to load the process into memory - then dumping the memory image.
*
* Does not work for suid apps under linux 2.2.x.
* Only works for elf files for which the elf header and program header table
* are part of the loadable text segment (default for gcc).
*
* Generally, any data that appears in the file after loadable segments that
* extend their size (usually shdr) will not be recovered.
*
* NOTE: This is a proof of concept program. It is not robust.
* NOTE: Does not work on linux kernels between 2.4.21-pre6 .. 2.4.21-rc2
* due to an incorrect ptrace patch being applied to those kernels.
*/
/* undefine this if there is no elf.h file on the system */
#define HAVE_ELF_H
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <signal.h>
#ifdef HAVE_ELF_H
# include <elf.h>
#endif
#include <sys/types.h>
#include <sys/ptrace.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <unistd.h>
/*----------------------------------------------------------------------------*/
#ifdef __FreeBSD__
# define PTRACE_PEEKTEXT PT_READ_I
# define PTRACE_PEEKDATA PT_READ_D
# define PTRACE_TRACEME PT_TRACE_ME
# define PTRACE_KILL PT_KILL
#endif /* __FreeBSD__ */
/*----------------------------- ELF Definition -------------------------------*/
#ifndef HAVE_ELF_H
/* Type for a 16-bit quantity. */
typedef u_int16_t Elf32_Half;
/* Types for signed and unsigned 32-bit quantities. */
typedef u_int32_t Elf32_Word;
/* Type of addresses. */
typedef u_int32_t Elf32_Addr;
/* Type of file offsets. */
typedef u_int32_t Elf32_Off;
/* The ELF file header. This appears at the start of every ELF file. */
#define EI_NIDENT (16)
typedef struct
{
unsigned char e_ident[EI_NIDENT]; /* Magic number and other info */
Elf32_Half e_type; /* Object file type */
Elf32_Half e_machine; /* Architecture */
Elf32_Word e_version; /* Object file version */
Elf32_Addr e_entry; /* Entry point virtual address */
Elf32_Off e_phoff; /* Program header table file offset */
Elf32_Off e_shoff; /* Section header table file offset */
Elf32_Word e_flags; /* Processor-specific flags */
Elf32_Half e_ehsize; /* ELF header size in bytes */
Elf32_Half e_phentsize; /* Program header table entry size */
Elf32_Half e_phnum; /* Program header table entry count */
Elf32_Half e_shentsize; /* Section header table entry size */
Elf32_Half e_shnum; /* Section header table entry count */
Elf32_Half e_shstrndx; /* Section header string table index */
} Elf32_Ehdr;
#define EI_CLASS 4 /* File class byte index */
#define ELFCLASS32 1 /* 32-bit objects */
/* Legal values for e_type (object file type). */
#define ET_EXEC 2 /* Executable file */
#define ET_DYN 3 /* Shared object file */
/* Program segment header. */
typedef struct
{
Elf32_Word p_type; /* Segment type */
Elf32_Off p_offset; /* Segment file offset */
Elf32_Addr p_vaddr; /* Segment virtual address */
Elf32_Addr p_paddr; /* Segment physical address */
Elf32_Word p_filesz; /* Segment size in file */
Elf32_Word p_memsz; /* Segment size in memory */
Elf32_Word p_flags; /* Segment flags */
Elf32_Word p_align; /* Segment alignment */
} Elf32_Phdr;
/* Legal values for p_type (segment type). */
#define PT_NULL 0 /* Program header table entry unused */
#define PT_LOAD 1 /* Loadable program segment */
#endif /* Elf Definition */
/*----------------------------------------------------------------------------*/
/* this is the word datatype returned by ptrace for PEEK* */
#define PTRACE_WORD int32_t
/*
* PAGE_SIZE - the size of a memory page
* LO_USER - the lowest address accessible from user space (% PAGE_SIZE)
* HI_USER - the highest address accessible from user space (% PAGE_SIZE)
*/
#if defined (__linux__)
# define PAGE_SIZE 4096U
# define LO_USER 4096U
# define HI_USER 0xc0000000U
#elif defined (__FreeBSD__)
# define PAGE_SIZE 4096U
# define LO_USER 4096U
# define HI_USER 0xbfc00000U
#else
ERROR UNKNOWN OPERATING SYSTEM
#endif
#define PAGE_MASK (~(PAGE_SIZE-1))
/* ---------------- useful functions possibly found in libc ---------------- */
static char *
basename (char *pathname)
{
char *ptr;
ptr = strrchr(pathname, '/');
return ptr ? ptr + 1 : pathname;
}
static int
tolower (int c)
{
if ('A' <= c && c <= 'Z')
return c + 'a' - 'A';
return c;
}
/*
* Reads a given number of bytes from the text segment.
* num_bytes must be a multiple of the word size
*/
static int
read_text_segment (pid_t pid, unsigned int addr, char *buf, size_t num_bytes)
{
int i;
int num_words;
/* determine number of words required to read num_bytes */
num_words = num_bytes / sizeof(PTRACE_WORD);
if ((num_bytes % sizeof(PTRACE_WORD)) != 0)
num_words++;
for (i = 0; i < num_words; i++) {
*(((PTRACE_WORD *) buf) + i ) = ptrace(PTRACE_PEEKTEXT, pid,
addr + i * sizeof(PTRACE_WORD), 0);
if (errno != 0) {
char msg[1024];
snprintf(msg, sizeof(msg), "ptrace(PTRACE_PEEKTEXT, pid, 0x%08x, 0)",
addr + i * sizeof(PTRACE_WORD));
perror(msg);
return 0;
}
}
return 1;
}
/*---------------------------------------------------------------------------*/
#define LO_PAGE_ADDR(phdr) ((phdr)->p_offset & PAGE_MASK)
#define HI_PAGE_ADDR(phdr) (((phdr)->p_offset + ((phdr)->p_filesz) + PAGE_SIZE - 1) & PAGE_MASK)
#define MIN(a,b) ((a) < (b) ? (a) : (b))
#define INTERSECTS(off1, size1, off2, size2) ( ((off1) < (off2)) ? ((off2) < (off1) + (size1)) : ((off1) < ((off2) + (size2))) )
/*
* Prints warning message for the bytes in the file that couldn't be recovered.
* Uses 0/0 for offset/size to signal end of all lost data.
*/
static void
warn_lost_data (pid_t pid, Elf32_Ehdr *ehdr, Elf32_Phdr *phdr,
unsigned int offset, unsigned int size)
{
static unsigned int last_offset = 0; /* for recording last offset */
static unsigned int last_size = 0; /* and size - initialised to zero */
int p;
if (offset != 0 && size != 0) {
if ((last_offset + last_size) == offset) {
/* join offsets */
last_size += size;
return;
}
}
if (last_offset != 0 && last_size != 0) {
fprintf(stderr, "could not recover data - %d bytes at file offset %d\n",
last_size, last_offset);
for (p = 0; p < ehdr->e_phnum; p++) {
if (phdr[p].p_type != PT_NULL) {
if (INTERSECTS(last_offset, last_size,
phdr[p].p_offset, phdr[p].p_filesz))
{
fprintf(stderr, " ! data from phdr[%d] was not recovered\n", p);
}
}
}
if ((ehdr->e_shnum != 0) &&
INTERSECTS(last_offset, last_size, ehdr->e_shoff,
ehdr->e_shnum * ehdr->e_shentsize))
{
fprintf(stderr, " ! section header table was not recovered\n");
}
}
/* record this offset and size */
last_offset = offset;
last_size = size;
}
/*
* Writes the memory pages to the given filename. Requires that ehdr and phdr
* are in loaded memory.
*/
static int
create_file (char *filename, pid_t pid, Elf32_Ehdr *ehdr, Elf32_Phdr *phdr,
size_t file_size)
{
FILE *fptr;
Elf32_Phdr *this_phdr;
char page[PAGE_SIZE];
int i, p;
int num_pages;
unsigned int *pages;
unsigned int end_segment_address;
int okay, last_page;
num_pages = (file_size + PAGE_SIZE - 1) / PAGE_SIZE;
pages = malloc(num_pages * sizeof(*pages));
if (pages == NULL) {
fprintf(stderr, "malloc failed\n");
return 0;
}
/* map memory pages to position in file */
for (i = 0; i < num_pages; i++) {
pages[i] = 0;
for (p = 0; p < ehdr->e_phnum; p++) {
this_phdr = &phdr[p];
if (this_phdr->p_type == PT_LOAD) {
if (LO_PAGE_ADDR(this_phdr) <= (i * PAGE_SIZE) &&
((i + 1) * PAGE_SIZE) <= HI_PAGE_ADDR(this_phdr))
{
/* check for lost data in the last page of the segment */
end_segment_address = this_phdr->p_offset + this_phdr->p_filesz;
last_page = end_segment_address < ((i + 1) * PAGE_SIZE);
if (last_page && (this_phdr->p_memsz > this_phdr->p_filesz)) {
warn_lost_data(pid, ehdr, phdr, end_segment_address,
((i + 1) * PAGE_SIZE) - end_segment_address);
}
pages[i] = phdr[p].p_vaddr - phdr[p].p_offset + (i * PAGE_SIZE);
break;
}
}
}
/* warn about lost data if no memory page maps to file */
if (pages[i] == 0)
warn_lost_data(pid, ehdr, phdr, i * PAGE_SIZE, PAGE_SIZE);
}
/* signal that an attempt to recover all pages has been made */
warn_lost_data(pid, ehdr, phdr, 0, 0);
/* write memory pages to file */
okay = 0;
if ( (fptr = fopen(filename, "wb")) != NULL) {
for (i = 0; i < num_pages; i++) {
if (pages[i] != 0) {
if (! read_text_segment(pid, pages[i], page, PAGE_SIZE)) {
fclose(fptr);
free(pages);
return 0;
}
}
else {
memset(page, '\0', PAGE_SIZE);
}
fwrite(page, 1, MIN(file_size, PAGE_SIZE), fptr);
file_size -= PAGE_SIZE;
}
fclose(fptr);
okay = 1;
}
else {
char msg[1024];
snprintf(msg, sizeof(msg), "couldn't create file `%s'", filename);
perror(msg);
}
free(pages);
return okay;
}
/*
* Error check before writing the memory pages to disk.
*/
static int
save_to_file (char *filename, pid_t pid, unsigned int addr, size_t file_size)
{
char page[PAGE_SIZE];
Elf32_Ehdr *ehdr;
Elf32_Phdr *phdr;
int okay;
okay = 0;
if (read_text_segment(pid, addr, page, PAGE_SIZE)) {
/* ensure 32bit elf binary */
ehdr = (Elf32_Ehdr *) page;
if (page[EI_CLASS] == ELFCLASS32 && ehdr->e_type == ET_EXEC) {
/* ensure program header table is in same page as elf header */
if ((ehdr->e_phoff + ehdr->e_phnum * ehdr->e_phentsize) < PAGE_SIZE) {
phdr = (Elf32_Phdr *) (page + ehdr->e_phoff);
okay = create_file(filename, pid, ehdr, phdr, file_size);
}
else {
fprintf(stderr, "program header table could not be found\n");
}
}
else {
fprintf(stderr, "no 32bit elf executable, found at addr 0x%08x\n",
addr);
}
}
return okay;
}
#define NUM_ELF_HEADERS 10
/*
* Searches memory for an elf header.
*/
static unsigned int
find_elf_header (pid_t pid)
{
int i;
char *elf_hdr = "\177ELF";
Elf32_Ehdr hdr;
unsigned int possible[NUM_ELF_HEADERS];
int num_possible;
unsigned int addr;
PTRACE_WORD word;
int found_elf_header;
num_possible = 0;
/* search each page to see if elf header is found */
for (addr = LO_USER; addr < HI_USER; addr += PAGE_SIZE) {
found_elf_header = 0;
word = ptrace(PTRACE_PEEKTEXT, pid, addr, 0);
if ((errno == 0) && (word == *((PTRACE_WORD *) elf_hdr))) {
if (read_text_segment(pid, addr, (char *) &hdr, sizeof(hdr))) {
if (hdr.e_type == ET_EXEC)
found_elf_header = 1;
else if (hdr.e_type == ET_DYN)
fprintf(stderr, "discarding shared library at "
"virtual memory address 0x%08x\n", addr);
}
}
if (found_elf_header) {
if (num_possible == NUM_ELF_HEADERS) {
fprintf(stderr, "too many possible elf headers found (> %d)\n",
NUM_ELF_HEADERS);
return 0;
}
possible[num_possible] = addr;
num_possible++;
}
}
if (num_possible == 0) {
/* no elf header found */
return 0;
}
else if (num_possible == 1) {
/* a single elf header was found */
fprintf(stdout, "using elf header at virtual memory address "
" 0x%08x\n", possible[0]);
return possible[0];
}
else {
/* need to resolve conflicts - let user decide */
fprintf(stderr, "multiple elf headers found:\n");
for (i = 0; i < num_possible; i++) {
printf(" 0x%08x\n", possible[i]);
}
return 0;
}
}
int
main (int argc, char *argv[])
{
char *filename;
char out_filename[8192];
char buf[1024];
struct stat stat_buf;
pid_t pid;
int status;
int ret_val;
size_t file_size;
unsigned int addr;
int bad_usage;
filename = NULL;
addr = 0;
/* process args: assigning values to bad_usage filename and possibly addr */
bad_usage = 1;
if (argc == 2) {
filename = argv[1];
bad_usage = 0;
}
else if (argc == 4) {
filename = argv[3];
if (strcmp(argv[1], "-a") == 0) {
if ((argv[2][0] == '0') && (tolower(argv[2][1]) == 'x'))
addr = strtol(argv[2], NULL, 16);
else
addr = strtol(argv[2], NULL, 10);
if (errno != ERANGE)
bad_usage = 0;
}
}
if (bad_usage) {
fprintf(stderr, "Obtains an executable copy of a binary with execute "
"but no read permission\n"
"Usage: %s [-a addr] <file>\n"
" where addr is the memory address of the elf header\n",
argv[0]);
exit(EXIT_FAILURE);
}
if (stat(filename, &stat_buf) != 0) {
snprintf(buf, sizeof(buf), "couldn't stat file `%s'", filename);
perror(buf);
exit(EXIT_FAILURE);
}
/* remember file size of original file */
file_size = stat_buf.st_size;
if ( (pid = fork()) == 0) {
/* child */
if (ptrace(PTRACE_TRACEME, 0, 0, 0) == 0) {
execl(filename, filename, NULL);
snprintf(buf, sizeof(buf), "couldn't exec `%s'", filename);
perror(buf);
}
else {
perror("ptrace(PTRACE_TRACEME, ...)");
}
_exit(EXIT_FAILURE);
}
ret_val = EXIT_FAILURE;
if (waitpid(pid, &status, WUNTRACED) == pid) {
if (!WIFEXITED(status)) {
/* SIGTRAP is delivered to child after execve */
if (WIFSTOPPED(status) && WSTOPSIG(status) == SIGTRAP) {
if (addr == 0)
addr = find_elf_header(pid);
if (addr != 0) {
snprintf(out_filename, sizeof(out_filename), "%s.out",
basename(filename));
if (save_to_file(out_filename, pid, addr, file_size)) {
chmod(out_filename, 00755);
fprintf(stdout, "created file `%s'\n", out_filename);
ret_val = EXIT_SUCCESS;
}
}
else {
fprintf(stderr, "couldn't find elf header in memory\n");
}
}
else {
fprintf(stderr, "didn't receive SIGTRAP after execve\n");
}
/* kill child as we are finished */
ptrace(PTRACE_KILL, pid, 0, 0);
}
}
else {
perror("waitpid");
}
return(ret_val);
}