C++;和使用命名管道的C#通信

C++;和使用命名管道的C#通信,c#,c++,named-pipes,C#,C++,Named Pipes,我正在尝试反向工程一个注入进程的dll,它会钩住winsocksend(),并通过管道流发送数据 这是读取管道流的C代码: [StructLayout(LayoutKind.Sequential, Pack = 1, CharSet = CharSet.Ansi)] public struct PipeHeader { [MarshalAs(UnmanagedType.I1)] public byte command; [M

我正在尝试反向工程一个注入进程的dll,它会钩住winsock
send()
,并通过
管道流发送数据

这是读取管道流的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;
}
问题是,在C++ <代码> siZoof(PH)< /C> >或<代码> siZOF(缓冲区)< /C> >返回12字节。 相反,在C#中,相同结构的非托管大小(
Marshal.SizeOf()
)返回9个字节

更新2: 通过改变结构包装解决了尺寸差异。 但我仍然没有得到正确的C值。
代码更新。

我的第一篇帖子,请对我放松点:-)

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

我认为,您需要为SendPipeMessage()提供新参数

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

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

没有检查全部代码,因为我现在没时间。但我注意到:

    我确信你不能期望C++中的<代码> int /代码>总是4字节。当大小重要时,
    int
    不是最佳选择
  • 您在C#code中忘记了成员
    datasize
    [marshallas(UnmanagedType.I4)]
    。属性仅应用于下一个项目,而不是应用于以下所有项目
  • SendPipeMessage(buffer,sizeof(buffer))
    替换为
    SendPipeMessage((char*)&ph,sizeof(ph))
    ,以获得所需的结果。不需要
    memcpy
    到那里的字符数组,正如M.L.提到的
    sizeof(buffer)
    不是您想要的
  • sizeof(buf)
    替换为
    len
    。您已经有了长度,为什么要使用sizeof?而且
    sizeof(buf)
    也不是您想要的
  • 您可以将末尾的返回代码简化为
    returnfsucces。如果还有别的,就不需要了。或者只是
    返回WriteFile(…)
  • 为什么使用
    cbToWrite=sizeof(lpvMessage)?这也不是您想要的,您已经传递了
    ctToWrite
    ,您应该在调用
    WriteFile
    时使用它

    • 我对你的问题不是特别清楚。我不确定问题是在处理管道还是序列化。我提供了以下完整的C++管道服务器代码和C管道客户端代码。我尽量使事情尽可能简单,以便演示对话。我使用了您的结构和反序列化代码,并添加了一个序列化方法。在这两个代码示例中,都有一个指向MS doc的链接,如果需要的话,可以轻松地反转客户机-服务器关系。服务器和客户端代码是独立的,设计用于在不同的进程(两个控制台应用程序)中运行。没有线程或线程,服务器不会处理多个连接

      就简单(反/)序列化而言,我建议您使用结构中的BitConverter类和方法来手动转换字节数组。这为您提供了更好的控制级别,并且更易于调试。如果你有复杂的结构来表示,我建议你看看谷歌的“protobuf”。当然可以

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

      编译这两组代码。启动服务器代码,然后启动客户端。输出是最小的,但会显示数据交换,服务器会在发送回数据之前修改数据(显示双边数据交换)。除了以身作则,没有其他真正的目的


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

      在大多数32位和64位平台上,尤其是在我所知道的所有x64平台上,int是4字节。可能是真的,但在大小重要的地方使用
      int
      并不是一个好选择。我想这不是问题,但我想指出。你在这里不是最佳的选择是正确的,但是“尤其是64位它不是真的”的说法是错误的——尤其是如果你认为C通常是在Windows系统上使用的,在x86上,整数有4个字节,64位模式下的x64和安腾。sizeof(buf)-将返回类型指针的大小(通常为4)。buf长度以len为单位提供。这就是为什么你应该尝试<代码> SeDIPPIEMSEGE(BUF,LEN)< /COD>而不是<代码> SeDIPiMeMeSeAGE(BUF,sieZof(BUF))< /C>。请张贴整个C++代码。有许多变量未在代码中声明,如
      hPipe
      cbwrited
      。什么是pSend
BOOL SendPipeMessage(LPCVOID lpvMessage){
    // Send a message to the pipe server. 
   cbToWrite = sizeof(lpvMessage);
BOOL SendPipeMessage(LPCVOID lpvMessage, DWORD cbToWrite)
if(SendPipeMessage((LPCVOID)&ph, sizeof(ph))){
    if(SendPipeMessage(buf, len)){
#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;
    }
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;
            }
        }
    }