Resolve an API Set Function Name to On Disk Module and Function or Offset

Here is a script I wrote experimenting with resolving API Set function calls to on disk module/function/offset.

<#
.SYNOPSIS
    This script resolves an API set and API name to a specific module and function name.

.DESCRIPTION
    This script uses the NativeMethods class to call Windows API functions for resolving
    a given API set and API name to the actual module and function name they correspond to.

.PARAMETER ApiSet
    The API set string to be resolved. For example: "api-ms-win-devices-query-l1-1-0".

.PARAMETER Api
    The API name string to be resolved. For example: "DevGetObjects".

.PARAMETER ShowOffset
    Will show the offset of function instead of function name.
    Use if symbols don't resolve.

.EXAMPLE
    .\Resolve-ApiSet.ps1 -ApiSet "api-ms-win-devices-query-l1-1-0" -Api "DevGetObjects"
    This command will resolve the given ApiSet and ApiName to their corresponding module and function name.

   .\Resolve-ApiSet.ps1 -ApiSet "api-ms-win-devices-query-l1-1-0" -Api "DevGetObjects" -ShowOffSet
    This command will resolve the given ApiSet and ApiName to their corresponding module and offset (based on the image base address on disk)


.NOTES
    Run as 32-bit PowerShell for 32-bit modules
    64-bit PowerShell for 64-bit modules.
    If names are not resolving you may need to configure
    Microsoft Symbol Server or place name of your desired
    Symbol server in 2nd param to SymInitialize
#>
param(
    [Parameter(Mandatory=$true)]
    [string]$ApiSet,
    [Parameter(Mandatory=$true)]
    [string]$Api,
    [switch]$ShowOffset
)

Add-Type -TypeDefinition @'
using System;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Text;

public class NativeMethods
{
    [DllImport("dbghelp.dll", CharSet = CharSet.Unicode, SetLastError = true)]
    private static extern bool SymInitialize(IntPtr hProcess, string UserSearchPath, bool fInvadeProcess);

    [DllImport("dbghelp.dll", CharSet = CharSet.Unicode, SetLastError = true)]
    private static extern bool SymFromAddr(IntPtr hProcess, ulong Address, out ulong Displacement, IntPtr Symbol);

    [DllImport("dbghelp.dll", CharSet = CharSet.Unicode, SetLastError = true)]
    private static extern bool SymCleanup(IntPtr hProcess);

    [DllImport("kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
    private static extern IntPtr GetCurrentProcess();

    [DllImport("kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
    private static extern IntPtr LoadLibrary(string lpFileName);

    [DllImport("kernel32.dll", CharSet = CharSet.Ansi, SetLastError = true, ExactSpelling = true)]
    private static extern IntPtr GetProcAddress(IntPtr hModule, string procName);

    private const int MAX_SYM_NAME = 2000;

    [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
    private struct SYMBOL_INFO
    {
        public uint SizeOfStruct;
        public uint TypeIndex;
        public ulong Reserved1;
        public ulong Reserved2;
        public uint Index;
        public uint Size;
        public ulong ModBase;
        public uint Flags;
        public ulong Value;
        public ulong Address;
        public uint Register;
        public uint Scope;
        public uint Tag;
        public uint NameLen;
        public uint MaxNameLen;
        public char Name;
    }

    public static void SymInitialize()
    {
        if (!SymInitialize(GetCurrentProcess(), "", true))
        {
            // ignore errors
            // can only be called once per process instance
            //Marshal.ThrowExceptionForHR(Marshal.GetHRForLastWin32Error());
        }
    }

    public static string GetSymbolName(IntPtr address)
    {
        ulong displacement = 0;
        byte[] buffer = new byte[Marshal.SizeOf(typeof(SYMBOL_INFO)) + MAX_SYM_NAME * sizeof(char)];
        GCHandle bufferHandle = GCHandle.Alloc(buffer, GCHandleType.Pinned);
        IntPtr symbolPtr = bufferHandle.AddrOfPinnedObject();
        SYMBOL_INFO symbol = (SYMBOL_INFO)Marshal.PtrToStructure(symbolPtr, typeof(SYMBOL_INFO));
        symbol.SizeOfStruct = (uint)Marshal.SizeOf(typeof(SYMBOL_INFO));
        symbol.MaxNameLen = MAX_SYM_NAME;
        
        Marshal.StructureToPtr(symbol, symbolPtr, true);
        if (SymFromAddr(GetCurrentProcess(), (ulong)address, out displacement, symbolPtr))
        {
            symbol = (SYMBOL_INFO)Marshal.PtrToStructure(symbolPtr, typeof(SYMBOL_INFO));
            string functionName = Marshal.PtrToStringUni(symbolPtr + 84);
            return functionName;
        }
        else
        {
            Marshal.ThrowExceptionForHR(Marshal.GetHRForLastWin32Error());
        }

        //SymCleanup(GetCurrentProcess());
        bufferHandle.Free();

        return String.Empty;
    }

    [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
    public static extern uint GetModuleFileName(
        IntPtr hModule,
        [MarshalAs(UnmanagedType.LPTStr)] StringBuilder lpFilename,
        uint nSize
    );

    public static string GetModuleFileName(IntPtr hModule)
    {
        uint bufferSize = 1024;
        StringBuilder fileNameBuilder = new StringBuilder((int)bufferSize);
        uint result = GetModuleFileName(hModule, fileNameBuilder, bufferSize);

        if (result == 0)
        {
            throw new System.ComponentModel.Win32Exception(Marshal.GetLastWin32Error());
        }
        else if (result == bufferSize)
        {
            throw new InvalidOperationException("The file name is too long. Increase the buffer size and try again.");
        }

        return fileNameBuilder.ToString();
    }


    [DllImport("ntdll.dll", CharSet = CharSet.Unicode, SetLastError = true)]
    public static extern int LdrLoadDll(
        string DllPath,
        uint[] DllCharacteristics,
        ref UNICODE_STRING DllName,
        out IntPtr DllHandle
    );

    [DllImport("ntdll.dll", SetLastError = true, CallingConvention = CallingConvention.Cdecl)]
    public static extern int LdrUnloadDll(IntPtr ModuleHandle);

    [DllImport("ntdll.dll", CharSet = CharSet.Ansi, SetLastError = true)]
    public static extern int LdrGetProcedureAddress(
        IntPtr DllHandle,
        ref ANSI_STRING ProcedureName,
        uint ProcedureNumber,
        out IntPtr ProcedureAddress
    );

    [StructLayout(LayoutKind.Sequential)]
    public struct UNICODE_STRING
    {
        public ushort Length;
        public ushort MaximumLength;
        [MarshalAs(UnmanagedType.LPWStr)]
        public string Buffer;
    }

    [StructLayout(LayoutKind.Sequential)]
    public struct ANSI_STRING
    {
        public ushort Length;
        public ushort MaximumLength;
        [MarshalAs(UnmanagedType.LPStr)]
        public string Buffer;
    }

    public static UNICODE_STRING CreateUnicodeString(string str)
    {
        return new UNICODE_STRING
        {
            Length = (ushort)(str.Length * 2),
            MaximumLength = (ushort)((str.Length * 2) + 2),
            Buffer = str
        };
    }

    public static ANSI_STRING CreateAnsiString(string str)
    {
        return new ANSI_STRING
        {
            Length = (ushort)str.Length,
            MaximumLength = (ushort)(str.Length + 1),
            Buffer = str
        };
    }
}
'@

[Uint16]$IMAGE_DOS_SIGNATURE = 0x5A4D
[Uint16]$IMAGE_NT_OPTIONAL_HDR32_MAGIC = 0x10b
[UInt16]$IMAGE_NT_OPTIONAL_HDR64_MAGIC = 0x20b
[UInt16]$IMAGE_ROM_OPTIONAL_HDR_MAGIC = 0x107
[Uint16]$IMAGE_NUMBEROF_DIRECTORY_ENTRIES = 16
[Uint16]$IMAGE_SIZEOF_SHORT_NAME = 8
[Uint16]$IMAGE_SIZEOF_SECTION_HEADER = 40
 
enum IMAGE_FILE_MACHINE {
    IMAGE_FILE_RELOCS_STRIPPED           = 0x0001  # Relocation info stripped from file.
    IMAGE_FILE_EXECUTABLE_IMAGE          = 0x0002  # File is executable  (i.e. no unresolved external references).
    IMAGE_FILE_LINE_NUMS_STRIPPED        = 0x0004  # Line nunbers stripped from file.
    IMAGE_FILE_LOCAL_SYMS_STRIPPED       = 0x0008  # Local symbols stripped from file.
    IMAGE_FILE_AGGRESIVE_WS_TRIM         = 0x0010  # Aggressively trim working set
    IMAGE_FILE_LARGE_ADDRESS_AWARE       = 0x0020  # App can handle >2gb addresses
    IMAGE_FILE_BYTES_REVERSED_LO         = 0x0080  # Bytes of machine word are reversed.
    IMAGE_FILE_32BIT_MACHINE             = 0x0100  # 32 bit word machine.
    IMAGE_FILE_DEBUG_STRIPPED            = 0x0200  # Debugging info stripped from file in .DBG file
    IMAGE_FILE_REMOVABLE_RUN_FROM_SWAP   = 0x0400  # If Image is on removable media, copy and run from the swap file.
    IMAGE_FILE_NET_RUN_FROM_SWAP         = 0x0800  # If Image is on Net, copy and run from the swap file.
    IMAGE_FILE_SYSTEM                    = 0x1000  # System File.
    IMAGE_FILE_DLL                       = 0x2000  # File is a DLL.
    IMAGE_FILE_UP_SYSTEM_ONLY            = 0x4000  # File should only be run on a UP machine
    IMAGE_FILE_BYTES_REVERSED_HI         = 0x8000  # Bytes of machine word are reversed.
    IMAGE_FILE_MACHINE_UNKNOWN           = 0x0
    IMAGE_FILE_MACHINE_TARGET_HOST       = 0x0001  # Useful for indicating we want to interact with the host and not a WoW guest.
    IMAGE_FILE_MACHINE_I386              = 0x014c  # Intel 386.
    IMAGE_FILE_MACHINE_R3000             = 0x0162  # MIPS little-endian, = 0x160 big-endian
    IMAGE_FILE_MACHINE_R4000             = 0x0166  # MIPS little-endian
    IMAGE_FILE_MACHINE_R10000            = 0x0168  # MIPS little-endian
    IMAGE_FILE_MACHINE_WCEMIPSV2         = 0x0169  # MIPS little-endian WCE v2
    IMAGE_FILE_MACHINE_ALPHA             = 0x0184  # Alpha_AXP
    IMAGE_FILE_MACHINE_SH3               = 0x01a2  # SH3 little-endian
    IMAGE_FILE_MACHINE_SH3DSP            = 0x01a3
    IMAGE_FILE_MACHINE_SH3E              = 0x01a4  # SH3E little-endian
    IMAGE_FILE_MACHINE_SH4               = 0x01a6  # SH4 little-endian
    IMAGE_FILE_MACHINE_SH5               = 0x01a8  # SH5
    IMAGE_FILE_MACHINE_ARM               = 0x01c0  # ARM Little-Endian
    IMAGE_FILE_MACHINE_THUMB             = 0x01c2  # ARM Thumb/Thumb-2 Little-Endian
    IMAGE_FILE_MACHINE_ARMNT             = 0x01c4  # ARM Thumb-2 Little-Endian
    IMAGE_FILE_MACHINE_AM33              = 0x01d3
    IMAGE_FILE_MACHINE_POWERPC           = 0x01F0  # IBM PowerPC Little-Endian
    IMAGE_FILE_MACHINE_POWERPCFP         = 0x01f1
    IMAGE_FILE_MACHINE_IA64              = 0x0200  # Intel 64
    IMAGE_FILE_MACHINE_MIPS16            = 0x0266  # MIPS
    IMAGE_FILE_MACHINE_ALPHA64           = 0x0284  # ALPHA64
    IMAGE_FILE_MACHINE_MIPSFPU           = 0x0366  # MIPS
    IMAGE_FILE_MACHINE_MIPSFPU16         = 0x0466  # MIPS
    IMAGE_FILE_MACHINE_AXP64             = 0x0284  # IMAGE_FILE_MACHINE_ALPHA64
    IMAGE_FILE_MACHINE_TRICORE           = 0x0520  # Infineon
    IMAGE_FILE_MACHINE_CEF               = 0x0CEF
    IMAGE_FILE_MACHINE_EBC               = 0x0EBC  # EFI Byte Code
    IMAGE_FILE_MACHINE_AMD64             = 0x8664  # AMD64 (K8)
    IMAGE_FILE_MACHINE_M32R              = 0x9041  # M32R little-endian
    IMAGE_FILE_MACHINE_ARM64             = 0xAA64  # ARM64 Little-Endian
    IMAGE_FILE_MACHINE_CEE               = 0xC0EE
}
 
[Flags()] enum IMAGE_SUBSYSTEM
{
    IMAGE_SUBSYSTEM_UNKNOWN              = 0   # Unknown subsystem.
    IMAGE_SUBSYSTEM_NATIVE               = 1   # Image doesnt require a subsystem.
    IMAGE_SUBSYSTEM_WINDOWS_GUI          = 2   # Image runs in the Windows GUI subsystem.
    IMAGE_SUBSYSTEM_WINDOWS_CUI          = 3   # Image runs in the Windows character subsystem.
    IMAGE_SUBSYSTEM_OS2_CUI              = 5   # image runs in the OS/2 character subsystem.
    IMAGE_SUBSYSTEM_POSIX_CUI            = 7   # image runs in the Posix character subsystem.
    IMAGE_SUBSYSTEM_NATIVE_WINDOWS       = 8   # image is a native Win9x driver.
    IMAGE_SUBSYSTEM_WINDOWS_CE_GUI       = 9   # Image runs in the Windows CE subsystem.
    IMAGE_SUBSYSTEM_EFI_APPLICATION      = 10  #
    IMAGE_SUBSYSTEM_EFI_BOOT_SERVICE_DRIVER = 11   #
    IMAGE_SUBSYSTEM_EFI_RUNTIME_DRIVER   = 12  #
    IMAGE_SUBSYSTEM_EFI_ROM              = 13
    IMAGE_SUBSYSTEM_XBOX                 = 14
    IMAGE_SUBSYSTEM_WINDOWS_BOOT_APPLICATION = 16
    IMAGE_SUBSYSTEM_XBOX_CODE_CATALOG    = 17
}
 
[Flags()] enum IMAGE_DLLCHARACTERISTICS {
    IMAGE_DLLCHARACTERISTICS_HIGH_ENTROPY_VA = 0x0020  # Image can handle a high entropy 64-bit virtual address space.
    IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE = 0x0040     # DLL can move.
    IMAGE_DLLCHARACTERISTICS_FORCE_INTEGRITY   = 0x0080     # Code Integrity Image
    IMAGE_DLLCHARACTERISTICS_NX_COMPAT    = 0x0100     # Image is NX compatible
    IMAGE_DLLCHARACTERISTICS_NO_ISOLATION = 0x0200     # Image understands isolation and doesn't want it
    IMAGE_DLLCHARACTERISTICS_NO_SEH       = 0x0400     # Image does not use SEH.  No SE handler may reside in this image
    IMAGE_DLLCHARACTERISTICS_NO_BIND      = 0x0800     # Do not bind this image.
    IMAGE_DLLCHARACTERISTICS_APPCONTAINER = 0x1000     # Image should execute in an AppContainer
    IMAGE_DLLCHARACTERISTICS_WDM_DRIVER   = 0x2000     # Driver uses WDM model
    IMAGE_DLLCHARACTERISTICS_GUARD_CF     = 0x4000     # Image supports Control Flow Guard.
    IMAGE_DLLCHARACTERISTICS_TERMINAL_SERVER_AWARE     = 0x8000
}
 
enum IMAGE_DIRECTORY_ENTRY
{
    IMAGE_DIRECTORY_ENTRY_EXPORT          = 0   # Export Directory
    IMAGE_DIRECTORY_ENTRY_IMPORT          = 1   # Import Directory
    IMAGE_DIRECTORY_ENTRY_RESOURCE        = 2   # Resource Directory
    IMAGE_DIRECTORY_ENTRY_EXCEPTION       = 3   # Exception Directory
    IMAGE_DIRECTORY_ENTRY_SECURITY        = 4   # Security Directory
    IMAGE_DIRECTORY_ENTRY_BASERELOC       = 5   # Base Relocation Table
    IMAGE_DIRECTORY_ENTRY_DEBUG           = 6   # Debug Directory
    #     IMAGE_DIRECTORY_ENTRY_COPYRIGHT = 7   # (X86 usage)
    IMAGE_DIRECTORY_ENTRY_ARCHITECTURE    = 7   # Architecture Specific Data
    IMAGE_DIRECTORY_ENTRY_GLOBALPTR       = 8   # RVA of GP
    IMAGE_DIRECTORY_ENTRY_TLS             = 9   # TLS Directory
    IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG    = 10   # Load Configuration Directory
    IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT   = 11   # Bound Import Directory in headers
    IMAGE_DIRECTORY_ENTRY_IAT            = 12   # Import Address Table
    IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT   = 13   # Delay Load Import Descriptors
    IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR = 14   # COM Runtime descriptor
}
 
[Flags()] enum IMAGE_SCN {
#       IMAGE_SCN_TYPE_REG                   0x00000000  #  Reserved.
#       IMAGE_SCN_TYPE_DSECT                 0x00000001  #  Reserved.
#       IMAGE_SCN_TYPE_NOLOAD                0x00000002  #  Reserved.
#       IMAGE_SCN_TYPE_GROUP                 0x00000004  #  Reserved.
 IMAGE_SCN_TYPE_NO_PAD               = 0x00000008  #  Reserved.
#       IMAGE_SCN_TYPE_COPY                  0x00000010  #  Reserved.
 
 IMAGE_SCN_CNT_CODE                  = 0x00000020  #  Section contains code.
 IMAGE_SCN_CNT_INITIALIZED_DATA      = 0x00000040  #  Section contains initialized data.
 IMAGE_SCN_CNT_UNINITIALIZED_DATA    = 0x00000080  #  Section contains uninitialized data.
 
 IMAGE_SCN_LNK_OTHER                =  0x00000100  #  Reserved.
 IMAGE_SCN_LNK_INFO                 = 0x00000200  #  Section contains comments or some other type of information.
#       IMAGE_SCN_TYPE_OVER                  0x00000400  #  Reserved.
 IMAGE_SCN_LNK_REMOVE                = 0x00000800  #  Section contents will not become part of image.
 IMAGE_SCN_LNK_COMDAT                = 0x00001000  #  Section contents comdat.
#                                            0x00002000  #  Reserved.
#       IMAGE_SCN_MEM_PROTECTED - Obsolete =  0x00004000
 IMAGE_SCN_NO_DEFER_SPEC_EXC         = 0x00004000  #  Reset speculative exceptions handling bits in the TLB entries for this section.
 IMAGE_SCN_GPREL                     = 0x00008000  #  Section content can be accessed relative to GP
 IMAGE_SCN_MEM_FARDATA               = 0x00008000
#       IMAGE_SCN_MEM_SYSHEAP  - Obsolete    0x00010000
 IMAGE_SCN_MEM_PURGEABLE             = 0x00020000
 IMAGE_SCN_MEM_16BIT                 = 0x00020000
 IMAGE_SCN_MEM_LOCKED                = 0x00040000
 IMAGE_SCN_MEM_PRELOAD              =  0x00080000
 
 IMAGE_SCN_ALIGN_1BYTES             =  0x00100000  # 
 IMAGE_SCN_ALIGN_2BYTES             =  0x00200000  # 
 IMAGE_SCN_ALIGN_4BYTES             =  0x00300000  # 
 IMAGE_SCN_ALIGN_8BYTES             =  0x00400000  # 
 IMAGE_SCN_ALIGN_16BYTES            =  0x00500000  #  Default alignment if no others are specified.
 IMAGE_SCN_ALIGN_32BYTES            =  0x00600000  # 
 IMAGE_SCN_ALIGN_64BYTES            =  0x00700000  # 
 IMAGE_SCN_ALIGN_128BYTES           =  0x00800000  # 
 IMAGE_SCN_ALIGN_256BYTES           =  0x00900000  # 
 IMAGE_SCN_ALIGN_512BYTES           =  0x00A00000  # 
 IMAGE_SCN_ALIGN_1024BYTES          =  0x00B00000  # 
 IMAGE_SCN_ALIGN_2048BYTES          =  0x00C00000  # 
 IMAGE_SCN_ALIGN_4096BYTES          =  0x00D00000  # 
 IMAGE_SCN_ALIGN_8192BYTES          =  0x00E00000  # 
#  Unused                                    0x00F00000
 IMAGE_SCN_ALIGN_MASK               =  0x00F00000
 
 IMAGE_SCN_LNK_NRELOC_OVFL          =  0x01000000  #  Section contains extended relocations.
 IMAGE_SCN_MEM_DISCARDABLE          =  0x02000000  #  Section can be discarded.
 IMAGE_SCN_MEM_NOT_CACHED           =  0x04000000  #  Section is not cachable.
 IMAGE_SCN_MEM_NOT_PAGED            =  0x08000000  #  Section is not pageable.
 IMAGE_SCN_MEM_SHARED               =  0x10000000  #  Section is shareable.
 IMAGE_SCN_MEM_EXECUTE              =  0x20000000  #  Section is executable.
 IMAGE_SCN_MEM_READ                 =  0x40000000  #  Section is readable.
 IMAGE_SCN_MEM_WRITE                =  0x80000000  #  Section is writeable.
 
# 
#  TLS Characteristic Flags
# 
 IMAGE_SCN_SCALE_INDEX              =  0x00000001  #  Tls index is scaled
 
}
 
Function Convert-EnumToString([Enum]$enum)
{
    $type = [Type]$enum
}
$IMAGE_DOS_HEADER = New-Object System.Collections.Specialized.OrderedDictionary
 
$IMAGE_DOS_HEADER.Add("e_magic",[Uint16]0)                     # magic number
$IMAGE_DOS_HEADER.Add("e_cblp",[UInt16]0)                      # Bytes on last page of file
$IMAGE_DOS_HEADER.Add("e_cp",[UInt16]0)                        # Pages in file
$IMAGE_DOS_HEADER.Add("e_crlc",[UInt16]0)                      # Relocations
$IMAGE_DOS_HEADER.Add("e_cparhdr",[UInt16]0)                   # Size of header in paragraphs
$IMAGE_DOS_HEADER.Add("e_minalloc",[UInt16]0)                  # Minimum extra paragraphs needed
$IMAGE_DOS_HEADER.Add("e_maxalloc",[UInt16]0)                  # Maximum extra paragraphs needed
$IMAGE_DOS_HEADER.Add("e_ss",[UInt16]0)                        # Initial (relative) SS value
$IMAGE_DOS_HEADER.Add("e_sp",[UInt16]0)                        # Initial SP value
$IMAGE_DOS_HEADER.Add("e_csum",[UInt16]0)                      # Checksum
$IMAGE_DOS_HEADER.Add("e_ip",[UInt16]0)                        # Initial IP value
$IMAGE_DOS_HEADER.Add("e_cs",[UInt16]0)                        # Initial (relative) CS value
$IMAGE_DOS_HEADER.Add("e_lfarlc",[UInt16]0)                    # File address of relocation table
$IMAGE_DOS_HEADER.Add("e_ovno",[UInt16]0)                      # Overlay number
$IMAGE_DOS_HEADER.Add("e_res",(New-Object UInt16[] 4))         # Reserved words
$IMAGE_DOS_HEADER.Add("e_oemid",[UInt16]0)                     # OEM identifier (for e_oeminfo)
$IMAGE_DOS_HEADER.Add("e_oeminfo",[UInt16]0)                   # OEM information,[UInt16]0) e_oemid specific
$IMAGE_DOS_HEADER.Add("e_res2[10]",(New-Object UInt16[] 10))   # Reserved words
$IMAGE_DOS_HEADER.Add("e_lfanew",[Uint32]0)                    # File address of new exe header
 
$IMAGE_FILE_HEADER = New-Object System.Collections.Specialized.OrderedDictionary
$IMAGE_FILE_HEADER.Add("Machine",[UInt16]0)
$IMAGE_FILE_HEADER.Add("NumberOfSections",[UInt16]0)
$IMAGE_FILE_HEADER.Add("TimeDateStamp",[UInt32]0)
$IMAGE_FILE_HEADER.Add("PointerToSymbolTable",[UInt32]0)
$IMAGE_FILE_HEADER.Add("NumberOfSymbols",[UInt32]0)
$IMAGE_FILE_HEADER.Add("SizeOfOptionalHeader",[UInt16]0)
$IMAGE_FILE_HEADER.Add("Characteristics",[UInt16]0)
 
$IMAGE_OPTIONAL_HEADER = New-Object System.Collections.Specialized.OrderedDictionary
 
$IMAGE_OPTIONAL_HEADER.Add("Magic",[Uint16]0)
$IMAGE_OPTIONAL_HEADER.Add("MajorLinkerVersion",[Byte]0)
$IMAGE_OPTIONAL_HEADER.Add("MinorLinkerVersion",[Byte]0)
$IMAGE_OPTIONAL_HEADER.Add("SizeOfCode",[Uint32]0)
$IMAGE_OPTIONAL_HEADER.Add("SizeOfInitializedData",[Uint32]0)
$IMAGE_OPTIONAL_HEADER.Add("SizeOfUninitializedData",[Uint32]0)
$IMAGE_OPTIONAL_HEADER.Add("AddressOfEntryPoint",[Uint32]0)
$IMAGE_OPTIONAL_HEADER.Add("BaseOfCode",[Uint32]0)
$IMAGE_OPTIONAL_HEADER.Add("BaseOfData",[Uint32]0)
 
# NT Additional Fields
 
$IMAGE_OPTIONAL_HEADER.Add("ImageBase",[Uint32]0)
$IMAGE_OPTIONAL_HEADER.Add("SectionAlignment",[Uint32]0)
$IMAGE_OPTIONAL_HEADER.Add("FileAlignment",[Uint32]0)
$IMAGE_OPTIONAL_HEADER.Add("MajorOperatingSystemVersion",[Uint16]0)
$IMAGE_OPTIONAL_HEADER.Add("MinorOperatingSystemVersion",[Uint16]0)
$IMAGE_OPTIONAL_HEADER.Add("MajorImageVersion",[Uint16]0)
$IMAGE_OPTIONAL_HEADER.Add("MinorImageVersion",[Uint16]0)
$IMAGE_OPTIONAL_HEADER.Add("MajorSubsystemVersion",[Uint16]0)
$IMAGE_OPTIONAL_HEADER.Add("MinorSubsystemVersion",[Uint16]0)
$IMAGE_OPTIONAL_HEADER.Add("Win32VersionValue",[Uint32]0)
$IMAGE_OPTIONAL_HEADER.Add("SizeOfImage",[Uint32]0)
$IMAGE_OPTIONAL_HEADER.Add("SizeOfHeaders",[Uint32]0)
$IMAGE_OPTIONAL_HEADER.Add("CheckSum",[Uint32]0)
$IMAGE_OPTIONAL_HEADER.Add("Subsystem",[Uint16]0)
$IMAGE_OPTIONAL_HEADER.Add("DllCharacteristics",[Uint16]0)
$IMAGE_OPTIONAL_HEADER.Add("SizeOfStackReserve",[Uint32]0)
$IMAGE_OPTIONAL_HEADER.Add("SizeOfStackCommit",[Uint32]0)
$IMAGE_OPTIONAL_HEADER.Add("SizeOfHeapReserve",[Uint32]0)
$IMAGE_OPTIONAL_HEADER.Add("SizeOfHeapCommit",[Uint32]0)
$IMAGE_OPTIONAL_HEADER.Add("LoaderFlags",[Uint32]0)
$IMAGE_OPTIONAL_HEADER.Add("NumberOfRvaAndSizes",[Uint32]0)
$IMAGE_OPTIONAL_HEADER.Add("DataDirectory",(New-Object System.Collections.Specialized.OrderedDictionary[] $IMAGE_NUMBEROF_DIRECTORY_ENTRIES))
For ($i = 0; $i -lt $IMAGE_OPTIONAL_HEADER["DataDirectory"].Count;$i++)
{
    $IMAGE_OPTIONAL_HEADER["DataDirectory"][$i] = New-Object System.Collections.Specialized.OrderedDictionary
    $IMAGE_OPTIONAL_HEADER["DataDirectory"][$i].Add("VirtualAddress",[Uint32]0)
    $IMAGE_OPTIONAL_HEADER["DataDirectory"][$i].Add("Size",[UINt32]0)
}
 
 
$IMAGE_OPTIONAL_HEADER64 = New-Object System.Collections.Specialized.OrderedDictionary
$IMAGE_OPTIONAL_HEADER64.Add("Magic",[Uint16]0)
$IMAGE_OPTIONAL_HEADER64.Add("MajorLinkerVersion",[Byte]0)
$IMAGE_OPTIONAL_HEADER64.Add("MinorLinkerVersion",[Byte]0)
$IMAGE_OPTIONAL_HEADER64.Add("SizeOfCode",[Uint32]0)
$IMAGE_OPTIONAL_HEADER64.Add("SizeOfInitializedData",[Uint32]0)
$IMAGE_OPTIONAL_HEADER64.Add("SizeOfUninitializedData",[Uint32]0)
$IMAGE_OPTIONAL_HEADER64.Add("AddressOfEntryPoint",[Uint32]0)
$IMAGE_OPTIONAL_HEADER64.Add("BaseOfCode",[Uint32]0)
$IMAGE_OPTIONAL_HEADER64.Add("ImageBase",[Uint64]0)
$IMAGE_OPTIONAL_HEADER64.Add("SectionAlignment",[Uint32]0)
$IMAGE_OPTIONAL_HEADER64.Add("FileAlignment",[Uint32]0)
$IMAGE_OPTIONAL_HEADER64.Add("MajorOperatingSystemVersion",[Uint16]0)
$IMAGE_OPTIONAL_HEADER64.Add("MinorOperatingSystemVersion",[Uint16]0)
$IMAGE_OPTIONAL_HEADER64.Add("MajorImageVersion",[Uint16]0)
$IMAGE_OPTIONAL_HEADER64.Add("MinorImageVersion",[Uint16]0)
$IMAGE_OPTIONAL_HEADER64.Add("MajorSubsystemVersion",[Uint16]0)
$IMAGE_OPTIONAL_HEADER64.Add("MinorSubsystemVersion",[Uint16]0)
$IMAGE_OPTIONAL_HEADER64.Add("Win32VersionValue",[Uint32]0)
$IMAGE_OPTIONAL_HEADER64.Add("SizeOfImage",[Uint32]0)
$IMAGE_OPTIONAL_HEADER64.Add("SizeOfHeaders",[Uint32]0)
$IMAGE_OPTIONAL_HEADER64.Add("CheckSum",[Uint32]0)
$IMAGE_OPTIONAL_HEADER64.Add("Subsystem",[Uint16]0)
$IMAGE_OPTIONAL_HEADER64.Add("DllCharacteristics",[Uint16]0)
$IMAGE_OPTIONAL_HEADER64.Add("SizeOfStackReserve",[Uint64]0)
$IMAGE_OPTIONAL_HEADER64.Add("SizeOfStackCommit",[Uint64]0)
$IMAGE_OPTIONAL_HEADER64.Add("SizeOfHeapReserve",[Uint64]0)
$IMAGE_OPTIONAL_HEADER64.Add("SizeOfHeapCommit",[Uint64]0)
$IMAGE_OPTIONAL_HEADER64.Add("LoaderFlags",[Uint32]0)
$IMAGE_OPTIONAL_HEADER64.Add("NumberOfRvaAndSizes",[Uint32]0)
$IMAGE_OPTIONAL_HEADER64.Add("DataDirectory",(New-Object System.Collections.Specialized.OrderedDictionary[] $IMAGE_NUMBEROF_DIRECTORY_ENTRIES))
For ($i = 0; $i -lt $IMAGE_OPTIONAL_HEADER64["DataDirectory"].Count;$i++)
{
    $IMAGE_OPTIONAL_HEADER64["DataDirectory"][$i] = New-Object System.Collections.Specialized.OrderedDictionary
    $IMAGE_OPTIONAL_HEADER64["DataDirectory"][$i].Add("VirtualAddress",[Uint32]0)
    $IMAGE_OPTIONAL_HEADER64["DataDirectory"][$i].Add("Size",[UINt32]0)
}
 
$IMAGE_SECTION_HEADER = New-Object System.Collections.Specialized.OrderedDictionary
$IMAGE_SECTION_HEADER.Add("Name",(New-Object Byte[] $IMAGE_SIZEOF_SHORT_NAME))
$IMAGE_SECTION_HEADER.Add("Misc.PhysicalAddress",[Uint32]0)
$IMAGE_SECTION_HEADER.Add("Misc.VirtualSize",[Uint32]0)
$IMAGE_SECTION_HEADER.Add("VirtualAddress",[Uint32]0)
$IMAGE_SECTION_HEADER.Add("SizeOfRawData",[Uint32]0)
$IMAGE_SECTION_HEADER.Add("PointerToRawData",[Uint32]0)
$IMAGE_SECTION_HEADER.Add("PointerToRelocations",[Uint32]0)
$IMAGE_SECTION_HEADER.Add("PointerToLinenumbers",[Uint32]0)
$IMAGE_SECTION_HEADER.Add("NumberOfRelocations",[Uint16]0)
$IMAGE_SECTION_HEADER.Add("NumberOfLinenumbers",[Uint16]0)
$IMAGE_SECTION_HEADER.Add("Characteristics",[Uint32]0)
 
Function Convert-EnumToString
{
    param($enum,$value)
     
    $builder = New-Object System.Text.StringBuilder
    $names = ($enum.DeclaredFields | Select-Object -Property Name | Where-Object { $_.Name -ne "value__" }).Name
 
    ForEach ($name in $names)
    {
        if (!($value -ne 0 -and [Enum]::Parse($enum,$name).value__ -eq 0))
        {
            if ((([Enum]::Parse($enum,$name).value__) -band $value) -eq ([Enum]::Parse($enum,$name).value__))
            {
                if ($builder.Length -gt 0)
                {
                    [void]$builder.Append(" | ")
                }
                [void]$builder.Append($name)
            }
        }
    }
 
    return $builder.ToString()
     
}
Function Read-BinaryFile
{
    param(
    [System.IO.BinaryReader][ref]$reader,
    [System.Collections.Specialized.OrderedDictionary][ref]$items)
    
    
    $keys = New-Object String[] $items.Count
    $items.Keys.CopyTo($keys,0)
   
    for ($i = 0;$i -lt $items.Count;$i++)
    {
        $item = $keys[$i]
        
        if ($items[$item] -is [Array])
        {
            if ($items[$item][0] -is [System.Collections.Specialized.OrderedDictionary])
            {
                ForEach ($subItem in $items[$item])
                {
                    Read-BinaryFile -reader ([ref]$reader) -items ([ref]$subItem)
                }
            }
            else
            {
                $currentItem = $items[$item][0]
                For ($j = 0;$j -lt $items[$item].Length;$j++)
                {
                    switch($currentItem.GetType().Name)
                    {
                        "Byte"
                        {
                            $items[$item][$j] = $reader.ReadByte()
                        }
                        "Uint16"
                        { 
                            $items[$item][$j] = $Reader.ReadUint16()
                        }
                        "Uint32"
                        {
                            $items[$item][$j] = $reader.ReadUInt32()
                        }
                        "Uint64"
                        { 
                            $items[$item][$j] = $Reader.ReadUInt64()
                        }
                        default  { "Unknown Type! $($currentItem.GetType().Name)" }
                    }
                }
            }
        }
        else
        {
            $currentItem = $items[$item]
            if ($currentItem -is [System.Collections.Specialized.OrderedDictionary])
            {
                Read-BinaryFile -reader ([ref]$reader) -items ([ref]$items[$item])
            }
            else
            {
                switch($currentItem.GetType().Name)
                {
                    "Byte"
                    {
                        $items[$item] = $reader.ReadByte()
                    }
 
                    "Uint16" { 
                        $items[$item] = $reader.ReadUint16()
 
                    }
                    "Uint32"
                    { 
                         $items[$item] = $reader.ReadUint32()
                    }
                    "Uint64"
                    {
                        $items[$item] = $reader.ReadUint64()               
                    }
                    default  { "Unknown Type! $($currentItem.GetType().Name)" }
                }
            }
        }
    }
}

[NativeMethods]::SymInitialize()

$apiSetName = [NativeMethods]::CreateUnicodeString($ApiSet)
$apiName = [NativeMethods]::CreateAnsiString($Api)

[IntPtr]$dllHandle = [IntPtr]::Zero
[IntPtr]$procAddress = [IntPtr]::Zero

if ([NativeMethods]::LdrLoadDll("",0,[ref]$apiSetName,[ref]$dllHandle) -eq 0)
{
    if ([NativeMethods]::LdrGetProcedureAddress($dllHandle,[ref]$apiName,0,[ref]$procAddress) -eq 0)
    {
        if ($procAddress -ne [IntPtr]::Zero)
        {
            $filename = [NativeMethods]::GetModuleFileName($dllHandle)
            if ($ShowOffset)
            {
                $FileStream = [System.IO.File]::Open($filename, [System.IO.FileMode]::Open,[System.IO.FileAccess]::Read,[System.IO.FileShare]::Read)
 
                $BinaryReader = New-Object System.IO.BinaryReader($FileStream)
                [void]$BinaryReader.BaseStream.Seek(0,[System.IO.SeekOrigin]::Begin)
 
                Read-BinaryFile -reader ([ref]$BinaryReader) -Items ([ref]$IMAGE_DOS_HEADER)
 
                if ($IMAGE_DOS_HEADER["e_magic"] -eq $IMAGE_DOS_SIGNATURE)
                {
                    [void]$BinaryReader.BaseStream.Seek($IMAGE_DOS_HEADER["e_lfanew"] + 24,[System.IO.SeekOrigin]::Begin)
                    $magic = $BinaryReader.ReadInt16()
                    [void]$BinaryReader.BaseStream.Seek($IMAGE_DOS_HEADER["e_lfanew"],[System.IO.SeekOrigin]::Begin)
 
                    $IMAGE_NT_HEADER = New-Object System.Collections.Specialized.OrderedDictionary
                    $IMAGE_NT_HEADER.Add("Signature",[Uint32]0)
                    $IMAGE_NT_HEADER.Add("FileHeader",$IMAGE_FILE_HEADER)
 
                    switch ($magic)
                    {
                        $IMAGE_NT_OPTIONAL_HDR32_MAGIC
                        { 
                            $IMAGE_NT_HEADER.Add("OptionalHeader",$IMAGE_OPTIONAL_HEADER) 
                        }
                        $IMAGE_NT_OPTIONAL_HDR64_MAGIC
                        {
                            $IMAGE_NT_HEADER.Add("OptionalHeader",$IMAGE_OPTIONAL_HEADER64)
                        } 
                        $IMAGE_ROM_OPTIONAL_HDR_MAGIC { "ROM File, not supported!" }
                        default { "Unknown PE file type $($magic)!" } 
                    }
                    Read-BinaryFile -reader ([ref]$BinaryReader) -Items ([ref]$IMAGE_NT_HEADER)
 
                }
                else
                {
                    Write-Host "Not a valid PE file!"
                }
 
                $dllCharacteristics = Convert-EnumToString -enum ([IMAGE_DLLCHARACTERISTICS]) -value $IMAGE_NT_HEADER["OptionalHeader"]["DllCharacteristics"]
                $subSystem = [Enum]::Parse([IMAGE_SUBSYSTEM],$IMAGE_NT_HEADER["OptionalHeader"]["Subsystem"])
                $machine = [Enum]::Parse([IMAGE_FILE_MACHINE],$IMAGE_NT_HEADER["FileHeader"]["Machine"])

                [UInt64]$originalAddress = ($procAddress.ToInt64() -$dllHandle) + $IMAGE_NT_HEADER.OptionalHeader.ImageBase
                "$($filename)!0x{0:X}" -f $originalAddress
            }
            else
            {
                $symbolName = [NativeMethods]::GetSymbolName($procAddress)
                [Void][NativeMethods]::LdrUnloadDll($dllHandle)
                "$($filename)!$symbolName"
            }
        }
    }

}

About chentiangemalc

specializes in end-user computing technologies. disclaimer 1) use at your own risk. test any solution in your environment. if you do not understand the impact/consequences of what you're doing please stop, and ask advice from somebody who does. 2) views are my own at the time of posting and do not necessarily represent my current view or the view of my employer and family members/relatives. 3) over the years Microsoft/Citrix/VMWare have given me a few free shirts, pens, paper notebooks/etc. despite these gifts i will try to remain unbiased.
This entry was posted in Uncategorized. Bookmark the permalink.

Leave a comment