Jump to content

OAM DMA hijacking: Difference between revisions

>Torchickens
(Created page with "{{Arbitrary code execution}} {{Researchneeded|A technical explanation of what OAM DMA is and how it works would be great.}} {{clr}} '''OAM DMA hijacking''' is a form of arb...")
 
(24 intermediate revisions by one other user not shown)
Line 3:
{{clr}}
 
'''OAM DMA hijacking''' ('''Object Attribute Memory Direct Memory Access hijacking''') is a means of changing the OAM DMA routine at FF80-FF89 (though the actual end can be extended e.g. FF89 could use two bytes for a relative jump, in which case FF8A is occupied) in Game Boy games by glitching or hacking. In this case, this article will describe how to do it with glitches in the Pokémon games.
'''OAM DMA hijacking''' is a form of [[arbitrary code execution]] in Game Boy games, which allows for the player to execute code every frame.
 
This glitch works by hijacking the "OAM DMA" processroutine associated with sprites. {{elaborate}}
 
An easy means to perform OAM DMA hijacking in both {{RB}} and {{GS}} is to write to the HRAM region FF80, however care must be taken as this region will be executed every frame. For this reason it is safe to place a ret (0xC9) opcode at the beginning of this region, write the code after it and replace the ret with a nop (0x00) byte afterwards. This exploit was documented by Crystal_.
Line 11:
{{YouTube|1w2iQdAHPh4|Crystal_}}
 
It can be used for both arbitrary code execution and adding custom OAM sprite entries.
Another exploit for Red and Blue can involve making the following modifications to the HRAM, as documented by Luckytyphlosion:
 
==Normal behaviour==
At FF86, write "jr FFF9".
During gameplay, FF80 is typically always executed and in [[Pokémon Red and Blue]], [[Pokémon Yellow]] the OAM DMA [[I/O]] register (represented by FF46 on the memory map) is altered by the following routine:
At FFF9, write "dec a"
At FFFA, write "jr nz, FFF9"
At FFFC, write "jp [region]"
 
ld a,c3
Do note that this will disable moving the character.
ld (ff46),a
ld a,28
dec a
jr nz,ff86
ret
 
When C3 is written to FF46, it means that the OAM sprites will be mirrored at C3XX in RAM. The OAM sprites are stored at the sprite attribute table in FE00-FE9F. For more information, see [https://gbdev.io/pandocs/OAM.html#vram-sprite-attribute-table-oam this section in the Pan Docs].
 
==Setup (additional arbitrary code execution)==
Using another [[arbitrary code execution]] method (such an item configuration to run applicable GBZ80), write to the regions. Note the following:
 
Before doing anything, make sure to write C9 (ret) to FF80 first; unless the code writes to the region all at once as the game could freeze.
 
*Option 1 - Simplest but breaks OAM sprites - The player can write bytes from FF81 onward, such as a simple RAM modification code (15 EA 59 D0 C9), before adding 3E back to FF80. Another option for longer codes is to make FF80 read jp (source address). The ld a,C3 ld (ff00+46),a if overwritten like this will break OAM sprites (i.e. the sprites like Red on the screen, although from now on the code will run every frame regardless of where the player is in game; something that other arbitrary code execution methods cannot normally do (though hypothetically something may be possible without it by manipulating the stack).
*Option 2 - Keeps OAM sprites - A more elaborate code may be written to keep OAM sprite functionality. Note the ld a,C3 ld (ff00+46),a works with a delay to keep the program counter in HRAM until FF89's ret (jumping there if the z flag is unset, from the code ld a,28 dec a jr nz,ff86). If the program counter remains in HRAM for the wrong amount of time, the game will freeze after leaving it (for example, if the 28 was changed to a lower value). Hence, any new delay should address this before leaving HRAM, yet if properly accounted for it is possible to run code outside of HRAM in addition to OAM sprites still working.
*Option 3 - Keeps OAM sprites, but at the cost of potentially freezing the game if the HRAM addresses the player chose to change are overwritten again); Another option is to overwrite other HRAM address that don't often change beginning from a relative jump at FF89 (such as to the money coins amount at FF9F) to run additional code (and with a potential series of other relative jumps across HRAM) which can be ended with a ret, without having to ever leave HRAM as part of the routines the player writes.
 
OAM DMA hijacking is useful as a form of 'real-time' arbitrary code execution, allowing the player to perform exploits such as walk through walls in Generation II or writing a [[0x50 sub-tile]] permanently to the beginning of the screen data for Generation I.
 
==Setup (OAM sprite manipulation)==
By simply changing the value at FF81 (normally the C3 in ld a,C3); the player can change the source of OAM sprites from C300 to XX00.
 
This method can also be used for a creative purpose; for the player to add their own sprites to the screen; for example changing it to D300 will touch the start of the inventory items; D31C (the last Pokédex seen flag controls the y-coordinate of the eighth entry, D31D (the number of items) controls the x-coordinate of the entry, D31E (the first item) controls the sprite, D31F (the first item quantity) controls the attribute of that sprite). More control however starts at item 2 (D320) (or the ninth entry) and onward; two item pairs consisting of the item and its quantity will control one OAM entry (four bytes), so by adding specific items the player can display their own sprites on the screen. Note memory addresses are -1 in Yellow, so the same D31C is the number of items and so on.
 
In this case, it may be possible to animate them with additional code to change the items. Animations of the OAM entries are normally done by other routines (such as the overworld loop which is meant to change C3XX), but the player could for instance use [[map script arbitrary code execution]] to run their own sprite animation routine.
 
==Taking over the joypad==
 
At FF86, write "jr FFF9".
At FFF9, write "dec a"
At FFFA, write "jr nz, FFF9"
At FFFC, write "jp [regionRAM]"
 
At the desired RAM address (e.g. D34A), write:
 
ld a,(FFD3) ; a=random "hRandomAdd" number.
ld (FFF8),a ; put a into FFF8, which controls what buttons are being pressed.
ret ; Don't execute any code after D350.
 
This example is a simulation of RNG Plays Pokémon, but the routine at D34A can be adjusted for different inputs into FFF8.
 
This technique was documented by luckytyphlosion, and a method of setting it up from scratch using TheZZAZZGlitch's coordinates memory editing method by ChickasaurusGL.
 
{{YouTube|2rIqARgv5LU|ChickasaurusGL}}
===See also===
*[[I/O]]
{{stub}}
[[Category:Arbitrary code execution]]
Cookies help us deliver our services. By using our services, you agree to our use of cookies.