NAOMI Reversing for FUN and NOPROFIT: PART 1

This is the first part of the blogging for NAOMI games, mostly in exploit.ninja, but also can be downloaded from the github HERE.

As part of the reversing for Arcade games, in particular NAOMI and NAOMI2 cartridges a good friend of mine asked me for help on figuring out the format, as even MAME takes it as a bit of “magic” (they split the cartridges and load them into arbitrary addresses) and the knowledge is closed at this point, wanting to get the scene bigger we decided to hop into it and try to find some information.

Introduction

NAOMI and NAOMI2 platforms are based on the SH4 chipset, specifically running on little endian mode (this is important to read the correct address), using this knowledge we can check and verify the correct “base address” for the cartridges.

One cannot take the game as the start of the process, this is because the BIOS itself is the one that enables and sets up the I/O ports, reads the interrupt table and also verifies the cartridge quickly, the reversing of the BIOS is actually another process which will also be documented later on.

The Game Cartridge

The beggining of the game (real address 0x0) contains on the first 0x130 bytes (304 characters) the “Game Header” which is used to verify the platform, development company and the different regions for the game, this is probably not only for language but also for hidden levels and could be used even for characters.

SEGA usually puts the last 3 region headers as SAMPLE, other companies tend just to add the game name without a specific named region, SEGA was really descriptive on the zones, some companies didn’t care much (this was specially true if the game had only one language)

An example of a SEGA game header is:

Ricksons-MacBook-Air:NAOMI TheCatThatHacks$ hexdump -C CosmicSmash.bin | head -n 30
00000000  4e 41 4f 4d 49 20 20 20  20 20 20 20 20 20 20 20  |NAOMI           |
00000010  53 45 47 41 20 43 4f 52  50 4f 52 41 54 49 4f 4e  |SEGA CORPORATION|
00000020  20 20 20 20 20 20 20 20  20 20 20 20 20 20 20 20  |                |
00000030  43 4f 53 4d 49 43 20 53  4d 41 53 48 20 49 4e 20  |COSMIC SMASH IN |
00000040  4a 41 50 41 4e 20 20 20  20 20 20 20 20 20 20 20  |JAPAN           |
00000050  43 4f 53 4d 49 43 20 53  4d 41 53 48 20 49 4e 20  |COSMIC SMASH IN |
00000060  55 53 41 20 20 20 20 20  20 20 20 20 20 20 20 20  |USA             |
00000070  43 4f 53 4d 49 43 20 53  4d 41 53 48 20 49 4e 20  |COSMIC SMASH IN |
00000080  45 58 50 4f 52 54 20 20  20 20 20 20 20 20 20 20  |EXPORT          |
00000090  43 4f 53 4d 49 43 20 53  4d 41 53 48 20 49 4e 20  |COSMIC SMASH IN |
000000a0  4b 4f 52 45 41 20 20 20  20 20 20 20 20 20 20 20  |KOREA           |
000000b0  43 4f 53 4d 49 43 20 53  4d 41 53 48 20 49 4e 20  |COSMIC SMASH IN |
000000c0  41 55 53 54 52 41 4c 49  41 20 20 20 20 20 20 20  |AUSTRALIA       |
000000d0  53 41 4d 50 4c 45 20 20  20 20 20 20 20 20 20 20  |SAMPLE          |
000000e0  20 20 20 20 20 20 20 20  20 20 20 20 20 20 20 20  |                |
000000f0  53 41 4d 50 4c 45 20 20  20 20 20 20 20 20 20 20  |SAMPLE          |
00000100  20 20 20 20 20 20 20 20  20 20 20 20 20 20 20 20  |                |
00000110  53 41 4d 50 4c 45 20 20  20 20 20 20 20 20 20 20  |SAMPLE          |
00000120  20 20 20 20 20 20 20 20  20 20 20 20 20 20 20 20  |                |

An example of a game that has no “difference” on the regions looks like:

Ricksons-MacBook-Air:NAOMI TheCatThatHacks$ hexdump -C ZeroGunner2.bin | head -n 30
00000000  4e 41 4f 4d 49 20 20 20  20 20 20 20 20 20 20 20  |NAOMI           |
00000010  50 53 49 4b 59 4f 20 43  4f 2e 2c 4c 54 44 2e 20  |PSIKYO CO.,LTD. |
00000020  20 20 20 20 20 20 20 20  20 20 20 20 20 20 20 20  |                |
00000030  5a 45 52 4f 20 47 55 4e  4e 45 52 20 32 20 20 20  |ZERO GUNNER 2   |
00000040  20 20 20 20 20 20 20 20  20 20 20 20 20 20 20 20  |                |
00000050  5a 45 52 4f 20 47 55 4e  4e 45 52 20 32 20 20 20  |ZERO GUNNER 2   |
00000060  20 20 20 20 20 20 20 20  20 20 20 20 20 20 20 20  |                |
00000070  5a 45 52 4f 20 47 55 4e  4e 45 52 20 32 20 20 20  |ZERO GUNNER 2   |
00000080  20 20 20 20 20 20 20 20  20 20 20 20 20 20 20 20  |                |
00000090  5a 45 52 4f 20 47 55 4e  4e 45 52 20 32 20 20 20  |ZERO GUNNER 2   |
000000a0  20 20 20 20 20 20 20 20  20 20 20 20 20 20 20 20  |                |
000000b0  5a 45 52 4f 20 47 55 4e  4e 45 52 20 32 20 20 20  |ZERO GUNNER 2   |
000000c0  20 20 20 20 20 20 20 20  20 20 20 20 20 20 20 20  |                |
000000d0  5a 45 52 4f 20 47 55 4e  4e 45 52 20 32 20 20 20  |ZERO GUNNER 2   |
000000e0  20 20 20 20 20 20 20 20  20 20 20 20 20 20 20 20  |                |
000000f0  5a 45 52 4f 20 47 55 4e  4e 45 52 20 32 20 20 20  |ZERO GUNNER 2   |
00000100  20 20 20 20 20 20 20 20  20 20 20 20 20 20 20 20  |                |
00000110  5a 45 52 4f 20 47 55 4e  4e 45 52 20 32 20 20 20  |ZERO GUNNER 2   |
00000120  20 20 20 20 20 20 20 20  20 20 20 20 20 20 20 20  |                |

From here we can infer:

  1. First 16 characters (0x10) are reserved for the “platform” (always NAOMI)
  2. Next we have 32 characters (0x20) for the developer of the game
  3. From there we have 8 times slots of 32 characters (0x20) for the different regions
Size Description
0x10 Plaform
0x20 Dev Company
0x20 Region 1
0x20 Region 2
0x20 Region 3
0x20 Region 4
0x20 Region 5
0x20 Region 6
0x20 Region 7
0x20 Region 8

Let’s analyze the now “known” structure of the information, as we are guessing we shall call the function DSPGetAJob, in honor of e-begger DarkSydePhil and his 2,000 USD “cat reveal” and other scams.

In [35]:
from ctypes import *
import struct
import os, sys
from pprint import pformat

# This is a class to describe and apply into the beggining of the game the NAOMI game header
class DSPGetAJob(LittleEndianStructure):
    """The Games and even the NetDimm software for network boot have the same structure
    |Platform|Developer|Title Region 1|Title Region 2|...|Title Region 8|
    So we create that as a class and apply it, since is a lazy way of doing it, we name it
    based on a scrub like DSP.
    
    For more details on the structure read the previous block, for more details on DSP .. nothing I could do! *SNORT*
    
    "At least I'm not a 2,000 USD cat" --Rickson <TheCatThatHacks>
    """
    _pack_ = 1
    _fields_ = [('platform', c_char * 0x10),
               ('development company', c_char * 0x20),
               ('region 1', c_char * 0x20),
               ('region 2', c_char * 0x20),
               ('region 3', c_char * 0x20),
               ('region 4', c_char * 0x20),
               ('region 5', c_char * 0x20),
               ('region 6', c_char * 0x20),
               ('region 7', c_char * 0x20),
               ('region 8', c_char * 0x20)]
    
    def __init__(self, file):
        if file.readinto(self) != sizeof(self):
            raise EOFError("Not enough bytes in buffer to parse header, nothing I could do d00d!!")
    
    def dict_export(self):
        d = dict()
        for (varkey, vartype) in self._fields_:
            d[varkey] = getattr(self, varkey).rstrip() # We use rstrip to remove the spaces, looks nicer
        return d

    def __repr__(self):
        d = self.dict_export()
        return pformat(d, indent=1, width=64)

# Now we run the stuff to show the results
print("[ - ] Opening file CosmicSmash.bin")
filer = open("CosmicSmash.bin", "rb")
print("[ - ] File open, extracting header d00000d *snort*")
snort = DSPGetAJob(filer)
print("[ + ] Header information is:\n")
print(snort)
print("\n[ - ] Not enough contributions d0000d finishing")
[ - ] Opening file CosmicSmash.bin
[ - ] File open, extracting header d00000d *snort*
[ + ] Header information is:

{'development company': b'SEGA CORPORATION',
 'platform': b'NAOMI',
 'region 1': b'COSMIC SMASH IN JAPAN',
 'region 2': b'COSMIC SMASH IN USA',
 'region 3': b'COSMIC SMASH IN EXPORT',
 'region 4': b'COSMIC SMASH IN KOREA',
 'region 5': b'COSMIC SMASH IN AUSTRALIA',
 'region 6': b'SAMPLE',
 'region 7': b'SAMPLE',
 'region 8': b'SAMPLE'}

[ - ] Not enough contributions d0000d finishing

Well that was easy! So now let’s analyze the next set of bytes, from 0x130 to 0x500:

nahual@Neuromancer ~/projects/NAOMI_local $ hexdump -C -s 0x130 -n 0x3e0 -v CosmicSmash.bin
00000130  d0 07 0c 1c 42 43 48 42  01 00 00 00 00 00 00 00  |....BCHB........|
00000140  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
00000150  00 00 00 00 00 00 00 00  00 00 00 00 03 00 00 00  |................|
00000160  00 00 04 00 2b c8 69 e7  04 00 c3 e0 b6 43 04 00  |....+.i......C..|
00000170  96 c8 66 f7 04 00 60 2e  bf 4c 04 00 81 bb 26 7e  |..f...`..L....&~|
00000180  04 00 a8 b3 ea f2 04 00  c5 05 84 a0 04 00 13 9e  |................|
00000190  35 75 ff ff 00 00 00 00  ff ff 00 00 00 00 ff ff  |5u..............|
000001a0  00 00 00 00 ff ff 00 00  00 00 ff ff 00 00 00 00  |................|
000001b0  ff ff 00 00 00 00 ff ff  00 00 00 00 ff ff 00 00  |................|
000001c0  00 00 ff ff 00 00 00 00  ff ff 00 00 00 00 ff ff  |................|
000001d0  00 00 00 00 ff ff 00 00  00 00 ff ff 00 00 00 00  |................|
000001e0  00 02 00 01 00 00 00 00  01 01 01 01 01 01 01 01  |................|
000001f0  00 02 00 01 00 00 00 00  01 01 01 01 01 01 01 01  |................|
00000200  00 02 00 01 00 00 00 00  01 01 01 01 01 01 01 01  |................|
00000210  00 02 00 01 00 00 00 00  01 01 01 01 01 01 01 01  |................|
00000220  00 02 00 01 00 00 00 00  01 01 01 01 01 01 01 01  |................|
00000230  00 02 00 01 00 00 00 00  01 01 01 01 01 01 01 01  |................|
00000240  00 02 00 01 00 00 00 00  01 01 01 01 01 01 01 01  |................|
00000250  00 02 00 01 00 00 00 00  01 01 01 01 01 01 01 01  |................|
00000260  43 52 45 44 49 54 20 54  4f 20 53 54 41 52 54 20  |CREDIT TO START |
00000270  20 20 20 20 20 20 20 20  20 20 20 20 20 20 20 20  |                |
00000280  43 52 45 44 49 54 20 54  4f 20 43 4f 4e 54 49 4e  |CREDIT TO CONTIN|
00000290  55 45 20 20 20 20 20 20  20 20 20 20 20 20 20 20  |UE              |
000002a0  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
000002b0  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
000002c0  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
000002d0  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
000002e0  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
000002f0  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
00000300  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
00000310  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
00000320  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
00000330  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
00000340  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
00000350  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
00000360  00 00 02 00 00 00 02 0c  00 00 10 00 00 00 15 00  |................|
00000370  00 00 40 0c 00 00 02 00  00 00 19 00 00 00 48 0c  |..@...........H.|
00000380  00 00 02 00 ff ff ff ff  00 00 00 00 00 00 00 00  |................|
00000390  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
000003a0  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
000003b0  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
000003c0  00 00 12 00 00 00 02 0c  00 00 03 00 ff ff ff ff  |................|
000003d0  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
000003e0  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
000003f0  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
00000400  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
00000410  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
00000420  00 00 02 0c 00 00 02 0c  ff 00 00 01 00 ff ff ff  |................|
00000430  ff ff ff ff ff ff ff ff  ff ff ff ff ff ff ff ff  |................|
00000440  ff ff ff ff ff ff ff ff  ff ff ff ff ff ff ff ff  |................|
00000450  ff ff ff ff ff ff ff ff  ff ff ff ff ff ff ff ff  |................|
00000460  ff ff ff ff ff ff ff ff  ff ff ff ff ff ff ff ff  |................|
00000470  ff ff ff ff ff ff ff ff  ff ff ff ff ff ff ff ff  |................|
00000480  ff ff ff ff ff ff ff ff  ff ff ff ff ff ff ff ff  |................|
00000490  ff ff ff ff ff ff ff ff  ff ff ff ff ff ff ff ff  |................|
000004a0  ff ff ff ff ff ff ff ff  ff ff ff ff ff ff ff ff  |................|
000004b0  ff ff ff ff ff ff ff ff  ff ff ff ff ff ff ff ff  |................|
000004c0  ff ff ff ff ff ff ff ff  ff ff ff ff ff ff ff ff  |................|
000004d0  ff ff ff ff ff ff ff ff  ff ff ff ff ff ff ff ff  |................|
000004e0  ff ff ff ff ff ff ff ff  ff ff ff ff ff ff ff ff  |................|
000004f0  ff ff ff ff ff ff ff ff  ff ff ff ff ff ff ff ff  |................|
00000500  ff ff ff ff ff ff ff ff  ff ff ff ff ff ff ff ff  |................|
00000510
nahual@Neuromancer ~/projects/NAOMI_local $

So let’s compare with another binary, in this case ZeroGunner.bin:

nahual@Neuromancer ~/projects/NAOMI_local $ hexdump -C -s 0x130 -n 0x3e0 -v ZeroGunner2.bin
00000130  d1 07 04 01 42 44 43 30  01 00 00 00 00 00 00 00  |....BDC0........|
00000140  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
00000150  00 00 00 00 00 00 00 00  00 00 00 00 03 00 00 00  |................|
00000160  00 00 04 00 5f 9d 61 df  04 00 17 80 2e d2 04 00  |...._.a.........|
00000170  3d 8b 46 48 04 00 d0 b5  0e d9 04 00 53 be 0c b7  |=.FH........S...|
00000180  04 00 fe a5 57 75 04 00  55 09 73 7c 04 00 02 94  |....Wu..U.s|....|
00000190  9d 39 04 00 13 97 45 47  04 00 4b 09 14 3f ff ff  |.9....EG..K..?..|
000001a0  00 00 11 11 ff ff 00 00  11 11 ff ff 00 00 11 11  |................|
000001b0  ff ff 00 00 11 11 ff ff  00 00 11 11 ff ff 00 00  |................|
000001c0  11 11 ff ff 00 00 11 11  ff ff 00 00 11 11 ff ff  |................|
000001d0  bf a6 d4 df ff ff 34 12  76 98 ff ff 78 56 32 54  |......4.v...xV2T|
000001e0  00 02 00 01 00 00 00 00  01 01 01 01 01 01 01 01  |................|
000001f0  00 02 00 01 00 00 00 00  01 01 01 01 01 01 01 01  |................|
00000200  00 02 00 01 00 00 00 00  01 01 01 01 01 01 01 01  |................|
00000210  00 02 00 01 00 00 00 00  01 01 01 01 01 01 01 01  |................|
00000220  00 02 00 01 00 00 00 00  01 01 01 01 01 01 01 01  |................|
00000230  00 02 00 01 00 00 00 00  01 01 01 01 01 01 01 01  |................|
00000240  00 02 00 01 00 00 00 00  01 01 01 01 01 01 01 01  |................|
00000250  00 02 00 01 00 00 00 00  01 01 01 01 01 01 01 01  |................|
00000260  43 52 45 44 49 54 20 54  4f 20 53 54 41 52 54 20  |CREDIT TO START |
00000270  20 20 20 20 20 20 20 20  20 20 20 20 20 20 20 20  |                |
00000280  43 52 45 44 49 54 20 54  4f 20 43 4f 4e 54 49 4e  |CREDIT TO CONTIN|
00000290  55 45 20 20 20 20 20 20  20 20 20 20 20 20 20 20  |UE              |
000002a0  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
000002b0  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
000002c0  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
000002d0  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
000002e0  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
000002f0  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
00000300  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
00000310  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
00000320  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
00000330  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
00000340  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
00000350  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
00000360  00 00 00 00 00 00 02 0c  00 00 0e 00 00 00 0e 00  |................|
00000370  00 00 10 0c 00 00 10 00  00 00 1e 00 00 00 20 0c  |.............. .|
00000380  00 00 10 00 00 00 2e 00  00 00 30 0c 60 54 00 00  |..........0.`T..|
00000390  ff ff ff ff 00 00 00 00  00 00 00 00 00 00 00 00  |................|
000003a0  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
000003b0  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
000003c0  00 00 00 00 00 00 02 0c  00 00 0e 00 00 00 0e 00  |................|
000003d0  00 00 10 0c 00 00 10 00  00 00 1e 00 00 00 20 0c  |.............. .|
000003e0  00 00 10 00 00 00 2e 00  00 00 30 0c 60 54 00 00  |..........0.`T..|
000003f0  ff ff ff ff 00 00 00 00  00 00 00 00 00 00 00 00  |................|
00000400  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
00000410  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
00000420  00 10 02 0c 04 10 02 0c  ff 02 00 01 00 ff ff ff  |................|
00000430  ff ff ff ff ff ff ff ff  ff ff ff ff ff ff ff ff  |................|
00000440  ff ff ff ff ff ff ff ff  ff ff ff ff ff ff ff ff  |................|
00000450  ff ff ff ff ff ff ff ff  ff ff ff ff ff ff ff ff  |................|
00000460  ff ff ff ff ff ff ff ff  ff ff ff ff ff ff ff ff  |................|
00000470  ff ff ff ff ff ff ff ff  ff ff ff ff ff ff ff ff  |................|
00000480  ff ff ff ff ff ff ff ff  ff ff ff ff ff ff ff ff  |................|
00000490  ff ff ff ff ff ff ff ff  ff ff ff ff ff ff ff ff  |................|
000004a0  ff ff ff ff ff ff ff ff  ff ff ff ff ff ff ff ff  |................|
000004b0  ff ff ff ff ff ff ff ff  ff ff ff ff ff ff ff ff  |................|
000004c0  ff ff ff ff ff ff ff ff  ff ff ff ff ff ff ff ff  |................|
000004d0  ff ff ff ff ff ff ff ff  ff ff ff ff ff ff ff ff  |................|
000004e0  ff ff ff ff ff ff ff ff  ff ff ff ff ff ff ff ff  |................|
000004f0  ff ff ff ff ff ff ff ff  ff ff ff ff ff ff ff ff  |................|
00000500  42 60 01 77 54 62 03 63  14 73 6c 33 04 76 22 23  |B`.wTb.c.sl3.v"#|
00000510
nahual@Neuromancer ~/projects/NAOMI_local $

From this quick glance we can see large patterns between the 2 binaries, so let’s actually create a quick script to read every 4 bytes and check the difference, why 4 bytes? we are just trying to “guess” if there are any address or any different information

In [34]:
import binascii
import struct

with open("CosmicSmash.bin", "rb") as cosmicsmash:
    with open("ZeroGunner2.bin", "rb") as zerogunner2:
        print("[ + ] Comparing CosmicSmash.bin to ZeroGunner2.bin from 0x130 to 0x500")
        x = 0x130
        while x < 0x500:
            cosmicsmash.seek(x)
            zerogunner2.seek(x)
            # This is not pythonic at all .. I have to find an elegant way of doing this tbh :/
            cosmicsmash_content = struct.unpack('L', cosmicsmash.read(4))[0]
            #cosmicsmash.seek(4,1)
            zerogunner2_content = struct.unpack('L', zerogunner2.read(4))[0]
            #zerogunner2.seek(4,1)
            if cosmicsmash_content != zerogunner2_content:
                print("[ - ] Address %#x :: CS -> 0x%.8x == ZG -> 0x%.8x" % (x, cosmicsmash_content, zerogunner2_content))
            x += 4

print("[ + ] DONE")
[ + ] Comparing CosmicSmash.bin to ZeroGunner2.bin from 0x130 to 0x500
[ - ] Address 0x130 :: CS -> 0x1c0c07d0 == ZG -> 0x010407d1
[ - ] Address 0x134 :: CS -> 0x42484342 == ZG -> 0x30434442
[ - ] Address 0x164 :: CS -> 0xe769c82b == ZG -> 0xdf619d5f
[ - ] Address 0x168 :: CS -> 0xe0c30004 == ZG -> 0x80170004
[ - ] Address 0x16c :: CS -> 0x000443b6 == ZG -> 0x0004d22e
[ - ] Address 0x170 :: CS -> 0xf766c896 == ZG -> 0x48468b3d
[ - ] Address 0x174 :: CS -> 0x2e600004 == ZG -> 0xb5d00004
[ - ] Address 0x178 :: CS -> 0x00044cbf == ZG -> 0x0004d90e
[ - ] Address 0x17c :: CS -> 0x7e26bb81 == ZG -> 0xb70cbe53
[ - ] Address 0x180 :: CS -> 0xb3a80004 == ZG -> 0xa5fe0004
[ - ] Address 0x184 :: CS -> 0x0004f2ea == ZG -> 0x00047557
[ - ] Address 0x188 :: CS -> 0xa08405c5 == ZG -> 0x7c730955
[ - ] Address 0x18c :: CS -> 0x9e130004 == ZG -> 0x94020004
[ - ] Address 0x190 :: CS -> 0xffff7535 == ZG -> 0x0004399d
[ - ] Address 0x194 :: CS -> 0x00000000 == ZG -> 0x47459713
[ - ] Address 0x198 :: CS -> 0x0000ffff == ZG -> 0x094b0004
[ - ] Address 0x19c :: CS -> 0xffff0000 == ZG -> 0xffff3f14
[ - ] Address 0x1a0 :: CS -> 0x00000000 == ZG -> 0x11110000
[ - ] Address 0x1a8 :: CS -> 0xffff0000 == ZG -> 0xffff1111
[ - ] Address 0x1ac :: CS -> 0x00000000 == ZG -> 0x11110000
[ - ] Address 0x1b4 :: CS -> 0xffff0000 == ZG -> 0xffff1111
[ - ] Address 0x1b8 :: CS -> 0x00000000 == ZG -> 0x11110000
[ - ] Address 0x1c0 :: CS -> 0xffff0000 == ZG -> 0xffff1111
[ - ] Address 0x1c4 :: CS -> 0x00000000 == ZG -> 0x11110000
[ - ] Address 0x1cc :: CS -> 0xffff0000 == ZG -> 0xffff1111
[ - ] Address 0x1d0 :: CS -> 0x00000000 == ZG -> 0xdfd4a6bf
[ - ] Address 0x1d4 :: CS -> 0x0000ffff == ZG -> 0x1234ffff
[ - ] Address 0x1d8 :: CS -> 0xffff0000 == ZG -> 0xffff9876
[ - ] Address 0x1dc :: CS -> 0x00000000 == ZG -> 0x54325678
[ - ] Address 0x360 :: CS -> 0x00020000 == ZG -> 0x00000000
[ - ] Address 0x368 :: CS -> 0x00100000 == ZG -> 0x000e0000
[ - ] Address 0x36c :: CS -> 0x00150000 == ZG -> 0x000e0000
[ - ] Address 0x370 :: CS -> 0x0c400000 == ZG -> 0x0c100000
[ - ] Address 0x374 :: CS -> 0x00020000 == ZG -> 0x00100000
[ - ] Address 0x378 :: CS -> 0x00190000 == ZG -> 0x001e0000
[ - ] Address 0x37c :: CS -> 0x0c480000 == ZG -> 0x0c200000
[ - ] Address 0x380 :: CS -> 0x00020000 == ZG -> 0x00100000
[ - ] Address 0x384 :: CS -> 0xffffffff == ZG -> 0x002e0000
[ - ] Address 0x388 :: CS -> 0x00000000 == ZG -> 0x0c300000
[ - ] Address 0x38c :: CS -> 0x00000000 == ZG -> 0x00005460
[ - ] Address 0x390 :: CS -> 0x00000000 == ZG -> 0xffffffff
[ - ] Address 0x3c0 :: CS -> 0x00120000 == ZG -> 0x00000000
[ - ] Address 0x3c8 :: CS -> 0x00030000 == ZG -> 0x000e0000
[ - ] Address 0x3cc :: CS -> 0xffffffff == ZG -> 0x000e0000
[ - ] Address 0x3d0 :: CS -> 0x00000000 == ZG -> 0x0c100000
[ - ] Address 0x3d4 :: CS -> 0x00000000 == ZG -> 0x00100000
[ - ] Address 0x3d8 :: CS -> 0x00000000 == ZG -> 0x001e0000
[ - ] Address 0x3dc :: CS -> 0x00000000 == ZG -> 0x0c200000
[ - ] Address 0x3e0 :: CS -> 0x00000000 == ZG -> 0x00100000
[ - ] Address 0x3e4 :: CS -> 0x00000000 == ZG -> 0x002e0000
[ - ] Address 0x3e8 :: CS -> 0x00000000 == ZG -> 0x0c300000
[ - ] Address 0x3ec :: CS -> 0x00000000 == ZG -> 0x00005460
[ - ] Address 0x3f0 :: CS -> 0x00000000 == ZG -> 0xffffffff
[ - ] Address 0x420 :: CS -> 0x0c020000 == ZG -> 0x0c021000
[ - ] Address 0x424 :: CS -> 0x0c020000 == ZG -> 0x0c021004
[ - ] Address 0x428 :: CS -> 0x010000ff == ZG -> 0x010002ff
[ + ] DONE

So we can see at 0x360 to 0x390 some RAM addresses actually come up, so let’s actually reach that part and see how it looks on each binary:

nahual@Neuromancer ~/projects/NAOMI_local $ hexdump -C -s 0x360 -n 0x100 -v ZeroGunner2.bin
00000360  00 00 00 00 00 00 02 0c  00 00 0e 00 00 00 0e 00  |................|
00000370  00 00 10 0c 00 00 10 00  00 00 1e 00 00 00 20 0c  |.............. .|
00000380  00 00 10 00 00 00 2e 00  00 00 30 0c 60 54 00 00  |..........0.`T..|
00000390  ff ff ff ff 00 00 00 00  00 00 00 00 00 00 00 00  |................|
000003a0  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
000003b0  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
000003c0  00 00 00 00 00 00 02 0c  00 00 0e 00 00 00 0e 00  |................|
000003d0  00 00 10 0c 00 00 10 00  00 00 1e 00 00 00 20 0c  |.............. .|
000003e0  00 00 10 00 00 00 2e 00  00 00 30 0c 60 54 00 00  |..........0.`T..|
000003f0  ff ff ff ff 00 00 00 00  00 00 00 00 00 00 00 00  |................|
00000400  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
00000410  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
00000420  00 10 02 0c 04 10 02 0c  ff 02 00 01 00 ff ff ff  |................|
00000430  ff ff ff ff ff ff ff ff  ff ff ff ff ff ff ff ff  |................|
00000440  ff ff ff ff ff ff ff ff  ff ff ff ff ff ff ff ff  |................|
00000450  ff ff ff ff ff ff ff ff  ff ff ff ff ff ff ff ff  |................|
00000460
nahual@Neuromancer ~/projects/NAOMI_local $ hexdump -C -s 0x360 -n 0x100 -v CosmicSmash.bin
00000360  00 00 02 00 00 00 02 0c  00 00 10 00 00 00 15 00  |................|
00000370  00 00 40 0c 00 00 02 00  00 00 19 00 00 00 48 0c  |..@...........H.|
00000380  00 00 02 00 ff ff ff ff  00 00 00 00 00 00 00 00  |................|
00000390  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
000003a0  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
000003b0  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
000003c0  00 00 12 00 00 00 02 0c  00 00 03 00 ff ff ff ff  |................|
000003d0  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
000003e0  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
000003f0  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
00000400  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
00000410  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
00000420  00 00 02 0c 00 00 02 0c  ff 00 00 01 00 ff ff ff  |................|
00000430  ff ff ff ff ff ff ff ff  ff ff ff ff ff ff ff ff  |................|
00000440  ff ff ff ff ff ff ff ff  ff ff ff ff ff ff ff ff  |................|
00000450  ff ff ff ff ff ff ff ff  ff ff ff ff ff ff ff ff  |................|
00000460
nahual@Neuromancer ~/projects/NAOMI_local $

Address starting on 0x3c0 contains also another set of addresses, ZeroGunner contain almost the same, CosmicSmash doesn’t so more analysis will be done with other games (I will gather a few more games and analyze, otehr exercises are left to the reader)

In [33]:
import binascii
import struct

with open("CosmicSmash.bin", "rb") as cosmicsmash:
    with open("ZeroGunner2.bin", "rb") as zerogunner2:
        print("[ + ] Comparing CosmicSmash.bin to ZeroGunner2.bin from 0x360 to 0x500")
        x = 0x130
        while x < 0x260:
            cosmicsmash.seek(x)
            zerogunner2.seek(x)
            # This is not pythonic at all .. I have to find an elegant way of doing this tbh :/
            cosmicsmash_content = struct.unpack('L', cosmicsmash.read(4))[0]
            #cosmicsmash.seek(4,1)
            zerogunner2_content = struct.unpack('L', zerogunner2.read(4))[0]
            #zerogunner2.seek(4,1)
            if cosmicsmash_content != zerogunner2_content:
                print("[ - ] Address %#x :: CS -> 0x%.8x == ZG -> 0x%.8x" % (x, cosmicsmash_content, zerogunner2_content))
            x += 4

print("[ + ] DONE")
[ + ] Comparing CosmicSmash.bin to ZeroGunner2.bin from 0x360 to 0x500
[ - ] Address 0x130 :: CS -> 0x1c0c07d0 == ZG -> 0x010407d1
[ - ] Address 0x134 :: CS -> 0x42484342 == ZG -> 0x30434442
[ - ] Address 0x164 :: CS -> 0xe769c82b == ZG -> 0xdf619d5f
[ - ] Address 0x168 :: CS -> 0xe0c30004 == ZG -> 0x80170004
[ - ] Address 0x16c :: CS -> 0x000443b6 == ZG -> 0x0004d22e
[ - ] Address 0x170 :: CS -> 0xf766c896 == ZG -> 0x48468b3d
[ - ] Address 0x174 :: CS -> 0x2e600004 == ZG -> 0xb5d00004
[ - ] Address 0x178 :: CS -> 0x00044cbf == ZG -> 0x0004d90e
[ - ] Address 0x17c :: CS -> 0x7e26bb81 == ZG -> 0xb70cbe53
[ - ] Address 0x180 :: CS -> 0xb3a80004 == ZG -> 0xa5fe0004
[ - ] Address 0x184 :: CS -> 0x0004f2ea == ZG -> 0x00047557
[ - ] Address 0x188 :: CS -> 0xa08405c5 == ZG -> 0x7c730955
[ - ] Address 0x18c :: CS -> 0x9e130004 == ZG -> 0x94020004
[ - ] Address 0x190 :: CS -> 0xffff7535 == ZG -> 0x0004399d
[ - ] Address 0x194 :: CS -> 0x00000000 == ZG -> 0x47459713
[ - ] Address 0x198 :: CS -> 0x0000ffff == ZG -> 0x094b0004
[ - ] Address 0x19c :: CS -> 0xffff0000 == ZG -> 0xffff3f14
[ - ] Address 0x1a0 :: CS -> 0x00000000 == ZG -> 0x11110000
[ - ] Address 0x1a8 :: CS -> 0xffff0000 == ZG -> 0xffff1111
[ - ] Address 0x1ac :: CS -> 0x00000000 == ZG -> 0x11110000
[ - ] Address 0x1b4 :: CS -> 0xffff0000 == ZG -> 0xffff1111
[ - ] Address 0x1b8 :: CS -> 0x00000000 == ZG -> 0x11110000
[ - ] Address 0x1c0 :: CS -> 0xffff0000 == ZG -> 0xffff1111
[ - ] Address 0x1c4 :: CS -> 0x00000000 == ZG -> 0x11110000
[ - ] Address 0x1cc :: CS -> 0xffff0000 == ZG -> 0xffff1111
[ - ] Address 0x1d0 :: CS -> 0x00000000 == ZG -> 0xdfd4a6bf
[ - ] Address 0x1d4 :: CS -> 0x0000ffff == ZG -> 0x1234ffff
[ - ] Address 0x1d8 :: CS -> 0xffff0000 == ZG -> 0xffff9876
[ - ] Address 0x1dc :: CS -> 0x00000000 == ZG -> 0x54325678
[ + ] DONE
In [92]:
#!/usr/bin/env python
#

import binascii
import struct

with open("CosmicSmash.bin", "rb") as binary_file:
	binary_file.seek(0)
	binary_file.seek(0x360)
	bin_offset = struct.unpack('i', binary_file.read(4))
	binary_file.seek(4, 1)
	bin_size = struct.unpack('i', binary_file.read(4))
	binary_file.seek(4, 1)
	bin_ram = struct.unpack('i', binary_file.read(4))
#	bin_length = binary_file.read(4)
#	print(bin_offset[0])
	print("[ + ] Binary game start offset from ROM: %#x RAM Address is %#x Game binary size: %#x" % (bin_offset[0],bin_ram[0], bin_size[0]))
	print("[ - ] Moving to offset %#x" % bin_offset[0])
	binary_file.seek(bin_offset[0])
	print("[ - ] Reading %d (%#x) bytes" % (bin_size[0], bin_size[0]))
	game_data = binary_file.read(bin_size[0])
#	game_data = binary_file.read(2424832)
#	print binascii.hexlify(game_data)
	game_file = open("CosmicSmashMainLoopGame.bin", "wb")
	game_file.write(game_data)
	game_file.close()
	print("[ - ] Getting Entry point from 0x420")
	binary_file.seek(0)
	binary_file.seek(0x420)
	game_entry_point_addr = struct.unpack('i', binary_file.read(4))
	print("[ + ] Game entry address is %d (%#x)" % (game_entry_point_addr[0], game_entry_point_addr[0]))
	
[ + ] Binary game start offset from ROM: 0x20000 RAM Address is 0xc400000 Game binary size: 0x100000
[ - ] Moving to offset 0x20000
[ - ] Reading 1048576 (0x100000) bytes
[ - ] Getting Entry point from 0x420
[ + ] Game entry address is 201457664 (0xc020000)
In [36]:
import binascii
import struct

with open("FW_Netdimm_402.bin", "rb") as binary_file:
	binary_file.seek(0)
	binary_file.seek(0x360)
	bin_offset = struct.unpack('i', binary_file.read(4))
	bin_size = struct.unpack('i', binary_file.read(4))
	bin_ram = struct.unpack('i', binary_file.read(4))
#	bin_length = binary_file.read(4)
#	print(bin_offset[0])
	print("[ + ] Binary game start offset from ROM: %#x RAM Address is %#x Game binary size: %#x" % (bin_offset[0],bin_ram[0], bin_size[0]))
	print("[ - ] Moving to offset %#x" % (bin_offset[0] + 0x500))
	binary_file.seek(bin_offset[0])
	print("[ - ] Reading %d (%#x) bytes" % (bin_size[0], bin_size[0]))
	game_data = binary_file.read(bin_size[0])
#	game_data = binary_file.read(2424832)
#	print binascii.hexlify(game_data)
	game_file = open("CosmicSmashMainLoopGame.bin", "wb")
	game_file.write(game_data)
	game_file.close()
	print("[ - ] Getting Entry point from 0x420")
	binary_file.seek(0)
	binary_file.seek(0x420)
	game_entry_point_addr = struct.unpack('i', binary_file.read(4))
	print("[ + ] Game entry address is %d (%#x)" % (game_entry_point_addr[0], game_entry_point_addr[0]))
	
[ + ] Binary game start offset from ROM: 0x0 RAM Address is 0x100000 Game binary size: 0xc020000
[ - ] Moving to offset 0x500
[ - ] Reading 201457664 (0xc020000) bytes
[ - ] Getting Entry point from 0x420
[ + ] Game entry address is 201459712 (0xc020800)
In [9]:
#!/usr/bin/env python
#
# Running on the NetDimm

import binascii
import struct

with open("BIOS/KF Private/317hackedupdatergd.bin", "rb") as binary_file:
	binary_file.seek(0)
	binary_file.seek(0x360)
	bin_offset = struct.unpack('i', binary_file.read(4))
	bin_size = struct.unpack('i', binary_file.read(4))
	bin_ram = struct.unpack('i', binary_file.read(4))
#	bin_length = binary_file.read(4)
#	print(bin_offset[0])
	print("[ + ] Binary game start offset from ROM: %#x RAM Address is %#x Game binary size: %#x" % (bin_offset[0],bin_ram[0], bin_size[0]))
	print("[ - ] Moving to offset %#x" % bin_offset[0])
	binary_file.seek(bin_offset[0])
	print("[ - ] Reading %d (%#x) bytes" % (bin_size[0], bin_size[0]))
	game_data = binary_file.read(bin_size[0])
#	game_data = binary_file.read(2424832)
#	print binascii.hexlify(game_data)
	game_file = open("CosmicSmashMainLoopGame.bin", "wb")
	game_file.write(game_data)
	game_file.close()
	print("[ - ] Getting Entry point from 0x420")
	binary_file.seek(0)
	binary_file.seek(0x420)
	game_entry_point_addr = struct.unpack('i', binary_file.read(4))
	print("[ + ] Game entry address is %d (%#x)" % (game_entry_point_addr[0], game_entry_point_addr[0]))
[ + ] Binary game start offset from ROM: 0x0 RAM Address is 0x100000 Game binary size: 0xc020000
[ - ] Moving to offset 0x0
[ - ] Reading 201457664 (0xc020000) bytes
[ - ] Getting Entry point from 0x420
[ + ] Game entry address is 201459712 (0xc020800)

Let’s check the next data:

00000360  00 00 02 00 00 00 02 0c  00 00 10 00 00 00 15 00  |................|
00000370  00 00 40 0c 00 00 02 00  00 00 19 00 00 00 48 0c  |..@...........H.|
00000380  00 00 02 00 ff ff ff ff  00 00 00 00 00 00 00 00  |................|
00000390  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|

We see the 0x00020000 <-> 0x0c200000 <-> 0x00010000 (Our main loop data) but also see the same kind of data structure right after, so let’s make a quick loop, on a quick glance we can see that the 0xffffffff is on the offset so we should be able to read until 0xffffffff

In [114]:
import binascii
import struct

print("[ + ] CosmisSmash")

with open("CosmicSmash.bin", "rb") as binary_file:
    binary_file.seek(0) 
    binary_file.seek(0x360) 
    while (1): 
        bin_offset = struct.unpack('i', binary_file.read(4))
        bin_ram = struct.unpack('i', binary_file.read(4))
        bin_size = struct.unpack('i', binary_file.read(4))
        if bin_size[0] == 0 or bin_offset[0] < 0:
            break
        print("offset from ROM: %#x RAM Address is %#x Loop/Segment size: %#x" % (bin_offset[0],bin_ram[0], bin_size[0]))
    binary_file.seek(0x420)
    entry_addie = struct.unpack('i', binary_file.read(4))
    print("Entry address is %#x" % entry_addie)

print("[ + ] ZeroGunner2")

with open("ZeroGunner2.bin", "rb") as binary_file:
    binary_file.seek(0) 
    binary_file.seek(0x360) 
    while (1): 
        bin_offset = struct.unpack('i', binary_file.read(4))
        bin_ram = struct.unpack('i', binary_file.read(4))
        bin_size = struct.unpack('i', binary_file.read(4))
        if bin_size[0] == 0 or bin_offset[0] < 0:
            break
        print("offset from ROM: %#x RAM Address is %#x Loop/Segment size: %#x" % (bin_offset[0],bin_ram[0], bin_size[0]))
    binary_file.seek(0x420)
    entry_addie = struct.unpack('i', binary_file.read(4))
    print("Entry address is %#x" % entry_addie)

print("[ + ] MarvelVsCapcom2.bin")

with open("MarvelVsCapcom2.bin", "rb") as binary_file:
    binary_file.seek(0) 
    binary_file.seek(0x360) 
    while (1): 
        bin_offset = struct.unpack('i', binary_file.read(4))
        bin_ram = struct.unpack('i', binary_file.read(4))
        bin_size = struct.unpack('i', binary_file.read(4))
        if bin_size[0] == 0 or bin_offset[0] < 0:
            break
        print("offset from ROM: %#x RAM Address is %#x Loop/Segment size: %#x" % (bin_offset[0],bin_ram[0], bin_size[0]))
    binary_file.seek(0x420)
    entry_addie = struct.unpack('i', binary_file.read(4))
    print("Entry address is %#x" % entry_addie)
[ + ] CosmisSmash
offset from ROM: 0x20000 RAM Address is 0xc020000 Loop/Segment size: 0x100000
offset from ROM: 0x150000 RAM Address is 0xc400000 Loop/Segment size: 0x20000
offset from ROM: 0x190000 RAM Address is 0xc480000 Loop/Segment size: 0x20000
Entry address is 0xc020000
[ + ] ZeroGunner2
offset from ROM: 0x0 RAM Address is 0xc020000 Loop/Segment size: 0xe0000
offset from ROM: 0xe0000 RAM Address is 0xc100000 Loop/Segment size: 0x100000
offset from ROM: 0x1e0000 RAM Address is 0xc200000 Loop/Segment size: 0x100000
offset from ROM: 0x2e0000 RAM Address is 0xc300000 Loop/Segment size: 0x5460
Entry address is 0xc021000
[ + ] MarvelVsCapcom2.bin
offset from ROM: 0x1000 RAM Address is 0xc021000 Loop/Segment size: 0x250000
Entry address is 0xc021000
In [121]:
import binascii
import struct
from pprint import pformat

class gameEntries:
    """contains the address entries for the game memory segments and entry point for the game, 
    Exceptions are caught with the "I did everything right, and did nothing wrong" license ala DSP
    """
    def __init__(self):
        """Init the variable to be able to read all the entries into a dictionary to then just parse the dictionary"""
        self.entries = []
        self.PC = None
        self.PC2 = None #Feeling like I don't know the purpose, might delete later *insert meme pose*
    
    def readGameLoops(self, filename=None, loop_address=0x360):
        """Reads the entries and returns an array with the dictionary of them"""
        with open(filename, "rb") as binary_file:
            binary_file.seek(0) 
            binary_file.seek(loop_address) 
            while (1): 
                # More .. "pythonic" (Aka looks cool is kinda unreadable)
                (bin_offset, bin_ram, bin_size) = struct.unpack('iii', binary_file.read(12))
                if bin_size == 0 or bin_offset < 0:
                    break
                self.entries.append({'ROM_Offset':bin_offset, 'RAM_Address':bin_ram, 'Segment_Size':bin_size})
                #print("offset from ROM: %#x RAM Address is %#x Loop/Segment size: %#x" % (bin_offset,bin_ram, bin_size))
            """We get the entry address (there are 2 pointers, Maybe PC and another? will read both)"""
            if self.PC == None:
                binary_file.seek(0x420)
                (self.PC, self.PC2) = struct.unpack('ii', binary_file.read(8))
        return self.entries
    
    def getEntryPoint(self):
        """Returns current value of self.PC if not set it will return None"""
        return self.PC
    
    def getEntryPoint2(self):
        return self.PC2
    
    def cleanEntries(self):
        """Clean the entries array to allow to have either another binary or have another offset"""
        del self.entries[:]
        self.PC = self.PC2 = None
        return True
    
    def __repr__(self):
        return pformat(self.entries, indent=4, width=1)
    
    def _AddSegment(name, base_address, data):
        """Add a segment to the IDB with some basic options set for convenience."""
        s = idaapi.segment_t()

        s.startEA = base_address
        s.endEA = base_address + len(data)
        s.bitness = 1 # 32-bit
        s.align = idaapi.saRelByte
        s.comb = idaapi.scPub
        s.sel = idaapi.setup_selector(0)

        idaapi.add_segm_ex(s, name, None, idaapi.ADDSEG_NOSREG | idaapi.ADDSEG_OR_DIE)
        idaapi.mem2base(data, base_address)
        
    

Diff tables analysis

TBH too bored to write it out right now …. I blame @d0tSlash in twitter

In [118]:
## UGH debug :P
print("[ + ] Analyzing ZeroGunner2.bin first")
og = gameEntries()
entries = og.readGameLoops('ZeroGunner2.bin')
for entry in entries:
    print("offset from ROM: %#x RAM Address is %#x Loop/Segment size: %#x" % (entry['ROM_Offset'], entry['RAM_Address'], entry['Segment_Size']))
print("-- Entry point is %#x" % og.getEntryPoint())
print("-- Entry point 2 is %#x" % og.getEntryPoint2())
og.cleanEntries()
entries = og.readGameLoops(filename='ZeroGunner2.bin', loop_address=0x3c0)
for entry in entries:
    print("offset from ROM: %#x RAM Address is %#x Loop/Segment size: %#x" % (entry['ROM_Offset'], entry['RAM_Address'], entry['Segment_Size']))
og.cleanEntries()
print('-----------------')
print("[ + ] Analyzing CosmicSmash.bin now")
entries = og.readGameLoops('CosmicSmash.bin')
for entry in entries:
    print("offset from ROM: %#x RAM Address is %#x Loop/Segment size: %#x" % (entry['ROM_Offset'], entry['RAM_Address'], entry['Segment_Size']))
print("-- Entry point is %#x" % og.getEntryPoint())
print("-- Entry point 2 is %#x" % og.getEntryPoint2())
og.cleanEntries()
entries = og.readGameLoops(filename='CosmicSmash.bin', loop_address=0x3c0)
for entry in entries:
    print("offset from ROM: %#x RAM Address is %#x Loop/Segment size: %#x" % (entry['ROM_Offset'], entry['RAM_Address'], entry['Segment_Size']))
    
[ + ] Analyzing ZeroGunner2.bin first
offset from ROM: 0x0 RAM Address is 0xc020000 Loop/Segment size: 0xe0000
offset from ROM: 0xe0000 RAM Address is 0xc100000 Loop/Segment size: 0x100000
offset from ROM: 0x1e0000 RAM Address is 0xc200000 Loop/Segment size: 0x100000
offset from ROM: 0x2e0000 RAM Address is 0xc300000 Loop/Segment size: 0x5460
-- Entry point is 0xc021000
-- Entry point 2 is 0xc021004
offset from ROM: 0x0 RAM Address is 0xc020000 Loop/Segment size: 0xe0000
offset from ROM: 0xe0000 RAM Address is 0xc100000 Loop/Segment size: 0x100000
offset from ROM: 0x1e0000 RAM Address is 0xc200000 Loop/Segment size: 0x100000
offset from ROM: 0x2e0000 RAM Address is 0xc300000 Loop/Segment size: 0x5460
-----------------
[ + ] Analyzing CosmicSmash.bin now
offset from ROM: 0x20000 RAM Address is 0xc020000 Loop/Segment size: 0x100000
offset from ROM: 0x150000 RAM Address is 0xc400000 Loop/Segment size: 0x20000
offset from ROM: 0x190000 RAM Address is 0xc480000 Loop/Segment size: 0x20000
-- Entry point is 0xc020000
-- Entry point 2 is 0xc020000
offset from ROM: 0x120000 RAM Address is 0xc020000 Loop/Segment size: 0x30000
In [67]:
import binascii
import struct

with open("BIOS/KF Private/317hackedupdatergd.bin", "rb") as cosmicsmash:
    with open("BIOS/KF Private/FW_Dimm317.bin", "rb") as zerogunner2:
        print("[ + ] Comparing CosmicSmash.bin to ZeroGunner2.bin from 0x130 to 0x500")
        x = 0x360
        while x < 0x500:
            cosmicsmash.seek(x)
            zerogunner2.seek(x)
            # This is not pythonic at all .. I have to find an elegant way of doing this tbh :/
            cosmicsmash_content = struct.unpack('L', cosmicsmash.read(4))[0]
            #cosmicsmash.seek(4,1)
            zerogunner2_content = struct.unpack('L', zerogunner2.read(4))[0]
            #zerogunner2.seek(4,1)
            #print("[ - ] Address %#x :: CS -> 0x%.8x == ZG -> 0x%.8x" % (x, cosmicsmash_content, zerogunner2_content))
            x += 4

print("[ + ] DONE")
og.cleanEntries()
og.readGameLoops('BIOS/KF Private/317hackedupdatergd.bin')
print("-- Entry point is %#x" % og.getEntryPoint())
og.readGameLoops(filename='BIOS/KF Private/317hackedupdatergd.bin', loop_address=0x3c0)
[ + ] Comparing CosmicSmash.bin to ZeroGunner2.bin from 0x130 to 0x500
[ + ] DONE
offset from ROM: 0x0 RAM Address is 0xc020000 Loop/Segment size: 0x100000
offset from ROM: 0x100000 RAM Address is 0xd000000 Loop/Segment size: 0x200000
-- Entry point is 0xc020800
offset from ROM: 0x0 RAM Address is 0xc020000 Loop/Segment size: 0x100000
offset from ROM: 0x100000 RAM Address is 0xd000000 Loop/Segment size: 0x200000
Out[67]:
[{'ROM_Offset': 0, 'RAM_Address': 201457664, 'Segment_Size': 1048576},
 {'ROM_Offset': 1048576, 'RAM_Address': 218103808, 'Segment_Size': 2097152},
 {'ROM_Offset': 0, 'RAM_Address': 201457664, 'Segment_Size': 1048576},
 {'ROM_Offset': 1048576, 'RAM_Address': 218103808, 'Segment_Size': 2097152}]

THIS IS A FILE WHICH CHANGES EVERYDAY, FEEL FREE TO GIT PULL CONSTANTLY

In [ ]: