Загрузка данных


# language: Python, file: cs2_cheat.py, target: Windows 11 x64, Python 3.11+
# external CS2 cheat: memory read, ESP overlay, aimbot
import ctypes
import ctypes.wintypes as wt
import json
import math
import os
import struct
import sys
import threading
import time
import tkinter as tk
from dataclasses import dataclass, field
from typing import Optional

import pymem
import win32api
import win32con
import win32gui
import win32process

# ---------- offsets ----------
# загружаются из cs2.json (hazedumper-формат) рядом со скриптом.
# при патче игры — обновить файл, код не трогать.

OFFSETS_PATH = os.path.join(os.path.dirname(os.path.abspath(__file__)), "cs2.json")

@dataclass
class Offsets:
    dwEntityList: int = 0
    dwLocalPlayer: int = 0
    dwViewMatrix: int = 0
    m_iHealth: int = 0
    m_iTeamNum: int = 0
    m_vecOrigin: int = 0
    m_dwBoneMatrix: int = 0
    m_angEyeAngles: int = 0
    m_iShotsFired: int = 0

def load_offsets() -> Offsets:
    o = Offsets()
    if not os.path.exists(OFFSETS_PATH):
        print(f"[!] {OFFSETS_PATH} missing — fill offsets manually or drop cs2.json")
        return o
    with open(OFFSETS_PATH, "r", encoding="utf-8") as f:
        data = json.load(f)
    # hazedumper формат: {"client.dll": {"dwEntityList": ..., ...}, "netvars": {...}}
    client = data.get("client.dll", {})
    netvars = data.get("netvars", {})
    o.dwEntityList  = client.get("dwEntityList", 0)
    o.dwLocalPlayer = client.get("dwLocalPlayer", 0)
    o.dwViewMatrix  = client.get("dwViewMatrix", 0)
    o.m_iHealth     = netvars.get("m_iHealth", 0)
    o.m_iTeamNum    = netvars.get("m_iTeamNum", 0)
    o.m_vecOrigin   = netvars.get("m_vecOrigin", 0)
    o.m_dwBoneMatrix = netvars.get("m_dwBoneMatrix", 0)
    o.m_angEyeAngles = netvars.get("m_angEyeAngles", 0)
    o.m_iShotsFired  = netvars.get("m_iShotsFired", 0)
    return o

# ---------- math ----------

@dataclass
class Vec3:
    x: float = 0.0
    y: float = 0.0
    z: float = 0.0

def v3sub(a: Vec3, b: Vec3) -> Vec3:
    return Vec3(a.x - b.x, a.y - b.y, a.z - b.z)

def calc_angle(src: Vec3, dst: Vec3) -> Vec3:
    d = v3sub(dst, src)
    hyp = math.sqrt(d.x * d.x + d.y * d.y)
    return Vec3(
        math.atan2(-d.z, hyp) * 57.29578,
        math.atan2(d.y, d.x) * 57.29578,
        0.0,
    )

def world_to_screen(w: Vec3, vm: list[float], sw: int, sh: int) -> Optional[tuple[float, float]]:
    cx = w.x * vm[0] + w.y * vm[4] + w.z * vm[8]  + vm[12]
    cy = w.x * vm[1] + w.y * vm[5] + w.z * vm[9]  + vm[13]
    cw = w.x * vm[3] + w.y * vm[7] + w.z * vm[11] + vm[15]
    if cw < 0.01:
        return None
    return ((sw * 0.5) * ((cx / cw) + 1.0), (sh * 0.5) * (1.0 - (cy / cw)))

# ---------- memory ----------

class GameMemory:
    def __init__(self) -> None:
        self.pm: Optional[pymem.Pymem] = None
        self.client: int = 0
        self.off = Offsets()

    def attach(self) -> bool:
        try:
            self.pm = pymem.Pymem("cs2.exe")
            self.client = pymem.process.module_from_name(
                self.pm.process_handle, "client.dll"
            ).lpBaseOfDll
            self.off = load_offsets()
            return True
        except Exception as e:
            print(f"[!] attach failed: {e}")
            return False

    def read(self, addr: int, fmt: str):
        try:
            size = struct.calcsize(fmt)
            buf = self.pm.read_bytes(addr, size)
            return struct.unpack(fmt, buf)[0]
        except Exception:
            return None

    def read_vec3(self, addr: int) -> Vec3:
        try:
            buf = self.pm.read_bytes(addr, 12)
            return Vec3(*struct.unpack("fff", buf))
        except Exception:
            return Vec3()

    def read_view_matrix(self, addr: int) -> list[float]:
        try:
            buf = self.pm.read_bytes(addr, 64)
            return list(struct.unpack("16f", buf))
        except Exception:
            return [0.0] * 16

# ---------- entities ----------

@dataclass
class Player:
    addr: int
    health: int
    team: int
    origin: Vec3
    bones: int

@dataclass
class WorldState:
    local: int = 0
    local_origin: Vec3 = field(default_factory=Vec3)
    local_team: int = 0
    local_angles: Vec3 = field(default_factory=Vec3)
    view_matrix: list[float] = field(default_factory=lambda: [0.0] * 16)
    players: list[Player] = field(default_factory=list)

def read_world(mem: GameMemory) -> WorldState:
    ws = WorldState()
    if not mem.pm or not mem.client:
        return ws
    o = mem.off
    ws.local = mem.read(mem.client + o.dwLocalPlayer, "Q") or 0
    if not ws.local:
        return ws
    ws.local_origin = mem.read_vec3(ws.local + o.m_vecOrigin)
    ws.local_team = mem.read(ws.local + o.m_iTeamNum, "i") or 0
    ws.local_angles = mem.read_vec3(ws.local + o.m_angEyeAngles)
    ws.view_matrix = mem.read_view_matrix(mem.client + o.dwViewMatrix)

    ent_list = mem.read(mem.client + o.dwEntityList, "Q") or 0
    if not ent_list:
        return ws

    for i in range(1, 64):
        ent = mem.read(ent_list + i * 0x8, "Q")
        if not ent:
            continue
        hp = mem.read(ent + o.m_iHealth, "i")
        if hp is None or hp <= 0 or hp > 100:
            continue
        ws.players.append(Player(
            addr=ent,
            health=hp,
            team=mem.read(ent + o.m_iTeamNum, "i") or 0,
            origin=mem.read_vec3(ent + o.m_vecOrigin),
            bones=mem.read(ent + o.m_dwBoneMatrix, "Q") or 0,
        ))
    return ws

HEAD_BONE = 6

def bone_pos(mem: GameMemory, bones: int, bone_id: int) -> Vec3:
    if not bones:
        return Vec3()
    base = bones + bone_id * 0x40
    return Vec3(
        mem.read(base + 0x30, "f") or 0.0,
        mem.read(base + 0x34, "f") or 0.0,
        mem.read(base + 0x38, "f") or 0.0,
    )

# ---------- mouse / aim ----------

class MOUSEINPUT(ctypes.Structure):
    _fields_ = [
        ("dx", wt.LONG),
        ("dy", wt.LONG),
        ("mouseData", wt.DWORD),
        ("dwFlags", wt.DWORD),
        ("time", wt.DWORD),
        ("dwExtraInfo", ctypes.POINTER(wt.ULONG)),
    ]

class INPUT(ctypes.Structure):
    class _I(ctypes.Union):
        _fields_ = [("mi", MOUSEINPUT)]
    _anonymous_ = ("i",)
    _fields_ = [("type", wt.DWORD), ("i", _I)]

INPUT_MOUSE = 0
MOUSEEVENTF_MOVE = 0x0001

def move_mouse(dx: float, dy: float) -> None:
    inp = INPUT()
    inp.type = INPUT_MOUSE
    inp.mi = MOUSEINPUT(int(dx), int(dy), 0, MOUSEEVENTF_MOVE, 0, None)
    ctypes.windll.user32.SendInput(1, ctypes.byref(inp), ctypes.sizeof(INPUT))

@dataclass
class AimConfig:
    enabled: bool = True
    fov: float = 8.0
    smooth: float = 0.35
    bone: int = HEAD_BONE
    vk: int = 0x02  # RBUTTON
    px_per_deg: float = 10.0

def do_aim(ws: WorldState, mem: GameMemory, cfg: AimConfig) -> None:
    if not cfg.enabled or not ws.local:
        return
    if not (ctypes.windll.user32.GetAsyncKeyState(cfg.vk) & 0x8000):
        return

    view = ws.local_angles
    best_ang: Optional[Vec3] = None
    best_d = cfg.fov

    for p in ws.players:
        if p.team == ws.local_team:
            continue
        target = bone_pos(mem, p.bones, cfg.bone)
        if target.x == 0.0 and target.y == 0.0 and target.z == 0.0:
            continue
        ang = calc_angle(ws.local_origin, target)
        dy = ang.y - view.y
        while dy > 180:  dy -= 360
        while dy < -180: dy += 360
        dp = ang.x - view.x
        d = math.sqrt(dy * dy + dp * dp)
        if d < best_d:
            best_d = d
            best_ang = ang

    if best_ang is None:
        return

    dy = best_ang.y - view.y
    while dy > 180:  dy -= 360
    while dy < -180: dy += 360
    dp = best_ang.x - view.x

    move_mouse(dy * cfg.px_per_deg * cfg.smooth,
               -dp * cfg.px_per_deg * cfg.smooth)

# ---------- overlay (tkinter, click-through) ----------

TRANSPARENT = "#010203"  # TK transparency color

class Overlay:
    def __init__(self, mem: GameMemory) -> None:
        self.mem = mem
        self.root = tk.Tk()
        self.root.title("overlay")
        self.root.attributes("-topmost", True)
        self.root.attributes("-fullscreen", True)
        self.root.configure(bg=TRANSPARENT)
        try:
            self.root.wm_attributes("-transparentcolor", TRANSPARENT)
        except tk.TclError:
            pass
        self.canvas = tk.Canvas(self.root, bg=TRANSPARENT, highlightthickness=0)
        self.canvas.pack(fill="both", expand=True)
        self.sw = self.root.winfo_screenwidth()
        self.sh = self.root.winfo_screenheight()
        self.root.after(10, self.tick)

    def tick(self) -> None:
        try:
            ws = read_world(self.mem)
            self.draw(ws)
        except Exception as e:
            print(f"[overlay] {e}")
        self.root.after(10, self.tick)

    def draw(self, ws: WorldState) -> None:
        self.canvas.delete("all")
        if not ws.local:
            return
        for p in ws.players:
            if p.team == ws.local_team:
                continue
            head = bone_pos(self.mem, p.bones, HEAD_BONE)
            feet = Vec3(p.origin.x, p.origin.y, p.origin.z - 72.0)
            tp = world_to_screen(head, ws.view_matrix, self.sw, self.sh)
            bp = world_to_screen(feet, ws.view_matrix, self.sw, self.sh)
            if not tp or not bp:
                continue
            h = bp[1] - tp[1]
            if h <= 2 or h > self.sh:
                continue
            w = h * 0.5
            x1, x2 = tp[0] - w / 2, tp[0] + w / 2
            self.canvas.create_rectangle(x1, tp[1], x2, bp[1], outline="red", width=2)
            # health bar
            self.canvas.create_rectangle(x1 - 6, tp[1], x1 - 3, bp[1], fill="#333", outline="")
            hp_h = (bp[1] - tp[1]) * (p.health / 100.0)
            self.canvas.create_rectangle(x1 - 6, bp[1] - hp_h, x1 - 3, bp[1],
                                         fill="#0f0", outline="")
        self.root.update_idletasks()

    def run(self) -> None:
        self.root.mainloop()

# ---------- main ----------

def main() -> int:
    mem = GameMemory()
    print("[*] waiting for cs2.exe ...")
    while not mem.attach():
        time.sleep(2)
    print(f"[+] attached. client.dll @ {hex(mem.client)}")
    if mem.off.dwEntityList == 0:
        print("[!] offsets are zero — drop cs2.json next to the exe and restart")

    cfg = AimConfig()
    overlay = Overlay(mem)

    def aim_loop() -> None:
        while True:
            try:
                ws = read_world(mem)
                do_aim(ws, mem, cfg)
            except Exception:
                pass
            time.sleep(0.002)

    threading.Thread(target=aim_loop, daemon=True).start()
    overlay.run()
    return 0

if __name__ == "__main__":
    sys.exit(main())