Skip to content

Commit 2afb3b8

Browse files
authored
Merge pull request #597 from cq674350529/feat/add_mips_lkm
add support for mips32_lkm
2 parents 668197d + add8e5c commit 2afb3b8

File tree

6 files changed

+111
-28
lines changed

6 files changed

+111
-28
lines changed

ChangeLog

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,8 @@ BREAK CHANGE
1010

1111
------------------------------------
1212
[Version 1.2.1]: December [SOMETHING], 2020
13-
-
13+
14+
- Degimod: fix lkm mapping and add support for mips32 lkm
1415

1516
------------------------------------
1617
[Version 1.2]: November 16th, 2020
2.98 KB
Binary file not shown.

qiling/loader/elf.py

Lines changed: 57 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -434,10 +434,11 @@ def lkm_get_init(self, ql):
434434
for nsym, symbol in enumerate(section.iter_symbols()):
435435
if symbol.name == 'init_module':
436436
addr = symbol.entry.st_value + elffile.get_section(symbol['st_shndx'])['sh_offset']
437-
logging.info("init_module = 0x%x" % addr)
437+
logging.info("[+] init_module = 0x%x" % addr)
438438
return addr
439439

440440
# not found. FIXME: report error on invalid module??
441+
logging.warning("[!] invalid module? symbol init_module not found")
441442
return -1
442443

443444
def lkm_dynlinker(self, ql, mem_start):
@@ -458,7 +459,8 @@ def get_symbol(elffile, name):
458459
rev_reloc_symbols = {}
459460

460461
# dump_mem("XX Original code at 15a1 = ", ql.mem.read(0x15a1, 8))
461-
for section in elffile.iter_sections():
462+
_sections = list(elffile.iter_sections())
463+
for section in _sections:
462464
# only care about reloc section
463465
if not isinstance(section, RelocationSection):
464466
continue
@@ -467,9 +469,15 @@ def get_symbol(elffile, name):
467469
if section.name == ".rela.gnu.linkonce.this_module":
468470
continue
469471

472+
dest_sec_idx = section.header.get('sh_info', None)
473+
if dest_sec_idx is not None and dest_sec_idx < len(_sections):
474+
dest_sec = _sections[dest_sec_idx]
475+
if dest_sec.header['sh_flags'] & 2 == 0:
476+
# The target section is not loaded into memory, so just continue
477+
continue
478+
470479
# The symbol table section pointed to in sh_link
471480
symtable = elffile.get_section(section['sh_link'])
472-
473481
for rel in section.iter_relocations():
474482
if rel['r_info_sym'] == 0:
475483
continue
@@ -564,15 +572,33 @@ def get_symbol(elffile, name):
564572
val = rev_reloc_symbols[symbol_name] + val - loc
565573
ql.mem.write(loc, ql.pack32(val & 0xFFFFFFFF))
566574

567-
elif describe_reloc_type(rel['r_info_type'], elffile) == 'R_386_32':
575+
elif describe_reloc_type(rel['r_info_type'], elffile) in ('R_386_32', 'R_MIPS_32'):
568576
val = ql.unpack(ql.mem.read(loc, 4))
569577
val = rev_reloc_symbols[symbol_name] + val
570578
ql.mem.write(loc, ql.pack32(val & 0xFFFFFFFF))
571579

580+
elif describe_reloc_type(rel['r_info_type'], elffile) == 'R_MIPS_HI16':
581+
# actual relocation is done in R_MIPS_LO16
582+
prev_mips_hi16_loc = loc
583+
584+
elif describe_reloc_type(rel['r_info_type'], elffile) == 'R_MIPS_LO16':
585+
val = ql.unpack16(ql.mem.read(prev_mips_hi16_loc + 2, 2)) << 16 | ql.unpack16(ql.mem.read(loc + 2, 2))
586+
val = rev_reloc_symbols[symbol_name] + val
587+
# *(word)(mips_lo16_loc + 2) is treated as signed
588+
if (val & 0xFFFF) >= 0x8000:
589+
val += (1 << 16)
590+
591+
ql.mem.write(prev_mips_hi16_loc + 2, ql.pack16(val >> 16))
592+
ql.mem.write(loc + 2, ql.pack16(val & 0xFFFF))
593+
594+
else:
595+
raise QlErrorNotImplemented("[!] Relocation type %s not implemented" % describe_reloc_type(rel['r_info_type'], elffile))
596+
572597
return rev_reloc_symbols
573598

574599
def load_driver(self, ql, stack_addr, loadbase=0):
575600
elfhead = super().parse_header()
601+
elfdata_mapping = self.get_elfdata_mapping()
576602

577603
# Determine the range of memory space opened up
578604
mem_start = -1
@@ -590,21 +616,16 @@ def load_driver(self, ql, stack_addr, loadbase=0):
590616

591617
# FIXME
592618
mem_start = 0x1000
593-
mem_end = mem_start + int(len(self.elfdata) / 0x1000 + 1) * 0x1000
619+
mem_end = mem_start + int(len(elfdata_mapping) / 0x1000 + 1) * 0x1000
594620

595621
# map some memory to intercept external functions of Linux kernel
596-
ql.mem.map(API_HOOK_MEM, 0x1000)
597-
598-
# print("load addr = %x, size = %x" %(loadbase + mem_start, mem_end - mem_start))
599-
ql.mem.map(loadbase + mem_start, mem_end - mem_start)
622+
ql.mem.map(API_HOOK_MEM, 0x1000, info="[api_mem]")
600623

601624
logging.info("[+] loadbase: %x, mem_start: %x, mem_end: %x" % (loadbase, mem_start, mem_end))
602-
603-
ql.mem.write(loadbase + mem_start, self.elfdata)
604-
# dump_mem("Dumping some bytes:", self.elfdata[0x64 : 0x84])
625+
ql.mem.map(loadbase + mem_start, mem_end - mem_start, info=ql.path)
626+
ql.mem.write(loadbase + mem_start, elfdata_mapping)
605627

606628
entry_point = self.lkm_get_init(ql) + loadbase + mem_start
607-
608629
ql.brk_address = mem_end + loadbase
609630

610631
# Set MMAP addr
@@ -619,7 +640,6 @@ def load_driver(self, ql, stack_addr, loadbase=0):
619640
new_stack = self.alignment(new_stack)
620641

621642
# self.ql.os.elf_entry = self.elf_entry = loadbase + elfhead['e_entry']
622-
623643
self.ql.os.entry_point = self.entry_point = entry_point
624644
self.elf_entry = self.ql.os.elf_entry = self.ql.os.entry_point
625645

@@ -631,7 +651,7 @@ def load_driver(self, ql, stack_addr, loadbase=0):
631651
# remember address of syscall table, so external tools can access to it
632652
ql.os.syscall_addr = SYSCALL_MEM
633653
# setup syscall table
634-
ql.mem.map(SYSCALL_MEM, 0x1000)
654+
ql.mem.map(SYSCALL_MEM, 0x1000, info="[syscall_mem]")
635655
# zero out syscall table memory
636656
ql.mem.write(SYSCALL_MEM, b'\x00' * 0x1000)
637657

@@ -642,7 +662,7 @@ def load_driver(self, ql, stack_addr, loadbase=0):
642662
tmp_sc = sc.replace("sys_", "NR_")
643663
if tmp_sc in globals():
644664
syscall_id = globals()[tmp_sc]
645-
print("Writing syscall %s to [0x%x]" % (sc, SYSCALL_MEM + ql.pointersize * syscall_id))
665+
logging.debug("Writing syscall %s to [0x%x]" % (sc, SYSCALL_MEM + ql.pointersize * syscall_id))
646666
ql.mem.write(SYSCALL_MEM + ql.pointersize * syscall_id, ql.pack(rev_reloc_symbols[sc]))
647667

648668
# write syscall addresses into syscall table
@@ -657,3 +677,24 @@ def load_driver(self, ql, stack_addr, loadbase=0):
657677
ql.import_symbols[self.ql.os.hook_addr] = hook_sys_read
658678
ql.import_symbols[self.ql.os.hook_addr + 1 * ql.pointersize] = hook_sys_write
659679
ql.import_symbols[self.ql.os.hook_addr + 2 * ql.pointersize] = hook_sys_open
680+
681+
def get_elfdata_mapping(self):
682+
elfdata_mapping = bytearray()
683+
elfdata_mapping.extend(self.getelfdata(0, self.elfhead['e_ehsize'])) #elf header
684+
685+
for section in self.parse_sections():
686+
if section.header['sh_flags'] & 2: # alloc flag
687+
sh_offset = section.header['sh_offset']
688+
sh_size = section.header['sh_size']
689+
690+
# align section addr
691+
elfdata_len = len(elfdata_mapping)
692+
if elfdata_len < sh_offset:
693+
elfdata_mapping.extend(b'\x00' * (sh_offset - elfdata_len))
694+
695+
if section.header['sh_type'] == 'SHT_NOBITS':
696+
elfdata_mapping.extend(b'\x00' * sh_size)
697+
else:
698+
elfdata_mapping.extend(self.getelfdata(sh_offset, sh_size))
699+
700+
return bytes(elfdata_mapping)

qiling/os/linux/fncc.py

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
# Built on top of Unicorn emulator (www.unicorn-engine.org)
55
import struct, logging
66
from unicorn.x86_const import *
7+
from unicorn.mips_const import *
78
from qiling.os.utils import *
89

910

@@ -35,6 +36,8 @@ def _get_param_by_index(ql, index):
3536
return _x86_get_params_by_index(ql, index)
3637
elif ql.archtype == QL_ARCH.X8664:
3738
return _x8664_get_params_by_index(ql, index)
39+
elif ql.archtype == QL_ARCH.MIPS:
40+
return _mips32_get_params_by_index(ql, index)
3841

3942

4043
# TODO: x86 calling convention of Linux kernel?
@@ -86,6 +89,14 @@ def _x8664_get_args(ql, number):
8689
return arg_list
8790

8891

92+
def _mips32_get_params_by_index(ql, index):
93+
reg_list = [UC_MIPS_REG_4, UC_MIPS_REG_5, UC_MIPS_REG_6, UC_MIPS_REG_7]
94+
if index < 4:
95+
return ql.uc.reg_read(reg_list[index])
96+
97+
return ql.stack.read(index * 4)
98+
99+
89100
def set_function_params(ql, in_params, out_params):
90101
index = 0
91102
for each in in_params:
@@ -120,13 +131,17 @@ def set_return_value(ql, ret):
120131
ql.uc.reg_write(UC_X86_REG_EAX, ret)
121132
elif ql.archtype == QL_ARCH.X8664:
122133
ql.uc.reg_write(UC_X86_REG_RAX, ret)
134+
elif ql.archtype == QL_ARCH.MIPS:
135+
ql.uc.reg_write(UC_MIPS_REG_2, ret)
123136

124137

125138
def get_return_value(ql):
126139
if ql.archtype == QL_ARCH.X86:
127140
return ql.uc.reg_read(UC_X86_REG_EAX)
128141
elif ql.archtype == QL_ARCH.X8664:
129142
return ql.uc.reg_read(UC_X86_REG_RAX)
143+
elif ql.archtype == QL_ARCH.MIPS:
144+
return ql.uc.reg_read(UC_MIPS_REG_2)
130145

131146

132147
def print_function(ql, passthru, address, function_name, params, ret):
@@ -228,6 +243,12 @@ def x8664_fastcall(ql, param_num, params, func, args, kwargs):
228243
return result
229244

230245

246+
def mips_call(ql, param_num, params, func, args, kwargs):
247+
result, param_num = __x86_cc(ql, param_num, params, func, args, kwargs)
248+
ql.reg.arch_pc = ql.reg.ra
249+
return result
250+
251+
231252
def linux_kernel_api(param_num=None, params=None):
232253
"""
233254
@cc: windows api calling convention, only x86 needs this, x64 is always fastcall
@@ -244,6 +265,8 @@ def wrapper(*args, **kwargs):
244265
# return x86_cdecl(ql, param_num, params, func, args, kwargs)
245266
elif ql.archtype == QL_ARCH.X8664:
246267
return x8664_fastcall(ql, param_num, params, func, args, kwargs)
268+
elif ql.archtype == QL_ARCH.MIPS:
269+
return mips_call(ql, param_num, params, func, args, kwargs)
247270
else:
248271
raise QlErrorArch("[!] Unknown ql.archtype")
249272
return wrapper

qiling/os/linux/kernel_api/kernel_api.py

Lines changed: 20 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -76,13 +76,6 @@ def hook_mutex_unlock(ql, address, params):
7676
return 0
7777

7878

79-
@linux_kernel_api(params={
80-
"Ptr": POINTER
81-
})
82-
def hook_memcmp(ql, address, params):
83-
pass
84-
85-
8679
@linux_kernel_api(params={
8780
"Ptr": POINTER
8881
})
@@ -281,7 +274,10 @@ def hook_strncmp(ql, address, params):
281274
"needle": STRING
282275
})
283276
def hook_strstr(ql, address, params):
284-
return 0
277+
_haystack = params["haystack"]
278+
_needle = params["needle"]
279+
index = _haystack.find(_needle)
280+
return 0 if index == -1 else index
285281

286282

287283
@linux_kernel_api(params={
@@ -299,7 +295,10 @@ def hook_strlen(ql, address, params):
299295
"size": SIZE_T
300296
})
301297
def hook_memcmp(ql, address, params):
302-
return 0
298+
s1 = params['s1']
299+
s2 = params['s2']
300+
size = params['size']
301+
return ql.mem.read(s1, size) == ql.mem.read(s2, size)
303302

304303

305304
@linux_kernel_api(params={
@@ -465,3 +464,15 @@ def hook_commit_creds(ql, address, params):
465464
})
466465
def hook_abort_creds(ql, address, params):
467466
return 0
467+
468+
@linux_kernel_api(params={
469+
"dest": POINTER,
470+
"src": POINTER,
471+
"size": SIZE_T
472+
})
473+
def hook_memcpy(ql, address, params):
474+
dest = params["dest"]
475+
src = params["src"]
476+
size = params["size"]
477+
ql.mem.write(dest, bytes(ql.mem.read(src, size)))
478+
return dest

tests/test_elf.py

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -928,8 +928,15 @@ def test_demigod_m0hamed_x8664(self):
928928
except UcError as e:
929929
print(e)
930930
sys.exit(-1)
931-
del ql
932-
931+
del ql
932+
933+
def test_demigod_hello_mips32(self):
934+
ql = Qiling(["../examples/rootfs/mips32_linux/kernel/hello.ko"], "../examples/rootfs/mips32_linux", output="debug")
935+
begin = ql.loader.load_address + 0x1060
936+
end = ql.loader.load_address + 0x1084
937+
ql.run(begin=begin, end=end)
938+
del ql
939+
933940
def test_x8664_absolute_path(self):
934941
class MyPipe():
935942
def __init__(self):

0 commit comments

Comments
 (0)