Skip to content

Implement kernel ARM emulation #1531

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: dev
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 32 additions & 3 deletions qiling/loader/elf.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
from qiling.const import QL_ARCH, QL_ENDIAN, QL_OS
from qiling.exception import QlErrorELFFormat, QlMemoryMappedError
from qiling.loader.loader import QlLoader, Image
from qiling.os.memory import QlMemoryHeap
from qiling.os.linux.function_hook import FunctionHook
from qiling.os.linux.syscall_nums import SYSCALL_NR
from qiling.os.linux.kernel_api.hook import *
Expand Down Expand Up @@ -57,7 +58,7 @@ class AUXV(IntEnum):

# start area memory for API hooking
# we will reserve 0x1000 bytes for this (which contains multiple slots of 4/8 bytes, each for one api)
API_HOOK_MEM = 0x1000000
API_HOOK_MEM = 0x2000000
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Any reason for the change?


# memory for syscall table
SYSCALL_MEM = API_HOOK_MEM + 0x1000
Expand Down Expand Up @@ -87,6 +88,9 @@ def run(self):
stack_size = self.profile.getint('stack_size')
self.ql.mem.map(stack_address, stack_size, info='[stack]')

# Setup heap
self.ql.os.heap = QlMemoryHeap(self.ql, 0x3000000, 0x3000000 + 0x1000000)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Heap base and size should be set based on the value configured in the profile file. Using hardcoded values is not a good idea.


self.path = self.ql.path

with open(self.path, 'rb') as infile:
Expand All @@ -97,7 +101,7 @@ def run(self):

# is it a driver?
if elftype == 'ET_REL':
self.load_driver(elffile, stack_address + stack_size, loadbase=0x8000000)
self.load_driver(elffile, stack_address + stack_size, loadbase=0x1000000)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Any reason for this change here?
Keep in mind this module serves all ELF files on all architectures, not only ARM.

self.ql.hook_code(hook_kernel_api)

# is it an executable?
Expand Down Expand Up @@ -414,10 +418,19 @@ def lkm_get_init(self, elffile: ELFFile) -> int:
raise QlErrorELFFormat('invalid module: symbol init_module not found')

def lkm_dynlinker(self, elffile: ELFFile, mem_start: int) -> Mapping[str, int]:
self._symbol_name_map = None
def __get_symbol(name: str) -> Optional[Symbol]:
_symtab = elffile.get_section_by_name('.symtab')
_sym = _symtab.get_symbol_by_name(name)

# Cache
if self._symbol_name_map == None:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This could be simplified with an internal function decorated with @cache (available from Python 3.9) or @lru_cache (available earlier).

from functools import cache

@cache
def __get_cached_symbol(name: str) -> int:
    # access symtab symbol here and return the result
    ...

Also, I am not sure that _symbol_name_map should be assigned to self, but stay local.

self._symbol_name_map = {}
for i, sym in enumerate(_symtab.iter_symbols()):
if sym.name not in self._symbol_name_map:
self._symbol_name_map[sym.name] = []
self._symbol_name_map[sym.name].append(sym)

_sym = None if name not in self._symbol_name_map else self._symbol_name_map[name]
return _sym[0] if _sym else None

ql = self.ql
Expand Down Expand Up @@ -555,6 +568,22 @@ def __get_symbol(name: str) -> Optional[Symbol]:
ql.mem.write_ptr(prev_mips_hi16_loc + 2, (val >> 16), 2)
ql.mem.write_ptr(loc + 2, (val & 0xFFFF), 2)

elif desc in ('R_ARM_CALL', 'R_ARM_JUMP24'):
ins = ql.mem.read_ptr(loc, 4) & 0xFF000000
val = (((rev_reloc_symbols[symbol_name] - loc) >> 2) - 2) & 0x00FFFFFF
ql.mem.write_ptr(loc, ins | val)

elif desc == "R_ARM_ABS32":
val = ql.mem.read_ptr(loc, 4)
val += rev_reloc_symbols[symbol_name]
ql.mem.write_ptr(loc, (val & 0xFFFFFFFF), 4)

elif desc == "R_ARM_PREL31":
ql.log.warning(f'Ignoring relocation type {desc} at 0x{loc:x} for symbol "{symbol_name}" (0x{rev_reloc_symbols[symbol_name]:x})')

elif desc == "R_ARM_NONE":
ql.log.warning(f'Ignoring relocation type {desc} at 0x{loc:x} for symbol "{symbol_name}" (0x{rev_reloc_symbols[symbol_name]:x})')

else:
raise NotImplementedError(f'Relocation type {desc} not implemented')

Expand Down
7 changes: 6 additions & 1 deletion qiling/os/linux/kernel_api/hook.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
#

from qiling import Qiling
from qiling.const import QL_INTERCEPT
from qiling.const import QL_INTERCEPT, QL_ARCH
from qiling.exception import QlErrorSyscallError, QlErrorSyscallNotFound

# import all kernel api hooks to global namespace
Expand All @@ -25,6 +25,11 @@ def hook_kernel_api(ql: Qiling, address: int, size):
if api_func:
try:
api_func(ql, address, api_name)

# Restore PC
if ql.arch.type == QL_ARCH.ARM:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think function calls are automatically unwinded.
Have you checked this is?

ql.arch.regs.arch_sp -= ql.arch.pointersize
ql.arch.regs.arch_pc = ql.arch.regs.lr
except Exception:
ql.log.exception("")
ql.log.debug("%s Exception Found" % api_name)
Expand Down
25 changes: 22 additions & 3 deletions qiling/os/linux/kernel_api/kernel_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -115,10 +115,28 @@ def hook_kmalloc_caches(ql, address, params):
})
def hook_kmalloc(ql, address, params):
size = params['size']
addr = ql.heap.alloc(size)
addr = ql.os.heap.alloc(size)
return addr


@linux_kernel_api(params={
"size": SIZE_T,
"flags": INT
})
def hook___kmalloc(ql, address, params):
size = params['size']
addr = ql.os.heap.alloc(size)
return addr


@linux_kernel_api(params={
"p": POINTER
})
def hook_ksize(ql, address, params):
p = params['p']
size = ql.os.heap.size(p)
return size

@linux_kernel_api(params={
"dest": POINTER,
"c": INT,
Expand Down Expand Up @@ -147,10 +165,11 @@ def hook_get_by_key(ql, address, params):


@linux_kernel_api(params={
"Ptr": POINTER
"p": POINTER
})
def hook_kfree(ql, address, params):
pass
p = params['p']
ql.os.heap.free(p)


@linux_kernel_api(params={
Expand Down