1 /**
2 * Copyright (c) 2024 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 "node.h"
17
18 #include "utils/logger.h"
19
20 #include "configs/guard_context.h"
21 #include "graph_analyzer.h"
22 #include "program.h"
23 #include "util/assert_util.h"
24 #include "util/string_util.h"
25
26 namespace {
27 using OpcodeList = std::vector<panda::pandasm::Opcode>;
28
29 constexpr std::string_view TAG = "[Node]";
30 constexpr std::string_view ENTRY_FUNC_NAME = ".func_main_0";
31 constexpr std::string_view NORMALIZED_OHM_DELIMITER = "&";
32 constexpr std::string_view PATH_DELIMITER = "/";
33 constexpr std::string_view PACKAGE_MODULES_PREFIX = "pkg_modules";
34 constexpr std::string_view PKG_NAME_PREFIX = "pkgName@";
35 constexpr std::string_view SCOPE_NAMES_FIELD = "scopeNames";
36 constexpr std::string_view MODULE_RECORD_IDX_FIELD = "moduleRecordIdx";
37 constexpr std::string_view JSON_FILE_FIELD = "jsonFileContent";
38 constexpr size_t MAX_EXPORT_ITEM_LEN = 10000;
39 constexpr size_t STOBJBYVALUE_ACC_INDEX = 2;
40
41 constexpr std::string_view OBJECT_PROPERTY_OBJECT = "Object";
42 constexpr std::string_view OBJECT_PROPERTY_PROTOTYPE = "prototype";
43 constexpr std::string_view OBJECT_PROPERTY_GETOWNPROPERTYDESCRIPTOR = "getOwnPropertyDescriptor";
44 constexpr std::string_view OBJECT_PROPERTY_DEFINEPROPERTY = "defineProperty";
45 constexpr size_t NAMESPACE_OWN_PARAM_REG_INDEX = 3; // a3(this)
46
47 const OpcodeList METHOD_NAME_DIRECT_LIST = {
48 panda::pandasm::Opcode::STOWNBYNAME,
49 panda::pandasm::Opcode::STOWNBYNAMEWITHNAMESET,
50 };
51
52 const OpcodeList METHOD_NAME_INDIRECT_LIST = {
53 panda::pandasm::Opcode::DEFINEGETTERSETTERBYVALUE,
54 panda::pandasm::Opcode::STOWNBYVALUE,
55 panda::pandasm::Opcode::STOWNBYVALUEWITHNAMESET,
56 };
57
InOpcodeList(const panda::guard::InstructionInfo & info,const OpcodeList & list)58 bool InOpcodeList(const panda::guard::InstructionInfo &info, const OpcodeList &list)
59 {
60 return std::any_of(list.begin(), list.end(), [&](const auto &elem) { return elem == info.ins_->opcode; });
61 }
62
UpdateScopeNamesLiteralArray(panda::pandasm::LiteralArray & literalArray)63 void UpdateScopeNamesLiteralArray(panda::pandasm::LiteralArray &literalArray)
64 {
65 for (auto &literal : literalArray.literals_) {
66 if (!literal.IsStringValue()) {
67 continue;
68 }
69
70 const auto &value = std::get<std::string>(literal.value_);
71 literal.value_ = panda::guard::GuardContext::GetInstance()->GetNameMapping()->GetName(value);
72 }
73 }
74
75 template <typename T>
UpdateEntityNamespaceMemberExport(const std::string & entityIdx,const std::unordered_map<std::string,std::shared_ptr<T>> & map)76 void UpdateEntityNamespaceMemberExport(const std::string &entityIdx,
77 const std::unordered_map<std::string, std::shared_ptr<T>> &map)
78 {
79 PANDA_GUARD_ASSERT_PRINT(map.find(entityIdx) == map.end(), TAG, panda::guard::ErrorCode::GENERIC_ERROR,
80 "invalid entityIdx:" << entityIdx);
81 const auto &entity = map.at(entityIdx);
82 LOG(INFO, PANDAGUARD) << TAG << "update namespace export for entity:" << entityIdx;
83 entity->SetExportAndRefreshNeedUpdate(true);
84 }
85
IsRemoteHar(const std::string & name)86 bool IsRemoteHar(const std::string &name)
87 {
88 return panda::guard::StringUtil::IsPrefixMatched(name, PACKAGE_MODULES_PREFIX.data());
89 }
90 } // namespace
91
IsJsonFile(const pandasm::Record & record)92 bool panda::guard::Node::IsJsonFile(const pandasm::Record &record)
93 {
94 return std::any_of(record.field_list.begin(), record.field_list.end(),
95 [](const auto &field) { return field.name == JSON_FILE_FIELD; });
96 }
97
FindPkgName(const pandasm::Record & record,std::string & pkgName)98 bool panda::guard::Node::FindPkgName(const pandasm::Record &record, std::string &pkgName)
99 {
100 return std::any_of(
101 record.field_list.begin(), record.field_list.end(), [&](const panda::pandasm::Field &field) -> bool {
102 const bool found = field.name.rfind(PKG_NAME_PREFIX, 0) == 0;
103 if (found) {
104 pkgName = field.name.substr(PKG_NAME_PREFIX.size(), field.name.size() - PKG_NAME_PREFIX.size());
105 }
106 return found;
107 });
108 }
109
InitWithRecord(const pandasm::Record & record)110 void panda::guard::Node::InitWithRecord(const pandasm::Record &record)
111 {
112 if (IsJsonFile(record)) {
113 this->type_ = NodeType::JSON_FILE;
114 LOG(INFO, PANDAGUARD) << TAG << "json file:" << this->name_;
115 return;
116 }
117
118 std::string pkgName;
119 if (FindPkgName(record, pkgName)) {
120 this->type_ = NodeType::SOURCE_FILE;
121 this->pkgName_ = pkgName;
122 LOG(INFO, PANDAGUARD) << TAG << "source file:" << this->name_;
123 return;
124 }
125
126 if (record.metadata->IsAnnotation()) {
127 this->type_ = NodeType::ANNOTATION;
128 LOG(INFO, PANDAGUARD) << TAG << "annotation:" << this->name_;
129 return;
130 }
131 }
132
Build()133 void panda::guard::Node::Build()
134 {
135 LOG(INFO, PANDAGUARD) << TAG << "node create for " << this->name_ << " start";
136
137 this->sourceName_ = GuardContext::GetInstance()->GetGuardOptions()->GetSourceName(this->name_);
138 this->obfSourceName_ = this->sourceName_;
139 LOG(INFO, PANDAGUARD) << TAG << "node sourceName_ " << this->sourceName_;
140 this->isNormalizedOhmUrl_ = GuardContext::GetInstance()->GetGuardOptions()->IsUseNormalizedOhmUrl();
141 CreateFilePath();
142 this->filepath_.obfName = this->filepath_.name;
143 LOG(INFO, PANDAGUARD) << TAG << "pre part: " << this->filepath_.prePart;
144 LOG(INFO, PANDAGUARD) << TAG << "file path: " << this->filepath_.name;
145 LOG(INFO, PANDAGUARD) << TAG << "post part: " << this->filepath_.postPart;
146
147 if (this->type_ == NodeType::SOURCE_FILE) {
148 moduleRecord_.Create();
149
150 auto entryFunc = std::make_shared<Function>(this->program_, this->name_ + ENTRY_FUNC_NAME.data(), false);
151 entryFunc->Init();
152 entryFunc->Create();
153
154 const auto &function = entryFunc->GetOriginFunction();
155 this->sourceFile_ = function.source_file;
156 this->obfSourceFile_ = function.source_file;
157
158 entryFunc->EnumerateIns([&](const InstructionInfo &info) -> void { EnumerateIns(info, TOP_LEVEL); });
159
160 this->functionTable_.emplace(entryFunc->idx_, entryFunc);
161 }
162
163 this->ExtractNames();
164
165 LOG(INFO, PANDAGUARD) << TAG << "node create for " << this->name_ << " end";
166 }
167
GetRecord() const168 panda::pandasm::Record &panda::guard::Node::GetRecord() const
169 {
170 return this->program_->prog_->record_table.at(this->obfName_);
171 }
172
EnumerateIns(const InstructionInfo & info,Scope scope)173 void panda::guard::Node::EnumerateIns(const InstructionInfo &info, Scope scope)
174 {
175 CreateFunction(info, scope);
176 CreateProperty(info);
177 CreateClass(info, scope);
178 CreateOuterMethod(info);
179 CreateObject(info, scope);
180 CreateObjectOuterProperty(info);
181 FindStLexVarName(info);
182 AddNameForExportObject(info);
183 if (GuardContext::GetInstance()->GetGuardOptions()->IsDecoratorObfEnabled()) {
184 CreateUiDecorator(info, scope);
185 CreateObjectDecoratorProperty(info);
186 }
187 UpdateExportForNamespaceMember(info);
188 CreateArray(info);
189 }
190
CreateFunction(const InstructionInfo & info,Scope scope)191 void panda::guard::Node::CreateFunction(const InstructionInfo &info, Scope scope)
192 {
193 if (info.notEqualToOpcode(pandasm::Opcode::DEFINEFUNC)) {
194 return;
195 }
196
197 const std::string idx = info.ins_->GetId(0);
198 if (this->functionTable_.find(idx) != this->functionTable_.end()) {
199 this->functionTable_.at(idx)->defineInsList_.push_back(info);
200 return;
201 }
202
203 auto function = std::make_shared<Function>(this->program_, idx);
204 function->node_ = this;
205 function->scope_ = scope;
206 function->defineInsList_.push_back(info);
207 function->component_ = info.function_->component_;
208 function->Init();
209
210 function->export_ = this->moduleRecord_.IsExportVar(function->name_);
211 function->Create();
212
213 function->EnumerateIns([&](const InstructionInfo &insInfo) -> void { EnumerateIns(insInfo, FUNCTION); });
214
215 this->functionTable_.emplace(function->idx_, function);
216 }
217
218 /**
219 * e.g. class A {
220 * constructor {
221 * this.v1 = 1;
222 *
223 * let obj = {};
224 * obj.v2 = 2;
225 * }
226 * }
227 * v1: property, bind function
228 * v2: variable property, bind object
229 */
CreateProperty(const InstructionInfo & info) const230 void panda::guard::Node::CreateProperty(const InstructionInfo &info) const
231 {
232 if (!Property::IsPropertyIns(info)) {
233 return;
234 }
235
236 InstructionInfo nameInfo;
237 Property::GetPropertyNameInfo(info, nameInfo);
238 // if function is enum function, try to find property in acc
239 if (!nameInfo.IsValid() && info.function_->type_ == FunctionType::ENUM_FUNCTION) {
240 // e.g. this[0] = 'property'
241 LOG(INFO, PANDAGUARD) << TAG << "try to find property in acc";
242 GraphAnalyzer::GetLdaStr(info, nameInfo, STOBJBYVALUE_ACC_INDEX);
243 }
244
245 if (!nameInfo.IsValid()) {
246 LOG(INFO, PANDAGUARD) << TAG << "invalid nameInfo:" << info.index_ << " " << info.ins_->ToString();
247 return;
248 }
249
250 const std::string name = StringUtil::UnicodeEscape(nameInfo.ins_->GetId(0));
251 bool innerReg = info.IsInnerReg();
252 if (!innerReg && (info.function_->propertyTable_.find(name) != info.function_->propertyTable_.end())) {
253 info.function_->propertyTable_.at(name)->defineInsList_.emplace_back(info);
254 return;
255 }
256
257 auto property = std::make_shared<Property>(this->program_, name);
258 property->defineInsList_.push_back(info);
259 property->nameInfo_ = nameInfo;
260 if (!innerReg) {
261 property->scope_ = this->scope_;
262 property->export_ = this->export_;
263 property->Create();
264
265 LOG(INFO, PANDAGUARD) << TAG << "find property:" << property->name_;
266
267 info.function_->propertyTable_.emplace(name, property);
268 } else {
269 property->scope_ = FUNCTION;
270 property->export_ = false;
271 property->Create();
272
273 LOG(INFO, PANDAGUARD) << TAG << "find variable property:" << property->name_;
274
275 info.function_->variableProperties_.push_back(property);
276 }
277 }
278
CreateObjectDecoratorProperty(const InstructionInfo & info)279 void panda::guard::Node::CreateObjectDecoratorProperty(const InstructionInfo &info)
280 {
281 if (info.notEqualToOpcode(pandasm::Opcode::CALLTHIS2) && info.notEqualToOpcode(pandasm::Opcode::CALLTHIS3)) {
282 return;
283 }
284
285 std::string callName = GraphAnalyzer::GetCallName(info);
286 if (callName != OBJECT_PROPERTY_GETOWNPROPERTYDESCRIPTOR && callName != OBJECT_PROPERTY_DEFINEPROPERTY) {
287 return;
288 }
289
290 InstructionInfo objectParam;
291 GraphAnalyzer::GetCallTryLdGlobalByNameParam(info, INDEX_0, objectParam);
292 if (!objectParam.IsValid() || objectParam.ins_->GetId(0) != OBJECT_PROPERTY_OBJECT) {
293 return;
294 }
295
296 InstructionInfo param1;
297 GraphAnalyzer::GetCallLdObjByNameParam(info, INDEX_1, param1);
298 if (!param1.IsValid() || param1.ins_->GetId(0) != OBJECT_PROPERTY_PROTOTYPE) {
299 return;
300 }
301
302 InstructionInfo param2;
303 GraphAnalyzer::GetCallLdaStrParam(info, INDEX_2, param2);
304 if (!param2.IsValid()) {
305 return;
306 }
307
308 const std::string name = StringUtil::UnicodeEscape(param2.ins_->GetId(0));
309 auto property = std::make_shared<Property>(this->program_, name);
310 property->scope_ = this->scope_;
311 property->export_ = this->export_;
312 property->defineInsList_.push_back(param2);
313 property->nameInfo_ = param2;
314 property->Create();
315 LOG(INFO, PANDAGUARD) << TAG << "find object decorator property:" << property->name_;
316 info.function_->objectDecoratorProperties_.push_back(property);
317 }
318
CreateClass(const InstructionInfo & info,Scope scope)319 void panda::guard::Node::CreateClass(const InstructionInfo &info, Scope scope)
320 {
321 if (info.notEqualToOpcode(pandasm::Opcode::DEFINECLASSWITHBUFFER) &&
322 info.notEqualToOpcode(pandasm::Opcode::CALLRUNTIME_DEFINESENDABLECLASS)) {
323 return;
324 }
325
326 const std::string idx = info.ins_->GetId(1);
327 if (this->classTable_.find(idx) != this->classTable_.end()) {
328 this->classTable_.at(idx)->defineInsList_.push_back(info);
329 return;
330 }
331
332 auto clazz = std::make_shared<Class>(this->program_, info.ins_->GetId(0));
333 clazz->node_ = this;
334 clazz->moduleRecord_ = &this->moduleRecord_;
335 clazz->literalArrayIdx_ = idx;
336 clazz->defineInsList_.push_back(info);
337 if (GuardContext::GetInstance()->GetGuardOptions()->IsDecoratorObfEnabled()) {
338 clazz->component_ = GraphAnalyzer::IsComponentClass(info);
339 }
340 clazz->scope_ = scope;
341 if (info.ins_->opcode == pandasm::Opcode::CALLRUNTIME_DEFINESENDABLECLASS) {
342 clazz->callRunTimeInst_ = true;
343 }
344 clazz->Create();
345
346 clazz->EnumerateMethodIns([&](const InstructionInfo &insInfo) -> void { EnumerateIns(insInfo, FUNCTION); });
347
348 this->classTable_.emplace(clazz->literalArrayIdx_, clazz);
349 }
350
CreateOuterMethod(const InstructionInfo & info)351 void panda::guard::Node::CreateOuterMethod(const InstructionInfo &info)
352 {
353 if (info.notEqualToOpcode(pandasm::Opcode::DEFINEMETHOD)) {
354 return;
355 }
356
357 InstructionInfo defineInsInfo;
358 InstructionInfo nameInsInfo;
359 GraphAnalyzer::HandleDefineMethod(info, defineInsInfo, nameInsInfo);
360 PANDA_GUARD_ASSERT_PRINT(!defineInsInfo.IsValid(), TAG, ErrorCode::GENERIC_ERROR, "defineInsInfo is invalid");
361 // nameInsInfo maybe empty, therefore, there not check nameInsInfo. Instead, it is checked in actual use
362
363 const std::string methodIdx = info.ins_->GetId(0);
364 std::string literalArrayIdx;
365 if (defineInsInfo.equalToOpcode(pandasm::Opcode::DEFINECLASSWITHBUFFER) ||
366 defineInsInfo.equalToOpcode(pandasm::Opcode::CALLRUNTIME_DEFINESENDABLECLASS)) {
367 literalArrayIdx = defineInsInfo.ins_->GetId(1);
368 } else { // createobjectwithbuffer
369 literalArrayIdx = defineInsInfo.ins_->GetId(0);
370 }
371
372 const auto outerMethod = std::make_shared<OuterMethod>(this->program_, methodIdx);
373 outerMethod->node_ = this;
374 outerMethod->defineInsList_.push_back(info);
375 GetMethodNameInfo(nameInsInfo, outerMethod->nameInfo_);
376 outerMethod->Init();
377
378 if (this->classTable_.find(literalArrayIdx) != this->classTable_.end()) {
379 const auto &clazz = this->classTable_.at(literalArrayIdx);
380 outerMethod->className_ = clazz->name_;
381 outerMethod->export_ = clazz->export_;
382 outerMethod->scope_ = clazz->scope_;
383 outerMethod->component_ = clazz->component_;
384
385 outerMethod->Create();
386
387 LOG(INFO, PANDAGUARD) << TAG << "found method:" << methodIdx << " for class:" << clazz->name_;
388 clazz->outerMethods_.push_back(outerMethod);
389 } else if (this->objectTable_.find(literalArrayIdx) != this->objectTable_.end()) {
390 const auto &obj = this->objectTable_.at(literalArrayIdx);
391 outerMethod->export_ = obj->export_;
392 outerMethod->scope_ = obj->scope_;
393
394 outerMethod->Create();
395
396 LOG(INFO, PANDAGUARD) << TAG << "found method:" << methodIdx << " for obj:" << obj->literalArrayIdx_;
397 obj->outerMethods_.push_back(outerMethod);
398 } else {
399 PANDA_GUARD_ABORT_PRINT(TAG, ErrorCode::GENERIC_ERROR, "unexpect outer method for:" << literalArrayIdx);
400 }
401
402 outerMethod->EnumerateIns([&](const InstructionInfo &insInfo) -> void { EnumerateIns(insInfo, FUNCTION); });
403 }
404
CreateObject(const InstructionInfo & info,Scope scope)405 void panda::guard::Node::CreateObject(const InstructionInfo &info, Scope scope)
406 {
407 if (info.notEqualToOpcode(pandasm::Opcode::CREATEOBJECTWITHBUFFER)) {
408 return;
409 }
410
411 const std::string idx = info.ins_->GetId(0);
412 if (this->objectTable_.find(idx) != this->objectTable_.end()) {
413 this->objectTable_.at(idx)->defineInsList_.push_back(info);
414 return;
415 }
416
417 auto object = std::make_shared<Object>(this->program_, idx, this->name_);
418 LOG(INFO, PANDAGUARD) << TAG << "found record object:" << object->literalArrayIdx_;
419 object->node_ = this;
420 object->defineInsList_.push_back(info);
421 object->scope_ = scope;
422 object->Create();
423
424 object->EnumerateMethods([&](Function &function) -> void {
425 function.EnumerateIns([&](const InstructionInfo &insInfo) -> void { EnumerateIns(insInfo, FUNCTION); });
426 });
427
428 this->objectTable_.emplace(object->literalArrayIdx_, object);
429 }
430
CreateObjectOuterProperty(const panda::guard::InstructionInfo & info)431 void panda::guard::Node::CreateObjectOuterProperty(const panda::guard::InstructionInfo &info)
432 {
433 if (info.notEqualToOpcode(pandasm::Opcode::DEFINEPROPERTYBYNAME)) {
434 return;
435 }
436
437 InstructionInfo defineIns;
438 GraphAnalyzer::HandleDefineProperty(info, defineIns);
439 if (!defineIns.IsValid()) {
440 return;
441 }
442
443 PANDA_GUARD_ASSERT_PRINT(defineIns.notEqualToOpcode(pandasm::Opcode::CREATEOBJECTWITHBUFFER), TAG,
444 ErrorCode::GENERIC_ERROR, "unexpect related define ins");
445
446 const std::string literalArrayIdx = defineIns.ins_->GetId(0);
447 PANDA_GUARD_ASSERT_PRINT(this->objectTable_.find(literalArrayIdx) == this->objectTable_.end(), TAG,
448 ErrorCode::GENERIC_ERROR, "no record object for literalArrayIdx:" << literalArrayIdx);
449
450 const auto &object = this->objectTable_.at(literalArrayIdx);
451 const auto property = std::make_shared<Property>(this->program_, info.ins_->GetId(0));
452 property->defineInsList_.push_back(info);
453 property->nameInfo_ = info;
454 property->export_ = object->export_;
455 property->scope_ = object->scope_;
456
457 property->Create();
458
459 object->outerProperties_.push_back(property);
460 LOG(INFO, PANDAGUARD) << TAG << "found object outer property:" << property->name_;
461 }
462
AddNameForExportObject(const InstructionInfo & info)463 void panda::guard::Node::AddNameForExportObject(const InstructionInfo &info)
464 {
465 if (info.notEqualToOpcode(pandasm::Opcode::STMODULEVAR) &&
466 info.notEqualToOpcode(pandasm::Opcode::WIDE_STMODULEVAR)) {
467 return;
468 }
469
470 InstructionInfo defineIns;
471 GraphAnalyzer::GetStModuleVarDefineIns(info, defineIns);
472 if (!defineIns.IsValid()) {
473 return;
474 }
475
476 if (defineIns.notEqualToOpcode(pandasm::Opcode::CREATEOBJECTWITHBUFFER)) {
477 return;
478 }
479
480 const int64_t index = std::get<int64_t>(info.ins_->GetImm(0));
481 PANDA_GUARD_ASSERT_PRINT(index < 0 || index > MAX_EXPORT_ITEM_LEN, TAG, ErrorCode::GENERIC_ERROR,
482 "unexpect export item index:" << index);
483 const auto &exportName = this->moduleRecord_.GetLocalExportName(index);
484
485 const auto &objectIdx = defineIns.ins_->GetId(0);
486 PANDA_GUARD_ASSERT_PRINT(this->objectTable_.find(objectIdx) == this->objectTable_.end(), TAG,
487 ErrorCode::GENERIC_ERROR, "invalid objectIdx:" << objectIdx);
488 const auto &object = this->objectTable_.at(objectIdx);
489
490 object->SetExportName(exportName);
491 object->SetExportAndRefreshNeedUpdate(true);
492
493 LOG(INFO, PANDAGUARD) << TAG << "add export name:" << exportName << " for objectIdx:" << objectIdx;
494 }
495
UpdateExportForNamespaceMember(const InstructionInfo & info) const496 void panda::guard::Node::UpdateExportForNamespaceMember(const InstructionInfo &info) const
497 {
498 if (info.notEqualToOpcode(pandasm::Opcode::STOBJBYNAME) ||
499 (info.function_->type_ != FunctionType::NAMESPACE_FUNCTION) ||
500 ((info.ins_->GetReg(0) - info.function_->regsNum_) != NAMESPACE_OWN_PARAM_REG_INDEX)) {
501 return;
502 }
503
504 const auto propertyName = info.ins_->GetId(0);
505 PANDA_GUARD_ASSERT_PRINT(info.function_->propertyTable_.find(propertyName) == info.function_->propertyTable_.end(),
506 TAG, ErrorCode::GENERIC_ERROR, "invalid propertyName:" << propertyName);
507 const auto &property = info.function_->propertyTable_.at(propertyName);
508 property->SetExportAndRefreshNeedUpdate(true);
509
510 InstructionInfo defineIns;
511 GraphAnalyzer::GetStObjByNameDefineIns(info, defineIns);
512
513 if (!defineIns.IsValid()) {
514 return;
515 }
516
517 if (defineIns.equalToOpcode(pandasm::Opcode::DEFINEFUNC)) {
518 UpdateEntityNamespaceMemberExport(defineIns.ins_->GetId(0), this->functionTable_);
519 }
520
521 if (defineIns.equalToOpcode(pandasm::Opcode::DEFINECLASSWITHBUFFER) ||
522 defineIns.equalToOpcode(pandasm::Opcode::CALLRUNTIME_DEFINESENDABLECLASS)) {
523 UpdateEntityNamespaceMemberExport(defineIns.ins_->GetId(1), this->classTable_);
524 }
525
526 if (defineIns.equalToOpcode(pandasm::Opcode::CREATEOBJECTWITHBUFFER)) {
527 UpdateEntityNamespaceMemberExport(defineIns.ins_->GetId(0), this->objectTable_);
528 }
529 }
530
CreateArray(const InstructionInfo & info)531 void panda::guard::Node::CreateArray(const InstructionInfo &info)
532 {
533 if (info.notEqualToOpcode(pandasm::Opcode::CREATEARRAYWITHBUFFER)) {
534 return;
535 }
536
537 LOG(INFO, PANDAGUARD) << TAG << "found array:" << info.ins_->GetId(0);
538 auto array = std::make_shared<Array>(this->program_);
539 array->node_ = this;
540 array->nameInfo_ = info;
541 array->Create();
542
543 this->arrays_.emplace_back(array);
544 }
545
FindStLexVarName(const InstructionInfo & info)546 void panda::guard::Node::FindStLexVarName(const InstructionInfo &info)
547 {
548 if (info.notEqualToOpcode(pandasm::Opcode::STLEXVAR)) {
549 return;
550 }
551
552 InstructionInfo outInfo;
553 GraphAnalyzer::GetLdaStr(info, outInfo);
554 if (!outInfo.IsValid()) {
555 return;
556 }
557
558 LOG(INFO, PANDAGUARD) << TAG << "found stlexvar name:" << outInfo.ins_->GetId(0);
559 GuardContext::GetInstance()->GetNameMapping()->AddNameMapping(outInfo.ins_->GetId(0));
560 }
561
CreateUiDecorator(const InstructionInfo & info,Scope scope)562 void panda::guard::Node::CreateUiDecorator(const InstructionInfo &info, Scope scope)
563 {
564 if (!UiDecorator::IsUiDecoratorIns(info, scope)) {
565 return;
566 }
567
568 auto decorator = std::make_shared<UiDecorator>(this->program_, this->objectTable_);
569 decorator->scope_ = scope;
570 decorator->export_ = false;
571 decorator->baseInst_ = info;
572 decorator->Create();
573 if (!decorator->IsValidUiDecoratorType()) {
574 return;
575 }
576 this->uiDecorator_.emplace_back(decorator);
577 }
578
CreateFilePath()579 void panda::guard::Node::CreateFilePath()
580 {
581 const auto &options = GuardContext::GetInstance()->GetGuardOptions();
582 if (!options->IsFileNameObfEnabled() || options->IsReservedRemoteHarPkgNames(this->pkgName_)) {
583 this->filepath_.name = this->name_;
584 return;
585 }
586
587 if (this->isNormalizedOhmUrl_) {
588 CreateFilePathForNormalizedMode();
589 } else {
590 CreateFilePathForDefaultMode();
591 }
592 }
593
CreateFilePathForDefaultMode()594 void panda::guard::Node::CreateFilePathForDefaultMode()
595 {
596 if (IsRemoteHar(this->name_)) {
597 if (this->type_ == NodeType::JSON_FILE) {
598 this->filepath_.name = this->name_;
599 return;
600 }
601 std::string prefix = pkgName_ + PATH_DELIMITER.data();
602 PANDA_GUARD_ASSERT_PRINT(!StringUtil::IsPrefixMatched(name_, prefix), TAG, ErrorCode::GENERIC_ERROR,
603 "invalid remote har prefix");
604 filepath_.prePart = std::move(prefix);
605
606 filepath_.name = name_.substr(filepath_.prePart.size(), name_.size() - filepath_.prePart.size());
607 return;
608 }
609
610 // format: bundleName/hapPkgName@pkgName/filepath
611 size_t startPos = name_.find_first_of(PATH_DELIMITER.data(), 0);
612 if (startPos == std::string::npos) {
613 filepath_.name = name_;
614 return;
615 }
616
617 std::string toFound = pkgName_ + PATH_DELIMITER.data();
618 size_t foundPos = name_.find(toFound, startPos);
619 if (foundPos == std::string::npos) {
620 filepath_.name = name_;
621 return;
622 }
623
624 foundPos += toFound.size();
625 filepath_.prePart = name_.substr(0, foundPos);
626 filepath_.name = name_.substr(foundPos, name_.size() - foundPos);
627 }
628
CreateFilePathForNormalizedMode()629 void panda::guard::Node::CreateFilePathForNormalizedMode()
630 {
631 // [<bundle name>?]&<package name>/<file path>&[<version>?]
632 const size_t startPos = name_.find_first_of(NORMALIZED_OHM_DELIMITER.data(), 0);
633 const std::string prefix = this->type_ == NodeType::SOURCE_FILE
634 ? NORMALIZED_OHM_DELIMITER.data() + pkgName_ + PATH_DELIMITER.data()
635 : NORMALIZED_OHM_DELIMITER.data();
636
637 PANDA_GUARD_ASSERT_PRINT(!StringUtil::IsPrefixMatched(name_, prefix, startPos), TAG, ErrorCode::GENERIC_ERROR,
638 "invalid normalizedOhmUrl prefix");
639 size_t prefixEnd = startPos + prefix.size();
640 filepath_.prePart = name_.substr(0, prefixEnd);
641
642 size_t filePathEnd = name_.find_first_of(NORMALIZED_OHM_DELIMITER.data(), prefixEnd);
643 PANDA_GUARD_ASSERT_PRINT(filePathEnd == std::string::npos, TAG, ErrorCode::GENERIC_ERROR,
644 "invalid normalizedOhmUrl format");
645 filepath_.name = name_.substr(prefixEnd, filePathEnd - prefixEnd);
646
647 filepath_.postPart = name_.substr(filePathEnd, name_.size() - filePathEnd);
648 }
649
ExtractNames()650 void panda::guard::Node::ExtractNames()
651 {
652 moduleRecord_.ExtractNames(this->strings_);
653
654 for (const auto &[_, function] : this->functionTable_) {
655 function->ExtractNames(this->strings_);
656 }
657
658 for (const auto &[_, clazz] : this->classTable_) {
659 clazz->ExtractNames(this->strings_);
660 }
661
662 for (const auto &[_, object] : this->objectTable_) {
663 object->ExtractNames(this->strings_);
664 }
665
666 if (GuardContext::GetInstance()->GetGuardOptions()->IsDecoratorObfEnabled()) {
667 for (const auto &decorator : this->uiDecorator_) {
668 decorator->ExtractNames(this->strings_);
669 }
670 }
671
672 auto parts = StringUtil::Split(filepath_.name, PATH_DELIMITER.data());
673 for (const auto &part : parts) {
674 this->strings_.emplace(part);
675 }
676
677 GuardContext::GetInstance()->GetNameMapping()->AddReservedNames(this->strings_);
678
679 LOG(INFO, PANDAGUARD) << TAG << "strings:";
680 for (const auto &str : this->strings_) {
681 LOG(INFO, PANDAGUARD) << TAG << str;
682 }
683 }
684
RefreshNeedUpdate()685 void panda::guard::Node::RefreshNeedUpdate()
686 {
687 const auto &options = GuardContext::GetInstance()->GetGuardOptions();
688 if (options->IsUseNormalizedOhmUrl()) {
689 this->fileNameNeedUpdate_ = options->IsFileNameObfEnabled() && !options->IsReservedRemoteHarPkgNames(pkgName_);
690 } else {
691 this->fileNameNeedUpdate_ = options->IsFileNameObfEnabled() && !IsRemoteHar(this->name_);
692 }
693
694 if (options->IsKeepPath(this->name_)) {
695 LOG(INFO, PANDAGUARD) << TAG << "found keep rule for:" << this->name_;
696 this->contentNeedUpdate_ = false;
697 GuardContext::GetInstance()->GetNameMapping()->AddNameMapping(this->strings_);
698 }
699
700 for (auto &[_, object] : this->objectTable_) {
701 object->SetContentNeedUpdate(this->contentNeedUpdate_);
702 }
703
704 this->needUpdate_ = this->fileNameNeedUpdate_ || this->contentNeedUpdate_;
705 }
706
EnumerateFunctions(const std::function<FunctionTraver> & callback)707 void panda::guard::Node::EnumerateFunctions(const std::function<FunctionTraver> &callback)
708 {
709 for (auto &[_, function] : this->functionTable_) {
710 callback(*function);
711 }
712
713 for (auto &[_, clazz] : this->classTable_) {
714 clazz->EnumerateFunctions(callback);
715 }
716
717 for (auto &[_, object] : this->objectTable_) {
718 object->EnumerateMethods(callback);
719 }
720 }
721
Update()722 void panda::guard::Node::Update()
723 {
724 LOG(INFO, PANDAGUARD) << TAG << "node update for " << this->name_ << " start";
725
726 if (this->fileNameNeedUpdate_) {
727 this->UpdateFileNameDefine();
728 }
729
730 // fileNameNeedUpdate_ || contentNeedUpdate_
731 for (auto &[_, function] : this->functionTable_) {
732 function->Obfuscate();
733 function->WriteNameCache(this->sourceName_);
734 }
735
736 for (auto &[_, clazz] : this->classTable_) {
737 clazz->Obfuscate();
738 clazz->WriteNameCache(this->sourceName_);
739 }
740
741 for (auto &[_, object] : this->objectTable_) {
742 object->Obfuscate();
743 object->WriteNameCache(this->sourceName_);
744 }
745
746 for (const auto &annotation : this->annotations_) {
747 annotation->Obfuscate();
748 annotation->WriteNameCache(this->sourceName_);
749 }
750
751 if (GuardContext::GetInstance()->GetGuardOptions()->IsDecoratorObfEnabled()) {
752 for (const auto &decorator : this->uiDecorator_) {
753 decorator->Obfuscate();
754 decorator->WriteNameCache(this->sourceName_);
755 }
756 }
757
758 for (const auto &array : this->arrays_) {
759 array->Obfuscate();
760 }
761
762 if (this->contentNeedUpdate_) {
763 moduleRecord_.Obfuscate();
764 moduleRecord_.WriteNameCache(this->sourceName_);
765 }
766
767 this->UpdateScopeNames();
768 this->UpdateFieldsLiteralArrayIdx();
769
770 this->WriteFileCache(this->sourceName_);
771
772 LOG(INFO, PANDAGUARD) << TAG << "node update for " << this->name_ << " end";
773 }
774
WriteFileCache(const std::string & filePath)775 void panda::guard::Node::WriteFileCache(const std::string &filePath)
776 {
777 GuardContext::GetInstance()->GetNameCache()->AddObfName(filePath, this->obfSourceName_);
778 GuardContext::GetInstance()->GetNameCache()->AddSourceFile(filePath, this->sourceFile_, this->obfSourceFile_);
779 }
780
UpdateRecordTable()781 void panda::guard::Node::UpdateRecordTable()
782 {
783 auto entry = this->program_->prog_->record_table.extract(this->name_);
784 entry.key() = this->obfName_;
785 entry.mapped().name = this->obfName_;
786 if (!entry.mapped().source_file.empty()) {
787 this->UpdateSourceFile(entry.mapped().source_file);
788 entry.mapped().source_file = this->obfSourceFile_;
789 }
790 this->program_->prog_->record_table.insert(std::move(entry));
791 }
792
UpdateFileNameDefine()793 void panda::guard::Node::UpdateFileNameDefine()
794 {
795 filepath_.obfName = GuardContext::GetInstance()->GetNameMapping()->GetFilePath(filepath_.name);
796 obfName_ = filepath_.prePart + filepath_.obfName + filepath_.postPart;
797 UpdateRecordTable();
798
799 /* e.g.
800 * sourceName_: entry/src/main/ets/entryability/EntryAbility.ets
801 * filepath_.name: src/main/ets/entryability/EntryAbility
802 * sourceNamePrePart: entry/
803 * sourceNamePostPart: .ets
804 */
805 const auto &[sourceNamePrePart, sourceNamePostPart] = StringUtil::RSplitOnce(this->sourceName_, filepath_.name);
806 if (sourceNamePrePart.empty() && sourceNamePostPart.empty()) {
807 this->obfSourceName_ = obfName_;
808 } else {
809 this->obfSourceName_ = sourceNamePrePart + filepath_.obfName + sourceNamePostPart;
810 }
811 }
812
UpdateFileNameReferences()813 void panda::guard::Node::UpdateFileNameReferences()
814 {
815 moduleRecord_.UpdateFileNameReferences();
816 }
817
UpdateSourceFile(const std::string & file)818 void panda::guard::Node::UpdateSourceFile(const std::string &file)
819 {
820 PANDA_GUARD_ASSERT_PRINT(file != this->sourceFile_, TAG, ErrorCode::GENERIC_ERROR, "duplicate source file" << file);
821 if (this->sourceFileUpdated_) {
822 return;
823 }
824
825 this->sourceFile_ = file;
826
827 /* e.g.
828 * file: entry|entry|1.0.0|src/main/ets/entryability/EntryAbility.ets
829 * filepath_.name: src/main/ets/entryability/EntryAbility
830 * prefix: entry|entry|1.0.0|
831 * suffix: .ets
832 */
833 const auto &[prefix, suffix] = StringUtil::RSplitOnce(file, filepath_.name);
834 PANDA_GUARD_ASSERT_PRINT(file != filepath_.name && prefix.empty() && suffix.empty(), TAG, ErrorCode::GENERIC_ERROR,
835 "invalid source file" << file << ",record: " << this->name_);
836 this->obfSourceFile_ = prefix + filepath_.obfName + suffix;
837 this->sourceFileUpdated_ = true;
838
839 LOG(INFO, PANDAGUARD) << TAG << "source_file: " << this->sourceFile_;
840 }
841
UpdateScopeNames() const842 void panda::guard::Node::UpdateScopeNames() const
843 {
844 LOG(INFO, PANDAGUARD) << TAG << "update scopeNames for:" << this->name_;
845 auto &record = this->GetRecord();
846 for (auto &it : record.field_list) {
847 if (it.name == SCOPE_NAMES_FIELD) {
848 const auto &literalArrayIdx = it.metadata->GetValue()->GetValue<std::string>();
849 LOG(INFO, PANDAGUARD) << TAG << "scopeNames literalArrayIdx:" << literalArrayIdx;
850 auto &literalArray = this->program_->prog_->literalarray_table.at(literalArrayIdx);
851 UpdateScopeNamesLiteralArray(literalArray);
852 break;
853 }
854 }
855 }
856
UpdateFieldsLiteralArrayIdx()857 void panda::guard::Node::UpdateFieldsLiteralArrayIdx()
858 {
859 if (this->name_ == this->obfName_) {
860 return;
861 }
862
863 LOG(INFO, PANDAGUARD) << "update fields literalArrayIdx for:" << this->name_;
864 auto &record = this->GetRecord();
865 for (auto &it : record.field_list) {
866 if (it.name == SCOPE_NAMES_FIELD || it.name == MODULE_RECORD_IDX_FIELD) {
867 const auto &literalArrayIdx = it.metadata->GetValue()->GetValue<std::string>();
868 LOG(INFO, PANDAGUARD) << TAG << "literalArrayIdx:" << literalArrayIdx;
869
870 std::string updatedLiteralArrayIdx = literalArrayIdx;
871 updatedLiteralArrayIdx.replace(updatedLiteralArrayIdx.find(this->name_), this->name_.size(),
872 this->obfName_);
873 LOG(INFO, PANDAGUARD) << TAG << "updated literalArrayIdx:" << updatedLiteralArrayIdx;
874
875 UpdateLiteralArrayTableIdx(literalArrayIdx, updatedLiteralArrayIdx);
876
877 it.metadata->SetValue(
878 pandasm::ScalarValue::Create<pandasm::Value::Type::LITERALARRAY>(updatedLiteralArrayIdx));
879
880 if (it.name == MODULE_RECORD_IDX_FIELD) {
881 this->moduleRecord_.UpdateLiteralArrayIdx(updatedLiteralArrayIdx);
882 }
883 }
884 }
885 }
886
WriteNameCache()887 void panda::guard::Node::WriteNameCache()
888 {
889 this->WriteFileCache(this->sourceName_);
890 }
891
GetMethodNameInfo(const InstructionInfo & info,InstructionInfo & nameInfo)892 void panda::guard::Node::GetMethodNameInfo(const InstructionInfo &info, InstructionInfo &nameInfo)
893 {
894 if (!info.IsValid()) {
895 return;
896 }
897
898 if (InOpcodeList(info, METHOD_NAME_DIRECT_LIST)) {
899 nameInfo = info;
900 } else if (InOpcodeList(info, METHOD_NAME_INDIRECT_LIST)) {
901 GraphAnalyzer::GetLdaStr(info, nameInfo);
902 }
903 }
904