// lol id spoof #include "expresso_recovered.h" #include namespace expresso { // The five APlayerState slots, in the order they are pushed at // RVA 0x167B3C..0x167B5C const uint32_t kSteamIdCandidateSlots[5] = {0x3C0, 0x3C8, 0x3D0, 0x3B8, 0x3B0}; // RVA 0x167ABF..0x167BE9 find the real SteamID64 uint64_t LocateOriginalSteamId(const IMemory& mem, uint64_t game_base, LocalPlayerCache& cache, uint64_t now_ms) { const uint64_t pc = GetLocalPlayerController(mem, game_base, cache, now_ms); if (pc == 0) return 0; // 0x167B10 const uint64_t player_state = SafeReadPtr(mem, pc + world::kPlayerState); if (!IsCanonicalUserPointer(player_state)) return 0; // 0x167B2D for (size_t i = 0; i < kSteamIdCandidateSlotCount; ++i) { const uint64_t slot = player_state + kSteamIdCandidateSlots[i]; // the slot is a pointer to an object holding the ID const uint64_t pointee = SafeReadPtr(mem, slot); // 0x167B78 if (pointee > 0x10000) { // 0x167B80 for (uint32_t off = 0; off < kSteamIdProbeSpan; off += 8) { // 0x167BAD const uint64_t v = SafeReadPtr(mem, pointee + off); // 0x167B95 if (steam::IsPlausibleId64(v)) return v; // 0x167BA1 } } // the slot holds the ID inline const uint64_t direct = SafeReadPtr(mem, slot); // 0x167BB6 if (steam::IsPlausibleId64(direct)) return direct; // 0x167BC1 } return 0; // 0x167CB1 } // RVA 0x166B21..0x166B48 which regions get scanned namespace mem_flags { constexpr uint32_t kMemCommit = 0x00001000; constexpr uint32_t kMemImage = 0x01000000; constexpr uint32_t kPageReadWrite = 0x00000004; constexpr uint32_t kPageExecReadWrite= 0x00000040; constexpr uint32_t kPageGuard = 0x00000100; constexpr uint32_t kWritableMask = kPageReadWrite | kPageExecReadWrite; } // namespace mem_flags bool IsScannableRegion(const MemoryRegion& region) { if (region.state != mem_flags::kMemCommit) return false; // 0x166B21 if (region.type == mem_flags::kMemImage) return false; // 0x166B2A if ((region.protect & mem_flags::kWritableMask) == 0) return false; // 0x166B36 if (region.protect & mem_flags::kPageGuard) return false; // 0x166B3A if (region.size < 8) return false; // 0x166B44 return true; } // RVA 0x166690 the matcher size_t ScanRegionForQword(const IMemory& mem, uint64_t base, uint64_t size, uint64_t needle, uint64_t* out, size_t out_capacity) { if (out == nullptr || out_capacity == 0 || size < 8) return 0; const size_t cap = (out_capacity < kMaxHitsPerChunk) ? out_capacity : kMaxHitsPerChunk; size_t hits = 0; const uint64_t end = base + size - 7; // 0x1666A6 for (uint64_t p = base; p < end && hits < cap; p += 8) { uint64_t v = 0; if (!mem.Read(p, &v, sizeof(v))) continue; if (v == needle) out[hits++] = p; // 0x1666BF } return hits; } // RVA 0x1665E0 one guarded write bool ReplaceSteamIdAt(IMemory& mem, uint64_t addr, uint64_t new_id) { if (!IsCanonicalUserPointer(addr)) return false; // 0x166573 uint64_t current = 0; if (!mem.Read(addr, ¤t, sizeof(current))) return false; // 0x166625 if (!steam::IsPlausibleId64(current)) return false; // 0x166642 return mem.Write(addr, &new_id, sizeof(new_id)); // 0x166659 } // RVA 0x167260 apply across every located slot size_t ReplaceSteamId(IMemory& mem, uint64_t* slots, size_t& slot_count, uint64_t new_id) { if (slots == nullptr) return 0; size_t written = 0; for (size_t i = 0; i < slot_count; ++i) { if (ReplaceSteamIdAt(mem, slots[i], new_id)) { // 0x16734E slots[written++] = slots[i]; // 0x16736B compaction } } slot_count = written; // 0x16745B return written; } // RVA 0x167E70 the ProcessEvent blocklist const char* const kBlockedIdentityFunctions[11] = { "CreateSteamIDFromString", // .rdata 0x59AA20, inserted at 0x167F07 "GetUniqueNetIDFromPlayerState", // .rdata 0x59AA38 0x167F30 "SteamID64", // .rdata 0x59AA58 0x167F58 "GetLoginStatus", // 0x167F7F "GetUserAccount", // 0x167FA5 "GetUserPrivilege", // 0x167FCB "bAntiCheatProtected", // 0x167FF1 "GetUserID", // 0x168017 "LoginUser", // 0x16803D "OnPlayerLoginChanged", // 0x168063 "HasMatchmakingBan", // 0x16808C }; // RVA 0x168183: movabs r9, 0xCBF29CE484222325 // RVA 0x168192: movabs r8, 0x100000001B3 // RVA 0x1681A0: movzx eax,[rcx+rbx]; xor r9,rax; imul r9,r8 uint64_t Fnv1a64(const char* s) { uint64_t h = 0xCBF29CE484222325ULL; if (s == nullptr) return h; for (const unsigned char* p = reinterpret_cast(s); *p != 0; ++p) { h ^= static_cast(*p); h *= 0x100000001B3ULL; } return h; } bool IsBlockedIdentityFunction(const char* name) { if (name == nullptr) return false; const uint64_t h = Fnv1a64(name); for (size_t i = 0; i < kBlockedIdentityFunctionCount; ++i) { if (h == Fnv1a64(kBlockedIdentityFunctions[i]) && std::strcmp(name, kBlockedIdentityFunctions[i]) == 0) { return true; } } return false; } // RVA 0x167610 the event observer IdentityEvent ClassifyIdentityEvent(const char* function_name) { if (function_name == nullptr) return IdentityEvent::kOther; if (std::strstr(function_name, "ClientReservationPending")) // 0x1676DB return IdentityEvent::kReservationPending; if (std::strstr(function_name, "OnRep_ReservationAccepted")) // 0x16772E return IdentityEvent::kReservationAccepted; if (std::strstr(function_name, "ClientTravelInternal")) // 0x1677C3 return IdentityEvent::kClientTravelInternal; if (std::strstr(function_name, "ClientAckTraveling")) // 0x167885 return IdentityEvent::kClientAckTraveling; return IdentityEvent::kOther; } void OnIdentityEvent(IdentitySpoofState& state, IdentityEvent event, uint64_t now_ms) { if (!state.enabled) return; // 0x167633 switch (event) { case IdentityEvent::kReservationPending: // The client has asked the session for a slot. Mark it in flight and // stop any scan, so nothing races the handshake (0x1676FB/0x167706) state.reservation_pending = true; state.scan_in_progress = false; break; case IdentityEvent::kReservationAccepted: // The session said yes. Arm the swap for 1700 ms from now, late // enough that the reservation is settled, early enough to be in place // before the server reads the identity again. (0x16774E..0x167797) if (!state.reservation_pending && // 0x167755 state.candidate_count > 0 && // 0x167759 !state.applied && // 0x167762 !state.scan_in_progress && // 0x16776B !state.restore_armed) { // 0x167776 state.apply_deadline_ms = now_ms + kApplyDelayMs; // 0x167787 state.apply_armed = true; // 0x167797 } break; case IdentityEvent::kClientTravelInternal: // 0x1677E7 case IdentityEvent::kClientAckTraveling: // 0x1678A5 // Travel is under way: the identity has served its purpose. Disarm any // pending apply and schedule the real ID to go back 800 ms from now state.apply_armed = false; if (!state.scan_in_progress && state.applied && !state.restore_armed) { state.scan_in_progress = false; state.restore_armed = true; // 0x167825 state.restore_deadline_ms = now_ms + kRestoreDelayMs; // 0x167831 } state.reservation_pending = false; // 0x167859 break; case IdentityEvent::kOther: break; } } // RVA 0x167950 the per-frame tick, as a decision function IdentityTickAction IdentitySpoofTick(IdentitySpoofState& state, uint64_t now_ms) { // deferred apply, armed by the observer (0x16798C) if (state.apply_armed && now_ms >= state.apply_deadline_ms) { // 0x1679A4 state.apply_armed = false; // 0x1679AB if (!state.scan_in_progress && !state.restore_armed) { // 0x1679B1/BC state.applied = true; // 0x1679D7 state.applied_at_ms = now_ms; // 0x1679EB return IdentityTickAction::kApplyFake; } } // the toggle went off: undo everything (0x167A13) if (!state.enabled) { if (!state.was_enabled) return IdentityTickAction::kNothing; // 0x167A18 state.was_enabled = false; // 0x167A82 if (state.applied && state.original_id != 0) { // 0x167A25/2E state.applied = false; state.applied_at_ms = 0; state.reservation_pending = false; state.scan_in_progress = false; state.apply_armed = false; return IdentityTickAction::kRestoreOriginal; } return IdentityTickAction::kNothing; } state.was_enabled = true; // 0x167A8E // no real ID yet: locate it, with a 119-tick retry back-off if (state.original_id == 0) { // 0x167A95 if (state.retry_cooldown > 0) { // 0x167AA2 --state.retry_cooldown; // 0x167CDE return IdentityTickAction::kNothing; } if (state.scan_complete) return IdentityTickAction::kNothing; // 0x167AB0 return IdentityTickAction::kLocateOriginal; } // deferred restore, armed on travel (0x167D1B) if (state.restore_armed && now_ms >= state.restore_deadline_ms) { // 0x167D33 state.restore_armed = false; // 0x167D3A if (state.applied) { // 0x167D40 state.applied = false; // 0x167D5E state.applied_at_ms = 0; state.scan_in_progress = false; state.apply_armed = false; state.enabled = false; // 0x167D81 return IdentityTickAction::kRestoreOriginal; } } // --- hard auto-revert 10 s after the swap (0x167DA0) if (state.applied && state.applied_at_ms != 0 && !state.restore_armed && now_ms - state.applied_at_ms > kAutoRevertMs) { // 0x167DD4 state.applied = false; // 0x167DED state.applied_at_ms = 0; state.reservation_pending = false; state.scan_in_progress = false; state.apply_armed = false; state.enabled = false; // 0x167E16 return IdentityTickAction::kRestoreOriginal; } return IdentityTickAction::kNothing; } } // namespace expresso