Capturing Timestamp Precision for Digital Forensics

Document Sample
Capturing Timestamp Precision for Digital Forensics Powered By Docstoc
					Capturing Timestamp Precision for Digital

     James Madison University Infosec Techreport
          Department of Computer Science

                 Eugene Antsilevich

                    January 2009
    Capturing Timestamp Precision for Digital Forensics

                                        Eugene Antsilevich

                                  Term Paper for CS 680
                              Department of Computer Science
                                James Madison University


         In this paper we present a survey we conducted of popular operating systems and software
packages to determine what time precision and rounding behavior is used for timestamps stored on those
systems. The results of this survey are presented with emphasis on timestamp sources that can be utilized
by computer forensic investigators to order and correlate events.

         Furthermore, we introduce and describe a new Timestamp module for the Zeitline forensic
timeline editor [5]. This module allows a user to handle timestamps as “fuzzy” time ranges, where
precision and rounding method can be controlled for each timestamp source. This extension is based on a
computer clock model introduced by Buchholz [4].

         Modern operating systems are complex. They consist of various processes
working to their separate ends. Processes running over time affect their environment both
on purpose and as a by-product of their operation. This may happen as a result of human
actions, in which case human user actions may be matched to certain system events.
Many of these events are fixed or recorded in such a way, that their time may be known -
it is said that such event has a timestamp. Timing and order of events are of particular
interest in computer forensics, where logs and other data retrieved from the system at a
later date provide primary sources of information to reconstruct past actions of system
users. However, there is no one particular standard for recording event time, rather
various methods are provided by operating systems and used by processes. To reconcile
these times and reconstruct sequences of events, it is important first to understand time
sources and their capabilities. When so reconciled, event ordering may be better
understood and will provide higher quality forensic data.

        A number of papers discuss system clock operation and propose models to
describe behavior for such clocks. In “Formalising Event Time Bounding in Digital
Investigations” Gladyshev and Patel suggest a method for “event time bounding” based
on known timestamps of events logically preceding and following a given event [1].
        “A brief study of time” by Buchholz and Tjaden is “a first large scale study of
how hosts on the Internet manage their clocks” [2]. The authors discovered that the clock
behavior of Internet connected hosts is significantly non-uniform, both across hosts and
services provided by such hosts. The authors found that not all host clock results fit in
existing clock models and new models are needed to better understand their behavior.

        Schatz, Mohay and Clark present a survey of common assumptions for behavior
of computer clocks, as well as empirical results demonstrating how real clock behavior
diverges from such assumptions [3]. They suggest an approach to infer temporal behavior
of a given computer clock over a period of time by correlating timestamps from the
machine with external clock sources.

       There are a number of ways for a timestamp to be associated with an event:
   -   A process may query the time from the system, and record such time with event
   -   The system itself may record the time of certain events related to system objects.
       This is particularly common for files.
   -   A process may query a remote host or device for the time and record such time
       with event information.

        In “An Improved Clock Model for Translating Timestamps” [4] Buchholz
proposes a model to translate timestamps retrieved from a system to a common reference
time. He further describes a framework that would allow a forensic investigator to adjust
timestamps to a proper reference time as well as account for clock changes. A proof-of-
concept implementation of timestamp translation described by this paper is implemented
by the forensic timeline editor Zeitline [5]. “Zeitline is a Java/Swing tool that allows a
computer forensic investigator to import events from various sources of a computing
system or network and then order and classify them into one or more timelines of
events.” [5].

    The objective of this project was to investigate event timestamping in various systems
and software packages, and ways to correlate such timing information; to establish
correct ordering of timestamped events taking different clocks, granularities, and
rounding behaviors into account. The model of handling of timestamp values [4]
considers system timestamps of given precision to represent "fuzzy" time intervals during
which related events could have occurred. The aim is to reduce the duration of such
intervals to the minimum possible, to increase precision of event timeline.

    In a first part of this project we conducted a clock behavior survey concentrated on
four modern operating systems: Windows XP Professional (SP2), OS X Tiger (10.4),
FreeBSD 7 and Linux (Fedora, kernel 2.6.23). We looked at system clock access methods
and results, as well as time values available from popular server and client applications
for these systems.
    For the second part of this project we created a Timestamp module for the Zeitline
application, which implements some of the methods described by Buchholz in the model
of handling of timestamp values [4]. This paper includes a description of the above

Survey of Clock Behavior
        In the following we present a survey of the clock behavior of four modern
operating systems as well as a number of popular software packages. The objective of
this survey is to establish available precision of system clocks in these operating systems,
time value handling methods available to user applications and types and precision of
time values stored by such applications in log files and other types of application
produced output. This information may help build better timeline handling modules in
Zeitline editor, while providing reasonable bounding to periods representing each source
time values.

       A number of methods can be used to discover how certain operating systems and
software packages maintain or use time values. These methods depend on available
sources of information which differ by type of system or software. We used a variety of
methods in gathering information for this paper, including the following:


         Documentation and specification review was a necessary method of obtaining
initial information about system time keeping and reporting capabilities. It was also a
primary method for proprietary systems, in particular Microsoft Windows and Apple OS
X, where no source code is available for inspection or source availability is limited.
Directly applicable documentation may include clock API descriptions, methods to query
file system metadata, including timing information, and system event logging

Source code

         Open source systems provide an excellent platform to examine their internal
structure. Source code may be available for the operating system kernel, operating system
utilities or external services that are not system specific.
In particular, source code is available for two popular open source operating systems -
Linux and FreeBSD. While Linux is represented by multiple distributions, or collections
of specific services and utilities together with their configuration, all these distributions
share a common Linux kernel and a number of basic properties. In particular, they share
API libraries, especially as they pertain to time functions. Linux kernel source code is
available is available online in a browseable repository [6]. FreeBSD is a single-
distribution project with standardized source code available from single source [7].
        An inspection of the OS kernel for both FreeBSD and Linux provides
documentation of methods by which system clock is activated, system time counters are
maintained, as well as any sources limiting clock precision or granularity, and the ability
of system users to query time. In addition, file system source code examination provided
insight into precision of recorded file modification or access times.

       User level source code was inspected for patterns of time API use and ways of
handling and exposing to users values returned by such API. This is particularly
applicable to projects such as Apache [8], where time values may be modified during
handling for precision, format, etc., before being ultimately saved or delivered to users.

API testing

         This method in particular applied to system and file system timestamp precision.
The results of testing confirm and augment the results of source inspection and
documentation review. We started with a naive approach of executing an appropriate
time call in a loop, and recording the shortest time difference between two consecutive
calls. While this method may be appropriate in finding out the smallest difference in time
that a single program can register during a single thread runtime, it is limited by the
overhead of system call and loop execution time. If the time to execute the code is longer
than the clock resolution, it will govern what the results of the method would show.
Source code for the program demonstrating this approach is attached in Appendix B1.

        An alternative approach is based on examining the statistical distribution of
results returned by the clock API. The clock function is called repeatedly, and a specific
number of least significant bits of the returned value are examined. We count the number
of times each possible value is returned. For an ideal clock with a resolution of 1 unit of
time (i.e. possible return values using all least significant bits), we expect all value
counters to be equal over a significant period of time. Real clocks may differ from that
result by returning specific values more or less often than others due to lower granularity
of the internal clock or skew towards a certain range of values due to rounding or system
scheduler performance. Source code for the program demonstrating this approach is
attached in Appendix B2.

System configuration

       The testing was performed using available configurations of most common
operating systems on stock hardware. Where possible, testing of multiple operating
systems was performed on the same hardware. Otherwise, compatible hardware was used
to make testing results comparable. One value that may affect system clock performance
under different operating system executing on the same hardware is the Advanced
Programmable Interrupt Controller (APIC) timer [9]. An APIC timer is set up by the
operating system and, in turn, will control how often various timer-based routines,
including the system clock, are executed by the system.
       The following configurations were used:

         OS             Architecture/CPU        Clock Speed           Additional info
Linux Fedora 8         Intel Core2 Duo         CPU: 2.40GHz       RAM: 4 GByte
(kernel 2.6.23)        T7700 (amd64)                              APIC timer: 12.47 MHz
FreeBSD 7.0RC3         Intel Core2 Duo         CPU: 2.40GHz       RAM: 4 Gbyte
                       T7700 (amd64)                              APIC timer: 3.58 Mhz
Windows XP Pro         Intel Pentium 4         CPU: 1.7GHz        RAM: 2 GByte
OS X 10.4.11           Intel Core 2 Duo        CPU: 2.16GHz       RAM: 3 GByte
Darwin 8.11.1

Time API

        Primary time interfaces are time() and gettimeofday() functions, common
to all modern Unix and Posix compliant systems (POSIX.1-2001). gettimeofday()
returns time in a struct timeval that contains "number of conds and microseconds
since the Unix Epoch (defined as 00:00:00 UTC, January 1, 1970)". time() returns time
in seconds since Unix Epoch. The time returned by these functions may be modified by
updating the system clock.

        In addition, real time extension functions such as clock_gettime() are
provided in librt. These functions are defined in POSIX.1-2001. They present a choice of
clocks, including real time clock that returns the current system-wide clock value, as well
as a monotonic clock, which returns time since a specific starting point and cannot be
changed during system runtime. The function returns time in a struct timespec,
which contains the number of seconds and nanoseconds since Unix Epoch.

        The precision of the real time clock can be queried using clock_getres()
function, which returns clock precision in seconds and nanoseconds. Querying precision
on the sample hardware returned a 1 nsec resolution.

        To verify clock resolution, a statistic-based test running for a period of time was
used to determine the distribution of least significant digits in the clock’s nanosecond
return value. Results of the test are summarized in tables T1 and T2. The monotonic
clock shows greater uniformity in time value distribution, but both clocks appear capable
of returning data with high precision, subject to system performance limitations.










        0     500      1000      1500      2000      2500     3000      3500      4000         4500

    Table T1. 12 least significant bits of nanosecond time returned by Linux realtime clock.







        0     500      1000      1500      2000      2500     3000      3500      4000         4500

Table T2. 12 least significant bits of nanosecond time value returned by Linux monotonic clock.
File system timestamps.

         Linux supports a common Unix API to query file attributes: the function stat(),
based on BSD4.3 and POSIX.1-2001. This function returns times of last access, last
modification, and last status change for each file. The original BSD API supported only
timestamps containing a number of seconds since Unix Epoch. Current Linux versions
are extended to return seconds and nanoseconds since Unix Epoch. The ext3 file system,
which is currently a default file system for many Linux distributions, officially only
supports a 1 second timestamp resolution. However, the nanosecond resolution is
available by using undocumented large-size i-nodes. The Linux kernel supports
nanosecond resolution for file timestamps. This may cause issues with precise timestamp
determination - "while the inode is still held in Linux's inode cache, the time stamp will
retain its sub-second component, but once the inode was (sic!) dropped from the inode
cache and later re-read from disk, the sub-second part will be truncated to zero” [10].

         Our tests confirm that the number of nanoseconds returned by the Linux stat()
call is 0 for most files in the file system, though it may occasionally be set to a non-zero
value for recently modified files.

        The newer ext4 file system officially supports nanosecond-based timestamps [11].
At this time it is not in wide use and is not used by default in any of currently existing
major Linux distributions.

System timestamps and logging

        Linux distributions, similarly to other Unix-like systems, use system logging
daemon syslog. While the original syslogd is often supplanted by syslogd_ng (new
generation), both message input and output formats remain essentially the same. The log
file location and the types of messages logged are user configurable. The default log file
location is in the /var/log directory. Syslog writes text log messages, one line at a time.
Each message is prefixed with text timestamp containing month, date, and time with 1
second precision. The syslog timestamp is based on the system date returned by the
time() API call just after the log message is received by the syslog daemon from the
originating program on its listening socket.

        Kernel log messages are generated by a separate kernel log daemon, rklogd. This
daemon polls a kernel memory buffer for new message strings, and sends newly available
messages to syslog. The timestamps of kernel messages are created when syslogd
receives these messages from the kernel log daemon. Because of that it is possible for a
number of kernel log messages, particularly those generated during a system startup and
prior to the launch of rklogd and syslog, to be logged with the same timestamp
corresponding to the time of the logging daemon’s execution.
Time API

        The FreeBSD time API is substantially similar to that found in Linux systems.
This is due to their common Unix roots, as well as conforming to the same POSIX.1-
2001 specification. Standard time() and gettimeofday() functions are available,
returning time in seconds since Unix Epoch or seconds and microseconds since Unix
Epoch, respectively. Real time clocks are provided by the clock_gettime() function,
available in the default C API library. The clock precision can be queried using the
clock_getres() function. On the sample hardware, querying precision returned 280

         To verify clock resolution, we ran the same statistic-based test, used previously on
Linux. Clock values over a period of time were collected to determine the distribution of
least significant digits in the clock nanosecond return value. Results of the test are
summarized in Tables T3 and T4, respectively. Both real time and monotonic clocks
return values in a relatively narrow, well-defined distribution, providing what appears to
be a higher precision than that returned by clock_getres(). Low points in the
distribution correspond to specific values spaced approximately 1024 nanoseconds apart,
which occur approximately 10% less often than other values. This may be a result of the
system scheduler performing context switch operations every 1024 nanoseconds (or
about 1 microsecond), during which time the user space program does not execute.







            0     500      1000      1500     2000      2500      3000     3500      4000     4500

    Table T3. 12 least significant bits of nanosecond time value returned by FreeBSD realtime clock






              0   500      1000      1500     2000      2500     3000     3500      4000     4500

   Table T4. 12 least significant bits of nanosecond time value returned by FreeBSD monotonic clock

File system timestamps.

         As a BSD variant, FreeBSD supports the common Unix API to query file
attributes: the function stat(), based on BSD4.3 and POSIX.1-2001. The API function
works similarly to the one in Linux, returning times of last access, last modification, and
last status change for each file. The time is returned in seconds and nanoseconds since
Unix Epoch.

        The FreeBSD file system is UFS2, which supports file timestamps in seconds and
nanoseconds since Unix Epoch. FreeBSD can mount and use the Linux ext2 file system,
however it is not usually used as FreeBSD’s primary file store. The actual timestamp
values written to disk are controlled by a system wide setting. Using sysctl’s
vfs.timestamp_precision parameter, a system administrator can choose what
precision timestamps should be used. The following settings are available:

   -     0 - seconds only; nanoseconds zeroed.
   -     1 - seconds and nanoseconds, accurate within 1/HZ.
   -     2 - seconds and nanoseconds, truncated to microseconds.
   -     3 - seconds and nanoseconds, maximum precision.
        The default system timestamp precision is set to 1, essentially defaulting to legacy
1 second precision. Web and discussion board searches return very few matches for this
sysctl setting, suggesting that this system setting is not particularly widely known. It
appears unlikely to be modified from default on most systems.

  * Get a current timestamp.
vfs_timestamp(struct timespec *tsp)
     struct timeval tv;

    switch (timestamp_precision) {
    case TSP_SEC:
       tsp->tv_sec = time_second;
       tsp->tv_nsec = 0;
    case TSP_HZ:
    case TSP_USEC:
       TIMEVAL_TO_TIMESPEC(&tv, tsp);
    case TSP_NSEC:

          Fig.1. FreeBSD VFS subsystem timestamp query function source [7]

System timestamps and logging

        FreeBSD, like virtually all modern Unix derivatives, uses the system logging
daemon syslog. The behavior of the syslog daemon is substantially similar to that of
Linux system log daemon, with timestamps in local time with 1 second resolution.
Timestamps are based on the time of reception of the syslog message from the network or
a local socket.
Mac OS X
Time API

        Mac OS X is an operating system that merges features of Unix-style systems with
proprietary user interfaces and libraries. OS X’s open system API is based on an older
version of BSD system API (branched from FreeBSD 3.x). As such, OS X provides the
original BSD style gettimeofday() interface that returns the current date in seconds
and microseconds since Unix Epoch.

        An additional API [12] is available to commercially developed software using
non-free libraries. The function mach_absolute_time() returns the time in processor-
specific units. These units can be converted to seconds and nanoseconds using
appropriate API calls such as AbsoluteToNanoseconds(). It is impossible to tell
whether this API is commonly used, since software that uses it generally does not have its
source code available for inspection.

        To verify the available clock resolution our statistic-based distribution method
was used. Clock values over a period of time were collected to determine the distribution
of least significant digits in clock microsecond value, as returned by gettimeofday().
Results of the test are summarized in Table T5. As expected, values are relatively
uniform with a slight bias towards lower microsecond values.







            0     500      1000      1500     2000      2500      3000     3500      4000      4500

     Table T5. 12 least significant bits of microsecond time value returned by OS X gettimeofday()
File system timestamps.

         Mac OS X uses the HFS+ file system, which is a proprietary Apple Inc product.
All file system dates are stored “in unsigned 32-bit integers (UInt32) containing the
number of seconds since midnight, January 1, 1904, GMT” [13].

       OS X uses the standard Unix-style stat() function to query file properties.
Unlike its more modern POSIX-compliant equivalents, such as those found in Linux or
FreeBSD, the OS X version returns times of last access, last modification, and last status
change for each file in seconds since Unix Epoch. No support for higher resolution is
available at this time.

System timestamps and logging

       The OS X logging system is a hybrid, combining features of the Unix-style
syslog-based approach with a direct file logging approach for some of its services.

      The OS X syslog daemon writes its log files in the /var/log directory, although
names of these files do not follow the usual Unix convention. Timestamps for log
messages are recorded in local time with 1 second precision.

        In addition to this, a few log files are available in the directory /Library/Log.
These appear to be directly maintained by the appropriate programs or services. In
particular the following log files are available:
    - console.log – this file contains graphic console service messages (similar to X
        Windows server log on Unix systems). The file contains primarily errors and
        specific debug reporting messages from the graphic subsystem. Not all messages
        contain timestamps. Those that do have timestamps in local time, with
        millisecond precision (3 decimal digits past whole seconds).
    - Directory Service log – this file contains general purpose notifications from the
        directory service, such as “request occurred”, without specifying the nature of the
        request. It also contains notifications of the computer going to sleep or waking up.
        Timestamps in this file are in local time with 1 second precision.
    - Software Update log – contains information about software updates performed on
        the computer. Timestamps in this file are in local time with 1 second precision.
Mircosoft Windows XP
Time API

        Microsoft Windows XP is a popular proprietary desktop and server operating
system. It has little in common with Unix-based systems. The native Windows time API
is available on multiple Windows platforms, including Windows XP, Windows Vista,
Windows 2000 and Windows 2003. It includes the following functions:
    - GetTickCount() – returns the number of milliseconds that elapsed since the
        system was started. This function returns a 32-bit value, which limits the value to
        a total of 49.7 days. Its 64-bit counterpart GetTickCount64() has a significantly
        higher limit, representing almost 300 million years since startup time.
    - GetSystemTime() – returns the system time as a SYSTEMTIME structure. The
        structure contains separate fields for various time components and provides
        precision up to 1 millisecond. The function returns time in UTC. A separate
        function GetLocalTime() is available to retrieve the time in the local system’s
    - GetSystemTimeAsFileTime() – this function returns the system time as a
        FILETIME structure (described below). The structure provides time with 100
        nanosecond precision. [14]










              0           50           100           150           200           250             300

        Table T6. 8 least significant bits of 100-nanosecond time value returned by Windows XP
        To verify the clock resolution, our statistic distribution method was used. Clock
values over a period of time were collected to determine the distribution of least
significant digits in 100 nanosecond resolution clock values returned by
GetSystemTimeAsFileTime(). Results of the test are summarized in Table T6. It is
clear that only values divisible by 16 are ever returned by the clock, which corresponds to
an actual resolution of 1600 nanoseconds (or 1.6 microseconds).

File system timestamps.

       All Windows variants use NTFS as their default file system. In addition, the
legacy FAT file system may be used. FAT is particularly common on removable drives
and media.

        According to MSDN "Not all file systems can record creation and last access time
and not all file systems record them in the same manner. For example, on NT FAT, create
time has a resolution of 10 milliseconds, write time has a resolution of 2 seconds, and
access time has a resolution of 1 day (really, the access date). On NTFS, access time has
a resolution of 1 hour” [15].

       “The system records file times when applications create, access, and write to files.
The NTFS file system stores time values in UTC format, so they are not affected by
changes in time zone or daylight saving time. The FAT file system stores time values
based on the local time of the computer” [15].

        The file time API is based on a function GetFileTime(), which returns the
creation time, last access time, and last write time for a given file. The times are returned
in a FILETIME structure. This structure contains a 64-bit value that represents the
number of 100 nanosecond intervals that have elapsed since 12:00 A.M. January 1, 1601
Coordinated Universal Time (UTC).

        Testing was performed using a program designed to write data into files and
subsequently read their last write timestamps, to determine the write timestamp precision.
The program source code is listed in Appendix C3. The behavior of the program
depended on how the file was opened for writing. If a file was newly created by the
program, each subsequent write resulted in its last write timestamp being updated. If an
existing file was used for writing, the last write timestamp reflected the time of the very
first write into the file. Subsequent writes did not result in a last write timestamp update.
The last write timestamp was updated again at the time the file handle was closed by the
program. MSDN suggests that file timestamps are only guaranteed to be correct when the
file handle is closed.
System timestamps and logging

         Windows XP provides extensive logging capabilities through its Event Log
service. Windows maintains three logs – system, application, and security. The system
log is intended for use by the Windows operating system components. The application
log is available to all applications. The security log is limited to the Local Security
Authority Subsystem (LSASS). Log files contain events in a format described by in the
EVENTLOGRECORD structure [16]. Each log record contains two timestamps:
TimeGenerated is the time when the event was generated by the calling program or
service, TimeWritten is the time when the event record was written to the appropriate
file. Each timestamp reflects the number of seconds since Unix Epoch, UTC.

        The default log settings differ depending on the particular Windows version.
Desktop operating systems, such as Windows XP Home, generally have security logging
turned off by default. Server operating systems such as Windows XP Pro or Windows
2003 have some security events turned on. Specific events to be logged can be controlled
by editing the local computer policy (using the policy editor). Settings include logging
events related to user authentication, certain system object accesses, rights acquisitions
etc, both for success and failure of each operation. Turning on all possible security
settings may create a significant number of messages tracking user activity.

Application Timestamps
       In surveying various popular applications, it became apparent that those
applications that have cross-platform capabilities, generally exhibit the same behavior in
terms of timestamps and logging. This may, perhaps, be explained by the desire to
maintain a compatible output of file formats between platforms. Accordingly,
applications are placed in a separate section, with any platform differences noted on an
individual basis.

        Inspection of source code of a number of popular open source packages had
shown that time() and gettimeofday() are commonly used to establish the current
time both on Unix-compatible systems as well as on Windows. When gettimeofday()
is used, the microseconds field is often being ignored, effectively truncating the time
value to whole seconds.

X Windows

        X Windows is a common Unix graphic environment, used on both Linux and
FreeBSD. The default configuration of X Windows produces a log output file, usually
located in the /var/log directory. This file contains primarily error log information, as
well as notification of modules loaded and unloaded. There are no timestamps in this file.

        VNC is a popular package, providing remote users with remote access to the
graphic environment running on the system. It has servers available for Linux, FreeBSD
and Windows. The VNC server maintains a log which on Unix style systems can be
found in the user home directory in ~/.vnc/hostname:screen.log. This file contains
information about server startup time as well as successful and failed remote connections.
The information is presented in blocks, with each block preceded by timestamp in the
local computer time, with 1 second precision.
On Windows, the VNC server logs all connection attempts to the Windows Application
event log.


        Apache is a popular open source web server, available for most Unix platforms as
well as for Windows. The most recent Apache version is 2.2, with older versions still
available. It has highly configurable log file formats and locations. However, by
convention the log file format is kept in one of two common forms in all Apache
installations, providing interoperability with various web log analyzers [17].

         Apache produces two log files – access log and error log, which are usually found
in /var/log/httpd on Unix systems. On OS X Apache, as provided by Mac Ports, writes its
log files into /opt/local/apache/log. On Windows there is no default log file location, and
the Apache configuration must be consulted to locate them. The access log file contains
information about all access attempts to resources on the server. The error log contains
information about errors during access to resources. Each access attempt or error is
represented by a single line of the log, which contains a timestamp in local time with 1
second resolution.

        Internally in the server all times are stored as integer variables containing time
value in microseconds since Unix Epoch. The value is derived using the
gettimeofday() interface on all Unix-style systems, and
GetSystemTimeAsFileTime() on Windows. This high precision time is converted
into the 1 second resolution log file time by effectively truncating current time in
microseconds to the current whole seconds. A microsecond value cannot be used in log
file time format.

       The Firefox web browser is a popular cross-platform application, currently second
in popularity to Microsoft Internet Explorer. It is based on the Mozilla engine that is also
used for other applications. Versions 1.5, 2.x and 3.0 were examined. While some of the
information is common to all versions of Firefox, in version 3.0 a number of data storage
formats were changed. This will be noted below.

        Web browsers have a number of sources of potential timestamps, including web
cache, cookie files, web browsing and download history, etc.

       The Firefox web cache on Unix-like systems is located in user home directory
under ~/.mozilla/firefox/<profilename>/Cache. On Windows it is found under
Local Settings\Application Data\Mozilla\Firefox\Profiles\<profilename>\Cache. Under
OSX it is in ~/Library/Caches/Firefox/Profiles/<profilename>. Other data is stored in
~/Library/Application Support/Firefox/Profiles/<profilename>

        The profile directory contains copies of files downloaded by the browser from
remote web sites, as well as a cache file directory stored in the file _CACHE_MAP_,
potentially split into smaller parts. Cached web files are unmodified copies of documents
downloaded from remote servers and, generally, contain no timestamp information aside
from the regular file timestamps. The cache map files contain HTTP headers returned by
remote servers for each file, which should include the remote server time header. The
HTTP 1.1 protocol requires time and date to be present in UTC, and most web servers
follow this standard. However, small embedded servers or custom applications using
HTTP may still use HTTP 1.0 or fail to follow protocol requirements fully. In such cases
remote time may not be available. Web cache contains Last Modified time and Expire
Time for files, as sent by remote server. These times are based on the value of remote
server clock with 1 second resolution [18][19].

Firefox 1 and 2

       In Firefox Versions 1 and 2 cookies are stored in a single file “cookies.txt”,
located in the respective user profile directories for the system. This file stores cookies
one per line, with cookie expiration time in seconds since Unix Epoch for each cookie.
There is no cookie generation or write time.

        The browser history is stored in a file history.dat located in the user profile
directory. This file is in Mork file format for browsers version 1.x and 2.x, although the
newer Firefox 3.x is switching to a different format. Each history file entry contains a
URL and a time of last access in seconds since Unix Epoch [20].

        The download history is stored in a file downloads.rdf in the user profile
directory. This file is in an XML based format. Each downloaded file entry contains start
and end timestamps of each download in local computer time, with 1 second resolution.
       Firefox bookmarks are stored in a file bookmarks.html in HTML-compatible
format that can be opened using any web browser. Each bookmark is represented by an
HTML URL tag. The tag includes attributes for timestamps of when the bookmarks were
added, modified, and visited in seconds since Unix Epoch.

Firefox 3

        Firefox Version 3 takes a completely different approach to data storage from that
of Versions 1 and 2. It uses the open source SQLite embedded database engine to store
and access data [21]. All data is stored on disk in SQLite format files. These files can be
readily examined using the sqlite3 command line utility.

      Cookies are stored in SQLite database cookies.sqlite in a table
moz_cookies, and contain two integer timestamps – an expiration time in seconds since
Unix Epoch and a last accessed time in microseconds since Unix Epoch.

        The browser history is found in an SQlite database file places.sqlite.
History is stored in two separate tables: the table moz_places contains URLs each
assigned an integer index, while the table moz_historyvisits contains the time of last visit
for each URL as an integer number of microseconds since Unix Epoch, and an index
number of URL as defined in table moz_places.

        Firefox 3 bookmarks are stored in the database places.sqlite, along with
browser history. Bookmarks are stored in a table moz_bookmarks. Each bookmark record
contains integer timestamps of addition and last modification in microseconds since Unix
Epoch. Bookmarks appear to be joined to table moz_places on the URL integer index,
and through this join last visit time can be determined in a fashion similar to that of
entries in table moz_historyvisits..

       The download history is found in database file downloads.sqlite in a table
moz_downloads. Each table row contains a record of a download data including integer
timestamps for start and end times of the download in microseconds since Unix Epoch.

Internet Explorer

       Internet Explorer is the default browser in Microsoft Windows and the most
popular browser in the world. Windows XP was provided with version 6 of the Internet
Explorer which was used in this survey.

        The Internet Explorer browser cache is located in the user profile directory, under
Local Settings/Temporary Internet Files. Each object file stored in the cache has its URL
of origin, as well as "last modified", "last accessed", "last checked" dates, available for
inspection through a Windows Explorer plugin. The last modified time corresponds to the
value returned by the remote web server, while last accessed and last checked timestamps
are in local time, all with resolution of 1 second.
              In addition to this, an undocumented private cache is available under the
       subdirectory Content.IE5. Files in this subdirectory are not accompanied by a cache index
       and have only standard file system timestamps.

              Internet Explorer stores cookies in the user profile directory, under the
       subdirectory Cookies. Each cookie is stored in a separate file which has standard file
       system timestamps. These files are in a proprietary format, and contain an expiration
       timestamp in Windows FILETIME format with 100 nanosecond resolution [22].

               The Internet Explorer browser history is found in the user profile under Local
       Settings/History/History.IE5/MSHist<date range>/index.dat. The history data is stored in
       a proprietary data format. The date range in the name of history directory denotes the
       range during which specific history data was collected (i.e. 012008052620080602 for the
       range between 26 May 2008 and 2 June 2008, local time).

       Timestamp Source Summary
    Time Source         OS           Format          Prec.     How determined                 Notes
Linux file system    Linux        Seconds since     1 sec    Documentation on       Kernel supports higher
timestamps (ext2)                 Unix Epoch                 ext2fs, testing user   resolution but ext2fs
                                                             level source code      does not at this time
FreeBSD file         FreeBSD      Seconds and       1 ns     Documentation,         Maximum resolution
system timestamps                 nanoseconds                kernel source code,    not enabled by default,
(ufs2)                            since Unix                 testing user level     default is 1 sec
                                  Epoch                      code and utilities     resolution.
OS X file system     OS X         Seconds since     1 sec    Documentation,
timestamps (HFS+)                 specific date              user level code and
                                  (01/01/1904                utilities
Windows file         Win.XP       100 ns            100ns    Documentation,         Access time has
system timestamps                 intervals since            user level code        resolution of 1 hour,
(NTFS)                            1/1/1601                   testing                write and create time
                                                                                    have 100 ns resolution.
Syslog timestamps    Linux        Text date and     1 sec    Documentation,         Timestamp based on
                     FreeBSD      time, local                source code            syslog network
                     OS X         system time                inspection, utility    message arrival time
Event Log            Win.XP       Number of         1 sec    Documentation,         Each log entry contains
                                  seconds since              utility testing        two timestamps – time
                                  Unix Epoch                                        generated from caller
                                                                                    and time written, from
                                                                                    event log service
OS X GUI console     OS X         Text date and     1 ms     Utility testing, log   Not all messages
log                               time, local                review                 contain timestamps
                                  system time
OS X Directory         OS X      Text date and   1 sec    Utility testing, log
Service log, OS X                time, local              review
Software Update                  system time
VNC server             Linux     Text date and   1 sec    Server testing, log    On Windows VNC
                       FreeBSD   time, local              review                 uses Event Log service
                       OS X      system time                                     for messages
Apache server (2.2,    Linux     Text date and   1 sec    Source code            Higher precision time
1.x) error and         FreeBSD   time, local              inspection, server     available internally but
access logs            OS X      system time              testing, log review    there is no output
                       Win.XP                                                    format string to use it
                                                                                 in timestamps.
Firefox 1,2, 3 web     Linux     Text date and   1 sec    Documentation, file    Times from remote
cache                  FreeBSD   time, remote             inspection             server, not timestamps
                       OS X      server local                                    of download time
                       Win.XP    time
Firefox 1, 2 cookies   Linux     Seconds since   1 sec    Documentation, file    Cookie expiry time
                       FreeBSD   Unix Epoch               inspection             from remote server or
                       OS X                                                      based on configuration,
                       Win.XP                                                    not timestamps of
                                                                                 download time
Firefox 1, 2 browser Linux       Seconds since   1 sec    Documentation,         Time of last access for
history              FreeBSD     Unix Epoch               utility testing        URL
                     OS X
Firefox 1, 2         Linux       Text date and   1 sec    File inspection        Timestamps for start
download history     FreeBSD     time, local                                     and end of download
                     OS X        time
Firefox 1,2          Linux       Seconds since   1 sec    File inspection        Timestamp for
bookmarks            FreeBSD     Unix Epoch                                      addition, modification
                     OS X                                                        and last visit
Firefox 3 cookies    Linux       Seconds or      1 sec    Sqlite database        Timestamp for
                     FreeBSD     Microseconds    or       inspection             expiration in seconds,
                     OS X        since Unix      1 usec                          timestamp for last
                     Win.XP      Epoch                                           access in microseconds.
Firefox 3 browser    Linux       Microseconds    1 usec   Sqlite database        Time of last access for
history              FreeBSD     since Unix               inspection             URL
                     OS X        Epoch
Firefox 3 download Linux         Microseconds    1 usec   Sqlite database        Timestamps for start
history              FreeBSD     since Unix               inspection             and end of download
                     OS X        Epoch
Firefox 3              Linux        Microseconds     1 usec    Sqlite database         Timestamp for addition
bookmarks              FreeBSD      since Unix                 inspection              and modification
                       OS X         Epoch
Internet Explorer      Win.XP       Unknown          1 sec     Utility testing         Last modification, last
web cache                                                                              access and last checked
                                                                                       times – available
                                                                                       through Windows
Internet Explorer      Win.XP       100 ns since     100ns     Documentation,          Expiry timestamp
cookies                             1/1/1601                   utility testing         stored in file. Each
                                                                                       cookie is stored in a
                                                                                       separate file, with
                                                                                       system timestamps
Internet Explorer      Win.XP       Text, date       1 day     Utility testing         Only date ranges for all
browser history                                                                        history links in the
                                                                                       directory are available

        Timestamp Module for Zeitline
                Zeitline is a tool, written in Java, that permits a forensic investigator to import
        event timelines from various sources and order and classify them according to a number
        of criteria.

                Timelining is a “technique for event reconstruction [in which] discrete events that
        have a timestamp associated with them are ordered into a timeline … allowing an
        investigator to reconstruct the sequence of events that took place” [2]. The process of
        timeline reconstruction is not trivial, however, since timestamp sources may have
        different precision, report time information for a given event in a different manner (i.e. as
        soon as event occurred or after a delay), and the time values may be otherwise modified
        (rounded, truncated, etc.) before being stored in a log file or other event timeline source.

                Standard Java timestamp class java.sql.Timestamp permits storage of a single
        time value that consists of Java Date object and a fractional nanosecond part [23]. In turn
        java.util.Date contains time with millisecond precision since Unix Epoch. This provides a
        traditional way to store a single time value of undefined precision. To provide more
        flexibility in handling and comparing timestamp time values, in particular between
        different sources that may have differing precision or time generation approach, a new
        Timestamp class was created. The class implements an interface compatible to that of
        standard Java java.sql.Timestamp class, but provides additional methods and differs in
        internal behavior.
        Each timestamp object may represent a range of actual times, based on rounding
behavior and precision specified during object construction. While real world time is
continuous and has infinite precision, time values stored by computers are limited by
available computer resources. In practice precision is often limited to a range of particular
numeric data type used to store time values. In our Timestamp objects number of seconds
is stored as a 64 bit integer, since Unix Epoch, providing a range of over 580 billion
years. Partial seconds are stored up to 1E-9 precision (1 nanosecond). While 32 bit
integer would be sufficient to store nanosecond portion, we use a 64 bit integer as well,
which reduces conversions during time computation (at the expense of slightly increased
memory use).

       Available rounding behaviors include:
   -   Round - the clock used to generate timestamps, rounds time values to the nearest
       value specified by its precision. I.e. if the precision is 10 seconds, a value of 22
       seconds would be rounded to 20, and a value of 29 would be rounded to 30

   -   Truncate – the clock used to generate timestamps truncates the time value to its
       given precision. I.e. if the precision is 10 seconds, then values of 22 and 29
       seconds would both be truncated to 20 seconds.

   -   Unknown – if the clock rounding behavior is unknown, we have to assume the
       widest possible range. I.e. the lower range bound is defined by the rounding
       method, and the upper by truncation.

   -   None – clock values are specific points in time. This method permits
       compatibility with the Java standard Timestamp class.

   Timestamp precision may be specified as either a whole number of seconds or a
complement to a number of decimal places after seconds (up to nanosecond precision,
where 0 means full nanosecond precision, 6 - microseconds, 2 - 10 milliseconds etc).
This method allows easier computation of rounded time values, since the number of
decimal places can be directly used in their computation.

   As an example, a timestamp object created with a time value of 1219850906.13
seconds since Unix Epoch, using truncating behavior and 10 millisecond precision,
would actually represent a range of time beginning at 1219850906.13 and ending at
1219850906.139999999. That is to say – the system will represent any of these possible
points on the real timeline as a single numeric value of the timestamp above.

    Comparisons between two timestamps are performed based on the range value with
full ordering. The order is defined such that if timestamps T1 and T2 have strictly non-
overlapping ranges and range of timestamp T1 ends before range of timestamp T2 begins,
we say that T1 precedes T2. If ranges overlap, the timestamps are assumed to belong to
concurrent events – while their ordering cannot be established, a method is provided to
obtain the amount of possible overlap.
   The Timestamp class was integrated into the Zeitline application and allows users to
specify precision and rounding behavior when selecting a timestamp event source.

The following is a summary of public properties and methods present in class interface of
public enum Rounding { NONE,                 Enumeration defining valid rounding
TRUNCATE, ROUND, UNKNOWN }                   behaviors
public enum Precision {                      Enumeration defining common values of
NANOSECOND(0, 0),                            precision. Other values may be specified
MICROSECOND(0, 3),                           numerically.
SECOND(1, 0),
MINUTE(60, 0),
HOUR(3600, 0),
DAY(86400,0) }
public Timestamp()                           Constructor, no arguments. Creates
                                             Timestamp compatible with default system
                                             timestamp (with Rounding set to NONE
                                             and Precision set to full precision)
public Timestamp(long sec, long              Construct timestamp given number of
nsec)                                        seconds and nanoseconds

public Timestamp(long msec)                  Construct timestamp from given value of
                                             milliseconds (to be compatible with Java

public Timestamp(long sec, long              Construct timestamp with specific
nsec, Rounding rbh, int pSec, int            precision and rounding values
public Timestamp(long sec, long              Construct timestamp from given number of
nsec, Rounding rbh, Precision p)             seconds and nanoseconds, as well as
                                             precision and rounding values (using enum
                                             defined precision values)
public static Timestamp                      Create timestamp from Date class
fromDate(Date d)
public void setTime(long sec, long           Set value of existing Timestamp
public void setTime(long msec)               Set value of existing Timestamp
public void setNanos(long nsec)              Set value of existing Timestamp
public long getTime()                        Return Timestamp value in milliseconds
public long getSeconds()                     Return whole seconds component of
                                             Timestamp value
Public long getNanos()                       Return nanoseconds component of
                                             Timestamp value
Public void                          Set rounding behavior
setRoundingBehavior(Rounding rb)
Public void setPrecision(int sec,    Set precision in either whole seconds or
int nsecDec)                         number of decimal places in "fractional"
                                     (nanosecond) value. At least one argument
                                     must be 0
Public void setPrecision(Precision   Set precision using predefined enum values
public boolean before(Timestamp a)   Return true if object's time range ended
                                     before argument object's range begins
public boolean after(Timestamp a)    Return true if object's time range begins
                                     after argument object's range ended
public boolean overlaps(Timestamp    Returns true if ranges of this and argument
a)                                   objects overlap
public long getRange()               Returns length of time range represented by
                                     object, in nanoseconds
public long getRangeStart()          Returns time of beginning of range
                                     represented by object, in nanoseconds since
                                     Unix Epoch
public long getRangeEnd()            Returns time of end of range represented
                                     by object, in nanoseconds since Unix

public boolean equals(Timestamp a)   Returns true if timestamp objects are equal
public int compareTo(Timestamp a)    Returns -1 if this timestamp range ended
                                     before another one started, returns 1 if this
                                     timestamp range started after another one
                                     ended, returns 0 otherwise (i.e. if there is
                                     any overlap between ranges)
public String toString()             Convert Timestamp object value to string
                                     representation of format YYYY-MM-DD
public String toXMLString()          Convert Timestamp object value to string
                                     that can be used in XML document storage.
                                     String format is the same as in toString()
                                     with additional rounding behavior and
                                     precision values, i.e. T60S - truncate, 60
                                     seconds precision
public static Timestamp              Create Timestamp object from XML
fromXMLString(String s)              formatted string, such as that returned by
    In this paper we presented a survey of clock behavior in four modern operating
systems and a number of popular software packages. We looked at system clock access
methods and their returned values, as well as time values available from log files and
other timestamp sources.

     While there is significant diversity in available APIs and their resulting time values,
all systems provide user software with at least a millisecond time resolution. Often higher
resolution clock values are available. File system timestamps provide high resolution in
modern file system types, while older file systems may only support 1 second resolution
or less. The system clock value distributions were found to be adequate for millisecond
resolution time determination. Documentation for some timestamp APIs, such as that for
NTFS for example, advertises a very high clock precision. When examining the
timestamp distribution produced by a clock in these systems, we discovered that it was
unable to support its specified precision. This may result in a much lower effective time
resolution for the timestamps produced by these clocks, which forensic investigators may
need to be aware of.

    We found that most software packages still use 1 second resolution times both
internally and in their log files or other timestamp output data, commonly truncating
higher resolution time values obtained from the system API. This pattern seems to be
changing in recent versions of some applications, such as Firefox, which store higher
resolution time values where possible, with the full precision available from the system

    We created a Timestamp module for the Zeitline application, which implements some
of the methods to handle timestamps as “fuzzy” time intervals during which events could
have occurred. The module was integrated into Zeitline and provides plugin developers
with the ability to control precision and duration of timestamps imported from digital
evidence. In conjunction with information on precision of specific time sources, this
should allow Zeitline users to better compare timestamps from different sources of

    Further research may concentrate on additional timestamp source clock behavior, and
better modeling of their behavior in Zeitline Timestamp class.
  1. P. Gladyshev, A. Patel. “Formalising Event Time Bounding in Digital
      Investigations”. International Journal of Digital Evidence. Fall 2005, Volume 4,
      Issue 2
  2. F. Buchholz, B. Tjaden. “A brief study of time”. Digital Investigation. 4S (2007) :
  3. B. Schatz, G. Mohay, A. Clark. “A correlation method for establishing
      provenance of timestamps in digital evidence”. Digital Investigation. 3S (2006) :
  4. F. Buchholz. “An Improved Clock Model for Translating Timestamps”. James
      Madison University Infosec Techreport. May 2007
  5. Zeitline repository.
  6. R. Watson. FreeBSD and Linux Kernel Cross-Reference. Linux 2.6
  7. Ibid. FreeBSD 7.0.
  8. Apache – HTTP Server Project.
  9. Mike Rieker. “Advanced Programmable Interrupt Controller”. July 2002.
  10. U. Weigand. “Linux 2.6 nanosecond time stamp weirdness breaks GCC build”.
      Mailing list for GCC project. Apr. 2004.
  11. Fedora Project, Ext4 Features.
  12. Apple Developer Connection, “Mach Absolute Time Units”. Technical Q&A
  13. Apple Developer Connection. “HFS Plus Volume Format”. Technical Note
  14. Microsoft Developer Network. “Windows System Information: FILETIME
  15. Microsoft Developer Network. “Windows System Information: File Times”.
  16. Microsoft Developer Network. “Event Logging: EVENTLOGRECORD
  17. Apache – HTTP Server Project. “Log Files”. Documentation, Version 2.2.
  18. R. Fielding et al. “Hypertext Transfer Protocol – HTTP 1.1”. RFC 2616. Section
      14 – “Header Field Definitions”
  19. Mozilla project. Firefox 2.0. “about:cache”
  20. J. Zawinski et al. Mozilla URL history reader/MORK parser.
  21. SQLite – Embedded Database Engine.
  22. K. Jones. Foundstone. “Forensic Analysis of Microsoft Internet Explorer Cookie
      Files”, May 2003.
23. Sun Inc. Java 2 Platform, Standard Edition, v. 1.4.2, API Specification. “Class
Appendix C1
Utility to directly test time API in a cycle.

#include     <stdio.h>
#include     <stdlib.h>
#include     <string.h>
#include     <time.h>

#define NSEC_IN_SEC           1000000000

int main(int ac, char **av)
   int i, max, ndiff, ndiff_min;
   struct timespec   tv, tv_prev;

    if (ac < 2) return 0;

    max = atoi(av[1]);

    printf("%d\n", max);

    ndiff_min = NSEC_IN_SEC;
    clock_gettime(CLOCK_MONOTONIC, &tv);
    for (i = 0; i < max; ++i) {
       tv_prev.tv_sec = tv.tv_sec;
       tv_prev.tv_nsec = tv.tv_nsec;
       clock_gettime(CLOCK_MONOTONIC, &tv);

        if (tv.tv_sec == tv_prev.tv_sec) {
           ndiff = tv.tv_nsec - tv_prev.tv_nsec;
        } else {
           // adjust for second rollover
           ndiff = tv.tv_nsec + NSEC_IN_SEC - tv_prev.tv_nsec;

        if (ndiff < ndiff_min) {
           ndiff_min = ndiff;
           printf("Shorter differential: %d\n", ndiff_min);


    return 0;

Appendix C2
Utility to build statistic distribution of time API results (sample for Windows).

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

#define AMAX          0xff // length of suffix to examine

int main(int ac, char **av)
   int i, max;
   int arr[AMAX + 1];

    if (ac < 2) return 0;

    max = atoi(av[1]);

    printf("%d\n", max);
    memset(arr, 0, sizeof(int) * (AMAX + 1));

    for (i = 0; i < max; ++i) {


        // increment per-suffix counter
        arr[ft.dwLowDateTime & AMAX]++;                 }

    for (i = 0; i < (AMAX + 1); ++i) {
       printf("%d: %d\n", i, arr[i]);

return 0;

Appendix C3
Utility to test Windows NTFS file timestamp behavior.
#include <windows.h>
#include <stdio.h>

//   GetLastWriteTime - Retrieves the last-write time and converts
//                      the time to a string
//   Return value - TRUE if successful, FALSE otherwise
//   hFile      - Valid file handle
//   lpszString - Pointer to buffer to receive string

FILETIME ftSave = { 0, 0 };
int first = 1;

BOOL GetLastWriteTime(HANDLE hFile, char *buf, int size)
    FILETIME ftCreate, ftAccess, ftWrite;
    SYSTEMTIME stUTC, stLocal;
    DWORD dwRet;

      // Retrieve the file times for the file.
      if (!GetFileTime(hFile, &ftCreate, &ftAccess, &ftWrite))
          return FALSE;

      // Convert the last-write time to local time.

         if ((ftSave.dwLowDateTime != ftWrite.dwLowDateTime) || first) {
                 first = 0;
#if 1
               printf("Create Time in binary: %d %d\n", ftCreate.dwHighDateTime,
               FileTimeToSystemTime(&ftCreate, &stUTC);
               SystemTimeToTzSpecificLocalTime(NULL, &stUTC, &stLocal);
               printf("Create Time in human: %02d/%02d/%d %02d:%02d:%02d.%03d\n",
                       stLocal.wMonth, stLocal.wDay, stLocal.wYear,
                       stLocal.wHour, stLocal.wMinute, stLocal.wSecond,


               printf("Modify Time in binary: %d %d\n", ftWrite.dwHighDateTime,

               // Build a string showing the date and time.
               FileTimeToSystemTime(&ftWrite, &stUTC);
               SystemTimeToTzSpecificLocalTime(NULL, &stUTC, &stLocal);
               printf("Modify Time in human: %02d/%02d/%d %02d:%02d:%02d.%03d\n",
                       stLocal.wMonth, stLocal.wDay, stLocal.wYear,
                       stLocal.wHour, stLocal.wMinute, stLocal.wSecond,

                ftSave.dwHighDateTime = ftWrite.dwHighDateTime;
                ftSave.dwLowDateTime = ftWrite.dwLowDateTime;

      return TRUE;
int main(int argc, char *argv[])
    HANDLE hFile;
    char szBuf[MAX_PATH], buf[128];
        DWORD len, lout;
        int i;

    if( argc != 3 )
        printf("This sample takes a file name as a parameter\n");
        return 0;

    if (argv[1][0] == 'c') {
               hFile = CreateFile(argv[2], GENERIC_WRITE, FILE_SHARE_WRITE, NULL,
                      CREATE_ALWAYS, 0, NULL);

               if(hFile == INVALID_HANDLE_VALUE)    {
                      printf("CreateFile failed with %d\n", GetLastError());
                      return 0;

               sprintf(buf, "Hello World!\n");
               len = strlen(buf);

               while(1) {
                      if (WriteFile(hFile, buf, len, &lout, NULL) == FALSE) {
                              printf("WriteFile failed with %d\n", GetLastError());
                              return 0;

    } else {
               hFile = CreateFile(argv[2], GENERIC_READ, FILE_SHARE_READ, NULL,
                      OPEN_EXISTING, 0, NULL);

               if(hFile == INVALID_HANDLE_VALUE) {
                      printf("CreateFile failed with %d\n", GetLastError());
                      return 0;

               GetLastWriteTime( hFile, szBuf, MAX_PATH );


Shared By: