2015-05-06 84 views
15

我試圖扭轉引擎注入進程的DLL,那鉤子winsock send()並通過PipeStream發送數據。使用命名管道的C + +和C#通信

這是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個字節長PipeHeader STRUCT在管的消息,然後發送包含Winsock的另一消息send()數據,(在buf變量),閱讀關於C#應用的管並解析所述第一消息獲得下一個傳入消息的數據尺寸(這是的PipeHeaderdatasize變種),然後,使用datasize再次讀取管道以獲取send()緩衝區。 我認爲那是以這種方式工作的。我不太清楚Pipes是如何工作的。

反正主要目的是從C++ DLL發送send()緩衝到C#應用程序。

更新: 看來,我必須先序列化的方式PipeHeader結構,所以我可以在C#代碼中使用RawDeserializeEx()反序列化。 我試圖這樣做的:

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

的問題是,在C++ sizeof(ph),或sizeof(buffer)返回12個字節。 相反在C#同一結構體的非託管大小(Marshal.SizeOf()),返回9個字節。

更新2: 通過更改結構打包解決了大小差異。 但我仍然沒有在C#中獲得正確的值。 代碼已更新。

+0

請發佈整個C++代碼。有許多變量沒有在你的代碼中聲明,例如'hPipe'或'cbWritten'。什麼是pSend? –

回答

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)){ 
+0

謝謝,我會試試。 sizeof(buf) – Fr0z3n

+1

- 將返回類型指針的大小(通常爲4)。緩衝區長度由len提供。這就是爲什麼你應該嘗試'SendPipeMessage(buf,len)'而不是'SendPipeMessage(buf,sizeof(buf))'。 –

7

沒有檢查整個代碼,因爲我現在沒有時間了。但我注意到:

  • 我敢肯定,你不能指望一個int總是4個字節的C + +。如果尺寸很重要,int不是最佳選擇。
  • 您在C#代碼中忘記了會員datasize[MarshalAs(UnmanagedType.I4)]。屬性僅適用於下一個項目,而不適用於所有下列項目。
  • SendPipeMessage(buffer, sizeof(buffer))替換爲SendPipeMessage((char*)&ph, sizeof(ph))以獲得期望的結果。不需要memcpy那裏的char數組和M.L.提到sizeof(buffer)不是你想要的。
  • sizeof(buf)替換爲len。你已經有了這個長度,爲什麼使用sizeof?而sizeof(buf)再次不是你想要的。
  • 您可以將您的返回碼簡化爲return fSuccess;。不需要if-else那裏。或者只是return WriteFile(...);
  • 爲什麼使用cbToWrite = sizeof(lpvMessage);?這又不是你想要的,你已經通過了ctToWrite,你應該使用WriteFile
+1

int在大多數32和64位平臺上是4字節,特別是在我所知的所有x64平臺中。 – MikeMB

+0

可能是真的,但在尺寸重要的地方使用'int'不是一個好的選擇。我想這裏不會有問題,但我想指出它。 –

+2

你說得對,int並不是這裏的最佳選擇,但是聲明「尤其是對於64位,它不會是真的。」是錯誤的 - 特別是如果你考慮,c#通常用在windows系統上,整數在x86,x64和Itanium上有32字節和64位模式。 – MikeMB

0

你的問題對我來說不是特別清楚。我不確定問題是處理管道還是序列化。我提供了完整的C++管道服務器代碼和C#管道客戶端代碼。我試圖讓事情儘可能簡單,以便展示對話。我使用了你的結構和反序列化代碼,並添加了一個序列化方法。在這兩個代碼示例中都有一個鏈接到MS Doc的鏈接,如果需要,應該可以很容易地反轉客戶端服務器關係。服務器和客戶端代碼是獨立的,設計用於在不同的進程(兩個控制檯應用程序)中運行。沒有線程或服務器將不能處理多個連接。

根據簡單(de /)序列化我建議你在結構體內使用BitConverter類和方法手動轉換字節數組和字節數組。這給你一個更好的控制級別,更容易調試。如果你有複雜的結構來代表,我會建議你看看Google的'protobuf'。當然YMMV。

C++管道服務器代碼(視窗)

#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#客戶端管代碼(視窗)

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; 
      } 
     } 
    } 

編譯兩組代碼。啓動服務器代碼,然後啓動客戶端。輸出有點小,但顯示數據交換,服務器在發回數據之前修改數據(顯示雙面數據交換)。除了作爲一個例子之外,沒有什麼真正的目的。

希望這個例子能夠提供解決問題所需的基本信息。