├── .github └── workflows │ ├── build.yml │ └── deploy.yml ├── .gitignore ├── CONTRIBUTORS.md ├── Cargo.lock ├── Cargo.toml ├── LICENSE ├── README.md ├── install.sh ├── memflow-win32-defs ├── Cargo.toml ├── examples │ └── generate_offsets.rs └── src │ ├── kernel.rs │ ├── lib.rs │ └── offsets │ ├── builder.rs │ ├── mod.rs │ ├── offset_table.rs │ ├── pdb.rs │ ├── pdb │ └── data.rs │ └── symstore.rs └── memflow-win32 ├── Cargo.toml ├── build.rs ├── examples ├── dump_offsets.rs ├── keyboard_listen.rs ├── open_process.rs └── process_list.rs ├── offsets ├── 10_0_18362_X64_0AFB69F5FD264D54673570E37B38A3181.toml ├── 10_0_19041_X64_1C9875F76C8F0FBF3EB9A9D7C1C274061.toml ├── 10_0_19041_X64_9C00B19DBDE003DBFE4AB4216993C8431.toml ├── 10_0_19041_X64_BBED7C2955FBE4522AAA23F4B8677AD91.toml ├── 10_0_19041_X86_1B1D6AA205E1C87DC63A314ACAA50B491.toml ├── 10_0_19045_X64_5F0CF5D532F385333A9B4ABA25CA65961.toml ├── 10_0_22000_X86_55678BC384F099B6ED05E9E39046924A1.toml ├── 3_10_511_X86.toml ├── 4_0_1381_X86.toml ├── 5_2_3790_X64_82DCF67A38274C9CA99B60B421D2786D2.toml ├── 6_1_7601_X64_ECE191A20CFF4465AE46DF96C22638451.toml └── 6_1_7601_X86_684DA42A30CC450F81C535B4D18944B12.toml └── src ├── ida_signatures.rs ├── kernel ├── mod.rs ├── ntos.rs ├── ntos │ ├── pehelper.rs │ ├── x64.rs │ └── x86.rs ├── start_block.rs ├── start_block │ ├── aarch64.rs │ ├── x64.rs │ ├── x86.rs │ └── x86pae.rs └── sysproc.rs ├── lib.rs ├── offsets └── mod.rs ├── plugins.rs ├── win32.rs └── win32 ├── kernel.rs ├── kernel └── mem_map.rs ├── kernel_builder.rs ├── kernel_info.rs ├── keyboard.rs ├── module.rs ├── process.rs ├── unicode_string.rs ├── vat.rs └── vkey.rs /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: Build and test 2 | 3 | on: 4 | push: 5 | branches: 6 | - "**" 7 | pull_request: 8 | 9 | env: 10 | RUST_BACKTRACE: 1 11 | CARGO_TERM_COLOR: always 12 | 13 | jobs: 14 | build: 15 | runs-on: ${{ matrix.os }} 16 | strategy: 17 | matrix: 18 | os: [macos-latest, ubuntu-latest, windows-latest] 19 | flags: [--all-features, --no-default-features] 20 | steps: 21 | - uses: actions/checkout@v4 22 | - uses: dtolnay/rust-toolchain@stable 23 | - run: cargo build ${{ matrix.flags }} --release 24 | - run: cargo build ${{ matrix.flags }} --release --examples 25 | 26 | test: 27 | runs-on: ${{ matrix.os }} 28 | strategy: 29 | matrix: 30 | os: [macos-latest, ubuntu-latest, windows-latest] 31 | flags: [--all-features, --no-default-features] 32 | steps: 33 | - uses: actions/checkout@v4 34 | - uses: dtolnay/rust-toolchain@stable 35 | - run: cargo test ${{ matrix.flags }} --all 36 | 37 | lint: 38 | runs-on: ubuntu-latest 39 | steps: 40 | - uses: actions/checkout@v4 41 | - uses: dtolnay/rust-toolchain@stable 42 | with: 43 | components: rustfmt, clippy 44 | - run: cargo fmt -- --check 45 | - run: cargo clippy --all-targets --all-features 46 | -------------------------------------------------------------------------------- /.github/workflows/deploy.yml: -------------------------------------------------------------------------------- 1 | name: Deploy to memflow registry 2 | 3 | on: 4 | push: 5 | branches: 6 | - "**" 7 | 8 | env: 9 | RUST_BACKTRACE: 1 10 | CARGO_TERM_COLOR: always 11 | PLUGIN_NAME: memflow_win32 12 | 13 | jobs: 14 | deploy: 15 | name: ${{ matrix.platform.os_name }} with rust ${{ matrix.toolchain }} 16 | runs-on: ${{ matrix.platform.os }} 17 | strategy: 18 | fail-fast: false 19 | matrix: 20 | platform: 21 | - os_name: linux-x86_64 22 | os: ubuntu-24.04 23 | target: x86_64-unknown-linux-gnu 24 | - os_name: linux-aarch64 25 | os: ubuntu-24.04 26 | target: aarch64-unknown-linux-gnu 27 | - os_name: linux-arm 28 | os: ubuntu-24.04 29 | target: arm-unknown-linux-gnueabi 30 | - os_name: linux-i686 31 | os: ubuntu-24.04 32 | target: i686-unknown-linux-gnu 33 | - os_name: windows-aarch64 34 | os: windows-latest 35 | target: aarch64-pc-windows-msvc 36 | - os_name: windows-i686 37 | os: windows-latest 38 | target: i686-pc-windows-msvc 39 | - os_name: windows-x86_64 40 | os: windows-latest 41 | target: x86_64-pc-windows-msvc 42 | - os_name: macOS-x86_64 43 | os: macOS-latest 44 | target: x86_64-apple-darwin 45 | - os_name: macOS-aarch64 46 | os: macOS-latest 47 | target: aarch64-apple-darwin 48 | toolchain: 49 | - stable 50 | steps: 51 | - uses: actions/checkout@v4 52 | - name: Cache cargo & target directories 53 | uses: Swatinem/rust-cache@v2 54 | - name: Build binary 55 | uses: houseabsolute/actions-rust-cross@v0 56 | with: 57 | command: "build" 58 | target: ${{ matrix.platform.target }} 59 | toolchain: ${{ matrix.toolchain }} 60 | args: "--locked --release --all-features" 61 | strip: false 62 | 63 | - name: Install memflowup 64 | run: cargo install --git https://github.com/memflow/memflowup 65 | - run: echo "${{ secrets.MEMFLOW_REGISTRY_SIGNING_KEY}}" > ec-secp256k1-priv-key.pem 66 | - name: Upload plugin (linux) 67 | run: memflowup --skip-version-check push --token ${{ secrets.MEMFLOW_REGISTRY_TOKEN }} --priv-key ec-secp256k1-priv-key.pem --file target/${{ matrix.platform.target }}/release/lib${{ env. PLUGIN_NAME}}.so 68 | if: matrix.platform.os == 'ubuntu-24.04' 69 | - name: Upload plugin (windows) 70 | run: memflowup --skip-version-check push --token ${{ secrets.MEMFLOW_REGISTRY_TOKEN }} --priv-key ec-secp256k1-priv-key.pem --file target/${{ matrix.platform.target }}/release/${{ env. PLUGIN_NAME}}.dll 71 | if: matrix.platform.os == 'windows-latest' 72 | - name: Upload plugin (mac) 73 | run: memflowup --skip-version-check push --token ${{ secrets.MEMFLOW_REGISTRY_TOKEN }} --priv-key ec-secp256k1-priv-key.pem --file target/${{ matrix.platform.target }}/release/lib${{ env. PLUGIN_NAME}}.dylib 74 | if: matrix.platform.os == 'macOS-latest' 75 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | **/*.rs.bk 3 | *.swp 4 | *.so 5 | *.dll 6 | *.dylib 7 | .vscode 8 | *.so 9 | .vagrant 10 | TODO.md 11 | -------------------------------------------------------------------------------- /CONTRIBUTORS.md: -------------------------------------------------------------------------------- 1 | # Contributors 2 | > `memflow-win32` includes contributions from the following individuals 3 | 4 | - ko1N 5 | - h33p 6 | - ntdelta 7 | - xmr-slack 8 | - youduda 9 | - ConnorBP 10 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [profile.bench] 2 | debug = true 3 | 4 | [workspace] 5 | resolver = "1" 6 | 7 | members = [ 8 | "memflow-win32", 9 | "memflow-win32-defs", 10 | ] 11 | 12 | default-members = [ 13 | "memflow-win32", 14 | "memflow-win32-defs", 15 | ] 16 | 17 | # [patch.crates-io] 18 | # memflow = { path = "../memflow/memflow" } 19 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020-2022 ko1N 4 | Copyright (c) 2020-2022 Aurimas Blažulionis <0x60@pm.me> 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # memflow-win32 2 | [![Crates.io](https://img.shields.io/crates/v/memflow.svg)](https://crates.io/crates/memflow) 3 | ![build and test](https://github.com/memflow/memflow/workflows/Build%20and%20test/badge.svg?branch=dev) 4 | [![codecov](https://codecov.io/gh/memflow/memflow/branch/master/graph/badge.svg?token=XT7R158N6W)](https://codecov.io/gh/memflow/memflow) 5 | [![MIT licensed](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE) 6 | [![Discord](https://img.shields.io/discord/738739624976973835?color=%20%237289da&label=Discord)](https://discord.gg/afsEtMR) 7 | 8 | This crate provides integration for win32 targets for [memflow](https://github.com/memflow/memflow). This library can be used in addition to the memflow core itself read processes, modules, drivers, etc. 9 | 10 | Examples can be found in the `memflow-win32/examples` subdirectory. 11 | -------------------------------------------------------------------------------- /install.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | cargo build --release --all-features 4 | 5 | # install connector to system dir 6 | if [ ! -z "$1" ] && [ $1 = "--system" ]; then 7 | echo "installing connector system-wide in /usr/local/lib/memflow" 8 | if [[ ! -d /usr/local/lib/memflow ]]; then 9 | sudo mkdir /usr/local/lib/memflow 10 | fi 11 | sudo cp target/release/libmemflow_win32.so /usr/local/lib/memflow/libmemflow_win32.7.so 12 | fi 13 | 14 | # install connector in user dir 15 | echo "installing connector for user in ~/.local/lib/memflow" 16 | if [[ ! -d ~/.local/lib/memflow ]]; then 17 | mkdir -p ~/.local/lib/memflow 18 | fi 19 | cp target/release/libmemflow_win32.so ~/.local/lib/memflow/libmemflow_win32.7.so 20 | -------------------------------------------------------------------------------- /memflow-win32-defs/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "memflow-win32-defs" 3 | version = "0.2.0" 4 | authors = ["ko1N ", "Aurimas Blažulionis <0x60@pm.me>"] 5 | edition = "2018" 6 | description = "static offset templates for " 7 | documentation = "https://docs.rs/memflow-win32-defs" 8 | readme = "../README.md" 9 | homepage = "https://memflow.io" 10 | repository = "https://github.com/memflow/memflow-win32" 11 | license = "MIT" 12 | keywords = [ "memflow", "introspection", "memory", "dma" ] 13 | categories = [ "api-bindings", "memory-management", "os" ] 14 | 15 | [dependencies] 16 | memflow = { version = "0.2", default-features = false } 17 | log = { version = "0.4", default-features = false } 18 | no-std-compat = { version = "0.4", features = ["alloc"] } 19 | serde = { version = "1.0", default-features = false, optional = true, features = ["derive"] } 20 | 21 | # symbolstore 22 | dirs = { version = "5.0", optional = true } 23 | ureq = { version = "2.10", optional = true } 24 | pdb = { version = "0.8", optional = true } 25 | indicatif = { version = "0.17", optional = true } 26 | progress-streams = { version = "1.1", optional = true } 27 | 28 | [dev-dependencies] 29 | simplelog = "0.12" 30 | clap = { version = "4.5", features = ["cargo"] } 31 | toml = "0.8" 32 | 33 | [features] 34 | default = ["symstore", "download_progress"] 35 | std = ["no-std-compat/std"] 36 | symstore = ["dirs", "ureq", "pdb", "std"] 37 | download_progress = ["indicatif", "progress-streams"] 38 | 39 | [[example]] 40 | name = "generate_offsets" 41 | path = "examples/generate_offsets.rs" 42 | required-features = ["symstore", "serde"] 43 | -------------------------------------------------------------------------------- /memflow-win32-defs/examples/generate_offsets.rs: -------------------------------------------------------------------------------- 1 | use clap::*; 2 | use log::{error, Level}; 3 | use std::fs::{create_dir_all, File}; 4 | use std::io::Write; 5 | use std::path::PathBuf; 6 | 7 | use memflow_win32_defs::{kernel::*, offsets::*}; 8 | 9 | pub fn main() { 10 | let matches = Command::new("generate offsets example") 11 | .version(crate_version!()) 12 | .author(crate_authors!()) 13 | .arg(Arg::new("verbose").short('v').action(ArgAction::Count)) 14 | .arg( 15 | Arg::new("output") 16 | .short('o') 17 | .action(ArgAction::Set) 18 | .required(true), 19 | ) 20 | .get_matches(); 21 | 22 | let log_level = match matches.get_count("verbose") { 23 | 0 => Level::Error, 24 | 1 => Level::Warn, 25 | 2 => Level::Info, 26 | 3 => Level::Debug, 27 | 4 => Level::Trace, 28 | _ => Level::Trace, 29 | }; 30 | simplelog::TermLogger::init( 31 | log_level.to_level_filter(), 32 | simplelog::Config::default(), 33 | simplelog::TerminalMode::Stdout, 34 | simplelog::ColorChoice::Auto, 35 | ) 36 | .unwrap(); 37 | 38 | let win_ids = vec![ 39 | /* 40 | ( 41 | Win32Version::new(5, 2, 3790), 42 | Win32Guid::new("ntkrnlmp.pdb", "82DCF67A38274C9CA99B60B421D2786D2"), 43 | ), 44 | */ 45 | ( 46 | Win32Version::new(6, 1, 7601), 47 | Win32OffsetsArchitecture::X86, 48 | Win32Guid::new("ntkrpamp.pdb", "684DA42A30CC450F81C535B4D18944B12"), 49 | ), 50 | ( 51 | Win32Version::new(6, 1, 7601), 52 | Win32OffsetsArchitecture::X64, 53 | Win32Guid::new("ntkrnlmp.pdb", "ECE191A20CFF4465AE46DF96C22638451"), 54 | ), 55 | ( 56 | Win32Version::new(10, 0, 18362), 57 | Win32OffsetsArchitecture::X64, 58 | Win32Guid::new("ntkrnlmp.pdb", "0AFB69F5FD264D54673570E37B38A3181"), 59 | ), 60 | ( 61 | Win32Version::new(10, 0, 19041), 62 | Win32OffsetsArchitecture::X64, 63 | Win32Guid::new("ntkrnlmp.pdb", "BBED7C2955FBE4522AAA23F4B8677AD91"), 64 | ), 65 | ( 66 | Win32Version::new(10, 0, 19041), 67 | Win32OffsetsArchitecture::X64, 68 | Win32Guid::new("ntkrnlmp.pdb", "1C9875F76C8F0FBF3EB9A9D7C1C274061"), 69 | ), 70 | ( 71 | Win32Version::new(10, 0, 19041), 72 | Win32OffsetsArchitecture::X64, 73 | Win32Guid::new("ntkrnlmp.pdb", "9C00B19DBDE003DBFE4AB4216993C8431"), 74 | ), 75 | ( 76 | Win32Version::new(10, 0, 19045), 77 | Win32OffsetsArchitecture::X64, 78 | Win32Guid::new("ntkrnlmp.pdb", "5F0CF5D532F385333A9B4ABA25CA65961"), 79 | ), 80 | ( 81 | Win32Version::new(10, 0, 19041), 82 | Win32OffsetsArchitecture::X86, 83 | Win32Guid::new("ntkrpamp.pdb", "1B1D6AA205E1C87DC63A314ACAA50B491"), 84 | ), 85 | ( 86 | Win32Version::new(10, 0, 4026553840), 87 | Win32OffsetsArchitecture::X86, 88 | Win32Guid::new("ntkrnlmp.pdb", "55678BC384F099B6ED05E9E39046924A1"), 89 | ), 90 | ]; 91 | 92 | let out_dir = matches.get_one::("output").unwrap(); 93 | create_dir_all(out_dir).unwrap(); 94 | 95 | for win_id in win_ids.into_iter() { 96 | if let Ok(offsets) = Win32Offsets::builder() 97 | .symbol_store(SymbolStore::new()) 98 | .guid(win_id.2.clone()) 99 | .build() 100 | { 101 | let offset_file = Win32OffsetFile { 102 | header: Win32OffsetHeader { 103 | pdb_file_name: win_id.2.file_name.as_str().into(), 104 | pdb_guid: win_id.2.guid.as_str().into(), 105 | 106 | nt_major_version: win_id.0.major_version(), 107 | nt_minor_version: win_id.0.minor_version(), 108 | nt_build_number: win_id.0.build_number(), 109 | 110 | arch: win_id.1, 111 | }, 112 | 113 | offsets: offsets.0, 114 | }; 115 | 116 | let offsetstr = toml::to_string_pretty(&offset_file).unwrap(); 117 | 118 | let file_name = format!( 119 | "{}_{}_{}_{}_{}.toml", 120 | win_id.0.major_version(), 121 | win_id.0.minor_version(), 122 | win_id.0.build_number(), 123 | win_id.1, 124 | win_id.2.guid, 125 | ); 126 | 127 | let mut file = 128 | File::create([out_dir, &file_name].iter().collect::().as_path()).unwrap(); 129 | file.write_all(offsetstr.as_bytes()).unwrap(); 130 | } else { 131 | error!( 132 | "unable to find offsets for {} {:?} {:?}", 133 | win_id.0, win_id.1, win_id.2 134 | ) 135 | } 136 | } 137 | } 138 | -------------------------------------------------------------------------------- /memflow-win32-defs/src/kernel.rs: -------------------------------------------------------------------------------- 1 | use std::cmp::{Ord, Ordering, PartialEq}; 2 | use std::fmt; 3 | 4 | #[derive(Debug, Clone, Default)] 5 | #[cfg_attr(feature = "serde", derive(::serde::Serialize))] 6 | pub struct Win32Guid { 7 | pub file_name: String, 8 | pub guid: String, 9 | } 10 | 11 | impl Win32Guid { 12 | pub fn new(file_name: &str, guid: &str) -> Self { 13 | Self { 14 | file_name: file_name.to_string(), 15 | guid: guid.to_string(), 16 | } 17 | } 18 | } 19 | 20 | #[derive(Debug, Clone, Copy, Default)] 21 | #[cfg_attr(feature = "serde", derive(::serde::Serialize))] 22 | #[repr(C)] 23 | pub struct Win32Version { 24 | nt_major_version: u32, 25 | nt_minor_version: u32, 26 | nt_build_number: u32, 27 | } 28 | 29 | impl Win32Version { 30 | pub fn new(nt_major_version: u32, nt_minor_version: u32, nt_build_number: u32) -> Self { 31 | Self { 32 | nt_major_version, 33 | nt_minor_version, 34 | nt_build_number, 35 | } 36 | } 37 | 38 | #[inline] 39 | pub const fn mask_build_number(mut self) -> Self { 40 | self.nt_build_number &= 0xFFFF; 41 | self 42 | } 43 | 44 | #[inline] 45 | pub const fn major_version(&self) -> u32 { 46 | self.nt_major_version 47 | } 48 | 49 | #[inline] 50 | pub const fn minor_version(&self) -> u32 { 51 | self.nt_minor_version 52 | } 53 | 54 | #[inline] 55 | pub const fn build_number(&self) -> u32 { 56 | self.nt_build_number & 0xFFFF 57 | } 58 | 59 | #[inline] 60 | pub const fn is_checked_build(&self) -> bool { 61 | (self.nt_build_number & 0xF0000000) == 0xC0000000 62 | } 63 | 64 | #[inline] 65 | pub const fn as_tuple(&self) -> (u32, u32, u32) { 66 | ( 67 | self.major_version(), 68 | self.minor_version(), 69 | self.build_number(), 70 | ) 71 | } 72 | } 73 | 74 | impl PartialOrd for Win32Version { 75 | fn partial_cmp(&self, other: &Win32Version) -> Option { 76 | Some(self.cmp(other)) 77 | } 78 | } 79 | 80 | impl Ord for Win32Version { 81 | fn cmp(&self, other: &Win32Version) -> Ordering { 82 | if self.build_number() != 0 && other.build_number() != 0 { 83 | return self.build_number().cmp(&other.build_number()); 84 | } 85 | 86 | if self.nt_major_version != other.nt_major_version { 87 | self.nt_major_version.cmp(&other.nt_major_version) 88 | } else if self.nt_minor_version != other.nt_minor_version { 89 | self.nt_minor_version.cmp(&other.nt_minor_version) 90 | } else { 91 | Ordering::Equal 92 | } 93 | } 94 | } 95 | 96 | impl PartialEq for Win32Version { 97 | fn eq(&self, other: &Win32Version) -> bool { 98 | if self.nt_build_number != 0 && other.nt_build_number != 0 { 99 | self.nt_build_number.eq(&other.nt_build_number) 100 | } else { 101 | self.nt_major_version == other.nt_major_version 102 | && self.nt_minor_version == other.nt_minor_version 103 | } 104 | } 105 | } 106 | 107 | impl Eq for Win32Version {} 108 | 109 | impl From<(u32, u32)> for Win32Version { 110 | fn from((nt_major_version, nt_minor_version): (u32, u32)) -> Win32Version { 111 | Win32Version { 112 | nt_major_version, 113 | nt_minor_version, 114 | nt_build_number: 0, 115 | } 116 | } 117 | } 118 | 119 | impl From<(u32, u32, u32)> for Win32Version { 120 | fn from( 121 | (nt_major_version, nt_minor_version, nt_build_number): (u32, u32, u32), 122 | ) -> Win32Version { 123 | Win32Version { 124 | nt_major_version, 125 | nt_minor_version, 126 | nt_build_number, 127 | } 128 | } 129 | } 130 | 131 | impl fmt::Display for Win32Version { 132 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 133 | if self.nt_major_version != 0 { 134 | write!( 135 | f, 136 | "{}.{}.{}", 137 | self.major_version(), 138 | self.minor_version(), 139 | self.build_number() 140 | ) 141 | } else { 142 | write!(f, "{}", self.build_number()) 143 | } 144 | } 145 | } 146 | 147 | #[cfg(test)] 148 | mod tests { 149 | use core::cmp::Ordering; 150 | 151 | use super::Win32Version; 152 | 153 | #[test] 154 | fn win32_version_cmp() { 155 | let a = Win32Version::new(10, 0, 22621); // windows 11 156 | let b = Win32Version::new(10, 0, 4026550885); // windows 10 157 | assert_eq!(a.cmp(&b), Ordering::Greater); 158 | } 159 | } 160 | -------------------------------------------------------------------------------- /memflow-win32-defs/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![cfg_attr(not(feature = "std"), no_std)] 2 | extern crate no_std_compat as std; 3 | 4 | pub mod kernel; 5 | pub mod offsets; 6 | -------------------------------------------------------------------------------- /memflow-win32-defs/src/offsets/builder.rs: -------------------------------------------------------------------------------- 1 | use std::convert::TryFrom; 2 | 3 | #[cfg(feature = "symstore")] 4 | use super::symstore::SymbolStore; 5 | 6 | use super::offset_table::Win32OffsetFile; 7 | use super::{Win32Offsets, Win32OffsetsArchitecture}; 8 | 9 | use crate::kernel::{Win32Guid, Win32Version}; 10 | 11 | use memflow::error::{Error, ErrorKind, ErrorOrigin, Result}; 12 | 13 | pub struct Win32OffsetBuilder<'a> { 14 | #[cfg(feature = "symstore")] 15 | symbol_store: Option, 16 | 17 | guid: Option, 18 | winver: Option, 19 | arch: Option, 20 | 21 | offset_list: Option<&'a [Win32OffsetFile]>, 22 | } 23 | 24 | impl<'a> Default for Win32OffsetBuilder<'a> { 25 | fn default() -> Self { 26 | Self { 27 | #[cfg(feature = "symstore")] 28 | symbol_store: Some(SymbolStore::default()), 29 | 30 | guid: None, 31 | winver: None, 32 | arch: None, 33 | 34 | offset_list: None, 35 | } 36 | } 37 | } 38 | 39 | impl<'a> Win32OffsetBuilder<'a> { 40 | pub fn new() -> Self { 41 | Self::default() 42 | } 43 | 44 | pub fn build(self) -> Result { 45 | if self.guid.is_none() && self.winver.is_none() { 46 | return Err(Error(ErrorOrigin::OsLayer, ErrorKind::Configuration) 47 | .log_error("building win32 offsets requires either a guid or winver")); 48 | } 49 | 50 | // try to build via symbol store 51 | if let Ok(offs) = self.build_with_symbol_store() { 52 | return Ok(offs); 53 | } 54 | 55 | // use static offset list 56 | if let Ok(offs) = self.build_with_offset_list() { 57 | return Ok(offs); 58 | } 59 | 60 | Err(Error(ErrorOrigin::OsLayer, ErrorKind::Configuration) 61 | .log_error("no valid offset configuration found while building win32")) 62 | } 63 | 64 | fn build_with_offset_list(&self) -> Result { 65 | let offsets = self.offset_list.ok_or_else(|| { 66 | Error(ErrorOrigin::OsLayer, ErrorKind::Configuration) 67 | .log_error("no offset list supplied") 68 | })?; 69 | 70 | // Try matching exact guid 71 | if let Some(target_guid) = &self.guid { 72 | for offset in offsets.iter() { 73 | if let (Ok(file), Ok(guid)) = ( 74 | <&str>::try_from(&offset.header.pdb_file_name), 75 | <&str>::try_from(&offset.header.pdb_guid), 76 | ) { 77 | if target_guid.file_name == file && target_guid.guid == guid { 78 | return Ok(Win32Offsets(offset.offsets)); 79 | } 80 | } 81 | } 82 | } 83 | 84 | let mut closest_match = None; 85 | let mut prev_build_number = 0; 86 | 87 | // Try matching the newest build from that version that is not actually newer 88 | if let (Some(winver), Some(arch)) = (&self.winver, self.arch) { 89 | for offset in offsets.iter() { 90 | if winver.major_version() == offset.header.nt_major_version 91 | && winver.minor_version() == offset.header.nt_minor_version 92 | && winver.build_number() >= offset.header.nt_build_number 93 | && prev_build_number <= offset.header.nt_build_number 94 | && arch == offset.header.arch 95 | { 96 | prev_build_number = offset.header.nt_build_number; 97 | closest_match = Some(Win32Offsets(offset.offsets)); 98 | } 99 | } 100 | 101 | if prev_build_number != winver.build_number() { 102 | log::warn!( 103 | "no exact build number ({}) found! Closest match: {}", 104 | winver.build_number(), 105 | prev_build_number 106 | ); 107 | } 108 | } 109 | 110 | closest_match.ok_or_else(|| { 111 | Error(ErrorOrigin::OsLayer, ErrorKind::Configuration) 112 | .log_error("no valid offset configuration found while building win32") 113 | }) 114 | } 115 | 116 | #[cfg(feature = "symstore")] 117 | fn build_with_symbol_store(&self) -> Result { 118 | if let Some(store) = &self.symbol_store { 119 | if let Some(guid) = &self.guid { 120 | let pdb = store.load(guid)?; 121 | Win32Offsets::from_pdb_slice(&pdb[..]) 122 | } else { 123 | Err(Error(ErrorOrigin::OsLayer, ErrorKind::Configuration) 124 | .log_error("symbol store can only be used with a guid")) 125 | } 126 | } else { 127 | Err(Error(ErrorOrigin::OsLayer, ErrorKind::Configuration) 128 | .log_error("symbol store is disabled")) 129 | } 130 | } 131 | 132 | #[cfg(not(feature = "symstore"))] 133 | fn build_with_symbol_store(&self) -> Result { 134 | Err( 135 | Error(ErrorOrigin::OsLayer, ErrorKind::UnsupportedOptionalFeature) 136 | .log_error("symbol store is deactivated via a compilation feature"), 137 | ) 138 | } 139 | 140 | #[cfg(feature = "symstore")] 141 | pub fn symbol_store(mut self, symbol_store: SymbolStore) -> Self { 142 | self.symbol_store = Some(symbol_store); 143 | self 144 | } 145 | 146 | #[cfg(feature = "symstore")] 147 | pub fn no_symbol_store(mut self) -> Self { 148 | self.symbol_store = None; 149 | self 150 | } 151 | 152 | pub fn offset_list(mut self, offset_list: &'a [Win32OffsetFile]) -> Self { 153 | self.offset_list = Some(offset_list); 154 | self 155 | } 156 | 157 | pub fn guid(mut self, guid: Win32Guid) -> Self { 158 | self.guid = Some(guid); 159 | self 160 | } 161 | 162 | pub fn get_guid(&self) -> &Option { 163 | &self.guid 164 | } 165 | 166 | pub fn winver(mut self, winver: Win32Version) -> Self { 167 | self.winver = Some(winver); 168 | self 169 | } 170 | 171 | pub fn get_winver(&self) -> &Option { 172 | &self.winver 173 | } 174 | 175 | pub fn arch(mut self, arch: Win32OffsetsArchitecture) -> Self { 176 | self.arch = Some(arch); 177 | self 178 | } 179 | 180 | pub fn get_arch(&self) -> &Option { 181 | &self.arch 182 | } 183 | } 184 | -------------------------------------------------------------------------------- /memflow-win32-defs/src/offsets/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod builder; 2 | pub use builder::Win32OffsetBuilder; 3 | 4 | #[cfg(feature = "symstore")] 5 | pub mod pdb; 6 | #[cfg(feature = "symstore")] 7 | pub mod symstore; 8 | 9 | pub mod offset_table; 10 | #[doc(hidden)] 11 | pub use offset_table::{ 12 | MmVadOffsetTable, Win32OffsetFile, Win32OffsetHeader, Win32OffsetTable, 13 | Win32OffsetsArchitecture, 14 | }; 15 | 16 | #[cfg(feature = "symstore")] 17 | pub use { 18 | self::pdb::{PdbStruct, PdbSymbols}, 19 | symstore::*, 20 | }; 21 | 22 | use std::prelude::v1::*; 23 | 24 | use memflow::architecture::ArchitectureIdent; 25 | 26 | // those only required when compiling under std environment 27 | #[cfg(feature = "std")] 28 | use crate::kernel::Win32Guid; 29 | #[cfg(feature = "std")] 30 | use memflow::error::{Error, ErrorKind, ErrorOrigin, Result}; 31 | #[cfg(feature = "std")] 32 | use std::{fs::File, io::Read, path::Path}; 33 | 34 | #[derive(Debug, Copy, Clone)] 35 | #[repr(C)] 36 | #[cfg_attr(feature = "serde", derive(::serde::Serialize))] 37 | pub struct Win32ArchOffsets { 38 | pub peb_ldr: usize, // _PEB::Ldr 39 | pub peb_process_params: usize, // _PEB::ProcessParameters 40 | pub ldr_list: usize, // _PEB_LDR_DATA::InLoadOrderModuleList 41 | pub ldr_data_base: usize, // _LDR_DATA_TABLE_ENTRY::DllBase 42 | pub ldr_data_size: usize, // _LDR_DATA_TABLE_ENTRY::SizeOfImage 43 | pub ldr_data_full_name: usize, // _LDR_DATA_TABLE_ENTRY::FullDllName 44 | pub ldr_data_base_name: usize, // _LDR_DATA_TABLE_ENTRY::BaseDllName 45 | pub ppm_image_path_name: usize, // _RTL_USER_PROCESS_PARAMETERS::ImagePathName 46 | pub ppm_command_line: usize, // _RTL_USER_PROCESS_PARAMETERS::CommandLine 47 | } 48 | 49 | pub const X86: Win32ArchOffsets = Win32ArchOffsets { 50 | peb_ldr: 0xc, 51 | peb_process_params: 0x10, 52 | ldr_list: 0xc, 53 | ldr_data_base: 0x18, 54 | ldr_data_size: 0x20, 55 | ldr_data_full_name: 0x24, 56 | ldr_data_base_name: 0x2c, 57 | ppm_image_path_name: 0x38, 58 | ppm_command_line: 0x40, 59 | }; 60 | 61 | pub const X64: Win32ArchOffsets = Win32ArchOffsets { 62 | peb_ldr: 0x18, 63 | peb_process_params: 0x20, 64 | ldr_list: 0x10, 65 | ldr_data_base: 0x30, 66 | ldr_data_size: 0x40, 67 | ldr_data_full_name: 0x48, 68 | ldr_data_base_name: 0x58, 69 | ppm_image_path_name: 0x60, 70 | ppm_command_line: 0x70, 71 | }; 72 | 73 | pub const AARCH64: Win32ArchOffsets = Win32ArchOffsets { 74 | peb_ldr: 0x18, 75 | peb_process_params: 0x20, 76 | ldr_list: 0x10, 77 | ldr_data_base: 0x30, 78 | ldr_data_size: 0x40, 79 | ldr_data_full_name: 0x48, 80 | ldr_data_base_name: 0x58, 81 | ppm_image_path_name: 0x60, 82 | ppm_command_line: 0x70, 83 | }; 84 | 85 | impl Win32OffsetsArchitecture { 86 | #[inline] 87 | fn offsets(&self) -> &'static Win32ArchOffsets { 88 | match self { 89 | Win32OffsetsArchitecture::X64 => &X64, 90 | Win32OffsetsArchitecture::X86 => &X86, 91 | Win32OffsetsArchitecture::AArch64 => &AARCH64, 92 | } 93 | } 94 | } 95 | 96 | impl From for Win32ArchOffsets { 97 | fn from(arch: ArchitectureIdent) -> Win32ArchOffsets { 98 | *Win32OffsetsArchitecture::from(arch).offsets() 99 | } 100 | } 101 | 102 | #[repr(transparent)] 103 | #[derive(Debug, Clone)] 104 | #[cfg_attr(feature = "serde", derive(::serde::Serialize))] 105 | pub struct Win32Offsets(pub Win32OffsetTable); 106 | 107 | impl From for Win32Offsets { 108 | fn from(other: Win32OffsetTable) -> Self { 109 | Self(other) 110 | } 111 | } 112 | 113 | impl From for Win32OffsetTable { 114 | fn from(other: Win32Offsets) -> Self { 115 | other.0 116 | } 117 | } 118 | 119 | impl From for Win32OffsetsArchitecture { 120 | fn from(arch: ArchitectureIdent) -> Win32OffsetsArchitecture { 121 | match arch { 122 | ArchitectureIdent::X86(32, _) => Self::X86, 123 | ArchitectureIdent::X86(64, _) => Self::X64, 124 | ArchitectureIdent::AArch64(_) => Self::AArch64, 125 | _ => panic!("Invalid architecture specified"), 126 | } 127 | } 128 | } 129 | 130 | impl Win32Offsets { 131 | #[cfg(feature = "symstore")] 132 | pub fn from_pdb>(pdb_path: P) -> Result { 133 | let mut file = File::open(pdb_path).map_err(|_| { 134 | Error(ErrorOrigin::OsLayer, ErrorKind::Offset) 135 | .log_warn("unable to open user-supplied pdb file") 136 | })?; 137 | let mut buffer = Vec::new(); 138 | file.read_to_end(&mut buffer).map_err(|_| { 139 | Error(ErrorOrigin::OsLayer, ErrorKind::Offset) 140 | .log_warn("unable to read user-supplied pdb file") 141 | })?; 142 | Self::from_pdb_slice(&buffer[..]) 143 | } 144 | 145 | #[cfg(feature = "symstore")] 146 | pub fn from_pdb_slice(pdb_slice: &[u8]) -> Result { 147 | let symbols = PdbSymbols::new(pdb_slice).map_err(|_| { 148 | Error(ErrorOrigin::OsLayer, ErrorKind::Offset).log_warn("Symbols not found") 149 | })?; 150 | let list = PdbStruct::new(pdb_slice, "_LIST_ENTRY").map_err(|_| { 151 | Error(ErrorOrigin::OsLayer, ErrorKind::Offset).log_warn("_LIST_ENTRY not found") 152 | })?; 153 | let kproc = PdbStruct::new(pdb_slice, "_KPROCESS").map_err(|_| { 154 | Error(ErrorOrigin::OsLayer, ErrorKind::Offset).log_warn("_KPROCESS not found") 155 | })?; 156 | let eproc = PdbStruct::new(pdb_slice, "_EPROCESS").map_err(|_| { 157 | Error(ErrorOrigin::OsLayer, ErrorKind::Offset).log_warn("_EPROCESS not found") 158 | })?; 159 | let ethread = PdbStruct::new(pdb_slice, "_ETHREAD").map_err(|_| { 160 | Error(ErrorOrigin::OsLayer, ErrorKind::Offset).log_warn("_ETHREAD not found") 161 | })?; 162 | let kthread = PdbStruct::new(pdb_slice, "_KTHREAD").map_err(|_| { 163 | Error(ErrorOrigin::OsLayer, ErrorKind::Offset).log_warn("_KTHREAD not found") 164 | })?; 165 | let teb = PdbStruct::new(pdb_slice, "_TEB").map_err(|_| { 166 | Error(ErrorOrigin::OsLayer, ErrorKind::Offset).log_warn("_TEB not found") 167 | })?; 168 | let mm_vad = PdbStruct::new(pdb_slice, "_MMVAD_SHORT").map_err(|_| { 169 | Error(ErrorOrigin::OsLayer, ErrorKind::Offset).log_warn("_MMVAD_SHORT not found") 170 | })?; 171 | let mm_vad_flags = PdbStruct::new(pdb_slice, "_MMVAD_FLAGS").map_err(|_| { 172 | Error(ErrorOrigin::OsLayer, ErrorKind::Offset).log_warn("_MMVAD_FLAGS not found") 173 | })?; 174 | 175 | let phys_mem_block = symbols 176 | .find_symbol("MmPhysicalMemoryBlock") 177 | .or_else(|| symbols.find_symbol("_MmPhysicalMemoryBlock")) 178 | .copied() 179 | .unwrap_or(0); 180 | 181 | let list_blink = list 182 | .find_field("Blink") 183 | .ok_or_else(|| { 184 | Error(ErrorOrigin::OsLayer, ErrorKind::Offset) 185 | .log_warn("_LIST_ENTRY::Blink not found") 186 | })? 187 | .offset as _; 188 | 189 | let eproc_link = eproc 190 | .find_field("ActiveProcessLinks") 191 | .ok_or_else(|| { 192 | Error(ErrorOrigin::OsLayer, ErrorKind::Offset) 193 | .log_warn("_EPROCESS::ActiveProcessLinks not found") 194 | })? 195 | .offset as _; 196 | 197 | let kproc_dtb = kproc 198 | .find_field("DirectoryTableBase") 199 | .ok_or_else(|| { 200 | Error(ErrorOrigin::OsLayer, ErrorKind::Offset) 201 | .log_warn("_KPROCESS::DirectoryTableBase not found") 202 | })? 203 | .offset as _; 204 | let eproc_pid = eproc 205 | .find_field("UniqueProcessId") 206 | .ok_or_else(|| { 207 | Error(ErrorOrigin::OsLayer, ErrorKind::Offset) 208 | .log_warn("_EPROCESS::UniqueProcessId not found") 209 | })? 210 | .offset as _; 211 | let eproc_name = eproc 212 | .find_field("ImageFileName") 213 | .ok_or_else(|| { 214 | Error(ErrorOrigin::OsLayer, ErrorKind::Offset) 215 | .log_warn("_EPROCESS::ImageFileName not found") 216 | })? 217 | .offset as _; 218 | let eproc_peb = eproc 219 | .find_field("Peb") 220 | .ok_or_else(|| { 221 | Error(ErrorOrigin::OsLayer, ErrorKind::Offset).log_warn("_EPROCESS::Peb not found") 222 | })? 223 | .offset as _; 224 | let eproc_section_base = eproc 225 | .find_field("SectionBaseAddress") 226 | .ok_or_else(|| { 227 | Error(ErrorOrigin::OsLayer, ErrorKind::Offset) 228 | .log_warn("_EPROCESS::SectionBaseAddress not found") 229 | })? 230 | .offset as _; 231 | let eproc_exit_status = eproc 232 | .find_field("ExitStatus") 233 | .ok_or_else(|| { 234 | Error(ErrorOrigin::OsLayer, ErrorKind::Offset) 235 | .log_warn("_EPROCESS::ExitStatus not found") 236 | })? 237 | .offset as _; 238 | let eproc_thread_list = eproc 239 | .find_field("ThreadListHead") 240 | .ok_or_else(|| { 241 | Error(ErrorOrigin::OsLayer, ErrorKind::Offset) 242 | .log_warn("_EPROCESS::ThreadListHead not found") 243 | })? 244 | .offset as _; 245 | 246 | // windows 10 uses an uppercase W whereas older windows versions (windows 7) uses a lowercase w 247 | let eproc_wow64 = match eproc 248 | .find_field("WoW64Process") 249 | .or_else(|| eproc.find_field("Wow64Process")) 250 | { 251 | Some(f) => f.offset as _, 252 | None => 0, 253 | }; 254 | 255 | // threads 256 | let kthread_teb = kthread 257 | .find_field("Teb") 258 | .ok_or_else(|| { 259 | Error(ErrorOrigin::OsLayer, ErrorKind::Offset).log_warn("_KTHREAD::Teb not found") 260 | })? 261 | .offset as _; 262 | let ethread_list_entry = ethread 263 | .find_field("ThreadListEntry") 264 | .ok_or_else(|| { 265 | Error(ErrorOrigin::OsLayer, ErrorKind::Offset) 266 | .log_warn("_ETHREAD::ThreadListEntry not found") 267 | })? 268 | .offset as _; 269 | let teb_peb = teb 270 | .find_field("ProcessEnvironmentBlock") 271 | .ok_or_else(|| { 272 | Error(ErrorOrigin::OsLayer, ErrorKind::Offset) 273 | .log_warn("_TEB::ProcessEnvironmentBlock not found") 274 | })? 275 | .offset as _; 276 | let teb_peb_x86 = if let Ok(teb32) = PdbStruct::new(pdb_slice, "_TEB32").map_err(|_| { 277 | Error(ErrorOrigin::OsLayer, ErrorKind::Offset).log_warn("_TEB32 not found") 278 | }) { 279 | teb32 280 | .find_field("ProcessEnvironmentBlock") 281 | .ok_or_else(|| { 282 | Error(ErrorOrigin::OsLayer, ErrorKind::Offset) 283 | .log_warn("_TEB32::ProcessEnvironmentBlock not found") 284 | })? 285 | .offset as _ 286 | } else { 287 | 0 288 | }; 289 | 290 | let eproc_vad_root = eproc 291 | .find_field("VadRoot") // MM_AVL_TABLE *PhysicalVadRoot / MM_AVL_TABLE VadRoot / RTL_AVL_TREE VadRoot 292 | .ok_or_else(|| { 293 | Error(ErrorOrigin::OsLayer, ErrorKind::Offset) 294 | .log_warn("_EPROCESS::VadRoot not found") 295 | })? 296 | .offset as _; 297 | 298 | // On older versions VadNode was inlined into the structure - LeftChild being the first 299 | // field of a binary tree. 300 | let vad_node = mm_vad 301 | .find_field("VadNode") 302 | .or_else(|| mm_vad.find_field("LeftChild")) 303 | .map(|f| f.offset) 304 | .unwrap_or(0) as _; 305 | 306 | let starting_vpn = mm_vad 307 | .find_field("StartingVpn") 308 | .map(|f| f.offset) 309 | .unwrap_or(0) as _; 310 | let ending_vpn = mm_vad 311 | .find_field("EndingVpn") 312 | .map(|f| f.offset) 313 | .unwrap_or(0) as _; 314 | let starting_vpn_high = mm_vad 315 | .find_field("StartingVpnHigh") 316 | .map(|f| f.offset) 317 | .unwrap_or(0) as _; 318 | let ending_vpn_high = mm_vad 319 | .find_field("EndingVpnHigh") 320 | .map(|f| f.offset) 321 | .unwrap_or(0) as _; 322 | let u = mm_vad.find_field("u").map(|f| f.offset).unwrap_or(0) as _; 323 | 324 | let protection_bit = mm_vad_flags 325 | .find_field("Protection") 326 | .map(|f| f.bit_offset) 327 | .unwrap_or(0) as _; 328 | 329 | Ok(Self(Win32OffsetTable { 330 | list_blink, 331 | eproc_link, 332 | 333 | phys_mem_block, 334 | 335 | kproc_dtb, 336 | 337 | eproc_pid, 338 | eproc_name, 339 | eproc_peb, 340 | eproc_section_base, 341 | eproc_exit_status, 342 | eproc_thread_list, 343 | eproc_wow64, 344 | eproc_vad_root, 345 | 346 | kthread_teb, 347 | ethread_list_entry, 348 | teb_peb, 349 | teb_peb_x86, 350 | 351 | mmvad: MmVadOffsetTable { 352 | vad_node, 353 | starting_vpn, 354 | ending_vpn, 355 | starting_vpn_high, 356 | ending_vpn_high, 357 | u, 358 | protection_bit, 359 | }, 360 | })) 361 | } 362 | 363 | /// _LIST_ENTRY::Blink offset 364 | pub fn list_blink(&self) -> usize { 365 | self.0.list_blink as usize 366 | } 367 | /// _LIST_ENTRY::Flink offset 368 | pub fn eproc_link(&self) -> usize { 369 | self.0.eproc_link as usize 370 | } 371 | 372 | /// MmPhysicalMemoryBlock offset 373 | pub fn phys_mem_block(&self) -> usize { 374 | self.0.phys_mem_block as usize 375 | } 376 | 377 | /// _KPROCESS::DirectoryTableBase offset 378 | /// Exists since version 3.10 379 | pub fn kproc_dtb(&self) -> usize { 380 | self.0.kproc_dtb as usize 381 | } 382 | /// _EPROCESS::UniqueProcessId offset 383 | /// Exists since version 3.10 384 | pub fn eproc_pid(&self) -> usize { 385 | self.0.eproc_pid as usize 386 | } 387 | /// _EPROCESS::ImageFileName offset 388 | /// Exists since version 3.10 389 | pub fn eproc_name(&self) -> usize { 390 | self.0.eproc_name as usize 391 | } 392 | /// _EPROCESS::Peb offset 393 | /// Exists since version 5.10 394 | pub fn eproc_peb(&self) -> usize { 395 | self.0.eproc_peb as usize 396 | } 397 | /// _EPROCESS::SectionBaseAddress offset 398 | /// Exists since version 3.10 399 | pub fn eproc_section_base(&self) -> usize { 400 | self.0.eproc_section_base as usize 401 | } 402 | /// _EPROCESS::ExitStatus offset 403 | /// Exists since version 3.10 404 | pub fn eproc_exit_status(&self) -> usize { 405 | self.0.eproc_exit_status as usize 406 | } 407 | /// _EPROCESS::ThreadListHead offset 408 | /// Exists since version 5.10 409 | pub fn eproc_thread_list(&self) -> usize { 410 | self.0.eproc_thread_list as usize 411 | } 412 | /// _EPROCESS::VadRoot offset 413 | /// Exists since version 5.0 414 | pub fn eproc_wow64(&self) -> usize { 415 | self.0.eproc_wow64 as usize 416 | } 417 | /// _EPROCESS::WoW64Process offset 418 | /// Exists since version xxx 419 | pub fn eproc_vad_root(&self) -> usize { 420 | self.0.eproc_vad_root as usize 421 | } 422 | 423 | /// _KTHREAD::Teb offset 424 | /// Exists since version 6.2 425 | pub fn kthread_teb(&self) -> usize { 426 | self.0.kthread_teb as usize 427 | } 428 | /// _ETHREAD::ThreadListEntry offset 429 | /// Exists since version 6.2 430 | pub fn ethread_list_entry(&self) -> usize { 431 | self.0.ethread_list_entry as usize 432 | } 433 | /// _TEB::ProcessEnvironmentBlock offset 434 | /// Exists since version x.x 435 | pub fn teb_peb(&self) -> usize { 436 | self.0.teb_peb as usize 437 | } 438 | /// _TEB32::ProcessEnvironmentBlock offset 439 | /// Exists since version x.x 440 | pub fn teb_peb_x86(&self) -> usize { 441 | self.0.teb_peb_x86 as usize 442 | } 443 | 444 | /// _MMVAD_SHORT offsets 445 | pub fn mm_vad(&self) -> MmVadOffsetTable { 446 | self.0.mmvad 447 | } 448 | 449 | pub fn builder<'a>() -> Win32OffsetBuilder<'a> { 450 | Win32OffsetBuilder::default() 451 | } 452 | } 453 | 454 | #[cfg(test)] 455 | mod tests { 456 | use super::*; 457 | 458 | // this test is not ideal for the CI so it's disabled for now. 459 | /* 460 | #[test] 461 | fn download_pdb() { 462 | let guid = Win32Guid { 463 | file_name: "ntkrnlmp.pdb".to_string(), 464 | guid: "3844DBB920174967BE7AA4A2C20430FA2".to_string(), 465 | }; 466 | let offsets = Win32Offsets::builder() 467 | .symbol_store(SymbolStore::new().no_cache()) 468 | .guid(guid) 469 | .build() 470 | .unwrap(); 471 | 472 | assert_eq!(offsets.0.list_blink, 8); 473 | assert_eq!(offsets.0.eproc_link, 392); 474 | 475 | assert_eq!(offsets.0.kproc_dtb, 40); 476 | 477 | assert_eq!(offsets.0.eproc_pid, 384); 478 | assert_eq!(offsets.0.eproc_name, 736); 479 | assert_eq!(offsets.0.eproc_peb, 824); 480 | assert_eq!(offsets.0.eproc_thread_list, 776); 481 | assert_eq!(offsets.0.eproc_wow64, 800); 482 | 483 | assert_eq!(offsets.0.kthread_teb, 184); 484 | assert_eq!(offsets.0.ethread_list_entry, 1056); 485 | assert_eq!(offsets.0.teb_peb, 96); 486 | assert_eq!(offsets.0.teb_peb_x86, 48); 487 | } 488 | */ 489 | } 490 | -------------------------------------------------------------------------------- /memflow-win32-defs/src/offsets/offset_table.rs: -------------------------------------------------------------------------------- 1 | use std::prelude::v1::*; 2 | 3 | use std::convert::TryFrom; 4 | use std::str; 5 | 6 | use memflow::dataview::Pod; 7 | 8 | /// Describes an offset file. 9 | /// At compile time this crate will create a binary blob of all 10 | /// TOML files contained in the memflow-win32/offsets/ folder 11 | /// and merge the byte buffer directly into the build. 12 | /// 13 | /// This byte buffer is then transmuted back into a slice of 14 | /// Win32OffsetFile structs and parsed as a backup in case 15 | /// no symbol store is available. 16 | /// 17 | /// To get loaded properly this struct guarantees a certain alignment and no padding. 18 | /// This is enforced due to a compile time assert as well as the Pod derive itself. 19 | /// Especially in the case of cross-compilation where the target architecture 20 | /// is different from the architecture memflow is built with this could give potential issues. 21 | /// 22 | // # Safety 23 | // This struct guarantees that it does not contain any padding. 24 | #[repr(C, align(4))] 25 | #[derive(Debug, Clone, Pod)] 26 | #[cfg_attr(feature = "serde", derive(::serde::Serialize, ::serde::Deserialize))] 27 | pub struct Win32OffsetFile { 28 | pub header: Win32OffsetHeader, 29 | pub offsets: Win32OffsetTable, 30 | } 31 | 32 | #[repr(C, align(4))] 33 | #[derive(Debug, Clone, Pod)] 34 | #[cfg_attr(feature = "serde", derive(::serde::Serialize, ::serde::Deserialize))] 35 | pub struct Win32OffsetHeader { 36 | // Win32Guid 37 | #[cfg_attr(feature = "serde", serde(default))] 38 | pub pdb_file_name: BinaryString<128>, 39 | #[cfg_attr(feature = "serde", serde(default))] 40 | pub pdb_guid: BinaryString<128>, 41 | 42 | // Win32Version 43 | pub nt_major_version: u32, 44 | pub nt_minor_version: u32, 45 | pub nt_build_number: u32, 46 | 47 | // Architecture 48 | pub arch: Win32OffsetsArchitecture, 49 | } 50 | 51 | const _: [(); std::mem::size_of::<[Win32OffsetHeader; 16]>()] = 52 | [(); 16 * std::mem::size_of::()]; 53 | 54 | const _: [(); std::mem::size_of::<[Win32OffsetTable; 16]>()] = 55 | [(); 16 * std::mem::size_of::()]; 56 | 57 | const _: [(); std::mem::size_of::<[Win32OffsetFile; 16]>()] = 58 | [(); 16 * std::mem::size_of::()]; 59 | 60 | #[repr(u32)] 61 | #[derive(Clone, Copy, Debug, PartialEq, Eq)] 62 | #[cfg_attr(feature = "serde", derive(::serde::Serialize, ::serde::Deserialize))] 63 | pub enum Win32OffsetsArchitecture { 64 | X86 = 0, 65 | X64 = 1, 66 | AArch64 = 2, 67 | } 68 | 69 | impl std::fmt::Display for Win32OffsetsArchitecture { 70 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 71 | write!(f, "{self:?}") 72 | } 73 | } 74 | 75 | unsafe impl Pod for Win32OffsetsArchitecture {} 76 | 77 | #[derive(Clone)] 78 | pub struct BinaryString(pub [u8; N]); 79 | 80 | impl std::fmt::Debug for BinaryString { 81 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 82 | write!(f, "{:?}", <&str>::try_from(self)) 83 | } 84 | } 85 | 86 | impl Default for BinaryString { 87 | fn default() -> Self { 88 | (&[][..]).into() 89 | } 90 | } 91 | 92 | impl<'a, const N: usize> From<&'a [u8]> for BinaryString { 93 | fn from(other: &'a [u8]) -> Self { 94 | let mut arr = [0; N]; 95 | 96 | arr[..other.len()].copy_from_slice(other); 97 | 98 | Self(arr) 99 | } 100 | } 101 | 102 | impl<'a, const N: usize> TryFrom<&'a BinaryString> for &'a str { 103 | type Error = std::str::Utf8Error; 104 | fn try_from(other: &'a BinaryString) -> Result { 105 | Ok(str::from_utf8(&other.0)? 106 | .split_terminator('\0') 107 | .next() 108 | .unwrap()) 109 | } 110 | } 111 | 112 | impl<'a, const N: usize> From<&'a str> for BinaryString { 113 | fn from(other: &'a str) -> Self { 114 | let mut arr = [0; N]; 115 | 116 | arr[..other.len()].copy_from_slice(other.as_bytes()); 117 | 118 | Self(arr) 119 | } 120 | } 121 | 122 | impl From for BinaryString { 123 | fn from(other: String) -> Self { 124 | Self::from(other.as_str()) 125 | } 126 | } 127 | 128 | unsafe impl Pod for BinaryString {} 129 | 130 | #[cfg(feature = "serde")] 131 | impl ::serde::Serialize for BinaryString { 132 | fn serialize(&self, serializer: S) -> Result 133 | where 134 | S: ::serde::Serializer, 135 | { 136 | serializer.serialize_str( 137 | <&str>::try_from(self) 138 | .map_err(|_| ::serde::ser::Error::custom("invalid UTF-8 characters"))?, 139 | ) 140 | } 141 | } 142 | 143 | #[cfg(feature = "serde")] 144 | impl<'de, const N: usize> ::serde::de::Deserialize<'de> for BinaryString { 145 | fn deserialize(deserializer: D) -> Result 146 | where 147 | D: ::serde::de::Deserializer<'de>, 148 | { 149 | struct BinaryStringVisitor; 150 | 151 | impl<'de, const N: usize> ::serde::de::Visitor<'de> for BinaryStringVisitor { 152 | type Value = [u8; N]; 153 | 154 | fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { 155 | formatter.write_str("a string containing json data") 156 | } 157 | 158 | fn visit_str(self, v: &str) -> Result 159 | where 160 | E: ::serde::de::Error, 161 | { 162 | // unfortunately we lose some typed information 163 | // from errors deserializing the json string 164 | let mut result = [0u8; N]; 165 | 166 | result[..v.len()].copy_from_slice(v.as_bytes()); 167 | 168 | Ok(result) 169 | } 170 | } 171 | 172 | // use our visitor to deserialize an `ActualValue` 173 | let inner: [u8; N] = deserializer.deserialize_any(BinaryStringVisitor)?; 174 | Ok(Self(inner)) 175 | } 176 | } 177 | 178 | #[repr(C, align(4))] 179 | #[derive(Debug, Copy, Clone, Pod)] 180 | #[cfg_attr(feature = "serde", derive(::serde::Serialize, ::serde::Deserialize))] 181 | pub struct Win32OffsetTable { 182 | pub list_blink: u32, 183 | pub eproc_link: u32, 184 | 185 | pub phys_mem_block: u32, 186 | 187 | /// Since version 3.10 188 | pub kproc_dtb: u32, 189 | /// Since version 3.10 190 | pub eproc_pid: u32, 191 | /// Since version 3.10 192 | pub eproc_name: u32, 193 | /// Since version 5.10 194 | pub eproc_peb: u32, 195 | /// Since version 3.10 196 | pub eproc_section_base: u32, 197 | /// Since version 3.10 198 | pub eproc_exit_status: u32, 199 | /// Since version 5.10 200 | pub eproc_thread_list: u32, 201 | /// Since version 5.0 202 | pub eproc_wow64: u32, 203 | /// Since version xxx 204 | pub eproc_vad_root: u32, 205 | 206 | /// Since version 6.2 207 | pub kthread_teb: u32, 208 | /// Since version 6.2 209 | pub ethread_list_entry: u32, 210 | /// Since version x.x 211 | pub teb_peb: u32, 212 | /// Since version x.x 213 | pub teb_peb_x86: u32, 214 | 215 | pub mmvad: MmVadOffsetTable, 216 | } 217 | 218 | #[repr(C, align(4))] 219 | #[derive(Debug, Copy, Clone, Pod)] 220 | #[cfg_attr(feature = "serde", derive(::serde::Serialize, ::serde::Deserialize))] 221 | pub struct MmVadOffsetTable { 222 | pub vad_node: u32, 223 | pub starting_vpn: u32, 224 | pub ending_vpn: u32, 225 | pub starting_vpn_high: u32, 226 | pub ending_vpn_high: u32, 227 | pub u: u32, 228 | pub protection_bit: u32, 229 | } 230 | -------------------------------------------------------------------------------- /memflow-win32-defs/src/offsets/pdb.rs: -------------------------------------------------------------------------------- 1 | mod data; 2 | 3 | use std::convert::TryInto; 4 | use std::prelude::v1::*; 5 | 6 | use data::TypeSet; 7 | use std::collections::HashMap; 8 | use std::{fmt, io, result}; 9 | 10 | use pdb::{FallibleIterator, Result, Source, SourceSlice, SourceView, TypeData, PDB}; 11 | 12 | #[derive(Debug, Clone, PartialEq, Eq)] 13 | pub struct PdbSymbols { 14 | symbol_map: HashMap, 15 | } 16 | 17 | impl PdbSymbols { 18 | pub fn new(pdb_slice: &[u8]) -> Result { 19 | let pdb_buffer = PdbSourceBuffer::new(pdb_slice); 20 | let mut pdb = PDB::open(pdb_buffer)?; 21 | 22 | let symbol_table = pdb.global_symbols()?; 23 | let address_map = pdb.address_map()?; 24 | 25 | let mut symbol_map = HashMap::new(); 26 | 27 | let mut symbols = symbol_table.iter(); 28 | while let Some(symbol) = symbols.next()? { 29 | if let Ok(pdb::SymbolData::Public(data)) = symbol.parse() { 30 | let rva = data.offset.to_rva(&address_map).unwrap_or_default(); 31 | symbol_map.insert(data.name.to_string().into(), rva.0); 32 | } 33 | } 34 | 35 | Ok(Self { symbol_map }) 36 | } 37 | 38 | pub fn find_symbol(&self, name: &str) -> Option<&u32> { 39 | self.symbol_map.get(name) 40 | } 41 | } 42 | 43 | #[derive(Debug, Clone, PartialEq, Eq)] 44 | pub struct PdbField { 45 | pub type_name: String, 46 | pub offset: usize, 47 | pub bit_offset: usize, 48 | } 49 | 50 | #[derive(Debug, Clone, PartialEq, Eq)] 51 | pub struct PdbStruct { 52 | field_map: HashMap, 53 | } 54 | 55 | impl PdbStruct { 56 | pub fn new(pdb_slice: &[u8], class_name: &str) -> Result { 57 | let pdb_buffer = PdbSourceBuffer::new(pdb_slice); 58 | let mut pdb = PDB::open(pdb_buffer)?; 59 | 60 | let type_information = pdb.type_information()?; 61 | let mut type_finder = type_information.finder(); 62 | 63 | let mut needed_types = TypeSet::new(); 64 | let mut data = data::Data::new(); 65 | 66 | let mut type_iter = type_information.iter(); 67 | while let Some(typ) = type_iter.next()? { 68 | // keep building the index 69 | type_finder.update(&type_iter); 70 | 71 | if let Ok(TypeData::Class(class)) = typ.parse() { 72 | if class.name.as_bytes() == class_name.as_bytes() 73 | && !class.properties.forward_reference() 74 | { 75 | data.add(&type_finder, typ.index(), &mut needed_types)?; 76 | break; 77 | } 78 | } 79 | } 80 | 81 | // add all the needed types iteratively until we're done 82 | loop { 83 | // get the last element in needed_types without holding an immutable borrow 84 | let last = needed_types.iter().next_back().copied(); 85 | 86 | if let Some(type_index) = last { 87 | // remove it 88 | needed_types.remove(&type_index); 89 | 90 | // add the type 91 | data.add(&type_finder, type_index, &mut needed_types)?; 92 | } else { 93 | break; 94 | } 95 | } 96 | 97 | let mut field_map = HashMap::new(); 98 | for class in &data.classes { 99 | class.fields.iter().for_each(|f| { 100 | field_map.insert( 101 | f.name.to_string().into_owned(), 102 | PdbField { 103 | type_name: f.type_name.clone(), 104 | offset: f.offset as usize, // u16 can always be safely converted into usize 105 | bit_offset: f.bit_offset as usize, // u8 can always be safely converted into usize 106 | }, 107 | ); 108 | }); 109 | } 110 | 111 | Ok(Self { field_map }) 112 | } 113 | 114 | pub fn find_field(&self, name: &str) -> Option<&PdbField> { 115 | self.field_map.get(name) 116 | } 117 | } 118 | 119 | pub struct PdbSourceBuffer<'a> { 120 | bytes: &'a [u8], 121 | } 122 | 123 | impl<'a> PdbSourceBuffer<'a> { 124 | pub fn new(bytes: &'a [u8]) -> Self { 125 | Self { bytes } 126 | } 127 | } 128 | 129 | impl<'a> fmt::Debug for PdbSourceBuffer<'a> { 130 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 131 | write!(f, "PdbSourceBuffer({} bytes)", self.bytes.len()) 132 | } 133 | } 134 | 135 | impl<'a, 's> Source<'s> for PdbSourceBuffer<'a> { 136 | fn view( 137 | &mut self, 138 | slices: &[SourceSlice], 139 | ) -> result::Result>, io::Error> { 140 | let len = slices.iter().fold(0_usize, |acc, s| acc + s.size); 141 | 142 | let mut v = PdbSourceBufferView { 143 | bytes: Vec::with_capacity(len), 144 | }; 145 | v.bytes.resize(len, 0); 146 | 147 | let bytes = v.bytes.as_mut_slice(); 148 | let mut output_offset: usize = 0; 149 | for slice in slices { 150 | let offset = slice.offset.try_into().unwrap(); 151 | bytes[output_offset..(output_offset + slice.size)] 152 | .copy_from_slice(&self.bytes[offset..(offset + slice.size)]); 153 | output_offset += slice.size; 154 | } 155 | 156 | Ok(Box::new(v)) 157 | } 158 | } 159 | 160 | #[derive(Clone)] 161 | struct PdbSourceBufferView { 162 | bytes: Vec, 163 | } 164 | 165 | impl fmt::Debug for PdbSourceBufferView { 166 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 167 | write!(f, "PdbSourceBufferView({} bytes)", self.bytes.len()) 168 | } 169 | } 170 | 171 | impl SourceView<'_> for PdbSourceBufferView { 172 | fn as_slice(&self) -> &[u8] { 173 | self.bytes.as_slice() 174 | } 175 | } 176 | 177 | impl Drop for PdbSourceBufferView { 178 | fn drop(&mut self) { 179 | // no-op 180 | } 181 | } 182 | -------------------------------------------------------------------------------- /memflow-win32-defs/src/offsets/pdb/data.rs: -------------------------------------------------------------------------------- 1 | // https://github.com/willglynn/pdb/blob/master/examples/pdb2hpp.rs 2 | 3 | use std::prelude::v1::*; 4 | 5 | use log::{info, trace}; 6 | use std::collections::BTreeSet; 7 | 8 | pub type TypeSet = BTreeSet; 9 | 10 | pub fn type_name( 11 | type_finder: &pdb::TypeFinder<'_>, 12 | type_index: pdb::TypeIndex, 13 | needed_types: &mut TypeSet, 14 | ) -> pdb::Result { 15 | let mut name = match type_finder.find(type_index)?.parse()? { 16 | pdb::TypeData::Primitive(data) => { 17 | let mut name = match data.kind { 18 | pdb::PrimitiveKind::Void => "void".to_string(), 19 | pdb::PrimitiveKind::Char => "char".to_string(), 20 | pdb::PrimitiveKind::UChar => "unsigned char".to_string(), 21 | 22 | pdb::PrimitiveKind::I8 => "int8_t".to_string(), 23 | pdb::PrimitiveKind::U8 => "uint8_t".to_string(), 24 | pdb::PrimitiveKind::I16 => "int16_t".to_string(), 25 | pdb::PrimitiveKind::U16 => "uint16_t".to_string(), 26 | pdb::PrimitiveKind::I32 => "int32_t".to_string(), 27 | pdb::PrimitiveKind::U32 => "uint32_t".to_string(), 28 | pdb::PrimitiveKind::I64 => "int64_t".to_string(), 29 | pdb::PrimitiveKind::U64 => "uint64_t".to_string(), 30 | 31 | pdb::PrimitiveKind::F32 => "float".to_string(), 32 | pdb::PrimitiveKind::F64 => "double".to_string(), 33 | 34 | pdb::PrimitiveKind::Bool8 => "bool".to_string(), 35 | 36 | _ => format!("unhandled_primitive.kind /* {:?} */", data.kind), 37 | }; 38 | 39 | if data.indirection.is_some() { 40 | name.push_str(" *"); 41 | } 42 | 43 | name 44 | } 45 | 46 | pdb::TypeData::Class(data) => { 47 | needed_types.insert(type_index); 48 | data.name.to_string().into_owned() 49 | } 50 | 51 | pdb::TypeData::Enumeration(data) => { 52 | needed_types.insert(type_index); 53 | data.name.to_string().into_owned() 54 | } 55 | 56 | pdb::TypeData::Union(data) => { 57 | needed_types.insert(type_index); 58 | data.name.to_string().into_owned() 59 | } 60 | 61 | pdb::TypeData::Pointer(data) => format!( 62 | "{}*", 63 | type_name(type_finder, data.underlying_type, needed_types)? 64 | ), 65 | 66 | pdb::TypeData::Modifier(data) => { 67 | if data.constant { 68 | format!( 69 | "const {}", 70 | type_name(type_finder, data.underlying_type, needed_types)? 71 | ) 72 | } else if data.volatile { 73 | format!( 74 | "volatile {}", 75 | type_name(type_finder, data.underlying_type, needed_types)? 76 | ) 77 | } else { 78 | // ? 79 | type_name(type_finder, data.underlying_type, needed_types)? 80 | } 81 | } 82 | 83 | pdb::TypeData::Array(data) => { 84 | let mut name = type_name(type_finder, data.element_type, needed_types)?; 85 | for size in data.dimensions { 86 | name = format!("{name}[{size}]"); 87 | } 88 | name 89 | } 90 | 91 | x => format!("Type{type_index} /* TODO: figure out how to name it {x:?} */"), 92 | }; 93 | 94 | // TODO: search and replace std:: patterns 95 | if name == "std::basic_string,std::allocator >" { 96 | name = "std::string".to_string(); 97 | } 98 | 99 | Ok(name) 100 | } 101 | 102 | #[derive(Debug, Clone, PartialEq, Eq)] 103 | pub struct Class<'p> { 104 | pub kind: pdb::ClassKind, 105 | pub name: pdb::RawString<'p>, 106 | pub base_classes: Vec, 107 | pub fields: Vec>, 108 | pub instance_methods: Vec>, 109 | pub static_methods: Vec>, 110 | } 111 | 112 | impl<'p> Class<'p> { 113 | fn add_derived_from(&mut self, _: &pdb::TypeFinder<'p>, _: pdb::TypeIndex, _: &mut TypeSet) { 114 | // TODO 115 | } 116 | 117 | fn add_fields( 118 | &mut self, 119 | type_finder: &pdb::TypeFinder<'p>, 120 | type_index: pdb::TypeIndex, 121 | needed_types: &mut TypeSet, 122 | ) -> pdb::Result<()> { 123 | match type_finder.find(type_index)?.parse()? { 124 | pdb::TypeData::FieldList(data) => { 125 | for field in &data.fields { 126 | self.add_field(type_finder, field, needed_types)?; 127 | } 128 | 129 | if let Some(continuation) = data.continuation { 130 | // recurse 131 | self.add_fields(type_finder, continuation, needed_types)?; 132 | } 133 | } 134 | other => { 135 | info!( 136 | "trying to Class::add_fields() got {} -> {:?}", 137 | type_index, other 138 | ); 139 | panic!("unexpected type in Class::add_fields()"); 140 | } 141 | } 142 | 143 | Ok(()) 144 | } 145 | 146 | fn add_field( 147 | &mut self, 148 | type_finder: &pdb::TypeFinder<'p>, 149 | field: &pdb::TypeData<'p>, 150 | needed_types: &mut TypeSet, 151 | ) -> pdb::Result<()> { 152 | match *field { 153 | pdb::TypeData::Member(ref data) => { 154 | // TODO: attributes (static, virtual, etc.) 155 | 156 | let bit_offset = match type_finder.find(data.field_type)?.parse()? { 157 | pdb::TypeData::Bitfield(bitfield) => bitfield.position, 158 | _ => 0, 159 | }; 160 | 161 | self.fields.push(Field { 162 | type_name: type_name(type_finder, data.field_type, needed_types)?, 163 | name: data.name, 164 | offset: data.offset, 165 | bit_offset, 166 | }); 167 | } 168 | 169 | pdb::TypeData::Method(ref data) => { 170 | let method = Method::find( 171 | data.name, 172 | data.attributes, 173 | type_finder, 174 | data.method_type, 175 | needed_types, 176 | )?; 177 | if data.attributes.is_static() { 178 | self.static_methods.push(method); 179 | } else { 180 | self.instance_methods.push(method); 181 | } 182 | } 183 | 184 | pdb::TypeData::OverloadedMethod(ref data) => { 185 | // this just means we have more than one method with the same name 186 | // find the method list 187 | match type_finder.find(data.method_list)?.parse()? { 188 | pdb::TypeData::MethodList(method_list) => { 189 | for pdb::MethodListEntry { 190 | attributes, 191 | method_type, 192 | .. 193 | } in method_list.methods 194 | { 195 | // hooray 196 | let method = Method::find( 197 | data.name, 198 | attributes, 199 | type_finder, 200 | method_type, 201 | needed_types, 202 | )?; 203 | 204 | if attributes.is_static() { 205 | self.static_methods.push(method); 206 | } else { 207 | self.instance_methods.push(method); 208 | } 209 | } 210 | } 211 | other => { 212 | info!( 213 | "processing OverloadedMethod, expected MethodList, got {} -> {:?}", 214 | data.method_list, other 215 | ); 216 | panic!("unexpected type in Class::add_field()"); 217 | } 218 | } 219 | } 220 | 221 | pdb::TypeData::BaseClass(ref data) => self.base_classes.push(BaseClass { 222 | type_name: type_name(type_finder, data.base_class, needed_types)?, 223 | offset: data.offset, 224 | }), 225 | 226 | pdb::TypeData::VirtualBaseClass(ref data) => self.base_classes.push(BaseClass { 227 | type_name: type_name(type_finder, data.base_class, needed_types)?, 228 | offset: data.base_pointer_offset, 229 | }), 230 | 231 | _ => { 232 | // ignore everything else even though that's sad 233 | } 234 | } 235 | 236 | Ok(()) 237 | } 238 | } 239 | 240 | #[derive(Debug, Clone, PartialEq, Eq)] 241 | pub struct BaseClass { 242 | pub type_name: String, 243 | pub offset: u32, 244 | } 245 | 246 | #[derive(Debug, Clone, PartialEq, Eq)] 247 | pub struct Field<'p> { 248 | pub type_name: String, 249 | pub name: pdb::RawString<'p>, 250 | pub offset: u64, 251 | pub bit_offset: u8, 252 | } 253 | 254 | #[derive(Debug, Clone, PartialEq, Eq)] 255 | pub struct Method<'p> { 256 | pub name: pdb::RawString<'p>, 257 | pub return_type_name: String, 258 | pub arguments: Vec, 259 | pub is_virtual: bool, 260 | } 261 | 262 | impl<'p> Method<'p> { 263 | fn find( 264 | name: pdb::RawString<'p>, 265 | attributes: pdb::FieldAttributes, 266 | type_finder: &pdb::TypeFinder<'p>, 267 | type_index: pdb::TypeIndex, 268 | needed_types: &mut TypeSet, 269 | ) -> pdb::Result> { 270 | match type_finder.find(type_index)?.parse()? { 271 | pdb::TypeData::MemberFunction(data) => Ok(Method { 272 | name, 273 | return_type_name: type_name(type_finder, data.return_type, needed_types)?, 274 | arguments: argument_list(type_finder, data.argument_list, needed_types)?, 275 | is_virtual: attributes.is_virtual(), 276 | }), 277 | 278 | other => { 279 | info!("other: {:?}", other); 280 | Err(pdb::Error::UnimplementedFeature("that")) 281 | } 282 | } 283 | } 284 | } 285 | 286 | fn argument_list( 287 | type_finder: &pdb::TypeFinder<'_>, 288 | type_index: pdb::TypeIndex, 289 | needed_types: &mut TypeSet, 290 | ) -> pdb::Result> { 291 | match type_finder.find(type_index)?.parse()? { 292 | pdb::TypeData::ArgumentList(data) => { 293 | let mut args: Vec = Vec::new(); 294 | for arg_type in data.arguments { 295 | args.push(type_name(type_finder, arg_type, needed_types)?); 296 | } 297 | Ok(args) 298 | } 299 | _ => Err(pdb::Error::UnimplementedFeature( 300 | "argument list of non-argument-list type", 301 | )), 302 | } 303 | } 304 | 305 | #[derive(Debug, Clone, PartialEq, Eq)] 306 | pub struct Enum<'p> { 307 | name: pdb::RawString<'p>, 308 | underlying_type_name: String, 309 | values: Vec>, 310 | } 311 | 312 | impl<'p> Enum<'p> { 313 | fn add_fields( 314 | &mut self, 315 | type_finder: &pdb::TypeFinder<'p>, 316 | type_index: pdb::TypeIndex, 317 | needed_types: &mut TypeSet, 318 | ) -> pdb::Result<()> { 319 | match type_finder.find(type_index)?.parse()? { 320 | pdb::TypeData::FieldList(data) => { 321 | for field in &data.fields { 322 | self.add_field(type_finder, field, needed_types); 323 | } 324 | 325 | if let Some(continuation) = data.continuation { 326 | // recurse 327 | self.add_fields(type_finder, continuation, needed_types)?; 328 | } 329 | } 330 | other => { 331 | info!( 332 | "trying to Enum::add_fields() got {} -> {:?}", 333 | type_index, other 334 | ); 335 | panic!("unexpected type in Enum::add_fields()"); 336 | } 337 | } 338 | 339 | Ok(()) 340 | } 341 | 342 | fn add_field(&mut self, _: &pdb::TypeFinder<'p>, field: &pdb::TypeData<'p>, _: &mut TypeSet) { 343 | // ignore everything else even though that's sad 344 | if let pdb::TypeData::Enumerate(ref data) = field { 345 | self.values.push(EnumValue { 346 | name: data.name, 347 | value: data.value, 348 | }); 349 | } 350 | } 351 | } 352 | 353 | #[derive(Debug, Clone, PartialEq, Eq)] 354 | pub struct EnumValue<'p> { 355 | name: pdb::RawString<'p>, 356 | value: pdb::Variant, 357 | } 358 | 359 | #[derive(Debug, Clone, PartialEq, Eq)] 360 | pub struct ForwardReference<'p> { 361 | kind: pdb::ClassKind, 362 | name: pdb::RawString<'p>, 363 | } 364 | 365 | #[derive(Debug, Clone, PartialEq, Eq)] 366 | pub struct Data<'p> { 367 | pub forward_references: Vec>, 368 | pub classes: Vec>, 369 | pub enums: Vec>, 370 | } 371 | 372 | impl<'p> Data<'p> { 373 | pub fn new() -> Data<'p> { 374 | Data { 375 | forward_references: Vec::new(), 376 | classes: Vec::new(), 377 | enums: Vec::new(), 378 | } 379 | } 380 | 381 | pub fn add( 382 | &mut self, 383 | type_finder: &pdb::TypeFinder<'p>, 384 | type_index: pdb::TypeIndex, 385 | needed_types: &mut TypeSet, 386 | ) -> pdb::Result<()> { 387 | match type_finder.find(type_index)?.parse()? { 388 | pdb::TypeData::Class(data) => { 389 | if data.properties.forward_reference() { 390 | self.forward_references.push(ForwardReference { 391 | kind: data.kind, 392 | name: data.name, 393 | }); 394 | 395 | return Ok(()); 396 | } 397 | 398 | let mut class = Class { 399 | kind: data.kind, 400 | name: data.name, 401 | fields: Vec::new(), 402 | base_classes: Vec::new(), 403 | instance_methods: Vec::new(), 404 | static_methods: Vec::new(), 405 | }; 406 | 407 | if let Some(derived_from) = data.derived_from { 408 | class.add_derived_from(type_finder, derived_from, needed_types); 409 | } 410 | 411 | if let Some(fields) = data.fields { 412 | class.add_fields(type_finder, fields, needed_types)?; 413 | } 414 | 415 | self.classes.insert(0, class); 416 | } 417 | 418 | pdb::TypeData::Enumeration(data) => { 419 | let mut e = Enum { 420 | name: data.name, 421 | underlying_type_name: type_name( 422 | type_finder, 423 | data.underlying_type, 424 | needed_types, 425 | )?, 426 | values: Vec::new(), 427 | }; 428 | 429 | e.add_fields(type_finder, data.fields, needed_types)?; 430 | 431 | self.enums.insert(0, e); 432 | } 433 | 434 | // ignore 435 | other => trace!("don't know how to add {:?}", other), 436 | } 437 | 438 | Ok(()) 439 | } 440 | } 441 | -------------------------------------------------------------------------------- /memflow-win32-defs/src/offsets/symstore.rs: -------------------------------------------------------------------------------- 1 | use std::prelude::v1::*; 2 | 3 | use crate::offsets::Win32Guid; 4 | 5 | use std::fs::{self, File}; 6 | use std::io::{Read, Write}; 7 | use std::path::{Path, PathBuf}; 8 | 9 | use dirs::cache_dir; 10 | use log::info; 11 | 12 | use memflow::error::{Error, ErrorKind, ErrorOrigin, Result}; 13 | 14 | #[cfg(feature = "download_progress")] 15 | use { 16 | indicatif::{ProgressBar, ProgressStyle}, 17 | progress_streams::ProgressReader, 18 | std::sync::atomic::{AtomicBool, AtomicUsize, Ordering}, 19 | std::sync::Arc, 20 | }; 21 | 22 | #[cfg(feature = "download_progress")] 23 | fn read_to_end(reader: &mut T, len: usize) -> Result> { 24 | let mut buffer = vec![]; 25 | 26 | let total = Arc::new(AtomicUsize::new(0)); 27 | let mut reader = ProgressReader::new(reader, |progress: usize| { 28 | total.fetch_add(progress, Ordering::SeqCst); 29 | }); 30 | let pb = ProgressBar::new(len as u64); 31 | pb.set_style(ProgressStyle::default_bar() 32 | .template("{spinner:.green} [{elapsed_precise}] [{wide_bar:.cyan/blue}] {bytes}/{total_bytes} ({eta})") 33 | .unwrap() 34 | .progress_chars("#>-")); 35 | 36 | let finished = Arc::new(AtomicBool::new(false)); 37 | let thread = { 38 | let finished_thread = finished.clone(); 39 | let total_thread = total.clone(); 40 | 41 | std::thread::spawn(move || { 42 | while !finished_thread.load(Ordering::Relaxed) { 43 | pb.set_position(total_thread.load(Ordering::SeqCst) as u64); 44 | std::thread::sleep(std::time::Duration::from_millis(10)); 45 | } 46 | pb.finish_with_message("downloaded"); 47 | }) 48 | }; 49 | 50 | reader.read_to_end(&mut buffer).map_err(|_| { 51 | Error(ErrorOrigin::OsLayer, ErrorKind::Http).log_error("unable to read from http request") 52 | })?; 53 | finished.store(true, Ordering::Relaxed); 54 | thread.join().unwrap(); 55 | 56 | Ok(buffer) 57 | } 58 | 59 | #[cfg(not(feature = "download_progress"))] 60 | fn read_to_end(reader: &mut T, _len: usize) -> Result> { 61 | let mut buffer = vec![]; 62 | reader.read_to_end(&mut buffer).map_err(|_| { 63 | Error(ErrorOrigin::OsLayer, ErrorKind::Http).log_error("unable to read from http request") 64 | })?; 65 | Ok(buffer) 66 | } 67 | 68 | #[derive(Debug, Clone)] 69 | pub struct SymbolStore { 70 | base_url: String, 71 | cache_path: Option, 72 | } 73 | 74 | impl Default for SymbolStore { 75 | fn default() -> Self { 76 | let cache_dir = cache_dir().expect("unable to get cache directory"); 77 | Self { 78 | base_url: "https://msdl.microsoft.com/download/symbols".to_string(), 79 | cache_path: Some(cache_dir.join("memflow")), 80 | } 81 | } 82 | } 83 | 84 | impl SymbolStore { 85 | pub fn new() -> Self { 86 | Self::default() 87 | } 88 | 89 | pub fn load(&self, guid: &Win32Guid) -> Result> { 90 | if let Some(cache_path) = &self.cache_path { 91 | let cache_dir = cache_path.join(guid.file_name.clone()); 92 | let cache_file = cache_dir.join(guid.guid.clone()); 93 | 94 | let buffer = if cache_file.exists() { 95 | info!( 96 | "reading pdb from local cache: {}", 97 | cache_file.to_string_lossy() 98 | ); 99 | let mut file = File::open(cache_file).map_err(|_| { 100 | Error(ErrorOrigin::OsLayer, ErrorKind::UnableToReadFile) 101 | .log_error("unable to open pdb in local cache") 102 | })?; 103 | let mut buffer = Vec::new(); 104 | file.read_to_end(&mut buffer).map_err(|_| { 105 | Error(ErrorOrigin::OsLayer, ErrorKind::UnableToReadFile) 106 | .log_error("unable to read pdb from local cache") 107 | })?; 108 | buffer 109 | } else { 110 | let buffer = self.download(guid)?; 111 | 112 | if !cache_dir.exists() { 113 | info!("creating cache directory {:?}", cache_dir.to_str()); 114 | fs::create_dir_all(&cache_dir).map_err(|_| { 115 | Error(ErrorOrigin::OsLayer, ErrorKind::UnableToCreateDirectory) 116 | .log_error("unable to create folder in local pdb cache") 117 | })?; 118 | } 119 | 120 | info!( 121 | "writing pdb to local cache: {}", 122 | cache_file.to_string_lossy() 123 | ); 124 | let mut file = File::create(cache_file).map_err(|_| { 125 | Error(ErrorOrigin::OsLayer, ErrorKind::UnableToWriteFile) 126 | .log_error("unable to create file in local pdb cache") 127 | })?; 128 | file.write_all(&buffer[..]).map_err(|_| { 129 | Error(ErrorOrigin::OsLayer, ErrorKind::UnableToWriteFile) 130 | .log_error("unable to write pdb to local cache") 131 | })?; 132 | 133 | buffer 134 | }; 135 | 136 | Ok(buffer) 137 | } else { 138 | self.download(guid) 139 | } 140 | } 141 | 142 | fn download(&self, guid: &Win32Guid) -> Result> { 143 | let pdb_url = format!("{}/{}/{}", self.base_url, guid.file_name, guid.guid); 144 | 145 | self.download_file(&format!("{}/{}", pdb_url, guid.file_name)) 146 | .or_else(|_| self.download_file(&format!("{}/{}", pdb_url, "file.ptr"))) 147 | } 148 | 149 | fn download_file(&self, url: &str) -> Result> { 150 | info!("downloading pdb from {}", url); 151 | let resp = ureq::get(url).call().map_err(|_| { 152 | Error(ErrorOrigin::OsLayer, ErrorKind::Http).log_error("unable to download pdb") 153 | })?; 154 | 155 | assert!(resp.has("Content-Length")); 156 | let len = resp 157 | .header("Content-Length") 158 | .and_then(|s| s.parse::().ok()) 159 | .unwrap(); 160 | 161 | let mut reader = resp.into_reader(); 162 | let buffer = read_to_end(&mut reader, len)?; 163 | 164 | assert_eq!(buffer.len(), len); 165 | Ok(buffer) 166 | } 167 | 168 | // symbol store configurations 169 | pub fn base_url(mut self, base_url: &str) -> Self { 170 | self.base_url = base_url.to_string(); 171 | self 172 | } 173 | 174 | pub fn no_cache(mut self) -> Self { 175 | self.cache_path = None; 176 | self 177 | } 178 | 179 | pub fn cache_path>(mut self, cache_path: P) -> Self { 180 | self.cache_path = Some(cache_path.as_ref().to_path_buf()); 181 | self 182 | } 183 | } 184 | -------------------------------------------------------------------------------- /memflow-win32/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "memflow-win32" 3 | version = "0.2.1" 4 | authors = ["ko1N ", "Aurimas Blažulionis <0x60@pm.me>"] 5 | edition = "2021" 6 | rust-version = "1.65" 7 | description = "win32 integration of the memflow physical memory introspection framework" 8 | documentation = "https://docs.rs/memflow-win32" 9 | readme = "../README.md" 10 | homepage = "https://memflow.io" 11 | repository = "https://github.com/memflow/memflow-win32" 12 | license = "MIT" 13 | keywords = [ "memflow", "introspection", "memory", "dma" ] 14 | categories = [ "api-bindings", "memory-management", "os" ] 15 | 16 | [badges] 17 | maintenance = { status = "actively-developed" } 18 | codecov = { repository = "github", branch = "master", service = "github" } 19 | 20 | [lib] 21 | crate-type = ["lib", "cdylib"] 22 | 23 | [dependencies] 24 | memflow = { version = "0.2", default-features = false } 25 | log = { version = "0.4", default-features = false } 26 | pelite = { version = "0.10", default-features = false } 27 | widestring = { version = "1.1", default-features = false, features = ["alloc"] } 28 | no-std-compat = { version = "0.4", features = ["alloc"] } 29 | serde = { version = "1.0", default-features = false, optional = true, features = ["derive"] } 30 | memflow-win32-defs = { version = "0.2", path = "../memflow-win32-defs", default-features = false } 31 | muddy = "0.3.2" 32 | 33 | # will be replaced by our own signature scanner 34 | regex = { version = "1.11", optional = true } 35 | 36 | [dev-dependencies] 37 | simplelog = "0.12" 38 | rand = "0.8" 39 | rand_xorshift = "0.3" 40 | clap = { version = "4.5", features = ["cargo"] } 41 | toml = "0.8" 42 | 43 | [build-dependencies] 44 | toml = "0.8" 45 | serde = { version = "1.0", default-features = false, features = ["derive", "alloc"] } 46 | memflow = { version = "0.2", default-features = false } 47 | memflow-win32-defs = { version = "0.2", path = "../memflow-win32-defs", features = ["symstore"] } 48 | 49 | [features] 50 | default = ["std", "serde_derive", "embed_offsets", "symstore", "download_progress", "regex", "memflow/default", "plugins"] 51 | std = ["no-std-compat/std", "memflow/std", "pelite/std"] 52 | plugins = ["memflow/plugins"] 53 | embed_offsets = ["serde", "memflow/serde_derive", "memflow-win32-defs/serde"] 54 | serde_derive = ["serde", "memflow/serde_derive", "pelite/std", "pelite/serde", "memflow-win32-defs/serde"] 55 | symstore = ["memflow-win32-defs/symstore"] 56 | download_progress = ["memflow-win32-defs/download_progress"] 57 | 58 | [[example]] 59 | name = "dump_offsets" 60 | path = "examples/dump_offsets.rs" 61 | required-features = ["memflow/serde_derive"] 62 | 63 | [[example]] 64 | name = "keyboard_listen" 65 | path = "examples/keyboard_listen.rs" 66 | required-features = ["memflow/plugins"] 67 | 68 | [[example]] 69 | name = "open_process" 70 | path = "examples/open_process.rs" 71 | required-features = ["memflow/plugins"] 72 | 73 | [[example]] 74 | name = "process_list" 75 | path = "examples/process_list.rs" 76 | required-features = ["memflow/plugins"] 77 | -------------------------------------------------------------------------------- /memflow-win32/build.rs: -------------------------------------------------------------------------------- 1 | use std::{ 2 | env, 3 | error::Error, 4 | fs::{self, File}, 5 | io::{Read, Write}, 6 | path::Path, 7 | }; 8 | 9 | use memflow::dataview::PodMethods; 10 | use std::convert::TryInto; 11 | 12 | #[cfg(feature = "embed_offsets")] 13 | use memflow_win32_defs::{ 14 | kernel::*, 15 | offsets::{ 16 | offset_table::{Win32OffsetFile, Win32OffsetHeader}, 17 | *, 18 | }, 19 | }; 20 | 21 | #[repr(C, align(4))] 22 | #[derive(Debug, ::serde::Serialize, ::serde::Deserialize)] 23 | #[cfg(feature = "embed_offsets")] 24 | pub struct Win32OffsetFileStart { 25 | header: Win32OffsetHeader, 26 | } 27 | 28 | #[cfg(feature = "embed_offsets")] 29 | fn embed_offsets() -> Result<(), Box> { 30 | let regenerate_offsets = 31 | match &*env::var("MEMFLOW_WIN32_REGENERATE_OFFSETS").unwrap_or_default() { 32 | "on" | "1" => 1, 33 | "force" => 2, 34 | _ => 0, 35 | }; 36 | 37 | let out_dir = env::var("OUT_DIR")?; 38 | let dest_path = Path::new(&out_dir).join("win32_offsets.bin"); 39 | let mut all_the_files = File::create(dest_path)?; 40 | 41 | // iterate offsets folder 42 | for f in fs::read_dir("./offsets")? { 43 | let f = f?; 44 | 45 | if !f.file_type()?.is_file() { 46 | continue; 47 | } 48 | 49 | let fp = f.path(); 50 | 51 | let mut file = File::open(&fp)?; 52 | let mut tomlstr = String::new(); 53 | file.read_to_string(&mut tomlstr)?; 54 | 55 | std::mem::drop(file); 56 | 57 | let offsets = match toml::from_str::(&tomlstr) { 58 | x if regenerate_offsets == 2 || (regenerate_offsets == 1 && x.is_err()) => { 59 | let regenerate = || -> Result> { 60 | let mut header = match toml::from_str::(&tomlstr) { 61 | Ok(header) => header, 62 | _ => { 63 | // Parse header from the filename. Example format: 64 | // 10_0_18362_X64_0AFB69F5FD264D54673570E37B38A3181.toml 65 | let fname = fp.file_name().and_then(|s| s.to_str()).unwrap_or_default(); 66 | 67 | let parts = fname.split_terminator('_').collect::>(); 68 | 69 | if parts.len() != 5 { 70 | return Err(format!( 71 | "Cannot regenerate offsets for {fname} - invalid filename format" 72 | ) 73 | .into()); 74 | } 75 | 76 | let nt_major_version = str::parse(parts[0])?; 77 | let nt_minor_version = str::parse(parts[1])?; 78 | let nt_build_number = str::parse(parts[2])?; 79 | let pdb_guid = parts[4]; 80 | 81 | if !pdb_guid.ends_with(".toml") { 82 | return Err(format!("{fname} does not contain valid guid ({pdb_guid})").into()); 83 | } 84 | 85 | Win32OffsetFileStart { 86 | header: Win32OffsetHeader { 87 | pdb_file_name: "".into(), 88 | pdb_guid: pdb_guid.trim_end_matches(".toml").into(), 89 | arch: toml::from_str(&format!("\"{}\"", parts[3]))?, 90 | nt_major_version, 91 | nt_minor_version, 92 | nt_build_number, 93 | }, 94 | } 95 | } 96 | }.header; 97 | 98 | println!("{header:?}"); 99 | 100 | // Use pdb file name if available, backup if only file name is known. 101 | 102 | let file_names1: [&str; 1] = [(&header.pdb_file_name).try_into()?]; 103 | 104 | let file_names2 = ["ntkrnlmp.pdb", "ntkrpamp.pdb"]; 105 | 106 | let file_names = if file_names1[0].is_empty() { 107 | &file_names2[..] 108 | } else { 109 | &file_names1[..] 110 | }; 111 | 112 | let guid = (&header.pdb_guid).try_into()?; 113 | 114 | let (offsets, pdb_file_name) = file_names 115 | .iter() 116 | .map(|f| { 117 | Win32Offsets::builder() 118 | .symbol_store(SymbolStore::new()) 119 | .guid(Win32Guid::new(f, guid)) 120 | .build() 121 | .map(|v| (v.0, f)) 122 | }) 123 | .find_map(Result::ok) 124 | .ok_or("Failed to get symbols from the symbol store")?; 125 | 126 | header.pdb_file_name = (*pdb_file_name).into(); 127 | 128 | println!("{header:?}"); 129 | println!("{offsets:?}"); 130 | 131 | Ok(Win32OffsetFile { header, offsets }) 132 | }; 133 | 134 | match regenerate() { 135 | Ok(x) => { 136 | let mut file = File::create(&fp)?; 137 | file.write_all(toml::to_string(&x)?.as_bytes())?; 138 | 139 | x 140 | } 141 | Err(e) => { 142 | // Do not report failure if forced, but already contains correct configuration 143 | if x.is_ok() { 144 | x? 145 | } else { 146 | return Err(e); 147 | } 148 | } 149 | } 150 | } 151 | x => x?, 152 | }; 153 | 154 | all_the_files.write_all(offsets.as_bytes())?; 155 | } 156 | 157 | Ok(()) 158 | } 159 | 160 | #[cfg(not(feature = "embed_offsets"))] 161 | fn embed_offsets() -> Result<(), Box> { 162 | Ok(()) 163 | } 164 | 165 | fn main() -> Result<(), Box> { 166 | embed_offsets()?; 167 | Ok(()) 168 | } 169 | -------------------------------------------------------------------------------- /memflow-win32/examples/dump_offsets.rs: -------------------------------------------------------------------------------- 1 | /*! 2 | This example shows how to use a dynamically loaded connector in conjunction 3 | with memflow-win32. This example uses the `Inventory` feature of memflow 4 | but hard-wires the connector instance into the memflow-win32 OS layer. 5 | 6 | The example then dumps all the found offsets into the specified `output` file. 7 | 8 | # Usage: 9 | ```bash 10 | cargo run --release --example dump_offsets -- -vv -c kvm --output file.toml 11 | ``` 12 | */ 13 | use std::fs::File; 14 | use std::io::Write; 15 | 16 | use clap::*; 17 | use log::{error, Level}; 18 | 19 | use memflow::prelude::v1::{Result, *}; 20 | use memflow_win32::prelude::v1::*; 21 | 22 | pub fn main() -> Result<()> { 23 | let matches = parse_args(); 24 | let (chain, output) = extract_args(&matches)?; 25 | 26 | // create inventory + connector 27 | let inventory = Inventory::scan(); 28 | let connector = inventory.builder().connector_chain(chain).build()?; 29 | 30 | let os = Win32Kernel::builder(connector) 31 | .build_default_caches() 32 | .build() 33 | .unwrap(); 34 | 35 | let winver = os.kernel_info.kernel_winver; 36 | 37 | if winver != (0, 0).into() { 38 | let guid = os.kernel_info.kernel_guid.unwrap_or_default(); 39 | let offsets = Win32OffsetFile { 40 | header: Win32OffsetHeader { 41 | pdb_file_name: guid.file_name.as_str().into(), 42 | pdb_guid: guid.guid.as_str().into(), 43 | 44 | arch: os.kernel_info.os_info.arch.into(), 45 | 46 | nt_major_version: winver.major_version(), 47 | nt_minor_version: winver.minor_version(), 48 | nt_build_number: winver.build_number(), 49 | }, 50 | offsets: os.offsets.into(), 51 | }; 52 | 53 | // write offsets to file 54 | let offsetstr = toml::to_string_pretty(&offsets).unwrap(); 55 | match output { 56 | Some(output) => { 57 | let mut file = File::create(output).unwrap(); 58 | file.write_all(offsetstr.as_bytes()).unwrap(); 59 | } 60 | None => println!("{offsetstr}"), 61 | } 62 | } else { 63 | error!("kernel version has to be valid in order to generate a offsets file"); 64 | } 65 | 66 | Ok(()) 67 | } 68 | 69 | fn parse_args() -> ArgMatches { 70 | Command::new("dump_offsets example") 71 | .version(crate_version!()) 72 | .author(crate_authors!()) 73 | .arg(Arg::new("verbose").short('v').action(ArgAction::Count)) 74 | .arg( 75 | Arg::new("connector") 76 | .short('c') 77 | .action(ArgAction::Append) 78 | .required(true), 79 | ) 80 | .arg(Arg::new("os").short('o').action(ArgAction::Append)) 81 | .arg(Arg::new("output").long("output").action(ArgAction::Set)) 82 | .get_matches() 83 | } 84 | 85 | fn extract_args(matches: &ArgMatches) -> Result<(ConnectorChain<'_>, Option<&str>)> { 86 | let log_level = match matches.get_count("verbose") { 87 | 0 => Level::Error, 88 | 1 => Level::Warn, 89 | 2 => Level::Info, 90 | 3 => Level::Debug, 91 | 4 => Level::Trace, 92 | _ => Level::Trace, 93 | }; 94 | simplelog::TermLogger::init( 95 | log_level.to_level_filter(), 96 | simplelog::Config::default(), 97 | simplelog::TerminalMode::Stdout, 98 | simplelog::ColorChoice::Auto, 99 | ) 100 | .unwrap(); 101 | 102 | let conn_iter = matches 103 | .indices_of("connector") 104 | .zip(matches.get_many::("connector")) 105 | .map(|(a, b)| a.zip(b.map(String::as_str))) 106 | .into_iter() 107 | .flatten(); 108 | 109 | let os_iter = matches 110 | .indices_of("os") 111 | .zip(matches.get_many::("os")) 112 | .map(|(a, b)| a.zip(b.map(String::as_str))) 113 | .into_iter() 114 | .flatten(); 115 | 116 | Ok(( 117 | ConnectorChain::new(conn_iter, os_iter)?, 118 | matches.get_one::("output").map(String::as_str), 119 | )) 120 | } 121 | -------------------------------------------------------------------------------- /memflow-win32/examples/keyboard_listen.rs: -------------------------------------------------------------------------------- 1 | /*! 2 | This example shows how to use a dynamically loaded connector in conjunction 3 | with memflow-win32 to read the key-states of a windows computer. 4 | This example uses the `Inventory` feature of memflow but hard-wires the 5 | connector instance into the memflow-win32 OS layer. 6 | 7 | The example is an adaption of the memflow core process list example: 8 | https://github.com/memflow/memflow/blob/next/memflow/examples/process_list.rs 9 | 10 | # Usage: 11 | ```bash 12 | cargo run --release --example keyboard_listen -- -vv -c kvm 13 | ``` 14 | */ 15 | use clap::*; 16 | use log::Level; 17 | 18 | use memflow::prelude::v1::*; 19 | use memflow_win32::{prelude::v1::*, win32::vkey::*}; 20 | 21 | pub fn main() -> Result<()> { 22 | let matches = parse_args(); 23 | let (chain, conn_args) = extract_args(&matches)?; 24 | 25 | // create inventory + connector 26 | let inventory = Inventory::scan(); 27 | let connector = inventory 28 | .builder() 29 | .connector_chain(chain) 30 | .args(conn_args.parse()?) 31 | .build()?; 32 | 33 | let os = Win32Kernel::builder(connector) 34 | .build_default_caches() 35 | .build() 36 | .unwrap(); 37 | 38 | let mut kb = os.into_keyboard()?; 39 | println!("Running keyboard example...\n..............................."); 40 | 41 | println!("Checking all keys..."); 42 | 43 | for k in vkey_range(VK_LBUTTON, VK_NONE) { 44 | let down = if kb.is_down(k.into()) { "down" } else { "up" }; 45 | println!("Key {k} is {down}"); 46 | } 47 | 48 | println!("Starting keyboard listener..."); 49 | 50 | println!("Press ESC to exit"); 51 | println!("Press any key to see if it reads out here"); 52 | // listen for keyboard events until escape is pressed 53 | 'listener: loop { 54 | for k in vkey_range(VK_LBUTTON, VK_NONE) { 55 | // check escape first each time for responsiveness 56 | if kb.is_down(vkey::VK_ESCAPE.into()) { 57 | println!("Escape pressed, exiting..."); 58 | break 'listener; 59 | } 60 | if kb.is_down(k.into()) { 61 | println!("Key {k} is down"); 62 | // sleep for a bit to avoid spamming 63 | // note: this is for example purposes only, in a real application 64 | // you would probably not want to block the thread like this 65 | std::thread::sleep(std::time::Duration::from_millis(200)); 66 | } 67 | } 68 | } 69 | 70 | Ok(()) 71 | } 72 | 73 | fn parse_args() -> ArgMatches { 74 | Command::new("process_list example") 75 | .version(crate_version!()) 76 | .author(crate_authors!()) 77 | .arg(Arg::new("verbose").short('v').action(ArgAction::Count)) 78 | .arg( 79 | Arg::new("connector") 80 | .short('c') 81 | .action(ArgAction::Append) 82 | .required(true), 83 | ) 84 | .arg( 85 | Arg::new("connector_args") 86 | .short('a') 87 | .action(ArgAction::Append) 88 | .required(false), 89 | ) 90 | .arg(Arg::new("os").short('o').action(ArgAction::Append)) 91 | .get_matches() 92 | } 93 | 94 | fn extract_args(matches: &ArgMatches) -> Result<(ConnectorChain<'_>, String)> { 95 | let log_level = match matches.get_count("verbose") { 96 | 0 => Level::Error, 97 | 1 => Level::Warn, 98 | 2 => Level::Info, 99 | 3 => Level::Debug, 100 | 4 => Level::Trace, 101 | _ => Level::Trace, 102 | }; 103 | simplelog::TermLogger::init( 104 | log_level.to_level_filter(), 105 | simplelog::Config::default(), 106 | simplelog::TerminalMode::Stdout, 107 | simplelog::ColorChoice::Auto, 108 | ) 109 | .unwrap(); 110 | 111 | let conn_iter = matches 112 | .indices_of("connector") 113 | .zip(matches.get_many::("connector")) 114 | .map(|(a, b)| a.zip(b.map(String::as_str))) 115 | .into_iter() 116 | .flatten(); 117 | 118 | let os_iter = matches 119 | .indices_of("os") 120 | .zip(matches.get_many::("os")) 121 | .map(|(a, b)| a.zip(b.map(String::as_str))) 122 | .into_iter() 123 | .flatten(); 124 | 125 | let conn_args = matches 126 | .get_one::("connector_args") 127 | .map(String::to_owned) 128 | .unwrap_or(String::new()); 129 | 130 | Ok((ConnectorChain::new(conn_iter, os_iter)?, conn_args)) 131 | } 132 | -------------------------------------------------------------------------------- /memflow-win32/examples/open_process.rs: -------------------------------------------------------------------------------- 1 | /*! 2 | This example shows how to use a dynamically loaded connector in conjunction 3 | with memflow-win32. This example uses the `Inventory` feature of memflow 4 | but hard-wires the connector instance into the memflow-win32 OS layer. 5 | 6 | The example showcases how to retrieve extended process info data, 7 | opening the process and getting a list of all modules. 8 | 9 | # Usage: 10 | ```bash 11 | cargo run --release --example open_process -- -vv -c kvm -p "explorer.exe" 12 | ``` 13 | */ 14 | use clap::*; 15 | use log::{info, Level}; 16 | use muddy::muddy; 17 | 18 | use memflow::prelude::v1::*; 19 | use memflow_win32::prelude::v1::*; 20 | 21 | pub fn main() -> Result<()> { 22 | let matches = parse_args(); 23 | let (chain, process_name) = extract_args(&matches)?; 24 | let process_name = process_name.unwrap_or(muddy!("explorer.exe")); 25 | 26 | // create inventory + connector 27 | let inventory = Inventory::scan(); 28 | let connector = inventory.builder().connector_chain(chain).build()?; 29 | 30 | let mut os = Win32Kernel::builder(connector) 31 | .build_default_caches() 32 | .build() 33 | .expect("unable to initialize memflow-win32"); 34 | 35 | // display the extended process info for the process 36 | let process_info = os.process_info_by_name(process_name)?; 37 | let process_info_ext = os.process_info_from_base_info(process_info.clone())?; 38 | info!("{:?}", process_info_ext); 39 | 40 | // create a new process instance 41 | let mut process = os 42 | .into_process_by_info(process_info) 43 | .expect("unable to open process"); 44 | 45 | // retrieve all modules 46 | let module_list = process.module_list().expect("unable to read module list"); 47 | 48 | info!("{:>5} {:>10} {:^32} {:<}", "ADDR", "BASE", "NAME", "PATH"); 49 | 50 | for m in module_list { 51 | info!("{:>5} {:^16} {:^32} {}", m.address, m.base, m.name, m.path); 52 | } 53 | 54 | Ok(()) 55 | } 56 | 57 | fn parse_args() -> ArgMatches { 58 | Command::new("dump_offsets example") 59 | .version(crate_version!()) 60 | .author(crate_authors!()) 61 | .arg(Arg::new("verbose").short('v').action(ArgAction::Count)) 62 | .arg( 63 | Arg::new("connector") 64 | .short('c') 65 | .action(ArgAction::Append) 66 | .required(true), 67 | ) 68 | .arg(Arg::new("os").short('o').action(ArgAction::Append)) 69 | .arg(Arg::new("process").short('p').action(ArgAction::Set)) 70 | .get_matches() 71 | } 72 | 73 | fn extract_args(matches: &ArgMatches) -> Result<(ConnectorChain<'_>, Option<&str>)> { 74 | let log_level = match matches.get_count("verbose") { 75 | 0 => Level::Error, 76 | 1 => Level::Warn, 77 | 2 => Level::Info, 78 | 3 => Level::Debug, 79 | 4 => Level::Trace, 80 | _ => Level::Trace, 81 | }; 82 | simplelog::TermLogger::init( 83 | log_level.to_level_filter(), 84 | simplelog::Config::default(), 85 | simplelog::TerminalMode::Stdout, 86 | simplelog::ColorChoice::Auto, 87 | ) 88 | .unwrap(); 89 | 90 | let conn_iter = matches 91 | .indices_of("connector") 92 | .zip(matches.get_many::("connector")) 93 | .map(|(a, b)| a.zip(b.map(String::as_str))) 94 | .into_iter() 95 | .flatten(); 96 | 97 | let os_iter = matches 98 | .indices_of("os") 99 | .zip(matches.get_many::("os")) 100 | .map(|(a, b)| a.zip(b.map(String::as_str))) 101 | .into_iter() 102 | .flatten(); 103 | 104 | Ok(( 105 | ConnectorChain::new(conn_iter, os_iter)?, 106 | matches.get_one::("process").map(String::as_str), 107 | )) 108 | } 109 | -------------------------------------------------------------------------------- /memflow-win32/examples/process_list.rs: -------------------------------------------------------------------------------- 1 | /*! 2 | This example shows how to use a dynamically loaded connector in conjunction 3 | with memflow-win32. This example uses the `Inventory` feature of memflow 4 | but hard-wires the connector instance into the memflow-win32 OS layer. 5 | 6 | The example is an adaption of the memflow core process list example: 7 | https://github.com/memflow/memflow/blob/next/memflow/examples/process_list.rs 8 | 9 | # Usage: 10 | ```bash 11 | cargo run --release --example process_list -- -vv -c kvm 12 | ``` 13 | */ 14 | use clap::*; 15 | use log::{info, Level}; 16 | 17 | use memflow::prelude::v1::*; 18 | use memflow_win32::prelude::v1::*; 19 | 20 | pub fn main() -> Result<()> { 21 | let matches = parse_args(); 22 | let chain = extract_args(&matches)?; 23 | 24 | // create inventory + connector 25 | let inventory = Inventory::scan(); 26 | let connector = inventory.builder().connector_chain(chain).build()?; 27 | 28 | let mut os = Win32Kernel::builder(connector) 29 | .build_default_caches() 30 | .build() 31 | .unwrap(); 32 | 33 | let process_list = os.process_info_list().expect("unable to read process list"); 34 | 35 | info!( 36 | "{:>5} {:>10} {:>10} {:<}", 37 | "PID", "SYS ARCH", "PROC ARCH", "NAME" 38 | ); 39 | 40 | for p in process_list { 41 | info!( 42 | "{:>5} {:^10} {:^10} {}", 43 | p.pid, p.sys_arch, p.proc_arch, p.name 44 | ); 45 | } 46 | 47 | Ok(()) 48 | } 49 | 50 | fn parse_args() -> ArgMatches { 51 | Command::new("process_list example") 52 | .version(crate_version!()) 53 | .author(crate_authors!()) 54 | .arg(Arg::new("verbose").short('v').action(ArgAction::Count)) 55 | .arg( 56 | Arg::new("connector") 57 | .short('c') 58 | .action(ArgAction::Append) 59 | .required(true), 60 | ) 61 | .arg(Arg::new("os").short('o').action(ArgAction::Append)) 62 | .get_matches() 63 | } 64 | 65 | fn extract_args(matches: &ArgMatches) -> Result> { 66 | let log_level = match matches.get_count("verbose") { 67 | 0 => Level::Error, 68 | 1 => Level::Warn, 69 | 2 => Level::Info, 70 | 3 => Level::Debug, 71 | 4 => Level::Trace, 72 | _ => Level::Trace, 73 | }; 74 | simplelog::TermLogger::init( 75 | log_level.to_level_filter(), 76 | simplelog::Config::default(), 77 | simplelog::TerminalMode::Stdout, 78 | simplelog::ColorChoice::Auto, 79 | ) 80 | .unwrap(); 81 | 82 | let conn_iter = matches 83 | .indices_of("connector") 84 | .zip(matches.get_many::("connector")) 85 | .map(|(a, b)| a.zip(b.map(String::as_str))) 86 | .into_iter() 87 | .flatten(); 88 | 89 | let os_iter = matches 90 | .indices_of("os") 91 | .zip(matches.get_many::("os")) 92 | .map(|(a, b)| a.zip(b.map(String::as_str))) 93 | .into_iter() 94 | .flatten(); 95 | 96 | ConnectorChain::new(conn_iter, os_iter) 97 | } 98 | -------------------------------------------------------------------------------- /memflow-win32/offsets/10_0_18362_X64_0AFB69F5FD264D54673570E37B38A3181.toml: -------------------------------------------------------------------------------- 1 | [header] 2 | pdb_file_name = "ntkrnlmp.pdb" 3 | pdb_guid = "0AFB69F5FD264D54673570E37B38A3181" 4 | nt_major_version = 10 5 | nt_minor_version = 0 6 | nt_build_number = 18362 7 | arch = "X64" 8 | 9 | [offsets] 10 | list_blink = 8 11 | eproc_link = 752 12 | phys_mem_block = 5719112 13 | kproc_dtb = 40 14 | eproc_pid = 744 15 | eproc_name = 1104 16 | eproc_peb = 1016 17 | eproc_section_base = 968 18 | eproc_exit_status = 1620 19 | eproc_thread_list = 1160 20 | eproc_wow64 = 1064 21 | eproc_vad_root = 1624 22 | kthread_teb = 240 23 | ethread_list_entry = 1720 24 | teb_peb = 96 25 | teb_peb_x86 = 48 26 | 27 | [offsets.mmvad] 28 | vad_node = 0 29 | starting_vpn = 24 30 | ending_vpn = 28 31 | starting_vpn_high = 32 32 | ending_vpn_high = 33 33 | u = 48 34 | protection_bit = 7 35 | -------------------------------------------------------------------------------- /memflow-win32/offsets/10_0_19041_X64_1C9875F76C8F0FBF3EB9A9D7C1C274061.toml: -------------------------------------------------------------------------------- 1 | [header] 2 | pdb_file_name = "ntkrnlmp.pdb" 3 | pdb_guid = "1C9875F76C8F0FBF3EB9A9D7C1C274061" 4 | nt_major_version = 10 5 | nt_minor_version = 0 6 | nt_build_number = 19041 7 | arch = "X64" 8 | 9 | [offsets] 10 | list_blink = 8 11 | eproc_link = 1096 12 | phys_mem_block = 13612224 13 | kproc_dtb = 40 14 | eproc_pid = 1088 15 | eproc_name = 1448 16 | eproc_peb = 1360 17 | eproc_section_base = 1312 18 | eproc_exit_status = 2004 19 | eproc_thread_list = 1504 20 | eproc_wow64 = 1408 21 | eproc_vad_root = 2008 22 | kthread_teb = 240 23 | ethread_list_entry = 1256 24 | teb_peb = 96 25 | teb_peb_x86 = 48 26 | 27 | [offsets.mmvad] 28 | vad_node = 0 29 | starting_vpn = 24 30 | ending_vpn = 28 31 | starting_vpn_high = 32 32 | ending_vpn_high = 33 33 | u = 48 34 | protection_bit = 7 35 | -------------------------------------------------------------------------------- /memflow-win32/offsets/10_0_19041_X64_9C00B19DBDE003DBFE4AB4216993C8431.toml: -------------------------------------------------------------------------------- 1 | [header] 2 | pdb_file_name = "ntkrnlmp.pdb" 3 | pdb_guid = "9C00B19DBDE003DBFE4AB4216993C8431" 4 | nt_major_version = 10 5 | nt_minor_version = 0 6 | nt_build_number = 19041 7 | arch = "X64" 8 | 9 | [offsets] 10 | list_blink = 8 11 | eproc_link = 1096 12 | phys_mem_block = 13616296 13 | kproc_dtb = 40 14 | eproc_pid = 1088 15 | eproc_name = 1448 16 | eproc_peb = 1360 17 | eproc_section_base = 1312 18 | eproc_exit_status = 2004 19 | eproc_thread_list = 1504 20 | eproc_wow64 = 1408 21 | eproc_vad_root = 2008 22 | kthread_teb = 240 23 | ethread_list_entry = 1256 24 | teb_peb = 96 25 | teb_peb_x86 = 48 26 | 27 | [offsets.mmvad] 28 | vad_node = 0 29 | starting_vpn = 24 30 | ending_vpn = 28 31 | starting_vpn_high = 32 32 | ending_vpn_high = 33 33 | u = 48 34 | protection_bit = 7 35 | -------------------------------------------------------------------------------- /memflow-win32/offsets/10_0_19041_X64_BBED7C2955FBE4522AAA23F4B8677AD91.toml: -------------------------------------------------------------------------------- 1 | [header] 2 | pdb_file_name = "ntkrnlmp.pdb" 3 | pdb_guid = "BBED7C2955FBE4522AAA23F4B8677AD91" 4 | nt_major_version = 10 5 | nt_minor_version = 0 6 | nt_build_number = 19041 7 | arch = "X64" 8 | 9 | [offsets] 10 | list_blink = 8 11 | eproc_link = 1096 12 | phys_mem_block = 13612224 13 | kproc_dtb = 40 14 | eproc_pid = 1088 15 | eproc_name = 1448 16 | eproc_peb = 1360 17 | eproc_section_base = 1312 18 | eproc_exit_status = 2004 19 | eproc_thread_list = 1504 20 | eproc_wow64 = 1408 21 | eproc_vad_root = 2008 22 | kthread_teb = 240 23 | ethread_list_entry = 1256 24 | teb_peb = 96 25 | teb_peb_x86 = 48 26 | 27 | [offsets.mmvad] 28 | vad_node = 0 29 | starting_vpn = 24 30 | ending_vpn = 28 31 | starting_vpn_high = 32 32 | ending_vpn_high = 33 33 | u = 48 34 | protection_bit = 7 35 | -------------------------------------------------------------------------------- /memflow-win32/offsets/10_0_19041_X86_1B1D6AA205E1C87DC63A314ACAA50B491.toml: -------------------------------------------------------------------------------- 1 | [header] 2 | pdb_file_name = "ntkrpamp.pdb" 3 | pdb_guid = "1B1D6AA205E1C87DC63A314ACAA50B491" 4 | nt_major_version = 10 5 | nt_minor_version = 0 6 | nt_build_number = 19041 7 | arch = "X86" 8 | 9 | [offsets] 10 | list_blink = 4 11 | eproc_link = 232 12 | phys_mem_block = 3225556 13 | kproc_dtb = 24 14 | eproc_pid = 228 15 | eproc_name = 428 16 | eproc_peb = 380 17 | eproc_section_base = 352 18 | eproc_exit_status = 844 19 | eproc_thread_list = 464 20 | eproc_wow64 = 0 21 | eproc_vad_root = 848 22 | kthread_teb = 168 23 | ethread_list_entry = 740 24 | teb_peb = 48 25 | teb_peb_x86 = 48 26 | 27 | [offsets.mmvad] 28 | vad_node = 0 29 | starting_vpn = 12 30 | ending_vpn = 16 31 | starting_vpn_high = 0 32 | ending_vpn_high = 0 33 | u = 28 34 | protection_bit = 7 35 | -------------------------------------------------------------------------------- /memflow-win32/offsets/10_0_19045_X64_5F0CF5D532F385333A9B4ABA25CA65961.toml: -------------------------------------------------------------------------------- 1 | [header] 2 | pdb_file_name = 'ntkrnlmp.pdb' 3 | pdb_guid = '5F0CF5D532F385333A9B4ABA25CA65961' 4 | nt_major_version = 10 5 | nt_minor_version = 0 6 | nt_build_number = 19045 7 | arch = 'X64' 8 | 9 | [offsets] 10 | list_blink = 8 11 | eproc_link = 1096 12 | phys_mem_block = 13612216 13 | kproc_dtb = 40 14 | eproc_pid = 1088 15 | eproc_name = 1448 16 | eproc_peb = 1360 17 | eproc_section_base = 1312 18 | eproc_exit_status = 2004 19 | eproc_thread_list = 1504 20 | eproc_wow64 = 1408 21 | eproc_vad_root = 2008 22 | kthread_teb = 240 23 | ethread_list_entry = 1256 24 | teb_peb = 96 25 | teb_peb_x86 = 48 26 | 27 | [offsets.mmvad] 28 | vad_node = 0 29 | starting_vpn = 24 30 | ending_vpn = 28 31 | starting_vpn_high = 32 32 | ending_vpn_high = 33 33 | u = 48 34 | protection_bit = 7 35 | -------------------------------------------------------------------------------- /memflow-win32/offsets/10_0_22000_X86_55678BC384F099B6ED05E9E39046924A1.toml: -------------------------------------------------------------------------------- 1 | [header] 2 | pdb_file_name = "ntkrnlmp.pdb" 3 | pdb_guid = "55678BC384F099B6ED05E9E39046924A1" 4 | nt_major_version = 10 5 | nt_minor_version = 0 6 | nt_build_number = 22000 7 | arch = "X86" 8 | 9 | [offsets] 10 | list_blink = 8 11 | eproc_link = 1096 12 | phys_mem_block = 13658416 13 | kproc_dtb = 40 14 | eproc_pid = 1088 15 | eproc_name = 1448 16 | eproc_peb = 1360 17 | eproc_section_base = 1312 18 | eproc_exit_status = 2004 19 | eproc_thread_list = 1504 20 | eproc_wow64 = 1408 21 | eproc_vad_root = 2008 22 | kthread_teb = 240 23 | ethread_list_entry = 1336 24 | teb_peb = 96 25 | teb_peb_x86 = 48 26 | 27 | [offsets.mmvad] 28 | vad_node = 0 29 | starting_vpn = 24 30 | ending_vpn = 28 31 | starting_vpn_high = 32 32 | ending_vpn_high = 33 33 | u = 48 34 | protection_bit = 7 35 | -------------------------------------------------------------------------------- /memflow-win32/offsets/3_10_511_X86.toml: -------------------------------------------------------------------------------- 1 | [header] 2 | nt_major_version = 3 3 | nt_minor_version = 10 4 | nt_build_number = 511 5 | arch = 'X86' 6 | 7 | [offsets] 8 | phys_mem_block = 0 9 | 10 | list_blink = 4 11 | eproc_link = 0xb4 12 | 13 | kproc_dtb = 0x38 14 | eproc_pid = 0xb0 15 | eproc_name = 0x228 16 | eproc_peb = 0x1c0 17 | eproc_section_base = 0x1c4 18 | eproc_exit_status = 0 #5.1+ 19 | eproc_thread_list = 0 #5.1+ 20 | eproc_wow64 = 0 #5.0+ 21 | eproc_vad_root = 0x01a4 #3.10+ 22 | 23 | kthread_teb = 0 #6.2+ 24 | ethread_list_entry = 0x0 #5.0+ 25 | teb_peb = 0 #? 26 | teb_peb_x86 = 0 #? 27 | 28 | [offsets.mmvad] 29 | vad_node = 12 30 | starting_vpn = 0 31 | ending_vpn = 4 32 | starting_vpn_high = 0 33 | ending_vpn_high = 0 34 | u = 20 35 | protection_bit = 0 36 | -------------------------------------------------------------------------------- /memflow-win32/offsets/4_0_1381_X86.toml: -------------------------------------------------------------------------------- 1 | [header] 2 | nt_major_version = 4 3 | nt_minor_version = 0 4 | nt_build_number = 1381 5 | arch = 'X86' 6 | 7 | [offsets] 8 | phys_mem_block = 0 9 | 10 | list_blink = 4 11 | eproc_link = 0x98 12 | 13 | kproc_dtb = 0x18 14 | eproc_pid = 0x94 15 | eproc_name = 0x1dc 16 | eproc_peb = 0x18c 17 | eproc_section_base = 0x190 18 | eproc_exit_status = 0 #5.1+ 19 | eproc_thread_list = 0 #5.1+ 20 | eproc_wow64 = 0 #5.0+ 21 | eproc_vad_root = 0x0170 #3.10+ 22 | 23 | kthread_teb = 0 #6.2+ 24 | ethread_list_entry = 0x0 #5.0+ 25 | teb_peb = 0 #? 26 | teb_peb_x86 = 0 #? 27 | 28 | [offsets.mmvad] 29 | vad_node = 12 30 | starting_vpn = 0 31 | ending_vpn = 4 32 | starting_vpn_high = 0 33 | ending_vpn_high = 0 34 | u = 20 35 | protection_bit = 0 36 | -------------------------------------------------------------------------------- /memflow-win32/offsets/5_2_3790_X64_82DCF67A38274C9CA99B60B421D2786D2.toml: -------------------------------------------------------------------------------- 1 | [header] 2 | pdb_file_name = "ntkrnlmp.pdb" 3 | pdb_guid = "82DCF67A38274C9CA99B60B421D2786D2" 4 | nt_major_version = 5 5 | nt_minor_version = 2 6 | nt_build_number = 3790 7 | arch = "X64" 8 | 9 | [offsets] 10 | list_blink = 8 11 | eproc_link = 224 12 | phys_mem_block = 1921104 13 | kproc_dtb = 40 14 | eproc_pid = 216 15 | eproc_name = 616 16 | eproc_peb = 704 17 | eproc_section_base = 504 18 | eproc_exit_status = 908 19 | eproc_thread_list = 656 20 | eproc_wow64 = 680 21 | eproc_vad_root = 920 22 | kthread_teb = 176 23 | ethread_list_entry = 976 24 | teb_peb = 96 25 | teb_peb_x86 = 48 26 | 27 | [offsets.mmvad] 28 | vad_node = 8 29 | starting_vpn = 24 30 | ending_vpn = 32 31 | starting_vpn_high = 0 32 | ending_vpn_high = 0 33 | u = 40 34 | protection_bit = 56 35 | -------------------------------------------------------------------------------- /memflow-win32/offsets/6_1_7601_X64_ECE191A20CFF4465AE46DF96C22638451.toml: -------------------------------------------------------------------------------- 1 | [header] 2 | pdb_file_name = "ntkrnlmp.pdb" 3 | pdb_guid = "ECE191A20CFF4465AE46DF96C22638451" 4 | nt_major_version = 6 5 | nt_minor_version = 1 6 | nt_build_number = 7601 7 | arch = "X64" 8 | 9 | [offsets] 10 | list_blink = 8 11 | eproc_link = 392 12 | phys_mem_block = 2740280 13 | kproc_dtb = 40 14 | eproc_pid = 384 15 | eproc_name = 736 16 | eproc_peb = 824 17 | eproc_section_base = 624 18 | eproc_exit_status = 1092 19 | eproc_thread_list = 776 20 | eproc_wow64 = 800 21 | eproc_vad_root = 1096 22 | kthread_teb = 184 23 | ethread_list_entry = 1064 24 | teb_peb = 96 25 | teb_peb_x86 = 48 26 | 27 | [offsets.mmvad] 28 | vad_node = 8 29 | starting_vpn = 24 30 | ending_vpn = 32 31 | starting_vpn_high = 0 32 | ending_vpn_high = 0 33 | u = 40 34 | protection_bit = 56 35 | -------------------------------------------------------------------------------- /memflow-win32/offsets/6_1_7601_X86_684DA42A30CC450F81C535B4D18944B12.toml: -------------------------------------------------------------------------------- 1 | [header] 2 | pdb_file_name = "ntkrpamp.pdb" 3 | pdb_guid = "684DA42A30CC450F81C535B4D18944B12" 4 | nt_major_version = 6 5 | nt_minor_version = 1 6 | nt_build_number = 7601 7 | arch = "X86" 8 | 9 | [offsets] 10 | list_blink = 4 11 | eproc_link = 184 12 | phys_mem_block = 1484392 13 | kproc_dtb = 24 14 | eproc_pid = 180 15 | eproc_name = 364 16 | eproc_peb = 424 17 | eproc_section_base = 300 18 | eproc_exit_status = 628 19 | eproc_thread_list = 392 20 | eproc_wow64 = 0 21 | eproc_vad_root = 632 22 | kthread_teb = 136 23 | ethread_list_entry = 616 24 | teb_peb = 48 25 | teb_peb_x86 = 48 26 | 27 | [offsets.mmvad] 28 | vad_node = 4 29 | starting_vpn = 12 30 | ending_vpn = 16 31 | starting_vpn_high = 0 32 | ending_vpn_high = 0 33 | u = 20 34 | protection_bit = 24 35 | -------------------------------------------------------------------------------- /memflow-win32/src/ida_signatures.rs: -------------------------------------------------------------------------------- 1 | /// Generates a regex string from an ida-like hex string. 2 | /// Main benefit of this is that you can directly use your ida signatures, 3 | /// while not needing to decode them at runtime or manually beforehand. 4 | #[macro_export] 5 | macro_rules! ida_regex { 6 | ( $($token:tt)+ ) => { 7 | concat!( 8 | "(?-u)", // Search with unicode code-points disabled. \\x4C is actually equal to the hex byte 0x4C 9 | $( 10 | $crate::ida_regex_part!($token) 11 | ),* 12 | ) 13 | }; 14 | } 15 | 16 | #[macro_export] 17 | macro_rules! ida_regex_part { 18 | (?) => { 19 | "(?s:.)" 20 | }; 21 | ($hex:tt) => { 22 | concat!("\\x", stringify!($hex)) 23 | }; 24 | } 25 | -------------------------------------------------------------------------------- /memflow-win32/src/kernel/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod ntos; 2 | pub mod start_block; 3 | pub mod sysproc; 4 | 5 | pub use memflow_win32_defs::kernel::*; 6 | pub use start_block::StartBlock; 7 | -------------------------------------------------------------------------------- /memflow-win32/src/kernel/ntos.rs: -------------------------------------------------------------------------------- 1 | pub(crate) mod pehelper; 2 | 3 | mod x64; 4 | mod x86; 5 | 6 | use super::{StartBlock, Win32Guid, Win32Version}; 7 | 8 | use std::convert::TryInto; 9 | use std::prelude::v1::*; 10 | 11 | use log::{info, warn}; 12 | 13 | use memflow::architecture::ArchitectureObj; 14 | use memflow::error::{Error, ErrorKind, ErrorOrigin, PartialResultExt, Result}; 15 | use memflow::mem::{MemoryView, VirtualTranslate}; 16 | use memflow::types::{umem, Address}; 17 | 18 | use pelite::{self, pe64::debug::CodeView, pe64::exports::Export, PeView}; 19 | 20 | pub fn find( 21 | virt_mem: &mut T, 22 | start_block: &StartBlock, 23 | ) -> Result<(Address, umem)> { 24 | let arch_obj = ArchitectureObj::from(start_block.arch); 25 | if arch_obj.bits() == 64 { 26 | if !start_block.kernel_hint.is_null() { 27 | match x64::find_with_va_hint(virt_mem, start_block) { 28 | Ok(b) => return Ok(b), 29 | Err(e) => warn!("x64::find_with_va_hint() error: {}", e), 30 | } 31 | } 32 | 33 | match x64::find(virt_mem, start_block) { 34 | Ok(b) => return Ok(b), 35 | Err(e) => warn!("x64::find() error: {}", e), 36 | } 37 | } else if arch_obj.bits() == 32 { 38 | match x86::find(virt_mem, start_block) { 39 | Ok(b) => return Ok(b), 40 | Err(e) => warn!("x86::find() error: {}", e), 41 | } 42 | } 43 | 44 | Err(Error(ErrorOrigin::OsLayer, ErrorKind::ProcessNotFound) 45 | .log_info("unable to find ntoskrnl.exe")) 46 | } 47 | 48 | // TODO: move to pe::... 49 | pub fn find_guid(mem: &mut T, kernel_base: Address) -> Result { 50 | let image = pehelper::try_get_pe_image(mem, kernel_base)?; 51 | let pe = PeView::from_bytes(&image) 52 | .map_err(|err| Error(ErrorOrigin::OsLayer, ErrorKind::InvalidExeFile).log_info(err))?; 53 | 54 | let debug = match pe.debug() { 55 | Ok(d) => d, 56 | Err(_) => { 57 | return Err(Error(ErrorOrigin::OsLayer, ErrorKind::InvalidExeFile) 58 | .log_info("unable to read debug_data in pe header")) 59 | } 60 | }; 61 | 62 | let code_view = debug 63 | .iter() 64 | .map(|e| e.entry()) 65 | .filter_map(std::result::Result::ok) 66 | .find(|&e| e.as_code_view().is_some()) 67 | .ok_or_else(|| { 68 | Error(ErrorOrigin::OsLayer, ErrorKind::InvalidExeFile) 69 | .log_info("unable to find codeview debug_data entry") 70 | })? 71 | .as_code_view() 72 | .ok_or_else(|| { 73 | Error(ErrorOrigin::OsLayer, ErrorKind::InvalidExeFile) 74 | .log_info("unable to find codeview debug_data entry") 75 | })?; 76 | 77 | let signature = match code_view { 78 | CodeView::Cv70 { image, .. } => image.Signature, 79 | CodeView::Cv20 { .. } => { 80 | return Err(Error(ErrorOrigin::OsLayer, ErrorKind::InvalidExeFile) 81 | .log_info("invalid code_view entry version 2 found, expected 7")) 82 | } 83 | }; 84 | 85 | let file_name = code_view.pdb_file_name().to_str().map_err(|_| { 86 | Error(ErrorOrigin::OsLayer, ErrorKind::Encoding) 87 | .log_info("unable to convert pdb file name to string") 88 | })?; 89 | let guid = format!("{:X}{:X}", signature, code_view.age()); 90 | Ok(Win32Guid::new(file_name, &guid)) 91 | } 92 | 93 | fn get_export(pe: &PeView, name: &str) -> Result { 94 | info!("trying to find {} export", name); 95 | let export = match pe 96 | .get_export_by_name(name) 97 | .map_err(|err| Error(ErrorOrigin::OsLayer, ErrorKind::ExportNotFound).log_info(err))? 98 | { 99 | Export::Symbol(s) => *s as umem, 100 | Export::Forward(_) => { 101 | return Err(Error(ErrorOrigin::OsLayer, ErrorKind::ExportNotFound) 102 | .log_info("Export found but it was a forwarded export")) 103 | } 104 | }; 105 | info!("{} found at 0x{:x}", name, export); 106 | Ok(export) 107 | } 108 | 109 | pub fn find_winver(mem: &mut T, kernel_base: Address) -> Result { 110 | let image = pehelper::try_get_pe_image(mem, kernel_base)?; 111 | let pe = PeView::from_bytes(&image) 112 | .map_err(|err| Error(ErrorOrigin::OsLayer, ErrorKind::InvalidExeFile).log_info(err))?; 113 | 114 | // NtBuildNumber 115 | let nt_build_number_ref = get_export(&pe, "NtBuildNumber")?; 116 | let rtl_get_version_ref = get_export(&pe, "RtlGetVersion"); 117 | 118 | let nt_build_number: u32 = mem.read(kernel_base + nt_build_number_ref)?; 119 | info!("nt_build_number: {}", nt_build_number); 120 | if nt_build_number == 0 { 121 | return Err(Error(ErrorOrigin::OsLayer, ErrorKind::InvalidExeFile) 122 | .log_info("unable to fetch nt build number")); 123 | } 124 | 125 | // TODO: these reads should be optional 126 | // try to find major/minor version 127 | // read from KUSER_SHARED_DATA. these fields exist since nt 4.0 so they have to exist in case NtBuildNumber exists. 128 | let mut nt_major_version: u32 = mem.read((0x7ffe0000 + 0x026C).into()).data_part()?; 129 | let mut nt_minor_version: u32 = mem.read((0x7ffe0000 + 0x0270).into()).data_part()?; 130 | 131 | // fallback on x64: try to parse RtlGetVersion assembly 132 | if nt_major_version == 0 && rtl_get_version_ref.is_ok() { 133 | let mut buf = [0u8; 0x100]; 134 | mem.read_into(kernel_base + rtl_get_version_ref.unwrap(), &mut buf) 135 | .data_part()?; 136 | 137 | nt_major_version = 0; 138 | nt_minor_version = 0; 139 | 140 | for i in 0..0xf0 { 141 | if nt_major_version == 0 142 | && nt_minor_version == 0 143 | && u32::from_le_bytes(buf[i..i + 4].try_into().unwrap()) == 0x441c748 144 | { 145 | nt_major_version = 146 | u16::from_le_bytes(buf[i + 4..i + 4 + 2].try_into().unwrap()) as u32; 147 | nt_minor_version = (buf[i + 5] & 0xF) as u32; 148 | } 149 | 150 | if nt_major_version == 0 151 | && u32::from_le_bytes(buf[i..i + 4].try_into().unwrap()) & 0xFFFFF == 0x441c7 152 | { 153 | nt_major_version = buf[i + 3] as u32; 154 | } 155 | 156 | if nt_minor_version == 0 157 | && u32::from_le_bytes(buf[i..i + 4].try_into().unwrap()) & 0xFFFFF == 0x841c7 158 | { 159 | nt_major_version = buf[i + 3] as u32; 160 | } 161 | } 162 | } 163 | 164 | // construct Win32BuildNumber object (major and minor version might be null but build number should be set) 165 | let version = Win32Version::new(nt_major_version, nt_minor_version, nt_build_number); 166 | info!("kernel version: {}", version); 167 | 168 | Ok(version) 169 | } 170 | -------------------------------------------------------------------------------- /memflow-win32/src/kernel/ntos/pehelper.rs: -------------------------------------------------------------------------------- 1 | use std::convert::TryInto; 2 | use std::prelude::v1::*; 3 | 4 | use log::debug; 5 | 6 | use memflow::error::{Error, ErrorKind, ErrorOrigin, PartialResultExt, Result}; 7 | use memflow::mem::MemoryView; 8 | use memflow::types::{size, umem, Address}; 9 | 10 | use pelite::{self, PeView}; 11 | 12 | pub fn try_get_pe_size(mem: &mut T, probe_addr: Address) -> Result { 13 | let mut probe_buf = vec![0; size::kb(4)]; 14 | mem.read_raw_into(probe_addr, &mut probe_buf)?; 15 | 16 | let pe_probe = PeView::from_bytes(&probe_buf) 17 | .map_err(|err| Error(ErrorOrigin::OsLayer, ErrorKind::InvalidExeFile).log_trace(err))?; 18 | 19 | let opt_header = pe_probe.optional_header(); 20 | let size_of_image = match opt_header { 21 | pelite::Wrap::T32(opt32) => opt32.SizeOfImage, 22 | pelite::Wrap::T64(opt64) => opt64.SizeOfImage, 23 | }; 24 | if size_of_image > 0 { 25 | debug!( 26 | "found pe header for image with a size of {} bytes.", 27 | size_of_image 28 | ); 29 | Ok(size_of_image as umem) 30 | } else { 31 | Err(Error(ErrorOrigin::OsLayer, ErrorKind::InvalidExeFile) 32 | .log_trace("pe size_of_image is zero")) 33 | } 34 | } 35 | 36 | pub fn try_get_pe_image(mem: &mut T, probe_addr: Address) -> Result> { 37 | let size_of_image = try_get_pe_size(mem, probe_addr)?; 38 | mem.read_raw(probe_addr, size_of_image.try_into().unwrap()) 39 | .data_part() 40 | } 41 | 42 | pub fn try_get_pe_name(mem: &mut T, probe_addr: Address) -> Result { 43 | let image = try_get_pe_image(mem, probe_addr)?; 44 | let pe = PeView::from_bytes(&image) 45 | .map_err(|err| Error(ErrorOrigin::OsLayer, ErrorKind::InvalidExeFile).log_trace(err))?; 46 | let name = pe 47 | .exports() 48 | .map_err(|_| { 49 | Error(ErrorOrigin::OsLayer, ErrorKind::InvalidExeFile) 50 | .log_trace("unable to get exports") 51 | })? 52 | .dll_name() 53 | .map_err(|_| { 54 | Error(ErrorOrigin::OsLayer, ErrorKind::InvalidExeFile) 55 | .log_trace("unable to get dll name") 56 | })? 57 | .to_str() 58 | .map_err(|_| { 59 | Error(ErrorOrigin::OsLayer, ErrorKind::Encoding) 60 | .log_trace("unable to convert dll name string") 61 | })?; 62 | debug!("try_get_pe_name: found pe header for {}", name); 63 | Ok(name.to_string()) 64 | } 65 | -------------------------------------------------------------------------------- /memflow-win32/src/kernel/ntos/x64.rs: -------------------------------------------------------------------------------- 1 | use std::prelude::v1::*; 2 | 3 | use super::pehelper; 4 | use crate::kernel::StartBlock; 5 | 6 | use log::{debug, trace}; 7 | 8 | use memflow::architecture::{x86::x64, ArchitectureObj}; 9 | use memflow::cglue::tuple::*; 10 | use memflow::dataview::PodMethods; 11 | use memflow::error::{Error, ErrorKind, ErrorOrigin, PartialResultExt, Result}; 12 | use memflow::iter::PageChunks; 13 | use memflow::mem::{MemoryView, VirtualTranslate}; 14 | use memflow::types::{mem, size, smem, umem, Address}; 15 | 16 | use pelite::image::IMAGE_DOS_HEADER; 17 | 18 | pub fn find_with_va_hint( 19 | virt_mem: &mut T, 20 | start_block: &StartBlock, 21 | ) -> Result<(Address, umem)> { 22 | debug!( 23 | "x64::find_with_va_hint: trying to find ntoskrnl.exe with va hint at {:x}", 24 | start_block.kernel_hint.to_umem() 25 | ); 26 | 27 | // va was found previously 28 | let mut va_base = start_block.kernel_hint.to_umem() & !0x0001_ffff; 29 | while va_base + mem::mb(16) > start_block.kernel_hint.to_umem() { 30 | trace!("x64::find_with_va_hint: probing at {:x}", va_base); 31 | 32 | match find_with_va(virt_mem, va_base) { 33 | Ok(a) => { 34 | let addr = Address::from(a); 35 | let size_of_image = pehelper::try_get_pe_size(virt_mem, addr)?; 36 | return Ok((addr, size_of_image)); 37 | } 38 | Err(e) => trace!("x64::find_with_va_hint: probe error {:?}", e), 39 | } 40 | 41 | va_base -= mem::mb(2); 42 | } 43 | 44 | Err(Error(ErrorOrigin::OsLayer, ErrorKind::ProcessNotFound) 45 | .log_trace("x64::find_with_va_hint: unable to locate ntoskrnl.exe via va hint")) 46 | } 47 | 48 | fn find_with_va(virt_mem: &mut T, va_base: umem) -> Result { 49 | let mut buf = vec![0; size::mb(2)]; 50 | virt_mem 51 | .read_raw_into(Address::from(va_base), &mut buf) 52 | .data_part()?; 53 | 54 | buf.chunks_exact(x64::ARCH.page_size()) 55 | .enumerate() 56 | .map(|(i, c)| { 57 | let view = PodMethods::as_data_view(c); 58 | (i, c, view.read::(0)) // TODO: potential endian mismatch 59 | }) 60 | .filter(|(_, _, p)| p.e_magic == 0x5a4d) // MZ 61 | .filter(|(_, _, p)| p.e_lfanew <= 0x800) 62 | .inspect(|(i, _, _)| { 63 | trace!( 64 | "x64::find_with_va: found potential header flags at offset {:x}", 65 | *i as umem * x64::ARCH.page_size() as umem 66 | ) 67 | }) 68 | .find(|(i, _, _)| { 69 | let probe_addr = Address::from(va_base + (*i as umem) * x64::ARCH.page_size() as umem); 70 | let name = pehelper::try_get_pe_name(virt_mem, probe_addr).unwrap_or_default(); 71 | name == "ntoskrnl.exe" 72 | }) 73 | .map(|(i, _, _)| va_base + i as umem * x64::ARCH.page_size() as umem) 74 | .ok_or_else(|| { 75 | Error(ErrorOrigin::OsLayer, ErrorKind::ProcessNotFound) 76 | .log_trace("unable to locate ntoskrnl.exe") 77 | }) 78 | } 79 | 80 | pub fn find( 81 | virt_mem: &mut T, 82 | start_block: &StartBlock, 83 | ) -> Result<(Address, umem)> { 84 | debug!("x64::find: trying to find ntoskrnl.exe with page map",); 85 | 86 | let page_map = virt_mem.virt_page_map_range_vec( 87 | smem::mb(2), 88 | (!0u64 - (1u64 << (ArchitectureObj::from(start_block.arch).address_space_bits() - 1))) 89 | .into(), 90 | (!0u64).into(), 91 | ); 92 | 93 | match page_map 94 | .into_iter() 95 | .flat_map(|CTup3(address, size, _)| size.page_chunks(address, size::mb(2))) 96 | .filter(|(_, size)| *size > mem::kb(256)) 97 | .filter_map(|(va, _)| find_with_va(virt_mem, va.to_umem()).ok()) 98 | .next() 99 | { 100 | Some(a) => { 101 | let addr = Address::from(a); 102 | let size_of_image = pehelper::try_get_pe_size(virt_mem, addr)?; 103 | Ok((addr, size_of_image)) 104 | } 105 | None => Err(Error(ErrorOrigin::OsLayer, ErrorKind::ProcessNotFound) 106 | .log_trace("x64::find: unable to locate ntoskrnl.exe with a page map")), 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /memflow-win32/src/kernel/ntos/x86.rs: -------------------------------------------------------------------------------- 1 | use std::prelude::v1::*; 2 | 3 | use super::pehelper; 4 | use crate::kernel::StartBlock; 5 | 6 | use memflow::dataview::PodMethods; 7 | use memflow::error::{Error, ErrorKind, ErrorOrigin, PartialResultExt, Result}; 8 | use memflow::mem::MemoryView; 9 | use memflow::types::{size, umem, Address}; 10 | 11 | use log::{debug, info}; 12 | 13 | use muddy::muddy; 14 | 15 | use pelite::image::IMAGE_DOS_HEADER; 16 | 17 | const SIZE_256MB: usize = size::mb(256); 18 | const SIZE_8MB: usize = size::mb(8); 19 | const SIZE_4KB: usize = size::kb(4); 20 | 21 | // https://github.com/ufrisk/MemProcFS/blob/f2d15cf4fe4f19cfeea3dad52971fae2e491064b/vmm/vmmwininit.c#L410 22 | pub fn find(virt_mem: &mut T, _start_block: &StartBlock) -> Result<(Address, umem)> { 23 | debug!("x86::find: trying to find ntoskrnl.exe"); 24 | 25 | for base_addr in (0..SIZE_256MB).step_by(SIZE_8MB) { 26 | let base_addr = size::gb(2) + base_addr; 27 | // search in each page in the first 8mb chunks in the first 64mb of virtual memory 28 | let mut buf = vec![0; SIZE_8MB]; 29 | virt_mem 30 | .read_raw_into(base_addr.into(), &mut buf) 31 | .data_part()?; 32 | 33 | for addr in (0..SIZE_8MB).step_by(SIZE_4KB) { 34 | // TODO: potential endian mismatch in pod 35 | let view = PodMethods::as_data_view(&buf[addr..]); 36 | 37 | // check for dos header signature (MZ) // TODO: create global 38 | if view.read::(0).e_magic != 0x5a4d { 39 | continue; 40 | } 41 | 42 | if view.read::(0).e_lfanew > 0x800 { 43 | continue; 44 | } 45 | 46 | let image_base = Address::from(base_addr + addr); 47 | if let Ok(name) = pehelper::try_get_pe_name(virt_mem, image_base) { 48 | if name == muddy!("ntoskrnl.exe") { 49 | info!("{}", muddy!("ntoskrnl found")); 50 | // TODO: unify pe name + size 51 | if let Ok(size_of_image) = pehelper::try_get_pe_size(virt_mem, image_base) { 52 | return Ok((image_base, size_of_image)); 53 | } 54 | } 55 | } 56 | } 57 | } 58 | 59 | Err( 60 | Error(ErrorOrigin::OsLayer, ErrorKind::ProcessNotFound).log_trace(muddy!( 61 | "find_x86(): unable to locate ntoskrnl.exe in high mem" 62 | )), 63 | ) 64 | } 65 | -------------------------------------------------------------------------------- /memflow-win32/src/kernel/start_block.rs: -------------------------------------------------------------------------------- 1 | mod aarch64; 2 | mod x64; 3 | mod x86; 4 | mod x86pae; 5 | 6 | use std::prelude::v1::*; 7 | 8 | use log::warn; 9 | 10 | use memflow::architecture::ArchitectureIdent; 11 | use memflow::error::{Error, ErrorKind, ErrorOrigin, Result}; 12 | use memflow::mem::PhysicalMemory; 13 | use memflow::types::{size, Address, PhysicalAddress}; 14 | 15 | // PROCESSOR_START_BLOCK 16 | #[derive(Debug, Copy, Clone)] 17 | #[cfg_attr(feature = "serde", derive(::serde::Serialize))] 18 | pub struct StartBlock { 19 | pub arch: ArchitectureIdent, 20 | pub kernel_hint: Address, 21 | pub dtb: Address, 22 | } 23 | 24 | pub fn find_fallback( 25 | mem: &mut T, 26 | arch: ArchitectureIdent, 27 | ) -> Result { 28 | match arch { 29 | ArchitectureIdent::X86(64, _) => { 30 | // read low 16mb stub 31 | let mut low16m = vec![0; size::mb(16)]; 32 | mem.phys_read_into(PhysicalAddress::NULL, low16m.as_mut_slice())?; 33 | 34 | x64::find(&low16m) 35 | } 36 | ArchitectureIdent::AArch64(_) => { 37 | // read low 16mb stub 38 | let mut low16m = vec![0; size::mb(16)]; 39 | 40 | //TODO: configure this, but so far arm null starts at this address 41 | mem.phys_read_into(aarch64::PHYS_BASE.into(), low16m.as_mut_slice())?; 42 | 43 | aarch64::find(&low16m) 44 | } 45 | _ => Err(Error(ErrorOrigin::OsLayer, ErrorKind::NotImplemented) 46 | .log_error("start_block: fallback not implemented for given arch")), 47 | } 48 | } 49 | 50 | // bcdedit /set firstmegabytepolicyuseall 51 | pub fn find(mem: &mut T, arch: Option) -> Result { 52 | if let Some(arch) = arch { 53 | match arch { 54 | ArchitectureIdent::X86(64, _) => { 55 | // read low 1mb stub 56 | let mut low1m = vec![0; size::mb(1)]; 57 | mem.phys_read_into(PhysicalAddress::NULL, low1m.as_mut_slice())?; 58 | 59 | // find x64 dtb in low stub < 1M 60 | match x64::find_lowstub(&low1m) { 61 | Ok(d) => { 62 | if d.dtb.to_umem() != 0 { 63 | return Ok(d); 64 | } 65 | } 66 | Err(e) => warn!("x64::find_lowstub() error: {}", e), 67 | } 68 | 69 | find_fallback(mem, arch) 70 | } 71 | ArchitectureIdent::X86(32, true) => { 72 | let mut low16m = vec![0; size::mb(16)]; 73 | mem.phys_read_into(PhysicalAddress::NULL, low16m.as_mut_slice())?; 74 | x86pae::find(&low16m) 75 | } 76 | ArchitectureIdent::X86(32, false) => { 77 | let mut low16m = vec![0; size::mb(16)]; 78 | mem.phys_read_into(PhysicalAddress::NULL, low16m.as_mut_slice())?; 79 | x86::find(&low16m) 80 | } 81 | ArchitectureIdent::AArch64(_) => find_fallback(mem, arch), 82 | _ => Err(Error(ErrorOrigin::OsLayer, ErrorKind::NotSupported) 83 | .log_error("Unsupported architecture")), 84 | } 85 | } else { 86 | find(mem, Some(ArchitectureIdent::X86(64, false))) 87 | .or_else(|_| find(mem, Some(ArchitectureIdent::X86(32, true)))) 88 | .or_else(|_| find(mem, Some(ArchitectureIdent::X86(32, false)))) 89 | .or_else(|_| find(mem, Some(ArchitectureIdent::AArch64(size::kb(4))))) 90 | .map_err(|_| { 91 | Error(ErrorOrigin::OsLayer, ErrorKind::NotFound).log_error("unable to find dtb") 92 | }) 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /memflow-win32/src/kernel/start_block/aarch64.rs: -------------------------------------------------------------------------------- 1 | use crate::kernel::StartBlock; 2 | 3 | use std::convert::TryInto; 4 | 5 | use memflow::architecture::arm::aarch64; 6 | use memflow::error::{Error, ErrorKind, ErrorOrigin, Result}; 7 | use memflow::types::{mem, umem, Address}; 8 | 9 | #[allow(clippy::unnecessary_cast)] 10 | pub const PHYS_BASE: u64 = mem::gb(1) as u64; 11 | 12 | // mem here has to be a single page (4kb sized) 13 | fn find_pt(addr: Address, mem: &[u8]) -> Option
{ 14 | // TODO: global define / config setting 15 | #[allow(clippy::unnecessary_cast)] 16 | let max_mem = mem::gb(512) as u64; 17 | 18 | let pte = u64::from_le_bytes(mem[0..8].try_into().unwrap()); 19 | 20 | if (pte & 0x0000_0000_0000_0fff) != 0xf03 || (pte & 0x0000_ffff_ffff_f000) > max_mem { 21 | return None; 22 | } 23 | 24 | // Second half must have a self ref entry 25 | // This is usually enough to filter wrong data out 26 | #[allow(clippy::unnecessary_cast)] 27 | mem[0x800..] 28 | .chunks(8) 29 | .map(|c| u64::from_le_bytes(c.try_into().unwrap())) 30 | .find(|a| (a ^ 0xf03) & (!0u64 >> 12) == addr.to_umem() as u64)?; 31 | 32 | // A page table does need to have some entries, right? Particularly, kernel-side page table 33 | // entries must exist 34 | mem[0x800..] 35 | .chunks(8) 36 | .map(|c| u64::from_le_bytes(c.try_into().unwrap())) 37 | .filter(|a| (a & 0xff) == 0x03) 38 | .nth(5)?; 39 | 40 | Some(addr) 41 | } 42 | 43 | pub fn find(mem: &[u8]) -> Result { 44 | mem.chunks_exact(aarch64::ARCH.page_size()) 45 | .enumerate() 46 | .filter_map(|(i, c)| { 47 | find_pt( 48 | Address::from(PHYS_BASE) + (i as umem * aarch64::ARCH.page_size() as umem), 49 | c, 50 | ) 51 | }) 52 | .map(|addr| StartBlock { 53 | arch: aarch64::ARCH.ident(), 54 | kernel_hint: Address::NULL, 55 | dtb: addr, 56 | }) 57 | .next() 58 | .ok_or_else(|| { 59 | Error(ErrorOrigin::OsLayer, ErrorKind::NotFound) 60 | .log_warn("unable to find aarch64 dtb in lowstub < 16M") 61 | }) 62 | } 63 | -------------------------------------------------------------------------------- /memflow-win32/src/kernel/start_block/x64.rs: -------------------------------------------------------------------------------- 1 | use crate::kernel::StartBlock; 2 | 3 | use std::convert::TryInto; 4 | 5 | use memflow::architecture::x86::x64; 6 | use memflow::error::{Error, ErrorKind, ErrorOrigin, Result}; 7 | use memflow::types::{mem, umem, Address}; 8 | 9 | // https://github.com/ufrisk/MemProcFS/blob/f2d15cf4fe4f19cfeea3dad52971fae2e491064b/vmm/vmmwininit.c#L560 10 | pub fn find_lowstub(stub: &[u8]) -> Result { 11 | stub.chunks_exact(x64::ARCH.page_size()) 12 | .skip(1) 13 | .filter(|c| { 14 | (0xffff_ffff_ffff_00ff & u64::from_le_bytes(c[0..8].try_into().unwrap())) 15 | == 0x0000_0001_0006_00E9 16 | }) // start bytes 17 | .filter(|c| { 18 | (0xffff_f800_0000_0003 & u64::from_le_bytes(c[0x70..0x70 + 8].try_into().unwrap())) 19 | == 0xffff_f800_0000_0000 20 | }) // kernel entry 21 | .find(|c| { 22 | (0xffff_ff00_0000_0fff & u64::from_le_bytes(c[0xa0..0xa0 + 8].try_into().unwrap())) == 0 23 | }) // pml4 24 | .map(|c| StartBlock { 25 | arch: x64::ARCH.ident(), 26 | kernel_hint: u64::from_le_bytes(c[0x70..0x70 + 8].try_into().unwrap()).into(), 27 | dtb: u64::from_le_bytes(c[0xa0..0xa0 + 8].try_into().unwrap()).into(), 28 | }) 29 | .ok_or_else(|| { 30 | Error(ErrorOrigin::OsLayer, ErrorKind::NotFound) 31 | .log_warn("unable to find x64 dtb in lowstub < 1M") 32 | }) 33 | } 34 | 35 | fn find_pt(addr: Address, mem: &[u8]) -> Option
{ 36 | // TODO: global define / config setting 37 | #[allow(clippy::unnecessary_cast)] 38 | let max_mem = mem::gb(512) as u64; 39 | 40 | let pte = u64::from_le_bytes(mem[0..8].try_into().unwrap()); 41 | 42 | if (pte & 0x0000_0000_0000_0087) != 0x7 || (pte & 0x0000_ffff_ffff_f000) > max_mem { 43 | return None; 44 | } 45 | 46 | // Second half must have a self ref entry 47 | // This is usually enough to filter wrong data out 48 | #[allow(clippy::unnecessary_cast)] 49 | mem[0x800..] 50 | .chunks(8) 51 | .map(|c| u64::from_le_bytes(c.try_into().unwrap())) 52 | .find(|a| (a ^ 0x0000_0000_0000_0063) & !(1u64 << 63) == addr.to_umem() as u64)?; 53 | 54 | // A page table does need to have some entries, right? Particularly, kernel-side page table 55 | // entries must be marked as such 56 | mem[0x800..] 57 | .chunks(8) 58 | .map(|c| u64::from_le_bytes(c.try_into().unwrap())) 59 | .filter(|a| (a & 0xff) == 0x63) 60 | .nth(5)?; 61 | 62 | Some(addr) 63 | } 64 | 65 | pub fn find(mem: &[u8]) -> Result { 66 | mem.chunks_exact(x64::ARCH.page_size()) 67 | .enumerate() 68 | .filter_map(|(i, c)| find_pt((i as umem * x64::ARCH.page_size() as umem).into(), c)) 69 | .map(|addr| StartBlock { 70 | arch: x64::ARCH.ident(), 71 | kernel_hint: Address::NULL, 72 | dtb: addr, 73 | }) 74 | .next() 75 | .ok_or_else(|| { 76 | Error(ErrorOrigin::OsLayer, ErrorKind::NotFound) 77 | .log_warn("unable to find x64 dtb in lowstub < 16M") 78 | }) 79 | } 80 | -------------------------------------------------------------------------------- /memflow-win32/src/kernel/start_block/x86.rs: -------------------------------------------------------------------------------- 1 | use crate::kernel::StartBlock; 2 | 3 | use std::convert::TryInto; 4 | 5 | use memflow::architecture::x86::x32; 6 | use memflow::error::{Error, ErrorKind, ErrorOrigin, Result}; 7 | use memflow::iter::PageChunks; 8 | use memflow::types::Address; 9 | 10 | fn check_page(base: Address, mem: &[u8]) -> bool { 11 | if mem[0] != 0x67 { 12 | return false; 13 | } 14 | 15 | let dword = u32::from_le_bytes(mem[0xc00..0xc00 + 4].try_into().unwrap()); 16 | if (dword & 0xffff_f003) != TryInto::::try_into(base.to_umem() + 0x3).unwrap() { 17 | return false; 18 | } 19 | 20 | matches!(mem 21 | .iter() 22 | .step_by(4) 23 | .skip(0x200) 24 | .filter(|&&x| x == 0x63 || x == 0xe3) 25 | .count(), x if x > 16) 26 | } 27 | 28 | pub fn find(mem: &[u8]) -> Result { 29 | mem.page_chunks(Address::NULL, x32::ARCH.page_size()) 30 | .find(|(a, c)| check_page(*a, c)) 31 | .map(|(a, _)| StartBlock { 32 | arch: x32::ARCH.ident(), 33 | kernel_hint: Address::NULL, 34 | dtb: a, 35 | }) 36 | .ok_or_else(|| { 37 | Error(ErrorOrigin::OsLayer, ErrorKind::NotFound) 38 | .log_warn("unable to find x86 dtb in lowstub < 16M") 39 | }) 40 | } 41 | -------------------------------------------------------------------------------- /memflow-win32/src/kernel/start_block/x86pae.rs: -------------------------------------------------------------------------------- 1 | use crate::kernel::StartBlock; 2 | 3 | use std::convert::TryInto; 4 | 5 | use memflow::architecture::x86::x32_pae; 6 | use memflow::error::{Error, ErrorKind, ErrorOrigin, Result}; 7 | use memflow::iter::PageChunks; 8 | use memflow::types::Address; 9 | 10 | #[allow(clippy::unnecessary_cast)] 11 | fn check_page(addr: Address, mem: &[u8]) -> bool { 12 | for (i, chunk) in mem.to_vec().chunks_exact(8).enumerate() { 13 | let qword = u64::from_le_bytes(chunk[0..8].try_into().unwrap()); 14 | if (i < 4 && qword != addr.to_umem() as u64 + ((i as u64 * 8) << 9) + 0x1001) 15 | || (i >= 4 && qword != 0) 16 | { 17 | return false; 18 | } 19 | } 20 | true 21 | } 22 | 23 | pub fn find(mem: &[u8]) -> Result { 24 | mem.page_chunks(Address::NULL, x32_pae::ARCH.page_size()) 25 | .find(|(a, c)| check_page(*a, c)) 26 | .map(|(a, _)| StartBlock { 27 | arch: x32_pae::ARCH.ident(), 28 | kernel_hint: Address::NULL, 29 | dtb: a, 30 | }) 31 | .ok_or_else(|| { 32 | Error(ErrorOrigin::OsLayer, ErrorKind::NotFound) 33 | .log_warn("unable to find x86_pae dtb in lowstub < 16M") 34 | }) 35 | } 36 | -------------------------------------------------------------------------------- /memflow-win32/src/kernel/sysproc.rs: -------------------------------------------------------------------------------- 1 | use std::prelude::v1::*; 2 | 3 | use super::ntos::pehelper; 4 | use super::StartBlock; 5 | 6 | use std::convert::TryInto; 7 | 8 | use log::{debug, info, warn}; 9 | 10 | use memflow::architecture::ArchitectureObj; 11 | use memflow::error::{Error, ErrorKind, ErrorOrigin, Result}; 12 | use memflow::mem::MemoryView; 13 | use memflow::types::{size, umem, Address}; 14 | 15 | use muddy::muddy; 16 | 17 | use pelite::{self, pe64::exports::Export, PeView}; 18 | 19 | pub fn find( 20 | virt_mem: &mut T, 21 | start_block: &StartBlock, 22 | ntos: Address, 23 | ) -> Result
{ 24 | debug!("trying to find system eprocess"); 25 | 26 | match find_exported(virt_mem, start_block, ntos) { 27 | Ok(e) => return Ok(e), 28 | Err(e) => warn!("{}", e), 29 | } 30 | 31 | match find_in_section(virt_mem, start_block, ntos) { 32 | Ok(e) => return Ok(e), 33 | Err(e) => warn!("{}", e), 34 | } 35 | 36 | Err(Error(ErrorOrigin::OsLayer, ErrorKind::NotFound).log_info("unable to find system eprocess")) 37 | } 38 | 39 | // find from exported symbol 40 | pub fn find_exported( 41 | virt_mem: &mut T, 42 | start_block: &StartBlock, 43 | kernel_base: Address, 44 | ) -> Result
{ 45 | // PsInitialSystemProcess -> PsActiveProcessHead 46 | let image = pehelper::try_get_pe_image(virt_mem, kernel_base)?; 47 | let pe = PeView::from_bytes(&image) 48 | .map_err(|err| Error(ErrorOrigin::OsLayer, ErrorKind::InvalidExeFile).log_info(err))?; 49 | let tgt = muddy!("PsInitialSystemProcess"); 50 | let sys_proc = match pe 51 | .get_export_by_name(tgt) 52 | .map_err(|err| Error(ErrorOrigin::OsLayer, ErrorKind::ExportNotFound).log_info(err))? 53 | { 54 | Export::Symbol(s) => kernel_base + *s as umem, 55 | Export::Forward(_) => { 56 | return Err( 57 | Error(ErrorOrigin::OsLayer, ErrorKind::ExportNotFound).log_info(muddy!( 58 | "PsInitialSystemProcess found but it was a forwarded export" 59 | )), 60 | ) 61 | } 62 | }; 63 | info!("{tgt} found at 0x{:x}", sys_proc); 64 | 65 | let arch_obj: ArchitectureObj = start_block.arch.into(); 66 | 67 | // read containing value 68 | let mut buf = vec![0u8; arch_obj.size_addr()]; 69 | let sys_proc_addr: Address = match arch_obj.bits() { 70 | 64 => { 71 | virt_mem.read_raw_into(sys_proc, &mut buf)?; 72 | u64::from_le_bytes(buf[0..8].try_into().unwrap()).into() 73 | } 74 | 32 => { 75 | virt_mem.read_raw_into(sys_proc, &mut buf)?; 76 | u32::from_le_bytes(buf[0..4].try_into().unwrap()).into() 77 | } 78 | _ => return Err(Error(ErrorOrigin::OsLayer, ErrorKind::InvalidArchitecture)), 79 | }; 80 | Ok(sys_proc_addr) 81 | } 82 | 83 | // TODO: scan in pdb 84 | 85 | // scan in section 86 | pub fn find_in_section( 87 | virt_mem: &mut T, 88 | _start_block: &StartBlock, 89 | ntos: Address, 90 | ) -> Result
{ 91 | // find section ALMOSTRO 92 | // scan for va of system process (dtb.va) 93 | // ... check if its 32 or 64bit 94 | 95 | let mut header_buf = vec![0; size::mb(32)]; 96 | virt_mem.read_raw_into(ntos, &mut header_buf)?; 97 | 98 | /* 99 | let mut pe_opts = ParseOptions::default(); 100 | pe_opts.resolve_rva = false; 101 | 102 | let header = PE::parse_with_opts(&header_buf, &pe_opts).unwrap(); // TODO: error 103 | let _sect = header 104 | .sections 105 | .iter() 106 | .filter(|s| String::from_utf8(s.name.to_vec()).unwrap_or_default() == "ALMOSTRO") 107 | .nth(0) 108 | .ok_or_else(|| Error::new("unable to find section ALMOSTRO"))?; 109 | */ 110 | 111 | Err(Error(ErrorOrigin::OsLayer, ErrorKind::NotImplemented) 112 | .log_info("sysproc::find_in_section(): not implemented yet")) 113 | } 114 | -------------------------------------------------------------------------------- /memflow-win32/src/lib.rs: -------------------------------------------------------------------------------- 1 | /*! 2 | This crate contains memflow's win32 implementation. 3 | It is used to interface with windows targets. 4 | */ 5 | #![cfg_attr(not(feature = "std"), no_std)] 6 | extern crate no_std_compat as std; 7 | 8 | pub mod kernel; 9 | 10 | pub mod offsets; 11 | 12 | pub mod win32; 13 | 14 | #[cfg(feature = "regex")] 15 | pub mod ida_signatures; 16 | 17 | pub mod prelude { 18 | pub mod v1 { 19 | pub use crate::kernel::*; 20 | pub use crate::offsets::*; 21 | pub use crate::win32::*; 22 | } 23 | pub use v1::*; 24 | } 25 | 26 | #[cfg(feature = "plugins")] 27 | pub mod plugins; 28 | -------------------------------------------------------------------------------- /memflow-win32/src/offsets/mod.rs: -------------------------------------------------------------------------------- 1 | pub use memflow_win32_defs::offsets::*; 2 | 3 | use crate::prelude::v1::*; 4 | 5 | #[repr(align(16))] 6 | struct Align16(pub T); 7 | 8 | #[cfg(feature = "embed_offsets")] 9 | const WIN32_OFFSETS: Align16< 10 | [u8; include_bytes!(concat!(env!("OUT_DIR"), "/win32_offsets.bin")).len()], 11 | > = Align16(*include_bytes!(concat!( 12 | env!("OUT_DIR"), 13 | "/win32_offsets.bin" 14 | ))); 15 | 16 | pub fn offset_builder<'a>() -> Win32OffsetBuilder<'a> { 17 | let builder = Win32Offsets::builder(); 18 | 19 | #[cfg(feature = "embed_offsets")] 20 | { 21 | // # Safety 22 | // Struct padding and alignment is compile-time guaranteed by the struct (see mod offset_table). 23 | let offsets = unsafe { 24 | core::slice::from_raw_parts( 25 | WIN32_OFFSETS.0.as_ptr() as *const Win32OffsetFile, 26 | WIN32_OFFSETS.0.len() / std::mem::size_of::(), 27 | ) 28 | }; 29 | builder.offset_list(offsets) 30 | } 31 | #[cfg(not(feature = "embed_offsets"))] 32 | builder 33 | } 34 | 35 | pub fn offset_builder_with_kernel_info<'a>( 36 | kernel_info: &Win32KernelInfo, 37 | ) -> Win32OffsetBuilder<'a> { 38 | let builder = offset_builder(); 39 | kernel_info.into_offset_builder(builder) 40 | } 41 | -------------------------------------------------------------------------------- /memflow-win32/src/plugins.rs: -------------------------------------------------------------------------------- 1 | use crate::offsets::SymbolStore; 2 | use crate::win32::{Win32Kernel, Win32KernelBuilder}; 3 | 4 | use memflow::cglue; 5 | use memflow::plugins::{args, OsArgs}; 6 | use memflow::prelude::v1::*; 7 | use memflow::types::cache::TimedCacheValidator; 8 | 9 | use std::time::Duration; 10 | 11 | #[os(name = "win32", accept_input = true, return_wrapped = true)] 12 | pub fn create_os( 13 | args: &OsArgs, 14 | mem: Option>, 15 | lib: LibArc, 16 | ) -> Result> { 17 | let mem = mem.ok_or_else(|| { 18 | Error(ErrorOrigin::OsLayer, ErrorKind::Configuration).log_error("Must provide memory!") 19 | })?; 20 | 21 | let builder = Win32Kernel::builder(mem); 22 | build_dtb(builder, &args.extra_args, lib) 23 | } 24 | 25 | fn build_final< 26 | A: 'static + PhysicalMemory + Clone, 27 | B: 'static + PhysicalMemory + Clone, 28 | C: 'static + VirtualTranslate2 + Clone, 29 | >( 30 | kernel_builder: Win32KernelBuilder, 31 | _: &Args, 32 | lib: LibArc, 33 | ) -> Result> { 34 | log::info!( 35 | "Building kernel of type {}", 36 | std::any::type_name::>() 37 | ); 38 | let kernel = kernel_builder.build()?; 39 | Ok(group_obj!((kernel, lib) as OsInstance)) 40 | } 41 | 42 | fn build_arch< 43 | A: 'static + PhysicalMemory + Clone, 44 | B: 'static + PhysicalMemory + Clone, 45 | C: 'static + VirtualTranslate2 + Clone, 46 | >( 47 | builder: Win32KernelBuilder, 48 | args: &Args, 49 | lib: LibArc, 50 | ) -> Result> { 51 | match args.get("arch").map(|a| a.to_lowercase()).as_deref() { 52 | Some("x64") => build_final(builder.arch(ArchitectureIdent::X86(64, false)), args, lib), 53 | Some("x32") => build_final(builder.arch(ArchitectureIdent::X86(32, false)), args, lib), 54 | Some("x32_pae") => build_final(builder.arch(ArchitectureIdent::X86(32, true)), args, lib), 55 | Some("aarch64") => build_final( 56 | builder.arch(ArchitectureIdent::AArch64(size::kb(4))), 57 | args, 58 | lib, 59 | ), 60 | _ => build_final(builder, args, lib), 61 | } 62 | } 63 | 64 | fn build_symstore< 65 | A: 'static + PhysicalMemory + Clone, 66 | B: 'static + PhysicalMemory + Clone, 67 | C: 'static + VirtualTranslate2 + Clone, 68 | >( 69 | builder: Win32KernelBuilder, 70 | args: &Args, 71 | lib: LibArc, 72 | ) -> Result> { 73 | match args.get("symstore") { 74 | Some("uncached") => build_arch( 75 | builder.symbol_store(SymbolStore::new().no_cache()), 76 | args, 77 | lib, 78 | ), 79 | Some("none") => build_arch(builder.no_symbol_store(), args, lib), 80 | _ => build_arch(builder, args, lib), 81 | } 82 | } 83 | 84 | fn build_kernel_hint< 85 | A: 'static + PhysicalMemory + Clone, 86 | B: 'static + PhysicalMemory + Clone, 87 | C: 'static + VirtualTranslate2 + Clone, 88 | >( 89 | builder: Win32KernelBuilder, 90 | args: &Args, 91 | lib: LibArc, 92 | ) -> Result> { 93 | match args 94 | .get("kernel_hint") 95 | .and_then(|d| u64::from_str_radix(d, 16).ok()) 96 | { 97 | Some(dtb) => build_symstore(builder.kernel_hint(Address::from(dtb)), args, lib), 98 | _ => build_symstore(builder, args, lib), 99 | } 100 | } 101 | 102 | fn build_vat< 103 | A: 'static + PhysicalMemory + Clone, 104 | B: 'static + PhysicalMemory + Clone, 105 | C: 'static + VirtualTranslate2 + Clone, 106 | >( 107 | builder: Win32KernelBuilder, 108 | args: &Args, 109 | lib: LibArc, 110 | ) -> Result> { 111 | match args::parse_vatcache(args)? { 112 | Some((0, _)) => build_kernel_hint( 113 | builder.build_vat_cache(|v, a| { 114 | CachedVirtualTranslate::builder(v).arch(a).build().unwrap() 115 | }), 116 | args, 117 | lib, 118 | ), 119 | Some((size, time)) => build_kernel_hint( 120 | builder.build_vat_cache(move |v, a| { 121 | let builder = CachedVirtualTranslate::builder(v).arch(a).entries(size); 122 | 123 | if time > 0 { 124 | builder 125 | .validator(TimedCacheValidator::new(Duration::from_millis(time).into())) 126 | .build() 127 | .unwrap() 128 | } else { 129 | builder.build().unwrap() 130 | } 131 | }), 132 | args, 133 | lib, 134 | ), 135 | None => build_kernel_hint(builder, args, lib), 136 | } 137 | } 138 | 139 | fn build_dtb< 140 | A: 'static + PhysicalMemory + Clone, 141 | B: 'static + PhysicalMemory + Clone, 142 | C: 'static + VirtualTranslate2 + Clone, 143 | >( 144 | builder: Win32KernelBuilder, 145 | args: &Args, 146 | lib: LibArc, 147 | ) -> Result> { 148 | match args 149 | .get("dtb") 150 | .and_then(|d| u64::from_str_radix(d, 16).ok()) 151 | { 152 | Some(dtb) => build_vat(builder.dtb(Address::from(dtb)), args, lib), 153 | _ => build_vat(builder, args, lib), 154 | } 155 | } 156 | -------------------------------------------------------------------------------- /memflow-win32/src/win32.rs: -------------------------------------------------------------------------------- 1 | pub mod kernel; 2 | pub mod kernel_builder; 3 | pub mod kernel_info; 4 | 5 | pub use kernel::Win32Kernel; 6 | pub use kernel_builder::Win32KernelBuilder; 7 | pub use kernel_info::Win32KernelInfo; 8 | 9 | pub mod keyboard; 10 | pub mod module; 11 | pub mod process; 12 | pub mod unicode_string; 13 | pub mod vat; 14 | pub mod vkey; 15 | 16 | pub use keyboard::*; 17 | pub use module::*; 18 | pub use process::*; 19 | pub use unicode_string::*; 20 | pub use vat::*; 21 | -------------------------------------------------------------------------------- /memflow-win32/src/win32/kernel/mem_map.rs: -------------------------------------------------------------------------------- 1 | use std::prelude::v1::*; 2 | 3 | use log::{info, trace}; 4 | use std::fmt; 5 | 6 | use memflow::mem::{MemoryMap, MemoryView}; 7 | use memflow::types::{mem, umem, Address}; 8 | 9 | use memflow::dataview::Pod; 10 | 11 | #[allow(clippy::unnecessary_cast)] 12 | const SIZE_4KB: u64 = mem::kb(4) as u64; 13 | 14 | /// The number of PhysicalMemoryRuns contained in the Header 15 | pub const PHYSICAL_MEMORY_MAX_RUNS: usize = 32; 16 | 17 | #[repr(C)] 18 | #[derive(Copy, Clone, Debug)] 19 | pub struct PhysicalMemoryRun { 20 | pub base_page: T, 21 | pub page_count: T, 22 | } 23 | unsafe impl Pod for PhysicalMemoryRun {} 24 | 25 | #[repr(C)] 26 | #[derive(Copy, Clone, Debug)] 27 | pub struct PhysicalMemoryDescriptor { 28 | pub number_of_runs: T, 29 | pub number_of_pages: T, 30 | pub runs: [PhysicalMemoryRun; PHYSICAL_MEMORY_MAX_RUNS], 31 | } 32 | unsafe impl Pod for PhysicalMemoryDescriptor {} 33 | const _: [(); std::mem::size_of::>()] = [(); 0x108]; 34 | const _: [(); std::mem::size_of::>()] = [(); 0x210]; 35 | 36 | pub fn parse>( 37 | virt_mem: &mut T, 38 | descriptor_ptr_ptr: Address, 39 | ) -> Option> { 40 | let descriptor_ptr = virt_mem.read_addr64(descriptor_ptr_ptr).ok()?; 41 | 42 | trace!("found phys_mem_block pointer at: {}", descriptor_ptr); 43 | let descriptor: PhysicalMemoryDescriptor = virt_mem.read(descriptor_ptr).ok()?; 44 | 45 | trace!("found phys_mem_block: {:?}", descriptor); 46 | if descriptor.number_of_runs.into() <= PHYSICAL_MEMORY_MAX_RUNS as u64 { 47 | let mut mem_map = MemoryMap::new(); 48 | 49 | for i in 0..descriptor.number_of_runs.into() { 50 | let base = descriptor.runs[i as usize].base_page.into() * SIZE_4KB; 51 | let size = descriptor.runs[i as usize].page_count.into() * SIZE_4KB; 52 | 53 | trace!("adding memory mapping: base={:x} size={:x}", base, size); 54 | mem_map.push_remap(base.into(), size as umem, Address::from(base)); 55 | } 56 | 57 | Some(mem_map) 58 | } else { 59 | info!( 60 | "too many memory segments in phys_mem_block: {} found, {} expected", 61 | descriptor.number_of_runs.into(), 62 | PHYSICAL_MEMORY_MAX_RUNS 63 | ); 64 | None 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /memflow-win32/src/win32/kernel_builder.rs: -------------------------------------------------------------------------------- 1 | use std::prelude::v1::*; 2 | 3 | use super::{Win32Kernel, Win32KernelInfo}; 4 | use crate::offsets::Win32Offsets; 5 | 6 | #[cfg(feature = "symstore")] 7 | use crate::offsets::SymbolStore; 8 | 9 | use crate::offsets::offset_builder_with_kernel_info; 10 | 11 | use memflow::architecture::ArchitectureIdent; 12 | use memflow::cglue::forward::ForwardMut; 13 | use memflow::error::Result; 14 | use memflow::mem::{ 15 | phys_mem::CachedPhysicalMemory, virt_translate::CachedVirtualTranslate, DirectTranslate, 16 | PhysicalMemory, VirtualTranslate2, 17 | }; 18 | use memflow::types::{Address, DefaultCacheValidator}; 19 | 20 | /// Builder for a Windows Kernel structure. 21 | /// 22 | /// This function encapsulates the entire setup process for a Windows target 23 | /// and will make sure the user gets a properly initialized object at the end. 24 | /// 25 | /// This function is a high level abstraction over the individual parts of initialization a Windows target: 26 | /// - Scanning for the ntoskrnl and retrieving the `Win32KernelInfo` struct. 27 | /// - Retrieving the Offsets for the target Windows version. 28 | /// - Creating a struct which implements `VirtualTranslate2` for virtual to physical address translations. 29 | /// - Optionally wrapping the Connector or the `VirtualTranslate2` object into a cached object. 30 | /// - Initialization of the Kernel structure itself. 31 | /// 32 | /// # Examples 33 | /// 34 | /// Using the builder with default values: 35 | /// ``` 36 | /// use memflow::mem::PhysicalMemory; 37 | /// use memflow_win32::win32::Win32Kernel; 38 | /// 39 | /// fn test(connector: T) { 40 | /// let _kernel = Win32Kernel::builder(connector) 41 | /// .build() 42 | /// .unwrap(); 43 | /// } 44 | /// ``` 45 | /// 46 | /// Using the builder with default cache configurations: 47 | /// ``` 48 | /// use memflow::mem::PhysicalMemory; 49 | /// use memflow_win32::win32::Win32Kernel; 50 | /// 51 | /// fn test(connector: T) { 52 | /// let _kernel = Win32Kernel::builder(connector) 53 | /// .build_default_caches() 54 | /// .build() 55 | /// .unwrap(); 56 | /// } 57 | /// ``` 58 | /// 59 | /// Customizing the caches: 60 | /// ``` 61 | /// use memflow::mem::{PhysicalMemory, CachedPhysicalMemory, CachedVirtualTranslate}; 62 | /// use memflow_win32::win32::Win32Kernel; 63 | /// 64 | /// fn test(connector: T) { 65 | /// let _kernel = Win32Kernel::builder(connector) 66 | /// .build_page_cache(|connector, arch| { 67 | /// CachedPhysicalMemory::builder(connector) 68 | /// .arch(arch) 69 | /// .build() 70 | /// .unwrap() 71 | /// }) 72 | /// .build_vat_cache(|vat, arch| { 73 | /// CachedVirtualTranslate::builder(vat) 74 | /// .arch(arch) 75 | /// .build() 76 | /// .unwrap() 77 | /// }) 78 | /// .build() 79 | /// .unwrap(); 80 | /// } 81 | /// ``` 82 | /// 83 | /// # Remarks 84 | /// 85 | /// Manual initialization of the above examples would look like the following: 86 | /// ``` 87 | /// use memflow::prelude::v1::*; 88 | /// use memflow_win32::prelude::{ 89 | /// Win32KernelInfo, 90 | /// Win32Offsets, 91 | /// Win32Kernel, 92 | /// offset_builder_with_kernel_info 93 | /// }; 94 | /// 95 | /// fn test(mut connector: T) { 96 | /// // Use the ntoskrnl scanner to find the relevant KernelInfo (start_block, arch, dtb, ntoskrnl, etc) 97 | /// let kernel_info = Win32KernelInfo::scanner(connector.forward_mut()).scan().unwrap(); 98 | /// // Download the corresponding pdb from the default symbol store 99 | /// let offsets = offset_builder_with_kernel_info(&kernel_info).build().unwrap(); 100 | /// 101 | /// // Create a struct for doing virtual to physical memory translations 102 | /// let vat = DirectTranslate::new(); 103 | /// 104 | /// // Create a Page Cache layer with default values 105 | /// let mut connector_cached = CachedPhysicalMemory::builder(connector) 106 | /// .arch(kernel_info.os_info.arch) 107 | /// .build() 108 | /// .unwrap(); 109 | /// 110 | /// // Create a Tlb Cache layer with default values 111 | /// let vat_cached = CachedVirtualTranslate::builder(vat) 112 | /// .arch(kernel_info.os_info.arch) 113 | /// .build() 114 | /// .unwrap(); 115 | /// 116 | /// // Initialize the final Kernel object 117 | /// let _kernel = Win32Kernel::new(connector_cached, vat_cached, offsets, kernel_info); 118 | /// } 119 | /// ``` 120 | pub struct Win32KernelBuilder { 121 | connector: T, 122 | 123 | arch: Option, 124 | kernel_hint: Option
, 125 | dtb: Option
, 126 | 127 | #[cfg(feature = "symstore")] 128 | symbol_store: Option, 129 | 130 | build_page_cache: Box TK>, 131 | build_vat_cache: Box VK>, 132 | } 133 | 134 | impl Win32KernelBuilder 135 | where 136 | T: PhysicalMemory, 137 | { 138 | pub fn new(connector: T) -> Win32KernelBuilder { 139 | Win32KernelBuilder { 140 | connector, 141 | 142 | arch: None, 143 | kernel_hint: None, 144 | dtb: None, 145 | 146 | #[cfg(feature = "symstore")] 147 | symbol_store: Some(SymbolStore::default()), 148 | 149 | build_page_cache: Box::new(|connector, _| connector), 150 | build_vat_cache: Box::new(|vat, _| vat), 151 | } 152 | } 153 | } 154 | 155 | impl<'a, T, TK, VK> Win32KernelBuilder 156 | where 157 | T: PhysicalMemory, 158 | TK: 'static + PhysicalMemory + Clone, 159 | VK: 'static + VirtualTranslate2 + Clone, 160 | { 161 | pub fn build(mut self) -> Result> { 162 | // find kernel_info 163 | let mut kernel_scanner = Win32KernelInfo::scanner(self.connector.forward_mut()); 164 | if let Some(arch) = self.arch { 165 | kernel_scanner = kernel_scanner.arch(arch); 166 | } 167 | if let Some(kernel_hint) = self.kernel_hint { 168 | kernel_scanner = kernel_scanner.kernel_hint(kernel_hint); 169 | } 170 | if let Some(dtb) = self.dtb { 171 | kernel_scanner = kernel_scanner.dtb(dtb); 172 | } 173 | let kernel_info = kernel_scanner.scan()?; 174 | 175 | // acquire offsets from the symbol store 176 | let offsets = self.build_offsets(&kernel_info)?; 177 | 178 | // TODO: parse memory maps 179 | 180 | // create a vat object 181 | let vat = DirectTranslate::new(); 182 | 183 | // create caches 184 | let kernel_connector = (self.build_page_cache)(self.connector, kernel_info.os_info.arch); 185 | let kernel_vat = (self.build_vat_cache)(vat, kernel_info.os_info.arch); 186 | 187 | // create the final kernel object 188 | Ok(Win32Kernel::new( 189 | kernel_connector, 190 | kernel_vat, 191 | offsets, 192 | kernel_info, 193 | )) 194 | } 195 | 196 | #[cfg(feature = "symstore")] 197 | fn build_offsets(&self, kernel_info: &Win32KernelInfo) -> Result { 198 | let mut builder = offset_builder_with_kernel_info(kernel_info); 199 | if let Some(store) = &self.symbol_store { 200 | builder = builder.symbol_store(store.clone()); 201 | } else { 202 | builder = builder.no_symbol_store(); 203 | } 204 | builder.build() 205 | } 206 | 207 | #[cfg(not(feature = "symstore"))] 208 | fn build_offsets(&self, kernel_info: &Win32KernelInfo) -> Result { 209 | offset_builder_with_kernel_info(&kernel_info).build() 210 | } 211 | 212 | pub fn arch(mut self, arch: ArchitectureIdent) -> Self { 213 | self.arch = Some(arch); 214 | self 215 | } 216 | 217 | pub fn kernel_hint(mut self, kernel_hint: Address) -> Self { 218 | self.kernel_hint = Some(kernel_hint); 219 | self 220 | } 221 | 222 | pub fn dtb(mut self, dtb: Address) -> Self { 223 | self.dtb = Some(dtb); 224 | self 225 | } 226 | 227 | /// Configures the symbol store to be used when constructing the Kernel. 228 | /// This will override the default symbol store that is being used if no other setting is configured. 229 | /// 230 | /// # Examples 231 | /// 232 | /// ``` 233 | /// use memflow::mem::PhysicalMemory; 234 | /// use memflow_win32::prelude::{Win32Kernel, SymbolStore}; 235 | /// 236 | /// fn test(connector: T) { 237 | /// let _kernel = Win32Kernel::builder(connector) 238 | /// .symbol_store(SymbolStore::new().no_cache()) 239 | /// .build() 240 | /// .unwrap(); 241 | /// } 242 | /// ``` 243 | #[cfg(feature = "symstore")] 244 | pub fn symbol_store(mut self, symbol_store: SymbolStore) -> Self { 245 | self.symbol_store = Some(symbol_store); 246 | self 247 | } 248 | 249 | /// Disables the symbol store when constructing the Kernel. 250 | /// By default a default symbol store will be used when constructing a kernel. 251 | /// This option allows the user to disable the symbol store alltogether 252 | /// and fall back to the built-in offsets table. 253 | /// 254 | /// # Examples 255 | /// 256 | /// ``` 257 | /// use memflow::mem::PhysicalMemory; 258 | /// use memflow_win32::win32::Win32Kernel; 259 | /// use memflow_win32::offsets::SymbolStore; 260 | /// 261 | /// fn test(connector: T) { 262 | /// let _kernel = Win32Kernel::builder(connector) 263 | /// .no_symbol_store() 264 | /// .build() 265 | /// .unwrap(); 266 | /// } 267 | /// ``` 268 | #[cfg(feature = "symstore")] 269 | pub fn no_symbol_store(mut self) -> Self { 270 | self.symbol_store = None; 271 | self 272 | } 273 | 274 | /// Creates the Kernel structure with default caching enabled. 275 | /// 276 | /// If this option is specified, the Kernel structure is generated 277 | /// with a (page level cache)[../index.html] with default settings. 278 | /// On top of the page level cache a [vat cache](../index.html) will be setupped. 279 | /// 280 | /// # Examples 281 | /// 282 | /// ``` 283 | /// use memflow::mem::PhysicalMemory; 284 | /// use memflow_win32::win32::Win32Kernel; 285 | /// 286 | /// fn test(connector: T) { 287 | /// let _kernel = Win32Kernel::builder(connector) 288 | /// .build_default_caches() 289 | /// .build() 290 | /// .unwrap(); 291 | /// } 292 | /// ``` 293 | pub fn build_default_caches( 294 | self, 295 | ) -> Win32KernelBuilder< 296 | T, 297 | CachedPhysicalMemory<'a, T, DefaultCacheValidator>, 298 | CachedVirtualTranslate, 299 | > { 300 | Win32KernelBuilder { 301 | connector: self.connector, 302 | 303 | arch: self.arch, 304 | kernel_hint: self.kernel_hint, 305 | dtb: self.dtb, 306 | 307 | #[cfg(feature = "symstore")] 308 | symbol_store: self.symbol_store, 309 | 310 | build_page_cache: Box::new(|connector, arch| { 311 | CachedPhysicalMemory::builder(connector) 312 | .arch(arch) 313 | .build() 314 | .unwrap() 315 | }), 316 | build_vat_cache: Box::new(|vat, arch| { 317 | CachedVirtualTranslate::builder(vat) 318 | .arch(arch) 319 | .build() 320 | .unwrap() 321 | }), 322 | } 323 | } 324 | 325 | /// Creates a Kernel structure by constructing the page cache from the given closure. 326 | /// 327 | /// This function accepts a `FnOnce` closure that is being evaluated 328 | /// after the ntoskrnl has been found. 329 | /// 330 | /// # Examples 331 | /// 332 | /// ``` 333 | /// use memflow::mem::{PhysicalMemory, CachedPhysicalMemory}; 334 | /// use memflow_win32::win32::Win32Kernel; 335 | /// 336 | /// fn test(connector: T) { 337 | /// let _kernel = Win32Kernel::builder(connector) 338 | /// .build_page_cache(|connector, arch| { 339 | /// CachedPhysicalMemory::builder(connector) 340 | /// .arch(arch) 341 | /// .build() 342 | /// .unwrap() 343 | /// }) 344 | /// .build() 345 | /// .unwrap(); 346 | /// } 347 | /// ``` 348 | pub fn build_page_cache TKN + 'static>( 349 | self, 350 | func: F, 351 | ) -> Win32KernelBuilder 352 | where 353 | TKN: PhysicalMemory, 354 | { 355 | Win32KernelBuilder { 356 | connector: self.connector, 357 | 358 | arch: self.arch, 359 | kernel_hint: self.kernel_hint, 360 | dtb: self.dtb, 361 | 362 | #[cfg(feature = "symstore")] 363 | symbol_store: self.symbol_store, 364 | 365 | build_page_cache: Box::new(func), 366 | build_vat_cache: self.build_vat_cache, 367 | } 368 | } 369 | 370 | /// Creates a Kernel structure by constructing the vat cache from the given closure. 371 | /// 372 | /// This function accepts a `FnOnce` closure that is being evaluated 373 | /// after the ntoskrnl has been found. 374 | /// 375 | /// # Examples 376 | /// 377 | /// ``` 378 | /// use memflow::mem::{PhysicalMemory, CachedVirtualTranslate}; 379 | /// use memflow_win32::win32::Win32Kernel; 380 | /// 381 | /// fn test(connector: T) { 382 | /// let _kernel = Win32Kernel::builder(connector) 383 | /// .build_vat_cache(|vat, arch| { 384 | /// CachedVirtualTranslate::builder(vat) 385 | /// .arch(arch) 386 | /// .build() 387 | /// .unwrap() 388 | /// }) 389 | /// .build() 390 | /// .unwrap(); 391 | /// } 392 | /// ``` 393 | pub fn build_vat_cache VKN + 'static>( 394 | self, 395 | func: F, 396 | ) -> Win32KernelBuilder 397 | where 398 | VKN: VirtualTranslate2, 399 | { 400 | Win32KernelBuilder { 401 | connector: self.connector, 402 | 403 | arch: self.arch, 404 | kernel_hint: self.kernel_hint, 405 | dtb: self.dtb, 406 | 407 | #[cfg(feature = "symstore")] 408 | symbol_store: self.symbol_store, 409 | 410 | build_page_cache: self.build_page_cache, 411 | build_vat_cache: Box::new(func), 412 | } 413 | } 414 | 415 | // TODO: more builder configurations 416 | // kernel_info_builder() 417 | // offset_builder() 418 | } 419 | -------------------------------------------------------------------------------- /memflow-win32/src/win32/kernel_info.rs: -------------------------------------------------------------------------------- 1 | use crate::kernel::{self, StartBlock}; 2 | use crate::kernel::{Win32Guid, Win32Version}; 3 | 4 | use log::{info, warn}; 5 | 6 | use memflow::architecture::ArchitectureIdent; 7 | use memflow::cglue::forward::ForwardMut; 8 | use memflow::error::Result; 9 | use memflow::mem::{DirectTranslate, PhysicalMemory, VirtualDma}; 10 | use memflow::os::OsInfo; 11 | use memflow::types::Address; 12 | 13 | use super::Win32VirtualTranslate; 14 | 15 | use crate::offsets::Win32OffsetBuilder; 16 | 17 | #[derive(Debug, Clone)] 18 | #[cfg_attr(feature = "serde", derive(::serde::Serialize))] 19 | pub struct Win32KernelInfo { 20 | pub os_info: OsInfo, 21 | pub dtb: Address, 22 | 23 | pub kernel_guid: Option, 24 | pub kernel_winver: Win32Version, 25 | 26 | pub eprocess_base: Address, 27 | } 28 | 29 | impl Win32KernelInfo { 30 | pub fn scanner(mem: T) -> KernelInfoScanner { 31 | KernelInfoScanner::new(mem) 32 | } 33 | 34 | pub fn into_offset_builder<'a>( 35 | &self, 36 | mut offsets: Win32OffsetBuilder<'a>, 37 | ) -> Win32OffsetBuilder<'a> { 38 | if offsets.get_guid().is_none() && self.kernel_guid.is_some() { 39 | offsets = offsets.guid(self.kernel_guid.clone().unwrap()); 40 | } 41 | 42 | if offsets.get_winver().is_none() { 43 | offsets = offsets.winver(self.kernel_winver); 44 | } 45 | 46 | if offsets.get_arch().is_none() { 47 | offsets = offsets.arch(self.os_info.arch.into()); 48 | } 49 | 50 | offsets 51 | } 52 | } 53 | 54 | pub struct KernelInfoScanner { 55 | mem: T, 56 | arch: Option, 57 | kernel_hint: Option
, 58 | dtb: Option
, 59 | } 60 | 61 | impl KernelInfoScanner { 62 | pub fn new(mem: T) -> Self { 63 | Self { 64 | mem, 65 | arch: None, 66 | kernel_hint: None, 67 | dtb: None, 68 | } 69 | } 70 | 71 | pub fn scan(mut self) -> Result { 72 | let start_block = if let (Some(arch), Some(dtb), Some(kernel_hint)) = 73 | (self.arch, self.dtb, self.kernel_hint) 74 | { 75 | // construct start block from user supplied hints 76 | StartBlock { 77 | arch, 78 | kernel_hint, 79 | dtb, 80 | } 81 | } else { 82 | let mut sb = kernel::start_block::find(&mut self.mem, self.arch)?; 83 | if self.kernel_hint.is_some() && sb.kernel_hint.is_null() { 84 | sb.kernel_hint = self.kernel_hint.unwrap() 85 | } 86 | // dtb is always set in start_block::find() 87 | sb 88 | }; 89 | 90 | self.scan_block(start_block).or_else(|_| { 91 | let start_block = kernel::start_block::find_fallback(&mut self.mem, start_block.arch)?; 92 | self.scan_block(start_block) 93 | }) 94 | } 95 | 96 | fn scan_block(&mut self, start_block: StartBlock) -> Result { 97 | info!( 98 | "arch={:?} kernel_hint={:x} dtb={:x}", 99 | start_block.arch, start_block.kernel_hint, start_block.dtb 100 | ); 101 | 102 | // construct virtual memory object for start_block 103 | let mut virt_mem = VirtualDma::with_vat( 104 | self.mem.forward_mut(), 105 | start_block.arch, 106 | Win32VirtualTranslate::new(start_block.arch, start_block.dtb), 107 | DirectTranslate::new(), 108 | ); 109 | 110 | // find ntoskrnl.exe base 111 | let (base, size) = kernel::ntos::find(&mut virt_mem, &start_block)?; 112 | info!("base={} size={}", base, size); 113 | 114 | // get ntoskrnl.exe guid 115 | let kernel_guid = kernel::ntos::find_guid(&mut virt_mem, base).ok(); 116 | info!("kernel_guid={:?}", kernel_guid); 117 | 118 | let kernel_winver = kernel::ntos::find_winver(&mut virt_mem, base).ok(); 119 | 120 | if kernel_winver.is_none() { 121 | warn!("Failed to retrieve kernel version! Some features may be disabled."); 122 | } 123 | 124 | let kernel_winver = kernel_winver.unwrap_or_else(|| Win32Version::new(3, 10, 511)); 125 | 126 | info!("kernel_winver={:?}", kernel_winver); 127 | 128 | // find eprocess base 129 | let eprocess_base = kernel::sysproc::find(&mut virt_mem, &start_block, base)?; 130 | info!("eprocess_base={:x}", eprocess_base); 131 | 132 | // start_block only contains the winload's dtb which might 133 | // be different to the one used in the actual kernel. 134 | // see Kernel::new() for more information. 135 | info!("start_block.dtb={:x}", start_block.dtb); 136 | 137 | let StartBlock { 138 | arch, 139 | kernel_hint: _, 140 | dtb, 141 | } = start_block; 142 | 143 | Ok(Win32KernelInfo { 144 | os_info: OsInfo { base, size, arch }, 145 | dtb, 146 | 147 | kernel_guid, 148 | kernel_winver, 149 | 150 | eprocess_base, 151 | }) 152 | } 153 | 154 | pub fn arch(mut self, arch: ArchitectureIdent) -> Self { 155 | self.arch = Some(arch); 156 | self 157 | } 158 | 159 | pub fn kernel_hint(mut self, kernel_hint: Address) -> Self { 160 | self.kernel_hint = Some(kernel_hint); 161 | self 162 | } 163 | 164 | pub fn dtb(mut self, dtb: Address) -> Self { 165 | self.dtb = Some(dtb); 166 | self 167 | } 168 | } 169 | -------------------------------------------------------------------------------- /memflow-win32/src/win32/module.rs: -------------------------------------------------------------------------------- 1 | use std::prelude::v1::*; 2 | 3 | use crate::offsets::Win32ArchOffsets; 4 | use crate::win32::VirtualReadUnicodeString; 5 | 6 | use log::trace; 7 | 8 | use memflow::architecture::ArchitectureIdent; 9 | use memflow::error::Result; 10 | use memflow::mem::MemoryView; 11 | use memflow::os::{AddressCallback, ModuleInfo}; 12 | use memflow::types::Address; 13 | 14 | const MAX_ITER_COUNT: usize = 65536; 15 | 16 | #[derive(Debug, Clone, Copy)] 17 | #[repr(C)] 18 | #[cfg_attr(feature = "serde", derive(::serde::Serialize))] 19 | pub struct Win32ModuleListInfo { 20 | module_base: Address, 21 | offsets: Win32ArchOffsets, 22 | } 23 | 24 | impl Win32ModuleListInfo { 25 | pub fn with_peb( 26 | mem: &mut impl MemoryView, 27 | env_block: Address, 28 | arch: ArchitectureIdent, 29 | ) -> Result { 30 | let offsets = Win32ArchOffsets::from(arch); 31 | let arch_obj = arch.into(); 32 | 33 | trace!("peb_ldr_offs={:x}", offsets.peb_ldr); 34 | trace!("ldr_list_offs={:x}", offsets.ldr_list); 35 | 36 | let env_block_ldr = mem.read_addr_arch(arch_obj, env_block + offsets.peb_ldr)?; 37 | trace!("peb_ldr={:x}", env_block_ldr); 38 | 39 | let module_base = mem.read_addr_arch(arch_obj, env_block_ldr + offsets.ldr_list)?; 40 | 41 | Self::with_base(module_base, arch) 42 | } 43 | 44 | pub fn with_base(module_base: Address, arch: ArchitectureIdent) -> Result { 45 | trace!("module_base={:x}", module_base); 46 | 47 | let offsets = Win32ArchOffsets::from(arch); 48 | trace!("offsets={:?}", offsets); 49 | 50 | Ok(Win32ModuleListInfo { 51 | module_base, 52 | offsets, 53 | }) 54 | } 55 | 56 | pub fn module_base(&self) -> Address { 57 | self.module_base 58 | } 59 | 60 | pub fn module_entry_list( 61 | &self, 62 | mem: &mut impl AsMut, 63 | arch: ArchitectureIdent, 64 | ) -> Result> { 65 | let mut out = vec![]; 66 | self.module_entry_list_callback(mem, arch, (&mut out).into())?; 67 | Ok(out) 68 | } 69 | 70 | pub fn module_entry_list_callback, V: MemoryView>( 71 | &self, 72 | mem: &mut M, 73 | arch: ArchitectureIdent, 74 | mut callback: AddressCallback, 75 | ) -> Result<()> { 76 | let list_start = self.module_base; 77 | let mut list_entry = list_start; 78 | let arch_obj = arch.into(); 79 | for _ in 0..MAX_ITER_COUNT { 80 | if !callback.call(list_entry) { 81 | break; 82 | } 83 | list_entry = mem.as_mut().read_addr_arch(arch_obj, list_entry)?; 84 | // Break on misaligned entry. On NT 4.0 list end is misaligned, maybe it's a flag? 85 | if list_entry.is_null() 86 | || (list_entry.to_umem() & 0b111) != 0 87 | || list_entry == self.module_base 88 | { 89 | break; 90 | } 91 | } 92 | 93 | Ok(()) 94 | } 95 | 96 | pub fn module_base_from_entry( 97 | &self, 98 | entry: Address, 99 | mem: &mut impl MemoryView, 100 | arch: ArchitectureIdent, 101 | ) -> Result
{ 102 | mem.read_addr_arch(arch.into(), entry + self.offsets.ldr_data_base) 103 | .map_err(From::from) 104 | } 105 | 106 | pub fn module_info_from_entry( 107 | &self, 108 | entry: Address, 109 | parent_eprocess: Address, 110 | mem: &mut impl MemoryView, 111 | arch: ArchitectureIdent, 112 | ) -> Result { 113 | let base = self.module_base_from_entry(entry, mem, arch)?; 114 | let arch_obj = arch.into(); 115 | 116 | trace!("base={:x}", base); 117 | 118 | let mut size = mem 119 | .read_addr_arch(arch_obj, entry + self.offsets.ldr_data_size)? 120 | .to_umem(); 121 | 122 | trace!("size={:x}", size); 123 | 124 | // If size here is messed up, try to parse it from the module pe file 125 | if size < 0x1000 { 126 | if let Ok(new_size) = crate::kernel::ntos::pehelper::try_get_pe_size(mem, base) { 127 | size = new_size; 128 | trace!("pe size={:x}", size); 129 | } 130 | } 131 | 132 | let path = mem 133 | .read_unicode_string(arch_obj, entry + self.offsets.ldr_data_full_name) 134 | .unwrap_or_else(|_| String::new()); 135 | trace!("path={}", path); 136 | 137 | let name = mem 138 | .read_unicode_string(arch_obj, entry + self.offsets.ldr_data_base_name) 139 | .unwrap_or_else(|_| String::new()); 140 | trace!("name={}", name); 141 | 142 | Ok(ModuleInfo { 143 | address: entry, 144 | parent_process: parent_eprocess, 145 | base, 146 | size, 147 | path: path.into(), 148 | name: name.into(), 149 | arch, 150 | }) 151 | } 152 | } 153 | -------------------------------------------------------------------------------- /memflow-win32/src/win32/process.rs: -------------------------------------------------------------------------------- 1 | use std::prelude::v1::*; 2 | 3 | use super::{Win32Kernel, Win32ModuleListInfo}; 4 | 5 | use crate::prelude::MmVadOffsetTable; 6 | 7 | use std::fmt; 8 | 9 | use memflow::mem::virt_translate::*; 10 | use memflow::prelude::v1::{Result, *}; 11 | 12 | // those only required when compiling cglue code 13 | #[cfg(feature = "plugins")] 14 | use memflow::cglue; 15 | 16 | use super::Win32VirtualTranslate; 17 | 18 | /// Exit status of a win32 process 19 | pub type Win32ExitStatus = i32; 20 | 21 | /// Process has not exited yet 22 | pub const EXIT_STATUS_STILL_ACTIVE: i32 = 259; 23 | 24 | /// EPROCESS ImageFileName byte length 25 | pub const IMAGE_FILE_NAME_LENGTH: usize = 15; 26 | 27 | #[derive(Debug, Clone)] 28 | #[cfg_attr(feature = "serde", derive(::serde::Serialize))] 29 | pub struct Win32ProcessInfo { 30 | pub base_info: ProcessInfo, 31 | 32 | // general information from eprocess 33 | pub section_base: Address, 34 | pub ethread: Address, 35 | pub wow64: Address, 36 | 37 | // teb 38 | pub teb: Option
, 39 | pub teb_wow64: Option
, 40 | 41 | // peb 42 | pub peb_native: Option
, 43 | pub peb_wow64: Option
, 44 | 45 | // modules 46 | pub module_info_native: Option, 47 | pub module_info_wow64: Option, 48 | 49 | // memory 50 | pub vad_root: Address, 51 | } 52 | 53 | impl Win32ProcessInfo { 54 | pub fn wow64(&self) -> Address { 55 | self.wow64 56 | } 57 | 58 | pub fn peb(&self) -> Option
{ 59 | if let Some(peb) = self.peb_wow64 { 60 | Some(peb) 61 | } else { 62 | self.peb_native 63 | } 64 | } 65 | 66 | pub fn peb_native(&self) -> Option
{ 67 | self.peb_native 68 | } 69 | 70 | pub fn peb_wow64(&self) -> Option
{ 71 | self.peb_wow64 72 | } 73 | 74 | /// Return the module list information of process native architecture 75 | /// 76 | /// If the process is a wow64 process, module_info_wow64 is returned, otherwise, module_info_native is 77 | /// returned. 78 | pub fn module_info(&self) -> Option { 79 | if !self.wow64.is_null() { 80 | self.module_info_wow64 81 | } else { 82 | self.module_info_native 83 | } 84 | } 85 | 86 | pub fn module_info_native(&self) -> Option { 87 | self.module_info_native 88 | } 89 | 90 | pub fn module_info_wow64(&self) -> Option { 91 | self.module_info_wow64 92 | } 93 | 94 | pub fn translator(&self) -> Win32VirtualTranslate { 95 | Win32VirtualTranslate::new(self.base_info.sys_arch, self.base_info.dtb1) 96 | } 97 | } 98 | 99 | #[cfg(feature = "plugins")] 100 | cglue_impl_group!(Win32Process, ProcessInstance, { VirtualTranslate }); 101 | #[cfg(feature = "plugins")] 102 | cglue_impl_group!(Win32Process, IntoProcessInstance, { VirtualTranslate }); 103 | 104 | pub struct Win32Process { 105 | pub virt_mem: VirtualDma, 106 | pub proc_info: Win32ProcessInfo, 107 | 108 | sysproc_dtb: D, 109 | offset_eproc_exit_status: usize, 110 | mmvad: MmVadOffsetTable, 111 | } 112 | 113 | // TODO: can be removed i think 114 | impl Clone for Win32Process { 115 | fn clone(&self) -> Self { 116 | Self { 117 | virt_mem: self.virt_mem.clone(), 118 | proc_info: self.proc_info.clone(), 119 | sysproc_dtb: self.sysproc_dtb.clone(), 120 | offset_eproc_exit_status: self.offset_eproc_exit_status, 121 | mmvad: self.mmvad, 122 | } 123 | } 124 | } 125 | 126 | impl AsMut> for Win32Process { 127 | fn as_mut(&mut self) -> &mut VirtualDma { 128 | &mut self.virt_mem 129 | } 130 | } 131 | 132 | impl MemoryView 133 | for Win32Process 134 | { 135 | fn read_raw_iter(&mut self, data: ReadRawMemOps) -> Result<()> { 136 | self.virt_mem.read_raw_iter(data) 137 | } 138 | 139 | fn write_raw_iter(&mut self, data: WriteRawMemOps) -> Result<()> { 140 | self.virt_mem.write_raw_iter(data) 141 | } 142 | 143 | fn metadata(&self) -> MemoryViewMetadata { 144 | self.virt_mem.metadata() 145 | } 146 | } 147 | 148 | impl VirtualTranslate 149 | for Win32Process 150 | { 151 | fn virt_to_phys_list( 152 | &mut self, 153 | addrs: &[VtopRange], 154 | out: VirtualTranslationCallback, 155 | out_fail: VirtualTranslationFailCallback, 156 | ) { 157 | self.virt_mem.virt_to_phys_list(addrs, out, out_fail) 158 | } 159 | } 160 | 161 | // TODO: implement VAD and rollback to the old bound! 162 | //impl Process for Win32Process { 163 | 164 | impl Process 165 | for Win32Process 166 | { 167 | /// Retrieves virtual address translator for the process (if applicable) 168 | //fn vat(&mut self) -> Option<&mut Self::VirtualTranslateType>; 169 | 170 | /// Retrieves the state of the process 171 | fn state(&mut self) -> ProcessState { 172 | if let Ok(exit_status) = self.virt_mem.read::( 173 | self.proc_info.base_info.address + self.offset_eproc_exit_status, 174 | ) { 175 | if exit_status == EXIT_STATUS_STILL_ACTIVE { 176 | ProcessState::Alive 177 | } else { 178 | ProcessState::Dead(exit_status) 179 | } 180 | } else { 181 | ProcessState::Unknown 182 | } 183 | } 184 | 185 | /// Changes the dtb this process uses for memory translations 186 | /// 187 | /// # Remarks 188 | /// 189 | /// For memflow-win32 the second parameter should be set to `Address::invalid()`. 190 | fn set_dtb(&mut self, dtb1: Address, _dtb2: Address) -> Result<()> { 191 | self.proc_info.base_info.dtb1 = dtb1; 192 | self.proc_info.base_info.dtb2 = Address::invalid(); 193 | self.virt_mem.set_translator(self.proc_info.translator()); 194 | Ok(()) 195 | } 196 | 197 | /// Walks the process' module list and calls the provided callback for each module 198 | fn module_address_list_callback( 199 | &mut self, 200 | target_arch: Option<&ArchitectureIdent>, 201 | mut callback: ModuleAddressCallback, 202 | ) -> memflow::error::Result<()> { 203 | let infos = [ 204 | ( 205 | self.proc_info.module_info_native, 206 | self.proc_info.base_info.sys_arch, 207 | ), 208 | ( 209 | self.proc_info.module_info_wow64, 210 | self.proc_info.base_info.proc_arch, 211 | ), 212 | ]; 213 | 214 | // Here we end up filtering out module_info_wow64 if it doesn't exist 215 | let iter = infos 216 | .iter() 217 | .filter(|(_, a)| { 218 | if let Some(ta) = target_arch { 219 | a == ta 220 | } else { 221 | true 222 | } 223 | }) 224 | .cloned() 225 | .filter_map(|(info, arch)| info.zip(Some(arch))); 226 | 227 | self.module_address_list_with_infos_callback(iter, &mut callback) 228 | .map_err(From::from) 229 | } 230 | 231 | /// Retrieves a module by its structure address and architecture 232 | /// 233 | /// # Arguments 234 | /// * `address` - address where module's information resides in 235 | /// * `architecture` - architecture of the module. Should be either `ProcessInfo::proc_arch`, or `ProcessInfo::sys_arch`. 236 | fn module_by_address( 237 | &mut self, 238 | address: Address, 239 | architecture: ArchitectureIdent, 240 | ) -> memflow::error::Result { 241 | let info = if architecture == self.proc_info.base_info.sys_arch { 242 | self.proc_info.module_info_native.as_mut() 243 | } else if architecture == self.proc_info.base_info.proc_arch { 244 | self.proc_info.module_info_wow64.as_mut() 245 | } else { 246 | None 247 | } 248 | .ok_or(Error(ErrorOrigin::OsLayer, ErrorKind::InvalidArchitecture))?; 249 | 250 | info.module_info_from_entry( 251 | address, 252 | self.proc_info.base_info.address, 253 | &mut self.virt_mem, 254 | architecture, 255 | ) 256 | .map_err(From::from) 257 | } 258 | 259 | /// Retrieves address of the primary module structure of the process 260 | /// 261 | /// This will be the module of the executable that is being run, and whose name is stored in 262 | /// _EPROCESS::IMAGE_FILE_NAME 263 | fn primary_module_address(&mut self) -> memflow::error::Result
{ 264 | let mut ret = Err(Error(ErrorOrigin::OsLayer, ErrorKind::ModuleNotFound)); 265 | let sptr = self as *mut Self; 266 | let callback = &mut |ModuleAddressInfo { address, arch }| { 267 | let s = unsafe { sptr.as_mut() }.unwrap(); 268 | let info = if arch == s.proc_info.base_info.sys_arch { 269 | s.proc_info.module_info_native.as_mut() 270 | } else { 271 | s.proc_info.module_info_wow64.as_mut() 272 | } 273 | .unwrap(); 274 | 275 | if let Ok((_, true)) = info 276 | .module_base_from_entry(address, &mut s.virt_mem, arch) 277 | .map(|b| (b, b == s.proc_info.section_base)) 278 | { 279 | ret = Ok(address); 280 | false 281 | } else { 282 | true 283 | } 284 | }; 285 | let proc_arch = self.proc_info.base_info.proc_arch; 286 | self.module_address_list_callback(Some(&proc_arch), callback.into())?; 287 | ret 288 | } 289 | 290 | fn module_import_list_callback( 291 | &mut self, 292 | info: &ModuleInfo, 293 | callback: ImportCallback, 294 | ) -> Result<()> { 295 | memflow::os::util::module_import_list_callback(&mut self.virt_mem, info, callback) 296 | } 297 | 298 | fn module_export_list_callback( 299 | &mut self, 300 | info: &ModuleInfo, 301 | callback: ExportCallback, 302 | ) -> Result<()> { 303 | memflow::os::util::module_export_list_callback(&mut self.virt_mem, info, callback) 304 | } 305 | 306 | fn module_section_list_callback( 307 | &mut self, 308 | info: &ModuleInfo, 309 | callback: SectionCallback, 310 | ) -> Result<()> { 311 | memflow::os::util::module_section_list_callback(&mut self.virt_mem, info, callback) 312 | } 313 | 314 | /// Retrieves the process info 315 | fn info(&self) -> &ProcessInfo { 316 | &self.proc_info.base_info 317 | } 318 | 319 | fn mapped_mem_range( 320 | &mut self, 321 | gap_size: imem, 322 | start: Address, 323 | end: Address, 324 | out: MemoryRangeCallback, 325 | ) { 326 | fn _walk_vad( 327 | mem: &mut impl MemoryView, 328 | vad_entry: Address, 329 | offsets: &MmVadOffsetTable, 330 | arch: ArchitectureObj, 331 | start: Address, 332 | end: Address, 333 | out: &mut MemoryRangeCallback, 334 | ) { 335 | if vad_entry.is_null() || start == end { 336 | return; 337 | } 338 | 339 | log::trace!("WALK VAD {vad_entry} {start} {end}"); 340 | 341 | let _ = (move || { 342 | // Older versions of windows store starting/ending VPNs as address ranges without 343 | // the high parts, as opposed to frame numbers 344 | let pfn_mul = if offsets.starting_vpn_high == offsets.ending_vpn_high { 345 | 1 346 | } else { 347 | 0x1000 348 | }; 349 | 350 | // TODO: handle starting/ending vpn high values 351 | 352 | let s = mem.read::(vad_entry + offsets.starting_vpn)? as umem; 353 | let s = Address::from(s * pfn_mul); 354 | let e = mem.read::(vad_entry + offsets.ending_vpn)? as umem; 355 | let e = Address::from(e * pfn_mul); 356 | 357 | let sl = mem.read::(vad_entry + offsets.starting_vpn_high)? as umem; 358 | let el = mem.read::(vad_entry + offsets.ending_vpn_high)? as umem; 359 | 360 | let fl = mem.read::(vad_entry + offsets.u)?; 361 | 362 | // Bits are as follows: 363 | // RXW (maybe) 364 | println!("FL {fl:b} | {}", offsets.protection_bit); 365 | 366 | let _r = fl & (0b1 << offsets.protection_bit); 367 | 368 | let fl = fl >> offsets.protection_bit; 369 | 370 | println!("FL {fl:b}"); 371 | 372 | let fl = fl & !(!0u32 << 5); 373 | 374 | println!("S {s} E {e} | {sl:x} {el:x} | {fl:b} {fl}"); 375 | 376 | if (s >= start && s < end) || (e <= end && e > start) { 377 | let left = mem.read_addr_arch(arch, vad_entry + offsets.vad_node)?; 378 | let right = 379 | mem.read_addr_arch(arch, vad_entry + offsets.vad_node + arch.size_addr())?; 380 | 381 | _walk_vad(mem, left, offsets, arch, start, s, out); 382 | 383 | if !out.call(CTup3( 384 | s, 385 | e.to_umem() - s.to_umem() + pfn_mul, 386 | Default::default(), 387 | )) { 388 | return Result::Ok(()); 389 | } 390 | 391 | _walk_vad(mem, right, offsets, arch, e, end, out); 392 | } 393 | 394 | Result::Ok(()) 395 | })(); 396 | } 397 | 398 | /*let mut gap_remover = memflow::types::util::GapRemover::new(out, gap_size, start, end); 399 | 400 | // Temporarily load up the sysproc dtb into the memory view 401 | self.sysproc_dtb = self.virt_mem.set_translator(self.sysproc_dtb); 402 | 403 | let out = &mut |data| { 404 | gap_remover.push_range(data); 405 | true 406 | }; 407 | 408 | let mut out = out.into(); 409 | 410 | _walk_vad( 411 | &mut self.virt_mem, 412 | self.proc_info.vad_root, 413 | &self.mmvad, 414 | self.proc_info.base_info.sys_arch.into(), 415 | start, 416 | end, 417 | &mut out, 418 | ); 419 | 420 | // Load back the original value 421 | self.sysproc_dtb = self.virt_mem.set_translator(self.sysproc_dtb);*/ 422 | 423 | self.virt_mem.virt_page_map_range(gap_size, start, end, out) 424 | } 425 | } 426 | 427 | // TODO: replace the following impls with a dedicated builder 428 | // TODO: add non cloneable thing 429 | impl Win32Process { 430 | pub fn with_kernel(kernel: Win32Kernel, proc_info: Win32ProcessInfo) -> Self { 431 | let mut virt_mem = kernel.virt_mem; 432 | virt_mem.set_proc_arch(proc_info.base_info.proc_arch.into()); 433 | let sysproc_dtb = virt_mem.set_translator(proc_info.translator()); 434 | 435 | Self { 436 | virt_mem, 437 | proc_info, 438 | sysproc_dtb, 439 | mmvad: kernel.offsets.mm_vad(), 440 | offset_eproc_exit_status: kernel.offsets.eproc_exit_status(), 441 | } 442 | } 443 | 444 | /// Consumes this process, returning the underlying memory and vat objects 445 | pub fn into_inner(self) -> (T, V) { 446 | self.virt_mem.into_inner() 447 | } 448 | } 449 | 450 | impl<'a, T: PhysicalMemory, V: VirtualTranslate2> 451 | Win32Process, Fwd<&'a mut V>, Win32VirtualTranslate> 452 | { 453 | /// Constructs a new process by borrowing a kernel object. 454 | /// 455 | /// Internally this will create a `VirtualDma` object that also 456 | /// borrows the PhysicalMemory and Vat objects from the kernel. 457 | /// 458 | /// The resulting process object is NOT cloneable due to the mutable borrowing. 459 | /// 460 | /// When u need a cloneable Process u have to use the `::with_kernel` function 461 | /// which will move the kernel object. 462 | pub fn with_kernel_ref(kernel: &'a mut Win32Kernel, proc_info: Win32ProcessInfo) -> Self { 463 | let sysproc_dtb = *kernel.virt_mem.translator(); 464 | 465 | let (phys_mem, vat) = kernel.virt_mem.mem_vat_pair(); 466 | let virt_mem = VirtualDma::with_vat( 467 | phys_mem.forward_mut(), 468 | proc_info.base_info.proc_arch, 469 | proc_info.translator(), 470 | vat.forward_mut(), 471 | ); 472 | 473 | Self { 474 | virt_mem, 475 | proc_info, 476 | sysproc_dtb, 477 | mmvad: kernel.offsets.mm_vad(), 478 | offset_eproc_exit_status: kernel.offsets.eproc_exit_status(), 479 | } 480 | } 481 | } 482 | 483 | impl Win32Process { 484 | fn module_address_list_with_infos_callback( 485 | &mut self, 486 | module_infos: impl Iterator, 487 | out: &mut ModuleAddressCallback, 488 | ) -> Result<()> { 489 | for (info, arch) in module_infos { 490 | let callback = &mut |address| out.call(ModuleAddressInfo { address, arch }); 491 | info.module_entry_list_callback(self, arch, callback.into())?; 492 | } 493 | Ok(()) 494 | } 495 | } 496 | 497 | impl fmt::Debug for Win32Process { 498 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 499 | write!(f, "{:?}", self.proc_info) 500 | } 501 | } 502 | -------------------------------------------------------------------------------- /memflow-win32/src/win32/unicode_string.rs: -------------------------------------------------------------------------------- 1 | use std::prelude::v1::*; 2 | 3 | use std::convert::TryInto; 4 | 5 | use memflow::architecture::{ArchitectureObj, Endianess}; 6 | use memflow::error::{Error, ErrorKind, ErrorOrigin, Result}; 7 | use memflow::mem::MemoryView; 8 | use memflow::types::Address; 9 | 10 | use widestring::U16CString; 11 | 12 | pub trait VirtualReadUnicodeString { 13 | fn read_unicode_string(&mut self, proc_arch: ArchitectureObj, addr: Address) -> Result; 14 | } 15 | 16 | // TODO: split up cpu and proc arch in read_helper.rs 17 | impl VirtualReadUnicodeString for T { 18 | fn read_unicode_string(&mut self, proc_arch: ArchitectureObj, addr: Address) -> Result { 19 | /* 20 | typedef struct _windows_unicode_string32 { 21 | uint16_t length; 22 | uint16_t maximum_length; 23 | uint32_t pBuffer; // pointer to string contents 24 | } __attribute__((packed)) win32_unicode_string_t; 25 | 26 | typedef struct _windows_unicode_string64 { 27 | uint16_t length; 28 | uint16_t maximum_length; 29 | uint32_t padding; // align pBuffer 30 | uint64_t pBuffer; // pointer to string contents 31 | } __attribute__((packed)) win64_unicode_string_t; 32 | */ 33 | 34 | // length is always the first entry 35 | let mut length = 0u16; 36 | self.read_into(addr, &mut length)?; 37 | if length == 0 { 38 | return Err(Error(ErrorOrigin::OsLayer, ErrorKind::Encoding) 39 | .log_debug("unable to read unicode string length (length is zero)")); 40 | } 41 | 42 | // TODO: chek if length exceeds limit 43 | // buffer is either aligned at 4 or 8 44 | let buffer = match proc_arch.bits() { 45 | 64 => self.read_addr64(addr + 8)?, 46 | 32 => self.read_addr32(addr + 4)?, 47 | _ => { 48 | return Err(Error(ErrorOrigin::OsLayer, ErrorKind::InvalidArchitecture)); 49 | } 50 | }; 51 | if buffer.is_null() { 52 | return Err(Error(ErrorOrigin::OsLayer, ErrorKind::Encoding) 53 | .log_debug("unable to read unicode string buffer")); 54 | } 55 | 56 | // check if buffer length is mod 2 (utf-16) 57 | if length % 2 != 0 { 58 | return Err(Error(ErrorOrigin::OsLayer, ErrorKind::Encoding) 59 | .log_debug("unicode string length is not a multiple of two")); 60 | } 61 | 62 | // read buffer 63 | let mut content = vec![0; length as usize + 2]; 64 | self.read_raw_into(buffer, &mut content)?; 65 | content[length as usize] = 0; 66 | content[length as usize + 1] = 0; 67 | 68 | let content16 = content 69 | .chunks_exact(2) 70 | .map(|b| { 71 | b[0..2] 72 | .try_into() 73 | .map_err(|_| Error(ErrorOrigin::OsLayer, ErrorKind::Encoding)) 74 | }) 75 | .filter_map(Result::ok) 76 | .map(|b| match proc_arch.endianess() { 77 | Endianess::LittleEndian => u16::from_le_bytes(b), 78 | Endianess::BigEndian => u16::from_be_bytes(b), 79 | }) 80 | .collect::>(); 81 | Ok(U16CString::from_vec_truncate(content16).to_string_lossy()) 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /memflow-win32/src/win32/vat.rs: -------------------------------------------------------------------------------- 1 | use memflow::{ 2 | architecture::{arm, x86, ArchitectureIdent, ArchitectureObj}, 3 | cglue::tuple::*, 4 | iter::SplitAtIndex, 5 | mem::{ 6 | MemoryView, PhysicalMemory, VirtualDma, VirtualTranslate2, VirtualTranslate3, 7 | VtopFailureCallback, VtopOutputCallback, 8 | }, 9 | types::{umem, Address}, 10 | }; 11 | 12 | #[derive(Debug, Clone, Copy)] 13 | pub struct Win32VirtualTranslate { 14 | pub sys_arch: ArchitectureObj, 15 | pub dtb: Address, 16 | } 17 | 18 | impl Win32VirtualTranslate { 19 | pub fn new(arch: ArchitectureIdent, dtb: Address) -> Self { 20 | Self { 21 | sys_arch: arch.into(), 22 | dtb, 23 | } 24 | } 25 | 26 | pub fn virt_mem( 27 | self, 28 | mem: T, 29 | vat: V, 30 | proc_arch: ArchitectureObj, 31 | ) -> impl MemoryView { 32 | VirtualDma::with_vat(mem, proc_arch, self, vat) 33 | } 34 | } 35 | 36 | impl VirtualTranslate3 for Win32VirtualTranslate { 37 | fn virt_to_phys_iter< 38 | T: PhysicalMemory + ?Sized, 39 | B: SplitAtIndex, 40 | VI: Iterator>, 41 | >( 42 | &self, 43 | mem: &mut T, 44 | addrs: VI, 45 | out: &mut VtopOutputCallback, 46 | out_fail: &mut VtopFailureCallback, 47 | tmp_buf: &mut [std::mem::MaybeUninit], 48 | ) { 49 | if let Ok(translator) = x86::new_translator(self.dtb, self.sys_arch) { 50 | translator.virt_to_phys_iter(mem, addrs, out, out_fail, tmp_buf) 51 | } else if let Ok(translator) = arm::new_translator_nonsplit(self.dtb, self.sys_arch) { 52 | translator.virt_to_phys_iter(mem, addrs, out, out_fail, tmp_buf) 53 | } else { 54 | panic!("Invalid architecture"); 55 | } 56 | } 57 | 58 | fn translation_table_id(&self, _address: Address) -> umem { 59 | self.dtb.to_umem().overflowing_shr(12).0 60 | } 61 | 62 | fn arch(&self) -> ArchitectureObj { 63 | self.sys_arch 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /memflow-win32/src/win32/vkey.rs: -------------------------------------------------------------------------------- 1 | use core::ops::{Deref, DerefMut}; 2 | 3 | /// Windows Virtual Key Codes 4 | /// Based on the windows rust api https://microsoft.github.io/windows-docs-rs/doc/windows/Win32/UI/Input/KeyboardAndMouse/ 5 | /// except more flexible and cross platform 6 | #[repr(transparent)] 7 | #[derive(Clone, Copy, Debug, Default, Eq, PartialEq)] 8 | pub struct VKEY(pub u16); 9 | 10 | /// auto implement Display based on actual enum name 11 | /// Utilizes the Debug trait to print the enum name 12 | /// This works better on enum types. 13 | impl std::fmt::Display for VKEY { 14 | fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 15 | write!(f, "{:?}", self) 16 | } 17 | } 18 | 19 | impl Deref for VKEY { 20 | type Target = u16; 21 | 22 | fn deref(&self) -> &Self::Target { 23 | &self.0 24 | } 25 | } 26 | impl DerefMut for VKEY { 27 | fn deref_mut(&mut self) -> &mut Self::Target { 28 | &mut self.0 29 | } 30 | } 31 | 32 | impl From for u16 { 33 | fn from(vk: VKEY) -> Self { 34 | vk.0 35 | } 36 | } 37 | impl From for VKEY { 38 | fn from(v: u16) -> Self { 39 | VKEY(v) 40 | } 41 | } 42 | impl From for u32 { 43 | fn from(vk: VKEY) -> Self { 44 | vk.0 as u32 45 | } 46 | } 47 | impl From for VKEY { 48 | fn from(v: u32) -> Self { 49 | VKEY(v as u16) 50 | } 51 | } 52 | impl From for VKEY { 53 | fn from(v: i32) -> Self { 54 | VKEY(v as u16) 55 | } 56 | } 57 | impl From for i32 { 58 | fn from(vk: VKEY) -> Self { 59 | vk.0 as i32 60 | } 61 | } 62 | 63 | /// Iterate over a range of VKEYs (inclusive start, exclusive end) 64 | /// Placeholder until we can use the `Step` trait 65 | /// in a stable way 66 | pub fn vkey_range(start: VKEY, end: VKEY) -> impl Iterator { 67 | (start.0..end.0).map(VKEY) 68 | } 69 | 70 | // #[cfg(all(feature = "nightly", nightly))] 71 | // use std::iter::Step; 72 | // #[cfg(all(feature = "nightly", nightly))] 73 | // impl std::iter::Step for VKEY { 74 | // fn steps_between(start: &Self, end: &Self) -> Option { 75 | // u16::steps_between(&start.0, &end.0) 76 | // } 77 | // fn forward_checked(start: Self, count: usize) -> Option { 78 | // u16::forward_checked(start.0, count).map(VKEY) 79 | // } 80 | // fn backward_checked(start: Self, count: usize) -> Option { 81 | // u16::backward_checked(start.0, count).map(VKEY) 82 | // } 83 | // } 84 | 85 | pub const VK_0: VKEY = VKEY(48u16); 86 | pub const VK_1: VKEY = VKEY(49u16); 87 | pub const VK_2: VKEY = VKEY(50u16); 88 | pub const VK_3: VKEY = VKEY(51u16); 89 | pub const VK_4: VKEY = VKEY(52u16); 90 | pub const VK_5: VKEY = VKEY(53u16); 91 | pub const VK_6: VKEY = VKEY(54u16); 92 | pub const VK_7: VKEY = VKEY(55u16); 93 | pub const VK_8: VKEY = VKEY(56u16); 94 | pub const VK_9: VKEY = VKEY(57u16); 95 | pub const VK_A: VKEY = VKEY(65u16); 96 | pub const VK_ABNT_C1: VKEY = VKEY(193u16); 97 | pub const VK_ABNT_C2: VKEY = VKEY(194u16); 98 | pub const VK_ACCEPT: VKEY = VKEY(30u16); 99 | pub const VK_ADD: VKEY = VKEY(107u16); 100 | pub const VK_APPS: VKEY = VKEY(93u16); 101 | pub const VK_ATTN: VKEY = VKEY(246u16); 102 | pub const VK_B: VKEY = VKEY(66u16); 103 | pub const VK_BACK: VKEY = VKEY(8u16); 104 | pub const VK_BROWSER_BACK: VKEY = VKEY(166u16); 105 | pub const VK_BROWSER_FAVORITES: VKEY = VKEY(171u16); 106 | pub const VK_BROWSER_FORWARD: VKEY = VKEY(167u16); 107 | pub const VK_BROWSER_HOME: VKEY = VKEY(172u16); 108 | pub const VK_BROWSER_REFRESH: VKEY = VKEY(168u16); 109 | pub const VK_BROWSER_SEARCH: VKEY = VKEY(170u16); 110 | pub const VK_BROWSER_STOP: VKEY = VKEY(169u16); 111 | pub const VK_C: VKEY = VKEY(67u16); 112 | pub const VK_CANCEL: VKEY = VKEY(3u16); 113 | pub const VK_CAPITAL: VKEY = VKEY(20u16); 114 | pub const VK_CLEAR: VKEY = VKEY(12u16); 115 | pub const VK_CONTROL: VKEY = VKEY(17u16); 116 | pub const VK_CONVERT: VKEY = VKEY(28u16); 117 | pub const VK_CRSEL: VKEY = VKEY(247u16); 118 | pub const VK_D: VKEY = VKEY(68u16); 119 | pub const VK_DBE_ALPHANUMERIC: VKEY = VKEY(240u16); 120 | pub const VK_DBE_CODEINPUT: VKEY = VKEY(250u16); 121 | pub const VK_DBE_DBCSCHAR: VKEY = VKEY(244u16); 122 | pub const VK_DBE_DETERMINESTRING: VKEY = VKEY(252u16); 123 | pub const VK_DBE_ENTERDLGCONVERSIONMODE: VKEY = VKEY(253u16); 124 | pub const VK_DBE_ENTERIMECONFIGMODE: VKEY = VKEY(248u16); 125 | pub const VK_DBE_ENTERWORDREGISTERMODE: VKEY = VKEY(247u16); 126 | pub const VK_DBE_FLUSHSTRING: VKEY = VKEY(249u16); 127 | pub const VK_DBE_HIRAGANA: VKEY = VKEY(242u16); 128 | pub const VK_DBE_KATAKANA: VKEY = VKEY(241u16); 129 | pub const VK_DBE_NOCODEINPUT: VKEY = VKEY(251u16); 130 | pub const VK_DBE_NOROMAN: VKEY = VKEY(246u16); 131 | pub const VK_DBE_ROMAN: VKEY = VKEY(245u16); 132 | pub const VK_DBE_SBCSCHAR: VKEY = VKEY(243u16); 133 | pub const VK_DECIMAL: VKEY = VKEY(110u16); 134 | pub const VK_DELETE: VKEY = VKEY(46u16); 135 | pub const VK_DIVIDE: VKEY = VKEY(111u16); 136 | pub const VK_DOWN: VKEY = VKEY(40u16); 137 | pub const VK_E: VKEY = VKEY(69u16); 138 | pub const VK_END: VKEY = VKEY(35u16); 139 | pub const VK_EREOF: VKEY = VKEY(249u16); 140 | pub const VK_ESCAPE: VKEY = VKEY(27u16); 141 | pub const VK_EXECUTE: VKEY = VKEY(43u16); 142 | pub const VK_EXSEL: VKEY = VKEY(248u16); 143 | 144 | pub const VK_F: VKEY = VKEY(70u16); 145 | pub const VK_F1: VKEY = VKEY(112u16); 146 | pub const VK_F10: VKEY = VKEY(121u16); 147 | pub const VK_F11: VKEY = VKEY(122u16); 148 | pub const VK_F12: VKEY = VKEY(123u16); 149 | pub const VK_F13: VKEY = VKEY(124u16); 150 | pub const VK_F14: VKEY = VKEY(125u16); 151 | pub const VK_F15: VKEY = VKEY(126u16); 152 | pub const VK_F16: VKEY = VKEY(127u16); 153 | pub const VK_F17: VKEY = VKEY(128u16); 154 | pub const VK_F18: VKEY = VKEY(129u16); 155 | pub const VK_F19: VKEY = VKEY(130u16); 156 | pub const VK_F2: VKEY = VKEY(113u16); 157 | pub const VK_F20: VKEY = VKEY(131u16); 158 | pub const VK_F21: VKEY = VKEY(132u16); 159 | pub const VK_F22: VKEY = VKEY(133u16); 160 | pub const VK_F23: VKEY = VKEY(134u16); 161 | pub const VK_F24: VKEY = VKEY(135u16); 162 | pub const VK_F3: VKEY = VKEY(114u16); 163 | pub const VK_F4: VKEY = VKEY(115u16); 164 | pub const VK_F5: VKEY = VKEY(116u16); 165 | pub const VK_F6: VKEY = VKEY(117u16); 166 | pub const VK_F7: VKEY = VKEY(118u16); 167 | pub const VK_F8: VKEY = VKEY(119u16); 168 | pub const VK_F9: VKEY = VKEY(120u16); 169 | pub const VK_FINAL: VKEY = VKEY(24u16); 170 | 171 | pub const VK_G: VKEY = VKEY(71u16); 172 | pub const VK_GAMEPAD_A: VKEY = VKEY(195u16); 173 | pub const VK_GAMEPAD_B: VKEY = VKEY(196u16); 174 | pub const VK_GAMEPAD_DPAD_DOWN: VKEY = VKEY(204u16); 175 | pub const VK_GAMEPAD_DPAD_LEFT: VKEY = VKEY(205u16); 176 | pub const VK_GAMEPAD_DPAD_RIGHT: VKEY = VKEY(206u16); 177 | pub const VK_GAMEPAD_DPAD_UP: VKEY = VKEY(203u16); 178 | pub const VK_GAMEPAD_LEFT_SHOULDER: VKEY = VKEY(200u16); 179 | pub const VK_GAMEPAD_LEFT_THUMBSTICK_BUTTON: VKEY = VKEY(209u16); 180 | pub const VK_GAMEPAD_LEFT_THUMBSTICK_DOWN: VKEY = VKEY(212u16); 181 | pub const VK_GAMEPAD_LEFT_THUMBSTICK_LEFT: VKEY = VKEY(214u16); 182 | pub const VK_GAMEPAD_LEFT_THUMBSTICK_RIGHT: VKEY = VKEY(213u16); 183 | pub const VK_GAMEPAD_LEFT_THUMBSTICK_UP: VKEY = VKEY(211u16); 184 | pub const VK_GAMEPAD_LEFT_TRIGGER: VKEY = VKEY(201u16); 185 | pub const VK_GAMEPAD_MENU: VKEY = VKEY(207u16); 186 | pub const VK_GAMEPAD_RIGHT_SHOULDER: VKEY = VKEY(199u16); 187 | pub const VK_GAMEPAD_RIGHT_THUMBSTICK_BUTTON: VKEY = VKEY(210u16); 188 | pub const VK_GAMEPAD_RIGHT_THUMBSTICK_DOWN: VKEY = VKEY(216u16); 189 | pub const VK_GAMEPAD_RIGHT_THUMBSTICK_LEFT: VKEY = VKEY(218u16); 190 | pub const VK_GAMEPAD_RIGHT_THUMBSTICK_RIGHT: VKEY = VKEY(217u16); 191 | pub const VK_GAMEPAD_RIGHT_THUMBSTICK_UP: VKEY = VKEY(215u16); 192 | pub const VK_GAMEPAD_RIGHT_TRIGGER: VKEY = VKEY(202u16); 193 | pub const VK_GAMEPAD_VIEW: VKEY = VKEY(208u16); 194 | pub const VK_GAMEPAD_X: VKEY = VKEY(197u16); 195 | pub const VK_GAMEPAD_Y: VKEY = VKEY(198u16); 196 | pub const VK_H: VKEY = VKEY(72u16); 197 | pub const VK_HANGEUL: VKEY = VKEY(21u16); 198 | pub const VK_HANGUL: VKEY = VKEY(21u16); 199 | pub const VK_HANJA: VKEY = VKEY(25u16); 200 | pub const VK_HELP: VKEY = VKEY(47u16); 201 | pub const VK_HOME: VKEY = VKEY(36u16); 202 | pub const VK_I: VKEY = VKEY(73u16); 203 | pub const VK_ICO_00: VKEY = VKEY(228u16); 204 | pub const VK_ICO_CLEAR: VKEY = VKEY(230u16); 205 | pub const VK_ICO_HELP: VKEY = VKEY(227u16); 206 | pub const VK_IME_OFF: VKEY = VKEY(26u16); 207 | pub const VK_IME_ON: VKEY = VKEY(22u16); 208 | pub const VK_INSERT: VKEY = VKEY(45u16); 209 | pub const VK_J: VKEY = VKEY(74u16); 210 | pub const VK_JUNJA: VKEY = VKEY(23u16); 211 | pub const VK_K: VKEY = VKEY(75u16); 212 | pub const VK_KANA: VKEY = VKEY(21u16); 213 | pub const VK_KANJI: VKEY = VKEY(25u16); 214 | pub const VK_L: VKEY = VKEY(76u16); 215 | pub const VK_LAUNCH_APP1: VKEY = VKEY(182u16); 216 | pub const VK_LAUNCH_APP2: VKEY = VKEY(183u16); 217 | pub const VK_LAUNCH_MAIL: VKEY = VKEY(180u16); 218 | pub const VK_LAUNCH_MEDIA_SELECT: VKEY = VKEY(181u16); 219 | pub const VK_LBUTTON: VKEY = VKEY(1u16); 220 | pub const VK_LCONTROL: VKEY = VKEY(162u16); 221 | pub const VK_LEFT: VKEY = VKEY(37u16); 222 | pub const VK_LMENU: VKEY = VKEY(164u16); 223 | pub const VK_LSHIFT: VKEY = VKEY(160u16); 224 | pub const VK_LWIN: VKEY = VKEY(91u16); 225 | pub const VK_M: VKEY = VKEY(77u16); 226 | pub const VK_MBUTTON: VKEY = VKEY(4u16); 227 | pub const VK_MEDIA_NEXT_TRACK: VKEY = VKEY(176u16); 228 | pub const VK_MEDIA_PLAY_PAUSE: VKEY = VKEY(179u16); 229 | pub const VK_MEDIA_PREV_TRACK: VKEY = VKEY(177u16); 230 | pub const VK_MEDIA_STOP: VKEY = VKEY(178u16); 231 | pub const VK_MENU: VKEY = VKEY(18u16); 232 | pub const VK_MODECHANGE: VKEY = VKEY(31u16); 233 | pub const VK_MULTIPLY: VKEY = VKEY(106u16); 234 | pub const VK_N: VKEY = VKEY(78u16); 235 | pub const VK_NAVIGATION_ACCEPT: VKEY = VKEY(142u16); 236 | pub const VK_NAVIGATION_CANCEL: VKEY = VKEY(143u16); 237 | pub const VK_NAVIGATION_DOWN: VKEY = VKEY(139u16); 238 | pub const VK_NAVIGATION_LEFT: VKEY = VKEY(140u16); 239 | pub const VK_NAVIGATION_MENU: VKEY = VKEY(137u16); 240 | pub const VK_NAVIGATION_RIGHT: VKEY = VKEY(141u16); 241 | pub const VK_NAVIGATION_UP: VKEY = VKEY(138u16); 242 | pub const VK_NAVIGATION_VIEW: VKEY = VKEY(136u16); 243 | pub const VK_NEXT: VKEY = VKEY(34u16); 244 | pub const VK_NONAME: VKEY = VKEY(252u16); 245 | pub const VK_NONCONVERT: VKEY = VKEY(29u16); 246 | pub const VK_NUMLOCK: VKEY = VKEY(144u16); 247 | pub const VK_NUMPAD0: VKEY = VKEY(96u16); 248 | pub const VK_NUMPAD1: VKEY = VKEY(97u16); 249 | pub const VK_NUMPAD2: VKEY = VKEY(98u16); 250 | pub const VK_NUMPAD3: VKEY = VKEY(99u16); 251 | pub const VK_NUMPAD4: VKEY = VKEY(100u16); 252 | pub const VK_NUMPAD5: VKEY = VKEY(101u16); 253 | pub const VK_NUMPAD6: VKEY = VKEY(102u16); 254 | pub const VK_NUMPAD7: VKEY = VKEY(103u16); 255 | pub const VK_NUMPAD8: VKEY = VKEY(104u16); 256 | pub const VK_NUMPAD9: VKEY = VKEY(105u16); 257 | pub const VK_O: VKEY = VKEY(79u16); 258 | pub const VK_OEM_1: VKEY = VKEY(186u16); 259 | pub const VK_OEM_102: VKEY = VKEY(226u16); 260 | pub const VK_OEM_2: VKEY = VKEY(191u16); 261 | pub const VK_OEM_3: VKEY = VKEY(192u16); 262 | pub const VK_OEM_4: VKEY = VKEY(219u16); 263 | pub const VK_OEM_5: VKEY = VKEY(220u16); 264 | pub const VK_OEM_6: VKEY = VKEY(221u16); 265 | pub const VK_OEM_7: VKEY = VKEY(222u16); 266 | pub const VK_OEM_8: VKEY = VKEY(223u16); 267 | pub const VK_OEM_ATTN: VKEY = VKEY(240u16); 268 | pub const VK_OEM_AUTO: VKEY = VKEY(243u16); 269 | pub const VK_OEM_AX: VKEY = VKEY(225u16); 270 | pub const VK_OEM_BACKTAB: VKEY = VKEY(245u16); 271 | pub const VK_OEM_CLEAR: VKEY = VKEY(254u16); 272 | pub const VK_OEM_COMMA: VKEY = VKEY(188u16); 273 | pub const VK_OEM_COPY: VKEY = VKEY(242u16); 274 | pub const VK_OEM_CUSEL: VKEY = VKEY(239u16); 275 | pub const VK_OEM_ENLW: VKEY = VKEY(244u16); 276 | pub const VK_OEM_FINISH: VKEY = VKEY(241u16); 277 | pub const VK_OEM_FJ_JISHO: VKEY = VKEY(146u16); 278 | pub const VK_OEM_FJ_LOYA: VKEY = VKEY(149u16); 279 | pub const VK_OEM_FJ_MASSHOU: VKEY = VKEY(147u16); 280 | pub const VK_OEM_FJ_ROYA: VKEY = VKEY(150u16); 281 | pub const VK_OEM_FJ_TOUROKU: VKEY = VKEY(148u16); 282 | pub const VK_OEM_JUMP: VKEY = VKEY(234u16); 283 | pub const VK_OEM_MINUS: VKEY = VKEY(189u16); 284 | pub const VK_OEM_NEC_EQUAL: VKEY = VKEY(146u16); 285 | pub const VK_OEM_PA1: VKEY = VKEY(235u16); 286 | pub const VK_OEM_PA2: VKEY = VKEY(236u16); 287 | pub const VK_OEM_PA3: VKEY = VKEY(237u16); 288 | pub const VK_OEM_PERIOD: VKEY = VKEY(190u16); 289 | pub const VK_OEM_PLUS: VKEY = VKEY(187u16); 290 | pub const VK_OEM_RESET: VKEY = VKEY(233u16); 291 | pub const VK_OEM_WSCTRL: VKEY = VKEY(238u16); 292 | pub const VK_P: VKEY = VKEY(80u16); 293 | pub const VK_PA1: VKEY = VKEY(253u16); 294 | pub const VK_PACKET: VKEY = VKEY(231u16); 295 | pub const VK_PAUSE: VKEY = VKEY(19u16); 296 | pub const VK_PLAY: VKEY = VKEY(250u16); 297 | pub const VK_PRINT: VKEY = VKEY(42u16); 298 | pub const VK_PRIOR: VKEY = VKEY(33u16); 299 | pub const VK_PROCESSKEY: VKEY = VKEY(229u16); 300 | pub const VK_Q: VKEY = VKEY(81u16); 301 | pub const VK_R: VKEY = VKEY(82u16); 302 | pub const VK_RBUTTON: VKEY = VKEY(2u16); 303 | pub const VK_RCONTROL: VKEY = VKEY(163u16); 304 | pub const VK_RETURN: VKEY = VKEY(13u16); 305 | pub const VK_RIGHT: VKEY = VKEY(39u16); 306 | pub const VK_RMENU: VKEY = VKEY(165u16); 307 | pub const VK_RSHIFT: VKEY = VKEY(161u16); 308 | pub const VK_RWIN: VKEY = VKEY(92u16); 309 | pub const VK_S: VKEY = VKEY(83u16); 310 | pub const VK_SCROLL: VKEY = VKEY(145u16); 311 | pub const VK_SELECT: VKEY = VKEY(41u16); 312 | pub const VK_SEPARATOR: VKEY = VKEY(108u16); 313 | pub const VK_SHIFT: VKEY = VKEY(16u16); 314 | pub const VK_SLEEP: VKEY = VKEY(95u16); 315 | pub const VK_SNAPSHOT: VKEY = VKEY(44u16); 316 | pub const VK_SPACE: VKEY = VKEY(32u16); 317 | pub const VK_SUBTRACT: VKEY = VKEY(109u16); 318 | pub const VK_T: VKEY = VKEY(84u16); 319 | pub const VK_TAB: VKEY = VKEY(9u16); 320 | 321 | pub const VK_U: VKEY = VKEY(85u16); 322 | pub const VK_UP: VKEY = VKEY(38u16); 323 | pub const VK_V: VKEY = VKEY(86u16); 324 | pub const VK_VOLUME_DOWN: VKEY = VKEY(174u16); 325 | pub const VK_VOLUME_MUTE: VKEY = VKEY(173u16); 326 | pub const VK_VOLUME_UP: VKEY = VKEY(175u16); 327 | 328 | pub const VK_W: VKEY = VKEY(87u16); 329 | pub const VK_X: VKEY = VKEY(88u16); 330 | pub const VK_XBUTTON1: VKEY = VKEY(5u16); 331 | pub const VK_XBUTTON2: VKEY = VKEY(6u16); 332 | pub const VK_Y: VKEY = VKEY(89u16); 333 | pub const VK_Z: VKEY = VKEY(90u16); 334 | pub const VK_ZOOM: VKEY = VKEY(251u16); 335 | pub const VK_NONE: VKEY = VKEY(255u16); 336 | --------------------------------------------------------------------------------