diff --git a/.github/workflows/check.yaml b/.github/workflows/check.yaml index fa30fe1..2ae9927 100644 --- a/.github/workflows/check.yaml +++ b/.github/workflows/check.yaml @@ -40,11 +40,14 @@ jobs: shell: bash run: RUSTFLAGS="-D warnings" cargo check --workspace - # fails due to `_Unwind_RaiseException` - # - name: Run cargo test - # shell: bash - # run: RUSTFLAGS="-D warnings" cargo test --workspace + - 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 + run: cargo build --verbose --release --package 'zipfixup' --package 'zippatch' + shell: bash + + - name: Run export checker + run: cargo run --package 'export-check' --target 'x86_64-unknown-linux-gnu' shell: bash diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index 83e0678..69b1fe0 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -70,7 +70,7 @@ jobs: run: rustup show - name: Build release binary - run: cargo build --verbose --release + run: cargo build --verbose --release --package 'zipfixup' --package 'zippatch' shell: bash - name: Build archive diff --git a/Cargo.lock b/Cargo.lock index 07637c9..1bffa3b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,6 +2,12 @@ # It is not intended for manual editing. version = 4 +[[package]] +name = "adler2" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa" + [[package]] name = "bitflags" version = "1.3.2" @@ -23,6 +29,15 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9555578bc9e57714c812a1f84e4fc5b4d21fcb063490c624de019f7464c91268" +[[package]] +name = "crc32fast" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9481c1c90cbf2ac953f07c8d4a58aa3945c425b7185c9154d67a65e4230da511" +dependencies = [ + "cfg-if", +] + [[package]] name = "deranged" version = "0.4.0" @@ -32,6 +47,23 @@ dependencies = [ "powerfmt", ] +[[package]] +name = "export-check" +version = "0.1.2" +dependencies = [ + "object", +] + +[[package]] +name = "flate2" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a3d7db9596fecd151c5f638c0ee5d5bd487b6e0ea232e5dc96d5250f6f94b1d" +dependencies = [ + "crc32fast", + "miniz_oxide", +] + [[package]] name = "generic-array" version = "0.14.7" @@ -67,6 +99,21 @@ dependencies = [ "libc", ] +[[package]] +name = "memchr" +version = "2.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a282da65faaf38286cf3be983213fcf1d2e2a58700e808f83f4ea9a4804bc0" + +[[package]] +name = "miniz_oxide" +version = "0.8.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316" +dependencies = [ + "adler2", +] + [[package]] name = "mmap-fixed-fixed" version = "0.1.3" @@ -83,6 +130,17 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" +[[package]] +name = "object" +version = "0.37.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03fd943161069e1768b4b3d050890ba48730e590f57e56d4aa04e7e090e61b4a" +dependencies = [ + "flate2", + "memchr", + "ruzstd", +] + [[package]] name = "once_cell" version = "1.21.3" @@ -141,6 +199,15 @@ dependencies = [ "slice-pool2", ] +[[package]] +name = "ruzstd" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3640bec8aad418d7d03c72ea2de10d5c646a598f9883c7babc160d91e3c1b26c" +dependencies = [ + "twox-hash", +] + [[package]] name = "serde" version = "1.0.219" @@ -203,6 +270,12 @@ version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c9e9a38711f559d9e3ce1cdb06dd7c5b8ea546bc90052da6d06bb76da74bb07c" +[[package]] +name = "twox-hash" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b907da542cbced5261bd3256de1b3a1bf340a3d37f93425a07362a1d687de56" + [[package]] name = "typenum" version = "1.18.0" diff --git a/crates/export-check/Cargo.toml b/crates/export-check/Cargo.toml new file mode 100644 index 0000000..4d526af --- /dev/null +++ b/crates/export-check/Cargo.toml @@ -0,0 +1,15 @@ +[package] +name = "export-check" +version.workspace = true + +description.workspace = true +authors.workspace = true +repository.workspace = true +readme.workspace = true +license.workspace = true + +edition.workspace = true +publish.workspace = true + +[dependencies] +object = "0.37.1" diff --git a/crates/export-check/src/main.rs b/crates/export-check/src/main.rs new file mode 100644 index 0000000..f2d6e1e --- /dev/null +++ b/crates/export-check/src/main.rs @@ -0,0 +1,133 @@ +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", +]; + +fn main() -> Result<(), Box> { + let contents = std::fs::read("target/i686-pc-windows-gnu/release/zipfixup.dll")?; + let data = contents.as_slice(); + + 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 export in &exported { + let name = export + .name + .ok_or_else(|| format!("export has no name: {:?}", export))?; + let name = str::from_utf8(name)?; + + 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); + } + 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()); + } + } + } + exports.sort(); + forwards.sort(); + + for name in exports.iter().copied() { + println!("{}", name); + } + + for name in forwards.iter().copied() { + println!("KERNEL32.{}", name); + } + + for expected in EXPECTED_EXPORTS { + if !exports.contains(expected) { + return Err(format!("missing export `{}`", expected).into()); + } + } + + for expected in EXPECTED_FORWARDS { + if !forwards.contains(expected) { + return Err(format!("missing forward `{}`", expected).into()); + } + } + + Ok(()) +} diff --git a/crates/zipfixup/src/exports.rs b/crates/zipfixup/src/exports.rs index 9d49c55..3ab0e57 100644 --- a/crates/zipfixup/src/exports.rs +++ b/crates/zipfixup/src/exports.rs @@ -2,20 +2,8 @@ use core::arch::global_asm; global_asm!(".section .drectve"); -macro_rules! forwarded_override { - ($exp_name:expr, $target_name:expr, $ordinal:expr) => { - global_asm!(concat!( - ".ascii \" -export:", - $exp_name, - "=", - $target_name, - "\"" - )); - }; -} - macro_rules! forwarded_kernel32 { - ($exp_name:expr, $ordinal:expr) => { + ($exp_name:expr) => { global_asm!(concat!( ".ascii \" -export:", $exp_name, @@ -33,70 +21,68 @@ macro_rules! forwarded_kernel32 { // EOP: Windows defender gets huffy if we leave them off. // Zunder: But we don't use the ordinals in the asm??? -// Functions we hook/override -forwarded_override!("GetTickCount", "get_tick_count", 469); - // Kernel32 functions for MW and others -forwarded_kernel32!("ReadFile", 679); -forwarded_kernel32!("CreateFileA", 80); -forwarded_kernel32!("FindNextFileA", 218); -forwarded_kernel32!("CloseHandle", 50); -forwarded_kernel32!("DeleteFileA", 130); -forwarded_kernel32!("SetFilePointer", 778); -forwarded_kernel32!("FindFirstFileA", 209); -forwarded_kernel32!("ExitProcess", 183); -forwarded_kernel32!("MoveFileA", 609); -forwarded_kernel32!("FormatMessageA", 236); -forwarded_kernel32!("Sleep", 836); -forwarded_kernel32!("LocalFree", 591); -forwarded_kernel32!("GetCommandLineA", 266); -forwarded_kernel32!("SetThreadPriority", 819); -forwarded_kernel32!("SetCurrentDirectoryA", 763); -forwarded_kernel32!("ResetEvent", 704); -forwarded_kernel32!("CreateEventA", 76); -forwarded_kernel32!("CreateThread", 109); -forwarded_kernel32!("WaitForSingleObject", 896); -forwarded_kernel32!("OutputDebugStringA", 639); -forwarded_kernel32!("SetEvent", 773); -forwarded_kernel32!("GetDriveTypeA", 332); -forwarded_kernel32!("GetTempPathA", 460); -forwarded_kernel32!("FreeLibrary", 241); -forwarded_kernel32!("GetProcAddress", 409); -forwarded_kernel32!("GetLogicalDriveStringsA", 366); -forwarded_kernel32!("GetFileSize", 348); -forwarded_kernel32!("GetFileTime", 350); -forwarded_kernel32!("LoadLibraryA", 581); -forwarded_kernel32!("lstrlenA", 953); -forwarded_kernel32!("GetVersionExA", 479); -forwarded_kernel32!("GlobalMemoryStatus", 506); -forwarded_kernel32!("QueryPerformanceFrequency", 662); -forwarded_kernel32!("GetThreadPriority", 465); -forwarded_kernel32!("QueryPerformanceCounter", 661); -forwarded_kernel32!("InitializeCriticalSection", 537); -forwarded_kernel32!("DeleteCriticalSection", 128); -forwarded_kernel32!("GetCurrentThread", 318); -forwarded_kernel32!("EnterCriticalSection", 151); -forwarded_kernel32!("ReleaseMutex", 692); -forwarded_kernel32!("LeaveCriticalSection", 580); -forwarded_kernel32!("GetModuleHandleA", 375); -forwarded_kernel32!("GetStartupInfoA", 431); -forwarded_kernel32!("CreateMutexA", 93); -forwarded_kernel32!("GetLastError", 361); -forwarded_kernel32!("WriteFile", 913); +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", 541); -forwarded_kernel32!("InterlockedIncrement", 545); -forwarded_kernel32!("CompareFileTime", 55); -forwarded_kernel32!("WaitForMultipleObjects", 894); -forwarded_kernel32!("GetSystemDefaultLangID", 440); -forwarded_kernel32!("CreateDirectoryA", 72); -forwarded_kernel32!("GetCurrentDirectoryA", 314); +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", 205); -forwarded_kernel32!("LoadResource", 586); -forwarded_kernel32!("LockResource", 600); -forwarded_kernel32!("SizeofResource", 835); -forwarded_kernel32!("FindResourceExA", 225); -forwarded_kernel32!("EnumResourceNamesA", 164); +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/overrides.rs b/crates/zipfixup/src/overrides.rs index 9971f59..de22646 100644 --- a/crates/zipfixup/src/overrides.rs +++ b/crates/zipfixup/src/overrides.rs @@ -4,9 +4,9 @@ use winapi::shared::minwindef::DWORD; static START_TIME: OnceLock = OnceLock::new(); -// TODO: work out how to hide this symbol but still get correct linkage #[unsafe(no_mangle)] -extern "system" fn get_tick_count() -> DWORD { +#[allow(non_snake_case)] +extern "system" fn GetTickCount() -> DWORD { let start_time = START_TIME.get_or_init(Instant::now); let elapsed = start_time.elapsed().as_millis(); elapsed as DWORD