mirror of
https://github.com/NohamR/Reclass.git
synced 2026-05-10 19:59:21 +00:00
Add macOS support for ProcessMemory plugin
Implement macOS-specific support for the ProcessMemory plugin and update plugin discovery/build. - Add macOS build/install support: include plugins/ProcessMemory in top-level CMake and copy the built plugin into Reclass.app/Contents/PlugIns on macOS. - Implement Apple-specific ProcessMemoryProvider: task_for_pid usage, mach_vm_read_overwrite/mach_vm_write, proc_pidpath/proc_regionfilename based module caching, region enumeration, symbol formatting, module enumeration, and proper cleanup (mach_port_deallocate). - Extend plugin header to track m_task and adjust readability checks for macOS. - Add macOS handling in getInitialBaseAddress and process enumeration to find base addresses and processes using proc APIs. - Improve PluginManager to probe multiple plugin locations (including Contents/PlugIns inside macOS bundles), aggregate/log candidate counts, and continue scanning multiple dirs. - Add macOS screenshot to README (docs/README_PIC6.png) and reference it in README. These changes enable the ProcessMemory plugin to operate on macOS and make plugin discovery more robust on macOS app bundles.
This commit is contained in:
@@ -599,8 +599,8 @@ if(BUILD_TESTING)
|
|||||||
|
|
||||||
endif() # BUILD_UI_TESTS
|
endif() # BUILD_UI_TESTS
|
||||||
endif()
|
endif()
|
||||||
|
add_subdirectory(plugins/ProcessMemory)
|
||||||
if(NOT APPLE)
|
if(NOT APPLE)
|
||||||
add_subdirectory(plugins/ProcessMemory)
|
|
||||||
add_subdirectory(plugins/RemoteProcessMemory)
|
add_subdirectory(plugins/RemoteProcessMemory)
|
||||||
endif()
|
endif()
|
||||||
if(WIN32)
|
if(WIN32)
|
||||||
|
|||||||
@@ -32,6 +32,8 @@ Built with C++17, Qt 6 (Qt 5 also supported), and QScintilla. The entire editor
|
|||||||
|
|
||||||

|

|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
## Features
|
## Features
|
||||||
|
|
||||||
### Editor
|
### Editor
|
||||||
|
|||||||
BIN
docs/README_PIC6.png
Normal file
BIN
docs/README_PIC6.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 312 KiB |
@@ -45,3 +45,12 @@ set_target_properties(ProcessMemoryPlugin PROPERTIES
|
|||||||
LIBRARY_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/Plugins"
|
LIBRARY_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/Plugins"
|
||||||
RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/Plugins"
|
RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/Plugins"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if(APPLE AND TARGET Reclass)
|
||||||
|
add_custom_command(TARGET ProcessMemoryPlugin POST_BUILD
|
||||||
|
COMMAND ${CMAKE_COMMAND} -E make_directory "$<TARGET_FILE_DIR:Reclass>/../PlugIns"
|
||||||
|
COMMAND ${CMAKE_COMMAND} -E copy_if_different
|
||||||
|
"$<TARGET_FILE:ProcessMemoryPlugin>"
|
||||||
|
"$<TARGET_FILE_DIR:Reclass>/../PlugIns/"
|
||||||
|
COMMENT "Copying ProcessMemoryPlugin into Reclass.app/Contents/PlugIns")
|
||||||
|
endif()
|
||||||
|
|||||||
@@ -10,6 +10,7 @@
|
|||||||
#include <QImage>
|
#include <QImage>
|
||||||
#include <QDir>
|
#include <QDir>
|
||||||
#include <QFileInfo>
|
#include <QFileInfo>
|
||||||
|
#include <QMap>
|
||||||
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) && defined(_WIN32)
|
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) && defined(_WIN32)
|
||||||
#include <QtWin>
|
#include <QtWin>
|
||||||
#endif
|
#endif
|
||||||
@@ -83,6 +84,13 @@ typedef struct alignas(8) _THREAD_BASIC_INFORMATION {
|
|||||||
#include <fstream>
|
#include <fstream>
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
|
#elif defined(__APPLE__)
|
||||||
|
#include <mach/mach.h>
|
||||||
|
#include <mach/mach_vm.h>
|
||||||
|
#include <libproc.h>
|
||||||
|
#include <sys/proc_info.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <cstring>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// ──────────────────────────────────────────────────────────────────────────
|
// ──────────────────────────────────────────────────────────────────────────
|
||||||
@@ -476,8 +484,239 @@ QVector<rcx::MemoryRegion> ProcessMemoryProvider::enumerateRegions() const
|
|||||||
return regions;
|
return regions;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#elif defined(__APPLE__)
|
||||||
|
|
||||||
|
ProcessMemoryProvider::ProcessMemoryProvider(uint32_t pid, const QString& processName)
|
||||||
|
: m_task(0)
|
||||||
|
, m_pid(pid)
|
||||||
|
, m_processName(processName)
|
||||||
|
, m_writable(false)
|
||||||
|
, m_base(0)
|
||||||
|
{
|
||||||
|
mach_port_t task = MACH_PORT_NULL;
|
||||||
|
kern_return_t kr = task_for_pid(mach_task_self(), static_cast<int>(pid), &task);
|
||||||
|
if (kr != KERN_SUCCESS || task == MACH_PORT_NULL)
|
||||||
|
return;
|
||||||
|
|
||||||
|
m_task = static_cast<uint32_t>(task);
|
||||||
|
m_writable = true;
|
||||||
|
|
||||||
|
proc_bsdinfo bsdInfo{};
|
||||||
|
int infoLen = proc_pidinfo(static_cast<int>(pid), PROC_PIDTBSDINFO, 0, &bsdInfo, sizeof(bsdInfo));
|
||||||
|
if (infoLen == (int)sizeof(bsdInfo)) {
|
||||||
|
#ifdef PROC_FLAG_LP64
|
||||||
|
m_pointerSize = (bsdInfo.pbi_flags & PROC_FLAG_LP64) ? 8 : 4;
|
||||||
|
#else
|
||||||
|
m_pointerSize = 8;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
cacheModules();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ProcessMemoryProvider::read(uint64_t addr, void* buf, int len) const
|
||||||
|
{
|
||||||
|
if (m_task == 0 || len <= 0)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
mach_vm_size_t outSize = 0;
|
||||||
|
kern_return_t kr = mach_vm_read_overwrite(
|
||||||
|
static_cast<mach_port_name_t>(m_task),
|
||||||
|
static_cast<mach_vm_address_t>(addr),
|
||||||
|
static_cast<mach_vm_size_t>(len),
|
||||||
|
reinterpret_cast<mach_vm_address_t>(buf),
|
||||||
|
&outSize);
|
||||||
|
|
||||||
|
if ((int)outSize < len)
|
||||||
|
memset((char*)buf + outSize, 0, len - outSize);
|
||||||
|
|
||||||
|
return kr == KERN_SUCCESS && outSize > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ProcessMemoryProvider::write(uint64_t addr, const void* buf, int len)
|
||||||
|
{
|
||||||
|
if (m_task == 0 || !m_writable || len <= 0)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
kern_return_t kr = mach_vm_write(
|
||||||
|
static_cast<mach_port_name_t>(m_task),
|
||||||
|
static_cast<mach_vm_address_t>(addr),
|
||||||
|
reinterpret_cast<vm_offset_t>(const_cast<void*>(buf)),
|
||||||
|
static_cast<mach_msg_type_number_t>(len));
|
||||||
|
return kr == KERN_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString ProcessMemoryProvider::getSymbol(uint64_t addr) const
|
||||||
|
{
|
||||||
|
for (const auto& mod : m_modules)
|
||||||
|
{
|
||||||
|
if (addr >= mod.base && addr < mod.base + mod.size)
|
||||||
|
{
|
||||||
|
uint64_t offset = addr - mod.base;
|
||||||
|
return QStringLiteral("%1+0x%2")
|
||||||
|
.arg(mod.name)
|
||||||
|
.arg(offset, 0, 16, QChar('0'));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
void ProcessMemoryProvider::cacheModules()
|
||||||
|
{
|
||||||
|
if (m_task == 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
m_modules.clear();
|
||||||
|
|
||||||
|
char mainPathBuf[PROC_PIDPATHINFO_MAXSIZE] = {};
|
||||||
|
QString mainPath;
|
||||||
|
if (proc_pidpath((int)m_pid, mainPathBuf, sizeof(mainPathBuf)) > 0)
|
||||||
|
mainPath = QString::fromUtf8(mainPathBuf);
|
||||||
|
|
||||||
|
struct Range { uint64_t base; uint64_t end; };
|
||||||
|
QMap<QString, Range> moduleRanges;
|
||||||
|
|
||||||
|
mach_vm_address_t addr = 0;
|
||||||
|
uint32_t depth = 0;
|
||||||
|
for (;;) {
|
||||||
|
mach_vm_size_t size = 0;
|
||||||
|
vm_region_submap_info_data_64_t info{};
|
||||||
|
mach_msg_type_number_t count = VM_REGION_SUBMAP_INFO_COUNT_64;
|
||||||
|
kern_return_t kr = mach_vm_region_recurse(
|
||||||
|
static_cast<mach_port_name_t>(m_task),
|
||||||
|
&addr,
|
||||||
|
&size,
|
||||||
|
&depth,
|
||||||
|
reinterpret_cast<vm_region_recurse_info_t>(&info),
|
||||||
|
&count);
|
||||||
|
if (kr != KERN_SUCCESS)
|
||||||
|
break;
|
||||||
|
|
||||||
|
if (info.is_submap) {
|
||||||
|
++depth;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (size == 0) {
|
||||||
|
++addr;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
char pathBuf[PROC_PIDPATHINFO_MAXSIZE] = {};
|
||||||
|
int pathLen = proc_regionfilename((int)m_pid, (uint64_t)addr, pathBuf, sizeof(pathBuf));
|
||||||
|
if (pathLen > 0) {
|
||||||
|
QString fullPath = QString::fromUtf8(pathBuf);
|
||||||
|
|
||||||
|
uint64_t regionBase = (uint64_t)addr;
|
||||||
|
uint64_t regionEnd = regionBase + (uint64_t)size;
|
||||||
|
auto it = moduleRanges.find(fullPath);
|
||||||
|
if (it == moduleRanges.end()) {
|
||||||
|
moduleRanges.insert(fullPath, {regionBase, regionEnd});
|
||||||
|
} else {
|
||||||
|
if (regionBase < it->base) it->base = regionBase;
|
||||||
|
if (regionEnd > it->end) it->end = regionEnd;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_base == 0 && !mainPath.isEmpty() && fullPath == mainPath && (info.protection & VM_PROT_EXECUTE))
|
||||||
|
m_base = regionBase;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint64_t next = (uint64_t)addr + (uint64_t)size;
|
||||||
|
if (next <= (uint64_t)addr)
|
||||||
|
break;
|
||||||
|
addr = (mach_vm_address_t)next;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_modules.reserve(moduleRanges.size());
|
||||||
|
for (auto it = moduleRanges.begin(); it != moduleRanges.end(); ++it)
|
||||||
|
{
|
||||||
|
QFileInfo fi(it.key());
|
||||||
|
m_modules.push_back(ModuleInfo{
|
||||||
|
fi.fileName(),
|
||||||
|
it.key(),
|
||||||
|
it->base,
|
||||||
|
it->end - it->base
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_base == 0 && !m_modules.isEmpty())
|
||||||
|
m_base = m_modules.front().base;
|
||||||
|
}
|
||||||
|
|
||||||
|
QVector<rcx::MemoryRegion> ProcessMemoryProvider::enumerateRegions() const
|
||||||
|
{
|
||||||
|
QVector<rcx::MemoryRegion> regions;
|
||||||
|
if (m_task == 0)
|
||||||
|
return regions;
|
||||||
|
|
||||||
|
mach_vm_address_t addr = 0;
|
||||||
|
uint32_t depth = 0;
|
||||||
|
for (;;) {
|
||||||
|
mach_vm_size_t size = 0;
|
||||||
|
vm_region_submap_info_data_64_t info{};
|
||||||
|
mach_msg_type_number_t count = VM_REGION_SUBMAP_INFO_COUNT_64;
|
||||||
|
kern_return_t kr = mach_vm_region_recurse(
|
||||||
|
static_cast<mach_port_name_t>(m_task),
|
||||||
|
&addr,
|
||||||
|
&size,
|
||||||
|
&depth,
|
||||||
|
reinterpret_cast<vm_region_recurse_info_t>(&info),
|
||||||
|
&count);
|
||||||
|
if (kr != KERN_SUCCESS)
|
||||||
|
break;
|
||||||
|
|
||||||
|
if (info.is_submap) {
|
||||||
|
++depth;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (size == 0) {
|
||||||
|
++addr;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool readable = (info.protection & VM_PROT_READ) != 0;
|
||||||
|
if (readable)
|
||||||
|
{
|
||||||
|
rcx::MemoryRegion region;
|
||||||
|
region.base = (uint64_t)addr;
|
||||||
|
region.size = (uint64_t)size;
|
||||||
|
region.readable = readable;
|
||||||
|
region.writable = (info.protection & VM_PROT_WRITE) != 0;
|
||||||
|
region.executable = (info.protection & VM_PROT_EXECUTE) != 0;
|
||||||
|
|
||||||
|
char pathBuf[PROC_PIDPATHINFO_MAXSIZE] = {};
|
||||||
|
int pathLen = proc_regionfilename((int)m_pid, region.base, pathBuf, sizeof(pathBuf));
|
||||||
|
if (pathLen > 0) {
|
||||||
|
QFileInfo fi(QString::fromUtf8(pathBuf));
|
||||||
|
region.moduleName = fi.fileName();
|
||||||
|
}
|
||||||
|
|
||||||
|
regions.append(region);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint64_t next = (uint64_t)addr + (uint64_t)size;
|
||||||
|
if (next <= (uint64_t)addr)
|
||||||
|
break;
|
||||||
|
addr = (mach_vm_address_t)next;
|
||||||
|
}
|
||||||
|
|
||||||
|
return regions;
|
||||||
|
}
|
||||||
|
|
||||||
#endif // platform
|
#endif // platform
|
||||||
|
|
||||||
|
#ifndef _WIN32
|
||||||
|
QVector<rcx::Provider::ModuleEntry> ProcessMemoryProvider::enumerateModules() const
|
||||||
|
{
|
||||||
|
QVector<ModuleEntry> result;
|
||||||
|
result.reserve(m_modules.size());
|
||||||
|
for (const auto& m : m_modules)
|
||||||
|
result.push_back(ModuleEntry{m.name, m.fullPath, m.base, m.size});
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
uint64_t ProcessMemoryProvider::symbolToAddress(const QString& name) const
|
uint64_t ProcessMemoryProvider::symbolToAddress(const QString& name) const
|
||||||
{
|
{
|
||||||
for (const auto& mod : m_modules) {
|
for (const auto& mod : m_modules) {
|
||||||
@@ -495,6 +734,9 @@ ProcessMemoryProvider::~ProcessMemoryProvider()
|
|||||||
#elif defined(__linux__)
|
#elif defined(__linux__)
|
||||||
if (m_fd >= 0)
|
if (m_fd >= 0)
|
||||||
::close(m_fd);
|
::close(m_fd);
|
||||||
|
#elif defined(__APPLE__)
|
||||||
|
if (m_task != 0)
|
||||||
|
mach_port_deallocate(mach_task_self(), static_cast<mach_port_name_t>(m_task));
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -504,6 +746,8 @@ int ProcessMemoryProvider::size() const
|
|||||||
return m_handle ? 0x10000 : 0;
|
return m_handle ? 0x10000 : 0;
|
||||||
#elif defined(__linux__)
|
#elif defined(__linux__)
|
||||||
return (m_fd >= 0) ? 0x10000 : 0;
|
return (m_fd >= 0) ? 0x10000 : 0;
|
||||||
|
#elif defined(__APPLE__)
|
||||||
|
return (m_task != 0) ? 0x10000 : 0;
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -654,6 +898,68 @@ uint64_t ProcessMemoryPlugin::getInitialBaseAddress(const QString& target) const
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
|
#elif defined(__APPLE__)
|
||||||
|
QStringList parts = target.split(':');
|
||||||
|
bool ok = false;
|
||||||
|
uint32_t pid = parts[0].toUInt(&ok);
|
||||||
|
if (!ok || pid == 0)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
mach_port_t task = MACH_PORT_NULL;
|
||||||
|
kern_return_t tkr = task_for_pid(mach_task_self(), static_cast<int>(pid), &task);
|
||||||
|
if (tkr != KERN_SUCCESS || task == MACH_PORT_NULL)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
char mainPathBuf[PROC_PIDPATHINFO_MAXSIZE] = {};
|
||||||
|
QString mainPath;
|
||||||
|
if (proc_pidpath((int)pid, mainPathBuf, sizeof(mainPathBuf)) > 0)
|
||||||
|
mainPath = QString::fromUtf8(mainPathBuf);
|
||||||
|
|
||||||
|
uint64_t base = 0;
|
||||||
|
mach_vm_address_t addr = 0;
|
||||||
|
uint32_t depth = 0;
|
||||||
|
for (;;) {
|
||||||
|
mach_vm_size_t size = 0;
|
||||||
|
vm_region_submap_info_data_64_t info{};
|
||||||
|
mach_msg_type_number_t count = VM_REGION_SUBMAP_INFO_COUNT_64;
|
||||||
|
kern_return_t kr = mach_vm_region_recurse(task, &addr, &size, &depth,
|
||||||
|
reinterpret_cast<vm_region_recurse_info_t>(&info),
|
||||||
|
&count);
|
||||||
|
if (kr != KERN_SUCCESS)
|
||||||
|
break;
|
||||||
|
|
||||||
|
if (info.is_submap) {
|
||||||
|
++depth;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (size == 0) {
|
||||||
|
++addr;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((info.protection & VM_PROT_EXECUTE) != 0) {
|
||||||
|
if (!mainPath.isEmpty()) {
|
||||||
|
char pathBuf[PROC_PIDPATHINFO_MAXSIZE] = {};
|
||||||
|
int pathLen = proc_regionfilename((int)pid, (uint64_t)addr, pathBuf, sizeof(pathBuf));
|
||||||
|
if (pathLen > 0 && QString::fromUtf8(pathBuf) == mainPath) {
|
||||||
|
base = (uint64_t)addr;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
base = (uint64_t)addr;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
uint64_t next = (uint64_t)addr + (uint64_t)size;
|
||||||
|
if (next <= (uint64_t)addr)
|
||||||
|
break;
|
||||||
|
addr = (mach_vm_address_t)next;
|
||||||
|
}
|
||||||
|
|
||||||
|
mach_port_deallocate(mach_task_self(), task);
|
||||||
|
return base;
|
||||||
#else
|
#else
|
||||||
Q_UNUSED(target);
|
Q_UNUSED(target);
|
||||||
return 0;
|
return 0;
|
||||||
@@ -797,6 +1103,61 @@ QVector<PluginProcessInfo> ProcessMemoryPlugin::enumerateProcesses()
|
|||||||
::close(exeFd);
|
::close(exeFd);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
processes.append(info);
|
||||||
|
}
|
||||||
|
#elif defined(__APPLE__)
|
||||||
|
QIcon defaultIcon = qApp->style()->standardIcon(QStyle::SP_ComputerIcon);
|
||||||
|
|
||||||
|
int bytes = proc_listpids(PROC_ALL_PIDS, 0, nullptr, 0);
|
||||||
|
if (bytes <= 0)
|
||||||
|
return processes;
|
||||||
|
|
||||||
|
int count = bytes / (int)sizeof(pid_t);
|
||||||
|
QVector<pid_t> pids(count);
|
||||||
|
bytes = proc_listpids(PROC_ALL_PIDS, 0, pids.data(), count * (int)sizeof(pid_t));
|
||||||
|
if (bytes <= 0)
|
||||||
|
return processes;
|
||||||
|
|
||||||
|
count = bytes / (int)sizeof(pid_t);
|
||||||
|
for (int i = 0; i < count; ++i) {
|
||||||
|
pid_t pid = pids[i];
|
||||||
|
if (pid <= 0)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
mach_port_t task = MACH_PORT_NULL;
|
||||||
|
if (task_for_pid(mach_task_self(), pid, &task) != KERN_SUCCESS || task == MACH_PORT_NULL)
|
||||||
|
continue;
|
||||||
|
mach_port_deallocate(mach_task_self(), task);
|
||||||
|
|
||||||
|
char nameBuf[PROC_PIDPATHINFO_MAXSIZE] = {};
|
||||||
|
int nameLen = proc_name(pid, nameBuf, sizeof(nameBuf));
|
||||||
|
QString procName;
|
||||||
|
if (nameLen > 0)
|
||||||
|
procName = QString::fromUtf8(nameBuf);
|
||||||
|
if (procName.isEmpty())
|
||||||
|
continue;
|
||||||
|
|
||||||
|
char pathBuf[PROC_PIDPATHINFO_MAXSIZE] = {};
|
||||||
|
QString procPath;
|
||||||
|
if (proc_pidpath(pid, pathBuf, sizeof(pathBuf)) > 0)
|
||||||
|
procPath = QString::fromUtf8(pathBuf);
|
||||||
|
|
||||||
|
PluginProcessInfo info;
|
||||||
|
info.pid = static_cast<uint32_t>(pid);
|
||||||
|
info.name = procName;
|
||||||
|
info.path = procPath;
|
||||||
|
info.icon = defaultIcon;
|
||||||
|
|
||||||
|
proc_bsdinfo bsdInfo{};
|
||||||
|
int infoLen = proc_pidinfo(pid, PROC_PIDTBSDINFO, 0, &bsdInfo, sizeof(bsdInfo));
|
||||||
|
if (infoLen == (int)sizeof(bsdInfo)) {
|
||||||
|
#ifdef PROC_FLAG_LP64
|
||||||
|
info.is32Bit = (bsdInfo.pbi_flags & PROC_FLAG_LP64) == 0;
|
||||||
|
#else
|
||||||
|
info.is32Bit = false;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
processes.append(info);
|
processes.append(info);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -35,6 +35,8 @@ public:
|
|||||||
return m_handle && len >= 0;
|
return m_handle && len >= 0;
|
||||||
#elif defined(__linux__)
|
#elif defined(__linux__)
|
||||||
return m_fd >= 0 && len >= 0;
|
return m_fd >= 0 && len >= 0;
|
||||||
|
#elif defined(__APPLE__)
|
||||||
|
return m_task != 0 && len >= 0;
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -53,6 +55,8 @@ private:
|
|||||||
void* m_handle;
|
void* m_handle;
|
||||||
#elif defined(__linux__)
|
#elif defined(__linux__)
|
||||||
int m_fd;
|
int m_fd;
|
||||||
|
#elif defined(__APPLE__)
|
||||||
|
uint32_t m_task;
|
||||||
#endif
|
#endif
|
||||||
uint32_t m_pid;
|
uint32_t m_pid;
|
||||||
QString m_processName;
|
QString m_processName;
|
||||||
|
|||||||
@@ -12,17 +12,16 @@ PluginManager::~PluginManager()
|
|||||||
|
|
||||||
void PluginManager::LoadPlugins()
|
void PluginManager::LoadPlugins()
|
||||||
{
|
{
|
||||||
// Get the Plugins directory relative to the executable
|
// Probe plugin locations relative to the executable.
|
||||||
QString appDir = QCoreApplication::applicationDirPath();
|
QString appDir = QCoreApplication::applicationDirPath();
|
||||||
QString pluginsDir = appDir + "/Plugins";
|
QStringList pluginDirs;
|
||||||
|
pluginDirs << (appDir + "/Plugins");
|
||||||
QDir dir(pluginsDir);
|
#ifdef __APPLE__
|
||||||
if (!dir.exists())
|
// In macOS app bundles, plugins may live in Contents/PlugIns or in
|
||||||
{
|
// the top-level build/Plugins directory during local development.
|
||||||
qWarning() << "PluginManager: Plugins directory not found:" << pluginsDir;
|
pluginDirs << QDir::cleanPath(appDir + "/../PlugIns");
|
||||||
return;
|
#endif
|
||||||
}
|
|
||||||
|
|
||||||
// Find all DLL files
|
// Find all DLL files
|
||||||
QStringList filters;
|
QStringList filters;
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
@@ -32,22 +31,36 @@ void PluginManager::LoadPlugins()
|
|||||||
#else
|
#else
|
||||||
filters << "*.so";
|
filters << "*.so";
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
dir.setNameFilters(filters);
|
int totalCandidates = 0;
|
||||||
QFileInfoList files = dir.entryInfoList(QDir::Files);
|
bool foundAnyDir = false;
|
||||||
|
for (const QString& pluginsDir : pluginDirs)
|
||||||
qDebug() << "PluginManager: Scanning for plugins in:" << pluginsDir;
|
|
||||||
qDebug() << "PluginManager: Found" << files.count() << "potential plugin(s)";
|
|
||||||
|
|
||||||
for (const QFileInfo& fileInfo : files)
|
|
||||||
{
|
{
|
||||||
// Skip the remote-inject payload binary — it's not a plugin and
|
QDir dir(pluginsDir);
|
||||||
// loading it (especially on Linux) spawns a rogue thread.
|
if (!dir.exists())
|
||||||
if (fileInfo.baseName().startsWith("rcx_payload"))
|
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
LoadPlugin(fileInfo.absoluteFilePath());
|
foundAnyDir = true;
|
||||||
|
dir.setNameFilters(filters);
|
||||||
|
QFileInfoList files = dir.entryInfoList(QDir::Files);
|
||||||
|
totalCandidates += files.count();
|
||||||
|
|
||||||
|
qDebug() << "PluginManager: Scanning for plugins in:" << pluginsDir;
|
||||||
|
for (const QFileInfo& fileInfo : files)
|
||||||
|
{
|
||||||
|
// Skip the remote-inject payload binary — it's not a plugin and
|
||||||
|
// loading it (especially on Linux) spawns a rogue thread.
|
||||||
|
if (fileInfo.baseName().startsWith("rcx_payload"))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
LoadPlugin(fileInfo.absoluteFilePath());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!foundAnyDir)
|
||||||
|
qWarning() << "PluginManager: Plugins directory not found. Searched:" << pluginDirs;
|
||||||
|
else
|
||||||
|
qDebug() << "PluginManager: Found" << totalCandidates << "potential plugin(s)";
|
||||||
|
|
||||||
qDebug() << "PluginManager: Loaded" << m_plugins.count() << "plugin(s)";
|
qDebug() << "PluginManager: Loaded" << m_plugins.count() << "plugin(s)";
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user