mirror of https://github.com/beego/bee.git
687 lines
20 KiB
Go
687 lines
20 KiB
Go
package minidump
|
|
|
|
// Package minidump provides a loader for Windows Minidump files.
|
|
// Minidump files are the Windows equivalent of unix core dumps.
|
|
// They can be created by the kernel when a program crashes (however this is
|
|
// disabled for Go programs) or programmatically using either WinDbg or the
|
|
// ProcDump utility.
|
|
//
|
|
// The file format is described on MSDN starting at:
|
|
// https://docs.microsoft.com/en-us/windows/desktop/api/minidumpapiset/ns-minidumpapiset-_minidump_header
|
|
// which is the structure found at offset 0 on a minidump file.
|
|
//
|
|
// Further information on the format can be found reading
|
|
// chromium-breakpad's minidump loading code, specifically:
|
|
// https://chromium.googlesource.com/breakpad/breakpad/+/master/src/google_breakpad/common/minidump_cpu_amd64.h
|
|
// and:
|
|
// https://chromium.googlesource.com/breakpad/breakpad/+/master/src/google_breakpad/common/minidump_format.h
|
|
|
|
import (
|
|
"encoding/binary"
|
|
"fmt"
|
|
"io"
|
|
"io/ioutil"
|
|
"unicode/utf16"
|
|
"unsafe"
|
|
|
|
"github.com/go-delve/delve/pkg/proc/winutil"
|
|
)
|
|
|
|
type minidumpBuf struct {
|
|
buf []byte
|
|
kind string
|
|
off int
|
|
err error
|
|
ctx string
|
|
}
|
|
|
|
func (buf *minidumpBuf) u16() uint16 {
|
|
const stride = 2
|
|
if buf.err != nil {
|
|
return 0
|
|
}
|
|
if buf.off+stride >= len(buf.buf) {
|
|
buf.err = fmt.Errorf("minidump %s truncated at offset %#x while %s", buf.kind, buf.off, buf.ctx)
|
|
}
|
|
r := binary.LittleEndian.Uint16(buf.buf[buf.off : buf.off+stride])
|
|
buf.off += stride
|
|
return r
|
|
}
|
|
|
|
func (buf *minidumpBuf) u32() uint32 {
|
|
const stride = 4
|
|
if buf.err != nil {
|
|
return 0
|
|
}
|
|
if buf.off+stride >= len(buf.buf) {
|
|
buf.err = fmt.Errorf("minidump %s truncated at offset %#x while %s", buf.kind, buf.off, buf.ctx)
|
|
}
|
|
r := binary.LittleEndian.Uint32(buf.buf[buf.off : buf.off+stride])
|
|
buf.off += stride
|
|
return r
|
|
}
|
|
|
|
func (buf *minidumpBuf) u64() uint64 {
|
|
const stride = 8
|
|
if buf.err != nil {
|
|
return 0
|
|
}
|
|
if buf.off+stride >= len(buf.buf) {
|
|
buf.err = fmt.Errorf("minidump %s truncated at offset %#x while %s", buf.kind, buf.off, buf.ctx)
|
|
}
|
|
r := binary.LittleEndian.Uint64(buf.buf[buf.off : buf.off+stride])
|
|
buf.off += stride
|
|
return r
|
|
}
|
|
|
|
func streamBuf(stream *Stream, buf *minidumpBuf, name string) *minidumpBuf {
|
|
return &minidumpBuf{
|
|
buf: buf.buf,
|
|
kind: "stream",
|
|
off: stream.Offset,
|
|
err: nil,
|
|
ctx: fmt.Sprintf("reading %s stream at %#x", name, stream.Offset),
|
|
}
|
|
}
|
|
|
|
// ErrNotAMinidump is the error returned when the file being loaded is not a
|
|
// minidump file.
|
|
type ErrNotAMinidump struct {
|
|
what string
|
|
got uint32
|
|
}
|
|
|
|
func (err ErrNotAMinidump) Error() string {
|
|
return fmt.Sprintf("not a minidump, invalid %s %#x", err.what, err.got)
|
|
}
|
|
|
|
const (
|
|
minidumpSignature = 0x504d444d // 'MDMP'
|
|
minidumpVersion = 0xa793
|
|
)
|
|
|
|
// Minidump represents a minidump file
|
|
type Minidump struct {
|
|
Timestamp uint32
|
|
Flags FileFlags
|
|
|
|
Streams []Stream
|
|
|
|
Threads []Thread
|
|
Modules []Module
|
|
|
|
Pid uint32
|
|
|
|
MemoryRanges []MemoryRange
|
|
MemoryInfo []MemoryInfo
|
|
|
|
streamNum uint32
|
|
streamOff uint32
|
|
}
|
|
|
|
// Stream represents one (uninterpreted) stream in a minidump file.
|
|
// See: https://docs.microsoft.com/en-us/windows/desktop/api/minidumpapiset/ns-minidumpapiset-_minidump_directory
|
|
type Stream struct {
|
|
Type StreamType
|
|
Offset int
|
|
RawData []byte
|
|
}
|
|
|
|
// Thread represents an entry in the ThreadList stream.
|
|
// See: https://docs.microsoft.com/en-us/windows/desktop/api/minidumpapiset/ns-minidumpapiset-_minidump_thread
|
|
type Thread struct {
|
|
ID uint32
|
|
SuspendCount uint32
|
|
PriorityClass uint32
|
|
Priority uint32
|
|
TEB uint64
|
|
Context winutil.CONTEXT
|
|
}
|
|
|
|
// Module represents an entry in the ModuleList stream.
|
|
// See: https://docs.microsoft.com/en-us/windows/desktop/api/minidumpapiset/ns-minidumpapiset-_minidump_module
|
|
type Module struct {
|
|
BaseOfImage uint64
|
|
SizeOfImage uint32
|
|
Checksum uint32
|
|
TimeDateStamp uint32
|
|
Name string
|
|
VersionInfo VSFixedFileInfo
|
|
|
|
// CVRecord stores a CodeView record and is populated when a module's debug information resides in a PDB file. It identifies the PDB file.
|
|
CVRecord []byte
|
|
|
|
// MiscRecord is populated when a module's debug information resides in a DBG file. It identifies the DBG file. This field is effectively obsolete with modules built by recent toolchains.
|
|
MiscRecord []byte
|
|
}
|
|
|
|
// VSFixedFileInfo: Visual Studio Fixed File Info.
|
|
// See: https://docs.microsoft.com/en-us/windows/desktop/api/verrsrc/ns-verrsrc-tagvs_fixedfileinfo
|
|
type VSFixedFileInfo struct {
|
|
Signature uint32
|
|
StructVersion uint32
|
|
FileVersionHi uint32
|
|
FileVersionLo uint32
|
|
ProductVersionHi uint32
|
|
ProductVersionLo uint32
|
|
FileFlagsMask uint32
|
|
FileFlags uint32
|
|
FileOS uint32
|
|
FileType uint32
|
|
FileSubtype uint32
|
|
FileDateHi uint32
|
|
FileDateLo uint32
|
|
}
|
|
|
|
// MemoryRange represents a region of memory saved to the core file, it's constructed after either:
|
|
// 1. parsing an entry in the Memory64List stream.
|
|
// 2. parsing the stack field of an entry in the ThreadList stream.
|
|
type MemoryRange struct {
|
|
Addr uint64
|
|
Data []byte
|
|
}
|
|
|
|
// ReadMemory reads len(buf) bytes of memory starting at addr into buf from this memory region.
|
|
func (m *MemoryRange) ReadMemory(buf []byte, addr uintptr) (int, error) {
|
|
if len(buf) == 0 {
|
|
return 0, nil
|
|
}
|
|
if (uint64(addr) < m.Addr) || (uint64(addr)+uint64(len(buf)) > m.Addr+uint64(len(m.Data))) {
|
|
return 0, io.EOF
|
|
}
|
|
copy(buf, m.Data[uint64(addr)-m.Addr:])
|
|
return len(buf), nil
|
|
}
|
|
|
|
// MemoryInfo reprents an entry in the MemoryInfoList stream.
|
|
// See: https://docs.microsoft.com/en-us/windows/desktop/api/minidumpapiset/ns-minidumpapiset-_minidump_memory_info
|
|
type MemoryInfo struct {
|
|
Addr uint64
|
|
Size uint64
|
|
State MemoryState
|
|
Protection MemoryProtection
|
|
Type MemoryType
|
|
}
|
|
|
|
//go:generate stringer -type FileFlags,StreamType,Arch,MemoryState,MemoryType,MemoryProtection
|
|
|
|
// MemoryState is the type of the State field of MINIDUMP_MEMORY_INFO
|
|
type MemoryState uint32
|
|
|
|
const (
|
|
MemoryStateCommit MemoryState = 0x1000
|
|
MemoryStateReserve MemoryState = 0x2000
|
|
MemoryStateFree MemoryState = 0x10000
|
|
)
|
|
|
|
// MemoryType is the type of the Type field of MINIDUMP_MEMORY_INFO
|
|
type MemoryType uint32
|
|
|
|
const (
|
|
MemoryTypePrivate MemoryType = 0x20000
|
|
MemoryTypeMapped MemoryType = 0x40000
|
|
MemoryTypeImage MemoryType = 0x1000000
|
|
)
|
|
|
|
// MemoryProtection is the type of the Protection field of MINIDUMP_MEMORY_INFO
|
|
type MemoryProtection uint32
|
|
|
|
const (
|
|
MemoryProtectNoAccess MemoryProtection = 0x01 // PAGE_NOACCESS
|
|
MemoryProtectReadOnly MemoryProtection = 0x02 // PAGE_READONLY
|
|
MemoryProtectReadWrite MemoryProtection = 0x04 // PAGE_READWRITE
|
|
MemoryProtectWriteCopy MemoryProtection = 0x08 // PAGE_WRITECOPY
|
|
MemoryProtectExecute MemoryProtection = 0x10 // PAGE_EXECUTE
|
|
MemoryProtectExecuteRead MemoryProtection = 0x20 // PAGE_EXECUTE_READ
|
|
MemoryProtectExecuteReadWrite MemoryProtection = 0x40 // PAGE_EXECUTE_READWRITE
|
|
MemoryProtectExecuteWriteCopy MemoryProtection = 0x80 // PAGE_EXECUTE_WRITECOPY
|
|
// These options can be combined with the previous flags
|
|
MemoryProtectPageGuard MemoryProtection = 0x100 // PAGE_GUARD
|
|
MemoryProtectNoCache MemoryProtection = 0x200 // PAGE_NOCACHE
|
|
MemoryProtectWriteCombine MemoryProtection = 0x400 // PAGE_WRITECOMBINE
|
|
|
|
)
|
|
|
|
// FileFlags is the type of the Flags field of MINIDUMP_HEADER
|
|
type FileFlags uint64
|
|
|
|
const (
|
|
FileNormal FileFlags = 0x00000000
|
|
FileWithDataSegs FileFlags = 0x00000001
|
|
FileWithFullMemory FileFlags = 0x00000002
|
|
FileWithHandleData FileFlags = 0x00000004
|
|
FileFilterMemory FileFlags = 0x00000008
|
|
FileScanMemory FileFlags = 0x00000010
|
|
FileWithUnloadedModules FileFlags = 0x00000020
|
|
FileWithIncorrectlyReferencedMemory FileFlags = 0x00000040
|
|
FileFilterModulePaths FileFlags = 0x00000080
|
|
FileWithProcessThreadData FileFlags = 0x00000100
|
|
FileWithPrivateReadWriteMemory FileFlags = 0x00000200
|
|
FileWithoutOptionalData FileFlags = 0x00000400
|
|
FileWithFullMemoryInfo FileFlags = 0x00000800
|
|
FileWithThreadInfo FileFlags = 0x00001000
|
|
FileWithCodeSegs FileFlags = 0x00002000
|
|
FileWithoutAuxilliarySegs FileFlags = 0x00004000
|
|
FileWithFullAuxilliaryState FileFlags = 0x00008000
|
|
FileWithPrivateCopyMemory FileFlags = 0x00010000
|
|
FileIgnoreInaccessibleMemory FileFlags = 0x00020000
|
|
FileWithTokenInformation FileFlags = 0x00040000
|
|
)
|
|
|
|
// StreamType is the type of the StreamType field of MINIDUMP_DIRECTORY
|
|
type StreamType uint32
|
|
|
|
const (
|
|
UnusedStream StreamType = 0
|
|
ReservedStream0 StreamType = 1
|
|
ReservedStream1 StreamType = 2
|
|
ThreadListStream StreamType = 3
|
|
ModuleListStream StreamType = 4
|
|
MemoryListStream StreamType = 5
|
|
ExceptionStream StreamType = 6
|
|
SystemInfoStream StreamType = 7
|
|
ThreadExListStream StreamType = 8
|
|
Memory64ListStream StreamType = 9
|
|
CommentStreamA StreamType = 10
|
|
CommentStreamW StreamType = 11
|
|
HandleDataStream StreamType = 12
|
|
FunctionTableStream StreamType = 13
|
|
UnloadedModuleStream StreamType = 14
|
|
MiscInfoStream StreamType = 15
|
|
MemoryInfoListStream StreamType = 16
|
|
ThreadInfoListStream StreamType = 17
|
|
HandleOperationListStream StreamType = 18
|
|
TokenStream StreamType = 19
|
|
JavascriptDataStream StreamType = 20
|
|
SystemMemoryInfoStream StreamType = 21
|
|
ProcessVMCounterStream StreamType = 22
|
|
)
|
|
|
|
// Arch is the type of the ProcessorArchitecture field of MINIDUMP_SYSTEM_INFO.
|
|
type Arch uint16
|
|
|
|
const (
|
|
CpuArchitectureX86 Arch = 0
|
|
CpuArchitectureMips Arch = 1
|
|
CpuArchitectureAlpha Arch = 2
|
|
CpuArchitecturePPC Arch = 3
|
|
CpuArchitectureSHX Arch = 4 // Super-H
|
|
CpuArchitectureARM Arch = 5
|
|
CpuArchitectureIA64 Arch = 6
|
|
CpuArchitectureAlpha64 Arch = 7
|
|
CpuArchitectureMSIL Arch = 8 // Microsoft Intermediate Language
|
|
CpuArchitectureAMD64 Arch = 9
|
|
CpuArchitectureWoW64 Arch = 10
|
|
CpuArchitectureARM64 Arch = 12
|
|
CpuArchitectureUnknown Arch = 0xffff
|
|
)
|
|
|
|
// Open reads the minidump file at path and returns it as a Minidump structure.
|
|
func Open(path string, logfn func(fmt string, args ...interface{})) (*Minidump, error) {
|
|
rawbuf, err := ioutil.ReadFile(path) //TODO(aarzilli): mmap?
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
buf := &minidumpBuf{buf: rawbuf, kind: "file"}
|
|
|
|
var mdmp Minidump
|
|
|
|
readMinidumpHeader(&mdmp, buf)
|
|
if buf.err != nil {
|
|
return nil, buf.err
|
|
}
|
|
|
|
if logfn != nil {
|
|
logfn("Minidump Header\n")
|
|
logfn("Num Streams: %d\n", mdmp.streamNum)
|
|
logfn("Streams offset: %#x\n", mdmp.streamOff)
|
|
logfn("File flags: %s\n", fileFlagsToString(mdmp.Flags))
|
|
logfn("Offset after header %#x\n", buf.off)
|
|
}
|
|
|
|
readDirectory(&mdmp, buf)
|
|
if buf.err != nil {
|
|
return nil, buf.err
|
|
}
|
|
|
|
for i := range mdmp.Streams {
|
|
stream := &mdmp.Streams[i]
|
|
if stream.Type != SystemInfoStream {
|
|
continue
|
|
}
|
|
|
|
sb := streamBuf(stream, buf, "system info")
|
|
if buf.err != nil {
|
|
return nil, buf.err
|
|
}
|
|
|
|
arch := Arch(sb.u16())
|
|
|
|
if logfn != nil {
|
|
logfn("Found processor architecture %s\n", arch.String())
|
|
}
|
|
|
|
if arch != CpuArchitectureAMD64 {
|
|
return nil, fmt.Errorf("unsupported architecture %s", arch.String())
|
|
}
|
|
}
|
|
|
|
for i := range mdmp.Streams {
|
|
stream := &mdmp.Streams[i]
|
|
if logfn != nil {
|
|
logfn("Stream %d: type:%s off:%#x size:%#x\n", i, stream.Type, stream.Offset, len(stream.RawData))
|
|
}
|
|
switch stream.Type {
|
|
case ThreadListStream:
|
|
readThreadList(&mdmp, streamBuf(stream, buf, "thread list"))
|
|
if logfn != nil {
|
|
for i := range mdmp.Threads {
|
|
logfn("\tID:%#x TEB:%#x\n", mdmp.Threads[i].ID, mdmp.Threads[i].TEB)
|
|
}
|
|
}
|
|
case ModuleListStream:
|
|
readModuleList(&mdmp, streamBuf(stream, buf, "module list"))
|
|
if logfn != nil {
|
|
for i := range mdmp.Modules {
|
|
logfn("\tName:%q BaseOfImage:%#x SizeOfImage:%#x\n", mdmp.Modules[i].Name, mdmp.Modules[i].BaseOfImage, mdmp.Modules[i].SizeOfImage)
|
|
}
|
|
}
|
|
case ExceptionStream:
|
|
//TODO(aarzilli): this stream contains the exception that made the
|
|
//process stop and caused the minidump to be taken. If we ever start
|
|
//caring about this we should parse this.
|
|
case Memory64ListStream:
|
|
readMemory64List(&mdmp, streamBuf(stream, buf, "memory64 list"), logfn)
|
|
case MemoryInfoListStream:
|
|
readMemoryInfoList(&mdmp, streamBuf(stream, buf, "memory info list"), logfn)
|
|
case MiscInfoStream:
|
|
readMiscInfo(&mdmp, streamBuf(stream, buf, "misc info"))
|
|
if logfn != nil {
|
|
logfn("\tPid: %#x\n", mdmp.Pid)
|
|
}
|
|
case CommentStreamW:
|
|
if logfn != nil {
|
|
logfn("\t%q\n", decodeUTF16(stream.RawData))
|
|
}
|
|
case CommentStreamA:
|
|
if logfn != nil {
|
|
logfn("\t%s\n", string(stream.RawData))
|
|
}
|
|
}
|
|
if buf.err != nil {
|
|
return nil, buf.err
|
|
}
|
|
}
|
|
|
|
return &mdmp, nil
|
|
}
|
|
|
|
// decodeUTF16 converts a NUL-terminated UTF16LE string to (non NUL-terminated) UTF8.
|
|
func decodeUTF16(in []byte) string {
|
|
utf16encoded := []uint16{}
|
|
for i := 0; i+1 < len(in); i += 2 {
|
|
var ch uint16
|
|
ch = uint16(in[i]) + uint16(in[i+1])<<8
|
|
utf16encoded = append(utf16encoded, ch)
|
|
}
|
|
s := string(utf16.Decode(utf16encoded))
|
|
if len(s) > 0 && s[len(s)-1] == 0 {
|
|
s = s[:len(s)-1]
|
|
}
|
|
return s
|
|
}
|
|
|
|
func fileFlagsToString(flags FileFlags) string {
|
|
out := []byte{}
|
|
for i, name := range _FileFlags_map {
|
|
if i == 0 {
|
|
continue
|
|
}
|
|
if flags&i != 0 {
|
|
if len(out) > 0 {
|
|
out = append(out, '|')
|
|
}
|
|
out = append(out, name...)
|
|
}
|
|
}
|
|
if len(out) == 0 {
|
|
return flags.String()
|
|
}
|
|
return string(out)
|
|
}
|
|
|
|
// readMinidumpHeader reads the minidump file header
|
|
func readMinidumpHeader(mdmp *Minidump, buf *minidumpBuf) {
|
|
buf.ctx = "reading minidump header"
|
|
|
|
if sig := buf.u32(); sig != minidumpSignature {
|
|
buf.err = ErrNotAMinidump{"signature", sig}
|
|
return
|
|
}
|
|
|
|
if ver := buf.u16(); ver != minidumpVersion {
|
|
buf.err = ErrNotAMinidump{"version", uint32(ver)}
|
|
return
|
|
}
|
|
|
|
buf.u16() // implementation specific version
|
|
mdmp.streamNum = buf.u32()
|
|
mdmp.streamOff = buf.u32()
|
|
buf.u32() // checksum, but it's always 0
|
|
mdmp.Timestamp = buf.u32()
|
|
mdmp.Flags = FileFlags(buf.u64())
|
|
}
|
|
|
|
// readDirectory reads the list of streams (i.e. the minidum "directory")
|
|
func readDirectory(mdmp *Minidump, buf *minidumpBuf) {
|
|
buf.off = int(mdmp.streamOff)
|
|
|
|
mdmp.Streams = make([]Stream, mdmp.streamNum)
|
|
for i := range mdmp.Streams {
|
|
buf.ctx = fmt.Sprintf("reading stream directory entry %d", i)
|
|
stream := &mdmp.Streams[i]
|
|
stream.Type = StreamType(buf.u32())
|
|
stream.Offset, stream.RawData = readLocationDescriptor(buf)
|
|
if buf.err != nil {
|
|
return
|
|
}
|
|
}
|
|
}
|
|
|
|
// readLocationDescriptor reads a location descriptor structure (a structure
|
|
// which describes a subregion of the file), and returns the destination
|
|
// offset and a slice into the minidump file's buffer.
|
|
func readLocationDescriptor(buf *minidumpBuf) (off int, rawData []byte) {
|
|
sz := buf.u32()
|
|
off = int(buf.u32())
|
|
if buf.err != nil {
|
|
return off, nil
|
|
}
|
|
end := off + int(sz)
|
|
if off >= len(buf.buf) || end > len(buf.buf) {
|
|
buf.err = fmt.Errorf("location starting at %#x of size %#x is past the end of file, while %s", off, sz, buf.ctx)
|
|
return 0, nil
|
|
}
|
|
rawData = buf.buf[off:end]
|
|
return
|
|
}
|
|
|
|
func readString(buf *minidumpBuf) string {
|
|
startOff := buf.off
|
|
sz := buf.u32()
|
|
if buf.err != nil {
|
|
return ""
|
|
}
|
|
end := buf.off + int(sz)
|
|
if buf.off >= len(buf.buf) || end > len(buf.buf) {
|
|
buf.err = fmt.Errorf("string starting at %#x of size %#x is past the end of file, while %s", startOff, sz, buf.ctx)
|
|
return ""
|
|
}
|
|
return decodeUTF16(buf.buf[buf.off:end])
|
|
}
|
|
|
|
// readThreadList reads a thread list stream and adds the threads to the minidump.
|
|
func readThreadList(mdmp *Minidump, buf *minidumpBuf) {
|
|
threadNum := buf.u32()
|
|
if buf.err != nil {
|
|
return
|
|
}
|
|
|
|
mdmp.Threads = make([]Thread, threadNum)
|
|
|
|
for i := range mdmp.Threads {
|
|
buf.ctx = fmt.Sprintf("reading thread list entry %d", i)
|
|
thread := &mdmp.Threads[i]
|
|
|
|
thread.ID = buf.u32()
|
|
thread.SuspendCount = buf.u32()
|
|
thread.PriorityClass = buf.u32()
|
|
thread.Priority = buf.u32()
|
|
thread.TEB = buf.u64()
|
|
if buf.err != nil {
|
|
return
|
|
}
|
|
|
|
readMemoryDescriptor(mdmp, buf) // thread stack
|
|
_, rawThreadContext := readLocationDescriptor(buf) // thread context
|
|
thread.Context = *((*winutil.CONTEXT)(unsafe.Pointer(&rawThreadContext[0])))
|
|
if buf.err != nil {
|
|
return
|
|
}
|
|
}
|
|
}
|
|
|
|
// readModuleList reads a module list stream and adds the modules to the minidump.
|
|
func readModuleList(mdmp *Minidump, buf *minidumpBuf) {
|
|
moduleNum := buf.u32()
|
|
if buf.err != nil {
|
|
return
|
|
}
|
|
|
|
mdmp.Modules = make([]Module, moduleNum)
|
|
|
|
for i := range mdmp.Modules {
|
|
buf.ctx = fmt.Sprintf("reading module list entry %d", i)
|
|
module := &mdmp.Modules[i]
|
|
|
|
module.BaseOfImage = buf.u64()
|
|
module.SizeOfImage = buf.u32()
|
|
module.Checksum = buf.u32()
|
|
module.TimeDateStamp = buf.u32()
|
|
nameOff := int(buf.u32())
|
|
|
|
versionInfoVec := make([]uint32, unsafe.Sizeof(VSFixedFileInfo{})/unsafe.Sizeof(uint32(0)))
|
|
for j := range versionInfoVec {
|
|
versionInfoVec[j] = buf.u32()
|
|
}
|
|
|
|
module.VersionInfo = *(*VSFixedFileInfo)(unsafe.Pointer(&versionInfoVec[0]))
|
|
|
|
_, module.CVRecord = readLocationDescriptor(buf)
|
|
_, module.MiscRecord = readLocationDescriptor(buf)
|
|
|
|
if buf.err != nil {
|
|
return
|
|
}
|
|
|
|
nameBuf := minidumpBuf{buf: buf.buf, kind: "file", off: nameOff, err: nil, ctx: buf.ctx}
|
|
module.Name = readString(&nameBuf)
|
|
if nameBuf.err != nil {
|
|
buf.err = nameBuf.err
|
|
return
|
|
}
|
|
}
|
|
}
|
|
|
|
// readMemory64List reads a _MINIDUMP_MEMORY64_LIST structure, containing
|
|
// the description of the process memory.
|
|
// See: https://docs.microsoft.com/en-us/windows/desktop/api/minidumpapiset/ns-minidumpapiset-_minidump_memory64_list
|
|
// And: https://docs.microsoft.com/en-us/windows/desktop/api/minidumpapiset/ns-minidumpapiset-_minidump_memory_descriptor
|
|
func readMemory64List(mdmp *Minidump, buf *minidumpBuf, logfn func(fmt string, args ...interface{})) {
|
|
rangesNum := buf.u64()
|
|
baseOff := int(buf.u64())
|
|
if buf.err != nil {
|
|
return
|
|
}
|
|
|
|
for i := uint64(0); i < rangesNum; i++ {
|
|
addr := buf.u64()
|
|
sz := buf.u64()
|
|
|
|
end := baseOff + int(sz)
|
|
if baseOff >= len(buf.buf) || end > len(buf.buf) {
|
|
buf.err = fmt.Errorf("memory range at %#x of size %#x is past the end of file, while %s", baseOff, sz, buf.ctx)
|
|
return
|
|
}
|
|
|
|
mdmp.addMemory(addr, buf.buf[baseOff:end])
|
|
|
|
if logfn != nil {
|
|
logfn("\tMemory %d addr:%#x size:%#x FileOffset:%#x\n", i, addr, sz, baseOff)
|
|
}
|
|
|
|
baseOff = end
|
|
}
|
|
}
|
|
|
|
func readMemoryInfoList(mdmp *Minidump, buf *minidumpBuf, logfn func(fmt string, args ...interface{})) {
|
|
startOff := buf.off
|
|
sizeOfHeader := int(buf.u32())
|
|
sizeOfEntry := int(buf.u32())
|
|
numEntries := buf.u64()
|
|
|
|
buf.off = startOff + sizeOfHeader
|
|
|
|
mdmp.MemoryInfo = make([]MemoryInfo, numEntries)
|
|
|
|
for i := range mdmp.MemoryInfo {
|
|
memInfo := &mdmp.MemoryInfo[i]
|
|
startOff := buf.off
|
|
|
|
memInfo.Addr = buf.u64()
|
|
buf.u64() // allocation_base
|
|
|
|
buf.u32() // allocation_protection
|
|
buf.u32() // alignment
|
|
|
|
memInfo.Size = buf.u64()
|
|
|
|
memInfo.State = MemoryState(buf.u32())
|
|
memInfo.Protection = MemoryProtection(buf.u32())
|
|
memInfo.Type = MemoryType(buf.u32())
|
|
|
|
if logfn != nil {
|
|
logfn("\tMemoryInfo %d Addr:%#x Size:%#x %s %s %s\n", i, memInfo.Addr, memInfo.Size, memInfo.State, memInfo.Protection, memInfo.Type)
|
|
}
|
|
|
|
buf.off = startOff + sizeOfEntry
|
|
}
|
|
}
|
|
|
|
// readMiscInfo reads the process_id from a MiscInfo stream.
|
|
func readMiscInfo(mdmp *Minidump, buf *minidumpBuf) {
|
|
buf.u32() // size of info
|
|
buf.u32() // flags1
|
|
|
|
mdmp.Pid = buf.u32() // process_id
|
|
// there are more fields here, but we don't care about them
|
|
}
|
|
|
|
// readMemoryDescriptor reads a memory descriptor struct and adds it to the memory map of the minidump.
|
|
func readMemoryDescriptor(mdmp *Minidump, buf *minidumpBuf) {
|
|
addr := buf.u64()
|
|
if buf.err != nil {
|
|
return
|
|
}
|
|
_, rawData := readLocationDescriptor(buf)
|
|
if buf.err != nil {
|
|
return
|
|
}
|
|
mdmp.addMemory(addr, rawData)
|
|
}
|
|
|
|
func (mdmp *Minidump) addMemory(addr uint64, data []byte) {
|
|
mdmp.MemoryRanges = append(mdmp.MemoryRanges, MemoryRange{addr, data})
|
|
}
|