如何以编程方式分析本机DLL以读取其导入?
编辑:我最初的问题如下所示,还有一大块有缺陷的代码。请参阅下面的答案,以获得更正确的代码。
位于C#的此链接代码用于打印本机DLL的导入。
我发现,当我使用原始示例的目标MSCOREE.DLL运行示例代码时,它会很好地打印所有导入。但是,当我使用其他dll(如GDI32.DLL或WSOCK32.DLL )时,导入不会被打印。这段代码中缺少什么可以让它像DUMPBIN.EXE那样打印所有的导入?
发布于 2010-12-31 18:29:14
在代码中有一个非常大的问题(即THUNK_DATA
的定义)和其他各种较小的问题,主要涉及表尾检测(使用IsBadReadPtr
而不是空检查,也没有根据需要添加基本地址)。
下面是一个固定版本,它产生与dumpbin相同的输出,至少用于wsock32
using System;
using System.Runtime.InteropServices;
using System.Security;
namespace PETest2
{
[StructLayout(LayoutKind.Explicit)]
public unsafe struct IMAGE_IMPORT_BY_NAME
{
[FieldOffset(0)]
public ushort Hint;
[FieldOffset(2)]
public fixed char Name[1];
}
[StructLayout(LayoutKind.Explicit)]
public struct IMAGE_IMPORT_DESCRIPTOR
{
#region union
/// <summary>
/// CSharp doesnt really support unions, but they can be emulated by a field offset 0
/// </summary>
[FieldOffset(0)]
public uint Characteristics; // 0 for terminating null import descriptor
[FieldOffset(0)]
public uint OriginalFirstThunk; // RVA to original unbound IAT (PIMAGE_THUNK_DATA)
#endregion
[FieldOffset(4)]
public uint TimeDateStamp;
[FieldOffset(8)]
public uint ForwarderChain;
[FieldOffset(12)]
public uint Name;
[FieldOffset(16)]
public uint FirstThunk;
}
[StructLayout(LayoutKind.Explicit)]
public struct THUNK_DATA
{
[FieldOffset(0)]
public uint ForwarderString; // PBYTE
[FieldOffset(0)]
public uint Function; // PDWORD
[FieldOffset(0)]
public uint Ordinal;
[FieldOffset(0)]
public uint AddressOfData; // PIMAGE_IMPORT_BY_NAME
}
public unsafe class Interop
{
#region Public Constants
public static readonly ushort IMAGE_DIRECTORY_ENTRY_IMPORT = 1;
#endregion
#region Private Constants
#region CallingConvention CALLING_CONVENTION
/// <summary>
/// Specifies the calling convention.
/// </summary>
/// <remarks>
/// Specifies <see cref="CallingConvention.Winapi" /> for Windows to
/// indicate that the default should be used.
/// </remarks>
private const CallingConvention CALLING_CONVENTION = CallingConvention.Winapi;
#endregion CallingConvention CALLING_CONVENTION
#region IMPORT DLL FUNCTIONS
private const string KERNEL_DLL = "kernel32";
private const string DBGHELP_DLL = "Dbghelp";
#endregion
#endregion Private Constants
[DllImport(KERNEL_DLL, CallingConvention = CALLING_CONVENTION, EntryPoint = "GetModuleHandleA"), SuppressUnmanagedCodeSecurity]
public static extern void* GetModuleHandleA(/*IN*/ char* lpModuleName);
[DllImport(KERNEL_DLL, CallingConvention = CALLING_CONVENTION, EntryPoint = "GetModuleHandleW"), SuppressUnmanagedCodeSecurity]
public static extern void* GetModuleHandleW(/*IN*/ char* lpModuleName);
[DllImport(KERNEL_DLL, CallingConvention = CALLING_CONVENTION, EntryPoint = "IsBadReadPtr"), SuppressUnmanagedCodeSecurity]
public static extern bool IsBadReadPtr(void* lpBase, uint ucb);
[DllImport(DBGHELP_DLL, CallingConvention = CALLING_CONVENTION, EntryPoint = "ImageDirectoryEntryToData"), SuppressUnmanagedCodeSecurity]
public static extern void* ImageDirectoryEntryToData(void* Base, bool MappedAsImage, ushort DirectoryEntry, out uint Size);
}
static class Foo
{
// From winbase.h in the Win32 platform SDK.
//
const uint DONT_RESOLVE_DLL_REFERENCES = 0x00000001;
const uint LOAD_IGNORE_CODE_AUTHZ_LEVEL = 0x00000010;
[DllImport("kernel32.dll"), SuppressUnmanagedCodeSecurity]
static extern uint LoadLibraryEx(string fileName, uint notUsedMustBeZero, uint flags);
public static void Main()
{
//var path = @"c:\windows\system32\mscoree.dll";
//var path = @"c:\windows\system32\gdi32.dll";
var path = @"c:\windows\system32\wsock32.dll";
var hLib = LoadLibraryEx(path, 0,
DONT_RESOLVE_DLL_REFERENCES | LOAD_IGNORE_CODE_AUTHZ_LEVEL);
TestImports(hLib, true);
}
// using mscoree.dll as an example as it doesnt export any thing
// so nothing shows up if you use your own module.
// and the only none delayload in mscoree.dll is the Kernel32.dll
private static void TestImports(uint hLib, bool mappedAsImage)
{
unsafe
{
//fixed (char* pszModule = "mscoree.dll")
{
//void* hMod = Interop.GetModuleHandleW(pszModule);
void* hMod = (void*)hLib;
uint size = 0;
uint BaseAddress = (uint)hMod;
if (hMod != null)
{
Console.WriteLine("Got handle");
IMAGE_IMPORT_DESCRIPTOR* pIID = (IMAGE_IMPORT_DESCRIPTOR*)Interop.ImageDirectoryEntryToData((void*)hMod, mappedAsImage, Interop.IMAGE_DIRECTORY_ENTRY_IMPORT, out size);
if (pIID != null)
{
Console.WriteLine("Got Image Import Descriptor");
while (pIID->OriginalFirstThunk != 0)
{
try
{
char* szName = (char*)(BaseAddress + pIID->Name);
string name = Marshal.PtrToStringAnsi((IntPtr)szName);
Console.WriteLine("pIID->Name = {0} BaseAddress - {1}", name, (uint)BaseAddress);
THUNK_DATA* pThunkOrg = (THUNK_DATA*)(BaseAddress + pIID->OriginalFirstThunk);
while (pThunkOrg->AddressOfData != 0)
{
char* szImportName;
uint Ord;
if ((pThunkOrg->Ordinal & 0x80000000) > 0)
{
Ord = pThunkOrg->Ordinal & 0xffff;
Console.WriteLine("imports ({0}).Ordinal{1} - Address: {2}", name, Ord, pThunkOrg->Function);
}
else
{
IMAGE_IMPORT_BY_NAME* pIBN = (IMAGE_IMPORT_BY_NAME*)(BaseAddress + pThunkOrg->AddressOfData);
if (!Interop.IsBadReadPtr((void*)pIBN, (uint)sizeof(IMAGE_IMPORT_BY_NAME)))
{
Ord = pIBN->Hint;
szImportName = (char*)pIBN->Name;
string sImportName = Marshal.PtrToStringAnsi((IntPtr)szImportName); // yes i know i am a lazy ass
Console.WriteLine("imports ({0}).{1}@{2} - Address: {3}", name, sImportName, Ord, pThunkOrg->Function);
}
else
{
Console.WriteLine("Bad ReadPtr Detected or EOF on Imports");
break;
}
}
pThunkOrg++;
}
}
catch (AccessViolationException e)
{
Console.WriteLine("An Access violation occured\n" +
"this seems to suggest the end of the imports section\n");
Console.WriteLine(e);
}
pIID++;
}
}
}
}
}
Console.WriteLine("Press Any Key To Continue......");
Console.ReadKey();
}
}
}
发布于 2011-01-14 22:58:06
在Jester对原始示例的更正的基础上,这里有一个同时读取导入和导出的类。我已经成功地在32位DLL上使用了它,但是还不知道64位。
using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using System.Security;
/*
Inspirations:
* http://www.bearcanyon.com/dotnet/#AssemblyParser (Mike Woodring's "Parsing PE File Headers to Determine if a DLL or EXE is an Assembly")
* http://stackoverflow.com/questions/1563134/how-do-i-read-the-pe-header-of-a-module-loaded-in-memory ("How do I read the PE header of a module loaded in memory?")
* http://stackoverflow.com/questions/2975639/resolving-rvas-for-import-and-export-tables-within-a-pe-file ("Resolving RVA's for Import and Export tables within a PE file.")
* http://www.lenholgate.com/blog/2006/04/i-love-it-when-a-plan-comes-together.html
* http://www.gamedev.net/community/forums/topic.asp?topic_id=409936
* http://stackoverflow.com/questions/4571088/how-to-programatically-read-native-dll-imports-in-c
*/
namespace PE
{
public unsafe class PortableExecutableParser
{
public delegate void DLog(string fmt, params object[] args);
private readonly DLog _fnLog;
private void Log( string fmt, params object[] args )
{
if (_fnLog != null)
_fnLog(fmt, args);
}
private readonly List<string> _exports = new List<string>();
public IEnumerable<string> Exports { get { return _exports; } }
private readonly List<Tuple<string, List<string>>> _imports = new List<Tuple<string, List<string>>>();
public IEnumerable<Tuple<string, List<string>>> Imports { get { return _imports; } }
public PortableExecutableParser( string path, DLog fnLog=null )
{
_fnLog = fnLog;
LOADED_IMAGE loadedImage;
if (MapAndLoad(path, null, out loadedImage, true, true))
{
LoadExports(loadedImage);
LoadImports(loadedImage);
}
}
private void LoadExports(LOADED_IMAGE loadedImage)
{
var hMod = (void*)loadedImage.MappedAddress;
if (hMod != null)
{
Log("Got handle");
uint size;
var pExportDir = (IMAGE_EXPORT_DIRECTORY*)ImageDirectoryEntryToData(
(void*)loadedImage.MappedAddress,
false,
IMAGE_DIRECTORY_ENTRY_EXPORT,
out size);
if (pExportDir != null)
{
Log("Got Image Export Descriptor");
var pFuncNames = (uint*)RvaToVa(loadedImage, pExportDir->AddressOfNames);
for (uint i = 0; i < pExportDir->NumberOfNames; i++)
{
uint funcNameRva = pFuncNames[i];
if (funcNameRva != 0)
{
var funcName =
(char*)RvaToVa(loadedImage, funcNameRva);
var name = Marshal.PtrToStringAnsi((IntPtr)funcName);
Log(" funcName: {0}", name);
_exports.Add(name);
}
}
}
}
}
private static IntPtr RvaToVa( LOADED_IMAGE loadedImage, uint rva )
{
return ImageRvaToVa(loadedImage.FileHeader, loadedImage.MappedAddress, rva, IntPtr.Zero);
}
private static IntPtr RvaToVa(LOADED_IMAGE loadedImage, IntPtr rva)
{
return RvaToVa(loadedImage, (uint)(rva.ToInt32()) );
}
private void LoadImports(LOADED_IMAGE loadedImage)
{
var hMod = (void*)loadedImage.MappedAddress;
if (hMod != null)
{
Console.WriteLine("Got handle");
uint size;
var pImportDir =
(IMAGE_IMPORT_DESCRIPTOR*)
ImageDirectoryEntryToData(hMod, false,
IMAGE_DIRECTORY_ENTRY_IMPORT, out size);
if (pImportDir != null)
{
Log("Got Image Import Descriptor");
while (pImportDir->OriginalFirstThunk != 0)
{
try
{
var szName = (char*) RvaToVa(loadedImage, pImportDir->Name);
string name = Marshal.PtrToStringAnsi((IntPtr) szName);
var pr = new Tuple<string, List<string>>(name, new List<string>());
_imports.Add(pr);
var pThunkOrg = (THUNK_DATA*)RvaToVa(loadedImage, pImportDir->OriginalFirstThunk);
while (pThunkOrg->AddressOfData != IntPtr.Zero)
{
uint ord;
if ((pThunkOrg->Ordinal & 0x80000000) > 0)
{
ord = pThunkOrg->Ordinal & 0xffff;
Log("imports ({0}).Ordinal{1} - Address: {2}", name, ord,
pThunkOrg->Function);
}
else
{
var pImageByName =
(IMAGE_IMPORT_BY_NAME*) RvaToVa(loadedImage, pThunkOrg->AddressOfData);
if (
!IsBadReadPtr(pImageByName, (uint) sizeof (IMAGE_IMPORT_BY_NAME)))
{
ord = pImageByName->Hint;
var szImportName = pImageByName->Name;
string sImportName = Marshal.PtrToStringAnsi((IntPtr) szImportName);
Log("imports ({0}).{1}@{2} - Address: {3}", name,
sImportName, ord, pThunkOrg->Function);
pr.Item2.Add( sImportName );
}
else
{
Log("Bad ReadPtr Detected or EOF on Imports");
break;
}
}
pThunkOrg++;
}
}
catch (AccessViolationException e)
{
Log("An Access violation occured\n" +
"this seems to suggest the end of the imports section\n");
Log(e.ToString());
}
pImportDir++;
}
}
}
}
// ReSharper disable InconsistentNaming
private const ushort IMAGE_DIRECTORY_ENTRY_IMPORT = 1;
private const ushort IMAGE_DIRECTORY_ENTRY_EXPORT = 0;
private const CallingConvention WINAPI = CallingConvention.Winapi;
private const string KERNEL_DLL = "kernel32";
private const string DBGHELP_DLL = "Dbghelp";
private const string IMAGEHLP_DLL = "ImageHlp";
// ReSharper restore InconsistentNaming
[DllImport(KERNEL_DLL, CallingConvention = WINAPI, EntryPoint = "GetModuleHandleA"), SuppressUnmanagedCodeSecurity]
public static extern void* GetModuleHandleA(/*IN*/ char* lpModuleName);
[DllImport(KERNEL_DLL, CallingConvention = WINAPI, EntryPoint = "GetModuleHandleW"), SuppressUnmanagedCodeSecurity]
public static extern void* GetModuleHandleW(/*IN*/ char* lpModuleName);
[DllImport(KERNEL_DLL, CallingConvention = WINAPI, EntryPoint = "IsBadReadPtr"), SuppressUnmanagedCodeSecurity]
public static extern bool IsBadReadPtr(void* lpBase, uint ucb);
[DllImport(DBGHELP_DLL, CallingConvention = WINAPI, EntryPoint = "ImageDirectoryEntryToData"), SuppressUnmanagedCodeSecurity]
public static extern void* ImageDirectoryEntryToData(void* pBase, bool mappedAsImage, ushort directoryEntry, out uint size);
[DllImport(DBGHELP_DLL, CallingConvention = WINAPI), SuppressUnmanagedCodeSecurity]
public static extern IntPtr ImageRvaToVa(
IntPtr pNtHeaders,
IntPtr pBase,
uint rva,
IntPtr pLastRvaSection);
[DllImport(DBGHELP_DLL, CallingConvention = CallingConvention.StdCall), SuppressUnmanagedCodeSecurity]
public static extern IntPtr ImageNtHeader(IntPtr pImageBase);
[DllImport(IMAGEHLP_DLL, CallingConvention = CallingConvention.Winapi), SuppressUnmanagedCodeSecurity]
public static extern bool MapAndLoad(string imageName, string dllPath, out LOADED_IMAGE loadedImage, bool dotDll, bool readOnly);
}
// ReSharper disable InconsistentNaming
[StructLayout(LayoutKind.Sequential)]
public struct LOADED_IMAGE
{
public IntPtr moduleName;
public IntPtr hFile;
public IntPtr MappedAddress;
public IntPtr FileHeader;
public IntPtr lastRvaSection;
public UInt32 numbOfSections;
public IntPtr firstRvaSection;
public UInt32 charachteristics;
public ushort systemImage;
public ushort dosImage;
public ushort readOnly;
public ushort version;
public IntPtr links_1; // these two comprise the LIST_ENTRY
public IntPtr links_2;
public UInt32 sizeOfImage;
}
[StructLayout(LayoutKind.Explicit)]
public unsafe struct IMAGE_IMPORT_BY_NAME
{
[FieldOffset(0)]
public ushort Hint;
[FieldOffset(2)]
public fixed char Name[1];
}
[StructLayout(LayoutKind.Explicit)]
public struct IMAGE_IMPORT_DESCRIPTOR
{
#region union
/// <summary>
/// CSharp doesnt really support unions, but they can be emulated by a field offset 0
/// </summary>
[FieldOffset(0)]
public uint Characteristics; // 0 for terminating null import descriptor
[FieldOffset(0)]
public uint OriginalFirstThunk; // RVA to original unbound IAT (PIMAGE_THUNK_DATA)
#endregion
[FieldOffset(4)]
public uint TimeDateStamp;
[FieldOffset(8)]
public uint ForwarderChain;
[FieldOffset(12)]
public uint Name;
[FieldOffset(16)]
public uint FirstThunk;
}
[StructLayout(LayoutKind.Sequential)]
public struct IMAGE_EXPORT_DIRECTORY
{
public UInt32 Characteristics;
public UInt32 TimeDateStamp;
public UInt16 MajorVersion;
public UInt16 MinorVersion;
public UInt32 Name;
public UInt32 Base;
public UInt32 NumberOfFunctions;
public UInt32 NumberOfNames;
public IntPtr AddressOfFunctions; // RVA from base of image
public IntPtr AddressOfNames; // RVA from base of image
public IntPtr AddressOfNameOrdinals; // RVA from base of image
}
[StructLayout(LayoutKind.Explicit)]
public struct THUNK_DATA
{
[FieldOffset(0)]
public uint ForwarderString; // PBYTE
[FieldOffset(0)]
public uint Function; // PDWORD
[FieldOffset(0)]
public uint Ordinal;
[FieldOffset(0)]
public IntPtr AddressOfData; // PIMAGE_IMPORT_BY_NAME
}
// ReSharper restore InconsistentNaming
}
发布于 2015-12-16 09:24:06
要使埃里克的回答在x64上工作,您必须更改IMAGE_EXPORT_DIRECTORY中RVA地址(AddressOfFunctions、AddressOfNames和AddressOfNameOrdinals)的数据类型。它们总是32位长(UInt32)。IntPtr在x86上是32位,x64是64位。
[StructLayout(LayoutKind.Sequential)]
public struct IMAGE_EXPORT_DIRECTORY
{
public UInt32 Characteristics;
public UInt32 TimeDateStamp;
public UInt16 MajorVersion;
public UInt16 MinorVersion;
public UInt32 Name;
public UInt32 Base;
public UInt32 NumberOfFunctions;
public UInt32 NumberOfNames;
public UInt32 AddressOfFunctions; // RVA from base of image
public UInt32 AddressOfNames; // RVA from base of image
public UInt32 AddressOfNameOrdinals; // RVA from base of image
}
https://stackoverflow.com/questions/4571088
复制相似问题