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
91 changes: 45 additions & 46 deletions qiling/debugger/gdb/gdb.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,13 @@
# gdb remote protocol:
# https://sourceware.org/gdb/current/onlinedocs/gdb/Remote-Protocol.html

import os, socket, re, tempfile
import os
import socket
import re
import tempfile
from functools import partial
from logging import Logger
from typing import Iterator, Mapping, Optional, Union
import typing
from typing import IO, Iterator, MutableMapping, Optional, Union

from unicorn import UcError
from unicorn.unicorn_const import (
Expand All @@ -32,7 +35,6 @@
from qiling.debugger.gdb.xmlregs import QlGdbFeatures
from qiling.debugger.gdb.utils import QlGdbUtils
from qiling.os.linux.procfs import QlProcFS
from qiling.os.mapper import QlFsMappedCallable, QlFsMappedObject

# gdb logging prompt
PROMPT = r'gdb>'
Expand Down Expand Up @@ -62,6 +64,7 @@
# reply type
Reply = Union[bytes, str]


class QlGdb(QlDebugger):
"""A simple gdbserver implementation.
"""
Expand Down Expand Up @@ -104,10 +107,7 @@ def __init__(self, ql: Qiling, ip: str = '127.0.0.1', port: int = 9999):
self.features = QlGdbFeatures(self.ql.arch.type, self.ql.os.type)
self.regsmap = self.features.regsmap

# https://sourceware.org/bugzilla/show_bug.cgi?id=17760
# 42000 is the magic pid to indicate the remote process.
self.ql.add_fs_mapper(r'/proc/42000/maps', QlFsMappedCallable(QlProcFS.self_map, self.ql.mem))
self.fake_procfs: Mapping[int, typing.IO] = {}
self.fake_procfs: MutableMapping[int, IO] = {}

def run(self):
server = GdbSerialConn(self.ip, self.port, self.ql.log)
Expand Down Expand Up @@ -158,11 +158,9 @@ def __swap_endianess(value: int) -> int:

return int.from_bytes(raw, 'big')


def handle_exclaim(subcmd: str) -> Reply:
return REPLY_OK


def handle_qmark(subcmd: str) -> Reply:
"""Request status.

Expand Down Expand Up @@ -211,7 +209,6 @@ def __get_reg_info(ucreg: int) -> str:

return f'T{SIGTRAP:02x}{bp_info}{sp_info}{pc_info}'


def handle_c(subcmd: str) -> Reply:
try:
self.gdb.resume_emu()
Expand Down Expand Up @@ -246,7 +243,6 @@ def handle_c(subcmd: str) -> Reply:

return reply


def handle_g(subcmd: str) -> Reply:
# NOTE: in the past the 'g' reply packet for arm included the f0-f7 and fps registers between pc
# and cpsr, which placed cpsr at index (regnum) 25. as the f-registers became obsolete the cpsr
Expand All @@ -261,7 +257,6 @@ def handle_g(subcmd: str) -> Reply:

return ''.join(__get_reg_value(*entry) for entry in self.regsmap)


def handle_G(subcmd: str) -> Reply:
data = subcmd

Expand All @@ -272,7 +267,6 @@ def handle_G(subcmd: str) -> Reply:

return REPLY_OK


def handle_H(subcmd: str) -> Reply:
op = subcmd[0]

Expand All @@ -281,14 +275,12 @@ def handle_H(subcmd: str) -> Reply:

return REPLY_EMPTY


def handle_k(subcmd: str) -> Reply:
global killed

killed = True
return REPLY_OK


def handle_m(subcmd: str) -> Reply:
"""Read target memory.
"""
Expand All @@ -302,7 +294,6 @@ def handle_m(subcmd: str) -> Reply:
else:
return data


def handle_M(subcmd: str) -> Reply:
"""Write target memory.
"""
Expand All @@ -322,7 +313,6 @@ def handle_M(subcmd: str) -> Reply:
else:
return REPLY_OK


def handle_p(subcmd: str) -> Reply:
"""Read register value by index.
"""
Expand All @@ -331,7 +321,6 @@ def handle_p(subcmd: str) -> Reply:

return __get_reg_value(*self.regsmap[idx])


def handle_P(subcmd: str) -> Reply:
"""Write register value by index.
"""
Expand All @@ -346,7 +335,6 @@ def handle_P(subcmd: str) -> Reply:

return 'E00'


def handle_Q(subcmd: str) -> Reply:
"""General queries.

Expand All @@ -369,14 +357,12 @@ def handle_Q(subcmd: str) -> Reply:

return REPLY_OK if feature in supported else REPLY_EMPTY


def handle_D(subcmd: str) -> Reply:
"""Detach.
"""

return REPLY_OK


def handle_q(subcmd: str) -> Reply:
query, *data = subcmd.split(':')

Expand All @@ -396,13 +382,9 @@ def handle_q(subcmd: str) -> Reply:
'QAgent+',
'QCatchSyscalls+',
'QDisableRandomization+',
'QEnvironmentHexEncoded+',
'QEnvironmentReset+',
'QEnvironmentUnset+',
'QNonStop+',
'QPassSignals+',
'QProgramSignals+',
'QSetWorkingDir+',
'QStartNoAckMode+',
'QStartupWithShell+',
'QTBuffer:size+',
Expand All @@ -415,7 +397,6 @@ def handle_q(subcmd: str) -> Reply:
'hwbreak+',
'multiprocess+',
'no-resumed+',
'qXfer:auxv:read+',
'qXfer:features:read+',
# 'qXfer:libraries-svr4:read+',
# 'qXfer:osdata:read+',
Expand Down Expand Up @@ -453,9 +434,19 @@ def handle_q(subcmd: str) -> Reply:

# os dependent features
if not self.ql.interpreter:
features += [
'QEnvironmentHexEncoded+',
'QEnvironmentReset+',
'QEnvironmentUnset+'
]

# filesystem dependent features
if hasattr(self.ql.os, 'path'):
features.append('qXfer:exec-file:read+')
features += [
'QSetWorkingDir+',
'qXfer:auxv:read+',
'qXfer:exec-file:read+'
]

# process dependent features
if hasattr(self.ql.os, 'pid'):
Expand Down Expand Up @@ -489,7 +480,7 @@ def handle_q(subcmd: str) -> Reply:
elif feature == 'auxv' and op == 'read':
try:
with self.ql.os.fs_mapper.open('/proc/self/auxv', 'rb') as infile:
infile.seek(offset, 0) # SEEK_SET
infile.seek(offset, 0) # SEEK_SET
auxv_data = infile.read(length)

except FileNotFoundError:
Expand Down Expand Up @@ -568,7 +559,6 @@ def handle_q(subcmd: str) -> Reply:

return REPLY_EMPTY


def handle_v(subcmd: str) -> Reply:
if subcmd == 'MustReplyEmpty':
return REPLY_EMPTY
Expand All @@ -590,15 +580,29 @@ def handle_v(subcmd: str) -> Reply:

virtpath = self.ql.os.path.virtual_abspath(path)

if virtpath.startswith(r'/proc') and self.ql.os.fs_mapper.has_mapping(virtpath):
# Mapped object by itself is not backed with a host fd and thus a tempfile can
# 1. Make pread easy to implement and avoid duplicate code like seek, fd etc.
# 2. Avoid fd clash if we assign a generated fd.
tfile = tempfile.TemporaryFile("rb+")
tfile.write(self.ql.os.fs_mapper.open(virtpath, "rb+").read())
tfile.seek(0, os.SEEK_SET)
fd = tfile.fileno()
self.fake_procfs[fd] = tfile
if virtpath.startswith(r'/proc/'):
pid, _, vfname = virtpath[6:].partition(r'/')

# 42000 is a magic number indicating the remote process' pid
# see: https://sourceware.org/bugzilla/show_bug.cgi?id=17760
if pid == '42000':
vfmap = {
'maps': lambda: partial(QlProcFS.self_map, self.ql.mem)
}

if vfname in vfmap and not self.ql.os.fs_mapper.has_mapping(virtpath):
self.ql.add_fs_mapper(virtpath, vfmap[vfname]())

if self.ql.os.fs_mapper.has_mapping(virtpath):
# Mapped object by itself is not backed with a host fd and thus a tempfile can
# 1. Make pread easy to implement and avoid duplicate code like seek, fd etc.
# 2. Avoid fd clash if we assign a generated fd.
tfile = tempfile.TemporaryFile("rb+")
tfile.write(self.ql.os.fs_mapper.open(virtpath, "rb+").read())
tfile.seek(0, os.SEEK_SET)

fd = tfile.fileno()
self.fake_procfs[fd] = tfile
else:
host_path = self.ql.os.path.virtual_to_host_path(path)

Expand All @@ -621,10 +625,10 @@ def handle_v(subcmd: str) -> Reply:
fd = int(fd, 16)

os.close(fd)

if fd in self.fake_procfs:
del self.fake_procfs[fd]

return 'F0'

return REPLY_EMPTY
Expand Down Expand Up @@ -658,7 +662,6 @@ def handle_v(subcmd: str) -> Reply:

return REPLY_EMPTY


def handle_s(subcmd: str) -> Reply:
"""Perform a single step.
"""
Expand All @@ -673,7 +676,6 @@ def handle_s(subcmd: str) -> Reply:

return f'S{SIGTRAP:02x}'


def handle_X(subcmd: str) -> Reply:
"""Write data to memory.
"""
Expand All @@ -692,7 +694,6 @@ def handle_X(subcmd: str) -> Reply:
else:
return REPLY_OK


def handle_Z(subcmd: str) -> Reply:
"""Insert breakpoints or watchpoints.
"""
Expand All @@ -713,7 +714,6 @@ def handle_Z(subcmd: str) -> Reply:

return REPLY_EMPTY


def handle_z(subcmd: str) -> Reply:
"""Remove breakpoints or watchpoints.
"""
Expand All @@ -730,7 +730,6 @@ def handle_z(subcmd: str) -> Reply:

return REPLY_EMPTY


handlers = {
'!': handle_exclaim,
'?': handle_qmark,
Expand Down
25 changes: 11 additions & 14 deletions qiling/os/linux/linux.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
# Cross Platform and Multi Architecture Advanced Binary Emulation Framework
#

from functools import partial

from unicorn import UcError
from unicorn.x86_const import UC_X86_INS_SYSCALL

Expand All @@ -15,12 +17,12 @@
from qiling.os.fcall import QlFunctionCall
from qiling.os.const import *
from qiling.os.linux.procfs import QlProcFS
from qiling.os.mapper import QlFsMappedCallable
from qiling.os.posix.posix import QlOsPosix

from . import futex
from . import thread


class QlOsLinux(QlOsPosix):
type = QL_OS.LINUX

Expand Down Expand Up @@ -49,7 +51,6 @@ def __init__(self, ql: Qiling):
self.elf_mem_start = 0x0
self.load()


def load(self):
self.futexm = futex.QlLinuxFutexManagement()

Expand Down Expand Up @@ -98,7 +99,7 @@ def load(self):
self.ql.hook_insn(self.hook_syscall, UC_X86_INS_SYSCALL)
# Keep test for _cc
#self.ql.hook_insn(hook_posix_api, UC_X86_INS_SYSCALL)
self.thread_class = thread.QlLinuxX8664Thread
self.thread_class = thread.QlLinuxX8664Thread

elif self.ql.arch.type == QL_ARCH.RISCV:
self.ql.arch.enable_float()
Expand All @@ -120,28 +121,24 @@ def load(self):
if getattr(self.fd[i], 'close_on_exec', 0):
self.fd[i] = None


def setup_procfs(self):
self.fs_mapper.add_fs_mapping(r'/proc/self/auxv', QlFsMappedCallable(QlProcFS.self_auxv, self))
self.fs_mapper.add_fs_mapping(r'/proc/self/cmdline', QlFsMappedCallable(QlProcFS.self_cmdline, self))
self.fs_mapper.add_fs_mapping(r'/proc/self/environ', QlFsMappedCallable(QlProcFS.self_environ, self))
self.fs_mapper.add_fs_mapping(r'/proc/self/exe', QlFsMappedCallable(QlProcFS.self_exe, self))
self.fs_mapper.add_fs_mapping(r'/proc/self/maps', QlFsMappedCallable(QlProcFS.self_map, self.ql.mem))
self.fs_mapper.add_fs_mapping(r'/proc/self/auxv', partial(QlProcFS.self_auxv, self))
self.fs_mapper.add_fs_mapping(r'/proc/self/cmdline', partial(QlProcFS.self_cmdline, self))
self.fs_mapper.add_fs_mapping(r'/proc/self/environ', partial(QlProcFS.self_environ, self))
self.fs_mapper.add_fs_mapping(r'/proc/self/exe', partial(QlProcFS.self_exe, self))
self.fs_mapper.add_fs_mapping(r'/proc/self/maps', partial(QlProcFS.self_map, self.ql.mem))

def hook_syscall(self, ql, intno = None):
return self.load_syscall()


def register_function_after_load(self, function):
if function not in self.function_after_load_list:
self.function_after_load_list.append(function)


def run_function_after_load(self):
for f in self.function_after_load_list:
f()


def run(self):
# do not set-up procfs for drivers and shellcode
if not self.ql.code and not self.ql.loader.is_driver:
Expand All @@ -154,14 +151,14 @@ def run(self):
if self.ql.code:
self.ql.emu_start(self.entry_point, (self.entry_point + len(self.ql.code)), self.ql.timeout, self.ql.count)
else:
if self.ql.multithread == True:
if self.ql.multithread:
# start multithreading
thread_management = thread.QlLinuxThreadManagement(self.ql)
self.ql.os.thread_management = thread_management
thread_management.run()

else:
if self.ql.entry_point is not None:
if self.ql.entry_point is not None:
self.ql.loader.elf_entry = self.ql.entry_point

elif self.ql.loader.elf_entry != self.ql.loader.entry_point:
Expand Down
Loading