From 3dbac6020336d26adfbee26dc7a0c96be0d1e83f Mon Sep 17 00:00:00 2001 From: mountcloud Date: Thu, 30 Jun 2022 01:48:43 +0800 Subject: [PATCH] =?UTF-8?q?1=EF=BC=9A=E9=87=8D=E6=9E=84=E8=A7=84=E5=88=99?= =?UTF-8?q?=E5=BC=95=E6=93=8E=EF=BC=8C=E6=94=AF=E6=8C=81=E5=A4=9A=E7=A7=8D?= =?UTF-8?q?=E9=80=BB=E8=BE=91=E8=BF=90=E7=AE=97=EF=BC=8C=E6=94=AF=E6=8C=81?= =?UTF-8?q?=E5=A4=9A=E7=A7=8D=E7=BB=9F=E8=AE=A1=E8=BF=90=E7=AE=97=EF=BC=8C?= =?UTF-8?q?=E6=94=AF=E6=8C=81=E5=A4=9A=E7=A7=8D=E6=AF=94=E8=BE=83=E8=BF=90?= =?UTF-8?q?=E7=AE=97=E3=80=82=202=EF=BC=9A=E9=87=8D=E6=9E=84=E7=89=B9?= =?UTF-8?q?=E5=BE=81=E5=BA=93=E6=A0=BC=E5=BC=8F=EF=BC=8C=E4=BB=8Ejson?= =?UTF-8?q?=E6=94=B9=E7=94=A8yaml=EF=BC=88json=E7=A1=AE=E5=AE=9E=E7=9C=8B?= =?UTF-8?q?=E8=B5=B7=E6=9D=A5=E4=B8=8D=E5=A5=BD=E7=9C=8B=EF=BC=89=E3=80=82?= =?UTF-8?q?=203=EF=BC=9A=E6=9B=B4=E6=94=B9=E7=89=B9=E5=BE=81=E5=8C=B9?= =?UTF-8?q?=E9=85=8D=E9=80=BB=E8=BE=91=EF=BC=8C=E5=B0=86=E5=AD=97=E8=8A=82?= =?UTF-8?q?=E5=8C=B9=E9=85=8D=E4=B8=8E=E6=A0=A1=E9=AA=8C=E5=8C=B9=E9=85=8D?= =?UTF-8?q?=E7=BB=93=E6=9E=9C=E6=8B=86=E5=88=86=E3=80=82=204=EF=BC=9A?= =?UTF-8?q?=E7=BC=96=E8=BE=91=E5=99=A8=E9=80=82=E9=85=8D=E6=96=B0=E7=9A=84?= =?UTF-8?q?=E8=A7=84=E5=88=99=E5=BC=95=E6=93=8E=E5=92=8C=E7=89=B9=E5=BE=81?= =?UTF-8?q?=E5=BA=93=E6=A0=BC=E5=BC=8F=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- FireDog/featurelibrary.cpp | 437 +++++------------- FireDog/featurelibrary.h | 21 +- FireDog/matcher.cpp | 142 ++---- FireDog/matcher.h | 10 +- FireDog/rule/rule.cpp | 13 +- FireDogEditor/FireDogEditor.cpp | 157 +++++-- FireDogEditor/FireDogEditor.h | 2 +- FireDogEditor/FireDogEditor.pri | 11 +- FireDogEditor/FireDogEditor.qrc | 1 + FireDogEditor/FireDogEditor.ui | 3 + FireDogEditor/FireDogFeatureInfo.cpp | 2 - FireDogEditor/FireDogFeatureRuleInfo.cpp | 176 ++++++- FireDogEditor/FireDogFeatureRuleInfo.h | 4 +- FireDogEditor/FireDogFeatureRuleInfo.ui | 4 +- FireDogEditor/matchthread.cpp | 71 ++- FireDogEditor/matchthread.h | 2 +- .../resources/fdlanguages/en-us.json | 3 + .../resources/fdlanguages/zh-cn.json | 3 + README.md | 68 +++ images/v1.3.1-rule-gui.png | Bin 0 -> 39692 bytes temp/README.txt | 63 +++ temp/demo/demo.fdog | 28 ++ temp/demo/using-gui-open-demo.txt | 57 +++ 23 files changed, 730 insertions(+), 548 deletions(-) create mode 100644 images/v1.3.1-rule-gui.png create mode 100644 temp/README.txt create mode 100644 temp/demo/demo.fdog create mode 100644 temp/demo/using-gui-open-demo.txt diff --git a/FireDog/featurelibrary.cpp b/FireDog/featurelibrary.cpp index cc65b3b..a7b0c38 100644 --- a/FireDog/featurelibrary.cpp +++ b/FireDog/featurelibrary.cpp @@ -26,120 +26,6 @@ FeatureLibrary::~FeatureLibrary() { } } -FeatureLibrary* FeatureLibrary::createByJson(string json, int* errorcode) { - FeatureLibrary* result = NULL; - nlohmann::json root; - bool parseStatus = false; - try { - root = json::parse(json); - parseStatus = true; - } - catch (nlohmann::detail::parse_error er) { - parseStatus = false; - - } - if (!parseStatus || !root.is_object()|| !root.contains("version") || !root.contains("items")) { - *errorcode = FL_CONTENT_FORMATE_ERROR; - return result; - } - - bool validstate = false; - auto schema = json::parse(FIREDOG_FEATURE_LIBRARAY_JSON_SCHEMA); - - json_validator validator; // create validator - validator.set_root_schema(schema); - try { - validator.validate(root); - validstate = true; - } - catch (const std::exception& e) { - validstate = false; - } - - if (!validstate) { - *errorcode = FL_CONTENT_FORMATE_ERROR; - return result; - } - - //get version - string version = root["version"]; - - //check version - if (version != FIREDOG_FEATURE_LIBRARY_VERSION) { - *errorcode = FL_CONTENT_VERSION_ERROR; - } - - result = new FeatureLibrary(); - - result->version = version; - - nlohmann::json items = root["items"]; - - if (items.is_array()) { - for (int i = 0; i < items.size(); i++) { - nlohmann::json item = items[i]; - if (item.is_object() - && item.contains("author") && item["author"].is_string() - && item.contains("name") && item["name"].is_string() - && item.contains("describe") && item["describe"].is_string() - && item.contains("features") && item["features"].is_array() && item["features"].size() > 0 - && item.contains("rule") && item["rule"].is_object()) { - - //Ïȿ´¹æÔò - nlohmann::json rulejson = item["rule"]; - - vector rules = parseRulesByJson(rulejson); - if (rules.size()==0) { - continue; - } - - Rule* rule = NULL; - if (rules.size()==1) { - rule = rules.at(0); - } - else { - AndRule* andRule = new AndRule(); - andRule->addRules(rules); - } - - FeatureLibraryItem* fitem = new FeatureLibraryItem(); - fitem->author = item["author"]; - fitem->name = item["name"]; - fitem->describe = item["describe"]; - fitem->rule = rule; - - nlohmann::json features = item["features"]; - for (int y = 0; y < features.size(); y++) { - nlohmann::json feature = features[y]; - if (feature.contains("key") && feature["key"].is_string() - && (feature.contains("hex") && feature["hex"].is_string() - || feature.contains("text") && feature["text"].is_string())) { - string fkey = feature["key"]; - string fhex = ""; - string ftext = ""; - if (feature.contains("hex")) { - fhex = feature["hex"]; - } - - if (feature.contains("text")) { - ftext = feature["text"]; - } - - Feature* feature = new Feature(); - feature->key = fkey; - feature->hex = fhex; - feature->text = ftext; - fitem->features->push_back(feature); - } - } - result->items->push_back(fitem); - } - } - } - - return result; -} - FeatureLibrary* FeatureLibrary::createByYaml(std::string yaml, int* errorcode) { FeatureLibrary* result = NULL; @@ -243,226 +129,139 @@ FeatureLibrary* FeatureLibrary::createByYaml(std::string yaml, int* errorcode) { return result; } -vector FeatureLibrary::parseRulesByJson(nlohmann::json rulejson) { - vector rules; - - if (rulejson.is_string()) { - string id = rulejson; - CountRule* countRule = new CountRule(id); - rules.push_back(countRule); - }else if (rulejson.is_object()&&!rulejson.is_array()) { - for (auto ruleiter = rulejson.begin(); ruleiter != rulejson.end(); ruleiter++) { - std::string key = ruleiter.key(); - nlohmann::json childrulejson = rulejson[key]; - if (key == "$and" || key == "$or" || key == "$not") { - LogicRule* logicRule = NULL; - vector childRules = parseRulesByJson(childrulejson); - if (childRules.size()>0) { - if (key == "$and") { - logicRule = new AndRule(); - } - if (key == "$or") { - logicRule = new OrRule(); - } - if (key == "$not") { - logicRule = new NotRule(); - } - logicRule->addRules(childRules); - rules.push_back(logicRule); - } - } - else if (key == "$int") { - if (childrulejson.is_number()) { - long num = childrulejson; - IntRule* intRule = new IntRule(childrulejson); - rules.push_back(intRule); - } - } - else if (key == "$count") { - vector ids; - long num = 1L; - - if (childrulejson.is_string()) { - string id = childrulejson; - ids.push_back(id); - }else if (childrulejson.is_array()) { - int idslen = childrulejson.size(); - for (int i = 0; i < idslen; i++) { - nlohmann::json crj = childrulejson[i]; - if (crj.is_string()) { - string id = crj; - ids.push_back(id); - } - else if (i == idslen - 1 && crj.is_number()) { - long tempnum = crj; - if (tempnum > 1) { - num = tempnum; - } - } - } - } - - if (ids.size() > 0) { - CountRule* countRule = new CountRule(ids); - countRule->setCritical(num); - rules.push_back(countRule); - } - } - else if(key == "$lt"|| key == "$le" || key == "$gt" || key == "$ge") { - Rule* compareRule = NULL; - - vector childRules = parseRulesByJson(childrulejson); - - if (childRules.size()==2) { - - Rule* num1Rule = childRules[0]; - Rule* num2Rule = childRules[1]; - if (num1Rule != NULL && num1Rule->getBaseType() == RULE_BASE_TYPE_NUMBER - && num2Rule != NULL && num2Rule->getBaseType() == RULE_BASE_TYPE_NUMBER) { - if (key == "$lt") { - compareRule = new LtRule((NumberRule*)num1Rule, (NumberRule*)num2Rule); - } - if (key == "$le") { - compareRule = new LeRule((NumberRule*)num1Rule, (NumberRule*)num2Rule); - } - if (key == "$gt") { - compareRule = new GtRule((NumberRule*)num1Rule, (NumberRule*)num2Rule); - } - if (key == "$ge") { - compareRule = new GeRule((NumberRule*)num1Rule, (NumberRule*)num2Rule); - } - } - if (compareRule != NULL) { - rules.push_back(compareRule); - } - - } - } - } - } - return rules; -} - -Rule* FeatureLibrary::parseRuleByJson(nlohmann::json rulejson) { - Rule* rule = NULL; - - vector rules = parseRulesByJson(rulejson); - if (rules.size()>0) { - rule = rules.at(0); - } - - return rule; -} - FeatureLibraryItem::FeatureLibraryItem() { features = new vector(); } -std::string FeatureLibrary::toJson(int* state){ - json jsonFeatureLibrary; - jsonFeatureLibrary["version"] = FIREDOG_FEATURE_LIBRARY_VERSION; +std::string FeatureLibrary::toYaml(int* state) { + string ymlstr; + ryml::Tree tree; + try { + c4::yml::NodeRef root = tree.rootref(); + root |= ryml::MAP; + root["version"] << string(FIREDOG_FEATURE_LIBRARY_VERSION); + if (items != NULL) { + root["items"] |= ryml::SEQ; + for (int i = 0; i < items->size(); i++) { + FeatureLibraryItem* item = items->at(i); + root["items"][i] |= ryml::MAP; + root["items"][i]["name"] << item->name; + root["items"][i]["author"] << item->author; + root["items"][i]["describe"] << item->describe; - if (items!=NULL) { - std::vector jsonItems; - for (int i = 0; i < items->size(); i++) { - json jsonItem; - - FeatureLibraryItem* item = items->at(i); - - jsonItem["name"] = item->name; - jsonItem["describe"] = item->describe; - jsonItem["author"] = item->author; - - std::vector jsonFeatures; - if (item->features != NULL && item->features->size() > 0) { - for (int y = 0; y < item->features->size(); y++) { - Feature* feature = item->features->at(y); - json jsonFeature; - jsonFeature["key"] = feature->key; - if (!feature->hex.empty()) { - jsonFeature["hex"] = feature->hex; + if (item->features != NULL && item->features->size() > 0) { + root["items"][i]["features"] |= ryml::SEQ;; + for (int y = 0; y < item->features->size(); y++) { + Feature* feature = item->features->at(y); + root["items"][i]["features"][y] |= ryml::MAP; + root["items"][i]["features"][y]["key"] << feature->key; + if (!feature->hex.empty()) { + root["items"][i]["features"][y]["hex"] << feature->hex; + } + else { + root["items"][i]["features"][y]["text"] << feature->text; + } } - else { - jsonFeature["text"] = feature->text; - } - jsonFeatures.push_back(jsonFeature); } + root["items"][i]["rules"] |= ryml::SEQ; + root["items"][i]["rules"][0] |= ryml::MAP; + c4::yml::NodeRef ymlrules = root["items"][i]["rules"][0]; + ruleToYaml(ymlrules, item->rule); } - else { - *state = FL_CONTENT_EXIST_EMPTY_FEATURES_FAIL; - return ""; - } - jsonItem["features"] = jsonFeatures; - - bool ruleState = false; - json ruleJson = ruleToJson(item->rule,&ruleState); - - if (!ruleState) { - *state = FL_CONTENT_EXIST_RULE_FORMATE_FAIL; - return ""; - } - - jsonItem["rule"] = ruleJson; - jsonItems.push_back(jsonItem); } - jsonFeatureLibrary["items"] = jsonItems; + *state = 0; } - else { - *state = FL_CONTENT_ITEMS_IS_EMPTY; - return ""; + catch (...) { + *state = -1; } - std::string json = jsonFeatureLibrary.dump(4); - *state = NO_ERROR; - return json; + + if (*state == 0) { + ymlstr = ryml::emitrs(tree); + } + + return ymlstr; } -nlohmann::json FeatureLibrary::ruleToJson(mountcloud::Rule* rule,bool* state) { - json jsonRule; - //if (rule != NULL) { - // if (!rule->id.empty()) { - // jsonRule = rule->id; - // *state = true; - // } - // else if(rule->ands!=NULL&&rule->ands->size()>0){ - // vector andsjson; - // for (int i = 0; i < rule->ands->size(); i++) { - // mountcloud::Rule* andRule = rule->ands->at(i); - // bool andRuleState = false; +void FeatureLibrary::ruleToYaml(c4::yml::NodeRef& parentNode, mountcloud::Rule* rule) { + if (rule->getType() == RULE_BASE_TYPE_ALL) { + AllRule* allRule = (AllRule*)rule; + if (allRule->getIds().size() > 0) { + parentNode["$all"] |= ryml::SEQ; + for (int i = 0; i < allRule->getIds().size(); i++) { + parentNode["$all"][i] << allRule->getIds().at(i); + } + } + } + //------------------------------------------------ + else if (rule->getBaseType() == RULE_BASE_TYPE_LOGIC) { + c4::yml::NodeRef logicNode; + if (rule->getType() == RULE_TYPE_LOGIC_AND) { + parentNode["$and"] |= ryml::SEQ; + logicNode = parentNode["$and"]; + } + else if (rule->getType() == RULE_TYPE_LOGIC_OR) { + parentNode["$or"] |= ryml::SEQ; + logicNode = parentNode["$or"]; + } + else if (rule->getType() == RULE_TYPE_LOGIC_NOT) { + parentNode["$not"] |= ryml::SEQ; + logicNode = parentNode["$not"]; + } - // json andJson = ruleToJson(andRule, &andRuleState); - // if (!andRuleState) { - // *state = false; - // return jsonRule; - // } - // andsjson.push_back(andJson); - // } - // *state = true; - // jsonRule["$and"] = andsjson; - // } - // else if(rule->ors!=NULL&&rule->ors->size()>0){ - // vector orsjson; - // for (int i = 0; i < rule->ors->size(); i++) { - // mountcloud::Rule* orRule = rule->ors->at(i); - // bool orRuleState = false; + LogicRule* logicRule = (LogicRule*)rule; - // json orJson = ruleToJson(orRule, &orRuleState); - // if (!orRuleState) { - // *state = false; - // return jsonRule; - // } - // orsjson.push_back(orJson); - // } - // *state = true; - // jsonRule["$or"] = orsjson; - // } - // else { - // *state = false; - // } - //} - //else { - // *state = false; - //} - return jsonRule; + for (int i = 0; i < logicRule->getRules()->size(); i++) { + Rule* tlr = logicRule->getRules()->at(i); + logicNode[i] |= ryml::MAP; + c4::yml::NodeRef logicChildNode = logicNode[i]; + ruleToYaml(logicChildNode, tlr); + } + } + //------------------------------------------------ + else if (rule->getType() == RULE_TYPE_NUMBER_INT) { + IntRule* intRule = (IntRule*)rule; + parentNode["$int"] << intRule->getNum(); + } + else if (rule->getType() == RULE_TYPE_NUMBER_COUNT) { + CountRule* countRule = (CountRule*)rule; + parentNode["$count"] |= ryml::SEQ; + for (int i = 0; i < countRule->getIds().size(); i++) { + parentNode["$count"][i] << countRule->getIds().at(i); + } + } + //------------------------------------------------ + else if (rule->getBaseType() == RULE_BASE_TYPE_COMPARE) { + c4::yml::NodeRef compaerNode; + if (rule->getType() == RULE_TYPE_COMPARE_LT) { + parentNode["$lt"] |= ryml::SEQ; + compaerNode = parentNode["$lt"]; + } + else if (rule->getType() == RULE_TYPE_COMPARE_LE) { + parentNode["$lt"] |= ryml::SEQ; + compaerNode = parentNode["$lt"]; + } + else if (rule->getType() == RULE_TYPE_COMPARE_GT) { + parentNode["$gt"] |= ryml::SEQ; + compaerNode = parentNode["$gt"]; + } + else if (rule->getType() == RULE_TYPE_COMPARE_GE) { + parentNode["$ge"] |= ryml::SEQ; + compaerNode = parentNode["$ge"]; + } + CompareRule* compareRule = (CompareRule*)rule; + + + compaerNode[0] |= ryml::MAP; + compaerNode[1] |= ryml::MAP; + + c4::yml::NodeRef num1Node = compaerNode[0]; + c4::yml::NodeRef num2Node = compaerNode[1]; + + ruleToYaml(num1Node, compareRule->getNum1()); + ruleToYaml(num2Node, compareRule->getNum2()); + + } + } std::vector FeatureLibrary::parseRulesByYaml(c4::yml::NodeRef node) { diff --git a/FireDog/featurelibrary.h b/FireDog/featurelibrary.h index cbfaf38..ff9d044 100644 --- a/FireDog/featurelibrary.h +++ b/FireDog/featurelibrary.h @@ -144,10 +144,9 @@ namespace firedog { class FeatureLibrary { public: /// - /// create by json + /// create by yml /// - static FeatureLibrary* createByJson(std::string json, int* errorcode); - static FeatureLibrary* createByYaml(std::string yaml, int* errorcode); + static FeatureLibrary* createByYaml(std::string yaml, int* errorcode); /// /// library version @@ -161,26 +160,20 @@ namespace firedog { std::vector* items = NULL; /// - /// to json + /// to yml /// /// - std::string toJson(int* state); + std::string toYaml(int* state); FeatureLibrary(); ~FeatureLibrary(); private: - static std::vector parseRulesByJson(nlohmann::json rulejson); - static mountcloud::Rule* parseRuleByJson(nlohmann::json rulejson); - static std::vector parseRulesByYaml(c4::yml::NodeRef node); static mountcloud::Rule* parseRuleByYaml(c4::yml::NodeRef node); - /// - /// rule to json object - /// - /// - /// - nlohmann::json ruleToJson(mountcloud::Rule* rule,bool* state); + + void ruleToYaml(c4::yml::NodeRef& parentNode, mountcloud::Rule* rule); + }; diff --git a/FireDog/matcher.cpp b/FireDog/matcher.cpp index 633d252..dbf405a 100644 --- a/FireDog/matcher.cpp +++ b/FireDog/matcher.cpp @@ -271,28 +271,14 @@ Matcher::~Matcher() { } -std::vector * Matcher::matchBytes(const char* bytes, int length) { - - std::vector * result = NULL; - +void Matcher::matchBytes(const char* bytes, int length) { for (int i = 0; i < length; i++) { char byte = bytes[i]; - std::vector * tempResult = this->matchByte(byte); - if (tempResult != NULL) { - //ƥÅ䵽ÁË - if (result == NULL) { - result = new std::vector(); - } - result->insert(result->end(), tempResult->begin(), tempResult->end()); - //ɾµôvectorµÄָÕë - delete tempResult; - } + this->matchByte(byte); } - return result; } -std::vector * Matcher::matchByte(const char byte) { - std::vector * result = NULL; +void Matcher::matchByte(const char byte) { if (matchCache == NULL) { matchCache = new std::vector(); } @@ -304,16 +290,8 @@ std::vector * Matcher::matchByte(const char byte) { MatcherData* mbd = matchCache->at(i); //УÑéÍêÕû×ֽÚ - std::vector * tempResult = checkMatcherFeature(mbd, byte, tempMatchBytesCache); - if (tempResult !=NULL) { - //ƥÅ䵽ÁË - if (result==NULL) { - result = new std::vector(); - } - result->insert(result->end(), tempResult->begin(), tempResult->end()); - //ɾµôvectorµÄָÕë - delete tempResult; - } + matcherFeature(mbd, byte, tempMatchBytesCache); + } this->matchCache->clear(); delete this->matchCache; @@ -322,90 +300,52 @@ std::vector * Matcher::matchByte(const char byte) { //step2:find by feature library Ȼºó´ÓÌØÕ÷¿âÖÐÀ­ȡ //УÑéÍêÕû×ֽÚ - std::vector * tempResult = checkMatcherFeature(this->dataSource->getData(), byte, this->matchCache); - if (tempResult != NULL) { - //ƥÅ䵽ÁË - if (result == NULL) { - result = new std::vector(); + matcherFeature(this->dataSource->getData(), byte, this->matchCache); +} + +std::vector * Matcher::check() { + std::vector * result = NULL; + + for (std::unordered_map::iterator iter = featureRuleData->begin(); iter != featureRuleData->end(); iter++) { + int featureId = iter->first; + mountcloud::RuleData* ruleData = iter->second; + FeatureLibraryItem* fitem = dataSource->getFeatureLibraryItem(featureId); + Rule* rule = fitem->rule; + if (rule != NULL && rule->check(ruleData)) { + if (result == NULL) { + result = new std::vector(); + } + MatcherResult* mr = new MatcherResult(); + mr->featureId = featureId; + mr->author = fitem->author; + mr->name = fitem->name; + mr->describe = fitem->describe; + result->push_back(mr); } - result->insert(result->end(), tempResult->begin(), tempResult->end()); - //ɾµôvectorµÄָÕë - delete tempResult; } return result; } -std::vector * Matcher::checkMatcherFeature(MatcherData* mbd, char byte, std::vector* tempMatchBytesCache) { - std::vector * result = NULL; +void Matcher::matcherFeature(MatcherData* mbd, char byte, std::vector* tempMatchBytesCache) { //¸ßλ const char highPos = StringUtil::byteHigh(byte); //µÍλ const char lowPos = StringUtil::byteLow(byte); //УÑéÍêÕû×ֽÚ - result = checkMatcherFeatureMap(mbd->fullData, byte, tempMatchBytesCache); - if (result != NULL) { - return result; - } + matcherFeatureMap(mbd->fullData, byte, tempMatchBytesCache); + //УÑé¸ßλ - result = checkMatcherFeatureMap(mbd->highPosData, highPos, tempMatchBytesCache); - if (result != NULL) { - return result; - } + matcherFeatureMap(mbd->highPosData, highPos, tempMatchBytesCache); + //УÑéµÍλ - result = checkMatcherFeatureMap(mbd->lowPosData, lowPos, tempMatchBytesCache); - if (result != NULL) { - return result; - } + matcherFeatureMap(mbd->lowPosData, lowPos, tempMatchBytesCache); - //УÑéany - //if (mbd->anyData != NULL) { - // //any ƥÅ䵽ÁË - // if (mbd->anyData->features != NULL) { - // result = new std::vector(); - // for (int i = 0; i < mbd->anyData->features->size(); i++) { - // MatcherFeature* mf = mbd->anyData->features->at(i); - // int featureId = mf->featureId; - // string featureKey = mf->featureKey; - - // RuleData* ruleData = NULL; - // if (featureRuleData == NULL) { - // featureRuleData = new unordered_map(); - // } - // if (featureRuleData->find(featureId) == featureRuleData->end()) { - // ruleData = new RuleData(); - // } - // else { - // ruleData = featureRuleData->at(featureId); - // } - - // ruleData->set(featureKey, true); - - // FeatureLibraryItem* fitem = dataSource->getFeatureLibraryItem(featureId); - // Rule* rule = fitem->rule; - // if (rule != NULL && rule->check(ruleData)) { - // MatcherResult* mr = new MatcherResult(); - // mr->featureId = featureId; - // mr->author = fitem->author; - // mr->name = fitem->name; - // mr->describe = fitem->describe; - // result->push_back(mr); - // } - // } - // } - // else { - // //anyûÓÐƥÅ䵽 - // tempMatchBytesCache->push_back(mbd->anyData); - // } - //} - - return result; } -std::vector* Matcher::checkMatcherFeatureMap(AvlMap* map, char byte, std::vector* tempMatchBytesCache) { - std::vector* result = NULL; +void Matcher::matcherFeatureMap(AvlMap* map, char byte, std::vector* tempMatchBytesCache) { if (map==NULL) { - return result; + return; } AvlMap::Iterator mbditer = map->find(byte); @@ -433,24 +373,10 @@ std::vector* Matcher::checkMatcherFeatureMap(AvlMapoperator[](featureId) = ruleData; - FeatureLibraryItem* fitem = dataSource->getFeatureLibraryItem(featureId); - Rule* rule = fitem->rule; - if (rule != NULL && rule->check(ruleData)) { - if (result==NULL) { - result = new std::vector(); - } - MatcherResult* mr = new MatcherResult(); - mr->featureId = featureId; - mr->author = fitem->author; - mr->name = fitem->name; - mr->describe = fitem->describe; - result->push_back(mr); - } } } tempMatchBytesCache->push_back(iterMbd); } - return result; } MatcherData* MatcherDataSource::getData() { diff --git a/FireDog/matcher.h b/FireDog/matcher.h index 139ca7b..82576af 100644 --- a/FireDog/matcher.h +++ b/FireDog/matcher.h @@ -90,8 +90,10 @@ namespace firedog { Matcher(MatcherDataSource* dataSource); ~Matcher(); - std::vector * matchBytes(const char* bytes, int length); - std::vector * matchByte(const char byte); + void matchBytes(const char* bytes, int length); + void matchByte(const char byte); + + std::vector * check(); private: /// /// data source, @@ -109,8 +111,8 @@ namespace firedog { std::unordered_map* featureRuleData = NULL; - std::vector* checkMatcherFeature(MatcherData* mbd, char byte, std::vector* tempMatchBytesCache); - std::vector* checkMatcherFeatureMap(AvlMap* map,char byte, std::vector* tempMatchBytesCache); + void matcherFeature(MatcherData* mbd, char byte, std::vector* tempMatchBytesCache); + void matcherFeatureMap(AvlMap* map,char byte, std::vector* tempMatchBytesCache); }; } diff --git a/FireDog/rule/rule.cpp b/FireDog/rule/rule.cpp index 4599646..6837129 100644 --- a/FireDog/rule/rule.cpp +++ b/FireDog/rule/rule.cpp @@ -118,6 +118,9 @@ bool AndRule::check(RuleData* data) { } return true; } + else { + return true; + } return true; } @@ -135,7 +138,10 @@ bool OrRule::check(RuleData* data) { } } } - return true; + else { + return true; + } + return false; } NotRule::NotRule() { @@ -153,7 +159,10 @@ bool NotRule::check(RuleData* data) { } return true; } - return true; + else { + return true; + } + return false; } NumberRule::NumberRule() { diff --git a/FireDogEditor/FireDogEditor.cpp b/FireDogEditor/FireDogEditor.cpp index 96c6fbf..7373836 100644 --- a/FireDogEditor/FireDogEditor.cpp +++ b/FireDogEditor/FireDogEditor.cpp @@ -15,6 +15,8 @@ #include "gui18n.h" #include "gui18nutil.h" +#include "stringutil.h" + using namespace firedog; //¹¹Ô캯Êý @@ -44,6 +46,9 @@ FireDogEditor::FireDogEditor(QWidget *parent) //³õʼ»¯ void FireDogEditor::init() { + QIcon iconFile(":/FireDogEditor/resources/icon.ico"); + this->setWindowIcon(iconFile); + QFile qss(":/qss/css/qss.css"); if (qss.open(QIODevice::ReadOnly)) { QByteArray qssBytes = qss.readAll(); @@ -409,14 +414,14 @@ void FireDogEditor::slots_saveFile() { QFile file(this->openFilePath); int state = -1; - std::string json = this->featureLibrary->toJson(&state); + std::string ymlstr = this->featureLibrary->toYaml(&state); if (state != NO_ERROR) { QssMessageBox::warn("Save fail,Feature library convert to json fail!.", this, "Warn"); return; } - QString jsonstr = QString::fromUtf8(QByteArray::fromRawData(json.c_str(), json.size())); + QString jsonstr = QString::fromUtf8(QByteArray::fromRawData(ymlstr.c_str(), ymlstr.size())); if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) { @@ -457,7 +462,7 @@ void FireDogEditor::slots_saveToFile() { } int state = -1; - std::string json = this->featureLibrary->toJson(&state); + std::string ymlstr = this->featureLibrary->toYaml(&state); if (state != NO_ERROR) { QString titleStr = Gui18n::GetInstance()->GetConfig("text-message-box-title-warning", "Warning"); @@ -466,7 +471,7 @@ void FireDogEditor::slots_saveToFile() { return; } - QString jsonstr = QString::fromUtf8(QByteArray::fromRawData(json.c_str(), json.size())); + QString jsonstr = QString::fromUtf8(QByteArray::fromRawData(ymlstr.c_str(), ymlstr.size())); if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) { @@ -1005,6 +1010,14 @@ void FireDogEditor::slots_featureLibraryInfoRuleMenuAddEvent() { bool isOk = this->fireDogFeatureRuleInfoView->updateRule(&rule, &childs, parent); if (isOk) { QStandardItem* ruleitem = new QStandardItem(rule); + if (rule=="$lt"||rule=="$le"||rule=="$gt"||rule=="$ge") { + for (int i=0;iappendRow(childitem); + } + } //Èç¹ûÊǿյÄ if (ruleIsEmpty) { featureLibraryInfoRuleTreeModel->appendRow(ruleitem); @@ -1018,6 +1031,7 @@ void FireDogEditor::slots_featureLibraryInfoRuleMenuAddEvent() { QModelIndex index = this->featureLibraryInfoRuleTreeModel->indexFromItem(ruleitem); ui.treeViewLibraryInfoRules->selectionModel()->setCurrentIndex(index, QItemSelectionModel::Select | QItemSelectionModel::Rows); + ui.treeViewLibraryInfoRules->expand(index); } } @@ -1044,7 +1058,24 @@ void FireDogEditor::slots_featureLibraryInfoRuleMenuEditEvent() { bool isOk = this->fireDogFeatureRuleInfoView->updateRule(&rule, &childs, parent); if (isOk) { + + bool resultIsCmp = false; + if (rule == "$lt" || rule == "$le" || rule == "$gt" || rule == "$ge") { + resultIsCmp = true; + } + selectItem->setText(rule); + + if (resultIsCmp) { + selectItem->removeRows(0, selectItem->rowCount()); + for (int i = 0; i < childs.size(); i++) + { + QString childStr = childs.at(i); + QStandardItem* childitem = new QStandardItem(childStr); + selectItem->appendRow(childitem); + } + ui.treeViewLibraryInfoRules->expand(selectItem->index()); + } } } @@ -1192,7 +1223,7 @@ mountcloud::Rule* FireDogEditor::itemToRule(QStandardItem* item) { goto fail; } - bool isOperation = text == "$and" || text == "$or"; + bool isOperation = text == "$and" || text == "$or" || text == "$not"; //¸ù¹æÔòֻÄÜÊÇÂ߼­±í´ïʽ if (item->parent()==NULL) { @@ -1210,34 +1241,96 @@ mountcloud::Rule* FireDogEditor::itemToRule(QStandardItem* item) { } } - mountcloud::Rule* result = new mountcloud::Rule(); + mountcloud::Rule* result = NULL; - // if (isOperation) { - // int childCount = item->rowCount(); - // for (int i = 0; i < childCount; i++) { - // QStandardItem* childItem = item->child(i); - // mountcloud::Rule* childRule = itemToRule(childItem); - // if (childRule==NULL) { - // delete result; - // return NULL; - // } - // if(text=="$and"){ - // if (result->ands == NULL) { - // result->ands = new std::vector(); - // } - // result->ands->push_back(childRule); - // } - // else { - // if (result->ors == NULL) { - // result->ors = new std::vector(); - // } - // result->ors->push_back(childRule); - // } - // } - //} - // else { - // result->id = text.toStdString(); - // } + + if (text == "$and" || text == "$or" || text == "$not") { + mountcloud::LogicRule* logicRule = NULL; + if (text == "$and") { + logicRule = new mountcloud::AndRule(); + } + else if (text == "$or") { + logicRule = new mountcloud::OrRule(); + } + else { + logicRule = new mountcloud::NotRule(); + } + int childCount = item->rowCount(); + for (int i = 0; i < childCount; i++) { + QStandardItem* childItem = item->child(i); + mountcloud::Rule* cr = itemToRule(childItem); + if (cr != NULL) { + logicRule->addRule(cr); + } + } + result = logicRule; + } + else if (text.startsWith("$all")) { + QString infostr = text.mid(text.indexOf(":") + 1); + QStringList idsQList = infostr.split(","); + if (idsQList.size() > 0) { + std::vector ids; + for (int i = 0; i < idsQList.size(); i++) { + QString id = idsQList.at(i); + ids.push_back(id.toStdString()); + } + mountcloud::AllRule* allRule = new mountcloud::AllRule(ids); + result = allRule; + } + } + else if (text.startsWith("$count")) { + QString infostr = text.mid(text.indexOf(":") + 1); + QStringList idsQList = infostr.split(","); + if (idsQList.size() > 0) { + std::vector ids; + for (int i = 0; i < idsQList.size(); i++) { + QString id = idsQList.at(i); + ids.push_back(id.toStdString()); + } + mountcloud::CountRule* countRule = new mountcloud::CountRule(ids); + result = countRule; + } + } + else if (text.startsWith("$int")) { + QString infostr = text.mid(text.indexOf(":") + 1); + std::string numstr = infostr.toStdString(); + if (StringUtil::isNumber(numstr)) { + int num = atoi(numstr.c_str()); + mountcloud::IntRule* intRule = new mountcloud::IntRule(num); + result = intRule; + } + } + else if (text == "$lt" || text == "$le" || text == "$gt" || text == "$ge") { + + int childCount = item->rowCount(); + if (childCount == 2) { + mountcloud::Rule* num1 = itemToRule(item->child(0)); + mountcloud::Rule* num2 = itemToRule(item->child(1)); + + if (num1 != NULL && num1->getBaseType() == RULE_BASE_TYPE_NUMBER + && num2 != NULL && num2->getBaseType() == RULE_BASE_TYPE_NUMBER) { + + mountcloud::NumberRule* numRule1 = (mountcloud::NumberRule*)num1; + mountcloud::NumberRule* numRule2 = (mountcloud::NumberRule*)num2; + + mountcloud::CompareRule* compareRule = NULL; + if (text == "$lt") { + compareRule = new mountcloud::LtRule(numRule1, numRule2); + } + else if (text == "$le") { + compareRule = new mountcloud::LeRule(numRule1, numRule2); + } + else if (text == "$gt") { + compareRule = new mountcloud::GtRule(numRule1, numRule2); + } + else if (text == "$ge") { + compareRule = new mountcloud::GeRule(numRule1, numRule2); + } + + result = compareRule; + } + } + } return result; diff --git a/FireDogEditor/FireDogEditor.h b/FireDogEditor/FireDogEditor.h index 18cf346..807e328 100644 --- a/FireDogEditor/FireDogEditor.h +++ b/FireDogEditor/FireDogEditor.h @@ -17,7 +17,7 @@ #include "Qss/Qss.h" -#define FIREDOG_EDITOR_VERSION "v2.1 DEV" +#define FIREDOG_EDITOR_VERSION "v2.1" #define EXIT_CODE_REBOOT -123456789 //ƥÅä¹ܼÒ diff --git a/FireDogEditor/FireDogEditor.pri b/FireDogEditor/FireDogEditor.pri index f073840..dc86559 100644 --- a/FireDogEditor/FireDogEditor.pri +++ b/FireDogEditor/FireDogEditor.pri @@ -19,10 +19,13 @@ HEADERS += ../FireDog/config.h \ ../FireDog/matcher.h \ ../FireDog/rule/rule.h \ ../FireDog/stringutil.h \ + ../FireDog/yaml/yaml.hpp \ + ./resource.h \ ./FireDogEditor.h \ ./FireDogFeatureInfo.h \ ./FireDogFeatureRuleInfo.h \ ./FireDogSwitchLanguage.h \ + ./FireDogAbout.h \ ./bigdatatableitem.h \ ./loadingdialog.h \ ./hexeditor.h \ @@ -32,9 +35,7 @@ HEADERS += ../FireDog/config.h \ ./partutil.h \ ./matchthread.h \ ./gui18n.h \ - ./gui18nutil.h \ - ./FireDogAbout.h \ - ./resource.h + ./gui18nutil.h SOURCES += ../FireDog/converter.cpp \ ../FireDog/featurelibrary.cpp \ ../FireDog/firedog.cpp \ @@ -45,6 +46,7 @@ SOURCES += ../FireDog/converter.cpp \ ../FireDog/json/json-schema/string-format-check.cpp \ ../FireDog/matcher.cpp \ ../FireDog/rule/rule.cpp \ + ./FireDogAbout.cpp \ ./FireDogFeatureInfo.cpp \ ./FireDogFeatureRuleInfo.cpp \ ./FireDogSwitchLanguage.cpp \ @@ -59,8 +61,7 @@ SOURCES += ../FireDog/converter.cpp \ ./partutil.cpp \ ./matchthread.cpp \ ./gui18n.cpp \ - ./gui18nutil.cpp \ - ./FireDogAbout.cpp + ./gui18nutil.cpp FORMS += ./FireDogEditor.ui \ ./FireDogFeatureInfo.ui \ ./FireDogFeatureRuleInfo.ui \ diff --git a/FireDogEditor/FireDogEditor.qrc b/FireDogEditor/FireDogEditor.qrc index fa27f39..d1fd7f9 100644 --- a/FireDogEditor/FireDogEditor.qrc +++ b/FireDogEditor/FireDogEditor.qrc @@ -2,5 +2,6 @@ resources/loading.gif resources/loading-new-backgroud.png + resources/icon.ico diff --git a/FireDogEditor/FireDogEditor.ui b/FireDogEditor/FireDogEditor.ui index 0fcccaf..15e39d1 100644 --- a/FireDogEditor/FireDogEditor.ui +++ b/FireDogEditor/FireDogEditor.ui @@ -245,6 +245,9 @@ + + QAbstractItemView::NoEditTriggers + true diff --git a/FireDogEditor/FireDogFeatureInfo.cpp b/FireDogEditor/FireDogFeatureInfo.cpp index 0ce310f..a45628f 100644 --- a/FireDogEditor/FireDogFeatureInfo.cpp +++ b/FireDogEditor/FireDogFeatureInfo.cpp @@ -18,8 +18,6 @@ FireDogFeatureInfo::FireDogFeatureInfo(QWidget *parent) : void FireDogFeatureInfo::init() { - - QRegExp hexregx(FEATURE_FORMATE_REG_HEX); this->hexValidator = new QRegExpValidator(hexregx, ui->lineEditContent); diff --git a/FireDogEditor/FireDogFeatureRuleInfo.cpp b/FireDogEditor/FireDogFeatureRuleInfo.cpp index 44e43c9..c85e20d 100644 --- a/FireDogEditor/FireDogFeatureRuleInfo.cpp +++ b/FireDogEditor/FireDogFeatureRuleInfo.cpp @@ -25,6 +25,13 @@ void FireDogFeatureRuleInfo::init() { this->keyValidator = new QRegExpValidator(keyregx, ui->lineEditKey); ui->lineEditKey->setValidator(keyValidator); + this->num1KeyValidator = new QRegExpValidator(keyregx, ui->lineEditNum1Count); + ui->lineEditNum1Count->setValidator(num1KeyValidator); + + this->num2KeyValidator = new QRegExpValidator(keyregx, ui->lineEditNum2Count); + ui->lineEditNum2Count->setValidator(num2KeyValidator); + + ui->comboBoxNum1Type->addItem("$count"); ui->comboBoxNum1Type->addItem("$int"); ui->spinBoxNum1Int->setVisible(false); @@ -54,7 +61,14 @@ void FireDogFeatureRuleInfo::init() { connect(ui->comboBoxNum2Type, SIGNAL(currentIndexChanged(QString)), this, SLOT(currentIndexChanged(QString))); //i18n - //Gui18nUtil::SetText(ui->labelType, "feature-rule-info-type"); + Gui18nUtil::SetText(ui->groupBoxType, "feature-rule-info-type"); + Gui18nUtil::SetText(ui->groupBoxRuleInfo, "feature-rule-info-ruleinfo"); + + Gui18nUtil::SetText(ui->labelKeyTips, "feature-rule-info-keytips"); + Gui18nUtil::SetText(ui->labelCompageTips, "feature-rule-info-keytips"); + + Gui18nUtil::SetText(ui->labelNumTips, "feature-rule-info-numtips"); + Gui18nUtil::SetText(ui->labelKey, "feature-rule-info-key"); Gui18nUtil::SetText(ui->pushButtonSave, "feature-rule-info-save"); Gui18nUtil::SetText(ui->pushButtonCancel, "feature-rule-info-cancel"); @@ -107,14 +121,26 @@ void FireDogFeatureRuleInfo::slots_radioClick() { void FireDogFeatureRuleInfo::slots_saveClick() { this->isOk = true; - //if (ui->radioButtonKey->isChecked()){ - // if (ui->lineEditKey->text().isEmpty()) { - // QString titleStr = Gui18n::GetInstance()->GetConfig("text-message-box-title-warning", "Warning"); - // QString messageStr = Gui18n::GetInstance()->GetConfig("feature-rule-please-enter-key", "Please enter [Key]."); - // QssMessageBox::warn(messageStr, this, titleStr); - // return; - // } - //} + //Õâ¸öº¯ÊýÖ÷ҪÊÇУÑé + if (ui->radioButtonAll->isChecked() || ui->radioButtonCount->isChecked()) { + if (ui->lineEditKey->text().isEmpty()) { + QString titleStr = Gui18n::GetInstance()->GetConfig("text-message-box-title-warning", "Warning"); + QString messageStr = Gui18n::GetInstance()->GetConfig("feature-rule-please-enter-key", "Please enter [Key]."); + QssMessageBox::warn(messageStr, this, titleStr); + return; + } + } + + if (ui->radioButtonLt->isChecked() || ui->radioButtonLe->isChecked() || + ui->radioButtonGt->isChecked() || ui->radioButtonGe->isChecked()) { + if ((ui->comboBoxNum1Type->currentText() == "$count" && ui->lineEditNum1Count->text().isEmpty()) + || (ui->comboBoxNum2Type->currentText() == "$count" && ui->lineEditNum2Count->text().isEmpty())) { + QString titleStr = Gui18n::GetInstance()->GetConfig("text-message-box-title-warning", "Warning"); + QString messageStr = Gui18n::GetInstance()->GetConfig("feature-rule-please-enter-key", "Please enter [Key]."); + QssMessageBox::warn(messageStr, this, titleStr); + return; + } + } this->hide(); } @@ -128,12 +154,6 @@ bool FireDogFeatureRuleInfo::updateRule(QString* rule, QVector* childs, this->isOk = false; setDefaultValue(); - // if (isRoot) { - // //ui->radioButtonKey->setEnabled(false); - // } - //else { - // //ui->radioButtonKey->setEnabled(true); - // } auto getRuleInfoByStr = [](QString& rulestr) { int index = rulestr.lastIndexOf(":"); @@ -141,6 +161,30 @@ bool FireDogFeatureRuleInfo::updateRule(QString* rule, QVector* childs, return infostr; }; + //¸ù¾ݸ¸½ڵãÆôÓýûÓÃ״̬ + if (parent.isEmpty()) { + ui->radioButtonAll->setEnabled(false); + ui->radioButtonCount->setEnabled(false); + ui->radioButtonInt->setEnabled(false); + + ui->radioButtonLt->setEnabled(false); + ui->radioButtonLe->setEnabled(false); + ui->radioButtonGt->setEnabled(false); + ui->radioButtonGe->setEnabled(false); + } + else if (parent == "$lt" || parent == "$le" || parent == "$gt" || parent == "$ge") { + ui->radioButtonAnd->setEnabled(false); + ui->radioButtonOr->setEnabled(false); + ui->radioButtonNot->setEnabled(false); + + ui->radioButtonAll->setEnabled(false); + + ui->radioButtonLt->setEnabled(false); + ui->radioButtonLe->setEnabled(false); + ui->radioButtonGt->setEnabled(false); + ui->radioButtonGe->setEnabled(false); + } + if (!rule->isEmpty()) { if (*rule == "$and") { ui->radioButtonAnd->setChecked(true); @@ -174,7 +218,7 @@ bool FireDogFeatureRuleInfo::updateRule(QString* rule, QVector* childs, else if (*rule == "$ge") { ui->radioButtonGe->setChecked(true); } - + //ÉèÖÃĬÈÏֵ if (rule->startsWith("$all") || rule->startsWith("$count")) { QString infostr = getRuleInfoByStr(*rule); @@ -186,6 +230,14 @@ bool FireDogFeatureRuleInfo::updateRule(QString* rule, QVector* childs, ui->spinBoxInt->setValue(intval); } else if (*rule == "$lt" || *rule == "$le" || *rule == "$gt" || *rule == "$ge") { + ui->radioButtonAnd->setEnabled(false); + ui->radioButtonOr->setEnabled(false); + ui->radioButtonNot->setEnabled(false); + + ui->radioButtonAll->setEnabled(false); + ui->radioButtonCount->setEnabled(false); + ui->radioButtonInt->setEnabled(false); + QString num1 = childs->at(0); QString num2 = childs->at(1); @@ -221,18 +273,87 @@ bool FireDogFeatureRuleInfo::updateRule(QString* rule, QVector* childs, this->exec(); if (this->isOk) { + if (ui->radioButtonAnd->isChecked()) { - rule->clear(); - rule->append("$and"); - } + rule->clear(); + rule->append("$and"); + } else if (ui->radioButtonOr->isChecked()) { rule->clear(); rule->append("$or"); - } + } + else if (ui->radioButtonNot->isChecked()) { + rule->clear(); + rule->append("$not"); + } + ///////////////////////////////////////////// + else if (ui->radioButtonAll->isChecked()) { + rule->clear(); + rule->append("$all:"); + rule->append(ui->lineEditKey->text()); + } + else if (ui->radioButtonCount->isChecked()) { + rule->clear(); + rule->append("$count:"); + rule->append(ui->lineEditKey->text()); + } + else if (ui->radioButtonInt->isChecked()) { + rule->clear(); + rule->append("$int:"); + rule->append(QString::number(ui->spinBoxInt->value())); + } + ///////////////////////////////////////////// + else if(ui->radioButtonLt->isChecked() || ui->radioButtonLe->isChecked() || ui->radioButtonGt->isChecked() || ui->radioButtonGe->isChecked()){ + rule->clear(); + if (ui->radioButtonLt->isChecked()) { + rule->append("$lt"); + } + else if (ui->radioButtonLe->isChecked()) { + rule->append("$le"); + } + else if (ui->radioButtonGt->isChecked()) { + rule->append("$gt"); + } + else if (ui->radioButtonGe->isChecked()) { + rule->append("$ge"); + } + childs->clear(); + QString num1Str = ""; + if (ui->comboBoxNum1Type->currentText()=="$count") { + num1Str.append("$count:"); + num1Str.append(ui->lineEditNum1Count->text()); + } + else { + num1Str.append("$int1:"); + num1Str.append(QString::number(ui->spinBoxNum1Int->value())); + } + + QString num2Str = ""; + if (ui->comboBoxNum2Type->currentText() == "$count") { + num2Str.append("$count:"); + num2Str.append(ui->lineEditNum2Count->text()); + } + else { + num2Str.append("$int1:"); + num2Str.append(QString::number(ui->spinBoxNum2Int->value())); + } + + childs->push_back(num1Str); + childs->push_back(num2Str); + } + + /*if (ui->radioButtonAnd->isChecked()) { + rule->clear(); + rule->append("$and"); + } + else if (ui->radioButtonOr->isChecked()) { + rule->clear(); + rule->append("$or"); + } else { rule->clear(); rule->append(ui->lineEditKey->text()); - } + }*/ } return this->isOk; @@ -279,5 +400,18 @@ void FireDogFeatureRuleInfo::setDefaultValue() { ui->stackedWidgetRuleInfo->setCurrentIndex(0); + ui->radioButtonAnd->setEnabled(true); + ui->radioButtonOr->setEnabled(true); + ui->radioButtonNot->setEnabled(true); + + ui->radioButtonAll->setEnabled(true); + ui->radioButtonCount->setEnabled(true); + ui->radioButtonInt->setEnabled(true); + + ui->radioButtonLt->setEnabled(true); + ui->radioButtonLe->setEnabled(true); + ui->radioButtonGt->setEnabled(true); + ui->radioButtonGe->setEnabled(true); + slots_radioClick(); } diff --git a/FireDogEditor/FireDogFeatureRuleInfo.h b/FireDogEditor/FireDogFeatureRuleInfo.h index 1c0195e..773d6fe 100644 --- a/FireDogEditor/FireDogFeatureRuleInfo.h +++ b/FireDogEditor/FireDogFeatureRuleInfo.h @@ -7,7 +7,7 @@ #include "Qss/Qss.h" -#define FEATURE_RULE_FORMATE_REG_KEY "[0-9a-zA-Z]{0,}" +#define FEATURE_RULE_FORMATE_REG_KEY "[0-9a-zA-Z,]{0,}" namespace Ui { class FireDogFeatureRuleInfo; @@ -33,6 +33,8 @@ private: Ui::FireDogFeatureRuleInfo *ui; QValidator* keyValidator = NULL; + QValidator* num1KeyValidator = NULL; + QValidator* num2KeyValidator = NULL; bool isOk = false; diff --git a/FireDogEditor/FireDogFeatureRuleInfo.ui b/FireDogEditor/FireDogFeatureRuleInfo.ui index 5a8cffe..b5507e1 100644 --- a/FireDogEditor/FireDogFeatureRuleInfo.ui +++ b/FireDogEditor/FireDogFeatureRuleInfo.ui @@ -435,7 +435,7 @@ Separate multiple ids with commas (,). - + 150 @@ -476,7 +476,7 @@ - + 150 diff --git a/FireDogEditor/matchthread.cpp b/FireDogEditor/matchthread.cpp index d04ad67..b9b6cb3 100644 --- a/FireDogEditor/matchthread.cpp +++ b/FireDogEditor/matchthread.cpp @@ -68,9 +68,9 @@ void MatchWorkThread::run() { while (true) { QByteArray bytes = file.read(WORK_READ_FILE_LENGTH); - bool matchState = match(bytes); + match(bytes); //ƥÅ䵽Á˻òÕßÎļþ¶ÁÍêÁË - if (bytes.size() < WORK_READ_FILE_LENGTH || matchState) { + if (bytes.size() < WORK_READ_FILE_LENGTH) { break; } } @@ -99,13 +99,42 @@ void MatchWorkThread::run() { QByteArray bytes = file.read(WORK_READ_FILE_LENGTH); readlength = readlength + len; - bool matchState = match(bytes); + match(bytes); //ƥÅ䵽Á˻òÕßÎļþ¶ÁÍêÁË - if (bytes.size() < WORK_READ_FILE_LENGTH || matchState) { + if (bytes.size() < WORK_READ_FILE_LENGTH) { break; } } } + vector* result = this->nowWork->matcher->check(); + if (result != NULL) { + + int sourceId = this->nowWork->sourceId; + int workType = this->nowWork->workType; + QString workContent = this->nowWork->content; + + for (int i = 0; i < result->size(); i++) { + + MatcherResult* mr = result->at(i); + + HitFeature hf; + hf.sourceId = sourceId; + hf.featureId = mr->featureId; + hf.workType = workType; + hf.content = workContent; + hf.name = QString::fromStdString(mr->name); + hf.author = QString::fromStdString(mr->author); + hf.describe = QString::fromStdString(mr->describe); + + emit hit(hf); + + delete mr; + mr = NULL; + } + + delete result; + result = NULL; + } } this->state = STATE_NO_ERROR; @@ -116,43 +145,13 @@ void MatchWorkThread::run() { emit workSuccess(this->id); } -bool MatchWorkThread::match(QByteArray byteArray) { +void MatchWorkThread::match(QByteArray byteArray) { Matcher* matcher = this->nowWork->matcher; bool matchstate = false; for (int i = 0; i < byteArray.size(); i++) { char byte = byteArray.at(i); - vector* result = matcher->matchByte(byte); - if (result != NULL) { - - int sourceId = this->nowWork->sourceId; - int workType = this->nowWork->workType; - QString workContent = this->nowWork->content; - - for (int i = 0; i < result->size(); i++) { - - MatcherResult* mr = result->at(i); - - HitFeature hf; - hf.sourceId = sourceId; - hf.featureId = mr->featureId; - hf.workType = workType; - hf.content = workContent; - hf.name = QString::fromStdString(mr->name); - hf.author = QString::fromStdString(mr->author); - hf.describe = QString::fromStdString(mr->describe); - - emit hit(hf); - - delete mr; - mr = NULL; - } - - delete result; - result = NULL; - matchstate = true; - } + matcher->matchByte(byte); } - return matchstate; } int MatchWorkThread::getState() { diff --git a/FireDogEditor/matchthread.h b/FireDogEditor/matchthread.h index 7ef6327..f08ce4d 100644 --- a/FireDogEditor/matchthread.h +++ b/FireDogEditor/matchthread.h @@ -68,7 +68,7 @@ private: int state; MatchWork* nowWork = NULL; - bool match(QByteArray byteArray); + void match(QByteArray byteArray); signals: void nextWork(MatchWorkThread* misson, int* state); diff --git a/FireDogEditor/resources/fdlanguages/en-us.json b/FireDogEditor/resources/fdlanguages/en-us.json index c6e6be6..e73f6d8 100644 --- a/FireDogEditor/resources/fdlanguages/en-us.json +++ b/FireDogEditor/resources/fdlanguages/en-us.json @@ -127,6 +127,9 @@ "feature-rule-window-title":"Feature Rule Editor", "feature-rule-info-type":"Type", + "feature-rule-info-ruleinfo":"RuleInfo", + "feature-rule-info-keytips":"Separate multiple ids with commas (,).", + "feature-rule-info-numtips":"Only numbers are allowed.", "feature-rule-info-key":"Key", "feature-rule-info-save":"Save", "feature-rule-info-cancel":"Cancel", diff --git a/FireDogEditor/resources/fdlanguages/zh-cn.json b/FireDogEditor/resources/fdlanguages/zh-cn.json index 966451d..1008991 100644 --- a/FireDogEditor/resources/fdlanguages/zh-cn.json +++ b/FireDogEditor/resources/fdlanguages/zh-cn.json @@ -127,6 +127,9 @@ "feature-rule-window-title":"特征规则编辑器", "feature-rule-info-type":"类型", + "feature-rule-info-ruleinfo":"规则详情", + "feature-rule-info-keytips":"多个id之间用英文“,”逗号分隔。", + "feature-rule-info-numtips":"只能使用数字。", "feature-rule-info-key":"Key", "feature-rule-info-save":"保存", "feature-rule-info-cancel":"取消", diff --git a/README.md b/README.md index 68b048d..fa0fc17 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,8 @@ # FireDog 开源跨平台轻量级病毒特征检测引擎。Open source cross-platform lightweight virus signature detection engine. +在Releases下载最新版本,跟路径有个demo文件夹,可以参考,并且READEME.txt中也有说明。 + # 语言 Language C++ 11 @@ -32,6 +34,28 @@ https://github.com/MountCloud/FireDog/releases # 更新日志 Update log +# v1.3.1 + +``` +FireDog Version: v1.3.1 +FireDog Editor Version: v2.1 +Feature Libraray Version: v1.2.1 + +1:重构规则引擎,支持多种逻辑运算,支持多种统计运算,支持多种比较运算。 +2:重构特征库格式,从json改用yaml(json确实看起来不好看)。 +3:更改特征匹配逻辑,将字节匹配与校验匹配结果拆分。 +4:编辑器适配新的规则引擎和特征库格式。 + +1: Refactor the rule engine to support multiple logical operations, multiple statistical operations, and multiple comparison operations. +2: Refactor the feature library format and use yaml instead of json (json does not look good). +3: Change the feature matching logic to split the byte matching and check matching results. +4: The editor adapts to the new rule engine and signature library format. +``` + +## v1.3.1 界面GUI +### 规则配置GUI Rule Configuration GUI +![](images/v1.3.1-rule-gui.png) + # v1.2.1 @@ -77,6 +101,50 @@ Feature Libraray Version: v1.1.0 Support hex, md5, text type detection. ``` +# 说明 + +## 特征说明 Feature Description + +``` +key:代表这个特征的标识。 +hex:hex是以二进制形式进行匹配的二进制数据,允许模糊匹配如:0F ?0 A? ??。 +text:以文本形式匹配的关键字。 + +key: The identifier representing this feature. +hex: hex is binary data that is matched in binary form, allowing fuzzy matching such as: 0F ?0 A? ??. +text: The keyword to match in text form. +``` +## 规则说明 Rules Description + +``` +$and:[规则数组] 并运算,只有子规则结果全部为true(通过)时,结果为true(通过),如果有一条子规则结果为false(不通过),则结果为false(不通过)。 +$or:[规则数组] 或运算,有一条子规则结果全部为true(通过)时,结果为true(通过),如果全部子规则结果为false(不通过),则结果为false(不通过)。 +$not:[规则数组] 非运算,只有子规则结果全部为false(不通过)时,结果为true(通过),如果有一条子规则结果为true(通过),则结果为false(不通过)。意思就是如果$not的子规则是$lt($count(key1),$int(5))=$not(key1出现次数小于5)=不能(key1出现的次数小于5)=key1出现的次数需要大于5 + +$all:[字符串数组] 表示一组key必须至少全部出现1次。$all(key1,key2)表示key1和key2必须至少都出现一次。 +$count:*数字规则*[字符串数组] 表示统计key出现的次数。$count(key1,key2)就是统计key1和key2出现的次数。 +$int:*数字规则*[数字类型] 表示一个数字。$int(1)代表数字1 + +$lt:*比较规则*,小于比较,使用两个*数字规则*进行比较,数字规则1 < 数字规则2,则为true(通过)。 +$le:*比较规则*,小于等于比较,使用两个*数字规则*进行比较,数字规则1 <= 数字规则2,则为true(通过)。 +$gt:*比较规则*,大于比较,使用两个*数字规则*进行比较,数字规则1 > 数字规则2,则为true(通过)。 +$ge:*比较规则*,大于等于比较,使用两个*数字规则*进行比较,数字规则1 >= 数字规则2,则为true(通过)。 + + +$and: [rule array] Parallel operation, only when the sub-rule results are all true (pass), the result is true (pass), if there is a sub-rule result is false (fail), the result is false (fail) . +$or: [rule array] OR operation, when one sub-rule result is all true (pass), the result is true (pass), if all sub-rule results are false (fail), the result is false (fail) . +$not: [rule array] Non-operation, only when the sub-rule results are all false (fail), the result is true (pass), if there is a sub-rule result is true (pass), the result is false (fail) . It means that if the sub-rule of $not is $lt($count(key1), $int(5)) = $not (the number of occurrences of key1 is less than 5) = cannot (the number of occurrences of key1 is less than 5) = the number of occurrences of key1 needs to be greater than 5 + +$all: [string array] Indicates that a set of keys must all appear at least once. $all(key1,key2) means that both key1 and key2 must appear at least once. +$count: *Number rule*[String array] Indicates the number of times the key appears. $count(key1,key2) is to count the number of occurrences of key1 and key2. +$int: *Number Rule* [Number Type] Represents a number. $int(1) represents the number 1 + +$lt: *comparison rule*, less than comparison, use two *number rule* for comparison, number rule 1 < number rule 2, true (pass). +$le: *comparison rule*, less than or equal comparison, use two *number rule* for comparison, number rule 1 <= number rule 2, it is true (pass). +$gt: *comparison rule*, greater than comparison, use two *number rule* for comparison, number rule 1 > number rule 2, it is true (pass). +$ge: *comparison rule*, greater than or equal to comparison, use two *number rule* for comparison, number rule 1 >= number rule 2, it is true (pass). +``` + # 使用 Use ## Step 1: download diff --git a/images/v1.3.1-rule-gui.png b/images/v1.3.1-rule-gui.png new file mode 100644 index 0000000000000000000000000000000000000000..37e2ab1172b139416e98869f0025dcdaa4e54872 GIT binary patch literal 39692 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

R5}h@5MUp2I}kJ{WBb)zz616Q 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操作->保存或者另存为!!! + +2:特征讲解 +key:代表这个特征的标识。 +hex:hex是以二进制形式进行匹配的二进制数据,允许模糊匹配如:0F ?0 A? ??。 +text:以文本形式匹配的关键字。 + +3:规则 +$and:[规则数组] 并运算,只有子规则结果全部为true(通过)时,结果为true(通过),如果有一条子规则结果为false(不通过),则结果为false(不通过)。 +$or:[规则数组] 或运算,有一条子规则结果全部为true(通过)时,结果为true(通过),如果全部子规则结果为false(不通过),则结果为false(不通过)。 +$not:[规则数组] 非运算,只有子规则结果全部为false(不通过)时,结果为true(通过),如果有一条子规则结果为true(通过),则结果为false(不通过)。意思就是如果$not的子规则是$lt($count(key1),$int(5))=$not(key1出现次数小于5)=不能(key1出现的次数小于5)=key1出现的次数需要大于5 + +$all:[字符串数组] 表示一组key必须至少全部出现1次。$all(key1,key2)表示key1和key2必须至少都出现一次。 +$count:*数字规则*[字符串数组] 表示统计key出现的次数。$count(key1,key2)就是统计key1和key2出现的次数。 +$int:*数字规则*[数字类型] 表示一个数字。$int(1)代表数字1 + +$lt:*比较规则*,小于比较,使用两个*数字规则*进行比较,数字规则1 < 数字规则2,则为true(通过)。 +$le:*比较规则*,小于等于比较,使用两个*数字规则*进行比较,数字规则1 <= 数字规则2,则为true(通过)。 +$gt:*比较规则*,大于比较,使用两个*数字规则*进行比较,数字规则1 > 数字规则2,则为true(通过)。 +$ge:*比较规则*,大于等于比较,使用两个*数字规则*进行比较,数字规则1 >= 数字规则2,则为true(通过)。 + +4:demo中的规则描述(请勿复制描述的规则,因为引擎不识别注释): +规则库格式是yaml,yaml语法可以自学一下。 +规则建议还是别手写,用gui配置减少错误率,而且gui用起来多爽。 + +特征库讲解: +这个规则结果是:ab至少出现一次,并且c至少出现一次,并且d至少出现一次,并且a至少出现3次($ge是大于等于), +所以aaabcd能被该规则匹配到: + +version: 1.2.1 //当前规则引擎版本号 +items: //规则集合 + - name: test //单条规则名称 + author: mountcloud //作者 + describe: test new rule engine. //规则描述 + features: //特征集合 + - key: key1 //特征的key + text: a //text代表文本类型,hex代表二进制类型 + - key: key2 + text: b + - key: key3 + text: c + - key: key4 + text: d + - key: key5 + hex: 0F ?0 A? ?? //二进制类型允许使用模糊匹配 + rules: //规则 + - $and: //(规则是第1层)规则是And类型 + - $all: //($and的子规则)All是以下key至少全部都出现一次 + - key1 + - key2 + - $and: //(第一层$and的子规则) + - $all: //(第二层$and的子规则) + - key3 + - $all: + - key4 + - $ge: //(第一层$and的子规则)$ge是大于比较,比较第一个规则要大于第二个规则 + - $count: ($ge的子规则)$count是统计key出现的总数 + - key1 + - $int: 3 ($ge的子规则)$int代表一个固定数字,这里就是 key1出现的次数要大于3 diff --git a/temp/demo/demo.fdog b/temp/demo/demo.fdog new file mode 100644 index 0000000..1c918dd --- /dev/null +++ b/temp/demo/demo.fdog @@ -0,0 +1,28 @@ +version: 1.2.1 +items: + - name: test + author: mountcloud + describe: test new rule engine. + features: + - key: key1 + text: a + - key: key2 + text: b + - key: key3 + text: c + - key: key4 + text: d + rules: + - $and: + - $all: + - key1 + - key2 + - $and: + - $all: + - key3 + - $all: + - key4 + - $ge: + - $count: + - key1 + - $int: 3 diff --git a/temp/demo/using-gui-open-demo.txt b/temp/demo/using-gui-open-demo.txt new file mode 100644 index 0000000..664e910 --- /dev/null +++ b/temp/demo/using-gui-open-demo.txt @@ -0,0 +1,57 @@ +1:请使用gui打开/demo/demo.fdog来参考 +!!!修改完规则记得点击 添加或修改。修改完规则库记得点击顶部菜单->操作->保存或者另存为!!! + +2:特征讲解 +key:代表这个特征的标识。 +hex:hex是以二进制形式进行匹配的二进制数据,允许模糊匹配如:0F ?0 A? ??。 +text:以文本形式匹配的关键字。 + +3:规则 +$and:[规则数组] 并运算,只有子规则结果全部为true(通过)时,结果为true(通过),如果有一条子规则结果为false(不通过),则结果为false(不通过)。 +$or:[规则数组] 或运算,有一条子规则结果全部为true(通过)时,结果为true(通过),如果全部子规则结果为false(不通过),则结果为false(不通过)。 +$not:[规则数组] 非运算,只有子规则结果全部为false(不通过)时,结果为true(通过),如果有一条子规则结果为true(通过),则结果为false(不通过)。意思就是如果$not的子规则是$lt($count(key1),$int(5))=$not(key1出现次数小于5)=不能(key1出现的次数小于5)=key1出现的次数需要大于5 + +$all:[字符串数组] 表示一组key必须至少全部出现1次。$all(key1,key2)表示key1和key2必须至少都出现一次。 +$count:*数字规则*[字符串数组] 表示统计key出现的次数。$count(key1,key2)就是统计key1和key2出现的次数。 +$int:*数字规则*[数字类型] 表示一个数字。$int(1)代表数字1 + +$lt:*比较规则*,小于比较,使用两个*数字规则*进行比较,数字规则1 < 数字规则2,则为true(通过)。 +$le:*比较规则*,小于等于比较,使用两个*数字规则*进行比较,数字规则1 <= 数字规则2,则为true(通过)。 +$gt:*比较规则*,大于比较,使用两个*数字规则*进行比较,数字规则1 > 数字规则2,则为true(通过)。 +$ge:*比较规则*,大于等于比较,使用两个*数字规则*进行比较,数字规则1 >= 数字规则2,则为true(通过)。 + +4:demo中的规则描述(请勿复制描述的规则,因为引擎不识别注释): +规则库格式是yaml,yaml语法可以自学一下。 +规则建议还是别手写,用gui配置减少错误率,而且gui用起来多爽。 + +特征库讲解: +version: 1.2.1 //当前规则引擎版本号 +items: //规则集合 + - name: test //单条规则名称 + author: mountcloud //作者 + describe: test new rule engine. //规则描述 + features: //特征集合 + - key: key1 //特征的key + text: a //text代表文本类型,hex代表二进制类型 + - key: key2 + text: b + - key: key3 + text: c + - key: key4 + text: d + - key: key5 + hex: 0F ?0 A? ?? //二进制类型允许使用模糊匹配 + rules: //规则 + - $and: //(规则是第1层)规则是And类型 + - $all: //($and的子规则)All是以下key至少全部都出现一次 + - key1 + - key2 + - $and: //(第一层$and的子规则) + - $all: //(第二层$and的子规则) + - key3 + - $all: + - key4 + - $ge: //(第一层$and的子规则)$ge是大于比较,比较第一个规则要大于第二个规则 + - $count: ($ge的子规则)$count是统计key出现的总数 + - key1 + - $int: 3 ($ge的子规则)$int代表一个固定数字,这里就是 key1出现的次数要大于3