mirror of
https://github.com/trailofbits/manticore.git
synced 2026-10-12 02:23:44 +00:00
This is primarily a helper function that is useful in evaluating the capabilities of Manticore on Linux * Add Linux Kernel version constant to syscalls list * Add method for getting unimplemented syscalls This is specific per architecture
151 lines
4.7 KiB
Python
Executable File
151 lines
4.7 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
|
|
"""
|
|
Generate syscall tables from the Linux source. Used to generate
|
|
manticore/platforms/linux_syscalls.py.
|
|
|
|
This fetches the tables from kernel.org.
|
|
|
|
Usage:
|
|
|
|
./extract_syscalls.py [--linux_version linux_version] linux_syscalls.py
|
|
|
|
"""
|
|
|
|
import argparse
|
|
import os
|
|
import re
|
|
import subprocess
|
|
import sys
|
|
import tempfile
|
|
from urllib.request import urlopen
|
|
|
|
BASE_URL = (
|
|
"https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/plain/{}?id=refs/tags/v{}"
|
|
)
|
|
|
|
# Use an associative list rather than a dict to get deterministic output.
|
|
ARCH_TABLES = [
|
|
("i386", "arch/x86/entry/syscalls/syscall_32.tbl"),
|
|
("amd64", "arch/x86/entry/syscalls/syscall_64.tbl"),
|
|
("armv7", "arch/arm/tools/syscall.tbl"),
|
|
]
|
|
|
|
BITSPERLONG_HDR = "arch/{}/include/uapi/asm/bitsperlong.h"
|
|
ARCH_UNISTD_HDR = "arch/{}/include/uapi/asm/unistd.h"
|
|
UNISTD_HDR = "include/uapi/asm-generic/unistd.h"
|
|
|
|
# Format: Manticore arch, Linux arch.
|
|
# XXX: Code that uses this might need to be tweaked for other architectures to
|
|
# work properly.
|
|
UNISTD = [("aarch64", "arm64")]
|
|
|
|
__ARM_NR_BASE = 0xF0000
|
|
ADDITIONAL_SYSCALLS = {
|
|
"armv7": [
|
|
("sys_ARM_NR_breakpoint", __ARM_NR_BASE + 1),
|
|
("sys_ARM_NR_cacheflush", __ARM_NR_BASE + 2),
|
|
("sys_ARM_NR_usr26", __ARM_NR_BASE + 3),
|
|
("sys_ARM_NR_usr32", __ARM_NR_BASE + 4),
|
|
("sys_ARM_NR_set_tls", __ARM_NR_BASE + 5),
|
|
]
|
|
}
|
|
|
|
|
|
def open_url(url):
|
|
res = urlopen(url)
|
|
if res.code // 100 != 2:
|
|
sys.stderr.write("Failed retrieving file; check version and connection.\n")
|
|
sys.stderr.write(f"Url: {url}\n")
|
|
sys.exit(1)
|
|
return res
|
|
|
|
|
|
def write_without_includes(f, res):
|
|
for line in res.readlines():
|
|
line = line.decode()
|
|
line = line.strip()
|
|
if not line.startswith("#include"):
|
|
f.write(line + "\n")
|
|
|
|
|
|
if __name__ == "__main__":
|
|
parser = argparse.ArgumentParser(description="Generate syscall tables")
|
|
parser.add_argument("output", help="Python output to generate tables")
|
|
parser.add_argument(
|
|
"--linux_version", help="Major version of the Linux kernel to use", default="4.11"
|
|
)
|
|
args = parser.parse_args()
|
|
|
|
output = open(args.output, "w+")
|
|
output.write("#\n#\n# AUTOGENERATED, DO NOT EDIT\n#\n\n")
|
|
output.write(f'LINUX_KERNEL_VERSION = "{args.linux_version}"\n\n')
|
|
|
|
for arch, path in ARCH_TABLES:
|
|
url = BASE_URL.format(path, args.linux_version)
|
|
res = open_url(url)
|
|
|
|
output.write(f"{arch} = {{\n")
|
|
|
|
for line in res.readlines():
|
|
line = line.decode()
|
|
line = line.strip()
|
|
if line.startswith("#"):
|
|
continue
|
|
columns = line.split()
|
|
if len(columns) < 4:
|
|
continue
|
|
num, abi, name, entry = columns[:4]
|
|
output.write(f' {num}: "{entry}",\n')
|
|
|
|
for entry, num in ADDITIONAL_SYSCALLS.get(arch, {}):
|
|
output.write(f' {num}: "{entry}",\n')
|
|
|
|
output.write("}\n")
|
|
|
|
for march, larch in UNISTD:
|
|
bitsperlong_hdr = BITSPERLONG_HDR.format(larch)
|
|
arch_unistd_hdr = ARCH_UNISTD_HDR.format(larch)
|
|
|
|
bitsperlong_url = BASE_URL.format(bitsperlong_hdr, args.linux_version)
|
|
arch_unistd_url = BASE_URL.format(arch_unistd_hdr, args.linux_version)
|
|
unistd_url = BASE_URL.format(UNISTD_HDR, args.linux_version)
|
|
|
|
bitsperlong_res = open_url(bitsperlong_url)
|
|
arch_unistd_res = open_url(arch_unistd_url)
|
|
unistd_res = open_url(unistd_url)
|
|
|
|
syscall_rx = "SYSCALL: (\d+) ([a-z_0-9]+)"
|
|
syscall_define = "#define __SYSCALL(nr, sym) SYSCALL: nr sym"
|
|
|
|
output.write(f"{march} = {{\n")
|
|
|
|
fd, tmp_path = tempfile.mkstemp()
|
|
try:
|
|
with os.fdopen(fd, "w") as tmp:
|
|
# The order is important here for CPP to work correctly.
|
|
tmp.write(syscall_define + "\n")
|
|
write_without_includes(tmp, bitsperlong_res)
|
|
write_without_includes(tmp, arch_unistd_res)
|
|
write_without_includes(tmp, unistd_res)
|
|
|
|
process = subprocess.Popen(
|
|
["cpp", "-E", tmp_path], stdout=subprocess.PIPE, encoding="utf-8"
|
|
)
|
|
out, _ = process.communicate()
|
|
lines = out.split("\n")
|
|
for line in lines:
|
|
m = re.search(syscall_rx, line)
|
|
if m:
|
|
num = m.group(1)
|
|
entry = m.group(2)
|
|
if entry != "sys_ni_syscall": # not implemented syscall
|
|
output.write(f' {num}: "{entry}",\n')
|
|
finally:
|
|
os.remove(tmp_path)
|
|
|
|
for entry, num in ADDITIONAL_SYSCALLS.get(march, {}):
|
|
output.write(f' {num}: "{entry}",\n')
|
|
|
|
output.write("}\n")
|