Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(1284)

Unified Diff: syzygy/experimental/protect/protect_lib/integrity_check_transform.cc

Issue 2535563002: Added all code for integrity check transform (Closed)
Patch Set: Created 4 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: syzygy/experimental/protect/protect_lib/integrity_check_transform.cc
diff --git a/syzygy/experimental/protect/protect_lib/integrity_check_transform.cc b/syzygy/experimental/protect/protect_lib/integrity_check_transform.cc
new file mode 100644
index 0000000000000000000000000000000000000000..a96a1ebca55db028d28d75322fe74283cec2c925
--- /dev/null
+++ b/syzygy/experimental/protect/protect_lib/integrity_check_transform.cc
@@ -0,0 +1,2136 @@
+// Copyright 2015 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+#include "syzygy/experimental/protect/protect_lib/integrity_check_transform.h"
+#include <fstream>
+#include <stack>
+#include <algorithm>
+#include <random>
+#include "syzygy/assm/assembler.h"
+#include "syzygy/assm/assembler_base.h"
+#include "syzygy/block_graph/basic_block_assembler.h"
+#include "syzygy/block_graph/basic_block_decomposer.h"
+#include "syzygy/block_graph/basic_block_subgraph.h"
+#include "syzygy/block_graph/block_builder.h"
+#include "syzygy/block_graph/block_util.h"
+#include "syzygy/optimize/transforms/subgraph_transform.h"
+#include "syzygy/experimental/protect/protect_lib/code_randomizer.h"
+#include "syzygy/experimental/protect/protect_lib/protect_utils.h"
+#include <iostream>
+//#define PRINT_BLOCK_NAMES
+namespace protect {
+
+namespace {
+
+using block_graph::BasicBlockDecomposer;
+using block_graph::BasicBlockSubGraph;
+using block_graph::BlockBuilder;
+using block_graph::BlockGraph;
+using block_graph::BlockVector;
+using block_graph::BasicBlock;
+using block_graph::BasicCodeBlock;
+using block_graph::Instruction;
+using block_graph::BasicBlockReference;
+
+typedef BasicBlockSubGraph::BBCollection BBCollection;
+typedef BasicBlock::Instructions Instructions;
+typedef BlockGraph::Block::ReferrerSet ReferrerSet;
+typedef std::list<BlockGraph::Block*> BlockOrdering;
+
+ // Retrieves a unique identifier for a basic block.
+ // @param bb the basic block to be uniquely identifed.
+ // @param subgraph basic block subgraph in which the basic block resides.
+ // @return a unique ID for the basic block.
+uint64_t GetBasicBlockId(const BasicBlock *bb,
+ const BasicBlockSubGraph *subgraph) {
+ DCHECK(bb);
+ DCHECK(subgraph);
+
+ auto original_block = subgraph->original_block();
+ DCHECK(original_block);
+
+ return ((uint64_t)bb->offset() << 32) + original_block->id();
+}
+
+ // Retrieves the block where the _putwch_nolock function is declared.
+ // @param bragph block graph where to search of the function.
+ // @return the block where the function is declared.
+block_graph::BlockGraph::Block* GetPutwchNolock(
+ block_graph::BlockGraph* bgraph) {
+ DCHECK(bgraph);
+ block_graph::BlockGraph::Block *putwch_nolock = nullptr;
+
+ auto it = bgraph->blocks().begin();
+ for (; it != bgraph->blocks().end(); ++it)
+ if ((*it).second.name().compare("_putwch_nolock") == 0) {
+ putwch_nolock = bgraph->GetBlockById((*it).second.id());
+ break;
+ }
+ return putwch_nolock;
+}
+
+ // Adds assembly code for response function to a block graph.
+ // @param bgraph the block graph from where the response function is inserted.
+ // @return the block containing the newly inseted response function.
+BlockGraph::Block* AddResponseFunction(BlockGraph* bgraph) {
+ DCHECK(bgraph);
+ //TODO:BlockGraph::Block *response_function = GetPutwchNolock(bgraph);
+ BasicBlockSubGraph* subgraph = new BasicBlockSubGraph();
+ BlockGraph::Section* code_section = bgraph->FindOrAddSection(".text",
+ 0x60000000);
+ std::string bb_name = "response_bb1";
+ // Create the thunk for standard "load/store" (received address in EDX).
+ BasicBlockSubGraph::BlockDescription* block_desc =
+ subgraph->AddBlockDescription(bb_name, code_section->name(),
+ BlockGraph::CODE_BLOCK, code_section->id(),
+ 1, 0);
+
+ BasicCodeBlock* bb = subgraph->AddBasicCodeBlock(bb_name);
+ block_desc->basic_block_order.push_back(bb);
+
+ BasicBlock::Instructions::iterator inst_iter = bb->instructions().begin();
+ block_graph::BasicBlockAssembler assm(inst_iter,
+ &bb->instructions());
+
+ assm.push(assm::eax); // eax contains the actual hash value
+ // add size of instructions from hash function return up to response return
+ assm.add(assm::ebx, block_graph::Immediate(0xe));
+ assm.push(assm::ebx); // edx contains the address where to continue execution
+ //TODO:assm.call(block_graph::Immediate(response_function, 0)); // print char
+ assm.pop(assm::ebx); // edx gets changed by the previous call
+ assm.mov(assm::ebx, block_graph::Immediate((uint32_t)0x0));
+ assm.jmp(assm::ebx); // continue normal execution
+
+ // Condense into a block.
+ block_graph::BlockBuilder block_builder(bgraph);
+ if (!block_builder.Merge(subgraph))
+ return nullptr;
+
+ return block_builder.new_blocks().rbegin()[0];
+}
+
+ // Adds assembly code for hash function in a block graph.
+ // @param bgraph the block graph where the hash function is inserted.
+ // @return the block containing the newly inserted hash function.
+BlockGraph::Block* AddHashFunction(BlockGraph* bgraph) {
+ DCHECK(bgraph);
+ BlockGraph::Section* code_section = bgraph->FindOrAddSection(".text",
+ 0x60000000);
+ std::string bb_name = "hash_add_bb1";
+ BasicBlockSubGraph* subgraph = new BasicBlockSubGraph();
+ // Create the thunk for standard "load/store" (received address in EDX).
+ BasicBlockSubGraph::BlockDescription* block_desc =
+ subgraph->AddBlockDescription(bb_name, code_section->name(),
+ BlockGraph::CODE_BLOCK, code_section->id(),
+ 1, 0);
+
+ BasicCodeBlock* bb = subgraph->AddBasicCodeBlock(bb_name);
+ block_desc->basic_block_order.push_back(bb);
+
+ auto inst_iter = bb->instructions().begin();
+ block_graph::BasicBlockAssembler assm(inst_iter,
+ &bb->instructions());
+
+ // Create following BB that contains outer loop head.
+ bb_name = "hash_add_bb2";
+ block_desc = subgraph->AddBlockDescription(bb_name, code_section->name(),
+ BlockGraph::CODE_BLOCK,
+ code_section->id(), 1, 0);
+
+ bb = subgraph->AddBasicCodeBlock(bb_name);
+ block_desc->basic_block_order.push_front(bb);
+
+ // Function prolog.
+ assm.push(assm::ebp);
+ assm.mov(assm::ebp, assm::esp);
+
+ assm.pop(assm::eax); // pop ebp
+ assm.pop(assm::ebx); // pop return addres
+ assm.xor(assm::eax, assm::eax); // set eax to 0
+ // Get the base address of code section of this PE/DLL.
+ auto block_it = bgraph->blocks().begin();
+ for (; block_it != bgraph->blocks().end(); ++block_it) {
+ BlockGraph::BlockType type = block_it->second.type();
+ if (type == BlockGraph::BlockType::DATA_BLOCK)
+ break;
+ }
+ BlockGraph::Block* first_block = bgraph->GetBlockById(block_it->second.id());
+ // Get the start address of this basic block.
+ assm.mov(assm::ebx, block_graph::Immediate(first_block, 0));
+ // Compute hash of address.
+ assm.add(assm::al, assm::bl);
+ assm.add(assm::al, assm::bh);
+ assm.shr(assm::ebx, block_graph::Immediate(0x10));
+ assm.add(assm::al, assm::bl);
+ assm.add(assm::al, assm::bh);
+ // Save this hash of the address on the stack.
+ assm.pop(assm::ebx); // This is the designeated slot for the hash of address.
+ assm.pop(assm::ebx); // This is the designeated slot for the accumulator.
+ assm.xor(assm::ebx, assm::ebx); // Set accumulator to 0.
+ assm.push(assm::ebx); // Save accumulator.
+ assm.push(assm::eax); // Save hash of address.
+
+ assm.j(assm::ConditionCode::kEqual,
+ block_graph::Immediate(bb));
+
+ inst_iter = bb->instructions().begin();
+ block_graph::BasicBlockAssembler assm2(inst_iter,
+ &bb->instructions());
+
+ // Begin outer loop over all checkees passed to the hash function.
+ assm2.pop(assm::ebx); // Hash of address.
+ assm2.pop(assm::eax); // Accumulator for hash.
+ assm2.pop(assm::edx); // Get address of bb to hash.
+ assm2.sub(assm::ecx, block_graph::Immediate(1));
+ assm2.xchg(assm::ecx,
+ assm::OperandBase<block_graph::UntypedReference>(assm::esp));
+ assm2.push(assm::eax); // Accumulator for hash.
+ assm2.push(assm::ebx); // Hash of address.
+ assm2.sub(assm::eax, assm::eax); // Set eax to zero.
+
+ // Create following BB that contains inner loop over bytes of checkee.
+ bb_name = "hash_add_bb3";
+ block_desc = subgraph->AddBlockDescription(bb_name,
+ code_section->name(),
+ BlockGraph::CODE_BLOCK,
+ code_section->id(), 1, 0);
+
+ BasicCodeBlock* bb2 = subgraph->AddBasicCodeBlock(bb_name);
+ block_desc->basic_block_order.push_front(bb2);
+
+ assm2.jmp(block_graph::Immediate(bb2));
+
+ inst_iter = bb2->instructions().begin();
+ block_graph::BasicBlockAssembler assm3(inst_iter,
+ &bb2->instructions());
+
+ // Begin inner loop over instruction bytes of current checkee.
+ assm3.mov(assm::ebx,
+ assm::OperandBase<block_graph::UntypedReference>(assm::edx));
+ assm3.add(assm::al, assm::bl);
+ assm3.add(assm::edx, block_graph::Immediate(1));
+ assm3.sub(assm::ecx, block_graph::Immediate(1));
+ assm3.test(assm::ecx, assm::ecx);
+ assm3.j(assm::ConditionCode::kNotEqual, block_graph::Immediate(bb2));
+ // End inner loop.
+
+ // Subtract the hash of address from computed hash.
+ assm3.pop(assm::ebx); // Hash of address.
+ assm3.pop(assm::edx); // Accumulator for hash.
+ assm3.pop(assm::ecx); // Ourter loop counter.
+ assm3.xchg(assm::eax, // Load #checkees of checkee.
+ assm::OperandBase<block_graph::UntypedReference>(assm::esp));
+ assm3.imul(assm::ebx, assm::eax); // Multiply hash of address with #chekees
+ assm3.and(assm::ebx, block_graph::Immediate(0xFF)); // Modulo 256.
+ assm3.pop(assm::eax); // Get hash of the current checkee.
+ assm3.sub(assm::al, assm::bl); // Cancel base addresses of checkees in hash.
+ assm3.pop(assm::ebx); // Coeficient of current basic block.
+ assm3.imul(assm::eax, assm::ebx); // Multiply hash with coeficient.
+ assm3.and(assm::eax, block_graph::Immediate(0xFF)); // Modulo 256
+ assm3.add(assm::dl, assm::al); // Accumulate hash.
+ assm3.push(assm::edx); // Store accumulator for hash.
+ // The hash of the address is on the stack at a distance of 4 stack slots.
+ // Recover it because it was lost when ebx was multiplied by the #checkees of
+ // this checkee.
+ assm3.mov(assm::edx, block_graph::Operand(assm::esp,
+ block_graph::Displacement((unsigned int)-0x10)));
+ assm3.push(assm::edx); // Store hash of address.
+ // Check outer loop boundary.
+ assm3.test(assm::ecx, assm::ecx);
+ assm3.j(assm::ConditionCode::kNotEqual, block_graph::Immediate(bb));
+ // End outer loop.
+
+ assm3.pop(assm::eax); // Throw away hash of adddress.
+ assm3.pop(assm::eax); // Load final hash value.
+
+ // Function epilog.
+ assm3.mov(assm::esp, assm::ebp);
+ assm3.pop(assm::ebp);
+ // Jump over pivot byte.
+ assm3.add(assm::OperandBase<block_graph::UntypedReference>(assm::esp),
+ block_graph::Immediate(1));
+ // Load return address of edx into ebx, to be used by response function.
+ assm3.mov(assm::ebx,
+ assm::OperandBase<block_graph::UntypedReference>(assm::esp));
+ assm3.ret();
+
+ // Condense into a block.
+ block_graph::BlockBuilder block_builder(bgraph);
+ if (!block_builder.Merge(subgraph))
+ return nullptr;
+
+ return block_builder.new_blocks().begin()[0];
+}
+
+
+ // Adds assembly code for xor hash function
+ // @param bgraph - the block graph from where the subgraph was taken
+ // @param subgraph - the subgraph containing basic blocks we want to transform
+ // @return the block containing the hash function
+ // Adds assembly code for hash function
+ // @param bgraph - the block graph from where the subgraph was taken
+ // @param subgraph - the subgraph containing basic blocks we want to transform
+ // @return the block containing the hash function
+BlockGraph::Block* AddXorHashFunction(BlockGraph* bgraph) {
+ BlockGraph::Section* code_section = bgraph->FindOrAddSection(".text",
+ 0x60000000);
+ std::string bb_name = "get_xeip";
+ BasicBlockSubGraph* subgraph = new BasicBlockSubGraph();
+ // Create the thunk for standard "load/store" (received address in EDX).
+ BasicBlockSubGraph::BlockDescription* block_desc =
+ subgraph->AddBlockDescription(bb_name, code_section->name(),
+ BlockGraph::CODE_BLOCK, code_section->id(),
+ 1, 0);
+
+ BasicCodeBlock* bb = subgraph->AddBasicCodeBlock(bb_name);
+ block_desc->basic_block_order.push_back(bb);
+
+ auto inst_iter = bb->instructions().begin();
+ block_graph::BasicBlockAssembler assm(inst_iter, &bb->instructions());
+
+ // Create following BB that contains outer loop head
+ bb_name = "get_xeip2";
+ block_desc = subgraph->AddBlockDescription(bb_name, code_section->name(),
+ BlockGraph::CODE_BLOCK,
+ code_section->id(), 1, 0);
+
+ bb = subgraph->AddBasicCodeBlock(bb_name);
+ block_desc->basic_block_order.push_front(bb);
+
+ // function prolog
+ assm.push(assm::ebp);
+ assm.mov(assm::ebp, assm::esp);
+
+ assm.pop(assm::eax); // pop ebp
+ assm.pop(assm::eax); // pop return addres
+ assm.j(assm::ConditionCode::kEqual,
+ block_graph::Immediate(bb));
+
+ inst_iter = bb->instructions().begin();
+ block_graph::BasicBlockAssembler assm2(inst_iter,
+ &bb->instructions());
+
+ // begin outer loop
+ assm2.pop(assm::eax); // accumulator for hash
+
+ assm2.pop(assm::edx); // get address of bb to hash
+ assm2.sub(assm::ecx, block_graph::Immediate(1)); // decrement outer loop iter
+ assm2.xchg(assm::ecx, // swap outer loop iter with inner loop iter
+ assm::OperandBase<block_graph::UntypedReference>(assm::esp));
+ assm2.push(assm::eax); // save accumulator for hash
+ assm2.sub(assm::eax, assm::eax); // set eax to zero
+
+ // Create following BB that contains inner loop
+ bb_name = "get_xeip3";
+ block_desc = subgraph->AddBlockDescription(bb_name, code_section->name(),
+ BlockGraph::CODE_BLOCK,
+ code_section->id(), 1, 0);
+
+ BasicCodeBlock* bb2 = subgraph->AddBasicCodeBlock(bb_name);
+ block_desc->basic_block_order.push_front(bb2);
+
+ assm2.jmp(block_graph::Immediate(bb2));
+
+ inst_iter = bb2->instructions().begin();
+ block_graph::BasicBlockAssembler assm3(inst_iter,
+ &bb2->instructions());
+
+ // begin inner loop
+ assm3.mov(assm::ebx,
+ assm::OperandBase<block_graph::UntypedReference>(assm::edx));
+ assm3.xor(assm::al, assm::bl);
+ assm3.add(assm::edx, block_graph::Immediate(1));
+ assm3.sub(assm::ecx, block_graph::Immediate(1));
+ assm3.test(assm::ecx, assm::ecx);
+ assm3.j(assm::ConditionCode::kNotEqual, block_graph::Immediate(bb2));
+ // end inner loop
+
+ assm3.pop(assm::ebx); // hash accumulator
+ assm3.pop(assm::ecx); // ourter loop counter
+ assm3.xchg(assm::ecx, // swap outer loop iter with inner loop iter
+ assm::OperandBase<block_graph::UntypedReference>(assm::esp));
+ assm3.push(assm::ebx); // save accumulator for hash
+
+ // Create following BB that contains 2nd inner loop
+ bb_name = "get_xeip4";
+ block_desc = subgraph->AddBlockDescription(bb_name, code_section->name(),
+ BlockGraph::CODE_BLOCK,
+ code_section->id(), 1, 0);
+
+ BasicCodeBlock* bb3 = subgraph->AddBasicCodeBlock(bb_name);
+ block_desc->basic_block_order.push_front(bb3);
+
+ assm3.cmp(assm::ecx, block_graph::Immediate(0, assm::ValueSize::kSize32Bit));
+ assm3.j(assm::ConditionCode::kNotEqual, block_graph::Immediate(bb3));
+
+ inst_iter = bb3->instructions().begin();
+ block_graph::BasicBlockAssembler assm4(inst_iter,
+ &bb3->instructions());
+
+ // begin 2nd inner loop
+ assm4.mov(assm::ebx,
+ assm::OperandBase<block_graph::UntypedReference>(assm::edx));
+ assm4.add(assm::al, assm::bl);
+ assm4.add(assm::edx, block_graph::Immediate(1));
+ assm4.sub(assm::ecx, block_graph::Immediate(1));
+ assm4.test(assm::ecx, assm::ecx);
+ assm4.j(assm::ConditionCode::kNotEqual, block_graph::Immediate(bb3));
+ assm4.mov(assm::ecx, block_graph::Immediate(bb));
+ assm4.add(assm::ecx, block_graph::Immediate(0x34));
+ assm4.jmp(assm::ecx);
+ // end 2nd inner loop
+
+ assm3.pop(assm::edx); // load hash accumulator
+ assm3.pop(assm::ecx); // ourter loop counter
+ assm3.and(assm::eax, block_graph::Immediate(0xFF));
+
+ assm3.add(assm::dl, assm::al); // accumulate hash
+ assm3.xor(assm::eax, assm::eax); // set eax to 0
+ assm3.sub(assm::al, assm::dl); // al = -hash
+ assm3.push(assm::eax); // store hash accumulator
+ // check outer loop boundary
+ assm3.test(assm::ecx, assm::ecx);
+ assm3.j(assm::ConditionCode::kNotEqual, block_graph::Immediate(bb));
+ // end outer loop
+ assm3.pop(assm::eax); // final hash value
+
+ // function epilog
+ assm3.mov(assm::esp, assm::ebp);
+ assm3.pop(assm::ebp);
+ assm3.ret();
+
+ // Condense into a block.
+ block_graph::BlockBuilder block_builder(bgraph);
+ if (!block_builder.Merge(subgraph))
+ return NULL;
+
+ return block_builder.new_blocks().begin()[0];
+}
+
+// Traverse the call-graph in reverse call order (callee to caller) and push
+// blocks in post-order. The resulting ordering can be iterated to visit all
+// blocks from leaf to root. The ordering has the guarantee that all callees
+// have been visited before their callers (except for recursive calls and
+// indirect calls).
+// TODO(etienneb): Hoist this function into block_graph.
+void FlattenCallGraphPostOrder(BlockGraph* block_graph, BlockOrdering* order) {
+ DCHECK_NE(reinterpret_cast<BlockGraph*>(NULL), block_graph);
+ DCHECK_NE(reinterpret_cast<BlockOrdering*>(NULL), order);
+
+ // The algorithms uses a std::stack allocated in the heap to avoid stack
+ // overflow.
+ std::stack<BlockGraph::Block*> stack;
+ std::set<BlockGraph::Block*> visiting;
+
+ // Traverse the call-graph in depth-first.
+ BlockGraph::BlockMap& blocks = block_graph->blocks_mutable();
+ auto block_iter = blocks.begin();
+ for (; block_iter != blocks.end(); ++block_iter) {
+ BlockGraph::Block* block = &block_iter->second;
+
+ // This block is already visited.
+ if (!visiting.insert(block).second)
+ continue;
+
+ // This block needs to be visited, add it to the stack.
+ stack.push(block);
+
+ // Follow the referrers.
+ while (!stack.empty()) {
+ block = stack.top();
+
+ // Put unvisited referrers on the stack.
+ typedef std::map<BlockGraph::BlockId,
+ BlockGraph::Block*> OrderedBlockMap;
+ OrderedBlockMap missing;
+ bool missing_referrers = false;
+ if (block->type() == BlockGraph::CODE_BLOCK) {
+ const ReferrerSet& referrers = block->referrers();
+ auto referrer = referrers.begin();
+ for (; referrer != referrers.end(); ++referrer) {
+ BlockGraph::Block* from = referrer->first;
+ if (visiting.insert(from).second) {
+ missing.insert(std::make_pair(from->id(), from));
+ missing_referrers = true;
+ }
+ }
+ }
+
+ // Push missing referrers into the stack, ordered by block id.
+ auto referrer = missing.begin();
+ for (; referrer != missing.end(); ++referrer)
+ stack.push(referrer->second);
+
+ // When there are no missing referrers, this block is fully visited and
+ // can be pushed in the ordering (post-order).
+ if (!missing_referrers) {
+ order->push_front(block);
+ // Remove this block from the stack.
+ DCHECK_EQ(block, stack.top());
+ stack.pop();
+ }
+ }
+ }
+}
+
+// Retrieves the basic block in the given subgraph at the given offset.
+// @param subgraph the basic block subgraph where to search.
+// @param offset the offset of the basic block to search for.
+// @return the basic block object if found and NULL otherwise.
+BasicBlock* GetBasicBlockAtOffset(const BasicBlockSubGraph *subgraph,
+ const BasicBlock::Offset offset) {
+ DCHECK(subgraph);
+ DCHECK_LE(0, offset);
+
+ auto it(subgraph->basic_blocks().begin());
+ for (; it != subgraph->basic_blocks().end(); ++it) {
+ if ((*it)->offset() == offset)
+ return *it;
+ }
+
+ return nullptr;
+}
+
+
+}
+// namespace
+
+void IntegrityCheckTransform::PatchBlockReference(
+ BasicBlock::Instructions::iterator inst_itr,
+ block_graph::BlockGraph::Block* new_block,
+ block_graph::BlockGraph::Offset new_offset,
+ bool use_new_block = false){
+ DCHECK(new_block);
+ Instruction::BasicBlockReferenceMap &ref_block_map = inst_itr->references();
+ auto instruction_references_it = ref_block_map.begin();
+ BlockGraph::Offset reference_offset = instruction_references_it->first;
+
+ BasicBlockReference old_bb_ref = instruction_references_it->second;
+ BasicBlockReference new_bb_ref(old_bb_ref.reference_type(),
+ old_bb_ref.size(),
+ use_new_block ? new_block : old_bb_ref.block(),
+ new_offset,
+ new_offset);
+
+ ref_block_map[reference_offset] = new_bb_ref;
+}
+
+void SplitChunkReferencelabels(const std::string label,
+ uint64_t *checkee_id,
+ int *chunkIndex){
+ //split the string
+ std::istringstream iss(label);
+ std::vector<std::string> tokens;
+ copy(std::istream_iterator<std::string>(iss),
+ std::istream_iterator<std::string>(),
+ back_inserter(tokens));
+
+ *checkee_id = std::stoull(tokens.at(1));
+ *chunkIndex = std::stoi(tokens.at(2));
+}
+
+void IntegrityCheckTransform::GenerateLabelToBlockMap(BlockGraph *bgraph) {
+ BlockGraph::BlockMap &blocks = bgraph->blocks_mutable();
+ auto it(blocks.begin());
+ this->label_name_to_block_->clear();
+
+ for (; it != blocks.end(); ++it) {
+ auto label_map = it->second.labels();
+ auto lab_it(label_map.begin());
+ for (; lab_it != label_map.end(); ++lab_it) {
+ (*this->label_name_to_block_)[lab_it->second.name()] =
+ std::make_pair(&it->second, lab_it->first);
+ }
+ }
+}
+
+void IntegrityCheckTransform::UpdateLabelToBlockMap(BlockGraph::Block *block) {
+ auto label_map = block->labels();
+ auto lab_it(label_map.begin());
+ for (; lab_it != label_map.end(); ++lab_it) {
+ (*this->label_name_to_block_)[lab_it->second.name()] =
+ std::make_pair(block, lab_it->first);
+ }
+}
+
+bool IntegrityCheckTransform::PopulatePartitionKey(
+ const block_graph::Instruction instr,
+ uint8_t *num_abs_references) {
+ auto references = instr.references();
+ if (references.size() < 1)
+ return false;
+
+ auto it = references.begin();
+ for (; it != references.end(); ++it) {
+ block_graph::BlockGraph::ReferenceType type = it->second.reference_type();
+
+ if (type == block_graph::BlockGraph::ReferenceType::ABSOLUTE_REF) {
+ (*num_abs_references)++;
+ }
+ }
+ return true;
+}
+
+void IntegrityCheckTransform::PopulateCheckMaps(std::set<uint64_t> part_block) {
+ std::set<uint64_t> tmp = part_block;
+
+ while (tmp.size() > 0) {
+ // chose a random element
+ int index = rand() % tmp.size();
+ auto set_it = tmp.begin();
+ std::advance(set_it, index);
+
+ index = rand() % part_block.size();
+ auto set_it2 = part_block.begin();
+ std::advance(set_it2, index);
+
+ // pick different blocks as the pair of checkees
+ for (; set_it2 != part_block.end(); ++set_it2)
+ if ((uint32_t)(*set_it) != (uint32_t)(*set_it2))
+ break;
+
+ // if reached the end of the list then start from beginning
+ if (set_it2 == part_block.end())
+ set_it2 = part_block.begin();
+
+ // pick different blocks as the pair of checkees
+ int i = 0;
+ for (; i < index; ++i) {
+ if ((uint32_t)(*set_it) != (uint32_t)(*set_it2))
+ break;
+
+ ++set_it2;
+ }
+
+ if (((uint32_t)(*set_it) == (uint32_t)(*set_it2)) && (i >= index)) {
+ // skip this block of the partition
+ tmp.erase(set_it);
+ continue;
+ }
+
+ // use this when checkers are allowed to be in the same block as checkees
+ std::map<uint64_t, int> tuple;
+ tuple.insert(std::pair<uint64_t, int>(*set_it, 1));
+ tuple.insert(std::pair<uint64_t, int>(*set_it2, -1));
+ // use this when checkers are NOT allowed in the same block as checkees
+ std::list<uint32_t> tuple_blocks;
+ tuple_blocks.push_back((uint32_t)*set_it);
+ tuple_blocks.push_back((uint32_t)*set_it2);
+
+ uint64_t checker_id;
+ if (!RandomlySelectChecker(tuple_blocks, &checker_id)) {
+ tmp.erase(*set_it);
+ continue;
+ }
+
+ // Populate checker / checkee maps
+ (*this->checker_to_checkee_map_)[checker_id] = tuple;
+ fprintf(this->pfile_, "%llx,", checker_id);
+ auto list_it = tuple.begin();
+ for (; list_it != tuple.end(); ++list_it) {
+ this->is_bb_checked_map_[list_it->first] = 1;
+ fprintf(this->pfile_, "%d * %llx,", list_it->second, list_it->first);
+ }
+
+ fprintf(this->pfile_, "\n");
+ tmp.erase(set_it);
+ }
+}
+
+//
+bool IntegrityCheckTransform::RandomlySelectChecker(
+ std::list<uint32_t> tuple_blocks,
+ uint64_t *checker_id) {
+ // Randomly select checker
+ int index = rand() % this->precomputed_hashes_->size();
+ auto map_it = this->precomputed_hashes_->begin();
+ std::advance(map_it, index);
+
+ // checker must not be in the list of checkees and preferrably does not check
+ // other tuples as well
+ while ((map_it != this->precomputed_hashes_->end()) &&
+ ((std::find(tuple_blocks.begin(), tuple_blocks.end(),
+ (uint32_t)(*map_it).first) != tuple_blocks.end()) ||
+ (this->checker_to_checkee_map_->find((*map_it).first) !=
+ this->checker_to_checkee_map_->end()) &&
+ ((*this->checker_to_checkee_map_)[(*map_it).first].size() > 0)))
+ map_it++;
+
+ int i = 0;
+ // if reached end of list then start from the begining and go until index
+ if (map_it == this->precomputed_hashes_->end()) {
+ map_it = this->precomputed_hashes_->begin();
+ while ((i < index) &&
+ ((std::find(tuple_blocks.begin(), tuple_blocks.end(),
+ (uint32_t)(*map_it).first) != tuple_blocks.end()) ||
+ (this->checker_to_checkee_map_->find((*map_it).first) !=
+ this->checker_to_checkee_map_->end()) &&
+ ((*this->checker_to_checkee_map_)[(*map_it).first].size() > 0))) {
+ map_it++;
+ i++;
+ }
+ }
+
+ // if all checkers are already checking some tuple then we should only avoid
+ // selecting the checker in the list of checkees
+ if (i >= index) {
+ while ((map_it != this->precomputed_hashes_->end()) &&
+ ((std::find(tuple_blocks.begin(), tuple_blocks.end(),
+ (uint32_t)(*map_it).first) != tuple_blocks.end()))) {
+ map_it++;
+ }
+
+ int i = 0;
+ // if reached end of list then start from the begining and go until index
+ if (map_it == this->precomputed_hashes_->end()) {
+ map_it = this->precomputed_hashes_->begin();
+ while ((i < index) &&
+ ((std::find(tuple_blocks.begin(), tuple_blocks.end(),
+ (uint32_t)(*map_it).first) != tuple_blocks.end()))){
+ map_it++;
+ i++;
+ }
+
+ if (i >= index) {
+ return false; // can't find a checker that satisfies all conditions
+ }
+ }
+ }
+
+ DCHECK(std::find(tuple_blocks.begin(), tuple_blocks.end(),
+ (uint32_t)(*map_it).first) == tuple_blocks.end());
+
+ *checker_id = (*map_it).first;
+ return true;
+}
+
+#if defined COMPUTE_CHECKER_SIZE
+uint64_t total_checker_size = 0;
+#endif
+
+char* MakeChunkLabel(const uint64_t chunk_bb_id, const uint32_t chunk_index,
+ const bool before_chunk_integrity_code_added = false){
+ DCHECK(chunk_bb_id != MAXUINT64);
+ char *buffersearch = new char[50];
+
+ //only after chunk integrity code is prepended the first chunk label is
+ //update to n %llu %lu format. Before that the first instruction refers to
+ //the beginning of the block, which has %llu format
+ if (before_chunk_integrity_code_added && chunk_index == 0){
+ sprintf_s(buffersearch, 50, "%llu", chunk_bb_id);
+ } else {
+ sprintf_s(buffersearch, 50, "n %llu %lu", chunk_bb_id, chunk_index);
+ }
+ return buffersearch;
+}
+
+bool IntegrityCheckTransform::AddChunkIntegrityCheckCode(
+ BasicCodeBlock* bb,
+ BasicBlockSubGraph* subgraph,
+ BlockGraph *block_graph){
+ auto inst_iter = bb->instructions().begin();
+ if (inst_iter == bb->instructions().end())
+ return true;
+
+ BlockGraph::Label label(inst_iter->label());
+ uint64_t bb_id = GetBasicBlockIdByLabel(label, this->id_to_label_);
+
+ if (bb_id == -1)
+ return true;
+
+ if ((*this->checker_to_checkee_map_)[bb_id].size() < 1)
+ return true;
+
+ //given that the begining of the checker block never has an absolute reference
+ //therefore, it is the pointer for the first block chunk. So, we update it's
+ //label to a the chunk of index zero within the block
+ char *chunk_label = MakeChunkLabel(bb_id, 0);
+ inst_iter->set_label(BlockGraph::Label(chunk_label, BlockGraph::CODE_LABEL));
+ delete[] chunk_label;
+ std::set<uint32_t> chunk_set = (*ic_chunk_checker_to_checkee_map_)[bb_id];
+ CHECK(chunk_set.size() == this->num_chunks_per_block);
+
+ block_graph::BasicBlockAssembler assm(inst_iter, &bb->instructions());
+
+ uint32_t num_original_instr = bb->instructions().size();
+
+ assm.push(assm::eax);
+ assm.push(assm::ebx);
+ assm.push(assm::ecx);
+ assm.push(assm::edx);
+
+ assm.mov(assm::ecx, block_graph::Immediate(chunk_set.size(),
+ assm::ValueSize::kSize32Bit));
+
+ auto chunk_iter = chunk_set.begin();
+ uint32_t num_chunks = chunk_set.size();
+
+ std::map<uint32_t, std::tuple<uint64_t, uint32_t>> reference_free_labels;
+ for (uint32_t reference_index = 0; chunk_iter != chunk_set.end();
+ ++chunk_iter, ++reference_index){
+
+ auto chunk_info = (*ic_block_reference_free_chunks)[*chunk_iter];
+ uint64_t chunk_bb_id = chunk_info.block_id_;
+ uint32_t chunk_size = chunk_info.size_;
+ uint32_t chunk_index = chunk_info.chunk_index_;
+
+ //get chunk offset and block
+ char *buffersearch = MakeChunkLabel(chunk_bb_id, chunk_index,true);
+ auto found_label_it = label_name_to_block_->find(buffersearch);
+ DCHECK(found_label_it != label_name_to_block_->end());
+ delete[] buffersearch;
+ BlockGraph::Block* chunk_block = found_label_it->second.first;
+ uint32_t chunk_offset = found_label_it->second.second;
+
+ assm.push(block_graph::Immediate(chunk_info.next_instruction_size_,
+ assm::ValueSize::kSize32Bit));
+ assm.push(block_graph::Immediate(chunk_size, assm::ValueSize::kSize32Bit));
+
+ //keep the index of block instruction for labelling
+ uint32_t label_instr_index = bb->instructions().size() -
+ num_original_instr;
+ reference_free_labels.insert(std::make_pair(label_instr_index,
+ std::make_pair(chunk_bb_id, chunk_index)));
+
+ assm.push(block_graph::Immediate(chunk_block, chunk_offset));
+ }
+
+
+ assm.push(block_graph::Immediate(0, assm::kSize32Bit));
+ assm.call(block_graph::Immediate(this->xhash_block_, 0));
+ uint32_t no_pushed_words = 3 * num_chunks + 1;
+ assm.add(assm::esp, block_graph::Immediate(no_pushed_words * 4));
+ assm.push(assm::eax);
+ //test
+
+ //Insert label at the beginning of the block
+ inst_iter = bb->instructions().begin();
+ label = BlockGraph::Label(std::to_string(bb_id),
+ BlockGraph::CODE_LABEL);
+ inst_iter->set_label(label);
+
+
+ uint32_t num_added_chunk_labels = 0;
+ uint32_t label_index = 0;
+ uint32_t new_size = 0;
+ uint32_t num_added_instr = bb->instructions().size() - num_original_instr;
+ for (uint32_t instruction_index = 0;
+ inst_iter != bb->instructions().end() &&
+ instruction_index < num_added_instr;
+ ++instruction_index, ++inst_iter){
+ new_size += inst_iter->size();
+ auto label_it = reference_free_labels.find(label_index++);
+ //add reference free labels
+ if (label_it != reference_free_labels.end()){
+ char *buffer = new char[50];
+ uint64_t chunk_bb_id = std::get <0>(label_it->second);
+ uint32_t chunk_index = std::get<1>(label_it->second);
+ sprintf_s(buffer, 50, "nrc %llu %lu", chunk_bb_id,
+ chunk_index);
+ label = BlockGraph::Label(buffer, BlockGraph::CODE_LABEL);
+ delete[] buffer;
+ inst_iter->set_label(label);
+ ++num_added_chunk_labels;
+ ++num_chunk_reference_labels;
+ }
+ } //end for
+
+
+ //make sure all chunk block references are set
+ DCHECK_EQ(num_added_chunk_labels, static_cast<uint32_t>(chunk_set.size()));
+
+
+ //update size
+ uint32_t old_size = (*this->basic_block_sizes_)[bb_id];
+ (*this->basic_block_sizes_)[bb_id] = old_size + new_size;
+#if defined COMPUTE_CHECKER_SIZE
+ total_checker_size += old_size + new_size;
+#endif
+ return true;
+}
+void GetSizeTokenFromlabel(const std::string label,
+ uint64_t *checkee_id,
+ uint64_t *bb_id){
+ //split the string
+ std::istringstream iss(label);
+ std::vector<std::string> tokens;
+ copy(std::istream_iterator<std::string>(iss),
+ std::istream_iterator<std::string>(),
+ back_inserter(tokens));
+ *checkee_id = std::stoull(tokens.at(1));
+ *bb_id = std::stoull(tokens.at(2));
+}
+
+void GetBlockIdTokenFromlabel(const std::string label, uint64_t *checkee_id){
+ //split the string
+ std::istringstream iss(label);
+ std::vector<std::string> tokens;
+ copy(std::istream_iterator<std::string>(iss),
+ std::istream_iterator<std::string>(),
+ back_inserter(tokens));
+ *checkee_id = std::stoull(tokens.at(1));
+}
+
+//keep track of chunk indexes to update xor hash after size changes
+uint32_t last_visited_chunk_index = 0;
+uint64_t last_visited_chunk_bb_id = 0;
+
+bool IntegrityCheckTransform::PatchBlockReferencesAndSizes(
+ BasicCodeBlock* bb,
+ BasicBlockSubGraph* subgraph,
+ BlockGraph *block_graph){
+ bool found = false;
+
+ auto inst_iter = bb->instructions().begin();
+ if (inst_iter == bb->instructions().end()){
+ return true;
+ }
+
+ BlockGraph::Label label(inst_iter->label());
+ uint64_t block_id = GetBasicBlockIdByLabel(label, this->id_to_label_);
+
+ std::string sizeLabel = "size ";
+ std::string blockLabel = "block";
+ std::string chunk_blocklabel = "nrc";
+ std::string chunk_pointerlabel = "n ";
+ std::string chunk_no_reference = "ref";
+ std::string block_id_label = std::to_string(block_id);
+ auto end_block = bb->instructions().end();
+ for (; inst_iter != end_block; ++inst_iter)
+ {
+ if (!inst_iter->has_label()) continue;
+
+
+ if (inst_iter->label().name()
+ .compare(0, chunk_pointerlabel.length(), chunk_pointerlabel) == 0){
+ // update last visited chunk index
+ GetChunkTokensFromlabel(inst_iter->label().name(),
+ &last_visited_chunk_bb_id,
+ &last_visited_chunk_index);
+ } else if (inst_iter->label().name()
+ .compare(0, block_id_label.length(), block_id_label) == 0){
+ last_visited_chunk_bb_id = block_id;
+ last_visited_chunk_index = 0;
+#pragma region patch_size
+ } else if (inst_iter->label().name()
+ .compare(0, sizeLabel.length(), sizeLabel) == 0){
+ //extract block id for size retrieval
+ uint64_t checkee_id = 0;
+ uint64_t bb_id = 0;
+ GetSizeTokenFromlabel(inst_iter->label().name(), &checkee_id, &bb_id);
+ //modify bytes
+ ++num_size_reference_patched_labels;
+ auto old_data = inst_iter->GetMutableData();
+ DCHECK(old_data[0] == 0x68);
+ //if the block is checker then the new size must be bigger than the
+ //old one
+ uint32_t old_size = 0;
+ for (int j = 0; j < sizeof(uint32_t) && old_data[j] != NULL; j++)
+ {
+ old_size |= old_data[j + 1] << j * 8;
+ }
+ uint8_t* new_data = new uint8_t[inst_iter->size()];
+ new_data[0] = 0x68;
+ uint32_t new_size = (*this->basic_block_sizes_)[checkee_id];
+ for (int k = 0; k < sizeof(uint32_t); k++){
+ uint8_t value = (new_size >> k * 8) & 0xFF;
+ new_data[k + 1] = value;
+ }
+
+ //if the block is checker then the new size must be bigger than the
+ //old one
+ if ((*checker_to_checkee_map_)[checkee_id].size() > 0){
+ DCHECK_GE(new_size, old_size);
+ }
+
+ if (*perform_chunk_checks_) {
+ clock_t begin = clock();
+ //we have to recompute chunk that inlcudes this size
+ this->RecomputeXorChunks(bb_id, old_data, new_data,
+ last_visited_chunk_bb_id,
+ last_visited_chunk_index);
+ clock_t end = clock();
+ elapsed_secs_in_patching_chunks += double(end - begin) /
+ CLOCKS_PER_SEC;
+ }
+ for (uint8_t j = 0; j < inst_iter->size(); j++)
+ old_data[j] = new_data[j];
+
+ //prevent multiple patching
+ inst_iter->set_label(block_graph::BlockGraph::Label());
+ found++;
+ delete[] new_data;
+#pragma endregion
+ } else if (inst_iter->label().name() //patch block
+ .compare(0, blockLabel.length(), blockLabel) == 0){
+ //extract block id for offset patching
+ uint64_t checkee_id = 0;
+ GetBlockIdTokenFromlabel(inst_iter->label().name(), &checkee_id);
+#pragma region patch_block
+ auto label_itr = label_name_to_block_->find(std::to_string(checkee_id));
+ DCHECK(label_itr != label_name_to_block_->end());
+ PatchBlockReference(inst_iter, label_itr->second.first,
+ label_itr->second.second);
+#pragma endregion
+ } else if (inst_iter->label().name().compare(0, chunk_blocklabel.size(),
+ chunk_blocklabel) == 0){
+
+ uint64_t checkee_id_for_patch = 0;
+ int checkee_index_for_patch;
+ SplitChunkReferencelabels(inst_iter->label().name(),
+ &checkee_id_for_patch,
+ &checkee_index_for_patch);
+ CHECK(checkee_id_for_patch != 0);
+
+ num_chunk_reference_patched_labels++;
+
+ //find the offset of the reference free chunk within the checkee
+ char *chunk_label = MakeChunkLabel(checkee_id_for_patch,
+ checkee_index_for_patch);
+ auto label_to_block_it = label_name_to_block_->find(chunk_label);
+ delete[] chunk_label;
+ CHECK(label_to_block_it != label_name_to_block_->end());
+#pragma region patch_chunk_offset
+ //update instruction reference to the retrieved reference free
+ //offset (label_to_block_it.second is a pair of <block,offset>)
+ auto reference_free_block = label_to_block_it->second.first;
+ uint32_t new_bb_ref_offset = label_to_block_it->second.second;
+
+ PatchBlockReference(inst_iter, reference_free_block, new_bb_ref_offset);
+
+#pragma endregion
+
+
+ } else if (*perform_chunk_checks_ &&
+ inst_iter->label().name().compare(0, chunk_no_reference.size(),
+ chunk_no_reference) == 0){
+ //patch number of chunks per block
+ uint64_t bb_id = 0;
+ GetBlockIdTokenFromlabel(inst_iter->label().name(), &bb_id);
+ //modify bytes
+ ++num_no_chunk_patched_labels;
+ auto old_data = inst_iter->GetMutableData();
+ DCHECK(old_data[0] == 0x68);
+
+ uint8_t* new_data = new uint8_t[inst_iter->size()];
+ new_data[0] = 0x68;
+ uint32_t old_size = 0;
+ for (int j = 0; j < sizeof(uint32_t) && old_data[j] != NULL; j++)
+ {
+ old_size |= old_data[j + 1] << j * 8;
+ }
+ uint32_t new_size = old_size + this->num_chunks_per_block;
+ for (int k = 0; k < sizeof(uint32_t); k++){
+ uint8_t value = (new_size >> k * 8) & 0xFF;
+ new_data[k + 1] = value;
+ }
+
+ //we have to recompute the chunk that includes this instruction(if any)
+ this->RecomputeXorChunks(bb_id, old_data, new_data,
+ last_visited_chunk_bb_id,
+ last_visited_chunk_index);
+
+ for (uint8_t j = 0; j < inst_iter->size(); j++)
+ old_data[j] = new_data[j];
+
+ //prevent multiple patching
+ inst_iter->set_label(block_graph::BlockGraph::Label());
+ found++;
+ delete[] new_data;
+ }
+ } // end for
+
+ return true;
+}
+
+bool IntegrityCheckTransform::RecomputeXorChunks(
+ const uint64_t bb_id, const uint8_t old_size[],
+ const uint8_t new_size[], const uint64_t chunk_bb_id,
+ const uint32_t chunk_index){
+
+ DCHECK_EQ(bb_id, chunk_bb_id);
+
+ uint32_t vector_index =
+ (*ic_block_chunk_index_map_)[GetChunkUniqueKey(chunk_bb_id,chunk_index)];
+
+ DCHECK_GE(vector_index, static_cast<uint32_t>(0));
+ DCHECK_LT(vector_index, ic_block_reference_free_chunks->size());
+
+ auto chunk = (*ic_block_reference_free_chunks)[vector_index];
+ DCHECK(chunk.block_id_ == chunk_bb_id && chunk.chunk_index_ == chunk_index);
+ ////make sure we found the right chunk
+ DCHECK(sizeof(old_size) == sizeof(new_size));
+
+ uint8_t new_hash = chunk.hash_;
+ for (uint32_t i = 0; i < sizeof(old_size); i++)
+ {
+ //cancel out previous value
+ new_hash ^= old_size[i];
+ //compute new hash
+ new_hash ^= new_size[i];
+ }
+
+ chunk.hash_ = new_hash;
+ (*ic_block_reference_free_chunks)[vector_index] = chunk;
+ return true;
+}
+
+bool IsSize(BasicBlock::Instructions::iterator instruction_itr){
+ std::string size_label = "size";
+ if (instruction_itr->has_label() &&
+ instruction_itr->label().name()
+ .compare(0, size_label.length(), size_label) == 0){
+ return true;
+ }
+ return false;
+}
+bool IsPivot(BasicBlock::Instructions::iterator instruction_itr){
+ std::string pivot_label = "Pivot:";
+ if (instruction_itr->has_label() &&
+ instruction_itr->label().name()
+ .compare(0, pivot_label.length(), pivot_label) == 0){
+ return true;
+ }
+ return false;
+}
+
+bool HasAbsoluteReferences(BasicBlock::Instructions::iterator instruction_itr){
+ if (instruction_itr->references().size() > 0){
+ auto ref_it = instruction_itr->references().begin();
+ for (; ref_it != instruction_itr->references().end(); ++ref_it){
+ if (ref_it->second.reference_type() ==
+ block_graph::BlockGraph::ReferenceType::ABSOLUTE_REF)
+ return true;
+ }
+ }
+ return false;
+}
+
+void
+IntegrityCheckTransform::AddChunkIntoIndexMap(const uint64_t bb_id,
+ const uint32_t chunk_index,
+ const uint32_t vector_index){
+ auto unique_chunk_key = GetChunkUniqueKey(bb_id,chunk_index);
+ //make sure the key is really unique!
+ DCHECK(ic_block_chunk_index_map_->find(unique_chunk_key) ==
+ ic_block_chunk_index_map_->end());
+ (*ic_block_chunk_index_map_)[unique_chunk_key] = vector_index;
+}
+
+void IntegrityCheckTransform::ComputeChunks(BasicCodeBlock* bb){
+ auto inst_iter = bb->instructions().begin();
+ if (inst_iter == bb->instructions().end())
+ return;
+
+ BlockGraph::Label label(inst_iter->label());
+ uint64_t bb_id = GetBasicBlockIdByLabel(label, this->id_to_label_);
+ if (bb_id == -1)
+ return;
+ std::map<uint64_t, int> checkee_list =
+ (*this->checker_to_checkee_map_)[bb_id];
+
+ if (checkee_list.size() < 1)
+ return;
+
+ //uint32_t reference_free_start_offset = 0;
+ uint32_t reference_free_size = 0;
+ uint8_t reference_free_hash = 0;
+ uint32_t reference_free_index = 0;
+ uint16_t size_in_bytes = 0;
+ uint8_t num_found_pivots = 0;
+ uint32_t current_inst_size = 0;
+ std::string bb_id_label = std::to_string(bb_id);
+ bool has_references=false;
+ bool has_abs_references = false;
+ bool is_pivot=false;
+ // Process all instructions in BB
+ for (; inst_iter != bb->instructions().end(); ++inst_iter) {
+
+ current_inst_size = (*inst_iter).size();
+ size_in_bytes += current_inst_size;
+ const uint8_t *b2 = (*inst_iter).data();
+
+ uint8_t instruction_hash = 0;
+ for (uint32_t i = 0; i < current_inst_size; ++i){
+ instruction_hash ^= (*b2);
+ b2++;
+ }
+ if (IsPivot(inst_iter)){
+ ++num_found_pivots;
+ }
+ has_abs_references = HasAbsoluteReferences(inst_iter);
+ has_references = (inst_iter->references().size() > 0);
+
+ is_pivot = IsPivot(inst_iter);
+ if (!has_references && !is_pivot){
+ //we cannot place two labels on the same instruction, so if the beginning
+ //of the chunk has a label we skip it.
+ //In order to keep the first instruction of the block in a chunk without
+ //changing its label, we accept the block id label as a finger for the
+ //beginning of the chunk.
+ if (reference_free_size != 0 || !inst_iter->has_label()
+ || inst_iter->label().name().compare(bb_id_label)==0){
+ //this is the first instruction in the chunk where we place our label
+ //we don't need to put label at the first instruction, because it has
+ //block id label, first instruction label is detected when reference
+ //free index equals zero
+ if (reference_free_size == 0 && reference_free_index!=0){
+ char *buffer = MakeChunkLabel(bb_id, reference_free_index);
+ auto label = BlockGraph::Label(buffer, BlockGraph::CODE_LABEL);
+ delete[] buffer;
+ DCHECK(!inst_iter->has_label());
+ inst_iter->set_label(label);
+ }
+ //keep counting
+ reference_free_size += current_inst_size;
+ reference_free_hash ^= instruction_hash;
+ }
+ } else if (reference_free_size > 0){
+ //add offset and size of the reference free chunk
+ ic_block_reference_free_chunks->push_back(
+ ChunkInfo(bb_id, reference_free_size, reference_free_hash,
+ reference_free_index,
+ has_abs_references?current_inst_size:0));
+ AddChunkIntoIndexMap(bb_id, reference_free_index++,
+ ic_block_reference_free_chunks->size() - 1);
+ //once we add the chunk, we reset the size
+ reference_free_size = 0;
+ reference_free_hash = 0;
+ }
+ } //end for
+
+ //the last chunk of the instructions need to be added (if any)
+ if (reference_free_size > 0) {
+ //add offset and size of the reference free chunk
+ ic_block_reference_free_chunks->push_back(
+ ChunkInfo(bb_id, reference_free_size, reference_free_hash,
+ reference_free_index, 0));
+ AddChunkIntoIndexMap(bb_id, reference_free_index,
+ ic_block_reference_free_chunks->size() - 1);
+ //once we add the chunk, we reset the size
+ reference_free_size = reference_free_hash = 0;
+ }
+
+ //Exactly one pivot must be in each IC block
+ DCHECK_EQ(num_found_pivots, 1);
+}
+
+uint8_t IntegrityCheckTransform::PrecomputeHash(
+ BasicCodeBlock* bb,
+ std::list<uint32_t> *offset_sizes,
+ BasicBlockSubGraph* subgraph) {
+ DCHECK_NE(reinterpret_cast<BasicCodeBlock*>(NULL), bb);
+
+ if (bb->instructions().size() <= 0)
+ return 0;
+
+ uint16_t offset_in_bytes = 0;
+ uint16_t size_in_bytes = 0;
+ uint64_t bb_address = GetBasicBlockId(bb, subgraph);
+
+ // Match and rewrite based on patterns.
+ auto inst_iter = bb->instructions().begin();
+ size_in_bytes = 0;
+ uint8_t partition_key = 0;
+ BlockGraph::Label label;
+
+ label = BlockGraph::Label(std::to_string(bb_address),
+ BlockGraph::CODE_LABEL);
+ inst_iter->set_label(label);
+
+ (*this->id_to_label_)[bb_address] = label;
+ fprintf(this->prefile_, "\n\n%llx\n", std::stoull(label.name()));
+
+ // Process all instructions in BB
+ for (; inst_iter != bb->instructions().end(); ++inst_iter) {
+ uint32_t size = (*inst_iter).size();
+ uint8_t nr_refs_in_key = partition_key;
+
+ if (PopulatePartitionKey((*inst_iter), &partition_key)) {
+ this->basic_block_has_ref_[bb_address] = true;
+
+ int nr_added = partition_key - nr_refs_in_key;
+ if (nr_added == 1) {
+ uint64_t label_nr = bb_address + ((uint64_t)size_in_bytes << 32);
+ label = BlockGraph::Label(std::to_string(label_nr),
+ BlockGraph::CODE_LABEL);
+
+ if (inst_iter->has_label()) {
+ BlockGraph::Label existing_label = inst_iter->label();
+ } else {
+ inst_iter->set_label(label);
+ }
+ }
+ }
+
+ size_in_bytes += size;
+ } // end for
+
+ // put the last bytes in the basic block in the list of precomputed hashes
+ if ((size_in_bytes > 0)) { // don't put chunks of 0 size on stack
+ uint32_t offset_size = (offset_in_bytes << 16) | size_in_bytes;
+ offset_sizes->push_front(offset_size);
+ }
+
+ // populate precomputed hashes and bb-sizes
+ if (partition_key > 0) {
+ std::set<uint64_t> v = this->partition_map_[partition_key];
+ v.insert(bb_address);
+ this->partition_map_[partition_key] = v;
+ // save precomputed hash
+ (*this->precomputed_hashes_)[bb_address] = 0;
+ (*this->basic_block_sizes_)[bb_address] = size_in_bytes;
+
+ } else if (size_in_bytes > 0) {
+ std::set<uint64_t> v = this->partition_map_[0];
+ v.insert(bb_address);
+ this->partition_map_[0] = v;
+ // save precomputed hash
+ (*this->precomputed_hashes_)[bb_address] = 0;
+ (*this->basic_block_sizes_)[bb_address] = size_in_bytes;
+ }
+
+ bb_address += ((uint64_t)offset_in_bytes << 32);
+ return 1;
+}
+
+bool IntegrityCheckTransform::TransformBasicBlockSubGraph(
+ BlockGraph* bgraph,
+ BasicBlockSubGraph* subgraph,
+ IntegrityCheckTransform::ProcessingType step) {
+ DCHECK_NE(reinterpret_cast<BlockGraph*>(NULL), bgraph);
+
+ if (step == IntegrityCheckTransform::ADD_HASH_AND_RESPONSE) {
+ this->hash_block_ = AddHashFunction(bgraph);
+ this->xhash_block_ = AddXorHashFunction(bgraph);
+ this->response_block_ = AddResponseFunction(bgraph);
+
+ return (this->hash_block_ && this->xhash_block_ && this->response_block_);
+
+ } else {
+ DCHECK_NE(reinterpret_cast<BasicBlockSubGraph*>(NULL), subgraph);
+ std::list<uint32_t> instr_sizes;
+ uint8_t min_instructions = 0;
+ BasicBlockSubGraph::BBCollection& basic_blocks =
+ subgraph->basic_blocks(); // set of BB to protect
+
+ // Iterate over every basic block and insert integrity-checks
+ for (auto it = basic_blocks.begin(); it != basic_blocks.end(); ++it) {
+ BasicCodeBlock* bb = BasicCodeBlock::Cast(*it);
+ if ((bb == NULL) || (bb->instructions().size() < min_instructions))
+ continue;
+ switch (step) {
+ case IntegrityCheckTransform::PRECOMPUTE_HASHES: {
+ PrecomputeHash(bb, &instr_sizes, subgraph);
+ break;
+ }
+ case IntegrityCheckTransform::INSERT_CHECKS: {
+ AddIntegrityCheckCode(bb, subgraph, bgraph);
+ break;
+ }
+ case IntegrityCheckTransform::COMPUTE_CHUNKS:{
+ ComputeChunks(bb);
+ break;
+ }
+ case IntegrityCheckTransform::INSERT_CHUNK_CHECKS:{
+ AddChunkIntegrityCheckCode(bb, subgraph, bgraph);
+ break;
+ }
+ case IntegrityCheckTransform::PATCH_REFERENCES_SIZES: {
+ PatchBlockReferencesAndSizes(bb, subgraph, bgraph);
+ break;
+ }
+ default:
+ DbgRaiseAssertionFailure();
+ break;
+ }
+ } // end for
+ } // end else
+ return true;
+}
+
+uint8_t IntegrityCheckTransform::GetPartitionKey(uint64_t bb_id) {
+ auto it = this->partition_map_.begin();
+ for (; it != this->partition_map_.end(); ++it) {
+ if (it->second.find(bb_id) != it->second.end())
+ return it->first;
+ }
+
+ return 0;
+}
+
+void IntegrityCheckTransform::AddIntegrityCheckCode(
+ BasicCodeBlock* bb,
+ BasicBlockSubGraph* subgraph,
+ BlockGraph *block_graph) {
+ auto inst_iter = bb->instructions().begin();
+ if (inst_iter == bb->instructions().end())
+ return;
+
+ BlockGraph::Label label(inst_iter->label());
+ uint64_t bb_id = GetBasicBlockIdByLabel(label, this->id_to_label_);
+
+ if (bb_id == -1)
+ return;
+
+ uint8_t hash = 0;
+ std::map<uint64_t, int> checkee_list =
+ (*this->checker_to_checkee_map_)[bb_id];
+
+ if (checkee_list.size() < 1)
+ return;
+
+ // Count number of absolute references in basic block
+ uint8_t no_abs_references = 0;
+ // Count number of instructions in basic block
+ uint32_t no_bb_instructions = bb->instructions().size();
+ uint32_t no_orig_bb_instructions = bb->instructions().size();
+ std::map<uint32_t, uint64_t> checkee_label_map;
+ if (this->insert_file_ != NULL){
+ fprintf(this->insert_file_, "%s,", label.name().c_str());
+ }
+ // Remove old label from the beginning of the original code
+ inst_iter->set_label(BlockGraph::Label());
+
+ block_graph::BasicBlockAssembler assm(inst_iter,
+ &bb->instructions());
+
+ //in case we add chunk checker these pushes will be added by the chunk
+ //checker
+ if (!*perform_chunk_checks_){
+ assm.push(assm::eax);
+ assm.push(assm::ebx);
+ assm.push(assm::ecx);
+ assm.push(assm::edx);
+ }
+
+ assm.lea(assm::ecx, block_graph::Operand(
+ block_graph::Displacement(checkee_list.size(),
+ assm::ValueSize::kSize32Bit)));
+
+ uint32_t *checkee_size_index = new uint32_t[checkee_list.size()];
+ uint32_t *checkee_reference_index = new uint32_t[checkee_list.size()];
+
+ uint32_t pivot_instruction_index = 0;
+ uint32_t sub_instruction_index = 0;
+ uint32_t k = 0;
+ uint32_t reference_index = 0;
+ auto checkee_it = checkee_list.begin();
+ int last_coefficient = 0;
+ for (; checkee_it != checkee_list.end(); ++checkee_it) {
+
+ if (last_coefficient == checkee_it->second){
+ LOG(INFO) << "found equal coeffs";
+ }
+ last_coefficient = checkee_it->second;
+ assm.push(block_graph::Immediate(checkee_it->second,
+ assm::ValueSize::kSize32Bit));
+
+ // push the number of checkees of the checkee
+ uint32_t nr_of_checkees =
+ (*this->checker_to_checkee_map_)[checkee_it->first].size();
+ no_abs_references += nr_of_checkees + GetPartitionKey(bb_id) +
+ this->num_chunks_per_block;
+ //Here still we don't know how many chunks this checker is going to check
+ //depending on the coverage config and total number of discovered chunks
+ //this number should be added to the nr_of_chechees
+ //nr_of_checkees += this->num_chunks_per_block;
+ checkee_reference_index[reference_index++] =
+ bb->instructions().size() - no_orig_bb_instructions;
+ assm.push(block_graph::Immediate(nr_of_checkees,
+ assm::ValueSize::kSize32Bit));
+
+ // Count the number of instructions added so far into the basic block
+ // This information is used to set a label on the following push instr
+ checkee_size_index[k++] = bb->instructions().size() - no_bb_instructions;
+ no_bb_instructions = bb->instructions().size();
+
+ // push the size of the checkee
+ uint32_t size_of_checkee = (*this->basic_block_sizes_)[checkee_it->first];
+ assm.push(block_graph::Immediate(size_of_checkee,
+ assm::ValueSize::kSize32Bit));
+
+ BlockGraph::Label checkee_label =
+ (*this->id_to_label_)[checkee_it->first];
+ std::pair<BlockGraph::Block*, uint32_t> block_offset_pair =
+ (*this->label_name_to_block_)[checkee_label.name()];
+ BlockGraph::Block* checkee_block = block_offset_pair.first;
+ uint32_t checkee_offset = block_offset_pair.second;
+
+ DCHECK(checkee_block != NULL);
+
+ checkee_label_map.insert(std::make_pair(
+ bb->instructions().size() - no_orig_bb_instructions, checkee_it->first));
+ if (this->insert_file_ != NULL){
+ fprintf(this->insert_file_, "%s,", checkee_label.name().c_str());
+ }
+ if (checkee_block->id() != subgraph->original_block()->id()) {
+ assm.push(block_graph::Immediate(checkee_block, checkee_offset));
+ } else { // checkee is in the same subgraph as checker
+ BasicBlock *checkee_bb =
+ GetBasicBlockAtOffset(subgraph, checkee_offset);
+ DCHECK(checkee_bb != NULL);
+ assm.push(block_graph::Immediate(checkee_bb));
+ }
+
+ hash += (*this->precomputed_hashes_)[checkee_it->first] *
+ checkee_it->second;
+ }
+ if (this->insert_file_ != NULL) {
+ fprintf(this->insert_file_, "\n");
+ }
+
+ // 2 stack slots holding accumulator for hash and hash of return address
+ assm.sub(assm::esp, block_graph::Immediate(0x8));
+
+ // get size in bytes of the code inserted so far
+ uint32_t call_offset = 0;
+ uint32_t no_added_instructions = bb->instructions().size() -
+ no_orig_bb_instructions;
+ auto inst_iter3 = bb->instructions().begin();
+ for (uint32_t k = 0; (inst_iter3 != bb->instructions().end()) &&
+ (k < no_added_instructions); ++inst_iter3, ++k) {
+ call_offset += inst_iter3->size();
+ }
+ this->basic_block_hash_call_offset_[bb_id] = call_offset;
+
+ assm.call(block_graph::Immediate(this->hash_block_, 0));
+ //keep the index of the pivot byte/instruction
+ pivot_instruction_index =
+ bb->instructions().size() - no_orig_bb_instructions;
+
+ assm.data((uint8_t)0);
+ //let the result be in the stack so later we can retrieve it
+ uint32_t no_pushed_words = 4 * checkee_list.size() + 2;
+ assm.add(assm::esp, block_graph::Immediate(no_pushed_words * 4));
+ //checksum from the xor function must be added to the add checksum result
+ if (*perform_chunk_checks_) {
+ assm.pop(assm::ebx);
+ assm.add(assm::al, assm::bl);
+ } else {
+ // If we are not checking chunks we don't need to pop the runtime computed
+ // hash of the chunks. However, Syzygy loses the label added to the sub
+ // instruction (next instruction) because it tries to disassemble the data
+ // byte after the call to the hash function, which leads to different
+ // instructions than during execution. Label will be misaligned. Adding
+ // these instructions prevents runtime assertion check error about lost
+ // labels.
+ assm.push(block_graph::Immediate(0,assm::ValueSize::kSize32Bit));
+ assm.pop(assm::ebx);
+ assm.add(assm::al, assm::bl);
+ }
+ sub_instruction_index = bb->instructions().size() - no_orig_bb_instructions;
+ assm.sub(assm::al, block_graph::Immediate(hash, assm::ValueSize::kSize8Bit));
+ assm.data((uint8_t)0x66); // CBW
+ assm.data((uint8_t)0x98);
+ assm.xor(assm::al, assm::ah);
+ assm.sub(assm::al, assm::ah);
+ assm.sub(assm::al, block_graph::Immediate(no_abs_references,
+ assm::ValueSize::kSize8Bit));
+ assm.j(assm::ConditionCode::kAbove,
+ block_graph::Immediate(this->response_block_, 0));
+
+ assm.pop(assm::edx);
+ assm.pop(assm::ecx);
+ assm.pop(assm::ebx);
+ assm.pop(assm::eax);
+
+ // Add label to begining of integrity check
+ label = BlockGraph::Label(std::to_string(bb_id),
+ BlockGraph::CODE_LABEL);
+ inst_iter = bb->instructions().begin();
+ inst_iter->set_label(label);
+
+ (*this->id_to_label_)[bb_id] = label;
+ uint32_t num_no_chunk_added = 0;
+ uint32_t ref_instruction_index = 0;
+ // Update the size of the basic block to include integrity check code
+ // and add the sub instruction label
+ uint32_t new_size = 0;
+ for (uint32_t s = 0; inst_iter != bb->instructions().end(); ++inst_iter, s++)
+ {
+ new_size += inst_iter->size();
+ auto checkee_label_iter = checkee_label_map.find(s);
+ if (checkee_label_iter != checkee_label_map.end()){
+ char *buffer = new char[50];
+ sprintf_s(buffer, 50, "block %llu %llu",
+ checkee_label_iter->second,bb_id);
+ //LOG(INFO) << " Assigned pivot label: " << buffer;
+ label = BlockGraph::Label(base::StringPiece(buffer),
+ BlockGraph::CODE_LABEL);
+ inst_iter->set_label(label);
+ delete[] buffer;
+ } else if (s == pivot_instruction_index) { // add the pivot label
+ char *buffer = new char[50];
+ sprintf_s(buffer, 50, "Pivot:%llu", bb_id);
+ //LOG(INFO) << " Assigned pivot label: " << buffer;
+ label = BlockGraph::Label(base::StringPiece(buffer),
+ BlockGraph::CODE_LABEL);
+ inst_iter->set_label(label);
+ delete[] buffer;
+ } else if (s == sub_instruction_index) { // add the pivot label
+ char buffer[50];
+ sprintf_s(buffer, 50, "sub %llu", bb_id);
+ //LOG(INFO) << " Assigned pivot label: " << buffer;
+ label = BlockGraph::Label(buffer, BlockGraph::CODE_LABEL);
+ inst_iter->set_label(label);
+ }
+ else if (*perform_chunk_checks_ &&
+ ref_instruction_index < checkee_list.size() &&
+ s == checkee_reference_index[ref_instruction_index]) {
+ // add the no_checkee label
+ ++ref_instruction_index;
+ char *buffer = new char[50];
+ sprintf_s(buffer, 50, "ref %llu", bb_id);
+ //LOG(INFO) << " Assigned num chunk label: " << s <<" "<< buffer;
+ label = BlockGraph::Label(base::StringPiece(buffer),
+ BlockGraph::CODE_LABEL);
+ inst_iter->set_label(label);
+ num_no_chunk_labels++;
+ num_no_chunk_added++;
+ delete[] buffer;
+ }
+ }
+ uint32_t old_size = (*this->basic_block_sizes_)[bb_id];
+ DCHECK_GT(new_size, static_cast<uint32_t>(0x49));
+ DCHECK(old_size < new_size);
+ if (*perform_chunk_checks_){
+ CHECK_EQ(num_no_chunk_added, checkee_list.size());
+ }
+ (*this->basic_block_sizes_)[bb_id] = new_size;// - call_offset;
+
+ //Set iterator to the beginning of the list
+ inst_iter = bb->instructions().begin();
+
+
+ checkee_it = checkee_list.begin();
+ // Add labels to instructions which push basic block size
+ for (uint32_t k = 0; k < checkee_list.size(); ++k) {
+ for (uint32_t j = 0; j < checkee_size_index[k]; ++j) {
+ inst_iter++;
+ }
+ char *buffer=new char[50];
+ sprintf_s(buffer, 50, "size %llu %llu", checkee_it->first, bb_id);
+ checkee_it++;
+ label = BlockGraph::Label(base::StringPiece(buffer),
+ BlockGraph::CODE_LABEL);
+ inst_iter->set_label(label);
+ delete[] buffer;
+ num_size_reference_labels++;
+ }
+
+ delete[] checkee_size_index;
+ delete[] checkee_reference_index;
+ return; //remove this when you have inner BB references
+}
+uint32_t num_protecting_blocks = 0;
+
+bool IntegrityCheckTransform::ProcessAllBlocks(
+ const TransformPolicyInterface* policy,
+ BlockGraph* block_graph,
+ IntegrityCheckTransform::ProcessingType step) {
+ BlockOrdering order;
+ FlattenCallGraphPostOrder(block_graph, &order);
+#if defined PRINT_BLOCK_NAMES
+ std::ofstream blocknames_file;
+ blocknames_file.open("block_names.csv");
+#endif
+ auto block_iter = order.begin();
+ for (; block_iter != order.end(); ++block_iter) {
+ BlockGraph::Block* block = *block_iter;
+#if defined PRINT_BLOCK_NAMES
+ if (!policy->BlockIsSafeToBasicBlockDecompose(block))
+ continue;
+ blocknames_file <<block->name()<<",\n";
+ continue;
+#endif
+ if (!ShouldProcessBlock(block, this->target_names_))
+ continue;
+ // Use the decomposition policy to skip blocks that aren't eligible for
+ // basic-block decomposition.
+ if (!policy->BlockIsSafeToBasicBlockDecompose(block))
+ continue;
+
+ // Decompose block to basic blocks.
+ BasicBlockSubGraph *subgraph = new BasicBlockSubGraph();
+ BasicBlockDecomposer bb_decomposer(block, subgraph);
+ if (!bb_decomposer.Decompose())
+ return false;
+
+ if (!TransformBasicBlockSubGraph(
+ block_graph, subgraph, step)) {
+ return false;
+ }
+
+ // Update the block-graph post transform.
+ BlockBuilder builder(block_graph);
+ if (!builder.Merge(subgraph)) {
+ return false;
+ }
+ ++num_protecting_blocks;
+
+ const BlockVector& blocks = builder.new_blocks();
+ auto new_block = blocks.begin();
+ for (; new_block != blocks.end(); ++new_block) {
+ // This is needed until the labels refactoring.
+ (*new_block)->set_attribute(BlockGraph::BUILT_BY_SYZYGY);
+
+ if (step == INSERT_CHECKS || INSERT_CHUNK_CHECKS) {
+ UpdateLabelToBlockMap(*new_block);
+ }
+ }
+ }
+#if defined PRINT_BLOCK_NAMES
+ blocknames_file.close();
+ exit(1);
+#endif
+ return true;
+}
+
+uint64_t
+IntegrityCheckTransform::GetChunkOriginalBlockId(const ChunkInfo *chunk){
+ if (chunk->original_block_id_ == 0){
+ bool before_chunk_integrity_code_added = true;
+ char* chunk_label = MakeChunkLabel(chunk->block_id_, chunk->chunk_index_,
+ before_chunk_integrity_code_added);
+ auto chunk_label_it = label_name_to_block_->find(chunk_label);
+ CHECK(chunk_label_it != label_name_to_block_->end());
+ delete[] chunk_label;
+ chunk->original_block_id_ = chunk_label_it->second.first->id();
+ }
+ return chunk->original_block_id_;
+}
+
+std::set<uint32_t> IntegrityCheckTransform:: PickChunks(
+ const std::vector<ChunkInfo> chunks_vector,
+ const std::vector<uint32_t> partition_indexes,
+ const uint32_t num_picks,
+ const uint64_t checker_block_id,
+ const std::vector<uint32_t>::iterator end_chunk_it,
+ std::vector<uint32_t>::iterator last_visited_chunk,
+ std::set<uint32_t> *unused_chunks){
+ std::set<uint32_t> picked_set;
+
+
+ //attempt to pick from unused chunks
+ for (auto unused_chunk_it = unused_chunks->begin();
+ unused_chunk_it != unused_chunks->end() && picked_set.size() < num_picks;){
+ uint64_t chunk_orig_block_id = GetChunkOriginalBlockId(
+ &chunks_vector[*unused_chunk_it]);
+ if (chunk_orig_block_id != checker_block_id){
+ picked_set.insert(*unused_chunk_it);
+ unused_chunks->erase(unused_chunk_it);
+ unused_chunk_it = unused_chunks->begin();
+ }
+ else {
+ ++unused_chunk_it;
+ }
+ }
+
+ //iterate over chunks
+ for (; last_visited_chunk != end_chunk_it && picked_set.size() < num_picks;
+ ++last_visited_chunk){
+ uint64_t chunk_orig_block_id = GetChunkOriginalBlockId(
+ &chunks_vector[*last_visited_chunk]);
+ if (chunk_orig_block_id != checker_block_id){
+ picked_set.insert(*last_visited_chunk);
+ } else {
+ unused_chunks->insert(*last_visited_chunk);
+ }
+ }//end for
+
+ // if we don't have enough unique chunk, then we pick from
+ // already visited chunks
+ if (picked_set.size() < num_picks){
+ for (auto index_it = partition_indexes.begin();
+ index_it != partition_indexes.end() && picked_set.size() < num_picks;
+ ++index_it){
+ uint64_t chunk_orig_block_id = GetChunkOriginalBlockId(
+ &chunks_vector[*index_it]);
+ if (chunk_orig_block_id != checker_block_id){
+ picked_set.insert(*index_it);
+ }//end if
+ }//end for
+ }//end if
+
+ DCHECK_EQ(picked_set.size(), num_picks);
+ return picked_set;
+}
+
+std::map<uint64_t, std::set<uint32_t>>
+IntegrityCheckTransform::GenerateChunkCombinations(
+ const std::vector<ChunkInfo> chunks_vector,
+ const float chunk_coverage, const bool enforce_unique_chunks,
+ uint32_t *no_chunks_per_block){
+
+ DCHECK_GT(chunk_coverage, static_cast<float>(0));
+ DCHECK_LE(chunk_coverage, static_cast<float>(10));
+
+
+ std::vector<ChunkInfo> temp_chunk_vector = chunks_vector;
+ std::vector<uint32_t> temp_noref_chunk_vector;
+ std::vector<uint32_t> temp_ref_chunk_vector;
+ int i = 0;
+ //partition chunks based on their next instruction's absolute reference
+ //status
+ for (auto chunk_it = temp_chunk_vector.begin();
+ chunk_it != temp_chunk_vector.end(); ++chunk_it,++i){
+ if (chunk_it->next_instruction_size_ == 0)
+ temp_noref_chunk_vector.push_back(i);
+ else
+ temp_ref_chunk_vector.push_back(i);
+ }
+
+ //shuffle chunks to make sure that checkers check integrity of random blocks
+ auto engine = std::default_random_engine{};
+ std::shuffle(std::begin(temp_noref_chunk_vector),
+ std::end(temp_noref_chunk_vector), engine);
+
+ std::shuffle(std::begin(temp_ref_chunk_vector),
+ std::end(temp_ref_chunk_vector), engine);
+
+ //compute number of chunks according to the input coverage
+ uint32_t total_chunk_checks = chunks_vector.size() * chunk_coverage;
+ uint32_t num_ref_chunks = 0;
+ int32_t num_noref_chunks = 0;
+ //preference is to pick chunks with abs address at the end
+ if (temp_ref_chunk_vector.size() >= total_chunk_checks) {
+ num_ref_chunks = total_chunk_checks;
+ num_noref_chunks = 0;
+ } else if(chunk_coverage <= 1.0f) {
+ num_ref_chunks = temp_ref_chunk_vector.size();
+ num_noref_chunks = total_chunk_checks - num_ref_chunks;
+ } else {
+ num_ref_chunks = std::min(
+ static_cast<uint32_t>(temp_ref_chunk_vector.size()* chunk_coverage),
+ total_chunk_checks);
+ num_noref_chunks = total_chunk_checks - num_ref_chunks;
+ }
+
+ uint32_t no_chunks_per_checker = total_chunk_checks /
+ checker_to_checkee_map_->size();
+
+ //the base address cancelation only works for even number of chunks
+ if (no_chunks_per_checker % 2 != 0){
+ LOG(INFO) << "current coverage does not generate even number of chunks, "
+ << "thus the number of chunks was incremented!";
+ no_chunks_per_checker++;
+ }
+
+ LOG(INFO) << "chunk coverage:" << chunk_coverage;
+ LOG(INFO) << "#all chunks:" << total_chunk_checks;
+ LOG(INFO) << "#chunks per checker:" << no_chunks_per_checker;
+ LOG(INFO) << "#+chunks (with absolute instruction):" << num_ref_chunks;
+ LOG(INFO) << "#^chunks (no absolute instruction):" << num_noref_chunks;
+ *no_chunks_per_block = no_chunks_per_checker;
+
+ DCHECK_GE(no_chunks_per_checker, static_cast<uint32_t>(1));
+
+ auto checker_it = checker_to_checkee_map_->begin();
+ std::set<uint32_t> unused_noref_chunks;
+ std::set<uint32_t> unused_ref_chunks;
+ std::map<uint64_t, std::set<uint32_t>> temp_assignment_map;
+ auto noref_chunk_it = temp_noref_chunk_vector.begin();
+ auto ref_chunk_it = temp_ref_chunk_vector.begin();
+
+ auto noref_chunk_end_it = temp_noref_chunk_vector.end();
+ auto ref_chunk__end_it = temp_ref_chunk_vector.end();
+
+ while (checker_it != checker_to_checkee_map_->end()){
+ std::set<uint32_t> chunks;
+ uint64_t bb_id = checker_it->first;
+ BlockGraph::Label checker_label = (*this->id_to_label_)[bb_id];
+ auto checker_label_it = label_name_to_block_->find(checker_label.name());
+ CHECK(checker_label_it != label_name_to_block_->end());
+ uint64_t checker_block_id = checker_label_it->second.first->id();
+ //first pick from no reference partition
+ if (num_noref_chunks > 0){
+ chunks = PickChunks(chunks_vector, temp_noref_chunk_vector,
+ no_chunks_per_checker, checker_block_id,
+ noref_chunk_end_it, noref_chunk_it,
+ &unused_noref_chunks);
+ num_noref_chunks -= no_chunks_per_checker;
+ } else { // pick the rest of chunks from reference chunks
+ chunks = PickChunks(chunks_vector, temp_ref_chunk_vector,
+ no_chunks_per_checker, checker_block_id,
+ ref_chunk__end_it, ref_chunk_it,
+ &unused_ref_chunks);
+ }
+ temp_assignment_map[bb_id] = chunks;
+ ++checker_it;
+ }
+
+ return temp_assignment_map;
+}
+
+void IntegrityCheckTransform::GenerateBasicBlockCombinations() {
+ int partition_num = 1;
+ int nr_size_one = 0;
+ srand(time(NULL));
+
+ FILE* part_file = NULL;
+ fopen_s(&part_file, "partitions.csv", "w");
+ if (part_file == NULL)
+ LOG(INFO) << "Cannot open partition file";
+
+ auto it_part = this->partition_map_.begin();
+ for (; it_part != this->partition_map_.end(); ++it_part) {
+ LOG(INFO) << "Partition #" << partition_num << " : ";
+ LOG(INFO) << (*it_part).second.size();
+
+ if ((*it_part).second.size() <= 1) {
+ /*
+ std::list<std::set<uint64_t>> checkOrder;
+ checkOrder.push_back((*it_part).second);
+ checkOrder.push_back((*it_part).second);
+ */
+ ++nr_size_one;
+ } else { // there are multiple BBs in this partition
+ PopulateCheckMaps((*it_part).second);
+ }
+
+ ++partition_num;
+ }
+
+ // check if any blocks are not checking anything
+ auto checker_it = id_to_label_->begin();
+ for (; checker_it != id_to_label_->end(); ++checker_it){
+ auto checkee_list = (*checker_to_checkee_map_)[checker_it->first];
+ if (checkee_list.size() == 0) { // then this BB is not checking other BBs.
+ // Find a pair of basic blocks to check.
+ it_part = this->partition_map_.begin();
+ std::map<uint64_t, int> checkee_map;
+ bool found_pair = false;
+
+ for (; it_part != this->partition_map_.end(); ++it_part) {
+ if (it_part->second.size() < 2) // partition is too small
+ continue;
+ // Check if partition has at least 2 BBs that are not in the same block
+ // as the checker
+ uint32_t checker_block = (uint32_t) checker_it->first;
+ std::set<uint64_t> bbs_in_different_block;
+ auto part_block_it = it_part->second.begin();
+
+ for (; part_block_it != it_part->second.end(); ++part_block_it) {
+ if (checker_block != ((uint32_t)*part_block_it))
+ bbs_in_different_block.insert(*part_block_it);
+ }
+ if (bbs_in_different_block.size() > 1) { // use first 2 BBs
+ auto checkee_it = bbs_in_different_block.begin();
+ checkee_map[*checkee_it] = 1;
+ checkee_it++;
+ checkee_map[*checkee_it] = -1;
+ found_pair = true;
+ break;
+ }
+ }
+
+ DCHECK(checkee_map.size() == 2);
+ (*checker_to_checkee_map_)[checker_it->first] = checkee_map;
+ }
+ }
+
+ fclose(part_file);
+
+ LOG(INFO) << "nr_size_one : " << nr_size_one;
+}
+
+bool IntegrityCheckTransform::TransformBlockGraph(
+ const TransformPolicyInterface* policy,
+ BlockGraph* block_graph,
+ BlockGraph::Block* header_block) {
+ fopen_s(&this->pfile_, "integrityChecks.csv", "w");
+ if (this->pfile_ == NULL)
+ LOG(INFO) << "Cannot open graph file";
+
+ if (!TransformBasicBlockSubGraph(
+ block_graph, NULL,
+ IntegrityCheckTransform::ADD_HASH_AND_RESPONSE)) {
+ return false;
+ }
+
+ fopen_s(&this->prefile_, "preChecks.csv", "w");
+ if (this->prefile_ == NULL)
+ LOG(INFO) << "Cannot open graph file";
+
+ num_protecting_blocks = 0;
+ // Compute the hash of all basic blocks in all blocks of the block_graph.
+ // This hash will be hard-coded inside the integrity-check-code inserted in
+ // each basic block. It will be compared with the hash computed at runtime.
+ if (!ProcessAllBlocks(policy, block_graph,
+ IntegrityCheckTransform::PRECOMPUTE_HASHES))
+ return false;
+
+ if(num_protecting_blocks, this->target_names_.size())
+ LOG(INFO) << "Failed to find some targets, protected blocks:"
+ << num_protecting_blocks << " provided:"
+ << this->target_names_.size();
+
+ fclose(this->prefile_);
+
+ GenerateBasicBlockCombinations();
+
+ fclose(this->pfile_);
+
+ int nr_not_checked = 0;
+ int total_number = 0;
+ //Print all nodes not checked by any other nodes
+ auto map_it = this->precomputed_hashes_->begin();
+ for (; map_it != this->precomputed_hashes_->end(); ++map_it) {
+ if (this->is_bb_checked_map_.find((*map_it).first) ==
+ this->is_bb_checked_map_.end()) {
+ //LOG(INFO) << "BB " << (*mapIt).first << " is not checked ";
+ ++nr_not_checked;
+ }
+ ++total_number;
+ }
+
+ int nr_3_combo_found = 0;
+ LOG(INFO) << "Combo 3 Found: " << nr_3_combo_found;
+ LOG(INFO) << "Not Checked: " << nr_not_checked;
+ LOG(INFO) << "Total number:" << total_number;
+
+ fopen_s(&this->insert_file_, "inserted-integrityChecks.csv", "w");
+ if (this->insert_file_ == NULL)
+ LOG(INFO) << "Cannot open graph file";
+
+ GenerateLabelToBlockMap(block_graph);
+
+ // Add the assembly code representing integrity checks in each basic block
+ // that was picked to perform a dynamic check in the combination of basic
+ // blocks (see method GenerateBasicBlockCombinations()).
+ if (!ProcessAllBlocks(policy, block_graph,
+ IntegrityCheckTransform::INSERT_CHECKS))
+ return false;
+
+ fclose(this->insert_file_);
+ LOG(INFO) << "Inserting checks done";
+
+ fopen_s(&this->fix_file_, "fixIntegrityChecks.csv", "w");
+ if (this->fix_file_ == NULL)
+ LOG(INFO) << "Cannot open graph file";
+
+ if (*perform_chunk_checks_){
+ if (!ProcessAllBlocks(policy, block_graph,
+ IntegrityCheckTransform::COMPUTE_CHUNKS))
+ return false;
+ LOG(INFO) << "Computing integrity inter block chunks is done";
+
+ //Require label update
+ GenerateLabelToBlockMap(block_graph);
+
+
+ //shuffle up integrity chunks
+ *ic_chunk_checker_to_checkee_map_ = GenerateChunkCombinations(
+ *ic_block_reference_free_chunks,
+ chunk_checking_coverage,
+ kForceUniqueChunks,
+ &num_chunks_per_block);
+
+ LOG(INFO) << "Shuffling integrity inter block chunks is done";
+
+ if (!ProcessAllBlocks(policy, block_graph, INSERT_CHUNK_CHECKS))
+ return false;
+ LOG(INFO) << "Inserting chunk checks is done";
+ } else {
+ LOG(INFO) << "Xor chunk protection is switched off.";
+ }
+ //Require label update
+ GenerateLabelToBlockMap(block_graph);
+
+ // Patch inter block references that were broken by the insertion of
+ // integrity checks.
+ if (!ProcessAllBlocks(policy, block_graph,
+ IntegrityCheckTransform::PATCH_REFERENCES_SIZES)) {
+ return false;
+ }
+
+ LOG(INFO) << "Patching block references and sizes are done";
+ LOG(INFO) << "Elapsed seconds in patching chunks(due to size changes:"
+ <<elapsed_secs_in_patching_chunks;
+ CHECK_EQ(num_chunk_reference_labels , num_chunk_reference_patched_labels);
+ CHECK_EQ(num_no_chunk_labels, num_no_chunk_patched_labels);
+ CHECK_EQ(num_size_reference_labels, num_size_reference_patched_labels);
+ if (num_size_reference_labels != num_size_reference_patched_labels){
+ LOG(ERROR) << "Some size labels were not patched, total lables:" <<
+ num_size_reference_labels << " patched:"
+ << num_size_reference_patched_labels;
+ }
+
+ //Require label update
+ GenerateLabelToBlockMap(block_graph);
+
+ fclose(this->fix_file_);
+
+ std::map<uint64_t, uint32_t> checkee_count_checker;
+ std::ofstream myfile;
+ myfile.open("graph.csv");
+ auto checker_it = this->checker_to_checkee_map_->begin();
+ for (; checker_it != this->checker_to_checkee_map_->end();
+ ++checker_it) {
+ for (auto checkee_it = checker_it->second.begin();
+ checkee_it != checker_it->second.end();
+ ++checkee_it) {
+ myfile << checker_it->first << "," << checkee_it->first << "\n";
+ ++checkee_count_checker[checkee_it->first];
+ }
+ } //end for
+ myfile.close();
+ myfile.open("notbeingchecked.csv");
+ checker_it = checker_to_checkee_map_->begin();
+ for (; checker_it != this->checker_to_checkee_map_->end();
+ ++checker_it) {
+ if (checkee_count_checker.find(checker_it->first) ==
+ checkee_count_checker.end()) {
+ myfile << checker_it->first << "\n";
+ }
+ }
+ myfile.close();
+#if defined COMPUTE_CHECKER_SIZE
+ myfile.open("checkersize.csv");
+ myfile << "total checker size(byte):"<<total_checker_size;
+ myfile.close();
+#endif
+ myfile.open("chunkinfo.csv");
+ myfile << "total chunks:" << ic_block_reference_free_chunks->size();
+ myfile << "total checked chunks:" << checker_to_checkee_map_->size() *
+ num_chunks_per_block;
+ myfile.close();
+ myfile.open("chunkgraph.csv");
+ for (auto chunk_checker_it = ic_chunk_checker_to_checkee_map_->begin();
+ chunk_checker_it != ic_chunk_checker_to_checkee_map_->end();
+ ++chunk_checker_it) {
+ for (auto chunk_checkee_it = chunk_checker_it->second.begin();
+ chunk_checkee_it != chunk_checker_it->second.end();
+ ++chunk_checkee_it) {
+ myfile << chunk_checker_it->first << "," <<
+ (*ic_block_reference_free_chunks)[*chunk_checkee_it].block_id_
+ <<"\n";
+ }
+ }
+ myfile.close();
+ return true;
+}
+
+IntegrityCheckTransform::~IntegrityCheckTransform() {
+ ic_block_reference_free_chunks->clear();
+ ic_block_chunk_index_map_->clear();
+ ic_chunk_checker_to_checkee_map_->clear();
+ //_CrtDumpMemoryLeaks();
+}
+
+// static vars
+const char IntegrityCheckTransform::kTransformName[] =
+"IntegrityCheckTransform";
+
+}// namespace protect

Powered by Google App Engine
This is Rietveld 408576698