أفضل طريقة للتحقق مما إذا كان ملف DLL عبارة عن مجموعة CLR في C#

StackOverflow https://stackoverflow.com/questions/1366503

  •  21-09-2019
  •  | 
  •  

سؤال

ما هي أفضل طريقة للتحقق مما إذا كان ملف DLL هو DLL Win32 أو إذا كان مجموعة CLR. في الوقت الحالي أستخدم هذا الرمز

    try
    {
        this.currentWorkingDirectory = Path.GetDirectoryName(assemblyPath);

        //Try to load the assembly.
        assembly = Assembly.LoadFile(assemblyPath);

        return assembly != null;
    }
    catch (FileLoadException ex)
    {
        exception = ex;
    }
    catch (BadImageFormatException ex)
    {
        exception = ex;
    }
    catch (ArgumentException ex)
    {
        exception = ex;
    }
    catch (Exception ex)
    {
        exception = ex;
    }

    if (exception is BadImageFormatException)
    {
        return false;
    }

لكني أحب التحقق قبل التحميل لأنني لا أريد هذه الاستثناءات (الوقت).

هل هناك طريقة أفضل؟

هل كانت مفيدة؟

المحلول

تحقق من رأس PE:

يبدأ رأس DOS من 0x0 ، يحتوي Dword في 0x3C على مؤشر لتوقيع PE (عادة 0x80) وهو 4 بايت ، وعشرين بايتات التالية هو رأس التاب بايت ويحتوي على دليل البيانات (عند 96 بايت في رأس PE = 0xf. الإدخال الخامس عشر (في 0x16 هو واصف رأس CLR (يسمى أحيانًا واصف com ، لكن هذا لا علاقة له بـ COM). إذا كان هذا فارغ (أي 0 في 8 بايت من 0x168 إلى 0x16F) ، فإن الملف ليس مجموعة .NET. إذا كنت تريد التحقق مما إذا كان com dll ، فيجب أن تبحث لمعرفة ما إذا كانت تصدير GetClassObject.

المرجع.

تحديث: هناك طريقة أكثر ".NET" لإنجاز هذا:

يستخدم Module.GetPEKind الطريقة والتحقق من PortableExecutableKinds تعداد:

notaportableExecutableImage الملف ليس بتنسيق ملف قابلة للتنفيذ (PE) المحمول.

ilonly يحتوي التنفيذ على لغة Microsoft الوسيطة فقط (MSIL) ، وبالتالي فهي محايدة فيما يتعلق بمنصات 32 بت أو 64 بت.

مطلوب 32 بت يمكن تشغيل القابلة للتنفيذ على منصة 32 بت ، أو في بيئة Windows 32 بت على Windows (WOW) على منصة 64 بت.

PE32Plus القابلة للتنفيذ يتطلب منصة 64 بت.

ungaged32bit القابل للتنفيذ يحتوي على رمز غير مُدار خالص.

نصائح أخرى

إذا تم تحميل التجميع على سبيل المثال Assembly.LoadFile(dotNetDllorExe) ولا يرمي أي استثناء ، إنه تجميع .NET صالح. إذا لم يكن الأمر كذلك ، فسيتم رمي "badimageformatexception".

فكرة التحقق من الطقس هو تجميع أو لا عن طريق تحميله والتحقق مما إذا كان الاستثناء قد تم إلقاؤه أم لا ؛ لا يبدو أنه نظيف للغاية. بعد كل الاستثناءات من المفترض أن تستخدم بشكل استثنائي.


.NET Assemblies عبارة عن ملفات Win32 PE منتظمة ، لا يفرق نظام التشغيل بين مجموعات .NET و Win32 القابلة للتنفيذ ، فهي نفس ملفات PE العادية. فكيف يعمل النظام إذا كان DLL أو EXE عبارة عن مجموعة مُدارة من أجل تحميل CLR؟

يقوم بالتحقق من صحة رأس الملف للتحقق مما إذا كانت مجموعة مُدارة أم لا. في مواصفات ECMA Partition II - بيانات التعريف التي يتم شحنها مع .NET SDK ، ترى أن هناك رأسًا منفصلاً CLI في تنسيق PE. انها دليل البيانات الخامس عشر في الرؤوس الاختيارية PE. لذلك ، بعبارات بسيطة ، إذا كان لدينا قيمة في دليل البيانات هذا ، فهذا يعني أن هذا عبارة عن مجموعة .NET صالحة ، وإلا فإنها ليست كذلك.

internal static class PortableExecutableHelper
{
    internal static bool IsDotNetAssembly(string peFile)
    {
        uint peHeader;
        uint peHeaderSignature;
        ushort machine;
        ushort sections;
        uint timestamp;
        uint pSymbolTable;
        uint noOfSymbol;
        ushort optionalHeaderSize;
        ushort characteristics;
        ushort dataDictionaryStart;
        uint[] dataDictionaryRVA = new uint[16];
        uint[] dataDictionarySize = new uint[16];


        Stream fs = new FileStream(peFile, FileMode.Open, FileAccess.Read);
        BinaryReader reader = new BinaryReader(fs);

        //PE Header starts @ 0x3C (60). Its a 4 byte header.
        fs.Position = 0x3C;

        peHeader = reader.ReadUInt32();

        //Moving to PE Header start location...
        fs.Position = peHeader;
        peHeaderSignature = reader.ReadUInt32();

        //We can also show all these value, but we will be       
        //limiting to the CLI header test.

        machine = reader.ReadUInt16();
        sections = reader.ReadUInt16();
        timestamp = reader.ReadUInt32();
        pSymbolTable = reader.ReadUInt32();
        noOfSymbol = reader.ReadUInt32();
        optionalHeaderSize = reader.ReadUInt16();
        characteristics = reader.ReadUInt16();

        /*
            Now we are at the end of the PE Header and from here, the
                        PE Optional Headers starts...
                To go directly to the datadictionary, we'll increase the      
                stream’s current position to with 96 (0x60). 96 because,
                        28 for Standard fields
                        68 for NT-specific fields
            From here DataDictionary starts...and its of total 128 bytes. DataDictionay has 16 directories in total,
            doing simple maths 128/16 = 8.
            So each directory is of 8 bytes.
                        In this 8 bytes, 4 bytes is of RVA and 4 bytes of Size.

            btw, the 15th directory consist of CLR header! if its 0, its not a CLR file :)
     */
        dataDictionaryStart = Convert.ToUInt16(Convert.ToUInt16(fs.Position) + 0x60);
        fs.Position = dataDictionaryStart;
        for (int i = 0; i < 15; i++)
        {
            dataDictionaryRVA[i] = reader.ReadUInt32();
            dataDictionarySize[i] = reader.ReadUInt32();
        }
        if (dataDictionaryRVA[14] == 0)
        {
            Console.WriteLine("This is NOT a valid CLR File!!");
            return false;
        }
        else
        {
            Console.WriteLine("This is a valid CLR File..");
            return true;
        }
        fs.Close();
    }
}

ECMA المرجع, مدونة المرجع

في مواجهة نفس المشكلة في الماضي ، لجأت إلى استخدام نهج الانعكاس الخاص بك لأن البديل هو قراءة رأس PE يدويًا مثله. بدا الأمر وكأنه مفرط في السيناريو الخاص بي ، لكنه قد يكون مفيدًا لك.

لم تحدد ما إذا كان عليك القيام بذلك في الرمز ، أو إذا كنت بحاجة شخصيًا إلى معرفة ما إذا كان الملف الذي تبحث عنه على نظامك هو مجموعة .NET (والتي ربما تعتقد أنك تتطلب منك كتابة الرمز الخاص بك القيام بذلك). إذا كان الأخير ، يمكنك استخدام Walker التبعية لمعرفة ما إذا كان لديه اعتماد على mscoree.dll ، وهو محرك وقت التشغيل .NET.

يمكنك استخدام شيء مثل:

        AssemblyName assemblyName = null;

        try
        {
            assemblyName = AssemblyName.GetAssemblyName(filename);
        }
        catch (System.IO.FileNotFoundException ex)
        {
            throw new Exception("File not found!", ex);
        }
        catch (System.BadImageFormatException ex)
        {
            throw new Exception("File is not an .Net Assembly.", ex);
        }

من فضلك تحقق أيضًا من: https://msdn.microsoft.com/en-us/library/ms173100.aspx

يمكنك قراءة أول وحدات البايت من الملف ، إذا كانت البايتات "MZ" ، فحاول قراءة اسم التجميع لتحديد (Microsoft بطيئة الطريق) صحة الجمعية.

    public static bool isValidAssembly (string sFileName)
    {
        try
        {
            using (FileStream fs = File.OpenRead(sFileName))
            {
                if ((fs.ReadByte() != 'M') || (fs.ReadByte() != 'Z'))
                {
                    fs.Close();
                    return false;
                }
                fs.Close();
            }

            // http://msdn.microsoft.com/en-us/library/ms173100.aspx
            object foo = SR.AssemblyName.GetAssemblyName(sFileName);
            return true;
        }
        catch 
        {
            return false;
        }
    }
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top