Repository navigation
Expand file tree
/
Copy pathX64InstructionRewriter.cpp
More file actions
172 lines (139 loc) · 7.14 KB
/
Copy pathX64InstructionRewriter.cpp
File metadata and controls
172 lines (139 loc) · 7.14 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
#include <codegen/x86/X64InstructionRewriter.hpp>
#include <codegen/x86/X64InstructionDecoder.hpp>
#include <codegen/x86/X64OpcodeConstants.hpp>
#include <codegen/CodegenException.hpp>
#include <codegen/IInstructionScanner.hpp>
#include <cstdint>
#include <limits>
namespace Codegen {
using namespace X64OpcodeConstants;
X64InstructionRewriter::ReferenceSite X64InstructionRewriter::ClassifyInstruction(
const std::uint8_t* data,
std::size_t length
) {
const auto info = X64InstructionDecoder{}.DecodeInstruction(data, length);
if (info.HasRipRelativeDisp) {
return {ReferenceKind::RipRelativeDisp32, info.RipRelativeDispOffset};
}
if (!info.HasBranchTarget) {
return {ReferenceKind::None, 0};
}
const bool rel32 = info.IsTwoByteOpcode || info.Opcode == OneByteCallRel32 || info.Opcode == OneByteJmpRel32;
return rel32
? ReferenceSite{ReferenceKind::Rel32Branch, info.Length - Disp32Size}
: ReferenceSite{ReferenceKind::Rel8Branch, info.Length - Disp8Size};
}
std::int32_t X64InstructionRewriter::ReadInt32(const std::uint8_t* data) {
return static_cast<std::int32_t>(
static_cast<std::uint32_t>(data[0]) |
(static_cast<std::uint32_t>(data[1]) << 8) |
(static_cast<std::uint32_t>(data[2]) << 16) |
(static_cast<std::uint32_t>(data[3]) << 24));
}
void X64InstructionRewriter::WriteInt32(std::uint8_t* data, std::int32_t value) {
const auto unsignedValue = static_cast<std::uint32_t>(value);
data[0] = static_cast<std::uint8_t>(unsignedValue & 0xFF);
data[1] = static_cast<std::uint8_t>((unsignedValue >> 8) & 0xFF);
data[2] = static_cast<std::uint8_t>((unsignedValue >> 16) & 0xFF);
data[3] = static_cast<std::uint8_t>((unsignedValue >> 24) & 0xFF);
}
std::int64_t X64InstructionRewriter::AdjustedDelta(
std::int64_t instrPosition,
std::int64_t targetPosition,
std::int64_t cutStart,
std::int64_t cutEnd,
std::int64_t sliceSize,
std::int64_t delta
) {
const bool instrShifts = instrPosition >= cutEnd && instrPosition < sliceSize;
const bool targetShifts = targetPosition >= cutEnd && targetPosition < sliceSize;
if (instrShifts == targetShifts) {
return 0;
}
return instrShifts ? -delta : delta;
}
RewriteResult X64InstructionRewriter::Rewrite(
const std::vector<std::uint8_t>& codeSection,
const RewriteRequest& request
) const {
const auto offset = static_cast<std::size_t>(request.Offset);
if (offset > codeSection.size()) {
throw CodegenException("Rewrite offset is out of bounds", request.Offset);
}
const auto scanner = MakeInstructionScanner();
const auto matches = scanner->ScanCodeSection(codeSection, 0, codeSection.size());
bool instructionStart = false;
for (const auto& match : matches) {
if (match.Offset == request.Offset) {
instructionStart = true;
break;
}
}
if (!instructionStart)
throw CodegenException("Rewrite offset is not an instruction boundary", request.Offset);
const X64InstructionDecoder decoder;
const std::size_t decodedLength = decoder.Decode(codeSection.data() + offset, codeSection.size() - offset);
const auto oldLength = static_cast<std::int64_t>(decodedLength);
const auto newLength = static_cast<std::int64_t>(request.NewBytes.size());
const std::int64_t delta = newLength - oldLength;
const auto cutStart = static_cast<std::int64_t>(offset);
const std::int64_t cutEnd = cutStart + oldLength;
std::vector<std::uint8_t> rewritten;
rewritten.reserve(codeSection.size() + static_cast<std::size_t>(delta > 0 ? delta : 0));
rewritten.insert(rewritten.end(), codeSection.begin(), codeSection.begin() + offset);
rewritten.insert(rewritten.end(), request.NewBytes.begin(), request.NewBytes.end());
rewritten.insert(rewritten.end(), codeSection.begin() + offset + decodedLength, codeSection.end());
std::vector<AddressAdjustment> adjustments;
for (const auto& match : matches) {
const auto matchOffset = static_cast<std::int64_t>(match.Offset);
if (matchOffset >= cutStart && matchOffset < cutEnd) {
continue;
}
const auto site = ClassifyInstruction(codeSection.data() + match.Offset, match.Length);
if (site.Kind != ReferenceKind::None) {
const std::int64_t instrEnd = matchOffset + static_cast<std::int64_t>(match.Length);
const std::size_t fieldPosition = static_cast<std::size_t>(match.Offset) + site.FieldOffset;
if (site.Kind == ReferenceKind::Rel8Branch) {
const auto disp = static_cast<std::int8_t>(codeSection[fieldPosition]);
const std::int64_t targetPosition = instrEnd + disp;
const std::int64_t adjust = AdjustedDelta(
matchOffset, targetPosition, cutStart, cutEnd,
static_cast<std::int64_t>(codeSection.size()), delta);
if (adjust != 0) {
const std::int64_t newDisp = static_cast<std::int64_t>(disp) + adjust;
if (newDisp < std::numeric_limits<std::int8_t>::min() ||
newDisp > std::numeric_limits<std::int8_t>::max()) {
throw CodegenException("Rel8 branch displacement overflow after rewrite", match.Offset);
}
const std::int64_t newMatchOffset = matchOffset < cutStart ? matchOffset : matchOffset + delta;
const auto newFieldPosition = static_cast<std::size_t>(
newMatchOffset + static_cast<std::int64_t>(site.FieldOffset));
rewritten[newFieldPosition] = static_cast<std::uint8_t>(static_cast<std::int8_t>(newDisp));
}
} else {
const auto disp = ReadInt32(codeSection.data() + fieldPosition);
const std::int64_t targetPosition = instrEnd + disp;
const std::int64_t adjust = AdjustedDelta(
matchOffset, targetPosition, cutStart, cutEnd,
static_cast<std::int64_t>(codeSection.size()), delta);
if (adjust != 0) {
const std::int64_t newDisp = static_cast<std::int64_t>(disp) + adjust;
if (newDisp < std::numeric_limits<std::int32_t>::min() ||
newDisp > std::numeric_limits<std::int32_t>::max()) {
throw CodegenException("Rel32/disp32 displacement overflow after rewrite", match.Offset);
}
const std::int64_t newMatchOffset = matchOffset < cutStart ? matchOffset : matchOffset + delta;
const auto newFieldPosition = static_cast<std::size_t>(
newMatchOffset + static_cast<std::int64_t>(site.FieldOffset));
WriteInt32(rewritten.data() + newFieldPosition, static_cast<std::int32_t>(newDisp));
}
}
}
if (matchOffset >= cutEnd && delta != 0) {
const auto newOffset = static_cast<Domain::FileByteOffset>(matchOffset + delta);
adjustments.push_back({match.Offset, newOffset, delta});
}
}
return {rewritten, adjustments};
}
}