Skip to content

Commit

Permalink
AnnotationMatcher support usingString matcher
Browse files Browse the repository at this point in the history
  • Loading branch information
teble committed Oct 16, 2023
1 parent 57eb58c commit 3b7806a
Show file tree
Hide file tree
Showing 6 changed files with 267 additions and 6 deletions.
97 changes: 97 additions & 0 deletions Core/dexkit/dex_item_matcher.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,30 @@ class Hungarian {

};

// NOLINTNEXTLINE
static std::vector<uint32_t> GetAnnotationUsingStrings(const ir::Annotation *annotation) {
if (annotation == nullptr) return {};
std::vector<uint32_t> result;
for (auto element: annotation->elements) {
if (element->value->type == dex::kEncodedString) {
result.push_back(element->value->u.string_value->orig_index);
} else if (element->value->type == dex::kEncodedAnnotation) {
auto sub_result = GetAnnotationUsingStrings(element->value->u.annotation_value);
result.insert(result.end(), sub_result.begin(), sub_result.end());
} else if (element->value->type == dex::kEncodedArray) {
for (auto sub_value: element->value->u.array_value->values) {
if (sub_value->type == dex::kEncodedString) {
result.push_back(sub_value->u.string_value->orig_index);
} else if (sub_value->type == dex::kEncodedAnnotation) {
auto sub_result = GetAnnotationUsingStrings(sub_value->u.annotation_value);
result.insert(result.end(), sub_result.begin(), sub_result.end());
}
}
}
}
return result;
}

void ConvertSimilarRegex(std::string_view &str, schema::StringMatchType &type) {
if (type == schema::StringMatchType::SimilarRegex) {
type = schema::StringMatchType::Contains;
Expand Down Expand Up @@ -231,6 +255,79 @@ bool DexItem::IsAnnotationMatched(const ir::Annotation *annotation, const schema
if (!IsAnnotationElementsMatched(annotation->elements, matcher->elements())) {
return false;
}
if (!IsAnnotationUsingStringsMatched(annotation, matcher)) {
return false;
}
return true;
}

bool DexItem::IsAnnotationUsingStringsMatched(const ir::Annotation *annotation, const schema::AnnotationMatcher *matcher) {
if (matcher->using_strings() == nullptr) {
return true;
}

typedef acdat::AhoCorasickDoubleArrayTrie<std::string_view> AcTrie;
typedef phmap::flat_hash_map<std::string_view, schema::StringMatchType> MatchTypeMap;
typedef std::set<std::string_view> StringSet;
std::shared_ptr<AcTrie> acTrie;
std::shared_ptr<MatchTypeMap> match_type_map;
std::shared_ptr<StringSet> real_keywords;

typedef std::tuple<std::shared_ptr<AcTrie>, std::shared_ptr<MatchTypeMap>, std::shared_ptr<StringSet>> KeywordsTuple;
std::vector<std::pair<std::string_view, bool>> keywords;
auto ptr = ThreadVariable::GetThreadVariable<KeywordsTuple>(POINT_CASE(matcher->using_strings()));
if (ptr == nullptr) {
auto trie = std::make_shared<AcTrie>();
auto map = std::make_shared<MatchTypeMap>();
auto result = BuildBatchFindKeywordsMap(matcher->using_strings(), keywords, *map);
auto string_set = std::make_shared<StringSet>(result);
acdat::Builder<std::string_view>().Build(keywords, trie.get());
auto tuple = std::make_tuple(trie, map, string_set);
ptr = ThreadVariable::SetThreadVariable<KeywordsTuple>(POINT_CASE(matcher->using_strings()), tuple);
}

acTrie = std::get<0>(*ptr);
match_type_map = std::get<1>(*ptr);
real_keywords = std::get<2>(*ptr);

auto using_empty_string_count = 0;
std::set<std::string_view> search_set;
auto using_strings = GetAnnotationUsingStrings(annotation);
for (auto idx: using_strings) {
if (idx == this->empty_string_id) ++using_empty_string_count;
auto str = this->strings[idx];
auto hits = acTrie->ParseText(str);
for (auto &hit: hits) {
auto match_type = match_type_map->find(hit.value)->second;
bool match;
switch (match_type) {
case schema::StringMatchType::Contains: match = true; break;
case schema::StringMatchType::StartWith: match = (hit.begin == 0); break;
case schema::StringMatchType::EndWith: match = (hit.end == str.size()); break;
case schema::StringMatchType::Equal: match = (hit.begin == 0 && hit.end == str.size()); break;
case schema::StringMatchType::SimilarRegex: abort();
}
if (match) {
search_set.insert(hit.value);
}
}
}
if (match_type_map->contains("")) {
DEXKIT_CHECK((*match_type_map)[""] == schema::StringMatchType::Equal);
if (using_empty_string_count) {
search_set.insert("");
}
}
if (search_set.size() < real_keywords->size()) {
return false;
}
std::vector<std::string_view> unique_vec;
std::set_intersection(search_set.begin(), search_set.end(),
real_keywords->begin(), real_keywords->end(),
std::inserter(unique_vec, unique_vec.begin()));
if (unique_vec.size() != real_keywords->size()) {
return false;
}
return true;
}

Expand Down
1 change: 1 addition & 0 deletions Core/dexkit/include/dex_item.h
Original file line number Diff line number Diff line change
Expand Up @@ -203,6 +203,7 @@ class DexItem {
);

bool IsAnnotationMatched(const ir::Annotation *annotation, const schema::AnnotationMatcher *matcher);
bool IsAnnotationUsingStringsMatched(const ir::Annotation *annotation, const schema::AnnotationMatcher *matcher);
bool IsAnnotationsMatched(const ir::AnnotationSet *annotationSet, const schema::AnnotationsMatcher *matcher);
bool IsAnnotationEncodeValueMatched(const ir::EncodedValue *encodedValue, schema::AnnotationEncodeValueMatcher type, const void *value);
bool IsAnnotationEncodeArrayMatcher(const std::vector<ir::EncodedValue *> &encodedValues, const dexkit::schema::AnnotationEncodeArrayMatcher *matcher);
Expand Down
33 changes: 31 additions & 2 deletions Core/dexkit/include/schema/matchers_generated.h
Original file line number Diff line number Diff line change
Expand Up @@ -857,7 +857,8 @@ struct AnnotationMatcher FLATBUFFERS_FINAL_CLASS : private ::flatbuffers::Table
VT_TYPE = 4,
VT_TARGET_ELEMENT_TYPES = 6,
VT_POLICY = 8,
VT_ELEMENTS = 10
VT_ELEMENTS = 10,
VT_USING_STRINGS = 12
};
const dexkit::schema::ClassMatcher *type() const {
return GetPointer<const dexkit::schema::ClassMatcher *>(VT_TYPE);
Expand All @@ -871,6 +872,9 @@ struct AnnotationMatcher FLATBUFFERS_FINAL_CLASS : private ::flatbuffers::Table
const dexkit::schema::AnnotationElementsMatcher *elements() const {
return GetPointer<const dexkit::schema::AnnotationElementsMatcher *>(VT_ELEMENTS);
}
const ::flatbuffers::Vector<::flatbuffers::Offset<dexkit::schema::StringMatcher>> *using_strings() const {
return GetPointer<const ::flatbuffers::Vector<::flatbuffers::Offset<dexkit::schema::StringMatcher>> *>(VT_USING_STRINGS);
}
bool Verify(::flatbuffers::Verifier &verifier) const {
return VerifyTableStart(verifier) &&
VerifyOffset(verifier, VT_TYPE) &&
Expand All @@ -880,6 +884,9 @@ struct AnnotationMatcher FLATBUFFERS_FINAL_CLASS : private ::flatbuffers::Table
VerifyField<int8_t>(verifier, VT_POLICY, 1) &&
VerifyOffset(verifier, VT_ELEMENTS) &&
verifier.VerifyTable(elements()) &&
VerifyOffset(verifier, VT_USING_STRINGS) &&
verifier.VerifyVector(using_strings()) &&
verifier.VerifyVectorOfTables(using_strings()) &&
verifier.EndTable();
}
};
Expand All @@ -900,6 +907,9 @@ struct AnnotationMatcherBuilder {
void add_elements(::flatbuffers::Offset<dexkit::schema::AnnotationElementsMatcher> elements) {
fbb_.AddOffset(AnnotationMatcher::VT_ELEMENTS, elements);
}
void add_using_strings(::flatbuffers::Offset<::flatbuffers::Vector<::flatbuffers::Offset<dexkit::schema::StringMatcher>>> using_strings) {
fbb_.AddOffset(AnnotationMatcher::VT_USING_STRINGS, using_strings);
}
explicit AnnotationMatcherBuilder(::flatbuffers::FlatBufferBuilder &_fbb)
: fbb_(_fbb) {
start_ = fbb_.StartTable();
Expand All @@ -916,8 +926,10 @@ inline ::flatbuffers::Offset<AnnotationMatcher> CreateAnnotationMatcher(
::flatbuffers::Offset<dexkit::schema::ClassMatcher> type = 0,
::flatbuffers::Offset<dexkit::schema::TargetElementTypesMatcher> target_element_types = 0,
dexkit::schema::RetentionPolicyType policy = dexkit::schema::RetentionPolicyType::Any,
::flatbuffers::Offset<dexkit::schema::AnnotationElementsMatcher> elements = 0) {
::flatbuffers::Offset<dexkit::schema::AnnotationElementsMatcher> elements = 0,
::flatbuffers::Offset<::flatbuffers::Vector<::flatbuffers::Offset<dexkit::schema::StringMatcher>>> using_strings = 0) {
AnnotationMatcherBuilder builder_(_fbb);
builder_.add_using_strings(using_strings);
builder_.add_elements(elements);
builder_.add_target_element_types(target_element_types);
builder_.add_type(type);
Expand All @@ -930,6 +942,23 @@ struct AnnotationMatcher::Traits {
static auto constexpr Create = CreateAnnotationMatcher;
};

inline ::flatbuffers::Offset<AnnotationMatcher> CreateAnnotationMatcherDirect(
::flatbuffers::FlatBufferBuilder &_fbb,
::flatbuffers::Offset<dexkit::schema::ClassMatcher> type = 0,
::flatbuffers::Offset<dexkit::schema::TargetElementTypesMatcher> target_element_types = 0,
dexkit::schema::RetentionPolicyType policy = dexkit::schema::RetentionPolicyType::Any,
::flatbuffers::Offset<dexkit::schema::AnnotationElementsMatcher> elements = 0,
const std::vector<::flatbuffers::Offset<dexkit::schema::StringMatcher>> *using_strings = nullptr) {
auto using_strings__ = using_strings ? _fbb.CreateVector<::flatbuffers::Offset<dexkit::schema::StringMatcher>>(*using_strings) : 0;
return dexkit::schema::CreateAnnotationMatcher(
_fbb,
type,
target_element_types,
policy,
elements,
using_strings__);
}

struct AnnotationsMatcher FLATBUFFERS_FINAL_CLASS : private ::flatbuffers::Table {
typedef AnnotationsMatcherBuilder Builder;
struct Traits;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,20 +25,24 @@ package org.luckypray.dexkit.query.matchers

import com.google.flatbuffers.FlatBufferBuilder
import org.luckypray.dexkit.InnerAnnotationMatcher
import org.luckypray.dexkit.query.StringMatcherList
import org.luckypray.dexkit.query.base.BaseQuery
import org.luckypray.dexkit.query.base.IAnnotationEncodeValue
import org.luckypray.dexkit.query.enums.MatchType
import org.luckypray.dexkit.query.enums.RetentionPolicyType
import org.luckypray.dexkit.query.enums.StringMatchType
import org.luckypray.dexkit.query.enums.TargetElementType
import org.luckypray.dexkit.query.matchers.base.IntRange
import org.luckypray.dexkit.query.matchers.base.StringMatcher
import org.luckypray.dexkit.query.matchers.base.TargetElementTypesMatcher

class AnnotationMatcher : BaseQuery(), IAnnotationEncodeValue {
var typeMatcher: ClassMatcher? = null
private set
var targetElementTypesMatcher: TargetElementTypesMatcher? = null
private set
var usingStringsMatcher: MutableList<StringMatcher>? = null
private set

/**
* Annotation RetentionPolicy.
Expand Down Expand Up @@ -66,6 +70,22 @@ class AnnotationMatcher : BaseQuery(), IAnnotationEncodeValue {
type(value)
}

/**
* Using string list. Default match type is contains, if you need to match exactly,
* please use [usingStrings] or [addUsingString] overloaded function for each string.
* ----------------
* 使用字符串列表。默认匹配关系为包含,如需为每个字符串设置匹配关系,
* 请使用 [usingStrings] 或者 [addUsingString] 重载函数。
*/
var usingStrings: Collection<String>
@JvmSynthetic
@Deprecated("Property can only be written.", level = DeprecationLevel.ERROR)
get() = throw NotImplementedError()
@JvmSynthetic
set(value) {
usingStrings(value)
}

/**
* Annotation type class matcher.
* ----------------
Expand Down Expand Up @@ -286,6 +306,94 @@ class AnnotationMatcher : BaseQuery(), IAnnotationEncodeValue {
(elementsMatcher as AnnotationElementsMatcher).count(min, max)
}

/**
* Using strings matcher.
* ----------------
* 使用字符串列表匹配器。
*
* usingStrings(StringMatcherList().add(StringMatcher("string")))
*
* @param usingStrings using string list matcher / 使用字符串列表匹配器
* @return [AnnotationMatcher]
*/
fun usingStrings(usingStrings: StringMatcherList) = also {
this.usingStringsMatcher = usingStrings
}

/**
* Using strings matcher.
* ----------------
* 使用字符串匹配器。
*
* usingStrings(List.of("TAG", "Activity"), StringMatchType.Equals, false)
*
* @param usingStrings using string list / 使用字符串列表
* @param matchType string match type / 字符串匹配类型
* @param ignoreCase ignore case / 忽略大小写
* @return [AnnotationMatcher]
*/
@JvmOverloads
fun usingStrings(
usingStrings: Collection<String>,
matchType: StringMatchType = StringMatchType.Contains,
ignoreCase: Boolean = false
) = also {
this.usingStringsMatcher = usingStrings.map { StringMatcher(it, matchType, ignoreCase) }.toMutableList()
}

/**
* Using strings matcher. default match type is contains, if you need to match exactly,
* please use [usingStrings] or [addUsingString] overloaded function for each string.
* ----------------
* 使用字符串匹配器。默认匹配关系为包含,如需为每个字符串设置匹配关系,
* 请使用 [usingStrings] 或者 [addUsingString] 重载函数。
*
* usingStrings("TAG", "Activity")
*
* @param usingStrings using string list / 使用字符串列表
* @return [AnnotationMatcher]
*/
fun usingStrings(vararg usingStrings: String) = also {
this.usingStringsMatcher = usingStrings.map { StringMatcher(it) }.toMutableList()
}

/**
* Add using string matcher.
* ----------------
* 添加使用字符串的匹配器。
*
* addUsingString(StringMatcher("string", StringMatchType.Equals, false))
*
* @param usingString using string matcher / 使用字符串匹配器
* @return [AnnotationMatcher]
*/
fun addUsingString(usingString: StringMatcher) = also {
usingStringsMatcher = usingStringsMatcher ?: mutableListOf()
usingStringsMatcher!!.add(usingString)
}

/**
* Add using string.
* ----------------
* 添加使用字符串。
*
* addUsingString("string", StringMatchType.Equals, false)
*
* @param usingString using string / 使用字符串
* @param matchType string match type / 字符串匹配类型
* @param ignoreCase ignore case / 忽略大小写
* @return [AnnotationMatcher]
*/
@JvmOverloads
fun addUsingString(
usingString: String,
matchType: StringMatchType = StringMatchType.Contains,
ignoreCase: Boolean = false
) = also {
usingStringsMatcher = usingStringsMatcher ?: mutableListOf()
usingStringsMatcher!!.add(StringMatcher(usingString, matchType, ignoreCase))
}

// region DSL

/**
Expand Down Expand Up @@ -333,7 +441,9 @@ class AnnotationMatcher : BaseQuery(), IAnnotationEncodeValue {
typeMatcher?.build(fbb) ?: 0,
targetElementTypesMatcher?.build(fbb) ?: 0,
policy?.value ?: 0,
elementsMatcher?.build(fbb) ?: 0
elementsMatcher?.build(fbb) ?: 0,
usingStringsMatcher?.map { it.build(fbb) }?.toIntArray()
?.let { fbb.createVectorOfTables(it) } ?: 0
)
fbb.finish(root)
return root
Expand Down
Loading

0 comments on commit 3b7806a

Please sign in to comment.