我想在C#代码中调用5个WinDivert函数,第一反射:PInvoke这里是签名:
internal enum WINDIVERT_LAYER
{
WINDIVERT_LAYER_NETWORK = 0, /* Network layer. */
WINDIVERT_LAYER_NETWORK_FORWARD = 1 /* Network layer (forwarded packets) */
}
internal const UInt64 WINDIVERT_FLAG_SNIFF = 1;
/*
* typedef struct
{
UINT32 IfIdx;
UINT32 SubIfIdx;
UINT8 Direction;
} WINDIVERT_ADDRESS, *PWINDIVERT_ADDRESS;
* */
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
internal struct WINDIVERT_ADDRESS
{
UInt32 IfIdx;
UInt32 SubIfIdx;
byte Direction;
}
/*
* typedef struct
{
UINT8 HdrLength:4;
UINT8 Version:4;
UINT8 TOS;
UINT16 Length;
UINT16 Id;
UINT16 FragOff0;
UINT8 TTL;
UINT8 Protocol;
UINT16 Checksum;
UINT32 SrcAddr;
UINT32 DstAddr;
} WINDIVERT_IPHDR, *PWINDIVERT_IPHDR;
* */
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
internal struct WINDIVERT_IPHDR
{
byte HdrLengthAndVersion;
byte TOS;
UInt16 Length;
UInt16 Id;
UInt16 FragOff0;
byte TTL;
byte Protocol;
UInt16 Checksum;
UInt32 SrcAddr;
UInt32 DstAddr;
}
/*
* typedef struct
{
UINT16 SrcPort;
UINT16 DstPort;
UINT16 Length;
UINT16 Checksum;
} WINDIVERT_UDPHDR, *PWINDIVERT_UDPHDR;
* */
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
internal struct WINDIVERT_UDPHDR
{
UInt16 SrcPort;
UInt16 DstPort;
UInt16 Length;
UInt16 Checksum;
}
/*
* HANDLE WinDivertOpen(
__in const char *filter,
__in WINDIVERT_LAYER layer,
__in INT16 priority,
__in UINT64 flags
);
* */
[DllImport("WinDivert.dll", EntryPoint = "WinDivertOpen", SetLastError = true, CharSet = CharSet.Auto,
ExactSpelling = true, CallingConvention = CallingConvention.Cdecl)]
internal static extern IntPtr WinDivertOpen(
[MarshalAs(UnmanagedType.LPStr)] string filter,
WINDIVERT_LAYER layer,
Int16 priority,
UInt64 flags);
/*
* BOOL WinDivertClose(
__in HANDLE handle
);
* */
[DllImport("WinDivert.dll", EntryPoint = "WinDivertClose", SetLastError = true, CharSet = CharSet.Auto,
ExactSpelling = true, CallingConvention = CallingConvention.Cdecl)]
[return:MarshalAs(UnmanagedType.Bool)]
internal static extern bool WinDivertClose(IntPtr handle);
/*
* BOOL WinDivertRecv(
__in HANDLE handle,
__out PVOID pPacket,
__in UINT packetLen,
__out_opt PWINDIVERT_ADDRESS pAddr,
__out_opt UINT *recvLen
);
* */
[DllImport("WinDivert.dll", EntryPoint = "WinDivertRecv", SetLastError = true, CharSet = CharSet.Auto,
ExactSpelling = true, CallingConvention = CallingConvention.Cdecl)]
[return: MarshalAs(UnmanagedType.Bool)]
internal static extern bool WinDivertRecv(IntPtr handle, out IntPtr pPacket, uint packetLen, [Optional] out IntPtr pAddr, [Optional] out uint readLen);
/*
* BOOL WinDivertHelperParsePacket(
__in PVOID pPacket,
__in UINT packetLen,
__out_opt PWINDIVERT_IPHDR *ppIpHdr,
__out_opt PWINDIVERT_IPV6HDR *ppIpv6Hdr,
__out_opt PWINDIVERT_ICMPHDR *ppIcmpHdr,
__out_opt PWINDIVERT_ICMPV6HDR *ppIcmpv6Hdr,
__out_opt PWINDIVERT_TCPHDR *ppTcpHdr,
__out_opt PWINDIVERT_UDPHDR *ppUdpHdr,
__out_opt PVOID *ppData,
__out_opt UINT *pDataLen
);
* */
[DllImport("WinDivert.dll", EntryPoint = "WinDivertHelperParsePacket", SetLastError = true, CharSet = CharSet.Auto,
ExactSpelling = true, CallingConvention = CallingConvention.Cdecl)]
[return: MarshalAs(UnmanagedType.Bool)]
internal static extern bool WinDivertHelperParsePacket(IntPtr pPacket, uint packetLen, [Optional] out IntPtr ppIpHdr, [Optional] out IntPtr ppIpv6Hdr,
[Optional] out IntPtr ppIcmpHdr, [Optional] out IntPtr ppTcpHdr, [Optional] out IntPtr ppUdpHdr, [Optional] out IntPtr ppData,
[Optional]out uint pDataLen);
/*
* BOOL WinDivertSend(
__in HANDLE handle,
__in PVOID pPacket,
__in UINT packetLen,
__in PWINDIVERT_ADDRESS pAddr,
__out_opt UINT *sendLen
);
* */
[DllImport("WinDivert.dll", EntryPoint = "WinDivertSend", SetLastError = true, CharSet = CharSet.Auto,
ExactSpelling = true, CallingConvention = CallingConvention.Cdecl)]
[return: MarshalAs(UnmanagedType.Bool)]
internal static extern bool WinDivertSend(IntPtr handle, IntPtr pPacket, uint packetLen, IntPtr pAddr, [Optional] out uint sendLen);
我设法避免在87 = ERROR_INVALID_PARAMETER
,998 = ERROR_NOACCESS
和WinDivertOpen()
的来电时出现(WinDivertClose()
),(WinDivertRecv()
)错误,但我还是在尝试拨打System.AccessViolationException : Attempted to read or write protected memory. This is often an indication that other memory has been corrupted
时仍然获得(998 = ERROR_NOACCESS)
和WinDivertHelperParsePacket()
。
以下是代码:
static void Main(string[] args)
{
const uint MAXBUF = 0xFFFF;
IntPtr handle;
IntPtr addr = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(NativeMethods.WINDIVERT_ADDRESS)));
IntPtr packet = Marshal.AllocHGlobal((int)MAXBUF);
uint packetLen;
IntPtr ip_header = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(NativeMethods.WINDIVERT_IPHDR)));
IntPtr udp_header = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(NativeMethods.WINDIVERT_UDPHDR)));
IntPtr payload;
uint payload_len;
uint sendLen;
IntPtr opt_param = IntPtr.Zero;
byte[] managedPacket;
IntPtr INVALID_HANDLE_VALUE = new IntPtr(-1);
handle = NativeMethods.WinDivertOpen("udp.DstPort == 53", NativeMethods.WINDIVERT_LAYER.WINDIVERT_LAYER_NETWORK, 404, NativeMethods.WINDIVERT_FLAG_SNIFF);
if (handle == INVALID_HANDLE_VALUE) Console.WriteLine("open error:" + Marshal.GetLastWin32Error());
else
{
while (true)
{
if (!NativeMethods.WinDivertRecv(handle, out packet, MAXBUF, out addr, out packetLen))
{
Console.WriteLine("Recv error:" + Marshal.GetLastWin32Error());
continue;
}
try
{
managedPacket = new byte[(int)packetLen];
Marshal.Copy(packet, managedPacket, 0, (int)packetLen); // causes AccessViolationException
Console.WriteLine("---------------------------------");
/*for (int i = 0; i < packetLen; i++)
{
Console.Write("{0:X}", managedPacket[i]);
}*/
Console.WriteLine("---------------------------------");
}
catch(Exception ex)
{
Console.WriteLine("copy error :" + ex.Message);
}
if (!NativeMethods.WinDivertHelperParsePacket(packet, packetLen, out ip_header, out opt_param, out opt_param, out opt_param, out udp_header, out payload, out payload_len)) // causes AccessViolationException
{
Console.WriteLine("Parse error:" + Marshal.GetLastWin32Error());
//continue;
}
if (!NativeMethods.WinDivertSend(handle, packet, packetLen, addr, out sendLen))
{
Console.WriteLine("Send error:" + Marshal.GetLastWin32Error());
continue;
}
}
/*if (!NativeMethods.WinDivertClose(handle))
Console.WriteLine("close error:" + Marshal.GetLastWin32Error());*/
}
Console.ReadKey();
}
我的老板告诉我,在C ++中编写一个包含C调用并将其暴露给C#的COM对象更好/更容易,以避免编组和内存处理的痛苦。我应该坚持使用PInvoke还是采用COM方式?
编辑:更新
我尝试了两种不同的分配非托管内存的方法,但都失败了(允许使用不安全的代码):
byte[] managedPacket = new byte[(int)packetLen];
NativeMethods.WINDIVERT_ADDRESS windivertAddr = new NativeMethods.WINDIVERT_ADDRESS();
GCHandle managedPacketHandle = GCHandle.Alloc(managedPacket, GCHandleType.Pinned);
IntPtr managedPacketPointer = managedPacketHandle.AddrOfPinnedObject();
GCHandle windivertAddrHandle = GCHandle.Alloc(windivertAddr, GCHandleType.Pinned);
IntPtr windivertAddrPointer = managedPacketHandle.AddrOfPinnedObject();
NativeMethods.WinDivertRecv(handle, out managedPacketPointer, (uint)(packetLen * Marshal.SizeOf(typeof(System.Byte))), out windivertAddrPointer , out readLen);
// output of managed array and struct fields = 0 and it still causes unhandled AccessViolationException even inside a try/catch
managedPacketHandle.Free();
windivertAddrPointer.Free();
和:
IntPtr packet = Marshal.AllocHGlobal((int)packetLen * Marshal.SizeOf(typeof(System.Byte)));
IntPtr addr = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(NativeMethods.WINDIVERT_ADDRESS)));
NativeMethods.WinDivertRecv(handle, out packet, (uint)(packetLen * Marshal.SizeOf(typeof(System.Byte))), out addr, out readLen)
byte[] managedPacket = new byte[(int)packetLen];
Marshal.Copy(packet, managedPacket, 0, (int)readLen);
NativeMethods.WINDIVERT_ADDRESS windivertAddr = (NativeMethods.WINDIVERT_ADDRESS)Marshal.PtrToStructure(addr, typeof(NativeMethods.WINDIVERT_ADDRESS));
// no output of managed array and struct fields and the same unhandled AccessViolationException
Marshal.FreeHGlobal(addr);
Marshal.FreeHGlobal(packet);
有时候在循环中,WinDivRecv会因LastWin32错误而失败:6 = INVALID_HANDLE_VALUE
是不是因为GC搞乱了句柄?我试过了GC.KeepAlive(handle)
并且它没有改变任何东西。
C ++ \ CLI包装器:(非托管C DLL和托管C#代码之间的桥梁)
[以下评论中的建议选项]
我按照以下步骤操作:
我应该将WinDivert DLL添加为引用还是仅添加WinDivert.h?内核驱动程序.sys文件怎么样? 说实话,它并不比PInvoke容易,但更糟糕的是,我仍然必须对非blittable数据类型和struct / enum定义进行相同的编组操作!