smtddr's notes

[Things I find interesting]

Tatsunoko-vs-Capcom - LeaderBoard hack part.3 - Checkmate on TvC gamestats2 network traffic.

Checkmate.

After weeks of searching within game memory, I have the sha1 salt and constant used for xor’ing the checksum. Calculating the hash is straight-forward and the data in the url is base64 encoded just like for Pokemon.

In terms of the RankingCharts, I can now change the following:

  • Nickname(8 chars max)

  • Ranking(0×00 - 0×1A, gives me Beginner rank to UltimateHero). Setting this to 0×1B-0xFF will still be “UltimateHero” rank.

  • BattlePoints(This is a 2 byte unsigned short int, so the max is 65535).

    I can place myself at the top of the chart as SMT@DDR UltimateHero 65535 BP http://www.youtube.com/watch?v=lxj11crtQjA

The Ranking, nickname, BP and pid are completely unrelated values. I can set it to be “2600HCKQ”, Beginner, 31337 BP using a non-existent PID. This is probably because gamestats2 is a generic server for a bunch of Nintendo wifi games, so no logic for any particular game is built into it. I am also able to remove other people from the chart(by setting their BP to zero) because the server-response for the charts also contain the PID of each person on it. Note that these changes aren’t very permanent. As soon as you have one regular rankedbattle, gamestats2 updates that player’s info. However, what I can do is change the PID to some unused number, like 1337, and since nobody will legitimately play as a pid like that(too short) that 1st place fake user would remain on the board until Nintendo notices as removes the record from their database.

I’m done with this hack project in terms of gamestats2.gs.nintendowifi.net. Because of the nature of this hack, I won’t be posting the exact code for public-viewing. I’ll just say this about what I discovered:

When asking Nintendo for the ranking boards, the datastream returned is:

[16 byte HEADER]
[PLAYER INFO]
[40 byte FOOTER]

The first 16 bytes are header-stuff, ignore it… for now. The player info area is chain of structs of size 100-bytes, of which:

  • The first 4 bytes are ProfileID of the player in little-endian(the same pid used in gamestats2 GET requests)
  • The next 2 bytes are BattlePoints little-endian
  • The next 32 bytes, not sure yet.
  • The next 2 bytes are BattlePoints big-endian
  • The next 23 bytes, not sure yet.
  • The next byte is ranking(0×00 - 0×1A) being “Beginner” to “UltimateHero”
  • The next 16 bytes, double-byte chars(UTF-16BE) for Nicknames. If name is shorter than 8, it is padded with trailing spaces(0×20)
  • The next 2 bytes are always 0×00 & 0×20(even if name already has trailing spaces)
  • The next 18 bytes are nulls

The 40 bytes at the end of the server data looks like a checksum of some kind.

Also, the gamestats2 data sent when you complete a ranked battle has a structure nearly exactly described as above with just 1 struct; yours. This is sent anytime you complete a RankedBattle. If your opponent “rage quits” before reaching the score-screen, then the game won’t send it. The info stored on gamestats2’s ranking board has nothing to do with the info that is used while you’re playing online. Even though I have 65535BP on the ranking, when I play against people and gain/lose BP it’s the regular amount I always had before my hack.

終 -^_^-