Private/DllUtils.psm1

using module .\Exceptions.psm1
using module .\Utilities.psm1
using namespace System
using namespace System.Collections.Generic
using namespace System.Runtime.InteropServices

class DllUtils {
    static [object] ConvertToString([System.String] $Path) {
        $FileStream = New-Object -TypeName IO.FileStream -ArgumentList (Resolve-Path $Path), 'Open', 'Read'
        $Encoding = [Text.Encoding]::GetEncoding(28591)
        $StreamReader = New-Object IO.StreamReader($FileStream, $Encoding)
        $BinaryText = $StreamReader.ReadToEnd()
        $StreamReader.Close()
        $FileStream.Close()
        return $BinaryText
    }

    static [double] GetEntropy([System.Byte[]] $ByteArray, [System.IO.FileInfo] $FilePath) {
        if ($FilePath) {
            $ByteArray = [IO.File]::ReadAllBytes($FilePath.FullName)
        }
        if (!$ByteArray) { return 0.0 }
        
        $FrequencyTable = @{}
        foreach ($Byte in $ByteArray) {
            $FrequencyTable[$Byte] = [int]$FrequencyTable[$Byte] + 1
        }
        
        $Entropy = 0.0
        foreach ($Byte in 0..255) {
            $ByteProbability = ([Double] $FrequencyTable[[Byte]$Byte]) / $ByteArray.Length
            if ($ByteProbability -gt 0) {
                $Entropy += - $ByteProbability * [Math]::Log($ByteProbability, 2)
            }
        }
        return $Entropy
    }

    static [object[]] GetStrings([string[]] $Paths, [System.String] $Encoding = 'Default', [System.UInt32] $MinimumLength = 3) {
        $Results = @()
        foreach ($Path in $Paths) {
            if ($Encoding -eq 'Unicode' -or $Encoding -eq 'Default') {
                $UnicodeFileContents = Get-Content -Encoding Unicode $Path -Raw
                $UnicodeRegex = [Regex]::new("[\u0020-\u007E]{$MinimumLength,}")
                foreach ($match in $UnicodeRegex.Matches($UnicodeFileContents)) { $Results += $match.Value }
            }
            if ($Encoding -eq 'Ascii' -or $Encoding -eq 'Default') {
                $AsciiFileContents = Get-Content -Encoding Ascii $Path -Raw
                $AsciiRegex = [Regex]::new("[\x20-\x7E]{$MinimumLength,}")
                foreach ($match in $AsciiRegex.Matches($AsciiFileContents)) { $Results += $match.Value }
            }
        }
        return $Results
    }

    # Helper for creating delegate types
    static [Type] GetDelegateType([Type[]]$Parameters, [Type]$ReturnType) {
        $Domain = [AppDomain]::CurrentDomain
        $DynAssembly = [System.Reflection.AssemblyName]::new('ReflectedDelegate')
        $AssemblyBuilder = $Domain.DefineDynamicAssembly($DynAssembly, [System.Reflection.Emit.AssemblyBuilderAccess]::Run)
        $ModuleBuilder = $AssemblyBuilder.DefineDynamicModule('InMemoryModule')
        $TypeBuilder = $ModuleBuilder.DefineType('MyDelegateType', 'Class, Public, Sealed, AnsiClass, AutoClass', [System.MulticastDelegate])
        
        $ConstructorBuilder = $TypeBuilder.DefineConstructor('RTSpecialName, HideBySig, Public', [System.Reflection.CallingConventions]::Standard, $Parameters)
        $ConstructorBuilder.SetImplementationFlags('Runtime, Managed')
        
        $MethodBuilder = $TypeBuilder.DefineMethod('Invoke', 'Public, HideBySig, NewSlot, Virtual', $ReturnType, $Parameters)
        $MethodBuilder.SetImplementationFlags('Runtime, Managed')
        
        return $TypeBuilder.CreateType()
    }

    static [Type] $Win32NativeMethods = $null

    static [void] InitNativeMethods() {
        if ($null -ne [DllUtils]::Win32NativeMethods) { return }
        $type = ('DllUtils.NativeMethods' -as [type])
        if ($type) {
            [DllUtils]::Win32NativeMethods = $type
            return
        }
        $Signature = @'
[DllImport("kernel32.dll", SetLastError=true)]
public static extern IntPtr VirtualAlloc(IntPtr lpAddress, uint dwSize, uint flAllocationType, uint flProtect);
[DllImport("kernel32.dll", SetLastError=true, CharSet=CharSet.Unicode)]
public static extern IntPtr LoadLibrary(string lpFileName);
[DllImport("kernel32.dll", SetLastError=true, CharSet=CharSet.Ansi)]
public static extern IntPtr GetProcAddress(IntPtr hModule, string procName);
'@

        [DllUtils]::Win32NativeMethods = Add-Type -MemberDefinition $Signature -Name "NativeMethods" -Namespace "DllUtils" -PassThru
    }

    # PInvoke for VirtualAlloc
    static [IntPtr] VirtualAlloc([IntPtr]$lpAddress, [uint32]$dwSize, [uint32]$flAllocationType, [uint32]$flProtect) {
        [DllUtils]::InitNativeMethods()
        return [DllUtils]::Win32NativeMethods::VirtualAlloc($lpAddress, $dwSize, $flAllocationType, $flProtect)
    }

    static [IntPtr] LoadLibrary([string]$lpFileName) {
        [DllUtils]::InitNativeMethods()
        return [DllUtils]::Win32NativeMethods::LoadLibrary($lpFileName)
    }

    static [IntPtr] GetProcAddress([IntPtr]$hModule, [string]$procName) {
        [DllUtils]::InitNativeMethods()
        return [DllUtils]::Win32NativeMethods::GetProcAddress($hModule, $procName)
    }

    static [Delegate] NewFunctionDelegate([Type[]]$Parameters, [Type]$ReturnType, [Byte[]]$FunctionBytes, [IntPtr]$FunctionAddress, [CallingConvention]$CallingConvention) {
        if ($FunctionBytes) {
            $PBytes = [DllUtils]::VirtualAlloc([IntPtr]::Zero, $FunctionBytes.Length, 0x3000, 0x40)
            [Marshal]::Copy($FunctionBytes, 0, $PBytes, $FunctionBytes.Length)
            $FunctionAddress = $PBytes
        }
        
        $DelegateType = [DllUtils]::GetDelegateType($Parameters, $ReturnType)
        return [Marshal]::GetDelegateForFunctionPointer($FunctionAddress, $DelegateType)
    }

    # dnlib integration
    static [object] GetAssemblyImplementedMethods([string]$AssemblyPath) {
        # Assuming dnlib is loaded
        $Module = [dnlib.DotNet.ModuleDefMD]::Load($AssemblyPath)
        $Methods = New-Object 'System.Collections.Generic.List[dnlib.DotNet.MethodDef]'
        foreach ($Type in $Module.GetTypes()) {
            foreach ($Method in $Type.Methods) {
                if ($Method.HasBody) { $Methods.Add($Method) }
            }
        }
        return $Methods
    }
}