It Was DNS: Getting My Wife's Mahjong App Back in Brisbane
My wife has a mahjong app she loves, 琼崖海南麻将. Back in China it was part of her day: a few rounds here, a few rounds there. A few months ago we moved to Brisbane, and the app quietly stopped working. She could still log in and look around the lobby, but the moment she tried to join a game it got stuck, and after a while it just gave up. So, eventually, did she.
I don’t play it myself, so I’d filed it under “China blocks foreign IPs, nothing we can do”.
The VPN That Didn’t Help
Yesterday, for a completely unrelated reason, we set up a VPN back to China. She looked at it and said, “Oh, maybe my mahjong app works now?”
We tried. She tapped into the mahjong hall (麻将大厅). “游戏正在加载……” appeared, then a second message on top of it: “正在登录游戏[1]…”, then [2], [3], [4]. And there it sat. A little later both messages disappeared and she was back in the lobby. Exactly like before.
But this time I noticed something. Login worked fine. The shop page loaded. The news page loaded. Pretty much everything worked except that one step. A real block doesn’t usually pick and choose like that. It looked more like something was hanging in the middle, and hanging is the kind of problem you can usually untangle.
So I thought about reverse engineering the app. Then I thought about how long that would take me on my own. Then I thought about AI.
Opening the Box
It was already late, so I opened GitHub Copilot in my terminal, pointed it at the APK, told it the story above and asked it to figure out how to get her connected. One rule: please don’t download an Android emulator. I didn’t want gigabytes of tooling for a mahjong game.
The first few minutes went something like this:
- The app is built on Cocos2d-x, and its logic lives in about 2,000 Lua scripts, all encrypted. The agent found the key sitting in the app’s native library, right next to the encryption marker, and decrypted everything into readable Lua. Minutes.
- Then it searched for the message we were stuck on. Nothing. Not a single match anywhere in the APK.
That was confusing for a moment, until it worked out why: the mahjong hall isn’t really inside the APK. The app downloads game “modules” from an update server when you open them. The agent followed the module config to the server, downloaded the mahjong module’s scripts, decrypted those too, and there was our message, counter and all.
Reading the code around it turned [1] to [4] into something meaningful:
- Download the module’s settings.
- Ask a lookup service which game server to use, with a fixed fallback address.
- Connect to that server.
- Send the login and wait for a reply.
There’s also a 30-second timer that closes everything if login never finishes, which is why both messages vanished on their own. So we were stuck at “connected, said hello, nobody answered”.
Same Name, Different Address
Next the agent looked at the network from my PC, which sits on the same home Wi-Fi as her phone. It looked up the game’s server names, tried connecting to them, and poked at that lookup service.
From Brisbane, the lookups pointed at addresses that simply didn’t answer. Then it asked Google’s DNS the same questions, but as if they were coming from China. Completely different answers: mainland addresses.
That was the lightbulb moment. A lot of Chinese services give you a different IP for the same name depending on where you ask from. Usually that’s helpful, because it sends you somewhere nearby. Here, the “overseas” answers were the broken ones. The app wasn’t blocked at all. It was being handed the wrong addresses.
The Fix: One Setting
The obvious fix was to change the DNS, but the VPN app didn’t let me. So I asked whether there was any other way on a Xiaomi phone.
It turns out Android has Private DNS built in (DNS over TLS), and it doesn’t need a VPN at all. The catch was finding a resolver that would hand out the mainland answers even when asked from Australia. The agent wrote a little script and tried a bunch of them. AliDNS, DNSPod, Google and Cloudflare all gave the overseas answer. Then 360’s dot.360.net came back with the mainland addresses, every single time.
On her phone: Settings → Connection & sharing (连接与共享) → Private DNS (私人DNS) → hostname → dot.360.net.
She force-stopped the game, opened it again, tapped into the mahjong hall… and she was sitting at a table. It even worked with the VPN switched off.
About 35 minutes from “here’s an APK” to her playing again. She was thrilled like a kid, and honestly that was the best part of the whole night.
dot.360.net before reaching for a VPN.The Next Evening: Why No VPN?
I couldn’t quite let it go. The AI’s own theory had been that the game servers only accept Chinese IPs. Its connection tests from my PC, and from about 40 locations around the world, had all timed out. By that logic we should have needed the VPN. But it worked without one, so something didn’t add up.
So we checked the phone itself. I turned on USB debugging, plugged it into my PC, and the agent read the game’s live connections over adb. The game wasn’t talking to that “blocked” server at all.
Then it played detective with the addresses it did see. It took every hostname in the app’s code, looked each one up through the same 360 DNS, and matched them up. One IP was a shared front door for about ten of the developer’s own services. Another was their analytics. And one odd connection on port 9377 wasn’t the game at all: that number turned up hard-coded inside an ad SDK bundled with the app. Ha.
This was my favourite bit. The AI got it wrong first, and then it sorted itself out by measuring rather than arguing.
So the VPN was never needed, just the right DNS answer. Of course it was DNS.
Still To Do
There’s one loose end. She occasionally gets “登录信息失效,请重新登录” (your login has expired) after the phone has been locked for a while. So far we know Xiaomi freezes the app a few seconds after it goes into the background, and when it wakes up the server rejects its login token. Why the server does that is still a mystery. Next time it happens I’ll note the time and we’ll dig through the phone’s logs.
Thoughts
The most useful clue came before any tools were involved: some things worked and one step didn’t. That’s what made it feel solvable.
After that, AI made it fast and, honestly, effortless. Decrypting thousands of scripts, chasing a downloaded module, testing DNS servers, reading the phone’s connections: any one of those would have taken me an evening. Here I mostly answered questions and, every now and then, asked “how do you know?”.
My wife is back to her daily mahjong. 🀄