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

Unified Diff: syzygy/experimental/protect/protect_lib/integrity_check_layout_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_layout_transform.cc
diff --git a/syzygy/experimental/protect/protect_lib/integrity_check_layout_transform.cc b/syzygy/experimental/protect/protect_lib/integrity_check_layout_transform.cc
new file mode 100644
index 0000000000000000000000000000000000000000..9acaee6e79b164cab76eac6d8b84b8c6b420d516
--- /dev/null
+++ b/syzygy/experimental/protect/protect_lib/integrity_check_layout_transform.cc
@@ -0,0 +1,366 @@
+// 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_layout_transform.h"
+
+//TODO: remove this include
+#include <inttypes.h>
+
+#include "syzygy/core/address.h"
+#include "syzygy/experimental/protect/protect_lib/protect_utils.h"
+#include "syzygy/pe/pe_file_writer.h"
+#include "syzygy/pe/pe_utils.h"
+
+namespace protect {
+
+uint8_t IntegrityCheckLayoutTransform::ComputeAggregatedChunksHash(
+ const std::set<uint32_t> chunk_indexes){
+ uint8_t precomputed_xor = 0;
+ for (auto chunk_it = chunk_indexes.rbegin();
+ chunk_it != chunk_indexes.rend();
+ ++chunk_it) {
+ auto chunk_info = (*ic_block_reference_free_chunks)[*chunk_it];
+ precomputed_xor += chunk_info.hash_ + chunk_info.hash_of_next_instruction_;
+ precomputed_xor = -precomputed_xor;
+ }
+ return precomputed_xor;
+}
+
+uint8_t
+IntegrityCheckLayoutTransform::ComputeAggregatedBlocksHash(uint64_t bb_id){
+ uint8_t precomputed_hash = 0;
+ auto checkee_iter = (*this->checker_to_checkee_map_)[bb_id].begin();
+ for (; checkee_iter != (*this->checker_to_checkee_map_)[bb_id].end();
+ ++checkee_iter){
+ precomputed_hash += (*this->precomputed_hashes_)[checkee_iter->first] *
+ checkee_iter->second;
+ }
+ return precomputed_hash;
+}
+
+bool IntegrityCheckLayoutTransform::RecomputePivot(
+ const uint64_t bb_id,
+ const uint8_t precomputed_hash,
+ const uint8_t precomputed_xor,
+ const size_t pivot_offset,
+ const size_t sub_offset,
+ block_graph::BlockGraph::Block *block){
+
+ const uint8_t *pivot_byte = block->data() + pivot_offset;
+ DCHECK(*pivot_byte == 0x00);
+
+ const uint8_t *sub_opcode = block->data() + sub_offset;
+ DCHECK(*sub_opcode == 0x2c);
+
+ const uint8_t *hash_byte = sub_opcode + 1;
+ //+1 is the sub hash value
+ uint8_t new_bytes[3];
+ new_bytes[0] = *pivot_byte;
+ new_bytes[1] = *sub_opcode;
+ new_bytes[2] = *hash_byte;
+
+ uint8_t old_hash = *hash_byte;
+ new_bytes[2] = precomputed_hash + precomputed_xor; // new hash
+ const uint8_t* data = block->data();
+ size_t data_size = block->data_size();
+ uint8_t* new_data = new uint8_t[data_size];
+ memcpy(new_data, data, data_size);
+
+ // Set new pivot byte. Starts at offset 0
+ uint8_t new_pivot = old_hash - new_bytes[2];
+ new_bytes[0] = new_pivot;
+
+ DCHECK_EQ((uint8_t)(new_pivot + precomputed_hash + precomputed_xor),
+ old_hash);
+ new_data[pivot_offset] = new_bytes[0];
+ new_data[sub_offset + 1] = new_bytes[2];
+
+ block->CopyData(data_size, new_data);
+ delete[] new_data;
+
+ return true;
+}
+
+bool IntegrityCheckLayoutTransform::PatchPivot(BlockGraph::Label 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;
+
+ uint8_t precomputed_hash = ComputeAggregatedBlocksHash(bb_id);
+ uint8_t precomputed_xor = 0;
+ if (*perform_chunk_checks_) {
+ //recompute xor hash
+ auto checkee_chunks_it = ic_chunk_checker_to_checkee_map_->find(bb_id);
+ DCHECK(checkee_chunks_it != ic_chunk_checker_to_checkee_map_->end());
+
+ DCHECK_NE(checkee_chunks_it->second.size(), static_cast<uint32_t>(0));
+ precomputed_xor = ComputeAggregatedChunksHash(checkee_chunks_it->second);
+ }
+ char *buffer = new char[50];
+ sprintf_s(buffer, 50, "Pivot:%llu", bb_id);
+ // offset of sub instruction after returning from hash function
+ size_t pivot_offset = (*label_name_to_block_)[buffer].second;
+ auto block = (*label_name_to_block_)[buffer].first;
+
+ sprintf_s(buffer, 50, "sub %llu", bb_id);
+ // offset of sub instruction after returning from hash function
+ auto sub_instr_block = label_name_to_block_->find(buffer);
+ DCHECK(sub_instr_block != label_name_to_block_->end());
+ size_t sub_offset = sub_instr_block->second.second;
+ delete[] buffer;
+ if (RecomputePivot(bb_id, precomputed_hash, precomputed_xor,
+ pivot_offset, sub_offset, block)){
+ this->nr_hashes_patched_++;
+ }
+ return true;
+}
+
+int IntegrityCheckLayoutTransform::PatchPrecomputedHashes(
+ const TransformPolicyInterface* policy,
+ BlockGraph::Block* block) {
+ if (!ShouldPostProcessBlock(block, this->id_to_label_))
+ return 0;
+
+ // Iterate over every label in the block and patch the pivot
+ auto it = block->labels().begin();
+ for (; it != block->labels().end(); ++it) {
+ if (!PatchPivot(it->second))
+ return 0;
+ }
+
+ return 0;
+}
+// This function adjusts the inter-block references that have shifted after
+// code was inserted
+bool IntegrityCheckLayoutTransform::CheckHash(
+ BasicCodeBlock* bb,
+ std::vector<uint8_t> new_block_buffer,
+ const core::AbsoluteAddress image_base) {
+ auto inst_iter = bb->instructions().begin();
+ uint64_t bb_id = (uint64_t)-1;
+ if ((inst_iter != bb->instructions().end()) && (inst_iter->has_label())) {
+ bb_id = GetBasicBlockIdByLabel(inst_iter->label(), this->id_to_label_);
+ if (bb_id != -1) {
+ auto buf_it = new_block_buffer.begin();
+ std::advance(buf_it, bb->offset());
+
+ uint8_t hash = 0;
+ uint32_t block_size = (*this->basic_block_sizes_)[bb_id];
+ for (uint32_t i = 0; i < block_size; ++i) {
+ hash += *buf_it;
+ if (i % 16 == 0)
+ fprintf(phash, "\n");
+ else if (i % 8 == 0)
+ fprintf(phash, " ");
+ fprintf(phash, "%02X ", *buf_it);
+ ++buf_it;
+ }
+ // Compute hash of image_base.
+ uint8_t hash_image_base = 0;
+ for (uint8_t i = 0; i < 4; i++) {
+ hash_image_base += image_base.value() >> (i*8);
+ }
+
+ // For each chunk or checkee subtract hash of image base.
+ uint8_t nr_checkees = (*this->checker_to_checkee_map_)[bb_id].size() +
+ (*ic_chunk_checker_to_checkee_map_)[bb_id].size();
+ hash -= hash_image_base * nr_checkees;
+
+ uint8_t precompute_hash = (*this->precomputed_hashes_)[bb_id];
+ if (precompute_hash != hash) {
+ (*this->precomputed_hashes_)[bb_id] = hash;
+ }
+
+ fprintf(phash, "\n%s,", bb->subgraph()->original_block()->name().c_str());
+ fprintf(phash, "%" PRIx64 ",", bb_id);
+ fprintf(phash, "%" PRIx32 ",",
+ bb->subgraph()->original_block()->addr().value() + bb->offset());
+ fprintf(phash, "%" PRIx8 "\n", hash);
+ } //end if
+ } //end if
+
+ //We need to compute hash of the chunks whose last instruction has absolute
+ // address. If there is no chunk checking this step is not needed.
+ if (!*perform_chunk_checks_) return true;
+
+ std::string chunk_pointerlabel = "n ";
+ auto end_block = bb->instructions().end();
+ uint64_t chunk_bb_id;
+ uint32_t chunk_index;
+ uint32_t offset = bb->offset();
+ for (; inst_iter != end_block; ++inst_iter)
+ {
+ offset += inst_iter->size();
+ 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(),
+ &chunk_bb_id,
+ &chunk_index);
+
+ size_t unique_key = GetChunkUniqueKey(chunk_bb_id, chunk_index);
+
+ uint32_t vector_index = (*ic_block_chunk_index_map_)[unique_key];
+
+ 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);
+ DCHECK(chunk.chunk_index_ == chunk_index);
+ //we need to recompute chunks whose last instruction has absoloute address
+ if (chunk.next_instruction_size_ == 0) continue;
+
+ uint32_t chunk_offset = offset + chunk.size_ - inst_iter->size();
+
+ auto buf_it = new_block_buffer.begin();
+ std::advance(buf_it, chunk_offset);
+
+ uint8_t hash = 0;
+ for (uint32_t i = 0; i < chunk.next_instruction_size_; ++i) {
+ hash += *buf_it;
+ ++buf_it;
+ }
+
+ chunk.hash_of_next_instruction_ = hash;
+ (*ic_block_reference_free_chunks)[vector_index] = chunk;
+ }
+ }
+
+ return true;
+}
+
+bool IntegrityCheckLayoutTransform::FixPrecomputedHashes(
+ const TransformPolicyInterface* policy,
+ const core::AbsoluteAddress image_base,
+ BlockGraph::Block* block,
+ std::vector<uint8_t> new_block_buffer) {
+
+ if (!ShouldPostProcessBlock(block, this->id_to_label_))
+ return false;
+
+ // Use the decomposition policy to skip blocks that aren't eligible for
+ // basic-block decomposition.
+ if (!policy->BlockIsSafeToBasicBlockDecompose(block))
+ return false;
+
+ // Decompose block to basic blocks.
+ BasicBlockSubGraph *subgraph = new BasicBlockSubGraph();
+ BasicBlockDecomposer bb_decomposer(block, subgraph);
+ if (!bb_decomposer.Decompose())
+ return false;
+
+ BasicBlockSubGraph::BBCollection& basic_blocks =
+ subgraph->basic_blocks(); // set of BB to protect
+
+ // Iterate over every basic block and recompute the hash
+ for (auto it = basic_blocks.begin(); it != basic_blocks.end(); ++it) {
+ BasicCodeBlock* bb = BasicCodeBlock::Cast(*it);
+
+ if (bb == NULL)
+ continue;
+
+ CheckHash(bb, new_block_buffer, image_base);
+ }
+
+ return true;
+}
+
+bool IntegrityCheckLayoutTransform::TransformImageLayout(
+ const TransformPolicyInterface* policy,
+ const pe::ImageLayout* image_layout,
+ const OrderedBlockGraph* ordered_block_graph) {
+ pe::PEFileWriter writer(*image_layout);
+
+ if (!writer.ValidateHeaders())
+ return false;
+
+ if (!writer.CalculateSectionRanges())
+ return false;
+
+ core::AbsoluteAddress* image_base = writer.GetImageBase();
+
+ // Create the output buffer, reserving enough room for the whole file.
+ DCHECK(!image_layout->sections.empty());
+ size_t image_size = writer.GetImageSize();
+ std::vector<uint8_t> buffer;
+ buffer.reserve(image_size);
+
+ // Iterate through all blocks in the address space writing them as we go.
+ BlockGraph::AddressSpace::RangeMap::const_iterator block_it2(
+ image_layout->blocks.address_space_impl().ranges().begin());
+ BlockGraph::AddressSpace::RangeMap::const_iterator block_end(
+ image_layout->blocks.address_space_impl().ranges().end());
+
+ BlockGraph::AddressSpace::RangeMap::const_iterator block_it(
+ image_layout->blocks.address_space_impl().ranges().begin());
+
+ BlockGraph::SectionId section_id = BlockGraph::kInvalidSectionId;
+ size_t section_index = BlockGraph::kInvalidSectionId;
+
+ // TODO: remove file
+ phash = fopen("phash.txt", "w");
+ fprintf(phash, "Block name, BBid, Address, hash\n");
+
+ for (; block_it != block_end; ++block_it) {
+ BlockGraph::Block* block =
+ const_cast<BlockGraph::Block*>(block_it->second);
+
+ // If we're jumping to a new section output the necessary padding.
+ if (block->section() != section_id) {
+ writer.FlushSection(section_index, &buffer);
+ section_id = block->section();
+ section_index++;
+ DCHECK_GT(image_layout->sections.size(), section_index);
+ }
+
+ core::FileOffsetAddress size_before(buffer.size());
+
+ if (!writer.WriteOneBlock(*image_base, section_index, block,
+ &buffer, &size_before)) {
+ LOG(ERROR) << "Failed to write block \"" << block->name() << "\".";
+ return false;
+ }
+
+ // compute new hash value of block
+ auto buf_it = buffer.begin();
+ std::advance(buf_it, size_before.value());
+ std::vector<uint8_t> new_block_buffer(buf_it, buffer.end());
+
+ // Compute the new hash values inside buffer
+ FixPrecomputedHashes(policy, *image_base, block, new_block_buffer);
+ } // end for
+
+ fclose(phash);
+
+ block_it = image_layout->blocks.address_space_impl().ranges().begin();
+ for (; block_it != block_end; ++block_it) {
+ BlockGraph::Block* block =
+ const_cast<BlockGraph::Block*>(block_it->second);
+ // patch the hash values in-place
+ PatchPrecomputedHashes(policy, block);
+ }
+
+ return true;
+}
+
+// static vars
+const char IntegrityCheckLayoutTransform::kTransformName[] =
+ "IntegrityCheckLayoutTransform";
+} // namespace protect

Powered by Google App Engine
This is Rietveld 408576698