From 7c7d78ee3a032e0c526d683ede3aeab7e6c42860 Mon Sep 17 00:00:00 2001 From: attaboy11 Date: Sat, 30 May 2026 23:53:11 +0100 Subject: [PATCH] feat: add baseline anti-cheat monitor --- AntiCheat.cpp | 270 +++++++++++++++++++++++--------- README.md | 83 +++++++++- anti_cheat.py | 173 ++++++++++++++++++++ docs/DEMO.md | 12 ++ docs/javelin-anticheat-demo.gif | Bin 0 -> 31800 bytes tests/test_anti_cheat.py | 87 ++++++++++ tests/test_cpp_build.py | 59 +++++++ 7 files changed, 610 insertions(+), 74 deletions(-) create mode 100644 anti_cheat.py create mode 100644 docs/DEMO.md create mode 100644 docs/javelin-anticheat-demo.gif create mode 100644 tests/test_anti_cheat.py create mode 100644 tests/test_cpp_build.py diff --git a/AntiCheat.cpp b/AntiCheat.cpp index 7a76632..b19ea4e 100644 --- a/AntiCheat.cpp +++ b/AntiCheat.cpp @@ -1,19 +1,42 @@ // AntiCheat.cpp -// Javelin Project - Minimal Anti-Cheat guards -// Features: debugger detection, suspicious process scan, basic self-integrity (CRC32) +// Javelin Project - baseline anti-cheat guards. +// +// Features: +// - debugger detection +// - suspicious process scan +// - optional self-integrity check using CRC32 of the running executable -#include -#include -#include +#include +#include +#include +#include +#include #include -#include +#include +#include #include -#include +#include + +#ifdef _WIN32 +#define NOMINMAX +#include +#include +#else +#include +#ifdef __APPLE__ +#include +#include +#endif +#endif + +namespace { -static const char* kTag = "[Javelin AntiCheat] "; +constexpr const char* kTag = "[Javelin AntiCheat] "; +constexpr int kExitDebugger = 0xDB; +constexpr int kExitSuspiciousProcess = 0xBA; +constexpr int kExitIntegrity = 0xC0; -// --- Configurable lists --- -static std::vector kSuspiciousProcesses = { +const std::vector kSuspiciousProcesses = { "cheatengine.exe", "ollydbg.exe", "x64dbg.exe", @@ -21,101 +44,204 @@ static std::vector kSuspiciousProcesses = { "ida.exe", "ida64.exe", "scylla.exe", - "processhacker.exe" + "processhacker.exe", }; -// --- Utils --- -static std::string toLower(std::string s) { - std::transform(s.begin(), s.end(), s.begin(), ::tolower); - return s; +std::string toLower(std::string value) { + std::transform(value.begin(), value.end(), value.begin(), [](unsigned char c) { + return static_cast(std::tolower(c)); + }); + return value; } -// Simple CRC32 (polynomial 0xEDB88320) -static uint32_t crc32(const std::vector& data) { +std::string basename(std::string path) { + const auto slash = path.find_last_of("/\\"); + if (slash != std::string::npos) { + path = path.substr(slash + 1); + } + return path; +} + +std::string stripExeSuffix(const std::string& name) { + constexpr const char* suffix = ".exe"; + if (name.size() >= 4 && name.compare(name.size() - 4, 4, suffix) == 0) { + return name.substr(0, name.size() - 4); + } + return name; +} + +bool isSuspiciousProcessName(const std::string& processName) { + const std::string normalized = stripExeSuffix(toLower(basename(processName))); + for (const std::string& bad : kSuspiciousProcesses) { + const std::string suspicious = stripExeSuffix(toLower(bad)); + if (normalized == suspicious) { + return true; + } + } + return false; +} + +uint32_t crc32(const std::vector& data) { uint32_t crc = 0xFFFFFFFFu; - for (uint8_t b : data) { - crc ^= b; + for (uint8_t byte : data) { + crc ^= byte; for (int i = 0; i < 8; ++i) { - uint32_t mask = -(crc & 1u); + const uint32_t mask = -(crc & 1u); crc = (crc >> 1) ^ (0xEDB88320u & mask); } } return ~crc; } -static bool readFile(const std::wstring& path, std::vector& out) { - std::ifstream f(path, std::ios::binary); - if (!f) return false; - f.seekg(0, std::ios::end); - std::streamsize size = f.tellg(); - if (size <= 0) return false; - f.seekg(0, std::ios::beg); +bool readFile(const std::string& path, std::vector& out) { + std::ifstream file(path, std::ios::binary); + if (!file) { + return false; + } + + file.seekg(0, std::ios::end); + const std::streamsize size = file.tellg(); + if (size <= 0) { + return false; + } + + file.seekg(0, std::ios::beg); out.resize(static_cast(size)); - if (!f.read(reinterpret_cast(out.data()), size)) return false; - return true; + return static_cast(file.read(reinterpret_cast(out.data()), size)); } -// --- Checks --- -static bool checkDebugger() { - if (IsDebuggerPresent()) return true; - - // Secondary anti-debug: CheckBeingDebugged flag in PEB (best-effort) -#ifdef _M_IX86 - // 32-bit: fs:[30h] -> PEB, offset 2 = BeingDebugged (BYTE) - __try { - BYTE* peb = *(BYTE**)_readfsdword(0x30); - if (peb && peb[2]) return true; - } __except (EXCEPTION_EXECUTE_HANDLER) {} -#elif defined(_M_X64) - // 64-bit: gs:[60h] -> PEB - __try { - BYTE* peb = *(BYTE**)_readgsqword(0x60); - if (peb && peb[2]) return true; - } __except (EXCEPTION_EXECUTE_HANDLER) {} +bool getExecutablePath(std::string& out) { +#ifdef _WIN32 + std::array path{}; + const DWORD written = GetModuleFileNameA(nullptr, path.data(), static_cast(path.size())); + if (written == 0 || written >= path.size()) { + return false; + } + out.assign(path.data(), written); + return true; +#elif defined(__APPLE__) + uint32_t size = 0; + _NSGetExecutablePath(nullptr, &size); + if (size == 0) { + return false; + } + std::string path(size, '\0'); + if (_NSGetExecutablePath(path.data(), &size) != 0) { + return false; + } + path.resize(std::char_traits::length(path.c_str())); + out = path; + return true; +#else + std::array path{}; + const ssize_t written = readlink("/proc/self/exe", path.data(), path.size() - 1); + if (written <= 0) { + return false; + } + out.assign(path.data(), static_cast(written)); + return true; #endif +} +bool checkDebugger() { +#ifdef _WIN32 + if (IsDebuggerPresent()) { + return true; + } + + BOOL remoteDebuggerPresent = FALSE; + if (CheckRemoteDebuggerPresent(GetCurrentProcess(), &remoteDebuggerPresent) && + remoteDebuggerPresent) { + return true; + } + return false; +#elif defined(__linux__) + std::ifstream status("/proc/self/status"); + std::string line; + while (std::getline(status, line)) { + if (line.rfind("TracerPid:", 0) == 0) { + std::istringstream stream(line.substr(10)); + int tracerPid = 0; + stream >> tracerPid; + return tracerPid != 0; + } + } + return false; +#elif defined(__APPLE__) + int mib[4] = {CTL_KERN, KERN_PROC, KERN_PROC_PID, getpid()}; + kinfo_proc info{}; + size_t size = sizeof(info); + if (sysctl(mib, 4, &info, &size, nullptr, 0) != 0) { + return false; + } + return (info.kp_proc.p_flag & P_TRACED) != 0; +#else return false; +#endif } -static bool checkSuspiciousProcesses() { +bool checkSuspiciousProcesses() { +#ifdef _WIN32 HANDLE snap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); - if (snap == INVALID_HANDLE_VALUE) return false; + if (snap == INVALID_HANDLE_VALUE) { + return false; + } - PROCESSENTRY32 pe{}; - pe.dwSize = sizeof(pe); - if (!Process32First(snap, &pe)) { + PROCESSENTRY32A entry{}; + entry.dwSize = sizeof(entry); + if (!Process32FirstA(snap, &entry)) { CloseHandle(snap); return false; } do { - std::string name = toLower(pe.szExeFile); - for (const auto& bad : kSuspiciousProcesses) { - if (name == toLower(bad)) { - CloseHandle(snap); - return true; - } + if (isSuspiciousProcessName(entry.szExeFile)) { + CloseHandle(snap); + return true; } - } while (Process32Next(snap, &pe)); + } while (Process32NextA(snap, &entry)); CloseHandle(snap); return false; +#else + FILE* pipe = popen("ps -axo comm", "r"); + if (pipe == nullptr) { + return false; + } + + std::array buffer{}; + while (fgets(buffer.data(), static_cast(buffer.size()), pipe) != nullptr) { + std::string processName(buffer.data()); + processName.erase(std::remove(processName.begin(), processName.end(), '\n'), processName.end()); + if (isSuspiciousProcessName(processName)) { + pclose(pipe); + return true; + } + } + + pclose(pipe); + return false; +#endif } -static bool checkSelfIntegrity(uint32_t expectedCrc) { - wchar_t path[MAX_PATH]{}; - if (!GetModuleFileNameW(nullptr, path, MAX_PATH)) return false; +bool checkSelfIntegrity(uint32_t expectedCrc) { + std::string path; + if (!getExecutablePath(path)) { + return false; + } std::vector bytes; - if (!readFile(path, bytes)) return false; + if (!readFile(path, bytes)) { + return false; + } - uint32_t current = crc32(bytes); - return current == expectedCrc; + return crc32(bytes) == expectedCrc; } -// --- Entry helper (embed a baseline CRC once you ship a build) --- +} // namespace + #ifndef JAVELIN_EXPECTED_CRC32 -#define JAVELIN_EXPECTED_CRC32 0u // Set this at build time (e.g., /DJAVELIN_EXPECTED_CRC32=0x12345678) +#define JAVELIN_EXPECTED_CRC32 0u #endif int main() { @@ -123,19 +249,17 @@ int main() { if (checkDebugger()) { std::cerr << kTag << "Debugger detected. Exiting.\n"; - return 0xDEB; // code for debugger + return kExitDebugger; } if (checkSuspiciousProcesses()) { std::cerr << kTag << "Suspicious process detected. Exiting.\n"; - return 0xBAD; // code for bad process + return kExitSuspiciousProcess; } - if (JAVELIN_EXPECTED_CRC32 != 0u) { - if (!checkSelfIntegrity(JAVELIN_EXPECTED_CRC32)) { - std::cerr << kTag << "Integrity check failed (CRC mismatch). Exiting.\n"; - return 0xCRC; // custom code (note: non-standard, may be truncated) - } + if (JAVELIN_EXPECTED_CRC32 != 0u && !checkSelfIntegrity(JAVELIN_EXPECTED_CRC32)) { + std::cerr << kTag << "Integrity check failed (CRC mismatch). Exiting.\n"; + return kExitIntegrity; } std::cout << kTag << "All clear. Continue.\n"; diff --git a/README.md b/README.md index eb4d6af..3b88d22 100644 --- a/README.md +++ b/README.md @@ -1 +1,82 @@ -# py-workedtask \ No newline at end of file +# py-workedtask + +Baseline anti-cheat guards for the Javelin Project. + +## What Is Covered + +- C++ client debugger detection. +- C++ client suspicious process detection. +- Optional C++ self-integrity verification using a build-time CRC32 value. +- Python monitor debugger detection. +- Python monitor suspicious process detection. +- Optional Python self-integrity verification using `JAVELIN_EXPECTED_SHA256`. + +The guards fail closed: when a configured check fails, the process exits before the protected application would continue. + +## Run The Python Monitor + +```bash +python3 anti_cheat.py +``` + +To enable Python script integrity verification, compute the SHA-256 of the monitor and pass it through the environment: + +```bash +export JAVELIN_EXPECTED_SHA256="$(python3 - <<'PY' +import hashlib +from pathlib import Path +print(hashlib.sha256(Path("anti_cheat.py").read_bytes()).hexdigest()) +PY +)" +python3 anti_cheat.py +``` + +If the script changes after the hash is generated, the monitor exits with the integrity failure code. + +## Build The C++ Client + +Linux/macOS: + +```bash +c++ -std=c++17 -Wall -Wextra -pedantic AntiCheat.cpp -o javelin-anticheat +./javelin-anticheat +``` + +Windows Developer Command Prompt: + +```bat +cl /std:c++17 /EHsc AntiCheat.cpp /Fe:javelin-anticheat.exe +javelin-anticheat.exe +``` + +To enable C++ executable integrity verification, build once, calculate the CRC32 of the release executable, and rebuild with the expected value: + +```bash +python3 - <<'PY' +import binascii +from pathlib import Path +print(hex(binascii.crc32(Path("javelin-anticheat").read_bytes()) & 0xffffffff)) +PY +c++ -std=c++17 -DJAVELIN_EXPECTED_CRC32=0x12345678 AntiCheat.cpp -o javelin-anticheat +``` + +Replace `0x12345678` with the CRC32 produced for your release artifact. A mismatch exits before the protected application continues. + +## Exit Codes + +| Code | Meaning | +| --- | --- | +| `0` | All checks passed | +| `0xDB` | Debugger detected | +| `0xBA` | Suspicious process detected | +| `0xC0` | Integrity verification failed | + +## Tests + +```bash +python3 -m unittest discover -s tests +python3 -m py_compile anti_cheat.py +c++ -std=c++17 -Wall -Wextra -pedantic AntiCheat.cpp -o /tmp/javelin-anticheat-check +/tmp/javelin-anticheat-check +git diff --check +``` diff --git a/anti_cheat.py b/anti_cheat.py new file mode 100644 index 0000000..6afbd84 --- /dev/null +++ b/anti_cheat.py @@ -0,0 +1,173 @@ +"""Baseline anti-cheat monitor for the Javelin Project. + +The monitor intentionally avoids third-party dependencies so it can run beside +the C++ client in minimal environments. +""" + +from __future__ import annotations + +import csv +import ctypes +import hashlib +import os +import subprocess +import sys +from dataclasses import dataclass +from pathlib import Path +from typing import Callable, Iterable, Sequence + + +TAG = "[Javelin AntiCheat]" +EXIT_DEBUGGER = 0xDB +EXIT_SUSPICIOUS_PROCESS = 0xBA +EXIT_INTEGRITY = 0xC0 + +SUSPICIOUS_PROCESSES: tuple[str, ...] = ( + "cheatengine.exe", + "ollydbg.exe", + "x64dbg.exe", + "httpdebuggerui.exe", + "ida.exe", + "ida64.exe", + "scylla.exe", + "processhacker.exe", +) + + +@dataclass(frozen=True) +class CheckResult: + ok: bool + exit_code: int + message: str + + +ProcessRunner = Callable[..., subprocess.CompletedProcess[str]] + + +def _normalize_process_name(name: str) -> str: + cleaned = name.strip().strip('"').replace("\\", "/") + base = Path(cleaned).name.lower() + if base.endswith(".exe"): + base = base[:-4] + return base + + +def is_suspicious_process_name(name: str, suspicious: Sequence[str] = SUSPICIOUS_PROCESSES) -> bool: + normalized = _normalize_process_name(name) + return normalized in {_normalize_process_name(candidate) for candidate in suspicious} + + +def _is_debugger_present_windows() -> bool: + try: + return bool(ctypes.windll.kernel32.IsDebuggerPresent()) + except (AttributeError, OSError): + return False + + +def _tracer_pid_from_proc_status(status_path: Path = Path("/proc/self/status")) -> int: + try: + for line in status_path.read_text(encoding="utf-8").splitlines(): + if line.startswith("TracerPid:"): + return int(line.split(":", 1)[1].strip()) + except (OSError, ValueError): + return 0 + return 0 + + +def is_debugger_attached() -> bool: + if sys.gettrace() is not None: + return True + if os.name == "nt": + return _is_debugger_present_windows() + return _tracer_pid_from_proc_status() != 0 + + +def _process_command() -> list[str]: + if os.name == "nt": + return ["tasklist", "/FO", "CSV", "/NH"] + return ["ps", "-axo", "comm="] + + +def _parse_process_names(output: str) -> list[str]: + if os.name == "nt": + names: list[str] = [] + for row in csv.reader(output.splitlines()): + if row: + names.append(row[0]) + return names + return [line.strip() for line in output.splitlines() if line.strip()] + + +def list_process_names(runner: ProcessRunner = subprocess.run) -> list[str]: + try: + completed = runner( + _process_command(), + check=False, + capture_output=True, + text=True, + timeout=5, + ) + except (OSError, subprocess.TimeoutExpired): + return [] + if completed.returncode != 0: + return [] + return _parse_process_names(completed.stdout) + + +def has_suspicious_process( + process_names: Iterable[str] | None = None, + suspicious: Sequence[str] = SUSPICIOUS_PROCESSES, +) -> bool: + names = process_names if process_names is not None else list_process_names() + return any(is_suspicious_process_name(name, suspicious) for name in names) + + +def sha256_file(path: Path) -> str: + digest = hashlib.sha256() + with path.open("rb") as handle: + for chunk in iter(lambda: handle.read(1024 * 1024), b""): + digest.update(chunk) + return digest.hexdigest() + + +def expected_sha256_from_env() -> str | None: + value = os.environ.get("JAVELIN_EXPECTED_SHA256") + if value is None: + return None + value = value.strip().lower() + return value or None + + +def check_self_integrity(expected_sha256: str | None = None, script_path: Path | None = None) -> bool: + expected = expected_sha256 if expected_sha256 is not None else expected_sha256_from_env() + if expected is None: + return True + if len(expected) != 64 or any(char not in "0123456789abcdef" for char in expected): + return False + target = script_path if script_path is not None else Path(__file__) + try: + return sha256_file(target) == expected + except OSError: + return False + + +def run_checks() -> CheckResult: + if is_debugger_attached(): + return CheckResult(False, EXIT_DEBUGGER, "Debugger detected. Exiting.") + if has_suspicious_process(): + return CheckResult(False, EXIT_SUSPICIOUS_PROCESS, "Suspicious process detected. Exiting.") + if not check_self_integrity(): + return CheckResult(False, EXIT_INTEGRITY, "Integrity check failed (SHA-256 mismatch). Exiting.") + return CheckResult(True, 0, "All clear. Continue.") + + +def main() -> int: + print(f"{TAG} starting checks...") + result = run_checks() + stream = sys.stdout if result.ok else sys.stderr + print(f"{TAG} {result.message}", file=stream) + return result.exit_code + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/docs/DEMO.md b/docs/DEMO.md new file mode 100644 index 0000000..33a2c58 --- /dev/null +++ b/docs/DEMO.md @@ -0,0 +1,12 @@ +# Javelin Anti-Cheat Demo + +This folder contains a short visual demo for PR review: + +![Javelin anti-cheat demo](./javelin-anticheat-demo.gif) + +The demo shows: + +- Python regression tests passing. +- C++ client compiling and passing the all-clear path. +- Python SHA-256 integrity mismatch exiting with the guarded failure code. +- C++ CRC integrity mismatch exiting with the guarded failure code. diff --git a/docs/javelin-anticheat-demo.gif b/docs/javelin-anticheat-demo.gif new file mode 100644 index 0000000000000000000000000000000000000000..9487ba91995c8c273bb5f30bfa3a7cc40c0af7b9 GIT binary patch literal 31800 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%0DuXA5!Bh#k47 z$>6Oq_)wtjm9nB6UoOH~xbK%cTC^s$y<5W`T>R`%_B%s?D;WEf?e*-Ei+R1`hJ3g0 z_>_s!L#tuapST-{8n{CM#o27cLg4wqdtPJgOXq?v)=VDH4CDPbYjKYMgdLN}bC5g` z>q>IxoC?str(Ss!d5>K5BxxoTWeb-+{$$~L|B1OxRROiBSpDqFYXikg<$X6amm-Ex zHWS4|_h*+xo|+s$k_GuQ4m4JOYu{T`X{&$l#PzF;Rfc$wL_olcLkH5ezBteiRLF4% z6!PZtu7l?%eF?>Z{k=v$HPu_2ZUI81cQgZH@xCSASH(Nu+CN8^BFpv`AqCVAF8TSr zFj9e2+V%+Tzx*%OV*kkT%KrV&ZcW&Xs}f(ojOy)r9+&q?^&KHgvPN&;gPiXT|7I<2 zE6*dRSgE#h8y!9db!v;mOVyL+NlEZ#XH4QtcNeYQ@7M9#Xb zf;^YC_-&b-=|+=KJ_N&u$cd;JL-D>QDBGlnvOHd$g($V2P>R-35A zp1egGuZH86>D6D~d0z^3>Ru}CyV?9#PMK^E4>LIdDt7Dvd{FMJkZDQu&}gA(1&ilG zl@-k10E5QKsR^{df)42G%motH{vQS+I;{DRJwGv0(FS}`5mL64D zQDs(-l_)ju&WK<>=!YChWsCXX$M{8D(S$p-4q`11*YwgOkh=sfsfbOh6@ul6V*?n ze7xNWb~6(bHMLznzSkP;=6+7pw($B=q6qeHMJDTd415Dp8|*)*PcmLb`362BTwFRg zSwGq3d%e2h;@9hw4fDKy!CeH0?`e~b9}WC&jyE{`teI?Djq(e5PjLJ-G1t zgX8a?lPw%x3YAyO2_!nz3N@sJ3pYAJPE56-qbZScTF!jur`kokDbdFpol!wk9a0eg zSOYB=!Stz4c|-sBvyCnywNqV3qWx*^TCRI1r@Bvc``^FT=(>Mnsz(v(nv>H*LETsLel%Xv*_eJ63JJ{T)%MgAongip1{Ml8c^aOW8BT}} zERxf{Y#HU#q;tKgAIfA4j=vI;}$Ih>(hTJx3-pm*i|B+k88G7EgMJt*8?+Lp1wL(H9-hXP4{BS>|WVP@|^3xrcN?cUM0k`#M@;zT# z0ytZppY9yB>SgrzC0Zzli)#7%*~t)*25y zg{Vh_oe2j2)j%w>Bt^!O(95k)gCiXkpda;P1r~`F8EyO_G%0dy6(Z{KXne(02u+_< zG^IIbN5U#ZI@r)`ae5URQHlC3O0i8d@0#`vkPa?tV4t)NYqa)wws#>&k)PT)f@GS^ zdYkYuf-IO65Zor_Mk-SvirXL4W;MmxXfUa)L?S+3hthpzyD1~fV)hS^5QQ5w6BGNhf~GF}^eT;W=mc6|2I@uL(sN;Td+C?+(OB;&g1t z@uoX`R%DtvxdX4<@_n@HDZ9hbo8Nvc-efG^Y>dxhz0*>;yE8bluKd^u*%-&i4Se1$ z#aAIK`Y>lOD7lc&ZNPv`Ci{_TGdxB`%r>78asWAAyHzj9+Nisg=QK{U2xAyYHHy^e z>FVw2`~>kK(*o^j{#2ce2BRVc#>F~~vV6@*ie3QDuqdPFrce*3>l0L(M!P%4ciCE( zW>51CA#*k6TW82)axjrW&ugMt>*MpwPO$yR|>qzCV8~K4-E2sTi*= z&Y+9Zf5wy0sQ}d^$3~NB-^&dHK0zVeOM7c`cnJ7VN_+2A|7J&BUtP-!a%|fA%X%8E zOg5%@S^GIT{&6J1oJcDR94IC85n2fW8odVDWbV^z3mRIKj7=izT4ELnxH(=Wo4c`UxX zBy`!B74=4pZ1c^?LTByyFM9U6Thf}3MHwqMc|RHIQ)=4Yr?W6H5c8sWd_CeLjb>?i z>L)oq`|7Lu$X8vlLnG@a*U7X`vN69pPj