Reverse engineering the Unity Network Discovery protocol
Written by Tamás Deme on Tue Dec 19 2017.
I’m working on a project where a .net core backend is used as a server to provide data for multiple Unity clients. To ease development and usage in Unity it’d great if we could use the built-in network discovery module, so that’s what I did.
First steps: how it works in Unity #
Clickety clicks: [New Project], [New > Create Empty], [Add Component], [NetworkDiscovery]. Upon hitting the play button we’re presented with a simple GUI allowing us to start broadcasting or listening.
I noticed that the component says “(Script)” at the end, so let’s check that out before breaking out Wireshark. Clicking the little [cog] icon and selecting [edit script] results in nothing as it’s compiled into the UnityEngine.Networking.dll. Luckily a quick search results in the source where we can see that the StartAsServer method calls the NetworkTransport.StartBroadcastDiscover method. Again a little searching to find the NetworkTransport source aaaand that how far down the rabbit hole goes.
[GeneratedByOldBindingsGenerator]
[MethodImpl(MethodImplOptions.InternalCall)]
private static extern bool StartBroadcastDiscoveryWithoutData(int hostId, int broadcastPort, int key, int version, int subversion, int timeout, out byte error);
Well, Wireshark it is… #
Select your main network interface, and enter udp.port == 64764 as a filter to match the port specified in the component. If you now start broadcasting in Unity you’ll see the following:
Selecting any packet and opening the Data part shows us the interesting stuff:
We need more data #
Let’s open up Unity again, and start changing around values to see what happens with the data bytes. After a few variations I ended up with the following text file:
Immediately we can see patterns emerging:
-
all the samples have a bunch of zeroes in the middle
-
all the samples start with [0x00 0x00 0x09]
-
the data field is at the end, as the beginnings look similar enough
With a little trial and error it’s not hard to figure it all out:
-
We begin with the mentioned [0x00 0x00 0x09] sequence
-
Two random bytes are inserted that remain the same for the broadcasting session (So if you change nothing except restart the broadcast these will be different)
-
The key integer appears in 4 bytes using the reversed endianness of the windows default.
-
We see 8 4 byte blocks of zeroes, probably for future fields
-
We have the version integer on the same reversed endian 4 bytes
-
We have the subversion integer the same way
-
And finally we see the data string, ASCII encoded with a 0x00 byte between every character
Replicating this in C# is simple enough, a few byte arrays, a little LINQ and a BitConverter here and there — here is the GitHub Gist for a .net core console app.
To make sure that we did everything right let’s see it in action:
I hope this served as a little introduction on network protocol reverse engineering, and is useful for interacting with Unity as well.
shore party out