【问题标题】:Get type of binary on filesystem via C# running in 64 bit通过以 64 位运行的 C# 获取文件系统上的二进制文件类型
【发布时间】:2017-06-02 21:11:51
【问题描述】:

我有一个 C# 应用程序,在 Visual Studio 2017 中在“任何 CPU”目标上编译,并禁用了“首选 32 位”选项。在这个应用程序中,我尝试调用 kernel32!GetBinaryType()。在启用“首选 32 位”的情况下运行时,它工作正常。从 C++ 可执行文件以 32 位或 64 位模式运行时,它工作正常。我不确定我在使用 64 位 C# 应用程序时做错了什么。

这是我的 pinvoke 签名:

[DllImport("kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool GetBinaryTypeW([MarshalAs(UnmanagedType.LPWStr)] string path, out UInt32 result);

从64位模式调用这个,GetLastError()返回193,根据https://msdn.microsoft.com/en-us/library/windows/desktop/ms681382(v=vs.85).aspx

ERROR_BAD_EXE_FORMAT。

我已将一个 64 位调试器附加到我的 C# 应用程序和我的 C++ 应用程序,以确保字符串到达​​堆栈上正确位置的 GetBinaryTypeW(),并且在我看来确实如此。

我担心我在编组器中遇到了一些微妙的错误。

我的方法有什么问题?

更新

cmets 中引用的问题与这种情况不符。在那个问题中,对LoadLibrary() 的调用失败了,因为它被用来尝试将 32 位 DLL 加载到 64 位进程中。我不想加载任何 DLL。我只是尝试使用 GetBinaryType() 来检查可执行文件的 PE 标头。

【问题讨论】:

  • 就您要达到的目标而言不是重复的,但考虑到结果可能是重复的。
  • kernel32.dll 也是 64 位二进制文​​件的名称。在 64 位 Windows 上,C:\Windows\System32\kernel32.dll 是 64 位 DLL,C:\Windows\SysWOW64\kernel32.dll 是 32 位 DLL。
  • 我们看不到检查错误的代码。 minimal reproducible example 在哪里?
  • Windows 不会有错误吧?问题在你的最后。拥抱那个。向我们展示您的 mcve。

标签: c# windows pinvoke marshalling x86-64


【解决方案1】:

GetBinaryType 在 WOW6432 空间中运行进程时会做一些有趣的事情,而 .NET 运行时可能会加剧这种情况。不幸的是,我现在不记得所有的细节。但是,这里有一个更强大的解决方案,它适用于 EXE 和 DLL。请注意,这仅检测 PE 二进制类型。如果您正在处理编译为 AnyCPU 的 .NET 程序集,您可能会也可能不会获得您期望的结果。我会将任何进一步的调查留给读者作为练习。

public enum BinaryType : uint
{
    SCS_32BIT_BINARY = 0,
    SCS_64BIT_BINARY = 6,
    SCS_DOS_BINARY = 1,
    SCS_OS216_BINARY = 5,
    SCS_PIF_BINARY = 3,
    SCS_POSIX_BINARY = 4,
    SCS_WOW_BINARY = 2
}

public static BinaryType? GetBinaryType(string path)
{
    using (FileStream stream = new FileStream(path, FileMode.Open, FileAccess.Read))
    {
        stream.Seek(0x3C, SeekOrigin.Begin);
        using (var reader = new BinaryReader(stream))
        {
            if (stream.Position + sizeof(int) > stream.Length)
                return null;
            var peOffset = reader.ReadInt32();
            stream.Seek(peOffset, SeekOrigin.Begin);
            if (stream.Position + sizeof(uint) > stream.Length)
                return null;
            var peHead = reader.ReadUInt32();
            if (peHead != 0x00004550) // "PE\0\0"
                return null;
            if (stream.Position + sizeof(ushort) > stream.Length)
                return null;
            switch (reader.ReadUInt16())
            {
                case 0x14c:
                    return BinaryType.SCS_32BIT_BINARY;
                case 0x8664:
                    return BinaryType.SCS_64BIT_BINARY;
                default:
                    return null;
            }
        }
    }
}

【讨论】:

  • 谢谢,这成功了。至于细节,我可以告诉你,当我单步执行kernel32!GetBinaryType() 时,它调用kernel32!RtlImageNtHeader() 并且该调用失败并显示相同的错误代码。执行从kernel32 中返回。
猜你喜欢
  • 2011-09-08
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-11-18
  • 1970-01-01
  • 2012-04-12
  • 2020-07-06
相关资源
最近更新 更多