通过以64位运行的C#获取文件系统上的二进制类型

时间:2017-06-02 21:11:51

标签: c# windows pinvoke marshalling x86-64

我有一个C#应用程序,在Visual Studio 2017中编译在任何CPU'目标,使用'首选32位'选项已禁用。在这个应用程序中,我试图pinvoke 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。

我已经为我的C#应用​​程序和我的C ++应用程序附加了一个64位调试器,以确保字符串在堆栈中的正确位置到达GetBinaryTypeW(),并且在我看来它是。

我担心我在封送者身上遇到了一些微妙的错误。

我的做法出了什么问题?

更新

评论中引用的问题与此情况不符。在该问题中,对LoadLibrary()的调用失败,因为它被用于尝试将32位DLL加载到64位进程中。我不是要加载任何DLL。我只是试图使用GetBinaryType()来检查可执行文件的PE头。

1 个答案:

答案 0 :(得分:2)

当进程在WOW6432空间中运行时,

GetBinaryType会做一些有趣的事情,而.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;
            }
        }
    }
}