├── .gitignore ├── lib └── demangling.c ├── Cargo.toml ├── src ├── files.rs ├── setup.rs ├── demangle.rs ├── main.rs └── pdb.rs ├── 3rd party notices ├── LICENSE-MIT-cc-rs ├── LICENSE-native-windows-gui ├── LICENSE-pdb └── LICENSE-md5.md ├── LICENSE ├── README.md └── Cargo.lock /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | -------------------------------------------------------------------------------- /lib/demangling.c: -------------------------------------------------------------------------------- 1 | #pragma comment(lib, "DbgHelp.lib") 2 | 3 | #include "Windows.h" 4 | #include "DbgHelp.h" 5 | 6 | const char* demangle(const char* symbol) 7 | { 8 | char buffer[1024]; 9 | memset(buffer, 0, 1024); 10 | UnDecorateSymbolName(symbol, buffer, 1024, 0); 11 | return buffer; 12 | } -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "bedrock_dumper" 3 | version = "0.1.0" 4 | edition = "2018" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | native-windows-gui = "1.0.12" 10 | native-windows-derive = "1.0.3" 11 | md5 = "0.7.0" 12 | pdb = "0.7.0" 13 | 14 | [build-dependencies] 15 | cc = "1.0" -------------------------------------------------------------------------------- /src/files.rs: -------------------------------------------------------------------------------- 1 | /// This checks if a file or directory exists 2 | pub fn path_exists(path: &str) -> bool { 3 | std::fs::metadata(path).is_ok() 4 | } 5 | 6 | /// This function will create the output file the dump will be written to 7 | /// 8 | /// # Arguments 9 | /// 10 | /// * `file_type`: Determines, well, the file type. It can be .hpp (for a C++ header) or .txt 11 | /// for a text file 12 | /// 13 | /// returns: Result 14 | /// 15 | /// # Examples 16 | /// 17 | /// ``` 18 | /// 19 | /// ``` 20 | pub fn create_file(file_type: &str) -> Result { 21 | let file_path = if file_type == ".txt" { 22 | "./SymHook.txt" 23 | } else if file_type == ".hpp" { 24 | "./SymHook.hpp" 25 | } else { 26 | return Err("Invalid File Type"); 27 | }; 28 | 29 | match std::fs::File::create(file_path) { 30 | Ok(_) => Ok(std::fs::OpenOptions::new() 31 | .write(true) 32 | .append(true) 33 | .open(file_path) 34 | .unwrap()), 35 | Err(_) => Err("Could not create file"), 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /3rd party notices/LICENSE-MIT-cc-rs: -------------------------------------------------------------------------------- 1 | Copyright (c) 2014 Alex Crichton 2 | 3 | Permission is hereby granted, free of charge, to any 4 | person obtaining a copy of this software and associated 5 | documentation files (the Software), to deal in the 6 | Software without restriction, including without 7 | limitation the rights to use, copy, modify, merge, 8 | publish, distribute, sublicense, andor sell copies of 9 | the Software, and to permit persons to whom the Software 10 | is furnished to do so, subject to the following 11 | conditions 12 | 13 | The above copyright notice and this permission notice 14 | shall be included in all copies or substantial portions 15 | of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED AS IS, WITHOUT WARRANTY OF 18 | ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED 19 | TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 20 | PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT 21 | SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 22 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 23 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR 24 | IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 25 | DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /3rd party notices/LICENSE-native-windows-gui: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Gabriel Dube 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /3rd party notices/LICENSE-pdb: -------------------------------------------------------------------------------- 1 | Copyright (c) 2017 pdb Developers 2 | 3 | Permission is hereby granted, free of charge, to any 4 | person obtaining a copy of this software and associated 5 | documentation files (the "Software"), to deal in the 6 | Software without restriction, including without 7 | limitation the rights to use, copy, modify, merge, 8 | publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software 10 | is furnished to do so, subject to the following 11 | conditions: 12 | 13 | The above copyright notice and this permission notice 14 | shall be included in all copies or substantial portions 15 | of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF 18 | ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED 19 | TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 20 | PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT 21 | SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 22 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 23 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR 24 | IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 25 | DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | BSD 3-Clause License 2 | 3 | Copyright (c) 2021, Luke7720 4 | All rights reserved. 5 | 6 | Redistribution and use in source and binary forms, with or without 7 | modification, are permitted provided that the following conditions are met: 8 | 9 | 1. Redistributions of source code must retain the above copyright notice, this 10 | list of conditions and the following disclaimer. 11 | 12 | 2. Redistributions in binary form must reproduce the above copyright notice, 13 | this list of conditions and the following disclaimer in the documentation 14 | and/or other materials provided with the distribution. 15 | 16 | 3. Neither the name of the copyright holder nor the names of its 17 | contributors may be used to endorse or promote products derived from 18 | this software without specific prior written permission. 19 | 20 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 21 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 23 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 24 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 26 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 27 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 28 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | -------------------------------------------------------------------------------- /src/setup.rs: -------------------------------------------------------------------------------- 1 | use {crate::files, std::fs::File, std::io::Write}; 2 | 3 | /// This function will create the filter settings file if it doesn't exist 4 | pub fn filter_manager() -> bool { 5 | if !files::path_exists("./dumpFilter.txt") { 6 | File::create("dumpFilter.txt").unwrap(); 7 | 8 | return false; 9 | } 10 | true 11 | } 12 | 13 | /// This writes the 'header' of the dump output 14 | /// 15 | /// # Arguments 16 | /// 17 | /// * `pdb_path`: Location of the pdb file to be dumped 18 | /// * `file_type`: Output format type (.hpp for C++ header, .txt for plain text) 19 | /// 20 | /// returns: Result 21 | /// 22 | /// # Examples 23 | /// 24 | /// ``` 25 | /// 26 | /// ``` 27 | pub fn dump_init(pdb_path: &str, file_type: &str) -> Result { 28 | if files::path_exists(pdb_path) == false { 29 | return Err(String::from(&format!("File does not exist: {}", pdb_path))); 30 | } 31 | 32 | let mut dump_file = match files::create_file(&file_type) { 33 | Ok(file) => file, 34 | Err(str) => { 35 | return Err(String::from(&format!("{}: {}", str, file_type))); 36 | } 37 | }; 38 | 39 | write!( 40 | dump_file, 41 | "/*###############################################################\ 42 | \nBDS function symbols and RVAs\ 43 | \nFile generated by BDumper, a rust bds pdb dumper made by Luke7720\ 44 | \n###############################################################*/\n" 45 | ) 46 | .expect("ERROR: Could not write to file"); 47 | 48 | if file_type == ".hpp" { 49 | write!(dump_file, "#pragma once\n").expect("ERROR: Could not write to file"); 50 | } 51 | 52 | Ok(dump_file) 53 | } 54 | -------------------------------------------------------------------------------- /src/demangle.rs: -------------------------------------------------------------------------------- 1 | use crate::demangle; 2 | use std::ffi::{CStr, CString}; 3 | 4 | /// A wrapper for the C demangling function in order to isolate the unsafe code 5 | /// 6 | /// # Arguments 7 | /// 8 | /// * `symbol`: A reference to a string that contains the MSVC symbol to demangle 9 | /// 10 | /// returns: String 11 | /// 12 | /// # Examples 13 | /// 14 | /// ``` 15 | /// 16 | /// ``` 17 | pub fn undecorate(symbol: &str) -> String { 18 | unsafe { 19 | let cstr = CString::new(symbol).unwrap(); 20 | let result: &CStr = CStr::from_ptr(demangle(cstr.as_ptr())); 21 | 22 | return result.to_str().unwrap().to_owned(); 23 | } 24 | } 25 | 26 | /// Formats already demangled symbols to make the output nicer and more readable 27 | /// 28 | /// # Arguments 29 | /// 30 | /// * `symbol`: Demangled function prototype 31 | /// 32 | /// returns: String 33 | /// 34 | /// # Examples 35 | /// 36 | /// ``` 37 | /// 38 | /// ``` 39 | pub fn cleanup_symbol(symbol: &str) -> String { 40 | let res = undecorate(symbol); 41 | let demangled_name = res.replace("const", " const").replace("(", "( "); 42 | let mut declaration: Vec<&str> = demangled_name.split(" ").collect(); 43 | 44 | for i in 0..declaration.len() { 45 | if &declaration[i] as &str == "const" && declaration[i - 1].starts_with("__") && i != 0 { 46 | let check_space = if &declaration[i - 1] as &str == " " { 47 | i - 1 48 | } else { 49 | i - 2 50 | }; 51 | 52 | declaration.swap(i as usize, check_space); 53 | } 54 | } 55 | 56 | declaration 57 | .join(" ") 58 | .replace("class", "") 59 | .replace("struct", "") 60 | .replace(" ", " ") 61 | .replace(" ", " ") 62 | .replace("< ", "<") 63 | .replace(" >", ">") 64 | .replace(" &", "&") 65 | .replace(" *", "*") 66 | .replace("( ", "(") 67 | } 68 | -------------------------------------------------------------------------------- /3rd party notices/LICENSE-md5.md: -------------------------------------------------------------------------------- 1 | # License 2 | 3 | The project is dual licensed under the terms of the Apache License, Version 2.0, 4 | and the MIT License. You may obtain copies of the two licenses at 5 | 6 | * https://www.apache.org/licenses/LICENSE-2.0 and 7 | * https://opensource.org/licenses/MIT, respectively. 8 | 9 | The following two notices apply to every file of the project. 10 | 11 | ## The Apache License 12 | 13 | ``` 14 | Copyright 2015–2019 The md5 Developers 15 | 16 | Licensed under the Apache License, Version 2.0 (the “License”); you may not use 17 | this file except in compliance with the License. You may obtain a copy of the 18 | License at 19 | 20 | http://www.apache.org/licenses/LICENSE-2.0 21 | 22 | Unless required by applicable law or agreed to in writing, software distributed 23 | under the License is distributed on an “AS IS” BASIS, WITHOUT WARRANTIES OR 24 | CONDITIONS OF ANY KIND, either express or implied. See the License for the 25 | specific language governing permissions and limitations under the License. 26 | ``` 27 | 28 | ## The MIT License 29 | 30 | ``` 31 | Copyright 2015–2019 The md5 Developers 32 | 33 | Permission is hereby granted, free of charge, to any person obtaining a copy of 34 | this software and associated documentation files (the “Software”), to deal in 35 | the Software without restriction, including without limitation the rights to 36 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 37 | the Software, and to permit persons to whom the Software is furnished to do so, 38 | subject to the following conditions: 39 | 40 | The above copyright notice and this permission notice shall be included in all 41 | copies or substantial portions of the Software. 42 | 43 | THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 44 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 45 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 46 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 47 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 48 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 49 | ``` -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # THIS PROJECT IS NOW OBSOLETE. SEE [outcrop](https://github.com/0x4c37373230/outcrop-final) 2 | #### I decided to archive all my standalone bedrock reverse engineering and modding projects in order to combine all of them into a single, user friendly tool so others can use them as well. BDumper and all other BDS tools will be continued and maintained there. 3 | 4 | # BDumper 5 | A windows BDS .pdb dumper written in rust which dumps symbols, reconstructed function prototypes from demangled symbols and RVAs (Relative Virtual Addresses) into either a C++ header (.hpp) or a text file (.txt) and can also find data corresponding to specific functions. This project was inspired by [Player]'s BDS .pdb dumper. The newest version has a GUI and can search and find specific functions while the older versions are CLI programs. The variables in the headers are named after the symbol's MD5 hashes. 6 | 7 | ## Dependencies 8 | 9 | ### Current version 10 | 11 | - [md5 0.7.0](https://crates.io/crates/md5) 12 | - [native-windows-gui 1.0.12](https://crates.io/crates/native-windows-gui) 13 | - [native-windows-derive 1.0.3](https://crates.io/crates/native-windows-derive) 14 | - [pdb 0.7.0](https://crates.io/crates/pdb) 15 | - [cc-rs 1.0](https://crates.io/crates/cc) 16 | 17 | ## Usage 18 | 19 | ### Current Version UI 20 |

21 | 22 |

23 | 24 | ### Full Dump 25 | 26 | Input the pdb file path and the file type and then click the 'Dump Data' button which will create a SymHook file in the project/executable directory. The latter can be either '.txt' or '.hpp'. The C++ header mode uses the symbol md5 hashes as variable names 27 | 28 | ### Filtered Dump 29 | 30 | Upon startup for the first time, BDumper creates a file with the name 'dumpFilter.txt'. This file will hold the functions to be dumped in this mode. BDumper will ignore lines starting with '#' (which are considered comments) and newlines. An example: 31 | 32 | ``` 33 | # Dump Filter Example 34 | # This is a comment, BDumper ignores lines starting with '#' and empty lines 35 | 36 | OceanMonumentFeature::isFeatureChunk 37 | FossilFeature::place 38 | PistonBlock::canSurvive 39 | PistonBlockActor::_attachedBlockWalker 40 | BlockSource::getSeenPercent 41 | ``` 42 | 43 | Click on the 'Filtered Dump' button to dump the desired functions. 44 | 45 | ### Function Search 46 | 47 | If you need to quickly hook into a function or update an RVA, this function can find an individual function with the same method the filtered dump uses. Input the function name (`PistonBlockActor::_checkAttachedBlocks` as an example) and click on the 'FInd Function' button. A window will pop up with the symbol and RVA if the function exists 48 | -------------------------------------------------------------------------------- /src/main.rs: -------------------------------------------------------------------------------- 1 | #![windows_subsystem = "windows"] 2 | 3 | mod demangle; 4 | mod files; 5 | mod pdb; 6 | mod setup; 7 | 8 | extern crate native_windows_derive as nwd; 9 | extern crate native_windows_gui as nwg; 10 | 11 | use { 12 | nwd::NwgUi, 13 | nwg::{CheckBoxState, NativeUi}, 14 | std::os::raw::c_char, 15 | }; 16 | 17 | extern "C" { 18 | /// C function that acts as an interface between BDumper and the windows debug function 19 | /// UnDecorateSymbolName 20 | /// 21 | /// # Arguments 22 | /// 23 | /// * `s`: A C string that holds the MSVC symbol to be demangled 24 | /// 25 | /// returns: *const i8 26 | /// 27 | /// # Examples 28 | /// 29 | /// ``` 30 | /// 31 | /// ``` 32 | fn demangle(s: *const c_char) -> *const c_char; 33 | } 34 | 35 | #[derive(Default, NwgUi)] 36 | pub struct BedrockDumper { 37 | #[nwg_control(size: (590, 225), position: (300, 300), title: "BDumper", flags: "WINDOW|VISIBLE")] 38 | #[nwg_events( OnWindowClose: [BedrockDumper::exit_program] )] 39 | window: nwg::Window, 40 | 41 | #[nwg_control(text: "BDumper is a .pdb file dumper made in Rust by Luke7720 designed to extract \ 42 | function prototypes and RVAs (Relative Virtual Addresses) and export them into either text \ 43 | or C++ header files. It can also find specific functions within the pdb\n\ 44 | -----------------------------------------------------------------------\ 45 | ----------------------------------------------------------------------- 46 | ", size: (580, 70), position: (10, 10))] 47 | label: nwg::Label, 48 | 49 | #[nwg_control(text: "Input your .pdb file path here", size: (280, 25), position: (10, 80))] 50 | label2: nwg::Label, 51 | 52 | #[nwg_control(text: "", size: (280, 25), position: (10, 100))] 53 | pdb_path: nwg::TextInput, 54 | 55 | #[nwg_control(text: "Input your file type (.hpp or .txt) here", size: (280, 25), position: (300, 80))] 56 | label3: nwg::Label, 57 | 58 | #[nwg_control(text: "", size: (280, 25), position: (300, 100))] 59 | file_type: nwg::TextInput, 60 | 61 | #[nwg_control(text: "Input a function's name here", size: (280, 25), position: (10, 130))] 62 | label4: nwg::Label, 63 | //570 25 64 | #[nwg_control(text: "", size: (280, 25), position: (10, 150))] 65 | func_name: nwg::TextInput, 66 | 67 | #[nwg_control(text: "Include demangled function prototypes", size: (280, 25), position: (300, 150))] 68 | should_demangle: nwg::CheckBox, 69 | 70 | #[nwg_control(text: "Dump Data", size: (185, 30), position: (10, 180))] 71 | #[nwg_events( OnButtonClick: [BedrockDumper::dump] )] 72 | dump: nwg::Button, 73 | 74 | #[nwg_control(text: "Find Function", size: (185, 30), position: (200, 180))] 75 | #[nwg_events( OnButtonClick: [BedrockDumper::find] )] 76 | find: nwg::Button, 77 | 78 | #[nwg_control(text: "Filtered Dump", size: (185, 30), position: (390, 180))] 79 | #[nwg_events( OnButtonClick: [BedrockDumper::filtered_dump] )] 80 | find_filtered: nwg::Button, 81 | } 82 | 83 | impl BedrockDumper { 84 | fn dump(&self) { 85 | let pdb_path: &str = &self.pdb_path.text(); 86 | let file_type: &str = &self.file_type.text(); 87 | let demangle = if &self.should_demangle.check_state() == &CheckBoxState::Checked { 88 | true 89 | } else { 90 | false 91 | }; 92 | 93 | match setup::dump_init(pdb_path, file_type) { 94 | Ok(dump_file) => pdb::pdb_dump(pdb_path, file_type, dump_file, demangle) 95 | .expect("ERROR: Failed to dump pdb contents"), 96 | Err(str) => { 97 | nwg::simple_message("Error", &str); 98 | return; 99 | } 100 | } 101 | } 102 | 103 | fn find(&self) { 104 | match pdb::find_function(&self.pdb_path.text(), &self.func_name.text()) { 105 | Ok(bds_func) => nwg::simple_message( 106 | "Found a match", 107 | &format!( 108 | "Function name: {}\nSymbol: {}\nRVA: {}", 109 | bds_func.name, bds_func.symbol, bds_func.rva 110 | ), 111 | ), 112 | Err(str) => nwg::simple_message("Error", &str), 113 | }; 114 | } 115 | 116 | fn filtered_dump(&self) { 117 | let pdb_path: &str = &self.pdb_path.text(); 118 | let file_type: &str = &self.file_type.text(); 119 | 120 | match setup::dump_init(pdb_path, file_type) { 121 | Ok(dump_file) => match pdb::find_functions(pdb_path, file_type, dump_file) { 122 | Err(str) => { 123 | nwg::simple_message("Error", &str); 124 | } 125 | _ => {} 126 | }, 127 | Err(str) => { 128 | nwg::simple_message("Error", &str); 129 | } 130 | } 131 | } 132 | 133 | fn exit_program(&self) { 134 | nwg::stop_thread_dispatch(); 135 | } 136 | } 137 | 138 | fn main() { 139 | nwg::init().expect("Failed to init Native Windows GUI"); 140 | setup::filter_manager(); 141 | 142 | let _app = BedrockDumper::build_ui(Default::default()).expect("Failed to build UI"); 143 | 144 | nwg::dispatch_thread_events(); 145 | } 146 | -------------------------------------------------------------------------------- /src/pdb.rs: -------------------------------------------------------------------------------- 1 | use { 2 | crate::demangle, 3 | pdb::{FallibleIterator, Rva}, 4 | std::io::{BufRead, BufReader}, 5 | std::{fs::File, io::Write, time::Instant}, 6 | }; 7 | 8 | pub struct BDSFunction { 9 | pub name: String, 10 | pub symbol: String, 11 | pub rva: Rva, 12 | } 13 | 14 | impl BDSFunction { 15 | /// This function acts as a constructor for BDSFunction instances 16 | /// 17 | /// # Arguments 18 | /// 19 | /// * `name`: Symbol (function or constant) name 20 | /// * `symbol`: Mangled function symbol 21 | /// * `rva`: Relative virtual address of the symbol 22 | /// 23 | /// returns: BDSFunction 24 | /// 25 | /// # Examples 26 | /// 27 | /// ``` 28 | /// 29 | /// ``` 30 | fn create_instance(name: String, symbol: String, rva: Rva) -> BDSFunction { 31 | return BDSFunction { name, symbol, rva }; 32 | } 33 | } 34 | 35 | /// 36 | /// 37 | /// # Arguments 38 | /// 39 | /// * `pdb_path`: Location of the pdb file to be dumped 40 | /// * `file_type`: Dump result output format 41 | /// * `dump_file`: Handle to the file to write the PDB contents to 42 | /// * `should_demangle`: Determine if names should be demangled or not (the latter increases 43 | /// performance) 44 | /// 45 | /// returns: Result<(), Error> 46 | /// 47 | /// # Examples 48 | /// 49 | /// ``` 50 | /// 51 | /// ``` 52 | pub fn pdb_dump( 53 | pdb_path: &str, 54 | file_type: &str, 55 | mut dump_file: File, 56 | should_demangle: bool, 57 | ) -> pdb::Result<()> { 58 | let start = Instant::now(); 59 | let file_path = File::open(&pdb_path)?; 60 | let mut pdb = pdb::PDB::open(file_path)?; 61 | let symbol_table = pdb.global_symbols()?; 62 | let address_map = pdb.address_map()?; 63 | let mut symbols = symbol_table.iter(); 64 | 65 | while let Some(symbol) = symbols.next()? { 66 | match symbol.parse() { 67 | Ok(pdb::SymbolData::Public(data)) if data.function => { 68 | let rva = data.offset.to_rva(&address_map).unwrap_or_default(); 69 | 70 | if file_type == ".txt" { 71 | write!( 72 | dump_file, 73 | "{}\n{}\n{}\n\n", 74 | data.name, 75 | demangle::cleanup_symbol(&data.name.to_string()), 76 | rva 77 | ) 78 | .expect("ERROR: Could not write to file"); 79 | } else if file_type == ".hpp" { 80 | if should_demangle { 81 | write!( 82 | dump_file, 83 | "//{}\n//{}\nconstexpr unsigned int MD5_{:x} = {};\n\n", 84 | data.name, 85 | demangle::cleanup_symbol(&data.name.to_string()), 86 | md5::compute(data.name.to_string().to_string()), 87 | rva 88 | ) 89 | .expect("ERROR: Could not write to file"); 90 | } else { 91 | write!( 92 | dump_file, 93 | "//{}\nconstexpr unsigned int MD5_{:x} = {};\n\n", 94 | data.name, 95 | md5::compute(data.name.to_string().to_string()), 96 | rva 97 | ) 98 | .expect("ERROR: Could not write to file"); 99 | } 100 | } else { 101 | break; 102 | } 103 | } 104 | _ => {} 105 | } 106 | } 107 | nwg::simple_message( 108 | "Completed", 109 | &format!("Completed dumping {} in {:?}", pdb_path, start.elapsed()), 110 | ); 111 | 112 | Ok(()) 113 | } 114 | 115 | /// Similar to the function above except that this returns the data corresponding to only one 116 | /// function therefore making it more performant since only the function symbol is checked every 117 | /// iteration, no data writing and nothing else is accessed 118 | /// 119 | /// # Arguments 120 | /// 121 | /// * `pdb_path`: Location of the PDB file to be dumped 122 | /// * `function_name`: Function name to find formatted as [Class Name]::[Function] 123 | /// 124 | /// returns: Result 125 | /// 126 | /// # Examples 127 | /// 128 | /// ``` 129 | /// 130 | /// ``` 131 | pub fn find_function(pdb_path: &str, function_name: &str) -> Result { 132 | match File::open(&pdb_path) { 133 | Ok(file_path) => { 134 | let mut pdb = pdb::PDB::open(file_path).unwrap(); 135 | let symbol_table = pdb.global_symbols().unwrap(); 136 | let address_map = pdb.address_map().unwrap(); 137 | let mut symbols = symbol_table.iter(); 138 | 139 | while let Some(symbol) = symbols.next().unwrap() { 140 | match symbol.parse() { 141 | Ok(pdb::SymbolData::Public(data)) if data.function => { 142 | let symbol = data.name.to_string().to_string(); 143 | let rva = data.offset.to_rva(&address_map).unwrap_or_default(); 144 | let function_sym: Vec<&str> = function_name.split("::").collect(); 145 | let substr = format!("{}@{}", function_sym[1], function_sym[0]); 146 | 147 | if symbol.contains(&substr) { 148 | let found_function = BDSFunction::create_instance( 149 | demangle::cleanup_symbol(&symbol), 150 | symbol, 151 | rva, 152 | ); 153 | return Ok(found_function); 154 | } 155 | } 156 | _ => {} 157 | } 158 | } 159 | 160 | Err(String::from( 161 | "Function was either not found or does not exist", 162 | )) 163 | } 164 | Err(_) => { 165 | Err(String::from(format!("File does not exist: {}", pdb_path))) 166 | } 167 | } 168 | } 169 | 170 | /// Same as above but it finds multiple functions that should be written down in the filter file 171 | /// generated by this program. Less performant 172 | /// 173 | /// # Arguments 174 | /// 175 | /// * `pdb_path`: Location of the PDB to be dumped 176 | /// * `file_type`: Output format 177 | /// * `dump_file`: Output file handle 178 | /// 179 | /// returns: Result<(), String> 180 | /// 181 | /// # Examples 182 | /// 183 | /// ``` 184 | /// 185 | /// ``` 186 | pub fn find_functions(pdb_path: &str, file_type: &str, mut dump_file: File) -> Result<(), String> { 187 | let file = File::open("./dumpFilter.txt").unwrap(); 188 | let functions = BufReader::new(file); 189 | 190 | for line in functions.lines() { 191 | let line_ref: &str = &line.unwrap(); 192 | 193 | if line_ref.starts_with("#") || line_ref.is_empty() { 194 | continue; 195 | } 196 | 197 | match find_function(pdb_path, line_ref) { 198 | Ok(bds_func) => { 199 | if file_type == ".txt" { 200 | write!( 201 | dump_file, 202 | "{}\n{}\n{}\n\n", 203 | &bds_func.symbol, 204 | demangle::cleanup_symbol(&bds_func.symbol), 205 | bds_func.rva 206 | ) 207 | .expect("ERROR: Could not write to file"); 208 | } else if file_type == ".hpp" { 209 | write!( 210 | dump_file, 211 | "//{}\n//{}\nconstexpr unsigned int MD5_{:x} = {};\n\n", 212 | &bds_func.symbol, 213 | demangle::cleanup_symbol(&bds_func.symbol), 214 | md5::compute(&bds_func.symbol), 215 | bds_func.rva 216 | ) 217 | .expect("ERROR: Could not write to file"); 218 | } 219 | } 220 | Err(str) => { 221 | return Err(str); 222 | } 223 | } 224 | } 225 | 226 | nwg::simple_message( 227 | "Completed", 228 | &format!("Completed filtered dumping of {}", pdb_path), 229 | ); 230 | Ok(()) 231 | } 232 | -------------------------------------------------------------------------------- /Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 3 4 | 5 | [[package]] 6 | name = "autocfg" 7 | version = "1.0.1" 8 | source = "registry+https://github.com/rust-lang/crates.io-index" 9 | checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a" 10 | 11 | [[package]] 12 | name = "bedrock_dumper" 13 | version = "0.1.0" 14 | dependencies = [ 15 | "cc", 16 | "md5", 17 | "native-windows-derive", 18 | "native-windows-gui", 19 | "pdb", 20 | ] 21 | 22 | [[package]] 23 | name = "bitflags" 24 | version = "1.3.2" 25 | source = "registry+https://github.com/rust-lang/crates.io-index" 26 | checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" 27 | 28 | [[package]] 29 | name = "bumpalo" 30 | version = "3.8.0" 31 | source = "registry+https://github.com/rust-lang/crates.io-index" 32 | checksum = "8f1e260c3a9040a7c19a12468758f4c16f31a81a1fe087482be9570ec864bb6c" 33 | 34 | [[package]] 35 | name = "cc" 36 | version = "1.0.73" 37 | source = "registry+https://github.com/rust-lang/crates.io-index" 38 | checksum = "2fff2a6927b3bb87f9595d67196a70493f627687a71d87a0d692242c33f58c11" 39 | 40 | [[package]] 41 | name = "cfg-if" 42 | version = "1.0.0" 43 | source = "registry+https://github.com/rust-lang/crates.io-index" 44 | checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" 45 | 46 | [[package]] 47 | name = "fallible-iterator" 48 | version = "0.2.0" 49 | source = "registry+https://github.com/rust-lang/crates.io-index" 50 | checksum = "4443176a9f2c162692bd3d352d745ef9413eec5782a80d8fd6f8a1ac692a07f7" 51 | 52 | [[package]] 53 | name = "js-sys" 54 | version = "0.3.55" 55 | source = "registry+https://github.com/rust-lang/crates.io-index" 56 | checksum = "7cc9ffccd38c451a86bf13657df244e9c3f37493cce8e5e21e940963777acc84" 57 | dependencies = [ 58 | "wasm-bindgen", 59 | ] 60 | 61 | [[package]] 62 | name = "lazy_static" 63 | version = "1.4.0" 64 | source = "registry+https://github.com/rust-lang/crates.io-index" 65 | checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" 66 | 67 | [[package]] 68 | name = "libm" 69 | version = "0.1.4" 70 | source = "registry+https://github.com/rust-lang/crates.io-index" 71 | checksum = "7fc7aa29613bd6a620df431842069224d8bc9011086b1db4c0e0cd47fa03ec9a" 72 | 73 | [[package]] 74 | name = "log" 75 | version = "0.4.14" 76 | source = "registry+https://github.com/rust-lang/crates.io-index" 77 | checksum = "51b9bbe6c47d51fc3e1a9b945965946b4c44142ab8792c50835a980d362c2710" 78 | dependencies = [ 79 | "cfg-if", 80 | ] 81 | 82 | [[package]] 83 | name = "md5" 84 | version = "0.7.0" 85 | source = "registry+https://github.com/rust-lang/crates.io-index" 86 | checksum = "490cc448043f947bae3cbee9c203358d62dbee0db12107a74be5c30ccfd09771" 87 | 88 | [[package]] 89 | name = "native-windows-derive" 90 | version = "1.0.4" 91 | source = "registry+https://github.com/rust-lang/crates.io-index" 92 | checksum = "8164b076b08fb63f7cb4da207abe8fd94b5c604e17d9f49618807482dbe25507" 93 | dependencies = [ 94 | "proc-macro-crate", 95 | "proc-macro2", 96 | "quote", 97 | "syn", 98 | ] 99 | 100 | [[package]] 101 | name = "native-windows-gui" 102 | version = "1.0.12" 103 | source = "registry+https://github.com/rust-lang/crates.io-index" 104 | checksum = "c1e049bccae62e28782c5eff82e0c8a4dace50b9783fe03b95447fef3172bb89" 105 | dependencies = [ 106 | "bitflags", 107 | "lazy_static", 108 | "plotters", 109 | "plotters-backend", 110 | "stretch", 111 | "winapi", 112 | "winapi-build", 113 | ] 114 | 115 | [[package]] 116 | name = "num-traits" 117 | version = "0.2.14" 118 | source = "registry+https://github.com/rust-lang/crates.io-index" 119 | checksum = "9a64b1ec5cda2586e284722486d802acf1f7dbdc623e2bfc57e65ca1cd099290" 120 | dependencies = [ 121 | "autocfg", 122 | ] 123 | 124 | [[package]] 125 | name = "pdb" 126 | version = "0.7.0" 127 | source = "registry+https://github.com/rust-lang/crates.io-index" 128 | checksum = "13f4d162ecaaa1467de5afbe62d597757b674b51da8bb4e587430c5fdb2af7aa" 129 | dependencies = [ 130 | "fallible-iterator", 131 | "scroll", 132 | "uuid", 133 | ] 134 | 135 | [[package]] 136 | name = "plotters" 137 | version = "0.3.1" 138 | source = "registry+https://github.com/rust-lang/crates.io-index" 139 | checksum = "32a3fd9ec30b9749ce28cd91f255d569591cdf937fe280c312143e3c4bad6f2a" 140 | dependencies = [ 141 | "num-traits", 142 | "plotters-backend", 143 | "wasm-bindgen", 144 | "web-sys", 145 | ] 146 | 147 | [[package]] 148 | name = "plotters-backend" 149 | version = "0.3.2" 150 | source = "registry+https://github.com/rust-lang/crates.io-index" 151 | checksum = "d88417318da0eaf0fdcdb51a0ee6c3bed624333bff8f946733049380be67ac1c" 152 | 153 | [[package]] 154 | name = "proc-macro-crate" 155 | version = "0.1.5" 156 | source = "registry+https://github.com/rust-lang/crates.io-index" 157 | checksum = "1d6ea3c4595b96363c13943497db34af4460fb474a95c43f4446ad341b8c9785" 158 | dependencies = [ 159 | "toml", 160 | ] 161 | 162 | [[package]] 163 | name = "proc-macro2" 164 | version = "1.0.30" 165 | source = "registry+https://github.com/rust-lang/crates.io-index" 166 | checksum = "edc3358ebc67bc8b7fa0c007f945b0b18226f78437d61bec735a9eb96b61ee70" 167 | dependencies = [ 168 | "unicode-xid", 169 | ] 170 | 171 | [[package]] 172 | name = "quote" 173 | version = "1.0.10" 174 | source = "registry+https://github.com/rust-lang/crates.io-index" 175 | checksum = "38bc8cc6a5f2e3655e0899c1b848643b2562f853f114bfec7be120678e3ace05" 176 | dependencies = [ 177 | "proc-macro2", 178 | ] 179 | 180 | [[package]] 181 | name = "scroll" 182 | version = "0.10.2" 183 | source = "registry+https://github.com/rust-lang/crates.io-index" 184 | checksum = "fda28d4b4830b807a8b43f7b0e6b5df875311b3e7621d84577188c175b6ec1ec" 185 | 186 | [[package]] 187 | name = "serde" 188 | version = "1.0.130" 189 | source = "registry+https://github.com/rust-lang/crates.io-index" 190 | checksum = "f12d06de37cf59146fbdecab66aa99f9fe4f78722e3607577a5375d66bd0c913" 191 | 192 | [[package]] 193 | name = "stretch" 194 | version = "0.3.2" 195 | source = "registry+https://github.com/rust-lang/crates.io-index" 196 | checksum = "7b0dc6d20ce137f302edf90f9cd3d278866fd7fb139efca6f246161222ad6d87" 197 | dependencies = [ 198 | "lazy_static", 199 | "libm", 200 | ] 201 | 202 | [[package]] 203 | name = "syn" 204 | version = "1.0.80" 205 | source = "registry+https://github.com/rust-lang/crates.io-index" 206 | checksum = "d010a1623fbd906d51d650a9916aaefc05ffa0e4053ff7fe601167f3e715d194" 207 | dependencies = [ 208 | "proc-macro2", 209 | "quote", 210 | "unicode-xid", 211 | ] 212 | 213 | [[package]] 214 | name = "toml" 215 | version = "0.5.8" 216 | source = "registry+https://github.com/rust-lang/crates.io-index" 217 | checksum = "a31142970826733df8241ef35dc040ef98c679ab14d7c3e54d827099b3acecaa" 218 | dependencies = [ 219 | "serde", 220 | ] 221 | 222 | [[package]] 223 | name = "unicode-xid" 224 | version = "0.2.2" 225 | source = "registry+https://github.com/rust-lang/crates.io-index" 226 | checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3" 227 | 228 | [[package]] 229 | name = "uuid" 230 | version = "0.8.2" 231 | source = "registry+https://github.com/rust-lang/crates.io-index" 232 | checksum = "bc5cf98d8186244414c848017f0e2676b3fcb46807f6668a97dfe67359a3c4b7" 233 | 234 | [[package]] 235 | name = "wasm-bindgen" 236 | version = "0.2.78" 237 | source = "registry+https://github.com/rust-lang/crates.io-index" 238 | checksum = "632f73e236b219150ea279196e54e610f5dbafa5d61786303d4da54f84e47fce" 239 | dependencies = [ 240 | "cfg-if", 241 | "wasm-bindgen-macro", 242 | ] 243 | 244 | [[package]] 245 | name = "wasm-bindgen-backend" 246 | version = "0.2.78" 247 | source = "registry+https://github.com/rust-lang/crates.io-index" 248 | checksum = "a317bf8f9fba2476b4b2c85ef4c4af8ff39c3c7f0cdfeed4f82c34a880aa837b" 249 | dependencies = [ 250 | "bumpalo", 251 | "lazy_static", 252 | "log", 253 | "proc-macro2", 254 | "quote", 255 | "syn", 256 | "wasm-bindgen-shared", 257 | ] 258 | 259 | [[package]] 260 | name = "wasm-bindgen-macro" 261 | version = "0.2.78" 262 | source = "registry+https://github.com/rust-lang/crates.io-index" 263 | checksum = "d56146e7c495528bf6587663bea13a8eb588d39b36b679d83972e1a2dbbdacf9" 264 | dependencies = [ 265 | "quote", 266 | "wasm-bindgen-macro-support", 267 | ] 268 | 269 | [[package]] 270 | name = "wasm-bindgen-macro-support" 271 | version = "0.2.78" 272 | source = "registry+https://github.com/rust-lang/crates.io-index" 273 | checksum = "7803e0eea25835f8abdc585cd3021b3deb11543c6fe226dcd30b228857c5c5ab" 274 | dependencies = [ 275 | "proc-macro2", 276 | "quote", 277 | "syn", 278 | "wasm-bindgen-backend", 279 | "wasm-bindgen-shared", 280 | ] 281 | 282 | [[package]] 283 | name = "wasm-bindgen-shared" 284 | version = "0.2.78" 285 | source = "registry+https://github.com/rust-lang/crates.io-index" 286 | checksum = "0237232789cf037d5480773fe568aac745bfe2afbc11a863e97901780a6b47cc" 287 | 288 | [[package]] 289 | name = "web-sys" 290 | version = "0.3.55" 291 | source = "registry+https://github.com/rust-lang/crates.io-index" 292 | checksum = "38eb105f1c59d9eaa6b5cdc92b859d85b926e82cb2e0945cd0c9259faa6fe9fb" 293 | dependencies = [ 294 | "js-sys", 295 | "wasm-bindgen", 296 | ] 297 | 298 | [[package]] 299 | name = "winapi" 300 | version = "0.3.9" 301 | source = "registry+https://github.com/rust-lang/crates.io-index" 302 | checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" 303 | dependencies = [ 304 | "winapi-i686-pc-windows-gnu", 305 | "winapi-x86_64-pc-windows-gnu", 306 | ] 307 | 308 | [[package]] 309 | name = "winapi-build" 310 | version = "0.1.1" 311 | source = "registry+https://github.com/rust-lang/crates.io-index" 312 | checksum = "2d315eee3b34aca4797b2da6b13ed88266e6d612562a0c46390af8299fc699bc" 313 | 314 | [[package]] 315 | name = "winapi-i686-pc-windows-gnu" 316 | version = "0.4.0" 317 | source = "registry+https://github.com/rust-lang/crates.io-index" 318 | checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" 319 | 320 | [[package]] 321 | name = "winapi-x86_64-pc-windows-gnu" 322 | version = "0.4.0" 323 | source = "registry+https://github.com/rust-lang/crates.io-index" 324 | checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" 325 | --------------------------------------------------------------------------------