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

Side by Side Diff: syzygy/experimental/protect/protect_lib/integrity_check_transform.cc

Issue 2535563002: Added all code for integrity check transform (Closed)
Patch Set: Created 4 years 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 unified diff | Download patch
OLDNEW
(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 #include "syzygy/experimental/protect/protect_lib/integrity_check_transform.h"
15 #include <fstream>
16 #include <stack>
17 #include <algorithm>
18 #include <random>
19 #include "syzygy/assm/assembler.h"
20 #include "syzygy/assm/assembler_base.h"
21 #include "syzygy/block_graph/basic_block_assembler.h"
22 #include "syzygy/block_graph/basic_block_decomposer.h"
23 #include "syzygy/block_graph/basic_block_subgraph.h"
24 #include "syzygy/block_graph/block_builder.h"
25 #include "syzygy/block_graph/block_util.h"
26 #include "syzygy/optimize/transforms/subgraph_transform.h"
27 #include "syzygy/experimental/protect/protect_lib/code_randomizer.h"
28 #include "syzygy/experimental/protect/protect_lib/protect_utils.h"
29 #include <iostream>
30 //#define PRINT_BLOCK_NAMES
31 namespace protect {
32
33 namespace {
34
35 using block_graph::BasicBlockDecomposer;
36 using block_graph::BasicBlockSubGraph;
37 using block_graph::BlockBuilder;
38 using block_graph::BlockGraph;
39 using block_graph::BlockVector;
40 using block_graph::BasicBlock;
41 using block_graph::BasicCodeBlock;
42 using block_graph::Instruction;
43 using block_graph::BasicBlockReference;
44
45 typedef BasicBlockSubGraph::BBCollection BBCollection;
46 typedef BasicBlock::Instructions Instructions;
47 typedef BlockGraph::Block::ReferrerSet ReferrerSet;
48 typedef std::list<BlockGraph::Block*> BlockOrdering;
49
50 // Retrieves a unique identifier for a basic block.
51 // @param bb the basic block to be uniquely identifed.
52 // @param subgraph basic block subgraph in which the basic block resides.
53 // @return a unique ID for the basic block.
54 uint64_t GetBasicBlockId(const BasicBlock *bb,
55 const BasicBlockSubGraph *subgraph) {
56 DCHECK(bb);
57 DCHECK(subgraph);
58
59 auto original_block = subgraph->original_block();
60 DCHECK(original_block);
61
62 return ((uint64_t)bb->offset() << 32) + original_block->id();
63 }
64
65 // Retrieves the block where the _putwch_nolock function is declared.
66 // @param bragph block graph where to search of the function.
67 // @return the block where the function is declared.
68 block_graph::BlockGraph::Block* GetPutwchNolock(
69 block_graph::BlockGraph* bgraph) {
70 DCHECK(bgraph);
71 block_graph::BlockGraph::Block *putwch_nolock = nullptr;
72
73 auto it = bgraph->blocks().begin();
74 for (; it != bgraph->blocks().end(); ++it)
75 if ((*it).second.name().compare("_putwch_nolock") == 0) {
76 putwch_nolock = bgraph->GetBlockById((*it).second.id());
77 break;
78 }
79 return putwch_nolock;
80 }
81
82 // Adds assembly code for response function to a block graph.
83 // @param bgraph the block graph from where the response function is inserted.
84 // @return the block containing the newly inseted response function.
85 BlockGraph::Block* AddResponseFunction(BlockGraph* bgraph) {
86 DCHECK(bgraph);
87 //TODO:BlockGraph::Block *response_function = GetPutwchNolock(bgraph);
88 BasicBlockSubGraph* subgraph = new BasicBlockSubGraph();
89 BlockGraph::Section* code_section = bgraph->FindOrAddSection(".text",
90 0x60000000);
91 std::string bb_name = "response_bb1";
92 // Create the thunk for standard "load/store" (received address in EDX).
93 BasicBlockSubGraph::BlockDescription* block_desc =
94 subgraph->AddBlockDescription(bb_name, code_section->name(),
95 BlockGraph::CODE_BLOCK, code_section->id(),
96 1, 0);
97
98 BasicCodeBlock* bb = subgraph->AddBasicCodeBlock(bb_name);
99 block_desc->basic_block_order.push_back(bb);
100
101 BasicBlock::Instructions::iterator inst_iter = bb->instructions().begin();
102 block_graph::BasicBlockAssembler assm(inst_iter,
103 &bb->instructions());
104
105 assm.push(assm::eax); // eax contains the actual hash value
106 // add size of instructions from hash function return up to response return
107 assm.add(assm::ebx, block_graph::Immediate(0xe));
108 assm.push(assm::ebx); // edx contains the address where to continue execution
109 //TODO:assm.call(block_graph::Immediate(response_function, 0)); // print char
110 assm.pop(assm::ebx); // edx gets changed by the previous call
111 assm.mov(assm::ebx, block_graph::Immediate((uint32_t)0x0));
112 assm.jmp(assm::ebx); // continue normal execution
113
114 // Condense into a block.
115 block_graph::BlockBuilder block_builder(bgraph);
116 if (!block_builder.Merge(subgraph))
117 return nullptr;
118
119 return block_builder.new_blocks().rbegin()[0];
120 }
121
122 // Adds assembly code for hash function in a block graph.
123 // @param bgraph the block graph where the hash function is inserted.
124 // @return the block containing the newly inserted hash function.
125 BlockGraph::Block* AddHashFunction(BlockGraph* bgraph) {
126 DCHECK(bgraph);
127 BlockGraph::Section* code_section = bgraph->FindOrAddSection(".text",
128 0x60000000);
129 std::string bb_name = "hash_add_bb1";
130 BasicBlockSubGraph* subgraph = new BasicBlockSubGraph();
131 // Create the thunk for standard "load/store" (received address in EDX).
132 BasicBlockSubGraph::BlockDescription* block_desc =
133 subgraph->AddBlockDescription(bb_name, code_section->name(),
134 BlockGraph::CODE_BLOCK, code_section->id(),
135 1, 0);
136
137 BasicCodeBlock* bb = subgraph->AddBasicCodeBlock(bb_name);
138 block_desc->basic_block_order.push_back(bb);
139
140 auto inst_iter = bb->instructions().begin();
141 block_graph::BasicBlockAssembler assm(inst_iter,
142 &bb->instructions());
143
144 // Create following BB that contains outer loop head.
145 bb_name = "hash_add_bb2";
146 block_desc = subgraph->AddBlockDescription(bb_name, code_section->name(),
147 BlockGraph::CODE_BLOCK,
148 code_section->id(), 1, 0);
149
150 bb = subgraph->AddBasicCodeBlock(bb_name);
151 block_desc->basic_block_order.push_front(bb);
152
153 // Function prolog.
154 assm.push(assm::ebp);
155 assm.mov(assm::ebp, assm::esp);
156
157 assm.pop(assm::eax); // pop ebp
158 assm.pop(assm::ebx); // pop return addres
159 assm.xor(assm::eax, assm::eax); // set eax to 0
160 // Get the base address of code section of this PE/DLL.
161 auto block_it = bgraph->blocks().begin();
162 for (; block_it != bgraph->blocks().end(); ++block_it) {
163 BlockGraph::BlockType type = block_it->second.type();
164 if (type == BlockGraph::BlockType::DATA_BLOCK)
165 break;
166 }
167 BlockGraph::Block* first_block = bgraph->GetBlockById(block_it->second.id());
168 // Get the start address of this basic block.
169 assm.mov(assm::ebx, block_graph::Immediate(first_block, 0));
170 // Compute hash of address.
171 assm.add(assm::al, assm::bl);
172 assm.add(assm::al, assm::bh);
173 assm.shr(assm::ebx, block_graph::Immediate(0x10));
174 assm.add(assm::al, assm::bl);
175 assm.add(assm::al, assm::bh);
176 // Save this hash of the address on the stack.
177 assm.pop(assm::ebx); // This is the designeated slot for the hash of address.
178 assm.pop(assm::ebx); // This is the designeated slot for the accumulator.
179 assm.xor(assm::ebx, assm::ebx); // Set accumulator to 0.
180 assm.push(assm::ebx); // Save accumulator.
181 assm.push(assm::eax); // Save hash of address.
182
183 assm.j(assm::ConditionCode::kEqual,
184 block_graph::Immediate(bb));
185
186 inst_iter = bb->instructions().begin();
187 block_graph::BasicBlockAssembler assm2(inst_iter,
188 &bb->instructions());
189
190 // Begin outer loop over all checkees passed to the hash function.
191 assm2.pop(assm::ebx); // Hash of address.
192 assm2.pop(assm::eax); // Accumulator for hash.
193 assm2.pop(assm::edx); // Get address of bb to hash.
194 assm2.sub(assm::ecx, block_graph::Immediate(1));
195 assm2.xchg(assm::ecx,
196 assm::OperandBase<block_graph::UntypedReference>(assm::esp));
197 assm2.push(assm::eax); // Accumulator for hash.
198 assm2.push(assm::ebx); // Hash of address.
199 assm2.sub(assm::eax, assm::eax); // Set eax to zero.
200
201 // Create following BB that contains inner loop over bytes of checkee.
202 bb_name = "hash_add_bb3";
203 block_desc = subgraph->AddBlockDescription(bb_name,
204 code_section->name(),
205 BlockGraph::CODE_BLOCK,
206 code_section->id(), 1, 0);
207
208 BasicCodeBlock* bb2 = subgraph->AddBasicCodeBlock(bb_name);
209 block_desc->basic_block_order.push_front(bb2);
210
211 assm2.jmp(block_graph::Immediate(bb2));
212
213 inst_iter = bb2->instructions().begin();
214 block_graph::BasicBlockAssembler assm3(inst_iter,
215 &bb2->instructions());
216
217 // Begin inner loop over instruction bytes of current checkee.
218 assm3.mov(assm::ebx,
219 assm::OperandBase<block_graph::UntypedReference>(assm::edx));
220 assm3.add(assm::al, assm::bl);
221 assm3.add(assm::edx, block_graph::Immediate(1));
222 assm3.sub(assm::ecx, block_graph::Immediate(1));
223 assm3.test(assm::ecx, assm::ecx);
224 assm3.j(assm::ConditionCode::kNotEqual, block_graph::Immediate(bb2));
225 // End inner loop.
226
227 // Subtract the hash of address from computed hash.
228 assm3.pop(assm::ebx); // Hash of address.
229 assm3.pop(assm::edx); // Accumulator for hash.
230 assm3.pop(assm::ecx); // Ourter loop counter.
231 assm3.xchg(assm::eax, // Load #checkees of checkee.
232 assm::OperandBase<block_graph::UntypedReference>(assm::esp));
233 assm3.imul(assm::ebx, assm::eax); // Multiply hash of address with #chekees
234 assm3.and(assm::ebx, block_graph::Immediate(0xFF)); // Modulo 256.
235 assm3.pop(assm::eax); // Get hash of the current checkee.
236 assm3.sub(assm::al, assm::bl); // Cancel base addresses of checkees in hash.
237 assm3.pop(assm::ebx); // Coeficient of current basic block.
238 assm3.imul(assm::eax, assm::ebx); // Multiply hash with coeficient.
239 assm3.and(assm::eax, block_graph::Immediate(0xFF)); // Modulo 256
240 assm3.add(assm::dl, assm::al); // Accumulate hash.
241 assm3.push(assm::edx); // Store accumulator for hash.
242 // The hash of the address is on the stack at a distance of 4 stack slots.
243 // Recover it because it was lost when ebx was multiplied by the #checkees of
244 // this checkee.
245 assm3.mov(assm::edx, block_graph::Operand(assm::esp,
246 block_graph::Displacement((unsigned int)-0x10)));
247 assm3.push(assm::edx); // Store hash of address.
248 // Check outer loop boundary.
249 assm3.test(assm::ecx, assm::ecx);
250 assm3.j(assm::ConditionCode::kNotEqual, block_graph::Immediate(bb));
251 // End outer loop.
252
253 assm3.pop(assm::eax); // Throw away hash of adddress.
254 assm3.pop(assm::eax); // Load final hash value.
255
256 // Function epilog.
257 assm3.mov(assm::esp, assm::ebp);
258 assm3.pop(assm::ebp);
259 // Jump over pivot byte.
260 assm3.add(assm::OperandBase<block_graph::UntypedReference>(assm::esp),
261 block_graph::Immediate(1));
262 // Load return address of edx into ebx, to be used by response function.
263 assm3.mov(assm::ebx,
264 assm::OperandBase<block_graph::UntypedReference>(assm::esp));
265 assm3.ret();
266
267 // Condense into a block.
268 block_graph::BlockBuilder block_builder(bgraph);
269 if (!block_builder.Merge(subgraph))
270 return nullptr;
271
272 return block_builder.new_blocks().begin()[0];
273 }
274
275
276 // Adds assembly code for xor hash function
277 // @param bgraph - the block graph from where the subgraph was taken
278 // @param subgraph - the subgraph containing basic blocks we want to transform
279 // @return the block containing the hash function
280 // Adds assembly code for hash function
281 // @param bgraph - the block graph from where the subgraph was taken
282 // @param subgraph - the subgraph containing basic blocks we want to transform
283 // @return the block containing the hash function
284 BlockGraph::Block* AddXorHashFunction(BlockGraph* bgraph) {
285 BlockGraph::Section* code_section = bgraph->FindOrAddSection(".text",
286 0x60000000);
287 std::string bb_name = "get_xeip";
288 BasicBlockSubGraph* subgraph = new BasicBlockSubGraph();
289 // Create the thunk for standard "load/store" (received address in EDX).
290 BasicBlockSubGraph::BlockDescription* block_desc =
291 subgraph->AddBlockDescription(bb_name, code_section->name(),
292 BlockGraph::CODE_BLOCK, code_section->id(),
293 1, 0);
294
295 BasicCodeBlock* bb = subgraph->AddBasicCodeBlock(bb_name);
296 block_desc->basic_block_order.push_back(bb);
297
298 auto inst_iter = bb->instructions().begin();
299 block_graph::BasicBlockAssembler assm(inst_iter, &bb->instructions());
300
301 // Create following BB that contains outer loop head
302 bb_name = "get_xeip2";
303 block_desc = subgraph->AddBlockDescription(bb_name, code_section->name(),
304 BlockGraph::CODE_BLOCK,
305 code_section->id(), 1, 0);
306
307 bb = subgraph->AddBasicCodeBlock(bb_name);
308 block_desc->basic_block_order.push_front(bb);
309
310 // function prolog
311 assm.push(assm::ebp);
312 assm.mov(assm::ebp, assm::esp);
313
314 assm.pop(assm::eax); // pop ebp
315 assm.pop(assm::eax); // pop return addres
316 assm.j(assm::ConditionCode::kEqual,
317 block_graph::Immediate(bb));
318
319 inst_iter = bb->instructions().begin();
320 block_graph::BasicBlockAssembler assm2(inst_iter,
321 &bb->instructions());
322
323 // begin outer loop
324 assm2.pop(assm::eax); // accumulator for hash
325
326 assm2.pop(assm::edx); // get address of bb to hash
327 assm2.sub(assm::ecx, block_graph::Immediate(1)); // decrement outer loop iter
328 assm2.xchg(assm::ecx, // swap outer loop iter with inner loop iter
329 assm::OperandBase<block_graph::UntypedReference>(assm::esp));
330 assm2.push(assm::eax); // save accumulator for hash
331 assm2.sub(assm::eax, assm::eax); // set eax to zero
332
333 // Create following BB that contains inner loop
334 bb_name = "get_xeip3";
335 block_desc = subgraph->AddBlockDescription(bb_name, code_section->name(),
336 BlockGraph::CODE_BLOCK,
337 code_section->id(), 1, 0);
338
339 BasicCodeBlock* bb2 = subgraph->AddBasicCodeBlock(bb_name);
340 block_desc->basic_block_order.push_front(bb2);
341
342 assm2.jmp(block_graph::Immediate(bb2));
343
344 inst_iter = bb2->instructions().begin();
345 block_graph::BasicBlockAssembler assm3(inst_iter,
346 &bb2->instructions());
347
348 // begin inner loop
349 assm3.mov(assm::ebx,
350 assm::OperandBase<block_graph::UntypedReference>(assm::edx));
351 assm3.xor(assm::al, assm::bl);
352 assm3.add(assm::edx, block_graph::Immediate(1));
353 assm3.sub(assm::ecx, block_graph::Immediate(1));
354 assm3.test(assm::ecx, assm::ecx);
355 assm3.j(assm::ConditionCode::kNotEqual, block_graph::Immediate(bb2));
356 // end inner loop
357
358 assm3.pop(assm::ebx); // hash accumulator
359 assm3.pop(assm::ecx); // ourter loop counter
360 assm3.xchg(assm::ecx, // swap outer loop iter with inner loop iter
361 assm::OperandBase<block_graph::UntypedReference>(assm::esp));
362 assm3.push(assm::ebx); // save accumulator for hash
363
364 // Create following BB that contains 2nd inner loop
365 bb_name = "get_xeip4";
366 block_desc = subgraph->AddBlockDescription(bb_name, code_section->name(),
367 BlockGraph::CODE_BLOCK,
368 code_section->id(), 1, 0);
369
370 BasicCodeBlock* bb3 = subgraph->AddBasicCodeBlock(bb_name);
371 block_desc->basic_block_order.push_front(bb3);
372
373 assm3.cmp(assm::ecx, block_graph::Immediate(0, assm::ValueSize::kSize32Bit));
374 assm3.j(assm::ConditionCode::kNotEqual, block_graph::Immediate(bb3));
375
376 inst_iter = bb3->instructions().begin();
377 block_graph::BasicBlockAssembler assm4(inst_iter,
378 &bb3->instructions());
379
380 // begin 2nd inner loop
381 assm4.mov(assm::ebx,
382 assm::OperandBase<block_graph::UntypedReference>(assm::edx));
383 assm4.add(assm::al, assm::bl);
384 assm4.add(assm::edx, block_graph::Immediate(1));
385 assm4.sub(assm::ecx, block_graph::Immediate(1));
386 assm4.test(assm::ecx, assm::ecx);
387 assm4.j(assm::ConditionCode::kNotEqual, block_graph::Immediate(bb3));
388 assm4.mov(assm::ecx, block_graph::Immediate(bb));
389 assm4.add(assm::ecx, block_graph::Immediate(0x34));
390 assm4.jmp(assm::ecx);
391 // end 2nd inner loop
392
393 assm3.pop(assm::edx); // load hash accumulator
394 assm3.pop(assm::ecx); // ourter loop counter
395 assm3.and(assm::eax, block_graph::Immediate(0xFF));
396
397 assm3.add(assm::dl, assm::al); // accumulate hash
398 assm3.xor(assm::eax, assm::eax); // set eax to 0
399 assm3.sub(assm::al, assm::dl); // al = -hash
400 assm3.push(assm::eax); // store hash accumulator
401 // check outer loop boundary
402 assm3.test(assm::ecx, assm::ecx);
403 assm3.j(assm::ConditionCode::kNotEqual, block_graph::Immediate(bb));
404 // end outer loop
405 assm3.pop(assm::eax); // final hash value
406
407 // function epilog
408 assm3.mov(assm::esp, assm::ebp);
409 assm3.pop(assm::ebp);
410 assm3.ret();
411
412 // Condense into a block.
413 block_graph::BlockBuilder block_builder(bgraph);
414 if (!block_builder.Merge(subgraph))
415 return NULL;
416
417 return block_builder.new_blocks().begin()[0];
418 }
419
420 // Traverse the call-graph in reverse call order (callee to caller) and push
421 // blocks in post-order. The resulting ordering can be iterated to visit all
422 // blocks from leaf to root. The ordering has the guarantee that all callees
423 // have been visited before their callers (except for recursive calls and
424 // indirect calls).
425 // TODO(etienneb): Hoist this function into block_graph.
426 void FlattenCallGraphPostOrder(BlockGraph* block_graph, BlockOrdering* order) {
427 DCHECK_NE(reinterpret_cast<BlockGraph*>(NULL), block_graph);
428 DCHECK_NE(reinterpret_cast<BlockOrdering*>(NULL), order);
429
430 // The algorithms uses a std::stack allocated in the heap to avoid stack
431 // overflow.
432 std::stack<BlockGraph::Block*> stack;
433 std::set<BlockGraph::Block*> visiting;
434
435 // Traverse the call-graph in depth-first.
436 BlockGraph::BlockMap& blocks = block_graph->blocks_mutable();
437 auto block_iter = blocks.begin();
438 for (; block_iter != blocks.end(); ++block_iter) {
439 BlockGraph::Block* block = &block_iter->second;
440
441 // This block is already visited.
442 if (!visiting.insert(block).second)
443 continue;
444
445 // This block needs to be visited, add it to the stack.
446 stack.push(block);
447
448 // Follow the referrers.
449 while (!stack.empty()) {
450 block = stack.top();
451
452 // Put unvisited referrers on the stack.
453 typedef std::map<BlockGraph::BlockId,
454 BlockGraph::Block*> OrderedBlockMap;
455 OrderedBlockMap missing;
456 bool missing_referrers = false;
457 if (block->type() == BlockGraph::CODE_BLOCK) {
458 const ReferrerSet& referrers = block->referrers();
459 auto referrer = referrers.begin();
460 for (; referrer != referrers.end(); ++referrer) {
461 BlockGraph::Block* from = referrer->first;
462 if (visiting.insert(from).second) {
463 missing.insert(std::make_pair(from->id(), from));
464 missing_referrers = true;
465 }
466 }
467 }
468
469 // Push missing referrers into the stack, ordered by block id.
470 auto referrer = missing.begin();
471 for (; referrer != missing.end(); ++referrer)
472 stack.push(referrer->second);
473
474 // When there are no missing referrers, this block is fully visited and
475 // can be pushed in the ordering (post-order).
476 if (!missing_referrers) {
477 order->push_front(block);
478 // Remove this block from the stack.
479 DCHECK_EQ(block, stack.top());
480 stack.pop();
481 }
482 }
483 }
484 }
485
486 // Retrieves the basic block in the given subgraph at the given offset.
487 // @param subgraph the basic block subgraph where to search.
488 // @param offset the offset of the basic block to search for.
489 // @return the basic block object if found and NULL otherwise.
490 BasicBlock* GetBasicBlockAtOffset(const BasicBlockSubGraph *subgraph,
491 const BasicBlock::Offset offset) {
492 DCHECK(subgraph);
493 DCHECK_LE(0, offset);
494
495 auto it(subgraph->basic_blocks().begin());
496 for (; it != subgraph->basic_blocks().end(); ++it) {
497 if ((*it)->offset() == offset)
498 return *it;
499 }
500
501 return nullptr;
502 }
503
504
505 }
506 // namespace
507
508 void IntegrityCheckTransform::PatchBlockReference(
509 BasicBlock::Instructions::iterator inst_itr,
510 block_graph::BlockGraph::Block* new_block,
511 block_graph::BlockGraph::Offset new_offset,
512 bool use_new_block = false){
513 DCHECK(new_block);
514 Instruction::BasicBlockReferenceMap &ref_block_map = inst_itr->references();
515 auto instruction_references_it = ref_block_map.begin();
516 BlockGraph::Offset reference_offset = instruction_references_it->first;
517
518 BasicBlockReference old_bb_ref = instruction_references_it->second;
519 BasicBlockReference new_bb_ref(old_bb_ref.reference_type(),
520 old_bb_ref.size(),
521 use_new_block ? new_block : old_bb_ref.block(),
522 new_offset,
523 new_offset);
524
525 ref_block_map[reference_offset] = new_bb_ref;
526 }
527
528 void SplitChunkReferencelabels(const std::string label,
529 uint64_t *checkee_id,
530 int *chunkIndex){
531 //split the string
532 std::istringstream iss(label);
533 std::vector<std::string> tokens;
534 copy(std::istream_iterator<std::string>(iss),
535 std::istream_iterator<std::string>(),
536 back_inserter(tokens));
537
538 *checkee_id = std::stoull(tokens.at(1));
539 *chunkIndex = std::stoi(tokens.at(2));
540 }
541
542 void IntegrityCheckTransform::GenerateLabelToBlockMap(BlockGraph *bgraph) {
543 BlockGraph::BlockMap &blocks = bgraph->blocks_mutable();
544 auto it(blocks.begin());
545 this->label_name_to_block_->clear();
546
547 for (; it != blocks.end(); ++it) {
548 auto label_map = it->second.labels();
549 auto lab_it(label_map.begin());
550 for (; lab_it != label_map.end(); ++lab_it) {
551 (*this->label_name_to_block_)[lab_it->second.name()] =
552 std::make_pair(&it->second, lab_it->first);
553 }
554 }
555 }
556
557 void IntegrityCheckTransform::UpdateLabelToBlockMap(BlockGraph::Block *block) {
558 auto label_map = block->labels();
559 auto lab_it(label_map.begin());
560 for (; lab_it != label_map.end(); ++lab_it) {
561 (*this->label_name_to_block_)[lab_it->second.name()] =
562 std::make_pair(block, lab_it->first);
563 }
564 }
565
566 bool IntegrityCheckTransform::PopulatePartitionKey(
567 const block_graph::Instruction instr,
568 uint8_t *num_abs_references) {
569 auto references = instr.references();
570 if (references.size() < 1)
571 return false;
572
573 auto it = references.begin();
574 for (; it != references.end(); ++it) {
575 block_graph::BlockGraph::ReferenceType type = it->second.reference_type();
576
577 if (type == block_graph::BlockGraph::ReferenceType::ABSOLUTE_REF) {
578 (*num_abs_references)++;
579 }
580 }
581 return true;
582 }
583
584 void IntegrityCheckTransform::PopulateCheckMaps(std::set<uint64_t> part_block) {
585 std::set<uint64_t> tmp = part_block;
586
587 while (tmp.size() > 0) {
588 // chose a random element
589 int index = rand() % tmp.size();
590 auto set_it = tmp.begin();
591 std::advance(set_it, index);
592
593 index = rand() % part_block.size();
594 auto set_it2 = part_block.begin();
595 std::advance(set_it2, index);
596
597 // pick different blocks as the pair of checkees
598 for (; set_it2 != part_block.end(); ++set_it2)
599 if ((uint32_t)(*set_it) != (uint32_t)(*set_it2))
600 break;
601
602 // if reached the end of the list then start from beginning
603 if (set_it2 == part_block.end())
604 set_it2 = part_block.begin();
605
606 // pick different blocks as the pair of checkees
607 int i = 0;
608 for (; i < index; ++i) {
609 if ((uint32_t)(*set_it) != (uint32_t)(*set_it2))
610 break;
611
612 ++set_it2;
613 }
614
615 if (((uint32_t)(*set_it) == (uint32_t)(*set_it2)) && (i >= index)) {
616 // skip this block of the partition
617 tmp.erase(set_it);
618 continue;
619 }
620
621 // use this when checkers are allowed to be in the same block as checkees
622 std::map<uint64_t, int> tuple;
623 tuple.insert(std::pair<uint64_t, int>(*set_it, 1));
624 tuple.insert(std::pair<uint64_t, int>(*set_it2, -1));
625 // use this when checkers are NOT allowed in the same block as checkees
626 std::list<uint32_t> tuple_blocks;
627 tuple_blocks.push_back((uint32_t)*set_it);
628 tuple_blocks.push_back((uint32_t)*set_it2);
629
630 uint64_t checker_id;
631 if (!RandomlySelectChecker(tuple_blocks, &checker_id)) {
632 tmp.erase(*set_it);
633 continue;
634 }
635
636 // Populate checker / checkee maps
637 (*this->checker_to_checkee_map_)[checker_id] = tuple;
638 fprintf(this->pfile_, "%llx,", checker_id);
639 auto list_it = tuple.begin();
640 for (; list_it != tuple.end(); ++list_it) {
641 this->is_bb_checked_map_[list_it->first] = 1;
642 fprintf(this->pfile_, "%d * %llx,", list_it->second, list_it->first);
643 }
644
645 fprintf(this->pfile_, "\n");
646 tmp.erase(set_it);
647 }
648 }
649
650 //
651 bool IntegrityCheckTransform::RandomlySelectChecker(
652 std::list<uint32_t> tuple_blocks,
653 uint64_t *checker_id) {
654 // Randomly select checker
655 int index = rand() % this->precomputed_hashes_->size();
656 auto map_it = this->precomputed_hashes_->begin();
657 std::advance(map_it, index);
658
659 // checker must not be in the list of checkees and preferrably does not check
660 // other tuples as well
661 while ((map_it != this->precomputed_hashes_->end()) &&
662 ((std::find(tuple_blocks.begin(), tuple_blocks.end(),
663 (uint32_t)(*map_it).first) != tuple_blocks.end()) ||
664 (this->checker_to_checkee_map_->find((*map_it).first) !=
665 this->checker_to_checkee_map_->end()) &&
666 ((*this->checker_to_checkee_map_)[(*map_it).first].size() > 0)))
667 map_it++;
668
669 int i = 0;
670 // if reached end of list then start from the begining and go until index
671 if (map_it == this->precomputed_hashes_->end()) {
672 map_it = this->precomputed_hashes_->begin();
673 while ((i < index) &&
674 ((std::find(tuple_blocks.begin(), tuple_blocks.end(),
675 (uint32_t)(*map_it).first) != tuple_blocks.end()) ||
676 (this->checker_to_checkee_map_->find((*map_it).first) !=
677 this->checker_to_checkee_map_->end()) &&
678 ((*this->checker_to_checkee_map_)[(*map_it).first].size() > 0))) {
679 map_it++;
680 i++;
681 }
682 }
683
684 // if all checkers are already checking some tuple then we should only avoid
685 // selecting the checker in the list of checkees
686 if (i >= index) {
687 while ((map_it != this->precomputed_hashes_->end()) &&
688 ((std::find(tuple_blocks.begin(), tuple_blocks.end(),
689 (uint32_t)(*map_it).first) != tuple_blocks.end()))) {
690 map_it++;
691 }
692
693 int i = 0;
694 // if reached end of list then start from the begining and go until index
695 if (map_it == this->precomputed_hashes_->end()) {
696 map_it = this->precomputed_hashes_->begin();
697 while ((i < index) &&
698 ((std::find(tuple_blocks.begin(), tuple_blocks.end(),
699 (uint32_t)(*map_it).first) != tuple_blocks.end()))){
700 map_it++;
701 i++;
702 }
703
704 if (i >= index) {
705 return false; // can't find a checker that satisfies all conditions
706 }
707 }
708 }
709
710 DCHECK(std::find(tuple_blocks.begin(), tuple_blocks.end(),
711 (uint32_t)(*map_it).first) == tuple_blocks.end());
712
713 *checker_id = (*map_it).first;
714 return true;
715 }
716
717 #if defined COMPUTE_CHECKER_SIZE
718 uint64_t total_checker_size = 0;
719 #endif
720
721 char* MakeChunkLabel(const uint64_t chunk_bb_id, const uint32_t chunk_index,
722 const bool before_chunk_integrity_code_added = false){
723 DCHECK(chunk_bb_id != MAXUINT64);
724 char *buffersearch = new char[50];
725
726 //only after chunk integrity code is prepended the first chunk label is
727 //update to n %llu %lu format. Before that the first instruction refers to
728 //the beginning of the block, which has %llu format
729 if (before_chunk_integrity_code_added && chunk_index == 0){
730 sprintf_s(buffersearch, 50, "%llu", chunk_bb_id);
731 } else {
732 sprintf_s(buffersearch, 50, "n %llu %lu", chunk_bb_id, chunk_index);
733 }
734 return buffersearch;
735 }
736
737 bool IntegrityCheckTransform::AddChunkIntegrityCheckCode(
738 BasicCodeBlock* bb,
739 BasicBlockSubGraph* subgraph,
740 BlockGraph *block_graph){
741 auto inst_iter = bb->instructions().begin();
742 if (inst_iter == bb->instructions().end())
743 return true;
744
745 BlockGraph::Label label(inst_iter->label());
746 uint64_t bb_id = GetBasicBlockIdByLabel(label, this->id_to_label_);
747
748 if (bb_id == -1)
749 return true;
750
751 if ((*this->checker_to_checkee_map_)[bb_id].size() < 1)
752 return true;
753
754 //given that the begining of the checker block never has an absolute reference
755 //therefore, it is the pointer for the first block chunk. So, we update it's
756 //label to a the chunk of index zero within the block
757 char *chunk_label = MakeChunkLabel(bb_id, 0);
758 inst_iter->set_label(BlockGraph::Label(chunk_label, BlockGraph::CODE_LABEL));
759 delete[] chunk_label;
760 std::set<uint32_t> chunk_set = (*ic_chunk_checker_to_checkee_map_)[bb_id];
761 CHECK(chunk_set.size() == this->num_chunks_per_block);
762
763 block_graph::BasicBlockAssembler assm(inst_iter, &bb->instructions());
764
765 uint32_t num_original_instr = bb->instructions().size();
766
767 assm.push(assm::eax);
768 assm.push(assm::ebx);
769 assm.push(assm::ecx);
770 assm.push(assm::edx);
771
772 assm.mov(assm::ecx, block_graph::Immediate(chunk_set.size(),
773 assm::ValueSize::kSize32Bit));
774
775 auto chunk_iter = chunk_set.begin();
776 uint32_t num_chunks = chunk_set.size();
777
778 std::map<uint32_t, std::tuple<uint64_t, uint32_t>> reference_free_labels;
779 for (uint32_t reference_index = 0; chunk_iter != chunk_set.end();
780 ++chunk_iter, ++reference_index){
781
782 auto chunk_info = (*ic_block_reference_free_chunks)[*chunk_iter];
783 uint64_t chunk_bb_id = chunk_info.block_id_;
784 uint32_t chunk_size = chunk_info.size_;
785 uint32_t chunk_index = chunk_info.chunk_index_;
786
787 //get chunk offset and block
788 char *buffersearch = MakeChunkLabel(chunk_bb_id, chunk_index,true);
789 auto found_label_it = label_name_to_block_->find(buffersearch);
790 DCHECK(found_label_it != label_name_to_block_->end());
791 delete[] buffersearch;
792 BlockGraph::Block* chunk_block = found_label_it->second.first;
793 uint32_t chunk_offset = found_label_it->second.second;
794
795 assm.push(block_graph::Immediate(chunk_info.next_instruction_size_,
796 assm::ValueSize::kSize32Bit));
797 assm.push(block_graph::Immediate(chunk_size, assm::ValueSize::kSize32Bit));
798
799 //keep the index of block instruction for labelling
800 uint32_t label_instr_index = bb->instructions().size() -
801 num_original_instr;
802 reference_free_labels.insert(std::make_pair(label_instr_index,
803 std::make_pair(chunk_bb_id, chunk_index)));
804
805 assm.push(block_graph::Immediate(chunk_block, chunk_offset));
806 }
807
808
809 assm.push(block_graph::Immediate(0, assm::kSize32Bit));
810 assm.call(block_graph::Immediate(this->xhash_block_, 0));
811 uint32_t no_pushed_words = 3 * num_chunks + 1;
812 assm.add(assm::esp, block_graph::Immediate(no_pushed_words * 4));
813 assm.push(assm::eax);
814 //test
815
816 //Insert label at the beginning of the block
817 inst_iter = bb->instructions().begin();
818 label = BlockGraph::Label(std::to_string(bb_id),
819 BlockGraph::CODE_LABEL);
820 inst_iter->set_label(label);
821
822
823 uint32_t num_added_chunk_labels = 0;
824 uint32_t label_index = 0;
825 uint32_t new_size = 0;
826 uint32_t num_added_instr = bb->instructions().size() - num_original_instr;
827 for (uint32_t instruction_index = 0;
828 inst_iter != bb->instructions().end() &&
829 instruction_index < num_added_instr;
830 ++instruction_index, ++inst_iter){
831 new_size += inst_iter->size();
832 auto label_it = reference_free_labels.find(label_index++);
833 //add reference free labels
834 if (label_it != reference_free_labels.end()){
835 char *buffer = new char[50];
836 uint64_t chunk_bb_id = std::get <0>(label_it->second);
837 uint32_t chunk_index = std::get<1>(label_it->second);
838 sprintf_s(buffer, 50, "nrc %llu %lu", chunk_bb_id,
839 chunk_index);
840 label = BlockGraph::Label(buffer, BlockGraph::CODE_LABEL);
841 delete[] buffer;
842 inst_iter->set_label(label);
843 ++num_added_chunk_labels;
844 ++num_chunk_reference_labels;
845 }
846 } //end for
847
848
849 //make sure all chunk block references are set
850 DCHECK_EQ(num_added_chunk_labels, static_cast<uint32_t>(chunk_set.size()));
851
852
853 //update size
854 uint32_t old_size = (*this->basic_block_sizes_)[bb_id];
855 (*this->basic_block_sizes_)[bb_id] = old_size + new_size;
856 #if defined COMPUTE_CHECKER_SIZE
857 total_checker_size += old_size + new_size;
858 #endif
859 return true;
860 }
861 void GetSizeTokenFromlabel(const std::string label,
862 uint64_t *checkee_id,
863 uint64_t *bb_id){
864 //split the string
865 std::istringstream iss(label);
866 std::vector<std::string> tokens;
867 copy(std::istream_iterator<std::string>(iss),
868 std::istream_iterator<std::string>(),
869 back_inserter(tokens));
870 *checkee_id = std::stoull(tokens.at(1));
871 *bb_id = std::stoull(tokens.at(2));
872 }
873
874 void GetBlockIdTokenFromlabel(const std::string label, uint64_t *checkee_id){
875 //split the string
876 std::istringstream iss(label);
877 std::vector<std::string> tokens;
878 copy(std::istream_iterator<std::string>(iss),
879 std::istream_iterator<std::string>(),
880 back_inserter(tokens));
881 *checkee_id = std::stoull(tokens.at(1));
882 }
883
884 //keep track of chunk indexes to update xor hash after size changes
885 uint32_t last_visited_chunk_index = 0;
886 uint64_t last_visited_chunk_bb_id = 0;
887
888 bool IntegrityCheckTransform::PatchBlockReferencesAndSizes(
889 BasicCodeBlock* bb,
890 BasicBlockSubGraph* subgraph,
891 BlockGraph *block_graph){
892 bool found = false;
893
894 auto inst_iter = bb->instructions().begin();
895 if (inst_iter == bb->instructions().end()){
896 return true;
897 }
898
899 BlockGraph::Label label(inst_iter->label());
900 uint64_t block_id = GetBasicBlockIdByLabel(label, this->id_to_label_);
901
902 std::string sizeLabel = "size ";
903 std::string blockLabel = "block";
904 std::string chunk_blocklabel = "nrc";
905 std::string chunk_pointerlabel = "n ";
906 std::string chunk_no_reference = "ref";
907 std::string block_id_label = std::to_string(block_id);
908 auto end_block = bb->instructions().end();
909 for (; inst_iter != end_block; ++inst_iter)
910 {
911 if (!inst_iter->has_label()) continue;
912
913
914 if (inst_iter->label().name()
915 .compare(0, chunk_pointerlabel.length(), chunk_pointerlabel) == 0){
916 // update last visited chunk index
917 GetChunkTokensFromlabel(inst_iter->label().name(),
918 &last_visited_chunk_bb_id,
919 &last_visited_chunk_index);
920 } else if (inst_iter->label().name()
921 .compare(0, block_id_label.length(), block_id_label) == 0){
922 last_visited_chunk_bb_id = block_id;
923 last_visited_chunk_index = 0;
924 #pragma region patch_size
925 } else if (inst_iter->label().name()
926 .compare(0, sizeLabel.length(), sizeLabel) == 0){
927 //extract block id for size retrieval
928 uint64_t checkee_id = 0;
929 uint64_t bb_id = 0;
930 GetSizeTokenFromlabel(inst_iter->label().name(), &checkee_id, &bb_id);
931 //modify bytes
932 ++num_size_reference_patched_labels;
933 auto old_data = inst_iter->GetMutableData();
934 DCHECK(old_data[0] == 0x68);
935 //if the block is checker then the new size must be bigger than the
936 //old one
937 uint32_t old_size = 0;
938 for (int j = 0; j < sizeof(uint32_t) && old_data[j] != NULL; j++)
939 {
940 old_size |= old_data[j + 1] << j * 8;
941 }
942 uint8_t* new_data = new uint8_t[inst_iter->size()];
943 new_data[0] = 0x68;
944 uint32_t new_size = (*this->basic_block_sizes_)[checkee_id];
945 for (int k = 0; k < sizeof(uint32_t); k++){
946 uint8_t value = (new_size >> k * 8) & 0xFF;
947 new_data[k + 1] = value;
948 }
949
950 //if the block is checker then the new size must be bigger than the
951 //old one
952 if ((*checker_to_checkee_map_)[checkee_id].size() > 0){
953 DCHECK_GE(new_size, old_size);
954 }
955
956 if (*perform_chunk_checks_) {
957 clock_t begin = clock();
958 //we have to recompute chunk that inlcudes this size
959 this->RecomputeXorChunks(bb_id, old_data, new_data,
960 last_visited_chunk_bb_id,
961 last_visited_chunk_index);
962 clock_t end = clock();
963 elapsed_secs_in_patching_chunks += double(end - begin) /
964 CLOCKS_PER_SEC;
965 }
966 for (uint8_t j = 0; j < inst_iter->size(); j++)
967 old_data[j] = new_data[j];
968
969 //prevent multiple patching
970 inst_iter->set_label(block_graph::BlockGraph::Label());
971 found++;
972 delete[] new_data;
973 #pragma endregion
974 } else if (inst_iter->label().name() //patch block
975 .compare(0, blockLabel.length(), blockLabel) == 0){
976 //extract block id for offset patching
977 uint64_t checkee_id = 0;
978 GetBlockIdTokenFromlabel(inst_iter->label().name(), &checkee_id);
979 #pragma region patch_block
980 auto label_itr = label_name_to_block_->find(std::to_string(checkee_id));
981 DCHECK(label_itr != label_name_to_block_->end());
982 PatchBlockReference(inst_iter, label_itr->second.first,
983 label_itr->second.second);
984 #pragma endregion
985 } else if (inst_iter->label().name().compare(0, chunk_blocklabel.size(),
986 chunk_blocklabel) == 0){
987
988 uint64_t checkee_id_for_patch = 0;
989 int checkee_index_for_patch;
990 SplitChunkReferencelabels(inst_iter->label().name(),
991 &checkee_id_for_patch,
992 &checkee_index_for_patch);
993 CHECK(checkee_id_for_patch != 0);
994
995 num_chunk_reference_patched_labels++;
996
997 //find the offset of the reference free chunk within the checkee
998 char *chunk_label = MakeChunkLabel(checkee_id_for_patch,
999 checkee_index_for_patch);
1000 auto label_to_block_it = label_name_to_block_->find(chunk_label);
1001 delete[] chunk_label;
1002 CHECK(label_to_block_it != label_name_to_block_->end());
1003 #pragma region patch_chunk_offset
1004 //update instruction reference to the retrieved reference free
1005 //offset (label_to_block_it.second is a pair of <block,offset>)
1006 auto reference_free_block = label_to_block_it->second.first;
1007 uint32_t new_bb_ref_offset = label_to_block_it->second.second;
1008
1009 PatchBlockReference(inst_iter, reference_free_block, new_bb_ref_offset);
1010
1011 #pragma endregion
1012
1013
1014 } else if (*perform_chunk_checks_ &&
1015 inst_iter->label().name().compare(0, chunk_no_reference.size(),
1016 chunk_no_reference) == 0){
1017 //patch number of chunks per block
1018 uint64_t bb_id = 0;
1019 GetBlockIdTokenFromlabel(inst_iter->label().name(), &bb_id);
1020 //modify bytes
1021 ++num_no_chunk_patched_labels;
1022 auto old_data = inst_iter->GetMutableData();
1023 DCHECK(old_data[0] == 0x68);
1024
1025 uint8_t* new_data = new uint8_t[inst_iter->size()];
1026 new_data[0] = 0x68;
1027 uint32_t old_size = 0;
1028 for (int j = 0; j < sizeof(uint32_t) && old_data[j] != NULL; j++)
1029 {
1030 old_size |= old_data[j + 1] << j * 8;
1031 }
1032 uint32_t new_size = old_size + this->num_chunks_per_block;
1033 for (int k = 0; k < sizeof(uint32_t); k++){
1034 uint8_t value = (new_size >> k * 8) & 0xFF;
1035 new_data[k + 1] = value;
1036 }
1037
1038 //we have to recompute the chunk that includes this instruction(if any)
1039 this->RecomputeXorChunks(bb_id, old_data, new_data,
1040 last_visited_chunk_bb_id,
1041 last_visited_chunk_index);
1042
1043 for (uint8_t j = 0; j < inst_iter->size(); j++)
1044 old_data[j] = new_data[j];
1045
1046 //prevent multiple patching
1047 inst_iter->set_label(block_graph::BlockGraph::Label());
1048 found++;
1049 delete[] new_data;
1050 }
1051 } // end for
1052
1053 return true;
1054 }
1055
1056 bool IntegrityCheckTransform::RecomputeXorChunks(
1057 const uint64_t bb_id, const uint8_t old_size[],
1058 const uint8_t new_size[], const uint64_t chunk_bb_id,
1059 const uint32_t chunk_index){
1060
1061 DCHECK_EQ(bb_id, chunk_bb_id);
1062
1063 uint32_t vector_index =
1064 (*ic_block_chunk_index_map_)[GetChunkUniqueKey(chunk_bb_id,chunk_index)];
1065
1066 DCHECK_GE(vector_index, static_cast<uint32_t>(0));
1067 DCHECK_LT(vector_index, ic_block_reference_free_chunks->size());
1068
1069 auto chunk = (*ic_block_reference_free_chunks)[vector_index];
1070 DCHECK(chunk.block_id_ == chunk_bb_id && chunk.chunk_index_ == chunk_index);
1071 ////make sure we found the right chunk
1072 DCHECK(sizeof(old_size) == sizeof(new_size));
1073
1074 uint8_t new_hash = chunk.hash_;
1075 for (uint32_t i = 0; i < sizeof(old_size); i++)
1076 {
1077 //cancel out previous value
1078 new_hash ^= old_size[i];
1079 //compute new hash
1080 new_hash ^= new_size[i];
1081 }
1082
1083 chunk.hash_ = new_hash;
1084 (*ic_block_reference_free_chunks)[vector_index] = chunk;
1085 return true;
1086 }
1087
1088 bool IsSize(BasicBlock::Instructions::iterator instruction_itr){
1089 std::string size_label = "size";
1090 if (instruction_itr->has_label() &&
1091 instruction_itr->label().name()
1092 .compare(0, size_label.length(), size_label) == 0){
1093 return true;
1094 }
1095 return false;
1096 }
1097 bool IsPivot(BasicBlock::Instructions::iterator instruction_itr){
1098 std::string pivot_label = "Pivot:";
1099 if (instruction_itr->has_label() &&
1100 instruction_itr->label().name()
1101 .compare(0, pivot_label.length(), pivot_label) == 0){
1102 return true;
1103 }
1104 return false;
1105 }
1106
1107 bool HasAbsoluteReferences(BasicBlock::Instructions::iterator instruction_itr){
1108 if (instruction_itr->references().size() > 0){
1109 auto ref_it = instruction_itr->references().begin();
1110 for (; ref_it != instruction_itr->references().end(); ++ref_it){
1111 if (ref_it->second.reference_type() ==
1112 block_graph::BlockGraph::ReferenceType::ABSOLUTE_REF)
1113 return true;
1114 }
1115 }
1116 return false;
1117 }
1118
1119 void
1120 IntegrityCheckTransform::AddChunkIntoIndexMap(const uint64_t bb_id,
1121 const uint32_t chunk_index,
1122 const uint32_t vector_index){
1123 auto unique_chunk_key = GetChunkUniqueKey(bb_id,chunk_index);
1124 //make sure the key is really unique!
1125 DCHECK(ic_block_chunk_index_map_->find(unique_chunk_key) ==
1126 ic_block_chunk_index_map_->end());
1127 (*ic_block_chunk_index_map_)[unique_chunk_key] = vector_index;
1128 }
1129
1130 void IntegrityCheckTransform::ComputeChunks(BasicCodeBlock* bb){
1131 auto inst_iter = bb->instructions().begin();
1132 if (inst_iter == bb->instructions().end())
1133 return;
1134
1135 BlockGraph::Label label(inst_iter->label());
1136 uint64_t bb_id = GetBasicBlockIdByLabel(label, this->id_to_label_);
1137 if (bb_id == -1)
1138 return;
1139 std::map<uint64_t, int> checkee_list =
1140 (*this->checker_to_checkee_map_)[bb_id];
1141
1142 if (checkee_list.size() < 1)
1143 return;
1144
1145 //uint32_t reference_free_start_offset = 0;
1146 uint32_t reference_free_size = 0;
1147 uint8_t reference_free_hash = 0;
1148 uint32_t reference_free_index = 0;
1149 uint16_t size_in_bytes = 0;
1150 uint8_t num_found_pivots = 0;
1151 uint32_t current_inst_size = 0;
1152 std::string bb_id_label = std::to_string(bb_id);
1153 bool has_references=false;
1154 bool has_abs_references = false;
1155 bool is_pivot=false;
1156 // Process all instructions in BB
1157 for (; inst_iter != bb->instructions().end(); ++inst_iter) {
1158
1159 current_inst_size = (*inst_iter).size();
1160 size_in_bytes += current_inst_size;
1161 const uint8_t *b2 = (*inst_iter).data();
1162
1163 uint8_t instruction_hash = 0;
1164 for (uint32_t i = 0; i < current_inst_size; ++i){
1165 instruction_hash ^= (*b2);
1166 b2++;
1167 }
1168 if (IsPivot(inst_iter)){
1169 ++num_found_pivots;
1170 }
1171 has_abs_references = HasAbsoluteReferences(inst_iter);
1172 has_references = (inst_iter->references().size() > 0);
1173
1174 is_pivot = IsPivot(inst_iter);
1175 if (!has_references && !is_pivot){
1176 //we cannot place two labels on the same instruction, so if the beginning
1177 //of the chunk has a label we skip it.
1178 //In order to keep the first instruction of the block in a chunk without
1179 //changing its label, we accept the block id label as a finger for the
1180 //beginning of the chunk.
1181 if (reference_free_size != 0 || !inst_iter->has_label()
1182 || inst_iter->label().name().compare(bb_id_label)==0){
1183 //this is the first instruction in the chunk where we place our label
1184 //we don't need to put label at the first instruction, because it has
1185 //block id label, first instruction label is detected when reference
1186 //free index equals zero
1187 if (reference_free_size == 0 && reference_free_index!=0){
1188 char *buffer = MakeChunkLabel(bb_id, reference_free_index);
1189 auto label = BlockGraph::Label(buffer, BlockGraph::CODE_LABEL);
1190 delete[] buffer;
1191 DCHECK(!inst_iter->has_label());
1192 inst_iter->set_label(label);
1193 }
1194 //keep counting
1195 reference_free_size += current_inst_size;
1196 reference_free_hash ^= instruction_hash;
1197 }
1198 } else if (reference_free_size > 0){
1199 //add offset and size of the reference free chunk
1200 ic_block_reference_free_chunks->push_back(
1201 ChunkInfo(bb_id, reference_free_size, reference_free_hash,
1202 reference_free_index,
1203 has_abs_references?current_inst_size:0));
1204 AddChunkIntoIndexMap(bb_id, reference_free_index++,
1205 ic_block_reference_free_chunks->size() - 1);
1206 //once we add the chunk, we reset the size
1207 reference_free_size = 0;
1208 reference_free_hash = 0;
1209 }
1210 } //end for
1211
1212 //the last chunk of the instructions need to be added (if any)
1213 if (reference_free_size > 0) {
1214 //add offset and size of the reference free chunk
1215 ic_block_reference_free_chunks->push_back(
1216 ChunkInfo(bb_id, reference_free_size, reference_free_hash,
1217 reference_free_index, 0));
1218 AddChunkIntoIndexMap(bb_id, reference_free_index,
1219 ic_block_reference_free_chunks->size() - 1);
1220 //once we add the chunk, we reset the size
1221 reference_free_size = reference_free_hash = 0;
1222 }
1223
1224 //Exactly one pivot must be in each IC block
1225 DCHECK_EQ(num_found_pivots, 1);
1226 }
1227
1228 uint8_t IntegrityCheckTransform::PrecomputeHash(
1229 BasicCodeBlock* bb,
1230 std::list<uint32_t> *offset_sizes,
1231 BasicBlockSubGraph* subgraph) {
1232 DCHECK_NE(reinterpret_cast<BasicCodeBlock*>(NULL), bb);
1233
1234 if (bb->instructions().size() <= 0)
1235 return 0;
1236
1237 uint16_t offset_in_bytes = 0;
1238 uint16_t size_in_bytes = 0;
1239 uint64_t bb_address = GetBasicBlockId(bb, subgraph);
1240
1241 // Match and rewrite based on patterns.
1242 auto inst_iter = bb->instructions().begin();
1243 size_in_bytes = 0;
1244 uint8_t partition_key = 0;
1245 BlockGraph::Label label;
1246
1247 label = BlockGraph::Label(std::to_string(bb_address),
1248 BlockGraph::CODE_LABEL);
1249 inst_iter->set_label(label);
1250
1251 (*this->id_to_label_)[bb_address] = label;
1252 fprintf(this->prefile_, "\n\n%llx\n", std::stoull(label.name()));
1253
1254 // Process all instructions in BB
1255 for (; inst_iter != bb->instructions().end(); ++inst_iter) {
1256 uint32_t size = (*inst_iter).size();
1257 uint8_t nr_refs_in_key = partition_key;
1258
1259 if (PopulatePartitionKey((*inst_iter), &partition_key)) {
1260 this->basic_block_has_ref_[bb_address] = true;
1261
1262 int nr_added = partition_key - nr_refs_in_key;
1263 if (nr_added == 1) {
1264 uint64_t label_nr = bb_address + ((uint64_t)size_in_bytes << 32);
1265 label = BlockGraph::Label(std::to_string(label_nr),
1266 BlockGraph::CODE_LABEL);
1267
1268 if (inst_iter->has_label()) {
1269 BlockGraph::Label existing_label = inst_iter->label();
1270 } else {
1271 inst_iter->set_label(label);
1272 }
1273 }
1274 }
1275
1276 size_in_bytes += size;
1277 } // end for
1278
1279 // put the last bytes in the basic block in the list of precomputed hashes
1280 if ((size_in_bytes > 0)) { // don't put chunks of 0 size on stack
1281 uint32_t offset_size = (offset_in_bytes << 16) | size_in_bytes;
1282 offset_sizes->push_front(offset_size);
1283 }
1284
1285 // populate precomputed hashes and bb-sizes
1286 if (partition_key > 0) {
1287 std::set<uint64_t> v = this->partition_map_[partition_key];
1288 v.insert(bb_address);
1289 this->partition_map_[partition_key] = v;
1290 // save precomputed hash
1291 (*this->precomputed_hashes_)[bb_address] = 0;
1292 (*this->basic_block_sizes_)[bb_address] = size_in_bytes;
1293
1294 } else if (size_in_bytes > 0) {
1295 std::set<uint64_t> v = this->partition_map_[0];
1296 v.insert(bb_address);
1297 this->partition_map_[0] = v;
1298 // save precomputed hash
1299 (*this->precomputed_hashes_)[bb_address] = 0;
1300 (*this->basic_block_sizes_)[bb_address] = size_in_bytes;
1301 }
1302
1303 bb_address += ((uint64_t)offset_in_bytes << 32);
1304 return 1;
1305 }
1306
1307 bool IntegrityCheckTransform::TransformBasicBlockSubGraph(
1308 BlockGraph* bgraph,
1309 BasicBlockSubGraph* subgraph,
1310 IntegrityCheckTransform::ProcessingType step) {
1311 DCHECK_NE(reinterpret_cast<BlockGraph*>(NULL), bgraph);
1312
1313 if (step == IntegrityCheckTransform::ADD_HASH_AND_RESPONSE) {
1314 this->hash_block_ = AddHashFunction(bgraph);
1315 this->xhash_block_ = AddXorHashFunction(bgraph);
1316 this->response_block_ = AddResponseFunction(bgraph);
1317
1318 return (this->hash_block_ && this->xhash_block_ && this->response_block_);
1319
1320 } else {
1321 DCHECK_NE(reinterpret_cast<BasicBlockSubGraph*>(NULL), subgraph);
1322 std::list<uint32_t> instr_sizes;
1323 uint8_t min_instructions = 0;
1324 BasicBlockSubGraph::BBCollection& basic_blocks =
1325 subgraph->basic_blocks(); // set of BB to protect
1326
1327 // Iterate over every basic block and insert integrity-checks
1328 for (auto it = basic_blocks.begin(); it != basic_blocks.end(); ++it) {
1329 BasicCodeBlock* bb = BasicCodeBlock::Cast(*it);
1330 if ((bb == NULL) || (bb->instructions().size() < min_instructions))
1331 continue;
1332 switch (step) {
1333 case IntegrityCheckTransform::PRECOMPUTE_HASHES: {
1334 PrecomputeHash(bb, &instr_sizes, subgraph);
1335 break;
1336 }
1337 case IntegrityCheckTransform::INSERT_CHECKS: {
1338 AddIntegrityCheckCode(bb, subgraph, bgraph);
1339 break;
1340 }
1341 case IntegrityCheckTransform::COMPUTE_CHUNKS:{
1342 ComputeChunks(bb);
1343 break;
1344 }
1345 case IntegrityCheckTransform::INSERT_CHUNK_CHECKS:{
1346 AddChunkIntegrityCheckCode(bb, subgraph, bgraph);
1347 break;
1348 }
1349 case IntegrityCheckTransform::PATCH_REFERENCES_SIZES: {
1350 PatchBlockReferencesAndSizes(bb, subgraph, bgraph);
1351 break;
1352 }
1353 default:
1354 DbgRaiseAssertionFailure();
1355 break;
1356 }
1357 } // end for
1358 } // end else
1359 return true;
1360 }
1361
1362 uint8_t IntegrityCheckTransform::GetPartitionKey(uint64_t bb_id) {
1363 auto it = this->partition_map_.begin();
1364 for (; it != this->partition_map_.end(); ++it) {
1365 if (it->second.find(bb_id) != it->second.end())
1366 return it->first;
1367 }
1368
1369 return 0;
1370 }
1371
1372 void IntegrityCheckTransform::AddIntegrityCheckCode(
1373 BasicCodeBlock* bb,
1374 BasicBlockSubGraph* subgraph,
1375 BlockGraph *block_graph) {
1376 auto inst_iter = bb->instructions().begin();
1377 if (inst_iter == bb->instructions().end())
1378 return;
1379
1380 BlockGraph::Label label(inst_iter->label());
1381 uint64_t bb_id = GetBasicBlockIdByLabel(label, this->id_to_label_);
1382
1383 if (bb_id == -1)
1384 return;
1385
1386 uint8_t hash = 0;
1387 std::map<uint64_t, int> checkee_list =
1388 (*this->checker_to_checkee_map_)[bb_id];
1389
1390 if (checkee_list.size() < 1)
1391 return;
1392
1393 // Count number of absolute references in basic block
1394 uint8_t no_abs_references = 0;
1395 // Count number of instructions in basic block
1396 uint32_t no_bb_instructions = bb->instructions().size();
1397 uint32_t no_orig_bb_instructions = bb->instructions().size();
1398 std::map<uint32_t, uint64_t> checkee_label_map;
1399 if (this->insert_file_ != NULL){
1400 fprintf(this->insert_file_, "%s,", label.name().c_str());
1401 }
1402 // Remove old label from the beginning of the original code
1403 inst_iter->set_label(BlockGraph::Label());
1404
1405 block_graph::BasicBlockAssembler assm(inst_iter,
1406 &bb->instructions());
1407
1408 //in case we add chunk checker these pushes will be added by the chunk
1409 //checker
1410 if (!*perform_chunk_checks_){
1411 assm.push(assm::eax);
1412 assm.push(assm::ebx);
1413 assm.push(assm::ecx);
1414 assm.push(assm::edx);
1415 }
1416
1417 assm.lea(assm::ecx, block_graph::Operand(
1418 block_graph::Displacement(checkee_list.size(),
1419 assm::ValueSize::kSize32Bit)));
1420
1421 uint32_t *checkee_size_index = new uint32_t[checkee_list.size()];
1422 uint32_t *checkee_reference_index = new uint32_t[checkee_list.size()];
1423
1424 uint32_t pivot_instruction_index = 0;
1425 uint32_t sub_instruction_index = 0;
1426 uint32_t k = 0;
1427 uint32_t reference_index = 0;
1428 auto checkee_it = checkee_list.begin();
1429 int last_coefficient = 0;
1430 for (; checkee_it != checkee_list.end(); ++checkee_it) {
1431
1432 if (last_coefficient == checkee_it->second){
1433 LOG(INFO) << "found equal coeffs";
1434 }
1435 last_coefficient = checkee_it->second;
1436 assm.push(block_graph::Immediate(checkee_it->second,
1437 assm::ValueSize::kSize32Bit));
1438
1439 // push the number of checkees of the checkee
1440 uint32_t nr_of_checkees =
1441 (*this->checker_to_checkee_map_)[checkee_it->first].size();
1442 no_abs_references += nr_of_checkees + GetPartitionKey(bb_id) +
1443 this->num_chunks_per_block;
1444 //Here still we don't know how many chunks this checker is going to check
1445 //depending on the coverage config and total number of discovered chunks
1446 //this number should be added to the nr_of_chechees
1447 //nr_of_checkees += this->num_chunks_per_block;
1448 checkee_reference_index[reference_index++] =
1449 bb->instructions().size() - no_orig_bb_instructions;
1450 assm.push(block_graph::Immediate(nr_of_checkees,
1451 assm::ValueSize::kSize32Bit));
1452
1453 // Count the number of instructions added so far into the basic block
1454 // This information is used to set a label on the following push instr
1455 checkee_size_index[k++] = bb->instructions().size() - no_bb_instructions;
1456 no_bb_instructions = bb->instructions().size();
1457
1458 // push the size of the checkee
1459 uint32_t size_of_checkee = (*this->basic_block_sizes_)[checkee_it->first];
1460 assm.push(block_graph::Immediate(size_of_checkee,
1461 assm::ValueSize::kSize32Bit));
1462
1463 BlockGraph::Label checkee_label =
1464 (*this->id_to_label_)[checkee_it->first];
1465 std::pair<BlockGraph::Block*, uint32_t> block_offset_pair =
1466 (*this->label_name_to_block_)[checkee_label.name()];
1467 BlockGraph::Block* checkee_block = block_offset_pair.first;
1468 uint32_t checkee_offset = block_offset_pair.second;
1469
1470 DCHECK(checkee_block != NULL);
1471
1472 checkee_label_map.insert(std::make_pair(
1473 bb->instructions().size() - no_orig_bb_instructions, checkee_it->first));
1474 if (this->insert_file_ != NULL){
1475 fprintf(this->insert_file_, "%s,", checkee_label.name().c_str());
1476 }
1477 if (checkee_block->id() != subgraph->original_block()->id()) {
1478 assm.push(block_graph::Immediate(checkee_block, checkee_offset));
1479 } else { // checkee is in the same subgraph as checker
1480 BasicBlock *checkee_bb =
1481 GetBasicBlockAtOffset(subgraph, checkee_offset);
1482 DCHECK(checkee_bb != NULL);
1483 assm.push(block_graph::Immediate(checkee_bb));
1484 }
1485
1486 hash += (*this->precomputed_hashes_)[checkee_it->first] *
1487 checkee_it->second;
1488 }
1489 if (this->insert_file_ != NULL) {
1490 fprintf(this->insert_file_, "\n");
1491 }
1492
1493 // 2 stack slots holding accumulator for hash and hash of return address
1494 assm.sub(assm::esp, block_graph::Immediate(0x8));
1495
1496 // get size in bytes of the code inserted so far
1497 uint32_t call_offset = 0;
1498 uint32_t no_added_instructions = bb->instructions().size() -
1499 no_orig_bb_instructions;
1500 auto inst_iter3 = bb->instructions().begin();
1501 for (uint32_t k = 0; (inst_iter3 != bb->instructions().end()) &&
1502 (k < no_added_instructions); ++inst_iter3, ++k) {
1503 call_offset += inst_iter3->size();
1504 }
1505 this->basic_block_hash_call_offset_[bb_id] = call_offset;
1506
1507 assm.call(block_graph::Immediate(this->hash_block_, 0));
1508 //keep the index of the pivot byte/instruction
1509 pivot_instruction_index =
1510 bb->instructions().size() - no_orig_bb_instructions;
1511
1512 assm.data((uint8_t)0);
1513 //let the result be in the stack so later we can retrieve it
1514 uint32_t no_pushed_words = 4 * checkee_list.size() + 2;
1515 assm.add(assm::esp, block_graph::Immediate(no_pushed_words * 4));
1516 //checksum from the xor function must be added to the add checksum result
1517 if (*perform_chunk_checks_) {
1518 assm.pop(assm::ebx);
1519 assm.add(assm::al, assm::bl);
1520 } else {
1521 // If we are not checking chunks we don't need to pop the runtime computed
1522 // hash of the chunks. However, Syzygy loses the label added to the sub
1523 // instruction (next instruction) because it tries to disassemble the data
1524 // byte after the call to the hash function, which leads to different
1525 // instructions than during execution. Label will be misaligned. Adding
1526 // these instructions prevents runtime assertion check error about lost
1527 // labels.
1528 assm.push(block_graph::Immediate(0,assm::ValueSize::kSize32Bit));
1529 assm.pop(assm::ebx);
1530 assm.add(assm::al, assm::bl);
1531 }
1532 sub_instruction_index = bb->instructions().size() - no_orig_bb_instructions;
1533 assm.sub(assm::al, block_graph::Immediate(hash, assm::ValueSize::kSize8Bit));
1534 assm.data((uint8_t)0x66); // CBW
1535 assm.data((uint8_t)0x98);
1536 assm.xor(assm::al, assm::ah);
1537 assm.sub(assm::al, assm::ah);
1538 assm.sub(assm::al, block_graph::Immediate(no_abs_references,
1539 assm::ValueSize::kSize8Bit));
1540 assm.j(assm::ConditionCode::kAbove,
1541 block_graph::Immediate(this->response_block_, 0));
1542
1543 assm.pop(assm::edx);
1544 assm.pop(assm::ecx);
1545 assm.pop(assm::ebx);
1546 assm.pop(assm::eax);
1547
1548 // Add label to begining of integrity check
1549 label = BlockGraph::Label(std::to_string(bb_id),
1550 BlockGraph::CODE_LABEL);
1551 inst_iter = bb->instructions().begin();
1552 inst_iter->set_label(label);
1553
1554 (*this->id_to_label_)[bb_id] = label;
1555 uint32_t num_no_chunk_added = 0;
1556 uint32_t ref_instruction_index = 0;
1557 // Update the size of the basic block to include integrity check code
1558 // and add the sub instruction label
1559 uint32_t new_size = 0;
1560 for (uint32_t s = 0; inst_iter != bb->instructions().end(); ++inst_iter, s++)
1561 {
1562 new_size += inst_iter->size();
1563 auto checkee_label_iter = checkee_label_map.find(s);
1564 if (checkee_label_iter != checkee_label_map.end()){
1565 char *buffer = new char[50];
1566 sprintf_s(buffer, 50, "block %llu %llu",
1567 checkee_label_iter->second,bb_id);
1568 //LOG(INFO) << " Assigned pivot label: " << buffer;
1569 label = BlockGraph::Label(base::StringPiece(buffer),
1570 BlockGraph::CODE_LABEL);
1571 inst_iter->set_label(label);
1572 delete[] buffer;
1573 } else if (s == pivot_instruction_index) { // add the pivot label
1574 char *buffer = new char[50];
1575 sprintf_s(buffer, 50, "Pivot:%llu", bb_id);
1576 //LOG(INFO) << " Assigned pivot label: " << buffer;
1577 label = BlockGraph::Label(base::StringPiece(buffer),
1578 BlockGraph::CODE_LABEL);
1579 inst_iter->set_label(label);
1580 delete[] buffer;
1581 } else if (s == sub_instruction_index) { // add the pivot label
1582 char buffer[50];
1583 sprintf_s(buffer, 50, "sub %llu", bb_id);
1584 //LOG(INFO) << " Assigned pivot label: " << buffer;
1585 label = BlockGraph::Label(buffer, BlockGraph::CODE_LABEL);
1586 inst_iter->set_label(label);
1587 }
1588 else if (*perform_chunk_checks_ &&
1589 ref_instruction_index < checkee_list.size() &&
1590 s == checkee_reference_index[ref_instruction_index]) {
1591 // add the no_checkee label
1592 ++ref_instruction_index;
1593 char *buffer = new char[50];
1594 sprintf_s(buffer, 50, "ref %llu", bb_id);
1595 //LOG(INFO) << " Assigned num chunk label: " << s <<" "<< buffer;
1596 label = BlockGraph::Label(base::StringPiece(buffer),
1597 BlockGraph::CODE_LABEL);
1598 inst_iter->set_label(label);
1599 num_no_chunk_labels++;
1600 num_no_chunk_added++;
1601 delete[] buffer;
1602 }
1603 }
1604 uint32_t old_size = (*this->basic_block_sizes_)[bb_id];
1605 DCHECK_GT(new_size, static_cast<uint32_t>(0x49));
1606 DCHECK(old_size < new_size);
1607 if (*perform_chunk_checks_){
1608 CHECK_EQ(num_no_chunk_added, checkee_list.size());
1609 }
1610 (*this->basic_block_sizes_)[bb_id] = new_size;// - call_offset;
1611
1612 //Set iterator to the beginning of the list
1613 inst_iter = bb->instructions().begin();
1614
1615
1616 checkee_it = checkee_list.begin();
1617 // Add labels to instructions which push basic block size
1618 for (uint32_t k = 0; k < checkee_list.size(); ++k) {
1619 for (uint32_t j = 0; j < checkee_size_index[k]; ++j) {
1620 inst_iter++;
1621 }
1622 char *buffer=new char[50];
1623 sprintf_s(buffer, 50, "size %llu %llu", checkee_it->first, bb_id);
1624 checkee_it++;
1625 label = BlockGraph::Label(base::StringPiece(buffer),
1626 BlockGraph::CODE_LABEL);
1627 inst_iter->set_label(label);
1628 delete[] buffer;
1629 num_size_reference_labels++;
1630 }
1631
1632 delete[] checkee_size_index;
1633 delete[] checkee_reference_index;
1634 return; //remove this when you have inner BB references
1635 }
1636 uint32_t num_protecting_blocks = 0;
1637
1638 bool IntegrityCheckTransform::ProcessAllBlocks(
1639 const TransformPolicyInterface* policy,
1640 BlockGraph* block_graph,
1641 IntegrityCheckTransform::ProcessingType step) {
1642 BlockOrdering order;
1643 FlattenCallGraphPostOrder(block_graph, &order);
1644 #if defined PRINT_BLOCK_NAMES
1645 std::ofstream blocknames_file;
1646 blocknames_file.open("block_names.csv");
1647 #endif
1648 auto block_iter = order.begin();
1649 for (; block_iter != order.end(); ++block_iter) {
1650 BlockGraph::Block* block = *block_iter;
1651 #if defined PRINT_BLOCK_NAMES
1652 if (!policy->BlockIsSafeToBasicBlockDecompose(block))
1653 continue;
1654 blocknames_file <<block->name()<<",\n";
1655 continue;
1656 #endif
1657 if (!ShouldProcessBlock(block, this->target_names_))
1658 continue;
1659 // Use the decomposition policy to skip blocks that aren't eligible for
1660 // basic-block decomposition.
1661 if (!policy->BlockIsSafeToBasicBlockDecompose(block))
1662 continue;
1663
1664 // Decompose block to basic blocks.
1665 BasicBlockSubGraph *subgraph = new BasicBlockSubGraph();
1666 BasicBlockDecomposer bb_decomposer(block, subgraph);
1667 if (!bb_decomposer.Decompose())
1668 return false;
1669
1670 if (!TransformBasicBlockSubGraph(
1671 block_graph, subgraph, step)) {
1672 return false;
1673 }
1674
1675 // Update the block-graph post transform.
1676 BlockBuilder builder(block_graph);
1677 if (!builder.Merge(subgraph)) {
1678 return false;
1679 }
1680 ++num_protecting_blocks;
1681
1682 const BlockVector& blocks = builder.new_blocks();
1683 auto new_block = blocks.begin();
1684 for (; new_block != blocks.end(); ++new_block) {
1685 // This is needed until the labels refactoring.
1686 (*new_block)->set_attribute(BlockGraph::BUILT_BY_SYZYGY);
1687
1688 if (step == INSERT_CHECKS || INSERT_CHUNK_CHECKS) {
1689 UpdateLabelToBlockMap(*new_block);
1690 }
1691 }
1692 }
1693 #if defined PRINT_BLOCK_NAMES
1694 blocknames_file.close();
1695 exit(1);
1696 #endif
1697 return true;
1698 }
1699
1700 uint64_t
1701 IntegrityCheckTransform::GetChunkOriginalBlockId(const ChunkInfo *chunk){
1702 if (chunk->original_block_id_ == 0){
1703 bool before_chunk_integrity_code_added = true;
1704 char* chunk_label = MakeChunkLabel(chunk->block_id_, chunk->chunk_index_,
1705 before_chunk_integrity_code_added);
1706 auto chunk_label_it = label_name_to_block_->find(chunk_label);
1707 CHECK(chunk_label_it != label_name_to_block_->end());
1708 delete[] chunk_label;
1709 chunk->original_block_id_ = chunk_label_it->second.first->id();
1710 }
1711 return chunk->original_block_id_;
1712 }
1713
1714 std::set<uint32_t> IntegrityCheckTransform:: PickChunks(
1715 const std::vector<ChunkInfo> chunks_vector,
1716 const std::vector<uint32_t> partition_indexes,
1717 const uint32_t num_picks,
1718 const uint64_t checker_block_id,
1719 const std::vector<uint32_t>::iterator end_chunk_it,
1720 std::vector<uint32_t>::iterator last_visited_chunk,
1721 std::set<uint32_t> *unused_chunks){
1722 std::set<uint32_t> picked_set;
1723
1724
1725 //attempt to pick from unused chunks
1726 for (auto unused_chunk_it = unused_chunks->begin();
1727 unused_chunk_it != unused_chunks->end() && picked_set.size() < num_picks;){
1728 uint64_t chunk_orig_block_id = GetChunkOriginalBlockId(
1729 &chunks_vector[*unused_chunk_it]);
1730 if (chunk_orig_block_id != checker_block_id){
1731 picked_set.insert(*unused_chunk_it);
1732 unused_chunks->erase(unused_chunk_it);
1733 unused_chunk_it = unused_chunks->begin();
1734 }
1735 else {
1736 ++unused_chunk_it;
1737 }
1738 }
1739
1740 //iterate over chunks
1741 for (; last_visited_chunk != end_chunk_it && picked_set.size() < num_picks;
1742 ++last_visited_chunk){
1743 uint64_t chunk_orig_block_id = GetChunkOriginalBlockId(
1744 &chunks_vector[*last_visited_chunk]);
1745 if (chunk_orig_block_id != checker_block_id){
1746 picked_set.insert(*last_visited_chunk);
1747 } else {
1748 unused_chunks->insert(*last_visited_chunk);
1749 }
1750 }//end for
1751
1752 // if we don't have enough unique chunk, then we pick from
1753 // already visited chunks
1754 if (picked_set.size() < num_picks){
1755 for (auto index_it = partition_indexes.begin();
1756 index_it != partition_indexes.end() && picked_set.size() < num_picks;
1757 ++index_it){
1758 uint64_t chunk_orig_block_id = GetChunkOriginalBlockId(
1759 &chunks_vector[*index_it]);
1760 if (chunk_orig_block_id != checker_block_id){
1761 picked_set.insert(*index_it);
1762 }//end if
1763 }//end for
1764 }//end if
1765
1766 DCHECK_EQ(picked_set.size(), num_picks);
1767 return picked_set;
1768 }
1769
1770 std::map<uint64_t, std::set<uint32_t>>
1771 IntegrityCheckTransform::GenerateChunkCombinations(
1772 const std::vector<ChunkInfo> chunks_vector,
1773 const float chunk_coverage, const bool enforce_unique_chunks,
1774 uint32_t *no_chunks_per_block){
1775
1776 DCHECK_GT(chunk_coverage, static_cast<float>(0));
1777 DCHECK_LE(chunk_coverage, static_cast<float>(10));
1778
1779
1780 std::vector<ChunkInfo> temp_chunk_vector = chunks_vector;
1781 std::vector<uint32_t> temp_noref_chunk_vector;
1782 std::vector<uint32_t> temp_ref_chunk_vector;
1783 int i = 0;
1784 //partition chunks based on their next instruction's absolute reference
1785 //status
1786 for (auto chunk_it = temp_chunk_vector.begin();
1787 chunk_it != temp_chunk_vector.end(); ++chunk_it,++i){
1788 if (chunk_it->next_instruction_size_ == 0)
1789 temp_noref_chunk_vector.push_back(i);
1790 else
1791 temp_ref_chunk_vector.push_back(i);
1792 }
1793
1794 //shuffle chunks to make sure that checkers check integrity of random blocks
1795 auto engine = std::default_random_engine{};
1796 std::shuffle(std::begin(temp_noref_chunk_vector),
1797 std::end(temp_noref_chunk_vector), engine);
1798
1799 std::shuffle(std::begin(temp_ref_chunk_vector),
1800 std::end(temp_ref_chunk_vector), engine);
1801
1802 //compute number of chunks according to the input coverage
1803 uint32_t total_chunk_checks = chunks_vector.size() * chunk_coverage;
1804 uint32_t num_ref_chunks = 0;
1805 int32_t num_noref_chunks = 0;
1806 //preference is to pick chunks with abs address at the end
1807 if (temp_ref_chunk_vector.size() >= total_chunk_checks) {
1808 num_ref_chunks = total_chunk_checks;
1809 num_noref_chunks = 0;
1810 } else if(chunk_coverage <= 1.0f) {
1811 num_ref_chunks = temp_ref_chunk_vector.size();
1812 num_noref_chunks = total_chunk_checks - num_ref_chunks;
1813 } else {
1814 num_ref_chunks = std::min(
1815 static_cast<uint32_t>(temp_ref_chunk_vector.size()* chunk_coverage),
1816 total_chunk_checks);
1817 num_noref_chunks = total_chunk_checks - num_ref_chunks;
1818 }
1819
1820 uint32_t no_chunks_per_checker = total_chunk_checks /
1821 checker_to_checkee_map_->size();
1822
1823 //the base address cancelation only works for even number of chunks
1824 if (no_chunks_per_checker % 2 != 0){
1825 LOG(INFO) << "current coverage does not generate even number of chunks, "
1826 << "thus the number of chunks was incremented!";
1827 no_chunks_per_checker++;
1828 }
1829
1830 LOG(INFO) << "chunk coverage:" << chunk_coverage;
1831 LOG(INFO) << "#all chunks:" << total_chunk_checks;
1832 LOG(INFO) << "#chunks per checker:" << no_chunks_per_checker;
1833 LOG(INFO) << "#+chunks (with absolute instruction):" << num_ref_chunks;
1834 LOG(INFO) << "#^chunks (no absolute instruction):" << num_noref_chunks;
1835 *no_chunks_per_block = no_chunks_per_checker;
1836
1837 DCHECK_GE(no_chunks_per_checker, static_cast<uint32_t>(1));
1838
1839 auto checker_it = checker_to_checkee_map_->begin();
1840 std::set<uint32_t> unused_noref_chunks;
1841 std::set<uint32_t> unused_ref_chunks;
1842 std::map<uint64_t, std::set<uint32_t>> temp_assignment_map;
1843 auto noref_chunk_it = temp_noref_chunk_vector.begin();
1844 auto ref_chunk_it = temp_ref_chunk_vector.begin();
1845
1846 auto noref_chunk_end_it = temp_noref_chunk_vector.end();
1847 auto ref_chunk__end_it = temp_ref_chunk_vector.end();
1848
1849 while (checker_it != checker_to_checkee_map_->end()){
1850 std::set<uint32_t> chunks;
1851 uint64_t bb_id = checker_it->first;
1852 BlockGraph::Label checker_label = (*this->id_to_label_)[bb_id];
1853 auto checker_label_it = label_name_to_block_->find(checker_label.name());
1854 CHECK(checker_label_it != label_name_to_block_->end());
1855 uint64_t checker_block_id = checker_label_it->second.first->id();
1856 //first pick from no reference partition
1857 if (num_noref_chunks > 0){
1858 chunks = PickChunks(chunks_vector, temp_noref_chunk_vector,
1859 no_chunks_per_checker, checker_block_id,
1860 noref_chunk_end_it, noref_chunk_it,
1861 &unused_noref_chunks);
1862 num_noref_chunks -= no_chunks_per_checker;
1863 } else { // pick the rest of chunks from reference chunks
1864 chunks = PickChunks(chunks_vector, temp_ref_chunk_vector,
1865 no_chunks_per_checker, checker_block_id,
1866 ref_chunk__end_it, ref_chunk_it,
1867 &unused_ref_chunks);
1868 }
1869 temp_assignment_map[bb_id] = chunks;
1870 ++checker_it;
1871 }
1872
1873 return temp_assignment_map;
1874 }
1875
1876 void IntegrityCheckTransform::GenerateBasicBlockCombinations() {
1877 int partition_num = 1;
1878 int nr_size_one = 0;
1879 srand(time(NULL));
1880
1881 FILE* part_file = NULL;
1882 fopen_s(&part_file, "partitions.csv", "w");
1883 if (part_file == NULL)
1884 LOG(INFO) << "Cannot open partition file";
1885
1886 auto it_part = this->partition_map_.begin();
1887 for (; it_part != this->partition_map_.end(); ++it_part) {
1888 LOG(INFO) << "Partition #" << partition_num << " : ";
1889 LOG(INFO) << (*it_part).second.size();
1890
1891 if ((*it_part).second.size() <= 1) {
1892 /*
1893 std::list<std::set<uint64_t>> checkOrder;
1894 checkOrder.push_back((*it_part).second);
1895 checkOrder.push_back((*it_part).second);
1896 */
1897 ++nr_size_one;
1898 } else { // there are multiple BBs in this partition
1899 PopulateCheckMaps((*it_part).second);
1900 }
1901
1902 ++partition_num;
1903 }
1904
1905 // check if any blocks are not checking anything
1906 auto checker_it = id_to_label_->begin();
1907 for (; checker_it != id_to_label_->end(); ++checker_it){
1908 auto checkee_list = (*checker_to_checkee_map_)[checker_it->first];
1909 if (checkee_list.size() == 0) { // then this BB is not checking other BBs.
1910 // Find a pair of basic blocks to check.
1911 it_part = this->partition_map_.begin();
1912 std::map<uint64_t, int> checkee_map;
1913 bool found_pair = false;
1914
1915 for (; it_part != this->partition_map_.end(); ++it_part) {
1916 if (it_part->second.size() < 2) // partition is too small
1917 continue;
1918 // Check if partition has at least 2 BBs that are not in the same block
1919 // as the checker
1920 uint32_t checker_block = (uint32_t) checker_it->first;
1921 std::set<uint64_t> bbs_in_different_block;
1922 auto part_block_it = it_part->second.begin();
1923
1924 for (; part_block_it != it_part->second.end(); ++part_block_it) {
1925 if (checker_block != ((uint32_t)*part_block_it))
1926 bbs_in_different_block.insert(*part_block_it);
1927 }
1928 if (bbs_in_different_block.size() > 1) { // use first 2 BBs
1929 auto checkee_it = bbs_in_different_block.begin();
1930 checkee_map[*checkee_it] = 1;
1931 checkee_it++;
1932 checkee_map[*checkee_it] = -1;
1933 found_pair = true;
1934 break;
1935 }
1936 }
1937
1938 DCHECK(checkee_map.size() == 2);
1939 (*checker_to_checkee_map_)[checker_it->first] = checkee_map;
1940 }
1941 }
1942
1943 fclose(part_file);
1944
1945 LOG(INFO) << "nr_size_one : " << nr_size_one;
1946 }
1947
1948 bool IntegrityCheckTransform::TransformBlockGraph(
1949 const TransformPolicyInterface* policy,
1950 BlockGraph* block_graph,
1951 BlockGraph::Block* header_block) {
1952 fopen_s(&this->pfile_, "integrityChecks.csv", "w");
1953 if (this->pfile_ == NULL)
1954 LOG(INFO) << "Cannot open graph file";
1955
1956 if (!TransformBasicBlockSubGraph(
1957 block_graph, NULL,
1958 IntegrityCheckTransform::ADD_HASH_AND_RESPONSE)) {
1959 return false;
1960 }
1961
1962 fopen_s(&this->prefile_, "preChecks.csv", "w");
1963 if (this->prefile_ == NULL)
1964 LOG(INFO) << "Cannot open graph file";
1965
1966 num_protecting_blocks = 0;
1967 // Compute the hash of all basic blocks in all blocks of the block_graph.
1968 // This hash will be hard-coded inside the integrity-check-code inserted in
1969 // each basic block. It will be compared with the hash computed at runtime.
1970 if (!ProcessAllBlocks(policy, block_graph,
1971 IntegrityCheckTransform::PRECOMPUTE_HASHES))
1972 return false;
1973
1974 if(num_protecting_blocks, this->target_names_.size())
1975 LOG(INFO) << "Failed to find some targets, protected blocks:"
1976 << num_protecting_blocks << " provided:"
1977 << this->target_names_.size();
1978
1979 fclose(this->prefile_);
1980
1981 GenerateBasicBlockCombinations();
1982
1983 fclose(this->pfile_);
1984
1985 int nr_not_checked = 0;
1986 int total_number = 0;
1987 //Print all nodes not checked by any other nodes
1988 auto map_it = this->precomputed_hashes_->begin();
1989 for (; map_it != this->precomputed_hashes_->end(); ++map_it) {
1990 if (this->is_bb_checked_map_.find((*map_it).first) ==
1991 this->is_bb_checked_map_.end()) {
1992 //LOG(INFO) << "BB " << (*mapIt).first << " is not checked ";
1993 ++nr_not_checked;
1994 }
1995 ++total_number;
1996 }
1997
1998 int nr_3_combo_found = 0;
1999 LOG(INFO) << "Combo 3 Found: " << nr_3_combo_found;
2000 LOG(INFO) << "Not Checked: " << nr_not_checked;
2001 LOG(INFO) << "Total number:" << total_number;
2002
2003 fopen_s(&this->insert_file_, "inserted-integrityChecks.csv", "w");
2004 if (this->insert_file_ == NULL)
2005 LOG(INFO) << "Cannot open graph file";
2006
2007 GenerateLabelToBlockMap(block_graph);
2008
2009 // Add the assembly code representing integrity checks in each basic block
2010 // that was picked to perform a dynamic check in the combination of basic
2011 // blocks (see method GenerateBasicBlockCombinations()).
2012 if (!ProcessAllBlocks(policy, block_graph,
2013 IntegrityCheckTransform::INSERT_CHECKS))
2014 return false;
2015
2016 fclose(this->insert_file_);
2017 LOG(INFO) << "Inserting checks done";
2018
2019 fopen_s(&this->fix_file_, "fixIntegrityChecks.csv", "w");
2020 if (this->fix_file_ == NULL)
2021 LOG(INFO) << "Cannot open graph file";
2022
2023 if (*perform_chunk_checks_){
2024 if (!ProcessAllBlocks(policy, block_graph,
2025 IntegrityCheckTransform::COMPUTE_CHUNKS))
2026 return false;
2027 LOG(INFO) << "Computing integrity inter block chunks is done";
2028
2029 //Require label update
2030 GenerateLabelToBlockMap(block_graph);
2031
2032
2033 //shuffle up integrity chunks
2034 *ic_chunk_checker_to_checkee_map_ = GenerateChunkCombinations(
2035 *ic_block_reference_free_chunks,
2036 chunk_checking_coverage,
2037 kForceUniqueChunks,
2038 &num_chunks_per_block);
2039
2040 LOG(INFO) << "Shuffling integrity inter block chunks is done";
2041
2042 if (!ProcessAllBlocks(policy, block_graph, INSERT_CHUNK_CHECKS))
2043 return false;
2044 LOG(INFO) << "Inserting chunk checks is done";
2045 } else {
2046 LOG(INFO) << "Xor chunk protection is switched off.";
2047 }
2048 //Require label update
2049 GenerateLabelToBlockMap(block_graph);
2050
2051 // Patch inter block references that were broken by the insertion of
2052 // integrity checks.
2053 if (!ProcessAllBlocks(policy, block_graph,
2054 IntegrityCheckTransform::PATCH_REFERENCES_SIZES)) {
2055 return false;
2056 }
2057
2058 LOG(INFO) << "Patching block references and sizes are done";
2059 LOG(INFO) << "Elapsed seconds in patching chunks(due to size changes:"
2060 <<elapsed_secs_in_patching_chunks;
2061 CHECK_EQ(num_chunk_reference_labels , num_chunk_reference_patched_labels);
2062 CHECK_EQ(num_no_chunk_labels, num_no_chunk_patched_labels);
2063 CHECK_EQ(num_size_reference_labels, num_size_reference_patched_labels);
2064 if (num_size_reference_labels != num_size_reference_patched_labels){
2065 LOG(ERROR) << "Some size labels were not patched, total lables:" <<
2066 num_size_reference_labels << " patched:"
2067 << num_size_reference_patched_labels;
2068 }
2069
2070 //Require label update
2071 GenerateLabelToBlockMap(block_graph);
2072
2073 fclose(this->fix_file_);
2074
2075 std::map<uint64_t, uint32_t> checkee_count_checker;
2076 std::ofstream myfile;
2077 myfile.open("graph.csv");
2078 auto checker_it = this->checker_to_checkee_map_->begin();
2079 for (; checker_it != this->checker_to_checkee_map_->end();
2080 ++checker_it) {
2081 for (auto checkee_it = checker_it->second.begin();
2082 checkee_it != checker_it->second.end();
2083 ++checkee_it) {
2084 myfile << checker_it->first << "," << checkee_it->first << "\n ";
2085 ++checkee_count_checker[checkee_it->first];
2086 }
2087 } //end for
2088 myfile.close();
2089 myfile.open("notbeingchecked.csv");
2090 checker_it = checker_to_checkee_map_->begin();
2091 for (; checker_it != this->checker_to_checkee_map_->end();
2092 ++checker_it) {
2093 if (checkee_count_checker.find(checker_it->first) ==
2094 checkee_count_checker.end()) {
2095 myfile << checker_it->first << "\n";
2096 }
2097 }
2098 myfile.close();
2099 #if defined COMPUTE_CHECKER_SIZE
2100 myfile.open("checkersize.csv");
2101 myfile << "total checker size(byte):"<<total_checker_size;
2102 myfile.close();
2103 #endif
2104 myfile.open("chunkinfo.csv");
2105 myfile << "total chunks:" << ic_block_reference_free_chunks->size();
2106 myfile << "total checked chunks:" << checker_to_checkee_map_->size() *
2107 num_chunks_per_block;
2108 myfile.close();
2109 myfile.open("chunkgraph.csv");
2110 for (auto chunk_checker_it = ic_chunk_checker_to_checkee_map_->begin();
2111 chunk_checker_it != ic_chunk_checker_to_checkee_map_->end();
2112 ++chunk_checker_it) {
2113 for (auto chunk_checkee_it = chunk_checker_it->second.begin();
2114 chunk_checkee_it != chunk_checker_it->second.end();
2115 ++chunk_checkee_it) {
2116 myfile << chunk_checker_it->first << "," <<
2117 (*ic_block_reference_free_chunks)[*chunk_checkee_it].b lock_id_
2118 <<"\n";
2119 }
2120 }
2121 myfile.close();
2122 return true;
2123 }
2124
2125 IntegrityCheckTransform::~IntegrityCheckTransform() {
2126 ic_block_reference_free_chunks->clear();
2127 ic_block_chunk_index_map_->clear();
2128 ic_chunk_checker_to_checkee_map_->clear();
2129 //_CrtDumpMemoryLeaks();
2130 }
2131
2132 // static vars
2133 const char IntegrityCheckTransform::kTransformName[] =
2134 "IntegrityCheckTransform";
2135
2136 }// namespace protect
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698