예제 #1
0
        /////////////////////////////////////////////////////
        //                                                 //
        // 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;
        }
예제 #2
0
            /////////////////////////////////////////////////////
            //                                                 //
            // 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;
            }
예제 #3
0
            /////////////////////////////////////////////////////
            //                                                 //
            // 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;
            }
예제 #4
0
            /////////////////////////////////////////////////////
            //                                                 //
            // 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;
            }
예제 #5
0
            /////////////////////////////////////////////////////
            //                                                 //
            // 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];
            }