Example #1
0
        /// <summary>
        /// Draw a chart node
        /// </summary>
        /// <param name="node">The node</param>
        /// <param name="position">Where on the chart to draw it</param>
        /// <param name="font">The font to use for text</param>
        /// <returns>If the node was clicked</returns>
        private bool DrawNode(ItemNode node, Vector2 position, ImFontPtr font)
        {
            Vector2 cursor = ImGui.GetCursorScreenPos();

            if (!InFrame(position - cursor))
            {
                return(false);
            }

            var DrawList = ImGui.GetWindowDrawList();

            bool isSelected = node == _selectedNode;

            switch (node.TLtype)
            {
            case Logging.eTimelineEvent.ProcessStart:
            case Logging.eTimelineEvent.ProcessEnd:
            {
                TraceRecord trace = (TraceRecord)node.reference;
                switch (trace.TraceState)
                {
                case TraceRecord.ProcessState.eTerminated:
                    DrawList.AddCircleFilled(position, 18, isSelected ? 0xffDDDDDD : 0xFFFFFFFF);
                    DrawList.AddText(font, 25, position - new Vector2(12.5f, 12.5f), 0xff0000ff, $"{ImGuiController.FA_ICON_COGS}");
                    DrawList.AddText(position + new Vector2(20, -14), 0xff000000, $"Process {trace.PID} (Exited)");
                    break;

                case TraceRecord.ProcessState.eRunning:
                    DrawList.AddCircleFilled(position, 18, isSelected ? 0xffDDDDDD : 0xFFFFFFFF);
                    DrawList.AddText(font, 25, position - new Vector2(12.5f, 12.5f), 0xff00ff00, $"{ImGuiController.FA_ICON_COGS}");
                    DrawList.AddText(position + new Vector2(20, -14), 0xff000000, $"Process {trace.PID} (Running)");
                    break;

                case TraceRecord.ProcessState.eSuspended:
                    DrawList.AddCircleFilled(position, 18, isSelected ? 0xffDDDDDD : 0xFFFFFFFF);
                    DrawList.AddText(font, 25, position - new Vector2(12.5f, 12.5f), 0xff00ffff, $"{ImGuiController.FA_ICON_COGS}");
                    DrawList.AddText(position + new Vector2(20, -14), 0xff000000, $"Process {trace.PID} (Suspended)");
                    break;

                default:
                    Debug.Assert(false, "Bad trace state");
                    break;
                }
            }
            break;

            case Logging.eTimelineEvent.ThreadStart:
            case Logging.eTimelineEvent.ThreadEnd:
            {
                ProtoGraph graph = (ProtoGraph)node.reference;
                if (graph.Terminated)
                {
                    DrawList.AddCircleFilled(position, 18, isSelected ? 0xffDDDDDD : 0xFFFFFFFF);
                    DrawList.AddText(font, 25, position - new Vector2(12.5f, 12.5f), 0xff0000ff, $"{ImGuiController.FA_ICON_COG}");
                    DrawList.AddText(position + new Vector2(20, -14), 0xff000000, $"Thread {graph.ThreadID} (Exited)");
                }
                else
                {
                    DrawList.AddCircleFilled(position, 18, isSelected ? 0xffDDDDDD : 0xFFFFFFFF);
                    DrawList.AddText(font, 25, position - new Vector2(12.5f, 12.5f), 0xff00ff00, $"{ImGuiController.FA_ICON_COG}");
                    DrawList.AddText(position + new Vector2(20, -14), 0xff000000, $"Thread {graph.ThreadID} (Active)");
                }
            }
            break;

            case Logging.eTimelineEvent.APICall:
                Logging.TIMELINE_EVENT apiEvent = (Logging.TIMELINE_EVENT)node.reference;
                Logging.APICALL        apicall  = (Logging.APICALL)apiEvent.Item;
                if (!apicall.APIDetails.HasValue)
                {
                    return(false);
                }
                APIDetailsWin.API_ENTRY details = apicall.APIDetails.Value;

                DrawList.AddCircleFilled(position, 18, isSelected ? 0xffDDDDDD : 0xFFFFFFFF);
                switch (details.FilterType)
                {
                case "File":
                    DrawList.AddText(font, 20, position - new Vector2(10f, 10f), 0xff000000, $"{ImGuiController.FA_ICON_FILECODE}");
                    DrawList.AddText(position + new Vector2(20, -15), 0xff000000, "File Interaction");
                    DrawList.AddText(position + new Vector2(20, 5), 0xff000000, node.label);
                    break;

                case "Registry":
                    DrawList.AddText(font, 25, position - new Vector2(12.5f, 12.5f), 0xff000000, $"{ImGuiController.FA_ICON_SQUAREGRID}");
                    DrawList.AddText(position + new Vector2(20, -15), 0xff000000, "Registry Interaction");
                    DrawList.AddText(position + new Vector2(20, 5), 0xff000000, node.label);
                    break;

                case "Process":
                    DrawList.AddText(font, 25, position - new Vector2(12.5f, 12.5f), 0xff000000, $"{ImGuiController.FA_ICON_COGS}");
                    DrawList.AddText(position + new Vector2(20, -15), 0xff000000, "Process Interaction");
                    DrawList.AddText(position + new Vector2(20, 5), 0xff000000, node.label);
                    break;

                case "Network":
                    DrawList.AddText(font, 25, position - new Vector2(12.5f, 12.5f), 0xff000000, $"{ImGuiController.FA_ICON_NETWORK}");
                    DrawList.AddText(position + new Vector2(20, -15), 0xff000000, "Network Interaction");
                    DrawList.AddText(position + new Vector2(20, 5), 0xff000000, node.label);
                    break;

                default:
                    DrawList.AddText(font, 25, position - new Vector2(12.5f, 12.5f), 0xff000000, $"{ImGuiController.FA_ICON_UP}");
                    DrawList.AddText(position + new Vector2(20, -15), 0xff000000, details.FilterType);
                    DrawList.AddText(position + new Vector2(20, 5), 0xff000000, node.label);
                    break;
                }
                break;


            default:
                DrawList.AddCircleFilled(position, nodeSize, 0xff000000);
                break;
            }


            if (node == _selectedNode)
            {
                DrawList.AddCircle(position, 18, 0xff222222);
            }
            ImGui.SetCursorScreenPos(position - new Vector2(12, 12));
            ImGui.InvisibleButton($"##{position.X}-{position.Y}", new Vector2(25, 25));
            bool clicked = false;

            if (ImGui.IsItemClicked())
            {
                clicked       = true;
                _selectedNode = node;
                if (_selectedNode.TLtype == Logging.eTimelineEvent.APICall)
                {
                    SelectedEntity   = node;
                    SelectedAPIEvent = (Logging.TIMELINE_EVENT)node.reference;
                }
                else
                {
                    SelectedEntity   = null;
                    SelectedAPIEvent = null;
                }
            }

            //Vector2 labelSize = ImGui.CalcTextSize(node.label);
            //DrawList.AddRectFilled(position, position + labelSize, 0xddffffff);
            ImGui.SetCursorScreenPos(cursor);
            return(clicked);
        }
Example #2
0
        private void AddThreadItems(ItemNode?parentProcess, TraceRecord trace)
        {
            string   nodeName     = $"PROCNODE_{trace.PID}_{trace.LaunchedTime}";
            ItemNode?startProcess = null;

            lock (_lock)
            {
                if (!addedNodes.TryGetValue(nodeName, out startProcess))
                {
                    startProcess = new ItemNode(nodeName, Logging.eTimelineEvent.ProcessStart, trace);
                    sbgraph.AddVertex(startProcess);
                    addedNodes[nodeName] = startProcess;
                    if (parentProcess != null)
                    {
                        sbgraph.AddEdge(new Edge <ItemNode>(parentProcess, startProcess));
                    }
                }


                var threads = trace.ProtoGraphs;
                foreach (var thread in threads)
                {
                    string threadName = $"THREADNODE_{thread.TraceData.randID}_{thread.ThreadID}";
                    if (!addedNodes.ContainsKey(threadName))
                    {
                        ItemNode threadNode = new ItemNode(threadName, Logging.eTimelineEvent.ThreadStart, thread);
                        sbgraph.AddVertex(threadNode);
                        sbgraph.AddEdge(new Edge <ItemNode>(startProcess, threadNode));
                        addedNodes[threadName] = threadNode;
                    }
                }

                var timelineEntries = trace.GetTimeLineEntries();
                foreach (Logging.TIMELINE_EVENT timelineEvent in timelineEntries)
                {
                    if (timelineEvent.TimelineEventType == Logging.eTimelineEvent.APICall)
                    {
                        var call = (Logging.APICALL)(timelineEvent.Item);
                        if (call.APIDetails != null)
                        {
                            APIDetailsWin.API_ENTRY apiinfo = call.APIDetails.Value;
                            if (apiinfo.Effects != null)
                            {
                                ProtoGraph?caller = call.Graph;
                                if (caller is null || call.Node is null || call.Index >= call.Node.callRecordsIndexs.Count)
                                {
                                    Logging.RecordLogEvent($"Warning: Call {call.APIDetails.Value.ModuleName}:{call.APIDetails.Value.Symbol} tried to place call {call.Index} on timeline, but only {call.Node?.callRecordsIndexs.Count} recorded");
                                    continue;
                                }
                                int recordsIndex = (int)call.Node.callRecordsIndexs[call.Index];
                                if (recordsIndex >= caller.SymbolCallRecords.Count)
                                {
                                    Logging.RecordLogEvent($"Warning: Call {call.APIDetails.Value.ModuleName}:{call.APIDetails.Value.Symbol} tried to place record {recordsIndex} on timeline, but caller has only {caller.SymbolCallRecords} recorded");
                                    continue;
                                }
                                APICALLDATA APICallRecord = caller.SymbolCallRecords[recordsIndex];
                                string      threadName    = $"THREADNODE_{caller.TraceData.randID}_{caller.ThreadID}";
                                ItemNode    threadNode    = addedNodes[threadName];

                                foreach (APIDetailsWin.InteractionEffect effectBase in apiinfo.Effects)
                                {
                                    switch (effectBase)
                                    {
                                    case APIDetailsWin.LinkReferenceEffect linkEffect:
                                    {
                                        APIDetailsWin.API_PARAM_ENTRY entityParamRecord    = apiinfo.LoggedParams[linkEffect.EntityIndex];
                                        APIDetailsWin.API_PARAM_ENTRY referenceParamRecord = apiinfo.LoggedParams[linkEffect.ReferenceIndex];

                                        int entityParamLoggedIndex    = APICallRecord.argList.FindIndex(x => x.Item1 == entityParamRecord.Index);
                                        int referenceParamLoggedIndex = APICallRecord.argList.FindIndex(x => x.Item1 == referenceParamRecord.Index);

                                        if (entityParamLoggedIndex == -1 || referenceParamLoggedIndex == -1)
                                        {
                                            string error = $"API call record for {apiinfo.ModuleName}:{apiinfo.Symbol} [LinkReference] didn't have correct parameters. The instrumentation library or apidata file may not match.";
                                            timelineEvent.MetaError = error;
                                            Logging.RecordLogEvent(error, Logging.LogFilterType.Debug);
                                            break;
                                        }

                                        if (!_interactionEntities.TryGetValue(entityParamRecord.EntityType, out Dictionary <string, ItemNode>?entityDict))
                                        {
                                            entityDict = new Dictionary <string, ItemNode>();
                                            _interactionEntities.Add(entityParamRecord.EntityType, entityDict);
                                        }
                                        string entityString = APICallRecord.argList[entityParamLoggedIndex].Item2;

                                        ItemNode entityNode;
                                        if (!entityDict.ContainsKey(entityString))
                                        {
                                            entityNode = new ItemNode(entityString, Logging.eTimelineEvent.APICall, timelineEvent);
                                            entityDict.Add(entityString, entityNode);
                                            sbgraph.AddVertex(entityNode);
                                            addedNodes[entityString] = entityNode;
                                        }
                                        else
                                        {
                                            entityNode = addedNodes[entityString];
                                        }
                                        AddAPIEdge(threadNode, entityNode, apiinfo.Label);

                                        if (!_timelineEventEntities.TryGetValue(timelineEvent, out ItemNode? existingEntity))
                                        {
                                            _timelineEventEntities.Add(timelineEvent, entityNode);
                                        }
                                        Debug.Assert(existingEntity == null || existingEntity == entityNode);


                                        //link this entity to the reference the api all created (eg associate a file path with the file handle that 'CreateFile' created)
                                        string referenceString = APICallRecord.argList[referenceParamLoggedIndex].Item2;
                                        if (referenceParamRecord.NoCase)
                                        {
                                            referenceString = referenceString.ToLower();
                                        }

                                        if (!_interactionEntityReferences.ContainsKey(referenceParamRecord.RawType))
                                        {
                                            _interactionEntityReferences.Add(referenceParamRecord.RawType, new Dictionary <string, ItemNode>());
                                        }
                                        if (!_interactionEntityReferences[referenceParamRecord.RawType].ContainsKey(referenceString))
                                        {
                                            _interactionEntityReferences[referenceParamRecord.RawType].Add(referenceString, entityNode);
                                        }

                                        break;
                                    }

                                    // this api performs some action on a refererence to an entity
                                    // record this as a label on the edge and link this event to the entity
                                    case APIDetailsWin.UseReferenceEffect useEffect:
                                    {
                                        APIDetailsWin.API_PARAM_ENTRY referenceParamRecord = apiinfo.LoggedParams[useEffect.ReferenceIndex];
                                        int referenceParamLoggedIndex = APICallRecord.argList.FindIndex(x => x.Item1 == referenceParamRecord.Index);
                                        if (referenceParamLoggedIndex == -1)
                                        {
                                            timelineEvent.MetaError = $"API call record for {apiinfo.ModuleName}:{apiinfo.Symbol} [UseReference] didn't have correct parameters";
                                            Logging.RecordLogEvent(timelineEvent.MetaError, Logging.LogFilterType.Debug);
                                            break;
                                        }

                                        string referenceString = APICallRecord.argList[referenceParamLoggedIndex].Item2;
                                        if (referenceParamRecord.NoCase)
                                        {
                                            referenceString = referenceString.ToLower();
                                        }

                                        bool resolvedReference = false;
                                        if (_interactionEntityReferences.TryGetValue(referenceParamRecord.RawType, out Dictionary <string, ItemNode>?typeEntityList))
                                        {
                                            if (typeEntityList.TryGetValue(referenceString, out ItemNode? entityNode))
                                            {
                                                resolvedReference = true;
                                                if (!_timelineEventEntities.ContainsKey(timelineEvent))
                                                {
                                                    _timelineEventEntities.Add(timelineEvent, entityNode);
                                                }

                                                AddAPIEdge(threadNode, entityNode, apiinfo.Label);
                                            }
                                        }
                                        if (!resolvedReference)
                                        {
                                            timelineEvent.MetaError = $"API call record for {apiinfo.ModuleName}:{apiinfo.Symbol} [UseReference] reference was not linked to an entity ({referenceString})";
                                            Logging.RecordLogEvent(timelineEvent.MetaError, Logging.LogFilterType.Debug);
                                        }
                                        break;
                                    }

                                    // this api invalidates a reference
                                    // remove the link between the reference and the entity it references
                                    case APIDetailsWin.DestroyReferenceEffect destroyEffect:
                                    {
                                        APIDetailsWin.API_PARAM_ENTRY referenceParamRecord = apiinfo.LoggedParams[destroyEffect.ReferenceIndex];
                                        int referenceParamLoggedIndex = APICallRecord.argList.FindIndex(x => x.Item1 == referenceParamRecord.Index);
                                        if (referenceParamLoggedIndex == -1)
                                        {
                                            timelineEvent.MetaError = $"API call record for {apiinfo.ModuleName}:{apiinfo.Symbol} [DestroyReference] didn't have correct parameters";
                                            Logging.RecordLogEvent(timelineEvent.MetaError, Logging.LogFilterType.Debug);
                                            break;
                                        }

                                        string referenceString = APICallRecord.argList[referenceParamLoggedIndex].Item2;
                                        if (referenceParamRecord.NoCase)
                                        {
                                            referenceString = referenceString.ToLower();
                                        }

                                        bool resolvedReference = false;
                                        if (_interactionEntityReferences.TryGetValue(referenceParamRecord.RawType, out Dictionary <string, ItemNode>?typeEntityList))
                                        {
                                            if (typeEntityList.TryGetValue(referenceString, out ItemNode? entityNode))
                                            {
                                                resolvedReference = true;
                                                if (!_timelineEventEntities.ContainsKey(timelineEvent))
                                                {
                                                    _timelineEventEntities.Add(timelineEvent, entityNode);
                                                }

                                                AddAPIEdge(threadNode, entityNode, apiinfo.Label);
                                                typeEntityList.Remove(referenceString);
                                            }
                                        }
                                        if (!resolvedReference)
                                        {
                                            timelineEvent.MetaError = $"API call record for {apiinfo.ModuleName}:{apiinfo.Symbol} [DestroyReference] reference was not linked to an entity ({referenceString})";
                                            Logging.RecordLogEvent(timelineEvent.MetaError, Logging.LogFilterType.Debug);
                                        }

                                        break;
                                    }

                                    default:
                                        timelineEvent.MetaError = $"API call record for {apiinfo.ModuleName}:{apiinfo.Symbol}: had invalid effect {effectBase}";
                                        Logging.RecordLogEvent(timelineEvent.MetaError, Logging.LogFilterType.Debug);
                                        break;
                                    }
                                }
                            }
                        }
                    }
                }

                if (startProcess is not null)
                {
                    foreach (var child in trace.GetChildren())
                    {
                        string childName = $"PROCNODE_{child.PID}_{child.LaunchedTime}";
                        if (!addedNodes.TryGetValue(childName, out ItemNode? childProcess) || childProcess is null)
                        {
                            childProcess = new ItemNode(childName, Logging.eTimelineEvent.ProcessStart, child);
                            sbgraph.AddVertex(childProcess);
                            addedNodes[childName] = childProcess;

                            sbgraph.AddEdge(new Edge <ItemNode>(startProcess, childProcess));
                        }
                        AddThreadItems(parentProcess, child);
                    }
                }
            }
        }