Jump to content
Sign in to follow this  
joakim

setMACE (timestomp) in au3

Recommended Posts

joakim

As part of another PoC tool, I've whipped together an au3 that uses NtSetInformationFile to update timestamps as available in the FILE_BASIC_INFORMATION structure. However, is it ok to post it?

The tool follows similar logic as Timestomp that was written in 2005 by James C. Foster and Vincent Liu, and presented at BlackHat the same year; http://www.forensicswiki.org/wiki/Timestomp

Joakim

Last version added 16.08.11; http://www.mediafire.com/?77f3bw52oxc9v5g (including au3 and exe)

Edited by joakim

Share this post


Link to post
Share on other sites
joakim

Here it is; http://www.mediafire.com/file/o9s82nzom2lj47k/setmace.zip exe and au3.

This tool follows similar logic as Timestomp that was written in 2005 by James C. Foster and Vincent Liu, and presented at BlackHat the same year (http://www.forensicswiki.org/wiki/Timestomp). It is a specialized tool for manipulating all 4 MAC(E) timestamps on files located on an NTFS volume (MAC values on FAT). It will update information in the $STANDARD_INFORMATION attribute of an MFT entry. Technically it is using the NtSetInformationFile function inside ntdll.dll with the FILE_BASIC_INFORMATION structure in FILE_INFORMATION_CLASS.

As is explained on the referenced site above, the $FILE_NAME attribute cannot be set by api, but a workaround is explained. That workaround is implemented in this tool by the -x parameter. Ie, it will update wanted timestamp for file and then move the target file into a randomly named subdirectory before redoing the update of timestamp on the target file now located in a new directory. This way all 8 timestamps may be modified.

Explanation of usage:

Commandline with parameter order fixed and must be followed.

- Parameter 1 is input/target file. Must be full path like "%CD%\file.ext" or "c:\folder\file.ext"

- Parameter 2 is determining which timestamp to update.

"-m" = LastWriteTime

"-a" = LastAccessTime

"-c" = CreationTime

"-e" = ChangeTime (in $MFT)

"-z" = all 4

- Parameter 3 is the wanted new timestamp. Format must be strictly followed like; "1954:20:49:22:39:44". That is YYYY:MM:DD:HH:MM:SS. Mseconds are always set to 0 to generate valid NTFS timestamps (1 second = 10.000). The smallest possible value is; "1601:01:01:00:00:00". However for that specific value mseconds is set to 1, so that the value is not ignored when writing it. It is written as UTC and thus will show up in explorer as interpreted by your timezone location.

- Parameter 4 determines if $FILE_NAME attribute should be updated as well.

"-n" will only update timestamps in $STANDARD_INFORMATION.

"-x" will also update timestamps in $FILE_NAME as well as in $STANDARD_INFORMATION. A subdir is created where the file is moved into.

Example;

setmace.exe "%CD%\file.txt" -z "2000:01:01:00:00:00" -x

Currently annoying message boxes is used to show error messages. That may change into logfile writing.

Thanks to wraithdu for good hints from the NtQueryInformationFile implementation in;

and Ascend4nt for the _WinTimeFunctions.au3

Source;

#Region ;**** Directives created by AutoIt3Wrapper_GUI ****
#AutoIt3Wrapper_Res_Comment=Same logic as timestomp written by  James C. Foster and Vincent Liu in 2005.
#AutoIt3Wrapper_Res_Description=Changes MACE timestamps as you like
#EndRegion ;**** Directives created by AutoIt3Wrapper_GUI ****
;-----------------------------
;by Joakim
;-----------------------------
#include "_WinTimeFunctions.au3";Ascend4nt
#include <WinAPI.au3>
#include <Date.au3>
#include <FileConstants.au3>
Opt("MustDeclareVars", 1)
Global $file = ""
Global $input2LocalFileTime = ""
Global Const $FileBasicInformation = 4
Global Const $OBJ_CASE_INSENSITIVE = 0x00000040
Global Const $FILE_DIRECTORY_FILE = 0x00000002
Global Const $FILE_NON_DIRECTORY_FILE = 0x00000040
Global Const $FILE_RANDOM_ACCESS = 0x00000800
Global Const $tagFILEBASICINFO = "int64 CreationTime;int64 LastAccessTime;int64 LastWriteTime;int64 ChangeTime;ulong FileAttributes;"
Global Const $tagIOSTATUSBLOCK = "dword Status;dword Information"
Global Const $tagOBJECTATTRIBUTES = "ulong Length;hwnd RootDirectory;ptr ObjectName;ulong Attributes;ptr SecurityDescriptor;ptr SecurityQualityOfService"
Global Const $tagUNICODESTRING = "ushort Length;ushort MaximumLength;ptr Buffer"

_validate_parameters()
_config_timestamp()
If $input2LocalFileTime = -1 Then
    MsgBox(0,"Error","Timestamp generation went wrong")
    Exit
EndIf
;divisible by 10,000,000 (one second)
_set_mace($file)
If $cmdline[4] = "-x" Then
    _fn_mace()
    Exit
EndIf
Exit

Func _set_mace($file)
    Local $hNTDLL = DllOpen("ntdll.dll")
    Local $szName = DllStructCreate("wchar[260]")
    Local $sUS = DllStructCreate($tagUNICODESTRING)
    Local $sOA = DllStructCreate($tagOBJECTATTRIBUTES)
    Local $sISB = DllStructCreate($tagIOSTATUSBLOCK)
    Local $buffer = DllStructCreate("byte[16384]")
    Local $ret
    $file = "\??\" & $file
    DllStructSetData($szName, 1, $file)
    $ret = DllCall($hNTDLL, "none", "RtlInitUnicodeString", "ptr", DllStructGetPtr($sUS), "ptr", DllStructGetPtr($szName))
    DllStructSetData($sOA, "Length", DllStructGetSize($sOA))
    DllStructSetData($sOA, "RootDirectory", Chr(0))
    DllStructSetData($sOA, "ObjectName", DllStructGetPtr($sUS))
    DllStructSetData($sOA, "Attributes", $OBJ_CASE_INSENSITIVE)
    DllStructSetData($sOA, "SecurityDescriptor", Chr(0))
    DllStructSetData($sOA, "SecurityQualityOfService", Chr(0))
    $ret = DllCall($hNTDLL, "int", "NtOpenFile", "hwnd*", "", "dword", $GENERIC_WRITE, "ptr", DllStructGetPtr($sOA), "ptr", DllStructGetPtr($sISB), _
                                "ulong", $FILE_SHARE_WRITE, "ulong", BitOR($FILE_NON_DIRECTORY_FILE, $FILE_RANDOM_ACCESS))
    Local $hFile = $ret[1]
    Local $pFSO = DllStructGetPtr($buffer)
    Local $sFSO = DllStructCreate($tagFILEBASICINFO, $pFSO)

    Select
        Case $cmdline[2] = "-m"
            DllStructSetData($sFSO, "LastWriteTime",$input2LocalFileTime)
        Case $cmdline[2] = "-a"
            DllStructSetData($sFSO, "LastAccessTime",$input2LocalFileTime)
        Case $cmdline[2] = "-c"
            DllStructSetData($sFSO, "CreationTime",$input2LocalFileTime)
        Case $cmdline[2] = "-e"
            DllStructSetData($sFSO, "ChangeTime",$input2LocalFileTime)
        Case $cmdline[2] = "-z"
            DllStructSetData($sFSO, "LastWriteTime",$input2LocalFileTime)
            DllStructSetData($sFSO, "LastAccessTime",$input2LocalFileTime)
            DllStructSetData($sFSO, "CreationTime",$input2LocalFileTime)
            DllStructSetData($sFSO, "ChangeTime",$input2LocalFileTime)
    EndSelect

    $ret = DllCall($hNTDLL, "int", "NtSetInformationFile", "hwnd", $hFile, "ptr", DllStructGetPtr($sISB), "ptr", DllStructGetPtr($sFSO), _
                                "int", 16384, "int", $FileBasicInformation)
    $ret = DllCall($hNTDLL, "int", "NtClose", "hwnd", $hFile)
    DllClose($hNTDLL)
EndFunc

Func _config_timestamp()
Local $timestamp_array
Local $msec = 0
If StringLen($cmdline[3]) <> 19 Then
    MsgBox(0,"Error","Length of date/time is not correct: " & $cmdline[3])
    Exit
EndIf
$timestamp_array = StringSplit(StringReplace($cmdline[3],'"',''),":")
If $timestamp_array[0] <> 6 Then
    MsgBox(0,"Error","Not right date/time parameters supplied: " & $timestamp_array[0])
    Exit
EndIf
For $dateinputs = 1 To $timestamp_array[0]
    If StringIsDigit($timestamp_array[$dateinputs]) <> 1 Then
        MsgBox(0,"Error","Not right date/time format supplied: " & $timestamp_array[$dateinputs])
        Exit
    EndIf
    If StringLen($timestamp_array[$dateinputs]) <> 2 And StringLen($timestamp_array[$dateinputs]) <> 4 Then
        MsgBox(0,"Error","Not right date/time format supplied: " & $timestamp_array[$dateinputs])
        Exit
    EndIf
    ContinueLoop
Next
If $cmdline[3] = "1601:01:01:00:00:00" Then
    $msec = 1
EndIf
$input2LocalFileTime = _WinTime_SystemTimeToLocalFileTime($timestamp_array[1],$timestamp_array[2],$timestamp_array[3],$timestamp_array[4],$timestamp_array[5],$timestamp_array[6],$msec,-1)
Return $input2LocalFileTime
EndFunc

Func _validate_parameters()
If $cmdline[0] <> 4 Then
    MsgBox(0,"Error","Wrong number of parameters supplied: " & $cmdline[0])
    Exit
EndIf
If $cmdline[2] <> "-m" And  $cmdline[2] <> "-a" And $cmdline[2] <> "-c" And $cmdline[2] <> "-e" And $cmdline[2] <> "-z" Then
    MsgBox(0,"Error","Input parameter number 2 is unknown: " & $cmdline[2])
    Exit
EndIf
If $cmdline[4] <> "-x" And $cmdline[4] <> "-n" Then
    MsgBox(0,"Error","Input parameter number 4 is unknown: " & $cmdline[4])
    Exit
EndIf
If FileExists($cmdline[1]) <> 1 Then
    MsgBox(0,"Error","Target file does not exist: " & $cmdline[1])
    Exit
EndIf
$file = $cmdline[1]
EndFunc

Func _fn_mace()
Local $l, $rnddir
Local $inptarget_lenght, $targetname, $output_folder, $newfile
$rnddir = Random(99, 99999, 1)
$l = 1
If StringInStr($cmdline[1],"\") Then
    $inptarget_lenght = StringLen($cmdline[1])
    For $l = 1 To $inptarget_lenght
        If StringMid($cmdline[1],$inptarget_lenght-$l,1) = "\" Then ExitLoop
    Next
    $targetname = StringMid($cmdline[1],$inptarget_lenght-$l+1,$l)
EndIf
$output_folder = StringMid($cmdline[1],1,$inptarget_lenght-$l) & $rnddir & "\"
FileMove($file,$output_folder,9)
$newfile = $output_folder & $targetname
_set_mace($newfile)
EndFunc

Func NT_SUCCESS($status)
    If 0 <= $status And $status <= 0x7FFFFFFF Then
        Return True
    Else
        Return False
    EndIf
EndFunc

Share this post


Link to post
Share on other sites
joakim

Added millisec as configurable in the timestamps; http://www.mediafire.com/?77f3bw52oxc9v5g

(au3 and exe included)

New sample use;

setmace.exe "%CD%\file.txt" -z "2000:01:01:11:22:33:666" -x

Share this post


Link to post
Share on other sites
KaFu
KaFu

Thanks a lot ;), I'll definitly will take a deeper look into this one. But on a second glance the site you're hosting on looks so interesting I think I'll sign-up anyhow :)...

Share this post


Link to post
Share on other sites
joakim

Made a new version with complete $FILE_NAME attribute timestamp support by writing the timestamps directly to physical disk. That means an extra 4 (and 8 for files with long file names because of the extra attribute) can now also be set to the most accurate precision possible (nanosec). That stupid "move" trick is now history and directories are supported too. Some restriction are present on nt6.x which means the systemdrive of a running system (and the volume with the pagefile) can not be written to without the help of a kernel mode driver. Also added support for dumping all $STANDARD_INFORMATION and $FILE_NAME for a file/directory (4+4 or 4+8).

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
Sign in to follow this  

×