Silly hack: Fixing Rainbow Six’s UPNP The Wrong Way 2/3

Now that we know what goes wrong when starting RainbowSix, the NAT type stays Strict (without VPN).

Having decided that the upnp request must leave my Windows PC in good form, this leaves two options. Either having the game fixed or intercepting the request and modifying it on the fly. After writing a forum post to Ubisoft’s Siege forums, I continued with plan B.

Turns out that there are easy tools for capturing and modifying network traffic on the fly.

In GNU/Linux world, the obvious choice would’ve been  to use Netsed together with some iptables rules:

$ netsed tcp 9000 192.168.0.35 8080 's/test/trial/'

For Windows, the closest thing to Netsed I found was WinDivert: “Windows Packet Divert (WinDivert) is a user-mode packet capture-and-divert package” which comes with bunch of features. I decided to start with Divert.Net, a C# wrapper for WinDivert.

WinDivert architecture from readme at https://reqrypt.org/windivert-readme.txt

After installing the tools I realize this sums up differences between GNU/Linux and Windows tools nicely: instead of getting the job done with small tool from an official repository+small configuration+vim, I end up using somebody else’s binary through privileged application and writing/modifying a hundred lines of boiler plate with Visual Studio. WinDivert is open source though, so I had the option of compiling the binaries myself.

After modifying Divert.Net example to suit this purpose and use filter

string filter = "tcp.PayloadLength > 100 and tcp.DstPort == 2189 and ip.DstAddr == 10.0.2.1";

Lo and behold, after firing up the executable, it is actually able to search matched packet contents for string <NewInternalClient></NewInternalClient>!

Let’s try fixing the request and see what happens. While doing that we also have  to update the Content-Length HTTP header.

Mildly concerned with BattleEye still running on my computer, I start the game and wait for results.

Success! We’re now able to actually intercept and fix the requests done by RainbowSix.exe on the fly.

D’uh. So the port gets opened and I can see traffic start flowing through it. What’s still wrong?

With my WinDivert application running we end up having unexpected packet loss between my Windows and a local router. Sub-optimal! Red is the color of blood splashing after shooting yourself in the leg.

Without WinDivert, the miniupnpd starts finalizing the TCP connection after the HTTP 500 failure with FIN-ACK and the connection terminates.

With my attempt at using WinDivert, we get the POST request to miniupnpd, but everything after that is lost as the client is retransmitting the HTTP POST (as it never gets the ACK at #29) and server is retransmitting HTTP 200. So the client doesn’t know the request succeeded. I actually managed to break HTTP.

Next attempt is to redo the same thing with Python and pydivert wrapper. With Python, the first stab of the problem actually fits in one screen:

But the results stay the same.

Even though I believe the follow-up packets shouldn’t get picked on the filter string, they are not travelling through.

The game is likely trying to open multiple ports and decides that upnp is still broken as the HTTP 200’s are still missing and quits attempting after the first request.

Summary:

  • Feels like finding WinDivert was worth having some networking issues
  • I have now two competing implementations that fix a single request but introduce unexpected packet loss to my LAN.
  • The first request now actually creates a NAT rule!
  • Small problem: the bad behavior has now shifted from user mode to kernel mode