feat: add dump-backed runtime modifiers
This commit is contained in:
parent
6fc8d8c16b
commit
632dc46116
|
|
@ -1,7 +1,7 @@
|
|||
#include "Framework.h"
|
||||
#include <cmath>
|
||||
|
||||
// 内存修改类功能 (NoSway/InfStamina/NoFallDmg) 已移除:
|
||||
// 它们依赖 IL2CPP 版偏移, 在 EXFIL DELTA (Mono 0.16.9) 上偏移不兼容, 会乱写内存。
|
||||
// 只实现 dump 明确的对象字段;后座/故障链路等类布局确认后再接入,当前不写内存。
|
||||
|
||||
void CFramework::GetESPInfo(const int& SpawnType, std::string& vOutStr, ImColor& vOutColor)
|
||||
{
|
||||
|
|
@ -168,4 +168,240 @@ void CFramework::GetESPInfo(const int& SpawnType, std::string& vOutStr, ImColor&
|
|||
vOutColor = ImColor(1.f, 1.f, 1.f, 1.f);
|
||||
break;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
namespace
|
||||
{
|
||||
bool IsUsableAddress(const uintptr_t address)
|
||||
{
|
||||
// x64 user-mode canonical range; reject null/obviously bogus chains before any write.
|
||||
return address > 0x10000ULL && address < 0x0000800000000000ULL;
|
||||
}
|
||||
|
||||
bool IsFiniteValue(const float value)
|
||||
{
|
||||
return std::isfinite(value);
|
||||
}
|
||||
|
||||
bool IsFiniteVector(const Vector3& value)
|
||||
{
|
||||
return IsFiniteValue(value.x) && IsFiniteValue(value.y) && IsFiniteValue(value.z);
|
||||
}
|
||||
|
||||
bool IsUsableCapacity(const float value)
|
||||
{
|
||||
return IsFiniteValue(value) && value > 0.f && value < 100000.f;
|
||||
}
|
||||
}
|
||||
|
||||
void CFramework::UpdateFeatures()
|
||||
{
|
||||
if (!tarkov->IsInRaid() || !IsUsableAddress(tarkov->m_pLocalGameWorld))
|
||||
{
|
||||
ResetFeatureOverrides();
|
||||
return;
|
||||
}
|
||||
|
||||
// Refresh MainPlayer from the current LocalGameWorld instead of trusting a stale list-thread cache.
|
||||
uintptr_t currentLocal = 0;
|
||||
if (!m.TryRead(tarkov->m_pLocalGameWorld + offset::MainPlayer, currentLocal) ||
|
||||
!IsUsableAddress(currentLocal))
|
||||
{
|
||||
ResetFeatureOverrides();
|
||||
return;
|
||||
}
|
||||
local.m_address = currentLocal;
|
||||
|
||||
if (g.g_InfStamina)
|
||||
|
||||
UpdateStaminaOverride();
|
||||
else
|
||||
ResetStaminaOverride();
|
||||
|
||||
if (g.g_NoFallDamage)
|
||||
UpdateFallDamageOverride();
|
||||
else
|
||||
ResetFallDamageOverride();
|
||||
|
||||
if (g.g_NoSway)
|
||||
UpdateSwayOverride();
|
||||
else
|
||||
ResetSwayOverride();
|
||||
}
|
||||
|
||||
void CFramework::UpdateStaminaOverride()
|
||||
{
|
||||
uintptr_t physical = 0;
|
||||
uintptr_t stamina = 0;
|
||||
uintptr_t handsStamina = 0;
|
||||
|
||||
if (!m.TryRead(local.m_address + offset::Player::pPhysics, physical) ||
|
||||
!IsUsableAddress(physical) ||
|
||||
!m.TryRead(physical + offset::Physics::Stamina, stamina) ||
|
||||
!m.TryRead(physical + offset::Physics::HandsStamina, handsStamina) ||
|
||||
!IsUsableAddress(stamina) ||
|
||||
!IsUsableAddress(handsStamina))
|
||||
{
|
||||
ResetStaminaOverride();
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_staminaOverride.captured &&
|
||||
(m_staminaOverride.player != local.m_address ||
|
||||
m_staminaOverride.physical != physical ||
|
||||
m_staminaOverride.stamina != stamina ||
|
||||
m_staminaOverride.handsStamina != handsStamina))
|
||||
{
|
||||
ResetStaminaOverride();
|
||||
}
|
||||
|
||||
float staminaCapacity = 0.f;
|
||||
float handsCapacity = 0.f;
|
||||
if (!m.TryRead(physical + offset::Physics::StaminaCapacity, staminaCapacity) ||
|
||||
!m.TryRead(physical + offset::Physics::HandsCapacity, handsCapacity) ||
|
||||
!IsUsableCapacity(staminaCapacity) ||
|
||||
!IsUsableCapacity(handsCapacity))
|
||||
{
|
||||
ResetStaminaOverride();
|
||||
return;
|
||||
}
|
||||
|
||||
if (!m_staminaOverride.captured)
|
||||
{
|
||||
float staminaCurrent = 0.f;
|
||||
float handsCurrent = 0.f;
|
||||
if (!m.TryRead(stamina + offset::PhysicalState::Current, staminaCurrent) ||
|
||||
!m.TryRead(handsStamina + offset::PhysicalState::Current, handsCurrent) ||
|
||||
!IsFiniteValue(staminaCurrent) ||
|
||||
!IsFiniteValue(handsCurrent))
|
||||
{
|
||||
ResetStaminaOverride();
|
||||
return;
|
||||
}
|
||||
|
||||
m_staminaOverride.player = local.m_address;
|
||||
m_staminaOverride.physical = physical;
|
||||
m_staminaOverride.stamina = stamina;
|
||||
m_staminaOverride.handsStamina = handsStamina;
|
||||
m_staminaOverride.staminaCurrent = staminaCurrent;
|
||||
m_staminaOverride.handsCurrent = handsCurrent;
|
||||
m_staminaOverride.captured = true;
|
||||
}
|
||||
|
||||
// Both current fields and both capacities are dump-confirmed object fields.
|
||||
m.TryWrite<float>(stamina + offset::PhysicalState::Current, staminaCapacity);
|
||||
m.TryWrite<float>(handsStamina + offset::PhysicalState::Current, handsCapacity);
|
||||
}
|
||||
|
||||
void CFramework::UpdateFallDamageOverride()
|
||||
{
|
||||
uintptr_t physical = 0;
|
||||
if (!m.TryRead(local.m_address + offset::Player::pPhysics, physical) ||
|
||||
!IsUsableAddress(physical))
|
||||
{
|
||||
ResetFallDamageOverride();
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_fallDamageOverride.captured &&
|
||||
(m_fallDamageOverride.player != local.m_address ||
|
||||
m_fallDamageOverride.physical != physical))
|
||||
{
|
||||
ResetFallDamageOverride();
|
||||
}
|
||||
|
||||
if (!m_fallDamageOverride.captured)
|
||||
{
|
||||
float multiplier = 0.f;
|
||||
if (!m.TryRead(physical + offset::Physics::FallDamageMultiplier, multiplier) ||
|
||||
!IsFiniteValue(multiplier))
|
||||
{
|
||||
ResetFallDamageOverride();
|
||||
return;
|
||||
}
|
||||
|
||||
m_fallDamageOverride.player = local.m_address;
|
||||
m_fallDamageOverride.physical = physical;
|
||||
m_fallDamageOverride.multiplier = multiplier;
|
||||
m_fallDamageOverride.captured = true;
|
||||
}
|
||||
|
||||
m.TryWrite<float>(physical + offset::Physics::FallDamageMultiplier, 0.f);
|
||||
}
|
||||
|
||||
void CFramework::UpdateSwayOverride()
|
||||
{
|
||||
uintptr_t weaponAnimation = 0;
|
||||
uintptr_t motionEffector = 0;
|
||||
if (!m.TryRead(local.m_address + offset::Player::pWeaponAnimation, weaponAnimation) ||
|
||||
!IsUsableAddress(weaponAnimation) ||
|
||||
!m.TryRead(weaponAnimation + offset::WeaponAnimations::MotionReact, motionEffector) ||
|
||||
!IsUsableAddress(motionEffector))
|
||||
{
|
||||
ResetSwayOverride();
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_swayOverride.captured &&
|
||||
(m_swayOverride.player != local.m_address ||
|
||||
m_swayOverride.weaponAnimation != weaponAnimation ||
|
||||
m_swayOverride.motionEffector != motionEffector))
|
||||
{
|
||||
ResetSwayOverride();
|
||||
}
|
||||
|
||||
if (!m_swayOverride.captured)
|
||||
{
|
||||
Vector3 swayFactors{};
|
||||
if (!m.TryRead(motionEffector + offset::MotionEffector::SwayFactors, swayFactors) ||
|
||||
!IsFiniteVector(swayFactors))
|
||||
{
|
||||
ResetSwayOverride();
|
||||
return;
|
||||
}
|
||||
|
||||
m_swayOverride.player = local.m_address;
|
||||
m_swayOverride.weaponAnimation = weaponAnimation;
|
||||
m_swayOverride.motionEffector = motionEffector;
|
||||
m_swayOverride.swayFactors = swayFactors;
|
||||
m_swayOverride.captured = true;
|
||||
}
|
||||
|
||||
m.TryWrite<Vector3>(motionEffector + offset::MotionEffector::SwayFactors, Vector3{});
|
||||
}
|
||||
|
||||
void CFramework::ResetStaminaOverride()
|
||||
{
|
||||
if (m_staminaOverride.captured)
|
||||
{
|
||||
if (IsUsableAddress(m_staminaOverride.stamina))
|
||||
m.TryWrite<float>(m_staminaOverride.stamina + offset::PhysicalState::Current, m_staminaOverride.staminaCurrent);
|
||||
if (IsUsableAddress(m_staminaOverride.handsStamina))
|
||||
m.TryWrite<float>(m_staminaOverride.handsStamina + offset::PhysicalState::Current, m_staminaOverride.handsCurrent);
|
||||
}
|
||||
|
||||
m_staminaOverride = {};
|
||||
}
|
||||
|
||||
void CFramework::ResetFallDamageOverride()
|
||||
{
|
||||
if (m_fallDamageOverride.captured && IsUsableAddress(m_fallDamageOverride.physical))
|
||||
m.TryWrite<float>(m_fallDamageOverride.physical + offset::Physics::FallDamageMultiplier, m_fallDamageOverride.multiplier);
|
||||
|
||||
m_fallDamageOverride = {};
|
||||
}
|
||||
|
||||
void CFramework::ResetSwayOverride()
|
||||
{
|
||||
if (m_swayOverride.captured && IsUsableAddress(m_swayOverride.motionEffector))
|
||||
m.TryWrite<Vector3>(m_swayOverride.motionEffector + offset::MotionEffector::SwayFactors, m_swayOverride.swayFactors);
|
||||
|
||||
m_swayOverride = {};
|
||||
}
|
||||
|
||||
void CFramework::ResetFeatureOverrides()
|
||||
{
|
||||
ResetStaminaOverride();
|
||||
ResetFallDamageOverride();
|
||||
ResetSwayOverride();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -22,10 +22,52 @@ public:
|
|||
void RenderMenu();
|
||||
void RenderESP();
|
||||
void AimAll();
|
||||
void UpdateFeatures();
|
||||
void ResetFeatureOverrides();
|
||||
private:
|
||||
struct StaminaOverrideState
|
||||
{
|
||||
uintptr_t player{ 0 };
|
||||
uintptr_t physical{ 0 };
|
||||
uintptr_t stamina{ 0 };
|
||||
uintptr_t handsStamina{ 0 };
|
||||
float staminaCurrent{ 0.f };
|
||||
float handsCurrent{ 0.f };
|
||||
bool captured{ false };
|
||||
};
|
||||
|
||||
struct FallDamageOverrideState
|
||||
{
|
||||
uintptr_t player{ 0 };
|
||||
uintptr_t physical{ 0 };
|
||||
float multiplier{ 0.f };
|
||||
bool captured{ false };
|
||||
};
|
||||
|
||||
struct SwayOverrideState
|
||||
{
|
||||
uintptr_t player{ 0 };
|
||||
uintptr_t weaponAnimation{ 0 };
|
||||
uintptr_t motionEffector{ 0 };
|
||||
Vector3 swayFactors{};
|
||||
bool captured{ false };
|
||||
};
|
||||
|
||||
void UpdateStaminaOverride();
|
||||
void UpdateFallDamageOverride();
|
||||
void UpdateSwayOverride();
|
||||
void ResetStaminaOverride();
|
||||
void ResetFallDamageOverride();
|
||||
void ResetSwayOverride();
|
||||
|
||||
CGame* tarkov = new CGame();
|
||||
CPlayer local;
|
||||
|
||||
StaminaOverrideState m_staminaOverride{};
|
||||
FallDamageOverrideState m_fallDamageOverride{};
|
||||
SwayOverrideState m_swayOverride{};
|
||||
|
||||
|
||||
std::mutex m_mtxEntityList;
|
||||
std::mutex m_mtxExfilList;
|
||||
std::mutex m_mtxItemList;
|
||||
|
|
@ -81,6 +123,9 @@ private:
|
|||
void AimMemory(const Vector3& target, const Vector3& localPos);
|
||||
void ScanRotationOffset(); // 2阶段扫描: Player->MC 与 MC->Rotation 偏移
|
||||
|
||||
// Runtime feature backup state is declared above.
|
||||
|
||||
|
||||
// 旋转偏移扫描器状态
|
||||
int m_scanPhase = 0; // 0 = 待机/Step1, 1 = 等待转动鼠标后 Step2
|
||||
char m_scanResult[192]{ 0 };
|
||||
|
|
|
|||
|
|
@ -120,10 +120,16 @@ void CFramework::RenderMenu()
|
|||
ImGui::Separator();
|
||||
ImGui::Spacing();
|
||||
|
||||
ImGui::TextDisabled("内存修改类功能已移除");
|
||||
ImGui::TextDisabled("(Mono 版偏移不兼容, 防止乱写内存)");
|
||||
ImGui::Checkbox("无限耐力", &g.g_InfStamina);
|
||||
ImGui::Checkbox("无跌落伤害", &g.g_NoFallDamage);
|
||||
ImGui::Checkbox("无摇摆", &g.g_NoSway);
|
||||
|
||||
ImGui::Spacing();
|
||||
ImGui::Separator();
|
||||
ImGui::TextDisabled("仅写入 dump 已确认的对象字段");
|
||||
ImGui::TextDisabled("后座 / 故障:等待下一轮类布局确认");
|
||||
ImGui::EndChild();
|
||||
|
||||
|
||||
ImGui::BeginChild("##C021", ImVec2(ImGui::GetContentRegionAvail()), true);
|
||||
|
||||
ImGui::EndChild();
|
||||
|
|
@ -305,9 +311,18 @@ void CFramework::RenderMenu()
|
|||
case 2: // Misc
|
||||
ImGui::BeginChild("##C200", ImVec2(ImGui::GetContentRegionAvail()), true);
|
||||
|
||||
ImGui::Text("功能状态");
|
||||
ImGui::Separator();
|
||||
ImGui::Spacing();
|
||||
ImGui::Text("局内:%s", tarkov->IsInRaid() ? "是" : "否");
|
||||
ImGui::Text("本地:0x%llX", (unsigned long long)local.m_address);
|
||||
ImGui::TextDisabled("关闭开关或离开局内会恢复原值");
|
||||
ImGui::TextDisabled("非法/不完整指针链不会写入");
|
||||
|
||||
ImGui::EndChild();
|
||||
break;
|
||||
case 3: // Setting
|
||||
|
||||
ImGui::BeginChild("##C300", ImVec2(ImGui::GetContentRegionAvail()), true);
|
||||
|
||||
ImGui::Text("退出");
|
||||
|
|
|
|||
|
|
@ -102,9 +102,22 @@ namespace offset
|
|||
namespace WeaponAnimations
|
||||
{
|
||||
constexpr auto Breath = 0x30;
|
||||
constexpr auto MotionReact = 0x40; // MotionEffector
|
||||
constexpr auto isAiming = 0x1C5;
|
||||
}
|
||||
|
||||
// -.MotionEffector
|
||||
namespace MotionEffector
|
||||
{
|
||||
constexpr auto SwayFactors = 0x94; // UnityEngine.Vector3
|
||||
}
|
||||
|
||||
// -.GClass774 (stamina/oxygen state)
|
||||
namespace PhysicalState
|
||||
{
|
||||
constexpr auto Current = 0x48; // Single
|
||||
}
|
||||
|
||||
// .BasePhysicalClass
|
||||
namespace Physics
|
||||
{
|
||||
|
|
@ -112,6 +125,9 @@ namespace offset
|
|||
constexpr auto HandsStamina = 0x40;
|
||||
constexpr auto Oxygen = 0x48;
|
||||
constexpr auto FallDamageMultiplier = 0xBC;
|
||||
constexpr auto StaminaCapacity = 0xC0;
|
||||
constexpr auto HandsCapacity = 0xC8;
|
||||
constexpr auto OxygenRestoreRate = 0xD4;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -26,8 +26,12 @@ void CFramework::UpdateList()
|
|||
std::vector<CPlayer> list_player{};
|
||||
std::vector<uintptr_t> list_grenade{};
|
||||
|
||||
if (g.g_ESP || g.g_Aim)
|
||||
if (g.g_ESP || g.g_Aim || g.g_InfStamina || g.g_NoFallDamage || g.g_NoSway)
|
||||
{
|
||||
|
||||
// Failed refreshes must not leave a stale player address available to memory features.
|
||||
local.m_address = 0;
|
||||
|
||||
// not In raid
|
||||
if (!tarkov->Update())
|
||||
continue;
|
||||
|
|
|
|||
|
|
@ -29,7 +29,11 @@ struct Globals
|
|||
float g_ESP_MaxDistance = 500.f;
|
||||
float g_ESP_MaxItemDistance = 50.f;
|
||||
|
||||
// Misc (内存修改类功能已移除: Mono 版偏移不兼容, 防止乱写内存)
|
||||
// Misc (仅使用 dump 已确认的对象字段)
|
||||
bool g_InfStamina = false;
|
||||
bool g_NoFallDamage = false;
|
||||
bool g_NoSway = false;
|
||||
|
||||
|
||||
// Aim (自瞄)
|
||||
bool g_Aim = false;
|
||||
|
|
|
|||
|
|
@ -46,11 +46,36 @@ public:
|
|||
{
|
||||
WriteProcessMemory(pHandle, reinterpret_cast<void*>(address), &value, sizeof(T), NULL);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
bool TryRead(const uintptr_t& address, T& value) const noexcept
|
||||
{
|
||||
value = T{};
|
||||
if (!pHandle || !address)
|
||||
return false;
|
||||
|
||||
SIZE_T sizeRead = 0;
|
||||
return !!ReadProcessMemory(pHandle, reinterpret_cast<const void*>(address), &value, sizeof(T), &sizeRead)
|
||||
&& sizeRead == sizeof(T);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
bool TryWrite(const uintptr_t& address, const T& value) const noexcept
|
||||
{
|
||||
if (!pHandle || !address)
|
||||
return false;
|
||||
|
||||
SIZE_T sizeWritten = 0;
|
||||
return !!WriteProcessMemory(pHandle, reinterpret_cast<void*>(address), &value, sizeof(T), &sizeWritten)
|
||||
&& sizeWritten == sizeof(T);
|
||||
}
|
||||
|
||||
uintptr_t ReadChain(uintptr_t address, const std::vector<uintptr_t>& offsets)
|
||||
{
|
||||
if (!address || offsets.empty())
|
||||
return 0;
|
||||
|
||||
|
||||
uintptr_t result = Read<uintptr_t>(address + offsets.front());
|
||||
for (std::size_t i = 1; i < offsets.size(); ++i)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -23,6 +23,9 @@ void Overlay::OverlayUserFunction()
|
|||
if (g.g_ESP)
|
||||
cheat->RenderESP();
|
||||
|
||||
// 只在局内且指针链完整时更新 dump 已确认的运行时字段。
|
||||
cheat->UpdateFeatures();
|
||||
|
||||
// 自瞄/压枪 (Aim.cpp)
|
||||
cheat->AimAll();
|
||||
|
||||
|
|
@ -91,9 +94,10 @@ int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine
|
|||
std::thread([&]() { cheat->UpdateStaticList(); }).detach();
|
||||
|
||||
overlay->OverlayLoop();
|
||||
g.process_active = false;
|
||||
cheat->ResetFeatureOverrides();
|
||||
overlay->DestroyOverlay();
|
||||
m.DetachProcess();
|
||||
g.process_active = false;
|
||||
|
||||
return 0;
|
||||
}
|
||||
Loading…
Reference in New Issue