/** * @file debugger.cpp * @author Sina Karvandi (sina@rayanfam.com) * @brief Interpret general fields * @details * @version 0.1 * @date 2020-05-27 * * @copyright This project is released under the GNU Public License v3. * */ #include "pch.h" // // Global Variables // extern UINT64 g_EventTag; extern LIST_ENTRY g_EventTrace; extern BOOLEAN g_EventTraceInitialized; extern BOOLEAN g_BreakPrintingOutput; extern BOOLEAN g_AutoUnpause; /** * @brief shows the error message * * @param Error * @return BOOLEAN */ BOOLEAN ShowErrorMessage(UINT32 Error) { switch (Error) { case DEBUGEER_ERROR_TAG_NOT_EXISTS: ShowMessages("err, tag not found (%x)\n", Error); break; case DEBUGEER_ERROR_INVALID_ACTION_TYPE: ShowMessages("err, invalid action type (%x)\n", Error); break; case DEBUGEER_ERROR_ACTION_BUFFER_SIZE_IS_ZERO: ShowMessages("err, action buffer size is zero (%x)\n", Error); break; case DEBUGEER_ERROR_EVENT_TYPE_IS_INVALID: ShowMessages("err, event type is invalid (%x)\n", Error); break; case DEBUGEER_ERROR_UNABLE_TO_CREATE_EVENT: ShowMessages("err, unable to create event (%x)\n", Error); break; case DEBUGEER_ERROR_INVALID_ADDRESS: ShowMessages("err, invalid address (%x)\n", Error); break; case DEBUGEER_ERROR_INVALID_CORE_ID: ShowMessages("err, invalid core id (%x)\n", Error); break; case DEBUGEER_ERROR_EXCEPTION_INDEX_EXCEED_FIRST_32_ENTRIES: ShowMessages("err, exception index exceed first 32 entries (%x)\n", Error); break; case DEBUGEER_ERROR_INTERRUPT_INDEX_IS_NOT_VALID: ShowMessages("err, interrupt index is not valid (%x)\n", Error); break; case DEBUGEER_ERROR_UNABLE_TO_HIDE_OR_UNHIDE_DEBUGGER: ShowMessages("err, unable to hide or unhide debugger (%x)\n", Error); break; case DEBUGEER_ERROR_DEBUGGER_ALREADY_UHIDE: ShowMessages("err, debugger already unhide (%x)\n", Error); break; case DEBUGGER_ERROR_EDIT_MEMORY_STATUS_INVALID_PARAMETER: ShowMessages("err, edit memeory request has invalid parameters (%x)\n", Error); break; case DEBUGGER_ERROR_EDIT_MEMORY_STATUS_INVALID_ADDRESS_BASED_ON_CURRENT_PROCESS: ShowMessages("err, edit memory request has invalid address based on " "current process layout (%x)\n", Error); break; case DEBUGGER_ERROR_EDIT_MEMORY_STATUS_INVALID_ADDRESS_BASED_ON_OTHER_PROCESS: ShowMessages("err, edit memory request has invalid address based on other " "process layout (%x)\n", Error); break; case DEBUGGER_ERROR_MODIFY_EVENTS_INVALID_TAG: ShowMessages("err, modify event with invalid tag (%x)\n", Error); break; case DEBUGGER_ERROR_MODIFY_EVENTS_INVALID_TYPE_OF_ACTION: ShowMessages("err, modify event with invalid type of action (%x)\n", Error); break; case DEBUGGER_ERROR_STEPPING_INVALID_PARAMETER: ShowMessages("err, invalid parameter passsed to stepping core. (%x)\n", Error); break; case DEBUGGER_ERROR_STEPPINGS_EITHER_THREAD_NOT_FOUND_OR_DISABLED: ShowMessages( "err, target thread not found or the thread is disabled (%x)\n", Error); break; default: ShowMessages("err, error not found (%x)\n", Error); return FALSE; break; } return TRUE; } /** * @brief Check whether the tag exists or not, if the tag is * DEBUGGER_MODIFY_EVENTS_APPLY_TO_ALL_TAG then if we find just one event, it * also means that the tag is found * * @param Tag * @return BOOLEAN */ BOOLEAN IsTagExist(UINT64 Tag) { PLIST_ENTRY TempList = 0; PDEBUGGER_GENERAL_EVENT_DETAIL CommandDetail = {0}; if (!g_EventTraceInitialized) { return FALSE; } TempList = &g_EventTrace; while (&g_EventTrace != TempList->Blink) { TempList = TempList->Blink; CommandDetail = CONTAINING_RECORD(TempList, DEBUGGER_GENERAL_EVENT_DETAIL, CommandsEventList); if (CommandDetail->Tag == Tag || Tag == DEBUGGER_MODIFY_EVENTS_APPLY_TO_ALL_TAG) { return TRUE; } } // // Not found // return FALSE; } /** * @brief Interpret conditions (if an event has condition) and custom code * @details If this function returns true then it means that there is a condtion * or code buffer in this command split and the details are returned in the * input structure * * @param SplittedCommand the initialized command that are splitted by space * @param IsConditionBuffer is it a condition buffer or a custom code buffer * @param BufferAddrss the address that the allocated buffer will be saved on it * @param BufferLength the length of the buffer * @return BOOLEAN shows whether the interpret was successful (true) or not * successful (false) */ BOOLEAN InterpretConditionsAndCodes(vector *SplittedCommand, BOOLEAN IsConditionBuffer, PUINT64 BufferAddrss, PUINT32 BufferLength) { BOOLEAN IsTextVisited = FALSE; BOOLEAN IsInState = FALSE; BOOLEAN IsEnded = FALSE; string Temp; string AppendedFinalBuffer; vector SaveBuffer; vector ParsedBytes; vector IndexesToRemove; UCHAR *FinalBuffer; int Index = 0; for (auto Section : *SplittedCommand) { Index++; if (IsInState) { // // Check if the buffer is ended or not // if (!Section.compare("}")) { // // Save to remove this string from the command // IndexesToRemove.push_back(Index); IsEnded = TRUE; break; } // // Check if the condition is end or not // if (HasEnding(Section, "}")) { // // Save to remove this string from the command // IndexesToRemove.push_back(Index); // // remove the last character and append it to the ConditionBuffer // SaveBuffer.push_back(Section.substr(0, Section.size() - 1)); IsEnded = TRUE; break; } // // Save to remove this string from the command // IndexesToRemove.push_back(Index); // // Add the codes into condition bi // SaveBuffer.push_back(Section); // // We want to stay in this condition // continue; } if (IsTextVisited && !Section.compare("{")) { // // Save to remove this string from the command // IndexesToRemove.push_back(Index); IsInState = TRUE; continue; } if (IsTextVisited && Section.rfind("{", 0) == 0) { // // Section starts with { // // // Check if it ends with } // if (HasEnding(Section, "}")) { // // Save to remove this string from the command // IndexesToRemove.push_back(Index); Temp = Section.erase(0, 1); SaveBuffer.push_back(Temp.substr(0, Temp.size() - 1)); IsEnded = TRUE; break; } // // Save to remove this string from the command // IndexesToRemove.push_back(Index); SaveBuffer.push_back(Section.erase(0, 1)); IsInState = TRUE; continue; } if (IsConditionBuffer) { if (!Section.compare("condition")) { // // Save to remove this string from the command // IndexesToRemove.push_back(Index); IsTextVisited = TRUE; continue; } } else { // // It's code // if (!Section.compare("code")) { // // Save to remove this string from the command // IndexesToRemove.push_back(Index); IsTextVisited = TRUE; continue; } } if (IsConditionBuffer) { if (!Section.compare("condition{")) { // // Save to remove this string from the command // IndexesToRemove.push_back(Index); IsTextVisited = TRUE; IsInState = TRUE; continue; } } else { // // It's code // if (!Section.compare("code{")) { // // Save to remove this string from the command // IndexesToRemove.push_back(Index); IsTextVisited = TRUE; IsInState = TRUE; continue; } } if (IsConditionBuffer) { if (Section.rfind("condition{", 0) == 0) { // // Save to remove this string from the command // IndexesToRemove.push_back(Index); IsTextVisited = TRUE; IsInState = TRUE; if (!HasEnding(Section, "}")) { // // Section starts with condition{ // SaveBuffer.push_back(Section.erase(0, 10)); continue; } else { // // remove the last character and first character append it to the // ConditionBuffer // Temp = Section.erase(0, 10); SaveBuffer.push_back(Temp.substr(0, Temp.size() - 1)); IsEnded = TRUE; break; } } } else { // // It's a code // if (Section.rfind("code{", 0) == 0) { // // Save to remove this string from the command // IndexesToRemove.push_back(Index); IsTextVisited = TRUE; IsInState = TRUE; if (!HasEnding(Section, "}")) { // // Section starts with condition{ // SaveBuffer.push_back(Section.erase(0, 5)); continue; } else { // // remove the last character and first character append it to the // ConditionBuffer // Temp = Section.erase(0, 5); SaveBuffer.push_back(Temp.substr(0, Temp.size() - 1)); IsEnded = TRUE; break; } } } } // // Now we have everything in condition buffer // Check to see if it is empty or not // if (SaveBuffer.size() == 0) { // // Nothing in condition buffer, return zero // return FALSE; } // // Check if we see '}' at the end // if (!IsEnded) { // // Nothing in condition buffer, return zero // return FALSE; } // // Append a 'ret' at the end of the buffer // SaveBuffer.push_back("c3"); // // If we reach here then there is sth in condition buffer // for (auto Section : SaveBuffer) { // // Check if the section is started with '0x' // if (Section.rfind("0x", 0) == 0 || Section.rfind("0X", 0) == 0 || Section.rfind("\\x", 0) == 0 || Section.rfind("\\X", 0) == 0) { Temp = Section.erase(0, 2); } else if (Section.rfind("x", 0) == 0 || Section.rfind("X", 0) == 0) { Temp = Section.erase(0, 1); } else { Temp = Section; } // // replace \x s // ReplaceAll(Temp, "\\x", ""); // // check if the buffer is aligned to 2 // if (Temp.size() % 2 != 0) { // // Add a zero to the start of the buffer // Temp.insert(0, 1, '0'); } if (!IsHexNotation(Temp)) { ShowMessages("Please enter condition code in a hex notation.\n"); return FALSE; } AppendedFinalBuffer.append(Temp); } // // Convert it to vectored bytes // ParsedBytes = HexToBytes(AppendedFinalBuffer); // // Convert to a contigues buffer // FinalBuffer = (unsigned char *)malloc(ParsedBytes.size()); std::copy(ParsedBytes.begin(), ParsedBytes.end(), FinalBuffer); // // Set the buffer and length // *BufferAddrss = (UINT64)FinalBuffer; *BufferLength = ParsedBytes.size(); // // Removing the code or condition indexes from the command // int NewIndexToRemove = 0; for (auto IndexToRemove : IndexesToRemove) { NewIndexToRemove++; SplittedCommand->erase(SplittedCommand->begin() + (IndexToRemove - NewIndexToRemove)); } return TRUE; } /** * @brief Register the event to the kernel * * @param Event the event structure to send * @param EventBufferLength the buffer length of event * @return BOOLEAN if the request was successful then true * if the request was not successful then false */ BOOLEAN SendEventToKernel(PDEBUGGER_GENERAL_EVENT_DETAIL Event, UINT32 EventBufferLength) { BOOL Status; ULONG ReturnedLength; DEBUGGER_EVENT_AND_ACTION_REG_BUFFER ReturnedBuffer = {0}; // // Test // /* ShowMessages("Tag : %llx\n", Event->Tag); ShowMessages("Command String : %s\n", Event->CommandStringBuffer); ShowMessages("CoreId : 0x%x\n", Event->CoreId); ShowMessages("Pid : 0x%x\n", Event->ProcessId); ShowMessages("Optional Param 1 : %llx\n", Event->OptionalParam1); ShowMessages("Optional Param 2 : %llx\n", Event->OptionalParam2); ShowMessages("Optional Param 3 : %llx\n", Event->OptionalParam3); ShowMessages("Optional Param 4 : %llx\n", Event->OptionalParam4); ShowMessages("Count of Actions : %d\n", Event->CountOfActions); ShowMessages("Event Type : %d\n", Event->EventType); return TRUE; */ if (!g_DeviceHandle) { ShowMessages("Handle not found, probably the driver is not loaded. Did you " "use 'load' command?\n"); return FALSE; } // // Send IOCTL // Status = DeviceIoControl(g_DeviceHandle, // Handle to device IOCTL_DEBUGGER_REGISTER_EVENT, // IO Control code Event, // Input Buffer to driver. EventBufferLength, // Input buffer length &ReturnedBuffer, // Output Buffer from driver. sizeof(DEBUGGER_EVENT_AND_ACTION_REG_BUFFER), // Length // of // output // buffer // in // bytes. &ReturnedLength, // Bytes placed in buffer. NULL // synchronous call ); if (!Status) { ShowMessages("ioctl failed with code 0x%x\n", GetLastError()); return FALSE; } if (ReturnedBuffer.IsSuccessful && ReturnedBuffer.Error == 0) { // // The returned buffer shows that this event was // successful and now we can add it to the list of // events // InsertHeadList(&g_EventTrace, &(Event->CommandsEventList)); // // Check for auto-unpause mode // if (g_BreakPrintingOutput && g_AutoUnpause) { // // Allow debugger to show its contents // ShowMessages("\n"); g_BreakPrintingOutput = FALSE; } } else { // // It was not successful, we have to free the buffers for command // string and buffer for the event itself // free(Event->CommandStringBuffer); free(Event); // // Show the error // if (ReturnedBuffer.Error != 0) { ShowErrorMessage(ReturnedBuffer.Error); } } return TRUE; } /** * @brief Register the action to the event * * @param Action the action buffer structure * @param ActionsBufferLength length of action buffer * @return BOOLEAN BOOLEAN if the request was successful then true * if the request was not successful then false */ BOOLEAN RegisterActionToEvent(PDEBUGGER_GENERAL_ACTION Action, UINT32 ActionsBufferLength) { BOOL Status; ULONG ReturnedLength; DEBUGGER_EVENT_AND_ACTION_REG_BUFFER ReturnedBuffer = {0}; // // Test // /* ShowMessages("Tag : %llx\n", Action->EventTag); ShowMessages("Action Type : %d\n", Action->ActionType); ShowMessages("Custom Code Buffer Size : 0x%x\n", Action->CustomCodeBufferSize); return TRUE; */ if (!g_DeviceHandle) { ShowMessages("Handle not found, probably the driver is not loaded. Did you " "use 'load' command?\n"); return FALSE; } // // Send IOCTL // Status = DeviceIoControl(g_DeviceHandle, // Handle to device IOCTL_DEBUGGER_ADD_ACTION_TO_EVENT, // IO Control code Action, // Input Buffer to driver. ActionsBufferLength, // Input buffer length &ReturnedBuffer, // Output Buffer from driver. sizeof(DEBUGGER_EVENT_AND_ACTION_REG_BUFFER), // Length // of // output // buffer // in // bytes. &ReturnedLength, // Bytes placed in buffer. NULL // synchronous call ); if (!Status) { ShowMessages("ioctl failed with code 0x%x\n", GetLastError()); return FALSE; } // // The action buffer is allocated once using malloc and // is not used anymore, we have to free it // free(Action); return TRUE; } /** * @brief Get the New Debugger Event Tag object and increase the * global variable for tag * * @return UINT64 */ UINT64 GetNewDebuggerEventTag() { return g_EventTag++; } /** * @brief Interpret general event fields * * @param SplittedCommand the commands that was splitted by space * @param EventType type of event * @param EventDetailsToFill a pointer address that will be filled * by event detail buffer * @param EventBufferLength a pointer the receives the buffer length * of the event * @param ActionDetailsToFill a pointer address that will be filled * by action detail buffer * @param ActionBufferLength a pointer the receives the buffer length * of the action * @return BOOLEAN If this function returns true then it means that there * was no error in parsing the general event details */ BOOLEAN InterpretGeneralEventAndActionsFields( vector *SplittedCommand, DEBUGGER_EVENT_TYPE_ENUM EventType, PDEBUGGER_GENERAL_EVENT_DETAIL *EventDetailsToFill, PUINT32 EventBufferLength, PDEBUGGER_GENERAL_ACTION *ActionDetailsToFill, PUINT32 ActionBufferLength) { PDEBUGGER_GENERAL_EVENT_DETAIL TempEvent; UINT64 ConditionBufferAddress; UINT32 ConditionBufferLength = 0; UINT64 CodeBufferAddress; UINT32 CodeBufferLength = 0; UINT32 LengthOfEventBuffer = 0; string CommandString; BOOLEAN HasConditionBuffer = FALSE; BOOLEAN HasCodeBuffer = FALSE; BOOLEAN IsNextCommandPid = FALSE; BOOLEAN IsNextCommandCoreId = FALSE; BOOLEAN IsNextCommandBufferSize = FALSE; UINT32 CoreId; UINT32 ProcessId; UINT32 RequestBuffer = 0; vector IndexesToRemove; int NewIndexToRemove = 0; int Index = 0; // // Create a command string to show in the history // for (auto Section : *SplittedCommand) { CommandString.append(Section); CommandString.append(" "); } // // Compute the size of buffer + 1 null for the end of buffer // UINT64 BufferOfCommandStringLength = CommandString.size() + 1; // // Allocate Buffer and zero for command to the buffer // PVOID BufferOfCommandString = malloc(BufferOfCommandStringLength); RtlZeroMemory(BufferOfCommandString, BufferOfCommandStringLength); // // Copy the string to the buffer // memcpy(BufferOfCommandString, CommandString.c_str(), CommandString.size()); // // Check if there is a condition buffer in the command // if (!InterpretConditionsAndCodes(SplittedCommand, TRUE, &ConditionBufferAddress, &ConditionBufferLength)) { // // Indicate condition is not available // HasConditionBuffer = FALSE; // // ShowMessages("\nNo condition!\n"); // } else { // // Indicate condition is available // HasConditionBuffer = TRUE; /* ShowMessages( "\n========================= Condition =========================\n"); ShowMessages( "\nUINT64 DebuggerCheckForCondition(PGUEST_REGS Regs_RCX, PVOID " "Context_RDX)\n{\n"); // // Disassemble the buffer // HyperDbgDisassembler64((unsigned char *)ConditionBufferAddress, 0x0, ConditionBufferLength); ShowMessages("}\n\n"); ShowMessages( "=============================================================\n"); */ } // // Check if there is a code buffer in the command // if (!InterpretConditionsAndCodes(SplittedCommand, FALSE, &CodeBufferAddress, &CodeBufferLength)) { // // Indicate code is not available // HasCodeBuffer = FALSE; // // ShowMessages("\nNo custom code!\n"); // } else { // // Indicate code is available // HasCodeBuffer = TRUE; /* ShowMessages( "\n========================= Code =========================\n"); ShowMessages("\nPVOID DebuggerRunCustomCodeFunc(PVOID " "PreAllocatedBufferAddress_RCX, " "PGUEST_REGS Regs_RDX, PVOID Context_R8)\n{\n"); // // Disassemble the buffer // HyperDbgDisassembler64((unsigned char *)CodeBufferAddress, 0x0, CodeBufferLength); ShowMessages("}\n\n"); ShowMessages( "=============================================================\n"); */ } // // Create action and event based on previously parsed buffers // (DEBUGGER_GENERAL_ACTION) // // Allocate the buffer (with ConditionBufferLength and CodeBufferLength) // /* Layout of Buffer ________________________________ | | | DEBUGGER_GENERAL_EVENT_DETAIL | | | |________________________________| | | | Condition Buffer | | | |________________________________| | | | DEBUGGER_GENERAL_ACTION | | | |________________________________| | | | Condition Custom Code | | | |________________________________| */ LengthOfEventBuffer = sizeof(DEBUGGER_GENERAL_EVENT_DETAIL) + ConditionBufferLength; TempEvent = (PDEBUGGER_GENERAL_EVENT_DETAIL)malloc(LengthOfEventBuffer); RtlZeroMemory(TempEvent, LengthOfEventBuffer); // // Check if buffer is availabe // if (TempEvent == NULL) { // // The heap is not available // free(BufferOfCommandString); return FALSE; } // // Event is enabled by default when created // TempEvent->IsEnabled = TRUE; // // Get a new tag for it // TempEvent->Tag = GetNewDebuggerEventTag(); // // Set the core Id and Process Id to all cores and all // processes, next time we check whether the user needs // a special core or a special process then we change it // TempEvent->CoreId = DEBUGGER_EVENT_APPLY_TO_ALL_CORES; TempEvent->ProcessId = DEBUGGER_EVENT_APPLY_TO_ALL_PROCESSES; // // Set the event type // TempEvent->EventType = EventType; // // Get the current time // TempEvent->CreationTime = time(0); // // Set buffer string command // TempEvent->CommandStringBuffer = BufferOfCommandString; // // Fill the buffer of condition for event // if (HasConditionBuffer) { memcpy((PVOID)((UINT64)TempEvent + sizeof(DEBUGGER_GENERAL_EVENT_DETAIL)), (PVOID)ConditionBufferAddress, ConditionBufferLength); // // Set the size of the buffer for event condition // TempEvent->ConditionBufferSize = ConditionBufferLength; } // // Allocate the Action (THIS ACTION BUFFER WILL BE FREED WHEN WE SENT IT TO // THE KERNEL AND RETURNED FROM THE KERNEL AS WE DON'T NEED IT ANYMORE) // UINT32 LengthOfActionBuffer = sizeof(DEBUGGER_GENERAL_ACTION) + CodeBufferLength; PDEBUGGER_GENERAL_ACTION TempAction = (PDEBUGGER_GENERAL_ACTION)malloc(LengthOfActionBuffer); RtlZeroMemory(TempAction, LengthOfActionBuffer); // // Fill the buffer of custom code for action // if (HasCodeBuffer) { memcpy((PVOID)((UINT64)TempAction + sizeof(DEBUGGER_GENERAL_ACTION)), (PVOID)CodeBufferAddress, CodeBufferLength); // // Set the action Tag // TempAction->EventTag = TempEvent->Tag; // // Set the action type // TempAction->ActionType = RUN_CUSTOM_CODE; // // Set the action buffer size // TempAction->CustomCodeBufferSize = CodeBufferLength; // // Increase the count of actions // TempEvent->CountOfActions = TempEvent->CountOfActions + 1; } // // Interpret rest of the command // for (auto Section : *SplittedCommand) { Index++; if (IsNextCommandBufferSize) { if (!ConvertStringToUInt32(Section, &RequestBuffer)) { return FALSE; } else { // // Set the specific requested buffer size // TempAction->PreAllocatedBuffer = RequestBuffer; } IsNextCommandBufferSize = FALSE; // // Add index to remove it from the command // IndexesToRemove.push_back(Index); continue; } if (IsNextCommandPid) { if (!ConvertStringToUInt32(Section, &ProcessId)) { free(BufferOfCommandString); free(TempEvent); free(TempAction); return FALSE; } else { // // Set the specific process id // TempEvent->ProcessId = ProcessId; } IsNextCommandPid = FALSE; // // Add index to remove it from the command // IndexesToRemove.push_back(Index); continue; } if (IsNextCommandCoreId) { if (!ConvertStringToUInt32(Section, &CoreId)) { free(BufferOfCommandString); free(TempEvent); free(TempAction); return FALSE; } else { // // Set the specific core id // TempEvent->CoreId = CoreId; } IsNextCommandCoreId = FALSE; // // Add index to remove it from the command // IndexesToRemove.push_back(Index); continue; } if (!Section.compare("pid")) { IsNextCommandPid = TRUE; // // Add index to remove it from the command // IndexesToRemove.push_back(Index); continue; } if (!Section.compare("core")) { IsNextCommandCoreId = TRUE; // // Add index to remove it from the command // IndexesToRemove.push_back(Index); continue; } if (!Section.compare("buffer")) { IsNextCommandBufferSize = TRUE; // // Add index to remove it from the command // IndexesToRemove.push_back(Index); continue; } } // // Additional validation // if (IsNextCommandCoreId) { ShowMessages("error : please specify a value for 'core'\n\n"); free(BufferOfCommandString); free(TempEvent); free(TempAction); return FALSE; } if (IsNextCommandPid) { ShowMessages("error : please specify a value for 'pid'\n\n"); free(BufferOfCommandString); free(TempEvent); free(TempAction); return FALSE; } if (IsNextCommandBufferSize) { ShowMessages("errlr : please specify a value for 'buffer'\n\n"); free(BufferOfCommandString); free(TempEvent); free(TempAction); return FALSE; } // // Fill the address and length of event before release // *EventDetailsToFill = TempEvent; *EventBufferLength = LengthOfEventBuffer; // // Fill the address and length of action before release // *ActionDetailsToFill = TempAction; *ActionBufferLength = LengthOfActionBuffer; // // Remove the command that we interpreted above from the command // for (auto IndexToRemove : IndexesToRemove) { NewIndexToRemove++; SplittedCommand->erase(SplittedCommand->begin() + (IndexToRemove - NewIndexToRemove)); } // // Check if list is initialized or not // if (!g_EventTraceInitialized) { InitializeListHead(&g_EventTrace); g_EventTraceInitialized = TRUE; } // // Add the event to the trace list // UPDATE : if we add it here, then if the event was not // successful then still the event shows this event, so // we have to add it lated // // InsertHeadList(&g_EventTrace, &(TempEvent->CommandsEventList)); // // Everything is ok, let's return TRUE // return TRUE; }