Humpheh / nest-observe

Unofficial API for observing Google Nest devices which works with Google authentication
MIT License
13 stars 1 forks source link

Interested in how you extracted the protobuf spec #2

Open mdrago1026 opened 4 years ago

mdrago1026 commented 4 years ago

Hi Humpheh,

I stumbled upon your library while trying to reverse engineer the Nest browser client Observe API. It has been extremely helpful so far. I am trying to reverse engineer the Nest Security products. I am focusing on the detects and the guard itself right now.

I see the events coming in, but obviously the protobufs haven't been dissected yet and the important parts are gibberish. For example, I can see arm/disarm events, but no protobuf decoder/base64->UTF-8 decoder yields anything meaningful for the data that I assume tells me the arm state. At this point I only know the arm state changed.

I am about to dive into the rabbit-hole of trying to build the protobuf from the un-uglified/minified JS, but I wanted to see what your approach was. I would love to contribute and help build this library!

Humpheh commented 4 years ago

Hey @mdrago1026, thanks for your interest in the library!

That sounds like a cool addition. Yeah you sound correct, to decode the message we need to implement some of the protobuf specs so that it can correctly decode the traits. Here's the way I went about getting them - it's pretty manual and not that great but it appears to work!

  1. Load the Nest app in a web browser on Chrome (other browsers may work but I use that so these are how I do it) https://home.nest.com/home

  2. Open the developer tools and open the console. There is a global on the Nest app called proto which stores all of the protobuf messages registered in the JS app. Type in proto.<trait name> (eg proto.nest.trait.security.SecurityArmTrait). The console should do auto complete which allows you to browse the other messages. When you submit that it should print out a basic function definition (it doesn't run anything).

    image

  3. Right click the function output in the console and select 'Show function definition'.

    image

    This will load a Javascript source file in the 'Sources' tab at the location that the message is defined. If the text is minified click the '{}' button to format it and it should look nice.

    image

  4. There should be a couple of properties on that object you've navigated to that will describe the message (usually they are below the object definition in the source - toObject and deserializeBinaryFromReader. For me deserializeBinaryFromReader is easier to read - it contains a switch statement which reads each of the fields. The case number will be the index of that field in the message and then the code in the case will describe how it's type and name. For example:

            case 1:
                var r = e.readEnum();
                t.setArmState(r);
                break;
            case 2:
                r = e.readUint32();
                t.setSecurityArmSessionId(r);
                break;

    These two fields show that field 1 is an enum called armState (you may need to dig into the set function to find the type of the enum etc), and that field 2 is a uint32 called securityArmSessionId. This looks something like this in a message:

message SecurityArmTrait {
    AnEnum armState = 1;
    uint32 securityArmSessionId = 2;
    // ...rest of the fields
}
  1. Add the message into a protobuf file in protobuf/pb/nest.security.proto (in your case). Base the file off the other ones in that directory, but make sure the package name is correct (like nest.trait.security). You should also add any new protobuf files that you create into the gen_pb.sh script (then run it), and then also add a require statement for it in the index.js file. If you add it in both those places it should just start decoding when it sees the messages!

Hopefully that helps, let me know if there is anything there that catches you out. Like I said, it's a bit manual at the moment but you should be able to get something out of it!