2026.05.31 13:43
AI zhakował grę - pierwszy raz mi się tak udało

Wczoraj pierwszy raz udało mi się, że AI na moją prośbę skutecznie zhakował grę. Oto jak to zrobiliśmy.
Jest w Internecie taka strona z grami do grania online https://www.crazygames.com/ . Od dawna próbowałem hakować te gry, ale tylko nieliczne mi się udawało. Wczoraj pomyślałem, że wezmę jedną z gier i każę AI-owi ją zhakować. Na ofiarę wybrałem grę https://www.crazygames.com/game/nuts-puzzle-sort-by-color . Odpaliłem pewnego agenta AI, który pod maską korzysta z różnych modeli (sam sobie wybiera), ale zwykle z modeli Claude'a, w Chrome pograłem trochę w tę grze, potem z devtools z zakładki network zapisałem wszystkie requesty i response'y jako plik har, dałem ten plik agentowi i kazałem kombinować. On długo kombinował, dawał mi kawałki javaskryptu do odpalenia z konsoli w devtools, mówiłem mu co widzę to w tej zakładce to w tamtej, aż na koniec AI napisał mi kawałek kodu, który pozwala zmienić w grze liczbę monet. Ten kawałek kodu jest przystosowany do tego, żeby używał go programista, który zna się na kompuyterze i rozumie, co robi. Używa się go tak:
Najpierw wchodzę w przeglądarce na https://www.crazygames.com/game/nuts-puzzle-sort-by-color i gram trochę, żeby przejść początkowy tutorial (ja grałem tyle, żeby dojść do 120 punktów, czyli tych takich monetek).
Wtedy w devtools idę do zakładki "console", tam z dropdowna wybieram, na którym ifame chcę działać. Domyślnie wybrany jest iframe "top", mamy wybrać "nuts-puzzle-sort-by-color.game-files.crazygames.com".
W console wklejam ten snippet i go uruchamiam.
On mnie pyta, ile mam punktów, i następnie szuka czy gdzieś na stercie Unity leży ta liczba. Mówi ile wystąpień znalazł. Mam wtedy do wyboru: albo nadpisać wszystkie te miejsca nową liczbą, albo jeszcze nie. Za pierwszym odpaleniem znajdzie pewnie ponad setkę wystąpień, więc może lepiej od razu nie nadpisywać, żeby gry nie wywalić. Następnie gram trochę dalej, żeby liczba punktów się zmieniła, i znowu odpalam ten kawałek kodu, i znowu mówię mu, ile teraz mam punktów. On przegląda tylko te miejsca w pamięci, co poprzednio były znalezione, i szuka tam ten nowej liczby. I tak robię, aż znajdzie małe kilka miejsc (zwykle wystarczają mi dwa odpalenia), każę mu nadpisać, gram chwilę (żeby liczba ze zmiennej trafiła na ekran) i babam!
U mnie to działa (testowałem w Chrome). Używałem też tego samego snippeta (dokłądnie tego samego) do zmieniania numeru lewela. Ciekawe, czy w innych grach zrobionych na Unity też by on działał. No chyba tak? W końcu szukanie zmiennych na starcie to szukanie zmiennych na stercie.
W każdym razie że AI zhakował mi grę, którą kazałem mu zhakować - to ważny moment.
(function() {
    // Check if we're in the right iframe
    if (typeof unityGameInstance === 'undefined') {
        console.error("❌ Wrong iframe! In DevTools console, click the dropdown at the top-left (shows 'top') and select the iframe with 'nuts-puzzle-sort-by-color.game-files.crazygames.com' or similar game URL. Then run this snippet again.");
        return;
    }
 
    let view = new DataView(unityGameInstance.Module.HEAPU8.buffer);
    let len = unityGameInstance.Module.HEAPU8.length;
 
    // If we have narrowed candidates from a previous run, offer to search within them
    if (window._coinCandidates && window._coinCandidates.length > 0) {
        let choice = prompt(
            "Found " + window._coinCandidates.length + " saved candidates from last run.\n\n" +
            "1 = Search NEW value only within saved candidates (to narrow down)\n" +
            "2 = Start fresh (full scan)\n" +
            "3 = Overwrite ALL saved candidates with a new value"
        );
 
        if (choice === "1") {
            let newVal = parseInt(prompt("What is the CURRENT coins value you see in the game?"));
            if (isNaN(newVal)) return console.log("Cancelled.");
            let stillMatch = window._coinCandidates.filter(off => view.getInt32(off, true) === newVal);
            console.log("🔍 " + stillMatch.length + " of " + window._coinCandidates.length + " candidates still have value " + newVal);
            window._coinCandidates = stillMatch;
            if (stillMatch.length === 0) {
                console.log("No matches. Run again and start fresh (option 2).");
                return;
            }
            if (stillMatch.length <= 10) {
                stillMatch.forEach(off => console.log("  offset " + off + " = " + view.getInt32(off, true)));
            }
            let action = prompt(stillMatch.length + " candidates remain.\n\n1 = Overwrite all with new value\n2 = Keep narrowing (play more, change coins, run again)");
            if (action === "1") {
                let setTo = parseInt(prompt("Set coins to:"));
                if (isNaN(setTo)) return console.log("Cancelled.");
                for (let off of stillMatch) view.setInt32(off, setTo, true);
                console.log("✅ Set " + setTo + " at " + stillMatch.length + " locations. Check in the game!");
            } else {
                console.log("👍 Saved " + stillMatch.length + " candidates. Play until coins change, then run again.");
            }
            return;
        } else if (choice === "3") {
            let setTo = parseInt(prompt("Set coins to:"));
            if (isNaN(setTo)) return console.log("Cancelled.");
            let count = 0;
            for (let off of window._coinCandidates) {
                view.setInt32(off, setTo, true);
                count++;
            }
            console.log("✅ Set " + setTo + " at " + count + " locations. Check in the game!");
            return;
        }
        // choice === "2" falls through to fresh scan
    }
 
    // Fresh scan
    let coins = parseInt(prompt("How many coins do you see in the game right now?"));
    if (isNaN(coins)) return console.log("Cancelled.");
 
    let found = [];
    for (let i = 0; i < len - 4; i += 4) {
        if (view.getInt32(i, true) === coins) {
            found.push(i);
        }
    }
    console.log("🔍 Found " + found.length + " locations with value " + coins);
 
    // Filter - close to a small number (1-20) which is likely current_win/level
    let narrow = found.filter(off => {
        for (let j = off - 64; j <= off + 64; j += 4) {
            if (j !== off && j >= 0 && j < len - 4) {
                let v = view.getInt32(j, true);
                if (v >= 1 && v <= 20) return true;
            }
        }
        return false;
    });
 
    let targets = narrow.length > 0 && narrow.length < 500 ? narrow : found;
    console.log("📋 After filtering: " + targets.length + " candidates" + (targets === narrow ? " (near small ints)" : " (unfiltered)"));
 
    window._coinCandidates = targets;
 
    let action = prompt(
        targets.length + " candidates found.\n\n" +
        "1 = Overwrite ALL with new value now\n" +
        "2 = Save candidates & narrow down later (play until coins change, then run again)"
    );
 
    if (action === "1") {
        let setTo = parseInt(prompt("Set coins to:"));
        if (isNaN(setTo)) return console.log("Cancelled.");
        for (let off of targets) view.setInt32(off, setTo, true);
        console.log("✅ Set " + setTo + " at " + targets.length + " locations. Check in the game!");
    } else {
        console.log("👍 Saved " + targets.length + " candidates in window._coinCandidates. Play until coins change, then run this snippet again.");
    }
})();


komentarze:

ksywa:

tu wpisz cyfrę cztery: (tu wpisz cyfrę cztery: (to takie zabezpieczenie antyspamowe))

komentarze wulgarne albo co mi się nie spodobają będę kasował


powrót na stronę główną

RSS