add mft read method
This commit is contained in:
@@ -0,0 +1,141 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"b612.me/starlog"
|
||||
"b612.me/wincmd"
|
||||
"b612.me/wincmd/ntfs/mft"
|
||||
"b612.me/wincmd/ntfs/utf16"
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
_ "net/http/pprof"
|
||||
"regexp"
|
||||
"runtime"
|
||||
"time"
|
||||
)
|
||||
|
||||
func main() {
|
||||
f, size, err := mft.GetMFTFile(`C:\`)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
recordSize := int64(1024)
|
||||
i := int64(0)
|
||||
starlog.Infoln("start size is", size)
|
||||
alreadyGot := int64(0)
|
||||
maxRecordSize := size / recordSize
|
||||
if maxRecordSize > 1024 {
|
||||
maxRecordSize = 1024
|
||||
}
|
||||
for {
|
||||
for {
|
||||
if (size - alreadyGot) < maxRecordSize*recordSize {
|
||||
maxRecordSize--
|
||||
} else {
|
||||
break
|
||||
}
|
||||
}
|
||||
if maxRecordSize < 10 {
|
||||
maxRecordSize = 1
|
||||
}
|
||||
buf := make([]byte, maxRecordSize*recordSize)
|
||||
got, err := io.ReadFull(f, buf)
|
||||
if err != nil {
|
||||
if errors.Is(err, io.EOF) {
|
||||
break
|
||||
}
|
||||
log.Fatalln("Unable to read record data", err)
|
||||
}
|
||||
alreadyGot += int64(got)
|
||||
for j := int64(0); j < 1024*maxRecordSize; j += 1024 {
|
||||
record, err := mft.ParseRecord(buf[j : j+1024])
|
||||
if err != nil {
|
||||
// fmt.Println("Unable to parse MFT record", err)
|
||||
continue
|
||||
}
|
||||
|
||||
fname := record.FindAttributes(mft.AttributeTypeFileName)
|
||||
|
||||
for _, v := range fname {
|
||||
if (record.Flags&mft.RecordFlagIsIndex)>>3 == 0 && record.Flags&mft.RecordFlagInUse == 1 {
|
||||
filename := utf16.DecodeString(v.Data[66:], binary.LittleEndian)
|
||||
//data:=utf16.DecodeString(v.Data, binary.LittleEndian)
|
||||
if filename != "" {
|
||||
attbr := record.FindAttributes(mft.AttributeTypeStandardInformation)
|
||||
if len(attbr) == 0 {
|
||||
break
|
||||
}
|
||||
stand, _ := mft.ParseStandardInformation(attbr[0].Data)
|
||||
if stand.FileAttributes&mft.FileAttributeNormal == 1 {
|
||||
i++
|
||||
}
|
||||
//oo:=record.FindAttributes(mft.AttributeTypeData)
|
||||
//if len(oo)!=0 {
|
||||
// fmt.Println(oo[0].ActualSize)
|
||||
//}
|
||||
//r := binutil.NewLittleEndianReader(v.Data[:8])
|
||||
//fmt.Println(record.FileReference.ToUint64(), r.Uint64(0))
|
||||
//fmt.Println(filename)
|
||||
//log.Println("Read MFT record")
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
starlog.Infoln(i)
|
||||
|
||||
//go http.ListenAndServe("0.0.0.0:8888", nil)
|
||||
starlog.Debugln("开始获取Windows磁盘列表")
|
||||
lists, err := wincmd.ListDrivers()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
info := []wincmd.DiskInfo{}
|
||||
for _, v := range lists {
|
||||
data, err := wincmd.GetDiskInfo(v)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
fmt.Println("获取到磁盘信息:", data)
|
||||
info = append(info, data)
|
||||
}
|
||||
starlog.Warningln("忽略非NTFS磁盘")
|
||||
var m runtime.MemStats
|
||||
for _, v := range info {
|
||||
if v.Format == "NTFS" && v.Driver == `C:\` {
|
||||
|
||||
starlog.Infoln("开始获取NTFS USN日志,磁盘:", v.Driver)
|
||||
fileLists, err := wincmd.ListUsnFileFn(v.Driver, func(name string, typed uint8) bool {
|
||||
return true
|
||||
if ok, _ := regexp.MatchString(`\.exe$`, name); ok {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
})
|
||||
if err != nil {
|
||||
starlog.Panicln("获取失败", err)
|
||||
}
|
||||
fmt.Println(len(fileLists))
|
||||
return
|
||||
for k, _ := range fileLists {
|
||||
fmt.Println(k)
|
||||
}
|
||||
runtime.ReadMemStats(&m)
|
||||
log.Printf("Alloc = %v TotalAlloc = %v Sys = %v NumGC = %v\n", m.Alloc/1024, m.TotalAlloc/1024, m.Sys/1024, m.NumGC)
|
||||
runtime.GC()
|
||||
p := time.After(time.Second * 86400)
|
||||
for {
|
||||
select {
|
||||
case <-p:
|
||||
return
|
||||
case <-time.After(time.Second * 10):
|
||||
runtime.ReadMemStats(&m)
|
||||
log.Printf("Alloc = %v TotalAlloc = %v Sys = %v NumGC = %v\n", m.Alloc/1024, m.TotalAlloc/1024, m.Sys/1024, m.NumGC)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,251 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"b612.me/wincmd/ntfs/bootsect"
|
||||
"b612.me/wincmd/ntfs/fragment"
|
||||
"b612.me/wincmd/ntfs/mft"
|
||||
"flag"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
)
|
||||
|
||||
const supportedOemId = "NTFS "
|
||||
|
||||
const (
|
||||
exitCodeUserError int = iota + 2
|
||||
exitCodeFunctionalError
|
||||
exitCodeTechnicalError
|
||||
)
|
||||
|
||||
const isWin = runtime.GOOS == "windows"
|
||||
|
||||
var (
|
||||
// flags
|
||||
verbose = false
|
||||
overwriteOutputIfExists = false
|
||||
showProgress = false
|
||||
)
|
||||
|
||||
func main() {
|
||||
start := time.Now()
|
||||
verboseFlag := flag.Bool("v", false, "verbose; print details about what's going on")
|
||||
forceFlag := flag.Bool("f", false, "force; overwrite the output file if it already exists")
|
||||
progressFlag := flag.Bool("p", false, "progress; show progress during dumping")
|
||||
|
||||
flag.Usage = printUsage
|
||||
flag.Parse()
|
||||
|
||||
verbose = *verboseFlag
|
||||
overwriteOutputIfExists = *forceFlag
|
||||
showProgress = *progressFlag
|
||||
args := flag.Args()
|
||||
|
||||
if len(args) != 2 {
|
||||
printUsage()
|
||||
os.Exit(exitCodeUserError)
|
||||
return
|
||||
}
|
||||
|
||||
volume := args[0]
|
||||
if isWin {
|
||||
volume = `\\.\` + volume
|
||||
}
|
||||
//fmt.Println(volume)
|
||||
outfile := args[1]
|
||||
|
||||
in, err := os.Open(volume)
|
||||
if err != nil {
|
||||
fatalf(exitCodeTechnicalError, "Unable to open volume using path %s: %v\n", volume, err)
|
||||
}
|
||||
defer in.Close()
|
||||
|
||||
printVerbose("Reading boot sector\n")
|
||||
bootSectorData := make([]byte, 512)
|
||||
_, err = io.ReadFull(in, bootSectorData)
|
||||
if err != nil {
|
||||
fatalf(exitCodeTechnicalError, "Unable to read boot sector: %v\n", err)
|
||||
}
|
||||
|
||||
printVerbose("Read %d bytes of boot sector, parsing boot sector\n", len(bootSectorData))
|
||||
bootSector, err := bootsect.Parse(bootSectorData)
|
||||
if err != nil {
|
||||
fatalf(exitCodeTechnicalError, "Unable to parse boot sector data: %v\n", err)
|
||||
}
|
||||
|
||||
if bootSector.OemId != supportedOemId {
|
||||
fatalf(exitCodeFunctionalError, "Unknown OemId (file system type) %q (expected %q)\n", bootSector.OemId, supportedOemId)
|
||||
}
|
||||
|
||||
bytesPerCluster := bootSector.BytesPerSector * bootSector.SectorsPerCluster
|
||||
mftPosInBytes := int64(bootSector.MftClusterNumber) * int64(bytesPerCluster)
|
||||
|
||||
_, err = in.Seek(mftPosInBytes, 0)
|
||||
if err != nil {
|
||||
fatalf(exitCodeTechnicalError, "Unable to seek to MFT position: %v\n", err)
|
||||
}
|
||||
|
||||
mftSizeInBytes := bootSector.FileRecordSegmentSizeInBytes
|
||||
printVerbose("Reading $MFT file record at position %d (size: %d bytes)\n", mftPosInBytes, mftSizeInBytes)
|
||||
mftData := make([]byte, mftSizeInBytes)
|
||||
_, err = io.ReadFull(in, mftData)
|
||||
if err != nil {
|
||||
fatalf(exitCodeTechnicalError, "Unable to read $MFT record: %v\n", err)
|
||||
}
|
||||
|
||||
printVerbose("Parsing $MFT file record\n")
|
||||
record, err := mft.ParseRecord(mftData)
|
||||
if err != nil {
|
||||
fatalf(exitCodeTechnicalError, "Unable to parse $MFT record: %v\n", err)
|
||||
}
|
||||
|
||||
printVerbose("Reading $DATA attribute in $MFT file record\n")
|
||||
dataAttributes := record.FindAttributes(mft.AttributeTypeData)
|
||||
if len(dataAttributes) == 0 {
|
||||
fatalf(exitCodeTechnicalError, "No $DATA attribute found in $MFT record\n")
|
||||
}
|
||||
|
||||
if len(dataAttributes) > 1 {
|
||||
fatalf(exitCodeTechnicalError, "More than 1 $DATA attribute found in $MFT record\n")
|
||||
}
|
||||
|
||||
dataAttribute := dataAttributes[0]
|
||||
if dataAttribute.Resident {
|
||||
fatalf(exitCodeTechnicalError, "Don't know how to handle resident $DATA attribute in $MFT record\n")
|
||||
}
|
||||
|
||||
dataRuns, err := mft.ParseDataRuns(dataAttribute.Data)
|
||||
if err != nil {
|
||||
fatalf(exitCodeTechnicalError, "Unable to parse dataruns in $MFT $DATA record: %v\n", err)
|
||||
}
|
||||
|
||||
if len(dataRuns) == 0 {
|
||||
fatalf(exitCodeTechnicalError, "No dataruns found in $MFT $DATA record\n")
|
||||
}
|
||||
|
||||
fragments := mft.DataRunsToFragments(dataRuns, bytesPerCluster)
|
||||
totalLength := int64(0)
|
||||
for _, frag := range fragments {
|
||||
totalLength += int64(frag.Length)
|
||||
}
|
||||
|
||||
out, err := openOutputFile(outfile)
|
||||
if err != nil {
|
||||
fatalf(exitCodeFunctionalError, "Unable to open output file: %v\n", err)
|
||||
}
|
||||
defer out.Close()
|
||||
|
||||
printVerbose("Copying %d bytes (%s) of data to %s\n", totalLength, formatBytes(totalLength), outfile)
|
||||
n, err := copy(out, fragment.NewReader(in, fragments), totalLength)
|
||||
if err != nil {
|
||||
fatalf(exitCodeTechnicalError, "Error copying data to output file: %v\n", err)
|
||||
}
|
||||
|
||||
if n != totalLength {
|
||||
fatalf(exitCodeTechnicalError, "Expected to copy %d bytes, but copied only %d\n", totalLength, n)
|
||||
}
|
||||
end := time.Now()
|
||||
dur := end.Sub(start)
|
||||
printVerbose("Finished in %v\n", dur)
|
||||
}
|
||||
|
||||
func copy(dst io.Writer, src io.Reader, totalLength int64) (written int64, err error) {
|
||||
buf := make([]byte, 1024*1024)
|
||||
if !showProgress {
|
||||
return io.CopyBuffer(dst, src, buf)
|
||||
}
|
||||
|
||||
onePercent := float64(totalLength) / float64(100.0)
|
||||
totalSize := formatBytes(totalLength)
|
||||
|
||||
// Below copied from io.copyBuffer (https://golang.org/src/io/io.go?s=12796:12856#L380)
|
||||
for {
|
||||
printProgress(written, totalSize, onePercent)
|
||||
|
||||
nr, er := src.Read(buf)
|
||||
if nr > 0 {
|
||||
nw, ew := dst.Write(buf[0:nr])
|
||||
if nw > 0 {
|
||||
written += int64(nw)
|
||||
}
|
||||
if ew != nil {
|
||||
err = ew
|
||||
break
|
||||
}
|
||||
if nr != nw {
|
||||
err = io.ErrShortWrite
|
||||
break
|
||||
}
|
||||
}
|
||||
if er != nil {
|
||||
if er != io.EOF {
|
||||
err = er
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
printProgress(written, totalSize, onePercent)
|
||||
fmt.Println()
|
||||
return written, err
|
||||
}
|
||||
|
||||
func printProgress(n int64, totalSize string, onePercent float64) {
|
||||
percentage := float64(n) / onePercent
|
||||
barCount := int(percentage / 2.0)
|
||||
spaceCount := 50 - barCount
|
||||
fmt.Printf("\r[%s%s] %.2f%% (%s / %s) ", strings.Repeat("|", barCount), strings.Repeat(" ", spaceCount), percentage, formatBytes(n), totalSize)
|
||||
}
|
||||
|
||||
func openOutputFile(outfile string) (*os.File, error) {
|
||||
if overwriteOutputIfExists {
|
||||
return os.Create(outfile)
|
||||
} else {
|
||||
return os.OpenFile(outfile, os.O_WRONLY|os.O_CREATE|os.O_EXCL, 0666)
|
||||
}
|
||||
}
|
||||
|
||||
func printUsage() {
|
||||
out := os.Stderr
|
||||
exe := filepath.Base(os.Args[0])
|
||||
fmt.Fprintf(out, "\nusage: %s [flags] <volume> <output file>\n\n", exe)
|
||||
fmt.Fprintln(out, "Dump the MFT of a volume to a file. The volume should be NTFS formatted.")
|
||||
fmt.Fprintln(out, "\nFlags:")
|
||||
|
||||
flag.PrintDefaults()
|
||||
|
||||
fmt.Fprintf(out, "\nFor example: ")
|
||||
if isWin {
|
||||
fmt.Fprintf(out, "%s -v -f C: D:\\c.mft\n", exe)
|
||||
} else {
|
||||
fmt.Fprintf(out, "%s -v -f /dev/sdb1 ~/sdb1.mft\n", exe)
|
||||
}
|
||||
}
|
||||
|
||||
func fatalf(exitCode int, format string, v ...interface{}) {
|
||||
fmt.Printf(format, v...)
|
||||
os.Exit(exitCode)
|
||||
}
|
||||
|
||||
func printVerbose(format string, v ...interface{}) {
|
||||
if verbose {
|
||||
fmt.Printf(format, v...)
|
||||
}
|
||||
}
|
||||
|
||||
func formatBytes(b int64) string {
|
||||
if b < 1024 {
|
||||
return fmt.Sprintf("%dB", b)
|
||||
}
|
||||
if b < 1048576 {
|
||||
return fmt.Sprintf("%.2fKiB", float32(b)/float32(1024))
|
||||
}
|
||||
if b < 1073741824 {
|
||||
return fmt.Sprintf("%.2fMiB", float32(b)/float32(1048576))
|
||||
}
|
||||
return fmt.Sprintf("%.2fGiB", float32(b)/float32(1073741824))
|
||||
}
|
||||
Reference in New Issue
Block a user