diff --git a/.github/workflows/check.yaml b/.github/workflows/check.yaml index 2ae9927..32531d9 100644 --- a/.github/workflows/check.yaml +++ b/.github/workflows/check.yaml @@ -36,18 +36,18 @@ jobs: shell: bash run: cargo fmt --all -- --check - - name: Run cargo check + - name: Run cargo check (gnu) shell: bash - run: RUSTFLAGS="-D warnings" cargo check --workspace + run: RUSTFLAGS="-D warnings" cargo check --workspace --target 'i686-pc-windows-gnu' - name: Run cargo test shell: bash run: RUSTFLAGS="-D warnings" cargo test --workspace --target 'x86_64-unknown-linux-gnu' - - name: Run cargo build (release) - run: cargo build --verbose --release --package 'zipfixup' --package 'zippatch' + - name: Run cargo build (release gnu) shell: bash + run: cargo build --verbose --release --package 'zipfixup' --package 'zippatch' --target 'i686-pc-windows-gnu' - - name: Run export checker - run: cargo run --package 'export-check' --target 'x86_64-unknown-linux-gnu' + - name: Run export checker (gnu) shell: bash + run: cargo run --package 'export-check' --target 'x86_64-unknown-linux-gnu' -- 'target/i686-pc-windows-gnu/release/zipfixup.dll' diff --git a/crates/export-check/src/main.rs b/crates/export-check/src/main.rs index f2d6e1e..538337d 100644 --- a/crates/export-check/src/main.rs +++ b/crates/export-check/src/main.rs @@ -1,133 +1,143 @@ -use object::read::pe::{ExportTarget, PeFile32}; - -const EXPECTED_EXPORTS: &[&str] = &["DllMain", "GetTickCount"]; -const EXPECTED_FORWARDS: &[&str] = &[ - "CloseHandle", - "CompareFileTime", - "CreateDirectoryA", - "CreateEventA", - "CreateFileA", - "CreateMutexA", - "CreateThread", - "DeleteCriticalSection", - "DeleteFileA", - "EnterCriticalSection", - "EnumResourceNamesA", - "ExitProcess", - "FindClose", - "FindFirstFileA", - "FindNextFileA", - "FindResourceExA", - "FormatMessageA", - "FreeLibrary", - "GetCommandLineA", - "GetCurrentDirectoryA", - "GetCurrentThread", - "GetDriveTypeA", - "GetFileSize", - "GetFileTime", - "GetLastError", - "GetLogicalDriveStringsA", - "GetModuleHandleA", - "GetProcAddress", - "GetStartupInfoA", - "GetSystemDefaultLangID", - "GetTempPathA", - "GetThreadPriority", - "GetVersionExA", - "GlobalMemoryStatus", - "InitializeCriticalSection", - "InterlockedDecrement", - "InterlockedIncrement", - "LeaveCriticalSection", - "LoadLibraryA", - "LoadResource", - "LocalFree", - "LockResource", - "lstrlenA", - "MoveFileA", - "OutputDebugStringA", - "OutputDebugStringW", - "QueryPerformanceCounter", - "QueryPerformanceFrequency", - "ReadFile", - "ReleaseMutex", - "ResetEvent", - "SetCurrentDirectoryA", - "SetEvent", - "SetFilePointer", - "SetThreadPriority", - "SizeofResource", - "Sleep", - "WaitForMultipleObjects", - "WaitForSingleObject", - "WriteFile", -]; +use object::read::pe::{Export, ExportTarget, PeFile32}; +use std::collections::HashSet; +use std::fmt; + +const DEF_FILE: &str = include_str!("../../zipfixup/exports.def"); fn main() -> Result<(), Box> { - let contents = std::fs::read("target/i686-pc-windows-gnu/release/zipfixup.dll")?; - let data = contents.as_slice(); + let (expected_exports, expected_forwards) = parse_def_file()?; + + for path in std::env::args().skip(1) { + println!("=== {} ===", path); + + let contents = std::fs::read(path)?; + let data = contents.as_slice(); + let pe = PeFile32::parse(data)?; + let table = pe.export_table()?.ok_or("no export table")?; + let exported = table.exports()?; + + validate_exports(&exported, &expected_exports, &expected_forwards)?; + } + + Ok(()) +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] +struct Forward<'a> { + export: &'a str, + module: &'a str, + forward: &'a str, +} + +impl fmt::Display for Forward<'_> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{}={}.{}", self.export, self.module, self.forward) + } +} + +fn parse_def_file() -> Result<(Vec<&'static str>, Vec>), String> { + let mut it = DEF_FILE.lines(); + let line = it.next().unwrap(); + if line != "LIBRARY ZIPFIXUP" { + return Err(format!("expected `LIBRARY ZIPFIXUP`, found `{line}`")); + } + let line = it.next().unwrap(); + if line != "EXPORTS" { + return Err(format!("expected `EXPORTS`, found `{line}`")); + } - let pe = PeFile32::parse(data)?; - let table = pe.export_table()?.ok_or("no export table")?; - let exported = table.exports()?; + let mut exports = Vec::new(); + let mut forwards = Vec::new(); + + for line in it { + let line = line.trim(); + if line.is_empty() { + continue; + } + + let parse: Vec<&str> = line.split(&['=', '.']).collect(); + match &parse[..] { + [export, module, forward] => { + forwards.push(Forward { + export, + module, + forward, + }); + } + _ => { + exports.push(line); + } + } + } + + Ok((exports, forwards)) +} +fn validate_exports( + exported: &[Export<'_>], + expected_exports: &[&'static str], + expected_forwards: &[Forward<'static>], +) -> Result<(), String> { let mut exports = Vec::new(); let mut forwards = Vec::new(); - for export in &exported { + for export in exported { let name = export .name - .ok_or_else(|| format!("export has no name: {:?}", export))?; - let name = str::from_utf8(name)?; + .ok_or_else(|| format!("export has no name: {export:?}"))?; + let name = + str::from_utf8(name).map_err(|_e| format!("export name is not unicode: {export:?}"))?; match export.target { ExportTarget::ForwardByName(module, forward) => { - if module != b"KERNEL32" { - return Err(format!("expected forward to KERNEL32: {:?}", export).into()); - } - if name.as_bytes() != forward { - return Err(format!( - "forward name mismatch `{}` != `{}`", - name, - forward.escape_ascii() - ) - .into()); - } - forwards.push(name); + let module = str::from_utf8(module) + .map_err(|_e| format!("export module is not unicode: {export:?}"))?; + let forward = str::from_utf8(forward) + .map_err(|_e| format!("export forward is not unicode: {export:?}"))?; + + forwards.push(Forward { + export: name, + module, + forward, + }); } ExportTarget::Address(_addr) => { exports.push(name); } - ExportTarget::ForwardByOrdinal(module, _ordinal) => { - if module != b"KERNEL32" { - return Err(format!("expected forward to KERNEL32: {:?}", export).into()); - } - return Err(format!("unexpected forward by ordinal: {:?}", export).into()); + ExportTarget::ForwardByOrdinal(_module, _ordinal) => { + return Err(format!("unexpected forward by ordinal: {export:?}")); } } } + + let mut actual_exports: HashSet<&str> = exports.iter().copied().collect(); + let mut actual_forwards: HashSet> = forwards.iter().copied().collect(); + exports.sort(); forwards.sort(); for name in exports.iter().copied() { - println!("{}", name); + println!("{name}"); } - for name in forwards.iter().copied() { - println!("KERNEL32.{}", name); + for forward in forwards.iter().copied() { + println!("{forward}"); } - for expected in EXPECTED_EXPORTS { - if !exports.contains(expected) { - return Err(format!("missing export `{}`", expected).into()); + for expected in expected_exports { + if !actual_exports.remove(expected) { + return Err(format!("missing export `{expected}`")); } } - for expected in EXPECTED_FORWARDS { - if !forwards.contains(expected) { - return Err(format!("missing forward `{}`", expected).into()); + for expected in expected_forwards { + if !actual_forwards.remove(expected) { + return Err(format!("missing forward `{expected}`")); } } + if !actual_forwards.is_empty() { + return Err(format!("unexpected forwards: {actual_forwards:?}")); + } + Ok(()) } diff --git a/crates/zipfixup/build.rs b/crates/zipfixup/build.rs new file mode 100644 index 0000000..26f2191 --- /dev/null +++ b/crates/zipfixup/build.rs @@ -0,0 +1,25 @@ +fn main() { + let env = std::env::var("CARGO_CFG_TARGET_ENV").unwrap(); + + let path = std::env::var("CARGO_MANIFEST_DIR").unwrap(); + let mut path = std::path::PathBuf::from(path); + path.push("exports.def"); + let path = format!("{}", path.display()); + + println!("cargo::warning={path}"); + println!("cargo::rerun-if-changed={path}"); + + match env.as_str() { + "gnu" => { + println!("cargo::warning=GNU"); + println!("cargo::rustc-link-arg-cdylib={path}"); + } + "msvc" => { + println!("cargo::warning=MSVC"); + println!("cargo::rustc-link-arg-cdylib=/DEF:{path}"); + } + _ => { + println!("cargo::warning=unknown env `{env}`"); + } + } +} diff --git a/crates/zipfixup/exports.def b/crates/zipfixup/exports.def new file mode 100644 index 0000000..785356d --- /dev/null +++ b/crates/zipfixup/exports.def @@ -0,0 +1,65 @@ +LIBRARY ZIPFIXUP +EXPORTS + DllMain + GetTickCount + + CloseHandle=KERNEL32.CloseHandle + CompareFileTime=KERNEL32.CompareFileTime + CreateDirectoryA=KERNEL32.CreateDirectoryA + CreateEventA=KERNEL32.CreateEventA + CreateFileA=KERNEL32.CreateFileA + CreateMutexA=KERNEL32.CreateMutexA + CreateThread=KERNEL32.CreateThread + DeleteCriticalSection=KERNEL32.DeleteCriticalSection + DeleteFileA=KERNEL32.DeleteFileA + EnterCriticalSection=KERNEL32.EnterCriticalSection + EnumResourceNamesA=KERNEL32.EnumResourceNamesA + ExitProcess=KERNEL32.ExitProcess + FindClose=KERNEL32.FindClose + FindFirstFileA=KERNEL32.FindFirstFileA + FindNextFileA=KERNEL32.FindNextFileA + FindResourceExA=KERNEL32.FindResourceExA + FormatMessageA=KERNEL32.FormatMessageA + FreeLibrary=KERNEL32.FreeLibrary + GetCommandLineA=KERNEL32.GetCommandLineA + GetCurrentDirectoryA=KERNEL32.GetCurrentDirectoryA + GetCurrentThread=KERNEL32.GetCurrentThread + GetDriveTypeA=KERNEL32.GetDriveTypeA + GetFileSize=KERNEL32.GetFileSize + GetFileTime=KERNEL32.GetFileTime + GetLastError=KERNEL32.GetLastError + GetLogicalDriveStringsA=KERNEL32.GetLogicalDriveStringsA + GetModuleHandleA=KERNEL32.GetModuleHandleA + GetProcAddress=KERNEL32.GetProcAddress + GetStartupInfoA=KERNEL32.GetStartupInfoA + GetSystemDefaultLangID=KERNEL32.GetSystemDefaultLangID + GetTempPathA=KERNEL32.GetTempPathA + GetThreadPriority=KERNEL32.GetThreadPriority + GetVersionExA=KERNEL32.GetVersionExA + GlobalMemoryStatus=KERNEL32.GlobalMemoryStatus + InitializeCriticalSection=KERNEL32.InitializeCriticalSection + InterlockedDecrement=KERNEL32.InterlockedDecrement + InterlockedIncrement=KERNEL32.InterlockedIncrement + LeaveCriticalSection=KERNEL32.LeaveCriticalSection + LoadLibraryA=KERNEL32.LoadLibraryA + LoadResource=KERNEL32.LoadResource + LocalFree=KERNEL32.LocalFree + LockResource=KERNEL32.LockResource + lstrlenA=KERNEL32.lstrlenA + MoveFileA=KERNEL32.MoveFileA + OutputDebugStringA=KERNEL32.OutputDebugStringA + OutputDebugStringW=KERNEL32.OutputDebugStringW + QueryPerformanceCounter=KERNEL32.QueryPerformanceCounter + QueryPerformanceFrequency=KERNEL32.QueryPerformanceFrequency + ReadFile=KERNEL32.ReadFile + ReleaseMutex=KERNEL32.ReleaseMutex + ResetEvent=KERNEL32.ResetEvent + SetCurrentDirectoryA=KERNEL32.SetCurrentDirectoryA + SetEvent=KERNEL32.SetEvent + SetFilePointer=KERNEL32.SetFilePointer + SetThreadPriority=KERNEL32.SetThreadPriority + SizeofResource=KERNEL32.SizeofResource + Sleep=KERNEL32.Sleep + WaitForMultipleObjects=KERNEL32.WaitForMultipleObjects + WaitForSingleObject=KERNEL32.WaitForSingleObject + WriteFile=KERNEL32.WriteFile diff --git a/crates/zipfixup/src/exports.rs b/crates/zipfixup/src/exports.rs deleted file mode 100644 index 3ab0e57..0000000 --- a/crates/zipfixup/src/exports.rs +++ /dev/null @@ -1,88 +0,0 @@ -use core::arch::global_asm; - -global_asm!(".section .drectve"); - -macro_rules! forwarded_kernel32 { - ($exp_name:expr) => { - global_asm!(concat!( - ".ascii \" -export:", - $exp_name, - "=KERNEL32.", - $exp_name, - "\"" - )); - }; -} - -// The linker gets very unhappy if code is after this. -// It's probably due to generated code after weird inline asm sections - -// Ordinals here refer to the imports. -// EOP: Windows defender gets huffy if we leave them off. -// Zunder: But we don't use the ordinals in the asm??? - -// Kernel32 functions for MW and others -forwarded_kernel32!("ReadFile"); // ordinal: 679 -forwarded_kernel32!("CreateFileA"); // ordinal: 80 -forwarded_kernel32!("FindNextFileA"); // ordinal: 218 -forwarded_kernel32!("CloseHandle"); // ordinal: 50 -forwarded_kernel32!("DeleteFileA"); // ordinal: 130 -forwarded_kernel32!("SetFilePointer"); // ordinal: 778 -forwarded_kernel32!("FindFirstFileA"); // ordinal: 209 -forwarded_kernel32!("ExitProcess"); // ordinal: 183 -forwarded_kernel32!("MoveFileA"); // ordinal: 609 -forwarded_kernel32!("FormatMessageA"); // ordinal: 236 -forwarded_kernel32!("Sleep"); // ordinal: 836 -forwarded_kernel32!("LocalFree"); // ordinal: 591 -forwarded_kernel32!("GetCommandLineA"); // ordinal: 266 -forwarded_kernel32!("SetThreadPriority"); // ordinal: 819 -forwarded_kernel32!("SetCurrentDirectoryA"); // ordinal: 763 -forwarded_kernel32!("ResetEvent"); // ordinal: 704 -forwarded_kernel32!("CreateEventA"); // ordinal: 76 -forwarded_kernel32!("CreateThread"); // ordinal: 109 -forwarded_kernel32!("WaitForSingleObject"); // ordinal: 896 -forwarded_kernel32!("OutputDebugStringA"); // ordinal: 639 -forwarded_kernel32!("OutputDebugStringW"); -forwarded_kernel32!("SetEvent"); // ordinal: 773 -forwarded_kernel32!("GetDriveTypeA"); // ordinal: 332 -forwarded_kernel32!("GetTempPathA"); // ordinal: 460 -forwarded_kernel32!("FreeLibrary"); // ordinal: 241 -forwarded_kernel32!("GetProcAddress"); // ordinal: 409 -forwarded_kernel32!("GetLogicalDriveStringsA"); // ordinal: 366 -forwarded_kernel32!("GetFileSize"); // ordinal: 348 -forwarded_kernel32!("GetFileTime"); // ordinal: 350 -forwarded_kernel32!("LoadLibraryA"); // ordinal: 581 -forwarded_kernel32!("lstrlenA"); // ordinal: 953 -forwarded_kernel32!("GetVersionExA"); // ordinal: 479 -forwarded_kernel32!("GlobalMemoryStatus"); // ordinal: 506 -forwarded_kernel32!("QueryPerformanceFrequency"); // ordinal: 662 -forwarded_kernel32!("GetThreadPriority"); // ordinal: 465 -forwarded_kernel32!("QueryPerformanceCounter"); // ordinal: 661 -forwarded_kernel32!("InitializeCriticalSection"); // ordinal: 537 -forwarded_kernel32!("DeleteCriticalSection"); // ordinal: 128 -forwarded_kernel32!("GetCurrentThread"); // ordinal: 318 -forwarded_kernel32!("EnterCriticalSection"); // ordinal: 151 -forwarded_kernel32!("ReleaseMutex"); // ordinal: 692 -forwarded_kernel32!("LeaveCriticalSection"); // ordinal: 580 -forwarded_kernel32!("GetModuleHandleA"); // ordinal: 375 -forwarded_kernel32!("GetStartupInfoA"); // ordinal: 431 -forwarded_kernel32!("CreateMutexA"); // ordinal: 93 -forwarded_kernel32!("GetLastError"); // ordinal: 361 -forwarded_kernel32!("WriteFile"); // ordinal: 913 - -// Kernel32 functions for RC -forwarded_kernel32!("InterlockedDecrement"); // ordinal: 541 -forwarded_kernel32!("InterlockedIncrement"); // ordinal: 545 -forwarded_kernel32!("CompareFileTime"); // ordinal: 55 -forwarded_kernel32!("WaitForMultipleObjects"); // ordinal: 894 -forwarded_kernel32!("GetSystemDefaultLangID"); // ordinal: 440 -forwarded_kernel32!("CreateDirectoryA"); // ordinal: 72 -forwarded_kernel32!("GetCurrentDirectoryA"); // ordinal: 314 - -// Kernel32 functions for PM -forwarded_kernel32!("FindClose"); // ordinal: 205 -forwarded_kernel32!("LoadResource"); // ordinal: 586 -forwarded_kernel32!("LockResource"); // ordinal: 600 -forwarded_kernel32!("SizeofResource"); // ordinal: 835 -forwarded_kernel32!("FindResourceExA"); // ordinal: 225 -forwarded_kernel32!("EnumResourceNamesA"); // ordinal: 164 diff --git a/crates/zipfixup/src/lib.rs b/crates/zipfixup/src/lib.rs index 20fa9ff..8cae322 100644 --- a/crates/zipfixup/src/lib.rs +++ b/crates/zipfixup/src/lib.rs @@ -1,7 +1,6 @@ #![cfg(windows)] mod dbg; mod dll_main; -mod exports; mod hook; mod mech3; mod overrides;