Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,4 @@ dist
/src/config/config.json
/.vs/
/.idea/
/proto_samples/
8 changes: 7 additions & 1 deletion src/config/example.config.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,11 @@
"default_port": 8081,
"trafficlight_identifier": "AwesomeProtoSender",
"redirect_to_golbat_url": null,
"redirect_to_golbat_token": null
"redirect_to_golbat_token": null,
"sample_saving": {
"enabled": true,
"save_directory": "./proto_samples",
"max_samples_per_method": 1,
"endpoints": ["traffic", "golbat"]
}
}
30 changes: 22 additions & 8 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import http from "http";
import fs from "fs";
import { WebStreamBuffer, getIPAddress, handleData, moduleConfigIsAvailable, redirect_post_golbat } from "./utils";
import { decodePayload, decodePayloadTraffic } from "./parser/proto-parser";
import SampleSaver from "./utils/sample-saver";

// try looking if config file exists...
let config = require("./config/example.config.json");
Expand All @@ -14,6 +15,9 @@ const incomingProtoWebBufferInst = new WebStreamBuffer();
const outgoingProtoWebBufferInst = new WebStreamBuffer();
const portBind = config["default_port"];

// Initialize sample saver
const sampleSaver = config.sample_saving ? new SampleSaver(config.sample_saving) : null;

// server
const httpServer = http.createServer(function (req, res) {
let incomingData: Array<Buffer> = [];
Expand Down Expand Up @@ -44,11 +48,25 @@ const httpServer = http.createServer(function (req, res) {
}
const identifier = parsedData['username'];
for (let i = 0; i < parsedData['contents'].length; i++) {
const rawRequest = parsedData['contents'][i].request || "";
const rawResponse = parsedData['contents'][i].payload || "";

const parsedRequestData = decodePayloadTraffic(
parsedData['contents'][i].type,
parsedData['contents'][i].request,
rawRequest,
"request"
);
const parsedResponseData = decodePayloadTraffic(
parsedData['contents'][i].type,
rawResponse,
"response"
);

// Save sample if enabled
if (sampleSaver && parsedRequestData.length > 0 && parsedResponseData.length > 0) {
sampleSaver.savePair(parsedRequestData[0], parsedResponseData[0], rawRequest, rawResponse, "golbat");
}

if (typeof parsedRequestData === "string") {
incomingProtoWebBufferInst.write({ error: parsedRequestData });
} else {
Expand All @@ -57,11 +75,7 @@ const httpServer = http.createServer(function (req, res) {
incomingProtoWebBufferInst.write(parsedObject);
}
}
const parsedResponseData = decodePayloadTraffic(
parsedData['contents'][i].type,
parsedData['contents'][i].payload,
"response"
);

if (typeof parsedResponseData === "string") {
outgoingProtoWebBufferInst.write({ error: parsedResponseData });
} else {
Expand All @@ -85,10 +99,10 @@ const httpServer = http.createServer(function (req, res) {
res.end("");
if (Array.isArray(parsedData)) {
for (let i = 0; i < parsedData.length; i++) {
handleData(incomingProtoWebBufferInst, outgoingProtoWebBufferInst, identifier, parsedData[i])
handleData(incomingProtoWebBufferInst, outgoingProtoWebBufferInst, identifier, parsedData[i], sampleSaver)
}
} else {
handleData(incomingProtoWebBufferInst, outgoingProtoWebBufferInst, identifier, parsedData)
handleData(incomingProtoWebBufferInst, outgoingProtoWebBufferInst, identifier, parsedData, sampleSaver)
}
});
break;
Expand Down
86 changes: 66 additions & 20 deletions src/parser/proto-parser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,35 +2,22 @@ import { b64Decode } from "../utils";
import { requestMessagesResponses } from "../constants";
import { DecodedProto } from "../types";

// For decode dynamics action social.
let action_social = 0;
/**
* Callback as used by {@link DecoderInternalPayloadAsResponse}.
* @type {function}
* @param {number|any}
*/
/**
* Returns decoded proto as JSON. Uses Tuples by https://github.com/Furtif/pogo-protos/blob/master/test/test.js, if that implemented.
*/
function DecoderInternalPayloadAsResponse(method: number, data: any): any {
// Reset value.
action_social = 0;
let proto_tuple: any = Object.values(requestMessagesResponses)[method];
let result: any = { Not_Implemented_yet: data };

if (!data) {
return {};
}
for (let i = 0; i < Object.keys(requestMessagesResponses).length; i++) {
proto_tuple = Object.values(requestMessagesResponses)[i];
const my_req = proto_tuple[0];
if (my_req == method) {
if (proto_tuple[2] != null && b64Decode(data)) {
if (proto_tuple[2] != null && data && b64Decode(data).length > 0) {
try {
result = proto_tuple[2].decode(b64Decode(data)).toJSON();
/*
// This not need more because protos as replaced bytes for the proto.
if (method == 10010) {
let profile = POGOProtos.Rpc.PlayerPublicProfileProto.decode(b64Decode(result.friend[0].player.public_data)).toJSON();
result.friend[0].player.public_data = profile;
}
*/
}
catch (error) {
console.error(`Intenal ProxySocial decoder ${my_req} Error: ${error}`);
Expand Down Expand Up @@ -85,14 +72,22 @@ export const decodePayload = (contents: any, dataType: string): DecodedProto[] =

export const decodeProto = (method: number, data: string, dataType: string): DecodedProto | string => {
let returnObject: DecodedProto | string = "Not Found";
let methodFound = false;

for (let i = 0; i < Object.keys(requestMessagesResponses).length; i++) {
let foundMethod: any = Object.values(requestMessagesResponses)[i];
let foundMethodString: string = Object.keys(requestMessagesResponses)[i];
const foundReq = foundMethod[0];
if (foundReq == method) {
methodFound = true;
if (foundMethod[1] != null && dataType === "request") {
try {
let parsedData = foundMethod[1].decode(b64Decode(data)).toJSON();
let parsedData;
if (!data || data === "") {
parsedData = {};
} else {
parsedData = foundMethod[1].decode(b64Decode(data)).toJSON();
}
if (foundMethod[0] === 5012 || foundMethod[0] === 600005) {
action_social = parsedData.action;
Object.values(requestMessagesResponses).forEach(val => {
Expand All @@ -109,13 +104,35 @@ export const decodeProto = (method: number, data: string, dataType: string): Dec
};
} catch (error) {
console.error(`Error parsing request ${foundMethodString} -> ${error}`);
returnObject = {
methodId: foundMethod[0],
methodName: remasterOrCleanMethodString(foundMethodString) + " [PARSE ERROR]",
data: {
error: "Failed to decode proto",
rawBase64: data,
errorMessage: error.toString()
},
};
}
} else if (dataType === "request") {
console.warn(`Request ${foundMethod[0]} Not Implemented`)
returnObject = {
methodId: foundMethod[0],
methodName: remasterOrCleanMethodString(foundMethodString) + " [NOT IMPLEMENTED]",
data: {
error: "Proto not implemented",
rawBase64: data
},
};
}
if (foundMethod[2] != null && dataType === "response") {
try {
let parsedData = foundMethod[2].decode(b64Decode(data)).toJSON();
let parsedData;
if (!data || data === "") {
parsedData = {};
} else {
parsedData = foundMethod[2].decode(b64Decode(data)).toJSON();
}
if (foundMethod[0] === 5012 && action_social > 0 && parsedData.payload) {
parsedData.payload = DecoderInternalPayloadAsResponse(action_social, parsedData.payload);
}
Expand All @@ -129,11 +146,40 @@ export const decodeProto = (method: number, data: string, dataType: string): Dec
};
} catch (error) {
console.error(`Error parsing response ${foundMethodString} method: [${foundReq}] -> ${error}`);
returnObject = {
methodId: foundMethod[0],
methodName: remasterOrCleanMethodString(foundMethodString) + " [PARSE ERROR]",
data: {
error: "Failed to decode proto",
rawBase64: data,
errorMessage: error.toString()
},
};
}
} else if (dataType === "response") {
console.warn(`Response ${foundReq} Not Implemented`)
returnObject = {
methodId: foundMethod[0],
methodName: remasterOrCleanMethodString(foundMethodString) + " [NOT IMPLEMENTED]",
data: {
error: "Proto not implemented",
rawBase64: data
},
};
}
}
}

if (!methodFound && returnObject === "Not Found") {
returnObject = {
methodId: method.toString(),
methodName: `Unknown Method ${method} [UNKNOWN]`,
data: {
error: "Unknown method ID",
rawBase64: data
},
};
}

return returnObject;
};
2 changes: 1 addition & 1 deletion src/types/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,5 @@ export type DecodedProto = {
identifier?: string;
methodId: string;
methodName: string;
data: string;
data: any;
};
27 changes: 20 additions & 7 deletions src/utils/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@ import { WebStreamBuffer } from "./web-stream-buffer";
import { decodePayloadTraffic } from "../parser/proto-parser";

export const b64Decode = (data: string) => {
if (!data || data === "") {
return Buffer.alloc(0);
}
return Buffer.from(data, "base64");
};

Expand All @@ -30,13 +33,27 @@ export function getIPAddress() {
return '0.0.0.0';
}

export function handleData(incoming: WebStreamBuffer, outgoing: WebStreamBuffer, identifier: any, parsedData: string) {
export function handleData(incoming: WebStreamBuffer, outgoing: WebStreamBuffer, identifier: any, parsedData: string, sampleSaver?: any) {
for (let i = 0; i < parsedData['protos'].length; i++) {
const rawRequest = parsedData['protos'][i].request || "";
const rawResponse = parsedData['protos'][i].response || "";

const parsedRequestData = decodePayloadTraffic(
parsedData['protos'][i].method,
parsedData['protos'][i].request,
rawRequest,
"request"
);
const parsedResponseData = decodePayloadTraffic(
parsedData['protos'][i].method,
rawResponse,
"response"
);

// Save sample if enabled
if (sampleSaver && parsedRequestData.length > 0 && parsedResponseData.length > 0) {
sampleSaver.savePair(parsedRequestData[0], parsedResponseData[0], rawRequest, rawResponse, "traffic");
}

if (typeof parsedRequestData === "string") {
incoming.write({ error: parsedRequestData });
} else {
Expand All @@ -45,11 +62,7 @@ export function handleData(incoming: WebStreamBuffer, outgoing: WebStreamBuffer,
incoming.write(parsedObject);
}
}
const parsedResponseData = decodePayloadTraffic(
parsedData['protos'][i].method,
parsedData['protos'][i].response,
"response"
);

if (typeof parsedResponseData === "string") {
outgoing.write({ error: parsedResponseData });
} else {
Expand Down
Loading
Loading