我试图反转引入注入流程的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#中获得正确的价值观。 代码已更新。
答案 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)
我没有检查整个代码,因为我现在没时间了。但是我注意到了:
int
不是最佳选择。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;
}
}
}
编译两组代码。启动服务器代码,然后启动客户端。输出有点微小,但显示数据交换,服务器在发送数据之前修改数据(显示双面数据交换)。除了作为一个例子之外,没有其他真正的目的。
希望这个例子能够提供解决问题所需的基本信息。