fix: update offsets from dump

This commit is contained in:
kedou 2026-08-07 20:42:19 +08:00
parent 4dca333267
commit 52454e23ea
11 changed files with 278 additions and 160 deletions

View File

@ -365,8 +365,8 @@ void CFramework::ScanRotationOffset()
s_cands.clear(); s_cands.clear();
m_scanResult[0] = 0; m_scanResult[0] = 0;
// Player 構造 [0x100, 0x700] 内の 8byte アライン済みポインタ // Player 构造 [0x20, 0x700] 内的 8byte アライン済みポインタ
for (uintptr_t mcOff = 0x100; mcOff <= 0x700; mcOff += 0x8) for (uintptr_t mcOff = 0x20; mcOff <= 0x700; mcOff += 0x8)
{ {
uintptr_t ptr = m.Read<uintptr_t>(local.m_address + mcOff); uintptr_t ptr = m.Read<uintptr_t>(local.m_address + mcOff);
if (!ptr) if (!ptr)
@ -392,7 +392,7 @@ void CFramework::ScanRotationOffset()
if (s_cands.empty()) if (s_cands.empty())
{ {
strcpy_s(m_scanResult, "未找到候选,请扩大扫描范围 (0x100-0x700)"); strcpy_s(m_scanResult, "未找到候选,请扩大扫描范围 (0x20-0x700)");
m_scanPhase = 0; m_scanPhase = 0;
return; return;
} }

View File

@ -102,6 +102,9 @@ private:
} }
void HealthBar(float x, float y, float w, float h, int value, int v_max) void HealthBar(float x, float y, float w, float h, int value, int v_max)
{ {
if (v_max <= 0)
return;
RectFilled(x, y, x + w, y + h, ImColor(0.f, 0.f, 0.f, 0.725f), 0.f, 0); RectFilled(x, y, x + w, y + h, ImColor(0.f, 0.f, 0.f, 0.725f), 0.f, 0);
RectFilled(x, y, x + w, y + ((h / float(v_max)) * (float)value), ImColor(min(510 * (v_max - value) / 100, 255), min(510 * value / 100, 255), 25, 255), 0.0f, 0); RectFilled(x, y, x + w, y + ((h / float(v_max)) * (float)value), ImColor(min(510 * (v_max - value) / 100, 255), min(510 * value / 100, 255), 25, 255), 0.0f, 0);
} }

View File

@ -234,11 +234,11 @@ void CFramework::RenderMenu()
ImGui::InputInt("MC", &g.g_AimMcOffset, 8, 8, ImGuiInputTextFlags_CharsHexadecimal); ImGui::InputInt("MC", &g.g_AimMcOffset, 8, 8, ImGuiInputTextFlags_CharsHexadecimal);
ImGui::InputInt("旋转", &g.g_AimRotOffset, 4, 4, ImGuiInputTextFlags_CharsHexadecimal); ImGui::InputInt("旋转", &g.g_AimRotOffset, 4, 4, ImGuiInputTextFlags_CharsHexadecimal);
// 误改输入框会直接导致 Memory 模式失效, 一键恢复本客户端实测默认值 // dump.txt: MovementContext = Player +0x58, Rotation_1 = MovementContext +0x27C
if (ImGui::Button("重置偏移 (0x1E0/0x3AC)", ImVec2(ImGui::GetContentRegionAvail().x, 0.f))) if (ImGui::Button("重置偏移 (0x58/0x27C)", ImVec2(ImGui::GetContentRegionAvail().x, 0.f)))
{ {
g.g_AimMcOffset = 0x1E0; g.g_AimMcOffset = 0x58;
g.g_AimRotOffset = 0x3AC; g.g_AimRotOffset = 0x27C;
} }
if (ImGui::Button("扫描旋转偏移(两步)", ImVec2(ImGui::GetContentRegionAvail().x, 0.f))) if (ImGui::Button("扫描旋转偏移(两步)", ImVec2(ImGui::GetContentRegionAvail().x, 0.f)))

View File

@ -4,83 +4,130 @@ std::vector<int> PlayerBoneList{ BONE_PELVIS, BONE_LEFT_THIGH, BONE_LEFT_FOOT, B
bool CPlayer::Update() bool CPlayer::Update()
{ {
// Position m_vecOrigin = GetBonePosition(BONE_ORIGIN);
m_vecOrigin = GetBonePosition(BONE_ORIGIN);
if (Vec3_Empty(m_vecOrigin)) return !Vec3_Empty(m_vecOrigin);
return false;
return true;
} }
void CPlayer::UpdateStatic() void CPlayer::UpdateStatic()
{ {
// Health m_pHealthController = 0;
uintptr_t m_pHealthController = m.Read<uintptr_t>(m_address + offset::Player::pHealthController); // HealthController m_pProfile = 0;
uintptr_t m_pHealthBody = m.Read<uintptr_t>(m_pHealthController + 0x68); m_pInfo = 0;
m_pBodyController = m.Read<uintptr_t>(m_pHealthBody + 0x18); m_pSetting = 0;
m_pWeaponAnimation = 0;
m_iSpawnType = 0;
m_pBoneArray = {};
m_vecBoneList.clear();
// Bone if (!m_address)
uintptr_t boneMatrix = m.ReadChain(m_address, { offset::Player::pPlayerBody, 0x30, 0x30, 0x10 }); return;
m_pBoneArray = m.Read<CBoneArray>(boneMatrix + 0x20);
// Other pointer // EFT.Player fields from dump.txt.
m_pProfile = m.Read<uintptr_t>(m_address + offset::Player::pProfile); m_pHealthController = m.Read<uintptr_t>(m_address + offset::Player::pHealthController);
m_pInfo = m.Read<uintptr_t>(m_pProfile + 0x40); m_pWeaponAnimation = m.Read<uintptr_t>(m_address + offset::Player::pWeaponAnimation);
m_pSetting = m.Read<uintptr_t>(m_pInfo + 0x50);
m.Read<uintptr_t>(m_address + offset::Player::pWeaponAnimation); m_pProfile = m.Read<uintptr_t>(m_address + offset::Player::pProfile);
if (m_pProfile)
{
m_pInfo = m.Read<uintptr_t>(m_pProfile + offset::Profile::pInfoClass);
if (m_pInfo)
{
m_pSetting = m.Read<uintptr_t>(m_pInfo + offset::InfoClass::profileInfoSettingsClass);
if (m_pSetting)
m_iSpawnType = m.Read<int>(m_pSetting + 0x10); // ProfileInfoSettingsClass.Role
}
}
// Player data // PlayerBody +0x30 = SkeletonRootJoint; Skeleton +0x30 = values List;
m_iSpawnType = m.Read<int>(m_pSetting + 0x10); // SpawnType // List +0x10 = Transform[] data pointer, as described by dump.txt.
const uintptr_t boneMatrix = m.ReadChain(m_address, { offset::Player::pPlayerBody, 0x30, 0x30, 0x10 });
if (boneMatrix)
m_pBoneArray = m.Read<CBoneArray>(boneMatrix + 0x20);
} }
void CPlayer::UpdateHealth() void CPlayer::UpdateHealth()
{ {
float h{ 0 }; m_fHealth = 0.f;
float max{ 0 }; m_fHealthMax = 0.f;
for (int j = 0; j < 7; j++) if (!m_address)
{ return;
uintptr_t body_part = m.Read<uintptr_t>(m_pBodyController + 0x30 + (j * 0x18));
uintptr_t health_container = m.Read<uintptr_t>(body_part + 0x10);
h += m.Read<float>(health_container + 0x10);
max += m.Read<float>(health_container + 0x14);
}
m_fHealth = h; const uintptr_t healthController = m_pHealthController != 0
m_fHealthMax = max; ? m_pHealthController
: m.Read<uintptr_t>(m_address + offset::Player::pHealthController);
if (!healthController)
return;
// GClass3009<T>.Dictionary_0_1 contains BodyPartState<T> entries.
const uintptr_t bodyParts = m.Read<uintptr_t>(healthController + offset::HealthController::BodyParts);
if (!bodyParts)
return;
const uintptr_t entries = m.Read<uintptr_t>(bodyParts + offset::HealthController::DictionaryEntries);
const int entryCount = m.Read<int>(bodyParts + offset::HealthController::DictionaryCount);
if (!entries || entryCount <= 0 || entryCount > 64)
return;
for (int i = 0; i < entryCount; ++i)
{
const uintptr_t entry = entries + offset::HealthController::ArrayData
+ static_cast<uintptr_t>(i) * offset::HealthController::EntryStride;
const uintptr_t bodyPartState = m.Read<uintptr_t>(entry + offset::HealthController::EntryValue);
if (!bodyPartState)
continue;
const uintptr_t healthValue = m.Read<uintptr_t>(bodyPartState + offset::HealthController::BodyPartHealth);
if (!healthValue)
continue;
m_fHealth += m.Read<float>(healthValue + offset::HealthController::HealthValue + offset::HealthController::Current);
m_fHealthMax += m.Read<float>(healthValue + offset::HealthController::HealthValue + offset::HealthController::Maximum);
}
} }
void CPlayer::UpdateBone() void CPlayer::UpdateBone()
{ {
m_vecHeadOrigin = GetBonePosition(BONE_HEAD); m_vecHeadOrigin = GetBonePosition(BONE_HEAD);
m_vecNeckOrigin = GetBonePosition(BONE_NECK); m_vecNeckOrigin = GetBonePosition(BONE_NECK);
m_vecBoneList.clear();
if (g.g_ESP_Skeleton) if (!g.g_ESP_Skeleton)
{ return;
for (auto& id : PlayerBoneList)
{
Vector3 bone = GetBonePosition(id);
if (Vec3_Empty(bone)) { m_vecBoneList.reserve(PlayerBoneList.size());
m_vecBoneList.clear(); for (const auto id : PlayerBoneList)
break; {
} const Vector3 bone = GetBonePosition(id);
if (Vec3_Empty(bone))
{
m_vecBoneList.clear();
return;
}
m_vecBoneList.push_back(bone); m_vecBoneList.push_back(bone);
} }
}
} }
bool CPlayer::IsAiming() bool CPlayer::IsAiming()
{ {
return m.Read<bool>(m_pWeaponAnimation + offset::WeaponAnimations::isAiming); return m_pWeaponAnimation != 0 && m.Read<bool>(m_pWeaponAnimation + offset::WeaponAnimations::isAiming);
} }
Vector3 CPlayer::GetBonePosition(const int BoneId) Vector3 CPlayer::GetBonePosition(const int BoneId)
{ {
uintptr_t TransformPtr = m.Read<uintptr_t>(m_pBoneArray.address[BoneId] + 0x10); constexpr int kBoneCount = static_cast<int>(sizeof(m_pBoneArray.address) / sizeof(m_pBoneArray.address[0]));
if (BoneId < 0 || BoneId >= kBoneCount)
return {};
return GetTransformPosition(TransformPtr); const uintptr_t bone = m_pBoneArray.address[BoneId];
if (!bone)
return {};
uintptr_t transform = m.Read<uintptr_t>(bone + 0x10);
if (!transform)
return {};
return GetTransformPosition(transform);
} }

View File

@ -13,7 +13,7 @@ public:
uintptr_t m_pProfile{ 0 }; uintptr_t m_pProfile{ 0 };
uintptr_t m_pInfo{ 0 }; uintptr_t m_pInfo{ 0 };
uintptr_t m_pSetting{ 0 }; uintptr_t m_pSetting{ 0 };
uintptr_t m_pBodyController{ 0 }; uintptr_t m_pHealthController{ 0 };
uintptr_t m_pWeaponAnimation{ 0 }; uintptr_t m_pWeaponAnimation{ 0 };
// データ // データ

View File

@ -21,6 +21,9 @@ float GetDistance(Vector3 value1, Vector3 value2)
Vector3 GetTransformPosition(uintptr_t& transform) Vector3 GetTransformPosition(uintptr_t& transform)
{ {
if (!transform)
return {};
__m128 result{}; __m128 result{};
const __m128 mulVec0 = { -2.000, 2.000, -2.000, 0.000 }; const __m128 mulVec0 = { -2.000, 2.000, -2.000, 0.000 };
@ -29,6 +32,9 @@ Vector3 GetTransformPosition(uintptr_t& transform)
TransformAccessReadOnly pTransformAccessReadOnly = m.Read<TransformAccessReadOnly>(transform + 0x38); TransformAccessReadOnly pTransformAccessReadOnly = m.Read<TransformAccessReadOnly>(transform + 0x38);
unsigned int Index = m.Read<unsigned int>(transform + 0x40); unsigned int Index = m.Read<unsigned int>(transform + 0x40);
if (!pTransformAccessReadOnly.pTransformData)
return {};
TransformData transform_data = m.Read<TransformData>(pTransformAccessReadOnly.pTransformData + 0x18); TransformData transform_data = m.Read<TransformData>(pTransformAccessReadOnly.pTransformData + 0x18);
if (transform_data.pTransformArray && transform_data.pTransformIndices) if (transform_data.pTransformArray && transform_data.pTransformIndices)
@ -96,6 +102,9 @@ bool WorldToScreen(Matrix ViewMatrix, const Vector2& GameSize, Vector3& vIn, Vec
uintptr_t GetObjectFromList(uintptr_t listPtr, uintptr_t lastObjectPtr, const char* objectName) uintptr_t GetObjectFromList(uintptr_t listPtr, uintptr_t lastObjectPtr, const char* objectName)
{ {
if (!listPtr || !objectName)
return 0;
char ObjectName[128]{}; char ObjectName[128]{};
uintptr_t classNamePtr = 0; uintptr_t classNamePtr = 0;
BaseObject activeObject = m.Read<BaseObject>(listPtr); BaseObject activeObject = m.Read<BaseObject>(listPtr);
@ -111,7 +120,8 @@ uintptr_t GetObjectFromList(uintptr_t listPtr, uintptr_t lastObjectPtr, const ch
if (strcmp(ObjectName, objectName) == 0) if (strcmp(ObjectName, objectName) == 0)
return activeObject.object; return activeObject.object;
Sleep(1); if (!activeObject.nextObjectLink)
break;
activeObject = m.Read<BaseObject>(activeObject.nextObjectLink); activeObject = m.Read<BaseObject>(activeObject.nextObjectLink);
} }
@ -135,13 +145,22 @@ bool CGame::InitAddress()
{ {
// GameObjectManager // GameObjectManager
uintptr_t pGOM = m.Read<uintptr_t>(m.m_gBaseAddress + offset::dwGameObjectManager); uintptr_t pGOM = m.Read<uintptr_t>(m.m_gBaseAddress + offset::dwGameObjectManager);
if (!pGOM)
{
m_objGameObjectManager = {};
return false;
}
m_objGameObjectManager = m.Read<GameObjectManager>(pGOM); m_objGameObjectManager = m.Read<GameObjectManager>(pGOM);
return m_objGameObjectManager.lastActiveObject != NULL ? true : false; return m_objGameObjectManager.lastActiveObject != 0;
} }
bool CGame::Update() bool CGame::Update()
{ {
m_pGameWorld = 0;
m_pLocalGameWorld = 0;
// GameWorld // GameWorld
uintptr_t activeNodes = m.Read<uintptr_t>(m_objGameObjectManager.ActiveNodes); uintptr_t activeNodes = m.Read<uintptr_t>(m_objGameObjectManager.ActiveNodes);
uintptr_t lastActiveNode = m.Read<uintptr_t>(m_objGameObjectManager.LastActiveNode); uintptr_t lastActiveNode = m.Read<uintptr_t>(m_objGameObjectManager.LastActiveNode);
@ -161,21 +180,30 @@ bool CGame::Update()
bool CGame::UpdateCamera() bool CGame::UpdateCamera()
{ {
Camera all_cameras = m.Read<Camera>(m.Read<uintptr_t>(m.m_gBaseAddress + offset::dwAllCamera)); m_pFpsCamera = 0;
if (all_cameras.count == 0) uintptr_t all_camera_ptr = m.Read<uintptr_t>(m.m_gBaseAddress + offset::dwAllCamera);
if (!all_camera_ptr)
return false;
Camera all_cameras = m.Read<Camera>(all_camera_ptr);
if (all_cameras.list == 0 || all_cameras.count == 0)
return false; return false;
for (int i = 0; i < all_cameras.count; i++) for (int i = 0; i < all_cameras.count; i++)
{ {
uintptr_t camera = m.Read<uintptr_t>(all_cameras.list + (i * 0x8)); uintptr_t camera = m.Read<uintptr_t>(all_cameras.list + (i * 0x8));
if (camera != NULL) if (camera != 0)
{ {
uintptr_t camera_obj = m.Read<uintptr_t>(camera + 0x30); uintptr_t camera_obj = m.Read<uintptr_t>(camera + 0x30);
if (!camera_obj)
continue;
uintptr_t camera_name_ptr = m.Read<uintptr_t>(camera_obj + 0x60); uintptr_t camera_name_ptr = m.Read<uintptr_t>(camera_obj + 0x60);
if (!camera_name_ptr) if (!camera_name_ptr)
return false; continue;
char name[64]{}; char name[64]{};
m.ReadString(camera_name_ptr, name, sizeof(name)); m.ReadString(camera_name_ptr, name, sizeof(name));
@ -197,9 +225,17 @@ bool CGame::IsInRaid()
Matrix CGame::GetViewMatrix() Matrix CGame::GetViewMatrix()
{ {
if (!m_pFpsCamera)
return {};
// ここ省略できるかチェック // ここ省略できるかチェック
uintptr_t dw = m.Read<uintptr_t>(m_pFpsCamera + 0x30); uintptr_t dw = m.Read<uintptr_t>(m_pFpsCamera + 0x30);
if (!dw)
return {};
uintptr_t viewmatrix_ptr = m.Read<uintptr_t>(dw + 0x18); uintptr_t viewmatrix_ptr = m.Read<uintptr_t>(dw + 0x18);
if (!viewmatrix_ptr)
return {};
return m.Read<Matrix>(viewmatrix_ptr + 0x100); return m.Read<Matrix>(viewmatrix_ptr + 0x100);
} }

View File

@ -1,81 +1,88 @@
#pragma once #pragma once
// by Unispect and UC // by Unispect and UC
namespace offset namespace offset
{ {
// Base // Base (not present in dump.txt; keep the native UnityPlayer values used by this build)
constexpr auto dwGameObjectManager = 0x1CF93E0; constexpr auto dwGameObjectManager = 0x1CF93E0;
constexpr auto dwAllCamera = 0x1BF8BC0; constexpr auto dwAllCamera = 0x1BF8BC0;
// EFT.GameWorld // EFT.GameWorld / EFT.ClientLocalGameWorld
constexpr auto ExfilController = 0x30; constexpr auto ExfilController = 0x30;
constexpr auto LootList = 0x118; constexpr auto LootList = 0x118;
constexpr auto RegisteredPlayers = 0x140; constexpr auto RegisteredPlayers = 0x140;
constexpr auto MainPlayer = 0x1A8; constexpr auto MainPlayer = 0x1A8;
constexpr auto GrenadeList = 0x200; constexpr auto GrenadeList = 0x200;
// EFT.Player // EFT.Player (dump.txt: EFT.Player)
namespace Player namespace Player
{ {
constexpr auto pPlayerBody = 0xC0; constexpr auto pPlayerBody = 0xC0; // _playerBody
constexpr auto pMovementContext = 0x1E0; // EXFIL DELTA (Mono 0.16.9) 实测. IL2CPP 0.15.x 是 0x170 constexpr auto pMovementContext = 0x58; // <MovementContext>k__BackingField
constexpr auto pWeaponAnimation = 0x1E0; // IL2CPP 版定义, Mono 版实际是 MovementContext (框架旧代码保留) constexpr auto pWeaponAnimation = 0x1E0; // <ProceduralWeaponAnimation>k__BackingField
constexpr auto pProfile = 0x600; constexpr auto pProfile = 0x618; // <Profile>k__BackingField
constexpr auto pPhysics = 0x610; constexpr auto pPhysics = 0x630; // Physical
constexpr auto pHealthController = 0x640; constexpr auto pHealthController = 0x660; // _healthController
} }
// EFT.MovementContext // EFT.MovementContext (dump.txt: EFT.MovementContext)
namespace MovementContext namespace MovementContext
{ {
// Vector2 (x = yaw, y = pitch) [rad] // Vector2 (x = yaw, y = pitch), in radians.
// EXFIL DELTA (Mono 0.16.9) 实测: +0x3AC = Rotation_1 (视角旋转) constexpr auto Rotation = 0x27C; // Rotation_1
// +0x3A4 = MovementDirection_1 (移动方向, 勿用!) 依据 Mono.Cecil 字段表 }
constexpr auto Rotation = 0x3AC;
}
// EFT.Profile // EFT.Profile
namespace Profile namespace Profile
{ {
constexpr auto Id = 0x10; // str constexpr auto Id = 0x10;
constexpr auto AccountId = 0x18; // str constexpr auto AccountId = 0x18;
constexpr auto pInfoClass = 0x40; // pointer constexpr auto pInfoClass = 0x40; // Info
} }
// EFT.Profile->InfoClass // EFT.Profile -> InfoClass
namespace InfoClass namespace InfoClass
{ {
constexpr auto Nickname = 0x10; // str constexpr auto Nickname = 0x10;
constexpr auto EntryPoint = 0x18; // str constexpr auto EntryPoint = 0x18;
constexpr auto MainProfileNickname = 0x20; // str constexpr auto MainProfileNickname = 0x20;
constexpr auto MGroupId = 0x28; // str constexpr auto MGroupId = 0x28;
constexpr auto TeamId = 0x30; // str constexpr auto TeamId = 0x30;
constexpr auto profileInfoSettingsClass = 0x50; // pointer constexpr auto profileInfoSettingsClass = 0x48; // ProfileInfoSettingsClass
constexpr auto eplayerSide_0 = 0x90; // int constexpr auto eplayerSide_0 = 0x88;
constexpr auto RegistrationDate = 0x94; // int constexpr auto RegistrationDate = 0x8C;
constexpr auto Type = 0x98; // int constexpr auto Type = 0x90;
} }
// EFT.Player->.BasePhysicalClass // EFT.HealthSystem.ActiveHealthController / GClass3009<T>
namespace Physics namespace HealthController
{ {
constexpr auto BodyParts = 0x68; // Dictionary<int, BodyPartState<T>>
constexpr auto DictionaryEntries = 0x18;
constexpr auto DictionaryCount = 0x40;
constexpr auto ArrayData = 0x20;
constexpr auto EntryStride = 0x18;
constexpr auto EntryValue = 0x10;
constexpr auto BodyPartHealth = 0x10; // BodyPartState.Health
constexpr auto HealthValue = 0x10; // HealthValue.Value
constexpr auto Current = 0x00;
constexpr auto Maximum = 0x04;
}
} // EFT.Animations.ProceduralWeaponAnimation
namespace WeaponAnimations
{
constexpr auto Breath = 0x30;
constexpr auto isAiming = 0x1C5;
}
// EFT.Animations.ProceduralWeaponAnimation // .BasePhysicalClass
namespace WeaponAnimations namespace Physics
{ {
constexpr auto Breath = 0x30; constexpr auto Stamina = 0x38;
constexpr auto isAiming = 0x1C5; constexpr auto HandsStamina = 0x40;
} constexpr auto Oxygen = 0x48;
constexpr auto FallDamageMultiplier = 0xBC;
// .BasePhysicalClass constexpr auto OxygenRestoreRate = 0xD4;
namespace Physics }
{
constexpr auto Stamina = 0x38;
constexpr auto HandsStamina = 0x40;
constexpr auto Oxygen = 0x40;
constexpr auto FallDamageMultiplier = 0xBC;
constexpr auto OxygenRestoreRate = 0xD4;
}
} }

View File

@ -91,12 +91,13 @@ void CFramework::UpdateStaticList() // C6262 :(
std::vector<CItem> list_item{}; std::vector<CItem> list_item{};
std::vector<CExfil> list_exfil{}; std::vector<CExfil> list_exfil{};
const auto GameWorld = tarkov->m_pLocalGameWorld;
tarkov->UpdateCamera(); tarkov->UpdateCamera();
// ItemList // ItemList
if (tarkov->Update()) if (tarkov->Update())
{ {
const auto GameWorld = tarkov->m_pLocalGameWorld;
// Exfil // Exfil
if (g.g_ESP_Exfil) if (g.g_ESP_Exfil)
{ {

View File

@ -53,13 +53,11 @@ struct Globals
float g_RecoilStrength = 4.f; // 每次下拉的鼠标单位(mickeys) float g_RecoilStrength = 4.f; // 每次下拉的鼠标单位(mickeys)
int g_RecoilInterval = 15; // 下拉间隔 [ms] int g_RecoilInterval = 15; // 下拉间隔 [ms]
// Aim Offset (运行时可调, 默认 = 本客户端实测值) // Aim Offset (运行时可调, 由 F:\desktop\Release\dump.txt 校准)
// Memory 模式自瞄用: Player + g_AimMcOffset -> + g_AimRotOffset (Vector2 yaw/pitch) // Memory 模式自瞄用: Player + g_AimMcOffset -> + g_AimRotOffset (Vector2 yaw/pitch)
// EXFIL DELTA (Mono 0.16.9) 实测: Player+0x1E0 = MovementContext // EFT.Player: MovementContext = +0x58; EFT.MovementContext: Rotation_1 = +0x27C
// +0x3A4 = MovementDirection(移动方向, 非视角!), +0x3AC = Rotation_1(视角旋转, 正解) int g_AimMcOffset = 0x58;
// 依据: Mono.Cecil 字段表 + 运行时验证 (0x3A4 读出的俯仰恒 0) int g_AimRotOffset = 0x27C;
int g_AimMcOffset = 0x1E0;
int g_AimRotOffset = 0x3AC;
// System // System
bool g_StreamProof = false; bool g_StreamProof = false;

View File

@ -58,19 +58,24 @@ uintptr_t Memory::GetModuleBase(const std::string moduleName)
entry.dwSize = sizeof(MODULEENTRY32); entry.dwSize = sizeof(MODULEENTRY32);
const auto snapShot = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, PID); const auto snapShot = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, PID);
while (Module32Next(snapShot, &entry)) if (snapShot == INVALID_HANDLE_VALUE)
return 0;
if (Module32First(snapShot, &entry))
{ {
if (!moduleName.compare(entry.szModule)) do
{ {
CloseHandle(snapShot); if (!moduleName.compare(entry.szModule))
return reinterpret_cast<uintptr_t>(entry.modBaseAddr); {
} const auto base = reinterpret_cast<uintptr_t>(entry.modBaseAddr);
CloseHandle(snapShot);
return base;
}
} while (Module32Next(snapShot, &entry));
} }
if (snapShot) CloseHandle(snapShot);
CloseHandle(snapShot); return 0;
return reinterpret_cast<uintptr_t>(entry.modBaseAddr);
} }
PROCESSENTRY32 Memory::GetProcess(const std::string processName) PROCESSENTRY32 Memory::GetProcess(const std::string processName)
@ -79,17 +84,22 @@ PROCESSENTRY32 Memory::GetProcess(const std::string processName)
entry.dwSize = sizeof(PROCESSENTRY32); entry.dwSize = sizeof(PROCESSENTRY32);
const auto snapShot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, NULL); const auto snapShot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, NULL);
while (Process32Next(snapShot, &entry)) if (snapShot == INVALID_HANDLE_VALUE)
return PROCESSENTRY32();
if (Process32First(snapShot, &entry))
{ {
if (!processName.compare(entry.szExeFile)) do
{ {
CloseHandle(snapShot); if (!processName.compare(entry.szExeFile))
return entry; {
} CloseHandle(snapShot);
return entry;
}
} while (Process32Next(snapShot, &entry));
} }
CloseHandle(snapShot); CloseHandle(snapShot);
return PROCESSENTRY32(); return PROCESSENTRY32();
} }

View File

@ -48,9 +48,17 @@ public:
} }
uintptr_t ReadChain(uintptr_t address, const std::vector<uintptr_t>& offsets) uintptr_t ReadChain(uintptr_t address, const std::vector<uintptr_t>& offsets)
{ {
uintptr_t result = Read<uintptr_t>(address + offsets.at(0)); if (!address || offsets.empty())
for (int i = 1; i < offsets.size(); i++) return 0;
result = Read<uintptr_t>(result + offsets.at(i));
uintptr_t result = Read<uintptr_t>(address + offsets.front());
for (std::size_t i = 1; i < offsets.size(); ++i)
{
if (!result)
return 0;
result = Read<uintptr_t>(result + offsets[i]);
}
return result; return result;
} }
@ -62,9 +70,17 @@ public:
} }
std::string ReadString(uintptr_t address, int length) std::string ReadString(uintptr_t address, int length)
{ {
static char Buffer[256]{}; if (!address || length <= 0)
ReadProcessMemory(pHandle, (void*)(address), Buffer, length, nullptr); return {};
char Buffer[256]{};
if (length >= static_cast<int>(sizeof(Buffer)))
length = static_cast<int>(sizeof(Buffer)) - 1;
if (!ReadProcessMemory(pHandle, reinterpret_cast<void*>(address), Buffer, length, nullptr))
return {};
Buffer[length] = '\0';
return std::string(Buffer); return std::string(Buffer);
}; };
wchar_t* ReadWString(uintptr_t address, int length) wchar_t* ReadWString(uintptr_t address, int length)