Skip to content

Commit

Permalink
Merge pull request #5 from hirusha-adi/python-dev
Browse files Browse the repository at this point in the history
Add discord token recovery
  • Loading branch information
hirusha-adi authored Mar 19, 2023
2 parents cc218e9 + 90aecf7 commit 5592842
Show file tree
Hide file tree
Showing 5 changed files with 149 additions and 6 deletions.
6 changes: 4 additions & 2 deletions config/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,9 @@ class Constant:

username = getuser()
temp_dir = tempfile.gettempdir()

local_dir = os.getenv('LOCALAPPDATA')
roaming_dir = os.getenv('APPDATA')

log_filename = f'{username}-{datetime}.log'

# Final Base Stuff
Expand All @@ -41,7 +43,7 @@ class Constant:

# File Content
seperator = "\n\n" + "="*20 + "\n\n"

# Arguments
# -----------------------------------
class Args:
Expand Down
3 changes: 2 additions & 1 deletion modules/__init__.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
from .browsers import WebBookmarksRecovery, WebHistoryRecovery, ChromiumRecovery
from .network import NetworkInfoRecovery, WifiPasswordRecovery
from .systeminfo import SystemInfoRecovery
from .systeminfo import SystemInfoRecovery
from .applications import DiscordRecovery
1 change: 1 addition & 0 deletions modules/applications/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
from .discord import DiscordRecovery
128 changes: 128 additions & 0 deletions modules/applications/discord.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
import os

from ctypes import windll
from ctypes import wintypes
from ctypes import byref
from ctypes import cdll
from ctypes import Structure
from ctypes import POINTER
from ctypes import c_char
from ctypes import c_buffer
from Crypto.Cipher import AES
from base64 import b64decode
from json import loads as json_loads
import threading
import re

from config import Constant
from config import ModuleManager


class DATA_BLOB(Structure):
_fields_ = [
('cbData', wintypes.DWORD),
('pbData', POINTER(c_char))
]

class DiscordRecovery(ModuleManager):
def __init__(self) -> None:
super().__init__(module_name="DiscordRecovery")

self.banner("""
_______ ______ _ _
|.-----.| (______)(_) | |
||x . x|| _ _ _ ___ ____ ___ ____ __| |
||_.-._|| | | | | |/___)/ ___) _ \ / ___) _ |
`--)-(--` | |__/ /| |___ ( (__| |_| | | ( (_| |
__[=== o]___ |_____/ |_(___/ \____)___/|_| \____|
|:::::::::::|\
`-=========-`() Recover Lost Discord Accounts
""")

self.discord_folder = os.path.join(self.output_folder_user, 'applications', 'discord')

if not os.path.isdir(self.discord_folder):
os.makedirs(self.discord_folder)

self.discordInstallations = [
[f"{Constant.roaming_dir}/Discord","Discord"],
[f"{Constant.roaming_dir}/Lightcord","Lightcord"],
[f"{Constant.roaming_dir}/discordcanary","DiscordCanary"],
[f"{Constant.roaming_dir}/discordptb","DiscordPTB"]
]

self.tokensTMP = ''
self.tokensCount = 0

def GetData(self, blob_out):
cbData = int(blob_out.cbData)
pbData = blob_out.pbData
buffer = c_buffer(cbData)
cdll.msvcrt.memcpy(buffer, pbData, cbData)
windll.kernel32.LocalFree(pbData)
return buffer.raw

def CryptUnprotectData(self, encrypted_bytes, entropy=b''):
buffer_in = c_buffer(encrypted_bytes, len(encrypted_bytes))
buffer_entropy = c_buffer(entropy, len(entropy))
blob_in = DATA_BLOB(len(encrypted_bytes), buffer_in)
blob_entropy = DATA_BLOB(len(entropy), buffer_entropy)
blob_out = DATA_BLOB()

if windll.crypt32.CryptUnprotectData(byref(blob_in), None, byref(blob_entropy), None, None, 0x01, byref(blob_out)):
return self.GetData(blob_out)

def DecryptValue(self, buff, master_key=None):
starts = buff.decode(encoding='utf8', errors='ignore')[:3]
if starts == 'v10' or starts == 'v11':
iv = buff[3:15]
payload = buff[15:]
cipher = AES.new(master_key, AES.MODE_GCM, iv)
decrypted_pass = cipher.decrypt(payload)
decrypted_pass = decrypted_pass[:-16].decode()
return decrypted_pass

def GetDiscord(self, path, savefname):
if not os.path.exists(f"{path}/Local State"):
self.merror(f"[{savefname}] Client is not available at: {path}. Continuing...")
return

pathC = path + "/Local Storage/leveldb"

pathKey = path + "/Local State"
with open(pathKey, 'r', encoding='utf-8') as f:
local_state = json_loads(f.read())
master_key = b64decode(local_state['os_crypt']['encrypted_key'])
master_key = self.CryptUnprotectData(master_key[5:])
self.mdebug(f"[{savefname}] Found and loaded master key")

tokens = []
for file in os.listdir(pathC):
if file.endswith(".log") or file.endswith(".ldb"):
self.mdebug(f"[{savefname}] Searching in {file}")
for line in [x.strip() for x in open(f"{pathC}\\{file}", errors="ignore").readlines() if x.strip()]:
for token in re.findall(r"dQw4w9WgXcQ:[^.*\['(.*)'\].*$][^\"]*", line):
tokenDecoded = self.DecryptValue(b64decode(token.split('dQw4w9WgXcQ:')[1]), master_key)
if not tokenDecoded in self.tokensTMP:
self.tokensTMP += tokenDecoded
self.mdebug(f"[{savefname}] Found token in {file}")
tokens.append(tokenDecoded)
self.tokensCount += 1

with open(os.path.join(self.discord_folder, f"{savefname}.txt"), 'w', encoding='utf-8') as file:
file.write('\n'.join(tokens))
self.mprint(f"[{savefname}] Saved {len(tokens)} tokens to {savefname}.txt")

def run(self):
try:
self.mdebug(f"Starting to look for discord tokens")

for patt in self.discordInstallations:
self.mdebug(f"Running `GetDiscord()` on {patt[1]} at {patt[0]}")
self.GetDiscord(path=patt[0], savefname=patt[1])

self.mprint(f"Found a total of {self.tokensCount} Discord Tokens")

except Exception as e:
self.merror(f"Unable to locate any discord token: {e}. Skipping this module")
return
17 changes: 14 additions & 3 deletions recover.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
from modules import ChromiumRecovery, WebHistoryRecovery, WebBookmarksRecovery # browser
from modules import NetworkInfoRecovery, WifiPasswordRecovery # network
from modules import SystemInfoRecovery # system

from modules import DiscordRecovery # applications

class args:
browser_passwords = False
Expand All @@ -16,6 +16,7 @@ class args:
network_wifi = False
network_info = False
system_all = False
applications_discord = False


def parser():
Expand All @@ -42,6 +43,7 @@ def parser():
--network-wifi, -nw Get Wifi Passwords
--network-info, -ni Get All Network Information
--system-all, -sa Get All Network Information and Wifi Passwords
--apps-discord, -ad Get Discord Tokens of Logged in Accounts
"""

argsv = sys.argv[:]
Expand Down Expand Up @@ -114,7 +116,13 @@ def parser():
args.system_all = True
else:
args.system_all = False


# applications
if ("--apps-discord" in argsv) or ("-ad" in argsv):
args.applications_discord = True
else:
args.applications_discord = False

if ("--all" in argsv) or ("-a" in argsv):
args.browser_bookmakrs = True
args.browser_history = True
Expand All @@ -123,7 +131,7 @@ def parser():
args.network_wifi = True
args.system_all = True

if not (args.browser_passwords or args.browser_history or args.browser_bookmakrs or args.network_info or args.network_wifi or args.system_all):
if not (args.browser_passwords or args.browser_history or args.browser_bookmakrs or args.network_info or args.network_wifi or args.system_all or args.applications_discord):
print(__help_message)
sys.exit()

Expand Down Expand Up @@ -174,6 +182,9 @@ def main():
if args.system_all:
SystemInfoRecovery().run()

if args.applications_discord:
DiscordRecovery().run()

cexit()


Expand Down

0 comments on commit 5592842

Please sign in to comment.