diff --git a/Makefile b/Makefile index 4a49b3ae124..314374cf357 100644 --- a/Makefile +++ b/Makefile @@ -17,6 +17,10 @@ references: release: go run make.go -v release +# Basic darwin binary - no yara. +darwin: + go run make.go -v DarwinBase + linux: go run make.go -v linux diff --git a/artifacts/definitions/System/VFS/ListDirectory.yaml b/artifacts/definitions/System/VFS/ListDirectory.yaml index 4ee4c3d5339..c2db64915e1 100644 --- a/artifacts/definitions/System/VFS/ListDirectory.yaml +++ b/artifacts/definitions/System/VFS/ListDirectory.yaml @@ -38,7 +38,8 @@ sources: Name, Size, Mode.String AS Mode, Mtime as mtime, Atime as atime, - Ctime as ctime + Ctime as ctime, + Btime AS btime FROM glob( globs=if(condition=Depth, then=format(format='/**%v', args=Depth), diff --git a/file_store/api/accessor.go b/file_store/api/accessor.go index 661772dc8e0..d5b213cf57c 100644 --- a/file_store/api/accessor.go +++ b/file_store/api/accessor.go @@ -105,6 +105,10 @@ func (self *FileStoreFileInfo) FullPath() string { return self.FullPath_ } +func (self *FileStoreFileInfo) Btime() utils.TimeVal { + return utils.TimeVal{} +} + func (self *FileStoreFileInfo) Mtime() utils.TimeVal { return utils.TimeVal{} } diff --git a/file_store/api/adapter.go b/file_store/api/adapter.go index bdd4d09fb28..eabcc1d0b4c 100644 --- a/file_store/api/adapter.go +++ b/file_store/api/adapter.go @@ -37,6 +37,10 @@ func (self FileInfoAdapter) FullPath() string { return self.full_path } +func (self FileInfoAdapter) Btime() utils.TimeVal { + return utils.TimeVal{} +} + func (self FileInfoAdapter) Mtime() utils.TimeVal { return utils.TimeVal{} } diff --git a/file_store/mysql/mysql.go b/file_store/mysql/mysql.go index ab5e3805ba0..e7512aa5dc4 100644 --- a/file_store/mysql/mysql.go +++ b/file_store/mysql/mysql.go @@ -105,6 +105,13 @@ func (self *MysqlFileStoreFileInfo) Mtime() utils.TimeVal { } } +func (self *MysqlFileStoreFileInfo) Btime() utils.TimeVal { + return utils.TimeVal{ + Sec: self.timestamp, + Nsec: self.timestamp * 1000000000, + } +} + func (self MysqlFileStoreFileInfo) Atime() utils.TimeVal { return utils.TimeVal{} } diff --git a/glob/accessor_darwin.go b/glob/accessor_darwin.go index d0e68246170..e944b7b6dde 100644 --- a/glob/accessor_darwin.go +++ b/glob/accessor_darwin.go @@ -42,27 +42,42 @@ func (self *OSFileInfo) FullPath() string { return self._full_path } +func (self *OSFileInfo) Btime() utils.TimeVal { + ts := self.sys().Birthtimespec + return utils.TimeVal{ + Sec: ts.Sec, + Nsec: ts.Nsec + ts.Sec*1000000000, + } +} + func (self *OSFileInfo) Mtime() utils.TimeVal { + ts := self.sys().Mtimespec return utils.TimeVal{ - Sec: int64(self.sys().Mtimespec.Sec), - Nsec: int64(self.sys().Mtimespec.Nsec), + Sec: ts.Sec, + Nsec: ts.Nsec + ts.Sec*1000000000, } } func (self *OSFileInfo) Ctime() utils.TimeVal { + ts := self.sys().Ctimespec return utils.TimeVal{ - Sec: int64(self.sys().Ctimespec.Sec), - Nsec: int64(self.sys().Ctimespec.Nsec), + Sec: ts.Sec, + Nsec: ts.Nsec + ts.Sec*1000000000, } } func (self *OSFileInfo) Atime() utils.TimeVal { + ts := self.sys().Atimespec return utils.TimeVal{ - Sec: int64(self.sys().Atimespec.Sec), - Nsec: int64(self.sys().Atimespec.Nsec), + Sec: ts.Sec, + Nsec: ts.Nsec + ts.Sec*1000000000, } } +func (self *OSFileInfo) _Sys() *syscall.Stat_t { + return self.sys() +} + func (self *OSFileInfo) Data() interface{} { return nil } diff --git a/glob/accessor_linux.go b/glob/accessor_linux.go index f32aef38d17..44d8add6854 100644 --- a/glob/accessor_linux.go +++ b/glob/accessor_linux.go @@ -114,6 +114,14 @@ func (self *OSFileInfo) FullPath() string { return self._full_path } +// On Linux we need xstat() support to get birth time. +func (self *OSFileInfo) Btime() utils.TimeVal { + return utils.TimeVal{ + Sec: 0, + Nsec: 0, + } +} + func (self *OSFileInfo) Mtime() utils.TimeVal { ts := int64(self._Sys().Mtim.Sec) return utils.TimeVal{ diff --git a/glob/common.go b/glob/common.go index 4ed6f04619e..b09cab69f4c 100644 --- a/glob/common.go +++ b/glob/common.go @@ -77,6 +77,10 @@ func (self *VirtualDirectoryPath) Mtime() utils.TimeVal { return utils.TimeVal{} } +func (self *VirtualDirectoryPath) Btime() utils.TimeVal { + return utils.TimeVal{} +} + func (self *VirtualDirectoryPath) Ctime() utils.TimeVal { return utils.TimeVal{} } diff --git a/glob/glob.go b/glob/glob.go index f73a9f894bd..6a30cec444c 100644 --- a/glob/glob.go +++ b/glob/glob.go @@ -40,7 +40,12 @@ type FileInfo interface { Name() string ModTime() time.Time FullPath() string + + // Time the file was birthed (initially created) + Btime() utils.TimeVal Mtime() utils.TimeVal + + // Time the inode was changed. Ctime() utils.TimeVal Atime() utils.TimeVal Data() interface{} diff --git a/gui/velociraptor/src/components/vfs/file-list.js b/gui/velociraptor/src/components/vfs/file-list.js index 44b6b3c8b6e..42e702f1c2b 100644 --- a/gui/velociraptor/src/components/vfs/file-list.js +++ b/gui/velociraptor/src/components/vfs/file-list.js @@ -378,7 +378,8 @@ class VeloFileList extends Component { {dataField: "Mode", text: "Mode", sort: true}, {dataField: "mtime", text: "mtime", sort: true}, {dataField: "atime", text: "atime", sort: true}, - {dataField: "ctime", text: "ctime", sort: true} + {dataField: "ctime", text: "ctime", sort: true}, + {dataField: "btime", text: "btime", sort: true} ]); return ( diff --git a/services/vfs_service/vfs_service.go b/services/vfs_service/vfs_service.go index 8c3c820c727..14927cdba2d 100644 --- a/services/vfs_service/vfs_service.go +++ b/services/vfs_service/vfs_service.go @@ -145,7 +145,6 @@ func (self *VFSService) ProcessListDirectory( var current_vfs_components []string = nil for row := range row_chan { - utils.Debug(row) full_path, _ := row.GetString("_FullPath") accessor, _ := row.GetString("_Accessor") name, _ := row.GetString("Name") diff --git a/utils/file.go b/utils/file.go index ecce6c6110c..b284ad1fa65 100644 --- a/utils/file.go +++ b/utils/file.go @@ -156,6 +156,11 @@ func (self *DataFileInfo) ModTime() time.Time { func (self *DataFileInfo) FullPath() string { return string(self.data) } + +func (self *DataFileInfo) Btime() TimeVal { + return TimeVal{} +} + func (self *DataFileInfo) Mtime() TimeVal { return TimeVal{} } diff --git a/vql/filesystem/gzip.go b/vql/filesystem/gzip.go index fabc8e29c51..1d6280df47f 100644 --- a/vql/filesystem/gzip.go +++ b/vql/filesystem/gzip.go @@ -96,6 +96,10 @@ func (self *GzipFileInfo) Mtime() utils.TimeVal { return self._modtime } +func (self *GzipFileInfo) Btime() utils.TimeVal { + return self._modtime +} + func (self *GzipFileInfo) Ctime() utils.TimeVal { return self._modtime } diff --git a/vql/filesystem/raw_registry.go b/vql/filesystem/raw_registry.go index 389fc3342f3..6fbb0b45b03 100644 --- a/vql/filesystem/raw_registry.go +++ b/vql/filesystem/raw_registry.go @@ -106,6 +106,10 @@ func (self *RawRegKeyInfo) Ctime() utils.TimeVal { return self.Mtime() } +func (self *RawRegKeyInfo) Btime() utils.TimeVal { + return self.Mtime() +} + func (self *RawRegKeyInfo) Atime() utils.TimeVal { return self.Mtime() } diff --git a/vql/filesystem/zip.go b/vql/filesystem/zip.go index 6d3b1270fbc..81fe528123b 100644 --- a/vql/filesystem/zip.go +++ b/vql/filesystem/zip.go @@ -143,6 +143,10 @@ func (self *ZipFileInfo) Ctime() utils.TimeVal { return self.Mtime() } +func (self *ZipFileInfo) Btime() utils.TimeVal { + return self.Mtime() +} + func (self *ZipFileInfo) Atime() utils.TimeVal { return self.Mtime() } diff --git a/vql/protocols.go b/vql/protocols.go index b8746898198..4e509f7b5c2 100644 --- a/vql/protocols.go +++ b/vql/protocols.go @@ -125,7 +125,7 @@ func (self _GlobFileInfoAssociative) Associative( // FROM ... func (self _GlobFileInfoAssociative) GetMembers( scope vfilter.Scope, a vfilter.Any) []string { - return []string{"Name", "ModTime", "FullPath", "Mtime", + return []string{"Name", "ModTime", "FullPath", "Mtime", "Btime", "Ctime", "Atime", "Data", "Size", "IsDir", "IsLink", "Mode", "Sys"} } diff --git a/vql/windows/filesystems/ntfs_lazy_windows.go b/vql/windows/filesystems/ntfs_lazy_windows.go index a19b02e972b..9cf296dc5fc 100644 --- a/vql/windows/filesystems/ntfs_lazy_windows.go +++ b/vql/windows/filesystems/ntfs_lazy_windows.go @@ -177,6 +177,14 @@ func (self *LazyNTFSFileInfo) Mtime() utils.TimeVal { } } +func (self *LazyNTFSFileInfo) Btime() utils.TimeVal { + self.ensureCachedInfo() + + return utils.TimeVal{ + Sec: self.cached_info.Btime.Unix(), + } +} + func (self *LazyNTFSFileInfo) Ctime() utils.TimeVal { self.ensureCachedInfo() diff --git a/vql/windows/filesystems/ntfs_windows.go b/vql/windows/filesystems/ntfs_windows.go index afd53f3eaf8..749977d8b15 100644 --- a/vql/windows/filesystems/ntfs_windows.go +++ b/vql/windows/filesystems/ntfs_windows.go @@ -145,6 +145,14 @@ func (self *NTFSFileInfo) FullPath() string { return self._full_path } +func (self *NTFSFileInfo) Btime() utils.TimeVal { + nsec := self.info.Btime.UnixNano() + return utils.TimeVal{ + Sec: nsec / 1000000000, + Nsec: nsec, + } +} + func (self *NTFSFileInfo) Mtime() utils.TimeVal { nsec := self.info.Mtime.UnixNano() return utils.TimeVal{ diff --git a/vql/windows/filesystems/os_windows.go b/vql/windows/filesystems/os_windows.go index 1a127f5382f..f67ad0b026d 100644 --- a/vql/windows/filesystems/os_windows.go +++ b/vql/windows/filesystems/os_windows.go @@ -64,6 +64,15 @@ func (self *OSFileInfo) Data() interface{} { return ordereddict.NewDict() } +func (self *OSFileInfo) Btime() utils.TimeVal { + nsec := self.sys().CreationTime.Nanoseconds() + return utils.TimeVal{ + Sec: nsec / 1000000000, + Nsec: nsec, + } +} + + func (self *OSFileInfo) Mtime() utils.TimeVal { nsec := self.sys().LastWriteTime.Nanoseconds() return utils.TimeVal{ @@ -72,8 +81,10 @@ func (self *OSFileInfo) Mtime() utils.TimeVal { } } +// Windows does not provide the ctime (inode change time) using the +// APIs. func (self *OSFileInfo) Ctime() utils.TimeVal { - nsec := self.sys().CreationTime.Nanoseconds() + nsec := self.sys().LastWriteTime.Nanoseconds() return utils.TimeVal{ Sec: nsec / 1000000000, Nsec: nsec, diff --git a/vql/windows/filesystems/registry_windows.go b/vql/windows/filesystems/registry_windows.go index 8b74f591594..383dd85b2a9 100644 --- a/vql/windows/filesystems/registry_windows.go +++ b/vql/windows/filesystems/registry_windows.go @@ -105,6 +105,10 @@ func (self *RegKeyInfo) Mtime() utils.TimeVal { } } +func (self *RegKeyInfo) Btime() utils.TimeVal { + return self.Mtime() +} + func (self *RegKeyInfo) Ctime() utils.TimeVal { return self.Mtime() } diff --git a/vtesting/file_info.go b/vtesting/file_info.go index 80bf9e5788b..6623619feb7 100644 --- a/vtesting/file_info.go +++ b/vtesting/file_info.go @@ -21,6 +21,7 @@ func (self MockFileInfo) ModTime() time.Time { return time.Time{} } func (self MockFileInfo) IsDir() bool { return true } func (self MockFileInfo) Sys() interface{} { return nil } func (self MockFileInfo) FullPath() string { return self.FullPath_ } +func (self MockFileInfo) Btime() utils.TimeVal { return utils.TimeVal{} } func (self MockFileInfo) Mtime() utils.TimeVal { return utils.TimeVal{} } func (self MockFileInfo) Atime() utils.TimeVal { return utils.TimeVal{} } func (self MockFileInfo) Ctime() utils.TimeVal { return utils.TimeVal{} }