///////////////////////////////////////////////////// // // // GetMitigateItems() // // // ///////////////////////////////////////////////////// //Description: Crawls the GUI listview controls that // hold the registry, file and memory // signature matches. It builds an XML // structure from these values in preparation // for sending them to the agent for mitigation. // //Returns: void ///////////////////////////////////////////////////// private CwXML.CodewordAgentSignatureMatches GetCollectMitigateItems(ref string outputMessage, string mitigateOrCollectMsg) { int numRegMitigate = 0,numFileMitigate=0,numMemMitigate=0; int count = 0; bool mitigateAll = false; CwXML.CodewordAgentSignatureMatches matches = new CwXML.CodewordAgentSignatureMatches(); outputMessage = ""; //------------------------------------- // CALCULATE COUNTS //------------------------------------- foreach (ListViewItem lvi in AgentResults_RegistryListview.Items) if (lvi.Checked) numRegMitigate++; foreach (ListViewItem lvi in AgentResults_FileListview.Items) if (lvi.Checked) numFileMitigate++; foreach (ListViewItem lvi in AgentResults_MemoryListview.Items) if (lvi.Checked) numMemMitigate++; //if there were no items selected, prompt to mitigate all findings if ((numRegMitigate + numFileMitigate + numMemMitigate) == 0) { if (MessageBox.Show("No findings were selected. Would you like to "+mitigateOrCollectMsg+" all findings?", mitigateOrCollectMsg+" all findings?", MessageBoxButtons.YesNoCancel, MessageBoxIcon.Question) != DialogResult.Yes) return null; numRegMitigate = AgentResults_RegistryListview.Items.Count; numFileMitigate = AgentResults_FileListview.Items.Count; numMemMitigate = AgentResults_MemoryListview.Items.Count; mitigateAll = true; } CwXML.RegistrySignatureMatch[] regMatchesToMitigate = new CwXML.RegistrySignatureMatch[numRegMitigate]; CwXML.FileSignatureMatch[] fileMatchesToMitigate = new CwXML.FileSignatureMatch[numFileMitigate]; CwXML.MemorySignatureMatch[] memMatchesToMitigate = new CwXML.MemorySignatureMatch[numMemMitigate]; //------------------------------------- // REGISTRY //------------------------------------- if (numRegMitigate > 0) { outputMessage += "Registry findings (" + numRegMitigate + "):\n"; //build list of registry signature matches to mitigate foreach (ListViewItem lvi in AgentResults_RegistryListview.Items) { if (!lvi.Checked && !mitigateAll) continue; //add to display based on action selected for this finding outputMessage += " " + lvi.SubItems[0].Text + "\\" + lvi.SubItems[1] + " : " + lvi.SubItems[5].Text + "\n"; regMatchesToMitigate[count] = new CwXML.RegistrySignatureMatch(); regMatchesToMitigate[count].RegistryKeyName = lvi.SubItems[0].Text; regMatchesToMitigate[count].RegistryValueName = lvi.SubItems[1].Text; regMatchesToMitigate[count].RegistryValueData = lvi.SubItems[2].Text; regMatchesToMitigate[count].RegistryChangeValueData = lvi.SubItems[3].Text; try { regMatchesToMitigate[count].IsFileOnDisk = bool.Parse(lvi.SubItems[4].Text); } catch (Exception) { regMatchesToMitigate[count].IsFileOnDisk = false; } regMatchesToMitigate[count].Action = lvi.SubItems[5].Text; try { regMatchesToMitigate[count].ActionSuccessful = bool.Parse(lvi.SubItems[6].Text); } catch (Exception) { regMatchesToMitigate[count].ActionSuccessful = false; } count++; } count = 0; } //------------------------------------- // FILE //------------------------------------- if (numFileMitigate > 0) { outputMessage += "File findings (" + numFileMitigate + "):\n"; //build list of registry signature matches to mitigate foreach (ListViewItem lvi in AgentResults_FileListview.Items) { if (!lvi.Checked && !mitigateAll) continue; //add to display based on action selected for this finding if (lvi.SubItems[0].Text != "") //filename outputMessage += " " + lvi.SubItems[1].Text + " : " + lvi.SubItems[8].Text + "\n"; else if (lvi.SubItems[3].Text != "") //hash outputMessage += " [Hash=" + lvi.SubItems[3].Text + "] : " + lvi.SubItems[8].Text + "\n"; else if (lvi.SubItems[2].Text != "") //filesize outputMessage += " [FileSize=" + lvi.SubItems[2].Text + "] : " + lvi.SubItems[8].Text + "\n"; fileMatchesToMitigate[count] = new CwXML.FileSignatureMatch(); fileMatchesToMitigate[count].FileName = lvi.SubItems[0].Text; fileMatchesToMitigate[count].FullPath = lvi.SubItems[1].Text; long.TryParse(lvi.SubItems[2].Text, out fileMatchesToMitigate[count].FileSize); fileMatchesToMitigate[count].FileHash = lvi.SubItems[3].Text; fileMatchesToMitigate[count].FilePEHeaderSignature = lvi.SubItems[4].Text; fileMatchesToMitigate[count].FileCreationDate = lvi.SubItems[5].Text; fileMatchesToMitigate[count].FileLastAccessDate = lvi.SubItems[6].Text; fileMatchesToMitigate[count].FileLastModifiedDate = lvi.SubItems[7].Text; fileMatchesToMitigate[count].Action = lvi.SubItems[8].Text; try { fileMatchesToMitigate[count].ActionSuccessful = bool.Parse(lvi.SubItems[9].Text); } catch (Exception) { fileMatchesToMitigate[count].ActionSuccessful = false; } count++; } count = 0; } //------------------------------------- // MEMORY //------------------------------------- if (numMemMitigate > 0) { outputMessage += "Memory findings (" + numMemMitigate + "):\n"; //build list of registry signature matches to mitigate foreach (ListViewItem lvi in AgentResults_MemoryListview.Items) { if (!lvi.Checked && !mitigateAll) continue; outputMessage += " " + lvi.SubItems[2].Text + " (" + lvi.SubItems[0].Text + ") : " + lvi.SubItems[7].Text; //we cant populate all the fields of the memorysignaturematch structure, //because we didn't populate the GUI listview with all these fields (there are too many) //however, memory mitigation consists of killing the process by name/pid or suspending the thread. //so we dont need all that crap anyway. memMatchesToMitigate[count] = new CwXML.MemorySignatureMatch(); uint.TryParse(lvi.SubItems[0].Text, out memMatchesToMitigate[count].ProcessId); uint.TryParse(lvi.SubItems[1].Text, out memMatchesToMitigate[count].ParentProcessId); memMatchesToMitigate[count].ProcessName = lvi.SubItems[2].Text; memMatchesToMitigate[count].ChildThreadIds = lvi.SubItems[6].Text; memMatchesToMitigate[count].Action = lvi.SubItems[7].Text; try { memMatchesToMitigate[count].ActionSuccessful = bool.Parse(lvi.SubItems[8].Text); } catch (Exception) { memMatchesToMitigate[count].ActionSuccessful = false; } count++; } } matches.RegistrySignatureMatches = regMatchesToMitigate; matches.FileSignatureMatches = fileMatchesToMitigate; matches.MemorySignatureMatches = memMatchesToMitigate; return matches; }
///////////////////////////////////////////////////// // // // SearchProcessLoadedModuleList() // // // ///////////////////////////////////////////////////// //Description: scans all module entries for the given // process using unamanged APIs. simply // checks the module's name against the // known list of module names and also // the module's command line startup. //Returns: true if match found ///////////////////////////////////////////////////// internal bool SearchProcessLoadedModuleList(uint pid, uint ppid, string procName, string action, ArrayList keywords, ref ArrayList matches) { //take a snapshot of the module list for this process IntPtr hSnapshotModules = Win32Helper.CreateToolhelp32Snapshot(0x00000008, (uint)pid); if (hSnapshotModules == (IntPtr)(-1)) { MemoryHelperLog.AppendLine("ERROR: Could not create module snapshot, skipping this process (" + pid + ")..."); MemoryHelperLog.AppendLine("ERROR: Error data = " + Win32Helper.GetLastError32()); return false; } //get a pointer to the module list for this process Win32Helper.MODULEENTRY32 procModuleList = new Win32Helper.MODULEENTRY32(); procModuleList.dwSize = new IntPtr(Marshal.SizeOf(typeof(Win32Helper.MODULEENTRY32))); if (!Win32Helper.Module32First(hSnapshotModules, ref procModuleList)) { MemoryHelperLog.AppendLine("ERROR: Could not obtain a pointer to the process module list, skipping this process (" + pid + ")..."); MemoryHelperLog.AppendLine("ERROR: Error data = " + Win32Helper.GetLastError32()); return false; } //now loop over module list for this process do { string moduleName = procModuleList.szModule.ToString(); string moduleBaseAddr = procModuleList.modBaseAddr.ToString(); string moduleEndAddr = (procModuleList.modBaseAddr.ToInt32() + procModuleList.modBaseSize).ToString(); string moduleSize = procModuleList.modBaseSize.ToString(); string modulePath = procModuleList.szExePath.ToString(); //loop through all keywords and search for this loaded module name in them foreach (string kw in keywords) { if (moduleName.Contains(kw)) { CwXML.MemorySignatureMatch matchRecord = new CwXML.MemorySignatureMatch(); matchRecord.Action = action; matchRecord.MatchingBlock = moduleName; matchRecord.ProcessId = pid; matchRecord.ParentProcessId=ppid; matchRecord.ProcessName = procName; matchRecord.Keywords = string.Join(",", ((string[])keywords.ToArray(typeof(string)))); matchRecord.ChildThreadIds = GetChildThreadIds((uint)pid); matchRecord.MaliciousLoadedModuleName = moduleName; matchRecord.MaliciousLoadedModuleBaseAddr = moduleBaseAddr; matchRecord.MaliciousLoadedModuleEndAddr = moduleEndAddr; matchRecord.MaliciousLoadedModulePath = modulePath; matchRecord.MaliciousLoadedModuleSize = moduleSize; matches.Add(matchRecord); } } } while (Win32Helper.Module32Next(hSnapshotModules, ref procModuleList)); Win32Helper.CloseHandle(hSnapshotModules); //close handle to snapshot of heap list if (matches.Count > 0) return true; return false; }
///////////////////////////////////////////////////// // // // SearchProcessCmdline() // // // ///////////////////////////////////////////////////// //Description: scans the command line argument for the // given pid, searching the string for // any keyword in the keywords list //Returns: true if match found ///////////////////////////////////////////////////// internal bool SearchProcessCmdline(uint pid, uint ppid, string action, ArrayList keywords, ref ArrayList matches) { //use WMI query SelectQuery sQuery = new SelectQuery("SELECT * FROM Win32_Process WHERE ProcessId=" + pid); ManagementObjectSearcher processSearcher = new ManagementObjectSearcher(sQuery); string cmdLine = "", processName=""; //there will only be one result, but .net makes you loop over it.. foreach (ManagementObject process in processSearcher.Get()) { cmdLine = process["CommandLine"].ToString(); processName = process["Name"].ToString(); } //dont continue if there was no command line if (cmdLine == "") { return false; } else { //scan keyword list and see if this process's CMDLINE contains the keyword foreach (string kw in keywords) { if (cmdLine.Contains(kw)) { CwXML.MemorySignatureMatch matchRecord = new CwXML.MemorySignatureMatch(); matchRecord.MatchingBlock = cmdLine; matchRecord.ProcessId = pid; matchRecord.ParentProcessId=ppid; matchRecord.ProcessName = processName; matchRecord.Keywords = string.Join(",", ((string[])keywords.ToArray(typeof(string)))); matchRecord.ChildThreadIds = GetChildThreadIds((uint)pid); matchRecord.Action = action; matches.Add(matchRecord); } } } if (matches.Count > 0) return true; return false; }
///////////////////////////////////////////////////// // // // SearchProcessHeap() // // // ///////////////////////////////////////////////////// //Description: scans all heap entries for the given // process using unamanged APIs. tries // to Toolhelp32ReadProcessMemory() on // each heap range and then search those // bytes for keywords. // If a memory page is protected, use // VirtualProtectEx() to unprotect it. //Returns: void ///////////////////////////////////////////////////// internal bool SearchProcessHeap(uint pid, uint ppid, string procName, string action, ArrayList keywords, ref ArrayList matches) { //---------------------------------------- // CREATE HEAP SNAPSHOT //---------------------------------------- IntPtr hSnapshotHeap = Win32Helper.CreateToolhelp32Snapshot(0x00000001, pid); if (hSnapshotHeap == (IntPtr)(-1)) { MemoryHelperLog.AppendLine("ERROR: Could not create heap snapshot, skipping this process (" + pid + ")..."); MemoryHelperLog.AppendLine("ERROR: Error data = " + Win32Helper.GetLastError32()); return false; } //---------------------------------------- // GET PTR TO FIRST HEAP BLOCK //---------------------------------------- Win32Helper.HEAPLIST32 procHeapList = new Win32Helper.HEAPLIST32(); procHeapList.dwSize = new IntPtr(Marshal.SizeOf(typeof(Win32Helper.HEAPLIST32))); if (!Win32Helper.Heap32ListFirst(hSnapshotHeap, ref procHeapList)) { MemoryHelperLog.AppendLine("ERROR: Could not obtain a pointer to the process heap, skipping this process (" + pid + ")..."); MemoryHelperLog.AppendLine("ERROR: Error data = " + Win32Helper.GetLastError32()); return false; } //---------------------------------------- // ITERATE OVER HEAP LIST //---------------------------------------- do { //---------------------------------------- //GET PTR TO FIRST LIST OF BLOCKS FOR THIS HEAP //---------------------------------------- //get a pointer to the first block in the heap, so we can iterate over all blocks Win32Helper.HEAPENTRY32 heapBlock = new Win32Helper.HEAPENTRY32(); heapBlock.dwSize = (uint)Marshal.SizeOf(typeof(Win32Helper.HEAPENTRY32)); //if we fail to get the first heap block, must skip this process.. if (!Win32Helper.Heap32First(ref heapBlock, procHeapList.th32ProcessID, procHeapList.th32HeapID)) { MemoryHelperLog.AppendLine("ERROR: Could not obtain a pointer to the first heap block, skipping this process (" + pid + ")..."); MemoryHelperLog.AppendLine("ERROR: Error data = " + Win32Helper.GetLastError32()); break; } //---------------------------------------- // ITERATE OVER HEAP BLOCK LIST //---------------------------------------- do { MemoryHelperLog.AppendLine("SCAN: Searching a " + heapBlock.dwBlockSize.ToString() + "-byte heap in " + pid + " from 0x" + heapBlock.dwAddress.ToString("x") + " - 0x" + (heapBlock.dwAddress + heapBlock.dwBlockSize).ToString("x")); //YAY!! WE finally have a ptr to the heap data...whew IntPtr pBuffer = Marshal.AllocHGlobal((int)heapBlock.dwBlockSize); IntPtr pBytesRead = Marshal.AllocHGlobal(4); // //---------------------------------------- // UNPROTECT THE MEMORY //---------------------------------------- // uint oldProtection = 0; //remember the old protection so we can restore it bool needToRevertPageProtection = UnprotectMemoryRange(pid, (IntPtr)heapBlock.dwAddress, heapBlock.dwSize,ref oldProtection); //---------------------------------------- // READ THE HEAP BLOCK //---------------------------------------- //but to actually obtain the data to search through it, //we have to call Toolhelp32ReadProcessMemory() - if it fails, go to next heap if (!Win32Helper.Toolhelp32ReadProcessMemory(pid, (IntPtr)heapBlock.dwAddress, pBuffer, heapBlock.dwBlockSize, pBytesRead)) { MemoryHelperLog.AppendLine("ERROR: Failed to read from the requested address range: " + Win32Helper.GetLastError32() + ". This may indicate this is an inaccessible private heap."); //according to MSDN, if the error returned is ERROR_PARTIAL_COPY, this means the process is 64-bit //however, i've seen this error occur on 32-bit OS running in a VM on a 64-bit host machine. if (Win32Helper.GetLastError() == Win32Helper.ERROR_PARTIAL_COPY) MemoryHelperLog.AppendLine("NOTE: This error often indicates the process being searched is a 64-bit process, and we are running as a 32-bit process."); //reinitialize size of this block in preparation for Heap32Next() heapBlock.dwSize = (uint)Marshal.SizeOf(typeof(Win32Helper.HEAPENTRY32)); continue; } //---------------------------------------- // RE-PROTECT THE MEMORY //---------------------------------------- // if (needToRevertPageProtection) ProtectMemoryRange(pid, (IntPtr)heapBlock.dwAddress, heapBlock.dwSize, oldProtection); //---------------------------------------- // COPY BUFFER AND DECODE //---------------------------------------- byte[] buffer = new byte[unchecked((int)heapBlock.dwBlockSize)]; Marshal.Copy(pBuffer, buffer, 0, (int)heapBlock.dwBlockSize); Marshal.FreeHGlobal(pBuffer); Marshal.FreeHGlobal(pBytesRead); StringBuilder ASCIIData = new StringBuilder(); StringBuilder UnicodeData = new StringBuilder(); string haystackASCII = ""; string haystackUnicode = ""; //ascii try { char[] chars = new char[unchecked(Encoding.ASCII.GetCharCount(buffer, 0, buffer.Length))]; Encoding.ASCII.GetChars(buffer, 0, buffer.Length, chars, 0); ASCIIData.Append(chars); haystackASCII = ASCIIData.ToString(); ASCIIData.Remove(0, ASCIIData.Length); } catch (Exception) { }//swallow //unicode try { char[] chars = new char[unchecked(Encoding.Unicode.GetCharCount(buffer, 0, buffer.Length))]; Encoding.Unicode.GetChars(buffer, 0, buffer.Length, chars, 0); UnicodeData.Append(chars); haystackUnicode = UnicodeData.ToString(); UnicodeData.Remove(0, UnicodeData.Length); } catch (Exception) { }//swallow //bail on this heap block if both decodings failed if (haystackASCII == "" && haystackUnicode == "") { //reinitialize size of this block in preparation for Heap32Next() heapBlock.dwSize = (uint)Marshal.SizeOf(typeof(Win32Helper.HEAPENTRY32)); continue; } //---------------------------------------- // SEARCH LOCAL COPY OF HEAP BLOCK //---------------------------------------- //loop through all keywords and search for it in heap data foreach (string kw in keywords) { string containingBlob = ""; //ASCII if (haystackASCII.Contains(kw)) { int loc = haystackASCII.IndexOf(kw); int start = loc - 25; //start 25 characters before match int end = loc + 25; //end 25 characters after match //make sure we are within bounds of the string if (start < 0) start = 0; if (end > haystackASCII.Length) end = haystackASCII.Length; containingBlob = haystackASCII.Substring(start, end - start); } //Unicode else if (haystackUnicode.Contains(kw)) { int loc = haystackUnicode.IndexOf(kw); int start = loc - 25; //start 25 characters before match int end = loc + 25; //end 25 characters after match //make sure we are within bounds of the string if (start < 0) start = 0; if (end > haystackUnicode.Length) end = haystackUnicode.Length; containingBlob = haystackUnicode.Substring(start, end - start); } //continue if no matches. if (containingBlob == "") continue; CwXML.MemorySignatureMatch matchRecord = new CwXML.MemorySignatureMatch(); matchRecord.Action = action; matchRecord.MatchingBlock = containingBlob; matchRecord.ProcessId = pid; matchRecord.ProcessName = procName; matchRecord.Keywords = string.Join(",", ((string[])keywords.ToArray(typeof(string)))); matchRecord.ChildThreadIds = GetChildThreadIds(pid); matchRecord.SuspiciousHeapBlockRange = (heapBlock.dwAddress + heapBlock.dwBlockSize).ToString("x"); matches.Add(matchRecord); } haystackASCII = ""; haystackUnicode = ""; //reinitialize size of this block in preparation for Heap32Next() heapBlock.dwSize = (uint)Marshal.SizeOf(typeof(Win32Helper.HEAPENTRY32)); } while (Win32Helper.Heap32Next(out heapBlock)); } while (Win32Helper.Heap32ListNext(hSnapshotHeap, ref procHeapList)); Win32Helper.CloseHandle(hSnapshotHeap); //close handle to snapshot of heap list if (matches.Count > 0) return true; return false; }
///////////////////////////////////////////////////// // // // ScanForMemorySignatures() // // // ///////////////////////////////////////////////////// //Description: this function searches the heap space of // process(es) of interest for supplied keyword(s) //Returns: true if keyword found ///////////////////////////////////////////////////// internal unsafe void ScanForMemorySignatures(CwXML.RegistrySignatureMatch[] RegistryFindings, CwXML.MemorySignature[] MemorySignatures, ref CwXML.MemorySignatureMatch[] matches, bool SearchCmdline, bool SearchHeap, bool searchModuleList, bool searchRegistryFindings) { //we will use this regex to validate whether a given keyword is a valid filename/path or not //this is used when we are walking the module list Regex filePathValidator = new Regex(@"^(([a-zA-Z]\:)|(\\))(\\{1}|((\\{1})[^\\]([^/:*?<>""|]*))+)$"); ArrayList Findings=new ArrayList(); // //loop through each memory signature and search // foreach (CwXML.MemorySignature m in MemorySignatures) { //is this process name for this signature in the list of active processes? string ProcessName = m.ProcessName; ArrayList ProcessInfo = GetActiveProcessInfo(ProcessName); string action = m.Action; //if not, skip it. if (ProcessInfo == null) { MemoryHelperLog.AppendLine("SCAN: The target process " + ProcessName + " is not running, skipping this signature..."); continue; } if (ProcessInfo.Count == 0) { MemoryHelperLog.AppendLine("SCAN: The target process " + ProcessName + " did not return a full data set, skipping this signature..."); continue; } //* PROCESS OF INTEREST IS IN THE LIST OF RUNNING PROCESSES... *// uint pid = (uint)ProcessInfo[0]; uint ppid = (uint)ProcessInfo[1]; uint threadCount = (uint)ProcessInfo[2]; //were any keywords given to search this process heap/cmdline/modlist? string[] Keywords = m.Keywords.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries); //if no keywords were given, then we can conclude our task was only to identify that //this process name was running on the system. if that's the case, return a hit. if (Keywords.Length == 0) { MemoryHelperLog.AppendLine("SCAN: Match found for process name '" + ProcessName + "'!"); CwXML.MemorySignatureMatch matchRecord = new CwXML.MemorySignatureMatch(); matchRecord.MatchingBlock = ProcessName; matchRecord.ProcessId = pid; matchRecord.ParentProcessId = ppid; matchRecord.ProcessName = ProcessName; matchRecord.Keywords = ""; matchRecord.ChildThreadIds = GetChildThreadIds((uint)pid); matchRecord.Action = action; //note: we are adding the match this way to be consistent with how //we have to add them in SearchProcessCmdLine() and other funcs ArrayList tmpMatches = new ArrayList(); tmpMatches.Add(matchRecord); Findings.Add(tmpMatches); continue; } //otherwise, we need to search either the process heap, cmd line, or module list //for the given keywords in this memory signature. ArrayList KeywordList = new ArrayList(); KeywordList.AddRange(Keywords); //if the user wants to use registry findings in our keyword scan, add them now if (searchRegistryFindings) foreach (CwXML.RegistrySignatureMatch regMatch in RegistryFindings) if (regMatch.RegistryValueData != null) if (regMatch.RegistryValueData.Length < 300) KeywordList.Add(regMatch.RegistryValueData); //********************************* // SCAN OPTIONS //********************************* //perform the following per-process scans, depending on user options: // 1) use WMI to search the Cmdline for this process for keywords/indicators // 2) use Win32 API to search the module list for keywords/indicators // 3) use Win32 API to search the heap space for keywords/indicators MemoryHelperLog.AppendLine("SCAN: Searching target process " + ProcessName + " (PID=" + pid.ToString() + ")..."); MemoryHelperLog.AppendLine("SCAN: Using keyword search list (" + KeywordList.Count + "): '" + string.Join(",", ((string[])KeywordList.ToArray(typeof(string)))) + "'"); //********************************* // PERFORM SCANS //********************************* // 1) use WMI to search the Cmdline for this process for keywords/indicators if (SearchCmdline) { ArrayList cmdlineFindings = new ArrayList(); MemoryHelperLog.Append("SCAN: Searching command line ..."); if (!SearchProcessCmdline(pid, ppid, action, KeywordList, ref cmdlineFindings)) MemoryHelperLog.Append("nothing."); else { MemoryHelperLog.Append(cmdlineFindings.Count + " matches!"); Findings.Add(cmdlineFindings); } MemoryHelperLog.AppendLine(); } // 2) use Win32 API to search the module list for keywords/indicators if (searchModuleList) { ArrayList modListFindings = new ArrayList(); MemoryHelperLog.Append("SCAN: Searching loaded module list ..."); if (!SearchProcessLoadedModuleList(pid, ppid, ProcessName, action, KeywordList, ref modListFindings)) MemoryHelperLog.Append("nothing."); else { MemoryHelperLog.Append(modListFindings.Count + " matches!"); Findings.Add(modListFindings); } MemoryHelperLog.AppendLine(); } // 3) use Win32 API to search the heap space for keywords/indicators if (SearchHeap) { ArrayList heapFindings = new ArrayList(); MemoryHelperLog.AppendLine("SCAN: Searching heap space ..."); if (!SearchProcessHeap((uint)pid, ppid, ProcessName, action, KeywordList, ref heapFindings)) MemoryHelperLog.Append("SCAN: Nothing."); else { MemoryHelperLog.AppendLine("SCAN: "+heapFindings.Count + " matches!"); Findings.Add(heapFindings); } MemoryHelperLog.AppendLine(); } } MemoryHelperLog.AppendLine("SCAN: Done scanning processes, collating results..."); //first find out how many findings we had if (Findings.Count > 0) { MemoryHelperLog.AppendLine("SCAN: There are " + Findings.Count + " memory finding set matches."); int retBufSize = 0, i = 0; foreach (ArrayList ar in Findings) foreach (CwXML.MemorySignatureMatch m in ar) retBufSize++; matches = new CwXML.MemorySignatureMatch[retBufSize]; //loop through all matches and add them to findings foreach (ArrayList ar in Findings) { foreach (CwXML.MemorySignatureMatch m in ar) { matches[i] = new CwXML.MemorySignatureMatch(); matches[i] = m; i++; } } } else matches = new CwXML.MemorySignatureMatch[0]; }