memflow_win32_defs/offsets/
mod.rs

1pub mod builder;
2pub use builder::Win32OffsetBuilder;
3
4#[cfg(feature = "symstore")]
5pub mod pdb;
6#[cfg(feature = "symstore")]
7pub mod symstore;
8
9pub mod offset_table;
10#[doc(hidden)]
11pub use offset_table::{
12    MmVadOffsetTable, Win32OffsetFile, Win32OffsetHeader, Win32OffsetTable,
13    Win32OffsetsArchitecture,
14};
15
16#[cfg(feature = "symstore")]
17pub use {
18    self::pdb::{PdbStruct, PdbSymbols},
19    symstore::*,
20};
21
22use std::prelude::v1::*;
23
24use memflow::architecture::ArchitectureIdent;
25
26// those only required when compiling under std environment
27#[cfg(feature = "std")]
28use crate::kernel::Win32Guid;
29#[cfg(feature = "std")]
30use memflow::error::{Error, ErrorKind, ErrorOrigin, Result};
31#[cfg(feature = "std")]
32use std::{fs::File, io::Read, path::Path};
33
34#[derive(Debug, Copy, Clone)]
35#[repr(C)]
36#[cfg_attr(feature = "serde", derive(::serde::Serialize))]
37pub struct Win32ArchOffsets {
38    pub peb_ldr: usize,             // _PEB::Ldr
39    pub peb_process_params: usize,  // _PEB::ProcessParameters
40    pub ldr_list: usize,            // _PEB_LDR_DATA::InLoadOrderModuleList
41    pub ldr_data_base: usize,       // _LDR_DATA_TABLE_ENTRY::DllBase
42    pub ldr_data_size: usize,       // _LDR_DATA_TABLE_ENTRY::SizeOfImage
43    pub ldr_data_full_name: usize,  // _LDR_DATA_TABLE_ENTRY::FullDllName
44    pub ldr_data_base_name: usize,  // _LDR_DATA_TABLE_ENTRY::BaseDllName
45    pub ppm_image_path_name: usize, // _RTL_USER_PROCESS_PARAMETERS::ImagePathName
46    pub ppm_command_line: usize,    // _RTL_USER_PROCESS_PARAMETERS::CommandLine
47}
48
49pub const X86: Win32ArchOffsets = Win32ArchOffsets {
50    peb_ldr: 0xc,
51    peb_process_params: 0x10,
52    ldr_list: 0xc,
53    ldr_data_base: 0x18,
54    ldr_data_size: 0x20,
55    ldr_data_full_name: 0x24,
56    ldr_data_base_name: 0x2c,
57    ppm_image_path_name: 0x38,
58    ppm_command_line: 0x40,
59};
60
61pub const X64: Win32ArchOffsets = Win32ArchOffsets {
62    peb_ldr: 0x18,
63    peb_process_params: 0x20,
64    ldr_list: 0x10,
65    ldr_data_base: 0x30,
66    ldr_data_size: 0x40,
67    ldr_data_full_name: 0x48,
68    ldr_data_base_name: 0x58,
69    ppm_image_path_name: 0x60,
70    ppm_command_line: 0x70,
71};
72
73pub const AARCH64: Win32ArchOffsets = Win32ArchOffsets {
74    peb_ldr: 0x18,
75    peb_process_params: 0x20,
76    ldr_list: 0x10,
77    ldr_data_base: 0x30,
78    ldr_data_size: 0x40,
79    ldr_data_full_name: 0x48,
80    ldr_data_base_name: 0x58,
81    ppm_image_path_name: 0x60,
82    ppm_command_line: 0x70,
83};
84
85impl Win32OffsetsArchitecture {
86    #[inline]
87    fn offsets(&self) -> &'static Win32ArchOffsets {
88        match self {
89            Win32OffsetsArchitecture::X64 => &X64,
90            Win32OffsetsArchitecture::X86 => &X86,
91            Win32OffsetsArchitecture::AArch64 => &AARCH64,
92        }
93    }
94}
95
96impl From<ArchitectureIdent> for Win32ArchOffsets {
97    fn from(arch: ArchitectureIdent) -> Win32ArchOffsets {
98        *Win32OffsetsArchitecture::from(arch).offsets()
99    }
100}
101
102#[repr(transparent)]
103#[derive(Debug, Clone)]
104#[cfg_attr(feature = "serde", derive(::serde::Serialize))]
105pub struct Win32Offsets(pub Win32OffsetTable);
106
107impl From<Win32OffsetTable> for Win32Offsets {
108    fn from(other: Win32OffsetTable) -> Self {
109        Self(other)
110    }
111}
112
113impl From<Win32Offsets> for Win32OffsetTable {
114    fn from(other: Win32Offsets) -> Self {
115        other.0
116    }
117}
118
119impl From<ArchitectureIdent> for Win32OffsetsArchitecture {
120    fn from(arch: ArchitectureIdent) -> Win32OffsetsArchitecture {
121        match arch {
122            ArchitectureIdent::X86(32, _) => Self::X86,
123            ArchitectureIdent::X86(64, _) => Self::X64,
124            ArchitectureIdent::AArch64(_) => Self::AArch64,
125            _ => panic!("Invalid architecture specified"),
126        }
127    }
128}
129
130impl Win32Offsets {
131    #[cfg(feature = "symstore")]
132    pub fn from_pdb<P: AsRef<Path>>(pdb_path: P) -> Result<Self> {
133        let mut file = File::open(pdb_path).map_err(|_| {
134            Error(ErrorOrigin::OsLayer, ErrorKind::Offset)
135                .log_warn("unable to open user-supplied pdb file")
136        })?;
137        let mut buffer = Vec::new();
138        file.read_to_end(&mut buffer).map_err(|_| {
139            Error(ErrorOrigin::OsLayer, ErrorKind::Offset)
140                .log_warn("unable to read user-supplied pdb file")
141        })?;
142        Self::from_pdb_slice(&buffer[..])
143    }
144
145    #[cfg(feature = "symstore")]
146    pub fn from_pdb_slice(pdb_slice: &[u8]) -> Result<Self> {
147        let symbols = PdbSymbols::new(pdb_slice).map_err(|_| {
148            Error(ErrorOrigin::OsLayer, ErrorKind::Offset).log_warn("Symbols not found")
149        })?;
150        let list = PdbStruct::new(pdb_slice, "_LIST_ENTRY").map_err(|_| {
151            Error(ErrorOrigin::OsLayer, ErrorKind::Offset).log_warn("_LIST_ENTRY not found")
152        })?;
153        let kproc = PdbStruct::new(pdb_slice, "_KPROCESS").map_err(|_| {
154            Error(ErrorOrigin::OsLayer, ErrorKind::Offset).log_warn("_KPROCESS not found")
155        })?;
156        let eproc = PdbStruct::new(pdb_slice, "_EPROCESS").map_err(|_| {
157            Error(ErrorOrigin::OsLayer, ErrorKind::Offset).log_warn("_EPROCESS not found")
158        })?;
159        let ethread = PdbStruct::new(pdb_slice, "_ETHREAD").map_err(|_| {
160            Error(ErrorOrigin::OsLayer, ErrorKind::Offset).log_warn("_ETHREAD not found")
161        })?;
162        let kthread = PdbStruct::new(pdb_slice, "_KTHREAD").map_err(|_| {
163            Error(ErrorOrigin::OsLayer, ErrorKind::Offset).log_warn("_KTHREAD not found")
164        })?;
165        let teb = PdbStruct::new(pdb_slice, "_TEB").map_err(|_| {
166            Error(ErrorOrigin::OsLayer, ErrorKind::Offset).log_warn("_TEB not found")
167        })?;
168        let mm_vad = PdbStruct::new(pdb_slice, "_MMVAD_SHORT").map_err(|_| {
169            Error(ErrorOrigin::OsLayer, ErrorKind::Offset).log_warn("_MMVAD_SHORT not found")
170        })?;
171        let mm_vad_flags = PdbStruct::new(pdb_slice, "_MMVAD_FLAGS").map_err(|_| {
172            Error(ErrorOrigin::OsLayer, ErrorKind::Offset).log_warn("_MMVAD_FLAGS not found")
173        })?;
174
175        let phys_mem_block = symbols
176            .find_symbol("MmPhysicalMemoryBlock")
177            .or_else(|| symbols.find_symbol("_MmPhysicalMemoryBlock"))
178            .copied()
179            .unwrap_or(0);
180
181        let list_blink = list
182            .find_field("Blink")
183            .ok_or_else(|| {
184                Error(ErrorOrigin::OsLayer, ErrorKind::Offset)
185                    .log_warn("_LIST_ENTRY::Blink not found")
186            })?
187            .offset as _;
188
189        let eproc_link = eproc
190            .find_field("ActiveProcessLinks")
191            .ok_or_else(|| {
192                Error(ErrorOrigin::OsLayer, ErrorKind::Offset)
193                    .log_warn("_EPROCESS::ActiveProcessLinks not found")
194            })?
195            .offset as _;
196
197        let kproc_dtb = kproc
198            .find_field("DirectoryTableBase")
199            .ok_or_else(|| {
200                Error(ErrorOrigin::OsLayer, ErrorKind::Offset)
201                    .log_warn("_KPROCESS::DirectoryTableBase not found")
202            })?
203            .offset as _;
204        let eproc_pid = eproc
205            .find_field("UniqueProcessId")
206            .ok_or_else(|| {
207                Error(ErrorOrigin::OsLayer, ErrorKind::Offset)
208                    .log_warn("_EPROCESS::UniqueProcessId not found")
209            })?
210            .offset as _;
211        let eproc_name = eproc
212            .find_field("ImageFileName")
213            .ok_or_else(|| {
214                Error(ErrorOrigin::OsLayer, ErrorKind::Offset)
215                    .log_warn("_EPROCESS::ImageFileName not found")
216            })?
217            .offset as _;
218        let eproc_peb = eproc
219            .find_field("Peb")
220            .ok_or_else(|| {
221                Error(ErrorOrigin::OsLayer, ErrorKind::Offset).log_warn("_EPROCESS::Peb not found")
222            })?
223            .offset as _;
224        let eproc_section_base = eproc
225            .find_field("SectionBaseAddress")
226            .ok_or_else(|| {
227                Error(ErrorOrigin::OsLayer, ErrorKind::Offset)
228                    .log_warn("_EPROCESS::SectionBaseAddress not found")
229            })?
230            .offset as _;
231        let eproc_exit_status = eproc
232            .find_field("ExitStatus")
233            .ok_or_else(|| {
234                Error(ErrorOrigin::OsLayer, ErrorKind::Offset)
235                    .log_warn("_EPROCESS::ExitStatus not found")
236            })?
237            .offset as _;
238        let eproc_thread_list = eproc
239            .find_field("ThreadListHead")
240            .ok_or_else(|| {
241                Error(ErrorOrigin::OsLayer, ErrorKind::Offset)
242                    .log_warn("_EPROCESS::ThreadListHead not found")
243            })?
244            .offset as _;
245
246        // windows 10 uses an uppercase W whereas older windows versions (windows 7) uses a lowercase w
247        let eproc_wow64 = match eproc
248            .find_field("WoW64Process")
249            .or_else(|| eproc.find_field("Wow64Process"))
250        {
251            Some(f) => f.offset as _,
252            None => 0,
253        };
254
255        // threads
256        let kthread_teb = kthread
257            .find_field("Teb")
258            .ok_or_else(|| {
259                Error(ErrorOrigin::OsLayer, ErrorKind::Offset).log_warn("_KTHREAD::Teb not found")
260            })?
261            .offset as _;
262        let ethread_list_entry = ethread
263            .find_field("ThreadListEntry")
264            .ok_or_else(|| {
265                Error(ErrorOrigin::OsLayer, ErrorKind::Offset)
266                    .log_warn("_ETHREAD::ThreadListEntry not found")
267            })?
268            .offset as _;
269        let teb_peb = teb
270            .find_field("ProcessEnvironmentBlock")
271            .ok_or_else(|| {
272                Error(ErrorOrigin::OsLayer, ErrorKind::Offset)
273                    .log_warn("_TEB::ProcessEnvironmentBlock not found")
274            })?
275            .offset as _;
276        let teb_peb_x86 = if let Ok(teb32) = PdbStruct::new(pdb_slice, "_TEB32").map_err(|_| {
277            Error(ErrorOrigin::OsLayer, ErrorKind::Offset).log_warn("_TEB32 not found")
278        }) {
279            teb32
280                .find_field("ProcessEnvironmentBlock")
281                .ok_or_else(|| {
282                    Error(ErrorOrigin::OsLayer, ErrorKind::Offset)
283                        .log_warn("_TEB32::ProcessEnvironmentBlock not found")
284                })?
285                .offset as _
286        } else {
287            0
288        };
289
290        let eproc_vad_root = eproc
291            .find_field("VadRoot") // MM_AVL_TABLE *PhysicalVadRoot / MM_AVL_TABLE VadRoot / RTL_AVL_TREE VadRoot
292            .ok_or_else(|| {
293                Error(ErrorOrigin::OsLayer, ErrorKind::Offset)
294                    .log_warn("_EPROCESS::VadRoot not found")
295            })?
296            .offset as _;
297
298        // On older versions VadNode was inlined into the structure - LeftChild being the first
299        // field of a binary tree.
300        let vad_node = mm_vad
301            .find_field("VadNode")
302            .or_else(|| mm_vad.find_field("LeftChild"))
303            .map(|f| f.offset)
304            .unwrap_or(0) as _;
305
306        let starting_vpn = mm_vad
307            .find_field("StartingVpn")
308            .map(|f| f.offset)
309            .unwrap_or(0) as _;
310        let ending_vpn = mm_vad
311            .find_field("EndingVpn")
312            .map(|f| f.offset)
313            .unwrap_or(0) as _;
314        let starting_vpn_high = mm_vad
315            .find_field("StartingVpnHigh")
316            .map(|f| f.offset)
317            .unwrap_or(0) as _;
318        let ending_vpn_high = mm_vad
319            .find_field("EndingVpnHigh")
320            .map(|f| f.offset)
321            .unwrap_or(0) as _;
322        let u = mm_vad.find_field("u").map(|f| f.offset).unwrap_or(0) as _;
323
324        let protection_bit = mm_vad_flags
325            .find_field("Protection")
326            .map(|f| f.bit_offset)
327            .unwrap_or(0) as _;
328
329        Ok(Self(Win32OffsetTable {
330            list_blink,
331            eproc_link,
332
333            phys_mem_block,
334
335            kproc_dtb,
336
337            eproc_pid,
338            eproc_name,
339            eproc_peb,
340            eproc_section_base,
341            eproc_exit_status,
342            eproc_thread_list,
343            eproc_wow64,
344            eproc_vad_root,
345
346            kthread_teb,
347            ethread_list_entry,
348            teb_peb,
349            teb_peb_x86,
350
351            mmvad: MmVadOffsetTable {
352                vad_node,
353                starting_vpn,
354                ending_vpn,
355                starting_vpn_high,
356                ending_vpn_high,
357                u,
358                protection_bit,
359            },
360        }))
361    }
362
363    /// _LIST_ENTRY::Blink offset
364    pub fn list_blink(&self) -> usize {
365        self.0.list_blink as usize
366    }
367    /// _LIST_ENTRY::Flink offset
368    pub fn eproc_link(&self) -> usize {
369        self.0.eproc_link as usize
370    }
371
372    /// MmPhysicalMemoryBlock offset
373    pub fn phys_mem_block(&self) -> usize {
374        self.0.phys_mem_block as usize
375    }
376
377    /// _KPROCESS::DirectoryTableBase offset
378    /// Exists since version 3.10
379    pub fn kproc_dtb(&self) -> usize {
380        self.0.kproc_dtb as usize
381    }
382    /// _EPROCESS::UniqueProcessId offset
383    /// Exists since version 3.10
384    pub fn eproc_pid(&self) -> usize {
385        self.0.eproc_pid as usize
386    }
387    /// _EPROCESS::ImageFileName offset
388    /// Exists since version 3.10
389    pub fn eproc_name(&self) -> usize {
390        self.0.eproc_name as usize
391    }
392    /// _EPROCESS::Peb offset
393    /// Exists since version 5.10
394    pub fn eproc_peb(&self) -> usize {
395        self.0.eproc_peb as usize
396    }
397    /// _EPROCESS::SectionBaseAddress offset
398    /// Exists since version 3.10
399    pub fn eproc_section_base(&self) -> usize {
400        self.0.eproc_section_base as usize
401    }
402    /// _EPROCESS::ExitStatus offset
403    /// Exists since version 3.10
404    pub fn eproc_exit_status(&self) -> usize {
405        self.0.eproc_exit_status as usize
406    }
407    /// _EPROCESS::ThreadListHead offset
408    /// Exists since version 5.10
409    pub fn eproc_thread_list(&self) -> usize {
410        self.0.eproc_thread_list as usize
411    }
412    /// _EPROCESS::VadRoot offset
413    /// Exists since version 5.0
414    pub fn eproc_wow64(&self) -> usize {
415        self.0.eproc_wow64 as usize
416    }
417    /// _EPROCESS::WoW64Process offset
418    /// Exists since version xxx
419    pub fn eproc_vad_root(&self) -> usize {
420        self.0.eproc_vad_root as usize
421    }
422
423    /// _KTHREAD::Teb offset
424    /// Exists since version 6.2
425    pub fn kthread_teb(&self) -> usize {
426        self.0.kthread_teb as usize
427    }
428    /// _ETHREAD::ThreadListEntry offset
429    /// Exists since version 6.2
430    pub fn ethread_list_entry(&self) -> usize {
431        self.0.ethread_list_entry as usize
432    }
433    /// _TEB::ProcessEnvironmentBlock offset
434    /// Exists since version x.x
435    pub fn teb_peb(&self) -> usize {
436        self.0.teb_peb as usize
437    }
438    /// _TEB32::ProcessEnvironmentBlock offset
439    /// Exists since version x.x
440    pub fn teb_peb_x86(&self) -> usize {
441        self.0.teb_peb_x86 as usize
442    }
443
444    /// _MMVAD_SHORT offsets
445    pub fn mm_vad(&self) -> MmVadOffsetTable {
446        self.0.mmvad
447    }
448
449    pub fn builder<'a>() -> Win32OffsetBuilder<'a> {
450        Win32OffsetBuilder::default()
451    }
452}
453
454#[cfg(test)]
455mod tests {
456    use super::*;
457
458    // this test is not ideal for the CI so it's disabled for now.
459    /*
460    #[test]
461    fn download_pdb() {
462        let guid = Win32Guid {
463            file_name: "ntkrnlmp.pdb".to_string(),
464            guid: "3844DBB920174967BE7AA4A2C20430FA2".to_string(),
465        };
466        let offsets = Win32Offsets::builder()
467            .symbol_store(SymbolStore::new().no_cache())
468            .guid(guid)
469            .build()
470            .unwrap();
471
472        assert_eq!(offsets.0.list_blink, 8);
473        assert_eq!(offsets.0.eproc_link, 392);
474
475        assert_eq!(offsets.0.kproc_dtb, 40);
476
477        assert_eq!(offsets.0.eproc_pid, 384);
478        assert_eq!(offsets.0.eproc_name, 736);
479        assert_eq!(offsets.0.eproc_peb, 824);
480        assert_eq!(offsets.0.eproc_thread_list, 776);
481        assert_eq!(offsets.0.eproc_wow64, 800);
482
483        assert_eq!(offsets.0.kthread_teb, 184);
484        assert_eq!(offsets.0.ethread_list_entry, 1056);
485        assert_eq!(offsets.0.teb_peb, 96);
486        assert_eq!(offsets.0.teb_peb_x86, 48);
487    }
488    */
489}