原文链接:https://www.x86matthew.com/view_post?id=eventpipe
这篇文章展示了我为隐蔽的进程间通信提出的一个想法。

当系统上的两个进程需要相互通信时,它们通常会使用命名管道或共享内存等常用方法。然而,这些都相当容易检测和监控——我决定想出一种使用 Windows 事件对象的新方法。
事件句柄只有两种状态——开和关。它们通常用于一般的多线程同步,通常不需要任何进一步的调查。
我的基本理论如下: 1. 使用CreateEvent
在接收程序中创建 256 个事件对象。2.在接收程序中对上述所有事件句柄 调用WaitForMultipleObjects 。
3. 发送方程序使用OpenEvent打开接收方程序创建的所有 256 个事件对象。
4、在发送方程序中调用SetEvent,触发每个字节发送的对应事件(0-255)。
上面的理论有几个问题。正如大多数 Windows 开发人员已经知道的那样,WaitForMultipleObjects函数最多只允许 64 个句柄。另一个问题是同步——我们需要知道最初触发事件的确切顺序。
要解决上述问题,需要进行以下更改:
1. 我们不使用 256 个事件对象(每个字节一个),而是将每个字节分成两个事件——我们将在 base-16 中称其为“高”和“低”。例如,当发送 0x95 时,我们会触发 0x9“高”事件和 0x5“低”事件。这意味着我们只需要在任何时候使用WaitForMultipleObjects等待 16 个事件句柄,而不是全部 256 个。作为副作用,这也意味着事件对象的总数减少到 32 个(16 高 + 16 低) .
2. 为了解决同步问题,我们将创建一个称为“确认”标志的额外事件句柄。处理完每个字节后,将在接收程序中触发此事件。发送方程序只会在确认标志设置后发送下一个字节。
我创建了一个函数库来演示这个概念:
#include <stdio.h>
#include <windows.h>
#define EVENT_DATA_HIGH_COUNT 16
#define EVENT_DATA_LOW_COUNT 16
struct EventPipeObjectStruct
{
HANDLE hEventDataHigh[EVENT_DATA_HIGH_COUNT];
HANDLE hEventDataLow[EVENT_DATA_LOW_COUNT];
HANDLE hEventAck;
};
DWORD EventPipe_GetHandles(char *pName, DWORD dwCreate, EventPipeObjectStruct *pEventPipeObject)
{
char szEventName[512];
EventPipeObjectStruct EventPipeObject;
// create "high" data event handles
for(DWORD i = 0; i < EVENT_DATA_HIGH_COUNT; i++)
{
// set current event name
memset(szEventName, 0, sizeof(szEventName));
_snprintf(szEventName, sizeof(szEventName) - 1, "EventPipe_%s_H%u", pName, i);
if(dwCreate == 0)
{
// open existing object
EventPipeObject.hEventDataHigh[i] = OpenEvent(EVENT_ALL_ACCESS, 0, szEventName);
}
else
{
// create new object
EventPipeObject.hEventDataHigh[i] = CreateEvent(NULL, 0, 0, szEventName);
}
// check for errors
if(EventPipeObject.hEventDataHigh[i] == NULL)
{
return 1;
}
}
// create "low" data event handles
for(i = 0; i < EVENT_DATA_LOW_COUNT; i++)
{
// set current event name
memset(szEventName, 0, sizeof(szEventName));
_snprintf(szEventName, sizeof(szEventName) - 1, "EventPipe_%s_L%u", pName, i);
if(dwCreate == 0)
{
// open existing object
EventPipeObject.hEventDataLow[i] = OpenEvent(EVENT_ALL_ACCESS, 0, szEventName);
}
else
{
// create new object
EventPipeObject.hEventDataLow[i] = CreateEvent(NULL, 0, 0, szEventName);
}
// check for errors
if(EventPipeObject.hEventDataLow[i] == NULL)
{
return 1;
}
}
// create acknowledgement event
memset(szEventName, 0, sizeof(szEventName));
_snprintf(szEventName, sizeof(szEventName) - 1, "EventPipe_%s_A", pName);
if(dwCreate == 0)
{
// open existing object
EventPipeObject.hEventAck = OpenEvent(EVENT_ALL_ACCESS, 0, szEventName);
}
else
{
// create new object
EventPipeObject.hEventAck = CreateEvent(NULL, 0, 0, szEventName);
}
if(EventPipeObject.hEventAck == NULL)
{
return 1;
}
// store data
memcpy((void*)pEventPipeObject, (void*)&EventPipeObject, sizeof(EventPipeObject));
return 0;
}
DWORD EventPipe_CreateReceiver(char *pName, EventPipeObjectStruct *pEventPipeObject)
{
// create event handles
if(EventPipe_GetHandles(pName, 1, pEventPipeObject) != 0)
{
return 1;
}
return 0;
}
DWORD EventPipe_Open(char *pName, EventPipeObjectStruct *pEventPipeObject)
{
// open event handles
if(EventPipe_GetHandles(pName, 0, pEventPipeObject) != 0)
{
return 1;
}
return 0;
}
DWORD EventPipe_RecvRawByte(EventPipeObjectStruct *pEventPipeObject, BYTE *pByte)
{
DWORD dwEventDataHighValue = 0;
DWORD dwEventDataLowValue = 0;
BYTE bByte = 0;
// wait for "high" data value
dwEventDataHighValue = WaitForMultipleObjects(EVENT_DATA_HIGH_COUNT, pEventPipeObject->hEventDataHigh, 0, INFINITE);
if(dwEventDataHighValue >= EVENT_DATA_HIGH_COUNT)
{
return 1;
}
// wait for "low" data value
dwEventDataLowValue = WaitForMultipleObjects(EVENT_DATA_LOW_COUNT, pEventPipeObject->hEventDataLow, 0, INFINITE);
if(dwEventDataLowValue >= EVENT_DATA_LOW_COUNT)
{
return 1;
}
// calculate byte value from high/low value
bByte = (BYTE)((dwEventDataHighValue * EVENT_DATA_LOW_COUNT) + dwEventDataLowValue);
// set acknowledgement event
if(SetEvent(pEventPipeObject->hEventAck) == 0)
{
return 1;
}
// store byte value
*pByte = bByte;
return 0;
}
DWORD EventPipe_RecvRawData(EventPipeObjectStruct *pEventPipeObject, BYTE *pData, DWORD dwLength)
{
BYTE *pCurrPtr = NULL;
// receive all requested bytes
pCurrPtr = pData;
for(DWORD i = 0; i < dwLength; i++)
{
// get current byte
if(EventPipe_RecvRawByte(pEventPipeObject, pCurrPtr) != 0)
{
return 1;
}
// increase ptr
pCurrPtr++;
}
return 0;
}
DWORD EventPipe_RecvDataBlock(EventPipeObjectStruct *pEventPipeObject, BYTE *pData, DWORD dwMaxLength, DWORD *pdwDataLength)
{
DWORD dwBlockLength = 0;
// get block length
if(EventPipe_RecvRawData(pEventPipeObject, (BYTE*)&dwBlockLength, sizeof(DWORD)) != 0)
{
return 1;
}
// validate length
if(dwBlockLength > dwMaxLength)
{
return 1;
}
// get block data
if(EventPipe_RecvRawData(pEventPipeObject, pData, dwBlockLength) != 0)
{
return 1;
}
if(pdwDataLength != NULL)
{
// store block length
*pdwDataLength = dwBlockLength;
}
return 0;
}
DWORD EventPipe_SendRawByte(EventPipeObjectStruct *pEventPipeObject, BYTE bByte)
{
// set "high" data event
if(SetEvent(pEventPipeObject->hEventDataHigh[bByte / EVENT_DATA_LOW_COUNT]) == 0)
{
return 1;
}
// set "low" data event
if(SetEvent(pEventPipeObject->hEventDataLow[bByte % EVENT_DATA_LOW_COUNT]) == 0)
{
return 1;
}
// wait for acknowledgement
if(WaitForSingleObject(pEventPipeObject->hEventAck, INFINITE) != 0)
{
return 1;
}
return 0;
}
DWORD EventPipe_SendRawData(EventPipeObjectStruct *pEventPipeObject, BYTE *pData, DWORD dwLength)
{
BYTE *pCurrPtr = NULL;
// send all requested bytes
pCurrPtr = pData;
for(DWORD i = 0; i < dwLength; i++)
{
// send current byte
if(EventPipe_SendRawByte(pEventPipeObject, *pCurrPtr) != 0)
{
return 1;
}
// increase ptr
pCurrPtr++;
}
return 0;
}
DWORD EventPipe_SendDataBlock(EventPipeObjectStruct *pEventPipeObject, BYTE *pData, DWORD dwLength)
{
// send block length
if(EventPipe_SendRawData(pEventPipeObject, (BYTE*)&dwLength, sizeof(DWORD)) != 0)
{
return 1;
}
// send block data
if(EventPipe_SendRawData(pEventPipeObject, pData, dwLength) != 0)
{
return 1;
}
return 0;
}
接收程序:
int main()
{
EventPipeObjectStruct EventPipeObject;
char szRecvString[512];
DWORD dwDataLength = 0;
memset((void*)&EventPipeObject, 0, sizeof(EventPipeObject));
if(EventPipe_CreateReceiver("x86matthew", &EventPipeObject) != 0)
{
return 1;
}
for(;;)
{
memset(szRecvString, 0, sizeof(szRecvString));
if(EventPipe_RecvDataBlock(&EventPipeObject, (BYTE*)szRecvString, sizeof(szRecvString) - 1, &dwDataLength) != 0)
{
return 1;
}
printf("Received %u bytes: '%s'\n", dwDataLength, szRecvString);
}
return 0;
}
发件人程序:
int main(int argc, char *argv[])
{
EventPipeObjectStruct EventPipeObject;
if(argc != 2)
{
return 1;
}
memset((void*)&EventPipeObject, 0, sizeof(EventPipeObject));
if(EventPipe_Open("x86matthew", &EventPipeObject) != 0)
{
return 1;
}
if(EventPipe_SendDataBlock(&EventPipeObject, (BYTE*)argv[1], strlen(argv[1])) != 0)
{
return 1;
}
return 0;
}
潜在的未来改进:
– 目前只有单向通信 – 添加双工支持。
– 事件对象的命名允许我们在远程进程中打开句柄 – 使它们“匿名”并将句柄复制到远程进程中。