Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
119 changes: 76 additions & 43 deletions AntiCheat.cpp
Original file line number Diff line number Diff line change
@@ -1,36 +1,33 @@
// AntiCheat.cpp
// Javelin Project - Minimal Anti-Cheat guards
// Features: debugger detection, suspicious process scan, basic self-integrity (CRC32)
// Features: debugger detection, suspicious process scan, self-integrity (CRC32 + optional SHA-256)

#include <windows.h>
#include <tlhelp32.h>
#include <wincrypt.h>
#include <iostream>
#include <fstream>
#include <vector>
#include <string>
#include <algorithm>
#include <iomanip>
#include <sstream>

#pragma comment(lib, "advapi32.lib")

static const char* kTag = "[Javelin AntiCheat] ";

// --- Configurable lists ---
static std::vector<std::string> kSuspiciousProcesses = {
"cheatengine.exe",
"ollydbg.exe",
"x64dbg.exe",
"httpdebuggerui.exe",
"ida.exe",
"ida64.exe",
"scylla.exe",
"processhacker.exe"
"cheatengine.exe", "ollydbg.exe", "x64dbg.exe", "httpdebuggerui.exe",
"ida.exe", "ida64.exe", "scylla.exe", "processhacker.exe"
};

// --- Utils ---
static std::string toLower(std::string s) {
std::transform(s.begin(), s.end(), s.begin(), ::tolower);
return s;
}

// Simple CRC32 (polynomial 0xEDB88320)
// CRC32 (poly 0xEDB88320)
static uint32_t crc32(const std::vector<uint8_t>& data) {
uint32_t crc = 0xFFFFFFFFu;
for (uint8_t b : data) {
Expand All @@ -51,90 +48,126 @@ static bool readFile(const std::wstring& path, std::vector<uint8_t>& out) {
if (size <= 0) return false;
f.seekg(0, std::ios::beg);
out.resize(static_cast<size_t>(size));
if (!f.read(reinterpret_cast<char*>(out.data()), size)) return false;
return true;
return static_cast<bool>(f.read(reinterpret_cast<char*>(out.data()), size));
}

static bool sha256Hex(const std::vector<uint8_t>& data, std::string& outHex) {
HCRYPTPROV hProv = 0;
HCRYPTHASH hHash = 0;
if (!CryptAcquireContext(&hProv, nullptr, nullptr, PROV_RSA_AES, CRYPT_VERIFYCONTEXT))
return false;
bool ok = false;
if (CryptCreateHash(hProv, CALG_SHA_256, 0, 0, &hHash)) {
if (CryptHashData(hHash, data.data(), static_cast<DWORD>(data.size()), 0)) {
DWORD len = 32;
BYTE hash[32]{};
if (CryptGetHashParam(hHash, HP_HASHVAL, hash, &len, 0)) {
std::ostringstream oss;
for (DWORD i = 0; i < len; ++i)
oss << std::hex << std::setw(2) << std::setfill('0') << (int)hash[i];
outHex = oss.str();
ok = true;
}
}
CryptDestroyHash(hHash);
}
CryptReleaseContext(hProv, 0);
return ok;
}

// --- 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) {}
#endif

return false;
}

static bool checkSuspiciousProcesses() {
HANDLE snap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
if (snap == INVALID_HANDLE_VALUE) return false;

PROCESSENTRY32 pe{};
pe.dwSize = sizeof(pe);
if (!Process32First(snap, &pe)) {
CloseHandle(snap);
return false;
}

if (!Process32First(snap, &pe)) { 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 (name == toLower(bad)) { CloseHandle(snap); return true; }
}
} while (Process32Next(snap, &pe));

CloseHandle(snap);
return false;
}

static bool checkSelfIntegrity(uint32_t expectedCrc) {
static bool getSelfPath(std::wstring& pathOut) {
wchar_t path[MAX_PATH]{};
if (!GetModuleFileNameW(nullptr, path, MAX_PATH)) return false;
pathOut = path;
return true;
}

static bool checkSelfIntegrityCrc(uint32_t expectedCrc) {
std::wstring path;
if (!getSelfPath(path)) return false;
std::vector<uint8_t> bytes;
if (!readFile(path, bytes)) return false;
return crc32(bytes) == expectedCrc;
}

uint32_t current = crc32(bytes);
return current == expectedCrc;
static bool checkSelfIntegritySha256(const std::string& expectedLowerHex) {
if (expectedLowerHex.empty()) return true;
std::wstring path;
if (!getSelfPath(path)) return false;
std::vector<uint8_t> bytes;
if (!readFile(path, bytes)) return false;
std::string got;
if (!sha256Hex(bytes, got)) return false;
std::string exp = expectedLowerHex;
for (char& c : exp) c = (char)tolower((unsigned char)c);
return got == exp;
}

// --- Entry helper (embed a baseline CRC once you ship a build) ---
#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

// Optional: /DJAVELIN_EXPECTED_SHA256=\"abc...\" (64 hex chars). Empty = skip.
#ifndef JAVELIN_EXPECTED_SHA256
#define JAVELIN_EXPECTED_SHA256 ""
#endif

int main() {
std::cout << kTag << "starting checks...\n";

if (checkDebugger()) {
std::cerr << kTag << "Debugger detected. Exiting.\n";
return 0xDEB; // code for debugger
return 0xDEB;
}

if (checkSuspiciousProcesses()) {
std::cerr << kTag << "Suspicious process detected. Exiting.\n";
return 0xBAD; // code for bad process
return 0xBAD;
}

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 (!checkSelfIntegrityCrc(JAVELIN_EXPECTED_CRC32)) {
std::cerr << kTag << "Integrity check failed (CRC32 mismatch). Exiting.\n";
return 0xC32;
}
}
{
std::string expectedSha = JAVELIN_EXPECTED_SHA256;
if (!expectedSha.empty()) {
if (!checkSelfIntegritySha256(expectedSha)) {
std::cerr << kTag << "Integrity check failed (SHA-256 mismatch). Exiting.\n";
return 0xA56;
}
}
}

Expand Down
47 changes: 47 additions & 0 deletions INTEGRITY.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
# Integrity verification

## Python

1. Compute the hash of `anticheat.py` after you freeze the file:

```bash
python -c "import hashlib,pathlib; p=pathlib.Path('anticheat.py'); print(hashlib.sha256(p.read_bytes()).hexdigest())"
```

2. Set the env var before launch:

```bash
export JAVELIN_EXPECTED_SHA256=<64-char-hex>
python anticheat.py
```

3. On mismatch the process exits with code `0xA56` (2646) and does not continue.

Unset `JAVELIN_EXPECTED_SHA256` to skip the check (dev mode).

## C++ (Windows)

### CRC32 (existing)

Compile with a build-time CRC of the final `.exe`:

```text
cl /EHsc /DJAVELIN_EXPECTED_CRC32=0xDEADBEEF AntiCheat.cpp
```

Use `0` (default) to skip CRC checks while iterating.

### SHA-256 (optional)

```text
cl /EHsc /DJAVELIN_EXPECTED_SHA256=\"<64-hex>\" AntiCheat.cpp
```

Compute the SHA-256 of the **built executable** after linking, then recompile once with that constant, or inject via your packer/CI.

Mismatch exits with code `0xA56`.

## Notes

- Hash the **artifact you ship** (script or exe), not intermediate objects.
- Recompute after every intentional release; CI should fail if constants go stale.
10 changes: 9 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1 +1,9 @@
# py-workedtask
# py-workedtask (Javelin AntiCheat)

Minimal anti-cheat guards:

- Debugger detection (Windows C++)
- Suspicious process scan (Windows C++)
- **Self-integrity**: CRC32 / SHA-256 of running executable (C++), SHA-256 of script via `JAVELIN_EXPECTED_SHA256` (Python)

See [INTEGRITY.md](INTEGRITY.md) for how to set expected hashes.
54 changes: 54 additions & 0 deletions anticheat.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
#!/usr/bin/env python3
"""Javelin minimal anti-cheat helpers (Python).

Integrity: SHA-256 of this script file vs env JAVELIN_EXPECTED_SHA256.
On mismatch, exits with a guarded non-zero code.
"""
from __future__ import annotations

import hashlib
import os
import sys
from pathlib import Path

TAG = "[Javelin AntiCheat] "
EXIT_INTEGRITY = 0xA56 # 2646


def script_path() -> Path:
return Path(__file__).resolve()


def sha256_file(path: Path) -> str:
h = hashlib.sha256()
with path.open("rb") as f:
for chunk in iter(lambda: f.read(1024 * 1024), b""):
h.update(chunk)
return h.hexdigest()


def check_integrity() -> None:
expected = os.environ.get("JAVELIN_EXPECTED_SHA256", "").strip().lower()
if not expected:
print(f"{TAG}integrity skipped (JAVELIN_EXPECTED_SHA256 unset)")
return
got = sha256_file(script_path())
if got != expected:
print(
f"{TAG}Integrity check failed (SHA-256 mismatch). "
f"expected={expected} got={got}",
file=sys.stderr,
)
raise SystemExit(EXIT_INTEGRITY)
print(f"{TAG}integrity ok")


def main() -> int:
print(f"{TAG}starting checks...")
check_integrity()
print(f"{TAG}All clear. Continue.")
return 0


if __name__ == "__main__":
raise SystemExit(main())