├── src ├── offsets.rs ├── util.rs ├── lib.rs ├── offset-server.rs ├── error.rs ├── db.rs ├── map.rs ├── primitives.rs ├── server.rs └── pe.rs ├── test ├── Foo.dll ├── tls.exe ├── x86.exe ├── baddos.exe ├── badmod.exe ├── basic.exe ├── badentry.exe ├── badproc.exe ├── badsection.exe ├── server.pem ├── server.key └── ca.pem ├── .gitignore ├── proto └── mmap.proto ├── examples ├── print_return_no_server.rs ├── print_return.rs ├── print_return_tls.rs └── print_return_with_dll.rs ├── LICENSE ├── Cargo.toml ├── README.md └── Cargo.lock /src/offsets.rs: -------------------------------------------------------------------------------- 1 | tonic::include_proto!("mmap"); 2 | -------------------------------------------------------------------------------- /test/Foo.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MrElectrify/mmap-loader-rs/HEAD/test/Foo.dll -------------------------------------------------------------------------------- /test/tls.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MrElectrify/mmap-loader-rs/HEAD/test/tls.exe -------------------------------------------------------------------------------- /test/x86.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MrElectrify/mmap-loader-rs/HEAD/test/x86.exe -------------------------------------------------------------------------------- /test/baddos.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MrElectrify/mmap-loader-rs/HEAD/test/baddos.exe -------------------------------------------------------------------------------- /test/badmod.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MrElectrify/mmap-loader-rs/HEAD/test/badmod.exe -------------------------------------------------------------------------------- /test/basic.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MrElectrify/mmap-loader-rs/HEAD/test/basic.exe -------------------------------------------------------------------------------- /test/badentry.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MrElectrify/mmap-loader-rs/HEAD/test/badentry.exe -------------------------------------------------------------------------------- /test/badproc.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MrElectrify/mmap-loader-rs/HEAD/test/badproc.exe -------------------------------------------------------------------------------- /test/badsection.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MrElectrify/mmap-loader-rs/HEAD/test/badsection.exe -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | src/main.rs 2 | .git/ 3 | .vscode/ 4 | target/ 5 | test/cache.json 6 | test/tls_cache.json 7 | .idea/ -------------------------------------------------------------------------------- /src/util.rs: -------------------------------------------------------------------------------- 1 | pub fn to_wide(string: &str) -> Vec { 2 | string.encode_utf16().chain(std::iter::once(0)).collect() 3 | } 4 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | mod db; 2 | /// All non-OS error codes that can be emitted upon loading a module 3 | pub mod error; 4 | #[cfg(target_os = "windows")] 5 | mod map; 6 | mod offsets; 7 | #[cfg(target_os = "windows")] 8 | /// The portable executable mapper 9 | pub mod pe; 10 | #[cfg(target_os = "windows")] 11 | mod primitives; 12 | /// The necessary parts to run a PDB offset server 13 | pub mod server; 14 | #[cfg(target_os = "windows")] 15 | mod util; 16 | 17 | #[cfg(target_os = "windows")] 18 | /// Exported for simple uses 19 | pub use error::Error; 20 | pub use pe::{NtContext, PortableExecutable}; 21 | -------------------------------------------------------------------------------- /proto/mmap.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | package mmap; 3 | 4 | service Offset { 5 | rpc GetOffsets (OffsetsRequest) returns (OffsetsResponse); 6 | } 7 | 8 | message OffsetsRequest { 9 | string ntdll_hash = 1; 10 | } 11 | 12 | message OffsetsResponse { 13 | uint32 LdrpHashTable = 1; 14 | uint32 LdrpModuleDatatableLock = 2; 15 | uint32 LdrpHandleTlsData = 3; 16 | uint32 LdrpReleaseTlsEntry = 4; 17 | uint32 LdrpMappingInfoIndex = 5; 18 | uint32 LdrpModuleBaseAddressIndex = 6; 19 | uint32 RtlInitializeHistoryTable = 7; 20 | uint32 RtlInsertInvertedFunctionTable = 8; 21 | } 22 | -------------------------------------------------------------------------------- /examples/print_return_no_server.rs: -------------------------------------------------------------------------------- 1 | /// Usage: print_return_no_server 2 | /// Description: Prints the executable return value. 3 | /// Includes the handler 4 | use mmap_loader::{ 5 | pe::{NtContext, PortableExecutable}, 6 | server::OffsetHandler, 7 | }; 8 | use std::env; 9 | 10 | #[tokio::main] 11 | async fn main() -> anyhow::Result<()> { 12 | // parse arguments 13 | let args: Vec = env::args().collect(); 14 | if args.len() != 2 { 15 | eprintln!("Usage: {} ", args[0]); 16 | return Ok(()); 17 | } 18 | // create the local handler 19 | let handler = OffsetHandler::new("test/cache.json")?; 20 | // fetch nt functions and constants 21 | let nt_ctx = NtContext::resolve_local(&handler).await?; 22 | // map the executable 23 | let mut executable = PortableExecutable::load(&args[1], nt_ctx)?; 24 | println!("Result: {}", unsafe { executable.run() }); 25 | Ok(()) 26 | } 27 | -------------------------------------------------------------------------------- /examples/print_return.rs: -------------------------------------------------------------------------------- 1 | /// Usage: print_return 2 | /// 3 | /// Description: Prints the executable return value. 4 | /// Requires a separate remote PDB server 5 | use mmap_loader::pe::{NtContext, PortableExecutable}; 6 | use std::env; 7 | 8 | #[tokio::main] 9 | async fn main() -> anyhow::Result<()> { 10 | // parse arguments 11 | let args: Vec = env::args().collect(); 12 | if args.len() < 2 || args.len() > 4 { 13 | eprintln!( 14 | "Usage: {} ", 15 | args[0] 16 | ); 17 | return Ok(()); 18 | } 19 | let host = args.get(2).map(String::as_str).unwrap_or("localhost"); 20 | let port = args.get(3).map(String::as_str).unwrap_or("42220").parse()?; 21 | // fetch nt functions and constants 22 | let nt_ctx = NtContext::resolve(host, port).await?; 23 | // map the executable 24 | let mut executable = PortableExecutable::load(&args[1], nt_ctx)?; 25 | println!("Result: {}", unsafe { executable.run() }); 26 | Ok(()) 27 | } 28 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Andrew Buck 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 | -------------------------------------------------------------------------------- /examples/print_return_tls.rs: -------------------------------------------------------------------------------- 1 | /// Usage: print_return 2 | /// 3 | /// Description: Prints the executable return value. 4 | /// Requires a separate remote PDB server 5 | use mmap_loader::pe::{NtContext, PortableExecutable}; 6 | use std::env; 7 | use tonic::transport::Certificate; 8 | 9 | #[tokio::main] 10 | async fn main() -> anyhow::Result<()> { 11 | // parse arguments 12 | let args: Vec = env::args().collect(); 13 | if args.len() < 2 || args.len() > 5 || args[1] == "-help" { 14 | eprintln!( 15 | "Usage: {} >", 16 | args[0] 17 | ); 18 | return Ok(()); 19 | } 20 | let host = args.get(2).map(String::as_str).unwrap_or("localhost"); 21 | let port = args.get(3).map(String::as_str).unwrap_or("443").parse()?; 22 | // load the cert file 23 | let ca_cert = match args.len() { 24 | 5 => Some(Certificate::from_pem(tokio::fs::read(&args[4]).await?)), 25 | _ => None, 26 | }; 27 | // fetch nt functions and constants 28 | let nt_ctx = NtContext::resolve_tls(host, port, ca_cert, None).await?; 29 | // map the executable 30 | let mut executable = PortableExecutable::load(&args[1], nt_ctx)?; 31 | println!("Result: {}", unsafe { executable.run() }); 32 | Ok(()) 33 | } 34 | -------------------------------------------------------------------------------- /src/offset-server.rs: -------------------------------------------------------------------------------- 1 | use std::{env, net::SocketAddr}; 2 | 3 | use mmap_loader::server::Server; 4 | use tonic::transport::Identity; 5 | 6 | #[tokio::main] 7 | async fn main() -> anyhow::Result<()> { 8 | let args: Vec = env::args().collect(); 9 | // they must specify both cert and key if they want TLS 10 | if args.len() > 6 || args.len() == 5 || args.get(1).map(|arg| arg.contains("-help")).unwrap_or(false) { 11 | eprintln!( 12 | "Usage: {} > >", 13 | args[0] 14 | ); 15 | return Ok(()); 16 | } 17 | let addr = args 18 | .get(1) 19 | .map(|str| str.as_str()) 20 | .unwrap_or("0.0.0.0") 21 | .parse()?; 22 | let port = args 23 | .get(2) 24 | .map(|str| str.as_str()) 25 | .unwrap_or("443") 26 | .parse()?; 27 | let cache_path = args.get(3).map(|str| str.as_str()).unwrap_or("cache.json"); 28 | let tls_identity = match args.len() { 29 | 6 => { 30 | let cert = tokio::fs::read(&args[4]).await?; 31 | let key = tokio::fs::read(&args[5]).await?; 32 | Some(Identity::from_pem(cert, key)) 33 | } 34 | _ => None, 35 | }; 36 | let server = Server::new(SocketAddr::new(addr, port), cache_path, tls_identity)?; 37 | server.run().await?; 38 | Ok(()) 39 | } 40 | -------------------------------------------------------------------------------- /examples/print_return_with_dll.rs: -------------------------------------------------------------------------------- 1 | /// Usage: print_return_with_dll 2 | /// 3 | /// Description: Prints the executable return value, after 4 | /// loading a DLL before the executable begins executing. 5 | /// Requires a separate remote PDB server 6 | use mmap_loader::pe::{NtContext, PortableExecutable}; 7 | use std::env; 8 | 9 | #[tokio::main] 10 | async fn main() -> anyhow::Result<()> { 11 | // parse arguments 12 | let args: Vec = env::args().collect(); 13 | if args.len() < 2 || args.len() > 4 { 14 | eprintln!( 15 | "Usage: {} ", 16 | args[0] 17 | ); 18 | return Ok(()); 19 | } 20 | let host = args.get(3).map(String::as_str).unwrap_or("localhost"); 21 | let port = args.get(4).map(String::as_str).unwrap_or("42220").parse()?; 22 | // fetch nt functions and constants 23 | let nt_ctx = NtContext::resolve(host, port).await?; 24 | // map the executable 25 | let mut executable = PortableExecutable::load(&args[1], nt_ctx.clone())?; 26 | // map the dll 27 | let mut dll = PortableExecutable::load(&args[2], nt_ctx)?; 28 | // run DllMain first 29 | if unsafe { dll.run() } == false as u8 { 30 | eprintln!("DllMain returned false!"); 31 | return Ok(()); 32 | } 33 | // now run the executable 34 | println!("Result: {}", unsafe { executable.run() }); 35 | Ok(()) 36 | } 37 | -------------------------------------------------------------------------------- /src/error.rs: -------------------------------------------------------------------------------- 1 | #![allow(clippy::nonstandard_macro_braces)] 2 | 3 | #[derive(Debug, thiserror::Error, PartialEq)] 4 | /// An error code associated with loading a PE file 5 | pub enum Error { 6 | #[error("The DOS header was out of bounds")] 7 | DOSOutOfBounds, 8 | #[error("The NT header was out of bounds")] 9 | NTOutOfBounds, 10 | #[error("The architecture was unsupported. Only ARM64/x86-64 is supported")] 11 | UnsupportedArch, 12 | #[error("A section header was out of bounds")] 13 | SectOutOfBounds, 14 | #[error("The entry point was out of bounds")] 15 | EPOutOfBounds, 16 | #[error("The import descriptor was out of bounds")] 17 | IDOutOfBounds, 18 | #[error("The IAT library name was out of bounds")] 19 | LibNameOutOfBounds, 20 | #[error("The IAT thunk was out of bounds")] 21 | IATOutOfBounds, 22 | #[error("The procedure name was out of bounds")] 23 | ProcNameOutOfBounds, 24 | #[error("The TLS directory was out of bounds")] 25 | TLSOutOfBounds, 26 | #[error("The TLS callback was out of bounds")] 27 | CallbackOutOfBounds, 28 | #[error("The exception handler table was out of bounds")] 29 | ExceptionTableOutOfBounds, 30 | #[error("The system failed to handle TLS data")] 31 | TLSData, 32 | #[error("The exception table entry failed")] 33 | ExceptionTableEntry, 34 | #[error("The procedure name was null")] 35 | NullProcName, 36 | #[error("NTDLL was not loaded")] 37 | NtDllNotLoaded, 38 | #[error("NTDLL debug info was missing")] 39 | NtDllDebugType, 40 | #[error("NTDLL RSDS signature was missing")] 41 | NtDllRsdsSig, 42 | #[error("The loader entry failed to initialize")] 43 | LdrEntry, 44 | } 45 | -------------------------------------------------------------------------------- /test/server.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIEmDCCAwCgAwIBAgIQVEJFCgU/CZk9JEwTucWPpzANBgkqhkiG9w0BAQsFADCB 3 | hTEeMBwGA1UEChMVbWtjZXJ0IGRldmVsb3BtZW50IENBMS0wKwYDVQQLDCRsdWNp 4 | b0BMdWNpb3MtV29yay1NQlAgKEx1Y2lvIEZyYW5jbykxNDAyBgNVBAMMK21rY2Vy 5 | dCBsdWNpb0BMdWNpb3MtV29yay1NQlAgKEx1Y2lvIEZyYW5jbykwHhcNMTkwNjAx 6 | MDAwMDAwWhcNMjkwOTI5MjMzNTM0WjBYMScwJQYDVQQKEx5ta2NlcnQgZGV2ZWxv 7 | cG1lbnQgY2VydGlmaWNhdGUxLTArBgNVBAsMJGx1Y2lvQEx1Y2lvcy1Xb3JrLU1C 8 | UCAoTHVjaW8gRnJhbmNvKTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEB 9 | APKm1szJhbO2C1q3UxcszMiRpB155VuzVj+oKkB3Bl79jlmlvOMYiMLFsHq81FKc 10 | RtgqnK0oc/Nh8zvx3wrxs++mepciJNcVPQlCXDU4csLV+vh7n3wYm6YCakxgWu2q 11 | /R9uZQVO5AZsfF44fa+OLRu+vc0pm5sx+QRk/dfiw1dehci5bYtpLw4wYqglp4IG 12 | 91uzMSJIaP+/0GOkzTItMfz000xfVP4W1Mqe5dqDmDumvqXMmPzf4zMAbLic6Ox5 13 | K5SKa6LIb3T8Lm0DnALRCHlFsOeQViELjgrGh/+Lr6Od9q14g82IgjBBX7Z/ob9o 14 | rWGCN6RPXSb96hFgNN/57lkCAwEAAaOBrzCBrDAOBgNVHQ8BAf8EBAMCBaAwEwYD 15 | VR0lBAwwCgYIKwYBBQUHAwEwDAYDVR0TAQH/BAIwADAfBgNVHSMEGDAWgBQdvlE4 16 | Bdcsjc9oaxjDCRu5FiuZkzBWBgNVHREETzBNggtleGFtcGxlLmNvbYINKi5leGFt 17 | cGxlLmNvbYIMZXhhbXBsZS50ZXN0gglsb2NhbGhvc3SHBH8AAAGHEAAAAAAAAAAA 18 | AAAAAAAAAAEwDQYJKoZIhvcNAQELBQADggGBAKb2TJ8l+e1eraNwZWizLw5fccAf 19 | y59J1JAWdLxZyAI/bkiTlVO3DQoPZpw7XwLhefCvILkwKAL4TtIGGVC9yTb5Q5eg 20 | rqGO3FC0yg1fn65Kf1VpVxxUVyoiM5PQ4pFJb4AicAv88rCOLD9FFuE0PKOKU/dm 21 | Tw0WgPStoh9wsJ1RXUuTJYZs1nd1kMBlfv9NbLilnL+cR2sLktS54X5XagsBYVlf 22 | oapRb0JtABOoQhX3U8QMq8UF8yzceRHNTN9yfLOUrW26s9nKtlWVniNhw1uPxZw9 23 | RHM7w9/4+a9LXtEDYg4IP/1mm0ywBoUqy1O6hA73uId+Yi/kFBks/GyYaGjKgYcO 24 | 23B75tkPGYEdGuGZYLzZNHbXg4V0UxFQG3KA1pUiSnD3bN2Rxs+CMpzORnOeK3xi 25 | EooKgAPYsehItoQOMPpccI2xHdSAMWtwUgOKrefUQujkx2Op+KFlspF0+WJ6AZEe 26 | 2D4hyWaEZsvvILXapwqHDCuN3/jSUlTIqUoE1w== 27 | -----END CERTIFICATE----- -------------------------------------------------------------------------------- /test/server.key: -------------------------------------------------------------------------------- 1 | -----BEGIN PRIVATE KEY----- 2 | MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDyptbMyYWztgta 3 | t1MXLMzIkaQdeeVbs1Y/qCpAdwZe/Y5ZpbzjGIjCxbB6vNRSnEbYKpytKHPzYfM7 4 | 8d8K8bPvpnqXIiTXFT0JQlw1OHLC1fr4e598GJumAmpMYFrtqv0fbmUFTuQGbHxe 5 | OH2vji0bvr3NKZubMfkEZP3X4sNXXoXIuW2LaS8OMGKoJaeCBvdbszEiSGj/v9Bj 6 | pM0yLTH89NNMX1T+FtTKnuXag5g7pr6lzJj83+MzAGy4nOjseSuUimuiyG90/C5t 7 | A5wC0Qh5RbDnkFYhC44Kxof/i6+jnfateIPNiIIwQV+2f6G/aK1hgjekT10m/eoR 8 | YDTf+e5ZAgMBAAECggEACODt7yRYjhDVLYaTtb9f5t7dYG67Y7WWLFIc6arxQryI 9 | XuNfm/ej2WyeXn9WTYeGWBaHERbv1zH4UnMxNBdP/C7dQXZwXqZaS2JwOUpNeK+X 10 | tUvgtAu6dkKUXSMRcKzXAjVp4N3YHhwOGOx8PNY49FDwZPdmyDD16aFAYIvdle6/ 11 | PSMrj38rB1sbQQdmRob2FjJBSDZ44nsr+/nilrcOFNfNnWv7tQIWYVXNcLfdK/WJ 12 | ZCDFhA8lr/Yon6MEq6ApTj2ZYRRGXPd6UeASJkmTZEUIUbeDcje/MO8cHkREpuRH 13 | wm3pCjR7OdO4vc+/d/QmEvu5ns6wbTauelYnL616YQKBgQD414gJtpCHauNEUlFB 14 | v/R3DzPI5NGp9PAqovOD8nCbI49Mw61gP/ExTIPKiR5uUX/5EL04uspaNkuohXk+ 15 | ys0G5At0NfV7W39lzhvALEaSfleybvYxppbBrc20/q8Gvi/i30NY+1LM3RdtMiEw 16 | hKHjU0SnFhJq0InFg3AO/iCeTQKBgQD5obkbzpOidSsa55aNsUlO2qjiUY9leq9b 17 | irAohIZ8YnuuixYvkOeSeSz1eIrA4tECeAFSgTZxYe1Iz+USru2Xg/0xNte11dJD 18 | rBoH/yMn2gDvBK7xQ6uFMPTeYtKG0vfvpXZYSWZzGntyrHTwFk6UV+xdrt9MBdd1 19 | XdSn7bwOPQKBgC9VQAko8uDvUf+C8PXiv2uONrl13PPJJY3WpR9qFEVOREnDxszS 20 | HNzVwxPZdTJiykbkCjoqPadfQJDzopZxGQLAifU29lTamKcSx3CMe3gOFDxaovXa 21 | zD5XAxP0hfJwZsdu1G6uj5dsTrJ0oJ+L+wc0pZBqwGIU/L/XOo9/g1DZAoGAUebL 22 | kuH98ik7EUK2VJq8EJERI9/ailLsQb6I+WIxtZGiPqwHhWencpkrNQZtj8dbB9JT 23 | rLwUHrMgZOlAoRafgTyez4zMzS3wJJ/Mkp8U67hM4h7JPwMSvUpIrMYDiJSjIA9L 24 | er/qSw1/Pypx22uWMHmAZWRAgvLPtAQrB0Wqk4kCgYEAr2H1PvfbwZwkSvlMt5o8 25 | WLnBbxcM3AKglLRbkShxxgiZYdEP71/uOtRMiL26du5XX8evItITN0DsvmXL/kcd 26 | h29LK7LM5uLw7efz0Qxs03G6kEyIHVkacowHi5I5Ul1qI61SoV3yMB1TjIU+bXZt 27 | 0ZjC07totO0fqPOLQxonjQg= 28 | -----END PRIVATE KEY----- -------------------------------------------------------------------------------- /test/ca.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIE3DCCA0SgAwIBAgIRAObeYbJFiVQSGR8yk44dsOYwDQYJKoZIhvcNAQELBQAw 3 | gYUxHjAcBgNVBAoTFW1rY2VydCBkZXZlbG9wbWVudCBDQTEtMCsGA1UECwwkbHVj 4 | aW9ATHVjaW9zLVdvcmstTUJQIChMdWNpbyBGcmFuY28pMTQwMgYDVQQDDCtta2Nl 5 | cnQgbHVjaW9ATHVjaW9zLVdvcmstTUJQIChMdWNpbyBGcmFuY28pMB4XDTE5MDky 6 | OTIzMzUzM1oXDTI5MDkyOTIzMzUzM1owgYUxHjAcBgNVBAoTFW1rY2VydCBkZXZl 7 | bG9wbWVudCBDQTEtMCsGA1UECwwkbHVjaW9ATHVjaW9zLVdvcmstTUJQIChMdWNp 8 | byBGcmFuY28pMTQwMgYDVQQDDCtta2NlcnQgbHVjaW9ATHVjaW9zLVdvcmstTUJQ 9 | IChMdWNpbyBGcmFuY28pMIIBojANBgkqhkiG9w0BAQEFAAOCAY8AMIIBigKCAYEA 10 | y/vE61ItbN/1qMYt13LMf+le1svwfkCCOPsygk7nWeRXmomgUpymqn1LnWiuB0+e 11 | 4IdVH2f5E9DknWEpPhKIDMRTCbz4jTwQfHrxCb8EGj3I8oO73pJO5S/xCedM9OrZ 12 | qWcYWwN0GQ8cO/ogazaoZf1uTrRNHyzRyQsKyb412kDBTNEeldJZ2ljKgXXvh4HO 13 | 2ZIk9K/ZAaAf6VN8K/89rlJ9/KPgRVNsyAapE+Pb8XXKtpzeFiEcUfuXVYWtkoW+ 14 | xyn/Zu8A1L2CXMQ1sARh7P/42BTMKr5pfraYgcBGxKXLrxoySpxCO9KqeVveKy1q 15 | fPm5FCwFsXDr0koFLrCiR58mcIO/04Q9DKKTV4Z2a+LoqDJRY37KfBSc8sDMPhw5 16 | k7g3WPoa6QwXRjZTCA5fHWVgLOtcwLsnju5tBE4LDxwF6s+1wPF8NI5yUfufcEjJ 17 | Z6JBwgoWYosVj27Lx7KBNLU/57PX9ryee691zmtswt0tP0WVBAgalhYWg99RXoa3 18 | AgMBAAGjRTBDMA4GA1UdDwEB/wQEAwICBDASBgNVHRMBAf8ECDAGAQH/AgEAMB0G 19 | A1UdDgQWBBQdvlE4Bdcsjc9oaxjDCRu5FiuZkzANBgkqhkiG9w0BAQsFAAOCAYEA 20 | BP/6o1kPINksMJZSSXgNCPZskDLyGw7auUZBnQ0ocDT3W6gXQvT/27LM1Hxoj9Eh 21 | qU1TYdEt7ppecLQSGvzQ02MExG7H75art75oLiB+A5agDira937YbK4MCjqW481d 22 | bDhw6ixJnY1jIvwjEZxyH6g94YyL927aSPch51fys0kSnjkFzC2RmuzDADScc4XH 23 | 5P1+/3dnIm3M5yfpeUzoaOrTXNmhn8p0RDIGrZ5kA5eISIGGD3Mm8FDssUNKndtO 24 | g4ojHUsxb14icnAYGeye1NOhGiqN6TEFcgr6MPd0XdFNZ5c0HUaBCfN6bc+JxDV5 25 | MKZVJdNeJsYYwilgJNHAyZgCi30JC20xeYVtTF7CEEsMrFDGJ70Kz7o/FnRiFsA1 26 | ZSwVVWhhkHG2VkT4vlo0O3fYeZpenYicvy+wZNTbGK83gzHWqxxNC1z3Etg5+HRJ 27 | F9qeMWPyfA3IHYXygiMcviyLcyNGG/SJ0EhUpYBN/Gg7wI5yFkcsxUDPPzd23O0M 28 | -----END CERTIFICATE----- -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "mmap-loader" 3 | authors = ["Andrew Buck"] 4 | description = "A Portable Executable manual map loader" 5 | documentation = "https://docs.rs/crate/mmap-loader" 6 | version = "0.5.3" 7 | edition = "2021" 8 | license-file = "LICENSE" 9 | readme = "README.md" 10 | repository = "https://github.com/MrElectrify/mmap-loader-rs" 11 | keywords = ["pe", "map", "manual-map"] 12 | 13 | [features] 14 | server = [] 15 | 16 | [[bin]] 17 | name = "mmap-offset-server" 18 | path = "src/offset-server.rs" 19 | required-features = ["server"] 20 | 21 | [profile.release] 22 | codegen-units = 1 23 | panic = "abort" 24 | lto = true 25 | 26 | [[example]] 27 | name = "print_return" 28 | path = "examples/print_return.rs" 29 | 30 | [[example]] 31 | name = "print_return_tls" 32 | path = "examples/print_return_tls.rs" 33 | 34 | [[example]] 35 | name = "print_return_with_dll" 36 | path = "examples/print_return_with_dll.rs" 37 | 38 | [[example]] 39 | name = "print_return_no_server" 40 | path = "examples/print_return_no_server.rs" 41 | 42 | [build-dependencies] 43 | tonic-build = { version = "0.11", features = ["prost"] } 44 | 45 | [dependencies] 46 | anyhow = "1" 47 | lazy_static = "1.4" 48 | log = { version = "0.4", features = [ 49 | "max_level_debug", 50 | "release_max_level_warn", 51 | ] } 52 | memoffset = "0.9" 53 | ntapi = "0.4" 54 | pdb = "0.8" 55 | prost = "0.12" 56 | reqwest = "0.12" 57 | serde = "1.0" 58 | serde_derive = "1.0" 59 | serde_json = "1.0" 60 | simple_logger = { version = "4.3" } 61 | thiserror = "1.0" 62 | tokio = { version = "1.37", features = ["fs", "macros", "rt-multi-thread"] } 63 | tonic = { version = "0.11", features = ["tls", "tls-roots"] } 64 | winapi = { version = "0.3", features = ["impl-default", "fileapi", "guiddef", "handleapi", "memoryapi", "std", "winbase", "ntdef", "ntstatus", "libloaderapi", "winnt", "winerror"] } 65 | 66 | [dev-dependencies] 67 | serial_test = "3.0" 68 | 69 | [package.metadata.docs.rs] 70 | features = ["server"] 71 | targets = ["x86_64-pc-windows-gnu", "x86_64-unknown-linux-gnu"] 72 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # mmap-loader-rs 2 | A Windows Portable Executable Manual Map Loader that supports both executable and DLL types. Written in Rust 3 | 4 | [Crate](https://crates.io/crates/mmap-loader) | [Documentation (excl TLS)](https://docs.rs/crate/mmap-loader) 5 | 6 | ## Requirements 7 | - A 1.77.1+ stable compiler. 8 | 9 | ## Features 10 | - Supports both DLL and EXE types 11 | - Remote or local PDB server for parsing out internal Windows functions 12 | - Supports C++ exceptions, vectored exception handling, and structured exception handling 13 | - Adds the entry to the loader structures, allowing support for functions such as `GetModuleHandle`, `GetModuleFileName`, and more 14 | - Allows for a module to be treated as a primary, resulting in calls to `GetModuleHandle(null)` to resolve to the mapped module 15 | - Unmaps the file and cleans up OS structures upon destruction for ephemeral executables 16 | - MSVC recognizes mapped executables and debugging of children is fully supported with symbols 17 | - Supports lazy execution, where multiple PE files can be loaded before any are executed 18 | - Returns control flow to the calling function after execution is complete 19 | 20 | ## Feature Flags 21 | - `server` 22 | - enables a separate server component 23 | - `tls` 24 | - enables tls support for the client and server 25 | 26 | ## Support 27 | Let me know if something doesn't work by opening an issue. It has only been tested on Windows 10 20H2, and likely won't work on Windows 7 and prior. To see if it works on your system, run `cargo test` 28 | 29 | ## Known Limitations 30 | - Docs.rs building with TLS support is broken due to [this issue](https://github.com/briansmith/ring/issues/1379). Build docs locally with a compliant C compiler using `cargo doc` to view full documentation 31 | - 32-bit environments are not supported 32 | - `GetModuleInformation` and related functions will not find the loaded module. This is because the linked lists that are used to find the module for these functions are sanity checked and protected by the kernel, and the first access after modifying these structures would result in a fatal OS exception. A suggested alternative is to use `VirtualQuery` to get the size of allocation 33 | 34 | ## Usage 35 | Check out the [examples](examples/) 36 | -------------------------------------------------------------------------------- /src/db.rs: -------------------------------------------------------------------------------- 1 | use crate::offsets::OffsetsResponse; 2 | use serde_derive::{Deserialize, Serialize}; 3 | use std::collections::HashMap; 4 | 5 | #[derive(Serialize, Deserialize, Clone, Copy, Debug)] 6 | pub struct Offsets { 7 | pub ldrp_hash_table: u32, 8 | pub ldrp_module_datatable_lock: u32, 9 | pub ldrp_handle_tls_data: u32, 10 | pub ldrp_release_tls_entry: u32, 11 | pub ldrp_mapping_info_index: u32, 12 | pub ldrp_module_base_address_index: u32, 13 | pub rtl_initialize_history_table: u32, 14 | pub rtl_insert_inverted_function_table: u32 15 | } 16 | 17 | impl From for Offsets { 18 | fn from(off: OffsetsResponse) -> Offsets { 19 | Offsets { 20 | ldrp_hash_table: off.ldrp_hash_table, 21 | ldrp_module_datatable_lock: off.ldrp_module_datatable_lock, 22 | ldrp_handle_tls_data: off.ldrp_handle_tls_data, 23 | ldrp_release_tls_entry: off.ldrp_release_tls_entry, 24 | ldrp_mapping_info_index: off.ldrp_mapping_info_index, 25 | ldrp_module_base_address_index: off.ldrp_module_base_address_index, 26 | rtl_initialize_history_table: off.rtl_initialize_history_table, 27 | rtl_insert_inverted_function_table: off.rtl_insert_inverted_function_table 28 | } 29 | } 30 | } 31 | 32 | impl From<&Offsets> for OffsetsResponse { 33 | fn from(off: &Offsets) -> OffsetsResponse { 34 | OffsetsResponse { 35 | ldrp_hash_table: off.ldrp_hash_table, 36 | ldrp_module_datatable_lock: off.ldrp_module_datatable_lock, 37 | ldrp_handle_tls_data: off.ldrp_handle_tls_data, 38 | ldrp_release_tls_entry: off.ldrp_release_tls_entry, 39 | ldrp_mapping_info_index: off.ldrp_mapping_info_index, 40 | ldrp_module_base_address_index: off.ldrp_module_base_address_index, 41 | rtl_initialize_history_table: off.rtl_initialize_history_table, 42 | rtl_insert_inverted_function_table: off.rtl_insert_inverted_function_table 43 | } 44 | } 45 | } 46 | 47 | impl From for OffsetsResponse { 48 | fn from(off: Offsets) -> OffsetsResponse { 49 | OffsetsResponse { 50 | ldrp_hash_table: off.ldrp_hash_table, 51 | ldrp_module_datatable_lock: off.ldrp_module_datatable_lock, 52 | ldrp_handle_tls_data: off.ldrp_handle_tls_data, 53 | ldrp_release_tls_entry: off.ldrp_release_tls_entry, 54 | ldrp_mapping_info_index: off.ldrp_mapping_info_index, 55 | ldrp_module_base_address_index: off.ldrp_module_base_address_index, 56 | rtl_initialize_history_table: off.rtl_initialize_history_table, 57 | rtl_insert_inverted_function_table: off.rtl_insert_inverted_function_table 58 | } 59 | } 60 | } 61 | 62 | #[derive(Serialize, Default, Deserialize, Debug)] 63 | pub struct OffsetsDatabase { 64 | pub offsets: HashMap, 65 | } 66 | -------------------------------------------------------------------------------- /src/map.rs: -------------------------------------------------------------------------------- 1 | use crate::{primitives::Handle, util::to_wide}; 2 | 3 | use std::{ 4 | ffi::c_void, 5 | io::{Error, Result}, 6 | ptr::{null, null_mut}, 7 | }; 8 | 9 | use winapi::um::{fileapi::*, memoryapi::*, winbase::*, winnt::*}; 10 | 11 | // A mapped executable image file in the process's address space 12 | #[derive(Debug)] 13 | pub struct MappedFile { 14 | _file: Handle, 15 | _mapping: Handle, 16 | contents: *mut c_void, 17 | } 18 | 19 | impl MappedFile { 20 | /// Returns the mutable contents of the mapped file 21 | pub fn contents(&self) -> *const c_void { 22 | self.contents 23 | } 24 | 25 | /// Returns the mutable contents of the mapped file 26 | pub fn contents_mut(&mut self) -> *mut c_void { 27 | self.contents 28 | } 29 | 30 | /// Gets the data at an RVA offset, checking to make sure 31 | /// 32 | /// # Arguments 33 | /// 34 | /// `offset`: The RVA offset to the data 35 | /// `required_size`: The required size of the allocation 36 | pub fn get_rva_size_chk(&self, offset: isize, required_size: usize) -> Option<*const T> { 37 | unsafe { 38 | let res = (self.contents as *const u8).offset(offset) as *const T; 39 | // query the memory location and ensure it is valid 40 | let mut mbi = MEMORY_BASIC_INFORMATION::default(); 41 | if VirtualQuery(res as *const c_void, &mut mbi, std::mem::size_of_val(&mbi)) == 0 42 | || mbi.State == MEM_FREE 43 | || res as usize + required_size > mbi.BaseAddress as usize + mbi.RegionSize 44 | { 45 | return None; 46 | } 47 | Some(res) 48 | } 49 | } 50 | 51 | /// Gets the mutable data at an RVA offset, checking to make sure 52 | /// 53 | /// # Arguments 54 | /// 55 | /// `offset`: The RVA offset to the data 56 | /// `required_size`: The required size of the allocation 57 | pub fn get_rva_size_chk_mut( 58 | &mut self, 59 | offset: isize, 60 | required_size: usize, 61 | ) -> Option<*mut T> { 62 | unsafe { 63 | let res = (self.contents as *mut u8).offset(offset) as *mut T; 64 | // query the memory location and ensure it is valid 65 | let mut mbi = MEMORY_BASIC_INFORMATION::default(); 66 | if VirtualQuery(res as *const c_void, &mut mbi, std::mem::size_of_val(&mbi)) == 0 67 | || mbi.State == MEM_FREE 68 | || res as usize + required_size > mbi.BaseAddress as usize + mbi.RegionSize 69 | { 70 | return None; 71 | } 72 | Some(res) 73 | } 74 | } 75 | 76 | /// Gets the data at an RVA offset. Performs necessary checks to ensure 77 | /// that the entire type fits within the allocation 78 | /// 79 | /// # Arguments 80 | /// 81 | /// `offset`: The RVA offset to the data 82 | pub fn get_rva(&self, offset: isize) -> Option<*const T> { 83 | self.get_rva_size_chk(offset, std::mem::size_of::()) 84 | } 85 | 86 | /// Gets the mutable data at an RVA offset. Performs necessary checks to ensure 87 | /// that the entire type fits within the allocation 88 | /// 89 | /// # Arguments 90 | /// 91 | /// `offset`: The RVA offset to the data 92 | pub fn get_rva_mut(&mut self, offset: isize) -> Option<*mut T> { 93 | self.get_rva_size_chk_mut(offset, std::mem::size_of::()) 94 | } 95 | 96 | /// Creates a mapped executable file 97 | /// 98 | /// # Arguments 99 | /// 100 | /// `path`: The path to the executable image file 101 | pub fn load(path: &str) -> Result { 102 | unsafe { 103 | // first open the file 104 | let file: Handle = CreateFileW( 105 | to_wide(path).as_ptr(), 106 | SYNCHRONIZE | GENERIC_READ | GENERIC_EXECUTE, 107 | FILE_SHARE_READ, 108 | null_mut(), 109 | OPEN_EXISTING, 110 | 0, 111 | null_mut(), 112 | ) 113 | .into(); 114 | if file.is_invalid() { 115 | return Err(Error::last_os_error()); 116 | } 117 | // create a file mapping 118 | let mapping: Handle = CreateFileMappingA( 119 | file.handle, 120 | null_mut(), 121 | PAGE_EXECUTE_READ | SEC_IMAGE, 122 | 0, 123 | 0, 124 | null(), 125 | ) 126 | .into(); 127 | if mapping.is_invalid() { 128 | return Err(Error::last_os_error()); 129 | } 130 | // actually map the file 131 | let contents = MapViewOfFile(mapping.handle, FILE_MAP_READ | FILE_MAP_EXECUTE, 0, 0, 0); 132 | if contents.is_null() { 133 | return Err(Error::last_os_error()); 134 | } 135 | Ok(Self { 136 | _file: file, 137 | _mapping: mapping, 138 | contents, 139 | }) 140 | } 141 | } 142 | } 143 | 144 | impl Drop for MappedFile { 145 | fn drop(&mut self) { 146 | // we need to unmap the file before the handle is freed 147 | unsafe { 148 | UnmapViewOfFile(self.contents); 149 | } 150 | // the handles will be dropped by the compiler 151 | } 152 | } 153 | 154 | #[cfg(test)] 155 | mod test { 156 | use super::*; 157 | use serial_test::serial; 158 | 159 | #[test] 160 | fn bad_file() { 161 | let err = MappedFile::load("badpath").unwrap_err(); 162 | assert_eq!(err.kind(), std::io::ErrorKind::NotFound); 163 | } 164 | 165 | #[test] 166 | #[serial] 167 | fn basic_file() { 168 | let file = MappedFile::load("test/basic.exe").unwrap(); 169 | assert_eq!(file.contents as usize, 0x140000000); 170 | unsafe { 171 | // check the MZ header 172 | assert_eq!( 173 | std::str::from_utf8_unchecked(std::slice::from_raw_parts( 174 | file.contents as *const u8, 175 | 2 176 | )), 177 | "MZ" 178 | ); 179 | } 180 | } 181 | } 182 | -------------------------------------------------------------------------------- /src/primitives.rs: -------------------------------------------------------------------------------- 1 | use ntapi::ntrtl::{ 2 | RtlRbInsertNodeEx, RtlReleaseSRWLockExclusive, RtlTryAcquireSRWLockExclusive, RTL_RB_TREE, 3 | }; 4 | use std::{ffi::c_void, io::Result, ops::{Deref, DerefMut}, ptr, ptr::null_mut}; 5 | use winapi::{ 6 | shared::{minwindef::DWORD, ntdef::PRTL_BALANCED_NODE}, 7 | um::{ 8 | handleapi::CloseHandle, 9 | memoryapi::VirtualProtect, 10 | winnt::{HANDLE, PAGE_READWRITE, RTL_SRWLOCK}, 11 | }, 12 | }; 13 | 14 | /// Insert a node into a red-black tree 15 | /// 16 | /// # Arguments 17 | /// 18 | /// `tree`: The tree that is being operated on 19 | /// 20 | /// `node`: The node to insert 21 | /// 22 | /// `compare`: The function that returns true if the first node is *less* than the second node 23 | pub unsafe fn rtl_rb_tree_insert bool>( 24 | tree: &mut RTL_RB_TREE, 25 | node: PRTL_BALANCED_NODE, 26 | compare: F, 27 | ) { 28 | // find the node and position to insert 29 | let (parent, right) = rtl_rb_tree_find_insert_location(tree, node, compare); 30 | RtlRbInsertNodeEx(tree, parent, right as u8, node); 31 | } 32 | 33 | /// Accesses a node based on the encoding mode 34 | /// 35 | /// # Arguments 36 | /// 37 | /// `tree`: The tree that the node is part of 38 | /// 39 | /// `node`: The node that is attempting to be accessed. 40 | /// The pointer to pointer must not be null 41 | unsafe fn rtl_rb_tree_access_node( 42 | tree: &mut RTL_RB_TREE, 43 | node: *const PRTL_BALANCED_NODE, 44 | ) -> PRTL_BALANCED_NODE { 45 | if (tree.Min as u64 & 1) != 0 { 46 | if (*node).is_null() { 47 | return null_mut(); 48 | } 49 | // it is xor-encoded in relation to its address 50 | (node as u64 ^ (*node) as u64) as PRTL_BALANCED_NODE 51 | } else { 52 | *node 53 | } 54 | } 55 | 56 | /// Finds the proper insert location for an RTL red-black tree 57 | /// 58 | /// # Arguments 59 | /// 60 | /// `tree`: The tree 61 | /// 62 | /// `node`: The node to insert 63 | /// 64 | /// `compare`: The function that returns true if the first node is *less* than the second node 65 | unsafe fn rtl_rb_tree_find_insert_location< 66 | F: Fn(PRTL_BALANCED_NODE, PRTL_BALANCED_NODE) -> bool, 67 | >( 68 | tree: &mut RTL_RB_TREE, 69 | node: PRTL_BALANCED_NODE, 70 | compare: F, 71 | ) -> (PRTL_BALANCED_NODE, bool) { 72 | let mut cur_node = rtl_rb_tree_access_node(tree, &tree.Root); 73 | let mut next_node; 74 | let mut right = false; 75 | while !cur_node.is_null() { 76 | if compare(node, cur_node) { 77 | // the node is less, and goes left 78 | next_node = rtl_rb_tree_access_node(tree, &(*cur_node).u.s().Left); 79 | if next_node.is_null() { 80 | right = false; 81 | break; 82 | } 83 | } else { 84 | next_node = rtl_rb_tree_access_node(tree, &(*cur_node).u.s().Right); 85 | if next_node.is_null() { 86 | right = true; 87 | break; 88 | } 89 | } 90 | cur_node = next_node; 91 | } 92 | (cur_node, right) 93 | } 94 | 95 | /// A raw HANDLE that is closed when dropped 96 | #[derive(Debug)] 97 | pub struct Handle { 98 | pub handle: HANDLE, 99 | } 100 | 101 | /// Protects a region of memory for the lifetime of the object 102 | pub struct ProtectionGuard { 103 | addr: *mut c_void, 104 | size: usize, 105 | old_prot: DWORD, 106 | } 107 | 108 | /// A mutex based on an RTL Slim Read/Write lock 109 | #[derive(Clone)] 110 | pub struct RtlMutex { 111 | val_ref: *mut T, 112 | lock_ref: *mut RTL_SRWLOCK, 113 | } 114 | 115 | /// A mutex guard that allows access to the value, and locks it upon `Drop` 116 | pub struct RtlMutexGuard<'a, T> { 117 | mutex: &'a RtlMutex, 118 | } 119 | 120 | /// Writes to protected memory, enforcing a new protection for 121 | /// the duration of the write 122 | /// 123 | /// # Arguments 124 | /// 125 | /// `addr`: The address to write to 126 | /// `val`: The value to write 127 | pub unsafe fn protected_write(addr: *mut T, val: T) -> Result<()> { 128 | // protect the value first with READWRITE 129 | let _prot_guard = ProtectionGuard::new( 130 | addr as *mut c_void, 131 | std::mem::size_of_val(&val), 132 | PAGE_READWRITE, 133 | )?; 134 | // write the value 135 | ptr::write_unaligned(addr, val); 136 | Ok(()) 137 | } 138 | 139 | impl Handle { 140 | /// Returns whether or not the handle is null or `INVALID_HANDLE_VALUE` 141 | pub fn is_invalid(&self) -> bool { 142 | self.is_null() || (self.handle as i64) == -1 143 | } 144 | /// Returns whether or not the handle is null 145 | pub fn is_null(&self) -> bool { 146 | self.handle.is_null() 147 | } 148 | } 149 | 150 | impl From for Handle { 151 | fn from(handle: HANDLE) -> Self { 152 | Self { handle } 153 | } 154 | } 155 | 156 | impl Drop for Handle { 157 | fn drop(&mut self) { 158 | if self.is_invalid() { 159 | return; 160 | } 161 | unsafe { 162 | CloseHandle(self.handle); 163 | } 164 | } 165 | } 166 | 167 | impl ProtectionGuard { 168 | /// Protects a region of the memory 169 | /// 170 | /// # Arguments 171 | /// 172 | /// `addr`: The base address to protect 173 | /// 174 | /// `size`: The number of bytes to protect 175 | /// 176 | /// `prot`: The protection to protect with 177 | pub fn new(addr: *mut c_void, size: usize, prot: DWORD) -> Result { 178 | let mut old_prot = 0; 179 | unsafe { 180 | match VirtualProtect(addr, size, prot, &mut old_prot) { 181 | 0 => Err(std::io::Error::last_os_error()), 182 | _ => Ok(ProtectionGuard { 183 | addr, 184 | size, 185 | old_prot, 186 | }), 187 | } 188 | } 189 | } 190 | } 191 | 192 | impl Drop for ProtectionGuard { 193 | fn drop(&mut self) { 194 | let mut dummy = 0; 195 | unsafe { 196 | VirtualProtect(self.addr, self.size, self.old_prot, &mut dummy); 197 | } 198 | } 199 | } 200 | 201 | impl RtlMutex { 202 | /// Locks the mutex and allows for access of the variable 203 | pub fn lock(&self) -> RtlMutexGuard { 204 | unsafe { 205 | RtlTryAcquireSRWLockExclusive(self.lock_ref); 206 | } 207 | RtlMutexGuard { mutex: self } 208 | } 209 | 210 | /// Creates a new mutex wrapped around a Rtl Slim Read/Write lock 211 | /// 212 | /// # Arguments 213 | /// 214 | /// `val_ref`: The reference to the value protected by the lock 215 | /// `lock`: The lock 216 | pub fn from_ref(val_ref: *mut T, lock_ref: *mut RTL_SRWLOCK) -> RtlMutex { 217 | RtlMutex { val_ref, lock_ref } 218 | } 219 | } 220 | 221 | unsafe impl Send for RtlMutex {} 222 | unsafe impl Sync for RtlMutex {} 223 | 224 | impl<'a, T> Deref for RtlMutexGuard<'a, T> { 225 | type Target = T; 226 | 227 | fn deref(&self) -> &Self::Target { 228 | unsafe { &*self.mutex.val_ref } 229 | } 230 | } 231 | 232 | impl<'a, T> DerefMut for RtlMutexGuard<'a, T> { 233 | fn deref_mut(&mut self) -> &mut Self::Target { 234 | unsafe { &mut *self.mutex.val_ref } 235 | } 236 | } 237 | 238 | impl<'a, T> Drop for RtlMutexGuard<'a, T> { 239 | fn drop(&mut self) { 240 | unsafe { RtlReleaseSRWLockExclusive(self.mutex.lock_ref) } 241 | } 242 | } 243 | -------------------------------------------------------------------------------- /src/server.rs: -------------------------------------------------------------------------------- 1 | #[cfg(feature = "server")] 2 | use crate::offsets::offset_server::OffsetServer; 3 | use crate::{ 4 | db::{Offsets, OffsetsDatabase}, 5 | offsets::{offset_server::Offset, OffsetsRequest, OffsetsResponse}, 6 | }; 7 | use pdb::{FallibleIterator, Source, SymbolData, SymbolTable, PDB}; 8 | use reqwest::StatusCode; 9 | #[cfg(feature = "server")] 10 | use std::net::SocketAddr; 11 | use std::{borrow::Cow, collections::HashMap, fs::read_to_string, io::Cursor, path::PathBuf}; 12 | use tokio::fs::write; 13 | use tokio::sync::Mutex; 14 | #[cfg(feature = "server")] 15 | use tonic::transport; 16 | #[cfg(feature = "server")] 17 | use tonic::transport::{Identity, ServerTlsConfig}; 18 | use tonic::{Request, Response, Status}; 19 | 20 | /// The actual handler for Offset requests. Owns an internal database 21 | pub struct OffsetHandler { 22 | pub database: Mutex, 23 | pub cache_path: PathBuf, 24 | } 25 | 26 | /// An offset server that parses PDBs and sends the parsed addresses to the client 27 | #[cfg(feature = "server")] 28 | pub struct Server { 29 | handler: OffsetHandler, 30 | endpoint: SocketAddr, 31 | #[allow(dead_code)] 32 | tls_identity: Option, 33 | } 34 | 35 | #[cfg(feature = "server")] 36 | impl Server { 37 | /// Creates a new server on an endpoint, with a JSON cache 38 | /// stored at the `cache_path` 39 | /// 40 | /// # Arguments 41 | /// 42 | /// `endpoint`: The network address to bind to 43 | /// 44 | /// `cache_path`: The path to the JSON cache. Will be created if it does not exist 45 | /// 46 | /// `tls_identity`: The TLS identity. If this is specified, TLS will be used with 47 | /// the given certificate and private key 48 | pub fn new>( 49 | endpoint: SocketAddr, 50 | cache_path: S, 51 | tls_identity: Option, 52 | ) -> Result { 53 | Ok(Server { 54 | handler: OffsetHandler::new(cache_path.as_ref())?, 55 | endpoint, 56 | tls_identity, 57 | }) 58 | } 59 | 60 | /// Runs the server 61 | pub async fn run(self) -> Result<(), anyhow::Error> { 62 | let endpoint = self.endpoint; 63 | let mut server = transport::Server::builder(); 64 | if let Some(tls_identity) = self.tls_identity { 65 | server = server.tls_config(ServerTlsConfig::new().identity(tls_identity))?; 66 | } 67 | server 68 | .add_service(OffsetServer::new(self.handler)) 69 | .serve(endpoint) 70 | .await?; 71 | Ok(()) 72 | } 73 | } 74 | 75 | impl OffsetHandler { 76 | pub fn new>(cache_path: S) -> anyhow::Result { 77 | let database = Mutex::new(match read_to_string(cache_path.as_ref()) { 78 | Ok(s) => serde_json::from_str(&s)?, 79 | _ => OffsetsDatabase::default(), 80 | }); 81 | Ok(OffsetHandler { 82 | database, 83 | cache_path: cache_path.as_ref().into(), 84 | }) 85 | } 86 | } 87 | 88 | /// Gets an offset in the PDB file 89 | macro_rules! get_offset { 90 | ($map:ident, $name:literal) => { 91 | match $map.get($name) { 92 | Some(offset) => offset, 93 | None => { 94 | eprintln!("Failed to find offset for {}", $name); 95 | return Ok(None); 96 | } 97 | } 98 | }; 99 | } 100 | 101 | /// Gets all of the offsets from the ntdll PDB 102 | /// 103 | /// # Arguments 104 | /// 105 | /// `s`: The source PDB bytes 106 | fn get_offsets_from_pdb_bytes<'a, S: 'a + Source<'a>>(s: S) -> pdb::Result> { 107 | // parse the pdb 108 | let mut pdb: PDB<'a, S> = pdb::PDB::open(s)?; 109 | let symbol_table: SymbolTable<'a> = pdb.global_symbols()?; 110 | let address_map = pdb.address_map()?; 111 | let map: HashMap, u32> = symbol_table 112 | .iter() 113 | .map(|sym| sym.parse()) 114 | .filter_map(|data| match data { 115 | SymbolData::Public(proc) => Ok(Some(proc)), 116 | _ => Ok(None), 117 | }) 118 | .filter_map(|proc| match proc.offset.to_rva(&address_map) { 119 | Some(rva) => Ok(Some((proc.name.to_string(), rva.0))), 120 | _ => Ok(None), 121 | }) 122 | .collect()?; 123 | let ldrp_hash_table = *get_offset!(map, "LdrpHashTable"); 124 | let ldrp_module_datatable_lock = *get_offset!(map, "LdrpModuleDatatableLock"); 125 | let ldrp_handle_tls_data = *get_offset!(map, "LdrpHandleTlsData"); 126 | let ldrp_release_tls_entry = *get_offset!(map, "LdrpReleaseTlsEntry"); 127 | let ldrp_mapping_info_index = *get_offset!(map, "LdrpMappingInfoIndex"); 128 | let ldrp_module_base_address_index = *get_offset!(map, "LdrpModuleBaseAddressIndex"); 129 | let rtl_initialize_history_table = *get_offset!(map, "RtlInitializeHistoryTable"); 130 | let rtl_insert_inverted_function_table = *get_offset!(map, "RtlInsertInvertedFunctionTable"); 131 | Ok(Some(Offsets { 132 | ldrp_hash_table, 133 | ldrp_module_datatable_lock, 134 | ldrp_handle_tls_data, 135 | ldrp_release_tls_entry, 136 | ldrp_mapping_info_index, 137 | ldrp_module_base_address_index, 138 | rtl_initialize_history_table, 139 | rtl_insert_inverted_function_table 140 | })) 141 | } 142 | 143 | #[tonic::async_trait] 144 | impl Offset for OffsetHandler { 145 | async fn get_offsets( 146 | &self, 147 | request: Request, 148 | ) -> Result, Status> { 149 | let hash = request.into_inner().ntdll_hash; 150 | // ensure the hex hash is the correct size 151 | if hash.len() != 33 { 152 | return Err(Status::invalid_argument("Bad hash length")); 153 | } 154 | // see if the hash is valid 155 | if !hash.chars().all(|x| char::is_ascii_hexdigit(&x)) { 156 | return Err(Status::invalid_argument("Bad hex digit")); 157 | } 158 | let database = &mut self.database.lock().await; 159 | let offsets_map = &mut database.offsets; 160 | // see if it is in cache already 161 | if let Some(offsets) = offsets_map.get(&hash) { 162 | return Ok(Response::new(offsets.into())); 163 | } 164 | // download the PDB 165 | let pdb = match reqwest::get(format!( 166 | "http://msdl.microsoft.com/download/symbols/ntdll.pdb/{}/ntdll.pdb", 167 | &hash 168 | )) 169 | .await 170 | { 171 | Ok(response) => response, 172 | Err(e) => return Err(Status::not_found(format!("Error on fetch: {}", e))), 173 | }; 174 | let status = pdb.status(); 175 | match status { 176 | StatusCode::OK => {} 177 | StatusCode::NOT_FOUND => { 178 | return Err(Status::not_found("PDB hash not found")); 179 | } 180 | c => { 181 | return Err(Status::internal(format!("Internal error: {}", c))); 182 | } 183 | }; 184 | let pdb = match pdb.bytes().await { 185 | Ok(bytes) => bytes, 186 | Err(e) => { 187 | return Err(Status::internal(format!("Error on bytes: {}", e))); 188 | } 189 | }; 190 | let offsets = match get_offsets_from_pdb_bytes(Cursor::new(&pdb)) { 191 | Ok(offsets) => offsets, 192 | Err(e) => { 193 | return Err(Status::internal(format!( 194 | "Processing error: {}. Bytes: {:?}", 195 | e, pdb 196 | ))); 197 | } 198 | }; 199 | let offsets = match offsets { 200 | Some(offsets) => offsets, 201 | None => { 202 | return Err(Status::internal("Failed to find some functions")); 203 | } 204 | }; 205 | // cache the lookup 206 | offsets_map.insert(hash, offsets); 207 | // serialize the database 208 | let s = match serde_json::to_string::(&*database) { 209 | Ok(s) => s, 210 | Err(e) => { 211 | eprintln!("Failed to serialize database: {}. DB: {:?}", e, database); 212 | return Ok(Response::new(offsets.into())); 213 | } 214 | }; 215 | match write(&self.cache_path, &s).await { 216 | Ok(_) => {} 217 | Err(e) => { 218 | eprintln!( 219 | "Failed to write database to cache file {}: {}. Payload: {}", 220 | &self.cache_path.as_path().to_string_lossy(), 221 | e, 222 | &s 223 | ) 224 | } 225 | } 226 | Ok(Response::new(offsets.into())) 227 | } 228 | } 229 | 230 | #[cfg(test)] 231 | mod test { 232 | use super::*; 233 | use crate::offsets::{offset_server::Offset, OffsetsRequest}; 234 | use tonic::Request; 235 | 236 | #[tokio::test] 237 | async fn hash_length() { 238 | let handler = OffsetHandler::new("test/cache.json").unwrap(); 239 | let request = Request::new(OffsetsRequest { 240 | ntdll_hash: "123".into(), 241 | }); 242 | let err = handler.get_offsets(request).await.unwrap_err(); 243 | assert_eq!(err.code(), tonic::Code::InvalidArgument); 244 | assert_eq!(err.message(), "Bad hash length"); 245 | } 246 | 247 | #[tokio::test] 248 | async fn hash_digits() { 249 | let handler = OffsetHandler::new("test/cache.json").unwrap(); 250 | let request = Request::new(OffsetsRequest { 251 | ntdll_hash: "46F6F5C30E7147E46F2A953A5DAF201AG".into(), 252 | }); 253 | let err = handler.get_offsets(request).await.unwrap_err(); 254 | assert_eq!(err.code(), tonic::Code::InvalidArgument); 255 | assert_eq!(err.message(), "Bad hex digit"); 256 | } 257 | 258 | #[tokio::test] 259 | async fn not_found() { 260 | let handler = OffsetHandler::new("test/cache.json").unwrap(); 261 | let request = Request::new(OffsetsRequest { 262 | ntdll_hash: "46F6F5C30E7147E46F2A953A5DAF201A2".into(), 263 | }); 264 | let err = handler.get_offsets(request).await.unwrap_err(); 265 | assert_eq!(err.message(), "PDB hash not found"); 266 | } 267 | 268 | #[tokio::test] 269 | async fn good_fetch() { 270 | let handler = OffsetHandler::new("test/cache.json").unwrap(); 271 | let request = Request::new(OffsetsRequest { 272 | ntdll_hash: "46F6F5C30E7147E46F2A953A5DAF201A1".into(), 273 | }); 274 | let response = handler.get_offsets(request).await.unwrap().into_inner(); 275 | // ensure it was cached 276 | assert!(handler 277 | .database 278 | .lock() 279 | .await 280 | .offsets 281 | .contains_key("46F6F5C30E7147E46F2A953A5DAF201A1")); 282 | assert_eq!(response.ldrp_hash_table, 0x16A140); 283 | assert_eq!(response.ldrp_module_datatable_lock, 0x16B240); 284 | assert_eq!(response.ldrp_handle_tls_data, 0x47C14); 285 | } 286 | } 287 | -------------------------------------------------------------------------------- /src/pe.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | error::Error, 3 | map::MappedFile, 4 | offsets::{ 5 | offset_client::OffsetClient, offset_server::Offset, OffsetsRequest, OffsetsResponse, 6 | }, 7 | primitives::{protected_write, rtl_rb_tree_insert, ProtectionGuard, RtlMutex}, 8 | server::OffsetHandler, 9 | util::to_wide, 10 | }; 11 | use log::debug; 12 | use ntapi::{ 13 | ntldr::{ 14 | LDR_DATA_TABLE_ENTRY_u1, LDR_DATA_TABLE_ENTRY_u2, LDR_DDAG_NODE_u, LdrModulesReadyToRun, 15 | LDRP_CSLIST, LDR_DATA_TABLE_ENTRY, LDR_DDAG_NODE, LDR_DDAG_STATE, PLDR_INIT_ROUTINE, 16 | }, 17 | ntpsapi::NtCurrentPeb, 18 | ntrtl::{ 19 | InsertTailList, RemoveEntryList, RtlHashUnicodeString, RtlImageDirectoryEntryToData, 20 | RtlInitUnicodeString, RtlRbRemoveNode, HASH_STRING_ALGORITHM_DEFAULT, RTL_RB_TREE, 21 | }, 22 | }; 23 | use std::{ 24 | ffi::{c_void, CStr, OsString}, 25 | os::windows::prelude::OsStrExt, 26 | os::windows::prelude::*, 27 | path::Path, 28 | pin::Pin, 29 | ptr, 30 | ptr::null_mut, 31 | }; 32 | use tonic::transport::Certificate; 33 | use tonic::transport::Channel; 34 | use tonic::transport::ClientTlsConfig; 35 | use winapi::{ 36 | shared::{ 37 | guiddef::GUID, 38 | minwindef::HMODULE, 39 | ntdef::{ 40 | LARGE_INTEGER, LIST_ENTRY, RTL_BALANCED_NODE, SINGLE_LIST_ENTRY, ULONGLONG, 41 | UNICODE_STRING, 42 | }, 43 | ntstatus::STATUS_SUCCESS, 44 | winerror::ERROR_FILE_NOT_FOUND, 45 | }, 46 | um::{ 47 | libloaderapi::{GetModuleHandleW, GetProcAddress, LoadLibraryA}, 48 | winnt::{ 49 | RtlDeleteFunctionTable, DLL_PROCESS_ATTACH, DLL_PROCESS_DETACH, IMAGE_DEBUG_DIRECTORY, 50 | IMAGE_DEBUG_TYPE_CODEVIEW, IMAGE_DIRECTORY_ENTRY_DEBUG, 51 | IMAGE_DIRECTORY_ENTRY_EXCEPTION, IMAGE_DIRECTORY_ENTRY_IMPORT, 52 | IMAGE_DIRECTORY_ENTRY_TLS, IMAGE_DOS_HEADER, IMAGE_FILE_MACHINE_AMD64, 53 | IMAGE_IMPORT_BY_NAME, IMAGE_IMPORT_DESCRIPTOR, IMAGE_NT_HEADERS64, 54 | IMAGE_NT_OPTIONAL_HDR64_MAGIC, IMAGE_ORDINAL_FLAG, IMAGE_RUNTIME_FUNCTION_ENTRY, 55 | IMAGE_SCN_MEM_EXECUTE, IMAGE_SCN_MEM_WRITE, IMAGE_SECTION_HEADER, IMAGE_THUNK_DATA, 56 | IMAGE_TLS_DIRECTORY, PAGE_EXECUTE_READ, PAGE_READONLY, PAGE_READWRITE, PVOID, 57 | RTL_SRWLOCK, 58 | }, 59 | }, 60 | }; 61 | 62 | /// The context of internal Nt functions and statics that are 63 | /// required for the mapper to work 64 | #[allow(non_snake_case)] 65 | #[derive(Clone)] 66 | pub struct NtContext { 67 | LdrpHashTable: RtlMutex<[LIST_ENTRY; 32]>, 68 | LdrpHandleTlsData: unsafe extern "stdcall" fn(*mut LDR_DATA_TABLE_ENTRY) -> i32, 69 | LdrpReleaseTlsEntry: unsafe extern "stdcall" fn(*mut LDR_DATA_TABLE_ENTRY, null: usize) -> i32, 70 | LdrpMappingInfoIndex: RtlMutex, 71 | LdrpModuleBaseAddressIndex: RtlMutex, 72 | RtlInitializeHistoryTable: unsafe extern "stdcall" fn(), 73 | RtlInsertInvertedFunctionTable: unsafe extern "fastcall" fn(HMODULE, usize) -> i32, 74 | } 75 | 76 | unsafe impl Send for NtContext {} 77 | 78 | /// This allows us to hold a pointer across a wait point 79 | struct Module(*const u8); 80 | 81 | unsafe impl Send for Module {} 82 | 83 | impl NtContext { 84 | /// Gets the loaded NTDLL hash 85 | /// 86 | /// # Arguments 87 | /// 88 | /// `ntdll`: The NTDLL instance pointer 89 | fn get_ntdll_hash(ntdll: *const u8) -> Result { 90 | unsafe { 91 | let dos_header = ntdll as *const IMAGE_DOS_HEADER; 92 | if dos_header.is_null() { 93 | return Err(Error::NtDllNotLoaded); 94 | } 95 | let nt_headers = &*((dos_header as *const u8).offset((*dos_header).e_lfanew as isize) 96 | as *const IMAGE_NT_HEADERS64); 97 | let debug_entry = &*((dos_header as *const u8).offset( 98 | nt_headers.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_DEBUG as usize] 99 | .VirtualAddress as isize, 100 | ) as *const IMAGE_DEBUG_DIRECTORY); 101 | if debug_entry.Type != IMAGE_DEBUG_TYPE_CODEVIEW { 102 | return Err(Error::NtDllDebugType); 103 | } 104 | let codeview_entry = &*((dos_header as *const u8) 105 | .offset(debug_entry.AddressOfRawData as isize) 106 | as *const IMAGE_DEBUG_CODEVIEW); 107 | if !codeview_entry 108 | .rsds_signature 109 | .eq_ignore_ascii_case("RSDS".as_bytes()) 110 | { 111 | return Err(Error::NtDllRsdsSig); 112 | } 113 | Ok(format!( 114 | "{:08X}{:04X}{:04X}{}{:X}", 115 | codeview_entry.guid.Data1, 116 | codeview_entry.guid.Data2, 117 | codeview_entry.guid.Data3, 118 | codeview_entry 119 | .guid 120 | .Data4 121 | .iter() 122 | .map(|b| format!("{:02X}", b)) 123 | .collect::>() 124 | .join(""), 125 | codeview_entry.age 126 | )) 127 | } 128 | } 129 | 130 | /// Processes an offsets response 131 | /// 132 | /// # Arguments 133 | /// 134 | /// `response`: The response to process 135 | fn process_response(response: OffsetsResponse, ntdll: *const u8) -> NtContext { 136 | unsafe { 137 | NtContext { 138 | LdrpHashTable: RtlMutex::from_ref( 139 | &mut *(ntdll.offset(response.ldrp_hash_table as isize) 140 | as *mut [LIST_ENTRY; 32]), 141 | &mut *(ntdll.offset(response.ldrp_module_datatable_lock as isize) 142 | as *mut RTL_SRWLOCK), 143 | ), 144 | #[allow(clippy::missing_transmute_annotations)] 145 | LdrpHandleTlsData: std::mem::transmute( 146 | ntdll.offset(response.ldrp_handle_tls_data as isize), 147 | ), 148 | #[allow(clippy::missing_transmute_annotations)] 149 | LdrpReleaseTlsEntry: std::mem::transmute( 150 | ntdll.offset(response.ldrp_release_tls_entry as isize), 151 | ), 152 | LdrpMappingInfoIndex: RtlMutex::from_ref( 153 | &mut *(ntdll.offset(response.ldrp_mapping_info_index as isize) 154 | as *mut RTL_RB_TREE), 155 | &mut *(ntdll.offset(response.ldrp_module_datatable_lock as isize) 156 | as *mut RTL_SRWLOCK), 157 | ), 158 | LdrpModuleBaseAddressIndex: RtlMutex::from_ref( 159 | &mut *(ntdll.offset(response.ldrp_module_base_address_index as isize) 160 | as *mut RTL_RB_TREE), 161 | &mut *(ntdll.offset(response.ldrp_module_datatable_lock as isize) 162 | as *mut RTL_SRWLOCK), 163 | ), 164 | #[allow(clippy::missing_transmute_annotations)] 165 | RtlInitializeHistoryTable: std::mem::transmute( 166 | ntdll.offset(response.rtl_initialize_history_table as isize), 167 | ), 168 | #[allow(clippy::missing_transmute_annotations)] 169 | RtlInsertInvertedFunctionTable: std::mem::transmute( 170 | ntdll.offset(response.rtl_insert_inverted_function_table as isize), 171 | ), 172 | } 173 | } 174 | } 175 | 176 | /// Resolves the context used by the mapper 177 | /// 178 | /// # Arguments 179 | /// 180 | /// `server_hostname`: The hostname of the endpoint of the PDB server 181 | /// 182 | /// `server_port`: The port of the endpoint of the PDB server 183 | pub async fn resolve>( 184 | server_hostname: S, 185 | server_port: u16, 186 | ) -> anyhow::Result { 187 | let channel = Channel::from_shared(format!( 188 | "http://{}:{}", 189 | server_hostname.as_ref(), 190 | server_port 191 | ))?; 192 | let mut client = OffsetClient::new(channel.connect().await?); 193 | // wrap this in a `Module` so we can send it across await points 194 | let ntdll = Module(unsafe { GetModuleHandleW(to_wide("ntdll").as_ptr()) as *const u8 }); 195 | let request = tonic::Request::new(OffsetsRequest { 196 | ntdll_hash: NtContext::get_ntdll_hash(ntdll.0)?, 197 | }); 198 | let response = client.get_offsets(request).await?.into_inner(); 199 | Ok(NtContext::process_response(response, ntdll.0)) 200 | } 201 | 202 | /// Resolves the context used by the mapper, over a secure TLS connection 203 | /// 204 | /// # Arguments 205 | /// 206 | /// `server_hostname`: The hostname of the endpoint of the PDB server 207 | /// 208 | /// `server_port`: The port of the endpoint of the PDB server 209 | /// 210 | /// `ca_cert`: A custom CA certificate. This is necessary if you have a 211 | /// self-signed certificate on the other end. If not specified, webPKI 212 | /// will use their store to verify the endpoint 213 | /// 214 | /// `domain`: The domain name to be verified 215 | pub async fn resolve_tls>( 216 | server_hostname: S, 217 | server_port: u16, 218 | ca_cert: Option, 219 | domain: Option, 220 | ) -> anyhow::Result { 221 | let mut tls_config = ClientTlsConfig::new(); 222 | // add a CA certificate in case a self-signed cert is used 223 | if let Some(ca_cert) = ca_cert { 224 | tls_config = tls_config.ca_certificate(ca_cert); 225 | } 226 | // add a pinned domain in case domain name verification is wanted 227 | if let Some(domain) = domain { 228 | tls_config = tls_config.domain_name(domain.as_ref()); 229 | } 230 | let channel = Channel::from_shared(format!( 231 | "http://{}:{}", 232 | server_hostname.as_ref(), 233 | server_port 234 | ))? 235 | .tls_config(tls_config)?; 236 | let mut client = OffsetClient::new(channel.connect().await?); 237 | // wrap this in a `Module` so we can send it across await points 238 | let ntdll = Module(unsafe { GetModuleHandleW(to_wide("ntdll").as_ptr()) as *const u8 }); 239 | let request = tonic::Request::new(OffsetsRequest { 240 | ntdll_hash: NtContext::get_ntdll_hash(ntdll.0)?, 241 | }); 242 | let response = client.get_offsets(request).await?.into_inner(); 243 | Ok(NtContext::process_response(response, ntdll.0)) 244 | } 245 | 246 | /// Resolves the context used by the mapper 247 | /// 248 | /// # Arguments 249 | /// 250 | /// `handler`: The local handler 251 | pub async fn resolve_local(handler: &OffsetHandler) -> anyhow::Result { 252 | // wrap this in a `Module` so we can send it across await points 253 | let ntdll = Module(unsafe { GetModuleHandleW(to_wide("ntdll").as_ptr()) as *const u8 }); 254 | let request = tonic::Request::new(OffsetsRequest { 255 | ntdll_hash: NtContext::get_ntdll_hash(ntdll.0)?, 256 | }); 257 | let response = handler.get_offsets(request).await?.into_inner(); 258 | Ok(NtContext::process_response(response, ntdll.0)) 259 | } 260 | } 261 | 262 | #[allow(non_camel_case_types)] 263 | #[repr(C)] 264 | struct IMAGE_DEBUG_CODEVIEW { 265 | rsds_signature: [u8; 4], 266 | guid: GUID, 267 | age: u32, 268 | } 269 | 270 | /// Sets the image as the primary image. Useful 271 | /// if you want `GetModuleHandle(null)` to return 272 | /// the handle to the mapped image. Returns the 273 | /// previous primary image base. The caller must ensure 274 | /// that the module is valid while the primary image is 275 | /// set, and must restore the old image base before it goes out 276 | /// of scope 277 | /// 278 | /// # Arguments 279 | /// 280 | /// `image_base`: The base address of the image 281 | unsafe fn set_as_primary_image(image_base: PVOID) -> PVOID { 282 | let peb = NtCurrentPeb(); 283 | let old_base = (*peb).ImageBaseAddress; 284 | (*peb).ImageBaseAddress = image_base; 285 | old_base 286 | } 287 | 288 | /// A portable executable that is all nicely wrapped up in a class 289 | pub struct PortableExecutable<'a> { 290 | file: MappedFile, 291 | file_name: Vec, 292 | file_path: Vec, 293 | nt_headers: &'a IMAGE_NT_HEADERS64, 294 | section_headers: &'a [IMAGE_SECTION_HEADER], 295 | entry_point: PLDR_INIT_ROUTINE, 296 | section_protections: Vec, 297 | loader_entry: Pin>, 298 | ddag_node: Pin>, 299 | context: NtContext, 300 | added_to_hash_tbl: bool, 301 | added_to_index: bool, 302 | added_to_peb: bool, 303 | called_entry_point: bool, 304 | last_primary: Option, 305 | } 306 | 307 | macro_rules! from_field_mut { 308 | ($parent: path, $field: tt, $field_ptr: expr) => { 309 | ($field_ptr as usize - memoffset::offset_of!($parent, $field)) as *mut $parent 310 | }; 311 | } 312 | 313 | impl<'a> PortableExecutable<'a> { 314 | /// Adds the module to the loader hash table, enabling functions like 315 | /// `GetModuleHandle` to work 316 | fn add_to_hash_table(&mut self) { 317 | // insert the entry into the hash table and other relevant structures 318 | unsafe { 319 | InsertTailList( 320 | &mut self.context.LdrpHashTable.lock() 321 | [(self.loader_entry.BaseNameHashValue & 0x1f) as usize], 322 | &mut self.loader_entry.HashLinks, 323 | ); 324 | self.added_to_hash_tbl = true; 325 | } 326 | } 327 | 328 | /// Removes the module from the loader hash table 329 | fn remove_from_hash_table(&mut self) { 330 | // remove hash table and linked list entries 331 | let mut hash_table = self.context.LdrpHashTable.lock(); 332 | // find our entry 333 | let first_entry = &mut hash_table[(self.loader_entry.BaseNameHashValue & 0x1f) as usize]; 334 | let mut entry = first_entry.Flink; 335 | // if the next entry is the first one, we are done 336 | while !ptr::eq(entry as *const LIST_ENTRY, first_entry) { 337 | if ptr::eq(entry as *const _, &self.loader_entry.HashLinks as *const _) { 338 | // remove the entry 339 | unsafe { 340 | RemoveEntryList(entry.as_mut().unwrap()); 341 | } 342 | } 343 | entry = unsafe { (*entry).Flink }; 344 | } 345 | debug!("Failed to find reference"); 346 | } 347 | 348 | /// Adds the module to the index red-black trees 349 | fn add_to_index(&mut self) { 350 | // add it to the mapping information table 351 | unsafe { 352 | rtl_rb_tree_insert( 353 | &mut self.context.LdrpMappingInfoIndex.lock(), 354 | &mut self.loader_entry.MappingInfoIndexNode, 355 | |l, r| { 356 | let l = from_field_mut!(LDR_DATA_TABLE_ENTRY, MappingInfoIndexNode, l); 357 | let r = from_field_mut!(LDR_DATA_TABLE_ENTRY, MappingInfoIndexNode, r); 358 | (*l).TimeDateStamp < (*r).TimeDateStamp 359 | || ((*l).TimeDateStamp <= (*r).TimeDateStamp 360 | && (*l).SizeOfImage < (*r).SizeOfImage) 361 | }, 362 | ); 363 | rtl_rb_tree_insert( 364 | &mut self.context.LdrpModuleBaseAddressIndex.lock(), 365 | &mut self.loader_entry.BaseAddressIndexNode, 366 | |l, r| { 367 | let l = from_field_mut!(LDR_DATA_TABLE_ENTRY, BaseAddressIndexNode, l); 368 | let r = from_field_mut!(LDR_DATA_TABLE_ENTRY, BaseAddressIndexNode, r); 369 | (*l).DllBase < (*r).DllBase 370 | }, 371 | ) 372 | } 373 | self.added_to_index = true; 374 | } 375 | 376 | /// Removes the module from the index red-black trees 377 | fn remove_from_index(&mut self) { 378 | unsafe { 379 | RtlRbRemoveNode( 380 | &mut (*self.context.LdrpMappingInfoIndex.lock()), 381 | &mut self.loader_entry.MappingInfoIndexNode, 382 | ); 383 | RtlRbRemoveNode( 384 | &mut (*self.context.LdrpModuleBaseAddressIndex.lock()), 385 | &mut self.loader_entry.BaseAddressIndexNode, 386 | ); 387 | } 388 | } 389 | 390 | /// Adds the module to the PEB structures 391 | fn add_to_peb(&mut self) { 392 | unsafe { 393 | let peb = NtCurrentPeb(); 394 | let ldr = (*peb).Ldr; 395 | InsertTailList( 396 | &mut (*ldr).InLoadOrderModuleList, 397 | &mut self.loader_entry.InLoadOrderLinks, 398 | ); 399 | InsertTailList( 400 | &mut (*ldr).InMemoryOrderModuleList, 401 | &mut self.loader_entry.InMemoryOrderLinks, 402 | ); 403 | } 404 | self.added_to_peb = true; 405 | } 406 | 407 | /// Removes the modules from the PEB structures 408 | fn remove_from_peb(&mut self) { 409 | unsafe { 410 | RemoveEntryList(&mut self.loader_entry.InLoadOrderLinks); 411 | RemoveEntryList(&mut self.loader_entry.InMemoryOrderLinks); 412 | } 413 | } 414 | 415 | /// Calls the function entry point 416 | /// 417 | /// # Arguments 418 | /// 419 | /// `reason`: The reason for the call. Ex: `DLL_PROCESS_ATTACH` 420 | unsafe fn call_entry_point(&mut self, reason: u32) -> u8 { 421 | if let Some(entry_point) = &self.entry_point { 422 | entry_point(self.file.contents_mut(), reason, null_mut()) 423 | } else { 424 | 0 425 | } 426 | } 427 | 428 | /// Enables exception handling for the module 429 | fn enable_exceptions(&mut self) -> Result<(), Error> { 430 | // get the exception table 431 | let exception_table = self.get_exception_table(); 432 | if exception_table.is_empty() { 433 | return Ok(()); 434 | } 435 | // register the exception table 436 | let res = unsafe { 437 | (self.context.RtlInsertInvertedFunctionTable)( 438 | self.module_handle(), 439 | self.nt_headers.OptionalHeader.SizeOfImage as usize, 440 | ) 441 | }; 442 | if res == 0 { 443 | return Err(Error::ExceptionTableEntry); 444 | } 445 | // this doesn't seem to work on x86-64 anymore... 446 | // // add the table to the process 447 | // if unsafe { 448 | // RtlAddFunctionTable( 449 | // exception_table.as_mut_ptr(), 450 | // exception_table.len() as u32, 451 | // self.file.contents() as u64, 452 | // ) == false as u8 453 | // } { 454 | // return Err(Error::ExceptionTableEntry); 455 | // } 456 | unsafe { (self.context.RtlInitializeHistoryTable)() } 457 | Ok(()) 458 | } 459 | 460 | /// Disables exception handling for the module 461 | fn disable_exceptions(&mut self) -> Result<(), Error> { 462 | // get the exception table 463 | let exception_table = self.get_exception_table(); 464 | if exception_table.is_empty() { 465 | return Ok(()); 466 | } 467 | // remove the table from the process 468 | if unsafe { RtlDeleteFunctionTable(exception_table.as_mut_ptr()) == false as u8 } { 469 | return Err(Error::ExceptionTableEntry); 470 | } 471 | Ok(()) 472 | } 473 | 474 | /// Execute TLS callbacks 475 | /// 476 | /// # Arguments 477 | /// 478 | /// `reason`: The reason for the call to the callbacks 479 | fn execute_tls_callbacks(&mut self, reason: u32) -> Result<(), Error> { 480 | let tls_directory = 481 | &self.nt_headers.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_TLS as usize]; 482 | // navigate to the tls directory if it exists 483 | if tls_directory.VirtualAddress == 0 { 484 | return Ok(()); 485 | } 486 | let tls_directory = self 487 | .file 488 | .get_rva::(tls_directory.VirtualAddress as isize) 489 | .ok_or(Error::TLSOutOfBounds)?; 490 | let tls_callbacks = unsafe { (*tls_directory).AddressOfCallBacks }; 491 | // it is possible that callbacks may not exist 492 | if tls_callbacks == 0 { 493 | return Ok(()); 494 | } 495 | let mut tls_callback = self 496 | .file 497 | .get_rva::>( 498 | (tls_callbacks - self.file.contents() as u64) as isize, 499 | ) 500 | .ok_or(Error::CallbackOutOfBounds)?; 501 | // execute all of the TLS callbacks 502 | unsafe { 503 | while (*tls_callback).is_some() { 504 | debug!("Executing TLS callback at {:p}", tls_callback); 505 | (*tls_callback).unwrap()(self.file.contents_mut(), reason, null_mut()); 506 | // TLS callbacks are in an array 507 | tls_callback = tls_callback.offset(1); 508 | } 509 | } 510 | Ok(()) 511 | } 512 | 513 | /// Frees all TLS data 514 | fn free_tls_data(&mut self) -> Result<(), Error> { 515 | if unsafe { (self.context.LdrpReleaseTlsEntry)(self.loader_entry.as_mut().get_mut(), 0) } 516 | != STATUS_SUCCESS 517 | { 518 | Err(Error::TLSData) 519 | } else { 520 | Ok(()) 521 | } 522 | } 523 | 524 | /// Handle TLS data 525 | fn handle_tls_data(&mut self) -> Result<(), Error> { 526 | if unsafe { (self.context.LdrpHandleTlsData)(self.loader_entry.as_mut().get_mut()) } 527 | != STATUS_SUCCESS 528 | { 529 | Err(Error::TLSData) 530 | } else { 531 | Ok(()) 532 | } 533 | } 534 | 535 | /// Gets the exception table for the module, and its size 536 | fn get_exception_table(&mut self) -> &'a mut [IMAGE_RUNTIME_FUNCTION_ENTRY] { 537 | let mut size: u32 = 0; 538 | let table = unsafe { 539 | #[allow(clippy::missing_transmute_annotations)] 540 | std::mem::transmute(RtlImageDirectoryEntryToData( 541 | self.file.contents_mut(), 542 | 1, 543 | IMAGE_DIRECTORY_ENTRY_EXCEPTION, 544 | &mut size, 545 | )) 546 | }; 547 | unsafe { 548 | &mut *ptr::slice_from_raw_parts_mut( 549 | table, 550 | size as usize / std::mem::size_of::(), 551 | ) 552 | } 553 | } 554 | 555 | /// Initializes the loader entry 556 | fn init_ldr_entry(&mut self) -> Result<(), Error> { 557 | self.loader_entry.DllBase = self.file.contents_mut(); 558 | self.loader_entry.DdagNode = self.ddag_node.as_mut().get_mut(); 559 | unsafe { 560 | RtlInitUnicodeString(&mut self.loader_entry.BaseDllName, self.file_name.as_ptr()); 561 | RtlInitUnicodeString(&mut self.loader_entry.FullDllName, self.file_path.as_ptr()); 562 | } 563 | // add the module hash value to the loader entry 564 | let mut hash = 0; 565 | unsafe { 566 | if RtlHashUnicodeString( 567 | &self.loader_entry.BaseDllName, 568 | true as u8, 569 | HASH_STRING_ALGORITHM_DEFAULT, 570 | &mut hash, 571 | ) != STATUS_SUCCESS 572 | { 573 | return Err(Error::LdrEntry); 574 | } 575 | } 576 | self.loader_entry.BaseNameHashValue = hash; 577 | // set that we need to process static imports 578 | unsafe { 579 | self.loader_entry.u2.set_ProcessStaticImport(1); 580 | } 581 | // add the executable size 582 | self.loader_entry.TimeDateStamp = self.nt_headers.FileHeader.TimeDateStamp; 583 | self.loader_entry.SizeOfImage = self.nt_headers.OptionalHeader.SizeOfImage; 584 | self.ddag_node.State = LdrModulesReadyToRun; 585 | self.ddag_node.LoadCount = u32::MAX; 586 | // add to the loader hash table 587 | self.add_to_hash_table(); 588 | // add to the red-black trees for traversal 589 | self.add_to_index(); 590 | // add to the PEB linked lists 591 | self.add_to_peb(); 592 | Ok(()) 593 | } 594 | 595 | /// Loads the portable executable, processing any options 596 | /// 597 | /// # Arguments 598 | /// 599 | /// `path`: The path to the executable file 600 | /// 601 | /// `context`: The resolved Nt Context 602 | pub fn load(path: &str, context: NtContext) -> Result, anyhow::Error> { 603 | PortableExecutable::load_as_primary(path, context, false) 604 | } 605 | 606 | /// Loads the portable executable, processing any options 607 | /// 608 | /// # Arguments 609 | /// 610 | /// `path`: The path to the executable file 611 | /// 612 | /// `context`: The resolved Nt Context 613 | /// 614 | /// `primary`: Whether or not this module should be the primary 615 | /// module, and be returned upon invocation of `GetModuleHandle(null)` 616 | pub fn load_as_primary( 617 | path: &str, 618 | context: NtContext, 619 | primary: bool, 620 | ) -> Result, anyhow::Error> { 621 | // first make sure we got all of the required functions 622 | let mut file = MappedFile::load(path)?; 623 | let path = Path::new(path); 624 | let file_name: Vec = path 625 | .file_name() 626 | .ok_or_else(|| std::io::Error::from_raw_os_error(ERROR_FILE_NOT_FOUND as i32))? 627 | .encode_wide() 628 | .chain(std::iter::once(0)) 629 | .collect(); 630 | // remove the "extended length path syntax" 631 | let file_path: Vec = path 632 | .canonicalize()? 633 | .as_os_str() 634 | .encode_wide() 635 | .skip(4) 636 | .chain(std::iter::once(0)) 637 | .collect(); 638 | debug!( 639 | "Loading {} at path {}", 640 | OsString::from_wide(&file_name[..]) 641 | .as_os_str() 642 | .to_string_lossy(), 643 | OsString::from_wide(&file_path[..]) 644 | .as_os_str() 645 | .to_string_lossy() 646 | ); 647 | let (nt_headers, section_headers) = PortableExecutable::load_headers(&mut file)?; 648 | let entry_point = PortableExecutable::load_entry_point(&mut file, nt_headers)?; 649 | let mut pe = PortableExecutable { 650 | file, 651 | file_name, 652 | file_path, 653 | nt_headers, 654 | section_headers, 655 | entry_point, 656 | section_protections: Vec::new(), 657 | loader_entry: Box::pin(LDR_DATA_TABLE_ENTRY { 658 | InLoadOrderLinks: LIST_ENTRY::default(), 659 | InMemoryOrderLinks: LIST_ENTRY::default(), 660 | u1: LDR_DATA_TABLE_ENTRY_u1 { 661 | InInitializationOrderLinks: LIST_ENTRY::default(), 662 | }, 663 | DllBase: null_mut(), 664 | EntryPoint: None, 665 | SizeOfImage: 0, 666 | FullDllName: UNICODE_STRING::default(), 667 | BaseDllName: UNICODE_STRING::default(), 668 | u2: LDR_DATA_TABLE_ENTRY_u2 { Flags: 0 }, 669 | ObsoleteLoadCount: u16::MAX, 670 | TlsIndex: 0, 671 | HashLinks: LIST_ENTRY::default(), 672 | TimeDateStamp: 0, 673 | EntryPointActivationContext: null_mut(), 674 | Lock: null_mut(), 675 | DdagNode: null_mut(), 676 | NodeModuleLink: LIST_ENTRY::default(), 677 | LoadContext: null_mut(), 678 | ParentDllBase: null_mut(), 679 | SwitchBackContext: null_mut(), 680 | BaseAddressIndexNode: RTL_BALANCED_NODE::default(), 681 | MappingInfoIndexNode: RTL_BALANCED_NODE::default(), 682 | OriginalBase: 0, 683 | LoadTime: LARGE_INTEGER::default(), 684 | BaseNameHashValue: 0, 685 | LoadReason: 0, 686 | ImplicitPathOptions: 0, 687 | ReferenceCount: 0, 688 | DependentLoadFlags: 0, 689 | SigningLevel: 0, 690 | }), 691 | ddag_node: Box::pin(LDR_DDAG_NODE { 692 | Modules: LIST_ENTRY::default(), 693 | ServiceTagList: null_mut(), 694 | LoadCount: 0, 695 | LoadWhileUnloadingCount: 0, 696 | LowestLink: 0, 697 | u: LDR_DDAG_NODE_u { 698 | Dependencies: LDRP_CSLIST { Tail: null_mut() }, 699 | }, 700 | IncomingDependencies: LDRP_CSLIST { Tail: null_mut() }, 701 | State: LDR_DDAG_STATE::default(), 702 | CondenseLink: SINGLE_LIST_ENTRY::default(), 703 | PreorderNumber: 0, 704 | }), 705 | context, 706 | added_to_hash_tbl: false, 707 | added_to_index: false, 708 | added_to_peb: false, 709 | called_entry_point: false, 710 | last_primary: None, 711 | }; 712 | pe.init_ldr_entry()?; 713 | pe.resolve_imports()?; 714 | pe.protect_sections()?; 715 | pe.enable_exceptions()?; 716 | if primary { 717 | pe.set_self_as_primary_image(); 718 | } 719 | pe.execute_tls_callbacks(DLL_PROCESS_ATTACH)?; 720 | pe.handle_tls_data()?; 721 | Ok(pe) 722 | } 723 | 724 | /// Loads the entry point from a mapped file 725 | /// 726 | /// # Arguments 727 | /// 728 | /// `file`: The mapped executable file 729 | fn load_entry_point( 730 | file: &mut MappedFile, 731 | nt_headers: &IMAGE_NT_HEADERS64, 732 | ) -> Result { 733 | let address_of_ep = nt_headers.OptionalHeader.AddressOfEntryPoint; 734 | if address_of_ep == 0 { 735 | // PEs can sometimes not have entry points 736 | return Ok(None); 737 | } 738 | unsafe { 739 | // we transmute here because I have no earthly idea how to return a generic function 740 | let entry_point: PLDR_INIT_ROUTINE = std::mem::transmute( 741 | file.get_rva::(address_of_ep as isize) 742 | .ok_or(Error::EPOutOfBounds)?, 743 | ); 744 | Ok(entry_point) 745 | } 746 | } 747 | 748 | /// Loads the headers from a mapped file 749 | /// 750 | /// # Arguments 751 | /// 752 | /// `file`: The mapped executable file 753 | fn load_headers( 754 | file: &mut MappedFile, 755 | ) -> Result<(&'a IMAGE_NT_HEADERS64, &'a [IMAGE_SECTION_HEADER]), Error> { 756 | unsafe { 757 | let dos_header = &*file 758 | .get_rva::(0) 759 | .ok_or(Error::DOSOutOfBounds)?; 760 | let nt_headers = &*file 761 | .get_rva::(dos_header.e_lfanew as isize) 762 | .ok_or(Error::NTOutOfBounds)?; 763 | // ensure supported architecture 764 | if nt_headers.FileHeader.Machine != IMAGE_FILE_MACHINE_AMD64 765 | || nt_headers.OptionalHeader.Magic != IMAGE_NT_OPTIONAL_HDR64_MAGIC 766 | { 767 | return Err(Error::UnsupportedArch); 768 | } 769 | // load section headers 770 | let section_headers = std::slice::from_raw_parts( 771 | file.get_rva_size_chk::( 772 | dos_header.e_lfanew as isize 773 | + std::mem::size_of::() as isize, 774 | nt_headers.FileHeader.NumberOfSections as usize 775 | * std::mem::size_of::(), 776 | ) 777 | .ok_or(Error::SectOutOfBounds)?, 778 | nt_headers.FileHeader.NumberOfSections as usize, 779 | ); 780 | Ok((nt_headers, section_headers)) 781 | } 782 | } 783 | 784 | /// Returns the handle for the loaded process 785 | pub fn module_handle(&mut self) -> HMODULE { 786 | self.file.contents() as HMODULE 787 | } 788 | 789 | /// Protects all of the sections with their specified protections 790 | fn protect_sections(&mut self) -> Result<(), anyhow::Error> { 791 | for §ion in self.section_headers { 792 | unsafe { 793 | self.section_protections.push(ProtectionGuard::new( 794 | self.file 795 | .get_rva_mut::(section.VirtualAddress as isize) 796 | .ok_or(Error::SectOutOfBounds)?, 797 | *section.Misc.VirtualSize() as usize, 798 | PortableExecutable::section_flags_to_prot(section.Characteristics), 799 | )?) 800 | } 801 | } 802 | Ok(()) 803 | } 804 | 805 | /// Resolves imports from the NT headers 806 | fn resolve_imports(&mut self) -> anyhow::Result<()> { 807 | unsafe { 808 | let iat_directory = &self.nt_headers.OptionalHeader.DataDirectory 809 | [IMAGE_DIRECTORY_ENTRY_IMPORT as usize]; 810 | // if there is not an IAT, just return 811 | if iat_directory.VirtualAddress == 0 { 812 | return Ok(()); 813 | } 814 | // grab the IAT from the header. ensure there is space for the entire directory 815 | let iat_entry = self 816 | .file 817 | .get_rva_size_chk::( 818 | iat_directory.VirtualAddress as isize, 819 | iat_directory.Size as usize, 820 | ) 821 | .ok_or(Error::IDOutOfBounds)?; 822 | // ignore the alignment check here to better support packed/encrypted executables 823 | self.resolve_import_descriptors(&*ptr::slice_from_raw_parts( 824 | iat_entry, 825 | (iat_directory.Size as usize) / std::mem::size_of::(), 826 | )) 827 | } 828 | } 829 | 830 | /// Resolves imports from the specified import descriptors. This 831 | /// is separate because some executables have separate IATs from the 832 | /// NT header IATs, and it is necessary to resolve them as well 833 | /// 834 | /// # Arguments 835 | /// 836 | /// `table`: The import descriptors 837 | pub fn resolve_import_descriptors( 838 | &mut self, 839 | table: &[IMAGE_IMPORT_DESCRIPTOR], 840 | ) -> anyhow::Result<()> { 841 | // we know the table is not null 842 | for &entry in table { 843 | // ignore empty entries 844 | if entry.Name == 0 { 845 | continue; 846 | } 847 | // load the name of the import. here we could crash if the string was on the edge 848 | // of the page. but that's a waste to check for every byte 849 | let name = self 850 | .file 851 | .get_rva::(entry.Name as isize) 852 | .ok_or(Error::LibNameOutOfBounds)?; 853 | unsafe { 854 | debug!("Loading library {:?}", CStr::from_ptr(name)); 855 | let library = LoadLibraryA(name); 856 | if library.is_null() { 857 | return Err(std::io::Error::last_os_error().into()); 858 | } 859 | // skip empty tables 860 | if entry.FirstThunk == 0 { 861 | continue; 862 | } 863 | let mut thunk = self 864 | .file 865 | .get_rva_mut::(entry.FirstThunk as isize) 866 | .ok_or(Error::IATOutOfBounds)?; 867 | while (*thunk).u1.AddressOfData() != &0 { 868 | let proc_name = if ((*thunk).u1.Ordinal() & IMAGE_ORDINAL_FLAG) != 0 { 869 | (*(*thunk).u1.Ordinal() & !IMAGE_ORDINAL_FLAG) as *const i8 870 | } else { 871 | &(*self 872 | .file 873 | .get_rva::(*(*thunk).u1.AddressOfData() as isize) 874 | .ok_or(Error::ProcNameOutOfBounds)?) 875 | .Name as *const i8 876 | }; 877 | if ((*thunk).u1.Ordinal() & IMAGE_ORDINAL_FLAG) != 0 { 878 | debug!( 879 | "Loading procedure with ordinal {}", 880 | (*thunk).u1.Ordinal() & !IMAGE_ORDINAL_FLAG 881 | ); 882 | } else { 883 | // if it is a pointer, make sure it is not null 884 | if proc_name.is_null() { 885 | return Err(Error::NullProcName.into()); 886 | } 887 | debug!( 888 | "Loading procedure with name {:?}", 889 | CStr::from_ptr(proc_name) 890 | ); 891 | } 892 | // load the function 893 | let func = GetProcAddress(library, proc_name); 894 | if func.is_null() { 895 | return Err(std::io::Error::last_os_error().into()); 896 | } 897 | let func_ptr = (*thunk).u1.Function_mut() as *mut ULONGLONG; 898 | // write the function address 899 | protected_write(func_ptr, func as ULONGLONG)?; 900 | thunk = thunk.add(1); 901 | } 902 | } 903 | } 904 | Ok(()) 905 | } 906 | 907 | /// Runs the executable's entry point with `DLL_PROCESS_ATTACH` 908 | /// 909 | /// # Safety 910 | /// 911 | /// The safety of this function is entirely dependent on whether or not 912 | /// the underlying executable is safe. This function panics if the executable 913 | /// entry point has already been called 914 | pub unsafe fn run(&mut self) -> u8 { 915 | if self.called_entry_point { 916 | panic!("OEP already called") 917 | } 918 | let res = self.call_entry_point(DLL_PROCESS_ATTACH); 919 | self.called_entry_point = true; 920 | res 921 | } 922 | 923 | /// Converts section flags to page protection flags 924 | /// 925 | /// # Arguments 926 | /// 927 | /// `section_flags`: The section flags 928 | fn section_flags_to_prot(section_flags: u32) -> u32 { 929 | if section_flags & IMAGE_SCN_MEM_EXECUTE != 0 { 930 | PAGE_EXECUTE_READ 931 | } else if section_flags & IMAGE_SCN_MEM_WRITE != 0 { 932 | PAGE_READWRITE 933 | } else { 934 | PAGE_READONLY 935 | } 936 | } 937 | 938 | /// Sets this mapped image as the primary image, 939 | /// as if by calling `set_as_primary_image(self.module_handle())`. 940 | fn set_self_as_primary_image(&mut self) { 941 | self.last_primary = 942 | Some(unsafe { set_as_primary_image(self.module_handle() as *mut c_void) }); 943 | } 944 | } 945 | 946 | impl<'a> Drop for PortableExecutable<'a> { 947 | // unload the loader entry. this is used for GetModuleHandle 948 | fn drop(&mut self) { 949 | // call each tls callback with process_detach 950 | if let Err(e) = self.execute_tls_callbacks(DLL_PROCESS_DETACH) { 951 | debug!("Failed to execute TLS callbacks on exit: {}", e.to_string()) 952 | }; 953 | // call the entry point with detach 954 | unsafe { self.call_entry_point(DLL_PROCESS_DETACH) }; 955 | // disable exceptions afterwards in case a TLS callback uses them 956 | if let Err(e) = self.disable_exceptions() { 957 | debug!("Failed to disable exceptions: {}", e.to_string()) 958 | } 959 | // remove ourselves from the PEB 960 | if self.added_to_peb { 961 | self.remove_from_peb(); 962 | } 963 | // remove ourselves from the index RB trees 964 | if self.added_to_index { 965 | self.remove_from_index(); 966 | } 967 | // remove ourselves from the hash table 968 | if self.added_to_hash_tbl { 969 | self.remove_from_hash_table(); 970 | } 971 | // free TLS data 972 | if let Err(e) = self.free_tls_data() { 973 | debug!("Failed to free TLS data: {}", e.to_string()) 974 | } 975 | // ensure we are not the primary image anymore 976 | if let Some(last_image) = self.last_primary { 977 | unsafe { set_as_primary_image(last_image) }; 978 | } 979 | } 980 | } 981 | 982 | unsafe impl<'a> Send for PortableExecutable<'a> {} 983 | 984 | #[cfg(test)] 985 | mod test { 986 | use super::*; 987 | use lazy_static::lazy_static; 988 | use serial_test::serial; 989 | use std::ptr::null; 990 | use tokio::runtime::Runtime; 991 | use winapi::{ 992 | shared::winerror::{ERROR_BAD_EXE_FORMAT, ERROR_MOD_NOT_FOUND, ERROR_PROC_NOT_FOUND}, 993 | um::libloaderapi::GetModuleFileNameW, 994 | }; 995 | 996 | lazy_static! { 997 | static ref NT_CONTEXT: NtContext = Runtime::new() 998 | .unwrap() 999 | .block_on(NtContext::resolve_local( 1000 | &OffsetHandler::new("test/cache.json").unwrap() 1001 | )) 1002 | .unwrap(); 1003 | } 1004 | 1005 | #[test] 1006 | #[serial] 1007 | fn bad_dos() { 1008 | let err: std::io::Error = PortableExecutable::load("test/baddos.exe", NT_CONTEXT.clone()) 1009 | .err() 1010 | .unwrap() 1011 | .downcast() 1012 | .unwrap(); 1013 | assert_eq!(err.raw_os_error().unwrap(), ERROR_BAD_EXE_FORMAT as i32); 1014 | } 1015 | 1016 | #[test] 1017 | #[serial] 1018 | fn bad_section() { 1019 | let err: std::io::Error = 1020 | PortableExecutable::load("test/badsection.exe", NT_CONTEXT.clone()) 1021 | .err() 1022 | .unwrap() 1023 | .downcast() 1024 | .unwrap(); 1025 | assert_eq!(err.raw_os_error().unwrap(), ERROR_BAD_EXE_FORMAT as i32); 1026 | } 1027 | 1028 | #[test] 1029 | #[serial] 1030 | fn bad_entry() { 1031 | let err: Error = PortableExecutable::load("test/badentry.exe", NT_CONTEXT.clone()) 1032 | .err() 1033 | .unwrap() 1034 | .downcast() 1035 | .unwrap(); 1036 | assert_eq!(err, Error::EPOutOfBounds); 1037 | } 1038 | 1039 | #[test] 1040 | #[serial] 1041 | fn bad_mod() { 1042 | let err: std::io::Error = PortableExecutable::load("test/badmod.exe", NT_CONTEXT.clone()) 1043 | .err() 1044 | .unwrap() 1045 | .downcast() 1046 | .unwrap(); 1047 | assert_eq!(err.raw_os_error().unwrap(), ERROR_MOD_NOT_FOUND as i32); 1048 | } 1049 | 1050 | #[test] 1051 | #[serial] 1052 | fn bad_proc() { 1053 | let err: std::io::Error = PortableExecutable::load("test/badproc.exe", NT_CONTEXT.clone()) 1054 | .err() 1055 | .unwrap() 1056 | .downcast() 1057 | .unwrap(); 1058 | assert_eq!(err.raw_os_error().unwrap(), ERROR_PROC_NOT_FOUND as i32); 1059 | } 1060 | 1061 | // we only support x86-64/ARM64 for now 1062 | #[test] 1063 | #[serial] 1064 | fn bad_arch() { 1065 | let err: Error = PortableExecutable::load("test/x86.exe", NT_CONTEXT.clone()) 1066 | .err() 1067 | .unwrap() 1068 | .downcast() 1069 | .unwrap(); 1070 | assert_eq!(err, Error::UnsupportedArch); 1071 | } 1072 | 1073 | #[test] 1074 | #[serial] 1075 | fn basic_image() { 1076 | let mut image = PortableExecutable::load("test/basic.exe", NT_CONTEXT.clone()).unwrap(); 1077 | unsafe { 1078 | assert_eq!(image.run(), 23); 1079 | } 1080 | } 1081 | 1082 | #[test] 1083 | #[serial] 1084 | fn ldr_entry() { 1085 | let mut file_name_buf = [0 as u16; 128]; 1086 | let handle; 1087 | { 1088 | let _image = PortableExecutable::load("test/basic.exe", NT_CONTEXT.clone()).unwrap(); 1089 | handle = unsafe { GetModuleHandleW(to_wide("basic.exe").as_ptr()) }; 1090 | assert!(!handle.is_null()); 1091 | let name_len = unsafe { 1092 | GetModuleFileNameW( 1093 | handle, 1094 | file_name_buf.as_mut_ptr(), 1095 | file_name_buf.len() as u32, 1096 | ) 1097 | }; 1098 | assert_ne!(name_len, 0); 1099 | } 1100 | assert!(unsafe { GetModuleHandleW(to_wide("basic.exe").as_ptr()) }.is_null()); 1101 | assert_eq!( 1102 | unsafe { 1103 | GetModuleFileNameW( 1104 | handle, 1105 | file_name_buf.as_mut_ptr(), 1106 | file_name_buf.len() as u32, 1107 | ) 1108 | }, 1109 | 0 1110 | ); 1111 | } 1112 | 1113 | #[test] 1114 | #[serial] 1115 | fn tls() { 1116 | let mut image = PortableExecutable::load("test/tls.exe", NT_CONTEXT.clone()).unwrap(); 1117 | unsafe { 1118 | assert_eq!(image.run(), 7); 1119 | } 1120 | } 1121 | 1122 | #[test] 1123 | #[serial] 1124 | fn primary_image() { 1125 | let original_handle = unsafe { GetModuleHandleW(null()) }; 1126 | { 1127 | let mut image = PortableExecutable::load("test/basic.exe", NT_CONTEXT.clone()).unwrap(); 1128 | assert_ne!(image.module_handle(), unsafe { GetModuleHandleW(null()) }); 1129 | image.set_self_as_primary_image(); 1130 | assert_eq!(image.module_handle(), unsafe { GetModuleHandleW(null()) }); 1131 | } 1132 | assert_eq!(original_handle, unsafe { GetModuleHandleW(null()) }); 1133 | } 1134 | 1135 | #[tokio::test] 1136 | #[serial] 1137 | #[cfg(feature = "server")] 1138 | async fn tls_server() { 1139 | use crate::server::Server; 1140 | use std::net::{Ipv4Addr, SocketAddr, SocketAddrV4}; 1141 | use tonic::transport::Identity; 1142 | // start the server 1143 | let endpoint = SocketAddr::V4(SocketAddrV4::new(Ipv4Addr::new(127, 0, 0, 1), 44443)); 1144 | // we stole these certificates from the tonic test suite 1145 | let cert = tokio::fs::read("test/server.pem").await.unwrap(); 1146 | let key = tokio::fs::read("test/server.key").await.unwrap(); 1147 | let identity = Identity::from_pem(cert, key); 1148 | let server = Server::new(endpoint, "test/tls_cache.json", Some(identity)).unwrap(); 1149 | // resolve 1150 | let ca_cert = tokio::fs::read("test/ca.pem").await.unwrap(); 1151 | let ca_cert = Certificate::from_pem(ca_cert); 1152 | tokio::select! { 1153 | _ = server.run() => {}, 1154 | res = NtContext::resolve_tls("localhost", 44443, Some(ca_cert), None) => { res.unwrap(); } 1155 | }; 1156 | } 1157 | } 1158 | -------------------------------------------------------------------------------- /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 = "addr2line" 7 | version = "0.22.0" 8 | source = "registry+https://github.com/rust-lang/crates.io-index" 9 | checksum = "6e4503c46a5c0c7844e948c9a4d6acd9f50cccb4de1c48eb9e291ea17470c678" 10 | dependencies = [ 11 | "gimli", 12 | ] 13 | 14 | [[package]] 15 | name = "adler" 16 | version = "1.0.2" 17 | source = "registry+https://github.com/rust-lang/crates.io-index" 18 | checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" 19 | 20 | [[package]] 21 | name = "aho-corasick" 22 | version = "1.1.3" 23 | source = "registry+https://github.com/rust-lang/crates.io-index" 24 | checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" 25 | dependencies = [ 26 | "memchr", 27 | ] 28 | 29 | [[package]] 30 | name = "anyhow" 31 | version = "1.0.86" 32 | source = "registry+https://github.com/rust-lang/crates.io-index" 33 | checksum = "b3d1d046238990b9cf5bcde22a3fb3584ee5cf65fb2765f454ed428c7a0063da" 34 | 35 | [[package]] 36 | name = "async-stream" 37 | version = "0.3.5" 38 | source = "registry+https://github.com/rust-lang/crates.io-index" 39 | checksum = "cd56dd203fef61ac097dd65721a419ddccb106b2d2b70ba60a6b529f03961a51" 40 | dependencies = [ 41 | "async-stream-impl", 42 | "futures-core", 43 | "pin-project-lite", 44 | ] 45 | 46 | [[package]] 47 | name = "async-stream-impl" 48 | version = "0.3.5" 49 | source = "registry+https://github.com/rust-lang/crates.io-index" 50 | checksum = "16e62a023e7c117e27523144c5d2459f4397fcc3cab0085af8e2224f643a0193" 51 | dependencies = [ 52 | "proc-macro2", 53 | "quote", 54 | "syn", 55 | ] 56 | 57 | [[package]] 58 | name = "async-trait" 59 | version = "0.1.81" 60 | source = "registry+https://github.com/rust-lang/crates.io-index" 61 | checksum = "6e0c28dcc82d7c8ead5cb13beb15405b57b8546e93215673ff8ca0349a028107" 62 | dependencies = [ 63 | "proc-macro2", 64 | "quote", 65 | "syn", 66 | ] 67 | 68 | [[package]] 69 | name = "atomic-waker" 70 | version = "1.1.2" 71 | source = "registry+https://github.com/rust-lang/crates.io-index" 72 | checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" 73 | 74 | [[package]] 75 | name = "autocfg" 76 | version = "1.3.0" 77 | source = "registry+https://github.com/rust-lang/crates.io-index" 78 | checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0" 79 | 80 | [[package]] 81 | name = "axum" 82 | version = "0.6.20" 83 | source = "registry+https://github.com/rust-lang/crates.io-index" 84 | checksum = "3b829e4e32b91e643de6eafe82b1d90675f5874230191a4ffbc1b336dec4d6bf" 85 | dependencies = [ 86 | "async-trait", 87 | "axum-core", 88 | "bitflags 1.3.2", 89 | "bytes", 90 | "futures-util", 91 | "http 0.2.12", 92 | "http-body 0.4.6", 93 | "hyper 0.14.30", 94 | "itoa", 95 | "matchit", 96 | "memchr", 97 | "mime", 98 | "percent-encoding", 99 | "pin-project-lite", 100 | "rustversion", 101 | "serde", 102 | "sync_wrapper 0.1.2", 103 | "tower", 104 | "tower-layer", 105 | "tower-service", 106 | ] 107 | 108 | [[package]] 109 | name = "axum-core" 110 | version = "0.3.4" 111 | source = "registry+https://github.com/rust-lang/crates.io-index" 112 | checksum = "759fa577a247914fd3f7f76d62972792636412fbfd634cd452f6a385a74d2d2c" 113 | dependencies = [ 114 | "async-trait", 115 | "bytes", 116 | "futures-util", 117 | "http 0.2.12", 118 | "http-body 0.4.6", 119 | "mime", 120 | "rustversion", 121 | "tower-layer", 122 | "tower-service", 123 | ] 124 | 125 | [[package]] 126 | name = "backtrace" 127 | version = "0.3.73" 128 | source = "registry+https://github.com/rust-lang/crates.io-index" 129 | checksum = "5cc23269a4f8976d0a4d2e7109211a419fe30e8d88d677cd60b6bc79c5732e0a" 130 | dependencies = [ 131 | "addr2line", 132 | "cc", 133 | "cfg-if", 134 | "libc", 135 | "miniz_oxide", 136 | "object", 137 | "rustc-demangle", 138 | ] 139 | 140 | [[package]] 141 | name = "base64" 142 | version = "0.21.7" 143 | source = "registry+https://github.com/rust-lang/crates.io-index" 144 | checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567" 145 | 146 | [[package]] 147 | name = "base64" 148 | version = "0.22.1" 149 | source = "registry+https://github.com/rust-lang/crates.io-index" 150 | checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" 151 | 152 | [[package]] 153 | name = "bitflags" 154 | version = "1.3.2" 155 | source = "registry+https://github.com/rust-lang/crates.io-index" 156 | checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" 157 | 158 | [[package]] 159 | name = "bitflags" 160 | version = "2.6.0" 161 | source = "registry+https://github.com/rust-lang/crates.io-index" 162 | checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" 163 | 164 | [[package]] 165 | name = "bumpalo" 166 | version = "3.16.0" 167 | source = "registry+https://github.com/rust-lang/crates.io-index" 168 | checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" 169 | 170 | [[package]] 171 | name = "byteorder" 172 | version = "1.5.0" 173 | source = "registry+https://github.com/rust-lang/crates.io-index" 174 | checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" 175 | 176 | [[package]] 177 | name = "bytes" 178 | version = "1.7.0" 179 | source = "registry+https://github.com/rust-lang/crates.io-index" 180 | checksum = "fca2be1d5c43812bae364ee3f30b3afcb7877cf59f4aeb94c66f313a41d2fac9" 181 | 182 | [[package]] 183 | name = "cc" 184 | version = "1.1.7" 185 | source = "registry+https://github.com/rust-lang/crates.io-index" 186 | checksum = "26a5c3fd7bfa1ce3897a3a3501d362b2d87b7f2583ebcb4a949ec25911025cbc" 187 | 188 | [[package]] 189 | name = "cfg-if" 190 | version = "1.0.0" 191 | source = "registry+https://github.com/rust-lang/crates.io-index" 192 | checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" 193 | 194 | [[package]] 195 | name = "colored" 196 | version = "2.1.0" 197 | source = "registry+https://github.com/rust-lang/crates.io-index" 198 | checksum = "cbf2150cce219b664a8a70df7a1f933836724b503f8a413af9365b4dcc4d90b8" 199 | dependencies = [ 200 | "lazy_static", 201 | "windows-sys 0.48.0", 202 | ] 203 | 204 | [[package]] 205 | name = "core-foundation" 206 | version = "0.9.4" 207 | source = "registry+https://github.com/rust-lang/crates.io-index" 208 | checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f" 209 | dependencies = [ 210 | "core-foundation-sys", 211 | "libc", 212 | ] 213 | 214 | [[package]] 215 | name = "core-foundation-sys" 216 | version = "0.8.6" 217 | source = "registry+https://github.com/rust-lang/crates.io-index" 218 | checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f" 219 | 220 | [[package]] 221 | name = "deranged" 222 | version = "0.3.11" 223 | source = "registry+https://github.com/rust-lang/crates.io-index" 224 | checksum = "b42b6fa04a440b495c8b04d0e71b707c585f83cb9cb28cf8cd0d976c315e31b4" 225 | dependencies = [ 226 | "powerfmt", 227 | ] 228 | 229 | [[package]] 230 | name = "either" 231 | version = "1.13.0" 232 | source = "registry+https://github.com/rust-lang/crates.io-index" 233 | checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0" 234 | 235 | [[package]] 236 | name = "encoding_rs" 237 | version = "0.8.34" 238 | source = "registry+https://github.com/rust-lang/crates.io-index" 239 | checksum = "b45de904aa0b010bce2ab45264d0631681847fa7b6f2eaa7dab7619943bc4f59" 240 | dependencies = [ 241 | "cfg-if", 242 | ] 243 | 244 | [[package]] 245 | name = "equivalent" 246 | version = "1.0.1" 247 | source = "registry+https://github.com/rust-lang/crates.io-index" 248 | checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" 249 | 250 | [[package]] 251 | name = "errno" 252 | version = "0.3.9" 253 | source = "registry+https://github.com/rust-lang/crates.io-index" 254 | checksum = "534c5cf6194dfab3db3242765c03bbe257cf92f22b38f6bc0c58d59108a820ba" 255 | dependencies = [ 256 | "libc", 257 | "windows-sys 0.52.0", 258 | ] 259 | 260 | [[package]] 261 | name = "fallible-iterator" 262 | version = "0.2.0" 263 | source = "registry+https://github.com/rust-lang/crates.io-index" 264 | checksum = "4443176a9f2c162692bd3d352d745ef9413eec5782a80d8fd6f8a1ac692a07f7" 265 | 266 | [[package]] 267 | name = "fastrand" 268 | version = "2.1.0" 269 | source = "registry+https://github.com/rust-lang/crates.io-index" 270 | checksum = "9fc0510504f03c51ada170672ac806f1f105a88aa97a5281117e1ddc3368e51a" 271 | 272 | [[package]] 273 | name = "fixedbitset" 274 | version = "0.4.2" 275 | source = "registry+https://github.com/rust-lang/crates.io-index" 276 | checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80" 277 | 278 | [[package]] 279 | name = "fnv" 280 | version = "1.0.7" 281 | source = "registry+https://github.com/rust-lang/crates.io-index" 282 | checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" 283 | 284 | [[package]] 285 | name = "foreign-types" 286 | version = "0.3.2" 287 | source = "registry+https://github.com/rust-lang/crates.io-index" 288 | checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" 289 | dependencies = [ 290 | "foreign-types-shared", 291 | ] 292 | 293 | [[package]] 294 | name = "foreign-types-shared" 295 | version = "0.1.1" 296 | source = "registry+https://github.com/rust-lang/crates.io-index" 297 | checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" 298 | 299 | [[package]] 300 | name = "form_urlencoded" 301 | version = "1.2.1" 302 | source = "registry+https://github.com/rust-lang/crates.io-index" 303 | checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456" 304 | dependencies = [ 305 | "percent-encoding", 306 | ] 307 | 308 | [[package]] 309 | name = "futures" 310 | version = "0.3.30" 311 | source = "registry+https://github.com/rust-lang/crates.io-index" 312 | checksum = "645c6916888f6cb6350d2550b80fb63e734897a8498abe35cfb732b6487804b0" 313 | dependencies = [ 314 | "futures-channel", 315 | "futures-core", 316 | "futures-executor", 317 | "futures-io", 318 | "futures-sink", 319 | "futures-task", 320 | "futures-util", 321 | ] 322 | 323 | [[package]] 324 | name = "futures-channel" 325 | version = "0.3.30" 326 | source = "registry+https://github.com/rust-lang/crates.io-index" 327 | checksum = "eac8f7d7865dcb88bd4373ab671c8cf4508703796caa2b1985a9ca867b3fcb78" 328 | dependencies = [ 329 | "futures-core", 330 | "futures-sink", 331 | ] 332 | 333 | [[package]] 334 | name = "futures-core" 335 | version = "0.3.30" 336 | source = "registry+https://github.com/rust-lang/crates.io-index" 337 | checksum = "dfc6580bb841c5a68e9ef15c77ccc837b40a7504914d52e47b8b0e9bbda25a1d" 338 | 339 | [[package]] 340 | name = "futures-executor" 341 | version = "0.3.30" 342 | source = "registry+https://github.com/rust-lang/crates.io-index" 343 | checksum = "a576fc72ae164fca6b9db127eaa9a9dda0d61316034f33a0a0d4eda41f02b01d" 344 | dependencies = [ 345 | "futures-core", 346 | "futures-task", 347 | "futures-util", 348 | ] 349 | 350 | [[package]] 351 | name = "futures-io" 352 | version = "0.3.30" 353 | source = "registry+https://github.com/rust-lang/crates.io-index" 354 | checksum = "a44623e20b9681a318efdd71c299b6b222ed6f231972bfe2f224ebad6311f0c1" 355 | 356 | [[package]] 357 | name = "futures-sink" 358 | version = "0.3.30" 359 | source = "registry+https://github.com/rust-lang/crates.io-index" 360 | checksum = "9fb8e00e87438d937621c1c6269e53f536c14d3fbd6a042bb24879e57d474fb5" 361 | 362 | [[package]] 363 | name = "futures-task" 364 | version = "0.3.30" 365 | source = "registry+https://github.com/rust-lang/crates.io-index" 366 | checksum = "38d84fa142264698cdce1a9f9172cf383a0c82de1bddcf3092901442c4097004" 367 | 368 | [[package]] 369 | name = "futures-util" 370 | version = "0.3.30" 371 | source = "registry+https://github.com/rust-lang/crates.io-index" 372 | checksum = "3d6401deb83407ab3da39eba7e33987a73c3df0c82b4bb5813ee871c19c41d48" 373 | dependencies = [ 374 | "futures-channel", 375 | "futures-core", 376 | "futures-io", 377 | "futures-sink", 378 | "futures-task", 379 | "memchr", 380 | "pin-project-lite", 381 | "pin-utils", 382 | "slab", 383 | ] 384 | 385 | [[package]] 386 | name = "getrandom" 387 | version = "0.2.15" 388 | source = "registry+https://github.com/rust-lang/crates.io-index" 389 | checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" 390 | dependencies = [ 391 | "cfg-if", 392 | "libc", 393 | "wasi", 394 | ] 395 | 396 | [[package]] 397 | name = "gimli" 398 | version = "0.29.0" 399 | source = "registry+https://github.com/rust-lang/crates.io-index" 400 | checksum = "40ecd4077b5ae9fd2e9e169b102c6c330d0605168eb0e8bf79952b256dbefffd" 401 | 402 | [[package]] 403 | name = "h2" 404 | version = "0.3.26" 405 | source = "registry+https://github.com/rust-lang/crates.io-index" 406 | checksum = "81fe527a889e1532da5c525686d96d4c2e74cdd345badf8dfef9f6b39dd5f5e8" 407 | dependencies = [ 408 | "bytes", 409 | "fnv", 410 | "futures-core", 411 | "futures-sink", 412 | "futures-util", 413 | "http 0.2.12", 414 | "indexmap 2.3.0", 415 | "slab", 416 | "tokio", 417 | "tokio-util", 418 | "tracing", 419 | ] 420 | 421 | [[package]] 422 | name = "h2" 423 | version = "0.4.5" 424 | source = "registry+https://github.com/rust-lang/crates.io-index" 425 | checksum = "fa82e28a107a8cc405f0839610bdc9b15f1e25ec7d696aa5cf173edbcb1486ab" 426 | dependencies = [ 427 | "atomic-waker", 428 | "bytes", 429 | "fnv", 430 | "futures-core", 431 | "futures-sink", 432 | "http 1.1.0", 433 | "indexmap 2.3.0", 434 | "slab", 435 | "tokio", 436 | "tokio-util", 437 | "tracing", 438 | ] 439 | 440 | [[package]] 441 | name = "hashbrown" 442 | version = "0.12.3" 443 | source = "registry+https://github.com/rust-lang/crates.io-index" 444 | checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" 445 | 446 | [[package]] 447 | name = "hashbrown" 448 | version = "0.14.5" 449 | source = "registry+https://github.com/rust-lang/crates.io-index" 450 | checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" 451 | 452 | [[package]] 453 | name = "heck" 454 | version = "0.5.0" 455 | source = "registry+https://github.com/rust-lang/crates.io-index" 456 | checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" 457 | 458 | [[package]] 459 | name = "hermit-abi" 460 | version = "0.3.9" 461 | source = "registry+https://github.com/rust-lang/crates.io-index" 462 | checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" 463 | 464 | [[package]] 465 | name = "http" 466 | version = "0.2.12" 467 | source = "registry+https://github.com/rust-lang/crates.io-index" 468 | checksum = "601cbb57e577e2f5ef5be8e7b83f0f63994f25aa94d673e54a92d5c516d101f1" 469 | dependencies = [ 470 | "bytes", 471 | "fnv", 472 | "itoa", 473 | ] 474 | 475 | [[package]] 476 | name = "http" 477 | version = "1.1.0" 478 | source = "registry+https://github.com/rust-lang/crates.io-index" 479 | checksum = "21b9ddb458710bc376481b842f5da65cdf31522de232c1ca8146abce2a358258" 480 | dependencies = [ 481 | "bytes", 482 | "fnv", 483 | "itoa", 484 | ] 485 | 486 | [[package]] 487 | name = "http-body" 488 | version = "0.4.6" 489 | source = "registry+https://github.com/rust-lang/crates.io-index" 490 | checksum = "7ceab25649e9960c0311ea418d17bee82c0dcec1bd053b5f9a66e265a693bed2" 491 | dependencies = [ 492 | "bytes", 493 | "http 0.2.12", 494 | "pin-project-lite", 495 | ] 496 | 497 | [[package]] 498 | name = "http-body" 499 | version = "1.0.1" 500 | source = "registry+https://github.com/rust-lang/crates.io-index" 501 | checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184" 502 | dependencies = [ 503 | "bytes", 504 | "http 1.1.0", 505 | ] 506 | 507 | [[package]] 508 | name = "http-body-util" 509 | version = "0.1.2" 510 | source = "registry+https://github.com/rust-lang/crates.io-index" 511 | checksum = "793429d76616a256bcb62c2a2ec2bed781c8307e797e2598c50010f2bee2544f" 512 | dependencies = [ 513 | "bytes", 514 | "futures-util", 515 | "http 1.1.0", 516 | "http-body 1.0.1", 517 | "pin-project-lite", 518 | ] 519 | 520 | [[package]] 521 | name = "httparse" 522 | version = "1.9.4" 523 | source = "registry+https://github.com/rust-lang/crates.io-index" 524 | checksum = "0fcc0b4a115bf80b728eb8ea024ad5bd707b615bfed49e0665b6e0f86fd082d9" 525 | 526 | [[package]] 527 | name = "httpdate" 528 | version = "1.0.3" 529 | source = "registry+https://github.com/rust-lang/crates.io-index" 530 | checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" 531 | 532 | [[package]] 533 | name = "hyper" 534 | version = "0.14.30" 535 | source = "registry+https://github.com/rust-lang/crates.io-index" 536 | checksum = "a152ddd61dfaec7273fe8419ab357f33aee0d914c5f4efbf0d96fa749eea5ec9" 537 | dependencies = [ 538 | "bytes", 539 | "futures-channel", 540 | "futures-core", 541 | "futures-util", 542 | "h2 0.3.26", 543 | "http 0.2.12", 544 | "http-body 0.4.6", 545 | "httparse", 546 | "httpdate", 547 | "itoa", 548 | "pin-project-lite", 549 | "socket2", 550 | "tokio", 551 | "tower-service", 552 | "tracing", 553 | "want", 554 | ] 555 | 556 | [[package]] 557 | name = "hyper" 558 | version = "1.4.1" 559 | source = "registry+https://github.com/rust-lang/crates.io-index" 560 | checksum = "50dfd22e0e76d0f662d429a5f80fcaf3855009297eab6a0a9f8543834744ba05" 561 | dependencies = [ 562 | "bytes", 563 | "futures-channel", 564 | "futures-util", 565 | "h2 0.4.5", 566 | "http 1.1.0", 567 | "http-body 1.0.1", 568 | "httparse", 569 | "itoa", 570 | "pin-project-lite", 571 | "smallvec", 572 | "tokio", 573 | "want", 574 | ] 575 | 576 | [[package]] 577 | name = "hyper-rustls" 578 | version = "0.27.2" 579 | source = "registry+https://github.com/rust-lang/crates.io-index" 580 | checksum = "5ee4be2c948921a1a5320b629c4193916ed787a7f7f293fd3f7f5a6c9de74155" 581 | dependencies = [ 582 | "futures-util", 583 | "http 1.1.0", 584 | "hyper 1.4.1", 585 | "hyper-util", 586 | "rustls 0.23.12", 587 | "rustls-pki-types", 588 | "tokio", 589 | "tokio-rustls 0.26.0", 590 | "tower-service", 591 | ] 592 | 593 | [[package]] 594 | name = "hyper-timeout" 595 | version = "0.4.1" 596 | source = "registry+https://github.com/rust-lang/crates.io-index" 597 | checksum = "bbb958482e8c7be4bc3cf272a766a2b0bf1a6755e7a6ae777f017a31d11b13b1" 598 | dependencies = [ 599 | "hyper 0.14.30", 600 | "pin-project-lite", 601 | "tokio", 602 | "tokio-io-timeout", 603 | ] 604 | 605 | [[package]] 606 | name = "hyper-tls" 607 | version = "0.6.0" 608 | source = "registry+https://github.com/rust-lang/crates.io-index" 609 | checksum = "70206fc6890eaca9fde8a0bf71caa2ddfc9fe045ac9e5c70df101a7dbde866e0" 610 | dependencies = [ 611 | "bytes", 612 | "http-body-util", 613 | "hyper 1.4.1", 614 | "hyper-util", 615 | "native-tls", 616 | "tokio", 617 | "tokio-native-tls", 618 | "tower-service", 619 | ] 620 | 621 | [[package]] 622 | name = "hyper-util" 623 | version = "0.1.6" 624 | source = "registry+https://github.com/rust-lang/crates.io-index" 625 | checksum = "3ab92f4f49ee4fb4f997c784b7a2e0fa70050211e0b6a287f898c3c9785ca956" 626 | dependencies = [ 627 | "bytes", 628 | "futures-channel", 629 | "futures-util", 630 | "http 1.1.0", 631 | "http-body 1.0.1", 632 | "hyper 1.4.1", 633 | "pin-project-lite", 634 | "socket2", 635 | "tokio", 636 | "tower", 637 | "tower-service", 638 | "tracing", 639 | ] 640 | 641 | [[package]] 642 | name = "idna" 643 | version = "0.5.0" 644 | source = "registry+https://github.com/rust-lang/crates.io-index" 645 | checksum = "634d9b1461af396cad843f47fdba5597a4f9e6ddd4bfb6ff5d85028c25cb12f6" 646 | dependencies = [ 647 | "unicode-bidi", 648 | "unicode-normalization", 649 | ] 650 | 651 | [[package]] 652 | name = "indexmap" 653 | version = "1.9.3" 654 | source = "registry+https://github.com/rust-lang/crates.io-index" 655 | checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" 656 | dependencies = [ 657 | "autocfg", 658 | "hashbrown 0.12.3", 659 | ] 660 | 661 | [[package]] 662 | name = "indexmap" 663 | version = "2.3.0" 664 | source = "registry+https://github.com/rust-lang/crates.io-index" 665 | checksum = "de3fc2e30ba82dd1b3911c8de1ffc143c74a914a14e99514d7637e3099df5ea0" 666 | dependencies = [ 667 | "equivalent", 668 | "hashbrown 0.14.5", 669 | ] 670 | 671 | [[package]] 672 | name = "ipnet" 673 | version = "2.9.0" 674 | source = "registry+https://github.com/rust-lang/crates.io-index" 675 | checksum = "8f518f335dce6725a761382244631d86cf0ccb2863413590b31338feb467f9c3" 676 | 677 | [[package]] 678 | name = "itertools" 679 | version = "0.12.1" 680 | source = "registry+https://github.com/rust-lang/crates.io-index" 681 | checksum = "ba291022dbbd398a455acf126c1e341954079855bc60dfdda641363bd6922569" 682 | dependencies = [ 683 | "either", 684 | ] 685 | 686 | [[package]] 687 | name = "itoa" 688 | version = "1.0.11" 689 | source = "registry+https://github.com/rust-lang/crates.io-index" 690 | checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" 691 | 692 | [[package]] 693 | name = "js-sys" 694 | version = "0.3.69" 695 | source = "registry+https://github.com/rust-lang/crates.io-index" 696 | checksum = "29c15563dc2726973df627357ce0c9ddddbea194836909d655df6a75d2cf296d" 697 | dependencies = [ 698 | "wasm-bindgen", 699 | ] 700 | 701 | [[package]] 702 | name = "lazy_static" 703 | version = "1.5.0" 704 | source = "registry+https://github.com/rust-lang/crates.io-index" 705 | checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" 706 | 707 | [[package]] 708 | name = "libc" 709 | version = "0.2.155" 710 | source = "registry+https://github.com/rust-lang/crates.io-index" 711 | checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c" 712 | 713 | [[package]] 714 | name = "linux-raw-sys" 715 | version = "0.4.14" 716 | source = "registry+https://github.com/rust-lang/crates.io-index" 717 | checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89" 718 | 719 | [[package]] 720 | name = "lock_api" 721 | version = "0.4.12" 722 | source = "registry+https://github.com/rust-lang/crates.io-index" 723 | checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17" 724 | dependencies = [ 725 | "autocfg", 726 | "scopeguard", 727 | ] 728 | 729 | [[package]] 730 | name = "log" 731 | version = "0.4.22" 732 | source = "registry+https://github.com/rust-lang/crates.io-index" 733 | checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" 734 | 735 | [[package]] 736 | name = "matchit" 737 | version = "0.7.3" 738 | source = "registry+https://github.com/rust-lang/crates.io-index" 739 | checksum = "0e7465ac9959cc2b1404e8e2367b43684a6d13790fe23056cc8c6c5a6b7bcb94" 740 | 741 | [[package]] 742 | name = "memchr" 743 | version = "2.7.4" 744 | source = "registry+https://github.com/rust-lang/crates.io-index" 745 | checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" 746 | 747 | [[package]] 748 | name = "memoffset" 749 | version = "0.9.1" 750 | source = "registry+https://github.com/rust-lang/crates.io-index" 751 | checksum = "488016bfae457b036d996092f6cb448677611ce4449e970ceaf42695203f218a" 752 | dependencies = [ 753 | "autocfg", 754 | ] 755 | 756 | [[package]] 757 | name = "mime" 758 | version = "0.3.17" 759 | source = "registry+https://github.com/rust-lang/crates.io-index" 760 | checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" 761 | 762 | [[package]] 763 | name = "miniz_oxide" 764 | version = "0.7.4" 765 | source = "registry+https://github.com/rust-lang/crates.io-index" 766 | checksum = "b8a240ddb74feaf34a79a7add65a741f3167852fba007066dcac1ca548d89c08" 767 | dependencies = [ 768 | "adler", 769 | ] 770 | 771 | [[package]] 772 | name = "mio" 773 | version = "1.0.1" 774 | source = "registry+https://github.com/rust-lang/crates.io-index" 775 | checksum = "4569e456d394deccd22ce1c1913e6ea0e54519f577285001215d33557431afe4" 776 | dependencies = [ 777 | "hermit-abi", 778 | "libc", 779 | "wasi", 780 | "windows-sys 0.52.0", 781 | ] 782 | 783 | [[package]] 784 | name = "mmap-loader" 785 | version = "0.5.3" 786 | dependencies = [ 787 | "anyhow", 788 | "lazy_static", 789 | "log", 790 | "memoffset", 791 | "ntapi", 792 | "pdb", 793 | "prost", 794 | "reqwest", 795 | "serde", 796 | "serde_derive", 797 | "serde_json", 798 | "serial_test", 799 | "simple_logger", 800 | "thiserror", 801 | "tokio", 802 | "tonic", 803 | "tonic-build", 804 | "winapi", 805 | ] 806 | 807 | [[package]] 808 | name = "multimap" 809 | version = "0.10.0" 810 | source = "registry+https://github.com/rust-lang/crates.io-index" 811 | checksum = "defc4c55412d89136f966bbb339008b474350e5e6e78d2714439c386b3137a03" 812 | 813 | [[package]] 814 | name = "native-tls" 815 | version = "0.2.12" 816 | source = "registry+https://github.com/rust-lang/crates.io-index" 817 | checksum = "a8614eb2c83d59d1c8cc974dd3f920198647674a0a035e1af1fa58707e317466" 818 | dependencies = [ 819 | "libc", 820 | "log", 821 | "openssl", 822 | "openssl-probe", 823 | "openssl-sys", 824 | "schannel", 825 | "security-framework", 826 | "security-framework-sys", 827 | "tempfile", 828 | ] 829 | 830 | [[package]] 831 | name = "ntapi" 832 | version = "0.4.1" 833 | source = "registry+https://github.com/rust-lang/crates.io-index" 834 | checksum = "e8a3895c6391c39d7fe7ebc444a87eb2991b2a0bc718fdabd071eec617fc68e4" 835 | dependencies = [ 836 | "winapi", 837 | ] 838 | 839 | [[package]] 840 | name = "num-conv" 841 | version = "0.1.0" 842 | source = "registry+https://github.com/rust-lang/crates.io-index" 843 | checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" 844 | 845 | [[package]] 846 | name = "num_threads" 847 | version = "0.1.7" 848 | source = "registry+https://github.com/rust-lang/crates.io-index" 849 | checksum = "5c7398b9c8b70908f6371f47ed36737907c87c52af34c268fed0bf0ceb92ead9" 850 | dependencies = [ 851 | "libc", 852 | ] 853 | 854 | [[package]] 855 | name = "object" 856 | version = "0.36.2" 857 | source = "registry+https://github.com/rust-lang/crates.io-index" 858 | checksum = "3f203fa8daa7bb185f760ae12bd8e097f63d17041dcdcaf675ac54cdf863170e" 859 | dependencies = [ 860 | "memchr", 861 | ] 862 | 863 | [[package]] 864 | name = "once_cell" 865 | version = "1.19.0" 866 | source = "registry+https://github.com/rust-lang/crates.io-index" 867 | checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" 868 | 869 | [[package]] 870 | name = "openssl" 871 | version = "0.10.66" 872 | source = "registry+https://github.com/rust-lang/crates.io-index" 873 | checksum = "9529f4786b70a3e8c61e11179af17ab6188ad8d0ded78c5529441ed39d4bd9c1" 874 | dependencies = [ 875 | "bitflags 2.6.0", 876 | "cfg-if", 877 | "foreign-types", 878 | "libc", 879 | "once_cell", 880 | "openssl-macros", 881 | "openssl-sys", 882 | ] 883 | 884 | [[package]] 885 | name = "openssl-macros" 886 | version = "0.1.1" 887 | source = "registry+https://github.com/rust-lang/crates.io-index" 888 | checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" 889 | dependencies = [ 890 | "proc-macro2", 891 | "quote", 892 | "syn", 893 | ] 894 | 895 | [[package]] 896 | name = "openssl-probe" 897 | version = "0.1.5" 898 | source = "registry+https://github.com/rust-lang/crates.io-index" 899 | checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" 900 | 901 | [[package]] 902 | name = "openssl-sys" 903 | version = "0.9.103" 904 | source = "registry+https://github.com/rust-lang/crates.io-index" 905 | checksum = "7f9e8deee91df40a943c71b917e5874b951d32a802526c85721ce3b776c929d6" 906 | dependencies = [ 907 | "cc", 908 | "libc", 909 | "pkg-config", 910 | "vcpkg", 911 | ] 912 | 913 | [[package]] 914 | name = "parking_lot" 915 | version = "0.12.3" 916 | source = "registry+https://github.com/rust-lang/crates.io-index" 917 | checksum = "f1bf18183cf54e8d6059647fc3063646a1801cf30896933ec2311622cc4b9a27" 918 | dependencies = [ 919 | "lock_api", 920 | "parking_lot_core", 921 | ] 922 | 923 | [[package]] 924 | name = "parking_lot_core" 925 | version = "0.9.10" 926 | source = "registry+https://github.com/rust-lang/crates.io-index" 927 | checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8" 928 | dependencies = [ 929 | "cfg-if", 930 | "libc", 931 | "redox_syscall", 932 | "smallvec", 933 | "windows-targets 0.52.6", 934 | ] 935 | 936 | [[package]] 937 | name = "pdb" 938 | version = "0.8.0" 939 | source = "registry+https://github.com/rust-lang/crates.io-index" 940 | checksum = "82040a392923abe6279c00ab4aff62d5250d1c8555dc780e4b02783a7aa74863" 941 | dependencies = [ 942 | "fallible-iterator", 943 | "scroll", 944 | "uuid", 945 | ] 946 | 947 | [[package]] 948 | name = "percent-encoding" 949 | version = "2.3.1" 950 | source = "registry+https://github.com/rust-lang/crates.io-index" 951 | checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" 952 | 953 | [[package]] 954 | name = "petgraph" 955 | version = "0.6.5" 956 | source = "registry+https://github.com/rust-lang/crates.io-index" 957 | checksum = "b4c5cc86750666a3ed20bdaf5ca2a0344f9c67674cae0515bec2da16fbaa47db" 958 | dependencies = [ 959 | "fixedbitset", 960 | "indexmap 2.3.0", 961 | ] 962 | 963 | [[package]] 964 | name = "pin-project" 965 | version = "1.1.5" 966 | source = "registry+https://github.com/rust-lang/crates.io-index" 967 | checksum = "b6bf43b791c5b9e34c3d182969b4abb522f9343702850a2e57f460d00d09b4b3" 968 | dependencies = [ 969 | "pin-project-internal", 970 | ] 971 | 972 | [[package]] 973 | name = "pin-project-internal" 974 | version = "1.1.5" 975 | source = "registry+https://github.com/rust-lang/crates.io-index" 976 | checksum = "2f38a4412a78282e09a2cf38d195ea5420d15ba0602cb375210efbc877243965" 977 | dependencies = [ 978 | "proc-macro2", 979 | "quote", 980 | "syn", 981 | ] 982 | 983 | [[package]] 984 | name = "pin-project-lite" 985 | version = "0.2.14" 986 | source = "registry+https://github.com/rust-lang/crates.io-index" 987 | checksum = "bda66fc9667c18cb2758a2ac84d1167245054bcf85d5d1aaa6923f45801bdd02" 988 | 989 | [[package]] 990 | name = "pin-utils" 991 | version = "0.1.0" 992 | source = "registry+https://github.com/rust-lang/crates.io-index" 993 | checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" 994 | 995 | [[package]] 996 | name = "pkg-config" 997 | version = "0.3.30" 998 | source = "registry+https://github.com/rust-lang/crates.io-index" 999 | checksum = "d231b230927b5e4ad203db57bbcbee2802f6bce620b1e4a9024a07d94e2907ec" 1000 | 1001 | [[package]] 1002 | name = "powerfmt" 1003 | version = "0.2.0" 1004 | source = "registry+https://github.com/rust-lang/crates.io-index" 1005 | checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" 1006 | 1007 | [[package]] 1008 | name = "ppv-lite86" 1009 | version = "0.2.18" 1010 | source = "registry+https://github.com/rust-lang/crates.io-index" 1011 | checksum = "dee4364d9f3b902ef14fab8a1ddffb783a1cb6b4bba3bfc1fa3922732c7de97f" 1012 | dependencies = [ 1013 | "zerocopy", 1014 | ] 1015 | 1016 | [[package]] 1017 | name = "prettyplease" 1018 | version = "0.2.20" 1019 | source = "registry+https://github.com/rust-lang/crates.io-index" 1020 | checksum = "5f12335488a2f3b0a83b14edad48dca9879ce89b2edd10e80237e4e852dd645e" 1021 | dependencies = [ 1022 | "proc-macro2", 1023 | "syn", 1024 | ] 1025 | 1026 | [[package]] 1027 | name = "proc-macro2" 1028 | version = "1.0.86" 1029 | source = "registry+https://github.com/rust-lang/crates.io-index" 1030 | checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77" 1031 | dependencies = [ 1032 | "unicode-ident", 1033 | ] 1034 | 1035 | [[package]] 1036 | name = "prost" 1037 | version = "0.12.6" 1038 | source = "registry+https://github.com/rust-lang/crates.io-index" 1039 | checksum = "deb1435c188b76130da55f17a466d252ff7b1418b2ad3e037d127b94e3411f29" 1040 | dependencies = [ 1041 | "bytes", 1042 | "prost-derive", 1043 | ] 1044 | 1045 | [[package]] 1046 | name = "prost-build" 1047 | version = "0.12.6" 1048 | source = "registry+https://github.com/rust-lang/crates.io-index" 1049 | checksum = "22505a5c94da8e3b7c2996394d1c933236c4d743e81a410bcca4e6989fc066a4" 1050 | dependencies = [ 1051 | "bytes", 1052 | "heck", 1053 | "itertools", 1054 | "log", 1055 | "multimap", 1056 | "once_cell", 1057 | "petgraph", 1058 | "prettyplease", 1059 | "prost", 1060 | "prost-types", 1061 | "regex", 1062 | "syn", 1063 | "tempfile", 1064 | ] 1065 | 1066 | [[package]] 1067 | name = "prost-derive" 1068 | version = "0.12.6" 1069 | source = "registry+https://github.com/rust-lang/crates.io-index" 1070 | checksum = "81bddcdb20abf9501610992b6759a4c888aef7d1a7247ef75e2404275ac24af1" 1071 | dependencies = [ 1072 | "anyhow", 1073 | "itertools", 1074 | "proc-macro2", 1075 | "quote", 1076 | "syn", 1077 | ] 1078 | 1079 | [[package]] 1080 | name = "prost-types" 1081 | version = "0.12.6" 1082 | source = "registry+https://github.com/rust-lang/crates.io-index" 1083 | checksum = "9091c90b0a32608e984ff2fa4091273cbdd755d54935c51d520887f4a1dbd5b0" 1084 | dependencies = [ 1085 | "prost", 1086 | ] 1087 | 1088 | [[package]] 1089 | name = "quote" 1090 | version = "1.0.36" 1091 | source = "registry+https://github.com/rust-lang/crates.io-index" 1092 | checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7" 1093 | dependencies = [ 1094 | "proc-macro2", 1095 | ] 1096 | 1097 | [[package]] 1098 | name = "rand" 1099 | version = "0.8.5" 1100 | source = "registry+https://github.com/rust-lang/crates.io-index" 1101 | checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" 1102 | dependencies = [ 1103 | "libc", 1104 | "rand_chacha", 1105 | "rand_core", 1106 | ] 1107 | 1108 | [[package]] 1109 | name = "rand_chacha" 1110 | version = "0.3.1" 1111 | source = "registry+https://github.com/rust-lang/crates.io-index" 1112 | checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" 1113 | dependencies = [ 1114 | "ppv-lite86", 1115 | "rand_core", 1116 | ] 1117 | 1118 | [[package]] 1119 | name = "rand_core" 1120 | version = "0.6.4" 1121 | source = "registry+https://github.com/rust-lang/crates.io-index" 1122 | checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" 1123 | dependencies = [ 1124 | "getrandom", 1125 | ] 1126 | 1127 | [[package]] 1128 | name = "redox_syscall" 1129 | version = "0.5.3" 1130 | source = "registry+https://github.com/rust-lang/crates.io-index" 1131 | checksum = "2a908a6e00f1fdd0dfd9c0eb08ce85126f6d8bbda50017e74bc4a4b7d4a926a4" 1132 | dependencies = [ 1133 | "bitflags 2.6.0", 1134 | ] 1135 | 1136 | [[package]] 1137 | name = "regex" 1138 | version = "1.10.5" 1139 | source = "registry+https://github.com/rust-lang/crates.io-index" 1140 | checksum = "b91213439dad192326a0d7c6ee3955910425f441d7038e0d6933b0aec5c4517f" 1141 | dependencies = [ 1142 | "aho-corasick", 1143 | "memchr", 1144 | "regex-automata", 1145 | "regex-syntax", 1146 | ] 1147 | 1148 | [[package]] 1149 | name = "regex-automata" 1150 | version = "0.4.7" 1151 | source = "registry+https://github.com/rust-lang/crates.io-index" 1152 | checksum = "38caf58cc5ef2fed281f89292ef23f6365465ed9a41b7a7754eb4e26496c92df" 1153 | dependencies = [ 1154 | "aho-corasick", 1155 | "memchr", 1156 | "regex-syntax", 1157 | ] 1158 | 1159 | [[package]] 1160 | name = "regex-syntax" 1161 | version = "0.8.4" 1162 | source = "registry+https://github.com/rust-lang/crates.io-index" 1163 | checksum = "7a66a03ae7c801facd77a29370b4faec201768915ac14a721ba36f20bc9c209b" 1164 | 1165 | [[package]] 1166 | name = "reqwest" 1167 | version = "0.12.5" 1168 | source = "registry+https://github.com/rust-lang/crates.io-index" 1169 | checksum = "c7d6d2a27d57148378eb5e111173f4276ad26340ecc5c49a4a2152167a2d6a37" 1170 | dependencies = [ 1171 | "base64 0.22.1", 1172 | "bytes", 1173 | "encoding_rs", 1174 | "futures-core", 1175 | "futures-util", 1176 | "h2 0.4.5", 1177 | "http 1.1.0", 1178 | "http-body 1.0.1", 1179 | "http-body-util", 1180 | "hyper 1.4.1", 1181 | "hyper-rustls", 1182 | "hyper-tls", 1183 | "hyper-util", 1184 | "ipnet", 1185 | "js-sys", 1186 | "log", 1187 | "mime", 1188 | "native-tls", 1189 | "once_cell", 1190 | "percent-encoding", 1191 | "pin-project-lite", 1192 | "rustls-pemfile", 1193 | "serde", 1194 | "serde_json", 1195 | "serde_urlencoded", 1196 | "sync_wrapper 1.0.1", 1197 | "system-configuration", 1198 | "tokio", 1199 | "tokio-native-tls", 1200 | "tower-service", 1201 | "url", 1202 | "wasm-bindgen", 1203 | "wasm-bindgen-futures", 1204 | "web-sys", 1205 | "winreg", 1206 | ] 1207 | 1208 | [[package]] 1209 | name = "ring" 1210 | version = "0.17.8" 1211 | source = "registry+https://github.com/rust-lang/crates.io-index" 1212 | checksum = "c17fa4cb658e3583423e915b9f3acc01cceaee1860e33d59ebae66adc3a2dc0d" 1213 | dependencies = [ 1214 | "cc", 1215 | "cfg-if", 1216 | "getrandom", 1217 | "libc", 1218 | "spin", 1219 | "untrusted", 1220 | "windows-sys 0.52.0", 1221 | ] 1222 | 1223 | [[package]] 1224 | name = "rustc-demangle" 1225 | version = "0.1.24" 1226 | source = "registry+https://github.com/rust-lang/crates.io-index" 1227 | checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" 1228 | 1229 | [[package]] 1230 | name = "rustix" 1231 | version = "0.38.34" 1232 | source = "registry+https://github.com/rust-lang/crates.io-index" 1233 | checksum = "70dc5ec042f7a43c4a73241207cecc9873a06d45debb38b329f8541d85c2730f" 1234 | dependencies = [ 1235 | "bitflags 2.6.0", 1236 | "errno", 1237 | "libc", 1238 | "linux-raw-sys", 1239 | "windows-sys 0.52.0", 1240 | ] 1241 | 1242 | [[package]] 1243 | name = "rustls" 1244 | version = "0.22.4" 1245 | source = "registry+https://github.com/rust-lang/crates.io-index" 1246 | checksum = "bf4ef73721ac7bcd79b2b315da7779d8fc09718c6b3d2d1b2d94850eb8c18432" 1247 | dependencies = [ 1248 | "log", 1249 | "ring", 1250 | "rustls-pki-types", 1251 | "rustls-webpki", 1252 | "subtle", 1253 | "zeroize", 1254 | ] 1255 | 1256 | [[package]] 1257 | name = "rustls" 1258 | version = "0.23.12" 1259 | source = "registry+https://github.com/rust-lang/crates.io-index" 1260 | checksum = "c58f8c84392efc0a126acce10fa59ff7b3d2ac06ab451a33f2741989b806b044" 1261 | dependencies = [ 1262 | "once_cell", 1263 | "rustls-pki-types", 1264 | "rustls-webpki", 1265 | "subtle", 1266 | "zeroize", 1267 | ] 1268 | 1269 | [[package]] 1270 | name = "rustls-native-certs" 1271 | version = "0.7.1" 1272 | source = "registry+https://github.com/rust-lang/crates.io-index" 1273 | checksum = "a88d6d420651b496bdd98684116959239430022a115c1240e6c3993be0b15fba" 1274 | dependencies = [ 1275 | "openssl-probe", 1276 | "rustls-pemfile", 1277 | "rustls-pki-types", 1278 | "schannel", 1279 | "security-framework", 1280 | ] 1281 | 1282 | [[package]] 1283 | name = "rustls-pemfile" 1284 | version = "2.1.2" 1285 | source = "registry+https://github.com/rust-lang/crates.io-index" 1286 | checksum = "29993a25686778eb88d4189742cd713c9bce943bc54251a33509dc63cbacf73d" 1287 | dependencies = [ 1288 | "base64 0.22.1", 1289 | "rustls-pki-types", 1290 | ] 1291 | 1292 | [[package]] 1293 | name = "rustls-pki-types" 1294 | version = "1.7.0" 1295 | source = "registry+https://github.com/rust-lang/crates.io-index" 1296 | checksum = "976295e77ce332211c0d24d92c0e83e50f5c5f046d11082cea19f3df13a3562d" 1297 | 1298 | [[package]] 1299 | name = "rustls-webpki" 1300 | version = "0.102.6" 1301 | source = "registry+https://github.com/rust-lang/crates.io-index" 1302 | checksum = "8e6b52d4fda176fd835fdc55a835d4a89b8499cad995885a21149d5ad62f852e" 1303 | dependencies = [ 1304 | "ring", 1305 | "rustls-pki-types", 1306 | "untrusted", 1307 | ] 1308 | 1309 | [[package]] 1310 | name = "rustversion" 1311 | version = "1.0.17" 1312 | source = "registry+https://github.com/rust-lang/crates.io-index" 1313 | checksum = "955d28af4278de8121b7ebeb796b6a45735dc01436d898801014aced2773a3d6" 1314 | 1315 | [[package]] 1316 | name = "ryu" 1317 | version = "1.0.18" 1318 | source = "registry+https://github.com/rust-lang/crates.io-index" 1319 | checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" 1320 | 1321 | [[package]] 1322 | name = "scc" 1323 | version = "2.1.6" 1324 | source = "registry+https://github.com/rust-lang/crates.io-index" 1325 | checksum = "05ccfb12511cdb770157ace92d7dda771e498445b78f9886e8cdbc5140a4eced" 1326 | dependencies = [ 1327 | "sdd", 1328 | ] 1329 | 1330 | [[package]] 1331 | name = "schannel" 1332 | version = "0.1.23" 1333 | source = "registry+https://github.com/rust-lang/crates.io-index" 1334 | checksum = "fbc91545643bcf3a0bbb6569265615222618bdf33ce4ffbbd13c4bbd4c093534" 1335 | dependencies = [ 1336 | "windows-sys 0.52.0", 1337 | ] 1338 | 1339 | [[package]] 1340 | name = "scopeguard" 1341 | version = "1.2.0" 1342 | source = "registry+https://github.com/rust-lang/crates.io-index" 1343 | checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" 1344 | 1345 | [[package]] 1346 | name = "scroll" 1347 | version = "0.11.0" 1348 | source = "registry+https://github.com/rust-lang/crates.io-index" 1349 | checksum = "04c565b551bafbef4157586fa379538366e4385d42082f255bfd96e4fe8519da" 1350 | 1351 | [[package]] 1352 | name = "sdd" 1353 | version = "2.1.0" 1354 | source = "registry+https://github.com/rust-lang/crates.io-index" 1355 | checksum = "177258b64c0faaa9ffd3c65cd3262c2bc7e2588dbbd9c1641d0346145c1bbda8" 1356 | 1357 | [[package]] 1358 | name = "security-framework" 1359 | version = "2.11.1" 1360 | source = "registry+https://github.com/rust-lang/crates.io-index" 1361 | checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02" 1362 | dependencies = [ 1363 | "bitflags 2.6.0", 1364 | "core-foundation", 1365 | "core-foundation-sys", 1366 | "libc", 1367 | "security-framework-sys", 1368 | ] 1369 | 1370 | [[package]] 1371 | name = "security-framework-sys" 1372 | version = "2.11.1" 1373 | source = "registry+https://github.com/rust-lang/crates.io-index" 1374 | checksum = "75da29fe9b9b08fe9d6b22b5b4bcbc75d8db3aa31e639aa56bb62e9d46bfceaf" 1375 | dependencies = [ 1376 | "core-foundation-sys", 1377 | "libc", 1378 | ] 1379 | 1380 | [[package]] 1381 | name = "serde" 1382 | version = "1.0.204" 1383 | source = "registry+https://github.com/rust-lang/crates.io-index" 1384 | checksum = "bc76f558e0cbb2a839d37354c575f1dc3fdc6546b5be373ba43d95f231bf7c12" 1385 | dependencies = [ 1386 | "serde_derive", 1387 | ] 1388 | 1389 | [[package]] 1390 | name = "serde_derive" 1391 | version = "1.0.204" 1392 | source = "registry+https://github.com/rust-lang/crates.io-index" 1393 | checksum = "e0cd7e117be63d3c3678776753929474f3b04a43a080c744d6b0ae2a8c28e222" 1394 | dependencies = [ 1395 | "proc-macro2", 1396 | "quote", 1397 | "syn", 1398 | ] 1399 | 1400 | [[package]] 1401 | name = "serde_json" 1402 | version = "1.0.121" 1403 | source = "registry+https://github.com/rust-lang/crates.io-index" 1404 | checksum = "4ab380d7d9f22ef3f21ad3e6c1ebe8e4fc7a2000ccba2e4d71fc96f15b2cb609" 1405 | dependencies = [ 1406 | "itoa", 1407 | "memchr", 1408 | "ryu", 1409 | "serde", 1410 | ] 1411 | 1412 | [[package]] 1413 | name = "serde_urlencoded" 1414 | version = "0.7.1" 1415 | source = "registry+https://github.com/rust-lang/crates.io-index" 1416 | checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" 1417 | dependencies = [ 1418 | "form_urlencoded", 1419 | "itoa", 1420 | "ryu", 1421 | "serde", 1422 | ] 1423 | 1424 | [[package]] 1425 | name = "serial_test" 1426 | version = "3.1.1" 1427 | source = "registry+https://github.com/rust-lang/crates.io-index" 1428 | checksum = "4b4b487fe2acf240a021cf57c6b2b4903b1e78ca0ecd862a71b71d2a51fed77d" 1429 | dependencies = [ 1430 | "futures", 1431 | "log", 1432 | "once_cell", 1433 | "parking_lot", 1434 | "scc", 1435 | "serial_test_derive", 1436 | ] 1437 | 1438 | [[package]] 1439 | name = "serial_test_derive" 1440 | version = "3.1.1" 1441 | source = "registry+https://github.com/rust-lang/crates.io-index" 1442 | checksum = "82fe9db325bcef1fbcde82e078a5cc4efdf787e96b3b9cf45b50b529f2083d67" 1443 | dependencies = [ 1444 | "proc-macro2", 1445 | "quote", 1446 | "syn", 1447 | ] 1448 | 1449 | [[package]] 1450 | name = "simple_logger" 1451 | version = "4.3.3" 1452 | source = "registry+https://github.com/rust-lang/crates.io-index" 1453 | checksum = "8e7e46c8c90251d47d08b28b8a419ffb4aede0f87c2eea95e17d1d5bacbf3ef1" 1454 | dependencies = [ 1455 | "colored", 1456 | "log", 1457 | "time", 1458 | "windows-sys 0.48.0", 1459 | ] 1460 | 1461 | [[package]] 1462 | name = "slab" 1463 | version = "0.4.9" 1464 | source = "registry+https://github.com/rust-lang/crates.io-index" 1465 | checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" 1466 | dependencies = [ 1467 | "autocfg", 1468 | ] 1469 | 1470 | [[package]] 1471 | name = "smallvec" 1472 | version = "1.13.2" 1473 | source = "registry+https://github.com/rust-lang/crates.io-index" 1474 | checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" 1475 | 1476 | [[package]] 1477 | name = "socket2" 1478 | version = "0.5.7" 1479 | source = "registry+https://github.com/rust-lang/crates.io-index" 1480 | checksum = "ce305eb0b4296696835b71df73eb912e0f1ffd2556a501fcede6e0c50349191c" 1481 | dependencies = [ 1482 | "libc", 1483 | "windows-sys 0.52.0", 1484 | ] 1485 | 1486 | [[package]] 1487 | name = "spin" 1488 | version = "0.9.8" 1489 | source = "registry+https://github.com/rust-lang/crates.io-index" 1490 | checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" 1491 | 1492 | [[package]] 1493 | name = "subtle" 1494 | version = "2.6.1" 1495 | source = "registry+https://github.com/rust-lang/crates.io-index" 1496 | checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" 1497 | 1498 | [[package]] 1499 | name = "syn" 1500 | version = "2.0.72" 1501 | source = "registry+https://github.com/rust-lang/crates.io-index" 1502 | checksum = "dc4b9b9bf2add8093d3f2c0204471e951b2285580335de42f9d2534f3ae7a8af" 1503 | dependencies = [ 1504 | "proc-macro2", 1505 | "quote", 1506 | "unicode-ident", 1507 | ] 1508 | 1509 | [[package]] 1510 | name = "sync_wrapper" 1511 | version = "0.1.2" 1512 | source = "registry+https://github.com/rust-lang/crates.io-index" 1513 | checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160" 1514 | 1515 | [[package]] 1516 | name = "sync_wrapper" 1517 | version = "1.0.1" 1518 | source = "registry+https://github.com/rust-lang/crates.io-index" 1519 | checksum = "a7065abeca94b6a8a577f9bd45aa0867a2238b74e8eb67cf10d492bc39351394" 1520 | 1521 | [[package]] 1522 | name = "system-configuration" 1523 | version = "0.5.1" 1524 | source = "registry+https://github.com/rust-lang/crates.io-index" 1525 | checksum = "ba3a3adc5c275d719af8cb4272ea1c4a6d668a777f37e115f6d11ddbc1c8e0e7" 1526 | dependencies = [ 1527 | "bitflags 1.3.2", 1528 | "core-foundation", 1529 | "system-configuration-sys", 1530 | ] 1531 | 1532 | [[package]] 1533 | name = "system-configuration-sys" 1534 | version = "0.5.0" 1535 | source = "registry+https://github.com/rust-lang/crates.io-index" 1536 | checksum = "a75fb188eb626b924683e3b95e3a48e63551fcfb51949de2f06a9d91dbee93c9" 1537 | dependencies = [ 1538 | "core-foundation-sys", 1539 | "libc", 1540 | ] 1541 | 1542 | [[package]] 1543 | name = "tempfile" 1544 | version = "3.10.1" 1545 | source = "registry+https://github.com/rust-lang/crates.io-index" 1546 | checksum = "85b77fafb263dd9d05cbeac119526425676db3784113aa9295c88498cbf8bff1" 1547 | dependencies = [ 1548 | "cfg-if", 1549 | "fastrand", 1550 | "rustix", 1551 | "windows-sys 0.52.0", 1552 | ] 1553 | 1554 | [[package]] 1555 | name = "thiserror" 1556 | version = "1.0.63" 1557 | source = "registry+https://github.com/rust-lang/crates.io-index" 1558 | checksum = "c0342370b38b6a11b6cc11d6a805569958d54cfa061a29969c3b5ce2ea405724" 1559 | dependencies = [ 1560 | "thiserror-impl", 1561 | ] 1562 | 1563 | [[package]] 1564 | name = "thiserror-impl" 1565 | version = "1.0.63" 1566 | source = "registry+https://github.com/rust-lang/crates.io-index" 1567 | checksum = "a4558b58466b9ad7ca0f102865eccc95938dca1a74a856f2b57b6629050da261" 1568 | dependencies = [ 1569 | "proc-macro2", 1570 | "quote", 1571 | "syn", 1572 | ] 1573 | 1574 | [[package]] 1575 | name = "time" 1576 | version = "0.3.36" 1577 | source = "registry+https://github.com/rust-lang/crates.io-index" 1578 | checksum = "5dfd88e563464686c916c7e46e623e520ddc6d79fa6641390f2e3fa86e83e885" 1579 | dependencies = [ 1580 | "deranged", 1581 | "itoa", 1582 | "libc", 1583 | "num-conv", 1584 | "num_threads", 1585 | "powerfmt", 1586 | "serde", 1587 | "time-core", 1588 | "time-macros", 1589 | ] 1590 | 1591 | [[package]] 1592 | name = "time-core" 1593 | version = "0.1.2" 1594 | source = "registry+https://github.com/rust-lang/crates.io-index" 1595 | checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3" 1596 | 1597 | [[package]] 1598 | name = "time-macros" 1599 | version = "0.2.18" 1600 | source = "registry+https://github.com/rust-lang/crates.io-index" 1601 | checksum = "3f252a68540fde3a3877aeea552b832b40ab9a69e318efd078774a01ddee1ccf" 1602 | dependencies = [ 1603 | "num-conv", 1604 | "time-core", 1605 | ] 1606 | 1607 | [[package]] 1608 | name = "tinyvec" 1609 | version = "1.8.0" 1610 | source = "registry+https://github.com/rust-lang/crates.io-index" 1611 | checksum = "445e881f4f6d382d5f27c034e25eb92edd7c784ceab92a0937db7f2e9471b938" 1612 | dependencies = [ 1613 | "tinyvec_macros", 1614 | ] 1615 | 1616 | [[package]] 1617 | name = "tinyvec_macros" 1618 | version = "0.1.1" 1619 | source = "registry+https://github.com/rust-lang/crates.io-index" 1620 | checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" 1621 | 1622 | [[package]] 1623 | name = "tokio" 1624 | version = "1.39.2" 1625 | source = "registry+https://github.com/rust-lang/crates.io-index" 1626 | checksum = "daa4fb1bc778bd6f04cbfc4bb2d06a7396a8f299dc33ea1900cedaa316f467b1" 1627 | dependencies = [ 1628 | "backtrace", 1629 | "bytes", 1630 | "libc", 1631 | "mio", 1632 | "pin-project-lite", 1633 | "socket2", 1634 | "tokio-macros", 1635 | "windows-sys 0.52.0", 1636 | ] 1637 | 1638 | [[package]] 1639 | name = "tokio-io-timeout" 1640 | version = "1.2.0" 1641 | source = "registry+https://github.com/rust-lang/crates.io-index" 1642 | checksum = "30b74022ada614a1b4834de765f9bb43877f910cc8ce4be40e89042c9223a8bf" 1643 | dependencies = [ 1644 | "pin-project-lite", 1645 | "tokio", 1646 | ] 1647 | 1648 | [[package]] 1649 | name = "tokio-macros" 1650 | version = "2.4.0" 1651 | source = "registry+https://github.com/rust-lang/crates.io-index" 1652 | checksum = "693d596312e88961bc67d7f1f97af8a70227d9f90c31bba5806eec004978d752" 1653 | dependencies = [ 1654 | "proc-macro2", 1655 | "quote", 1656 | "syn", 1657 | ] 1658 | 1659 | [[package]] 1660 | name = "tokio-native-tls" 1661 | version = "0.3.1" 1662 | source = "registry+https://github.com/rust-lang/crates.io-index" 1663 | checksum = "bbae76ab933c85776efabc971569dd6119c580d8f5d448769dec1764bf796ef2" 1664 | dependencies = [ 1665 | "native-tls", 1666 | "tokio", 1667 | ] 1668 | 1669 | [[package]] 1670 | name = "tokio-rustls" 1671 | version = "0.25.0" 1672 | source = "registry+https://github.com/rust-lang/crates.io-index" 1673 | checksum = "775e0c0f0adb3a2f22a00c4745d728b479985fc15ee7ca6a2608388c5569860f" 1674 | dependencies = [ 1675 | "rustls 0.22.4", 1676 | "rustls-pki-types", 1677 | "tokio", 1678 | ] 1679 | 1680 | [[package]] 1681 | name = "tokio-rustls" 1682 | version = "0.26.0" 1683 | source = "registry+https://github.com/rust-lang/crates.io-index" 1684 | checksum = "0c7bc40d0e5a97695bb96e27995cd3a08538541b0a846f65bba7a359f36700d4" 1685 | dependencies = [ 1686 | "rustls 0.23.12", 1687 | "rustls-pki-types", 1688 | "tokio", 1689 | ] 1690 | 1691 | [[package]] 1692 | name = "tokio-stream" 1693 | version = "0.1.15" 1694 | source = "registry+https://github.com/rust-lang/crates.io-index" 1695 | checksum = "267ac89e0bec6e691e5813911606935d77c476ff49024f98abcea3e7b15e37af" 1696 | dependencies = [ 1697 | "futures-core", 1698 | "pin-project-lite", 1699 | "tokio", 1700 | ] 1701 | 1702 | [[package]] 1703 | name = "tokio-util" 1704 | version = "0.7.11" 1705 | source = "registry+https://github.com/rust-lang/crates.io-index" 1706 | checksum = "9cf6b47b3771c49ac75ad09a6162f53ad4b8088b76ac60e8ec1455b31a189fe1" 1707 | dependencies = [ 1708 | "bytes", 1709 | "futures-core", 1710 | "futures-sink", 1711 | "pin-project-lite", 1712 | "tokio", 1713 | ] 1714 | 1715 | [[package]] 1716 | name = "tonic" 1717 | version = "0.11.0" 1718 | source = "registry+https://github.com/rust-lang/crates.io-index" 1719 | checksum = "76c4eb7a4e9ef9d4763600161f12f5070b92a578e1b634db88a6887844c91a13" 1720 | dependencies = [ 1721 | "async-stream", 1722 | "async-trait", 1723 | "axum", 1724 | "base64 0.21.7", 1725 | "bytes", 1726 | "h2 0.3.26", 1727 | "http 0.2.12", 1728 | "http-body 0.4.6", 1729 | "hyper 0.14.30", 1730 | "hyper-timeout", 1731 | "percent-encoding", 1732 | "pin-project", 1733 | "prost", 1734 | "rustls-native-certs", 1735 | "rustls-pemfile", 1736 | "rustls-pki-types", 1737 | "tokio", 1738 | "tokio-rustls 0.25.0", 1739 | "tokio-stream", 1740 | "tower", 1741 | "tower-layer", 1742 | "tower-service", 1743 | "tracing", 1744 | ] 1745 | 1746 | [[package]] 1747 | name = "tonic-build" 1748 | version = "0.11.0" 1749 | source = "registry+https://github.com/rust-lang/crates.io-index" 1750 | checksum = "be4ef6dd70a610078cb4e338a0f79d06bc759ff1b22d2120c2ff02ae264ba9c2" 1751 | dependencies = [ 1752 | "prettyplease", 1753 | "proc-macro2", 1754 | "prost-build", 1755 | "quote", 1756 | "syn", 1757 | ] 1758 | 1759 | [[package]] 1760 | name = "tower" 1761 | version = "0.4.13" 1762 | source = "registry+https://github.com/rust-lang/crates.io-index" 1763 | checksum = "b8fa9be0de6cf49e536ce1851f987bd21a43b771b09473c3549a6c853db37c1c" 1764 | dependencies = [ 1765 | "futures-core", 1766 | "futures-util", 1767 | "indexmap 1.9.3", 1768 | "pin-project", 1769 | "pin-project-lite", 1770 | "rand", 1771 | "slab", 1772 | "tokio", 1773 | "tokio-util", 1774 | "tower-layer", 1775 | "tower-service", 1776 | "tracing", 1777 | ] 1778 | 1779 | [[package]] 1780 | name = "tower-layer" 1781 | version = "0.3.2" 1782 | source = "registry+https://github.com/rust-lang/crates.io-index" 1783 | checksum = "c20c8dbed6283a09604c3e69b4b7eeb54e298b8a600d4d5ecb5ad39de609f1d0" 1784 | 1785 | [[package]] 1786 | name = "tower-service" 1787 | version = "0.3.2" 1788 | source = "registry+https://github.com/rust-lang/crates.io-index" 1789 | checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52" 1790 | 1791 | [[package]] 1792 | name = "tracing" 1793 | version = "0.1.40" 1794 | source = "registry+https://github.com/rust-lang/crates.io-index" 1795 | checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" 1796 | dependencies = [ 1797 | "pin-project-lite", 1798 | "tracing-attributes", 1799 | "tracing-core", 1800 | ] 1801 | 1802 | [[package]] 1803 | name = "tracing-attributes" 1804 | version = "0.1.27" 1805 | source = "registry+https://github.com/rust-lang/crates.io-index" 1806 | checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" 1807 | dependencies = [ 1808 | "proc-macro2", 1809 | "quote", 1810 | "syn", 1811 | ] 1812 | 1813 | [[package]] 1814 | name = "tracing-core" 1815 | version = "0.1.32" 1816 | source = "registry+https://github.com/rust-lang/crates.io-index" 1817 | checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54" 1818 | dependencies = [ 1819 | "once_cell", 1820 | ] 1821 | 1822 | [[package]] 1823 | name = "try-lock" 1824 | version = "0.2.5" 1825 | source = "registry+https://github.com/rust-lang/crates.io-index" 1826 | checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" 1827 | 1828 | [[package]] 1829 | name = "unicode-bidi" 1830 | version = "0.3.15" 1831 | source = "registry+https://github.com/rust-lang/crates.io-index" 1832 | checksum = "08f95100a766bf4f8f28f90d77e0a5461bbdb219042e7679bebe79004fed8d75" 1833 | 1834 | [[package]] 1835 | name = "unicode-ident" 1836 | version = "1.0.12" 1837 | source = "registry+https://github.com/rust-lang/crates.io-index" 1838 | checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" 1839 | 1840 | [[package]] 1841 | name = "unicode-normalization" 1842 | version = "0.1.23" 1843 | source = "registry+https://github.com/rust-lang/crates.io-index" 1844 | checksum = "a56d1686db2308d901306f92a263857ef59ea39678a5458e7cb17f01415101f5" 1845 | dependencies = [ 1846 | "tinyvec", 1847 | ] 1848 | 1849 | [[package]] 1850 | name = "untrusted" 1851 | version = "0.9.0" 1852 | source = "registry+https://github.com/rust-lang/crates.io-index" 1853 | checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" 1854 | 1855 | [[package]] 1856 | name = "url" 1857 | version = "2.5.2" 1858 | source = "registry+https://github.com/rust-lang/crates.io-index" 1859 | checksum = "22784dbdf76fdde8af1aeda5622b546b422b6fc585325248a2bf9f5e41e94d6c" 1860 | dependencies = [ 1861 | "form_urlencoded", 1862 | "idna", 1863 | "percent-encoding", 1864 | ] 1865 | 1866 | [[package]] 1867 | name = "uuid" 1868 | version = "1.10.0" 1869 | source = "registry+https://github.com/rust-lang/crates.io-index" 1870 | checksum = "81dfa00651efa65069b0b6b651f4aaa31ba9e3c3ce0137aaad053604ee7e0314" 1871 | 1872 | [[package]] 1873 | name = "vcpkg" 1874 | version = "0.2.15" 1875 | source = "registry+https://github.com/rust-lang/crates.io-index" 1876 | checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" 1877 | 1878 | [[package]] 1879 | name = "want" 1880 | version = "0.3.1" 1881 | source = "registry+https://github.com/rust-lang/crates.io-index" 1882 | checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e" 1883 | dependencies = [ 1884 | "try-lock", 1885 | ] 1886 | 1887 | [[package]] 1888 | name = "wasi" 1889 | version = "0.11.0+wasi-snapshot-preview1" 1890 | source = "registry+https://github.com/rust-lang/crates.io-index" 1891 | checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" 1892 | 1893 | [[package]] 1894 | name = "wasm-bindgen" 1895 | version = "0.2.92" 1896 | source = "registry+https://github.com/rust-lang/crates.io-index" 1897 | checksum = "4be2531df63900aeb2bca0daaaddec08491ee64ceecbee5076636a3b026795a8" 1898 | dependencies = [ 1899 | "cfg-if", 1900 | "wasm-bindgen-macro", 1901 | ] 1902 | 1903 | [[package]] 1904 | name = "wasm-bindgen-backend" 1905 | version = "0.2.92" 1906 | source = "registry+https://github.com/rust-lang/crates.io-index" 1907 | checksum = "614d787b966d3989fa7bb98a654e369c762374fd3213d212cfc0251257e747da" 1908 | dependencies = [ 1909 | "bumpalo", 1910 | "log", 1911 | "once_cell", 1912 | "proc-macro2", 1913 | "quote", 1914 | "syn", 1915 | "wasm-bindgen-shared", 1916 | ] 1917 | 1918 | [[package]] 1919 | name = "wasm-bindgen-futures" 1920 | version = "0.4.42" 1921 | source = "registry+https://github.com/rust-lang/crates.io-index" 1922 | checksum = "76bc14366121efc8dbb487ab05bcc9d346b3b5ec0eaa76e46594cabbe51762c0" 1923 | dependencies = [ 1924 | "cfg-if", 1925 | "js-sys", 1926 | "wasm-bindgen", 1927 | "web-sys", 1928 | ] 1929 | 1930 | [[package]] 1931 | name = "wasm-bindgen-macro" 1932 | version = "0.2.92" 1933 | source = "registry+https://github.com/rust-lang/crates.io-index" 1934 | checksum = "a1f8823de937b71b9460c0c34e25f3da88250760bec0ebac694b49997550d726" 1935 | dependencies = [ 1936 | "quote", 1937 | "wasm-bindgen-macro-support", 1938 | ] 1939 | 1940 | [[package]] 1941 | name = "wasm-bindgen-macro-support" 1942 | version = "0.2.92" 1943 | source = "registry+https://github.com/rust-lang/crates.io-index" 1944 | checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7" 1945 | dependencies = [ 1946 | "proc-macro2", 1947 | "quote", 1948 | "syn", 1949 | "wasm-bindgen-backend", 1950 | "wasm-bindgen-shared", 1951 | ] 1952 | 1953 | [[package]] 1954 | name = "wasm-bindgen-shared" 1955 | version = "0.2.92" 1956 | source = "registry+https://github.com/rust-lang/crates.io-index" 1957 | checksum = "af190c94f2773fdb3729c55b007a722abb5384da03bc0986df4c289bf5567e96" 1958 | 1959 | [[package]] 1960 | name = "web-sys" 1961 | version = "0.3.69" 1962 | source = "registry+https://github.com/rust-lang/crates.io-index" 1963 | checksum = "77afa9a11836342370f4817622a2f0f418b134426d91a82dfb48f532d2ec13ef" 1964 | dependencies = [ 1965 | "js-sys", 1966 | "wasm-bindgen", 1967 | ] 1968 | 1969 | [[package]] 1970 | name = "winapi" 1971 | version = "0.3.9" 1972 | source = "registry+https://github.com/rust-lang/crates.io-index" 1973 | checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" 1974 | dependencies = [ 1975 | "winapi-i686-pc-windows-gnu", 1976 | "winapi-x86_64-pc-windows-gnu", 1977 | ] 1978 | 1979 | [[package]] 1980 | name = "winapi-i686-pc-windows-gnu" 1981 | version = "0.4.0" 1982 | source = "registry+https://github.com/rust-lang/crates.io-index" 1983 | checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" 1984 | 1985 | [[package]] 1986 | name = "winapi-x86_64-pc-windows-gnu" 1987 | version = "0.4.0" 1988 | source = "registry+https://github.com/rust-lang/crates.io-index" 1989 | checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" 1990 | 1991 | [[package]] 1992 | name = "windows-sys" 1993 | version = "0.48.0" 1994 | source = "registry+https://github.com/rust-lang/crates.io-index" 1995 | checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" 1996 | dependencies = [ 1997 | "windows-targets 0.48.5", 1998 | ] 1999 | 2000 | [[package]] 2001 | name = "windows-sys" 2002 | version = "0.52.0" 2003 | source = "registry+https://github.com/rust-lang/crates.io-index" 2004 | checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" 2005 | dependencies = [ 2006 | "windows-targets 0.52.6", 2007 | ] 2008 | 2009 | [[package]] 2010 | name = "windows-targets" 2011 | version = "0.48.5" 2012 | source = "registry+https://github.com/rust-lang/crates.io-index" 2013 | checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" 2014 | dependencies = [ 2015 | "windows_aarch64_gnullvm 0.48.5", 2016 | "windows_aarch64_msvc 0.48.5", 2017 | "windows_i686_gnu 0.48.5", 2018 | "windows_i686_msvc 0.48.5", 2019 | "windows_x86_64_gnu 0.48.5", 2020 | "windows_x86_64_gnullvm 0.48.5", 2021 | "windows_x86_64_msvc 0.48.5", 2022 | ] 2023 | 2024 | [[package]] 2025 | name = "windows-targets" 2026 | version = "0.52.6" 2027 | source = "registry+https://github.com/rust-lang/crates.io-index" 2028 | checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" 2029 | dependencies = [ 2030 | "windows_aarch64_gnullvm 0.52.6", 2031 | "windows_aarch64_msvc 0.52.6", 2032 | "windows_i686_gnu 0.52.6", 2033 | "windows_i686_gnullvm", 2034 | "windows_i686_msvc 0.52.6", 2035 | "windows_x86_64_gnu 0.52.6", 2036 | "windows_x86_64_gnullvm 0.52.6", 2037 | "windows_x86_64_msvc 0.52.6", 2038 | ] 2039 | 2040 | [[package]] 2041 | name = "windows_aarch64_gnullvm" 2042 | version = "0.48.5" 2043 | source = "registry+https://github.com/rust-lang/crates.io-index" 2044 | checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" 2045 | 2046 | [[package]] 2047 | name = "windows_aarch64_gnullvm" 2048 | version = "0.52.6" 2049 | source = "registry+https://github.com/rust-lang/crates.io-index" 2050 | checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" 2051 | 2052 | [[package]] 2053 | name = "windows_aarch64_msvc" 2054 | version = "0.48.5" 2055 | source = "registry+https://github.com/rust-lang/crates.io-index" 2056 | checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" 2057 | 2058 | [[package]] 2059 | name = "windows_aarch64_msvc" 2060 | version = "0.52.6" 2061 | source = "registry+https://github.com/rust-lang/crates.io-index" 2062 | checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" 2063 | 2064 | [[package]] 2065 | name = "windows_i686_gnu" 2066 | version = "0.48.5" 2067 | source = "registry+https://github.com/rust-lang/crates.io-index" 2068 | checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" 2069 | 2070 | [[package]] 2071 | name = "windows_i686_gnu" 2072 | version = "0.52.6" 2073 | source = "registry+https://github.com/rust-lang/crates.io-index" 2074 | checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" 2075 | 2076 | [[package]] 2077 | name = "windows_i686_gnullvm" 2078 | version = "0.52.6" 2079 | source = "registry+https://github.com/rust-lang/crates.io-index" 2080 | checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" 2081 | 2082 | [[package]] 2083 | name = "windows_i686_msvc" 2084 | version = "0.48.5" 2085 | source = "registry+https://github.com/rust-lang/crates.io-index" 2086 | checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" 2087 | 2088 | [[package]] 2089 | name = "windows_i686_msvc" 2090 | version = "0.52.6" 2091 | source = "registry+https://github.com/rust-lang/crates.io-index" 2092 | checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" 2093 | 2094 | [[package]] 2095 | name = "windows_x86_64_gnu" 2096 | version = "0.48.5" 2097 | source = "registry+https://github.com/rust-lang/crates.io-index" 2098 | checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" 2099 | 2100 | [[package]] 2101 | name = "windows_x86_64_gnu" 2102 | version = "0.52.6" 2103 | source = "registry+https://github.com/rust-lang/crates.io-index" 2104 | checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" 2105 | 2106 | [[package]] 2107 | name = "windows_x86_64_gnullvm" 2108 | version = "0.48.5" 2109 | source = "registry+https://github.com/rust-lang/crates.io-index" 2110 | checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" 2111 | 2112 | [[package]] 2113 | name = "windows_x86_64_gnullvm" 2114 | version = "0.52.6" 2115 | source = "registry+https://github.com/rust-lang/crates.io-index" 2116 | checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" 2117 | 2118 | [[package]] 2119 | name = "windows_x86_64_msvc" 2120 | version = "0.48.5" 2121 | source = "registry+https://github.com/rust-lang/crates.io-index" 2122 | checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" 2123 | 2124 | [[package]] 2125 | name = "windows_x86_64_msvc" 2126 | version = "0.52.6" 2127 | source = "registry+https://github.com/rust-lang/crates.io-index" 2128 | checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" 2129 | 2130 | [[package]] 2131 | name = "winreg" 2132 | version = "0.52.0" 2133 | source = "registry+https://github.com/rust-lang/crates.io-index" 2134 | checksum = "a277a57398d4bfa075df44f501a17cfdf8542d224f0d36095a2adc7aee4ef0a5" 2135 | dependencies = [ 2136 | "cfg-if", 2137 | "windows-sys 0.48.0", 2138 | ] 2139 | 2140 | [[package]] 2141 | name = "zerocopy" 2142 | version = "0.6.6" 2143 | source = "registry+https://github.com/rust-lang/crates.io-index" 2144 | checksum = "854e949ac82d619ee9a14c66a1b674ac730422372ccb759ce0c39cabcf2bf8e6" 2145 | dependencies = [ 2146 | "byteorder", 2147 | "zerocopy-derive", 2148 | ] 2149 | 2150 | [[package]] 2151 | name = "zerocopy-derive" 2152 | version = "0.6.6" 2153 | source = "registry+https://github.com/rust-lang/crates.io-index" 2154 | checksum = "125139de3f6b9d625c39e2efdd73d41bdac468ccd556556440e322be0e1bbd91" 2155 | dependencies = [ 2156 | "proc-macro2", 2157 | "quote", 2158 | "syn", 2159 | ] 2160 | 2161 | [[package]] 2162 | name = "zeroize" 2163 | version = "1.8.1" 2164 | source = "registry+https://github.com/rust-lang/crates.io-index" 2165 | checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde" 2166 | --------------------------------------------------------------------------------