From a0be994c5a6ec365d14f62b79b3d380000ab77b7 Mon Sep 17 00:00:00 2001 From: "Mr. eXoDia" Date: Mon, 8 Jun 2015 00:42:43 +0200 Subject: [PATCH] DBG: new expression parser (should be far more stable, might affect issue #312 and #310) --- help/Input.htm | 16 +- x64_dbg_dbg/_global.h | 1 + x64_dbg_dbg/command.cpp | 30 +- x64_dbg_dbg/expressionparser.cpp | 469 +++++++++++++++++++++ x64_dbg_dbg/expressionparser.h | 69 ++++ x64_dbg_dbg/math.cpp | 520 ------------------------ x64_dbg_dbg/math.h | 14 - x64_dbg_dbg/value.cpp | 132 +++--- x64_dbg_dbg/value.h | 1 + x64_dbg_dbg/x64_dbg_dbg.vcxproj | 4 +- x64_dbg_dbg/x64_dbg_dbg.vcxproj.filters | 12 +- 11 files changed, 638 insertions(+), 630 deletions(-) create mode 100644 x64_dbg_dbg/expressionparser.cpp create mode 100644 x64_dbg_dbg/expressionparser.h delete mode 100644 x64_dbg_dbg/math.cpp delete mode 100644 x64_dbg_dbg/math.h diff --git a/help/Input.htm b/help/Input.htm index e1fdfb54..1a71dc77 100644 --- a/help/Input.htm +++ b/help/Input.htm @@ -27,19 +27,15 @@ x64).

registers (of all sizes) can be used as variables.

memory locations: You can read from a memory location by using one of the following expressions:
[addr]    - read a -DWORD/QWORD, depending on the architecture.
@addr     - same as -above.
n:[addr]  - read n bytes.
- @n:addr   - same as -above.
REMARKS:
- n is the amount of bytes to -read, this can be anything smaller than 4 on x32 and smaller than 8 on x64 when -specified, otherwise there will be an error.
- addr is directly interpreted -as a value, when you want to read [addr+1] you should use -brackets:
+DWORD/QWORD, depending on the +architecture.
n:[addr]  - read n bytes +from.
REMARKS:
- n is the amount of bytes to read, this can be anything +smaller than 4 on x32 and smaller than 8 on x64 when specified, otherwise - @(addr+1), @addr+1 will read: [addr]+1.

+ there will be an error.

flags: Debug flags (interpreted as integer) can be used as input. Flags are prefixed with a '!' following the flag name. Valid flags are: !cf, !pf, !af, !zf, !sf, !tf, !if, @@ -75,7 +71,7 @@ used. "oep" or "ep" the address of these will be returned instead.

Notice: Instead of the ':' delimiter you can also use a '.' If you need to query module information such as - "[module]:imagebase" or "[module]":entry" you are adviced to + "[module]:imagebase" or "[module]":entry" you are advised to use a '?' as delimiter instead ("[module]?entry"). The '?' does checking for named exports later, so it will still work when there is an export called "entry" in the module. diff --git a/x64_dbg_dbg/_global.h b/x64_dbg_dbg/_global.h index 44979de5..9451fc6a 100644 --- a/x64_dbg_dbg/_global.h +++ b/x64_dbg_dbg/_global.h @@ -15,6 +15,7 @@ #include #include #include +#include #include #include #include diff --git a/x64_dbg_dbg/command.cpp b/x64_dbg_dbg/command.cpp index f0ccc85c..b88537cf 100644 --- a/x64_dbg_dbg/command.cpp +++ b/x64_dbg_dbg/command.cpp @@ -268,6 +268,32 @@ static bool isvalidexpression(const char* expression) return valfromstring(expression, &value); } +/** +\brief Check if a character is a mathematical operator. Used to determine stuff like "a *= b" +\param ch The character to check. +\return true if the character is an operator, false otherwise. +*/ +static bool mathisoperator(const char ch) +{ + switch(ch) + { + case '*': + case '`': + case '/': + case '%': + case '+': + case '-': + case '<': + case '>': + case '&': + case '^': + case '|': + return true; + default: + return false; + } +} + /** \brief Special formats a given command. Used as a little hack to support stuff like 'x++' and 'x=y' \param [in,out] string String to format. @@ -300,7 +326,7 @@ static void specialformat(char* string) sprintf(str, "%s%c1", found, op); strcpy(found, str); } - if(mathisoperator(*a) > 2) //x*=3 -> x=x*3 + if(mathisoperator(*a)) //x*=3 -> x=x*3 { char op = *a; *a = 0; @@ -350,8 +376,6 @@ COMMAND* cmdfindmain(COMMAND* cmd_list, char* command) specialformat(command); cmd = cmdget(cmd_list, command); } - if(!cmd or !cmd->cbCommand) - mathformat(command); return cmd; } diff --git a/x64_dbg_dbg/expressionparser.cpp b/x64_dbg_dbg/expressionparser.cpp new file mode 100644 index 00000000..20130cb4 --- /dev/null +++ b/x64_dbg_dbg/expressionparser.cpp @@ -0,0 +1,469 @@ +#include "expressionparser.h" +#include "value.h" + +ExpressionParser::Token::Token(const String & data, const Type type) +{ + _data = data; + _type = type; +} + +const String ExpressionParser::Token::data() const +{ + return _data; +} + +const ExpressionParser::Token::Type ExpressionParser::Token::type() const +{ + return _type; +} + +const ExpressionParser::Token::Associativity ExpressionParser::Token::associativity() const +{ + switch(_type) + { + case Type::OperatorUnarySub: + case Type::OperatorNot: + return Associativity::RightToLeft; + case Type::OperatorMul: + case Type::OperatorHiMul: + case Type::OperatorDiv: + case Type::OperatorMod: + case Type::OperatorAdd: + case Type::OperatorSub: + case Type::OperatorShl: + case Type::OperatorShr: + case Type::OperatorAnd: + case Type::OperatorXor: + case Type::OperatorOr: + return Associativity::LeftToRight; + default: + return Associativity::Unspecified; + } +} + +const int ExpressionParser::Token::precedence() const +{ + switch(_type) + { + case Type::OperatorUnarySub: + case Type::OperatorNot: + return 7; + case Type::OperatorMul: + case Type::OperatorHiMul: + case Type::OperatorDiv: + case Type::OperatorMod: + return 6; + case Type::OperatorAdd: + case Type::OperatorSub: + return 5; + case Type::OperatorShl: + case Type::OperatorShr: + return 4; + case Type::OperatorAnd: + return 3; + case Type::OperatorXor: + return 2; + case Type::OperatorOr: + return 1; + default: + return 0; + } +} + +const bool ExpressionParser::Token::isOperator() const +{ + return _type != Type::Data && _type != Type::OpenBracket && _type != Type::CloseBracket; +} + +ExpressionParser::ExpressionParser(const String & expression) +{ + tokenize(fixClosingBrackets(expression)); + shuntingYard(); +} + +String ExpressionParser::fixClosingBrackets(const String & expression) +{ + int open = 0; + int close = 0; + size_t len = expression.length(); + for(size_t i = 0; i < len; i++) + { + if(expression[i] == '(') + open++; + else if(expression[i] == ')') + close++; + } + String result = expression; + if(close < open) + { + for(int i = 0; i < open - close; i++) + result += ")"; + } + return result; +} + +void ExpressionParser::tokenize(const String & expression) +{ + bool stateMemory = false; + size_t len = expression.length(); + for(size_t i = 0; i < len; i++) + { + char ch = expression[i]; + switch(ch) + { + case '[': + { + stateMemory = true; + _curToken += ch; + } + break; + + case ']': + { + stateMemory = false; + _curToken += ch; + } + break; + + default: + { + if(stateMemory) + _curToken += ch; + else + { + switch(ch) + { + case '(': + addOperatorToken(ch, Token::Type::OpenBracket); + break; + case ')': + addOperatorToken(ch, Token::Type::CloseBracket); + break; + case '~': + addOperatorToken(ch, Token::Type::OperatorNot); + break; + case '*': + addOperatorToken(ch, Token::Type::OperatorMul); + break; + case '`': + addOperatorToken(ch, Token::Type::OperatorHiMul); + break; + case '/': + addOperatorToken(ch, Token::Type::OperatorDiv); + break; + case '%': + addOperatorToken(ch, Token::Type::OperatorMod); + break; + case '+': + if(!isUnaryOperator()) //skip all unary add operators + addOperatorToken(ch, Token::Type::OperatorAdd); + break; + case '-': + if(isUnaryOperator()) + addOperatorToken(ch, Token::Type::OperatorUnarySub); + else + addOperatorToken(ch, Token::Type::OperatorSub); + break; + case '<': + addOperatorToken(ch, Token::Type::OperatorShl); + break; + case '>': + addOperatorToken(ch, Token::Type::OperatorShr); + break; + case '&': + addOperatorToken(ch, Token::Type::OperatorAnd); + break; + case '^': + addOperatorToken(ch, Token::Type::OperatorXor); + break; + case '|': + addOperatorToken(ch, Token::Type::OperatorOr); + break; + case ' ': //ignore spaces + break; + default: + _curToken += ch; + break; + } + } + } + break; + } + } + if(_curToken.length() != 0) //make sure the last token is added + _tokens.push_back(Token(_curToken, Token::Type::Data)); +} + +void ExpressionParser::addOperatorToken(const char ch, const Token::Type type) +{ + if(_curToken.length()) //add a new data token when there is data in the buffer + { + _tokens.push_back(Token(_curToken, Token::Type::Data)); + _curToken = ""; + } + String data; + data += ch; + _tokens.push_back(Token(data, type)); //add the operator token +} + +bool ExpressionParser::isUnaryOperator() +{ + if(_curToken.length()) //data before the operator means it is no unary operator + return false; + if(!_tokens.size()) //no tokens before the operator means it is an unary operator + return true; + Token lastToken = _tokens[_tokens.size() - 1]; + return lastToken.isOperator(); //if the previous operator is a token, the operator is an unary operator +} + +void ExpressionParser::shuntingYard() +{ + //Implementation of Dijkstra's Shunting-yard algorithm + std::vector queue; + std::stack stack; + size_t len = _tokens.size(); + //process the tokens + for(size_t i = 0; i < len; i++) + { + Token & token = _tokens[i]; + switch(token.type()) + { + case Token::Type::Data: + queue.push_back(token); + break; + case Token::Type::OpenBracket: + stack.push(token); + break; + case Token::Type::CloseBracket: + while(true) + { + if(stack.empty()) //empty stack = bracket mismatch + return; + Token curToken = stack.top(); + stack.pop(); + if(curToken.type() == Token::Type::OpenBracket) + break; + queue.push_back(curToken); + } + break; + default: //operator + Token & o1 = token; + while(!stack.empty()) + { + Token o2 = stack.top(); + if(o2.isOperator() && + (o1.associativity() == Token::Associativity::LeftToRight && o1.precedence() <= o2.precedence()) || + (o1.associativity() == Token::Associativity::RightToLeft && o1.precedence() < o2.precedence())) + { + queue.push_back(o2); + stack.pop(); + } + else + break; + } + stack.push(o1); + break; + } + } + //pop the remaining operators + while(!stack.empty()) + { + Token curToken = stack.top(); + stack.pop(); + if(curToken.type() == Token::Type::OpenBracket || curToken.type() == Token::Type::CloseBracket) //brackets on the stack means invalid expression + return; + queue.push_back(curToken); + } + _prefixTokens = queue; +} + +#ifdef _WIN64 +#include + +static inline unsigned long long umulhi(unsigned long long x, unsigned long long y) +{ + unsigned __int64 res; + _umul128(x, y, &res); + return res; +} + +static inline long long mulhi(long long x, long long y) +{ + __int64 res; + _mul128(x, y, &res); + return res; +} +#else +static inline unsigned int umulhi(unsigned int x, unsigned int y) +{ + return (unsigned int)(((unsigned long long)x * y) >> 32); +} + +static inline int mulhi(int x, int y) +{ + return (int)(((long long)x * y) >> 32); +} +#endif //__MINGW64__ + +template +static bool operation(const ExpressionParser::Token::Type type, const T op1, const T op2, T & result) +{ + result = 0; + switch(type) + { + case ExpressionParser::Token::Type::OperatorUnarySub: + result = op1 * ~0; + return true; + case ExpressionParser::Token::Type::OperatorNot: + result = ~op1; + return true; + case ExpressionParser::Token::Type::OperatorMul: + result = op1 * op2; + return true; + case ExpressionParser::Token::Type::OperatorHiMul: + result = umulhi(op1, op2); + return true; + case ExpressionParser::Token::Type::OperatorDiv: + if(op2 != 0) + { + result = op1 / op2; + return true; + } + return false; + case ExpressionParser::Token::Type::OperatorMod: + if(op2 != 0) + { + result = op1 % op2; + return true; + } + return false; + case ExpressionParser::Token::Type::OperatorAdd: + result = op1 + op2; + return true; + case ExpressionParser::Token::Type::OperatorSub: + result = op1 - op2; + return true; + case ExpressionParser::Token::Type::OperatorShl: + result = op1 << op2; + return true; + case ExpressionParser::Token::Type::OperatorShr: + result = op1 >> op2; + return true; + case ExpressionParser::Token::Type::OperatorAnd: + result = op1 & op2; + return true; + case ExpressionParser::Token::Type::OperatorXor: + result = op1 ^ op2; + return true; + case ExpressionParser::Token::Type::OperatorOr: + result = op1 | op2; + return true; + default: + return false; + } +} + +bool ExpressionParser::unsignedoperation(const Token::Type type, const uint op1, const uint op2, uint & result) +{ + if(type == Token::Type::OperatorHiMul) + { + result = umulhi(op1, op2); + return true; + } + else + return operation(type, op1, op2, result); +} + +bool ExpressionParser::signedoperation(const Token::Type type, const sint op1, const sint op2, uint & result) +{ + if(type == Token::Type::OperatorHiMul) + { + result = mulhi(op1, op2); + return true; + } + else + { + sint signedResult; + if(!operation(type, op1, op2, signedResult)) + return false; + result = (uint)signedResult; + return true; + } +} + +bool ExpressionParser::valFromString(const String & data, uint & value) +{ + return sscanf_s(data.c_str(), "%u", &value) == 1; +} + +bool ExpressionParser::calculate(uint & value, bool signedcalc, bool silent, bool baseonly, int* value_size, bool* isvar, bool* hexonly) +{ + value = 0; + if(!_prefixTokens.size()) + return false; + std::stack stack; + size_t len = _prefixTokens.size(); + //calculate the result from the RPN queue + for(size_t i = 0; i < len; i++) + { + Token & token = _prefixTokens[i]; + if(token.isOperator()) + { + uint op1 = 0; + uint op2 = 0; + uint result = 0; + switch(token.type()) + { + case Token::Type::OperatorUnarySub: + case Token::Type::OperatorNot: + if(stack.size() < 1) + return false; + op1 = stack.top(); + stack.pop(); + if(signedcalc) + signedoperation(token.type(), op1, op2, result); + else + unsignedoperation(token.type(), op1, op2, result); + stack.push(result); + break; + case Token::Type::OperatorMul: + case Token::Type::OperatorHiMul: + case Token::Type::OperatorDiv: + case Token::Type::OperatorMod: + case Token::Type::OperatorAdd: + case Token::Type::OperatorSub: + case Token::Type::OperatorShl: + case Token::Type::OperatorShr: + case Token::Type::OperatorAnd: + case Token::Type::OperatorXor: + case Token::Type::OperatorOr: + if(stack.size() < 2) + return false; + op2 = stack.top(); + stack.pop(); + op1 = stack.top(); + stack.pop(); + if(signedcalc) + signedoperation(token.type(), op1, op2, result); + else + unsignedoperation(token.type(), op1, op2, result); + stack.push(result); + break; + default: //do nothing + break; + } + } + else + { + uint result; + if(!valfromstring_noexpr(token.data().c_str(), &result, silent, baseonly, value_size, isvar, hexonly)) + return false; + stack.push(result); + } + + } + if(stack.empty()) //empty result stack means error + return false; + value = stack.top(); + return true; +} \ No newline at end of file diff --git a/x64_dbg_dbg/expressionparser.h b/x64_dbg_dbg/expressionparser.h new file mode 100644 index 00000000..fdbb2eca --- /dev/null +++ b/x64_dbg_dbg/expressionparser.h @@ -0,0 +1,69 @@ +#ifndef _EXPRESSION_PARSER_H +#define _EXPRESSION_PARSER_H + +#include "_global.h" + +class ExpressionParser +{ +public: + ExpressionParser(const String & expression); + bool calculate(uint & value, bool signedcalc, bool silent, bool baseonly, int* value_size, bool* isvar, bool* hexonly); + + class Token + { + public: + enum class Type + { + Data, + OpenBracket, + CloseBracket, + OperatorUnarySub, + OperatorNot, + OperatorMul, + OperatorHiMul, + OperatorDiv, + OperatorMod, + OperatorAdd, + OperatorSub, + OperatorShl, + OperatorShr, + OperatorAnd, + OperatorXor, + OperatorOr + }; + + enum class Associativity + { + LeftToRight, + RightToLeft, + Unspecified + }; + + Token(const String & data, const Type type); + const String data() const; + const Type type() const; + const Associativity associativity() const; + const int precedence() const; + const bool isOperator() const; + + private: + String _data; + Type _type; + }; + +private: + String fixClosingBrackets(const String & expression); + bool isUnaryOperator(); + void tokenize(const String & expression); + void shuntingYard(); + void addOperatorToken(const char ch, const Token::Type type); + bool unsignedoperation(const Token::Type type, const uint op1, const uint op2, uint & result); + bool signedoperation(const Token::Type type, const sint op1, const sint op2, uint & result); + bool valFromString(const String & data, uint & value); + + std::vector _tokens; + std::vector _prefixTokens; + String _curToken; +}; + +#endif //_EXPRESSION_PARSER_H \ No newline at end of file diff --git a/x64_dbg_dbg/math.cpp b/x64_dbg_dbg/math.cpp deleted file mode 100644 index 0cb36b1d..00000000 --- a/x64_dbg_dbg/math.cpp +++ /dev/null @@ -1,520 +0,0 @@ -/** - @file math.cpp - - @brief Implements various functionalities that have to do with handling expression text. - */ - -#include "math.h" -#include "value.h" - -/** -\brief A bracket pair. This structure describes a piece of brackets '(' and ')' -*/ -struct BRACKET_PAIR -{ - /** - \brief The position in the string of the opening bracket '('. - */ - int openpos; - - /** - \brief The position in the string of the closing bracket ')'. - */ - int closepos; - - /** - \brief The depth of the pair (for example when you have "((1+4)*4)" the second '(' has layer 2). - */ - int layer; - - /** - \brief 0 when there is nothing set, 1 when the openpos is set, 2 when everything is set (aka pair is complete). - */ - int isset; -}; - -/** -\brief An expression. This structure describes an expression in form of bracket pairs. -*/ -struct EXPRESSION -{ - /** - \brief The bracket pairs. - */ - BRACKET_PAIR* pairs; - - /** - \brief The total number of bracket pairs. - */ - int total_pairs; - - /** - \brief The expression text everything is derived from. - */ - const char* expression; -}; - -/** -\brief Determines if a characters is an operator. -\param ch The character to check. -\return The number of the operator. 0 when the character is no operator. Otherwise it returns one of the following numbers: - -- 1 ( ) -- 2 ~ (NOT) -- 3 * / % (MUL DIV) -- 4 + - (ADD SUB) -- 5 < > (SHL SHR) -- 6 & (AND) -- 7 ^ (XOR) -- 8 | (OR) -*/ -int mathisoperator(char ch) -{ - if(ch == '(' or ch == ')') - return 1; - else if(ch == '~') - return 2; - else if(ch == '*' or ch == '`' or ch == '/' or ch == '%') - return 3; - else if(ch == '+' or ch == '-') - return 4; - else if(ch == '<' or ch == '>') - return 5; - else if(ch == '&') - return 6; - else if(ch == '^') - return 7; - else if(ch == '|') - return 8; - return 0; -} - -/** -\brief Formats the given text. It removes double operators like "**" and "||" -\param [in,out] text The text to format. -*/ -void mathformat(char* text) -{ - int len = (int)strlen(text); - Memory temp(len + 1, "mathformat:temp"); - memset(temp, 0, len + 1); - for(int i = 0, j = 0; i < len; i++) - if(mathisoperator(text[i]) < 3 or text[i] != text[i + 1]) - j += sprintf(temp + j, "%c", text[i]); - strcpy(text, temp); -} - -/** -\brief Checks if the given text contains operators. -\param text The text to check. -\return true if the text contains operator, false if not. -*/ -bool mathcontains(const char* text) -{ - if(*text == '-') //ignore negative values - text++; - int len = (int)strlen(text); - for(int i = 0; i < len; i++) - if(mathisoperator(text[i])) - return true; - return false; -} - -#ifdef __MINGW64__ -static inline unsigned long long umulhi(unsigned long long x, unsigned long long y) -{ - return (unsigned long long)(((__uint128_t)x * y) >> 64); -} - -static inline long long mulhi(long long x, long long y) -{ - return (long long)(((__int128_t)x * y) >> 64); -} -#elif _WIN64 -#include -static inline unsigned long long umulhi(unsigned long long x, unsigned long long y) -{ - unsigned __int64 res; - _umul128(x, y, &res); - return res; -} - -static inline long long mulhi(long long x, long long y) -{ - __int64 res; - _mul128(x, y, &res); - return res; -} -#else -static inline unsigned int umulhi(unsigned int x, unsigned int y) -{ - return (unsigned int)(((unsigned long long)x * y) >> 32); -} - -static inline int mulhi(int x, int y) -{ - return (int)(((long long)x * y) >> 32); -} -#endif //__MINGW64__ - -/** -\brief Do an operation on two unsigned numbers. -\param op The operation to do (this must be a valid operator). -\param left The number left of the operator. -\param right The number right of the operator. -\param [out] result The result of the operator. Cannot be null. -\return true if the operation succeeded. It could fail on zero devision or an invalid operator. -*/ -bool mathdounsignedoperation(char op, uint left, uint right, uint* result) -{ - switch(op) - { - case '*': - *result = left * right; - return true; - case '`': - *result = umulhi(left, right); - return true; - case '/': - if(right) - { - *result = left / right; - return true; - } - return false; - case '%': - if(right) - { - *result = left % right; - return true; - } - return false; - case '+': - *result = left + right; - return true; - case '-': - *result = left - right; - return true; - case '<': - *result = left << right; - return true; - case '>': - *result = left >> right; - return true; - case '&': - *result = left & right; - return true; - case '^': - *result = left ^ right; - return true; - case '|': - *result = left | right; - return true; - } - return false; -} - -/** -\brief Do an operation on two signed numbers. -\param op The operation to do (this must be a valid operator). -\param left The number left of the operator. -\param right The number right of the operator. -\param [out] result The result of the operator. Cannot be null. -\return true if the operation succeeded. It could fail on zero devision or an invalid operator. -*/ -bool mathdosignedoperation(char op, sint left, sint right, sint* result) -{ - switch(op) - { - case '*': - *result = left * right; - return true; - case '`': - *result = mulhi(left, right); - return true; - case '/': - if(right) - { - *result = left / right; - return true; - } - return false; - case '%': - if(right) - { - *result = left % right; - return true; - } - return false; - case '+': - *result = left + right; - return true; - case '-': - *result = left - right; - return true; - case '<': - *result = left << right; - return true; - case '>': - *result = left >> right; - return true; - case '&': - *result = left & right; - return true; - case '^': - *result = left ^ right; - return true; - case '|': - *result = left | right; - return true; - } - return false; -} - -/** -\brief Fills a BRACKET_PAIR structure. -\param [in,out] expstruct The expression structure. Cannot be null. -\param pos The position to fill, position of '(' or ')'. -\param layer The layer this bracket is in. -*/ -static void fillpair(EXPRESSION* expstruct, int pos, int layer) -{ - for(int i = 0; i < expstruct->total_pairs; i++) - { - if(!expstruct->pairs[i].isset) - { - expstruct->pairs[i].layer = layer; - expstruct->pairs[i].openpos = pos; - expstruct->pairs[i].isset = 1; - break; - } - else if(expstruct->pairs[i].layer == layer and expstruct->pairs[i].isset == 1) - { - expstruct->pairs[i].closepos = pos; - expstruct->pairs[i].isset = 2; - break; - } - } -} - -/** -\brief This function recursively matches bracket pair in an EXPRESSION. -\param [in,out] expstruct The expression structure. Cannot be null. -\param expression The expression text to parse. Cannot be null. -\param endlayer The layer to stop on. This variable is used for the recursion termination condition. -\return The position in the \p expression mathpairs ended in. -*/ -static int matchpairs(EXPRESSION* expstruct, const char* expression, int endlayer = 0) -{ - int layer = endlayer; - int len = (int)strlen(expression); - for(int i = 0; i < len; i++) - { - if(expression[i] == '(') - { - layer++; - int pos = (int)(expression + i - expstruct->expression); - fillpair(expstruct, pos, layer); - i += matchpairs(expstruct, expression + i + 1, layer); - } - else if(expression[i] == ')') - { - if(layer == endlayer) - { - int pos = (int)(expression + i - expstruct->expression); - fillpair(expstruct, pos, layer); - return i; - } - layer--; - } - - } - return 0; -} - -/** -\brief Formats a given expression. This function checks if the number of brackets is even and adds brackets to the end if needed. -\param [in,out] exp The expression to format. -\return The number of bracket pairs in the expression or -1 on error. -*/ -static int expressionformat(char* exp, size_t bufsize) -{ - int len = (int)strlen(exp); - int open = 0; - int close = 0; - for(int i = 0; i < len; i++) - { - if(exp[i] == '(') - open++; - else if(exp[i] == ')') - close++; - } - if(close > open) - return -1; - int add = open - close; - if(add) - { - String closing; - for(int i = 0; i < add; i++) - closing += ')'; - strcat_s(exp, bufsize, closing.c_str()); - } - return open; -} - -/** -\brief Adjusts bracket pair positions to insert a new string in the expression. -\param [in,out] exps The expression structure. -\param cur_open The current opening bracket '(' position. -\param cur_close The current closing bracket ')' position. -\param cur_len The current string length in between the brackets. -\param new_len Length of the new string. -*/ -static void adjustpairs(EXPRESSION* exps, int cur_open, int cur_close, int cur_len, int new_len) -{ - for(int i = 0; i < exps->total_pairs; i++) - { - if(exps->pairs[i].openpos > cur_open) - exps->pairs[i].openpos += new_len - cur_len; - if(exps->pairs[i].closepos > cur_close) - exps->pairs[i].closepos += new_len - cur_len; - } -} - -/** -\brief Prints value of expressions in between brackets on a certain bracket layer (expression is resolved using mathfromstring(), which means the whole thing can work recursively). -\param [in,out] exp The expression to print. Cannot be null. -\param bufsize Buffer size of the expression parameter. -\param [in,out] exps The expression structure. Cannot be null. -\param layer The layer to print. -\param silent Value to pass on to mathfromstring(). -\param baseonly Value to pass on to mathfromstring(). -\return true if printing the layer was successful, false otherwise. -*/ -static bool printlayer(char* exp, size_t bufsize, EXPRESSION* exps, int layer, bool silent, bool baseonly) -{ - for(int i = 0; i < exps->total_pairs; i++) - { - if(exps->pairs[i].layer == layer) - { - const size_t explen = strlen(exp); - Memory temp(explen + 1); - Memory backup(explen + 1); - - int open = exps->pairs[i].openpos; - int close = exps->pairs[i].closepos; - int len = close - open; - strncpy_s(temp, temp.size(), exp + open + 1, len - 1); - - strcpy_s(backup, backup.size(), exp + open + len + 1); - - uint value; - if(!mathfromstring(temp, &value, silent, baseonly, 0, 0)) - return false; - - adjustpairs(exps, open, close, len + 1, sprintf_s(exp + open, bufsize - open, "%"fext"X", value)); - - if(*backup) - strcat_s(exp, bufsize, backup); - } - } - return true; -} - -/** -\brief Handle brackets in an expression (calculate the values of expressions in between brackets). -\param [in,out] expression Expression to handle. Cannot be null. You need at most 16 times the input size for this. -\param bufsize Buffer size of the expression parameter. -\param silent Value to pass on to printlayer(). -\param baseonly Value to pass on to printlayer(). -\return true if the brackets are correctly expanded, false otherwise. -*/ -bool mathhandlebrackets(char* expression, size_t bufsize, bool silent, bool baseonly) -{ - EXPRESSION expstruct; - expstruct.expression = expression; - int total_pairs = expressionformat(expression, bufsize); - if(total_pairs == -1) - return false; - else if(!total_pairs) - return true; - expstruct.total_pairs = total_pairs; - - Memory pairs(expstruct.total_pairs * sizeof(BRACKET_PAIR), "mathhandlebrackets:expstruct.pairs"); - expstruct.pairs = pairs; - memset(expstruct.pairs, 0, expstruct.total_pairs * sizeof(BRACKET_PAIR)); - matchpairs(&expstruct, expression); - int deepest = 0; - for(int i = 0; i < expstruct.total_pairs; i++) - if(expstruct.pairs[i].layer > deepest) - deepest = expstruct.pairs[i].layer; - - for(int i = deepest; i > 0; i--) - if(!printlayer(expression, bufsize, &expstruct, i, silent, baseonly)) - return false; - return true; -} - -/** -\brief Calculate the value of an expression string. -\param string The string to calculate the value of. Cannot be null. -\param [in,out] value The resulting value. Cannot be null. -\param silent Value to pass on to valfromstring(). -\param baseonly Value to pass on to valfromstring(). -\param [in,out] value_size Value to pass on to valfromstring(). Can be null. -\param [in,out] isvar Value to pass on to valfromstring(). Can be null. -\return true if the string was successfully parsed and the value was calculated. -*/ -bool mathfromstring(const char* string, uint* value, bool silent, bool baseonly, int* value_size, bool* isvar) -{ - int highestop = 0; - int highestop_pos = 0; - int len = (int)strlen(string); - bool negative = false; - if(*string == '-') - { - negative = true; - string++; - } - for(int i = 0; i < len; i++) - { - int curop = mathisoperator(string[i]); - if(curop > 1 and curop > highestop) - { - highestop = curop; - highestop_pos = i; - } - } - if(!highestop) - return valfromstring(string, value, silent, baseonly, value_size, isvar, 0); - Memory strleft(len + 1 + negative, "mathfromstring:strleft"); - Memory strright(len + 1, "mathfromstring:strright"); - strncpy(strleft, string - negative, highestop_pos + negative); - strcpy(strright, string + highestop_pos + 1); - strcpy(strleft, StringUtils::Trim(strleft).c_str()); - strcpy(strright, StringUtils::Trim(strright).c_str()); - //dprintf("left: %s, right: %s, op: %c\n", strleft(), strright(), string[highestop_pos]); - if(!*strright) - return false; - uint right = 0; - if(!valfromstring(strright, &right, silent, baseonly)) - return false; - if(string[highestop_pos] == '~') - { - right = ~right; - if(!strlen(strleft)) - { - *value = right; - return true; - } - } - uint left = 0; - if(!valfromstring(strleft, &left, silent, baseonly)) - return false; - bool math_ok; - if(valuesignedcalc()) - math_ok = mathdosignedoperation(string[highestop_pos], left, right, (sint*)value); - else - math_ok = mathdounsignedoperation(string[highestop_pos], left, right, value); - return math_ok; -} diff --git a/x64_dbg_dbg/math.h b/x64_dbg_dbg/math.h deleted file mode 100644 index 72ee1614..00000000 --- a/x64_dbg_dbg/math.h +++ /dev/null @@ -1,14 +0,0 @@ -#ifndef _MATH_H -#define _MATH_H - -#include "_global.h" - -int mathisoperator(char ch); -void mathformat(char* text); -bool mathcontains(const char* text); -bool mathhandlebrackets(char* expression, size_t bufsize, bool silent, bool baseonly); -bool mathfromstring(const char* string, uint* value, bool silent, bool baseonly, int* value_size, bool* isvar); -bool mathdounsignedoperation(char op, uint left, uint right, uint* result); -bool mathdosignedoperation(char op, sint left, sint right, sint* result); - -#endif // _MATH_H diff --git a/x64_dbg_dbg/value.cpp b/x64_dbg_dbg/value.cpp index 7bb31389..9e93c8a9 100644 --- a/x64_dbg_dbg/value.cpp +++ b/x64_dbg_dbg/value.cpp @@ -14,6 +14,7 @@ #include "symbolinfo.h" #include "module.h" #include "label.h" +#include "expressionparser.h" static bool dosignedcalc = false; @@ -1497,9 +1498,9 @@ static bool ishexnumber(const char* string) \param [out] value_size This function can output the value size parsed (for example memory location size or register size). Can be null. \param [out] isvar This function can output if the expression is variable (for example memory locations, registers or variables are variable). Can be null. \param [out] hexonly This function can output if the output value should only be printed as hexadecimal (for example addresses). Can be null. -\return true if the expression was parsed successfull, false otherwise. +\return true if the expression was parsed successful, false otherwise. */ -bool valfromstring(const char* string, uint* value, bool silent, bool baseonly, int* value_size, bool* isvar, bool* hexonly) +bool valfromstring_noexpr(const char* string, uint* value, bool silent, bool baseonly, int* value_size, bool* isvar, bool* hexonly) { if(!value or !string) return false; @@ -1508,50 +1509,7 @@ bool valfromstring(const char* string, uint* value, bool silent, bool baseonly, *value = 0; return true; } - else if(mathcontains(string)) //handle math - { - int len = (int)strlen(string); - Memory newstring(len * 2 + 1, "valfromstring:newstring"); - if(strstr(string, "[")) //memory brackets: [] - { - for(int i = 0, j = 0; i < len; i++) - { - if(string[i] == ']') - j += sprintf(newstring + j, ")"); - else if(isdigit(string[i]) and string[i + 1] == ':' and string[i + 2] == '[') //n:[ - { - j += sprintf(newstring + j, "@%c:(", string[i]); - i += 2; - } - else if(string[i] == '[') - j += sprintf(newstring + j, "@("); - else - j += sprintf(newstring + j, "%c", string[i]); - } - } - else - strcpy_s(newstring, newstring.size(), string); - Memory string_(len * 16 + deflen, "valfromstring:string_"); - strcpy_s(string_, string_.size(), newstring); - int add = 0; - bool negative = (*string_ == '-'); - while(mathisoperator(string_[add + negative]) > 2) - add++; - if(!mathhandlebrackets(string_ + add, string_.size() - add, silent, baseonly)) - return false; - return mathfromstring(string_ + add, value, silent, baseonly, value_size, isvar); - } - else if(*string == '-') //negative value - { - uint val; - if(!valfromstring(string + 1, &val, silent, baseonly, value_size, isvar, hexonly)) - return false; - val *= ~0; - if(value) - *value = val; - return true; - } - else if(*string == '@' or strstr(string, "[")) //memory location + else if(string[0] == '[' || (isdigit(string[0]) && string[1] == ':' && string[2] == '[')) //memory location { if(!DbgIsDebugging()) { @@ -1565,37 +1523,33 @@ bool valfromstring(const char* string, uint* value, bool silent, bool baseonly, return true; } int len = (int)strlen(string); - Memory newstring(len * 2 + 1, "valfromstring:newstring"); - if(strstr(string, "[")) + + String newstring; + bool foundStart = false; + for(int i = 0; i < len; i++) { - for(int i = 0, j = 0; i < len; i++) - { - if(string[i] == ']') - j += sprintf(newstring + j, ")"); - else if(isdigit(string[i]) and string[i + 1] == ':' and string[i + 2] == '[') //n:[ - { - j += sprintf(newstring + j, "@%c:(", string[i]); - i += 2; - } - else if(string[i] == '[') - j += sprintf(newstring + j, "@("); - else - j += sprintf(newstring + j, "%c", string[i]); - } + if(string[i] == '[') + foundStart = true; + else if(string[i] == ']') + break; + else if(foundStart) + newstring += string[i]; } - else - strcpy_s(newstring, newstring.size(), string); + int read_size = sizeof(uint); int add = 1; - if(newstring[2] == ':' and isdigit((newstring[1]))) //@n: (number of bytes to read) + if(string[1] == ':') //n:[ (number of bytes to read) { add += 2; - int new_size = newstring[1] - 0x30; + int new_size = string[0] - '0'; if(new_size < read_size) read_size = new_size; } - if(!valfromstring(newstring + add, value, silent, baseonly)) + if(!valfromstring(newstring.c_str(), value, silent, baseonly)) + { + dprintf("noexpr failed on %s\n", newstring.c_str()); return false; + } uint addr = *value; *value = 0; if(!MemRead((void*)addr, value, read_size, 0)) @@ -1610,7 +1564,7 @@ bool valfromstring(const char* string, uint* value, bool silent, bool baseonly, *isvar = true; return true; } - else if(isregister(string)) //register + else if(isregister(string)) //register { if(!DbgIsDebugging()) { @@ -1628,7 +1582,7 @@ bool valfromstring(const char* string, uint* value, bool silent, bool baseonly, *isvar = true; return true; } - else if(*string == '!' and isflag(string + 1)) //flag + else if(*string == '!' and isflag(string + 1)) //flag { if(!DbgIsDebugging()) { @@ -1652,7 +1606,7 @@ bool valfromstring(const char* string, uint* value, bool silent, bool baseonly, *isvar = true; return true; } - else if(isdecnumber(string)) //decimal numbers come 'first' + else if(isdecnumber(string)) //decimal numbers come 'first' { if(value_size) *value_size = 0; @@ -1661,7 +1615,7 @@ bool valfromstring(const char* string, uint* value, bool silent, bool baseonly, sscanf(string + 1, "%"fext"u", value); return true; } - else if(ishexnumber(string)) //then hex numbers + else if(ishexnumber(string)) //then hex numbers { if(value_size) *value_size = 0; @@ -1676,13 +1630,13 @@ bool valfromstring(const char* string, uint* value, bool silent, bool baseonly, } if(baseonly) return false; - else if(valapifromstring(string, value, value_size, true, silent, hexonly)) //then come APIs + else if(valapifromstring(string, value, value_size, true, silent, hexonly)) //then come APIs return true; - else if(LabelFromString(string, value)) //then come labels + else if(LabelFromString(string, value)) //then come labels return true; - else if(SymAddrFromName(string, value)) //then come symbols + else if(SymAddrFromName(string, value)) //then come symbols return true; - else if(varget(string, value, value_size, 0)) //finally variables + else if(varget(string, value, value_size, 0)) //finally variables { if(isvar) *isvar = true; @@ -1693,6 +1647,34 @@ bool valfromstring(const char* string, uint* value, bool silent, bool baseonly, return false; //nothing was OK } +/** +\brief Gets a value from a string. This function can parse expressions, memory locations, registers, flags, API names, labels, symbols and variables. +\param string The string to parse. +\param [out] value The value of the expression. This value cannot be null. +\param silent true to not output anything to the console. +\param baseonly true to skip parsing API names, labels, symbols and variables (basic expressions only). +\param [out] value_size This function can output the value size parsed (for example memory location size or register size). Can be null. +\param [out] isvar This function can output if the expression is variable (for example memory locations, registers or variables are variable). Can be null. +\param [out] hexonly This function can output if the output value should only be printed as hexadecimal (for example addresses). Can be null. +\return true if the expression was parsed successful, false otherwise. +*/ +bool valfromstring(const char* string, uint* value, bool silent, bool baseonly, int* value_size, bool* isvar, bool* hexonly) +{ + if(!value or !string) + return false; + if(!*string) + { + *value = 0; + return true; + } + ExpressionParser parser(string); + uint result; + if(!parser.calculate(result, valuesignedcalc(), silent, baseonly, value_size, isvar, hexonly)) + return false; + *value = result; + return true; +} + /** \brief Checks if a string is long enough. \param str The string to check. diff --git a/x64_dbg_dbg/value.h b/x64_dbg_dbg/value.h index 7b91afad..e23d1a6b 100644 --- a/x64_dbg_dbg/value.h +++ b/x64_dbg_dbg/value.h @@ -7,6 +7,7 @@ bool valuesignedcalc(); void valuesetsignedcalc(bool a); bool valapifromstring(const char* name, uint* value, int* value_size, bool printall, bool silent, bool* hexonly); +bool valfromstring_noexpr(const char* string, uint* value, bool silent = true, bool baseonly = false, int* value_size = 0, bool* isvar = 0, bool* hexonly = 0); bool valfromstring(const char* string, uint* value, bool silent = true, bool baseonly = false, int* value_size = 0, bool* isvar = 0, bool* hexonly = 0); bool valflagfromstring(uint eflags, const char* string); bool valtostring(const char* string, uint value, bool silent); diff --git a/x64_dbg_dbg/x64_dbg_dbg.vcxproj b/x64_dbg_dbg/x64_dbg_dbg.vcxproj index b1e13a3c..eccdf679 100644 --- a/x64_dbg_dbg/x64_dbg_dbg.vcxproj +++ b/x64_dbg_dbg/x64_dbg_dbg.vcxproj @@ -35,6 +35,7 @@ + @@ -42,7 +43,6 @@ - @@ -96,6 +96,7 @@ + @@ -109,7 +110,6 @@ - diff --git a/x64_dbg_dbg/x64_dbg_dbg.vcxproj.filters b/x64_dbg_dbg/x64_dbg_dbg.vcxproj.filters index 8e6f0756..3c6d8a8f 100644 --- a/x64_dbg_dbg/x64_dbg_dbg.vcxproj.filters +++ b/x64_dbg_dbg/x64_dbg_dbg.vcxproj.filters @@ -105,9 +105,6 @@ Source Files\Core - - Source Files\Core - Source Files\Core @@ -219,6 +216,9 @@ Source Files\Utilities + + Source Files\Core + @@ -260,9 +260,6 @@ Header Files\Core - - Header Files\Core - Header Files\Core @@ -518,5 +515,8 @@ Header Files\Utilities + + Header Files\Core + \ No newline at end of file