c# - 如何以编程方式读取 C# 中的 native DLL 导入?

标签 c# .net import portable-executable dbghelp

如何以编程方式分析 native DLL 以读取其导入?


[编辑:我最初的问题如下所示,还有一大块有缺陷的代码。请参阅下面的答案以获得更正确的代码。]

位于 this link 的 C# 代码旨在打印 native DLL 的导入。

我发现,当我使用原始示例的目标 MSCOREE.DLL 运行示例代码时,它可以很好地打印所有导入。但是当我使用 GDI32.DLL 或 WSOCK32.DLL 等其他 dll 时,不会打印导入。这段代码中缺少什么可以打印所有导入,例如 DUMPBIN.EXE 所做的?

最佳答案

代码中有一个非常大的问题(即 THUNK_DATA 的定义)和其他各种小问题,主要与表尾检测有关(使用 IsBadReadPtr 代替) NULL 检查,也没有根据需要添加基地址)。

这是一个固定版本,至少对于 wsock32 而言,它产生与 dumpbin 相同的输出:

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();
        }
    }
}

关于c# - 如何以编程方式读取 C# 中的 native DLL 导入?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/4571088/

相关文章:

.net - 正在寻找 SMT Z3 用例(如 DbC)和 Z3 的开源替代品的实际示例?

android - 将 Android 项目导入 Eclipse 时的项目名称问题

javac 添加类路径破坏了我的本地类编译

c# - EF Core 查询在真实数据库中运行良好,但测试失败

c# - 通过使用转换器来转换对象来绑定(bind)到任意 Dictionary<,>

c# - 如何在互联网中断后继续或恢复 FTP 上传

c# - CredUIPromptForCredentials 强制手动选择用户名

c# - 创建一个 IObservable 并立即返回缓存异步操作的结果

c# - DbSet.FirstOrDefault()?

Java 计时器启动应用程序时出现问题