Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/mumble-voip/mumble.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDavide Beatrici <git@davidebeatrici.dev>2020-11-11 07:21:02 +0300
committerDavide Beatrici <git@davidebeatrici.dev>2020-11-11 07:25:56 +0300
commit2feebe68018770b8565eaa3d13b97baceadf2974 (patch)
treeb6ef80da5ac7b0ee18a25d587c4825c6ebbbb917 /plugins
parent8542c9a8574e52873c0aac53348a515d84c6b617 (diff)
FEAT(positional-audio): Add plugin for Among Us
Tested with v2020.10.22s and v2020.09.22s. Unless the pattern we're searching for becomes invalid or the structures we're using change, the plugin should keep working.
Diffstat (limited to 'plugins')
-rw-r--r--plugins/amongus/CMakeLists.txt19
-rw-r--r--plugins/amongus/Game.cpp84
-rw-r--r--plugins/amongus/Game.h41
-rw-r--r--plugins/amongus/amongus.cpp123
-rw-r--r--plugins/amongus/structs.h253
5 files changed, 520 insertions, 0 deletions
diff --git a/plugins/amongus/CMakeLists.txt b/plugins/amongus/CMakeLists.txt
new file mode 100644
index 000000000..3cae1336a
--- /dev/null
+++ b/plugins/amongus/CMakeLists.txt
@@ -0,0 +1,19 @@
+# Copyright 2020 The Mumble Developers. All rights reserved.
+# Use of this source code is governed by a BSD-style license
+# that can be found in the LICENSE file at the root of the
+# Mumble source tree or at <https://www.mumble.info/LICENSE>.
+
+add_library(amongus SHARED
+ "amongus.cpp"
+ "Game.cpp"
+
+ "../Module.cpp"
+ "../Process.cpp"
+ "../ProcessWindows.cpp"
+)
+
+if(WIN32)
+ target_sources(amongus PRIVATE "../HostWindows.cpp")
+else()
+ target_sources(amongus PRIVATE "../HostLinux.cpp")
+endif()
diff --git a/plugins/amongus/Game.cpp b/plugins/amongus/Game.cpp
new file mode 100644
index 000000000..9c7b2e261
--- /dev/null
+++ b/plugins/amongus/Game.cpp
@@ -0,0 +1,84 @@
+// Copyright 2020 The Mumble Developers. All rights reserved.
+// Use of this source code is governed by a BSD-style license
+// that can be found in the LICENSE file at the root of the
+// Mumble source tree or at <https://www.mumble.info/LICENSE>.
+
+#include "Game.h"
+
+#include "mumble_plugin_utils.h"
+
+Game::Game(const procid_t id, const std::string name) : m_ok(false), m_proc(id, name) {
+ if (!m_proc.isOk()) {
+ return;
+ }
+
+ const auto &modules = m_proc.modules();
+ const auto iter = modules.find("GameAssembly.dll");
+ if (iter == modules.cend()) {
+ return;
+ }
+
+ // 74 89 jz short loc_????????
+ // A1 ?? ?? ?? ?? mov eax, AmongUsClient_c **
+ // 8B 40 5C mov eax, [eax+5Ch]
+ const std::vector< uint8_t > clientPattern = { 0x74, 0x39, 0xA1, '?', '?', '?', '?', 0x8B, 0x40, 0x5C };
+ m_client = m_proc.findPattern(clientPattern, iter->second);
+ if (!m_client) {
+ return;
+ }
+
+ // +3 in order to skip to the memory address we actually care about
+ m_client = m_proc.peekPtr(m_proc.peekPtr(m_client + 3));
+ if (!m_client) {
+ return;
+ }
+
+ const auto clientC = m_proc.peek< AmongUsClient_c >(m_client);
+ if (!clientC.staticFields) {
+ return;
+ }
+
+ const auto clientStaticFields = m_proc.peek< AmongUsClient_StaticFields >(clientC.staticFields);
+ if (!clientStaticFields.instance) {
+ return;
+ }
+
+ m_client = clientStaticFields.instance;
+
+ const auto fields = clientFields();
+
+ const auto playerControlO = m_proc.peek< PlayerControl_o >(fields.playerPrefab);
+ if (!playerControlO.klass) {
+ return;
+ }
+
+ const auto playerControlC = m_proc.peek< PlayerControl_c >(playerControlO.klass);
+ if (!playerControlC.staticFields) {
+ return;
+ }
+
+ m_playerControlStaticFields = playerControlC.staticFields;
+
+ m_ok = true;
+}
+
+PlayerControl_Fields Game::playerControlFields() {
+ const auto playerControlStaticFields = m_proc.peek< PlayerControl_StaticFields >(m_playerControlStaticFields);
+ if (playerControlStaticFields.localPlayer) {
+ return m_proc.peek< PlayerControl_o >(playerControlStaticFields.localPlayer).fields;
+ }
+
+ return {};
+}
+
+std::string Game::string(const procptr_t address) {
+ const auto object = m_proc.peek< String_o >(address);
+
+ std::u16string string;
+ string.resize(object.fields.length);
+ if (m_proc.peek(address + sizeof(object), &string[0], sizeof(char16_t) * string.size())) {
+ return utf16ToUtf8(string.data());
+ }
+
+ return {};
+}
diff --git a/plugins/amongus/Game.h b/plugins/amongus/Game.h
new file mode 100644
index 000000000..cee8fed53
--- /dev/null
+++ b/plugins/amongus/Game.h
@@ -0,0 +1,41 @@
+// Copyright 2020 The Mumble Developers. All rights reserved.
+// Use of this source code is governed by a BSD-style license
+// that can be found in the LICENSE file at the root of the
+// Mumble source tree or at <https://www.mumble.info/LICENSE>.
+
+#ifndef AMONGUS_GAME
+#define AMONGUS_GAME
+
+#include "structs.h"
+
+#include "ProcessWindows.h"
+
+class Game {
+protected:
+ bool m_ok;
+ ptr_t m_client;
+ ptr_t m_playerControlStaticFields;
+ ProcessWindows m_proc;
+
+public:
+ inline bool isOk() const { return m_ok; }
+
+ inline AmongUsClient_Fields clientFields() const { return m_proc.peek< AmongUsClient_o >(m_client).fields; }
+
+ inline GameData_PlayerInfo_Fields playerInfoFields(const ptr_t cachedData) const {
+ return m_proc.peek< GameData_PlayerInfo_o >(cachedData).fields;
+ }
+
+ inline UnityEngine_Vector2_Fields playerPosition(const ptr_t netTransform) const {
+ const auto networkTransform = m_proc.peek< CustomNetworkTransform_o >(netTransform);
+ return networkTransform.fields.prevPosSent.fields;
+ }
+
+ PlayerControl_Fields playerControlFields();
+
+ std::string string(const procptr_t address);
+
+ Game(const procid_t id, const std::string name);
+};
+
+#endif
diff --git a/plugins/amongus/amongus.cpp b/plugins/amongus/amongus.cpp
new file mode 100644
index 000000000..f92ffeb37
--- /dev/null
+++ b/plugins/amongus/amongus.cpp
@@ -0,0 +1,123 @@
+// Copyright 2020 The Mumble Developers. All rights reserved.
+// Use of this source code is governed by a BSD-style license
+// that can be found in the LICENSE file at the root of the
+// Mumble source tree or at <https://www.mumble.info/LICENSE>.
+
+#include "Game.h"
+
+#include "mumble_plugin.h"
+#include "mumble_plugin_utils.h"
+
+#include <sstream>
+
+std::unique_ptr< Game > game;
+
+static inline bool inGame(const AmongUsClient_Fields &fields) {
+ if (fields.gameMode != GameMode::FreePlay && fields.gameState != GameState::NotJoined) {
+ return true;
+ }
+
+ return false;
+}
+
+static int fetch(float *avatarPos, float *avatarFront, float *avatarTop, float *cameraPos, float *cameraFront,
+ float *cameraTop, std::string &context, std::wstring &identity) {
+ for (uint8_t i = 0; i < 3; ++i) {
+ avatarPos[i] = avatarFront[i] = avatarTop[i] = cameraPos[i] = cameraFront[i] = cameraTop[i] = 0.f;
+ }
+
+ const auto fields = game->clientFields();
+
+ if (!inGame(fields)) {
+ return false;
+ }
+
+ if (fields.gameState == GameState::Ended) {
+ return true;
+ }
+
+ const auto playerControlFields = game->playerControlFields();
+
+ // Mumble | Game
+ // X | X
+ // Y | -
+ // Z | Y
+ const auto position = game->playerPosition(playerControlFields.netTransform);
+ avatarPos[0] = cameraPos[0] = position.x;
+ avatarPos[2] = cameraPos[2] = position.y;
+
+ // Context
+ std::ostringstream stream;
+ stream << " {\"Game ID\": " << fields.gameId << "}";
+ context = stream.str();
+
+ stream.str(std::string());
+ stream.clear();
+
+ // Identity
+ const auto playerFields = game->playerInfoFields(playerControlFields.cachedData);
+
+ stream << "Name: " << game->string(playerFields.playerName) << '\n';
+ stream << "Client ID: " << std::to_string(fields.clientId) << '\n';
+ stream << "Player ID: " << std::to_string(playerFields.playerId) << '\n';
+ stream << "Color ID: " << std::to_string(playerFields.colorId) << '\n';
+ stream << "Skin ID: " << std::to_string(playerFields.skinId) << '\n';
+ stream << "Hat ID: " << std::to_string(playerFields.hatId) << '\n';
+ stream << "Pet ID: " << std::to_string(playerFields.petId) << '\n';
+ stream << "Dead: " << (playerFields.isDead ? "true" : "false") << '\n';
+ stream << "Host ID: " << std::to_string(fields.hostId) << '\n';
+ stream << "Public game: " << (fields.isGamePublic ? "true" : "false");
+
+ identity = utf8ToUtf16(stream.str());
+
+ return true;
+}
+
+static int tryLock(const std::multimap< std::wstring, unsigned long long int > &pids) {
+ const std::string name = "Among Us.exe";
+ const auto id = Process::find(name, pids);
+ if (!id) {
+ return false;
+ }
+
+ game = std::make_unique<Game>(id, name);
+ if (!game->isOk()) {
+ game.reset();
+ return false;
+ }
+
+ const auto fields = game->clientFields();
+ if (!inGame(fields)) {
+ game.reset();
+ return false;
+ }
+
+ return true;
+}
+
+static const std::wstring longDesc() {
+ return std::wstring(L"Supports Among Us with context and identity support.");
+}
+
+static std::wstring description(L"Among Us");
+static std::wstring shortName(L"Among Us");
+
+static int tryLock1() {
+ return tryLock(std::multimap< std::wstring, unsigned long long int >());
+}
+
+static void nullUnlock() {
+}
+
+static MumblePlugin amongusPlug = { MUMBLE_PLUGIN_MAGIC, description, shortName, nullptr, nullptr, tryLock1,
+ nullUnlock, longDesc, fetch };
+
+static MumblePlugin2 amongusPlug2 = { MUMBLE_PLUGIN_MAGIC_2, MUMBLE_PLUGIN_VERSION, tryLock };
+
+extern "C" MUMBLE_PLUGIN_EXPORT MumblePlugin *getMumblePlugin() {
+ return &amongusPlug;
+}
+
+extern "C" MUMBLE_PLUGIN_EXPORT MumblePlugin2 *getMumblePlugin2() {
+ return &amongusPlug2;
+}
diff --git a/plugins/amongus/structs.h b/plugins/amongus/structs.h
new file mode 100644
index 000000000..5b0256d37
--- /dev/null
+++ b/plugins/amongus/structs.h
@@ -0,0 +1,253 @@
+#ifndef AMONGUS_STRUCTS
+#define AMONGUS_STRUCTS
+
+#include <cstdint>
+
+typedef uint32_t ptr_t;
+typedef int32_t sptr_t;
+
+typedef ptr_t il2cpp_array_size_t; // Originally uintptr_t
+
+enum class DisconnectReason {
+ ExitGame = 0x0,
+ GameFull = 0x1,
+ GameStarted = 0x2,
+ GameNotFound = 0x3,
+ IncorrectVersion = 0x5,
+ Banned = 0x6,
+ Kicked = 0x7,
+ Custom = 0x8,
+ InvalidName = 0x9,
+ Hacking = 0xA,
+ Destroy = 0x10,
+ Error = 0x11,
+ IncorrectGame = 0x12,
+ ServerRequest = 0x13,
+ ServerFull = 0x14,
+ IntentionalLeaving = 0xD0,
+ FocusLostBackground = 0xCF,
+ FocusLost = 0xD1,
+ NewConnection = 0xD2
+};
+
+enum class DiscoverState { Off, Broadcast };
+
+enum class GameMode { LocalGame, OnlineGame, FreePlay };
+
+enum class GameState { NotJoined, Joined, Started, Ended };
+
+enum class Mode { None, Client, HostAndClient };
+
+struct Il2CppType {
+ ptr_t data;
+ uint32_t bits;
+};
+
+struct Il2CppClass_1 {
+ ptr_t image;
+ ptr_t gcDesc;
+ ptr_t name;
+ ptr_t namespaze;
+ Il2CppType byvalArg;
+ Il2CppType thisArg;
+ ptr_t elementClass;
+ ptr_t castClass;
+ ptr_t declaringType;
+ ptr_t parent;
+ ptr_t genericClass;
+ ptr_t typeDefinition;
+ ptr_t interopData;
+ ptr_t klass;
+ ptr_t fields;
+ ptr_t events;
+ ptr_t properties;
+ ptr_t methods;
+ ptr_t nestedTypes;
+ ptr_t implementedInterfaces;
+ ptr_t interfaceOffsets;
+};
+
+struct UnityEngine_Vector2_Fields {
+ float x;
+ float y;
+};
+
+struct UnityEngine_Vector2_o {
+ UnityEngine_Vector2_Fields fields;
+};
+
+struct String_Fields {
+ int32_t length;
+ // char16_t string[];
+};
+
+struct String_o {
+ ptr_t klass;
+ ptr_t monitor;
+ String_Fields fields;
+};
+
+struct UnityEngine_Object_Fields {
+ sptr_t cachedPtr;
+};
+
+struct InnerNet_InnerNetClient_Fields : UnityEngine_Object_Fields {
+ float minSendInterval;
+ uint32_t netIdCnt;
+ float timer;
+ ptr_t spawnableObjects;
+ bool inOnlineScene;
+ ptr_t destroyedObjects;
+ ptr_t allObjects;
+ ptr_t allObjectsFast;
+ ptr_t streams;
+ ptr_t networkAddress;
+ int32_t networkPort;
+ ptr_t connection;
+ Mode mode;
+ int32_t gameId;
+ int32_t hostId;
+ int32_t clientId;
+ ptr_t allClients;
+ DisconnectReason lastDisconnectReason;
+ ptr_t lastCustomDisconnect;
+ ptr_t preSpawnDispatcher;
+ ptr_t dispatcher;
+ bool isGamePublic;
+ GameState gameState;
+ ptr_t tempQueue;
+ bool appPaused;
+};
+
+struct InnerNet_InnerNetObject_Fields : UnityEngine_Object_Fields {
+ uint32_t spawnId;
+ uint32_t netId;
+ uint32_t dirtyBits;
+ uint8_t spawnFlags;
+ uint8_t sendMode;
+ int32_t ownerId;
+ bool despawnOnDestroy;
+};
+
+struct CustomNetworkTransform_Fields : InnerNet_InnerNetObject_Fields {
+ ptr_t xRange;
+ ptr_t yRange;
+ float sendInterval;
+ float snapThreshold;
+ float interpolateMovement;
+ ptr_t body;
+ UnityEngine_Vector2_o targetSyncPosition;
+ UnityEngine_Vector2_o targetSyncVelocity;
+ uint16_t lastSequenceId;
+ UnityEngine_Vector2_o prevPosSent;
+ UnityEngine_Vector2_o prevVelSent;
+};
+
+struct CustomNetworkTransform_o {
+ ptr_t klass;
+ ptr_t monitor;
+ CustomNetworkTransform_Fields fields;
+};
+
+struct AmongUsClient_Fields : InnerNet_InnerNetClient_Fields {
+ int32_t autoOpenStore;
+ GameMode gameMode;
+ ptr_t onlineScene;
+ ptr_t mainMenuScene;
+ ptr_t gameDataPrefab;
+ ptr_t playerPrefab;
+ ptr_t shipPrefabs;
+ int32_t tutorialMapId;
+ float spawnRadius;
+ DiscoverState discoverState;
+ ptr_t disconnectHandlers;
+ ptr_t gameListHandlers;
+};
+
+struct AmongUsClient_StaticFields {
+ ptr_t instance;
+};
+
+struct AmongUsClient_c {
+ Il2CppClass_1 _1;
+ ptr_t staticFields;
+};
+
+struct AmongUsClient_o {
+ ptr_t klass;
+ ptr_t monitor;
+ AmongUsClient_Fields fields;
+};
+
+struct PlayerControl_Fields : InnerNet_InnerNetObject_Fields {
+ int32_t lastStartCounter;
+ uint8_t playerId;
+ float maxReportDistance;
+ bool moveable;
+ bool inVent;
+ ptr_t cachedData;
+ ptr_t footSteps;
+ ptr_t killSfx;
+ ptr_t killAnimations;
+ float killTimer;
+ int32_t remainingEmergencies;
+ ptr_t nameText;
+ ptr_t lightPrefab;
+ ptr_t myLight;
+ ptr_t collider;
+ ptr_t myPhysics;
+ ptr_t netTransform;
+ ptr_t currentPet;
+ ptr_t hatRenderer;
+ ptr_t myRend;
+ ptr_t hitBuffer;
+ ptr_t myTasks;
+ ptr_t scannerAnims;
+ ptr_t scannersImages;
+ ptr_t closest;
+ bool isNew;
+ ptr_t cache;
+ ptr_t itemsInRange;
+ ptr_t newItemsInRange;
+ uint8_t scannerCount;
+ bool infectedSet;
+};
+
+struct PlayerControl_StaticFields {
+ ptr_t localPlayer;
+ ptr_t gameOptions;
+ ptr_t allPlayers;
+};
+
+struct PlayerControl_c {
+ Il2CppClass_1 _1;
+ ptr_t staticFields;
+};
+
+struct PlayerControl_o {
+ ptr_t klass;
+ ptr_t monitor;
+ PlayerControl_Fields fields;
+};
+
+struct GameData_PlayerInfo_Fields {
+ uint8_t playerId;
+ ptr_t playerName;
+ uint8_t colorId;
+ uint32_t hatId;
+ uint32_t petId;
+ uint32_t skinId;
+ bool disconnected;
+ ptr_t tasks;
+ bool isImpostor;
+ bool isDead;
+ ptr_t object;
+};
+
+struct GameData_PlayerInfo_o {
+ ptr_t klass;
+ ptr_t monitor;
+ GameData_PlayerInfo_Fields fields;
+};
+
+#endif