Skip to content

Commit f8cc8c0

Browse files
committed
Initial commit
1 parent 5a341cb commit f8cc8c0

4 files changed

Lines changed: 450 additions & 0 deletions

File tree

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
2+
Microsoft Visual Studio Solution File, Format Version 12.00
3+
# Visual Studio 15
4+
VisualStudioVersion = 15.0.26430.16
5+
MinimumVisualStudioVersion = 10.0.40219.1
6+
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "UserPreferencesExplorer", "UserPreferencesExplorer\UserPreferencesExplorer.vcxproj", "{69E20B05-83B9-444C-ADF6-6E28C9B056B0}"
7+
EndProject
8+
Global
9+
GlobalSection(SolutionConfigurationPlatforms) = preSolution
10+
Debug|x64 = Debug|x64
11+
Debug|x86 = Debug|x86
12+
Release|x64 = Release|x64
13+
Release|x86 = Release|x86
14+
EndGlobalSection
15+
GlobalSection(ProjectConfigurationPlatforms) = postSolution
16+
{69E20B05-83B9-444C-ADF6-6E28C9B056B0}.Debug|x64.ActiveCfg = Debug|x64
17+
{69E20B05-83B9-444C-ADF6-6E28C9B056B0}.Debug|x64.Build.0 = Debug|x64
18+
{69E20B05-83B9-444C-ADF6-6E28C9B056B0}.Debug|x86.ActiveCfg = Debug|Win32
19+
{69E20B05-83B9-444C-ADF6-6E28C9B056B0}.Debug|x86.Build.0 = Debug|Win32
20+
{69E20B05-83B9-444C-ADF6-6E28C9B056B0}.Release|x64.ActiveCfg = Release|x64
21+
{69E20B05-83B9-444C-ADF6-6E28C9B056B0}.Release|x64.Build.0 = Release|x64
22+
{69E20B05-83B9-444C-ADF6-6E28C9B056B0}.Release|x86.ActiveCfg = Release|Win32
23+
{69E20B05-83B9-444C-ADF6-6E28C9B056B0}.Release|x86.Build.0 = Release|Win32
24+
EndGlobalSection
25+
GlobalSection(SolutionProperties) = preSolution
26+
HideSolutionNode = FALSE
27+
EndGlobalSection
28+
EndGlobal
Lines changed: 253 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,253 @@
1+
#include <vector>
2+
#include <string>
3+
#include <fstream>
4+
#include <iostream>
5+
#include <openssl/evp.h>
6+
#include <Windows.h>
7+
8+
#pragma comment(lib, "libeay32")
9+
10+
/// <summary>
11+
/// Reads all bytes from the specified path.
12+
/// </summary>
13+
/// <param name="path">Path to the file to read.</param>
14+
/// <returns>Container of sequential bytes or empty container on error.</returns>
15+
std::vector<uint8_t> ReadAllBytes(const std::string &path)
16+
{
17+
try
18+
{
19+
std::ifstream inStream(path, std::ios::binary | std::ios::ate);
20+
auto fileSize = (size_t)inStream.tellg();
21+
auto fileBytes = std::vector<uint8_t>(fileSize);
22+
23+
inStream.seekg(0, std::ios::beg);
24+
inStream.read((char *)&fileBytes[0], fileSize);
25+
inStream.close();
26+
27+
return fileBytes;
28+
}
29+
catch (const std::exception& ex)
30+
{
31+
std::cerr << "Failed to read file '" << path << "' - " << ex.what() << std::endl;
32+
return std::vector<uint8_t>();
33+
}
34+
}
35+
36+
/// <summary>
37+
/// Decrypts the specified cipherText into <paramref name="out_plaintext"/> using the given key and initialization vector.
38+
/// </summary>
39+
/// <param name="cipherText">Encrypted bytes to decrypt.</param>
40+
/// <param name="key">Key used for decryption.</param>
41+
/// <param name="initialization_vector">Initialization vector used for decryption.</param>
42+
/// <param name="out_plaintext">[out] Decrypted bytes.</param>
43+
/// <returns>True on success.</returns>
44+
bool Decrypt(
45+
const std::vector<uint8_t> &cipherText,
46+
const std::vector<uint8_t> &key,
47+
const std::vector<uint8_t> &initialization_vector,
48+
std::vector<uint8_t> &out_plaintext)
49+
{
50+
out_plaintext.resize(cipherText.size());
51+
int plaintext_length = 0;
52+
53+
auto cipher = EVP_aes_256_cbc();
54+
auto ctx = EVP_CIPHER_CTX_new();
55+
56+
EVP_CIPHER_CTX_init(ctx);
57+
58+
auto is_successful = EVP_DecryptInit_ex(ctx, cipher, nullptr, &key[0], &initialization_vector[0]);
59+
if (is_successful == false)
60+
{
61+
std::cerr << "EVP_DecryptInit_ex - FAILED" << std::endl;
62+
EVP_CIPHER_CTX_free(ctx);
63+
return false;
64+
}
65+
66+
is_successful = EVP_DecryptUpdate(ctx, &out_plaintext[0], &plaintext_length, &cipherText[0], (int)cipherText.size());
67+
if (is_successful == false)
68+
{
69+
std::cerr << "EVP_DecryptUpdate - FAILED" << std::endl;
70+
EVP_CIPHER_CTX_free(ctx);
71+
return false;
72+
}
73+
74+
auto additional_length = 0;
75+
is_successful = EVP_DecryptFinal_ex(ctx, &out_plaintext[plaintext_length], &additional_length);
76+
if (is_successful == false)
77+
{
78+
std::cerr << "EVP_DecryptFinal_ex - FAILED" << std::endl;
79+
EVP_CIPHER_CTX_free(ctx);
80+
return false;
81+
}
82+
83+
out_plaintext.resize(plaintext_length + additional_length);
84+
EVP_CIPHER_CTX_free(ctx);
85+
return true;
86+
}
87+
88+
/// <summary>
89+
/// Generates a key and initialization_vector out of the specified salt and source bytes.
90+
/// </summary>
91+
/// <param name="salt">Salt bytes used to generate the key and initialization vector.</param>
92+
/// <param name="source_bytes">Source bytes used to generate the key and initialization vector.</param>
93+
/// <param name="out_key">[out] Generated key.</param>
94+
/// <param name="out_initialization_vector">[out] Generated initialization vector.</param>
95+
/// <returns>True on success.</returns>
96+
bool GetKeys(
97+
const std::vector<uint8_t> &salt,
98+
const std::vector<uint8_t> &source_bytes,
99+
std::vector<uint8_t> &out_key,
100+
std::vector<uint8_t> &out_initialization_vector)
101+
{
102+
static const int kIterationCount = 5;
103+
104+
auto cipher = EVP_aes_256_cbc();
105+
auto md = EVP_sha1();
106+
107+
out_key.resize(cipher->key_len);
108+
out_initialization_vector.resize(cipher->iv_len);
109+
110+
auto result = EVP_BytesToKey(cipher, md, &salt[0], &source_bytes[0], (int)source_bytes.size(), kIterationCount, &out_key[0], &out_initialization_vector[0]);
111+
return result > 0;
112+
}
113+
114+
/// <summary>
115+
/// Mangles data for use as source bytes when generating a key and initialization vector.
116+
/// </summary>
117+
/// <param name="data_to_mangle">Bytes to mangle.</param>
118+
/// <returns>Collection of mangled bytes.</returns>
119+
std::vector<uint8_t> MangleData(const std::vector<uint8_t> &data_to_mangle)
120+
{
121+
auto mangled_data = std::vector<uint8_t>(data_to_mangle);
122+
const auto kMangledDataSize = mangled_data.size();
123+
124+
for(size_t index = 0; index < kMangledDataSize; ++index)
125+
{
126+
auto mangled_character = ((index + 2) * mangled_data[index]) % 128;
127+
if (mangled_character != 0)
128+
{
129+
mangled_data[index] = (uint8_t)mangled_character;
130+
}
131+
}
132+
133+
return mangled_data;
134+
}
135+
136+
/// <summary>
137+
/// Gets the machine GUID.
138+
/// </summary>
139+
/// <returns>The machine GUID on success or an empty string on failure.</returns>
140+
std::string GetMachineGuid()
141+
{
142+
static const auto kMachineGuidPath = std::string("SOFTWARE\\Microsoft\\Cryptography");
143+
static const auto kMachineGuidName = std::string("MachineGuid");
144+
145+
HKEY key_handle;
146+
147+
auto result = RegOpenKeyExA(HKEY_LOCAL_MACHINE, kMachineGuidPath.c_str(), NULL, KEY_READ | KEY_WOW64_64KEY, &key_handle);
148+
if (result != ERROR_SUCCESS)
149+
{
150+
RegCloseKey(key_handle);
151+
std::cerr << "RegOpenKeyEx - FAILED" << std::endl;
152+
return "";
153+
}
154+
155+
DWORD buff_size = 0;
156+
result = RegQueryValueExA(key_handle, kMachineGuidName.c_str(), nullptr, nullptr, nullptr, &buff_size);
157+
if (result != ERROR_SUCCESS)
158+
{
159+
RegCloseKey(key_handle);
160+
std::cerr << "RegQueryValueEx - FAILED\n" << std::endl;
161+
return "";
162+
}
163+
164+
auto machine_guid = std::vector<uint8_t>(buff_size);
165+
result = RegQueryValueExA(key_handle, kMachineGuidName.c_str(), nullptr, nullptr, &machine_guid[0], &buff_size);
166+
if (result != ERROR_SUCCESS)
167+
{
168+
RegCloseKey(key_handle);
169+
std::cerr << "RegQueryValueEx - FAILED\n" << std::endl;
170+
return "";
171+
}
172+
173+
RegCloseKey(key_handle);
174+
175+
return std::string(machine_guid.begin(), machine_guid.end());
176+
}
177+
178+
/// <summary>
179+
/// Gets the path to the user's UserPreferences.bag.
180+
/// </summary>
181+
/// <returns>Path to the user's UserPreferences.bag.</returns>
182+
std::string GetPathToUserPreferencesBag()
183+
{
184+
static const auto kDefaultUserPreferencesPath = std::string("\\LindenLab\\SansarClient\\UserPreferences.bag");
185+
static const auto kEnvironmentVariable = std::string("localappdata");
186+
187+
// Resize our container to hold the environment variable
188+
std::vector<int8_t> path(1);
189+
auto required_path_length = GetEnvironmentVariableA(kEnvironmentVariable.c_str(), (LPSTR)&path[0], 0);
190+
path.resize(required_path_length);
191+
192+
// Get the actual environment variable
193+
GetEnvironmentVariableA(kEnvironmentVariable.c_str(), (LPSTR)&path[0], (DWORD)path.size());
194+
195+
// GetEnvironmentVariableA will add a null character in at the end, exclude it
196+
auto local_app_data_path = std::string(path.begin(), path.end() - 1);
197+
198+
return local_app_data_path + kDefaultUserPreferencesPath;
199+
}
200+
201+
int main(int argc, char* argv[])
202+
{
203+
// The salt is hardcoded
204+
static const std::vector<uint8_t> kSalt {
205+
0x6E, 0x3F, 0x03, 0x29, 0x49, 0x63, 0x7D, 0x2E
206+
};
207+
208+
auto machine_guid = GetMachineGuid();
209+
if (machine_guid.empty())
210+
{
211+
std::cerr << "Failed to get MachineGuid" << std::endl;
212+
return 0;
213+
}
214+
215+
auto machine_guid_bytes = std::vector<uint8_t>(machine_guid.begin(), machine_guid.end() - 1);
216+
auto mangled_data = MangleData(machine_guid_bytes);
217+
218+
std::vector<uint8_t> key;
219+
std::vector<uint8_t> initialization_vector;
220+
if (!GetKeys(kSalt, mangled_data, key, initialization_vector))
221+
{
222+
std::cerr << "Failed to generate keys" << std::endl;
223+
return EXIT_FAILURE;
224+
}
225+
226+
std::string path_to_decrypt;
227+
if (argc == 2)
228+
{
229+
path_to_decrypt = std::string(argv[1]);
230+
}
231+
else
232+
{
233+
path_to_decrypt = GetPathToUserPreferencesBag();
234+
}
235+
236+
auto cipher_text = ReadAllBytes(path_to_decrypt);
237+
if (cipher_text.empty())
238+
{
239+
std::cerr << "ReadAllBytes - FAILED" << std::endl;
240+
return EXIT_FAILURE;
241+
}
242+
243+
auto plaintext = std::vector<uint8_t>(cipher_text.size());
244+
if (!Decrypt(cipher_text, key, initialization_vector, plaintext))
245+
{
246+
std::cerr << "Decrypt - FAILED" << std::endl;
247+
return EXIT_FAILURE;
248+
}
249+
250+
std::cout << &plaintext[0] << std::endl;
251+
252+
return EXIT_SUCCESS;
253+
}

0 commit comments

Comments
 (0)