C#怎样去遍历一个由C++或E语言编写的本地DLL导出函数呢 只是在这里我建议对PE一无所知的人
你也许应先补补这方面的知识。我不知道为什么PE方面的 应用在C#中怎么这么少。我查阅过相关
C#的知识大概仅仅见一个人写过关于PE的应用 还仅仅是从PE信息中推断运行文件是X86还是X64方式
编译,难道C#程序猿真的非常差 真的仅仅能会点Asp.Net / MVC?想想看雪论坛那些玩inline-asm /
inline-hook的牛牛 真是感到有非常大差距 只是任何语言 在我看来事实上都差点儿相同 重点在于人是否
有心。尽管我不敢保证C#能够嵌入动态汇编(auto-asm)可是我能够保证C#能够做inline-hook尽管会
的人比較少,只是也还好。至少C#程序猿不会是一群渣渣。只是我在写下述代码时,但是累得紧 写
结构体部分有些麻烦 并且C#与C++有些不同 当然也能够动态偏移地址搞定只是那个有些麻烦了,你
想推敲地址可不是那么好玩的事情。可能你自己推敲半天结果发现你推敲错了,那种方法用在结构体
层次较少的情况下的确能够提升逼格 反正别人看不懂就好嘛? 呵呵。
以下的代码须要在X86的环境下
使用主要在于该代码中使用的PE信息全是32位的结构体而非64位的PE信息结构体 所以须要X86环境
只是不论是X86还是X64方法都是相等的。仅仅是两者的结构体与对称不太一样而已。
PE格式,是微软Win32环境可移植运行文件如(exe / sys / dll / vxd / vdm)等都是标准的文件格式
PE格式衍生于VAX / VMS上的COFF文件格式,Portable是指对于不同的Windows版本号和不同的
CPU类型上PE文件的格式是一样的,也许CPU不一指令与二进制编码不一,可是文件里各种东
西的布局是一至的。
PE文件里第一个字节是MS-DOS信息头即IMAGE_DOS_HEADER与IMAGE_NT_HEADER中包
含很多PE装载器用到。
[STAThread] unsafe static void Main() { IntPtr hFileBase = Win32Native._lopen(@"C:/Windows/System32/user32.dll", Win32Native.OF_SHARE_COMPAT); IntPtr hFileMapping = Win32Native.CreateFileMapping(hFileBase, Win32Native.NULL, Win32Native.PAGE_READONLY, 0, 0, null); IntPtr hMapViewOfFile = Win32Native.MapViewOfFile(hFileMapping, Win32Native.FILE_MAP_READ, 0, 0, Win32Native.NULL); IMAGE_DOS_HEADER sDosHearder = (IMAGE_DOS_HEADER)Marshal.PtrToStructure(hMapViewOfFile, typeof(IMAGE_DOS_HEADER)); IntPtr pWinNTHeader = (IntPtr)(sDosHearder.e_lfanew + (long)hMapViewOfFile); // e_lfanew 248 IMAGE_NT_HEADERS sWinNTHeader = (IMAGE_NT_HEADERS)Marshal.PtrToStructure(pWinNTHeader, typeof(IMAGE_NT_HEADERS)); // 63 63 72 75 6E 2E 63 6F 6D IntPtr pExportDirectory = Win32Native.ImageRvaToVa(pWinNTHeader, hMapViewOfFile, sWinNTHeader.OptionalHeader.ExportTable.VirtualAddress, Win32Native.NULL); IMAGE_EXPORT_DIRECTORY sExportDirectory = (IMAGE_EXPORT_DIRECTORY)Marshal.PtrToStructure(pExportDirectory, typeof(IMAGE_EXPORT_DIRECTORY)); IntPtr ppExportOfNames = Win32Native.ImageRvaToVa(pWinNTHeader, hMapViewOfFile, sExportDirectory.AddressOfNames, Win32Native.NULL); for (uint i = 0, nNoOfExports = sExportDirectory.NumberOfNames; i < nNoOfExports; i++) { IntPtr pstrExportOfName = Win32Native.ImageRvaToVa(pWinNTHeader, hMapViewOfFile, (uint)Marshal.ReadInt32(ppExportOfNames, (int)(i * sizeof(int))), Win32Native.NULL); Console.WriteLine(Marshal.PtrToStringAnsi(pstrExportOfName)); } Win32Native.UnmapViewOfFile(hMapViewOfFile); Win32Native.CloseHandle(hFileMapping); Win32Native._lclose(hFileBase); Console.ReadKey(false);
}
包括 入口点 Entry Point
文件偏移地址 File Offset
虚拟地址 Virtual Address(VA)
基地址 Image Base
相对虚拟地址 Relative Virual Address(RVA)
公式:RVA (相对虚拟地址) = VA(虚拟地址) - Image Base (基地址)
文件偏移地址和虚拟地址转换
在X86系统中,每一个内存页的大小是4KB
文件偏移地址 File Offset = RVA(相对虚拟地址) - ΔK
文件偏移地址 File Offset = VA(虚拟地址) - Image Base (基地址) - ΔK
详解内容请參考百度百科,反正你想真正理解还须要自己去研究PE文件
IMAGE_NT_HEADERS在MS-DOS信息头后面它是标准的Win32运行文件信息头,当中包括了
导入的函数表,导出函数表。资源信息表、CLR执行时头,IAT、TLS表、包含调试信息 等等
我们如今要做的就是获取在DLL中导出的函数名,而DLL是属于标准Win32运行文件里的一种
那么我们则必需要获取到IMAGE_NT_HEADERS结构,实际上需要定位NT结构是非常easy的,
由于在规定中NT信息头在DOS信息头后面,即IMAGE_DOS_HEADER.e_lfanew + IMAGE_DOS_HEADER
所以你会看到我在代码中有这样一句话IntPtr psNt32pe = (IntPtr)(sDos32pe.e_lfanew + (long)psDos32pe);
IMAGE_OPTIONAL_HEADER可选映像头是一个可选结构,可是IMAGE_FILE_HEADER结构不满足PE文件
需求定义的属性。因此这些属性在OPTIONAL结构中定义,因此FILE+OPTIONAL两个结构联合起来 才是一
个完整的PE文件结构。在当中包括了非常多重要的信息字段 如 AddressOfEntryPoint、DataDirectory、Subsystem
只是提到DataDirectory我想说一下,在C#中不好定义所以在代码中该字段换了还有一种方式定义。DataDirectory
默认是有16个IMAGE_DATA_DIRECTORY的尺寸,所以在代码中你能够看到有非常多该类型的定义。它们则是表
示DataDirectory中信息IMAGE_DIRECTORY_ENTRY_EXPORT导出表 我们如今仅仅须要获取它的信息,在这里
我们须要用到ImageRvaToVa(相对虚拟地址到虚拟地址)有人是这样理解的, 物理地址到虚拟地址 只是原来我在
理解时这个地方也是小小纠结了一番,只是后来则释然了。
ImageRvaToVa(NT_H, DOS_H, RVA, RvaSection);
IMAGE_DATA_DIRECTORY中包括两个字段,一个VirtualAddress(RVA)还有一个为Size(尺寸)获取到结构体中的
RVA可是这个地址我们无论怎么转换都没法使用,对的由于提供给我的地址根本没法用 那么我们则须要把RVA
转换为VA利用上面提到函数,仅仅有默默的感谢微软一番 呵呵,当转换后会得到IMAGE_EXPORT_DIRECTORY
在这里我须要提示一下大家。不是每一个DataDirectory包括的RVA相应的结构都是EXPORT每一个都有自己独立的
解释结构,不要搞混了 不然肯定会飞高的。
我们须要IMAGE_EXPORT_DIRECTORY中NumberOfNames(函数名总数)与AddressOfNames(函数名地址)
两个字段中的内容,只是AddressOfNames中包括的是相对虚拟地址RVA,所以我们须要做一次转换,会返回有
效char**的指针前提你提供的数据有效否则返回NULL,因为C#中你懂的char占两个字节,即char=wchar_t那么
我们查看指针中的数据肯定会有问题DLL导出函数名所有是Ascii编码,所以为了方便在C#专用干脆IntPtr方便通过
Marshal进行转换最后仅仅是进行一个资源释放的操作好了基本就是这个样子剩下的还须要大家自己去理解多说无益
using System; using System.Runtime.InteropServices; // #include "stdafx.h" // #include <ImageHlp.h> // #include <Windows.h> // #pragma comment(lib, "ImageHlp.lib")
static partial class Win32Native { [DllImport("dbghelp", SetLastError = true)] // PIMAGE_SECTION_HEADER LastRvaSection public static extern IntPtr ImageRvaToVa(IntPtr NtHeaders, IntPtr Base, uint Rva, int LastRvaSection); [DllImport("kernel32", SetLastError = true)] public static extern IntPtr _lopen(string lpPathName, int iReadWrite); [DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Auto)] public static extern IntPtr CreateFileMapping(IntPtr hFile, int lpFileMappingAttributes, int flProtect, uint dwMaximumSizeHigh, uint dwMaximumSizeLow, string lpName); [DllImport("kernel32.dll", SetLastError = true)] public static extern IntPtr MapViewOfFile(IntPtr hFileMappingObject, int dwDesiredAccess, uint dwFileOffsetHigh, uint dwFileOffsetLow, int dwNumberOfBytesToMap); [DllImport("kernel32.dll", SetLastError = true)] public static extern int UnmapViewOfFile(IntPtr hMapFile); [DllImport("kernel32.dll", SetLastError = true)] public static extern int _lclose(IntPtr hFile); [DllImport("kernel32.dll", SetLastError = true)] public static extern int CloseHandle(IntPtr hObject); } static partial class Win32Native { public const int NULL = 0; public const int OF_SHARE_COMPAT = 0; public const int PAGE_READONLY = 2; public const int FILE_MAP_READ = 4; public const int IMAGE_DIRECTORY_ENTRY_EXPORT = 0; } [StructLayout(LayoutKind.Sequential)] public struct IMAGE_DOS_HEADER { [MarshalAs(UnmanagedType.ByValArray, SizeConst = 2)] public char[] e_magic; // Magic number public ushort e_cblp; // Bytes on last page of file public ushort e_cp; // Pages in file public ushort e_crlc; // Relocations public ushort e_cparhdr; // Size of header in paragraphs public ushort e_minalloc; // Minimum extra paragraphs needed public ushort e_maxalloc; // Maximum extra paragraphs needed public ushort e_ss; // Initial (relative) SS value public ushort e_sp; // Initial SP value public ushort e_csum; // Checksum public ushort e_ip; // Initial IP value public ushort e_cs; // Initial (relative) CS value public ushort e_lfarlc; // File address of relocation table public ushort e_ovno; // Overlay number [MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)] public ushort[] e_res1; // Reserved words public ushort e_oemid; // OEM identifier (for e_oeminfo) public ushort e_oeminfo; // OEM information; e_oemid specific [MarshalAs(UnmanagedType.ByValArray, SizeConst = 10)] public ushort[] e_res2; // Reserved words public int e_lfanew; // File address of new exe header private string _e_magic { get { return new string(e_magic); } } public bool isValid { get { return _e_magic == "MZ"; } } } [StructLayout(LayoutKind.Explicit)] public struct IMAGE_OPTIONAL_HEADERS { [FieldOffset(0)] public MagicType Magic; [FieldOffset(2)] public byte MajorLinkerVersion; [FieldOffset(3)] public byte MinorLinkerVersion; [FieldOffset(4)] public uint SizeOfCode; [FieldOffset(8)] public uint SizeOfInitializedData; [FieldOffset(12)] public uint SizeOfUninitializedData; [FieldOffset(16)] public uint AddressOfEntryPoint; [FieldOffset(20)] public uint BaseOfCode; // PE32 contains this additional field [FieldOffset(24)] public uint BaseOfData; [FieldOffset(28)] public uint ImageBase; [FieldOffset(32)] public uint SectionAlignment; [FieldOffset(36)] public uint FileAlignment; [FieldOffset(40)] public ushort MajorOperatingSystemVersion; [FieldOffset(42)] public ushort MinorOperatingSystemVersion; [FieldOffset(44)] public ushort MajorImageVersion; [FieldOffset(46)] public ushort MinorImageVersion; [FieldOffset(48)] public ushort MajorSubsystemVersion; [FieldOffset(50)] public ushort MinorSubsystemVersion; [FieldOffset(52)] public uint Win32VersionValue; [FieldOffset(56)] public uint SizeOfImage; [FieldOffset(60)] public uint SizeOfHeaders; [FieldOffset(64)] public uint CheckSum; [FieldOffset(68)] public SubSystemType Subsystem; [FieldOffset(70)] public DllCharacteristicsType DllCharacteristics; [FieldOffset(72)] public uint SizeOfStackReserve; [FieldOffset(76)] public uint SizeOfStackCommit; [FieldOffset(80)] public uint SizeOfHeapReserve; [FieldOffset(84)] public uint SizeOfHeapCommit; [FieldOffset(88)] public uint LoaderFlags; [FieldOffset(92)] public uint NumberOfRvaAndSizes; [FieldOffset(96)] public IMAGE_DATA_DIRECTORY ExportTable; [FieldOffset(104)] public IMAGE_DATA_DIRECTORY ImportTable; [FieldOffset(112)] public IMAGE_DATA_DIRECTORY ResourceTable; [FieldOffset(120)] public IMAGE_DATA_DIRECTORY ExceptionTable; [FieldOffset(128)] public IMAGE_DATA_DIRECTORY CertificateTable; [FieldOffset(136)] public IMAGE_DATA_DIRECTORY BaseRelocationTable; [FieldOffset(144)] public IMAGE_DATA_DIRECTORY Debug; [FieldOffset(152)] public IMAGE_DATA_DIRECTORY Architecture; [FieldOffset(160)] public IMAGE_DATA_DIRECTORY GlobalPtr; [FieldOffset(168)] public IMAGE_DATA_DIRECTORY TLSTable; [FieldOffset(176)] public IMAGE_DATA_DIRECTORY LoadConfigTable; [FieldOffset(184)] public IMAGE_DATA_DIRECTORY BoundImport; [FieldOffset(192)] public IMAGE_DATA_DIRECTORY IAT; [FieldOffset(200)] public IMAGE_DATA_DIRECTORY DelayImportDescriptor; [FieldOffset(208)] public IMAGE_DATA_DIRECTORY CLRRuntimeHeader; [FieldOffset(216)] public IMAGE_DATA_DIRECTORY Reserved; } [StructLayout(LayoutKind.Sequential)] public struct IMAGE_FILE_HEADER { public ushort Machine; public ushort NumberOfSections; public uint TimeDateStamp; public uint PointerToSymbolTable; public uint NumberOfSymbols; public ushort SizeOfOptionalHeader; public ushort Characteristics; } public enum MachineType : ushort { Native = 0, I386 = 0x014c, Itanium = 0x0200, x64 = 0x8664 } public enum MagicType : ushort { IMAGE_NT_OPTIONAL_HDR32_MAGIC = 0x10b, IMAGE_NT_OPTIONAL_HDR64_MAGIC = 0x20b } public enum SubSystemType : ushort { IMAGE_SUBSYSTEM_UNKNOWN = 0, IMAGE_SUBSYSTEM_NATIVE = 1, IMAGE_SUBSYSTEM_WINDOWS_GUI = 2, IMAGE_SUBSYSTEM_WINDOWS_CUI = 3, IMAGE_SUBSYSTEM_POSIX_CUI = 7, IMAGE_SUBSYSTEM_WINDOWS_CE_GUI = 9, 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 } public enum DllCharacteristicsType : ushort { RES_0 = 0x0001, RES_1 = 0x0002, RES_2 = 0x0004, RES_3 = 0x0008, IMAGE_DLL_CHARACTERISTICS_DYNAMIC_BASE = 0x0040, IMAGE_DLL_CHARACTERISTICS_FORCE_INTEGRITY = 0x0080, IMAGE_DLL_CHARACTERISTICS_NX_COMPAT = 0x0100, IMAGE_DLLCHARACTERISTICS_NO_ISOLATION = 0x0200, IMAGE_DLLCHARACTERISTICS_NO_SEH = 0x0400, IMAGE_DLLCHARACTERISTICS_NO_BIND = 0x0800, RES_4 = 0x1000, IMAGE_DLLCHARACTERISTICS_WDM_DRIVER = 0x2000, IMAGE_DLLCHARACTERISTICS_TERMINAL_SERVER_AWARE = 0x8000 } [StructLayout(LayoutKind.Sequential)] public struct IMAGE_DATA_DIRECTORY { public uint VirtualAddress; public uint Size; } [StructLayout(LayoutKind.Sequential)] public struct IMAGE_EXPORT_DIRECTORY { public uint Characteristics; public uint TimeDateStamp; public ushort MajorVersion; public ushort MinorVersion; public uint Name; public uint Base; public uint NumberOfFunctions; public uint NumberOfNames; public uint AddressOfFunctions; // RVA from base of image public uint AddressOfNames; // RVA from base of image public uint AddressOfNameOrdinals; // RVA from base of image } [StructLayout(LayoutKind.Explicit)] public struct IMAGE_NT_HEADERS { [FieldOffset(0)] [MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)] public char[] Signature; [FieldOffset(4)] public IMAGE_FILE_HEADER FileHeader; [FieldOffset(24)] public IMAGE_OPTIONAL_HEADERS OptionalHeader; private string _Signature { get { return new string(Signature); } } public bool isValid { get { return _Signature == "PE " && (OptionalHeader.Magic == MagicType.IMAGE_NT_OPTIONAL_HDR32_MAGIC || OptionalHeader.Magic == MagicType.IMAGE_NT_OPTIONAL_HDR64_MAGIC); } } }