├── .gitignore ├── Cargo.toml ├── LICENSE ├── README.md └── src ├── lib.rs └── logger.rs /.gitignore: -------------------------------------------------------------------------------- 1 | # Generated by Cargo 2 | # will have compiled files and executables 3 | debug/ 4 | target/ 5 | 6 | # Remove Cargo.lock from gitignore if creating an executable, leave it for libraries 7 | # More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html 8 | Cargo.lock 9 | 10 | # These are backup files generated by rustfmt 11 | **/*.rs.bk 12 | 13 | # MSVC Windows builds of rustc generate these, which store debugging information 14 | *.pdb 15 | 16 | # RustRover 17 | # JetBrains specific template is maintained in a separate JetBrains.gitignore that can 18 | # be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore 19 | # and can be added to the global gitignore or merged into this file. For a more nuclear 20 | # option (not recommended) you can uncomment the following to ignore the entire idea folder. 21 | #.idea/ -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "vmw-logger" 3 | version = "0.1.2" 4 | edition = "2021" 5 | description = "A VMWare logger using built-in backdoor." 6 | readme = "README.md" 7 | repository = "https://github.com/Azvanzed/vmw-logger" 8 | license-file = "LICENSE" 9 | 10 | [dependencies] 11 | log = "0.4.22" 12 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024 wcscpy 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. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # vmw-logger-rs 2 | A VMware logger rust crate that leverages an undocumented built-in backdoor for guest-host interaction with no_std support. 3 | 4 | ### How to enable 5 | To enable this functionality, add the following lines to the *.VMX file: 6 | ``` 7 | replay.enableBackdoorPutChar = "TRUE" 8 | log.guestThrottleBytesPerSec = "100000000" 9 | log.throttleBytesPerSec = "100000000" 10 | log.append = "FALSE" 11 | log.keep = "FALSE" 12 | ``` 13 | 14 | ### Credits 15 | - jessie (intege_rs) 16 | - YushiOMOTE 17 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | 3 | mod logger; 4 | 5 | pub use crate::logger::{builder, init, init_with_filter, Builder}; -------------------------------------------------------------------------------- /src/logger.rs: -------------------------------------------------------------------------------- 1 | static LOGGER: VMWLogger = VMWLogger::new(); 2 | 3 | fn default_formatter(buffer: &mut dyn core::fmt::Write, record: &log::Record) -> Result<(), core::fmt::Error> { 4 | writeln!( 5 | buffer, 6 | "[{}:{} - {}] {}", 7 | record.file().unwrap_or(""), 8 | record.line().unwrap_or(0), 9 | record.level(), 10 | record.args(), 11 | ) 12 | } 13 | 14 | struct VMWLogger; 15 | 16 | impl VMWLogger { 17 | const fn new() -> Self { 18 | Self 19 | } 20 | } 21 | 22 | impl core::fmt::Write for VMWLogger { 23 | fn write_str(&mut self, s: &str) -> core::fmt::Result { 24 | // credits: jessie 25 | for c in s.chars() { 26 | unsafe { 27 | core::arch::asm!(r#" 28 | xchg rbx, {0} 29 | mov eax, 0x564D5868 30 | mov edx, 0x5658 31 | mov ecx, 0x19 32 | in eax, dx 33 | xchg rbx, {0} 34 | "#, 35 | // input is rbx, but llvm is annoying so 36 | // we just use a temp register and xchg them 37 | in(reg) c as usize, 38 | out("rax") _, 39 | out("rdx") _, 40 | out("rcx") _, 41 | ); 42 | } 43 | } 44 | 45 | Ok(()) 46 | } 47 | } 48 | 49 | impl log::Log for VMWLogger { 50 | fn enabled(&self, _m: &log::Metadata) -> bool { 51 | // credits: jessie 52 | unsafe { 53 | // invokes the 'get_version' command of the vmware backdoor 54 | // if ebx is not the magic value the backdoor isn't present 55 | let mut rbx: u64 = 0; 56 | core::arch::asm!(r#" 57 | xchg rbx, {_rbx} 58 | xchg {_rbp}, rbp 59 | xor rbp, rbp 60 | mov eax, 0x564D5868 61 | mov edx, 0x5658 62 | mov ecx, 0xA 63 | in eax, dx 64 | xchg rbp, {_rbp} 65 | xchg rbx, {_rbx} 66 | "#, 67 | in("rax") 0x564D5868, 68 | _rbx = inout(reg) rbx, 69 | in("rcx") 0xA, 70 | in("rdx") 0x5658, 71 | _rbp = in(reg) 0u64, 72 | in("rdi") 0, 73 | in("rsi") 0, 74 | ); 75 | rbx == 0x564D5868 76 | } 77 | } 78 | 79 | fn log(&self, record: &log::Record) { 80 | let _ = default_formatter(&mut VMWLogger::new(), record); // lol 81 | } 82 | 83 | fn flush(&self) {} 84 | } 85 | 86 | pub struct Builder { 87 | filter: log::LevelFilter, 88 | } 89 | 90 | impl Builder { 91 | /// Create a builder for fine-tuning logger. 92 | pub fn new() -> Self { 93 | Self { 94 | filter: log::LevelFilter::Info, 95 | } 96 | } 97 | 98 | /// Set the level filter. 99 | pub fn filter(mut self, filter: log::LevelFilter) -> Self { 100 | self.filter = filter; 101 | self 102 | } 103 | 104 | /// Setup a logger based on the configuration. 105 | pub fn setup(self) { 106 | log::set_logger(&LOGGER).unwrap(); 107 | 108 | log::set_max_level(self.filter); 109 | } 110 | } 111 | 112 | pub fn builder() -> Builder { 113 | Builder::new() 114 | } 115 | 116 | pub fn init() { 117 | builder().filter(log::LevelFilter::Info).setup(); 118 | } 119 | 120 | pub fn init_with_filter(filter: log::LevelFilter) { 121 | builder().filter(filter).setup(); 122 | } 123 | --------------------------------------------------------------------------------