[GH-ISSUE #3365] Jit emuhacks detected by games (Metal Gear Solid: Peace Walker demo) #1409

Open
opened 2026-03-17 23:16:28 +03:00 by kerem · 5 comments
Owner

Originally created by @unknownbrackets on GitHub (Aug 24, 2013).
Original GitHub issue: https://github.com/hrydgard/ppsspp/issues/3365

The jit, to detect when the game modifies its code, writes "emuhack" instructions over the original MIPS instructions. This way, if it's changed from an emuhack, we know that the code has likely been rewritten, and we need to recompile it. It also makes it faster since the emuhack tells us where the jitted code is.

However, this only works because virtually all games just run MIPS code, they don't read it themselves. Metal Gear Solid: Peace Walker demo actually hashes its own code, and then (it seems intentionally) goes into an infinite loop because it doesn't match.

The interpreter works fine since it doesn't need the emuhacks.

We should collect any other games with similar behavior here.

  • Metal Gear Solid: Peace Walker demo:
    Happens at 0x09C92818 or 08CCD80C when v1 == 00003119 (which reads from 0x09C92818.)

    Can be worked around by changing 0x0880FAE4 to 0A203F39 (j 0x0880FCE4), skipping the hash compare.

-[Unknown]

Originally created by @unknownbrackets on GitHub (Aug 24, 2013). Original GitHub issue: https://github.com/hrydgard/ppsspp/issues/3365 The jit, to detect when the game modifies its code, writes "emuhack" instructions over the original MIPS instructions. This way, if it's changed from an emuhack, we know that the code has likely been rewritten, and we need to recompile it. It also makes it faster since the emuhack tells us where the jitted code is. However, this only works because virtually all games just run MIPS code, they don't read it themselves. Metal Gear Solid: Peace Walker demo actually hashes its own code, and then (it seems intentionally) goes into an infinite loop because it doesn't match. The interpreter works fine since it doesn't need the emuhacks. We should collect any other games with similar behavior here. - Metal Gear Solid: Peace Walker demo: Happens at 0x09C92818 or 08CCD80C when v1 == 00003119 (which reads from 0x09C92818.) Can be worked around by changing 0x0880FAE4 to 0A203F39 (j 0x0880FCE4), skipping the hash compare. -[Unknown]
Author
Owner

@xsacha commented on GitHub (Jun 26, 2014):

Is it possible to detect reads to the jitted code and present a version without emuhacks?

<!-- gh-comment-id:47231167 --> @xsacha commented on GitHub (Jun 26, 2014): Is it possible to detect reads to the jitted code and present a version without emuhacks?
Author
Owner

@hrydgard commented on GitHub (Jun 26, 2014):

Possible in non-fast-mem, yes. but would require heavy checks for every read so is a non-starter performance-wise.

I've been thinking about trying some simple 2-level hash table instead of of overwriting instructions, PCSX2 uses that approach with good results but I don't know how it would compare speed-wise to the current approach.

<!-- gh-comment-id:47234832 --> @hrydgard commented on GitHub (Jun 26, 2014): Possible in non-fast-mem, yes. but would require heavy checks for every read so is a non-starter performance-wise. I've been thinking about trying some simple 2-level hash table instead of of overwriting instructions, PCSX2 uses that approach with good results but I don't know how it would compare speed-wise to the current approach.
Author
Owner

@unknownbrackets commented on GitHub (Jul 6, 2014):

It's hard to know how many games this affects. We can guess, but some things can also be jit bugs.

We could make it so that slow/safe mem (or some new dev option) makes all reads in the "possible emuhack op ranges" go through ReadInstruction. This would probably be a lot slower, but we could use it to safely determine which games might be affected.

Or we could just change it, but I suspect anything we do will make it slower, but maybe not really much slower.

-[Unknown]

<!-- gh-comment-id:48100554 --> @unknownbrackets commented on GitHub (Jul 6, 2014): It's hard to know how many games this affects. We can guess, but some things can also be jit bugs. We could make it so that slow/safe mem (or some new dev option) makes all reads in the "possible emuhack op ranges" go through ReadInstruction. This would probably be a lot slower, but we could use it to safely determine which games might be affected. Or we could just change it, but I suspect anything we do will make it slower, but maybe not really much slower. -[Unknown]
Author
Owner

@Linblow commented on GitHub (Jun 22, 2023):

@unknownbrackets
The same issue arises with SOCOM FireTeam Bravo 1 and 2 when the server sends anti-cheat queries to the client.
For example, server sends a hash query, which the client (game) executes, and of course it always results in different hashes because of the emuhacks thus triggering a false positive. Is there any workaround for this, such as invalidating the Icache before performing a memory read of the game's instructions? Could there be a good alternative to the jit blocks hack?

<!-- gh-comment-id:1602951526 --> @Linblow commented on GitHub (Jun 22, 2023): @unknownbrackets The same issue arises with SOCOM FireTeam Bravo 1 and 2 when the server sends anti-cheat queries to the client. For example, server sends a hash query, which the client (game) executes, and of course it always results in different hashes because of the emuhacks thus triggering a false positive. Is there any workaround for this, such as invalidating the Icache before performing a memory read of the game's instructions? Could there be a good alternative to the jit blocks hack?
Author
Owner

@hrydgard commented on GitHub (Jun 22, 2023):

The right way is to separate the block lookup from RAM and stop overwriting first-instruction-in-a-block, although we'd lose the inherent "change detection" of emuhacks (when they get overwritten, we necessarily end up recompiling).

Detecting changes can be done in other ways like playing games with page protection and exception handling, although been reluctant to go this route because it's hard to support on some of the ports we support (or would like to support).

<!-- gh-comment-id:1602969189 --> @hrydgard commented on GitHub (Jun 22, 2023): The right way is to separate the block lookup from RAM and stop overwriting first-instruction-in-a-block, although we'd lose the inherent "change detection" of emuhacks (when they get overwritten, we necessarily end up recompiling). Detecting changes can be done in other ways like playing games with page protection and exception handling, although been reluctant to go this route because it's hard to support on some of the ports we support (or would like to support).
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
starred/ppsspp#1409
No description provided.