使用命名管道进行C ++和C#通信

时间:2015-05-06 01:00:50

标签: c# c++ named-pipes

我试图反转引入注入流程的dll,它会挂钩winsock send()并通过PipeStream发送数据。

这是读取管道流的C#代码:

    [StructLayout(LayoutKind.Sequential, Pack = 1, CharSet = CharSet.Ansi)]
    public struct PipeHeader
    {
        [MarshalAs(UnmanagedType.I1)]
        public byte command;
        [MarshalAs(UnmanagedType.I4)]
        public int sockid;
        public int datasize;
    }

    public static object RawDeserializeEx(byte[] rawdatas, Type anytype)
    {
        int rawsize = Marshal.SizeOf(anytype);
        if (rawsize > rawdatas.Length)
            return null;
        GCHandle handle = GCHandle.Alloc(rawdatas, GCHandleType.Pinned);
        IntPtr buffer = handle.AddrOfPinnedObject();
        object retobj = Marshal.PtrToStructure(buffer, anytype);
        handle.Free();
        return retobj;
    }
    private void PipeRead()
    {
        byte[] dbPipeMsgIn = new byte[9];
        byte[] zero = new byte[] { 0 };

        byte[] dbPipeMsgInData;
    PipeLoop:
        while (pipeIn.Read(dbPipeMsgIn, 0, 9) != 0)
        {
            strPipeMsgIn = (PipeHeader)RawDeserializeEx(dbPipeMsgIn, typeof(PipeHeader));
            if (strPipeMsgIn.datasize != 0)
            {
                dbPipeMsgInData = new byte[strPipeMsgIn.datasize];
                pipeIn.Read(dbPipeMsgInData, 0, dbPipeMsgInData.Length);
                //do something with dbPipeMsgInData
            }
        }
        if (pipeIn.IsConnected) goto PipeLoop;
    }

到目前为止,我已经挂钩了send() func,通过管道连接和发送消息。 问题是收到的数据不是我期望的数据,所以我可能以错误的方式发送。我需要帮助,因为我几乎没有C ++知识。

C ++代码:

#pragma pack(1)
typedef struct
{
    byte command;
    int sockid;
    int datasize;
} PipeHeader;
#pragma pack(0)

int WINAPI MySend(SOCKET s, const char* buf, int len, int flags)
{
    PipeHeader ph;
    string p(buf);
    if(p.find("<TalkMsg") == 0){
        byte cmd = 0;
        ph.command = cmd;
        ph.sockid = s;
        ph.datasize = len;
        char buffer[sizeof(ph)];
        memcpy(buffer, &ph, sizeof(ph)); 
        if(SendPipeMessage(buffer, sizeof(buffer))){
            if(SendPipeMessage(buf, sizeof(buf))){
                MessageBox(NULL,"Message Sent", NULL, NULL);
            }
        }
        fopen_s(&pSendLogFile, "C:\\SendLog.txt", "a+");
        fprintf(pSendLogFile, "%s\n", buf);
        fclose(pSendLogFile);
    }
    return pSend(s, buf, len, flags);
}

BOOL SendPipeMessage(LPCVOID lpvMessage, DWORD ctToWrite){
    // Send a message to the pipe server. 
   cbToWrite = sizeof(lpvMessage);

   fSuccess = WriteFile( 
      hPipe,                  // pipe handle 
      lpvMessage,             // message 
      cbToWrite,              // message length 
      &cbWritten,             // bytes written 
      NULL);                  // not overlapped 

   if ( ! fSuccess) 
   {
      return false;
   }else return true;
}

修改,更多信息:

目标是在管道上发送包含9 Bytes long PipeHeader Struct的消息,然后发送包含winsock send()数据的另一条消息(buf变量),读取C#应用程序上的管道并解析第一条消息以获取下一条传入消息的DataSize(datasize var PipeHeader),然后使用datasize再次读取管道以获取send()缓冲区。 我认为那是以这种方式工作的。我不太了解Pipes是如何工作的。

无论如何,MAIN的目标是将send()缓冲区从C ++ Dll发送到C#应用程序。

更新 似乎我必须首先以某种方式序列化PipeHeader结构,以便我可以使用C#代码中的RawDeserializeEx()对其进行反序列化。 我试着这样做:

char buffer[sizeof(ph)];
memcpy(buffer, &ph, sizeof(ph)); 

问题在于,在C ++中sizeof(ph)sizeof(buffer)返回12个字节。 而在C#中,相同Struct的非托管大小(Marshal.SizeOf())将返回9个字节。

更新2: 通过更改结构填充来解决尺寸差异。 但我仍然没有在C#中获得正确的价值观。 代码已更新。

3 个答案:

答案 0 :(得分:9)

我的第一篇文章,请对我说: - )

BOOL SendPipeMessage(LPCVOID lpvMessage){
    // Send a message to the pipe server. 
   cbToWrite = sizeof(lpvMessage);

sizeof使用不正确。它将返回size of type LPCVOID而不是您计划的缓冲区大小。当你想发送9个字节时,你正在发送4个字节(在x86上)。

在我看来,您需要为SendPipeMessage()

提供新参数
BOOL SendPipeMessage(LPCVOID lpvMessage, DWORD cbToWrite)

您可以在第一次通话中使用sizeof。在第二次调用时,您可以使用len作为参数。

if(SendPipeMessage((LPCVOID)&ph, sizeof(ph))){
    if(SendPipeMessage(buf, len)){

答案 1 :(得分:7)

我没有检查整个代码,因为我现在没时间了。但是我注意到了:

  • 我很确定你不能指望{+ 1}在C ++中总是4个字节。如果尺寸很重要,则int不是最佳选择。
  • 您在C#代码中忘记了int成员[MarshalAs(UnmanagedType.I4)]。属性仅适用于下一个项目,而不适用于以下所有项目。
  • datasize替换为SendPipeMessage(buffer, sizeof(buffer))以获得所需的结果。不需要SendPipeMessage((char*)&ph, sizeof(ph))到那里的char数组和M.L.提到memcpy不是你想要的。
  • sizeof(buffer)替换为sizeof(buf)。你已经有了长度,为什么要使用sizeof? len再次不是你想要的。
  • 您可以将结尾的返回代码简化为sizeof(buf)。不需要if-else那里。或者只是return fSuccess;
  • 为什么使用return WriteFile(...);?这又不是你想要的,你已经通过了cbToWrite = sizeof(lpvMessage);,而你应该在ctToWrite的调用中使用WriteFile

答案 2 :(得分:0)

你的问题对我来说并不是特别清楚。我不确定问题是处理管道还是序列化。我在下面提供了完整的C ++管道服务器代码和C#管道客户端代码。我试图保持简单,这对于演示对话非常有用。我使用了struct和反序列化代码并添加了序列化方法。在两个代码示例中都有一个指向MS doc的链接,如果需要,可以轻松地反转客户端服务器关系。服务器和客户端代码是独立的,旨在在不同的进程(两个控制台应用程序)中运行。没有线程或服务器不会处理多个连接。

简单(de /)序列化而言,我建议您使用结构内部的BitConverter类和方法手动转换为字节数组。这为您提供了更好的控制级别,并且更易于调试。如果您有复杂的结构代表我建议您查看Google的#protobuf&#39;。当然是YMMV。

C ++管道服务器代码(Windows)

#include <stdio.h>
#include <stdint.h>
#include <winsock.h>
#include <string>

// see https://msdn.microsoft.com/en-us/library/bb546085(v=vs.110).aspx?cs-save-lang=1&cs-lang=csharp#code-snippet-1
// for information on creating pipe clients and servers in c++

using namespace std;

#pragma pack(1)
typedef struct {
    int8_t command;
    int32_t sockid;
    int32_t datasize;
    } PipeHeader;
#pragma pack()

int wmain( int argc, wchar_t* argv[] ) {
    auto pipename = "\\\\.\\pipe\\pipey"; // can be any name, but must start '\\.\pipe\'
    printf("Create pipe '%s'\r\n", pipename);
    auto pipeHandle = CreateNamedPipe(pipename, 
                                      PIPE_ACCESS_DUPLEX, 
                                      PIPE_TYPE_MESSAGE | PIPE_WAIT,
                                      1,  // # instances
                                      64, // out buff
                                      64, // in buff
                                      0,  // timeout, 0 = default of 50ms
                                      NULL); // security attrs
    printf("Waiting for pipe connect\r\n");
    if (ConnectNamedPipe(pipeHandle, NULL)) {
        printf("Pipe connected\r\n");
        PipeHeader hdr;
        DWORD bytesRead;
        int8_t cmd = -1;
        while (cmd != 0) {
            if (ReadFile(pipeHandle, &hdr, sizeof(PipeHeader), &bytesRead, NULL)) {
                // you can check or assert here that bytesRead == sizeof(PipeHeader)
                printf("Read %d bytes from pipe, {command: %d, sockid: %d, datasize: %d}\r\n", 
                       bytesRead, hdr.command, hdr.sockid, hdr.datasize);
                if (hdr.command == 0) { // assume 0 is the exit cmd
                    break;
                    }
                else if (hdr.command == 1) {
                    // do whatever cmd 1 is ...
                    }
                hdr.command = 4;
                hdr.sockid = 101;
                hdr.datasize *= 2;
                if (WriteFile(pipeHandle, &hdr, sizeof(PipeHeader), &bytesRead, NULL)) {
                    printf("Data written to pipe\r\n"); 
                    }
                else {
                    printf("Pipe write failed\r\n");
                    break;
                    }
                }
            else {
                printf("Read error: %d\r\n", GetLastError());
                break; // exit
                }
            }
        if (DisconnectNamedPipe(pipeHandle) == FALSE) {
            printf("Disconnect error: %d\r\n", GetLastError());
            }
        }
    else {
        printf("Pipe connect failed\r\n");
        }
    CloseHandle(pipeHandle);
    printf("Exiting program\r\n");
    return 0;
    }

C#客户端管道代码(Windows)

using System;
using System.Runtime.InteropServices;
using System.IO.Pipes;

namespace Pipes {
    // see https://msdn.microsoft.com/en-us/library/bb546085(v=vs.110).aspx?cs-save-lang=1&cs-lang=csharp#code-snippet-1
    // for information on creating pipe clients and servers in c#
    class Program {
        [StructLayout(LayoutKind.Sequential, Pack = 1, CharSet = CharSet.Ansi)]
        public struct PipeHeader {
            [MarshalAs(UnmanagedType.I1)]
            public byte command;
            [MarshalAs(UnmanagedType.I4)]
            public Int32 sockid;
            public Int32 datasize;
        }

        static void Main(string[] args) {
            var pipename = "pipey";
            var pipeClient = new NamedPipeClientStream(pipename);
            Console.WriteLine("Connecting to server pipe '{0}'", pipename);
            pipeClient.Connect();
            var hdr = new PipeHeader();
            var hdrSize = Marshal.SizeOf(hdr);
            hdr.command = 1;
            hdr.sockid = 1912;
            hdr.datasize = 32;
            var buf = Serialize(hdr);
            Console.WriteLine("Writing to server pipe");
            pipeClient.Write(buf, 0, hdrSize);
            pipeClient.Read(buf, 0, hdrSize);
            hdr = (PipeHeader) Deserialize(buf, hdr.GetType());
            Console.WriteLine("Pipe read {{ command: {0}, sockid: {1}, datasize: {2} }}",
                               hdr.command, hdr.sockid, hdr.datasize);
            hdr.command = 0; // tell server to disconnect
            buf = Serialize(hdr);
            Console.WriteLine("Sending disconnect");
            pipeClient.Write(buf, 0, hdrSize);
            pipeClient.Close();
            Console.WriteLine("Pipe closed");
            }

        public static object Deserialize(byte[] rawdatas, Type anytype) {
            int rawsize = Marshal.SizeOf(anytype);
            if (rawsize > rawdatas.Length)
                return null;
            GCHandle handle = GCHandle.Alloc(rawdatas, GCHandleType.Pinned);
            IntPtr buffer = handle.AddrOfPinnedObject();
            object retobj = Marshal.PtrToStructure(buffer, anytype);
            handle.Free();
            return retobj;
            }

        public static byte[] Serialize(object obj) {
            int rawsize = Marshal.SizeOf(obj);
            var rv = new byte[rawsize];
            IntPtr ptr = Marshal.AllocHGlobal(rawsize);
            Marshal.StructureToPtr(obj, ptr, true);
            Marshal.Copy(ptr, rv, 0, rawsize);
            Marshal.FreeHGlobal(ptr);
            return rv;
            }
        }
    }

编译两组代码。启动服务器代码,然后启动客户端。输出有点微小,但显示数据交换,服务器在发送数据之前修改数据(显示双面数据交换)。除了作为一个例子之外,没有其他真正的目的。

希望这个例子能够提供解决问题所需的基本信息。

相关问题