Skip to content

Commit

Permalink
Aarch64 vdso patching support
Browse files Browse the repository at this point in the history
  • Loading branch information
Keno authored and khuey committed May 23, 2020
1 parent 94cf24c commit 5aacf6e
Show file tree
Hide file tree
Showing 2 changed files with 94 additions and 12 deletions.
43 changes: 42 additions & 1 deletion src/Monkeypatcher.cc
Original file line number Diff line number Diff line change
Expand Up @@ -953,7 +953,48 @@ void patch_after_exec_arch<ARM64Arch>(RecordTask* t, Monkeypatcher& patcher) {
return;
}

FATAL() << "Unimplemented";
auto vdso_start = t->vm()->vdso().start();

VdsoReader reader(t);
auto syms = reader.read_symbols(".dynsym", ".dynstr");

static const named_syscall syscalls_to_monkeypatch[] = {
#define S(n) { "__kernel_" #n, ARM64Arch::n }
S(rt_sigreturn), S(gettimeofday), S(clock_gettime), S(clock_getres),
#undef S
};

for (auto& syscall : syscalls_to_monkeypatch) {
for (size_t i = 0; i < syms.size(); ++i) {
if (syms.is_name(i, syscall.name)) {
uintptr_t file_offset;
if (!reader.addr_to_offset(syms.addr(i), file_offset)) {
LOG(debug) << "Can't convert address " << HEX(syms.addr(i))
<< " to offset";
continue;
}

uint64_t file_offset_64 = file_offset;
static const uint64_t vdso_max_size = 0xffffLL;
uint64_t sym_offset = file_offset_64 & vdso_max_size;
uintptr_t absolute_address = vdso_start.as_int() + sym_offset;

uint8_t patch[ARM64VdsoMonkeypatch::size];
uint32_t syscall_number = syscall.syscall_number;
ARM64VdsoMonkeypatch::substitute(patch, syscall_number);

write_and_record_bytes(t, absolute_address, patch);
// Record the location of the syscall instruction, skipping the
// "mov $syscall_number,x8".
patcher.patched_vdso_syscalls.insert(
remote_code_ptr(absolute_address + 4));
LOG(debug) << "monkeypatched " << syscall.name << " to syscall "
<< syscall.syscall_number << " at "
<< HEX(absolute_address) << " (" << HEX(file_offset)
<< ")";
}
}
}
}

template <>
Expand Down
63 changes: 52 additions & 11 deletions src/assembly_templates.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,25 @@ def c_type(self):
types = { 8: 'uint64_t', 4: 'uint32_t', 2: 'uint16_t', 1: 'uint8_t' }
return types[self.byte_length]

class ShiftField(object):
"""A field embedded at some bit shift offset in another object."""
def __init__(self, parent, shift, name, byte_length):
self.parent = parent
self.shift = shift
self.name = name
self.byte_length = byte_length

def __len__(self):
return len(self.parent)

def c_type(self):
types = { 8: 'uint64_t', 4: 'uint32_t', 2: 'uint16_t', 1: 'uint8_t' }
return types[self.byte_length]

def patch_c_type(self):
types = { 8: 'uint64_t', 4: 'uint32_t', 2: 'uint16_t', 1: 'uint8_t' }
return types[len(self.parent)]

class AssemblyTemplate(object):
"""A sequence of RawBytes and Field objects, which can be used to verify
that a given sequence of assembly instructions matches the RawBytes while
Expand All @@ -33,7 +52,7 @@ def __init__(self, *chunks):
merged_chunks = []
current_raw_bytes = []
for c in chunks:
if isinstance(c, Field):
if isinstance(c, Field) or isinstance(c, ShiftField):
# Push any raw bytes before this.
if current_raw_bytes:
merged_chunks.append(RawBytes(*current_raw_bytes))
Expand All @@ -47,13 +66,15 @@ def __init__(self, *chunks):
self.chunks = merged_chunks

def fields(self):
return [c for c in self.chunks if isinstance(c, Field)]
return [c for c in self.chunks if (isinstance(c, Field) or isinstance(c, ShiftField))]

def bytes(self):
bytes = []
for c in self.chunks:
if isinstance(c, Field):
bytes.extend([0] * len(c))
elif isinstance(c, ShiftField):
bytes.extend(c.parent.bytes)
else:
bytes.extend(c.bytes)
return bytes
Expand Down Expand Up @@ -187,7 +208,14 @@ def bytes(self):
RawBytes(0x48, 0xc7, 0xc0), # movq $[syscallno], %rax
Field('syscallno', 4),
RawBytes(0x0f, 0x05) # syscall
)
),
'ARM64VdsoMonkeypatch': AssemblyTemplate(
ShiftField(
RawBytes(0x08, 0x00, 0x80, 0xd2), # movz x8, #0
5, 'syscall_number', 2),
RawBytes(0x01, 0x00, 0x00, 0xd4), # svc #0
RawBytes(0xc0, 0x03, 0x5f, 0xd6), # ret
),
}

def byte_array_name(name):
Expand All @@ -208,6 +236,9 @@ def generate_match_method(byte_array, template):
field_name = chunk.name
s.write(' memcpy(%s, &buffer[%d], sizeof(*%s));\n'
% (field_name, offset, field_name))
elif isinstance(chunk, ShiftField):
s.write(' (void)%s;' % chunk.name)
s.write(' assert(0 && "Matching not implemented for ShiftField");')
else:
s.write(' if (memcmp(&buffer[%d], &%s[%d], %d) != 0) { return false; }\n'
% (offset, byte_array, offset, len(chunk)))
Expand All @@ -216,6 +247,23 @@ def generate_match_method(byte_array, template):
s.write(' }')
return s.getvalue()

def generate_substitute_chunk(s, chunk, byte_array, offset):
if isinstance(chunk, Field):
field_name = chunk.name
s.write(' memcpy(&buffer[%d], &%s, sizeof(%s));\n'
% (offset, field_name, field_name))
elif isinstance(chunk, ShiftField):
generate_substitute_chunk(s, chunk.parent, byte_array, offset);
typ = chunk.patch_c_type()
field_name = chunk.name
s.write(' *((%s*)&buffer[%d]) |= (((%s)%s)<<%d);\n'
% (typ, offset, typ, field_name, chunk.shift))
else:
s.write(' memcpy(&buffer[%d], &%s[%d], %d);\n'
% (offset, byte_array, offset, len(chunk)))
offset += len(chunk)
return offset

def generate_substitute_method(byte_array, template):
s = StringIO()
fields = template.fields()
Expand All @@ -227,14 +275,7 @@ def generate_substitute_method(byte_array, template):
s.write(' static void substitute(uint8_t* buffer %s) {\n' % (args,))
offset = 0
for chunk in template.chunks:
if isinstance(chunk, Field):
field_name = chunk.name
s.write(' memcpy(&buffer[%d], &%s, sizeof(%s));\n'
% (offset, field_name, field_name))
else:
s.write(' memcpy(&buffer[%d], &%s[%d], %d);\n'
% (offset, byte_array, offset, len(chunk)))
offset += len(chunk)
offset = generate_substitute_chunk(s, chunk, byte_array, offset)
s.write(' }')
return s.getvalue()

Expand Down

0 comments on commit 5aacf6e

Please sign in to comment.