Skip to content

Commit ce572a6

Browse files
authored
Merge pull request #21 from RaizeTheLimit/polyx_support
Polyx support
2 parents 84335ef + 7fe5cc0 commit ce572a6

3 files changed

Lines changed: 231 additions & 3 deletions

File tree

README.md

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ Response Route: /raw
1818
Web Interface to View Protos: /
1919
Traffic Route mode /traffic
2020
Golbat Route mode /golbat
21+
PolyX Route mode /PolygonX/PostProtos
2122
2223
2324
Web Interface:
@@ -83,6 +84,33 @@ interface CombinedMessage {
8384
}
8485
```
8586

87+
### Support for PolygonX interfaces
88+
**Sent to** `/PolygonX/PostProtos`
89+
90+
```js
91+
interface CombinedMessage {
92+
protos: [
93+
{
94+
method: number
95+
proto: bytes
96+
request: bytes
97+
trainer_id: string
98+
trainer_level: number
99+
has_geotargeted_ar_scan_quest: boolean
100+
}
101+
]
102+
push_gateway_protos: [
103+
{
104+
method: number
105+
proto: bytes
106+
trainer_id: string
107+
trainer_level: number
108+
has_geotargeted_ar_scan_quest: boolean
109+
}
110+
]
111+
}
112+
```
113+
86114
### Requirements
87115

88116
```

src/index.ts

Lines changed: 82 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,9 @@ import http from "http";
22
import fs from "fs";
33
import crypto from "crypto";
44
import { WebStreamBuffer, getIPAddress, handleData, moduleConfigIsAvailable, redirect_post_golbat } from "./utils";
5-
import { decodePayload, decodePayloadTraffic } from "./parser/proto-parser";
5+
import { decodePayload, decodePayloadTraffic, decodeProtoFromBytes } from "./parser/proto-parser";
66
import SampleSaver from "./utils/sample-saver";
7+
import POGOProtos from "@na-ji/pogo-protos";
78

89
// try looking if config file exists...
910
let config = require("./config/example.config.json");
@@ -206,6 +207,85 @@ const httpServer = http.createServer(function (req, res) {
206207
}
207208
});
208209
break;
210+
case "/PolygonX/PostProtos":
211+
req.on("data", function (chunk) {
212+
incomingData.push(chunk);
213+
});
214+
req.on("end", function () {
215+
try {
216+
const binaryData = Buffer.concat(incomingData);
217+
res.writeHead(200, { "Content-Type": "application/json" });
218+
res.end("");
219+
220+
if (binaryData.length === 0) {
221+
console.error("Invalid PolygonX data: empty request body");
222+
return;
223+
}
224+
225+
// Decode the RawProtoCollection from binary protobuf
226+
const decoded = POGOProtos.Rpc.AllTypesAndMessagesResponsesProto.Hexagon.RawProtoCollectionMessage.decode(binaryData);
227+
228+
// Process RawProto entries (have both request and response)
229+
if (decoded.protos && Array.isArray(decoded.protos)) {
230+
for (const rawProto of decoded.protos) {
231+
const identifier = rawProto.trainer_id || "unknown";
232+
const method = rawProto.method?.valueOf() || 0;
233+
const requestBytes = rawProto.request;
234+
const responseBytes = rawProto.proto;
235+
236+
// Decode request
237+
if (requestBytes && requestBytes.length > 0 && method !== 0) {
238+
const parsedRequestData = decodeProtoFromBytes(method, requestBytes, "request");
239+
if (typeof parsedRequestData !== "string") {
240+
parsedRequestData.identifier = identifier;
241+
outgoingProtoWebBufferInst.write(parsedRequestData);
242+
}
243+
}
244+
245+
// Decode response
246+
if (responseBytes && responseBytes.length > 0) {
247+
const parsedResponseData = decodeProtoFromBytes(method, responseBytes, "response");
248+
if (typeof parsedResponseData !== "string") {
249+
parsedResponseData.identifier = identifier;
250+
incomingProtoWebBufferInst.write(parsedResponseData);
251+
}
252+
}
253+
254+
// Save sample if enabled
255+
if (sampleSaver && requestBytes && responseBytes) {
256+
const requestB64 = Buffer.from(requestBytes).toString("base64");
257+
const responseB64 = Buffer.from(responseBytes).toString("base64");
258+
const parsedRequest = decodeProtoFromBytes(method, requestBytes, "request");
259+
const parsedResponse = decodeProtoFromBytes(method, responseBytes, "response");
260+
if (typeof parsedRequest !== "string" && typeof parsedResponse !== "string") {
261+
sampleSaver.savePair(parsedRequest, parsedResponse, requestB64, responseB64, "polygonx");
262+
}
263+
}
264+
}
265+
}
266+
267+
// Process RawPushGatewayProto entries (response only)
268+
if (decoded.push_gateway_protos && Array.isArray(decoded.push_gateway_protos)) {
269+
for (const pushProto of decoded.push_gateway_protos) {
270+
const identifier = pushProto.trainer_id || "unknown";
271+
const method = pushProto.method?.valueOf() || 0;
272+
const responseBytes = pushProto.proto;
273+
274+
// Decode response
275+
if (responseBytes && responseBytes.length > 0) {
276+
const parsedResponseData = decodeProtoFromBytes(method, responseBytes, "response");
277+
if (typeof parsedResponseData !== "string") {
278+
parsedResponseData.identifier = identifier;
279+
incomingProtoWebBufferInst.write(parsedResponseData);
280+
}
281+
}
282+
}
283+
}
284+
} catch (error) {
285+
console.error("Error processing PolygonX request:", error);
286+
}
287+
});
288+
break;
209289
case "/traffic":
210290
req.on("data", function (chunk) {
211291
incomingData.push(chunk);
@@ -387,7 +467,7 @@ Server start access of this in urls: http://localhost:${portBind} or WLAN mode h
387467
388468
- Clients MITM:
389469
1) --=FurtiF™=- Tools EndPoints: http://${getIPAddress()}:${portBind}/traffic or http://${getIPAddress()}:${portBind}/golbat (depending on the modes chosen)
390-
2) If Other set here...
470+
2) PolygonX EndPoint: http://${getIPAddress()}:${portBind}/PolygonX/PostProtos (application/x-protobuf)
391471
3) ...
392472
393473
ProtoDecoderUI is not responsible for your errors.

src/parser/proto-parser.ts

Lines changed: 121 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ function DecoderInternalPayloadAsResponse(method: number, data: any): any {
5151
return result;
5252
}
5353

54-
function remasterOrCleanMethodString(str: string) {
54+
export function remasterOrCleanMethodString(str: string) {
5555
return str.replace(/^REQUEST_TYPE_/, '')
5656
.replace(/^METHOD_/, '')
5757
.replace(/^PLATFORM_/, '')
@@ -209,5 +209,125 @@ export const decodeProto = (method: number, data: string, dataType: string): Dec
209209
};
210210
}
211211

212+
return returnObject;
213+
};
214+
215+
// Decode proto from raw bytes (not base64 encoded) - used for PolygonX endpoint
216+
export const decodeProtoFromBytes = (method: number, data: Uint8Array, dataType: string): DecodedProto | string => {
217+
let returnObject: DecodedProto | string = "Not Found";
218+
let methodFound = false;
219+
220+
for (let i = 0; i < Object.keys(requestMessagesResponses).length; i++) {
221+
let foundMethod: any = Object.values(requestMessagesResponses)[i];
222+
let foundMethodString: string = Object.keys(requestMessagesResponses)[i];
223+
const foundReq = foundMethod[0];
224+
if (foundReq == method) {
225+
methodFound = true;
226+
if (foundMethod[1] != null && dataType === "request") {
227+
try {
228+
let parsedData;
229+
if (!data || data.length === 0) {
230+
parsedData = {};
231+
} else {
232+
parsedData = foundMethod[1].decode(data).toJSON();
233+
}
234+
if (foundMethod[0] === 5012) {
235+
action_social = parsedData.action;
236+
Object.values(requestMessagesResponses).forEach(val => {
237+
let req: any = val;
238+
if (req[0] == action_social && req[1] != null && parsedData.payload && b64Decode(parsedData.payload)) {
239+
parsedData.payload = req[1].decode(b64Decode(parsedData.payload)).toJSON();
240+
}
241+
});
242+
}
243+
else if (foundMethod[0] === 600005) {
244+
action_gar_proxy = parsedData.action;
245+
switch (action_gar_proxy) {
246+
case 4:
247+
parsedData.payload = POGOProtos.Rpc.InternalGarAccountInfoProto.decode(b64Decode(parsedData.payload)).toJSON();
248+
break;
249+
default:
250+
break;
251+
}
252+
}
253+
returnObject = {
254+
methodId: foundMethod[0],
255+
methodName: remasterOrCleanMethodString(foundMethodString),
256+
data: parsedData,
257+
};
258+
} catch (error) {
259+
console.error(`Error parsing request ${foundMethodString} -> ${error}`);
260+
returnObject = {
261+
methodId: foundMethod[0],
262+
methodName: remasterOrCleanMethodString(foundMethodString) + " [PARSE ERROR]",
263+
data: {
264+
error: "Failed to decode proto",
265+
errorMessage: error.toString()
266+
},
267+
};
268+
}
269+
} else if (dataType === "request") {
270+
console.warn(`Request ${foundMethod[0]} Not Implemented`)
271+
returnObject = {
272+
methodId: foundMethod[0],
273+
methodName: remasterOrCleanMethodString(foundMethodString) + " [NOT IMPLEMENTED]",
274+
data: {
275+
error: "Proto not implemented"
276+
},
277+
};
278+
}
279+
if (foundMethod[2] != null && dataType === "response") {
280+
try {
281+
let parsedData;
282+
if (!data || data.length === 0) {
283+
parsedData = {};
284+
} else {
285+
parsedData = foundMethod[2].decode(data).toJSON();
286+
}
287+
if (foundMethod[0] === 5012 && action_social > 0 && parsedData.payload) {
288+
parsedData.payload = DecoderInternalPayloadAsResponse(action_social, parsedData.payload);
289+
}
290+
else if (foundMethod[0] === 600005 && action_gar_proxy > 0 && parsedData.payload) {
291+
parsedData.payload = DecoderInternalGarPayloadAsResponse(action_gar_proxy, parsedData.payload);
292+
}
293+
returnObject = {
294+
methodId: foundMethod[0],
295+
methodName: remasterOrCleanMethodString(foundMethodString),
296+
data: parsedData,
297+
};
298+
} catch (error) {
299+
console.error(`Error parsing response ${foundMethodString} method: [${foundReq}] -> ${error}`);
300+
returnObject = {
301+
methodId: foundMethod[0],
302+
methodName: remasterOrCleanMethodString(foundMethodString) + " [PARSE ERROR]",
303+
data: {
304+
error: "Failed to decode proto",
305+
errorMessage: error.toString()
306+
},
307+
};
308+
}
309+
} else if (dataType === "response") {
310+
console.warn(`Response ${foundReq} Not Implemented`)
311+
returnObject = {
312+
methodId: foundMethod[0],
313+
methodName: remasterOrCleanMethodString(foundMethodString) + " [NOT IMPLEMENTED]",
314+
data: {
315+
error: "Proto not implemented"
316+
},
317+
};
318+
}
319+
}
320+
}
321+
322+
if (!methodFound && returnObject === "Not Found") {
323+
returnObject = {
324+
methodId: method.toString(),
325+
methodName: `Unknown Method ${method} [UNKNOWN]`,
326+
data: {
327+
error: "Unknown method ID"
328+
},
329+
};
330+
}
331+
212332
return returnObject;
213333
};

0 commit comments

Comments
 (0)