bigbro 0.3.11

Library for tracking file accesses of processes
Documentation
/* bigbro filetracking library
   Copyright (C) 2016 David Roundy

   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., 51 Franklin Street, Fifth Floor, Boston, MA
   02110-1301 USA */

#include <windows.h>
#include <stdio.h>
#include <stdlib.h>

#include "bigbro.h"
#include "hashset.h"

#include "win32/inject.h"
#include "win32/queue.h"
#include "win32/create_dlls.h"

// copy the string and return a pointer to the byte in dest *after*
// the null character we write.
static inline char *copy_string(char *dest, const char *input) {
  for (int i=0; input[i]; i++) {
    *dest++ = input[i];
  }
  *dest++ = 0;
  return dest;
}

int bigbro(const char *workingdir, pid_t *child_ptr,
           HANDLE stdoutfd, HANDLE stderrfd, char *envp[],
           const char *cmdline,
           char ***read_from_directories, char ***mkdir_directories,
           char ***read_from_files, char ***written_to_files) {
  // The following ensure that in case of error we return appropriate
  // pointers.
  *read_from_files = malloc(sizeof(char *));
  **read_from_files = 0;
  *read_from_directories = malloc(sizeof(char *));
  **read_from_directories = 0;
  *written_to_files = malloc(sizeof(char *));
  **written_to_files = 0;
  *mkdir_directories = malloc(sizeof(char *));
  **mkdir_directories = 0;
  create_dlls();
  /* struct queue q; */
  /* const char *shm_name = "stupid"; // fixme: need to generate unique name */
  /* if (queueInit(&q, shm_name)) { */
  /*   printf("Error allocating shared memory.\n"); */
  /*   return 1; */
  /* } */
  if (envp) {
    // create an environment with the desired environment variables,
    // plus one more to carry the name of our shared-memory segment.
    // This is trickier than on posix, since windows wants a
    // null-separated array of char, rather than a null-terminated
    // array of char *.  For uniformity, bigbro wants the latter.
    /* int size = 1; // 1 for the final null */
    /* for (char **e = envp; *e; e++) { */
    /*   size += strlen(*e) + 1; */
    /* } */
    /* size += strlen("bigbro_shm=") + strlen(shm_name) + 1; */
    /* char *new_windows_env = (char *)calloc(size, 1); */
    /* // here we join together all these strings into one big happy */
    /* // family. */
    /* char *location = new_windows_env; */
    /* for (char **e = envp; *e; e++) location = copy_string(location, *e); */
    /* location = copy_string(location, "bigbro_shm="); */
    /* location--; // overwrite the null */
    /* location = copy_string(location, shm_name); */
  } else {
    // FIXME BUG HERE! the following introduces a race condition: if
    // we call bigbro twice simultaneously, it is possible that one of
    // the two processes will end up sending its info to the wrong
    // shm.  However, right now I am just trying to get something
    // working, and this is easier.
    /* SetEnvironmentVariable("bigbro_shm", shm_name); */
  }

  STARTUPINFO si;
  PROCESS_INFORMATION pi;
  memset(&si, 0, sizeof(si));
  si.cb = sizeof(si);
  si.dwFlags |= STARTF_USESTDHANDLES;
  si.hStdError = stderrfd;
  if (!stderrfd) si.hStdError = GetStdHandle(STD_ERROR_HANDLE);
  si.hStdOutput = stdoutfd;
  if (!stdoutfd) si.hStdOutput = GetStdHandle(STD_OUTPUT_HANDLE);
  si.hStdInput = GetStdHandle(STD_INPUT_HANDLE);

  //if (!CreateProcess(0, (char *)cmdline, 0, 0, 0, CREATE_SUSPENDED, 0, 0, &si, &pi)) {
  if (!CreateProcess(NULL, (char *)cmdline, NULL, NULL,
                     TRUE, // we do want to inherit our handle
                     0, NULL, NULL, &si, &pi)) {
    return -1;
  }
  /* injectProcess(pi.hProcess); */
  //if (ResumeThread(pi.hThread) == -1) {
  //  printf("I got trouble ResumeThreading\n");
  //  return -1;
  //}
  if (WaitForSingleObject(pi.hThread, INFINITE) != WAIT_OBJECT_0) {
    printf("funny business in WaitForSingleObject...\n");
    return -1;
  }
  DWORD dword_return_code;
  if (!GetExitCodeProcess(pi.hProcess, &dword_return_code)) {
    printf("exit code was hard to get\n");
    return -1;
  }
  if (!CloseHandle(pi.hThread) || !CloseHandle(pi.hProcess)) {
    return 1;
  }
  /* hashset read, readdir, written; */
  /* init_hashset(&read, 1024); */
  /* init_hashset(&readdir, 1024); */
  /* init_hashset(&written, 1024); */
  /* for (uint32_t i=0; i<q.buf->written_to_here; i+= strlen(q.buf->data+i)+1) { */
  /*   const char *name = q.buf->data+i+1; */
  /*   switch (q.buf->data[i]) { */
  /*   case WRITE_OP: */
  /*     insert_hashset(&written, name); */
  /*     delete_from_hashset(&read, name); */
  /*     delete_from_hashset(&readdir, name); */
  /*     break; */
  /*   case READ_OP: */
  /*     if (!lookup_in_hash(&written, name)) { */
  /*       insert_hashset(&read, name); */
  /*     } */
  /*     break; */
  /*   case RENAME_OP: */
  /*     i += strlen(q.buf->data+i)+1; */
  /*     const char *from = name; */
  /*     const char *absto = q.buf->data+i; */
  /*     printf("handling rename %s -> %s\n", from, absto); */

  /*     if (lookup_in_hash(&written, from) || lookup_in_hash(&read, from)) { */
  /*       // Looks like it might be a file! */
  /*       delete_from_hashset(&written, from); */
  /*       delete_from_hashset(&read, from); */
  /*       insert_hashset(&written, absto); */
  /*     } else { */
  /*       // I think it might be a directory */
  /*       char *fromslash = malloc(strlen(from)+2); */
  /*       strcpy(fromslash, from); */
  /*       strcat(fromslash, "\\"); */
  /*       char *toslash = malloc(strlen(absto)+2); */
  /*       strcpy(toslash, absto); */
  /*       strcat(toslash, "\\"); */
  /*       int fromslashlen = strlen(fromslash); */
  /*       int toslashlen = strlen(toslash); */
  /*       { */
  /*         hashset new_written; */
  /*         init_hashset(&new_written, 2*read.num_entries); */
  /*         for (struct hash_entry *e = written.first; e; e = e->next) { */
  /*           if (strncmp(e->key, fromslash, fromslashlen) == 0) { */
  /*             char *newk = malloc(strlen(e->key) - fromslashlen + toslashlen + 1); */
  /*             strcpy(newk, toslash); */
  /*             strcat(newk, e->key + fromslashlen); */
  /*             printf("need to rename %s to %s\n", e->key, newk); */
  /*             insert_hashset(&new_written, newk); */
  /*             /\* insert_hashset(&written, newk); *\/ */
  /*             /\* delete_from_hashset(&written, e->key); *\/ */
  /*           } else { */
  /*             insert_hashset(&new_written, e->key); */
  /*           } */
  /*         } */
  /*         hashset new_read; */
  /*         init_hashset(&new_read, 2*read.num_entries); */
  /*         for (struct hash_entry *e = read.first; e; e = e->next) { */
  /*           if (strncmp(e->key, fromslash, fromslashlen) == 0) { */
  /*             char *newk = malloc(strlen(e->key) - fromslashlen + toslashlen + 1); */
  /*             strcpy(newk, toslash); */
  /*             strcat(newk, e->key + fromslashlen); */
  /*             printf("need to rename %s to %s\n", e->key, newk); */
  /*             insert_hashset(&new_written, newk); */
  /*           /\* insert_hashset(&written, newk); *\/ */
  /*           /\* delete_from_hashset(&read, e->key); *\/ */
  /*           } else { */
  /*             insert_hashset(&new_read, e->key); */
  /*           } */
  /*         } */
  /*         free_hashset(&written); */
  /*         free_hashset(&read); */
  /*         written = new_written; */
  /*         read = new_read; */
  /*       } */
  /*       /\* for (struct hash_entry *e = readdir.first; e; e = e->next) { *\/ */
  /*       /\*   if (strncmp(e->key, fromslash, fromslashlen) == 0) { *\/ */
  /*       /\*     delete_from_hashset(&readdir, e->key); *\/ */
  /*       /\*   } *\/ */
  /*       /\* } *\/ */
  /*     } */
  /*     break; */
  /*   case READDIR_OP: */
  /*     insert_hashset(&readdir, name); */
  /*     break; */
  /*   case GETINFO_OP: */
  /*     insert_hashset(&read, name); */
  /*     break; */
  /*   case SETINFO_OP: */
  /*     insert_hashset(&written, name); */
  /*     break; */
  /*   default: */
  /*     printf("BUG: I do not know why '%c' shows up!\n", q.buf->data[i]); */
  /*   } */
  /*   printf("%c -> %s\n",q.buf->data[i], &q.buf->data[i+1]); */
  /* } */
  /* free(*read_from_files); */
  /* free(*read_from_directories); */
  /* free(*written_to_files); */
  /* *read_from_files = hashset_to_array(&read); */
  /* *read_from_directories = hashset_to_array(&readdir); */
  /* *written_to_files = hashset_to_array(&written); */
  /* free_hashset(&read); */
  /* free_hashset(&readdir); */
  /* free_hashset(&written); */
  return dword_return_code;
}

int bigbro_blind(const char *workingdir, pid_t *child_ptr,
                 HANDLE stdoutfd, HANDLE stderrfd, char *envp[],
                 const char *cmdline) {

  STARTUPINFO si;
  PROCESS_INFORMATION pi;
  memset(&si, 0, sizeof(si));
  si.cb = sizeof(si);
  si.dwFlags |= STARTF_USESTDHANDLES;
  si.hStdError = stderrfd;
  if (!stderrfd) si.hStdError = GetStdHandle(STD_ERROR_HANDLE);
  si.hStdOutput = stdoutfd;
  if (!stdoutfd) si.hStdOutput = GetStdHandle(STD_OUTPUT_HANDLE);
  si.hStdInput = GetStdHandle(STD_INPUT_HANDLE);

  if (!CreateProcess(NULL, (char *)cmdline, NULL, NULL,
                     TRUE, // we do want to inherit our handle
                     0, NULL, NULL, &si, &pi)) {
    return -1;
  }
  if (WaitForSingleObject(pi.hThread, INFINITE) != WAIT_OBJECT_0) {
    printf("funny business in WaitForSingleObject...\n");
    return -1;
  }
  DWORD dword_return_code;
  if (!GetExitCodeProcess(pi.hProcess, &dword_return_code)) {
    printf("exit code was hard to get\n");
    return -1;
  }
  if (!CloseHandle(pi.hThread) || !CloseHandle(pi.hProcess)) {
    return 1;
  }
  return dword_return_code;
}