Instance, class decriptors, property descriptors + cleanup
This commit is contained in:
@@ -0,0 +1,43 @@
|
||||
#pragma once
|
||||
|
||||
#include "xrbx/engine/common.h"
|
||||
#include "xrbx/process.h"
|
||||
#include "offsets.h"
|
||||
#include <stdexcept>
|
||||
#include <vector>
|
||||
|
||||
namespace RBX {
|
||||
class PropertyDescriptor : public InternalObjectWrapper {
|
||||
public:
|
||||
PropertyDescriptor(BYTE *va, const Process &process) : InternalObjectWrapper(va, process) {}
|
||||
|
||||
inline std::string getName() const {
|
||||
return process().readCPPString(process().read<BYTE *>(VA() + OFF_PD_NAME));
|
||||
}
|
||||
};
|
||||
|
||||
class ClassDescriptor : public InternalObjectWrapper {
|
||||
public:
|
||||
ClassDescriptor(BYTE *va, const Process &process) : InternalObjectWrapper(va, process) {}
|
||||
|
||||
inline std::string getName() const {
|
||||
try {
|
||||
return process().readCPPString(process().read<BYTE *>(VA() + OFF_CD_NAME));
|
||||
} catch (...) {
|
||||
throw std::runtime_error(std::format("Failed to read class name for class @ %llx", uintptr_t(VA())));
|
||||
}
|
||||
}
|
||||
|
||||
inline std::vector<PropertyDescriptor> getPropertyDescriptors() const {
|
||||
std::vector<PropertyDescriptor> propdescs;
|
||||
BYTE *start = process().read<BYTE *>(VA() + OFF_CD_PROP_DESCRIPTORS);
|
||||
uint64_t length = process().read<uint64_t>(VA() + OFF_CD_PROP_DESCRIPTORS + 8);
|
||||
if (start == 0) return propdescs;
|
||||
for (BYTE *current = start; (current - start) / 2 < length; current += 16) {
|
||||
PropertyDescriptor pd(process().read<BYTE *>(current), process());
|
||||
propdescs.push_back(pd);
|
||||
}
|
||||
return propdescs;
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
#pragma once
|
||||
|
||||
#include <windows.h>
|
||||
#include "../process.h"
|
||||
|
||||
namespace RBX {
|
||||
class InternalObjectWrapper {
|
||||
private:
|
||||
BYTE *addr;
|
||||
const Process &proc;
|
||||
public:
|
||||
InternalObjectWrapper(BYTE *va, const Process &process) : addr(va), proc(process) {}
|
||||
inline BYTE *VA(void) const { return addr; }
|
||||
inline const Process &process(void) const { return proc; }
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
#pragma once
|
||||
|
||||
#include "xrbx/process.h"
|
||||
#include "offsets.h"
|
||||
#include "common.h"
|
||||
#include <optional>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <windows.h>
|
||||
|
||||
#include "classdescriptor.h"
|
||||
|
||||
namespace RBX {
|
||||
class Instance : public InternalObjectWrapper {
|
||||
public:
|
||||
Instance(BYTE *va, const Process &process) : InternalObjectWrapper(va, process) {}
|
||||
|
||||
inline std::string getName() const {
|
||||
return process().readCPPString(process().read<BYTE *>(VA() + OFF_INSTANCE_NAME));
|
||||
}
|
||||
|
||||
inline std::optional<Instance> getParent() const {
|
||||
BYTE *parent_va = process().read<BYTE *>(VA() + OFF_INSTANCE_PARENT);
|
||||
if (parent_va == 0) return std::nullopt;
|
||||
return Instance(parent_va, process());
|
||||
}
|
||||
|
||||
inline std::vector<Instance> getChildren() const {
|
||||
try {
|
||||
return process().readVector<Instance>(process().read<BYTE *>(VA() + OFF_INSTANCE_CHILDREN));
|
||||
} catch (...) {
|
||||
throw std::runtime_error(std::format("Failed to read children of instance '{}' @ {:x}", getName(), uintptr_t(VA())));
|
||||
}
|
||||
}
|
||||
|
||||
inline std::optional<Instance> findFirstChild(const std::string &name) {
|
||||
return process().readVectorElement<Instance>(process().read<BYTE *>(VA() + OFF_INSTANCE_CHILDREN), [&name](const Instance &inst) -> bool {
|
||||
return inst.getName() == name;
|
||||
});
|
||||
}
|
||||
|
||||
inline std::optional<Instance> findFirstChildOfClass(const std::string &name) {
|
||||
return process().readVectorElement<Instance>(process().read<BYTE *>(VA() + OFF_INSTANCE_CHILDREN), [&name](const Instance &inst) -> bool {
|
||||
return inst.getClassName() == name;
|
||||
});
|
||||
}
|
||||
|
||||
inline ClassDescriptor getClassDescriptor() const {
|
||||
BYTE *va = process().read<BYTE *>(VA() + OFF_INSTANCE_CLASS_DESCRIPTOR);
|
||||
if (va == 0) throw std::runtime_error("invalid class");
|
||||
return ClassDescriptor(va, process());
|
||||
}
|
||||
|
||||
inline std::string getClassName() const {
|
||||
return getClassDescriptor().getName();
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -6,3 +6,14 @@
|
||||
#define RVA_TASK_SCHEDULER RVA(0x1484A58E0) // "AllTaskSchedulerJobs"
|
||||
#define OFF_SCHEDULER_JOBS 0xC8 // "TaskScheduler::Job:"
|
||||
#define OFF_JOB_NAME 0xF0 // "{}({};{})"
|
||||
#define OFF_JOB_RUNSERVICE 0x1A0
|
||||
|
||||
#define OFF_INSTANCE_NAME 0x98
|
||||
#define OFF_INSTANCE_PARENT 0x68
|
||||
#define OFF_INSTANCE_CHILDREN 0x70
|
||||
#define OFF_INSTANCE_CLASS_DESCRIPTOR 0x18
|
||||
|
||||
#define OFF_CD_NAME 0x8
|
||||
#define OFF_CD_PROP_DESCRIPTORS 0x40
|
||||
|
||||
#define OFF_PD_NAME 0x8
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <windows.h>
|
||||
@@ -8,45 +9,42 @@
|
||||
|
||||
#include "offsets.h"
|
||||
#include "../process.h"
|
||||
#include "instance.h"
|
||||
#include "xrbx/engine/common.h"
|
||||
|
||||
namespace RBX {
|
||||
class Scheduler {
|
||||
private:
|
||||
BYTE *addr;
|
||||
const Process &proc;
|
||||
class Scheduler : public InternalObjectWrapper {
|
||||
public:
|
||||
class Job {
|
||||
private:
|
||||
BYTE *addr;
|
||||
const Process &proc;
|
||||
class Job : public InternalObjectWrapper {
|
||||
public:
|
||||
Job(BYTE *va, const Process &process) : addr(va), proc(process) {}
|
||||
|
||||
const BYTE * const VA() const { return addr; }
|
||||
Job(BYTE *va, const Process &process) : InternalObjectWrapper(va, process) {}
|
||||
|
||||
inline std::string getName() const {
|
||||
BYTE *name_ptr = addr + OFF_JOB_NAME;
|
||||
uint64_t length = proc.Read<uint64_t>(name_ptr + 8);
|
||||
BYTE *name_ptr = VA() + OFF_JOB_NAME;
|
||||
uint64_t length = process().read<uint64_t>(name_ptr + 8);
|
||||
std::string name(length, '\0');
|
||||
proc.ReadBytes(proc.Read<BYTE *>(name_ptr), (BYTE *)name.data(), length);
|
||||
process().readBytes(process().read<BYTE *>(name_ptr), (BYTE *)name.data(), length);
|
||||
name[length] = 0;
|
||||
return name;
|
||||
}
|
||||
|
||||
inline Instance getRunService() {
|
||||
return Instance(process().read<BYTE *>(VA() + OFF_JOB_RUNSERVICE), process());
|
||||
}
|
||||
};
|
||||
|
||||
Scheduler(BYTE *va, const Process &process) : addr(va), proc(process) {}
|
||||
Scheduler(BYTE *va, const Process &process) : InternalObjectWrapper(va, process) {}
|
||||
|
||||
inline std::vector<Job> getJobs() const {
|
||||
std::vector<Job> jobs;
|
||||
BYTE *jobsStart = proc.Read<BYTE *>(addr + OFF_SCHEDULER_JOBS);
|
||||
BYTE *jobsEnd = proc.Read<BYTE *>(addr + OFF_SCHEDULER_JOBS + 8);
|
||||
for (BYTE *job = jobsStart; job != jobsEnd; job += 16) {
|
||||
Job j(proc.Read<BYTE *>(job), proc);
|
||||
jobs.push_back(j);
|
||||
}
|
||||
return jobs;
|
||||
return process().readVector<Job>(VA() + OFF_SCHEDULER_JOBS);
|
||||
}
|
||||
|
||||
Job getJob(const std::string &name);
|
||||
Job getJob(const std::string &name) const {
|
||||
auto j = process().readVectorElement<Job>(VA() + OFF_SCHEDULER_JOBS, [&name](const Job &job) -> bool {
|
||||
return job.getName() == name;
|
||||
});
|
||||
if (!j.has_value()) throw std::runtime_error(std::format("Job '{}' not found", name));
|
||||
return j.value();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -63,4 +63,70 @@ namespace Nt {
|
||||
PVOID Mutant;
|
||||
PVOID ImageBaseAddress;
|
||||
} PEB_PARTIAL;
|
||||
|
||||
typedef struct _SYSTEM_THREAD_INFORMATION
|
||||
{
|
||||
LARGE_INTEGER KernelTime;
|
||||
LARGE_INTEGER UserTime;
|
||||
LARGE_INTEGER CreateTime;
|
||||
ULONG WaitTime;
|
||||
PVOID StartAddress;
|
||||
CLIENT_ID ClientId;
|
||||
LONG Priority;
|
||||
LONG BasePriority;
|
||||
ULONG ContextSwitches;
|
||||
ULONG ThreadState;
|
||||
ULONG WaitReason;
|
||||
} SYSTEM_THREAD_INFORMATION;
|
||||
|
||||
|
||||
typedef struct _SYSTEM_PROCESS_INFORMATION
|
||||
{
|
||||
ULONG NextEntryOffset;
|
||||
ULONG NumberOfThreads;
|
||||
|
||||
BYTE Reserved1[48];
|
||||
|
||||
UNICODE_STRING ImageName;
|
||||
|
||||
KPRIORITY BasePriority;
|
||||
|
||||
HANDLE UniqueProcessId;
|
||||
HANDLE InheritedFromUniqueProcessId;
|
||||
|
||||
ULONG HandleCount;
|
||||
ULONG SessionId;
|
||||
|
||||
SIZE_T UniqueProcessKey;
|
||||
|
||||
SIZE_T PeakVirtualSize;
|
||||
SIZE_T VirtualSize;
|
||||
|
||||
ULONG PageFaultCount;
|
||||
|
||||
SIZE_T PeakWorkingSetSize;
|
||||
SIZE_T WorkingSetSize;
|
||||
|
||||
SIZE_T QuotaPeakPagedPoolUsage;
|
||||
SIZE_T QuotaPagedPoolUsage;
|
||||
|
||||
SIZE_T QuotaPeakNonPagedPoolUsage;
|
||||
SIZE_T QuotaNonPagedPoolUsage;
|
||||
|
||||
SIZE_T PagefileUsage;
|
||||
SIZE_T PeakPagefileUsage;
|
||||
|
||||
SIZE_T PrivatePageCount;
|
||||
|
||||
LARGE_INTEGER ReadOperationCount;
|
||||
LARGE_INTEGER WriteOperationCount;
|
||||
LARGE_INTEGER OtherOperationCount;
|
||||
|
||||
LARGE_INTEGER ReadTransferCount;
|
||||
LARGE_INTEGER WriteTransferCount;
|
||||
LARGE_INTEGER OtherTransferCount;
|
||||
|
||||
SYSTEM_THREAD_INFORMATION Threads[1];
|
||||
|
||||
} SYSTEM_PROCESS_INFORMATION;
|
||||
}
|
||||
|
||||
+40
-2
@@ -1,6 +1,10 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <format>
|
||||
#include <functional>
|
||||
#include <iostream>
|
||||
#include <optional>
|
||||
#include <stdexcept>
|
||||
#include <windows.h>
|
||||
#include <vector>
|
||||
@@ -20,13 +24,14 @@ class Process {
|
||||
BYTE *getImageBase(void);
|
||||
public:
|
||||
static DWORD findProcessByName(const std::wstring &name);
|
||||
std::vector<DWORD> getThreads();
|
||||
|
||||
inline BYTE *imageBase(void) const {
|
||||
return base;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline T Read(BYTE *addr) const {
|
||||
inline T read(BYTE *addr) const {
|
||||
T buf;
|
||||
SIZE_T bytesRead;
|
||||
NTSTATUS status = Nt::resolve<Nt::ReadVirtualMemory>("NtReadVirtualMemory")(proc, addr, &buf, sizeof(buf), &bytesRead);
|
||||
@@ -36,7 +41,7 @@ class Process {
|
||||
return buf;
|
||||
}
|
||||
|
||||
inline void ReadBytes(BYTE *addr, BYTE *buf, size_t bytes) const {
|
||||
inline void readBytes(BYTE *addr, BYTE *buf, size_t bytes) const {
|
||||
SIZE_T bytesRead;
|
||||
NTSTATUS status = Nt::resolve<Nt::ReadVirtualMemory>("NtReadVirtualMemory")(proc, addr, buf, bytes, &bytesRead);
|
||||
if (status < 0) {
|
||||
@@ -44,6 +49,39 @@ class Process {
|
||||
}
|
||||
}
|
||||
|
||||
inline std::string readCPPString(BYTE *addr) const {
|
||||
uint64_t length = read<uint64_t>(addr + 16);
|
||||
uint64_t capacity = read<uint64_t>(addr + 24);
|
||||
std::string str(length, '\0');
|
||||
readBytes(capacity < 16 ? addr : read<BYTE *>(addr), (BYTE *)str.data(), length);
|
||||
return str;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline std::vector<T> readVector(BYTE *addr, uint8_t esz = 16) const {
|
||||
std::vector<T> vec;
|
||||
if (addr == 0) return vec;
|
||||
BYTE *start = read<BYTE *>(addr);
|
||||
BYTE *end = read<BYTE *>(addr + 8);
|
||||
for (BYTE *el = start; el != end; el += esz) {
|
||||
T o(read<BYTE *>(el), *this);
|
||||
vec.push_back(o);
|
||||
}
|
||||
return vec;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline std::optional<T> readVectorElement(BYTE *addr, std::function<bool(const T &o)> filter, uint8_t esz = 16) const {
|
||||
if (addr == 0) return std::nullopt;
|
||||
BYTE *start = read<BYTE *>(addr);
|
||||
BYTE *end = read<BYTE *>(addr + 8);
|
||||
for (BYTE *el = start; el != end; el += esz) {
|
||||
T o(read<BYTE *>(el), *this);
|
||||
if (filter(o)) return o;
|
||||
}
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
inline BYTE *VA(uintptr_t rva) const {
|
||||
return base + rva;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user