1 /**
2 * Copyright (c) 2024-2025 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16 #include "guard_name_cache.h"
17
18 #include <regex>
19
20 #include "utils/logger.h"
21 #include "utils/json_builder.h"
22
23 #include "util/assert_util.h"
24 #include "util/file_util.h"
25 #include "util/json_util.h"
26 #include "version.h"
27
28 namespace {
29 constexpr std::string_view TAG = "[Guard_NameCache]";
30 constexpr std::string_view IDENTIFIER_CACHE = "IdentifierCache";
31 constexpr std::string_view MEMBER_METHOD_CACHE = "MemberMethodCache";
32 constexpr std::string_view OBF_NAME = "obfName";
33 constexpr std::string_view ORI_SOURCE_FILE = "OriSourceFile";
34 constexpr std::string_view OBF_SOURCE_FILE = "ObfSourceFile";
35 constexpr std::string_view ENTRY_PACKAGE_INFO = "entryPackageInfo";
36 constexpr std::string_view COMPILE_SDK_VERSION = "compileSdkVersion";
37 constexpr std::string_view PROPERTY_CACHE = "PropertyCache";
38 constexpr std::string_view FILE_NAME_CACHE = "FileNameCache";
39
40 /**
41 * Remove the scope identifier # and its line number information from the mapping key value for incremental nameCache
42 * scenario
43 * @param originMap The format of the map key value is a string with scope identifier # and line number information
44 * @return The format of the map key value is a string without scope identifier # and line number information
45 * e.g. DeleteScopeAndLineNum({"#func:1:10", "a"}) => {"func", "a"}
46 */
DeleteScopeAndLineNum(const std::map<std::string,std::string> & originMap)47 std::map<std::string, std::string> DeleteScopeAndLineNum(const std::map<std::string, std::string> &originMap)
48 {
49 std::map<std::string, std::string> res;
50 for (const auto &item : originMap) {
51 auto key = item.first;
52 size_t pos = key.rfind('#');
53 if (pos != std::string::npos) {
54 key = key.substr(pos + 1);
55 }
56 pos = key.find(':');
57 if (pos != std::string::npos) {
58 key = key.substr(0, pos);
59 }
60 res.emplace(key, item.second);
61 }
62 return res;
63 }
64
HasLineNumberInfo(const std::map<std::string,std::string> & table,const std::string & str)65 bool HasLineNumberInfo(const std::map<std::string, std::string> &table, const std::string &str)
66 {
67 std::string match = str + ":\\d+:\\d+";
68 std::regex pattern(match);
69 return std::any_of(table.begin(), table.end(),
70 [&](const auto &item) { return std::regex_search(item.first, pattern); });
71 }
72
MapToJson(const std::map<std::string,std::string> & mapInfo,bool deduplicate=false)73 std::function<void(panda::JsonObjectBuilder &)> MapToJson(const std::map<std::string, std::string> &mapInfo,
74 bool deduplicate = false)
75 {
76 return [mapInfo, deduplicate](panda::JsonObjectBuilder &builder) {
77 for (const auto &item : mapInfo) {
78 if (deduplicate && item.first == item.second) {
79 continue;
80 }
81 builder.AddProperty(item.first, item.second);
82 }
83 };
84 }
85
FileNameCacheToJson(const panda::guard::FileNameCacheInfo & cacheInfo)86 std::function<void(panda::JsonObjectBuilder &)> FileNameCacheToJson(const panda::guard::FileNameCacheInfo &cacheInfo)
87 {
88 return [cacheInfo](panda::JsonObjectBuilder &builder) {
89 builder.AddProperty(IDENTIFIER_CACHE, MapToJson(cacheInfo.identifierCacheMap));
90 builder.AddProperty(MEMBER_METHOD_CACHE, MapToJson(cacheInfo.memberMethodCacheMap));
91 builder.AddProperty(OBF_NAME, cacheInfo.obfName);
92 if (!cacheInfo.oriSourceFile.empty()) {
93 builder.AddProperty(ORI_SOURCE_FILE, cacheInfo.oriSourceFile);
94 builder.AddProperty(OBF_SOURCE_FILE, cacheInfo.obfSourceFile);
95 }
96 };
97 }
98 } // namespace
99
Load(const std::string & applyNameCachePath)100 void panda::guard::NameCache::Load(const std::string &applyNameCachePath)
101 {
102 if (applyNameCachePath.empty()) {
103 return;
104 }
105 std::string content = FileUtil::GetFileContent(applyNameCachePath);
106 if (content.empty()) {
107 LOG(WARNING, PANDAGUARD) << TAG << "get apply name cache file content failed";
108 return;
109 }
110 LOG(INFO, PANDAGUARD) << TAG << "load apply name cache:" << applyNameCachePath;
111 LOG(INFO, PANDAGUARD) << TAG << "apply name cache content:" << content;
112 ParseAppliedNameCache(content);
113 ParseHistoryNameCacheToUsedNames();
114 }
115
GetHistoryUsedNames() const116 const std::set<std::string> &panda::guard::NameCache::GetHistoryUsedNames() const
117 {
118 return historyUsedNames_;
119 }
120
GetHistoryFileNameMapping() const121 std::map<std::string, std::string> panda::guard::NameCache::GetHistoryFileNameMapping() const
122 {
123 return historyNameCache_.fileNameCacheMap;
124 }
125
GetHistoryNameMapping() const126 std::map<std::string, std::string> panda::guard::NameCache::GetHistoryNameMapping() const
127 {
128 std::map<std::string, std::string> res = historyNameCache_.propertyCacheMap;
129 for (const auto &item : historyNameCache_.fileCacheInfoMap) {
130 res.insert(item.second.identifierCacheMap.begin(), item.second.identifierCacheMap.end());
131 res.insert(item.second.memberMethodCacheMap.begin(), item.second.memberMethodCacheMap.end());
132 }
133 return res;
134 }
135
AddObfIdentifierName(const std::string & filePath,const std::string & origin,const std::string & obfName)136 void panda::guard::NameCache::AddObfIdentifierName(const std::string &filePath, const std::string &origin,
137 const std::string &obfName)
138 {
139 if (filePath.empty() || origin.empty() || obfName.empty()) {
140 LOG(WARNING, PANDAGUARD) << TAG << "[" << __FUNCTION__ << "], filePath or origin or obfName is empty";
141 LOG(INFO, PANDAGUARD) << TAG << "filePath:" << filePath;
142 LOG(INFO, PANDAGUARD) << TAG << "origin:" << origin;
143 LOG(INFO, PANDAGUARD) << TAG << "obfName:" << obfName;
144 return;
145 }
146 auto fileItem = newNameCache_.fileCacheInfoMap.find(filePath);
147 if (fileItem != newNameCache_.fileCacheInfoMap.end()) {
148 if (!HasLineNumberInfo(fileItem->second.identifierCacheMap, origin)) {
149 fileItem->second.identifierCacheMap.emplace(origin, obfName);
150 }
151 return;
152 }
153 FileNameCacheInfo cacheInfo;
154 cacheInfo.identifierCacheMap.emplace(origin, obfName);
155 newNameCache_.fileCacheInfoMap.emplace(filePath, cacheInfo);
156 }
157
AddObfMemberMethodName(const std::string & filePath,const std::string & origin,const std::string & obfName)158 void panda::guard::NameCache::AddObfMemberMethodName(const std::string &filePath, const std::string &origin,
159 const std::string &obfName)
160 {
161 if (filePath.empty() || origin.empty() || obfName.empty()) {
162 LOG(WARNING, PANDAGUARD) << TAG << "[" << __FUNCTION__ << "], filePath or origin or obfName is empty";
163 LOG(INFO, PANDAGUARD) << TAG << "filePath:" << filePath;
164 LOG(INFO, PANDAGUARD) << TAG << "origin:" << origin;
165 LOG(INFO, PANDAGUARD) << TAG << "obfName:" << obfName;
166 return;
167 }
168 auto fileItem = newNameCache_.fileCacheInfoMap.find(filePath);
169 if (fileItem != newNameCache_.fileCacheInfoMap.end()) {
170 fileItem->second.memberMethodCacheMap.emplace(origin, obfName);
171 return;
172 }
173 FileNameCacheInfo cacheInfo;
174 cacheInfo.memberMethodCacheMap.emplace(origin, obfName);
175 newNameCache_.fileCacheInfoMap.emplace(filePath, cacheInfo);
176 }
177
AddObfName(const std::string & filePath,const std::string & obfName)178 void panda::guard::NameCache::AddObfName(const std::string &filePath, const std::string &obfName)
179 {
180 if (filePath.empty() || obfName.empty()) {
181 LOG(WARNING, PANDAGUARD) << TAG << "[" << __FUNCTION__ << "], filePath or obfName is empty";
182 LOG(INFO, PANDAGUARD) << TAG << "filePath:" << filePath;
183 LOG(INFO, PANDAGUARD) << TAG << "obfName:" << obfName;
184 return;
185 }
186 auto fileItem = newNameCache_.fileCacheInfoMap.find(filePath);
187 if (fileItem != newNameCache_.fileCacheInfoMap.end()) {
188 fileItem->second.obfName = obfName;
189 return;
190 }
191 FileNameCacheInfo cacheInfo;
192 cacheInfo.obfName = obfName;
193 newNameCache_.fileCacheInfoMap.emplace(filePath, cacheInfo);
194 }
195
AddSourceFile(const std::string & filePath,const std::string & oriSource,const std::string & obfSource)196 void panda::guard::NameCache::AddSourceFile(const std::string &filePath, const std::string &oriSource,
197 const std::string &obfSource)
198 {
199 if (filePath.empty() || oriSource.empty() || obfSource.empty()) {
200 LOG(WARNING, PANDAGUARD) << TAG << "[" << __FUNCTION__ << "], filePath or oriSource or obfSource is empty";
201 LOG(INFO, PANDAGUARD) << TAG << "filePath:" << filePath;
202 LOG(INFO, PANDAGUARD) << TAG << "oriSourceFile:" << oriSource;
203 LOG(INFO, PANDAGUARD) << TAG << "obfSourceFile:" << obfSource;
204 return;
205 }
206 auto fileItem = newNameCache_.fileCacheInfoMap.find(filePath);
207 if (fileItem != newNameCache_.fileCacheInfoMap.end()) {
208 fileItem->second.oriSourceFile = oriSource;
209 fileItem->second.obfSourceFile = obfSource;
210 return;
211 }
212 FileNameCacheInfo cacheInfo;
213 cacheInfo.oriSourceFile = oriSource;
214 cacheInfo.obfSourceFile = obfSource;
215 newNameCache_.fileCacheInfoMap.emplace(filePath, cacheInfo);
216 }
217
AddNewNameCacheObfFileName(const std::string & origin,const std::string & obfName)218 void panda::guard::NameCache::AddNewNameCacheObfFileName(const std::string &origin, const std::string &obfName)
219 {
220 if (origin.empty() || obfName.empty()) {
221 LOG(WARNING, PANDAGUARD) << TAG << "[" << __FUNCTION__ << "], origin or obfName is empty";
222 LOG(INFO, PANDAGUARD) << TAG << "origin:" << origin;
223 LOG(INFO, PANDAGUARD) << TAG << "obfName:" << obfName;
224 return;
225 }
226 newNameCache_.fileNameCacheMap.emplace(origin, obfName);
227 }
228
AddObfPropertyName(const std::string & origin,const std::string & obfName)229 void panda::guard::NameCache::AddObfPropertyName(const std::string &origin, const std::string &obfName)
230 {
231 if (origin.empty() || obfName.empty()) {
232 LOG(WARNING, PANDAGUARD) << TAG << "[" << __FUNCTION__ << "], origin or obfName is empty";
233 LOG(INFO, PANDAGUARD) << TAG << "origin:" << origin;
234 LOG(INFO, PANDAGUARD) << TAG << "obfName:" << obfName;
235 return;
236 }
237 newNameCache_.propertyCacheMap.emplace(origin, obfName);
238 }
239
MergeNameCache(ProjectNameCacheInfo & merge)240 void panda::guard::NameCache::MergeNameCache(ProjectNameCacheInfo &merge)
241 {
242 merge.fileCacheInfoMap = newNameCache_.fileCacheInfoMap;
243 merge.entryPackageInfo = options_->GetEntryPackageInfo();
244 merge.compileSdkVersion = options_->GetCompileSdkVersion();
245 merge.propertyCacheMap = historyNameCache_.propertyCacheMap;
246 merge.propertyCacheMap.insert(newNameCache_.propertyCacheMap.begin(), newNameCache_.propertyCacheMap.end());
247 merge.fileNameCacheMap = historyNameCache_.fileNameCacheMap;
248 merge.fileNameCacheMap.insert(newNameCache_.fileNameCacheMap.begin(), newNameCache_.fileNameCacheMap.end());
249 }
250
WriteCache()251 void panda::guard::NameCache::WriteCache()
252 {
253 std::string defaultPath = options_->GetDefaultNameCachePath();
254 std::string printPath = options_->GetPrintNameCache();
255 PANDA_GUARD_ASSERT_PRINT(defaultPath.empty() && printPath.empty(), TAG,
256 ErrorCode::NOT_CONFIGURED_DEFAULT_AND_PRINT_NAME_CACHE_PATH,
257 "the configuration file not configured field defaultNameCachePath and printNameCache");
258
259 ProjectNameCacheInfo merge;
260 MergeNameCache(merge);
261
262 std::string jsonStr = BuildJson(merge);
263 if (!defaultPath.empty()) {
264 FileUtil::WriteFile(defaultPath, jsonStr);
265 }
266
267 if (!printPath.empty()) {
268 FileUtil::WriteFile(printPath, jsonStr);
269 }
270 }
271
ParseAppliedNameCache(const std::string & content)272 void panda::guard::NameCache::ParseAppliedNameCache(const std::string &content)
273 {
274 JsonObject nameCacheObj(content);
275 PANDA_GUARD_ASSERT_PRINT(!nameCacheObj.IsValid(), TAG, ErrorCode::NAME_CACHE_FILE_FORMAT_ERROR,
276 "the name cache file is not a valid json");
277
278 for (size_t idx = 0; idx < nameCacheObj.GetSize(); idx++) {
279 auto key = nameCacheObj.GetKeyByIndex(idx);
280 if (key == ENTRY_PACKAGE_INFO) {
281 historyNameCache_.entryPackageInfo = JsonUtil::GetStringValue(&nameCacheObj, ENTRY_PACKAGE_INFO);
282 } else if (key == COMPILE_SDK_VERSION) {
283 historyNameCache_.compileSdkVersion = JsonUtil::GetStringValue(&nameCacheObj, COMPILE_SDK_VERSION);
284 } else if (key == PROPERTY_CACHE) {
285 historyNameCache_.propertyCacheMap = JsonUtil::GetMapStringValue(&nameCacheObj, PROPERTY_CACHE);
286 } else if (key == FILE_NAME_CACHE) {
287 historyNameCache_.fileNameCacheMap = JsonUtil::GetMapStringValue(&nameCacheObj, FILE_NAME_CACHE);
288 } else {
289 auto fileCacheInfoObj = JsonUtil::GetJsonObject(&nameCacheObj, key);
290 if (!fileCacheInfoObj) {
291 continue;
292 }
293 auto identifierCache = JsonUtil::GetMapStringValue(fileCacheInfoObj, IDENTIFIER_CACHE);
294 auto memberMethodCache = JsonUtil::GetMapStringValue(fileCacheInfoObj, MEMBER_METHOD_CACHE);
295
296 FileNameCacheInfo fileCacheInfo;
297 fileCacheInfo.identifierCacheMap = DeleteScopeAndLineNum(identifierCache);
298 fileCacheInfo.memberMethodCacheMap = DeleteScopeAndLineNum(memberMethodCache);
299 fileCacheInfo.obfName = JsonUtil::GetStringValue(fileCacheInfoObj, OBF_NAME);
300
301 historyNameCache_.fileCacheInfoMap.emplace(key, fileCacheInfo);
302 }
303 }
304 }
305
ParseHistoryNameCacheToUsedNames()306 void panda::guard::NameCache::ParseHistoryNameCacheToUsedNames()
307 {
308 AddHistoryUsedNames(historyNameCache_.entryPackageInfo);
309 AddHistoryUsedNames(historyNameCache_.compileSdkVersion);
310 AddHistoryUsedNames(historyNameCache_.propertyCacheMap);
311 AddHistoryUsedNames(historyNameCache_.fileNameCacheMap);
312 for (const auto &item : historyNameCache_.fileCacheInfoMap) {
313 AddHistoryUsedNames(item.second.identifierCacheMap);
314 AddHistoryUsedNames(item.second.memberMethodCacheMap);
315 AddHistoryUsedNames(item.second.obfName);
316 }
317 }
318
AddHistoryUsedNames(const std::string & value)319 void panda::guard::NameCache::AddHistoryUsedNames(const std::string &value)
320 {
321 historyUsedNames_.emplace(value);
322 }
323
AddHistoryUsedNames(const std::map<std::string,std::string> & values)324 void panda::guard::NameCache::AddHistoryUsedNames(const std::map<std::string, std::string> &values)
325 {
326 for (const auto &item : values) {
327 historyUsedNames_.emplace(item.second);
328 }
329 }
330
BuildJson(const ProjectNameCacheInfo & nameCacheInfo)331 std::string panda::guard::NameCache::BuildJson(const ProjectNameCacheInfo &nameCacheInfo)
332 {
333 JsonObjectBuilder builder;
334 for (const auto &item : nameCacheInfo.fileCacheInfoMap) {
335 builder.AddProperty(item.first, FileNameCacheToJson(item.second));
336 }
337 builder.AddProperty(ENTRY_PACKAGE_INFO, nameCacheInfo.entryPackageInfo);
338 builder.AddProperty(COMPILE_SDK_VERSION, nameCacheInfo.compileSdkVersion);
339 builder.AddProperty(ARK_GUARD_DYNAMIC_VERSION, OBFUSCATION_TOOL_VERSION);
340
341 if (options_->IsPropertyObfEnabled() || options_->IsExportObfEnabled()) {
342 builder.AddProperty(PROPERTY_CACHE, MapToJson(nameCacheInfo.propertyCacheMap, true));
343 }
344 if (options_->IsFileNameObfEnabled()) {
345 builder.AddProperty(FILE_NAME_CACHE, MapToJson(nameCacheInfo.fileNameCacheMap, true));
346 }
347 return std::move(builder).Build();
348 }
349