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 : #include "syzygy/pe/hot_patching_writer.h"
16 :
17 : namespace pe {
18 :
19 : namespace {
20 :
21 : using block_graph::BlockGraph;
22 :
23 : // Finalizes the references in a block that has been copied to executable
24 : // memory. This will go through all references of the block and writes the final
25 : // absolute of PC-relative address to the block at the offset of the reference.
26 : // @param block The block whose references should be finalized.
27 : // @pre This assumes that the data of the block has been laid out to its final
28 : // address. Also, all referred blocks must be backed up by in-memory
29 : // executable data.
30 E : void FinalizeReferences(BlockGraph::Block* block) {
31 E : DCHECK_NE(static_cast<BlockGraph::Block*>(nullptr), block);
32 :
33 : using block_graph::BlockGraph;
34 : typedef BlockGraph::Offset Offset;
35 :
36 : // Loop through the references and update them in the laid out block.
37 E : for (auto entry : block->references()) {
38 E : Offset offset = entry.first;
39 E : const BlockGraph::Reference& ref = entry.second;
40 :
41 : // We are going to write the new value to this memory address.
42 E : uint8* src_addr = const_cast<uint8*>(block->data()) + offset;
43 E : DCHECK(src_addr >= block->data());
44 E : DCHECK(src_addr < block->data() + block->data_size() - ref.size() + 1);
45 :
46 : // We only support direct references. This is enough for now, because the
47 : // hot patching decomposer does not emit indirect references.
48 E : DCHECK(ref.IsDirect());
49 :
50 : // Calculate the value that we need to write.
51 E : uint32 value = 0;
52 E : switch (ref.type()) {
53 : case BlockGraph::ABSOLUTE_REF: {
54 : value = reinterpret_cast<uint32>(ref.referenced()->data() +
55 E : ref.offset());
56 E : break;
57 : }
58 : case BlockGraph::PC_RELATIVE_REF: {
59 : // PC-relative references are always the last operand of an instruction
60 : // and expressed relative to the first byte after the instruction
61 : // (hence after the reference).
62 : value = (ref.referenced()->data() + ref.offset()) -
63 E : (block->data() + offset + ref.size());
64 E : break;
65 : }
66 : default:
67 i : NOTREACHED();
68 : }
69 :
70 : // Now store the new value.
71 E : switch (ref.size()) {
72 : case sizeof(uint8): {
73 E : *reinterpret_cast<uint8*>(src_addr) = static_cast<uint8>(value);
74 E : break;
75 : }
76 : case sizeof(uint16): {
77 i : *reinterpret_cast<uint16*>(src_addr) = static_cast<uint16>(value);
78 i : break;
79 : }
80 : case sizeof(uint32): {
81 E : *reinterpret_cast<uint32*>(src_addr) = static_cast<uint32>(value);
82 E : break;
83 : }
84 : default:
85 i : NOTREACHED();
86 : }
87 E : }
88 E : }
89 :
90 : } // namespace
91 :
92 : HotPatchingWriter::HotPatchingWriter() :
93 : virtual_memory_(nullptr),
94 : virtual_memory_size_(0),
95 E : virtual_memory_cursor_(nullptr) {
96 E : }
97 :
98 E : HotPatchingWriter::~HotPatchingWriter() {
99 E : }
100 :
101 : size_t HotPatchingWriter::GetUsedMemory() const {
102 : return virtual_memory_cursor_ - reinterpret_cast<uint8*>(virtual_memory_);
103 : }
104 :
105 E : bool HotPatchingWriter::Init(size_t virtual_memory_size) {
106 : // Allocate virtual memory.
107 : virtual_memory_ = ::VirtualAlloc(nullptr,
108 : virtual_memory_size,
109 : MEM_COMMIT,
110 E : PAGE_EXECUTE_READWRITE);
111 E : if (virtual_memory_ == nullptr) {
112 i : LOG(ERROR) << "Could not allocate virtual memory for hot patching writer.";
113 i : return false;
114 : }
115 :
116 : // Set up members.
117 E : virtual_memory_cursor_ = static_cast<uint8*>(virtual_memory_);
118 E : virtual_memory_size_ = virtual_memory_size;
119 :
120 E : return true;
121 E : }
122 :
123 : HotPatchingWriter::FunctionPointer HotPatchingWriter::Write(
124 E : BlockGraph::Block* block) {
125 E : DCHECK_NE(static_cast<BlockGraph::Block*>(nullptr), block);
126 :
127 : // Respect block padding.
128 E : uint8* block_location = virtual_memory_cursor_ + block->padding_before();
129 :
130 : // Respect block alignment.
131 : block_location = reinterpret_cast<uint8*>(common::AlignUp(
132 E : reinterpret_cast<size_t>(block_location), block->alignment()));
133 :
134 : // Check if we fit into the allocated memory.
135 : if (!(block_location + block->size() <
136 E : static_cast<uint8*>(virtual_memory_) + virtual_memory_size_)) {
137 E : return false;
138 : }
139 :
140 : // Move the virtual memory cursor ahead.
141 E : virtual_memory_cursor_ = block_location + block->size();
142 :
143 : // Copy the contents of the new block to the virtual memory.
144 E : ::memcpy(block_location, block->data(), block->data_size());
145 :
146 : // Set block data to the final location!
147 E : block->SetData(block_location, block->data_size());
148 :
149 : // Update the bytes of the references to their final value.
150 E : FinalizeReferences(block);
151 :
152 E : return reinterpret_cast<FunctionPointer>(block_location);
153 E : }
154 :
155 : } // namespace pe
|