/*
 * Jan 18 2003 --solarx
 * 
 * It's not authors fault if this sets your computer on fire or 
 * something even more nasty. This src is for educational purposes 
 * only. Some of the functions used within this program are based in 
 * part of article from Phrack 59.
 *
 * I keep hacking these same files and functions together and end up with things 
 * that do totally diffrent handy shit like this one does.
 *
 * Tested with: gcc 2.95.3, gcc 3.2.1
 * Tested with dietlibc but do to some header/name conflicts it failed,
 * however it but could be made to work rather easily.
 *
 * Usage:
 *
 * ntrace 	;# With no options it will try and attach to and print everything in its uid class.
 * ntrace <pid> ;# Attach to a single pid and print all symbols loaded in its memory and there address.
 * ntrace <pid> [function] [func... ;# Attach to a single pid and print location of each symboy given on the cmdline.
 *
 *
 * Compile:
 *
 * cc -o ntrace ntrace.c 
 * cc -o ntrace ntrace.c -DEBUG 
 *
 */

#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdlib.h>
#include <string.h>
#include <sys/mman.h>
#include <sys/ptrace.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>

#include <elf.h>
#include <fcntl.h>
#include <link.h>
#include <linux/user.h>

#ifdef EBUG
#define DBG(a)  a
#else				/* !EBUG */
#define DBG(a)			/* nothing */
#endif				/* EBUG */


unsigned char
 soloader[] = "\xeb\x13"	/*    jmp    15                     */
    "\x58"			/*    pop    %eax                   */
    "\x31\xc9"			/*    xor    %ecx,%ecx              */
    "\xba\x01\x00\x00\x00"	/*    mov    $0x1,%edx              */
    "\xbb\x78\x56\x34\x12"	/*    mov    $0x12345678,%ebx       */
    "\xff\xd3"			/*    call   *%ebx                  */
    "\x83\xc4\x04"		/*    add    $0x4,%esp              */
    "\xcc"			/*    int3                          */
    "\xe8\xe8\xff\xff\xff";	/*    call   2                      */
			    /*    hi mqe & yella !              */

unsigned char
 jmp_code[] = "\x68\x60\x70\x80\x90"	/* push dword 0x90807060 */
    "\xc3\x90\x90";		/* ret , nop , nop       */

unsigned long symtab;
unsigned long strtab;
int nchains;

int task_list[65536];

#undef DBG
#ifdef EBUG
#define DBG(a)  a
#else				/* !EBUG */
#define DBG(a)			/* nothing */
#endif				/* EBUG */


#ifndef PATH_MAX
#define PATH_MAX 255
#endif


char *iota(int num)
{
   static char str[17];
   int x = 0;
   int i = 16;

   str[16] = 0;
   if (!num) {
      str[15] = '0';
      return str + 15;
   }
   if (num < 0) {
      x = 1;
      num *= -1;
   }
   while (num) {
      i--;
      str[i] = '0' + (num % 10);
      num /= 10;
   }
   if (x) {
      i--;
      str[i] = '-';
   }
   return str + i;
}

char *getpidname(int pid)
{
   int fd;
   char *p;
   static char buf[255];
   if (pid < 0 && pid > 65536)
      return NULL;
   strcpy(buf, "/proc/");
   strncat(buf, iota(pid), sizeof(buf));
   strncat(buf, "/stat", sizeof(buf));
#ifndef __linux__
   strncat(buf, "us", sizeof(buf));
#endif

   if ((fd = open(buf, O_RDONLY)) < 0)
      return NULL;
   memset(buf, 0, sizeof(buf));
   if (read(fd, buf, sizeof(buf) - 1) > 0) {
#ifdef __linux__
      if (p = strchr(buf, ' ')) {
	 strcpy(buf, p + 2);
	 if (p = strrchr(buf, ')'))
	    *p = 0;
      }
#else
      if (p = strchr(buf, ' '))
	 *p = 0;
#endif
   }
   close(fd);
   return buf;
}

/* attach to pid */

long ptrace_attach(int pid)
{
   long ret;
   if (((ret = ptrace(PTRACE_ATTACH, pid, NULL, NULL))) < 0) {
      // DBG((perror("ptrace_attach")));
      return ret;
   }
   waitpid(pid, NULL, WUNTRACED);
   DBG((printf("Attached to %d - %s\n", pid, getpidname(pid))));
   return ret;
}


/* continue execution */

void ptrace_cont(int pid)
{
   int s;
   if ((ptrace(PTRACE_CONT, pid, NULL, NULL)) < 0) {
      // DBG((perror("ptrace_cont")));
      return;
   }
   while (!WIFSTOPPED(s))
      waitpid(pid, &s, WNOHANG);
}


/* detach process */

void ptrace_detach(int pid)
{
   if (ptrace(PTRACE_DETACH, pid, NULL, NULL) < 0) {
      // DBG((perror("ptrace_detach")));
      return;
   }
   DBG((printf("Detached from %d - %s\n", pid, getpidname(pid))));
}

/* read data from location addr */

void *read_data(int pid, unsigned long addr, void *vptr, int len)
{
   int i, count;
   long word;
   unsigned long *ptr = (unsigned long *) vptr;

   count = i = 0;

   while (count < len) {
      word = ptrace(PTRACE_PEEKTEXT, pid, addr + count, NULL);
      count += 4;
      ptr[i++] = word;
   }
}


/* write data to location addr */

void write_data(int pid, unsigned long addr, void *vptr, int len)
{
   int i, count;
   long word;

   i = count = 0;

   while (count < len) {
      memcpy(&word, vptr + count, sizeof(word));
      word = ptrace(PTRACE_POKETEXT, pid, addr + count, word);
      count += 4;
   }
}

void setaddr(unsigned char *buf, long addy)
{
   *(buf) = addy;
   *(buf + 1) = addy >> 8;
   *(buf + 2) = addy >> 16;
   *(buf + 3) = addy >> 24;
}

void redirect(int pid, long from, long to)
{
   setaddr(jmp_code + 1, to);
   write_data(pid, from, jmp_code, sizeof(jmp_code));
}

void free_link_map(struct link_map *map)
{
   if (map != NULL)
      free(map);
}

struct link_map *locate_linkmap(int pid)
{
   Elf32_Ehdr *ehdr = malloc(sizeof(Elf32_Ehdr));
   Elf32_Phdr *phdr = malloc(sizeof(Elf32_Phdr));
   Elf32_Dyn *dyn = malloc(sizeof(Elf32_Dyn));
   Elf32_Word got;
   struct link_map *l = malloc(sizeof(struct link_map));
   unsigned long phdr_addr, dyn_addr, map_addr;


   /* first we check from elf header , mapped at 0x08048000 , offset to
    * program header table from where we try to locate PT_DYNAMIC section.
    */

   read_data(pid, 0x08048000, ehdr, sizeof(Elf32_Ehdr));

   phdr_addr = 0x08048000 + ehdr->e_phoff;

   read_data(pid, phdr_addr, phdr, sizeof(Elf32_Phdr));

   while (phdr->p_type != PT_DYNAMIC) {
      read_data(pid, phdr_addr +=
		sizeof(Elf32_Phdr), phdr, sizeof(Elf32_Phdr));
   }


   /* now go through dynamic section until we find address of GOT */

   read_data(pid, phdr->p_vaddr, dyn, sizeof(Elf32_Dyn));

   dyn_addr = phdr->p_vaddr;

   while (dyn->d_tag != DT_PLTGOT) {
      read_data(pid, dyn_addr +=
		sizeof(Elf32_Dyn), dyn, sizeof(Elf32_Dyn));
   }

   got = (Elf32_Word) dyn->d_un.d_ptr;

   got += 4;			/* second GOT entry, remember? */

   /* now just read first link_map item and return it */

   read_data(pid, (unsigned long) got, &map_addr, 4);
   read_data(pid, map_addr, l, sizeof(struct link_map));


   return l;
}

void resolv_tables(int pid, struct link_map *map)
{
   Elf32_Dyn *dyn = malloc(sizeof(Elf32_Dyn));
   unsigned long addr;


   addr = (unsigned long) map->l_ld;
   read_data(pid, addr, dyn, sizeof(Elf32_Dyn));

   while (dyn->d_tag) {
      switch (dyn->d_tag) {
	 case DT_HASH:
	    read_data(pid, dyn->d_un.d_ptr + 4 + map->l_addr, &nchains,
		      sizeof(nchains));
	    break;

	 case DT_STRTAB:
	    strtab = dyn->d_un.d_ptr;
	    break;

	 case DT_SYMTAB:
	    symtab = dyn->d_un.d_ptr;
	    break;

	 default:
	    break;
      }

      addr += sizeof(Elf32_Dyn);
      read_data(pid, addr, dyn, sizeof(Elf32_Dyn));
   }

   free(dyn);
}

char *read_str(int pid, unsigned long addr)
{
   long ptr[128];
   static char string[128];
   int len;


   len = 128;
   read_data(pid, addr, ptr, len);

   bzero(string, sizeof(string));

   strncpy(string, (char *) ptr, strlen((char *) ptr));
   return string;
}


unsigned long
find_sym_in_tables(int pid, struct link_map *map, char *sym_name)
{
   Elf32_Sym *sym = malloc(sizeof(Elf32_Sym));
   char *str;
   int i;

   i = 0;

   while (i < nchains) {
      read_data(pid, symtab + (i * sizeof(Elf32_Sym)), sym,
		sizeof(Elf32_Sym));
      i++;

      if (ELF32_ST_TYPE(sym->st_info) != STT_FUNC)
	 continue;

      str = read_str(pid, strtab + sym->st_name);

      if (strncmp(str, sym_name, strlen(sym_name)) == 0)
	 return (map->l_addr + sym->st_value);

   }

   /* no symbol found, return 0 */
   return 0;

}

unsigned long find_sym_in_lib(int pid, char *sym_name, char *lib)
{
   struct link_map *map, *l_prev;
   unsigned long sym;
   char libname[256];

   sym = 0;
   map = locate_linkmap(pid);


   while (!sym && map->l_next) {
      read_data(pid, (unsigned long) map->l_next, map,
		sizeof(struct link_map));
      l_prev = map->l_next;

      read_data(pid, (unsigned long) map->l_name, libname,
		sizeof(libname));

      /* compare libname if its not NULL */
      if (lib)
	 if (strncmp(libname, lib, strlen(lib)) != 0)
	    continue;

      resolv_tables(pid, map);
      sym = find_sym_in_tables(pid, map, sym_name);
   }

   return sym;
}

void print_sym_tables(int pid, struct link_map *map)
{
   Elf32_Sym *sym = malloc(sizeof(Elf32_Sym));
   char *str;
   int i, x;
   i = x = 0;

   while (i < nchains) {
      read_data(pid, symtab + (i * sizeof(Elf32_Sym)), sym,
		sizeof(Elf32_Sym));
      i++;

      if (ELF32_ST_TYPE(sym->st_info) != STT_FUNC)
	 continue;

      str = read_str(pid, strtab + sym->st_name);
      for (x = 0; str[x]; x++)
	 if (!isprint(str[x]))
	    str[x] = '?';
      printf("%d/%s]\t%p\t%s\n", pid, getpidname(pid), map->l_addr + sym->st_value, str);
   }
}

void ptrace_print_regs(int pid)
{
   struct user_regs_struct regs;

   ptrace(PTRACE_GETREGS, pid, NULL, &regs);
   DBG((printf("Stopped %d at %p\n", pid, regs.eip)));
/*

 *   32 Bit| 16 Bit| 8 Bit (High)| 8 Bit (Low)
 *   -----------------------------------------
 *   EAX   | AX    | AH          | AL
 *   -----------------------------------------
 *   EBX   | BX    | BH          | BL
 *   -----------------------------------------
 *   ECX   | CX    | CH          | CL
 *   -----------------------------------------
 *   EDX   | DX    | DH          | DL
 *   -----------------------------------------
*/
//   printf("orig_eax=%p,", regs.orig_eax);
   printf("{eip=%p,", regs.eip);
   printf("eax=%p,", regs.eax);
   printf("ebx=%p,", regs.ebx);
   printf("ecx=%p,", regs.ecx);
   printf("edx=%p,", regs.edx);
   printf("esi=%p,", regs.esi);
   printf("edi=%p,", regs.edi);
   printf("ebp=%p,", regs.ebp);
   printf("esp=%p,", regs.esp);
   printf("eflags=%p,", regs.eflags);
   printf("cs=%p,", regs.cs);
   printf("ds=%p,", regs.ds);
   printf("es=%p,", regs.es);
   printf("fs=%p,", regs.fs);
   printf("gs=%p,", regs.gs);
   printf("ss=%p}\n", regs.ss);
//   printf("=%s\n", read_str(pid, regs.eip));
}

ptrace_cleanup()
{
//       ptrace_cont(task_list[i]);
//       kill(task_list[i], SIGCONT);

   int i;
   for (i = 0; i < 65536; i++)
      if (task_list[i] != 0) {
	 wait((int *) 0);
	 ptrace_detach(task_list[i]);
	 task_list[i] = 0;
      }
}
void signal_handler(int sig)
{
#define SIG_NAME(x) x == SIGURG ? "SIGURG" : \
                    x == SIGPIPE ? "SIGPIPE" : \
                    x == SIGQUIT ? "SIGQUIT" : \
                    x == SIGINT ? "SIGINT" : \
                    x == SIGTERM ? "SIGTERM" : \
                    x == SIGHUP ? "SIGHUP" : \
                    x == SIGSEGV ? "SIGSEGV" : \
                    x == SIGBUS ? "SIGBUS" : "UNKNOWN"

   printf("Got signal %s\n", SIG_NAME(sig));
   ptrace_cleanup();
   exit(0);
}

#include <assert.h>




void print_lib_shit(int pid, int argc, char **argv)
{
   struct link_map *map;
   unsigned long dlopen_loc, o_func;
   char *name;
   int i;

   /* locate _dl_open() */
   if (!(dlopen_loc = find_sym_in_lib(pid, "_dl_open", "/lib/libc."))) {
      DBG((fprintf(stderr, "Error! couldn't locate _dl_open()!\n")));
   }
   map = (struct link_map *) locate_linkmap(pid);
   resolv_tables(pid, map);
   DBG((printf("_dl_open at %p\n", dlopen_loc)));
   if (argc > 2) {
      for (i = 2; i < argc; i++) {
	 o_func = 0x0;
	 name = argv[i];
	 o_func = find_sym_in_tables(pid, map, name);
	 if (o_func)
	    printf("Found %s at %08x\n", name, o_func);
      }
   } else
      print_sym_tables(pid, map);

   free_link_map(map);

   ptrace_detach(pid);
   ptrace_cont(pid);
}

int main(int argc, char **argv)
{
   int i, ret;

   int pid, ppid, mypid;
   struct user_regs_struct regs;

   memset(&task_list, 0, sizeof(task_list));

   mypid = getpid();
   ppid = getppid();
   assert(PTRACE_GETREGS != PTRACE_GETFPREGS);


   signal(SIGHUP, SIG_IGN);
   signal(SIGURG, SIG_IGN);
   signal(SIGQUIT, &signal_handler);
   signal(SIGTERM, &signal_handler);
   signal(SIGINT, &signal_handler);

   if (argc > 1) {
      pid = atoi(argv[1]);

      if (ptrace_attach(pid) != (-1)) {
	 task_list[0] = pid;
	 print_lib_shit(pid, argc, argv);
      }
   } else {
      struct stat st;
      char buf[255];
      int uid;

      uid = getuid();
      for (pid = 0; pid != 65536; pid++) {
	 snprintf(buf, sizeof(buf), "/proc/%d", pid);
	 if ((stat(buf, &st)) != (-1)) {
	    if ((st.st_uid == uid) && (pid != ppid) && (pid != mypid)) {
	       if (ptrace_attach(pid) != (-1)) {
			task_list[pid] = pid;
			print_lib_shit(pid, argc, argv);
			wait((int *) 0);
			ptrace_detach(task_list[pid]);
			task_list[pid] = 0;
		} else {
			printf("Cant Attach to pid %d %s\n", pid, getpidname(pid));
		}
	    }
	 }
      }
   }
   exit(EXIT_SUCCESS);
}
