-
Notifications
You must be signed in to change notification settings - Fork 106
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Added inline compression of memory image (#57)
To get good quality memory images it is important to acquire the image as quickly as possible to avoid smear. The following algorithms are supported 1. None - no compression 2. S2 - this algorithm gains about 50% compression over none but very little loss of speed. The result is slightly faster than none. 3. Snappy - this is a reasonable trade off getting compression slightly less than gzip but about twice the time as none. 4. Gzip - This method is very slow and so not recommended but it is compatible with the traditional gzip tool Also added an extract command to convert the compressed images to raw images later.
- Loading branch information
Showing
19 changed files
with
1,175 additions
and
112 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,138 @@ | ||
package main | ||
|
||
import ( | ||
"io/ioutil" | ||
"os" | ||
"time" | ||
|
||
"github.com/Velocidex/WinPmem/go-winpmem" | ||
"github.com/alecthomas/kingpin" | ||
) | ||
|
||
var ( | ||
acquire = app.Command("acquire", "Acquire a memory image") | ||
|
||
service_name = acquire.Flag("service_name", "Name of the service to create"). | ||
Default("winpmem").String() | ||
|
||
driver_path = acquire.Flag("driver_path", "Where to store the driver"). | ||
String() | ||
|
||
filename = acquire.Arg("filename", | ||
"Output path to write image to").String() | ||
|
||
nosparse = acquire.Flag("nosparse", "Disable sparse output file").Bool() | ||
progress = acquire.Flag("progress", "Show progress").Bool() | ||
|
||
compression = acquire.Flag("compression", "Type of compression to apply"). | ||
Default("none").PlaceHolder("snappy|gzip").String() | ||
) | ||
|
||
func doAcquire() error { | ||
logger := winpmem.NewLogger(*verbose) | ||
|
||
if *progress { | ||
logger.SetProgress(1024) | ||
} | ||
|
||
var err error | ||
var fd *os.File | ||
|
||
if *driver_path == "" { | ||
fd, err = ioutil.TempFile("", "*.sys") | ||
if err != nil { | ||
return err | ||
} | ||
|
||
defer func() { | ||
logger.Info("Removing driver from %v", fd.Name()) | ||
err := os.Remove(fd.Name()) | ||
if err != nil { | ||
logger.Info("Error removing %v: %v", | ||
fd.Name(), err) | ||
} | ||
}() | ||
|
||
*driver_path = fd.Name() | ||
|
||
} else { | ||
fd, err = os.OpenFile(*driver_path, | ||
os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0600) | ||
if err != nil { | ||
return err | ||
} | ||
} | ||
|
||
driver_code, err := winpmem.Winpmem_x64() | ||
if err != nil { | ||
return err | ||
} | ||
|
||
fd.Write([]byte(driver_code)) | ||
fd.Close() | ||
|
||
logger.Info("Writing driver to %v", *driver_path) | ||
|
||
err = winpmem.InstallDriver(*driver_path, *service_name, logger) | ||
|
||
defer winpmem.UninstallDriver( | ||
*driver_path, *service_name, logger) | ||
|
||
imager, err := winpmem.NewImager(`\\.\pmem`, logger) | ||
if err != nil { | ||
return err | ||
} | ||
defer imager.Close() | ||
|
||
// We only support this mode now - it is the most reliable. | ||
imager.SetMode(winpmem.PMEM_MODE_PTE) | ||
|
||
logger.Info("Memory Info:\n") | ||
logger.Info(imager.Stats().ToYaml()) | ||
|
||
// We do not need to take the image - we are done. | ||
if *filename == "" { | ||
return nil | ||
} | ||
|
||
// Sparse writing is only possible with no compression. | ||
if *compression != "" && *compression != "none" { | ||
*nosparse = true | ||
} | ||
|
||
if !*nosparse { | ||
logger.Info("Setting sparse output file %v", *filename) | ||
imager.SetSparse() | ||
} | ||
|
||
out_fd, err := winpmem.CreateFileForWriting(!*nosparse, *filename) | ||
if err != nil { | ||
return err | ||
} | ||
defer out_fd.Close() | ||
|
||
start := time.Now() | ||
defer func() { | ||
logger.Info("Completed imaging in %v", time.Now().Sub(start)) | ||
}() | ||
|
||
compressed_writer, closer, err := winpmem.GetCompressor(*compression, out_fd) | ||
if err != nil { | ||
return err | ||
} | ||
defer closer() | ||
|
||
return imager.WriteTo(compressed_writer) | ||
} | ||
|
||
func init() { | ||
command_handlers = append(command_handlers, func(command string) bool { | ||
switch command { | ||
case acquire.FullCommand(): | ||
kingpin.FatalIfError(doAcquire(), "acquire") | ||
default: | ||
return false | ||
} | ||
return true | ||
}) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,73 @@ | ||
package main | ||
|
||
import ( | ||
"os" | ||
"time" | ||
|
||
"github.com/Velocidex/WinPmem/go-winpmem" | ||
"github.com/alecthomas/kingpin" | ||
) | ||
|
||
var ( | ||
decompress = app.Command("extract", "Decompress an image.") | ||
image = decompress.Arg("image", "Path to the image to decompress"). | ||
Required().String() | ||
|
||
decompress_output_filename = decompress.Arg("filename", | ||
"Output path to write image to").Required().String() | ||
) | ||
|
||
func doDecompress() error { | ||
fd, err := os.Open(*image) | ||
if err != nil { | ||
return err | ||
} | ||
defer fd.Close() | ||
|
||
header := make([]byte, 10) | ||
n, err := fd.Read(header) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
fd.Seek(0, os.SEEK_SET) | ||
|
||
decompressed_fd, err := winpmem.GetDecompressor(header[:n], fd) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
out_fd, err := winpmem.CreateFileForWriting(true, *decompress_output_filename) | ||
if err != nil { | ||
return err | ||
} | ||
defer out_fd.Close() | ||
|
||
logger := &DecompressionLogger{Logger: winpmem.NewLogger(*verbose)} | ||
return winpmem.CopyAndLog(decompressed_fd, out_fd, logger) | ||
} | ||
|
||
func init() { | ||
command_handlers = append(command_handlers, func(command string) bool { | ||
switch command { | ||
case decompress.FullCommand(): | ||
kingpin.FatalIfError(doDecompress(), "decompress") | ||
default: | ||
return false | ||
} | ||
return true | ||
}) | ||
} | ||
|
||
type DecompressionLogger struct { | ||
winpmem.Logger | ||
count int | ||
} | ||
|
||
func (self *DecompressionLogger) Progress(pages int) { | ||
self.count += pages | ||
if self.count%20000 == 0 { | ||
self.Info("%v: Decompressed %v Mb", time.Now().Format(time.RFC3339), | ||
self.count*winpmem.PAGE_SIZE/1024/1024) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.