OLD | NEW |
(Empty) | |
| 1 // Copyright 2015 Google Inc. All Rights Reserved. |
| 2 // |
| 3 // Licensed under the Apache License, Version 2.0 (the "License"); |
| 4 // you may not use this file except in compliance with the License. |
| 5 // You may obtain a copy of the License at |
| 6 // |
| 7 // http://www.apache.org/licenses/LICENSE-2.0 |
| 8 // |
| 9 // Unless required by applicable law or agreed to in writing, software |
| 10 // distributed under the License is distributed on an "AS IS" BASIS, |
| 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 12 // See the License for the specific language governing permissions and |
| 13 // limitations under the License. |
| 14 |
| 15 #ifndef SYZYGY_PROTECT_PROTECT_LIB_INTEGRITY_CHECK_TRANSFORM_H_ |
| 16 #define SYZYGY_PROTECT_PROTECT_LIB_INTEGRITY_CHECK_TRANSFORM_H_ |
| 17 |
| 18 #define _SILENCE_STDEXT_HASH_DEPRECATION_WARNINGS 1 |
| 19 |
| 20 #include "syzygy/block_graph/basic_block.h" |
| 21 #include "syzygy/block_graph/basic_block_subgraph.h" |
| 22 #include "syzygy/block_graph/block_graph.h" |
| 23 #include "syzygy/block_graph/transforms/named_transform.h" |
| 24 #include "syzygy/experimental/protect/protect_lib/protect_utils.h" |
| 25 |
| 26 namespace protect { |
| 27 |
| 28 class IntegrityCheckTransform |
| 29 : public block_graph::transforms:: |
| 30 NamedBlockGraphTransformImpl<IntegrityCheckTransform> { |
| 31 public: |
| 32 typedef block_graph::BlockGraph BlockGraph; |
| 33 typedef block_graph::BasicCodeBlock BasicCodeBlock; |
| 34 typedef block_graph::BasicBlockSubGraph BasicBlockSubGraph; |
| 35 typedef block_graph::TransformPolicyInterface TransformPolicyInterface; |
| 36 |
| 37 enum ProcessingType { |
| 38 ADD_HASH_AND_RESPONSE, |
| 39 PRECOMPUTE_HASHES, |
| 40 INSERT_CHECKS, |
| 41 COMPUTE_CHUNKS, |
| 42 INSERT_CHUNK_CHECKS, |
| 43 PATCH_REFERENCES_SIZES, |
| 44 PATCH_PIVOT |
| 45 }; |
| 46 |
| 47 enum ReferenceLabelType { |
| 48 Original_Block_Chunk, |
| 49 //Refers to a placeholder label that only after adding all integrity checks |
| 50 //it will be determined |
| 51 Integrity_Block_Chunk |
| 52 }; |
| 53 |
| 54 // The transform name. |
| 55 static const char kTransformName[]; |
| 56 |
| 57 // Constructor. |
| 58 explicit IntegrityCheckTransform(FlummoxConfig* config) { |
| 59 for (const std::string& target : config->target_set()) |
| 60 target_names_[target] = false; |
| 61 |
| 62 this->chunk_checking_coverage = config->chunk_checking_coverage(); |
| 63 if (chunk_checking_coverage == 0.0f){ |
| 64 bool *pcc = config->perform_chunk_checks(); |
| 65 *pcc = false; |
| 66 } |
| 67 this->basic_block_sizes_ = config->basic_block_sizes(); |
| 68 this->checker_to_checkee_map_ = config->checker_to_checkee_map(); |
| 69 this->ic_block_chunk_index_map_ = config->ic_block_chunk_index_map(); |
| 70 this->ic_block_reference_free_chunks = |
| 71 config->ic_block_reference_free_chunks(); |
| 72 this->ic_chunk_checker_to_checkee_map_ = |
| 73 config->ic_chunk_checker_to_checkee_map(); |
| 74 this->id_to_label_ = config->id_to_label(); |
| 75 this->label_name_to_block_ = config->label_name_to_block(); |
| 76 this->nr_hashes_patched_ = config->nr_hashes_patched(); |
| 77 this->perform_chunk_checks_ = config->perform_chunk_checks(); |
| 78 this->precomputed_hashes_ = config->precomputed_hashes(); |
| 79 this->chunk_checking_coverage = config->chunk_checking_coverage(); |
| 80 } |
| 81 |
| 82 // This is the main body of the transform. The transform decomposes each |
| 83 // block into a subgraph, applies the series of transform and rebuilds the |
| 84 // subgraph into a block. |
| 85 // |
| 86 // @param policy The policy object restricting how the transform is applied. |
| 87 // @param block_graph the block graph being transformed. |
| 88 // @param header_block the block to process. |
| 89 // @returns true on success, false otherwise. |
| 90 virtual bool TransformBlockGraph(const TransformPolicyInterface* policy, |
| 91 BlockGraph* block_graph, |
| 92 BlockGraph::Block* header_block) override; |
| 93 |
| 94 ~IntegrityCheckTransform(); |
| 95 |
| 96 protected: |
| 97 // Replaces the first basic block reference inside an instruction @p with a |
| 98 // reference to a new block. |
| 99 // @param inst_itr the instruction that contains the basic block reference. |
| 100 // @param new_block the block to which the new reference should point to. |
| 101 // @param new_offset the offset to which the new reference should have. |
| 102 // @param use_new_block flag indicating whether the new_block should be used. |
| 103 void PatchBlockReference( |
| 104 block_graph::BasicBlock::Instructions::iterator inst_itr, |
| 105 block_graph::BlockGraph::Block* new_block, |
| 106 block_graph::BlockGraph::Offset new_offset, |
| 107 bool use_new_block); |
| 108 |
| 109 // Constant value defining unique chunks should be selected in the chunk |
| 110 // combinator function |
| 111 const bool kForceUniqueChunks = true; |
| 112 |
| 113 // Constant value defining the number of chunks within original block |
| 114 // that each checker must check |
| 115 uint32_t num_chunks_per_block = 0; |
| 116 |
| 117 bool* perform_chunk_checks_; |
| 118 float chunk_checking_coverage =1.0f; |
| 119 |
| 120 // Creates a label to block map for all blocks in block graph @p. |
| 121 void GenerateLabelToBlockMap(BlockGraph *bgraph); |
| 122 |
| 123 // Update label to block map only with changes in the given block @p. |
| 124 void UpdateLabelToBlockMap(BlockGraph::Block *block); |
| 125 |
| 126 // Computes and writes the Q matrix to an output file. Also generates |
| 127 // combinations of basic blocks such that references to absolute addresses |
| 128 // cancel out. Finally it picks a random basic block to dynamically check |
| 129 // if the combination of precomputed hashes matches the runtime hashes. |
| 130 void GenerateBasicBlockCombinations(); |
| 131 |
| 132 // Assigns random chunks(without absolute references) to basic blocks |
| 133 //@chunks_vector - input proccessed chunks for assignment |
| 134 //@assignment_map - output assigned chunks per basic block |
| 135 //@no_chunks_per_checker - defines the number of chunks |
| 136 // that being assigned to a checker |
| 137 //@force_all_chunk_checks - This removes selected chunks from the random list |
| 138 //so they cannot be picked more than once |
| 139 std::map<uint64_t, std::set<uint32_t>> GenerateChunkCombinations( |
| 140 const std::vector<ChunkInfo> chunks_vector, |
| 141 const float chunk_coverage, const bool enforce_unique_chunks, |
| 142 uint32_t *no_chunks_per_block); |
| 143 |
| 144 // Iterates over all blocks of the block graph and decomposes them into |
| 145 // basic block subgraphs, which are then processed individually according |
| 146 // to the step parameter. The file parameter is optionally used to store |
| 147 // results of the processing. |
| 148 bool IntegrityCheckTransform::ProcessAllBlocks( |
| 149 const TransformPolicyInterface* policy, |
| 150 BlockGraph* block_graph, |
| 151 IntegrityCheckTransform::ProcessingType step); |
| 152 |
| 153 |
| 154 // Adds the assembly code which performs the integrity check |
| 155 // @param bb - the basic block where the integrity check will be inserted |
| 156 // @param checked_bb - the basic block that is actually checked |
| 157 // @param offset_sizes a list of (offset, size) pairs that indicate at which |
| 158 // offset from the beginning of the BB and how many bytes to hash |
| 159 // @param hash the value of the precomputed code hash |
| 160 // @param placeholder_flag a flag indicating if we need to insert |
| 161 // a placeholder in this basic block or not |
| 162 void AddIntegrityCheckCode(BasicCodeBlock* bb, |
| 163 BasicBlockSubGraph* subgraph, |
| 164 BlockGraph *block_graph); |
| 165 |
| 166 |
| 167 // Process the basic block subgraph inserting the hash function and the |
| 168 // integrity-checks |
| 169 // @param bgraph - block graph from where the subgraph was taken |
| 170 // @param subgraph - subgraph containing basic blocks we want to transform |
| 171 // @param modifyCode - flag that indicates whether the PE should be modified |
| 172 // @return - true if the transformation was successfull, false otherwise |
| 173 bool TransformBasicBlockSubGraph( |
| 174 BlockGraph* bgraph, |
| 175 BasicBlockSubGraph* subgraph, |
| 176 IntegrityCheckTransform::ProcessingType step); |
| 177 |
| 178 // Adds reference to address |
| 179 // @param bgraph - the block graph from where the subgraph was taken |
| 180 // @param block - block where to make the reference |
| 181 // @param offset - offset of reference in block |
| 182 // @return the block containing the response function |
| 183 BlockGraph::Block* AddReference(BlockGraph* bgraph, int dll_id); |
| 184 |
| 185 // Adds vector index of a chunk into the chunk index map |
| 186 // this index map is useful in chunk patching, basically O(n) to O(log(n)) |
| 187 // @param bb_id - basic block id where the chunk is located |
| 188 // @param chunk_index - index of the chunk within the basic block |
| 189 // @param vector_index - index of the chunk within the full chunks vector |
| 190 void IntegrityCheckTransform::AddChunkIntoIndexMap( |
| 191 const uint64_t bb_id, |
| 192 const uint32_t chunk_index, |
| 193 const uint32_t vector_index); |
| 194 |
| 195 // Iterates over instructions and places label over reference free |
| 196 // chunks. Appends discovered chunks references to |
| 197 // ic_block_reference_free_chunks |
| 198 // @bb_id - integrity checker block id |
| 199 // @bb_instruction - reference to basic block instruction set |
| 200 void ComputeChunks(BasicCodeBlock* bb); |
| 201 // Retrieves the original block id that chunk is located in, this function |
| 202 // relies on the label_name_to_block_ map |
| 203 // @chunk - instruction chunk info to find its original block |
| 204 uint64_t GetChunkOriginalBlockId(const ChunkInfo *chunk); |
| 205 |
| 206 // Selects chunks from the provided partition index from blocks different from |
| 207 // the checker block. It tries to use unique chunks, but if not enough chunks to |
| 208 // pick, it reuses the used chunks |
| 209 // @chunks_vector - vector of all chunks |
| 210 // @partition_indexes - the indexes that chunk can be selected from |
| 211 // (partitioning) |
| 212 // @num_picks - number of chunks to be picked |
| 213 // @checker_block_id - original block of the checker |
| 214 // @used - map of the used chunks |
| 215 std::set<uint32_t> IntegrityCheckTransform:: PickChunks( |
| 216 const std::vector<ChunkInfo> chunks_vector, |
| 217 const std::vector<uint32_t> partition_indexes, |
| 218 const uint32_t num_picks, |
| 219 const uint64_t checker_block_id, |
| 220 const std::vector<uint32_t>::iterator end_chunk_it, |
| 221 std::vector<uint32_t>::iterator last_visited_chunk, |
| 222 std::set<uint32_t> *unused_chunks); |
| 223 // Computes the hash that will be hard coded in the binary |
| 224 // @param bb - basic block for which we want to compute the hash |
| 225 // @param offset_sizes - list containing pairs of offsets and number of bytes |
| 226 // to hash by the hash function in order to obtain the same hash at runtime |
| 227 // @return - the hash value of the code |
| 228 uint8_t PrecomputeHash(BasicCodeBlock* bb, |
| 229 std::list<uint32_t> *offset_sizes, |
| 230 BasicBlockSubGraph* subgraph); |
| 231 |
| 232 // Retrieves the number of absolute references in the basic block indicated |
| 233 // by @p. This coincides with its index in the partition map. |
| 234 uint8_t IntegrityCheckTransform::GetPartitionKey(uint64_t bb_id); |
| 235 |
| 236 // Adds the assembly code which performs the chunk integrity check |
| 237 // @param bb - the basic block where the integrity check will be inserted |
| 238 // @param bgraph - block graph from where the subgraph was taken |
| 239 // @param subgraph - subgraph containing basic blocks we want to transform |
| 240 bool AddChunkIntegrityCheckCode( |
| 241 BasicCodeBlock* bb, |
| 242 BasicBlockSubGraph* subgraph, |
| 243 BlockGraph *block_graph); |
| 244 |
| 245 // Patches block references and sizes within the integrity checker assembly code |
| 246 // @param bb - the basic block where the patching will be done |
| 247 // @param bgraph - block graph from where the subgraph was taken |
| 248 // @param subgraph - subgraph containing basic blocks we want to transform |
| 249 bool PatchBlockReferencesAndSizes( |
| 250 BasicCodeBlock* bb, |
| 251 BasicBlockSubGraph* subgraph, |
| 252 BlockGraph *block_graph); |
| 253 |
| 254 // Updates the chunk's hash corresponding to the given inputs |
| 255 // with the value changes |
| 256 // @param bb - the basic block where the patching will be done |
| 257 // @param old_size - previous size value in included in the chunk |
| 258 // @param new_size - replacement size value for the chunk |
| 259 // @param chunk_bb_id - bb_id of the chunk |
| 260 // @param chunk_index - index of the chunk within the basic block |
| 261 bool RecomputeXorChunks( |
| 262 const uint64_t bb_id, const uint8_t old_size[], const uint8_t new_size[], |
| 263 const uint64_t chunk_bb_id, const uint32_t chunk_index); |
| 264 |
| 265 // Randomly picks a basic block to check-the given tuple of basic blocks. |
| 266 // Outputs the id of the basic block that was picked. |
| 267 bool RandomlySelectChecker(std::list<uint32_t> tuple_blocks, |
| 268 uint64_t *checker_id); |
| 269 |
| 270 // Randomly assigns checkers to checkee tuples |
| 271 void PopulateCheckMaps(std::set<uint64_t> part_block); |
| 272 |
| 273 // Checks if all basic blocks in the given map are protected by integrity |
| 274 // checks |
| 275 bool AllBasicBlocksChecked(std::map<std::set<uint64_t>, int> checkOrder); |
| 276 |
| 277 // Fills in the partition key multiset with names of the references. |
| 278 // @param instr assembly instruction references to basic blocks. |
| 279 // @param partitionKey set to fill out. |
| 280 // @return true if the instruction has references, false otherwise. |
| 281 bool PopulatePartitionKey(const block_graph::Instruction instr, |
| 282 uint8_t *num_abs_references); |
| 283 |
| 284 // Reference to the block that begins the computation of code hashes |
| 285 block_graph::BlockGraph::Block *hash_block_; |
| 286 |
| 287 // Reference to the block that begins the computation of code xor hashes |
| 288 block_graph::BlockGraph::Block *xhash_block_; |
| 289 |
| 290 // Reference to the block where the call to the reponse function is |
| 291 block_graph::BlockGraph::Block *response_block_; |
| 292 |
| 293 // Map of original custom basic block ID to a label |
| 294 std::map<uint64_t, BlockGraph::Label>* id_to_label_; |
| 295 |
| 296 // Map holding partition of relocation affected blocks |
| 297 std::map<uint8_t, std::set<uint64_t>> partition_map_; |
| 298 |
| 299 // Map holding precomputed hashes of original BB |
| 300 std::map<uint64_t, uint32_t>* precomputed_hashes_; |
| 301 |
| 302 // Map from BB address to its size |
| 303 std::map<uint64_t, uint32_t>* basic_block_sizes_; |
| 304 |
| 305 // Map containing the offset of the call to the hash function in the bb |
| 306 std::map<uint64_t, uint32_t> basic_block_hash_call_offset_; |
| 307 |
| 308 // Map containing true if basic block has a refenrece to another block |
| 309 std::map<uint64_t, bool> basic_block_has_ref_; |
| 310 |
| 311 // Map indicating which BB is checked by which other BBs |
| 312 std::map<uint64_t, uint32_t> is_bb_checked_map_; |
| 313 |
| 314 // Map indicating which BBs will be hashed by the checker |
| 315 std::map<uint64_t, std::map<uint64_t, int>>* checker_to_checkee_map_; |
| 316 |
| 317 // Vector indicating chunks within Integrity checker block without absolute |
| 318 // references |
| 319 std::vector<ChunkInfo>* ic_block_reference_free_chunks; |
| 320 // Map for retrieveing chunk id(unit32) from bb_id + chunk_index |
| 321 // useful in patching bb chunks |
| 322 std::map<uint64_t, uint32_t>* ic_block_chunk_index_map_; |
| 323 |
| 324 // Map< CheckerId, set < Chunk indexes > > |
| 325 std::map<uint64_t, std::set<uint32_t>>* ic_chunk_checker_to_checkee_map_; |
| 326 |
| 327 // File where to put the Q matrix |
| 328 FILE *prefile_; |
| 329 FILE *pfile_; |
| 330 FILE *insert_file_= NULL; |
| 331 FILE *fix_file_; |
| 332 |
| 333 // TODO: remove vector of BBSGs |
| 334 std::vector<block_graph::BasicBlockSubGraph*> subgraph_vector_; |
| 335 |
| 336 // Map of label name to the number of bytes all references to it should be |
| 337 // adjusted |
| 338 std::map<BlockGraph::Label, uint32_t> adjust_label_by_offset_; |
| 339 |
| 340 // This attribute keeps track of the address range that should be protected |
| 341 // by integrity-checks |
| 342 std::map<std::string, bool> target_names_; |
| 343 |
| 344 |
| 345 // Number of precomputed hash values which were patched |
| 346 int* nr_hashes_patched_; |
| 347 |
| 348 uint32_t num_no_chunk_patched_labels = 0; |
| 349 uint32_t num_no_chunk_labels = 0; |
| 350 uint32_t num_chunk_reference_labels = 0; |
| 351 uint32_t num_chunk_reference_patched_labels = 0; |
| 352 |
| 353 uint32_t num_xor_labels = 0; |
| 354 uint32_t num_xor_patched_labels = 0; |
| 355 |
| 356 uint32_t num_size_reference_labels = 0; |
| 357 uint32_t num_size_reference_patched_labels = 0; |
| 358 |
| 359 double elapsed_secs_in_patching_chunks = 0; |
| 360 |
| 361 // Map from ID of DLL to BlockReference |
| 362 std::map<int, std::pair<uint32_t, size_t>> dll_id_to_block_reference_; |
| 363 |
| 364 std::map<std::string, std::pair<BlockGraph::Block*, uint32_t>>* |
| 365 label_name_to_block_; |
| 366 private: |
| 367 DISALLOW_COPY_AND_ASSIGN(IntegrityCheckTransform); |
| 368 }; |
| 369 |
| 370 }// namespace protect |
| 371 |
| 372 #endif// SYZYGY_PROTECT_PROTECT_LIB_INTEGRITY_CHECK_TRANSFORM_H_ |
OLD | NEW |