Everhart, Glenn (FUSA)
From:	Daniel Brown [dbrown@CCDC.CAM.AC.UK]
Sent:	Tuesday, May 18, 1999 10:41 AM
To:	BUGTRAQ@NETSPACE.ORG
Subject:	Re: Clarification: LD_PRELOAD issue
Here's a similar snippet for Solaris using the procfs interface...

Dan.

--

#include <sys/types.h>
#include <sys/stat.h>
#include <sys/param.h>
#include <sys/syscall.h>
#include <sys/uio.h>

#include <fcntl.h>
#include <procfs.h>
#include <stdio.h>
#include <unistd.h>

/* Faking of the time() system call via procfs.
 *
 * Daniel Brown, dbrown@ccdc.cam.ac.uk
 *
 * Compiled and tested under Solaris 2.6/sparc and 2.6/x86
 */

int
read_status(pid_t pid, pstatus_t *status)
{
	char buf[MAXPATHLEN];
	int fd;
	
	sprintf(buf, "/proc/%d/status", (int) pid);
	
	if ((fd = open(buf, O_RDONLY)) == -1) {
		perror("read_status(open)");
		return 1;
	}
	
	if (read(fd, (void *) status, sizeof(pstatus_t)) != sizeof(pstatus_t))
	{
		perror("read_status(read)");
		return 1;
	}
	close(fd);
	
	return 0;
}

int
write_ctl(pid_t pid, long syscall, void *arg, size_t arglen)
{
	int fd;
	char buf[MAXPATHLEN];
	struct iovec vec[2];
	
	sprintf(buf, "/proc/%d/ctl", (int) pid);
	
	if ((fd = open(buf, O_WRONLY)) == -1) {
		perror("write_ctl(open)");
		return 1;
	}
	
	vec[0].iov_base = (void *) &syscall;
	vec[0].iov_len = sizeof(long);

	if (arg != NULL) {
		vec[1].iov_base = arg;
		vec[1].iov_len = arglen;
		
		if (writev(fd, vec, 2) != (vec[0].iov_len + vec[1].iov_len)) {
			perror("write_ctl(write2)");
			close(fd);
			return 1;
		}
	} else {
		if (writev(fd, vec, 1) != vec[0].iov_len) {
			perror("write_ctl(write1)");
			return 1;
		}
	}
	
	close(fd);
	
	return 0;
}
	
int
main(int argc, char **argv)
{
	pid_t pid, ppid;
	pstatus_t pstatus;
	sysset_t sysset;
	long val;
	
	setvbuf(stdout, (char *) NULL, _IONBF, (size_t) 0);
	
	ppid = getpid();
	
	if (read_status(ppid, &pstatus))
		exit(1);

	printf("Parent PID is %d\n", (int) ppid);
	
	printf("Setting PCSEXIT for time() : stop on exit from time().\n");
	
	premptyset(&sysset);
	praddset(&sysset, SYS_time);

	if (write_ctl(ppid, PCSEXIT, (void *) &sysset, sizeof(sysset_t)))
		exit(1);
		
	printf("Setting PR_FORK, so that the child inherits these traps.\n");

	val = PR_FORK;
	
	if (write_ctl(ppid, PCSET, (void *) &val, sizeof(long)))
		exit(1);

	printf("Finally, setting PCSENTRY for exit() "
	       ": stop on entry to exit().\n");
	
	premptyset(&sysset);
	praddset(&sysset, SYS_exit);

	if (write_ctl(ppid, PCSENTRY, (void *) &sysset, sizeof(sysset_t)))
		exit(1);
	
	if ((pid = fork()) < 0) {
		perror("fork");
		exit(1);
	} else if (pid > 0) { /* Parent */
		
		printf("Clearing exit() trap for the parent...\n");
		
		premptyset(&sysset);
		if (write_ctl(ppid, PCSENTRY, (void *) &sysset,
		sizeof(sysset_t)))
			exit(1);	/* Good luck! */
		
		if (read_status(pid, &pstatus))
			exit(1);
		
		printf("Child PID is %d and %s have a trap set on time().\n",
			(int) pstatus.pr_pid,
			(prismember(&pstatus.pr_sysexit,
			SYS_time)) ? "does" : "doesn't");
		
		while (1) {
		
		printf("Waiting for child to call time() or exit().\n");

		if (write_ctl(pid, PCWSTOP, (void *) 0, 0))
			exit(1);
			
		printf("Write PCWSTOP returned. Reading status.\n");
		
		if (read_status(pid, &pstatus))
			exit(1);
		
		if (pstatus.pr_lwp.pr_syscall == SYS_exit) {
			printf("Child has called exit(). Bye!\n");
			exit(0);
		} else if (pstatus.pr_lwp.pr_syscall != SYS_time) {
			printf("We've caught syscall %d! Eeeek!\n",
				(int) pstatus.pr_lwp.pr_syscall);
			exit(1);
		}
		
		printf("Child has called time().\n");
				
		printf("LWP register R_R0 is %d.\n",
			(int) pstatus.pr_lwp.pr_reg[R_R0]);
		
		printf("Setting LWP register R_R0 to 123.\n");
		
		pstatus.pr_lwp.pr_reg[R_R0] = 123;
		if (write_ctl(pid, PCSREG, (void *) &pstatus.pr_lwp.pr_reg,
				sizeof(prgregset_t)))
			exit(1);
				
		printf("Restarting the child.\n");
		
		val = 0L;
		
		if (write_ctl(pid, PCRUN, (void *) &val, sizeof(long)))
			exit(1);
		
		printf("Restarted.\n");
		
		}	/* while (1) */

		/* NOT REACHED */
	}
	
	/* Child */

	sleep(5);
	
	execl("/usr/bin/date", "/usr/bin/date", (char *) NULL);
		
	perror("execl failed");
	
	return 99;
}