Skip to main content

windows_bridge/file_transfer/
mod.rs

1mod bridge;
2mod recipe;
3
4use vmi::{
5    Va, VmiContext, VmiError,
6    arch::amd64::{Amd64, Registers},
7    driver::VmiFullDriver,
8    os::windows::WindowsOs,
9    utils::{injector::RecipeExecutor, shellcode::KernelRecipeData},
10};
11
12#[expect(unused_imports)]
13pub use self::bridge::{FileTransferBridge, FileTransferStatus};
14use self::recipe::file_transfer_recipe;
15
16/// State of a file transfer recorded by `NtWriteFile` and started during `NtClose`.
17#[expect(
18    clippy::large_enum_variant,
19    reason = "only a handful of transfers exist at a time, so boxing the recipe \
20              executor would add an allocation without improving readability"
21)]
22enum FileTransferState<Driver>
23where
24    Driver: VmiFullDriver<Architecture = Amd64>,
25{
26    /// Transfer has been created but not yet started.
27    Pending,
28
29    /// Transfer is currently being executed.
30    Executing(RecipeExecutor<WindowsOs<Driver>, KernelRecipeData<WindowsOs<Driver>>>),
31}
32
33/// File transfer that runs on the thread executing `NtClose`.
34pub struct FileTransfer<Driver>
35where
36    Driver: VmiFullDriver<Architecture = Amd64>,
37{
38    /// Process-local handle of the file being transferred.
39    handle: u64,
40
41    /// Guest `_FILE_OBJECT` address of the file being transferred.
42    file_object: Va,
43
44    /// Guest path of the file being transferred.
45    path: String,
46
47    /// Current state of the file transfer.
48    state: FileTransferState<Driver>,
49}
50
51impl<Driver> FileTransfer<Driver>
52where
53    Driver: VmiFullDriver<Architecture = Amd64>,
54{
55    /// Creates a pending transfer.
56    pub fn new(handle: u64, file_object: Va, path: String) -> Self {
57        Self {
58            handle,
59            file_object,
60            path,
61            state: FileTransferState::Pending,
62        }
63    }
64
65    /// Returns the process-local file handle.
66    pub fn handle(&self) -> u64 {
67        self.handle
68    }
69
70    /// Returns the guest `_FILE_OBJECT` address.
71    pub fn file_object(&self) -> Va {
72        self.file_object
73    }
74
75    /// Returns the file's guest path.
76    pub fn path(&self) -> &str {
77        &self.path
78    }
79
80    /// Starts the transfer on the thread that is about to close the file handle.
81    pub fn start(&mut self) {
82        assert!(
83            matches!(self.state, FileTransferState::Pending),
84            "file transfer started more than once"
85        );
86
87        self.state =
88            FileTransferState::Executing(RecipeExecutor::new(file_transfer_recipe(self.handle)));
89    }
90
91    /// Advances the `file-transfer` recipe on the current thread.
92    pub fn execute(
93        &mut self,
94        vmi: &VmiContext<'_, WindowsOs<Driver>>,
95    ) -> Result<Option<Registers>, VmiError> {
96        let executor = match &mut self.state {
97            FileTransferState::Executing(executor) => executor,
98            FileTransferState::Pending => panic!("pending file transfer cannot execute"),
99        };
100
101        executor.execute(vmi)
102    }
103
104    /// Returns whether the `file-transfer` recipe has finished.
105    pub fn done(&self) -> bool {
106        match &self.state {
107            FileTransferState::Pending => false,
108            FileTransferState::Executing(executor) => executor.done(),
109        }
110    }
111}