// Low level policy classes. // Built on top of the PolicyOpcode and OpcodeFatory, the low level policy // provides a way to define rules on strings and numbers but it is unaware // of Windows specific details or how the Interceptions must be set up. // To use these classes you construct one or more rules and add them to the // LowLevelPolicy object like this: // // 我们此前在OpcodeFactory的单元测试代码中看到过rule // low-level policy只是提供了定义rule的接口,它对于windows的特定细节以及Interceptions如何被安装 // 并不关心。使用的套路就是定义多个PolicyRule对象,每个对象代表一组rule,添加match的opcode,然后 // add到管理者LowLevelPolicy对象,同样是add/fire的设计模式。 // // PolicyRule rule1(ASK_BROKER); // rule1.AddStringMatch(IF, 0, L"\\\\/?/?\\c:\\*Microsoft*\\*.exe", true); // rule1.AddNumberMatch(IF_NOT, 1, CREATE_ALWAYS, EQUAL); // rule1.AddNumberMatch(IF, 2, FILE_ATTRIBUTE_NORMAL, EQUAL); // // PolicyRule rule2(FAKE_SUCCESS); // rule2.AddStringMatch(IF, 0, L"\\\\/?/?\\Pipe\\Chrome.*", false)); // rule2.AddNumberMatch(IF, 1, OPEN_EXISTING, EQUAL)); // // LowLevelPolicy policyGen(*policy_memory); // policyGen.AddRule(kNtCreateFileSvc, &rule1); // policyGen.AddRule(kNtCreateFileSvc, &rule2); // policyGen.Done(); // // At this point (error checking omitted) the policy_memory can be copied // to the target process where it can be evaluated. // 根据注释的说明,LowLevelPolicy是由broker控制的,在fire之后,需要把policy_memory传给target进程。
// Provides the means to collect rules into a policy store (memory) classLowLevelPolicy { public: // policy_store: must contain allocated memory and the internal // size fields set to correct values. // policy_store是个PolicyGlobal指针,这理应是个由外部分配的内存空间,具有某种设计的结构 explicitLowLevelPolicy(PolicyGlobal* policy_store);
// Destroys all the policy rules. ~LowLevelPolicy();
// Adds a rule to be generated when Done() is called. // service: The id of the service that this rule is associated with, // for example the 'Open Thread' service or the "Create File" service. // returns false on error. // 增加rule的方法 boolAddRule(int service, PolicyRule* rule);
// Generates all the rules added with AddRule() into the memory area // passed on the constructor. Returns false on error. // 生成所有由AddRule添加的rule,导入到policy_store_ boolDone();
LowLevelPolicy::~LowLevelPolicy() { // Delete all the rules. typedef std::list<RuleNode> RuleNodes; for (RuleNodes::iterator it = rules_.begin(); it != rules_.end(); ++it) { delete it->rule; } }
Add
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
// Adding a rule is nothing more than pushing it into an stl container. Done() // is called for the rule in case the code that made the rule in the first // place has not done it. // add方法不过是把PolicyRule和service二元组置入容器中 boolLowLevelPolicy::AddRule(int service, PolicyRule* rule){ // 如果已经fire了,那就不能再add了 if (!rule->Done()) { returnfalse; }
// Here is where the heavy byte shuffling is done. We take all the rules and // 'compile' them into a single memory region. Now, the rules are in random // order so the first step is to reorganize them into a stl map that is keyed // by the service id and as a value contains a list with all the rules that // belong to that service. Then we enter the big for-loop where we carve a // memory zone for the opcodes and the data and call RebindCopy on each rule // so they all end up nicely packed in the policy_store_. // 把rules_部署到PolicyGlobal中,经历了一个stl map的整理以及copy到最终的memory的过程 boolLowLevelPolicy::Done(){ typedef std::list<RuleNode> RuleNodes; typedef std::list<const PolicyRule*> RuleList; typedef std::map<uint32_t, RuleList> Mmap; Mmap mmap; // 整理所用map,service id做键,RuleList存储一堆PolicyRule
// 简单的迭代,把rules_打入到mmap for (RuleNodes::iterator it = rules_.begin(); it != rules_.end(); ++it) { mmap[it->service].push_back(it->rule); }
// Adds a string comparison to the rule. // rule_type: possible values are IF and IF_NOT. // parameter: the expected index of the argument for this rule. For example // in a 'create file' service the file name argument can be at index 0. // string: is the desired matching pattern. // match_opts: if the pattern matching is case sensitive or not. boolAddStringMatch(RuleType rule_type, int16_t parameter, constwchar_t* string, StringMatchOptions match_opts);
// Adds a number match comparison to the rule. // rule_type: possible values are IF and IF_NOT. // parameter: the expected index of the argument for this rule. // number: the value to compare the input to. // comparison_op: the comparison kind (equal, logical and, etc). boolAddNumberMatch(RuleType rule_type, int16_t parameter, uint32_t number, RuleOp comparison_op);
// Returns the number of opcodes generated so far. size_tGetOpcodeCount()const{ return buffer_->opcode_count; }
// Called when there is no more comparisons to add. Internally it generates // the last opcode (the action opcode). Returns false if this operation fails. boolDone();
private: voidoperator=(const PolicyRule&); // Called in a loop from AddStringMatch to generate the required string // match opcodes. rule_type, match_opts and parameter are the same as // in AddStringMatch. boolGenStringOpcode(RuleType rule_type, StringMatchOptions match_opts, uint16_t parameter, int state, bool last_call, int* skip_count, base::string16* fragment);
// Loop over all generated opcodes and copy them to increasing memory // addresses from opcode_start and copy the extra data (strings usually) into // decreasing addresses from data_start. Extra data is only present in the // string evaluation opcodes. boolRebindCopy(PolicyOpcode* opcode_start, size_t opcode_size, char* data_start, size_t* data_size)const; PolicyBuffer* buffer_; // 内部当然得有一个PolicyBuffer来承载opcode groups OpcodeFactory* opcode_factory_; // 指向管理这个group的工厂类 EvalResult action_; bool done_; };
// There are 'if' rules and 'if not' comparisons enumRuleType { IF = 0, IF_NOT = 1, };
// Possible comparisons for numbers enumRuleOp { EQUAL, AND, RANGE // TODO(cpu): Implement this option. };
boolPolicyRule::AddNumberMatch(RuleType rule_type, int16_t parameter, uint32_t number, RuleOp comparison_op){ if (done_) { // Do not allow to add more rules after generating the action opcode. returnfalse; } // rule_type映射成kPolNegateEval标记 uint32_t opts = (rule_type == IF_NOT) ? kPolNegateEval : kPolNone;
// comparison_op映射成整型match的类别,是相等还是逻辑与还是范围(这个还没实现) // 然后调用工厂类的具体make接口做出PolicyOpcode if (EQUAL == comparison_op) { if (!opcode_factory_->MakeOpNumberMatch(parameter, number, opts)) returnfalse; } elseif (AND == comparison_op) { if (!opcode_factory_->MakeOpNumberAndMatch(parameter, number, opts)) returnfalse; } ++buffer_->opcode_count; returntrue; }
enumStringMatchOptions { CASE_SENSITIVE = 0, // Pay or Not attention to the case as defined by CASE_INSENSITIVE = 1, // RtlCompareUnicodeString windows API. EXACT_LENGTH = 2// Don't do substring match. Do full string match. };
boolPolicyRule::AddStringMatch(RuleType rule_type, int16_t parameter, constwchar_t* string, StringMatchOptions match_opts){ if (done_) { // Do not allow to add more rules after generating the action opcode. returnfalse; }
constwchar_t* current_char = string; uint32_t last_char = kLastCharIsNone; int state = PENDING_NONE; int skip_count = 0; // counts how many '?' we have seen in a row. base::string16 fragment; // accumulates the non-wildcard part.
// 这里是处理windows路径转义的操作,算法就不细说了,工厂类的Make封装在GenStringOpcode while (L'\0' != *current_char) { switch (*current_char) { caseL'*': if (kLastCharIsWild & last_char) { // '**' and '&*' is an error. returnfalse; } if (!GenStringOpcode(rule_type, match_opts, parameter, state, false, &skip_count, &fragment)) { returnfalse; } last_char = kLastCharIsAsterisk; state = PENDING_ASTERISK; break; caseL'?': if (kLastCharIsAsterisk == last_char) { // '*?' is an error. returnfalse; } if (!GenStringOpcode(rule_type, match_opts, parameter, state, false, &skip_count, &fragment)) { returnfalse; } ++skip_count; last_char = kLastCharIsQuestionM; state = PENDING_QMARK; break; caseL'/': // Note: "/?" is an escaped '?'. Eat the slash and fall through. if (L'?' == current_char[1]) { ++current_char; } FALLTHROUGH; default: fragment += *current_char; last_char = kLastCharIsAlpha; } ++current_char; }
// This function get called from a simple state machine implemented in // AddStringMatch() which passes the current state (in state) and it passes // true in last_call if AddStringMatch() has finished processing the input // pattern string and this would be the last call to generate any pending // opcode. The skip_count is the currently accumulated number of '?' seen so // far and once the associated opcode is generated this function sets it back // to zero. boolPolicyRule::GenStringOpcode(RuleType rule_type, StringMatchOptions match_opts, uint16_t parameter, int state, bool last_call, int* skip_count, base::string16* fragment){ // The last opcode must: // 1) Always clear the context. // 2) Preserve the negation. // 3) Remove the 'OR' mode flag. // 根据传入的参数,设置相应的标记 uint32_t options = kPolNone; if (last_call) { if (IF_NOT == rule_type) { options = kPolClearContext | kPolNegateEval; } else { options = kPolClearContext; } } elseif (IF_NOT == rule_type) { options = kPolUseOREval | kPolNegateEval; }
PolicyOpcode* op = nullptr;
// The fragment string contains the accumulated characters to match with, it // never contains wildcards (unless they have been escaped) and while there // is no fragment there is no new string match opcode to generate. if (fragment->empty()) { // There is no new opcode to generate but in the last call we have to fix // the previous opcode because it was really the last but we did not know // it at that time. if (last_call && (buffer_->opcode_count > 0)) { op = &buffer_->opcodes[buffer_->opcode_count - 1]; op->SetOptions(options); } returntrue; }
// 这里各找各妈,根据state状态传参调用Make if (PENDING_ASTERISK == state) { if (last_call) { op = opcode_factory_->MakeOpWStringMatch(parameter, fragment->c_str(), kSeekToEnd, match_opts, options); } else { op = opcode_factory_->MakeOpWStringMatch( parameter, fragment->c_str(), kSeekForward, match_opts, options); }