Skip to content

Commit aa923a5

Browse files
authored
Merge pull request #1 from andro2157/dev
Password changer
2 parents fa2b6b4 + f41f69c commit aa923a5

8 files changed

Lines changed: 307 additions & 28 deletions

File tree

DiscordTokenProtector/Context.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ class Context {
6767
inline void initTokenState() {
6868
auto discoverToken = [this](bool hwid = false) {
6969
secure_string token = Discord::getStoredToken(true);
70-
if (token.empty() || Discord::getUserInfo(token).empty()) {//TODO invalid token message
70+
if (token.empty() || Discord::getUserInfo(token).id.empty()) {//TODO invalid token message
7171
this->state = hwid ? State::InvalidHWID : State::NoToken;
7272
}
7373
else if (hwid) {
@@ -87,7 +87,7 @@ class Context {
8787

8888
if (encryptionType_cache == EncryptionType::HWID) {
8989
secure_string token = g_secureKV->read("token", HWID_kd);
90-
if (token.empty() || Discord::getUserInfo(token).empty()) {
90+
if (token.empty() || Discord::getUserInfo(token).id.empty()) {
9191
discoverToken(true);
9292
}
9393
else {

DiscordTokenProtector/Crypto/CryptoUtils.h

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
#pragma once
22
#include "../Includes.h"
33
#include <cryptopp/hex.h>
4+
#include <cryptopp/osrng.h>
45

56
enum class EncryptionType {
67
HWID,
@@ -24,6 +25,23 @@ struct KeyData {
2425
static const KeyData HWID_kd({ EncryptionType::HWID, CryptoPP::SecByteBlock(), CryptoPP::SecByteBlock() });
2526

2627
namespace CryptoUtils {
28+
constexpr auto ALPHANUM = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
29+
constexpr auto ALPHANUM_LEN = 26 * 2 + 10;
30+
31+
inline secure_string secureRandomString(size_t len, const char* charRange = ALPHANUM, size_t charRangeLen = ALPHANUM_LEN) {
32+
using namespace CryptoPP;
33+
34+
secure_string output;
35+
output.reserve(len);
36+
37+
AutoSeededRandomPool rng;
38+
39+
for (size_t i = 0; i < len; i++) {
40+
output.push_back(ALPHANUM[rng.GenerateByte() % ALPHANUM_LEN]);
41+
}
42+
43+
return output;
44+
}
2745
inline std::string toHex(const std::string& in) {
2846
using namespace CryptoPP;
2947

DiscordTokenProtector/Discord.cpp

Lines changed: 112 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -339,7 +339,7 @@ WORD Discord::getDiscordRPCPort() {
339339
WORD workingPort = 0;
340340
for (WORD port = 6463; port <= 6472; port++) {
341341
try {
342-
std::string out;
342+
secure_string out;
343343
cURL_get(sf() << "http://127.0.0.1:" << port, nullptr, out);
344344
json outJson = json::parse(out);
345345
if (outJson["code"].get<int>() == 0 && outJson["message"].get<std::string>() == "Not Found") {
@@ -366,8 +366,8 @@ bool Discord::AcceptHandoff(const std::string& port, const std::string& key, con
366366
json handoffData;
367367
handoffData["key"] = key;
368368

369-
std::string handoffToken;
370-
cURL_post("https://discord.com/api/v8/auth/handoff", chunk, handoffData.dump(), handoffToken);
369+
secure_string handoffToken;
370+
cURL_post("https://discord.com/api/v8/auth/handoff", chunk, handoffData.dump().c_str(), handoffToken);
371371
handoffToken = json::parse(handoffToken)["handoff_token"].get<std::string>();
372372

373373
chunk = NULL;//Gets freed in cURL_post
@@ -381,8 +381,8 @@ bool Discord::AcceptHandoff(const std::string& port, const std::string& key, con
381381
handoffData["args"]["handoffToken"] = handoffToken;
382382
handoffData["nonce"] = getRandomUUID();
383383

384-
std::string handoffRPCOut;
385-
cURL_post("http://127.0.0.1:" + port + "/rpc?v=1", chunk, handoffData.dump(), handoffRPCOut);
384+
secure_string handoffRPCOut;
385+
cURL_post("http://127.0.0.1:" + port + "/rpc?v=1", chunk, handoffData.dump().c_str(), handoffRPCOut);
386386
json rpcResp = json::parse(handoffRPCOut);
387387
if (rpcResp.contains("code") && rpcResp.contains("message")) {//Error!
388388
throw std::runtime_error(sf() << "Error " << rpcResp["code"].get<int>() << " : " << rpcResp["message"].get<std::string>());
@@ -397,30 +397,35 @@ bool Discord::AcceptHandoff(const std::string& port, const std::string& key, con
397397
return true;
398398
}
399399

400-
std::string Discord::getUserInfo(const secure_string& token) {
400+
DiscordUserInfo Discord::getUserInfo(const secure_string& token) {
401401
using nlohmann::json;
402402

403403
try {
404404
struct curl_slist* chunk = NULL;
405405
chunk = curl_slist_append(chunk, ("Authorization: " + token).c_str());
406406
chunk = curl_slist_append(chunk, "Content-Type: application/json");
407407

408-
std::string userinfo;
408+
secure_string userinfo;
409409
cURL_get("https://discordapp.com/api/v6/users/@me", chunk, userinfo);
410410

411411
json userinfoJSON = json::parse(userinfo);
412412

413413
if (userinfoJSON.contains("message"))
414414
throw std::runtime_error(userinfoJSON["message"].get<std::string>());
415415

416-
return sf() << userinfoJSON["username"].get<std::string>() << "#" << userinfoJSON["discriminator"].get<std::string>()
417-
<< " (" << userinfoJSON["id"].get<std::string>() << ")";
416+
return {
417+
userinfoJSON["username"].get<std::string>() + "#" + userinfoJSON["discriminator"].get<std::string>(),
418+
userinfoJSON["username"].get<std::string>(),
419+
userinfoJSON["discriminator"].get<std::string>(),
420+
userinfoJSON["id"].get<std::string>(),
421+
userinfoJSON["mfa_enabled"].get<bool>()
422+
};
418423
}
419424
catch (const std::exception& e) {
420425
g_logger.error(sf() << "Failed getUserInfo : " << e.what());
421426
}
422427

423-
return std::string();
428+
return DiscordUserInfo();
424429
}
425430

426431
secure_string Discord::getStoredToken(bool verify) {
@@ -450,7 +455,7 @@ secure_string Discord::getStoredToken(bool verify) {
450455
secure_string match(matches[1].str());
451456
if (results.find(match) == results.end()) {//A new token! let's add it to the list
452457
if (std::find(invalids.begin(), invalids.end(), match) == invalids.end()) {//If not invalid
453-
if (verify && getUserInfo(match).empty())
458+
if (verify && getUserInfo(match).id.empty())
454459
invalids.push_back(match);
455460
else
456461
results.insert({ match, 1 });
@@ -480,6 +485,102 @@ secure_string Discord::getStoredToken(bool verify) {
480485
return "";
481486
}
482487

488+
bool Discord::changePassword(
489+
const secure_string& token,
490+
const secure_string& currentPassword,
491+
const secure_string& newPassword,
492+
const secure_string& mfaCode,
493+
secure_string& error) {
494+
495+
secure_string patchData;//We're avoiding the json lib to not keep the data in memory
496+
497+
auto jsonEscape = [](const secure_string& data) {
498+
secure_string out;
499+
out.reserve(data.size());
500+
501+
for (const char c : data) {
502+
switch (c) {
503+
case '\b': out += "\\b"; break;
504+
case '\f': out += "\\f"; break;
505+
case '\n': out += "\\n"; break;
506+
case '\r': out += "\\r"; break;
507+
case '\t': out += "\\t"; break;
508+
case '\"': out += "\\\""; break;
509+
case '\\': out += "\\\\"; break;
510+
default: out += c; break;
511+
}
512+
}
513+
514+
return out;
515+
};
516+
517+
patchData += "{\"password\":\"" + jsonEscape(currentPassword) +
518+
"\",\"new_password\":\"" + jsonEscape(newPassword) + "\"";
519+
520+
if (!mfaCode.empty()) {
521+
patchData += ",\"code\":\"" + jsonEscape(mfaCode) + "\"";
522+
}
523+
524+
patchData += "}";
525+
526+
try {
527+
struct curl_slist* chunk = NULL;
528+
chunk = curl_slist_append(chunk, ("Authorization: " + token).c_str());
529+
chunk = curl_slist_append(chunk, "Content-Type: application/json");
530+
chunk = curl_slist_append(chunk, "Accept-Language: en-US");
531+
532+
secure_string output;
533+
cURL_post("https://discord.com/api/v9/users/@me", chunk, patchData, output, "PATCH");
534+
535+
//To avoid using json or regex (insecure)
536+
auto getStringKey = [](const secure_string& data, secure_string key) {
537+
key = "\"" + key + "\": \"";
538+
size_t pos = data.find(key);
539+
if (pos == secure_string::npos) return secure_string();
540+
541+
size_t endPos = pos + key.size();
542+
while (true) {
543+
endPos = data.find("\"", endPos);
544+
if (endPos == secure_string::npos) return secure_string();
545+
if (data[endPos - 1] == '\\') {
546+
++endPos;
547+
continue;
548+
}
549+
break;
550+
}
551+
552+
return data.substr(pos + key.size(), endPos - pos - key.size());
553+
};
554+
555+
//Requires 2FA!
556+
if (auto pos = output.find("\"code\": 60008"); pos != secure_string::npos) {
557+
error = "2FA";
558+
return false;
559+
}
560+
561+
//Other errors
562+
if (secure_string message = getStringKey(output, "message"); !message.empty()) {
563+
error = message;
564+
return false;
565+
}
566+
567+
//Success!
568+
if (secure_string token = getStringKey(output, "token"); !token.empty()) {
569+
error = token;
570+
return true;
571+
}
572+
573+
//Unknown error
574+
error = "Unknown error";
575+
return false;
576+
}
577+
catch (std::exception& e) {
578+
g_logger.error(sf() << "Failed changePassword : " << e.what());
579+
error = e.what();
580+
return false;
581+
}
582+
}
583+
483584
//Credit : https://guidedhacking.com/threads/how-to-pass-multiple-arguments-with-createremotethread-to-injected-dll.15373/
484585

485586
uintptr_t GetModuleBaseAddress(DWORD procId, const char* modName)

DiscordTokenProtector/Discord.h

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,15 @@ enum class DiscordType {
1010
DiscordCanary
1111
};
1212

13+
//No need to store more info
14+
struct DiscordUserInfo {
15+
std::string fullUsername = "";//username#discriminator
16+
std::string username = "";
17+
std::string discriminator = "";
18+
std::string id = "";
19+
bool mfa = false;
20+
};
21+
1322
class Discord {
1423
private:
1524
typedef LONG(NTAPI* NtResumeProcess)(HANDLE ProcessHandle);
@@ -36,8 +45,16 @@ class Discord {
3645

3746
static WORD getDiscordRPCPort();
3847
static bool AcceptHandoff(const std::string& port, const std::string& key, const secure_string& token);
39-
static std::string getUserInfo(const secure_string& token);
48+
static DiscordUserInfo getUserInfo(const secure_string& token);
4049
static secure_string getStoredToken(bool verify);
50+
51+
//error = token if it is successfull
52+
static bool changePassword(
53+
const secure_string& token,
54+
const secure_string& currentPassword,
55+
const secure_string& newPassword,
56+
const secure_string& mfaCode,
57+
secure_string& error);
4158
private:
4259
static std::wstring getLocal();
4360
static std::vector<DWORD> getProcessIDbyName(std::wstring process_name);

DiscordTokenProtector/Includes.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ __forceinline void FATALERROR_STR(std::string str) {
2424
FATALERROR(str.c_str());
2525
}
2626

27-
#define VER "dev-4"
27+
#define VER "dev-5"
2828

2929
//The autostart gets flagged by some AV haha
3030
//#define DISABLE_AUTOSTART

0 commit comments

Comments
 (0)