mirror of
https://github.com/TiagoVal97/cheatspecter.git
synced 2026-10-24 23:56:14 +02:00
A quality pass after 0.4.0 was tried on a real console. - Fix the Learn page crashing in current browsers when changing lesson steps - Clear feedback for every search outcome; real progress with time left and Cancel - Dialogs scroll inside the screen with Save and Cancel always in view - Fix a console error on every closed connection to the payload - Copy buttons work when the page is served from the PS5 - The practice game sits beside the lesson's tools and lasts between steps - Damaged cheat files, vanishing error messages, unreadable memory shown as zeros - Phone layout: one-line header, larger switches, readable rows - Browser tests under test/ui and a guard against effects that return values
82 lines
6.4 KiB
JSON
82 lines
6.4 KiB
JSON
{
|
|
"format": "cheatspecter-lesson",
|
|
"version": 1,
|
|
"id": "decisions",
|
|
"title": "Code with decisions: only for the player",
|
|
"summary": "Reading a little assembly: compare, jump, and how a cheat can tell you from an enemy.",
|
|
"minutes": 10,
|
|
"games": {},
|
|
"steps": [
|
|
{
|
|
"kind": "read",
|
|
"title": "Instructions, one line at a time",
|
|
"text": "The Code tab shows the game's program as a list of **instructions**, one per line. This language is called assembly. You do not need to write it to make good cheats, and you can go a long way reading only a handful of words.\n\nEach instruction is a short verb and the things it works on:\n\n- `mov eax, [rbx+0x10]` means **copy**. Take the number stored at rbx+0x10 and put it in eax.\n- `sub eax, 12` means **subtract** 12 from eax. `add` is the opposite.\n- `mov [rbx+0x10], eax` copies the other way: store eax at rbx+0x10.\n\nThe names `eax`, `rbx`, `rcx` are **registers**: a few scratch boxes inside the processor where numbers are held while being worked on. Square brackets mean \"the memory at this address\". So `[rbx+0x10]` is \"the value 0x10 bytes into the block rbx points at\": the health field from the last lesson.\n\nRead those three lines together and they say: fetch the health, take 12 off, store it back."
|
|
},
|
|
{
|
|
"kind": "choice",
|
|
"title": "Check yourself",
|
|
"text": "",
|
|
"question": "What does \"add dword ptr [rcx+0x40], 25\" do?",
|
|
"options": [
|
|
"Copies 25 into the register rcx.",
|
|
"Adds 25 to the four-byte number stored 0x40 bytes into the block rcx points at.",
|
|
"Jumps 25 instructions ahead."
|
|
],
|
|
"answer": 1,
|
|
"explain": "Square brackets are memory; \"dword\" says the number is four bytes; add adds. This is what \"open a chest, get 25 gold\" looks like."
|
|
},
|
|
{
|
|
"kind": "read",
|
|
"title": "How code makes a decision",
|
|
"text": "Code runs from top to bottom, one instruction after another, unless something tells it to go elsewhere. Decisions are made of two instructions that always travel together:\n\n- `cmp` **compares** two things and remembers the result.\n- A **jump** then goes somewhere else, or does not, depending on that result.\n\nThe jumps you will meet most:\n\n- `je` jump if they were **equal** (also written `jz`)\n- `jne` jump if they were **not equal** (also `jnz`)\n- `jl`, `jg` jump if the first was **less**, or **greater**\n- `jmp` jump always, no question asked\n\nHere is a game deciding whether a hit should hurt, in four lines:\n\n1. `cmp dword ptr [rbx+0x18], 1` compare the fighter's team with 1\n2. `je 0x401040` if it is 1, jump ahead to 0x401040, past the next line\n3. `sub dword ptr [rbx+0x10], 12` otherwise: take 12 off the health\n4. (at 0x401040) the code carries on\n\nIn words: **if the fighter is on team 1, skip the damage.** A game with friendly fire switched off has exactly this somewhere."
|
|
},
|
|
{
|
|
"kind": "choice",
|
|
"title": "Check yourself",
|
|
"text": "",
|
|
"question": "In the four lines above, the fighter's team is 2. What happens?",
|
|
"options": [
|
|
"The jump is taken and no damage is done.",
|
|
"The jump is not taken, and 12 is subtracted from the health.",
|
|
"The game crashes, because 2 is not a valid team."
|
|
],
|
|
"answer": 1,
|
|
"explain": "cmp finds that 2 is not equal to 1, so \"jump if equal\" does nothing and the next line runs: the subtraction."
|
|
},
|
|
{
|
|
"kind": "read",
|
|
"title": "Making a decision go your way",
|
|
"text": "Two classic ways to change a decision, both with the tools you have used.\n\n# Remove the jump\n\nReplace a jump with no-ops and the code can no longer skip what follows. If the jump was \"skip the reward unless you paid\", everyone now gets the reward.\n\n# Make the jump unconditional\n\nChange `je` (jump if equal) into `jmp` (jump always) and what follows is always skipped. If what follows is \"take damage\", nobody is ever hurt.\n\nNeither of these tells you from an enemy, though. They change the rule for everyone, like the guard in lesson 10.\n\n# Add a decision of your own\n\nTo protect only the player, the code needs a new question: **is this block the player's?** The original code has no room for extra instructions, so the trick is a detour:\n\n1. Replace the damage instruction with a jump to an empty piece of memory.\n2. There, put your own few lines: compare something that differs between you and enemies, such as the team number at `[rbx+0x18]`. If it is yours, skip the damage. Otherwise do the original subtraction.\n3. Jump back to the instruction after the one you replaced.\n\nDone that way, enemies take damage as before and you do not. In the Code tab this is what **Run my own code here…** on an instruction's menu is for.\n\n> note: How do you know what differs between you and an enemy? Lesson 10: look at both blocks in the Memory tab, side by side. A team number, a \"controlled by player\" flag, or a pointer that only the player's block has, is usually within a few dozen bytes of the health."
|
|
},
|
|
{
|
|
"kind": "answer",
|
|
"title": "Check yourself",
|
|
"text": "",
|
|
"question": "A line reads \"jne 0x401200\". You want it to jump always, whatever was compared. What should its first word be?",
|
|
"accept": [
|
|
"jmp",
|
|
"jmp 0x401200"
|
|
],
|
|
"explain": "`jmp` jumps without asking. Swapping a conditional jump for `jmp`, or for no-ops, are the two smallest edits that flip a decision."
|
|
},
|
|
{
|
|
"kind": "choice",
|
|
"title": "Check yourself",
|
|
"text": "",
|
|
"question": "You want enemies to still take damage, but not you. The damage is done by one shared instruction. What is the right tool?",
|
|
"options": [
|
|
"Replace the instruction with no-ops.",
|
|
"A detour that checks whose block it is before subtracting.",
|
|
"Restart the game."
|
|
],
|
|
"answer": 1,
|
|
"explain": "No-ops would protect everyone. A detour with a comparison lets you choose who the subtraction applies to."
|
|
},
|
|
{
|
|
"kind": "soon",
|
|
"title": "Practice",
|
|
"text": "The practice games in these lessons cannot run code that you add yet, so this lesson has no practice step of its own. When they can, you will add the \"only for the player\" check here yourself.\n\nUntil then, lesson 10's practice game is a good place to read real instructions: use **Find what writes to this** on your health and press **Show** to see the code around it."
|
|
}
|
|
]
|
|
}
|