From e35a2191688d79874f757425696e1a838d38323f Mon Sep 17 00:00:00 2001 From: cocateh Date: Mon, 6 Dec 2021 14:28:36 +0100 Subject: [PATCH] added alpha, itanium and ppc support --- README.md | 7 +++- src/main.rs | 97 +++++++++++++++++++++++++++++++++++++++++++++++------ 2 files changed, 93 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index 841b2bb..7d051eb 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,10 @@ # Overview `wausyscall` is a tool for extracting Windows syscall numbers from usermode -syscall wrapper dlls such as ntdll.dll or win32u.dll +syscall wrapper dlls such as ntdll.dll or win32u.dll. + +It should support every platform Windows has ever run on natively so far!! +(i386, amd64, arm, arm64, alpha, mips, ppc, itanium). # Usage & Installation @@ -27,5 +30,7 @@ a path to the dll is required. Some of the functions which match to syscall regex, but are not syscalls, will return incorrect output. + `--only-erroneus` flag tries to print out problematic functions, yet it is not 100% accurate. + diff --git a/src/main.rs b/src/main.rs index c50a5c2..3041a1f 100644 --- a/src/main.rs +++ b/src/main.rs @@ -28,6 +28,9 @@ const COFF_X86_MACHINE: u16 = 0x014c; const COFF_AMD64_MACHINE: u16 = 0x8664; const COFF_R4000_MACHINE: u16 = 0x166; const COFF_AARCH64_MACHINE: u16 = 0xAA64; +const COFF_ITANIUM_MACHINE: u16 = 0x0200; +const COFF_POWERPC_MACHINE: u16 = 0x01f0; +const COFF_ALPHA_MACHINE: u16 = 0x0184; // const DATA_DIRECTORIES: usize = 16; @@ -84,6 +87,9 @@ enum Machine { /// MIPSIII R4000 R4000, AArch64, + Alpha, + PowerPC, + Itanium, } impl TryFrom for Machine { @@ -94,6 +100,9 @@ impl TryFrom for Machine { COFF_AMD64_MACHINE => Ok(Machine::AMD64), COFF_R4000_MACHINE => Ok(Machine::R4000), COFF_AARCH64_MACHINE => Ok(Machine::AArch64), + COFF_POWERPC_MACHINE => Ok(Machine::PowerPC), + COFF_ALPHA_MACHINE => Ok(Machine::Alpha), + COFF_ITANIUM_MACHINE => Ok(Machine::Itanium), _ => Err(Error::InvalidCoffMachine(val)), } } @@ -107,6 +116,9 @@ impl TryInto for Machine { Machine::AMD64 => Ok(COFF_AMD64_MACHINE), Machine::R4000 => Ok(COFF_R4000_MACHINE), Machine::AArch64 => Ok(COFF_AARCH64_MACHINE), + Machine::Alpha => Ok(COFF_ALPHA_MACHINE), + Machine::PowerPC => Ok(COFF_POWERPC_MACHINE), + Machine::Itanium => Ok(COFF_ITANIUM_MACHINE), } } } @@ -121,28 +133,83 @@ impl TryFrom for Bitness { type Error = Error; fn try_from(val: Machine) -> Result { match val { - Machine::X86 => Ok(Bitness::Bits32), Machine::AMD64 => Ok(Bitness::Bits64), - // MIPSIII was used in 32 bit mode - Machine::R4000 => Ok(Bitness::Bits32), Machine::AArch64 => Ok(Bitness::Bits64), + Machine::Itanium => Ok(Bitness::Bits64), + + // Windows used 32 bit exclusively until Windows XP + Machine::R4000 => Ok(Bitness::Bits32), + Machine::Alpha => Ok(Bitness::Bits32), + Machine::PowerPC => Ok(Bitness::Bits32), + Machine::X86 => Ok(Bitness::Bits32), } } } +/// Read Alpha syscall +fn read_alpha_syscall(mut reader: impl Read) -> Result { + /* + * lda $0, {syscall number}($31) ;; r32 is a zero register + * call_pal callsys ;; somekind of syscall invkoing mechanism + */ + typed_consume!(reader, u16, "Syscall number") +} + +/// Read PowerPC syscall +fn read_ppc_syscall(mut reader: impl Read) -> Result { + /* + * PowerPC uses syscall wrappers, cause there is no simple way of + * making syscalls. + * + * Example: + * addi r0, 0, {syscall number} ;; or li r0, {syscall number} + * b syscall_wrapper + */ + typed_consume!(reader, u16, "Syscall number") +} + +/// Read Itanium syscall +fn read_itanium_syscall(mut reader: impl Read) -> Result { + /* + * Itanium is hella weird. + * So because of weird instruction encoding and operand sizes we have to + * fuck around. + * mov takes a 22 bit immediate which we want parse into sane 16 bit, + * so we don't even have to read the whole bundle or even a whole slot. + * + * Example syscall (im trusting IDA output): + * mov r8 = {syscall number} + * movl r2 = 0xE0000000FFA0020;; # move KiFastSystemCall address + * nop.m 0 # padding? + * mov b6 = r2 # move address to branch register + * br.few b6;; # branch off + */ + let bundle = consume!(reader, 5, "Itanium instruction bundle")?; + let mut value_bytes = [0u8; 2]; + value_bytes[0] = ((bundle[1] & 0b00000011) << 6) | + ((bundle[2] & 0b11111100) >> 2); + value_bytes[1] = ((bundle[2] & 0b00000011) << 6) | + ((bundle[3] & 0b11111100) >> 2); + Ok(u16::from_le_bytes(value_bytes)) +} + +/// Read ARM AArch64 syscall fn read_aarch64_syscall(mut reader: impl Read) -> Result { /* * ARM Windows uses SVC instruction for syscalls and in contrast to * linux, it uses the exception number in an instruction for syscalls, * which requires us to use value mask on the instruction and then just * align the bits correcly. + * + * Example: + * svc {syscall_number} */ let svc_instruction = consume!(reader, 4, "SVC instruction")?; let mut value_bytes = [0u8; 2]; - value_bytes[0] = ((svc_instruction[1] & 0b11111) << 3) | + value_bytes[0] = ((svc_instruction[1] & 0b00011111) << 3) | ((svc_instruction[0] & 0b11100000) >> 5); value_bytes[1] = ((svc_instruction[1] & 0b11100000) >> 5) | - ((svc_instruction[2] & 0b11111) << 3); + ((svc_instruction[2] & 0b00011111) << 3); Ok(u16::from_le_bytes(value_bytes)) } @@ -152,22 +219,26 @@ fn read_r4000_syscall(mut reader: impl Read) -> Result { * Windows uses little endian on every platform, * and MIPS encodes immediate instructions to have 16 byte immediate * operands. + * + * Example: + * addiu $zero, ${syscall_num} + * syscall */ - Ok(typed_consume!(reader, u16, "Syscall number")?) + typed_consume!(reader, u16, "Syscall number") } /// Read x86 syscall for Windows 8 and higher fn read_x86_syscall(mut reader: impl Read) -> Result { /* * Windows 8 <= syscall functions are constructed in format - * mov r10, rcx ;; 3 byte instruction + * mov r10, rcx ;; 3 byte instruction * mov eax, {syscall number} ;; 5 bytes instruction * * reading 4 bytes removes these unnecessary opcodes and leaves us * with 32 bit syscall number to read */ let _opcodes = typed_consume!(reader, u32, "Useless opcodes")?; - Ok(typed_consume!(reader, u16, "Syscall number")?) + typed_consume!(reader, u16, "Syscall number") } /// Read x86 syscall for Windows XP and lower @@ -179,7 +250,7 @@ fn read_old_syscall(mut reader: impl Read) -> Result { * for arguments and eax for syscall numbers. * * NtTerminateProcess: - * mov eax, 101h ;; 5 byte instruction + * mov eax, 101h ;; 5 byte instruction * mov edx, KiFastSystemCall * call [edx] * @@ -187,7 +258,7 @@ fn read_old_syscall(mut reader: impl Read) -> Result { * but this parsing format is basically the same */ let _opcodes = consume!(reader, 1, "Useless opcodes")?; - Ok(typed_consume!(reader, u16, "Syscall number")?) + typed_consume!(reader, u16, "Syscall number") } /// Read syscalls from file at file_path. @@ -466,6 +537,12 @@ fn parse_syscalls(file_path: impl AsRef, syscall_num = read_r4000_syscall(&mut reader)?; } else if let Machine::AArch64 = machine { syscall_num = read_aarch64_syscall(&mut reader)?; + } else if let Machine::Alpha = machine { + syscall_num = read_alpha_syscall(&mut reader)?; + } else if let Machine::PowerPC = machine { + syscall_num = read_ppc_syscall(&mut reader)?; + } else if let Machine::Itanium = machine { + syscall_num = read_itanium_syscall(&mut reader)?; } else { return Err(Error::InvalidCoffMachine(machine.try_into()?)) }