Made by SteveAndrew
Ammo is easy to find on the other hand, however its tough to script it / and if there is a pointer it's more then 5 levels deep...
It's a double value, and it's dynamic address changes each time you die! (how lovely!) So you absolutely must make a script or find a pointer because otherwise it would be too painful to have to re-scan for it every single time you die (even though the second scan finds it easily)
I've gotten Infinite Ammo working though... ROCK SOLID NOW! Played through a speed run many chapters, and haven't gotten any more errors (well except ones that I would get even with the cheat off [dev is still ironing out some bugs])
Using it I've now beaten the game! And once you get the silenced uzi at start mask infinite ammo is really a joy in this game lol...
Edit saves.dat in game root directory with notepad (it's just a standard text file not binary data) and change the first line's 26 digits into all 1's to unlock all masks...
Note: I'm still attempting to find a godmode. (its not really a health cheat since you have no health at all) Though it would have to be something like avoiding collision detection of your player with enemy attacks... So it's very tricky to find as you die instantly... The masks which let you get hit with one/two bullets may help, but not by much since you usually get bombarded with bullets so being able to get hit with 1/2 isn't really helping much... Also they don't seem to always work (may have to try speed hack or something)
Something else interesting is the game appears to be made with game maker, which is surprising to me (found game maker strings in the games memory)
Well here it is: (possibly the first public cheat for this game) Very Happy Rock solid!
EDIT 2: Have fixed the issue and it's now working very solidly! Also added Infinite Throwing Knives cheat to the CT which can't be enabled at the same time as Infinite Ammo (same hook address used) but it's okay as when playing as the second character you can't use melee weapons / guns and vice versa... They use assert just to make sure you don't accidentally enable them both at once as well!
UPDATED AGAIN:
Code:
//Hotline Miami
//Infinite Ammo v1.1.1
//Steve Andrew
[enable]
alloc(InfiniteAmmo,128)
label(NinetyNineBullets)
label(AmmoCheck)
label(ApplyAmmo)
label(RegularCode)
label(AmmoRet)
assert(HotlineMiami.exe+2abc, FD F3 A5 89 C1)
InfiniteAmmo:
cmp [esp+c],18 //Filter out a lot of stuff
jne RegularCode
cmp [esp+18],e //0x0E == Narrow down
je AmmoCheck
cmp [esp+18],10//0x10 == Narrow down 2
je AmmoCheck
jmp RegularCode
AmmoCheck:
cmp [esp+fc],7 //one final narrow it down check
jne RegularCode
cmp [esp+c0],d //most guns
je ApplyAmmo
cmp [esp+c0],8 //double barrel shotgun (maybe more)
je ApplyAmmo
jmp RegularCode
ApplyAmmo:
fld qword ptr [NinetyNineBullets]
fstp qword ptr [edi-4]
jmp AmmoRet+c
RegularCode:
std
repe movsd
mov ecx,eax
jmp AmmoRet
NinetyNineBullets:
dq (double)99
HotlineMiami.exe+2abc:
jmp InfiniteAmmo
AmmoRet:
[disable]
HotlineMiami.exe+2abc:
db FD F3 A5 89 C1
dealloc(InfiniteAmmo)
Okay I realized the problem after today I couldn't enable it either... I knew why it wasn't enabling (the assert) but I wasn't sure why until I took a closer look... See attached image...
The assert I added was just to make sure you don't enable both at the same time, but the weird thing is when you disable the cheat cheat engine writes a different machine code for the instruction 'mov ecx,eax' then the game has originally... It's still the same instruction, just represented a little differently (I didn't know the same instruction could have more then one representation in bytes)
So my solution is to change the assert to test the machine code bytes that the game starts with, and then manually define those same bytes when we put it back... (when disabling the cheat) OR you could just remove the assert, but be sure not to enable ammo + knives at the same time (only one will be in effect at a time anyway [they hook the same address, the second one enabled's hook will overwrite the other if you enable both])
Just look at the image the memory viewer on top (just loaded the game didn't touch the memory yet) of the other memory viewer (after enabling + disabling ammo or knives cheat)
It was wrong of me to assume that CE would put the bytes back exactly the same as the game originally had them even though the same exact instructions were put back... Next time I use assert I'll make sure to DB on the disable section Very Happy
FIXING THE SCRIPT + CT FOR YOU GUYS NOW! Smile
mgr.inz.Player wrote:
@SteveAndrew, this game is using LUA?
@mgr.inz.Player Is there an easy way to check if the game is using LUA? I know it's using some sort of scripting engine (Does game maker use LUA? as the game is made with game maker I think)
I've heard that if a game is using lua though it makes it real easy to cheat if you hook the lua part and then you can run any lua scripts you want... (though not sure how you would figure out lua variable names and such)
Tons of addresses pass through and get written at the address I hooked (HotlineMiami+2abc) via a repe movsd instruction after esi and edi and ecx are loaded up... It also uses 'std' at the start and 'cld' at the end (This just reverses which way the repe movsd instruction goes right?) Because when debugging it I seen that it writes to the ammo address (which is a double) a dword at a time [well duh its movsd] backwards... So that's why I did [edi-4] when setting the double value directly... Then I jump skip the movsd and other movsb instructions the game normally does with everything (not sure what the following movsb does, but since I made the game change the ammo value differently then the rest I skipped it also to avoid issues...)
Take a look at my attached images and see if it looks like lua, these are the kind of error messages I got while screwing around with this hook address before I got ammo working properly! Very Happy Does it look like LUA to you?
Scripting language games always give me trouble as they aren't easy to script (pretty ironic isn't it? lol)
So is there an easy way to check if the game uses LUA? and if so how can we tear the game apart using LUA! I'm not too familiar with LUA or other scripting languages so help me out here !
-------------
This game doesn't use lua but GML (Game Maker Language, doh...). GML seems to be pseudo compiled, ie: scripts are shipped in (zlibbed?+) crypted text form, then decrypted and compiled (probably into pseudo code) during a slash screen at startup.
So far I found out that is you break execution at the ret 4 below:
Code:
HotlineMiami.exe+85A14 - 49 - dec ecx
HotlineMiami.exe+85A15 - 75 CA - jne HotlineMiami.exe+859E1
HotlineMiami.exe+85A17 - 5F - pop edi
HotlineMiami.exe+85A18 - 5E - pop esi
HotlineMiami.exe+85A19 - 5B - pop ebx
HotlineMiami.exe+85A1A - 5D - pop ebp
HotlineMiami.exe+85A1B - C2 0400 - ret 0004
...you can search & replace the string "ammo=6" (NON unicode) by "ammo=9" and now you get 9 shells when picking up a shotgun.
Replacing all instances of "ammo-=1" or "life-=1" did not produce any change though.
EDIT1: Even better: replace "my_id.ammo=ammo" by "my_id.ammo=9999". When low on ammo, throw your gun and pick it up again, you're good for another 10k shots.
EDIT2: Godmode against melee attacks only: replace "scrPlayerDie" by "//rPlayerDie".
@non-hackers: No it can't be put in a CT table. If anything player-friendly gets done out of my work, it'll be a loader that will launch the game and apply the cheats at the right moment. Those will not be deactivable without restarting the game.
@hackers: the best clues I can give so far for a gun godmode, is that the "press R to restart" message is triggered by "if !instance_exists(objPlayer)", so you're looking for something that involves the "instance_destroy()" function.
EDIT3: gun godmode: replace all:
Code:
if bullets>1 or round(random(1)) or energie<0 {
my_id=instance_create(x,y,objPlayerDead)
with:
Code:
if bullets>1 and bullets<1 /*1)) or energie*/ {
my_id=instance_create(x,y,objPlayerDead)
Now to put that into a loader...
---------------
Made by Gniarf
@non-hackers: godmode and plentiful ammo PATCH for HotlineMiami.exe v1.0: [Link]
releases notes:
How to use:
1-Extract the 7z archive to your Hotline Miami directory.
2-Run HotlineMiami.exe.GodMode+ammo-patch.exe. No, is is NOT a virus, whatever bitdefender says.
3-press "Patch", if the patcher does not find HotlineMiami.exe, point it to this file.
4-Enjoy, cheats are engraved into your game so you do not need to re-run the patcher.
Normally the patcher should have created HotlineMiami.exe.BAK, if you want to switch back to the original game.
Plentiful ammo: each time you throw a gun, its ammo count resets back to 9999.
@SteveAndrew
In the end, since I found gigantic code cave (the end of the CODE section) I managed to squeeze the whole loader into the exe, so a simple file patcher is enough for a player-friendly release.
The asm code I quoted earlier actually handles decrypting the scripts, which are stored in a single massive buffer. So the idea is that since you're sweeping the whole script buffer there, would you kindly (bioshock-like) patch our strings as well?
As a by-product I actually made a AA script (that I injected into the target, then copied to the exe that I used in a file comparer-patcher) if you wanna know how I've done it.
SteveAndrew wrote:
Don't know where my head was on that one!
Too close to the coffee machine imho...Get some sleep!
SteveAndrew wrote:
And you have to change all instances for each one you found right?
Actually I said "replace all instances" because I don't know what instance does what and testing them one by one would be too tedious. Plus the ascii dump column of CE's memory viewer isn't really a top-notch IDE!
SteveAndrew wrote:
Even though I already beat the game without godmode
Not going to happen on my end since keyboard is azerty and I can't change the key bindings (try to play an action game with zsqd on a qwerty keyboard...). After playing the 4 first maps like a gramps that never touched a keyboard in his life I decided to give up. So yeah, there might be issues with knives or other features I don't know about.
SteveAndrew wrote:
Here's my issue though, since you have to replace all instances of these strings, and they are scattered throughout the memory... Obviously it will crash if you just try to go from imagebase to 0x7fffffff without some way of knowing whether the address is valid or not...
1-Luckily all script are in the same memory region which is 01364495 bytes in size and the base address for this buffer is stored at [esi+4] in the code you hooked. I really should have mentioned earlier it was the decrypting function.
2-If the strings were really scattered all over the mem, you'd have to go from 1, not image base to 0x7fffffff.
SteveAndrew wrote:
I've tried using IsBadReadPtr so far, but it seems to be really slow.
1-IsBadReadPtr works by setting up an exception handler and trying to read the address you pointed. If the pointer you supplied is invalid IsBadReadPtr will trigger an access violation and handle it through its exception handler. I don't know enough about exceptions to know why the are so slow, but they are sluggish.
2-In your future scanners, I guess you could improve your scan speed by increasing the scan address by 0x1000 (size of a memory page) each time you get an invalid pointer, since memory protections apply to a whole page.
Loader source:
Code:
//Hotline miami
//plentiful ammo+godmode patch
//V3
[ENABLE]
label(CodeChecker)
label(MeleeScriptHook)
label(AmmoScriptHook)
label(OriginalCode)
HotlineMiami.exe+85A10:
jmp CodeChecker
HotlineMiami.exe+184292:
CodeChecker:
MOV BYTE PTR DS:[EBX+EAX],DL //original code
cmp dl,')' //did we just write
//"if bullets>1 or round(random(1)) or energie<0 {0xd 0xa
//my_id=instance_create(x,y,objPlayerDead)"
//?
//lazyness says: just check Dead,my_i,if bulle
jne MeleeScriptHook
cmp dword [EBX+EAX-4],'Dead'
jne MeleeScriptHook
cmp dword [EBX+EAX-27],'my_i'
jne MeleeScriptHook
cmp dword [EBX+EAX-58],'if b'
jne MeleeScriptHook
cmp dword [EBX+EAX-54],'ulle'
jne MeleeScriptHook
//yes, so turn "if bullets>1 or round(random(1)) or energie<0 {"
//into if "bullets>1 and bullets<1 /*1)) or energie*/"
mov dword [EBX+EAX-4b],'and ' //patch the string
mov dword [EBX+EAX-47],'bull'
mov dword [EBX+EAX-43],'ets<'
mov dword [EBX+EAX-3f],'1 /*'
mov word [EBX+EAX-2d],'*/'
jmp OriginalCode
MeleeScriptHook:
cmp dl,'e' //did we just write a "scrPlayerDie"?
jne AmmoScriptHook
cmp dword [EBX+EAX-4],'erDi'
jne AmmoScriptHook
cmp dword [EBX+EAX-8],'Play'
jne AmmoScriptHook
cmp word [EBX+EAX-a],'cr'
jne AmmoScriptHook
//yes,turn "scrPlayerDie" into "//rPlayerDie"
mov word [EBX+EAX-b],'//'
jmp OriginalCode
AmmoScriptHook:
cmp dl,'o' //did we just write a "my_id.ammo=ammo"?
jne OriginalCode
cmp dword [EBX+EAX-4],'=amm'
jne OriginalCode
cmp dword [EBX+EAX-8],'ammo'
jne OriginalCode
cmp dword [EBX+EAX-c],'_id.'
jne OriginalCode
cmp word [EBX+EAX-e],'my'
jne OriginalCode
//yes,turn "my_id.ammo=ammo" into "my_id.ammo=ammo=9999"
mov dword [EBX+EAX-3],'9999'
OriginalCode:
INC EAX
DEC ECX
jmp HotlineMiami.exe+85A15
[DISABLE]
//nothing!
--------------
Let's do a little experiment: you'll have to fix the patch yourself. To do so:
1-download ollydbg 1.10 and not another version.
2-In ollydbg, go to file->open->open HotlineMiami.exe.
3-Let olly sit that way.
4-In cheat engine target HotlineMiami.
5-In cheat engine, paste the bigass table at the bottom of this post.
6-Activate the "godmode and plentiful ammo" script. Normally the addresses for "HookPlace", "Loader from...", and "...to" will change (we don't give a damn about the value column).
7-Back in ollydbg, press ctrl+G (go to) and enter here the address of HookPlace. Ollydbg should select for you a line that contains "JMP HotlineM.{some numbers&letters}". If it doesn't, click once in the upper/left rectangle of the "CPU - main thread" window, and redo step 7.
8-Right-click on that line->copy to executable->selection. A new window will open, DO NOT CLOSE IT.
9-In the "CPU - main thread" window (upper/left region), press ctrl+G again, and this time enter the address of "Loader from...". Olly should select a "MOV BYTE PTR DS:[EAX+EBX],DL" for you.
10-Select this line+all lines between this and the next "ADD BYTE PTR DS:[EAX],AL" below. The last line of this pack will be like "JMP HotlineM.{some other numbers&letters}". The address of that line will match the address of the "...to" line in cheat engine.
11-Right click on your selection->copy to executable->selection.
12-Right click in the window titled "File C:\***\HotlineMiami.exe"->save file->save, yes.
13-Close ollydbg and cheat engine.
14-Congratulation, you have successfully patched your game.
Bonus-If you want to redistribute a patcher like I did, download a file patcher and use HotlineMiami.bak as your original, and HotlineMiami.exe as your patched file. Don't forget to mention the game version, and the patcher you used if you don't redistribute an exe.
bigass table at the bottom of the post:
Code:
<?xml version="1.0" encoding="utf-8"?>
<CheatTable>
<CheatEntries>
<CheatEntry>
<ID>0</ID>
<Description>"godmode and plentiful ammo"</Description>
<Color>80000008</Color>
<VariableType>Auto Assembler Script</VariableType>
<AssemblerScript>//Hotline miami
//plentiful ammo+godmode patch
//V4
[ENABLE]
aobscan(HookPlace_,88 14 03 40 49 75 CA)
aobscan(CodeCave,8D 40 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00)
label(HookPlace)
label(CodeChecker)
label(MeleeScriptHook)
label(AmmoScriptHook)
label(OriginalCode)
registersymbol(HookPlace)
registersymbol(CodeChecker)
registersymbol(OriginalCode)
HookPlace_:
HookPlace:
jmp CodeChecker
CodeCave+5:
CodeChecker:
MOV BYTE PTR DS:[EBX+EAX],DL //original code
cmp dl,')' //did we just write
//"if bullets>1 or round(random(1)) or energie<0 {0xd 0xa
//my_id=instance_create(x,y,objPlayerDead)"
//?
//lazyness says: just check Dead,my_i,if bulle
jne MeleeScriptHook
cmp dword [EBX+EAX-4],'Dead'
jne MeleeScriptHook
cmp dword [EBX+EAX-27],'my_i'
jne MeleeScriptHook
cmp dword [EBX+EAX-58],'if b'
jne MeleeScriptHook
cmp dword [EBX+EAX-54],'ulle'
jne MeleeScriptHook
//yes, so turn "if bullets>1 or round(random(1)) or energie<0 {"
//into if "bullets>1 and bullets<1 /*1)) or energie*/"
mov dword [EBX+EAX-4b],'and ' //patch the string
mov dword [EBX+EAX-47],'bull'
mov dword [EBX+EAX-43],'ets<'
mov dword [EBX+EAX-3f],'1 /*'
mov word [EBX+EAX-2d],'*/'
jmp OriginalCode
MeleeScriptHook:
cmp dl,'e' //did we just write a "scrPlayerDie"?
jne AmmoScriptHook
cmp dword [EBX+EAX-4],'erDi'
jne AmmoScriptHook
cmp dword [EBX+EAX-8],'Play'
jne AmmoScriptHook
cmp word [EBX+EAX-a],'cr'
jne AmmoScriptHook
//yes,turn "scrPlayerDie" into "//rPlayerDie"
mov word [EBX+EAX-b],'//'
jmp OriginalCode
AmmoScriptHook:
cmp dl,'o' //did we just write a "my_id.ammo=ammo"?
jne OriginalCode
cmp dword [EBX+EAX-4],'=amm'
jne OriginalCode
cmp dword [EBX+EAX-8],'ammo'
jne OriginalCode
cmp dword [EBX+EAX-c],'_id.'
jne OriginalCode
cmp word [EBX+EAX-e],'my'
jne OriginalCode
//yes,turn "my_id.ammo=ammo" into "my_id.ammo=ammo=9999"
mov dword [EBX+EAX-3],'9999'
OriginalCode:
INC EAX
DEC ECX
jmp HookPlace+5
[DISABLE]
unregistersymbol(HookPlace)
unregistersymbol(CodeChecker)
unregistersymbol(OriginalCode)
</AssemblerScript>
</CheatEntry>
<CheatEntry>
<ID>1</ID>
<Description>"HookPlace"</Description>
<Color>80000008</Color>
<VariableType>Byte</VariableType>
<Address>HookPlace</Address>
</CheatEntry>
<CheatEntry>
<ID>3</ID>
<Description>"Loader from..."</Description>
<Color>80000008</Color>
<VariableType>Byte</VariableType>
<Address>CodeChecker</Address>
</CheatEntry>
<CheatEntry>
<ID>2</ID>
<Description>"...to"</Description>
<Color>80000008</Color>
<VariableType>Byte</VariableType>
<Address>OriginalCode+2</Address>
</CheatEntry>
</CheatEntries>
</CheatTable>
How to use this cheat table?
- Install Cheat Engine
- Double-click the .CT file in order to open it.
- Click the PC icon in Cheat Engine in order to select the game process.
- Keep the list.
- Activate the trainer options by checking boxes or setting values from 0 to 1