# HG changeset patch # User telesto # Date 1401193900 -7200 # Tue May 27 14:31:40 2014 +0200 # Node ID 20c5327533a319b3175ed3a1407c36f6c5cb7121 # Parent 0000000000000000000000000000000000000000 created repo diff --git a/README.md b/README.md new file mode 100644 --- /dev/null +++ b/README.md @@ -0,0 +1,3 @@ +memdump - a tool for dumping the memory of a process into a file +================================================================ +Runs with linux only. Because of the limitations of Go this program will not work on 64-bit machines. diff --git a/cmd/dump/main.go b/cmd/dump/main.go new file mode 100644 --- /dev/null +++ b/cmd/dump/main.go @@ -0,0 +1,53 @@ +package main + +import ( + "bitbucket.org/telesto/memdump" + "flag" + "fmt" + "os" +) + +func usage() { + fmt.Fprintln(os.Stderr, "usage: dump -p pid") +} + +func main() { + pid := flag.Int("pid", 0, "pid of the process to dump") + split := flag.Bool("s", false, "write one file for each memory region") + file := flag.String("o", "", "write regions into file") + + flag.Parse() + if *pid <= 0 { + usage() + os.Exit(0) + } + + maps, err := memdump.ParseMappings(*pid) + if err != nil { + fmt.Println(err) + os.Exit(1) + } + conn, err := memdump.Open(*pid) + if err != nil { + fmt.Fprintln(os.Stderr, err) + os.Exit(1) + } + for i := range maps { + if *split { + err = memdump.Dump(conn, maps[i].Start, maps[i].End) + if err != nil { + fmt.Fprintln(os.Stderr, err) + conn.Close() + os.Exit(1) + } + } + if len(*file) != 0 { + // todo: + } + } + if err := conn.Close(); err != nil { + fmt.Fprintln(os.Stderr, err) + os.Exit(1) + } + os.Exit(0) +} diff --git a/linux.go b/linux.go new file mode 100644 --- /dev/null +++ b/linux.go @@ -0,0 +1,34 @@ +package memdump + +import ( + "bytes" + "fmt" + "io/ioutil" + "os" +) + +const proc = "/proc" + +func openMemory(pid int) (*os.File, error) { + return os.Open(fmt.Sprintf("%s/%d/mem", proc, pid)) +} + +func ParseMappings(pid int) ([]MemoryRegion, error) { + c, err := ioutil.ReadFile(fmt.Sprintf("%s/%d/maps", proc, pid)) + if err != nil { + return nil, err + } + maps := []MemoryRegion{} + for _, line := range bytes.Split(bytes.TrimSpace(c), []byte{'\n'}) { + var r MemoryRegion + n, err := fmt.Sscanf(string(line), "%x-%x", &r.Start, &r.End) + if err != nil { + return nil, err + } + if n != 2 { + return nil, fmt.Errorf("needed %d values for scanning, found %d", 2, n) + } + maps = append(maps, r) + } + return maps, nil +} diff --git a/memdump.go b/memdump.go new file mode 100644 --- /dev/null +++ b/memdump.go @@ -0,0 +1,61 @@ +package memdump + +import ( + "fmt" + "io" + "os" + "syscall" +) + +type MemoryRegion struct { + Start, End int64 +} + +type Connection interface { + io.ReaderAt + io.Closer +} + +type connection struct { + pid int + *os.File +} + +func (c *connection) Close() error { + err := c.File.Close() + err1 := syscall.PtraceDetach(c.pid) + if err1 != nil { + err = err1 + } + return err +} + +func Open(pid int) (Connection, error) { + var err error + if err = syscall.PtraceAttach(pid); err != nil { + return nil, err + } + if pid, err = syscall.Wait4(pid, nil, 0, nil); err != nil { + return nil, err + } + conn, err := openMemory(pid) + if err != nil { + syscall.PtraceDetach(pid) + return nil, err + } + return &connection{pid, conn}, nil +} + +func Dump(conn Connection, start, end int64) error { + dump, err := os.Create(fmt.Sprintf("%x-%x.bin", start, end)) + defer dump.Close() + if err != nil { + return err + } + written, err := io.Copy(dump, io.NewSectionReader(conn, start, end)) + if written != end-start { + return fmt.Errorf("written %d bytes, expected %d bytes to copy", + written, end-start) + } + return err +}