├── .gitattributes ├── taplo.toml ├── rust-toolchain.toml ├── .vscode └── settings.json ├── examples ├── Cargo.toml ├── memorys │ └── main.rs ├── threads │ └── main.rs ├── system │ └── main.rs ├── modules │ └── main.rs └── handles │ └── main.rs ├── src ├── lib.rs ├── error.rs ├── mapper.rs ├── data.rs └── parse.rs ├── rustfmt.toml ├── .gitignore ├── Justfile ├── .github └── workflows │ └── ci.yml ├── LICENSE-MIT ├── Cargo.toml ├── README.md ├── Cargo.lock └── LICENSE-APACHE /.gitattributes: -------------------------------------------------------------------------------- 1 | * text=auto eol=lf -------------------------------------------------------------------------------- /taplo.toml: -------------------------------------------------------------------------------- 1 | [formatting] 2 | crlf = true -------------------------------------------------------------------------------- /rust-toolchain.toml: -------------------------------------------------------------------------------- 1 | [toolchain] 2 | channel = "stable" 3 | targets = ["x86_64-pc-windows-msvc"] -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "rust-analyzer.linkedProjects": [ 3 | "Cargo.toml" 4 | ] 5 | } -------------------------------------------------------------------------------- /examples/Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | members = [ 3 | "handles", 4 | "memorys", 5 | "modules", 6 | "threads", 7 | ] -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | #![doc = include_str!("../README.md")] 2 | 3 | pub mod mapper; 4 | pub mod data; 5 | pub mod error; 6 | 7 | mod parse; 8 | pub use parse::*; 9 | -------------------------------------------------------------------------------- /examples/memorys/main.rs: -------------------------------------------------------------------------------- 1 | use userdmp::{error::UserDmpError, UserDump}; 2 | 3 | fn main() -> Result<(), UserDmpError> { 4 | let dmp = UserDump::new("C:\\Examples.dmp")?; 5 | 6 | for (_, memory) in dmp.memorys() { 7 | println!("Start: {}", memory.start_addr()); 8 | println!("End: {}", memory.end_addr()); 9 | println!("Data: {:?}", memory.data); 10 | // Access the other members ... 11 | } 12 | 13 | Ok(()) 14 | } 15 | -------------------------------------------------------------------------------- /examples/threads/main.rs: -------------------------------------------------------------------------------- 1 | use userdmp::{error::UserDmpError, UserDump}; 2 | 3 | fn main() -> Result<(), UserDmpError> { 4 | let dmp = UserDump::new("C:\\Examples.dmp")?; 5 | 6 | for (tid, thread) in dmp.threads().iter() { 7 | println!("[*] TID: {:?}", tid); 8 | println!("[*] TEB: {:?}", thread.teb); 9 | println!("[*] CONTEXT: {:#x?}", thread.context()); 10 | // Access the other members ... 11 | } 12 | 13 | Ok(()) 14 | } 15 | -------------------------------------------------------------------------------- /examples/system/main.rs: -------------------------------------------------------------------------------- 1 | use userdmp::{error::UserDmpError, UserDump}; 2 | 3 | fn main() -> Result<(), UserDmpError> { 4 | let dmp = UserDump::new("C:\\Examples.dmp")?; 5 | let system = dmp.system; 6 | 7 | println!("Number Of Processors: {}", system.number_of_processors); 8 | println!("Arch: {:?}", system.processor_architecture); 9 | println!("BuildNumber: {:?}", system.build_number); 10 | // Access the other members ... 11 | 12 | Ok(()) 13 | } 14 | -------------------------------------------------------------------------------- /examples/modules/main.rs: -------------------------------------------------------------------------------- 1 | use userdmp::{error::UserDmpError, UserDump}; 2 | 3 | fn main() -> Result<(), UserDmpError> { 4 | let dmp = UserDump::new("C:\\Examples.dmp")?; 5 | 6 | for (_, module) in dmp.modules().iter() { 7 | println!("[*] Path: {:?}", module.path); 8 | println!("[*] Range Address: {:?}", module.range); 9 | println!("[*] Checksum: {:?}", module.checksum); 10 | // Access the other members ... 11 | } 12 | 13 | Ok(()) 14 | } 15 | -------------------------------------------------------------------------------- /examples/handles/main.rs: -------------------------------------------------------------------------------- 1 | use userdmp::{error::UserDmpError, UserDump}; 2 | 3 | fn main() -> Result<(), UserDmpError> { 4 | let dmp = UserDump::new("C:\\Examples.dmp")?; 5 | 6 | for (_, handle) in dmp.handles() { 7 | println!("Handle: {}", handle.handle()); 8 | println!("Access: {}", handle.granted_access); 9 | println!("Type Name: {:?}", handle.type_name().unwrap_or("")); 10 | println!("Object Name: {:?}", handle.object_name().unwrap_or("")) 11 | } 12 | 13 | Ok(()) 14 | } 15 | -------------------------------------------------------------------------------- /rustfmt.toml: -------------------------------------------------------------------------------- 1 | # Maximum width of a line before rustfmt wraps it to the next line 2 | max_width = 150 3 | 4 | # Maximum width allowed for a function call before its arguments are wrapped across multiple lines 5 | fn_call_width = 100 6 | 7 | # Maximum width for method chains (e.g., .map().filter()) before rustfmt breaks them into multiple lines 8 | chain_width = 40 9 | 10 | # Prevents rustfmt from automatically reordering `use` statements 11 | # Keeps imports in the order you wrote them 12 | reorder_imports = false 13 | 14 | # Automatically wraps and formats long comments to respect `max_width` 15 | normalize_comments = true 16 | 17 | # Reorders items within `impl` blocks (e.g., consts, types, and functions) for consistency 18 | reorder_impl_items = true 19 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Generated by Cargo 2 | # will have compiled files and executables 3 | debug/ 4 | target/ 5 | 6 | # Remove Cargo.lock from gitignore if creating an executable, leave it for libraries 7 | # More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html 8 | examples/Cargo.lock 9 | 10 | # These are backup files generated by rustfmt 11 | **/*.rs.bk 12 | 13 | # MSVC Windows builds of rustc generate these, which store debugging information 14 | *.pdb 15 | 16 | # RustRover 17 | # JetBrains specific template is maintained in a separate JetBrains.gitignore that can 18 | # be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore 19 | # and can be added to the global gitignore or merged into this file. For a more nuclear 20 | # option (not recommended) you can uncomment the following to ignore the entire idea folder. 21 | #.idea/ -------------------------------------------------------------------------------- /Justfile: -------------------------------------------------------------------------------- 1 | # Aliases 2 | alias c := clean 3 | alias up := update 4 | 5 | # Use PowerShell shell on Windows 6 | set windows-shell := ["powershell.exe", "-NoLogo", "-Command"] 7 | 8 | # Clean target 9 | clean: 10 | cargo clean 11 | 12 | # Updates dependencies as per Cargo.toml 13 | update: 14 | cargo update 15 | 16 | # Publishes the crate to crates.io 17 | publish: 18 | cargo publish --allow-dirty 19 | 20 | # Formats all Rust source files 21 | fmt: 22 | cargo +nightly fmt --check 23 | 24 | # Builds local documentation 25 | docs: 26 | cargo doc --no-deps --open 27 | 28 | # Run only integration tests in /tests directory 29 | test: 30 | cargo test --test '*' -- --nocapture 31 | 32 | # Run a specific example 33 | example name: 34 | cargo run --example {{name}} 35 | 36 | # Test this workflow on your machine with `act` 37 | act: 38 | act -P windows-latest=-self-hosted -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: userdmp-ci 2 | 3 | on: [push, pull_request] 4 | 5 | jobs: 6 | clippy: 7 | name: Clippy Lint Check 8 | runs-on: windows-latest 9 | 10 | steps: 11 | - uses: actions/checkout@v4 12 | 13 | - name: Set up Rust 14 | run: | 15 | rustup default stable 16 | rustup component add clippy 17 | 18 | - name: Run Clippy 19 | run: cargo clippy 20 | 21 | build: 22 | name: Build Examples 23 | runs-on: windows-latest 24 | 25 | strategy: 26 | matrix: 27 | example: [handles, memorys, modules, system, threads] 28 | 29 | steps: 30 | - uses: actions/checkout@v4 31 | 32 | - name: Set up Rust 33 | run: rustup default stable 34 | 35 | - name: Build example ${{ matrix.example }} 36 | run: cargo build --release --example ${{ matrix.example }} 37 | 38 | - name: Upload ${{ matrix.example }}.exe 39 | uses: actions/upload-artifact@v4 40 | with: 41 | name: ${{ matrix.example }}.exe 42 | path: target/release/examples/${{ matrix.example }}.exe -------------------------------------------------------------------------------- /LICENSE-MIT: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 joaoviictorti 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "userdmp" 3 | version = "0.1.4" 4 | edition = "2024" 5 | description = "A library in Rust for parsing Minidump (.dmp) files generated in user mode on Windows" 6 | license = "MIT OR Apache-2.0" 7 | repository = "https://github.com/joaoviictorti/userdmp" 8 | homepage = "https://github.com/joaoviictorti/userdmp" 9 | documentation = "https://docs.rs/userdmp/latest" 10 | readme = "README.md" 11 | keywords = ["user","minidump", "windows", "rust"] 12 | categories = ["os", "filesystem"] 13 | include = [ 14 | "src/**", 15 | "Cargo.toml", 16 | "README.md", 17 | "LICENSE", 18 | ] 19 | 20 | [dependencies] 21 | binrw = "0.15.0" 22 | bytemuck = "1.21.0" 23 | thiserror = "2.0.9" 24 | 25 | [target.'cfg(windows)'.dependencies] 26 | windows-sys = { version = "0.59.0", features = ["Win32_Security", "Win32_System_Memory"] } 27 | 28 | [target.'cfg(unix)'.dependencies] 29 | libc = "0.2.169" 30 | 31 | [[example]] 32 | name = "handles" 33 | path = "examples/handles/main.rs" 34 | 35 | [[example]] 36 | name = "memorys" 37 | path = "examples/memorys/main.rs" 38 | 39 | [[example]] 40 | name = "modules" 41 | path = "examples/modules/main.rs" 42 | 43 | [[example]] 44 | name = "system" 45 | path = "examples/system/main.rs" 46 | 47 | [[example]] 48 | name = "threads" 49 | path = "examples/threads/main.rs" -------------------------------------------------------------------------------- /src/error.rs: -------------------------------------------------------------------------------- 1 | //! The module defines error types used throughout the library. 2 | 3 | use binrw::Error as BinrwError; 4 | use thiserror::Error; 5 | 6 | /// Represents errors that may occur during the processing of a minidump file. 7 | #[derive(Debug, Error)] 8 | pub enum UserDmpError { 9 | /// Raised when the application fails to open a file. 10 | #[error("Failed to open file: {0}")] 11 | FileOpenError(#[from] std::io::Error), 12 | 13 | /// Raised when the minidump contains an invalid signature. 14 | #[error("Invalid minidump signature.")] 15 | InvalidSignature, 16 | 17 | /// Raised when the minidump contains invalid or unsupported flags. 18 | #[error("The minidump contains invalid or unsupported flags: {0:#x}")] 19 | InvalidFlags(u64), 20 | 21 | /// Raised when the minidump specifies an unsupported architecture. 22 | #[error("Unsupported architecture: {0}")] 23 | UnsupportedArchitecture(u16), 24 | 25 | /// Raised when the application fails to parse the system information in the minidump. 26 | #[error("Failed to parse system info: {0}")] 27 | ParseSystemInfoError(std::io::Error), 28 | 29 | /// Raised when the application fails to parse the module list in the minidump. 30 | #[error("Failed to parse module list: {0}")] 31 | ParseModuleListError(std::io::Error), 32 | 33 | /// Raised when the minidump contains a module with an invalid memory range. 34 | #[error("Invalid memory range in module.")] 35 | InvalidMemoryRange, 36 | 37 | /// Raised when the application fails to create a file mapping for the minidump. 38 | #[error("Failed to create file mapping.")] 39 | CreateFileMappingError, 40 | 41 | /// Raised when the application fails to map a view of the minidump file (Windows). 42 | #[error("Failed to map view of file.")] 43 | MapViewOfFileError, 44 | 45 | /// Raised when the application fails to map a view of the minidump file (Unix). 46 | #[error("Failed to map view of file.")] 47 | MmapError, 48 | 49 | /// Raised when a parsing error occurs in the `binrw` library. 50 | #[error("Parsing error: {0}")] 51 | BinrwError(#[from] BinrwError), 52 | 53 | /// Raised when an address cannot be found in the `Memory64ListStream`. 54 | #[error("Address {0:#x?} was not found in Memory64ListStream")] 55 | AddressNotFound(u64), 56 | 57 | /// Raised when the context is invalid. 58 | #[error("Invalid context")] 59 | InvalidContext, 60 | } 61 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # userdmp 🦀 2 | 3 | ![Rust](https://img.shields.io/badge/made%20with-Rust-red) 4 | ![crate](https://img.shields.io/crates/v/userdmp.svg) 5 | ![docs](https://docs.rs/userdmp/badge.svg) 6 | ![License](https://img.shields.io/badge/license-MIT%2FApache--2.0-brightgreen) 7 | [![Actions status](https://github.com/joaoviictorti/userdmp/actions/workflows/ci.yml/badge.svg)](https://github.com/joaoviictorti/userdmp/actions) 8 | 9 | `userdmp` is library in Rust for parsing Minidump (.dmp) files generated in user mode on Windows 10 | 11 | ## Features 12 | 13 | - ✅ **Module List Stream (`ModuleListStream`)**: Contains information about all loaded modules (e.g., DLLs), including their file paths, base addresses, and sizes. 14 | - ✅ **Handle Data Stream (`HandleDataStream`)**: Captures details about open handles in the process, such as references to files, threads, and synchronization objects. 15 | - ✅ **System Info Stream (`SystemInfoStream`)**: Includes metadata about the operating system (e.g., version, build number) and hardware (e.g., CPU type and number of processors). 16 | - ✅ **Exception Stream (`ExceptionStream`)**: Records details about the exception that triggered the dump, including the exception code, address, and relevant parameters. 17 | - ✅ **Memory Stream (`MemoryListStream / MemoryInfoListStream`)**: Provides a list of memory regions that were included in the dump, allowing analysis of process memory contents at the time of the crash. 18 | 19 | ## Getting started 20 | 21 | Add `userdmp` to your project by updating your `Cargo.toml`: 22 | ```bash 23 | cargo add userdmp 24 | ``` 25 | 26 | ## Usage 27 | 28 | The userdmp library provides tools to parse and analyze Minidump (.dmp) files generated in user mode on Windows. Here's how you can use it: 29 | 30 | ### Parsing a Minidump File 31 | 32 | To start working with a Minidump file, use the `UserDump::new` function to parse the file and create a `UserDump` instance: 33 | ```rust, ignore 34 | use userdmp::{UserDump, UserDmpError}; 35 | 36 | fn main() -> Result<(), UserDmpError> { 37 | // Parse the Minidump file 38 | let dump = UserDump::new("example.dmp")?; 39 | println!("Minidump parsed successfully!"); 40 | 41 | Ok(()) 42 | } 43 | ``` 44 | 45 | ## Additional Resources 46 | 47 | For more examples, check the [examples](https://github.com/joaoviictorti/userdmp/tree/main/examples) folder in the repository. 48 | 49 | ## License 50 | 51 | userdmp is licensed under either of 52 | 53 | - Apache License, Version 2.0, ([LICENSE-APACHE](https://github.com/joaoviictorti/userdmp/tree/main/LICENSE-APACHE) or 54 | ) 55 | - MIT license ([LICENSE-MIT](https://github.com/joaoviictorti/userdmp/tree/main/LICENSE-MIT) or ) 56 | 57 | at your option. 58 | 59 | Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in userdmp 60 | by you, as defined in the Apache-2.0 license, shall be dually licensed as above, without any 61 | additional terms or conditions. -------------------------------------------------------------------------------- /Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 4 4 | 5 | [[package]] 6 | name = "array-init" 7 | version = "2.1.0" 8 | source = "registry+https://github.com/rust-lang/crates.io-index" 9 | checksum = "3d62b7694a562cdf5a74227903507c56ab2cc8bdd1f781ed5cb4cf9c9f810bfc" 10 | 11 | [[package]] 12 | name = "binrw" 13 | version = "0.15.0" 14 | source = "registry+https://github.com/rust-lang/crates.io-index" 15 | checksum = "81419ff39e6ed10a92a7f125290859776ced35d9a08a665ae40b23e7ca702f30" 16 | dependencies = [ 17 | "array-init", 18 | "binrw_derive", 19 | "bytemuck", 20 | ] 21 | 22 | [[package]] 23 | name = "binrw_derive" 24 | version = "0.15.0" 25 | source = "registry+https://github.com/rust-lang/crates.io-index" 26 | checksum = "376404e55ec40d0d6f8b4b7df3f87b87954bd987f0cf9a7207ea3b6ea5c9add4" 27 | dependencies = [ 28 | "either", 29 | "owo-colors", 30 | "proc-macro2", 31 | "quote", 32 | "syn", 33 | ] 34 | 35 | [[package]] 36 | name = "bytemuck" 37 | version = "1.21.0" 38 | source = "registry+https://github.com/rust-lang/crates.io-index" 39 | checksum = "ef657dfab802224e671f5818e9a4935f9b1957ed18e58292690cc39e7a4092a3" 40 | 41 | [[package]] 42 | name = "either" 43 | version = "1.13.0" 44 | source = "registry+https://github.com/rust-lang/crates.io-index" 45 | checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0" 46 | 47 | [[package]] 48 | name = "libc" 49 | version = "0.2.169" 50 | source = "registry+https://github.com/rust-lang/crates.io-index" 51 | checksum = "b5aba8db14291edd000dfcc4d620c7ebfb122c613afb886ca8803fa4e128a20a" 52 | 53 | [[package]] 54 | name = "owo-colors" 55 | version = "4.2.2" 56 | source = "registry+https://github.com/rust-lang/crates.io-index" 57 | checksum = "48dd4f4a2c8405440fd0462561f0e5806bd0f77e86f51c761481bdd4018b545e" 58 | 59 | [[package]] 60 | name = "proc-macro2" 61 | version = "1.0.92" 62 | source = "registry+https://github.com/rust-lang/crates.io-index" 63 | checksum = "37d3544b3f2748c54e147655edb5025752e2303145b5aefb3c3ea2c78b973bb0" 64 | dependencies = [ 65 | "unicode-ident", 66 | ] 67 | 68 | [[package]] 69 | name = "quote" 70 | version = "1.0.38" 71 | source = "registry+https://github.com/rust-lang/crates.io-index" 72 | checksum = "0e4dccaaaf89514f546c693ddc140f729f958c247918a13380cccc6078391acc" 73 | dependencies = [ 74 | "proc-macro2", 75 | ] 76 | 77 | [[package]] 78 | name = "syn" 79 | version = "2.0.94" 80 | source = "registry+https://github.com/rust-lang/crates.io-index" 81 | checksum = "987bc0be1cdea8b10216bd06e2ca407d40b9543468fafd3ddfb02f36e77f71f3" 82 | dependencies = [ 83 | "proc-macro2", 84 | "quote", 85 | "unicode-ident", 86 | ] 87 | 88 | [[package]] 89 | name = "thiserror" 90 | version = "2.0.9" 91 | source = "registry+https://github.com/rust-lang/crates.io-index" 92 | checksum = "f072643fd0190df67a8bab670c20ef5d8737177d6ac6b2e9a236cb096206b2cc" 93 | dependencies = [ 94 | "thiserror-impl", 95 | ] 96 | 97 | [[package]] 98 | name = "thiserror-impl" 99 | version = "2.0.9" 100 | source = "registry+https://github.com/rust-lang/crates.io-index" 101 | checksum = "7b50fa271071aae2e6ee85f842e2e28ba8cd2c5fb67f11fcb1fd70b276f9e7d4" 102 | dependencies = [ 103 | "proc-macro2", 104 | "quote", 105 | "syn", 106 | ] 107 | 108 | [[package]] 109 | name = "unicode-ident" 110 | version = "1.0.14" 111 | source = "registry+https://github.com/rust-lang/crates.io-index" 112 | checksum = "adb9e6ca4f869e1180728b7950e35922a7fc6397f7b641499e8f3ef06e50dc83" 113 | 114 | [[package]] 115 | name = "userdmp" 116 | version = "0.1.4" 117 | dependencies = [ 118 | "binrw", 119 | "bytemuck", 120 | "libc", 121 | "thiserror", 122 | "windows-sys", 123 | ] 124 | 125 | [[package]] 126 | name = "windows-sys" 127 | version = "0.59.0" 128 | source = "registry+https://github.com/rust-lang/crates.io-index" 129 | checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" 130 | dependencies = [ 131 | "windows-targets", 132 | ] 133 | 134 | [[package]] 135 | name = "windows-targets" 136 | version = "0.52.6" 137 | source = "registry+https://github.com/rust-lang/crates.io-index" 138 | checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" 139 | dependencies = [ 140 | "windows_aarch64_gnullvm", 141 | "windows_aarch64_msvc", 142 | "windows_i686_gnu", 143 | "windows_i686_gnullvm", 144 | "windows_i686_msvc", 145 | "windows_x86_64_gnu", 146 | "windows_x86_64_gnullvm", 147 | "windows_x86_64_msvc", 148 | ] 149 | 150 | [[package]] 151 | name = "windows_aarch64_gnullvm" 152 | version = "0.52.6" 153 | source = "registry+https://github.com/rust-lang/crates.io-index" 154 | checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" 155 | 156 | [[package]] 157 | name = "windows_aarch64_msvc" 158 | version = "0.52.6" 159 | source = "registry+https://github.com/rust-lang/crates.io-index" 160 | checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" 161 | 162 | [[package]] 163 | name = "windows_i686_gnu" 164 | version = "0.52.6" 165 | source = "registry+https://github.com/rust-lang/crates.io-index" 166 | checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" 167 | 168 | [[package]] 169 | name = "windows_i686_gnullvm" 170 | version = "0.52.6" 171 | source = "registry+https://github.com/rust-lang/crates.io-index" 172 | checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" 173 | 174 | [[package]] 175 | name = "windows_i686_msvc" 176 | version = "0.52.6" 177 | source = "registry+https://github.com/rust-lang/crates.io-index" 178 | checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" 179 | 180 | [[package]] 181 | name = "windows_x86_64_gnu" 182 | version = "0.52.6" 183 | source = "registry+https://github.com/rust-lang/crates.io-index" 184 | checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" 185 | 186 | [[package]] 187 | name = "windows_x86_64_gnullvm" 188 | version = "0.52.6" 189 | source = "registry+https://github.com/rust-lang/crates.io-index" 190 | checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" 191 | 192 | [[package]] 193 | name = "windows_x86_64_msvc" 194 | version = "0.52.6" 195 | source = "registry+https://github.com/rust-lang/crates.io-index" 196 | checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" 197 | -------------------------------------------------------------------------------- /src/mapper.rs: -------------------------------------------------------------------------------- 1 | //! The module provides functionality for memory mapping files into memory 2 | 3 | use std::{ffi::c_void, fs::File, io, path::Path}; 4 | use super::error::UserDmpError; 5 | 6 | /// Represents a memory-mapped file. 7 | /// This struct provides an abstraction for mapping a file into memory 8 | /// and accessing it as a slice of bytes. 9 | #[derive(Debug)] 10 | pub struct MappingFile<'a> { 11 | /// A slice representing the memory-mapped contents of the file. 12 | pub buffer: &'a [u8], 13 | 14 | /// The base address of the memory-mapped file in the process's address space. 15 | pub address: *mut c_void, 16 | } 17 | 18 | impl<'a> MappingFile<'a> { 19 | /// Creates a new `MappingFile` instance by mapping the contents of a file into memory. 20 | /// 21 | /// # Arguments 22 | /// 23 | /// * `path` - A reference to the path of the file to be mapped. 24 | /// 25 | /// # Example 26 | /// 27 | /// ```rust,ignore 28 | /// use userdmp::{MappingFile, UserDmpError}; 29 | /// 30 | /// fn main() -> Result<(), UserDmpError> { 31 | /// // Create a memory-mapped file. 32 | /// let mapped_file = MappingFile::new("example.dmp")?; 33 | /// println!("Memory-mapped file created. Size: {}", mapped_file.buffer.len()); 34 | /// 35 | /// Ok(()) 36 | /// } 37 | /// ``` 38 | pub fn new(path: impl AsRef) -> Result { 39 | let file = File::open(path)?; 40 | let (buffer, address) = map::map_file(file)?; 41 | Ok(Self { buffer, address }) 42 | } 43 | 44 | /// Creates a cursor for the memory-mapped file buffer. 45 | /// 46 | /// # Example 47 | /// 48 | /// ```rust,ignore 49 | /// use userdmp::{MappingFile, UserDmpError}; 50 | /// 51 | /// fn main() -> Result<(), UserDmpError> { 52 | /// // Create a memory-mapped file and a cursor for it. 53 | /// let mapped_file = MappingFile::new("example.dmp")?; 54 | /// let mut cursor = mapped_file.cursor(); 55 | /// 56 | /// // Read the first 4 bytes of the mapped file as a u32. 57 | /// let mut buffer = [0u8; 4]; 58 | /// cursor.read_exact(&mut buffer)?; 59 | /// let value = u32::from_le_bytes(buffer); 60 | /// 61 | /// println!("First value in file: {}", value); 62 | /// Ok(()) 63 | /// } 64 | /// ``` 65 | pub fn cursor(&self) -> io::Cursor<&'a [u8]> { 66 | io::Cursor::new(self.buffer) 67 | } 68 | } 69 | 70 | impl Drop for MappingFile<'_> { 71 | fn drop(&mut self) { 72 | if !self.address.is_null() { 73 | #[cfg(windows)] 74 | { 75 | use windows_sys::Win32::System::Memory::{MEMORY_MAPPED_VIEW_ADDRESS, UnmapViewOfFile}; 76 | 77 | // Create a MEMORY_MAPPED_VIEW_ADDRESS struct with the correct value. 78 | let address = MEMORY_MAPPED_VIEW_ADDRESS { Value: self.address }; 79 | 80 | // SAFETY: UnmapViewOfFile is called with a valid mapped address. 81 | unsafe { 82 | UnmapViewOfFile(address); 83 | } 84 | } 85 | 86 | #[cfg(unix)] 87 | { 88 | // SAFETY: munmap is called with a valid mapped address and size. 89 | unsafe { 90 | libc::munmap(self.address, self.buffer.len()); 91 | } 92 | } 93 | } 94 | } 95 | } 96 | 97 | mod map { 98 | use std::{ffi::c_void, ptr, slice}; 99 | use super::{File, UserDmpError}; 100 | 101 | /// Maps a file into memory and retrieves its memory buffer and base address (Windows). 102 | /// 103 | /// # Arguments 104 | /// 105 | /// * `file` - A `File` instance representing the file to be mapped. 106 | /// 107 | /// # Returns 108 | /// 109 | /// A tuple containing: 110 | /// * A slice of the memory-mapped file contents. 111 | /// * The base address of the memory-mapped file in the process's address space. 112 | /// 113 | /// # Example 114 | /// 115 | /// ```rust,ignore 116 | /// use std::fs::File; 117 | /// use userdmp::mapper; 118 | /// 119 | /// fn main() -> Result<(), Box> { 120 | /// let file = File::open("example.dmp")?; 121 | /// 122 | /// // Directly map the file on Windows. 123 | /// let (buffer, address) = mapper::map_file(file)?; 124 | /// println!("Mapped {} bytes at address {:?}", buffer.len(), address); 125 | /// 126 | /// Ok(()) 127 | /// } 128 | /// ``` 129 | #[cfg(windows)] 130 | pub fn map_file(file: File) -> Result<(&'static [u8], *mut c_void), UserDmpError> { 131 | use std::os::windows::io::AsRawHandle; 132 | use windows_sys::Win32::{ 133 | Foundation::CloseHandle, 134 | System::Memory::{CreateFileMappingA, FILE_MAP_READ, MapViewOfFile, PAGE_READONLY}, 135 | }; 136 | 137 | // Get the raw file handle. 138 | let h_file = file.as_raw_handle(); 139 | 140 | // Create a memory mapping for the file. 141 | let h_mapping = unsafe { CreateFileMappingA(h_file, ptr::null_mut(), PAGE_READONLY, 0, 0, ptr::null_mut()) }; 142 | 143 | // Return an error if the file mapping creation failed. 144 | if h_mapping.is_null() { 145 | return Err(UserDmpError::CreateFileMappingError); 146 | } 147 | 148 | // Get the file size and map the view of the file. 149 | let size = file.metadata()?.len() as usize; 150 | let base_address = unsafe { MapViewOfFile(h_mapping, FILE_MAP_READ, 0, 0, size) }; 151 | 152 | // Return an error if mapping the view failed. 153 | if base_address.Value.is_null() { 154 | unsafe { CloseHandle(h_mapping) }; 155 | return Err(UserDmpError::MapViewOfFileError); 156 | } 157 | 158 | // Close the file mapping handle; the view remains valid. 159 | unsafe { CloseHandle(h_mapping) }; 160 | 161 | // Return the memory-mapped buffer and its base address. 162 | unsafe { Ok((slice::from_raw_parts(base_address.Value as *const u8, size), base_address.Value)) } 163 | } 164 | 165 | /// Maps a file into memory and retrieves its memory buffer and base address (Unix). 166 | /// 167 | /// # Arguments 168 | /// 169 | /// * `file` - A `File` instance representing the file to be mapped. 170 | /// 171 | /// # Returns 172 | /// 173 | /// A tuple containing: 174 | /// * A slice of the memory-mapped file contents. 175 | /// * The base address of the memory-mapped file in the process's address space. 176 | /// 177 | /// # Example 178 | /// 179 | /// ```rust,ignore 180 | /// use std::fs::File; 181 | /// use userdmp::mapper; 182 | /// 183 | /// fn main() -> Result<(), Box> { 184 | /// let file = File::open("example.dmp")?; 185 | /// 186 | /// // Directly map the file on Unix. 187 | /// let (buffer, address) = mapper::map_file(file)?; 188 | /// println!("Mapped {} bytes at address {:?}", buffer.len(), address); 189 | /// 190 | /// Ok(()) 191 | /// } 192 | /// ``` 193 | #[cfg(unix)] 194 | pub fn map_file(file: File) -> Result<(&'static [u8], *mut c_void), UserDmpError> { 195 | use std::os::unix::io::AsRawFd; 196 | use libc::{MAP_FAILED, MAP_SHARED, PROT_READ, mmap}; 197 | 198 | // Get the raw file descriptor. 199 | let fd = file.as_raw_fd(); 200 | 201 | // Get the file size. 202 | let size = file.metadata()?.len() as usize; 203 | 204 | // Create a memory mapping for the file. 205 | let base_address = unsafe { mmap(ptr::null_mut(), size, PROT_READ, MAP_SHARED, fd, 0) }; 206 | 207 | // Return an error if the mapping failed. 208 | if base_address == MAP_FAILED { 209 | return Err(UserDmpError::MmapError); 210 | } 211 | 212 | // Return the memory-mapped buffer and its base address. 213 | unsafe { Ok((slice::from_raw_parts(base_address as *const u8, size), base_address)) } 214 | } 215 | } 216 | -------------------------------------------------------------------------------- /LICENSE-APACHE: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | END OF TERMS AND CONDITIONS 178 | 179 | APPENDIX: How to apply the Apache License to your work. 180 | 181 | To apply the Apache License to your work, attach the following 182 | boilerplate notice, with the fields enclosed by brackets "[]" 183 | replaced with your own identifying information. (Don't include 184 | the brackets!) The text should be enclosed in the appropriate 185 | comment syntax for the file format. We also recommend that a 186 | file or class name and description of purpose be included on the 187 | same "printed page" as the copyright notice for easier 188 | identification within third-party archives. 189 | 190 | Copyright [yyyy] [name of copyright owner] 191 | 192 | Licensed under the Apache License, Version 2.0 (the "License"); 193 | you may not use this file except in compliance with the License. 194 | You may obtain a copy of the License at 195 | 196 | http://www.apache.org/licenses/LICENSE-2.0 197 | 198 | Unless required by applicable law or agreed to in writing, software 199 | distributed under the License is distributed on an "AS IS" BASIS, 200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 201 | See the License for the specific language governing permissions and 202 | limitations under the License. -------------------------------------------------------------------------------- /src/data.rs: -------------------------------------------------------------------------------- 1 | //! The `data` module defines data structures and constants used in minidump parsing. 2 | 3 | #![allow(non_snake_case, non_camel_case_types)] 4 | 5 | /// Maximum number of parameters associated with an exception. 6 | pub const EXCEPTION_MAXIMUM_PARAMETERS: usize = 15; 7 | 8 | /// Signature to identify Minidump files ("MDMP" in ASCII). 9 | pub const MINIDUMP_SIGNATURE: u32 = 0x504D_444D; 10 | 11 | /// Default flags for configuring dumps. 12 | pub const DUMP_FLAGS: u64 = 0x001F_FFFF; 13 | 14 | /// Architecture code for 64-bit systems (x86_64). 15 | pub const ARCH_X64: u16 = 9; 16 | 17 | /// Architecture code for 32-bit systems (x86). 18 | pub const ARCH_X86: u16 = 0; 19 | 20 | /// Contains header information for the minidump file. 21 | /// 22 | /// For more details, see the official [Microsoft documentation](https://learn.microsoft.com/en-us/windows/win32/api/minidumpapiset/ns-minidumpapiset-minidump_header). 23 | #[derive(Copy, Clone)] 24 | #[binrw::binrw] 25 | #[brw(little)] 26 | pub struct MINIDUMP_HEADER { 27 | /// The signature. 28 | pub Signature: u32, 29 | 30 | /// The version of the minidump format. 31 | pub Version: u32, 32 | 33 | /// The number of streams in the minidump directory. 34 | pub NumberOfStreams: u32, 35 | 36 | /// The base RVA of the minidump directory. 37 | pub StreamDirectoryRva: u32, 38 | 39 | /// The checksum for the minidump file. 40 | pub CheckSum: u32, 41 | 42 | /// This member is reserved. 43 | pub Reserved: u32, 44 | 45 | // Time and date, in time_t format. 46 | pub TimeDateStamp: u32, 47 | 48 | /// One or more values from the MINIDUMP_TYPE enumeration type. 49 | pub Flags: u64, 50 | } 51 | 52 | /// Contains the information needed to access a specific data stream in a minidump file. 53 | /// 54 | /// For more details, see the official [Microsoft documentation](https://learn.microsoft.com/en-us/windows/win32/api/minidumpapiset/ns-minidumpapiset-minidump_directory). 55 | #[derive(Copy, Clone)] 56 | #[binrw::binrw] 57 | #[brw(little)] 58 | #[derive(Debug)] 59 | pub struct MINIDUMP_DIRECTORY { 60 | /// The type of data stream. 61 | pub StreamType: u32, 62 | 63 | /// A [`MINIDUMP_LOCATION_DESCRIPTOR`] structure that specifies the location of the data stream. 64 | pub Location: MINIDUMP_LOCATION_DESCRIPTOR, 65 | } 66 | 67 | /// Represents an exception information stream. 68 | /// 69 | /// For more details, see the official [Microsoft documentation](https://learn.microsoft.com/en-us/windows/win32/api/minidumpapiset/ns-minidumpapiset-minidump_exception_stream). 70 | #[derive(Copy, Clone)] 71 | #[binrw::binrw] 72 | #[brw(little)] 73 | pub struct MINIDUMP_EXCEPTION_STREAM { 74 | /// The identifier of the thread that caused the exception. 75 | pub ThreadId: u32, 76 | 77 | /// A variable for alignment. 78 | pub alignment: u32, 79 | 80 | /// A MINIDUMP_EXCEPTION structure. 81 | pub ExceptionRecord: MINIDUMP_EXCEPTION, 82 | 83 | /// A MINIDUMP_LOCATION_DESCRIPTOR structure. 84 | pub ThreadContext: MINIDUMP_LOCATION_DESCRIPTOR, 85 | } 86 | 87 | /// Represents an exception information stream. 88 | /// 89 | /// For more details, see the official [Microsoft documentation](https://learn.microsoft.com/en-us/windows/win32/api/minidumpapiset/ns-minidumpapiset-minidump_exception_stream). 90 | #[derive(Copy, Clone)] 91 | #[binrw::binrw] 92 | #[brw(little)] 93 | pub struct MINIDUMP_EXCEPTION { 94 | /// The reason the exception occurred. 95 | pub ExceptionCode: u32, 96 | 97 | /// This member can be either zero, indicating a continuable exception, or EXCEPTION_NONCONTINUABLE, indicating a noncontinuable exception. 98 | pub ExceptionFlags: u32, 99 | 100 | /// A pointer to an associated MINIDUMP_EXCEPTION structure. 101 | pub ExceptionRecord: u64, 102 | 103 | /// The address where the exception occurred. 104 | pub ExceptionAddress: u64, 105 | 106 | /// The number of parameters associated with the exception. 107 | pub NumberParameters: u32, 108 | 109 | /// Reserved for cross-platform structure member alignment. Do not set 110 | pub unusedAlignment: u32, 111 | 112 | /// An array of additional arguments that describe the exception. 113 | pub ExceptionInformation: [u64; EXCEPTION_MAXIMUM_PARAMETERS], 114 | } 115 | 116 | /// Contains a list of memory ranges. 117 | /// 118 | /// For more details, see the official [Microsoft documentation](https://learn.microsoft.com/en-us/windows/win32/api/minidumpapiset/ns-minidumpapiset-minidump_memory_info_list). 119 | #[derive(Clone)] 120 | #[binrw::binrw] 121 | #[brw(little)] 122 | pub struct MINIDUMP_MEMORY_INFO_LIST { 123 | /// Size of the header for this structure. 124 | pub SizeOfHeader: u32, 125 | 126 | /// Size of each entry in the memory info list. 127 | pub SizeOfEntry: u32, 128 | 129 | /// Number of entries in the memory info list. 130 | pub NumberOfEntries: u64, 131 | 132 | /// The list of memory info entries. 133 | #[br(count = NumberOfEntries)] 134 | pub Entries: Vec, 135 | } 136 | 137 | /// Describes a region of memory. 138 | /// 139 | /// For more details, see the official [Microsoft documentation](https://learn.microsoft.com/en-us/windows/win32/api/minidumpapiset/ns-minidumpapiset-minidump_memory_info). 140 | #[derive(Copy, Clone)] 141 | #[binrw::binrw] 142 | #[brw(little)] 143 | pub struct MINIDUMP_MEMORY_INFO { 144 | /// The base address of the memory region. 145 | pub BaseAddress: u64, 146 | 147 | /// The base address of the allocation containing the memory region. 148 | pub AllocationBase: u64, 149 | 150 | /// The memory protection applied at the time of allocation. 151 | pub AllocationProtect: u32, 152 | 153 | /// Alignment padding (unused). 154 | pub alignment1: u32, 155 | 156 | /// The size of the memory region in bytes. 157 | pub RegionSize: u64, 158 | 159 | /// The state of the memory region (e.g., committed, free, reserved). 160 | pub State: u32, 161 | 162 | /// The protection level of the memory region. 163 | pub Protect: u32, 164 | 165 | /// The type of memory region (e.g., private, mapped, image). 166 | pub Type: u32, 167 | 168 | /// Alignment padding (unused). 169 | pub alignment2: u32, 170 | } 171 | 172 | /// Contains a list of memory ranges. 173 | /// 174 | /// For more details, see the official [Microsoft documentation](https://learn.microsoft.com/en-us/windows/win32/api/minidumpapiset/ns-minidumpapiset-minidump_memory64_list). 175 | #[derive(Clone)] 176 | #[binrw::binrw] 177 | #[brw(little)] 178 | pub struct MINIDUMP_MEMORY64_LIST { 179 | /// The number of structures in the MemoryRanges array. 180 | pub NumberOfMemoryRanges: u64, 181 | 182 | /// An array of MINIDUMP_MEMORY_DESCRIPTOR structures. 183 | pub BaseRva: u64, 184 | 185 | /// Memory descriptors. 186 | #[br(count = NumberOfMemoryRanges)] 187 | pub Ranges: Vec, 188 | } 189 | 190 | /// Describes a range of memory. 191 | /// 192 | /// For more details, see the official [Microsoft documentation](https://learn.microsoft.com/en-us/windows/win32/api/minidumpapiset/ns-minidumpapiset-minidump_memory_descriptor). 193 | #[derive(Clone)] 194 | #[binrw::binrw] 195 | #[brw(little)] 196 | pub struct MINIDUMP_MEMORY_DESCRIPTOR64 { 197 | /// The starting address of the memory range. 198 | pub StartOfMemoryRange: u64, 199 | 200 | /// A MINIDUMP_LOCATION_DESCRIPTOR structure. 201 | pub DataSize: u64, 202 | } 203 | 204 | /// Contains processor and operating system information. 205 | /// 206 | /// For more details, see the official [Microsoft documentation](https://learn.microsoft.com/en-us/windows/win32/api/minidumpapiset/ns-minidumpapiset-minidump_system_info) 207 | #[derive(Copy, Clone)] 208 | #[binrw::binrw] 209 | #[brw(little)] 210 | pub struct MINIDUMP_SYSTEM_INFO { 211 | /// The system's processor architecture. 212 | pub ProcessorArchitecture: u16, 213 | 214 | /// The system's architecture-dependent processor level. 215 | pub ProcessorLevel: u16, 216 | 217 | /// The architecture-dependent processor revision. 218 | pub ProcessorRevision: u16, 219 | 220 | /// The number of processors in the system. 221 | pub NumberOfProcessors: u8, 222 | 223 | /// Any additional information about the system. 224 | pub ProductType: u8, 225 | 226 | /// The major version number of the operating system. 227 | pub MajorVersion: u32, 228 | 229 | /// The minor version number of the operating system. 230 | pub MinorVersion: u32, 231 | 232 | /// The build number of the operating system. 233 | pub BuildNumber: u32, 234 | 235 | /// The operating system platform. 236 | pub PlatformId: u32, 237 | 238 | /// An RVA (from the beginning of the dump) to a MINIDUMP_STRING that describes the latest Service Pack installed on the system. 239 | pub CSDVersionRva: u32, 240 | 241 | /// The bit flags that identify the product suites available on the system. 242 | pub SuiteMask: u16, 243 | 244 | /// This member is reserved for future use. 245 | pub Reserved2: u16, 246 | } 247 | 248 | /// Contains a list of modules. 249 | /// 250 | /// 251 | /// For more details, see the official [Microsoft documentation](https://learn.microsoft.com/en-us/windows/win32/api/minidumpapiset/ns-minidumpapiset-minidump_module_list) 252 | #[derive(Clone)] 253 | #[binrw::binrw] 254 | #[brw(little)] 255 | pub struct MINIDUMP_MODULE_LIST { 256 | /// The number of structures in the Modules array. 257 | pub NumberOfModules: u32, 258 | 259 | /// An array of MINIDUMP_MODULE structures. 260 | #[br(count = NumberOfModules)] 261 | pub Modules: Vec, 262 | } 263 | 264 | /// Contains information for a specific module. 265 | /// 266 | /// 267 | /// For more details, see the official [Microsoft documentation](https://learn.microsoft.com/en-us/windows/win32/api/minidumpapiset/ns-minidumpapiset-minidump_module) 268 | #[derive(Copy, Clone)] 269 | #[binrw::binrw] 270 | #[brw(little)] 271 | pub struct MINIDUMP_MODULE { 272 | /// The base address of the module executable image in memory. 273 | pub BaseOfImage: u64, 274 | 275 | /// The size of the module executable image in memory, in bytes. 276 | pub SizeOfImage: u32, 277 | 278 | /// The checksum value of the module executable image. 279 | pub CheckSum: u32, 280 | 281 | /// The timestamp value of the module executable image, in time_t format. 282 | pub TimeDateStamp: u32, 283 | 284 | /// An RVA to a MINIDUMP_STRING structure that specifies the name of the module. 285 | pub ModuleNameRva: u32, 286 | 287 | /// A VS_FIXEDFILEINFO structure that specifies the version of the module. 288 | pub VersionInfo: VS_FIXEDFILEINFO, 289 | 290 | /// A MINIDUMP_LOCATION_DESCRIPTOR structure that specifies the CodeView record of the module. 291 | pub CvRecord: MINIDUMP_LOCATION_DESCRIPTOR, 292 | 293 | /// A MINIDUMP_LOCATION_DESCRIPTOR structure that specifies the miscellaneous record of the module. 294 | pub MiscRecord: MINIDUMP_LOCATION_DESCRIPTOR, 295 | 296 | /// Reserved for future use. 297 | pub Reserved0: u64, 298 | 299 | /// Reserved for future use. 300 | pub Reserved1: u64, 301 | } 302 | 303 | /// Contains a bitmask that specifies the Boolean attributes of the file. 304 | #[repr(transparent)] 305 | #[derive(Copy, Clone)] 306 | #[binrw::binrw] 307 | #[brw(little)] 308 | pub struct VS_FIXEDFILEINFO_FILE_FLAGS(pub u32); 309 | 310 | /// The operating system for which this file was designed. 311 | #[repr(transparent)] 312 | #[derive(Copy, Clone)] 313 | #[binrw::binrw] 314 | #[brw(little)] 315 | pub struct VS_FIXEDFILEINFO_FILE_OS(pub u32); 316 | 317 | /// Contains version information for a file. 318 | /// 319 | /// For more details, see the official [Microsoft documentation](https://learn.microsoft.com/en-us/windows/win32/api/verrsrc/ns-verrsrc-vs_fixedfileinfo) 320 | #[derive(Copy, Clone)] 321 | #[binrw::binrw] 322 | #[brw(little)] 323 | pub struct VS_FIXEDFILEINFO { 324 | /// Contains the value 0xFEEF04BD. 325 | pub dwSignature: u32, 326 | 327 | /// The binary version number of this structure. 328 | pub dwStrucVersion: u32, 329 | 330 | /// The most significant 32 bits of the file's binary version number. 331 | pub dwFileVersionMS: u32, 332 | 333 | /// The least significant 32 bits of the file's binary version number. 334 | pub dwFileVersionLS: u32, 335 | 336 | /// The most significant 32 bits of the binary version number of the product with which this file was distributed. 337 | pub dwProductVersionMS: u32, 338 | 339 | /// The least significant 32 bits of the binary version number of the product with which this file was distributed. 340 | pub dwProductVersionLS: u32, 341 | 342 | /// Contains a bitmask that specifies the valid bits in dwFileFlags. 343 | pub dwFileFlagsMask: u32, 344 | 345 | /// Contains a bitmask that specifies the Boolean attributes of the file. 346 | pub dwFileFlags: VS_FIXEDFILEINFO_FILE_FLAGS, 347 | 348 | /// The operating system for which this file was designed. 349 | pub dwFileOS: VS_FIXEDFILEINFO_FILE_OS, 350 | 351 | /// The general type of file. 352 | pub dwFileType: u32, 353 | 354 | /// The function of the file. 355 | pub dwFileSubtype: u32, 356 | 357 | /// The most significant 32 bits of the file's 64-bit binary creation date and time stamp. 358 | pub dwFileDateMS: u32, 359 | 360 | /// The least significant 32 bits of the file's 64-bit binary creation date and time stamp. 361 | pub dwFileDateLS: u32, 362 | } 363 | 364 | /// Contains a list of threads. 365 | /// 366 | /// For more details, see the official [Microsoft documentation](https://learn.microsoft.com/en-us/windows/win32/api/minidumpapiset/ns-minidumpapiset-minidump_thread_list) 367 | #[derive(Clone)] 368 | #[binrw::binrw] 369 | #[brw(little)] 370 | pub struct MINIDUMP_THREAD_LIST { 371 | /// The number of structures in the Threads array. 372 | pub NumberOfThreads: u32, 373 | 374 | /// An array of MINIDUMP_THREAD structures. 375 | #[br(count = NumberOfThreads)] 376 | pub Threads: Vec, 377 | } 378 | 379 | /// Contains information for a specific thread. 380 | /// 381 | /// For more details, see the official [Microsoft documentation](https://learn.microsoft.com/en-us/windows/win32/api/minidumpapiset/ns-minidumpapiset-minidump_thread) 382 | #[derive(Copy, Clone)] 383 | #[binrw::binrw] 384 | #[brw(little)] 385 | pub struct MINIDUMP_THREAD { 386 | /// The identifier of the thread. 387 | pub ThreadId: u32, 388 | 389 | /// The suspend count for the thread. If the suspend count is greater than zero, the thread is suspended; otherwise, the thread is not suspended. 390 | pub SuspendCount: u32, 391 | 392 | /// The priority class of the thread. See Scheduling Priorities. 393 | pub PriorityClass: u32, 394 | 395 | /// The priority level of the thread. 396 | pub Priority: u32, 397 | 398 | /// The thread environment block. 399 | pub Teb: u64, 400 | 401 | /// A MINIDUMP_MEMORY_DESCRIPTOR structure. 402 | pub Stack: MINIDUMP_MEMORY_DESCRIPTOR, 403 | 404 | /// A MINIDUMP_LOCATION_DESCRIPTOR structure. 405 | pub ThreadContext: MINIDUMP_LOCATION_DESCRIPTOR, 406 | } 407 | 408 | /// Describes a range of memory. 409 | /// 410 | /// For more details, see the official [Microsoft documentation](https://learn.microsoft.com/en-us/windows/win32/api/minidumpapiset/ns-minidumpapiset-minidump_memory_descriptor) 411 | #[derive(Copy, Clone)] 412 | #[binrw::binrw] 413 | #[brw(little)] 414 | pub struct MINIDUMP_MEMORY_DESCRIPTOR { 415 | /// The starting address of the memory range. 416 | pub StartOfMemoryRange: u64, 417 | 418 | /// A MINIDUMP_LOCATION_DESCRIPTOR structure. 419 | pub Memory: MINIDUMP_LOCATION_DESCRIPTOR, 420 | } 421 | 422 | /// Contains information describing the location of a data stream within a minidump file. 423 | /// 424 | /// For more details, see the official [Microsoft documentation](https://learn.microsoft.com/en-us/windows/win32/api/minidumpapiset/ns-minidumpapiset-minidump_location_descriptor) 425 | #[derive(Copy, Clone, Debug)] 426 | #[binrw::binrw] 427 | #[brw(little)] 428 | pub struct MINIDUMP_LOCATION_DESCRIPTOR { 429 | /// The size of the data stream, in bytes. 430 | pub DataSize: u32, 431 | 432 | /// The relative virtual address (RVA) of the data. 433 | pub RVA: u32, 434 | } 435 | 436 | #[derive(Debug, Clone, Copy, binrw::NamedArgs)] 437 | pub struct HandleArgs { 438 | /// The size of the descriptor. 439 | pub size_of_descriptor: u32, 440 | } 441 | 442 | /// Represents the header for a handle data stream. 443 | /// 444 | /// For more details, see the official [Microsoft documentation](https://learn.microsoft.com/en-us/windows/win32/api/minidumpapiset/ns-minidumpapiset-minidump_handle_data_stream) 445 | #[derive(Clone)] 446 | #[binrw::binrw] 447 | #[brw(little)] 448 | pub struct MINIDUMP_HANDLE_DATA_STREAM { 449 | /// The size of the header information for the stream, in bytes. 450 | pub SizeOfHeader: u32, 451 | 452 | /// The size of a descriptor in the stream, in bytes. 453 | pub SizeOfDescriptor: u32, 454 | 455 | /// The number of descriptors in the stream. 456 | pub NumberOfDescriptors: u32, 457 | 458 | /// Reserved for future use; must be zero. 459 | pub Reserved: u32, 460 | 461 | /// List of handle descriptors. 462 | #[br( 463 | count = NumberOfDescriptors, 464 | args { inner: (SizeOfDescriptor,) } 465 | )] 466 | pub Handles: Vec, 467 | } 468 | 469 | /// Contains the state of an individual system handle at the time the minidump was written. 470 | /// 471 | /// For more details, see the official [Microsoft documentation](https://learn.microsoft.com/en-us/windows/win32/api/minidumpapiset/ns-minidumpapiset-minidump_handle_descriptor) 472 | #[derive(Clone)] 473 | #[binrw::binrw] 474 | #[brw(little, import(size_of_descriptor: u32))] 475 | #[allow(clippy::manual_non_exhaustive)] 476 | pub struct MINIDUMP_HANDLE_DESCRIPTOR { 477 | /// The operating system handle value. 478 | pub Handle: u64, 479 | 480 | /// An RVA to a MINIDUMP_STRING structure that specifies the object type of the handle. 481 | pub TypeNameRva: u32, 482 | 483 | /// An RVA to a MINIDUMP_STRING structure that specifies the object name of the handle. 484 | pub ObjectNameRva: u32, 485 | 486 | /// The meaning of this member depends on the handle type and the operating system. 487 | pub Attributes: u32, 488 | 489 | /// The meaning of this member depends on the handle type and the operating system. 490 | pub GrantedAccess: u32, 491 | 492 | /// The meaning of this member depends on the handle type and the operating system. 493 | pub HandleCount: u32, 494 | 495 | /// The meaning of this member depends on the handle type and the operating system. 496 | pub PointerCount: u32, 497 | 498 | /// Extra space to adjust the size of the descriptor. 499 | #[br(pad_after = (size_of_descriptor - size_of::() as u32) as usize)] 500 | _padding: (), 501 | } 502 | 503 | /// Describes a string. 504 | /// 505 | /// For more details, see the official [Microsoft documentation](https://learn.microsoft.com/en-us/windows/win32/api/minidumpapiset/ns-minidumpapiset-minidump_string) 506 | #[binrw::binrw] 507 | #[brw(little)] 508 | pub struct MINIDUMP_STRING { 509 | /// The size of the string in the Buffer member, in bytes. 510 | pub Length: u32, 511 | 512 | // The null-terminated string. 513 | #[br(count = Length / 2)] 514 | pub Buffer: Vec, 515 | } 516 | 517 | /// Represents the type of a minidump data stream. 518 | /// 519 | /// 520 | #[allow(dead_code)] 521 | #[repr(u32)] 522 | pub enum MINIDUMP_STREAM_TYPE { 523 | UnusedStream = 0, 524 | ReservedStream0 = 1, 525 | ReservedStream1 = 2, 526 | ThreadListStream = 3, 527 | ModuleListStream = 4, 528 | MemoryListStream = 5, 529 | ExceptionStream = 6, 530 | SystemInfoStream = 7, 531 | ThreadExListStream = 8, 532 | Memory64ListStream = 9, 533 | CommentStreamA = 10, 534 | CommentStreamW = 11, 535 | HandleDataStream = 12, 536 | FunctionTableStream = 13, 537 | UnloadedModuleListStream = 14, 538 | MiscInfoStream = 15, 539 | MemoryInfoListStream = 16, 540 | ThreadInfoListStream = 17, 541 | HandleOperationListStream = 18, 542 | TokenStream = 19, 543 | JavaScriptDataStream = 20, 544 | SystemMemoryInfoStream = 21, 545 | ProcessVmCountersStream = 22, 546 | IptTraceStream = 23, 547 | ThreadNamesStream = 24, 548 | ceStreamNull = 0x8000, 549 | ceStreamSystemInfo = 0x8001, 550 | ceStreamException = 0x8002, 551 | ceStreamModuleList = 0x8003, 552 | ceStreamProcessList = 0x8004, 553 | ceStreamThreadList = 0x8005, 554 | ceStreamThreadContextList = 0x8006, 555 | ceStreamThreadCallStackList = 0x8007, 556 | ceStreamMemoryVirtualList = 0x8008, 557 | ceStreamMemoryPhysicalList = 0x8009, 558 | ceStreamBucketParameters = 0x800A, 559 | ceStreamProcessModuleMap = 0x800B, 560 | ceStreamDiagnosisList = 0x800C, 561 | LastReservedStream = 0xffff, 562 | } 563 | 564 | impl TryFrom for MINIDUMP_STREAM_TYPE { 565 | type Error = &'static str; 566 | 567 | fn try_from(value: u32) -> Result { 568 | match value { 569 | 0 => Ok(MINIDUMP_STREAM_TYPE::UnusedStream), 570 | 1 => Ok(MINIDUMP_STREAM_TYPE::ReservedStream0), 571 | 2 => Ok(MINIDUMP_STREAM_TYPE::ReservedStream1), 572 | 3 => Ok(MINIDUMP_STREAM_TYPE::ThreadListStream), 573 | 4 => Ok(MINIDUMP_STREAM_TYPE::ModuleListStream), 574 | 5 => Ok(MINIDUMP_STREAM_TYPE::MemoryListStream), 575 | 6 => Ok(MINIDUMP_STREAM_TYPE::ExceptionStream), 576 | 7 => Ok(MINIDUMP_STREAM_TYPE::SystemInfoStream), 577 | 8 => Ok(MINIDUMP_STREAM_TYPE::ThreadExListStream), 578 | 9 => Ok(MINIDUMP_STREAM_TYPE::Memory64ListStream), 579 | 10 => Ok(MINIDUMP_STREAM_TYPE::CommentStreamA), 580 | 11 => Ok(MINIDUMP_STREAM_TYPE::CommentStreamW), 581 | 12 => Ok(MINIDUMP_STREAM_TYPE::HandleDataStream), 582 | 13 => Ok(MINIDUMP_STREAM_TYPE::FunctionTableStream), 583 | 14 => Ok(MINIDUMP_STREAM_TYPE::UnloadedModuleListStream), 584 | 15 => Ok(MINIDUMP_STREAM_TYPE::MiscInfoStream), 585 | 16 => Ok(MINIDUMP_STREAM_TYPE::MemoryInfoListStream), 586 | 17 => Ok(MINIDUMP_STREAM_TYPE::ThreadInfoListStream), 587 | 18 => Ok(MINIDUMP_STREAM_TYPE::HandleOperationListStream), 588 | 19 => Ok(MINIDUMP_STREAM_TYPE::TokenStream), 589 | 20 => Ok(MINIDUMP_STREAM_TYPE::JavaScriptDataStream), 590 | 21 => Ok(MINIDUMP_STREAM_TYPE::SystemMemoryInfoStream), 591 | 22 => Ok(MINIDUMP_STREAM_TYPE::ProcessVmCountersStream), 592 | 23 => Ok(MINIDUMP_STREAM_TYPE::IptTraceStream), 593 | 24 => Ok(MINIDUMP_STREAM_TYPE::ThreadNamesStream), 594 | 0x8000 => Ok(MINIDUMP_STREAM_TYPE::ceStreamNull), 595 | 0x8001 => Ok(MINIDUMP_STREAM_TYPE::ceStreamSystemInfo), 596 | 0x8002 => Ok(MINIDUMP_STREAM_TYPE::ceStreamException), 597 | 0x8003 => Ok(MINIDUMP_STREAM_TYPE::ceStreamModuleList), 598 | 0x8004 => Ok(MINIDUMP_STREAM_TYPE::ceStreamProcessList), 599 | 0x8005 => Ok(MINIDUMP_STREAM_TYPE::ceStreamThreadList), 600 | 0x8006 => Ok(MINIDUMP_STREAM_TYPE::ceStreamThreadContextList), 601 | 0x8007 => Ok(MINIDUMP_STREAM_TYPE::ceStreamThreadCallStackList), 602 | 0x8008 => Ok(MINIDUMP_STREAM_TYPE::ceStreamMemoryVirtualList), 603 | 0x8009 => Ok(MINIDUMP_STREAM_TYPE::ceStreamMemoryPhysicalList), 604 | 0x800A => Ok(MINIDUMP_STREAM_TYPE::ceStreamBucketParameters), 605 | 0x800B => Ok(MINIDUMP_STREAM_TYPE::ceStreamProcessModuleMap), 606 | 0x800C => Ok(MINIDUMP_STREAM_TYPE::ceStreamDiagnosisList), 607 | 0xffff => Ok(MINIDUMP_STREAM_TYPE::LastReservedStream), 608 | _ => Err("Invalid value for MINIDUMP_STREAM_TYPE"), 609 | } 610 | } 611 | } 612 | 613 | /// CONTEXT structure representing 64 bits 614 | #[derive(Debug)] 615 | #[repr(C, align(16))] 616 | pub struct CONTEXT_X64 { 617 | pub P1Home: u64, 618 | pub P2Home: u64, 619 | pub P3Home: u64, 620 | pub P4Home: u64, 621 | pub P5Home: u64, 622 | pub P6Home: u64, 623 | pub ContextFlags: u32, 624 | pub MxCsr: u32, 625 | pub SegCs: u16, 626 | pub SegDs: u16, 627 | pub SegEs: u16, 628 | pub SegFs: u16, 629 | pub SegGs: u16, 630 | pub SegSs: u16, 631 | pub EFlags: u32, 632 | pub Dr0: u64, 633 | pub Dr1: u64, 634 | pub Dr2: u64, 635 | pub Dr3: u64, 636 | pub Dr6: u64, 637 | pub Dr7: u64, 638 | pub Rax: u64, 639 | pub Rcx: u64, 640 | pub Rdx: u64, 641 | pub Rbx: u64, 642 | pub Rsp: u64, 643 | pub Rbp: u64, 644 | pub Rsi: u64, 645 | pub Rdi: u64, 646 | pub R8: u64, 647 | pub R9: u64, 648 | pub R10: u64, 649 | pub R11: u64, 650 | pub R12: u64, 651 | pub R13: u64, 652 | pub R14: u64, 653 | pub R15: u64, 654 | pub Rip: u64, 655 | pub Header: [u128; 2], 656 | pub Legacy: [u128; 8], 657 | pub Xmm0: u128, 658 | pub Xmm1: u128, 659 | pub Xmm2: u128, 660 | pub Xmm3: u128, 661 | pub Xmm4: u128, 662 | pub Xmm5: u128, 663 | pub Xmm6: u128, 664 | pub Xmm7: u128, 665 | pub Xmm8: u128, 666 | pub Xmm9: u128, 667 | pub Xmm10: u128, 668 | pub Xmm11: u128, 669 | pub Xmm12: u128, 670 | pub Xmm13: u128, 671 | pub Xmm14: u128, 672 | pub Xmm15: u128, 673 | pub Padding: [u8; 0x60], 674 | pub VectorRegister: [u128; 26], 675 | pub VectorControl: u64, 676 | pub DebugControl: u64, 677 | pub LastBranchToRip: u64, 678 | pub LastBranchFromRip: u64, 679 | pub LastExceptionToRip: u64, 680 | pub LastExceptionFromRip: u64, 681 | } 682 | 683 | /// CONTEXT structure representing 32 bits 684 | #[derive(Debug)] 685 | #[repr(C)] 686 | pub struct CONTEXT_X86 { 687 | pub ContextFlags: u32, 688 | pub Dr0: u32, 689 | pub Dr1: u32, 690 | pub Dr2: u32, 691 | pub Dr3: u32, 692 | pub Dr6: u32, 693 | pub Dr7: u32, 694 | pub ControlWord: u32, 695 | pub StatusWord: u32, 696 | pub TagWord: u32, 697 | pub ErrorOffset: u32, 698 | pub ErrorSelector: u32, 699 | pub DataOffset: u32, 700 | pub DataSelector: u32, 701 | pub RegisterArea: [u8; 80], 702 | pub Spare0: u32, 703 | pub SegGs: u32, 704 | pub SegFs: u32, 705 | pub SegEs: u32, 706 | pub SegDs: u32, 707 | pub Edi: u32, 708 | pub Esi: u32, 709 | pub Ebx: u32, 710 | pub Edx: u32, 711 | pub Ecx: u32, 712 | pub Eax: u32, 713 | pub Ebp: u32, 714 | pub Eip: u32, 715 | pub SegCs: u32, 716 | pub EFlags: u32, 717 | pub Esp: u32, 718 | pub SegSs: u32, 719 | pub ExtendedRegisters: [u8; 512], 720 | } 721 | -------------------------------------------------------------------------------- /src/parse.rs: -------------------------------------------------------------------------------- 1 | //! The module contains the core logic for parsing minidump files. 2 | 3 | use std::{ 4 | collections::BTreeMap, 5 | io::{self, Cursor, Seek}, 6 | path::Path, 7 | ptr::{self}, 8 | }; 9 | use binrw::BinRead; 10 | use crate::mapper::MappingFile; 11 | use crate::error::UserDmpError; 12 | use crate::data::{ 13 | MINIDUMP_STREAM_TYPE::{self, *}, 14 | *, 15 | }; 16 | 17 | /// Represents the modules in a minidump file, mapped by their starting memory address. 18 | pub type Modules<'a> = BTreeMap>; 19 | 20 | /// Represents the threads in a minidump file, mapped by their thread IDs. 21 | pub type Threads = BTreeMap; 22 | 23 | /// Represents the handles in a minidump file, mapped by their handle values. 24 | pub type Handles = BTreeMap; 25 | 26 | /// Represents memory regions in a minidump file, mapped by their base addresses. 27 | pub type Memorys<'a> = BTreeMap>; 28 | 29 | // Type of error 30 | pub type Result = std::result::Result; 31 | 32 | /// Represents the processor architecture of the captured process. 33 | #[derive(Copy, Debug, Clone, Default)] 34 | pub enum Arch { 35 | // 64-bit architecture 36 | #[default] 37 | X64, 38 | 39 | // 32-bit architecture 40 | X86, 41 | } 42 | 43 | /// Trait to represent the parsing of generic streams in a minidump file. 44 | pub trait MinidumpStream<'a> { 45 | /// Defines the type of output expected from the parser. 46 | type Output; 47 | 48 | /// Processes the stream and returns the corresponding output type. 49 | /// 50 | /// # Arguments 51 | /// 52 | /// * `cursor` - A mutable reference to a cursor pointing to the stream's binary data. 53 | /// 54 | /// # Returns 55 | /// 56 | /// The parsed output of the stream. 57 | fn parse(cursor: &mut Cursor<&'a [u8]>) -> Result; 58 | } 59 | 60 | /// Represents a parsed minidump file, containing metadata, modules, and threads. 61 | #[derive(Debug)] 62 | pub struct UserDump<'a> { 63 | /// Indicates that it is the ID of the thread directly related to the exception. 64 | pub exception_thread_id: Option, 65 | 66 | // System information on the dump 67 | pub system: System, 68 | 69 | /// The list of modules in the captured process. 70 | modules: Modules<'a>, 71 | 72 | /// The list of threads in the captured process. 73 | threads: Threads, 74 | 75 | /// The list of memorys in the captured process. 76 | memorys: Memorys<'a>, 77 | 78 | /// The list of handles in the captured process. 79 | handles: Handles, 80 | 81 | /// Mapped file information. 82 | pub mapped_file: MappingFile<'a>, 83 | } 84 | 85 | impl<'a> UserDump<'a> { 86 | /// Creates a new [`UserDump`] by parsing a minidump file from the given path. 87 | /// 88 | /// # Arguments 89 | /// 90 | /// * `path` - Path to the minidump file. 91 | /// 92 | /// # Example 93 | /// 94 | /// ```rust,ignore 95 | /// use userdmp::{UserDump}; 96 | /// 97 | /// match UserDump::new("example.dmp") { 98 | /// Ok(dump) => println!("Successfully parsed minidump."), 99 | /// Err(e) => eprintln!("Failed to parse minidump: {:?}", e), 100 | /// } 101 | /// ``` 102 | pub fn new(path: impl AsRef) -> Result { 103 | // Mapping the file in memory to the target environment (Windows or Linux). 104 | let mapped_file = MappingFile::new(path)?; 105 | Self::parse(mapped_file) 106 | } 107 | 108 | /// Returns a reference to the list of threads in the parsed minidump. 109 | /// 110 | /// # Example 111 | /// 112 | /// ```rust,ignore 113 | /// use userdmp::UserDump; 114 | /// 115 | /// let dump = UserDump::new("example.dmp").unwrap(); 116 | /// for (thread_id, thread) in dump.threads() { 117 | /// println!("Thread ID: {}, Priority: {}", thread_id, thread.priority); 118 | /// } 119 | /// ``` 120 | pub fn threads(&self) -> &Threads { 121 | &self.threads 122 | } 123 | 124 | /// Returns a reference to the list of modules in the parsed minidump 125 | /// 126 | /// # Example 127 | /// 128 | /// ```rust,ignore 129 | /// use userdmp::UserDump; 130 | /// 131 | /// let dump = UserDump::new("example.dmp").unwrap(); 132 | /// for (base_address, module) in dump.modules() { 133 | /// println!( 134 | /// "Module: {}, Base Address: 0x{:x}, Size: {} bytes", 135 | /// module.name().unwrap_or("Unknown"), 136 | /// base_address, 137 | /// module.len() 138 | /// ); 139 | /// } 140 | /// ``` 141 | pub fn modules(&self) -> &Modules<'_> { 142 | &self.modules 143 | } 144 | 145 | /// Returns a reference to the list of memory in the parsed minidump 146 | /// 147 | /// # Example 148 | /// 149 | /// ```rust,ignore 150 | /// use userdmp::UserDump; 151 | /// 152 | /// let dump = UserDump::new("example.dmp").unwrap(); 153 | /// for (base_address, memory) in dump.memorys() { 154 | /// println!( 155 | /// "Memory Region: Base Address: 0x{:x}, Size: {} bytes", 156 | /// base_address, 157 | /// memory.len() 158 | /// ); 159 | /// } 160 | /// ``` 161 | pub fn memorys(&self) -> &Memorys<'_> { 162 | &self.memorys 163 | } 164 | 165 | /// Returns a reference to the list of handles in the parsed minidump. 166 | /// 167 | /// # Example 168 | /// 169 | /// ```rust,ignore 170 | /// use userdmp::UserDump; 171 | /// 172 | /// let dump = UserDump::new("example.dmp").unwrap(); 173 | /// for (handle_id, handle) in dump.handles() { 174 | /// println!( 175 | /// "Handle ID: 0x{}, Type: {:?}, Object Name: {:?}, Attributes: {}, Access: {}", 176 | /// handle.handle(), 177 | /// handle.type_name(), 178 | /// handle.object_name(), 179 | /// handle.attributes, 180 | /// handle.granted_access 181 | /// ); 182 | /// } 183 | /// ``` 184 | pub fn handles(&self) -> &Handles { 185 | &self.handles 186 | } 187 | 188 | /// Parses a specific stream type from a minidump file using the `MinidumpStream` trait. 189 | /// 190 | /// # Type Parameters 191 | /// 192 | /// * `S` - The stream type to parse. Must implement the `MinidumpStream` trait. 193 | /// 194 | /// # Arguments 195 | /// 196 | /// * `cursor` - A mutable reference to a cursor positioned within the minidump file. 197 | /// 198 | /// # Returns 199 | /// 200 | /// The parsed result for the specific stream type. 201 | fn parse_stream(cursor: &mut Cursor<&'a [u8]>) -> Result 202 | where 203 | S: MinidumpStream<'a>, 204 | { 205 | S::parse(cursor) 206 | } 207 | 208 | /// Parses a minidump file into a [`UserDump`] structure. 209 | /// 210 | /// # Arguments 211 | /// 212 | /// * `mapped_file` - The memory-mapped minidump file. 213 | /// 214 | /// # Returns 215 | /// 216 | /// If the file is parsed successfully. 217 | fn parse(mapped_file: MappingFile<'a>) -> Result { 218 | // Creates a cursor to navigate the mapped file. 219 | let mut cursor = mapped_file.cursor(); 220 | 221 | // Reads minidump header. 222 | let header = MINIDUMP_HEADER::read(&mut cursor)?; 223 | 224 | // Does the file provided have a minidump signature? 225 | if header.Signature != MINIDUMP_SIGNATURE { 226 | return Err(UserDmpError::InvalidSignature); 227 | } 228 | 229 | // Checks if at least one of the bits defined in DUMP_FLAGS (0x001f_ffff) 230 | // is present in the Flags field of the header. These bits correspond to: 231 | // - 0x00000001: Includes data sections of loaded modules (MiniDumpWithDataSegs). 232 | // - 0x00000002: Includes the full memory of the process (MiniDumpWithFullMemory). 233 | // - 0x00000004: Includes handle information (MiniDumpWithHandleData). 234 | // - 0x00000008: Filters out unused memory (MiniDumpFilterMemory). 235 | // - 0x00000010: Adds directly referenced memory (MiniDumpScanMemory). 236 | // - 0x00000020: Includes unloaded modules (MiniDumpWithUnloadedModules). 237 | // - 0x00000040: Includes indirectly referenced memory (MiniDumpWithIndirectlyReferencedMemory). 238 | // - 0x00000200: Adds private read/write memory regions (MiniDumpWithPrivateReadWriteMemory). 239 | // - 0x00000800: Adds detailed memory information (MiniDumpWithFullMemoryInfo). 240 | // - 0x00001000: Includes detailed thread information (MiniDumpWithThreadInfo). 241 | // - 0x00002000: Includes code segments from modules (MiniDumpWithCodeSegs). 242 | if (header.Flags & DUMP_FLAGS) != 0 { 243 | return Err(UserDmpError::InvalidFlags(header.Flags)); 244 | } 245 | 246 | // Seeks to the stream directory. 247 | cursor.seek(io::SeekFrom::Start(header.StreamDirectoryRva.into()))?; 248 | 249 | // Collects all valid streams from the stream directory. 250 | let mut streams = (0..header.NumberOfStreams) 251 | .filter_map(|_| { 252 | let stream = MINIDUMP_DIRECTORY::read(&mut cursor).ok()?; 253 | (stream.StreamType != UnusedStream as u32).then_some(stream) 254 | }) 255 | .collect::>(); 256 | 257 | // Sort streams by their StreamType in descending order to ensure 258 | // that higher priority or dependent streams are processed first. 259 | streams.sort_by_key(|stream| std::cmp::Reverse(stream.StreamType)); 260 | 261 | let mut system = System::default(); 262 | let mut modules = Modules::new(); 263 | let mut threads = Threads::new(); 264 | let mut memory_info = Memorys::new(); 265 | let mut memory64 = Memorys::new(); 266 | let mut handles = Handles::new(); 267 | let mut exception_thread_id = None; 268 | 269 | // Processes each stream based on its type. 270 | for stream in &streams { 271 | // Seeks to the stream data. 272 | cursor.seek(io::SeekFrom::Start(stream.Location.RVA.into()))?; 273 | 274 | match MINIDUMP_STREAM_TYPE::try_from(stream.StreamType) { 275 | Ok(SystemInfoStream) => system = Self::parse_stream::(&mut cursor)?, 276 | Ok(ModuleListStream) => modules = Self::parse_stream::(&mut cursor)?, 277 | Ok(HandleDataStream) => handles = Self::parse_stream::(&mut cursor)?, 278 | Ok(ExceptionStream) => exception_thread_id = Some(Self::parser_exception(&mut cursor)?), 279 | Ok(ThreadListStream) => threads = Thread::parse(&mut cursor, &Some(system.processor_architecture))?, 280 | Ok(MemoryInfoListStream) => memory_info = Memory::parser_memory_info(&mut cursor)?, 281 | Ok(Memory64ListStream) => memory64 = Memory::parser_memory64_list(&mut cursor)?, 282 | _ => {} 283 | } 284 | } 285 | 286 | // Merges two maps of memory regions into a single map. 287 | let memorys = Memory::merge_memory(memory_info, memory64)?; 288 | 289 | // Returns the parsed UserDump. 290 | Ok(Self { 291 | exception_thread_id, 292 | system, 293 | modules, 294 | threads, 295 | memorys, 296 | handles, 297 | mapped_file, 298 | }) 299 | } 300 | 301 | /// Parses the exception information from the `ExceptionStream`. 302 | /// 303 | /// # Arguments 304 | /// 305 | /// * `cursor` - Cursor positioned at the exception stream. 306 | /// 307 | /// # Returns 308 | /// 309 | /// The thread ID associated with the exception. 310 | fn parser_exception(cursor: &mut Cursor<&'a [u8]>) -> Result { 311 | // Reads the exception stream. 312 | let exception = MINIDUMP_EXCEPTION_STREAM::read(cursor)?; 313 | 314 | // Returns the associated thread ID. 315 | Ok(exception.ThreadId) 316 | } 317 | 318 | /// Extracts raw data from a [`MINIDUMP_LOCATION_DESCRIPTOR`]. 319 | /// 320 | /// # Arguments 321 | /// 322 | /// * `cursor` - Cursor to read data from. 323 | /// * `location` - The descriptor indicating where the data is located. 324 | /// 325 | /// # Returns 326 | /// 327 | /// A slice containing the raw data. 328 | fn extract_raw_data(cursor: &Cursor<&'a [u8]>, location: MINIDUMP_LOCATION_DESCRIPTOR) -> io::Result<&'a [u8]> { 329 | // Reads the RVA. 330 | let rva = location.RVA; 331 | 332 | // Reads the size of the data. 333 | let size = location.DataSize; 334 | 335 | // Splits the slice at the RVA. 336 | let slice = cursor.get_ref(); 337 | let (_, tail) = slice.split_at(rva as usize); 338 | 339 | // Returns the extracted slice. 340 | Ok(&tail[..size as usize]) 341 | } 342 | } 343 | 344 | // Represents the system information captured in the minidump. 345 | /// The [`System`] struct contains details about the processor architecture, 346 | /// operating system version, and other general system information useful 347 | /// for analyzing the minidump. 348 | #[derive(Copy, Debug, Clone, Default)] 349 | pub struct System { 350 | /// The processor architecture captured in the minidump (e.g., x86 or x64). 351 | pub processor_architecture: Arch, 352 | 353 | /// The processor level. 354 | pub processor_level: u16, 355 | 356 | /// The processor revision. 357 | pub processor_revision: u16, 358 | 359 | /// The number of processors in the captured system. 360 | pub number_of_processors: u8, 361 | 362 | /// The product type of the operating system. 363 | pub product_type: u8, 364 | 365 | /// The major version of the operating system. 366 | pub major_version: u32, 367 | 368 | /// The minor version of the operating system. 369 | pub minor_version: u32, 370 | 371 | /// The build number of the operating system. 372 | pub build_number: u32, 373 | 374 | /// The platform identifier of the operating system. 375 | pub platform_id: u32, 376 | } 377 | 378 | impl MinidumpStream<'_> for System { 379 | type Output = System; 380 | 381 | /// Parses the system information from the `SystemInfoStream`. 382 | /// 383 | /// # Arguments 384 | /// 385 | /// * `cursor` - Cursor positioned at the system info stream. 386 | /// 387 | /// # Returns 388 | /// 389 | /// If the system are parsed successfully. 390 | fn parse(cursor: &mut Cursor<&'_ [u8]>) -> Result { 391 | // Reads the system info stream. 392 | let system_info = MINIDUMP_SYSTEM_INFO::read(cursor)?; 393 | 394 | // Converts MINIDUMP_SYSTEM_INFO into System. 395 | Ok(System::from(system_info)) 396 | } 397 | } 398 | 399 | impl From for System { 400 | /// Converts a `MINIDUMP_SYSTEM_INFO` structure into a `System` instance. 401 | /// 402 | /// # Parameters 403 | /// 404 | /// * `info` - A [`MINIDUMP_SYSTEM_INFO`] instance containing the raw data 405 | /// extracted from the system information stream. 406 | fn from(info: MINIDUMP_SYSTEM_INFO) -> Self { 407 | Self { 408 | processor_architecture: match info.ProcessorArchitecture { 409 | ARCH_X64 => Arch::X64, 410 | ARCH_X86 => Arch::X86, 411 | _ => panic!("Unsupported architecture: {:x}", info.ProcessorArchitecture), 412 | }, 413 | processor_level: info.ProcessorLevel, 414 | processor_revision: info.ProcessorRevision, 415 | number_of_processors: info.NumberOfProcessors, 416 | product_type: info.ProductType, 417 | major_version: info.MajorVersion, 418 | minor_version: info.MinorVersion, 419 | build_number: info.BuildNumber, 420 | platform_id: info.PlatformId, 421 | } 422 | } 423 | } 424 | 425 | /// Represents a module loaded in a process, including its memory range, checksum, path, 426 | /// timestamp, and additional records like CodeView (CV) and miscellaneous (MISC) information. 427 | #[derive(Debug, Clone)] 428 | pub struct Module<'a> { 429 | /// The memory range of the module. 430 | pub range: std::ops::Range, 431 | 432 | /// The checksum of the module. 433 | pub checksum: u32, 434 | 435 | /// The path to the module file. 436 | pub path: std::path::PathBuf, 437 | 438 | /// The timestamp when the module was built, represented as a 32-bit UNIX time value. 439 | pub time_date_stamp: u32, 440 | 441 | /// The CodeView (CV) record for debugging information. 442 | pub cv_record: &'a [u8], 443 | 444 | /// The miscellaneous (MISC) record, often containing additional debug metadata. 445 | pub misc_record: &'a [u8], 446 | } 447 | 448 | impl<'a> Module<'a> { 449 | /// Creates a new `Module` instance from a `MINIDUMP_MODULE` and its name. 450 | /// 451 | /// # Arguments 452 | /// 453 | /// * `module` - A reference to a `MINIDUMP_MODULE` containing information about the module. 454 | /// * `name` - A `String` representing the module's name or path. 455 | /// 456 | /// # Panics 457 | /// 458 | /// This function will panic if the memory range of the module is invalid (e.g., start >= end). 459 | pub fn new( 460 | module: &MINIDUMP_MODULE, 461 | name: String, 462 | cv_record: &'a [u8], 463 | misc_record: &'a [u8] 464 | ) -> Self { 465 | let range = std::ops::Range { 466 | start: module.BaseOfImage, 467 | end: module.BaseOfImage + module.SizeOfImage as u64, 468 | }; 469 | 470 | if range.is_empty() { 471 | panic!("Problem building the memory range") 472 | } 473 | 474 | Self { 475 | range, 476 | checksum: module.CheckSum, 477 | path: name.into(), 478 | time_date_stamp: module.TimeDateStamp, 479 | cv_record, 480 | misc_record, 481 | } 482 | } 483 | 484 | /// Returns the name of the module file, if available. 485 | /// 486 | /// # Returns 487 | /// 488 | /// Containing the file name, or `None` if the path is invalid or 489 | /// not UTF-8 encoded. 490 | pub fn name(&self) -> Option<&str> { 491 | self.path.file_name()?.to_str() 492 | } 493 | 494 | /// Returns the starting memory address of the module. 495 | /// 496 | /// # Returns 497 | /// 498 | /// The starting address of the module. 499 | pub fn start_addr(&self) -> u64 { 500 | self.range.start 501 | } 502 | 503 | /// Returns the ending memory address of the module (inclusive). 504 | /// 505 | /// # Returns 506 | /// 507 | /// The ending address of the module. 508 | pub fn end_addr(&self) -> u64 { 509 | self.range.end - 1 510 | } 511 | 512 | /// Returns the size of the module in bytes. 513 | /// 514 | /// # Returns 515 | /// 516 | /// The size of the module. 517 | pub fn len(&self) -> u64 { 518 | self.range.end - self.range.start 519 | } 520 | 521 | /// Returns true if the module has zero size. 522 | pub fn is_empty(&self) -> bool { 523 | self.len() == 0 524 | } 525 | } 526 | 527 | impl<'a> MinidumpStream<'a> for Module<'a> { 528 | type Output = Modules<'a>; 529 | 530 | /// Parses the list of modules from the `ModuleListStream`. 531 | /// 532 | /// # Arguments 533 | /// 534 | /// * `cursor` - Cursor positioned at the module list stream. 535 | /// 536 | /// # Returns 537 | /// 538 | /// If the modules are parsed successfully. 539 | fn parse(cursor: &mut Cursor<&'a [u8]>) -> Result> { 540 | // Reads the module list stream. 541 | let module_list = MINIDUMP_MODULE_LIST::read(cursor)?; 542 | 543 | // Parses each module entry in the list. 544 | let modules = module_list 545 | .Modules 546 | .iter() 547 | .map(|module| { 548 | // Seeks to the module name. 549 | cursor.seek(io::SeekFrom::Start(module.ModuleNameRva.into()))?; 550 | 551 | // reading the structure MINIDUMP_STRING 552 | let string = MINIDUMP_STRING::read(cursor)?; 553 | 554 | // Converts the name to UTF-8. 555 | let module_name = String::from_utf16_lossy(&string.Buffer) 556 | .trim_end_matches('\0') 557 | .to_string(); 558 | 559 | // Creates a new Module. 560 | let module = Module::new(module, module_name, &[], &[]); 561 | Ok((module.range.start, module)) 562 | }) 563 | .collect::>()?; 564 | 565 | // Returns the parsed modules. 566 | Ok(modules) 567 | } 568 | } 569 | 570 | /// Represents the processor context of a thread captured in the minidump. 571 | /// 572 | /// The `ThreadContext` enum encapsulates the architecture-specific context 573 | /// data, such as register states, for threads in the captured process. 574 | #[derive(Debug)] 575 | pub enum ThreadContext { 576 | /// Represents the 64-bit processor context (`CONTEXT_X64`) for the thread. 577 | X64(Box), 578 | 579 | /// Represents the 32-bit processor context (`CONTEXT_X86`) for the thread. 580 | X86(Box), 581 | } 582 | 583 | /// Represents a thread in the process, as captured in the minidump file. 584 | /// 585 | /// The `Thread` struct contains metadata about the thread, such as its ID, 586 | /// priority, and execution context. 587 | #[derive(Debug)] 588 | pub struct Thread { 589 | /// The unique identifier (ID) of the thread. 590 | pub thread_id: u32, 591 | 592 | /// The number of times the thread has been suspended. 593 | pub suspend_count: u32, 594 | 595 | /// The priority class of the thread. 596 | pub priority_class: u32, 597 | 598 | /// The priority level of the thread within its priority class. 599 | pub priority: u32, 600 | 601 | /// The address of the Thread Environment Block (TEB), containing per-thread information. 602 | pub teb: u64, 603 | 604 | /// The execution context of the thread, including register states. 605 | context: ThreadContext, 606 | } 607 | 608 | impl Thread { 609 | /// Creates a new `Thread` instance from a `MINIDUMP_THREAD` structure and its context. 610 | /// 611 | /// # Arguments 612 | /// 613 | /// * `thread` - A reference to a `MINIDUMP_THREAD` containing metadata about the thread. 614 | /// * `context` - The architecture-specific execution context of the thread. 615 | fn new(thread: &MINIDUMP_THREAD, context: ThreadContext) -> Self { 616 | Self { 617 | thread_id: thread.ThreadId, 618 | suspend_count: thread.SuspendCount, 619 | priority_class: thread.PriorityClass, 620 | priority: thread.Priority, 621 | teb: thread.Teb, 622 | context, 623 | } 624 | } 625 | 626 | /// Returns a reference to the execution context of the thread. 627 | pub fn context(&self) -> &ThreadContext { 628 | &self.context 629 | } 630 | 631 | /// Parses the list of threads from the `ThreadListStream`. 632 | /// 633 | /// # Arguments 634 | /// 635 | /// * `cursor` - Cursor positioned at the thread list stream. 636 | /// * `arch` - An optional `Arch` parameter that specifies the architecture (e.g., `X64` or `X86`). 637 | /// 638 | /// # Returns 639 | /// 640 | /// If the threads are parsed successfully. 641 | fn parse(cursor: &mut Cursor<&[u8]>, arch: &Option) -> Result { 642 | // Reads the thread list stream. 643 | let thread_list = MINIDUMP_THREAD_LIST::read(cursor)?; 644 | 645 | // Parses each thread entry in the list. 646 | let threads = thread_list 647 | .Threads 648 | .iter() 649 | .map(|thread| { 650 | // Extracts the thread context. 651 | let context_slice = UserDump::extract_raw_data(cursor, thread.ThreadContext)?; 652 | let context = arch 653 | .as_ref() 654 | .map(|arch| match arch { 655 | Arch::X64 => unsafe { 656 | let ctx = ptr::read_unaligned(context_slice.as_ptr() as *const CONTEXT_X64); 657 | ThreadContext::X64(Box::new(ctx)) 658 | }, 659 | Arch::X86 => unsafe { 660 | let ctx = ptr::read_unaligned(context_slice.as_ptr() as *const CONTEXT_X86); 661 | ThreadContext::X86(Box::new(ctx)) 662 | }, 663 | }) 664 | .ok_or(UserDmpError::InvalidContext)?; 665 | 666 | // Creates a new Thread. 667 | let thread = Thread::new(thread, context); 668 | Ok((thread.thread_id, thread)) 669 | }) 670 | .collect::>()?; 671 | 672 | Ok(threads) 673 | } 674 | } 675 | 676 | /// Represents a memory region in a minidump file, providing metadata about its state, 677 | /// protection level, allocation base, and type. 678 | #[derive(Default, Debug, Clone, PartialEq, Eq)] 679 | pub struct Memory<'a> { 680 | /// The range of memory addresses for this region. 681 | pub range: std::ops::Range, 682 | 683 | /// The base address where this memory allocation begins. 684 | pub allocation_base: u64, 685 | 686 | /// The protection attributes applied at the time of memory allocation. 687 | pub allocation_protect: u32, 688 | 689 | /// The current state of the memory region, indicating if it's committed, 690 | /// reserved, or free (e.g., `MEM_COMMIT` or `MEM_FREE`). 691 | pub state: u32, 692 | 693 | /// The protection level of the memory region (e.g., `PAGE_READWRITE`). 694 | pub protect: u32, 695 | 696 | /// The type of memory region (e.g., private, mapped, or image). 697 | pub type_: u32, 698 | 699 | /// The raw bytes of the memory region, as extracted from the minidump file. 700 | /// This data represents the actual content of the memory in this region 701 | /// and can be used for further analysis or reconstruction. 702 | pub data: &'a [u8], 703 | } 704 | 705 | impl<'a> Memory<'a> { 706 | /// Creates a new `Memory` instance from a `MINIDUMP_MEMORY_INFO` structure. 707 | /// 708 | /// # Arguments 709 | /// 710 | /// * `memory` - A reference to a `MINIDUMP_MEMORY_INFO` containing details about the memory region. 711 | /// 712 | /// # Panics 713 | /// 714 | /// This function will panic if the memory range is invalid (e.g., `start >= end`). 715 | fn new(memory: &MINIDUMP_MEMORY_INFO) -> Self { 716 | let range = std::ops::Range { 717 | start: memory.BaseAddress, 718 | end: memory.BaseAddress + memory.RegionSize, 719 | }; 720 | 721 | if range.is_empty() { 722 | panic!("Problem building the memory range") 723 | } 724 | 725 | Self { 726 | range, 727 | allocation_base: memory.AllocationBase, 728 | allocation_protect: memory.AllocationProtect, 729 | state: memory.State, 730 | protect: memory.Protect, 731 | type_: memory.Type, 732 | ..Default::default() 733 | } 734 | } 735 | 736 | /// Returns a textual description of the current memory state. 737 | /// 738 | /// The possible states are: 739 | /// - `MEM_COMMIT` (0x1000): Memory is committed and backed by physical storage or the page file. 740 | /// - `MEM_RESERVE` (0x2000): Memory is reserved but not yet committed. 741 | /// - `MEM_FREE` (0x10000): Memory is free and available for allocation. 742 | /// - `MEM_RESET` (0x8000): Memory has been reset to a clean state. 743 | /// - `MEM_TOP_DOWN` (0x100000): Allocation was made top-down from high memory addresses. 744 | /// 745 | /// # Returns 746 | /// 747 | /// Describing the state of the memory. 748 | pub fn state(&self) -> &str { 749 | if self.state == 0x10_000 { 750 | return ""; 751 | } 752 | 753 | match self.state { 754 | 0x1_000 => "MEM_COMMIT", 755 | 0x2_000 => "MEM_RESERVE", 756 | 0x10_000 => "MEM_FREE", 757 | 0x8_000 => "MEM_RESET", 758 | 0x100_000 => "MEM_TOP_DOWN", 759 | _ => "UNKNOWN", 760 | } 761 | } 762 | 763 | /// Returns a textual description of the memory type. 764 | /// 765 | /// The possible types are: 766 | /// - `MEM_PRIVATE` (0x20000): Memory is private to the process. 767 | /// - `MEM_MAPPED` (0x40000): Memory is mapped to a file. 768 | /// - `MEM_IMAGE` (0x1000000): Memory is associated with an executable image. 769 | /// 770 | /// # Returns 771 | /// 772 | /// Describing the type of the memory region. 773 | pub fn type_memory(&self) -> &str { 774 | match self.type_ { 775 | 0x20_000 => "MEM_PRIVATE", 776 | 0x40_000 => "MEM_MAPPED", 777 | 0x1_000_000 => "MEM_IMAGE", 778 | _ => "UNKNOWN", 779 | } 780 | } 781 | 782 | /// Returns the starting address of the memory region. 783 | /// 784 | /// # Returns 785 | /// 786 | /// The starting address of the memory region. 787 | pub fn start_addr(&self) -> u64 { 788 | self.range.start 789 | } 790 | 791 | /// Returns the ending address of the memory region (inclusive). 792 | /// 793 | /// # Returns 794 | /// 795 | /// The ending address of the memory region. 796 | pub fn end_addr(&self) -> u64 { 797 | self.range.end 798 | } 799 | 800 | /// Returns the size of the memory region in bytes. 801 | /// 802 | /// # Returns 803 | /// 804 | /// The size of the memory region. 805 | pub fn len(&self) -> u64 { 806 | self.range.end - self.range.start 807 | } 808 | 809 | /// Returns true if the memory region has zero size. 810 | pub fn is_empty(&self) -> bool { 811 | self.len() == 0 812 | } 813 | 814 | /// Merges two maps of memory regions into a single map. 815 | /// 816 | /// # Arguments 817 | /// 818 | /// * `memory_info` - Memory regions parsed from the `MemoryInfoListStream`. 819 | /// * `memory64` - Memory regions parsed from the `Memory64ListStream`. 820 | /// 821 | /// # Returns 822 | /// 823 | /// The combined map of memory regions. 824 | fn merge_memory(mut memory_info: Memorys<'a>, memory64: Memorys<'a>) -> Result> { 825 | // Insert memory64 regions into memory_info. 826 | for (address, memory) in memory64 { 827 | memory_info.insert(address, memory); 828 | } 829 | 830 | Ok(memory_info) 831 | } 832 | 833 | /// Parses memory information from the `MemoryInfoListStream`. 834 | /// 835 | /// # Arguments 836 | /// 837 | /// * `cursor` - Cursor positioned at the memory info list stream. 838 | /// 839 | /// # Returns 840 | /// 841 | /// A map of memory regions indexed by their base address. 842 | fn parser_memory_info(cursor: &mut Cursor<&'a [u8]>) -> Result> { 843 | // Reads the memory info list stream. 844 | let memory_info_list = MINIDUMP_MEMORY_INFO_LIST::read(cursor)?; 845 | 846 | // Parses each memory region in the list. 847 | let memorys = memory_info_list 848 | .Entries 849 | .iter() 850 | .map(|memory| { 851 | let memory_block = Memory::new(memory); 852 | 853 | Ok((memory.BaseAddress, memory_block)) 854 | }) 855 | .collect::>()?; 856 | 857 | Ok(memorys) 858 | } 859 | 860 | /// Parses memory information from the `Memory64ListStream`. 861 | /// 862 | /// # Arguments 863 | /// 864 | /// * `cursor` - Cursor positioned at the memory 64 list stream. 865 | /// 866 | /// # Returns 867 | /// 868 | /// A map of memory regions indexed by their base address. 869 | fn parser_memory64_list(cursor: &mut Cursor<&'a [u8]>) -> Result> { 870 | // Reads the Memory64List stream. 871 | let memory64_list = MINIDUMP_MEMORY64_LIST::read(cursor)?; 872 | 873 | let mut memorys = Memorys::new(); 874 | let mut current_rva = memory64_list.BaseRva; 875 | 876 | // Iterate over the memory descriptors in the list. 877 | for memory_descriptor in memory64_list.Ranges.iter() { 878 | let range = std::ops::Range { 879 | start: memory_descriptor.StartOfMemoryRange, 880 | end: memory_descriptor.StartOfMemoryRange + memory_descriptor.DataSize, 881 | }; 882 | 883 | // Seek to the data for the current memory descriptor. 884 | cursor.seek(io::SeekFrom::Start(current_rva))?; 885 | 886 | // Read the memory data. 887 | let data = { 888 | let data_slice = &cursor.get_ref()[(current_rva as usize)..]; 889 | &data_slice[..(memory_descriptor.DataSize as usize)] 890 | }; 891 | 892 | // Create a Memory instance. 893 | let memory = Memory { 894 | range, 895 | allocation_base: 0, 896 | allocation_protect: 0, 897 | state: 0, 898 | protect: 0, 899 | type_: 0, 900 | data, 901 | }; 902 | 903 | memorys.insert(memory_descriptor.StartOfMemoryRange, memory); 904 | 905 | // Update the current RVA for the next memory block. 906 | current_rva += memory_descriptor.DataSize; 907 | } 908 | 909 | Ok(memorys) 910 | } 911 | } 912 | 913 | /// Represents a handle in a minidump file, providing metadata about its type, 914 | /// object name, attributes, and granted access rights. 915 | #[derive(Default, Debug, Clone, PartialEq, Eq)] 916 | pub struct Handle { 917 | /// The unique identifier (handle value) for this object. 918 | pub handle: u64, 919 | 920 | /// The type name of the object associated with the handle (e.g., `File`, `Event`). 921 | type_name: Option, 922 | 923 | /// The object name associated with the handle, if available (e.g., file path). 924 | object_name: Option, 925 | 926 | /// The attributes of the handle (e.g., inheritance flags). 927 | pub attributes: u32, 928 | 929 | /// The access rights granted to this handle. 930 | pub granted_access: u32, 931 | } 932 | 933 | impl Handle { 934 | /// Creates a new `Handle` instance from a `MINIDUMP_HANDLE_DESCRIPTOR`. 935 | /// 936 | /// # Arguments 937 | /// 938 | /// * `type_name` - An optional string representing the type of the handle (e.g., `File`). 939 | /// * `object_name` - An optional string representing the name of the object (e.g., file path). 940 | /// * `handle` - A reference to a `MINIDUMP_HANDLE_DESCRIPTOR` structure containing handle details. 941 | pub fn new( 942 | type_name: Option, 943 | object_name: Option, 944 | handle: &MINIDUMP_HANDLE_DESCRIPTOR 945 | ) -> Self { 946 | Self { 947 | handle: handle.Handle, 948 | type_name, 949 | object_name, 950 | attributes: handle.Attributes, 951 | granted_access: handle.GrantedAccess, 952 | } 953 | } 954 | 955 | /// Returns the handle value as a hexadecimal string. 956 | /// 957 | /// # Returns 958 | /// 959 | /// The handle in hexadecimal format. 960 | pub fn handle(&self) -> String { 961 | format!("0x{:x}", self.handle) 962 | } 963 | 964 | /// Returns the type name of the object associated with the handle. 965 | /// 966 | /// # Returns 967 | /// 968 | /// Containing the type name, or `None` if unavailable. 969 | pub fn type_name(&self) -> Option<&str> { 970 | self.type_name.as_deref() 971 | } 972 | 973 | /// Returns the object name associated with the handle. 974 | /// 975 | /// # Returns 976 | /// 977 | /// Containing the object name, or `None` if unavailable. 978 | pub fn object_name(&self) -> Option<&str> { 979 | self.object_name.as_deref() 980 | } 981 | } 982 | 983 | impl<'a> MinidumpStream<'a> for Handle { 984 | type Output = Handles; 985 | 986 | /// Parses the list of handles from the `HandleDataStream`. 987 | /// 988 | /// # Arguments 989 | /// 990 | /// * `cursor` - Cursor positioned at the handle list stream. 991 | /// 992 | /// # Returns 993 | /// 994 | /// If the handles are parsed successfully. 995 | fn parse(cursor: &mut Cursor<&'a [u8]>) -> Result { 996 | // Reads the handle list stream. 997 | let handle_data = MINIDUMP_HANDLE_DATA_STREAM::read(cursor)?; 998 | 999 | // Parses each handle entry in the list. 1000 | let handles = handle_data 1001 | .Handles 1002 | .iter() 1003 | .map(|handle| { 1004 | let type_name = if handle.TypeNameRva != 0 { 1005 | // Seeks to the type name. 1006 | cursor.seek(io::SeekFrom::Start(handle.TypeNameRva.into()))?; 1007 | 1008 | // reading the structure MINIDUMP_STRING 1009 | let string = MINIDUMP_STRING::read(cursor)?; 1010 | 1011 | // Converts the name to UTF-8. 1012 | let name = String::from_utf16_lossy(&string.Buffer) 1013 | .trim_end_matches('\0') 1014 | .to_string(); 1015 | 1016 | Some(name) 1017 | } else { 1018 | None 1019 | }; 1020 | 1021 | let object_name = if handle.ObjectNameRva != 0 { 1022 | // Seeks to the object name. 1023 | cursor.seek(io::SeekFrom::Start(handle.ObjectNameRva.into()))?; 1024 | 1025 | // reading the structure MINIDUMP_STRING 1026 | let string = MINIDUMP_STRING::read(cursor)?; 1027 | 1028 | // Converts the name to UTF-8. 1029 | let name = String::from_utf16_lossy(&string.Buffer) 1030 | .trim_end_matches('\0') 1031 | .to_string(); 1032 | 1033 | Some(name) 1034 | } else { 1035 | None 1036 | }; 1037 | 1038 | // Creates a new Handle. 1039 | let handle = Handle::new(type_name, object_name, handle); 1040 | Ok((handle.handle, handle)) 1041 | }) 1042 | .collect::>()?; 1043 | 1044 | Ok(handles) 1045 | } 1046 | } 1047 | --------------------------------------------------------------------------------