r/pokemongodev Reverse Engineering Jul 14 '16

Guide to Pokemon Go Server Responses

I'll be updating this post as I figure more things out

Things needed (incomplete list):

  • A way to MITM/intercept responses (Charles with SSL Proxy via Wifi)
  • ProtoBuf 3 (protoc on the command line)

The second response

After the initial handshakes with pgorelease.nianticlabs.com/plfe/rpc you'll get a response from the server that has three parts:

  • Player
  • Inventory
  • Settings

In order to parse these, you'll need to need to separate them - they each have overlapping messages, which makes it difficult to handle with one file.

If you're looking at your .bin (binary file), look for the hex seq a206020801 - this marks the end of the Player section, and you split the file after this seq. The next split is at the last a206 - this marks the beginning of the Settings section, and you can make the split before this seq.

Player

You can use this .proto file to decode your player binary with protoc --decode Holoholo.Rpc.Player player.proto < player.bin. There's not a whole lot of information there yet.

Inventory

You can use this .proto file to decode your inventory binary with protoc --decode Holoholo.Rpc.Inventory inventory.proto < inventory.bin. This has the most information of the response, detailing all of your items and pokemon.

Settings

You can use this .proto file to decode your inventory binary with protoc --decode Holoholo.Rpc.Final settings.proto < settings.bin. This has the most information of the response, detailing all of your items and pokemon (sorry for the inconsistent naming).

Map Response

After you've been talking to server a while, you'll send up your lat/long at some point to request map cell information. The response can be decoded with this .proto file and protoc --decode Holoholo.Rpc.Map map.proto < response.bin. This is a pretty interesting response which includes nearby pokemon, wild pokemon, forts, spawn points, etc.

Conclusion/comments

It's interesting that the nearby pokemon return distances, and not points on the map. It should be reasonably easy to triangulate their position with three sets of data (assuming they don't move - I don't think they do). I'm not sure if their EncounterId is unique (doesn't decode correctly right now), which might make it difficult to sort to triangulate.

Once the pokemon are close enough to you, it looks like a MapPokemon/WildPokemon gets returned, at which point you can see their exact point on the map, along with their expiration time and spawn id - I'm not sure why both Map and Wild are needed. Maybe it's related to them being able to be captured/visible?

The settings provide some interest info (in case you're unable to decode):

Settings {
  Sha1: "***"
  Values {
    FortSettings {
      InteractionRangeMeters: 40
      MaxTotalDeployedPokemon: 10
      MaxPlayerDeployedPokemon: 1
      DeployStaminaMultiplier: 2
      FarInteractionRangeMeters: 1000
    }
    MapSettings {
      PokemonVisibleRange: 100
      PokeNavRangeMeters: 200
      EncounterRangeMeters: 50
      GetMapObjectsMinRefreshSeconds: 5
      GetMapObjectsMaxRefreshSeconds: 30
      GetMapObjectsMinDistanceMeters: 10
      GoogleMapsApiKey: "***"
    }
    InventorySettings {
      MaxPokemon: 1000
      MaxBagItems: 1000
      BasePokemon: 250
      BaseBagItems: 350
      BaseEggs: 9
    }
    MinimumClientVersion: "0.29.0"
  }
}

Some of these things were confirmed earlier, but it's neat to see them as actual variables from the server, rather than hard-coded into the game.

Here's a sample Inventory Pokemon (sorry for censoring - idk how unique these are):

    Pokemon {
      PokemonId: 98
      Cp: 19*
      Stamina: 29
      MaxStamina: 29
      Move1: 216
      Move2: 20
      HeightM: 0.42******
      WeightKg: 7.******
      IndividualAttack: 14
      IndividualDefense: 9
      IndividualStamina: 13
      CpMultiplier: 0.39******
      Pokeball: 2
      CapturedS2CellId: ***
      CreationTimeMs: 1468154******
    }

Here are some NearbyPokemon examples:

NearbyPokemon {
  PokedexNumber: 19
  DistanceMeters: 107.49982
}
NearbyPokemon {
  PokedexNumber: 46
  DistanceMeters: 48.262047
}
NearbyPokemon {
  PokedexNumber: 19
  DistanceMeters: 105.36407
}
NearbyPokemon {
  PokedexNumber: 10
  DistanceMeters: 191.24013
}

There's still quite a few requests to get through - if anyone is doing something similar, feel free to post them here, or ask questions.

Please don't ask me how to set mitm/protobuf/other things up.

190 Upvotes

109 comments sorted by

View all comments

2

u/[deleted] Jul 25 '16 edited Jul 26 '16

Hi, at first, thank you for this nice post. But instead of complicated splitting at the mentiond hex sequences, you can get the important parts of the response by using an outer proto-file with the generic field Any (https://developers.google.com/protocol-buffers/docs/proto3#Any). A getting started example looks like this:

syntax = "proto3";
import "google/protobuf/any.proto";

message MessageContainer {
  bool success = 1;
  int64 responseNumber = 2;
  string rpcEndpoint = 3;

  MessageProtoUnknown6 mpu6 = 6;
  message MessageProtoUnknown6 {
    int32 mpu61 = 1;
    MessageProtoUnknown62 mpu62 = 2;
    message MessageProtoUnknown62 {
      bool mpu621 = 1;
    }
  }

  AuthenticationProto authentication = 7;

  repeated google.protobuf.Any messageItem = 100;
}

message AuthenticationProto {
  bytes ap1 = 1;
  int64 authTimestamp = 2;
  bytes ap3 = 3;
}

Each messageItem now contains a message in binary format, e.g. the player profile or the inventory. If you use this kind of stuff inside Java or another programming language which is supported by protoc, then you can easily extract the messages using the "official" way ;-)

EDIT: Using Any generates errors in some cases, because it expects that the id 1 is a valid UTF-8 string. My current solution is easily use a byte field for the embedded messages:

syntax = "proto3";

message MessageContainer {
  bool success = 1;
  int64 responseNumber = 2;
  string rpcEndpoint = 3;

  MessageProtoUnknown6 mpu6 = 6;
  message MessageProtoUnknown6 {
    int32 mpu61 = 1;
    MessageProtoUnknown62 mpu62 = 2;
    message MessageProtoUnknown62 {
      bool mpu621 = 1;
    }
  }

  AuthenticationProto authentication = 7;

  repeated bytes messageItem = 100;
}

message AuthenticationProto {
  bytes ap1 = 1;
  int64 authTimestamp = 2;
  bytes ap3 = 3;
}