mirror of
https://github.com/HyperDbg/HyperDbg.git
synced 2026-07-13 19:19:38 +00:00
311 lines
8.4 KiB
C++
311 lines
8.4 KiB
C++
/**
|
|
* @file test-parser.cpp
|
|
* @author Sina Karvandi (sina@hyperdbg.org)
|
|
* @brief Perfrom test on command parser
|
|
* @details
|
|
* @version 0.11
|
|
* @date 2024-08-11
|
|
*
|
|
* @copyright This project is released under the GNU Public License v3.
|
|
*
|
|
*/
|
|
#include "pch.h"
|
|
|
|
typedef char ** CHAR_PTR_PTR; // Define CHAR_PTR_PTR as a char**
|
|
|
|
/**
|
|
* @brief Create an array of strings from a vector of strings
|
|
* @param testCases The vector of strings to copy
|
|
*
|
|
* @return A pointer to the array of strings
|
|
*/
|
|
CHAR_PTR_PTR
|
|
createTestCaseArray(const std::vector<std::string> & testCases)
|
|
{
|
|
//
|
|
// Allocate memory for the array of pointers (size: number of test cases)
|
|
//
|
|
CHAR_PTR_PTR testCaseArray = (CHAR_PTR_PTR)malloc(testCases.size() * sizeof(UINT64));
|
|
|
|
//
|
|
// Allocate memory for each string and copy the content
|
|
//
|
|
for (size_t i = 0; i < testCases.size(); ++i)
|
|
{
|
|
testCaseArray[i] = (char *)malloc(testCases[i].length() + 1); // +1 for the null terminator
|
|
std::strcpy(testCaseArray[i], testCases[i].c_str());
|
|
}
|
|
|
|
return testCaseArray;
|
|
}
|
|
|
|
/**
|
|
* @brief Free the memory allocated for the test case array
|
|
* @param testCaseArray The array of pointers to free
|
|
* @param size The size of the array
|
|
*
|
|
* @return VOID
|
|
*/
|
|
VOID
|
|
freeTestCaseArray(CHAR_PTR_PTR testCaseArray, size_t size)
|
|
{
|
|
//
|
|
// Free each string
|
|
//
|
|
for (size_t i = 0; i < size; ++i)
|
|
{
|
|
free(testCaseArray[i]);
|
|
}
|
|
|
|
//
|
|
// Free the array of pointers
|
|
//
|
|
free(testCaseArray);
|
|
}
|
|
|
|
/**
|
|
* @brief Parse the test cases from the file
|
|
* @param filename The name of the file to parse
|
|
*
|
|
* @return A vector of pairs, where each pair contains a command and a vector of tokens
|
|
*/
|
|
std::vector<std::pair<std::string, std::vector<std::string>>>
|
|
parseTestCases(const std::string & filename)
|
|
{
|
|
std::ifstream file(filename);
|
|
std::string line;
|
|
std::string command;
|
|
std::string currentToken;
|
|
std::vector<std::string> tokens;
|
|
std::vector<std::pair<std::string, std::vector<std::string>>> testCases;
|
|
bool isCommand = false;
|
|
bool addNewline = false;
|
|
|
|
const std::string tokenDelimiter = "----------------------------------";
|
|
const std::string commandDelimiter = "_____________________________________________________________";
|
|
|
|
while (std::getline(file, line))
|
|
{
|
|
if (line == commandDelimiter)
|
|
{
|
|
//
|
|
// No new line is needed after this token
|
|
//
|
|
addNewline = false;
|
|
|
|
//
|
|
// Save the previous command and its tokens if any
|
|
//
|
|
if (!command.empty())
|
|
{
|
|
if (!currentToken.empty())
|
|
{
|
|
tokens.push_back(currentToken);
|
|
currentToken.clear();
|
|
}
|
|
testCases.push_back({command, tokens});
|
|
command.clear();
|
|
tokens.clear();
|
|
}
|
|
|
|
//
|
|
// is command is true since a command is started
|
|
//
|
|
isCommand = true;
|
|
}
|
|
else if (line == tokenDelimiter)
|
|
{
|
|
//
|
|
// No new line is needed after this token
|
|
//
|
|
addNewline = false;
|
|
|
|
//
|
|
// not in command anymore
|
|
//
|
|
isCommand = false;
|
|
|
|
//
|
|
// If we're in the middle of collecting a token, save it
|
|
//
|
|
if (!currentToken.empty())
|
|
{
|
|
tokens.push_back(currentToken);
|
|
currentToken.clear();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Accumulate lines for the command or token
|
|
//
|
|
if (isCommand)
|
|
{
|
|
if (addNewline)
|
|
command += "\n";
|
|
command += line;
|
|
}
|
|
else
|
|
{
|
|
if (addNewline)
|
|
currentToken += "\n";
|
|
currentToken += line;
|
|
}
|
|
|
|
addNewline = true;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Store the last command and tokens if any
|
|
//
|
|
if (!command.empty())
|
|
{
|
|
if (!currentToken.empty())
|
|
{
|
|
tokens.push_back(currentToken);
|
|
}
|
|
testCases.push_back({command, tokens});
|
|
}
|
|
|
|
return testCases;
|
|
}
|
|
|
|
/**
|
|
* @brief Show parsed command and tokens
|
|
* @param testCases A vector of pairs, where each pair contains a command and a vector of tokens
|
|
*
|
|
* @return VOID
|
|
*/
|
|
VOID
|
|
ShowParsedCommandAndTokens(const std::pair<std::string, std::vector<std::string>> & testCase)
|
|
{
|
|
//
|
|
// Output the parsed test case
|
|
//
|
|
|
|
string showingCommand = testCase.first;
|
|
|
|
std::string::size_type pos = 0;
|
|
while ((pos = showingCommand.find("\n", pos)) != std::string::npos)
|
|
{
|
|
showingCommand.replace(pos, 1, "\\n");
|
|
pos += 2; // Move past the newly added characters
|
|
}
|
|
|
|
std::cout << "Command: \"" << showingCommand << "\"" << std::endl;
|
|
std::cout << "____________________________________\n";
|
|
|
|
std::cout << "Tokens: " << std::endl;
|
|
|
|
for (const auto & token : testCase.second)
|
|
{
|
|
string showingToken = token;
|
|
|
|
pos = 0;
|
|
while ((pos = showingToken.find("\n", pos)) != std::string::npos)
|
|
{
|
|
showingToken.replace(pos, 1, "\\n");
|
|
pos += 2; // Move past the newly added characters
|
|
}
|
|
|
|
std::cout << " - \"" << showingToken << "\"" << std::endl;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @brief Test command parser
|
|
*
|
|
* @return BOOLEAN
|
|
*/
|
|
BOOLEAN
|
|
TestCommandParser()
|
|
{
|
|
BOOLEAN overallResult = TRUE;
|
|
int testNum = 0;
|
|
CHAR filePath[MAX_PATH] = {0};
|
|
|
|
//
|
|
// Parse the test cases from the file
|
|
// Setup the path for the filename
|
|
//
|
|
if (!hyperdbg_u_setup_path_for_filename(COMMAND_PARSER_TEST_CASES_FILE, filePath, MAX_PATH, TRUE))
|
|
{
|
|
//
|
|
// Error could not find the test case files
|
|
//
|
|
cout << "[-] Could not find the test case files" << endl;
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Parse the test cases from the file
|
|
//
|
|
auto testCases = parseTestCases(filePath);
|
|
|
|
//
|
|
// Perfom testing test cases with parsed file
|
|
//
|
|
cout << "Perfom testing test cases with parsed file:" << endl;
|
|
|
|
//
|
|
// Output the parsed test cases
|
|
//
|
|
for (const auto & testCase : testCases)
|
|
{
|
|
testNum++;
|
|
|
|
//
|
|
// Create CHAR**
|
|
//
|
|
CHAR_PTR_PTR testCaseArray = createTestCaseArray(testCase.second);
|
|
|
|
//
|
|
// Check token with actual parser
|
|
//
|
|
if (hyperdbg_u_test_command_parser((CHAR *)testCase.first.c_str(), (UINT32)testCase.second.size(), testCaseArray))
|
|
{
|
|
cout << "[+] Test number " << testNum << " Passed " << endl;
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Set overall result to FALSE since one of the test cases failed
|
|
//
|
|
overallResult = FALSE;
|
|
|
|
//
|
|
// Show parsed command and tokens
|
|
//
|
|
cout << "\n============================================================" << endl;
|
|
cout << "\n********************* " << endl;
|
|
cout << "*** Error details *** " << endl;
|
|
cout << "********************* " << endl;
|
|
cout << "\nParsed tokens from HyperDbg main command parser:\n"
|
|
<< endl;
|
|
|
|
//
|
|
// Show tokens
|
|
//
|
|
hyperdbg_u_test_command_parser_show_tokens((CHAR *)testCase.first.c_str());
|
|
|
|
cout << "\n============================================================" << endl;
|
|
|
|
cout << "\nThe parsed command and tokens (From file):" << endl;
|
|
ShowParsedCommandAndTokens(testCase);
|
|
|
|
cout << "\n[-] Test number " << testNum << " Failed " << endl;
|
|
cout << "============================================================\n"
|
|
<< endl;
|
|
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Clean up memory
|
|
//
|
|
freeTestCaseArray(testCaseArray, testCase.second.size());
|
|
}
|
|
|
|
return overallResult;
|
|
}
|