Skip to content
Merged
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
2 changes: 1 addition & 1 deletion examples/fuzzing/dlink_dir815/dir815_mips32el_linux.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ def start_afl(_ql: Qiling):
addr = ql.mem.search("HTTP_COOKIE=uid=1234&password=".encode())
ql.target_addr = addr[0]

main_addr = ql.os.elf_entry
main_addr = ql.loader.elf_entry
ql.hook_address(callback=start_afl, address=main_addr)

try:
Expand Down
4 changes: 2 additions & 2 deletions qiling/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -694,8 +694,8 @@ def restore(self, saved_states: Mapping[str, Any] = {}, *, snapshot: Optional[st
if "fd" in saved_states:
self.os.fd.restore(saved_states["fd"])

if "os_context" in saved_states:
self.os.restore(saved_states["os_context"])
if "os" in saved_states:
self.os.restore(saved_states["os"])

if "loader" in saved_states:
self.loader.restore(saved_states["loader"])
Expand Down
2 changes: 1 addition & 1 deletion qiling/debugger/gdb/gdb.py
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ def __get_attach_addr() -> int:
entry_point = ql.loader.entry_point

elif ql.os.type in (QL_OS.LINUX, QL_OS.FREEBSD) and not ql.code:
entry_point = ql.os.elf_entry
entry_point = ql.loader.elf_entry

else:
entry_point = ql.os.entry_point
Expand Down
43 changes: 21 additions & 22 deletions qiling/loader/elf.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,9 +58,11 @@ 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_SIZE = 0x1000

# memory for syscall table
SYSCALL_MEM = API_HOOK_MEM + 0x1000
SYSCALL_SIZE = 0x1000


class QlLoaderELF(QlLoader):
Expand All @@ -85,6 +87,7 @@ def run(self):
# setup program stack
stack_address = self.profile.getint('stack_address')
stack_size = self.profile.getint('stack_size')
top_of_stack = stack_address + stack_size
self.ql.mem.map(stack_address, stack_size, info='[stack]')

self.path = self.ql.path
Expand All @@ -97,20 +100,20 @@ 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, top_of_stack, loadbase=0x8000000)
self.ql.hook_code(hook_kernel_api)

# is it an executable?
elif elftype == 'ET_EXEC':
load_address = 0

self.load_with_ld(elffile, stack_address + stack_size, load_address, self.argv, self.env)
self.load_with_ld(elffile, top_of_stack, load_address, self.argv, self.env)

# is it a shared object?
elif elftype == 'ET_DYN':
load_address = self.profile.getint('load_address')

self.load_with_ld(elffile, stack_address + stack_size, load_address, self.argv, self.env)
self.load_with_ld(elffile, top_of_stack, load_address, self.argv, self.env)

else:
raise QlErrorELFFormat(f'unexpected elf type value (e_type = {elftype})')
Expand Down Expand Up @@ -373,7 +376,6 @@ def __push_str(top: int, s: str) -> int:
self.init_sp = self.ql.arch.regs.arch_sp

self.ql.os.entry_point = self.entry_point = entry_point
self.ql.os.elf_entry = self.elf_entry
self.ql.os.function_hook = FunctionHook(self.ql, elf_phdr, elf_phnum, elf_phent, load_address, mem_end)

# If there is a loader, we ignore exit
Expand Down Expand Up @@ -574,44 +576,41 @@ def __get_symbol(name: str) -> Optional[Symbol]:
def load_driver(self, elffile: ELFFile, stack_addr: int, loadbase: int = 0) -> None:
elfdata_mapping = self.get_elfdata_mapping(elffile)

# FIXME: determine true memory boundaries, taking relocation into account (if requested)
mem_start = 0
mem_end = mem_start + self.ql.mem.align_up(len(elfdata_mapping), 0x1000)
mem_start = self.ql.mem.align(loadbase)
mem_end = self.ql.mem.align_up(loadbase + len(elfdata_mapping))

# map some memory to intercept external functions of Linux kernel
self.ql.mem.map(API_HOOK_MEM, 0x1000, info="[api_mem]")
self.ql.mem.map(API_HOOK_MEM, API_HOOK_SIZE, info="[api_mem]")

self.ql.log.debug(f'loadbase : {loadbase:#x}')
self.ql.log.debug(f'mem_start : {mem_start:#x}')
self.ql.log.debug(f'mem_end : {mem_end:#x}')

self.ql.mem.map(loadbase + mem_start, mem_end - mem_start, info=self.ql.path)
self.ql.mem.write(loadbase + mem_start, elfdata_mapping)
self.ql.mem.map(mem_start, mem_end - mem_start, info=os.path.basename(self.ql.path))
self.ql.mem.write(loadbase, elfdata_mapping)

init_module = self.lkm_get_init(elffile) + loadbase + mem_start
self.images.append(Image(mem_start, mem_end, os.path.abspath(self.path)))

init_module = loadbase + self.lkm_get_init(elffile)
self.ql.log.debug(f'init_module : {init_module:#x}')

self.brk_address = mem_end + loadbase
self.brk_address = mem_end

# Set MMAP addr
mmap_address = self.profile.getint('mmap_address')
self.ql.log.debug(f'mmap_address is : {mmap_address:#x}')

# self.ql.os.elf_entry = self.elf_entry = loadbase + elfhead['e_entry']
self.ql.os.entry_point = self.entry_point = init_module
self.elf_entry = self.ql.os.elf_entry = self.ql.os.entry_point
# there is no interperter so emulation entry point is also elf entry
self.elf_entry = self.entry_point = init_module
self.ql.os.entry_point = self.entry_point

self.stack_address = self.ql.mem.align(stack_addr, self.ql.arch.pointersize)
self.load_address = loadbase

# remember address of syscall table, so external tools can access to it
# self.ql.os.syscall_addr = SYSCALL_MEM

# setup syscall table
self.ql.mem.map(SYSCALL_MEM, 0x1000, info="[syscall_mem]")
self.ql.mem.write(SYSCALL_MEM, b'\x00' * 0x1000)
self.ql.mem.map(SYSCALL_MEM, SYSCALL_SIZE, info="[syscall_mem]")
self.ql.mem.write(SYSCALL_MEM, b'\x00' * SYSCALL_SIZE)

rev_reloc_symbols = self.lkm_dynlinker(elffile, mem_start + loadbase)
rev_reloc_symbols = self.lkm_dynlinker(elffile, loadbase)

# iterate over relocatable symbols, but pick only those who start with 'sys_'
for sc, addr in rev_reloc_symbols.items():
Expand Down
15 changes: 8 additions & 7 deletions qiling/os/posix/syscall/socket.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,9 @@
AF_INET = 2
AF_INET6 = 10

SOCK_STREAM = 1
SOCK_DGRAM = 2
SOCK_SEQPACKET = 5

def inet_aton(ipaddr: str) -> int:
# ipdata = bytes(int(a, 0) for a in ipaddr.split('.', 4))
Expand Down Expand Up @@ -765,10 +768,9 @@ def ql_syscall_recvfrom(ql: Qiling, sockfd: int, buf: int, length: int, flags: i
if sock is None:
return -1

SOCK_STREAM = 1

# For x8664, recvfrom() is called finally when calling recv() in TCP communications
if sock.socktype == SOCK_STREAM:
# calling recvfrom with a NULL addr argument is identical to calling recv, which is normally used only on a connected socket
if sock.socktype == SOCK_STREAM or (addr == 0 and addrlen == 0):
return ql_syscall_recv(ql, sockfd, buf, length, flags)

data_buf, address = sock.recvfrom(length, flags)
Expand Down Expand Up @@ -842,10 +844,9 @@ def ql_syscall_sendto(ql: Qiling, sockfd: int, buf: int, length: int, flags: int
if sock is None:
return -1

SOCK_STREAM = 1

# For x8664, sendto() is called finally when calling send() in TCP communications
if sock.socktype == SOCK_STREAM:
# if sendto is used on a connection-mode socket, the arguments addr and addrlen are ignored.
# also, calling sendto(sockfd, buf, length, flags, NULL, 0) is equivalent to send(sockfd, buf, length, flags)
if sock.socktype in (SOCK_STREAM, SOCK_SEQPACKET) or (addr == 0 and addrlen == 0):
return ql_syscall_send(ql, sockfd, buf, length, flags)

tmp_buf = ql.mem.read(buf, length)
Expand Down
26 changes: 22 additions & 4 deletions qiling/os/windows/dlls/advapi32.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
#!/usr/bin/env python3
#
#
# Cross Platform and Multi Architecture Advanced Binary Emulation Framework
#

Expand Down Expand Up @@ -81,12 +81,23 @@ def __RegQueryValue(ql: Qiling, address: int, params, wstring: bool):
ql.log.debug("Key value not found")
return ERROR_FILE_NOT_FOUND

# read how many bytes we are allowed to write into lpData, however this arg is optional
if lpcbData:
max_size = ql.mem.read_ptr(lpcbData, 4)
else:
max_size = 0

# lpcbData may be null only if lpData is also null. if lpData is allocated, but lpcbData is
# set to null, it means we have an out buffer without knowing its size
if lpData:
return ERROR_INVALID_PARAMETER

# set lpData
length = ql.os.registry_manager.write_reg_value_into_mem(reg_type, lpData, value, wstring)
length = ql.os.registry_manager.write_reg_value_into_mem(reg_type, lpData, value, max_size, wstring)

# set lpcbData
max_size = ql.mem.read_ptr(lpcbData, 4)
ql.mem.write_ptr(lpcbData, length, 4)
if lpcbData:
ql.mem.write_ptr(lpcbData, length, 4)

if max_size < length:
ret = ERROR_MORE_DATA
Expand Down Expand Up @@ -134,6 +145,9 @@ def __RegSetValue(ql: Qiling, address: int, params, wstring: bool):
# this is done so the print_function would print the correct value
params["hKey"] = s_hKey

if not lpData:
return ERROR_INVALID_PARAMETER

# dwType is expected to be REG_SZ and lpData to point to a null-terminated string
ql.os.registry_manager.write(s_hKey, lpSubKey, dwType, lpData, cbData, wstring)

Expand All @@ -147,8 +161,12 @@ def __RegSetValueEx(ql: Qiling, address: int, params, wstring: bool):
cbData = params["cbData"]

s_hKey = ql.os.handle_manager.get(hKey).obj
# this is done so the print_function would print the correct value
params["hKey"] = s_hKey

if not lpData:
return ERROR_INVALID_PARAMETER

ql.os.registry_manager.write(s_hKey, lpValueName, dwType, lpData, cbData, wstring)

return ERROR_SUCCESS
Expand Down
42 changes: 19 additions & 23 deletions qiling/os/windows/registry.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
#!/usr/bin/env python3
#
#
# Cross Platform and Multi Architecture Advanced Binary Emulation Framework
#

import json, os
import json
import os

from Registry import Registry
from typing import Any, MutableMapping, Optional, Tuple, Union

Expand All @@ -15,11 +17,11 @@
# Registry Manager reads data from two places
# 1. config.json
# if you want to modify the registry key/value, you can modify config.json
# If there is a registry entry in config.json that needs to be read,
# If there is a registry entry in config.json that needs to be read,
# Registry Manager will read from config.json first.
# 2. windows hive files

# Registry Manager will only write registry changes to config.json
# Registry Manager will only write registry changes to config.json
# and will not modify the hive file.

class RegConf:
Expand Down Expand Up @@ -208,14 +210,14 @@ def access(self, key: str, name: Optional[str] = None, type: Optional[int] = Non
self.ql.os.stats.log_reg_access(key, name, type, value)

def create(self, key: str) -> None:
self.regconf.create(key)
self.reghive.create(key)
self.regconf.create(key)
self.reghive.create(key)

def delete(self, key: str, subkey: str) -> None:
self.regconf.delete(key, subkey)
self.reghive.delete(key, subkey)
self.regconf.delete(key, subkey)
self.reghive.delete(key, subkey)

def __reg_mem_read(self, data_type: int, data_addr: int, data_size: int, wide: bool) -> Optional[Union[str, bytes, int]]:
def __reg_mem_read(self, data_type: int, data_addr: int, data_size: int, wide: bool) -> Union[str, bytes, int]:
if data_type in (Registry.RegSZ, Registry.RegExpandSZ):
os_utils = self.ql.os.utils
read_string = os_utils.read_wstring if wide else os_utils.read_cstring
Expand All @@ -232,11 +234,11 @@ def __reg_mem_read(self, data_type: int, data_addr: int, data_size: int, wide: b
data = bytes(self.ql.mem.read(data_addr, data_size))

else:
data = None
raise QlErrorNotImplemented(f'registry type {REG_TYPES[data_type]} not implemented')

return data

def __reg_mem_write(self, data_type: int, data_addr: int, data_val: Union[str, bytes, int], wide: bool) -> Optional[int]:
def __reg_mem_write(self, data_type: int, data_addr: int, data_val: Union[str, bytes, int], max_size: int, wide: bool) -> int:
if data_type in (Registry.RegSZ, Registry.RegExpandSZ):
assert type(data_val) is str

Expand All @@ -259,28 +261,22 @@ def __reg_mem_write(self, data_type: int, data_addr: int, data_val: Union[str, b
data = data_val

else:
return None
raise QlErrorNotImplemented(f'registry type {REG_TYPES[data_type]} not implemented')

self.ql.mem.write(data_addr, data)
# in case the out buffer is set to null or it is too small, skip data writing
if data_addr and max_size >= len(data):
self.ql.mem.write(data_addr, data)

return len(data)

def write(self, key: str, subkey: str, reg_type: int, data_addr: int, data_size: int, wide: bool) -> None:
data = self.__reg_mem_read(reg_type, data_addr, data_size, wide)

if data is None:
raise QlErrorNotImplemented(f'registry type {REG_TYPES[reg_type]} not implemented')

self.regconf.write(key, subkey, reg_type, data)
self.reghive.write(key, subkey, reg_type, data)

def write_reg_value_into_mem(self, data_type: int, data_addr: int, data_val: Union[str, bytes, int], wide: bool) -> int:
length = self.__reg_mem_write(data_type, data_addr, data_val, wide)

if length is None:
raise QlErrorNotImplemented(f'registry type {REG_TYPES[data_type]} not implemented')

return length
def write_reg_value_into_mem(self, data_type: int, data_addr: int, data_val: Union[str, bytes, int], max_size: int, wide: bool) -> int:
return self.__reg_mem_write(data_type, data_addr, data_val, max_size, wide)

def save(self):
self.regconf.save(self.regdiff)