Your question is really broad. I can tell you about how I did in one instance, for fun, in a small arcade game several years ago. This should make you gain some educational insight, but won't help you much otherwise, I'm afraid.
I simply had another program running in background, scoping the memory of the arcade game.
I started playing, and then I lost one life. Hit both shift keys -- and a snapshot of the memory was saved to disk unbeknownst to the running arcade. Then another. BAM another snapshot. A third, and a fourth. And then game over and I exited the arcade game.
Then I just compared the snapshots, looking for a memory location that changed from one snapshot to the next, either increasing or decreasing by exactly one. I remember finding some twenty of them. I could have checked all of them, but I spotted immediately that one among them, and only one, had value "five" at the start of the game, and became "four" on my first death. And I had started with five ships. Now that's interesting...
So I wrote a small program that would look for that memory address, recognizing it by its distance from other pieces of code and strings that didn't change, checking its value every few seconds, and just poking back "5" into it as soon as it became anything less:
look for string 'COPYRIGHT ACME GAMES'
is it a five?
no, abort, something went wrong
yes. lie in wait.
is it still a five?
no. Set it to five.
sleep for a couple of seconds
Sure enough, the next game, I died - and an instant later, my four remaining starships became five again. For the heck of it, I discovered that I could get up to seven 'lives' with this method (eight crashed the game).
By using a "debugger" it would also have been possible to stop the program as soon as the location was modified, thereby seeing which instruction had done the deed. Then you would just blot out that one instruction from the assembler code. Or you could follow the program flow in reverse, and discover where, for example, the collision check was made between your player's sprite and the enemy missiles'. Thwart that check, and the enemy missiles no longer affect you (possibly, also yours no longers affect them; it's a tricky business).
I expect the techniques to have matured and progressed a great deal in these years, but I'm confident that basically the idea is still the same - spot the change you don't like, then either defang the code causing it, or patch the code so that the damage is undone as soon as it happens.
To make things more difficult one could devise strategies to make the changes stand out less, or make them seemingly random. Add "chaff" or "dummy" variables that change just as if they were useful. Use them as telltales - if all variables don't tally, it means that someone is trying to hack.
In the end, however, you can't beat a determined and experienced hacker. All you can do is try making the deed not worth his while. There are code obfuscators, code protectors, code encryptors, debugger detectors, anti-virtualization techniques...