feat(relinker): load the fs:[0] thread pointer into any register in Windows guest code
This commit is contained in:
parent
bc51b337ad
commit
53d4ef41d2
|
|
@ -18,6 +18,7 @@ struct TlsAccess {
|
|||
std::size_t Length;
|
||||
bool StoreImmediate;
|
||||
std::uint32_t Immediate;
|
||||
std::uint8_t Register;
|
||||
};
|
||||
|
||||
void patchAccess(std::vector<PeSection>& sections, const TlsAccess& access, const std::uint32_t target) {
|
||||
|
|
@ -76,11 +77,12 @@ PeDirectory WindowsTlsBuilder::Build(const std::vector<std::uint8_t>& source, co
|
|||
if (value == 0x66) hasOperandSizePrefix = true;
|
||||
else if (value != 0x64 && !(prefix + 1 == position && value >= 0x40 && value <= 0x4f)) supportedPrefixes = false;
|
||||
}
|
||||
const bool loadPointer = supportedPrefixes && info.RexPrefix == 0x48 && info.Length - position == 7 && bytes[position] == 0x8b && bytes[position + 1] == 0x04 && bytes[position + 2] == 0x25 && Io::ReadU32(source, header.Offset + offset + position + 3) == 0;
|
||||
const auto loadRegister = info.Length - position == 7 ? static_cast<std::uint8_t>(((bytes[position + 1] >> 3) & 7) | ((info.RexPrefix & 4) << 1)) : std::uint8_t{4};
|
||||
const bool loadPointer = supportedPrefixes && (info.RexPrefix == 0x48 || info.RexPrefix == 0x4c) && loadRegister != 4 && bytes[position] == 0x8b && (bytes[position + 1] & 0xc7) == 0x04 && bytes[position + 2] == 0x25 && Io::ReadU32(source, header.Offset + offset + position + 3) == 0;
|
||||
const bool storeImmediate = supportedPrefixes && !hasOperandSizePrefix && (info.RexPrefix == 0 || info.RexPrefix == 0x40) && info.Length - position == 11 && bytes[position] == 0xc7 && bytes[position + 1] == 0x04 && bytes[position + 2] == 0x25 && Io::ReadU32(source, header.Offset + offset + position + 3) == 0x28;
|
||||
if (!loadPointer && !storeImmediate)
|
||||
throw Domain::RelinkerException("Unsupported Windows guest TLS instruction", header.Offset + offset);
|
||||
accesses.push_back({rva, info.Length, storeImmediate, storeImmediate ? Io::ReadU32(source, header.Offset + offset + position + 7) : 0});
|
||||
accesses.push_back({rva, info.Length, storeImmediate, storeImmediate ? Io::ReadU32(source, header.Offset + offset + position + 7) : 0, storeImmediate ? std::uint8_t{0} : loadRegister});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -135,15 +137,21 @@ PeDirectory WindowsTlsBuilder::Build(const std::vector<std::uint8_t>& source, co
|
|||
if (target != branchTargets.end() && *target < access.Rva + access.Length)
|
||||
throw Domain::RelinkerException("Branch enters a guest TLS instruction", *target);
|
||||
patchAccess(sections, access, code.GetRva());
|
||||
code.Emit({0x48, 0x8d, 0x64, 0x24, 0x80, 0x51});
|
||||
if (access.StoreImmediate) code.Emit({0x50});
|
||||
const bool preserveAccumulator = access.StoreImmediate || access.Register != 0;
|
||||
const bool preserveCounter = access.Register != 1;
|
||||
code.Emit({0x48, 0x8d, 0x64, 0x24, 0x80});
|
||||
if (preserveCounter) code.Emit({0x51});
|
||||
if (preserveAccumulator) code.Emit({0x50});
|
||||
loadPointer();
|
||||
if (access.StoreImmediate) {
|
||||
code.Emit({0xc7, 0x40, 0x28});
|
||||
code.U32(access.Immediate);
|
||||
code.Emit({0x58});
|
||||
} else if (access.Register != 0) {
|
||||
code.Emit({static_cast<std::uint8_t>(0x48 | (access.Register >> 3)), 0x89, static_cast<std::uint8_t>(0xc0 | (access.Register & 7))});
|
||||
}
|
||||
code.Emit({0x59, 0x48, 0x8d, 0xa4, 0x24, 0x80, 0, 0, 0});
|
||||
if (preserveAccumulator) code.Emit({0x58});
|
||||
if (preserveCounter) code.Emit({0x59});
|
||||
code.Emit({0x48, 0x8d, 0xa4, 0x24, 0x80, 0, 0, 0});
|
||||
code.Rip({0xe9}, CheckedRva(access.Rva + access.Length));
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -11,7 +11,22 @@ from test_optional_plt import fixture
|
|||
TLS_LOAD = bytes.fromhex("66 66 66 64 48 8b 04 25 00 00 00 00")
|
||||
|
||||
|
||||
def make_image(transfer, metadata, extent=None):
|
||||
def register_load(register):
|
||||
high, low = register >> 3, register & 7
|
||||
extension = b"\x41" if high else b""
|
||||
save = extension + bytes([0x50 + low]) if register else b""
|
||||
restore = extension + bytes([0x58 + low]) if register else b""
|
||||
prefix = save + bytes.fromhex("b8 07 00 00 00 b9 05 00 00 00")
|
||||
load = bytes([0x64, 0x48 | high << 2, 0x8b, 0x04 | low << 3, 0x25, 0, 0, 0, 0])
|
||||
rest = extension + bytes([0x8b, 0x40 | low]) + (b"\x24" if low == 4 else b"") + b"\xf0" + restore + b"\xc3"
|
||||
if register != 1:
|
||||
rest = bytes.fromhex("83 f9 05 75") + bytes([len(rest)]) + rest
|
||||
if register != 0:
|
||||
rest = bytes.fromhex("83 f8 07 75") + bytes([len(rest)]) + rest
|
||||
return len(prefix), prefix + load + rest + restore + bytes.fromhex("31 c0 c3")
|
||||
|
||||
|
||||
def make_image(transfer, metadata, extent=None, body=None):
|
||||
image = fixture()
|
||||
image.extend(b"\x90" * 0x1000)
|
||||
struct.pack_into("<Q", image, 24, 0x1200)
|
||||
|
|
@ -31,7 +46,8 @@ def make_image(transfer, metadata, extent=None):
|
|||
else:
|
||||
raise ValueError(transfer)
|
||||
image[0x1200:0x1200 + len(code)] = code
|
||||
body = TLS_LOAD + bytes.fromhex("8b 40 f0 c3")
|
||||
if body is None:
|
||||
body = TLS_LOAD + bytes.fromhex("8b 40 f0 c3")
|
||||
image[target:target + len(body)] = body
|
||||
image[0x1850:0x1850 + len(TLS_LOAD)] = TLS_LOAD
|
||||
struct.pack_into("<QQq", image, 0x700, 0x300, 8, 0x1200)
|
||||
|
|
@ -103,6 +119,13 @@ def main():
|
|||
external[0x1300:0x1310] = external[0x1240:0x1250]
|
||||
external[0x1240:0x1250] = b"\xe8" + struct.pack("<i", 0x1300 - 0x1245) + b"\xc3" + b"\x90" * 10
|
||||
convert("direct-call-from-indirect-block", external, tls_address=0x1300)
|
||||
for register in range(16):
|
||||
offset, body = register_load(register)
|
||||
image = make_image("register", "unwind", body=body)
|
||||
if register == 4:
|
||||
convert("load-register-4", image, "Unsupported Windows guest TLS instruction")
|
||||
else:
|
||||
convert("load-register-" + str(register), image, tls_address=0x1240 + offset)
|
||||
conflicting = make_image("register", "symbol")
|
||||
unwind = make_image("register", "unwind", 0x51)
|
||||
conflicting[0x900:0x9a0] = unwind[0x900:0x9a0]
|
||||
|
|
|
|||
Loading…
Reference in New Issue