mirror of
https://github.com/WerWolv/ImHex.git
synced 2026-03-28 07:47:03 -05:00
feat: Added simple UDP Data Provider
This commit is contained in:
@@ -41,6 +41,7 @@ set(LIBIMHEX_SOURCES
|
||||
source/helpers/semantic_version.cpp
|
||||
source/helpers/keys.cpp
|
||||
source/helpers/freetype.cpp
|
||||
source/helpers/udp_server.cpp
|
||||
|
||||
source/test/tests.cpp
|
||||
|
||||
|
||||
58
lib/libimhex/include/hex/helpers/udp_server.hpp
Normal file
58
lib/libimhex/include/hex/helpers/udp_server.hpp
Normal file
@@ -0,0 +1,58 @@
|
||||
#pragma once
|
||||
|
||||
#include <hex.hpp>
|
||||
|
||||
#include <functional>
|
||||
#include <thread>
|
||||
#include <atomic>
|
||||
|
||||
namespace hex {
|
||||
|
||||
class UDPServer {
|
||||
public:
|
||||
using Callback = std::function<void(std::span<const u8> data)>;
|
||||
UDPServer() = default;
|
||||
UDPServer(u16 port, Callback callback);
|
||||
~UDPServer();
|
||||
|
||||
UDPServer(const UDPServer&) = delete;
|
||||
UDPServer& operator=(const UDPServer&) = delete;
|
||||
UDPServer(UDPServer &&other) noexcept {
|
||||
m_port = other.m_port;
|
||||
m_callback = std::move(other.m_callback);
|
||||
m_thread = std::move(other.m_thread);
|
||||
m_running = other.m_running.load();
|
||||
other.m_running = false;
|
||||
m_socketFd = other.m_socketFd;
|
||||
other.m_socketFd = -1;
|
||||
}
|
||||
UDPServer& operator=(UDPServer &&other) noexcept {
|
||||
if (this != &other) {
|
||||
m_port = other.m_port;
|
||||
m_callback = std::move(other.m_callback);
|
||||
m_thread = std::move(other.m_thread);
|
||||
m_running = other.m_running.load();
|
||||
other.m_running = false;
|
||||
m_socketFd = other.m_socketFd;
|
||||
other.m_socketFd = -1;
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
void start();
|
||||
void stop();
|
||||
|
||||
[[nodiscard]] u16 getPort() const { return m_port; }
|
||||
|
||||
private:
|
||||
void run();
|
||||
|
||||
u16 m_port;
|
||||
Callback m_callback;
|
||||
std::thread m_thread;
|
||||
std::atomic<bool> m_running;
|
||||
int m_socketFd;
|
||||
};
|
||||
|
||||
}
|
||||
69
lib/libimhex/source/helpers/udp_server.cpp
Normal file
69
lib/libimhex/source/helpers/udp_server.cpp
Normal file
@@ -0,0 +1,69 @@
|
||||
#include <hex/helpers/udp_server.hpp>
|
||||
|
||||
#if defined(OS_WINDOWS)
|
||||
#include <winsock2.h>
|
||||
#include <ws2tcpip.h>
|
||||
using socklen_t = int;
|
||||
#else
|
||||
#include <arpa/inet.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/socket.h>
|
||||
#include <netinet/in.h>
|
||||
#endif
|
||||
|
||||
namespace hex {
|
||||
|
||||
UDPServer::UDPServer(u16 port, Callback callback)
|
||||
: m_port(port), m_callback(std::move(callback)), m_running(false), m_socketFd(-1) {
|
||||
}
|
||||
|
||||
UDPServer::~UDPServer() {
|
||||
stop();
|
||||
}
|
||||
|
||||
void UDPServer::start() {
|
||||
m_running = true;
|
||||
m_thread = std::thread(&UDPServer::run, this);
|
||||
}
|
||||
|
||||
void UDPServer::stop() {
|
||||
m_running = false;
|
||||
if (m_thread.joinable()) m_thread.join();
|
||||
|
||||
if (m_socketFd >= 0) {
|
||||
#if defined(OS_WINDOWS)
|
||||
::closesocket(m_socketFd);
|
||||
#else
|
||||
::close(sockfd_);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
void UDPServer::run() {
|
||||
m_socketFd = ::socket(AF_INET, SOCK_DGRAM, 0);
|
||||
if (m_socketFd < 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
sockaddr_in addr = { };
|
||||
addr.sin_family = AF_INET;
|
||||
addr.sin_addr.s_addr = INADDR_ANY;
|
||||
addr.sin_port = htons(m_port);
|
||||
|
||||
if (bind(m_socketFd, reinterpret_cast<sockaddr*>(&addr), sizeof(addr)) < 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::vector<u8> buffer(64 * 1024, 0x00);
|
||||
while (m_running) {
|
||||
sockaddr_in client{};
|
||||
socklen_t len = sizeof(client);
|
||||
const auto bytes = ::recvfrom(m_socketFd, reinterpret_cast<char*>(buffer.data()), buffer.size(), 0, reinterpret_cast<sockaddr*>(&client), &len);
|
||||
|
||||
if (bytes > 0) {
|
||||
buffer[bytes] = '\0';
|
||||
m_callback({ buffer.data(), buffer.data() + bytes });
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -68,6 +68,7 @@ add_imhex_plugin(
|
||||
source/content/providers/process_memory_provider.cpp
|
||||
source/content/providers/base64_provider.cpp
|
||||
source/content/providers/view_provider.cpp
|
||||
source/content/providers/udp_provider.cpp
|
||||
|
||||
source/content/tools/ascii_table.cpp
|
||||
source/content/tools/base_converter.cpp
|
||||
|
||||
62
plugins/builtin/include/content/providers/udp_provider.hpp
Normal file
62
plugins/builtin/include/content/providers/udp_provider.hpp
Normal file
@@ -0,0 +1,62 @@
|
||||
#pragma once
|
||||
|
||||
#include <hex/helpers/fmt.hpp>
|
||||
#include <hex/providers/provider.hpp>
|
||||
#include <hex/helpers/udp_server.hpp>
|
||||
#include <nlohmann/json.hpp>
|
||||
#include <mutex>
|
||||
|
||||
namespace hex::plugin::builtin {
|
||||
|
||||
class UDPProvider : public hex::prv::Provider {
|
||||
public:
|
||||
UDPProvider() = default;
|
||||
~UDPProvider() override = default;
|
||||
|
||||
[[nodiscard]] bool isAvailable() const override { return true; }
|
||||
[[nodiscard]] bool isReadable() const override { return true; }
|
||||
[[nodiscard]] bool isWritable() const override { return false; }
|
||||
[[nodiscard]] bool isResizable() const override { return false; }
|
||||
[[nodiscard]] bool isSavable() const override { return true; }
|
||||
|
||||
void readRaw(u64 offset, void *buffer, size_t size) override;
|
||||
void writeRaw(u64 offset, const void *buffer, size_t size) override;
|
||||
|
||||
[[nodiscard]] u64 getActualSize() const override;
|
||||
|
||||
|
||||
[[nodiscard]] bool hasLoadInterface() const override { return true; }
|
||||
[[nodiscard]] bool drawLoadInterface() override;
|
||||
bool hasInterface() const override { return true; }
|
||||
void drawInterface() override;
|
||||
|
||||
[[nodiscard]] bool open() override;
|
||||
void close() override;
|
||||
|
||||
void loadSettings(const nlohmann::json &) override;
|
||||
[[nodiscard]] nlohmann::json storeSettings(nlohmann::json) const override;
|
||||
|
||||
[[nodiscard]] UnlocalizedString getTypeName() const override {
|
||||
return "hex.builtin.provider.udp";
|
||||
}
|
||||
|
||||
std::string getName() const override { return hex::format("hex.builtin.provider.udp.name"_lang, m_udpServer.getPort()); }
|
||||
|
||||
protected:
|
||||
void receive(std::span<const u8> data);
|
||||
|
||||
private:
|
||||
UDPServer m_udpServer;
|
||||
int m_port = 0;
|
||||
|
||||
struct Message {
|
||||
std::vector<u8> data;
|
||||
std::chrono::system_clock::time_point timestamp;
|
||||
};
|
||||
|
||||
mutable std::mutex m_mutex;
|
||||
std::vector<Message> m_messages;
|
||||
u64 m_selectedMessage = 0;
|
||||
};
|
||||
|
||||
}
|
||||
@@ -462,6 +462,10 @@
|
||||
"hex.builtin.provider.process_memory.utils.inject_dll": "Inject DLL",
|
||||
"hex.builtin.provider.process_memory.utils.inject_dll.success": "Successfully injected DLL '{0}'!",
|
||||
"hex.builtin.provider.process_memory.utils.inject_dll.failure": "Failed to inject DLL '{0}'!",
|
||||
"hex.builtin.provider.udp": "UDP Server",
|
||||
"hex.builtin.provider.udp.name": "UDP Server on Port {}",
|
||||
"hex.builtin.provider.udp.port": "Server Port",
|
||||
"hex.builtin.provider.udp.timestamp": "Timestamp",
|
||||
"hex.builtin.provider.view": "View",
|
||||
"hex.builtin.setting.experiments": "Experiments",
|
||||
"hex.builtin.setting.experiments.description": "Experiments are features that are still in development and may not work correctly yet.\n\nFeel free to try them out and report any issues you encounter!",
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
#include "content/providers/view_provider.hpp"
|
||||
#include <content/providers/process_memory_provider.hpp>
|
||||
#include <content/providers/base64_provider.hpp>
|
||||
#include <content/providers/udp_provider.hpp>
|
||||
#include <popups/popup_notification.hpp>
|
||||
|
||||
#include <hex/api/project_file_manager.hpp>
|
||||
@@ -29,6 +30,7 @@ namespace hex::plugin::builtin {
|
||||
ContentRegistry::Provider::add<NullProvider>(false);
|
||||
#if !defined(OS_WEB)
|
||||
ContentRegistry::Provider::add<DiskProvider>();
|
||||
ContentRegistry::Provider::add<UDPProvider>();
|
||||
#endif
|
||||
ContentRegistry::Provider::add<GDBProvider>();
|
||||
ContentRegistry::Provider::add<IntelHexProvider>();
|
||||
|
||||
122
plugins/builtin/source/content/providers/udp_provider.cpp
Normal file
122
plugins/builtin/source/content/providers/udp_provider.cpp
Normal file
@@ -0,0 +1,122 @@
|
||||
#include <imgui.h>
|
||||
#include <content/providers/udp_provider.hpp>
|
||||
#include <hex/helpers/utils.hpp>
|
||||
#include <hex/ui/imgui_imhex_extensions.h>
|
||||
|
||||
namespace hex::plugin::builtin {
|
||||
|
||||
bool UDPProvider::open() {
|
||||
m_udpServer = UDPServer(m_port, [this](std::span<const u8> data) {
|
||||
this->receive(data);
|
||||
});
|
||||
m_udpServer.start();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void UDPProvider::close() {
|
||||
m_udpServer.stop();
|
||||
}
|
||||
|
||||
void UDPProvider::receive(std::span<const u8> data) {
|
||||
std::scoped_lock lock(m_mutex);
|
||||
|
||||
m_messages.emplace_back(
|
||||
std::vector(data.begin(), data.end()),
|
||||
std::chrono::system_clock::now()
|
||||
);
|
||||
}
|
||||
|
||||
u64 UDPProvider::getActualSize() const {
|
||||
std::scoped_lock lock(m_mutex);
|
||||
|
||||
if (m_messages.empty())
|
||||
return 0;
|
||||
|
||||
if (m_selectedMessage >= m_messages.size())
|
||||
return 0;
|
||||
|
||||
return m_messages[m_selectedMessage].data.size();
|
||||
}
|
||||
|
||||
void UDPProvider::readRaw(u64 offset, void* buffer, size_t size) {
|
||||
std::scoped_lock lock(m_mutex);
|
||||
|
||||
if (m_selectedMessage >= m_messages.size())
|
||||
return;
|
||||
|
||||
const auto &message = m_messages[m_selectedMessage];
|
||||
|
||||
if (offset >= message.data.size())
|
||||
return;
|
||||
|
||||
if (offset + size > message.data.size()) {
|
||||
if (offset >= message.data.size())
|
||||
size = offset - message.data.size();
|
||||
else
|
||||
return;
|
||||
}
|
||||
|
||||
std::memcpy(buffer, &message.data[offset], size);
|
||||
}
|
||||
|
||||
void UDPProvider::writeRaw(u64, const void*, size_t) {
|
||||
/* Not supported */
|
||||
}
|
||||
|
||||
void UDPProvider::drawInterface() {
|
||||
std::scoped_lock lock(m_mutex);
|
||||
|
||||
if (ImGui::BeginTable("##Messages", 2, ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg, ImGui::GetContentRegionAvail())) {
|
||||
ImGui::TableSetupColumn("hex.builtin.provider.udp.timestamp"_lang, ImGuiTableColumnFlags_WidthFixed, 32 * ImGui::CalcTextSize(" ").x);
|
||||
ImGui::TableSetupColumn("hex.ui.common.size"_lang, ImGuiTableColumnFlags_WidthStretch);
|
||||
ImGui::TableHeadersRow();
|
||||
ImGuiListClipper clipper;
|
||||
clipper.Begin(m_messages.size());
|
||||
while (clipper.Step())
|
||||
for (u64 i = clipper.DisplayStart; i != u64(clipper.DisplayEnd); i += 1) {
|
||||
ImGui::PushID(i + 1);
|
||||
const auto &message = m_messages[i];
|
||||
|
||||
ImGui::TableNextRow();
|
||||
ImGui::TableNextColumn();
|
||||
ImGuiExt::TextFormatted("{}", message.timestamp);
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Selectable("##selectable", i == m_selectedMessage, ImGuiSelectableFlags_SpanAllColumns))
|
||||
m_selectedMessage = i;
|
||||
|
||||
ImGui::TableNextColumn();
|
||||
ImGuiExt::TextFormatted("{}", hex::toByteString(message.data.size()));
|
||||
|
||||
ImGui::PopID();
|
||||
}
|
||||
|
||||
ImGui::EndTable();
|
||||
}
|
||||
}
|
||||
|
||||
bool UDPProvider::drawLoadInterface() {
|
||||
ImGui::InputInt("hex.builtin.provider.udp.port"_lang, &m_port, 0, 0);
|
||||
|
||||
if (m_port < 0)
|
||||
m_port = 0;
|
||||
else if (m_port > 0xFFFF)
|
||||
m_port = 0xFFFF;
|
||||
|
||||
return m_port != 0;
|
||||
}
|
||||
|
||||
|
||||
void UDPProvider::loadSettings(const nlohmann::json &settings) {
|
||||
Provider::loadSettings(settings);
|
||||
|
||||
m_port = settings.at("port").get<int>();
|
||||
}
|
||||
|
||||
nlohmann::json UDPProvider::storeSettings(nlohmann::json settings) const {
|
||||
settings["port"] = m_port;
|
||||
|
||||
return Provider::storeSettings(settings);
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user