Windows C / Indicator Removal: Timestomp

Author Unprotect
Platform Windows
Language C
Technique Indicator Removal: Timestomp

Description:

Original source and author: https://github.com/limbenjamin/nTimetools

Code

// #######################################################################
// ############ HEADER FILES
// #######################################################################
#include <windows.h>
#include <stdio.h>
#include <inttypes.h>
#include <math.h>

typedef LONG NTSTATUS;
char* VERSION_NO = "1.1";
HANDLE file = NULL;


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 *pNtSetInformationFile)(HANDLE, PIO_STATUS_BLOCK, PVOID, ULONG, FILE_INFORMATION_CLASS);
typedef NTSTATUS(WINAPI *pNtQueryInformationFile)(HANDLE, PIO_STATUS_BLOCK, PVOID, ULONG, FILE_INFORMATION_CLASS);

HANDLE LoadFile(char *filename, FILE_BASIC_INFORMATION *fbi);
VOID RetrieveFileBasicInformation(char *filename, FILE_BASIC_INFORMATION *fbi);
DWORD SetFileMACE(HANDLE file, DWORD fileAttributes, char *mtimestamp, char *atimestamp, char *ctimestamp, char *btimestamp);
LARGE_INTEGER ParseDateTimeInput(char *inputstring);
VOID About();
VOID Usage();

// #######################################################################
// ############ FUNCTIONS
// #######################################################################

VOID About() {
	printf("nTimestomp, Version %s\r\n", VERSION_NO);
	printf("Copyright (C) 2019 Benjamin Lim\r\n");
	printf("Available for free from https://limbenjamin.com/pages/ntimetools\r\n");
	printf("\r\n");
}

VOID Usage() {
	printf("\r\n");
	printf("Usage: .\\nTimestomp.exe [Modified Date] [Last Access Date] [Last Write Date] [Creation Date]\r\n");
	printf("Date Format: yyyy-mm-dd hh:mm:ss.ddddddd\r\n");
	printf("\r\n");
}

HANDLE LoadFile(char *filename, FILE_BASIC_INFORMATION *fbi) {

	HANDLE file = NULL;
	HMODULE ntdll = NULL;

	file = CreateFile(filename, GENERIC_READ | GENERIC_WRITE | FILE_WRITE_ATTRIBUTES, 0, NULL, OPEN_EXISTING, 0, NULL);
	if (file == INVALID_HANDLE_VALUE) {
		printf("Cannot open file: %S\r\n", filename);
		Usage();
		exit(1);
	}

	/* load ntdll and retrieve function pointer */
	ntdll = GetModuleHandle(TEXT("ntdll.dll"));
	if (ntdll == NULL) {
		printf("Cannot load ntdll\r\n");
		CloseHandle(file);
		exit(1);
	}
	FreeLibrary(ntdll);

	return file;
}

/* returns the handle on success or NULL on failure. this function opens a file and returns
the FILE_BASIC_INFORMATION on it. */
VOID RetrieveFileBasicInformation(HANDLE file, FILE_BASIC_INFORMATION *fbi) {

	HMODULE ntdll = NULL;
	pNtQueryInformationFile NtQueryInformationFile = NULL;
	IO_STATUS_BLOCK iostatus;

	/* load ntdll and retrieve function pointer */
	ntdll = GetModuleHandle(TEXT("ntdll.dll"));
	if (ntdll == NULL) {
		printf("Cannot load ntdll\r\n");
		CloseHandle(file);
		exit(1);
	}

	/* retrieve current timestamps including file attributes which we want to preserve */
	NtQueryInformationFile = (pNtQueryInformationFile)GetProcAddress(ntdll, "NtQueryInformationFile");
	if (NtQueryInformationFile == NULL) {
		CloseHandle(file);
		exit(1);
	}

	/* obtain the current file information including attributes */
	if (NtQueryInformationFile(file, &iostatus, fbi, sizeof(FILE_BASIC_INFORMATION), 4) < 0) {
		CloseHandle(file);
		exit(1);
	}

	/* clean up */
	FreeLibrary(ntdll);

}

DWORD SetFileMACE(HANDLE file, DWORD fileAttributes, char *mtimestamp, char *atimestamp, char *ctimestamp, char *btimestamp) {

	HMODULE ntdll = NULL;
	pNtSetInformationFile NtSetInformationFile = NULL;
	IO_STATUS_BLOCK iostatus;

	FILE_BASIC_INFORMATION fbi;
	fbi.LastWriteTime = ParseDateTimeInput(mtimestamp);
	fbi.LastAccessTime = ParseDateTimeInput(atimestamp);
	fbi.ChangeTime = ParseDateTimeInput(ctimestamp);
	fbi.CreationTime = ParseDateTimeInput(btimestamp);
	
	fbi.FileAttributes = fileAttributes;

	/* load ntdll and retrieve function pointer */
	ntdll = GetModuleHandle(TEXT("ntdll.dll"));
	if (ntdll == NULL) {
		printf("Cannot load ntdll\r\n");
		CloseHandle(file);
		exit(1);
	}

	NtSetInformationFile = (pNtSetInformationFile)GetProcAddress(ntdll, "NtSetInformationFile");
	if (NtSetInformationFile == NULL) {
		CloseHandle(file);
		exit(1);
	}

	if (NtSetInformationFile(file, &iostatus, &fbi, sizeof(FILE_BASIC_INFORMATION), FileBasicInformation) < 0) {
		CloseHandle(file);
		exit(1);
	}

	/* clean up */
	printf("File timestamp successfully set\r\n");
	FreeLibrary(ntdll);

	return 0;
}

LARGE_INTEGER ParseDateTimeInput(char *inputstring) {

	SYSTEMTIME systemtime = { 0 };
	LARGE_INTEGER nanoTime = { 0 };
	FILETIME filetime;
	LARGE_INTEGER dec = { 0 };
	LARGE_INTEGER res = { 0 };

	if (sscanf_s(inputstring, "%hu-%hu-%hu %hu:%hu:%hu.%7d", &systemtime.wYear, &systemtime.wMonth, &systemtime.wDay, &systemtime.wHour, &systemtime.wMinute, &systemtime.wSecond, &dec.QuadPart) == 0) {
		printf("Wrong Date Format");
		CloseHandle(file);
		exit(1);
	}

	/* sanitize input */

	if (systemtime.wMonth < 1 || systemtime.wMonth > 12) {
		printf("Wrong Date Format");
		CloseHandle(file);
		exit(1);
	}
	if (systemtime.wDay < 1 || systemtime.wDay > 31) {
		printf("Wrong Date Format");
		CloseHandle(file);
		exit(1);
	}
	if (systemtime.wYear < 1601 || systemtime.wYear > 30827) {
		printf("Wrong Date Format");
		CloseHandle(file);
		exit(1);
	}

	if (systemtime.wMinute < 0 || systemtime.wMinute > 59) {
		printf("Wrong Date Format");
		CloseHandle(file);
		exit(1);
	}
	if (systemtime.wSecond < 0 || systemtime.wSecond > 59) {
		printf("Wrong Date Format");
		CloseHandle(file);
		exit(1);
	}

	systemtime.wMilliseconds = 0;
	if (SystemTimeToFileTime(&systemtime, &filetime) == 0) {
		printf("Invalid filetime\r\n");
		CloseHandle(file);
		exit(1);
	}

	nanoTime.LowPart = filetime.dwLowDateTime;
	nanoTime.HighPart = filetime.dwHighDateTime;

	res.QuadPart = nanoTime.QuadPart + dec.QuadPart;

	return res;
}

/* 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 (FileTimeToSystemTime(&filetime, localsystemtime) == 0) {
		printf("Invalid filetime\r\n");
		exit(1);
	}

	return 1;
}

int main(int argc, char* argv[]) {

	if (argc < 5) {
		Usage();
		exit(1);
	}

	FILE_BASIC_INFORMATION fbi; 
	struct _SYSTEMTIME time = { 0 };
	wchar_t filename[4096] = { 0 };
	char str[256];
	CHAR lpVolumeNameBuffer[MAX_PATH + 1] = { 0 };
	CHAR lpFileSystemNameBuffer[MAX_PATH + 1] = { 0 };
	LARGE_INTEGER lpFileSizeHigh = { 0 };

	About();
	MultiByteToWideChar(CP_ACP, 0, argv[1], -1, filename, 4096);
	file = LoadFile(filename, &fbi);
	GetVolumeInformationByHandleW(file, &lpVolumeNameBuffer, ARRAYSIZE(lpVolumeNameBuffer), 0, 0, 0, &lpFileSystemNameBuffer, ARRAYSIZE(lpVolumeNameBuffer));
	GetFileSizeEx(file, &lpFileSizeHigh);

	printf("Filesystem type:		%S\r\n", lpFileSystemNameBuffer);
	printf("Filename:			%S\r\n", filename);
	printf("File size:			%d\r\n", lpFileSizeHigh.QuadPart);
	printf("\r\n");

	SetFileMACE(file, fbi.FileAttributes, argv[2], argv[3], argv[4], argv[5]);
	RetrieveFileBasicInformation(file, &fbi);

	printf("\r\n");
	ConvertLargeIntegerToLocalTime(&time, fbi.LastWriteTime);
	if (fbi.LastWriteTime.QuadPart != 0) {
		sprintf_s(str, 256, "%lld", fbi.LastWriteTime.QuadPart);
		printf("[M] Last Write Time:		%04d-%02d-%02d %02d:%02d:%02d.%7s UTC\r\n", time.wYear, time.wMonth, time.wDay, time.wHour, time.wMinute, time.wSecond, &str[11]);
	}
	else {
		printf("[M] Last Write Time:		%04d-%02d-%02d %02d:%02d:%02d.0000000 UTC\r\n", time.wYear, time.wMonth, time.wDay, time.wHour, time.wMinute, time.wSecond);
	}
	memset(&time, 0, sizeof(time));

	ConvertLargeIntegerToLocalTime(&time, fbi.LastAccessTime);
	if (fbi.LastAccessTime.QuadPart != 0) {
		sprintf_s(str, 256, "%lld", fbi.LastAccessTime.QuadPart);
		printf("[A] Last Access Time:		%04d-%02d-%02d %02d:%02d:%02d.%7s UTC\r\n", time.wYear, time.wMonth, time.wDay, time.wHour, time.wMinute, time.wSecond, &str[11]);
	}
	else {
		printf("[A] Last Access Time:		%04d-%02d-%02d %02d:%02d:%02d.0000000 UTC\r\n", time.wYear, time.wMonth, time.wDay, time.wHour, time.wMinute, time.wSecond);
	}
	memset(&time, 0, sizeof(time));

	ConvertLargeIntegerToLocalTime(&time, fbi.ChangeTime);
	if (fbi.ChangeTime.QuadPart != 0) {
		sprintf_s(str, 256, "%lld", fbi.ChangeTime.QuadPart);
		printf("[C] Metadata Change Time:	%04d-%02d-%02d %02d:%02d:%02d.%7s UTC\r\n", time.wYear, time.wMonth, time.wDay, time.wHour, time.wMinute, time.wSecond, &str[11]);
	}
	else {
		printf("[C] Metadata Change Time:	%04d-%02d-%02d %02d:%02d:%02d.0000000 UTC\r\n", time.wYear, time.wMonth, time.wDay, time.wHour, time.wMinute, time.wSecond);
	}
	memset(&time, 0, sizeof(time));

	ConvertLargeIntegerToLocalTime(&time, fbi.CreationTime);
	if (fbi.CreationTime.QuadPart != 0) {
		sprintf_s(str, 256, "%lld", fbi.CreationTime.QuadPart);
		printf("[B] Creation Time:		%04d-%02d-%02d %02d:%02d:%02d.%7s UTC\n", time.wYear, time.wMonth, time.wDay, time.wHour, time.wMinute, time.wSecond, &str[11]);
	}
	else {
		printf("[B] Creation Time:		%04d-%02d-%02d %02d:%02d:%02d.0000000 UTC\n", time.wYear, time.wMonth, time.wDay, time.wHour, time.wMinute, time.wSecond);
	}
	printf("\r\n");
	CloseHandle(file);
}

Created

June 19, 2022

Last Revised

April 22, 2024