327 lines
13 KiB
C#
327 lines
13 KiB
C#
|
|
using System.IO;
|
|||
|
|
using System.IO.Pipes;
|
|||
|
|
using System.Runtime.InteropServices;
|
|||
|
|
using System.Security.Principal;
|
|||
|
|
using System.Text.Json;
|
|||
|
|
using System.Threading.Tasks;
|
|||
|
|
using static System.Net.Mime.MediaTypeNames;
|
|||
|
|
//using WinIPC.Utils
|
|||
|
|
|
|||
|
|
using Google.Protobuf;
|
|||
|
|
using System;
|
|||
|
|
using System.IO;
|
|||
|
|
using System.IO.Pipes;
|
|||
|
|
using System.Threading;
|
|||
|
|
using System.Threading.Tasks;
|
|||
|
|
using WinIPC.Models; // Предполагается, что сгенерированные Protobuf-классы находятся здесь
|
|||
|
|
|
|||
|
|
namespace IpcCliClient
|
|||
|
|
{
|
|||
|
|
class Program
|
|||
|
|
{
|
|||
|
|
private static readonly string _pipeName = "Global\\ProcessMonitoringService.UserAgent.Pipe";
|
|||
|
|
private static uint _requestId = 0;
|
|||
|
|
|
|||
|
|
static async Task Main(string[] args)
|
|||
|
|
{
|
|||
|
|
Console.WriteLine("Клиентское приложение для тестирования IPC.");
|
|||
|
|
Console.WriteLine("Введите команду (например, get_windows) или 'exit' для выхода.");
|
|||
|
|
Console.WriteLine("---------------------------------------------");
|
|||
|
|
|
|||
|
|
while (true)
|
|||
|
|
{
|
|||
|
|
Console.Write("> ");
|
|||
|
|
string command = Console.ReadLine()?.Trim() ?? string.Empty;
|
|||
|
|
|
|||
|
|
if (string.IsNullOrWhiteSpace(command))
|
|||
|
|
{
|
|||
|
|
continue;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if (command.Equals("exit", StringComparison.OrdinalIgnoreCase))
|
|||
|
|
{
|
|||
|
|
break;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
try
|
|||
|
|
{
|
|||
|
|
await ExecuteCommand(command);
|
|||
|
|
}
|
|||
|
|
catch (Exception ex)
|
|||
|
|
{
|
|||
|
|
Console.WriteLine($"Ошибка: {ex.Message}");
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
Console.WriteLine("Приложение завершено.");
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/// <summary>
|
|||
|
|
/// Выполняет команду, введенную пользователем.
|
|||
|
|
/// </summary>
|
|||
|
|
private static async Task ExecuteCommand(string command)
|
|||
|
|
{
|
|||
|
|
Request request = new Request();
|
|||
|
|
string responseMessage = string.Empty;
|
|||
|
|
bool handled = true;
|
|||
|
|
|
|||
|
|
switch (command.ToLowerInvariant())
|
|||
|
|
{
|
|||
|
|
case "get_windows":
|
|||
|
|
request.Id = ++_requestId;
|
|||
|
|
request.Type = CommandType.GetWindowsInfo;
|
|||
|
|
Console.WriteLine("Отправка запроса на получение списка окон...");
|
|||
|
|
responseMessage = "Список окон:";
|
|||
|
|
break;
|
|||
|
|
|
|||
|
|
// TODO: Добавить другие команды (get_screenshot, get_pixel_color, input_action)
|
|||
|
|
// как только будут определены их структуры запросов/ответов.
|
|||
|
|
|
|||
|
|
default:
|
|||
|
|
Console.WriteLine($"Неизвестная команда: '{command}'");
|
|||
|
|
handled = false;
|
|||
|
|
break;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if (handled)
|
|||
|
|
{
|
|||
|
|
await SendAndReceive(request, responseMessage);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/// <summary>
|
|||
|
|
/// Отправляет запрос и получает ответ от сервера.
|
|||
|
|
/// </summary>
|
|||
|
|
private static async Task SendAndReceive(Request request, string successMessage)
|
|||
|
|
{
|
|||
|
|
try
|
|||
|
|
{
|
|||
|
|
using (var pipeClient = new NamedPipeClientStream(".", _pipeName, PipeDirection.InOut))
|
|||
|
|
{
|
|||
|
|
Console.WriteLine("Подключение к IPC серверу...");
|
|||
|
|
await pipeClient.ConnectAsync(5000); // Таймаут 5 секунд
|
|||
|
|
Console.WriteLine("Подключено.");
|
|||
|
|
|
|||
|
|
// Сериализация и отправка запроса
|
|||
|
|
byte[] requestBytes = request.ToByteArray();
|
|||
|
|
await WriteMessageAsync(pipeClient, requestBytes, CancellationToken.None);
|
|||
|
|
|
|||
|
|
// Чтение ответа
|
|||
|
|
byte[] responseBytes = await ReadMessageAsync(pipeClient, CancellationToken.None);
|
|||
|
|
var response = Response.Parser.ParseFrom(responseBytes);
|
|||
|
|
|
|||
|
|
// Обработка ответа
|
|||
|
|
if (response.Success)
|
|||
|
|
{
|
|||
|
|
Console.WriteLine($"Успешно: {successMessage}");
|
|||
|
|
if (response.Type == CommandType.GetWindowsInfo && response.Payload != null)
|
|||
|
|
{
|
|||
|
|
var windowsResponse = WindowsResponse.Parser.ParseFrom(response.Payload);
|
|||
|
|
Console.WriteLine($"Найдено {windowsResponse.Data.Count} окон:");
|
|||
|
|
foreach (var window in windowsResponse.Data)
|
|||
|
|
{
|
|||
|
|
Console.WriteLine($" - HWND: {window.Hwnd}, PID: {window.Pid}, Title: '{window.Title}'");
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
else
|
|||
|
|
{
|
|||
|
|
Console.WriteLine($"Получен успешный ответ. Сообщение: {response.Message}");
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
else
|
|||
|
|
{
|
|||
|
|
Console.WriteLine($"Ошибка ответа: {response.Message}");
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
catch (TimeoutException)
|
|||
|
|
{
|
|||
|
|
Console.WriteLine("Ошибка: Таймаут подключения к серверу.");
|
|||
|
|
}
|
|||
|
|
catch (Exception ex)
|
|||
|
|
{
|
|||
|
|
Console.WriteLine($"Ошибка при взаимодействии с сервером: {ex.Message}");
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/// <summary>
|
|||
|
|
/// Читает сообщение из pipe.
|
|||
|
|
/// </summary>
|
|||
|
|
private static async Task<byte[]> ReadMessageAsync(PipeStream pipe, CancellationToken cTok)
|
|||
|
|
{
|
|||
|
|
// Чтение префикса длины (4 байта)
|
|||
|
|
byte[] lengthBytes = new byte[4];
|
|||
|
|
int bytesRead = await pipe.ReadAsync(lengthBytes, 0, 4, cTok);
|
|||
|
|
if (bytesRead < 4)
|
|||
|
|
{
|
|||
|
|
throw new EndOfStreamException("Поток закрыт или достигнут конец потока при чтении длины сообщения.");
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
int messageLength = BitConverter.ToInt32(lengthBytes, 0);
|
|||
|
|
|
|||
|
|
if (messageLength <= 0)
|
|||
|
|
{
|
|||
|
|
throw new InvalidDataException($"Получена некорректная длина сообщения: {messageLength}.");
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Чтение самого сообщения
|
|||
|
|
byte[] messageBytes = new byte[messageLength];
|
|||
|
|
bytesRead = 0;
|
|||
|
|
while (bytesRead < messageLength)
|
|||
|
|
{
|
|||
|
|
int read = await pipe.ReadAsync(messageBytes, bytesRead, messageLength - bytesRead, cTok);
|
|||
|
|
if (read == 0)
|
|||
|
|
{
|
|||
|
|
throw new EndOfStreamException($"Поток закрыт или достигнут конец потока при чтении полезной нагрузки. Ожидалось {messageLength} байт, прочитано {bytesRead}.");
|
|||
|
|
}
|
|||
|
|
bytesRead += read;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return messageBytes;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/// <summary>
|
|||
|
|
/// Записывает сообщение в pipe.
|
|||
|
|
/// </summary>
|
|||
|
|
private static async Task WriteMessageAsync(PipeStream pipe, byte[] messageBytes, CancellationToken cTok)
|
|||
|
|
{
|
|||
|
|
byte[] lengthPrefix = BitConverter.GetBytes(messageBytes.Length);
|
|||
|
|
|
|||
|
|
await pipe.WriteAsync(lengthPrefix, 0, lengthPrefix.Length, cTok);
|
|||
|
|
await pipe.WriteAsync(messageBytes, 0, messageBytes.Length, cTok);
|
|||
|
|
await pipe.FlushAsync(cTok);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/*
|
|||
|
|
public class Datagram
|
|||
|
|
{
|
|||
|
|
public IPCWindowInfo Info;
|
|||
|
|
public string Title { get; set; } = String.Empty;
|
|||
|
|
public int Hwnd { get => Info.Hwnd; }
|
|||
|
|
public uint Pid { get => Info.Pid; }
|
|||
|
|
public uint ThreadId { get => Info.ThreadId; }
|
|||
|
|
public bool IsActive { get => Info.IsActive; }
|
|||
|
|
public int CursorX { get => Info.CursorX; }
|
|||
|
|
public int CursorY { get => Info.CursorY; }
|
|||
|
|
public int ContentWidth { get => Info.ContentWidth; }
|
|||
|
|
public int ContentHeight { get => Info.ContentHeight; }
|
|||
|
|
public int WindowX { get => Info.WindowX; }
|
|||
|
|
public int WindowY { get => Info.WindowY; }
|
|||
|
|
public int WindowWidth { get => Info.WindowWidth; }
|
|||
|
|
public int WindowHeight { get => Info.WindowHeight; }
|
|||
|
|
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
public class Program
|
|||
|
|
{
|
|||
|
|
public static async Task Main(string[] args)
|
|||
|
|
{
|
|||
|
|
Console.WriteLine("IPC Client started. Press any key to send a 'GetWindows' request...");
|
|||
|
|
Console.ReadKey();
|
|||
|
|
|
|||
|
|
await SendGetWindowsRequest();
|
|||
|
|
|
|||
|
|
Console.WriteLine("\nPress any key to exit.");
|
|||
|
|
Console.ReadKey();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
private static async Task SendGetWindowsRequest()
|
|||
|
|
{
|
|||
|
|
try
|
|||
|
|
{
|
|||
|
|
using (var pipe = new NamedPipeClientStream(
|
|||
|
|
".", // Сервер находится на локальной машине
|
|||
|
|
IPC.PipeName,
|
|||
|
|
PipeDirection.InOut,
|
|||
|
|
PipeOptions.Asynchronous,
|
|||
|
|
TokenImpersonationLevel.None,
|
|||
|
|
HandleInheritability.None))
|
|||
|
|
{
|
|||
|
|
Console.WriteLine($"Connecting to pipe: {IPC.PipeName}...");
|
|||
|
|
await pipe.ConnectAsync(5000); // Таймаут 5 секунд
|
|||
|
|
Console.WriteLine("Connected to pipe!");
|
|||
|
|
|
|||
|
|
IPCPayload payload;
|
|||
|
|
|
|||
|
|
#if WINDOWS
|
|||
|
|
if (IPC.SerializeIPC(new IPCHeader { Type = IPCType.ListWindows }, 0, out var data) > 0)
|
|||
|
|
{
|
|||
|
|
Console.WriteLine($"Writing to pipe...");
|
|||
|
|
if (await IPC.WriteMessageAsync(pipe, data, CancellationToken.None))
|
|||
|
|
{
|
|||
|
|
Console.WriteLine($"Writing successful");
|
|||
|
|
Console.WriteLine($"Reading from pipe...");
|
|||
|
|
if ((payload = await IPC.ReadMessageAsync(pipe, CancellationToken.None)).Header.Type == (IPCType.Response | IPCType.ListWindows))
|
|||
|
|
{
|
|||
|
|
Console.WriteLine($"Reading successful");
|
|||
|
|
var s = (IEnumerable<IPCWindowInfoPayload>?)payload.Data;
|
|||
|
|
var d = new List<Datagram>();
|
|||
|
|
|
|||
|
|
if (s != null) {
|
|||
|
|
foreach (var p in s)
|
|||
|
|
{
|
|||
|
|
d.Add(new Datagram { Info = p.Info, Title = p.Title });
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
Console.WriteLine($"Received response: {JsonSerializer.Serialize(d)}");
|
|||
|
|
}
|
|||
|
|
else
|
|||
|
|
{
|
|||
|
|
Console.WriteLine($"Not received! ID: {payload.Header.ID}, Code: {payload.Header.Code}, Type: {payload.Header.Type}");
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
#endif
|
|||
|
|
#if IMAGE
|
|||
|
|
if (IPC.SerializeIPC(new IPCHeader { Type = IPCType.GetScreenshot }, 0, out var d1) > 0 && IPC.SerializeIPC(new IPCScreenshotRequest { Hwnd = 66982 }, 0, out var d2) > 0)
|
|||
|
|
{
|
|||
|
|
byte[] data = new byte[d1.Length + d2.Length];
|
|||
|
|
|
|||
|
|
Buffer.BlockCopy(d1, 0, data, 0, d1.Length);
|
|||
|
|
Buffer.BlockCopy(d2, 0, data, d1.Length, d2.Length);
|
|||
|
|
|
|||
|
|
Console.WriteLine($"Writing to pipe...");
|
|||
|
|
if (await IPC.WriteMessageAsync(pipe, data, CancellationToken.None))
|
|||
|
|
{
|
|||
|
|
Console.WriteLine($"Writing successful");
|
|||
|
|
Console.WriteLine($"Reading from pipe...");
|
|||
|
|
if ((payload = await IPC.ReadMessageAsync(pipe, CancellationToken.None)).Header.Type == (IPCType.Response | IPCType.GetScreenshot))
|
|||
|
|
{
|
|||
|
|
Console.WriteLine($"Reading successful");
|
|||
|
|
var s = (IPCScreenshotPayload?)payload.Data;
|
|||
|
|
var d = new List<Datagram>();
|
|||
|
|
|
|||
|
|
if (s != null)
|
|||
|
|
{
|
|||
|
|
using (FileStream fs = new FileStream("C:\\Users\\lirent\\test.png", FileMode.Create, FileAccess.Write, FileShare.None))
|
|||
|
|
{
|
|||
|
|
await fs.WriteAsync(s.Value.Image, 0, s.Value.Image.Length);
|
|||
|
|
}
|
|||
|
|
Console.WriteLine("See C:\\Users\\lirent\\test.png");
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
else
|
|||
|
|
{
|
|||
|
|
Console.WriteLine($"Not received! ID: {payload.Header.ID}, Code: {payload.Header.Code}, Type: {payload.Header.Type}");
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
#endif
|
|||
|
|
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
catch (TimeoutException)
|
|||
|
|
{
|
|||
|
|
Console.WriteLine("Connection to pipe timed out. Ensure UserSessionAgent is running.");
|
|||
|
|
}
|
|||
|
|
catch (Exception ex)
|
|||
|
|
{
|
|||
|
|
Console.WriteLine($"An error occurred: {ex.Message}");
|
|||
|
|
Console.WriteLine(ex.ToString());
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
*/
|