通过 C# 运行 在 64 位中获取文件系统上的二进制文件类型
Get type of binary on filesystem via C# running in 64 bit
我有一个 C# 应用程序,在 Visual Studio 2017 年在 'Any CPU' 目标上编译,禁用了 'Prefer 32-bit' 选项。在此应用程序中,我尝试调用 kernel32!GetBinaryType()。当 运行 'Prefer 32-bit' 启用时,它工作正常。当 运行 从 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()
returns 193,根据 https://msdn.microsoft.com/en-us/library/windows/desktop/ms681382(v=vs.85).aspx 是
ERROR_BAD_EXE_FORMAT.
我已将 64 位调试器附加到我的 C# 应用程序和我的 C++ 应用程序,以确保字符串到达 GetBinaryTypeW()
在堆栈的正确位置,在我看来它是。
我担心我 运行 编组器中的一些细微错误。
我的方法有什么问题?
更新
评论中提到的问题不符合这种情况。在那个问题中,对 LoadLibrary()
的调用失败了,因为它被用来尝试将 32 位 DLL 加载到 64 位进程中。我没有尝试加载任何 DLL。我只是想使用 GetBinaryType() 来检查可执行文件的 PE header。
GetBinaryType
在 WOW6432 space 中进程为 运行 时会做一些有趣的事情,而 .NET 运行时可能会加剧这种情况。不幸的是,我现在不记得所有细节了。然而,这里有一个更健壮的解决方案,它适用于 EXE 和 DLL。请注意,这仅检测 PE 二进制类型。如果您正在处理编译为 AnyCPU 的 .NET 程序集,您可能会或可能不会得到您期望的结果。我将把任何进一步的调查留作 reader.
的练习
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[=10=][=10=]"
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;
}
}
}
}
我有一个 C# 应用程序,在 Visual Studio 2017 年在 'Any CPU' 目标上编译,禁用了 'Prefer 32-bit' 选项。在此应用程序中,我尝试调用 kernel32!GetBinaryType()。当 运行 'Prefer 32-bit' 启用时,它工作正常。当 运行 从 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()
returns 193,根据 https://msdn.microsoft.com/en-us/library/windows/desktop/ms681382(v=vs.85).aspx 是
ERROR_BAD_EXE_FORMAT.
我已将 64 位调试器附加到我的 C# 应用程序和我的 C++ 应用程序,以确保字符串到达 GetBinaryTypeW()
在堆栈的正确位置,在我看来它是。
我担心我 运行 编组器中的一些细微错误。
我的方法有什么问题?
更新
评论中提到的问题不符合这种情况。在那个问题中,对 LoadLibrary()
的调用失败了,因为它被用来尝试将 32 位 DLL 加载到 64 位进程中。我没有尝试加载任何 DLL。我只是想使用 GetBinaryType() 来检查可执行文件的 PE header。
GetBinaryType
在 WOW6432 space 中进程为 运行 时会做一些有趣的事情,而 .NET 运行时可能会加剧这种情况。不幸的是,我现在不记得所有细节了。然而,这里有一个更健壮的解决方案,它适用于 EXE 和 DLL。请注意,这仅检测 PE 二进制类型。如果您正在处理编译为 AnyCPU 的 .NET 程序集,您可能会或可能不会得到您期望的结果。我将把任何进一步的调查留作 reader.
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[=10=][=10=]"
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;
}
}
}
}