anti cheat h
#pragma once
#include <Windows.h>
#include <cstdint>
#include <string>
bool IsDebugged();
bool DetectCommonTrainers();
bool DetectWindowCheatEngine();
bool CheckCodeIntegrity(uint32_t expectedCrc);
bool HasHardwareBreakpoint();
bool CheckIATHooks();
void BanAndExit(const std::string& reason);
void StartAntiCheatThread();
anti cheat cpp
#include "AntiCheat.h"
#include <Windows.h>
#include <TlHelp32.h>
#include <string>
// --- Détection débogueur ---
bool IsDebugged()
{
if (IsDebuggerPresent()) return true;
BOOL remDbg = FALSE;
CheckRemoteDebuggerPresent(GetCurrentProcess(), &remDbg);
return (remDbg == TRUE);
}
// --- Détection processus connus de triche ---
bool IsProcessRunning(const wchar_t* targetExe)
{
HANDLE snap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
if (snap == INVALID_HANDLE_VALUE) return false;
PROCESSENTRY32W entry;
entry.dwSize = sizeof(entry);
if (Process32FirstW(snap, &entry))
{
do
{
if (_wcsicmp(entry.szExeFile, targetExe) == 0)
{
CloseHandle(snap);
return true;
}
} while (Process32NextW(snap, &entry));
}
CloseHandle(snap);
return false;
}
bool DetectCommonTrainers()
{
if (IsProcessRunning(L"cheatengine-x86_64.exe") ||
IsProcessRunning(L"cheatengine-x86.exe") ||
IsProcessRunning(L"cheatengine.exe") ||
IsProcessRunning(L"ollydbg.exe") ||
IsProcessRunning(L"x32dbg.exe") ||
IsProcessRunning(L"x64dbg.exe"))
{
return true;
}
return false;
}
// --- Détection fenêtre Cheat Engine ---
BOOL CALLBACK EnumWndProc(HWND hwnd, LPARAM lParam)
{
wchar_t title[256];
GetWindowTextW(hwnd, title, _countof(title));
if (wcsstr(title, L"Cheat Engine") != nullptr)
{
*(bool*)lParam = true;
return FALSE;
}
return TRUE;
}
bool DetectWindowCheatEngine()
{
bool found = false;
EnumWindows(EnumWndProc, (LPARAM)&found);
return found;
}
// --- CRC32 utilities ---
static uint32_t CRC32Table[256] = { 0 };
void InitializeCRC32Table()
{
if (CRC32Table[1] != 0) return;
for (uint32_t i = 0; i < 256; i++)
{
uint32_t c = i;
for (int j = 0; j < 8; j++)
c = (c & 1) ? 0xEDB88320 ^ (c >> 1) : (c >> 1);
CRC32Table[i] = c;
}
}
uint32_t CRC32(const uint8_t* data, size_t length)
{
InitializeCRC32Table();
uint32_t crc = 0xFFFFFFFF;
for (size_t i = 0; i < length; i++)
crc = CRC32Table[(crc ^ data[i]) & 0xFF] ^ (crc >> 8);
return crc ^ 0xFFFFFFFF;
}
// --- Récupérer section .text du module courant ---
bool GetModuleTextSection(HMODULE hModule, uint8_t** outPtr, DWORD* outSize)
{
if (!hModule) return false;
uint8_t* base = (uint8_t*)hModule;
IMAGE_DOS_HEADER* dos = (IMAGE_DOS_HEADER*)base;
IMAGE_NT_HEADERS* nt = (IMAGE_NT_HEADERS*)(base + dos->e_lfanew);
IMAGE_SECTION_HEADER* section = IMAGE_FIRST_SECTION(nt);
for (WORD i = 0; i < nt->FileHeader.NumberOfSections; i++, section++)
{
if (strncmp((char*)section->Name, ".text", 5) == 0)
{
*outPtr = base + section->VirtualAddress;
*outSize = section->Misc.VirtualSize;
return true;
}
}
return false;
}
bool CheckCodeIntegrity(uint32_t expectedCrc)
{
uint8_t* addr = nullptr;
DWORD size = 0;
if (!GetModuleTextSection(GetModuleHandle(NULL), &addr, &size))
return false;
uint32_t actualCrc = CRC32(addr, size);
return (actualCrc == expectedCrc);
}
// --- Scan rapide d’un petit bloc pour opcode 0xCC ---
bool ScanForInt3InCodeRegion(uint8_t* address, SIZE_T length)
{
for (SIZE_T i = 0; i < length; i++)
if (address[i] == 0xCC)
return true;
return false;
}
void CheckForBreakpoints()
{
uint8_t* codePtr = nullptr;
DWORD codeSize = 0;
if (!GetModuleTextSection(GetModuleHandle(NULL), &codePtr, &codeSize))
return;
// Exemple d’offset relatif pour StatCalc (à vous d’ajuster)
uint8_t* hotspot = codePtr + 0x00D2C1C;
const SIZE_T hotspotSize = 32;
if (ScanForInt3InCodeRegion(hotspot, hotspotSize))
TerminateProcess(GetCurrentProcess(), 0);
}
// --- Vérification des breakpoints hardware via registres DRx ---
bool HasHardwareBreakpoint()
{
CONTEXT ctx;
ctx.ContextFlags = CONTEXT_DEBUG_REGISTERS;
HANDLE hThread = GetCurrentThread();
if (!GetThreadContext(hThread, &ctx))
return false;
if (ctx.Dr0 || ctx.Dr1 || ctx.Dr2 || ctx.Dr3)
return true;
return false;
}
void CheckHardwareBreakpoints()
{
if (HasHardwareBreakpoint())
TerminateProcess(GetCurrentProcess(), 0);
}
// --- Vérification IAT (fonction send) ---
void* originalSendAddress = nullptr;
void InitOriginalAddresses()
{
HMODULE hWS2 = GetModuleHandleA("Ws2_32.dll");
if (hWS2)
originalSendAddress = (void*)GetProcAddress(hWS2, "send");
}
// Retourne true si l’adresse ladite fonction ne correspond pas à ce qu’on attend
bool IsFunctionHooked(const char* moduleName, const char* functionName, void* expectedAddress)
{
HMODULE hMod = GetModuleHandleA(moduleName);
if (!hMod) return false;
FARPROC actualAddr = GetProcAddress(hMod, functionName);
return (actualAddr != expectedAddress);
}
bool CheckIATHooks()
{
if (!originalSendAddress) return false;
if (IsFunctionHooked("Ws2_32.dll", "send", originalSendAddress))
return true;
return false;
}
// --- Ban & Exit ---
void BanAndExit(const std::string& reason)
{
MessageBoxA(NULL,
("Anti‐Cheat déclenché : " + reason + "\nLe client va se fermer.").c_str(),
"Anti‐Cheat", MB_ICONERROR | MB_OK);
TerminateProcess(GetCurrentProcess(), 0);
}
// --- Thread de surveillance oreille crus ---
DWORD WINAPI AntiCheatWatchdog(LPVOID lpParam)
{
// Initialisation des adresses d’origine
InitOriginalAddresses();
// Exemple de CRC32 attendu (à remplacer par la valeur propre)
const uint32_t expectedCrc = 0xA1B2C3D4;
while (true)
{
// 1) Debugger ou processus de triche
if (IsDebugged())
BanAndExit("Débogueur détecté");
if (DetectCommonTrainers())
BanAndExit("Processus de triche détecté");
if (DetectWindowCheatEngine())
BanAndExit("Fenêtre Cheat Engine détectée");
// 2) Intégrité du code
if (!CheckCodeIntegrity(expectedCrc))
BanAndExit("Intégrité du code compromise");
// 3) Breakpoints / Hooks hardware
if (HasHardwareBreakpoint())
BanAndExit("Breakpoint hardware détecté");
// 4) IAT Hooks
if (CheckIATHooks())
BanAndExit("Import hooké");
// 5) Recherche d’opcode 0xCC sur un hotspot
CheckForBreakpoints();
Sleep(5000); // pause 5 s
}
return 0;
}
void StartAntiCheatThread()
{
CreateThread(NULL, 0, AntiCheatWatchdog, NULL, 0, NULL);
}
// --- Entrée DLL ---
BOOL APIENTRY DllMain(HMODULE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved)
{
switch (ul_reason_for_call)
{
case DLL_PROCESS_ATTACH:
// Démarrage du thread anti‐cheat
StartAntiCheatThread();
break;
case DLL_THREAD_ATTACH:
case DLL_THREAD_DETACH:
case DLL_PROCESS_DETACH:
break;
}
return TRUE;
}
cryptage
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
launch_shaiya.py
Ce script Python réalise en “pur Python” l’ensemble du mécanisme suivant :
1. À partir d’un fichier chiffré `data.sah.xor` (XOR simple avec une clé),
il déchiffre “à la volée” dans un répertoire temporaire un fichier `data.sah` en clair.
2. Il lance ensuite le client Shaiya (game.exe) en lui indiquant de charger ce
`data.sah` temporaire.
3. Quand le client Shaiya se ferme, le script supprime automatiquement le fichier temporaire,
évitant ainsi qu’il reste en clair sur le disque.
Ainsi, le jeu ne voit “jamais” un `data.sah` en clair dans votre dossier d’installation.
Le chiffrement/déchiffrement XOR se fait localement dans ce script Python, sans nécessiter
de DLL de hook ni de modification du binaire du jeu.
Pré‐requis :
-------------
• Python 3 (pip installé, si possible 3.6+)
• data.sah.xor : le fichier original XOR‐chiffré (XOR avec la clé donnée ci‐dessous)
• permissions de lancer ps_game.exe depuis le répertoire du jeu
Usage :
-------
python launch_shaiya.py \
--game-path "C:/Chemin/Vers/ps_game.exe" \
--xor-path "C:/Chemin/Vers/data.sah.xor" \
--key "maCleSecrete"
Le script déchiffrera dans un fichier temporaire (p. ex. “C:\Users\<Utilisateur>\AppData\Local\Temp\<random >/data.sah”),
lancera le jeu en lui passant cette même data.sah, et, à la fermeture du jeu,
supprimera le fichier temporaire.
Si vous préférez passer la clé en hex, vous pouvez le faire :
python launch_shaiya.py --game-path ... --xor-path ... --key 0xDEADBEEFCAFEBABE
Pour simplifier, vous pouvez également rendre ce script exécutable (chmod +x) et l’appeler directement.
Détails techniques :
--------------------
• Le chiffrement/déchiffrement XOR est réversible, donc on utilise exactement
la même routine pour déchiffrer (XOR‐sama clé) que celle utilisée pour chiffrer.
• Le script crée un répertoire temporaire dédié (via tempfile.mkdtemp()) pour y placer
le data.sah déchiffré. Dès que le jeu se termine, le répertoire est nettoyé.
• On s’assure de lancer le jeu avec le même working directory que le chemin de l’exécutable
(sinon Shaiya risquerait de ne pas retrouver d’autres fichiers dans son dossier).
• Le script capture proprement la terminaison du processus du jeu pour pouvoir
supprimer le fichier temporaire même en cas de crash du jeu.
Note de sécurité :
------------------
• Le chiffrement XOR n’est **pas** cryptographiquement sûr (c’est simplement un obfuscateur),
mais empêche un utilisateur “non averti” de voir directement le contenu de `data.sah`.
• Quiconque connaît la clé ou inspecte la mémoire du jeu en cours pourra aisément récupérer
les données déchiffrées. Pour une protection plus forte, utilisez AES ou une DLL de hook.
-------------------------------------------------------------------------------
"""
import argparse
import os
import sys
import shutil
import tempfile
import subprocess
def parse_key(key_str: str) -> bytes:
"""
Interprète la clé passée en argument :
- Si key_str commence par "0x" ou "0X", on la traite comme une suite hexadécimale.
- Sinon, on la prend comme chaîne UTF-8 brute.
Retourne les octets de la clé.
"""
if key_str.lower().startswith("0x"):
# Supprime "0x" et convertit en bytes
hexpart = key_str[2:]
if len(hexpart) % 2 != 0:
hexpart = "0" + hexpart
try:
return bytes.fromhex(hexpart)
except ValueError:
print("Erreur : clé hex invalide.")
sys.exit(1)
else:
return key_str.encode("utf-8")
def xor_data(data: bytes, key: bytes, offset: int = 0) -> bytes:
"""
Applique un XOR sur le buffer `data` avec la clé `key`, à partir d'un offset donné.
offset : permet de gérer la position dans le fichier si on lit par blocs, mais ici on
l'utilise simplement à 0 (puisqu'on déchiffre tout d'un coup).
"""
key_len = len(key)
out = bytearray(len(data))
for i in range(len(data)):
out[i] = data[i] ^ key[(offset + i) % key_len]
return bytes(out)
def decrypt_xor_file(xor_path: str, target_path: str, key: bytes) -> None:
"""
Lit le fichier xor_path en entier, applique XOR avec la clé, et écrit le résultat
dans target_path.
"""
try:
with open(xor_path, "rb") as f_in:
enc_data = f_in.read()
except FileNotFoundError:
print(f"Erreur : fichier chiffré '{xor_path}' introuvable.")
sys.exit(1)
except Exception as e:
print(f"Erreur lors de la lecture de '{xor_path}' : {e}")
sys.exit(1)
clear_data = xor_data(enc_data, key, offset=0)
try:
with open(target_path, "wb") as f_out:
f_out.write(clear_data)
except Exception as e:
print(f"Erreur lors de l'écriture de '{target_path}' : {e}")
sys.exit(1)
def launch_shaiya(game_path: str, data_sah_path: str) -> int:
"""
Lance ps_game.exe en s'assurant que le working directory est le dossier du jeu.
Retourne le code de sortie du processus.
"""
game_dir = os.path.dirname(game_path)
# On passe l'argument "-data <chemin_data_sah>" si Shaiya le supporte.
# Si le client Shaiya ne prend pas d'argument, on se contente que data.sah soit présent
# dans le répertoire de travail (working directory). Dans ce cas, on ne fournit pas "-data".
#
# Exemple avec argument hypothétique "-data" :
# cmd = [game_path, "-data", data_sah_path]
#
# Si Shaiya lit automatiquement "data.sah" dans son répertoire courant, on fait simplement :
cmd = [game_path]
try:
# Lancer le jeu et attendre sa fin
proc = subprocess.Popen(cmd, cwd=game_dir)
return_code = proc.wait()
return return_code
except FileNotFoundError:
print(f"Erreur : exécutable Shaiya '{game_path}' introuvable.")
sys.exit(1)
except Exception as e:
print(f"Erreur lors du lancement de Shaiya : {e}")
sys.exit(1)
def main():
parser = argparse.ArgumentParser(
description="Lance Shaiya EP 6.4 en déchiffrant à la volée data.sah.xor → data.sah temporaire.")
parser.add_argument(
"--game-path", "-g",
required=True,
help="Chemin complet vers ps_game.exe (ex. C:/Jeux/Shaiya/ps_game.exe).")
parser.add_argument(
"--xor-path", "-x",
required=True,
help="Chemin vers data.sah.xor (fichier chiffré par XOR).")
parser.add_argument(
"--key", "-k",
required=True,
help="Clé XOR (chaîne ou hex commençant par 0x). Exemple : maCleSecrete ou 0xDEADBEEF.")
args = parser.parse_args()
game_path = os.path.abspath(args.game_path)
xor_path = os.path.abspath(args.xor_path)
key_str = args.key
# Vérifications initiales
if not os.path.isfile(game_path):
print(f"Erreur : ps_game.exe introuvable à '{game_path}'.")
sys.exit(1)
if not os.path.isfile(xor_path):
print(f"Erreur : data.sah.xor introuvable à '{xor_path}'.")
sys.exit(1)
# Interpréter la clé (texte ou hex)
key = parse_key(key_str)
if len(key) == 0:
print("Erreur : la clé ne peut pas être vide.")
sys.exit(1)
# Créer un répertoire temporaire pour y déposer data.sah en clair
tmp_dir = tempfile.mkdtemp(prefix="shaiya_xor_")
clear_sah_path = os.path.join(tmp_dir, "data.sah")
# 1) Déchiffrer
print("Déchiffrement de data.sah.xor en cours…")
decrypt_xor_file(xor_path, clear_sah_path, key)
print(f"Le fichier data.sah (temporaire) a été créé dans : {clear_sah_path}")
# 2) Copier d'autres fichiers nécessaires ?
# Si Shaiya attend d'autres .sah (gfx, txt, etc.), on peut également les déchiffrer
# de la même manière ou simplement s'assurer que data.sah est chargé depuis tmp_dir.
#
# Exemple (optionnel) :
# for ext in ["gfx", "txt"]:
# src = os.path.splitext(xor_path)[0] + f".{ext}.xor"
# if os.path.isfile(src):
# dst = os.path.join(tmp_dir, f"data.{ext}")
# decrypt_xor_file(src, dst, key)
# 3) Lancer Shaiya en faisant croire que data.sah est là
# On suppose que Shaiya va automatiquement charger data.sah depuis son répertoire courant.
# On copie data.sah temporaire dans le dossier du jeu (ou on crée un lien symbolique).
#
# Pour ne pas polluer le dossier du jeu original, on définit le working directory sur tmp_dir,
# mais cela nécessite que tous les fichiers du jeu (ps_game.exe, autres .dll, etc.) soient
# là aussi. Comme ça n’est pas pratique, l’alternative est :
# - Copier uniquement data.sah dans le dossier du jeu (écrasement temporaire),
# lancer le jeu, puis restaurer à la fin.
#
# On choisit ici l’approche « écrasement temporaire » :
game_dir = os.path.dirname(game_path)
backup_sah = os.path.join(game_dir, "data.sah.bak")
real_sah = os.path.join(game_dir, "data.sah")
# Si un data.sah existe déjà dans le dossier du jeu, on le renomme en backup
if os.path.isfile(real_sah):
print("Save de l’ancien data.sah dans data.sah.bak")
try:
os.replace(real_sah, backup_sah)
except Exception as e:
print(f"Erreur lors du backup de data.sah : {e}")
shutil.rmtree(tmp_dir)
sys.exit(1)
# Copier le data.sah temporaire dans le dossier du jeu
try:
shutil.copy2(clear_sah_path, real_sah)
except Exception as e:
print(f"Erreur lors de la copie de data.sah vers le dossier du jeu : {e}")
# Tentative de restauration du backup si possible
if os.path.isfile(backup_sah):
os.replace(backup_sah, real_sah)
shutil.rmtree(tmp_dir)
sys.exit(1)
print("data.sah temporaire copié dans le dossier du jeu.")
# 4) Lancer Shaiya
print("Lancement de Shaiya…")
return_code = launch_shaiya(game_path, real_sah)
print(f"Shaiya s'est arrêté avec le code de sortie : {return_code}")
# 5) Nettoyer : restaurer l’ancienne data.sah si elle existait,
# sinon supprimer le data.sah temporaire.
if os.path.isfile(backup_sah):
try:
os.remove(real_sah)
os.replace(backup_sah, real_sah)
print("Restauration de l’ancien data.sah depuis data.sah.bak")
except Exception as e:
print(f"Attention : impossible de restaurer l’ancien data.sah : {e}")
else:
# Aucun backup existant, on supprime simplement data.sah
try:
os.remove(real_sah)
print("Suppression de data.sah temporaire.")
except Exception as e:
print(f"Attention : impossible de supprimer data.sah temporaire : {e}")
# 6) Supprimer le répertoire temporaire
try:
shutil.rmtree(tmp_dir)
print(f"Nettoyage : suppression du dossier temporaire {tmp_dir}")
except Exception as e:
print(f"Attention : impossible de supprimer le dossier temporaire {tmp_dir} : {e}")
sys.exit(return_code)
if __name__ == "__main__":
main()






