├── .gitignore ├── LICENSE ├── README.md ├── SteelSeries.ApiGetter ├── README.md ├── SteelSeries.ApiGetter.sln ├── SteelSeries.ApiGetter │ ├── Program.cs │ ├── Properties │ │ └── launchSettings.json │ ├── SteelSeries.ApiGetter.csproj │ ├── SwaggerExtensions.cs │ ├── appsettings.Development.json │ └── appsettings.json └── global.json ├── image-1.png ├── initializing ├── 1.sock.txt ├── 2.configs.default.txt ├── 3.audioDevices.txt ├── 4.configs.txt ├── 5.configs.selected.txt ├── 6.onboarding.txt ├── 7.chatMix.txt ├── 8.mode.txt └── 9.volumeSettings.txt ├── openapi └── swagger.json └── volumeSettings ├── classic ├── mute.txt └── volume.txt └── streamer ├── monitoring.mute.txt ├── monitoring.volume.txt ├── streamer.volume.txt └── streaming.mute.txt /.gitignore: -------------------------------------------------------------------------------- 1 | /cert.crt 2 | /*.pcapng 3 | /decrypted 4 | /tls 5 | SteelSeries.ApiGetter/.idea/ 6 | bin/ 7 | obj/ -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Permission is hereby granted, free of charge, to any person obtaining 2 | a copy of this software and associated documentation files (the 3 | "Software"), to deal in the Software without restriction, including 4 | without limitation the rights to use, copy, modify, merge, publish, 5 | distribute, sublicense, and/or sell copies of the Software, and to 6 | permit persons to whom the Software is furnished to do so, subject to 7 | the following conditions: 8 | 9 | The above copyright notice and this permission notice shall be included 10 | in all copies or substantial portions of the Software. 11 | 12 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 13 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 14 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 15 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 16 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 17 | TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 18 | SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # SteelSeries GG - Sonar API reverse engineering 2 | 3 | Sonar is using HTTP REST API to control Sonar backend service. 4 | 5 | On my first reverse engineering session request destinated to port 10430. 6 | Based on previous GameSense-documentation and educated guess the port will be randomized every time the application starts. 7 | 8 | **Port changed to 10749 after restart - confirmed** 9 | 10 | --- 11 | 12 | C:\ProgramData\SteelSeries\GG contains some interesting files: 13 | - coreProps.json 14 | - GameSense address 15 | - Two unknown endpoints (encrypted) 16 | - encryptedAddress -> Wireshark + premaster secret log 17 | - ggEncryptedAddress -> Wireshark + premaster secret log 18 | - db/database.db 19 | - Nothing useful 20 | - apps/sonar/db/database.db 21 | - Easy alternative to fetch KVP-settings / configs? 22 | 23 | --- 24 | 25 | ## Wireshark TLS decryption results 26 | 27 | Used Wireshark with premaster secret log enabled and ...**BOOM**! 28 | 29 | HTTP GET with TLS v1.2 to ggEncryptedAddress' /subApps responds with stuff we really need. 30 | 31 | ```json 32 | { 33 | "subApps": { 34 | "engine": { 35 | "name": "engine", 36 | "isEnabled": true, 37 | "isReady": true, 38 | "isRunning": true, 39 | "shouldAutoStart": true, 40 | "isWindowsSupported": true, 41 | "isMacSupported": true, 42 | "toggleViaSettings": false, 43 | "metadata": { 44 | "encryptedWebServerAddress": "127.0.0.1:12261", 45 | "webServerAddress": "127.0.0.1:12260" 46 | }, 47 | "secretMetadata": { 48 | "encryptedWebServerAddressCertText": "REMOVED FOR REASONS" 49 | } 50 | }, 51 | "sonar": { 52 | "name": "sonar", 53 | "isEnabled": true, 54 | "isReady": false, 55 | "isRunning": true, 56 | "shouldAutoStart": true, 57 | "isWindowsSupported": true, 58 | "isMacSupported": false, 59 | "toggleViaSettings": true, 60 | "metadata": { 61 | "encryptedWebServerAddress": "", 62 | "webServerAddress": "http://localhost:12268" 63 | }, 64 | "secretMetadata": { 65 | "encryptedWebServerAddressCertText": "" 66 | } 67 | }, 68 | "threeDAT": { 69 | "name": "threeDAT", 70 | "isEnabled": true, 71 | "isReady": false, 72 | "isRunning": false, 73 | "shouldAutoStart": false, 74 | "isWindowsSupported": true, 75 | "isMacSupported": false, 76 | "toggleViaSettings": false, 77 | "metadata": { 78 | "encryptedWebServerAddress": "", 79 | "webServerAddress": "" 80 | }, 81 | "secretMetadata": { 82 | "encryptedWebServerAddressCertText": "" 83 | } 84 | } 85 | } 86 | } 87 | ``` 88 | 89 | Ok, `subApps.sonar.metadata.webServerAddress` is what we need. 90 | 91 | Certificate is self-signed and valid for a year. Based on certificate validity times I guess it is also regenerated every runtime. 92 | 93 | --- 94 | 95 | Just to test it again, I ran 96 | 97 | ``` 98 | > curl https://127.0.0.1:6327/subApps -k 99 | ``` 100 | ```json 101 | {"subApps": {"engine":{"name":"engine","isEnabled":true,"isReady":true,"isRunning":true,"shouldAutoStart":true,"isWindowsSupported":true,"isMacSupported":true,"toggleViaSettings":false,"metadata":{"encryptedWebServerAddress":"127.0.0.1:12261","webServerAddress":"127.0.0.1:12260"},"secretMetadata":{"encryptedWebServerAddressCertText":"REMOVED FO REASONS"}},"sonar":{"name":"sonar","isEnabled":true,"isReady":true,"isRunning":true,"shouldAutoStart":true,"isWindowsSupported":true,"isMacSupported":false,"toggleViaSettings":true,"metadata":{"encryptedWebServerAddress":"","webServerAddress":"http://localhost:12268"},"secretMetadata":{"encryptedWebServerAddressCertText":""}},"threeDAT":{"name":"threeDAT","isEnabled":true,"isReady":false,"isRunning":false,"shouldAutoStart":false,"isWindowsSupported":true,"isMacSupported":false,"toggleViaSettings":false,"metadata":{"encryptedWebServerAddress":"","webServerAddress":""},"secretMetadata":{"encryptedWebServerAddressCertText":""}}}} 102 | ``` 103 | 104 | So this is how we get the connection information. 105 | 106 | --- 107 | 108 | Two subfolders have some raw captures from plain HTTP traffic between Sonar & backend (`initializing/` and `volumeSettings/`). 109 | 110 | These examples can be used to do some basic volume controlling over API. 111 | 112 | --- 113 | 114 | To capture more just run Wireshark with port filtering 115 | `tcp.port == ####` where `####` is port number from `subApps.sonar.metadata.webServerAddress`. 116 | 117 | ## Some examples 118 | 119 | Mute/unmute classic-mode Game-channel 120 | ``` 121 | curl -X PUT http://127.0.0.1:13108/volumeSettings/classic/game/Mute/true -H "Content-Length: 0" -H "Host: localhost:13108" 122 | curl -X PUT http://127.0.0.1:13108/volumeSettings/classic/game/Mute/false -H "Content-Length: 0" -H "Host: localhost:13108" 123 | ``` 124 | 125 | Set classic-mode Game-channel volume to 100% 126 | ``` 127 | curl -X PUT http://127.0.0.1:13108/volumeSettings/classic/game/Volume/1.00 -H "Content-Length: 0" -H "Host: localhost:13108" 128 | ``` 129 | 130 | ## How to get that certificate (in Sonar) 131 | 132 | ![TrustSteelSeriesCertificates()](image-1.png) 133 | 134 | Seems like we are connecting to `ggEncryptedAddress` and trusting the certificate there. (I am not 100% sure since code is using somekind of DI with C# and had literally 5 minutes to check out the Sonar's end.) 135 | 136 | ## Future? 137 | 138 | I started to reverse engineer this stuff for a plugin to Elgato Streamdeck. 139 | 140 | Unfortunaly I am already using Elgato Wavelink, so the priority of this project has fallen really low. 141 | 142 | I hope by releasing this information someone will see the effort to create the plugin! -------------------------------------------------------------------------------- /SteelSeries.ApiGetter/README.md: -------------------------------------------------------------------------------- 1 | # C# solution for getting openapi configuration of sonar 2 | 3 | ## Launch: 4 | * You need .net 8 SDK installed 5 | * In SteelSeries.ApiGetter/SteelSeries.ApiGetter.csproj change SonarAssembliesPath. 6 | It should be path for Sonar *.dll files, like "Sonar.Logging.dll", "SoundStage.Api.dll", "SoundStage.AudioRepository.dll", "SoundStage.IoC", "SoundStage.Models", "SoundStage.Services" 7 | * run in terminal dotnet run 8 | * Now you can use swagger UI at http://localhost:{pot-from-terminal}/swagger 9 | * And json file generated at /openapi 10 | -------------------------------------------------------------------------------- /SteelSeries.ApiGetter/SteelSeries.ApiGetter.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SteelSeries.ApiGetter", "SteelSeries.ApiGetter\SteelSeries.ApiGetter.csproj", "{9D5FDAEB-1A02-455A-A59D-791B74940640}" 4 | EndProject 5 | Global 6 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 7 | Debug|Any CPU = Debug|Any CPU 8 | Release|Any CPU = Release|Any CPU 9 | EndGlobalSection 10 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 11 | {9D5FDAEB-1A02-455A-A59D-791B74940640}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 12 | {9D5FDAEB-1A02-455A-A59D-791B74940640}.Debug|Any CPU.Build.0 = Debug|Any CPU 13 | {9D5FDAEB-1A02-455A-A59D-791B74940640}.Release|Any CPU.ActiveCfg = Release|Any CPU 14 | {9D5FDAEB-1A02-455A-A59D-791B74940640}.Release|Any CPU.Build.0 = Release|Any CPU 15 | EndGlobalSection 16 | EndGlobal 17 | -------------------------------------------------------------------------------- /SteelSeries.ApiGetter/SteelSeries.ApiGetter/Program.cs: -------------------------------------------------------------------------------- 1 | using SoundStage.IoC.Extensions; 2 | using SteelSeries.ApiGetter; 3 | 4 | var builder = WebApplication.CreateBuilder(args); 5 | 6 | // Add services to the container. 7 | // Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle 8 | builder.Services.AddEndpointsApiExplorer(); 9 | builder.Services.AddSwaggerGen(); 10 | 11 | builder.Services.AddSonarByConventions() 12 | .AddControllers(); 13 | 14 | var app = builder.Build(); 15 | 16 | // Configure the HTTP request pipeline. 17 | if (app.Environment.IsDevelopment()) 18 | { 19 | app.UseSwagger(); 20 | app.UseSwaggerUI(); 21 | } 22 | 23 | app.UseHttpsRedirection(); 24 | app.Services.SaveSwaggerJson(); 25 | 26 | await app.RunAsync(); 27 | -------------------------------------------------------------------------------- /SteelSeries.ApiGetter/SteelSeries.ApiGetter/Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://json.schemastore.org/launchsettings.json", 3 | "iisSettings": { 4 | "windowsAuthentication": false, 5 | "anonymousAuthentication": true, 6 | "iisExpress": { 7 | "applicationUrl": "http://localhost:39149", 8 | "sslPort": 44300 9 | } 10 | }, 11 | "profiles": { 12 | "http": { 13 | "commandName": "Project", 14 | "dotnetRunMessages": true, 15 | "launchBrowser": true, 16 | "launchUrl": "swagger", 17 | "applicationUrl": "http://localhost:5134", 18 | "environmentVariables": { 19 | "ASPNETCORE_ENVIRONMENT": "Development" 20 | } 21 | }, 22 | "https": { 23 | "commandName": "Project", 24 | "dotnetRunMessages": true, 25 | "launchBrowser": true, 26 | "launchUrl": "swagger", 27 | "applicationUrl": "https://localhost:7213;http://localhost:5134", 28 | "environmentVariables": { 29 | "ASPNETCORE_ENVIRONMENT": "Development" 30 | } 31 | }, 32 | "IIS Express": { 33 | "commandName": "IISExpress", 34 | "launchBrowser": true, 35 | "launchUrl": "swagger", 36 | "environmentVariables": { 37 | "ASPNETCORE_ENVIRONMENT": "Development" 38 | } 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /SteelSeries.ApiGetter/SteelSeries.ApiGetter/SteelSeries.ApiGetter.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | net8.0 5 | enable 6 | enable 7 | 8 | C:\Program Files\SteelSeries\GG\apps\sonar\ 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | $(SonarAssembliesPath)Sonar.Logging.dll 20 | 21 | 22 | $(SonarAssembliesPath)SoundStage.Api.dll 23 | 24 | 25 | $(SonarAssembliesPath)SoundStage.AudioRepository.dll 26 | 27 | 28 | $(SonarAssembliesPath)SoundStage.IoC.dll 29 | 30 | 31 | $(SonarAssembliesPath)SoundStage.Models.dll 32 | 33 | 34 | $(SonarAssembliesPath)SoundStage.Services.dll 35 | 36 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /SteelSeries.ApiGetter/SteelSeries.ApiGetter/SwaggerExtensions.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.OpenApi.Extensions; 2 | using Microsoft.OpenApi.Models; 3 | using Swashbuckle.AspNetCore.Swagger; 4 | 5 | namespace SteelSeries.ApiGetter; 6 | 7 | public static class SwaggerExtensions 8 | { 9 | public static void SaveSwaggerJson(this IServiceProvider provider) 10 | { 11 | ISwaggerProvider sw = provider.GetRequiredService(); 12 | OpenApiDocument doc = sw.GetSwagger("v1", null, "/"); 13 | string swaggerFile = doc.SerializeAsJson(Microsoft.OpenApi.OpenApiSpecVersion.OpenApi3_0); 14 | File.WriteAllText("../../openapi/swagger.json", swaggerFile); 15 | } 16 | } -------------------------------------------------------------------------------- /SteelSeries.ApiGetter/SteelSeries.ApiGetter/appsettings.Development.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Information", 5 | "Microsoft.AspNetCore": "Warning" 6 | } 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /SteelSeries.ApiGetter/SteelSeries.ApiGetter/appsettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Information", 5 | "Microsoft.AspNetCore": "Warning" 6 | } 7 | }, 8 | "AllowedHosts": "*", 9 | "SteelSeriesAssembliesPath":"" 10 | } 11 | -------------------------------------------------------------------------------- /SteelSeries.ApiGetter/global.json: -------------------------------------------------------------------------------- 1 | { 2 | "sdk": { 3 | "version": "8.0.0", 4 | "rollForward": "latestMinor", 5 | "allowPrerelease": false 6 | } 7 | } -------------------------------------------------------------------------------- /image-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wex/sonar-rev/f1f4201750d4092a654aaa6cf72ab022fdbe2059/image-1.png -------------------------------------------------------------------------------- /initializing/1.sock.txt: -------------------------------------------------------------------------------- 1 | ` 2 | xµ(Ä(¾mãQhÈP'öÏ:GET /sock HTTP/1.1 3 | Host: localhost:10430 4 | Connection: Upgrade 5 | Pragma: no-cache 6 | Cache-Control: no-cache 7 | User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) steelseries-gg-client/3.0.0 Chrome/110.0.5481.179 Electron/23.1.3 Safari/537.36 8 | Upgrade: websocket 9 | Origin: file:// 10 | Sec-WebSocket-Version: 13 11 | Accept-Encoding: gzip, deflate, br 12 | Accept-Language: en-US 13 | Sec-WebSocket-Key: xyXoB/9HjdRO1qHIkhBVVw== 14 | Sec-WebSocket-Extensions: permessage-deflate; client_max_window_bits 15 | 16 | ` [ ú(¾(ÄhÈmåVP'öÃ,HTTP/1.1 101 Switching Protocols 17 | Upgrade: websocket 18 | Server: Microsoft-HTTPAPI/2.0 19 | X-Content-Type-Options: nosniff 20 | Connection: Upgrade 21 | Sec-WebSocket-Accept: jABRFTHBKbBuUZPy8klvBTkz7mw= 22 | Date: Tue, 25 Jul 2023 22:20:47 GMT 23 | 24 | -------------------------------------------------------------------------------- /initializing/2.configs.default.txt: -------------------------------------------------------------------------------- 1 | ` £H(Å(¾Ú.C õo