| 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
|
|
|