├── .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 | 
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