Skip to content

Commit e470d52

Browse files
authored
Fixed detection (#61)
1 parent cbacf72 commit e470d52

14 files changed

Lines changed: 152 additions & 69 deletions

File tree

DiscordTokenProtector/Crypto/Crypto.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -199,7 +199,7 @@ namespace Crypto {
199199
return retries;
200200
}
201201

202-
int Yubi::authentificate(const secure_string& pin) {
202+
int Yubi::authenticate(const secure_string& pin) {
203203
int tries = 0;
204204
m_err = ykpiv_verify(m_state, pin.c_str(), &tries);
205205
if (m_err == YKPIV_OK || m_err == YKPIV_WRONG_PIN)

DiscordTokenProtector/Crypto/Crypto.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ namespace Crypto {
5858
int getRetryCount();
5959

6060
//Returns -1 if authentificated, else it returns the amount of retries
61-
int authentificate(const secure_string& pin);
61+
int authenticate(const secure_string& pin);
6262
//in.size() == 256
6363
secure_string signData(const CryptoPP::SecByteBlock& in);
6464

DiscordTokenProtector/Discord.cpp

Lines changed: 42 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -486,6 +486,32 @@ DiscordUserInfo Discord::getUserInfo(const secure_string& token) {
486486
};
487487
}
488488

489+
std::vector<secure_string> Discord::extractTokens(const secure_string& data) {
490+
constexpr auto MAX_TOKEN_SIZE = 200;
491+
492+
static const std::vector<std::regex> tokenRegex = {
493+
//std::regex(R"([\w-]{24}\.[\w-]{6}\.[\w-]{27,})"),
494+
std::regex(R"([\w-]{24,30}\.[\w-]{6}\.[\w-]{27,100})"),
495+
std::regex(R"(mfa\.[\w-]{84})")
496+
};
497+
498+
std::vector<secure_string> results;
499+
500+
for (const auto& reg : tokenRegex) {
501+
std::smatch matches;
502+
secure_string data_copy(data);
503+
504+
while (std::regex_search(data_copy, matches, reg)) {
505+
if (matches.length(0) <= MAX_TOKEN_SIZE) {
506+
results.push_back(secure_string(matches.str()));
507+
}
508+
data_copy = secure_string(matches.suffix().str());//Unsafe maybe ?
509+
}
510+
}
511+
512+
return results;
513+
}
514+
489515
secure_string Discord::getMemoryToken(bool verify) {
490516
DWORD discordPid = getDiscordPID(DiscordType::Discord);
491517
if (discordPid == 0) {
@@ -547,36 +573,25 @@ secure_string Discord::getMemoryToken(bool verify) {
547573
else if (currentString.size() < MIN_TOKEN_SIZE)
548574
currentString.clear();
549575
else {
550-
static const std::vector<std::regex> tokenRegex = {
551-
std::regex(R"([\w-]{24}\.[\w-]{6}\.[\w-]{27,})"),
552-
std::regex(R"(mfa\.[\w-]{84})")
553-
};
554-
555-
for (const auto& reg : tokenRegex) {
556-
auto possize = getRegPosSize(currentString, reg);
557-
558-
for (const auto& [pos, size] : possize) {
559-
secure_string match = currentString.substr(pos, size);
560-
561-
if (results.find(match) == results.end()) {//A new token! let's add it to the list
562-
if (std::find(invalids.begin(), invalids.end(), match) == invalids.end()) {//If not invalid
563-
if (verify) {
564-
try {
565-
auto userinfo = getUserInfo(match);
566-
results.insert({ match, 1 });
567-
}
568-
catch (invalid_token_exception& e) {
569-
invalids.push_back(match);
570-
}
571-
//Every other exceptions won't be catched!
572-
}
573-
else
576+
for (const auto& match : extractTokens(currentString)) {
577+
if (results.find(match) == results.end()) {//A new token! let's add it to the list
578+
if (std::find(invalids.begin(), invalids.end(), match) == invalids.end()) {//If not invalid
579+
if (verify) {
580+
try {
581+
auto userinfo = getUserInfo(match);
574582
results.insert({ match, 1 });
583+
}
584+
catch (invalid_token_exception& e) {
585+
invalids.push_back(match);
586+
}
587+
//Every other exceptions won't be catched!
575588
}
589+
else
590+
results.insert({ match, 1 });
576591
}
577-
else {//Already known!
578-
results[match] += 1;
579-
}
592+
}
593+
else {//Already known!
594+
results[match] += 1;
580595
}
581596
}
582597

DiscordTokenProtector/Discord.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,8 @@ class Discord {
4343
static bool AcceptHandoff(const std::string& port, const std::string& key, const secure_string& token);
4444
static DiscordUserInfo getUserInfo(const secure_string& token);
4545

46+
static std::vector<secure_string> extractTokens(const secure_string& data);
47+
4648
secure_string getMemoryToken(bool verify);
4749

4850
[[deprecated("Patched by Discord : Use memory reading one instead")]]

DiscordTokenProtector/DiscordTokenProtector.rc

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ MAINICON ICON "512x icon.ico"
6464

6565
VS_VERSION_INFO VERSIONINFO
6666
FILEVERSION 1,0,0,0
67-
PRODUCTVERSION 0,0,0,10
67+
PRODUCTVERSION 0,0,0,11
6868
FILEFLAGSMASK 0x3fL
6969
#ifdef _DEBUG
7070
FILEFLAGS 0x1L
@@ -86,7 +86,7 @@ BEGIN
8686
VALUE "LegalCopyright", "Copyright (C) 2021"
8787
VALUE "OriginalFilename", "DiscordTokenProtector.exe"
8888
VALUE "ProductName", "Discord Token Protector"
89-
VALUE "ProductVersion", "0.0.0.10"
89+
VALUE "ProductVersion", "0.0.0.11"
9090
END
9191
END
9292
BLOCK "VarFileInfo"

DiscordTokenProtector/Includes.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ __forceinline void FATALERROR_STR(std::string str) {
2828
FATALERROR(str.c_str());
2929
}
3030

31-
#define VER "dev-10"
31+
#define VER "dev-11"
3232

3333
#pragma comment(lib, "OpenGL32.lib")
3434
#pragma comment(lib, "crypt32.lib")

DiscordTokenProtector/Menu/Menu.cpp

Lines changed: 90 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,9 @@ namespace Menu {
3838

3939
bool running = true;
4040

41+
//TODO make a proper thing
42+
static bool addingAccount = false;
43+
4144
EasyAsync stopAsync([]() {
4245
g_context.stopProtection();
4346
});
@@ -365,7 +368,7 @@ namespace Menu {
365368
#ifdef YUBIKEYSUPPORT
366369
if (g_context.encryptionType_cache == EncryptionType::Yubi) {
367370
try {
368-
ykRetries = yk->authentificate(pin);
371+
ykRetries = yk->authenticate(pin);
369372
if (ykRetries != -1) {
370373
MessageBoxA(NULL, "Invalid PIN", "Discord Token Protector", MB_ICONWARNING | MB_OK);
371374
return;
@@ -535,13 +538,36 @@ namespace Menu {
535538
return tokenDetectionFuture.wait_for(std::chrono::milliseconds(1)) == std::future_status::ready;
536539
};
537540

541+
//TODO proper ui ?
542+
auto tryToGetTokenInClip = []() {
543+
if (ImGui::GetIO().KeyCtrl && ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_V))) {
544+
secure_string clipboard = ImGui::GetClipboardText();
545+
auto tokens = Discord::extractTokens(clipboard);
546+
if (!tokens.empty()) {
547+
try {
548+
userInfo = Discord::getUserInfo(tokens[0]);
549+
detectedToken = tokens[0];
550+
g_context.state = State::DiscoveredToken;
551+
return true;
552+
}
553+
catch (...) {
554+
555+
}
556+
}
557+
}
558+
return false;
559+
};
560+
538561
if (updateTimer.getElapsed<std::chrono::milliseconds>() > 1000) {
539562
if (step == 0) {
540563
if (g_discord->isDiscordRunning() != DiscordType::None) {
541564
step = 1;
542565
//g_context.initTokenState();
543566
startDetection();
544567
}
568+
else {
569+
tryToGetTokenInClip();
570+
}
545571
}
546572
else {
547573
if (detectionReady()) {
@@ -555,12 +581,13 @@ namespace Menu {
555581
}
556582
catch (discord_not_running_exception& e) {
557583
error = "Please run Discord";
584+
step = 0;
558585
}
559586
catch (no_token_exception& e) {
560587
error = "Unable to find token in memory";
561588
}
562589

563-
if (!error.empty()) {
590+
if (!error.empty() && !tryToGetTokenInClip()) {
564591
startDetection();
565592
step++;
566593
}
@@ -585,6 +612,9 @@ namespace Menu {
585612

586613
if (step > 15)
587614
ImGui::TextWrapped("If the detection doesn\'t work, please make a ticket on GitHub.");
615+
616+
ImGui::NewLine();
617+
ImGui::TextColored(Colors::GrayPurple, "(Tip : hold CTRL+V with your token in the clipboard to add it)");
588618
}
589619
else if (g_context.state == State::DiscoveredToken) {
590620
ImGui::TextWrapped("We found this account:");
@@ -679,9 +709,9 @@ namespace Menu {
679709
if (ImGui::Button("Refresh"))
680710
ykDetectAsync.start();
681711

682-
ImGui::TextWrapped("Please make sure you have a certificate in the card authentification slot!");
712+
ImGui::TextWrapped("Please make sure you have a certificate in the card authentication slot!");
683713
ImGui::SameLine();
684-
ImGui::TextTooltip("(?)", "Using the YubiKey Manager app, you can generate a card authentification certificate. "
714+
ImGui::TextTooltip("(?)", "Using the YubiKey Manager app, you can generate a card authentication certificate. "
685715
"A random message signed with this certificate is going to be used as the encryption key. "
686716
"A guide can be found on the Github repo.");
687717

@@ -739,7 +769,7 @@ namespace Menu {
739769
else if (selectedEncryptionMode == 3) {
740770
g_context.kd.type = EncryptionType::Yubi;
741771
try {
742-
ykRetries = yk->authentificate(pin);
772+
ykRetries = yk->authenticate(pin);
743773
if (ykRetries != -1) {
744774
MessageBoxA(NULL, "Invalid PIN", "Discord Token Protector", MB_ICONWARNING | MB_OK);
745775
return;
@@ -770,7 +800,7 @@ namespace Menu {
770800
}
771801

772802
g_tokenManager.firstSetup(detectedToken, userInfo);
773-
detectedToken.clear();
803+
secure_string().swap(detectedToken);
774804

775805
//Clean the local storage and session storage
776806
g_context.remover_canary_LocalStorage.Remove();
@@ -835,24 +865,30 @@ namespace Menu {
835865
void MainMenu() {
836866
ImGui::Indent(4.f);
837867

838-
if (ImGui::BeginTabBar("##Tabs")) {
839-
if (ImGui::BeginTabItem("Home")) {
840-
HomeTab();
841-
ImGui::EndTabItem();
842-
}
843-
if (ImGui::BeginTabItem("Accounts")) {
844-
AccountTab();
845-
ImGui::EndTabItem();
846-
}
847-
if (ImGui::BeginTabItem("Settings")) {
848-
SettingsTab();
849-
ImGui::EndTabItem();
850-
}
851-
if (ImGui::BeginTabItem("About")) {
852-
AboutTab();
853-
ImGui::EndTabItem();
868+
//TODO proper thing ?
869+
if (addingAccount) {
870+
AccountTab();
871+
}
872+
else {
873+
if (ImGui::BeginTabBar("##Tabs")) {
874+
if (ImGui::BeginTabItem("Home")) {
875+
HomeTab();
876+
ImGui::EndTabItem();
877+
}
878+
if (ImGui::BeginTabItem("Accounts")) {
879+
AccountTab();
880+
ImGui::EndTabItem();
881+
}
882+
if (ImGui::BeginTabItem("Settings")) {
883+
SettingsTab();
884+
ImGui::EndTabItem();
885+
}
886+
if (ImGui::BeginTabItem("About")) {
887+
AboutTab();
888+
ImGui::EndTabItem();
889+
}
890+
ImGui::EndTabBar();
854891
}
855-
ImGui::EndTabBar();
856892
}
857893

858894
ImGui::Unindent();
@@ -964,7 +1000,6 @@ namespace Menu {
9641000

9651001
void AccountTab() {
9661002
if (ImGui::BeginChild("AccountTab")) {
967-
static bool addingAccount = false;
9681003
if (addingAccount) {
9691004
//TODO combine with setup code
9701005
static secure_string detectedToken;
@@ -1003,6 +1038,26 @@ namespace Menu {
10031038
addingAccount = false;
10041039
};
10051040

1041+
//TODO proper ui ?
1042+
auto tryToGetTokenInClip = [&reset]() {
1043+
if (ImGui::GetIO().KeyCtrl && ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_V))) {
1044+
secure_string clipboard = ImGui::GetClipboardText();
1045+
auto tokens = Discord::extractTokens(clipboard);
1046+
if (!tokens.empty()) {
1047+
try {
1048+
g_tokenManager.addToken(tokens[0], Discord::getUserInfo(tokens[0]));
1049+
Discord::killDiscord();
1050+
reset();
1051+
return true;
1052+
}
1053+
catch (...) {
1054+
1055+
}
1056+
}
1057+
}
1058+
return false;
1059+
};
1060+
10061061
if (killDiscordAsync.isRunning()) {
10071062
ImGui::Text("Closing Discord...");
10081063
ImGui::NewLine();
@@ -1018,6 +1073,9 @@ namespace Menu {
10181073
step = 1;
10191074
startDetection();
10201075
}
1076+
else {
1077+
tryToGetTokenInClip();
1078+
}
10211079
}
10221080
else {
10231081
if (detectionReady()) {
@@ -1034,12 +1092,13 @@ namespace Menu {
10341092
}
10351093
catch (discord_not_running_exception& e) {
10361094
error = "Please run Discord";
1095+
step = 0;
10371096
}
10381097
catch (no_token_exception& e) {
10391098
error = "Unable to find token in memory";
10401099
}
10411100

1042-
if (!error.empty()) {
1101+
if (!error.empty() && !tryToGetTokenInClip()) {
10431102
startDetection();
10441103
step++;
10451104
}
@@ -1067,6 +1126,9 @@ namespace Menu {
10671126

10681127
if (step > 15)
10691128
ImGui::TextWrapped("If the detection doesn\'t work, please make a ticket on GitHub.");
1129+
1130+
ImGui::NewLine();
1131+
ImGui::TextColored(Colors::GrayPurple, "(Tip : hold CTRL+V with your token in the clipboard to add it)");
10701132
}
10711133
}
10721134
else {
@@ -1360,9 +1422,9 @@ namespace Menu {
13601422
if (ImGui::Button("Refresh"))
13611423
ykDetectAsync.start();
13621424

1363-
ImGui::TextWrapped("Please make sure you have a certificate in the card authentification slot!");
1425+
ImGui::TextWrapped("Please make sure you have a certificate in the card authentication slot!");
13641426
ImGui::SameLine();
1365-
ImGui::TextTooltip("(?)", "Using the YubiKey Manager app, you can generate a card authentification certificate. "
1427+
ImGui::TextTooltip("(?)", "Using the YubiKey Manager app, you can generate a card authentication certificate. "
13661428
"A random message signed with this certificate is going to be used as the encryption key. "
13671429
"A guide can be found on the Github repo.");
13681430

@@ -1422,7 +1484,7 @@ namespace Menu {
14221484
else if (selectedEncryptionMode == 3) {
14231485
newKeydata.type = EncryptionType::Yubi;
14241486
try {
1425-
ykRetries = yk->authentificate(pin);
1487+
ykRetries = yk->authenticate(pin);
14261488
if (ykRetries != -1) {
14271489
MessageBoxA(NULL, "Invalid PIN", "Discord Token Protector", MB_ICONWARNING | MB_OK);
14281490
return;

DiscordTokenProtector/Storage/TokenManager.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,7 @@ bool TokenManager::migrateOldToken() {
9494
return true;
9595
}
9696

97-
size_t TokenManager::addToken(const secure_string token, const DiscordUserInfo info) {
97+
void TokenManager::addToken(const secure_string token, const DiscordUserInfo info) {
9898
m_mutex.lock();
9999

100100
secure_string encrypted_token = CryptoUtils::KD_encrypt(token, g_context.kd);

0 commit comments

Comments
 (0)