Skip to content

Commit 87fc7a4

Browse files
authored
Merge pull request #947 from elicn/gdb-improv
Misc. improvements to gdb module
2 parents e608872 + 5d8f5aa commit 87fc7a4

File tree

4 files changed

+136
-206
lines changed

4 files changed

+136
-206
lines changed

qiling/debugger/gdb/gdb.py

Lines changed: 67 additions & 128 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,11 @@
66
# gdbserver --remote-debug 0.0.0.0:9999 /path/to binary
77
# documentation: according to https://sourceware.org/gdb/current/onlinedocs/gdb/Remote-Protocol.html#Remote-Protocol
88

9-
from unicorn import *
10-
11-
import struct, os, re, socket
9+
import struct, os, socket
1210
from binascii import unhexlify
11+
from typing import Iterator, Literal
1312

14-
from .utils import QlGdbUtils
13+
from qiling import Qiling
1514
from qiling.const import *
1615
from qiling.utils import *
1716
from qiling.debugger import QlDebugger
@@ -25,6 +24,8 @@
2524
from qiling.arch.mips_const import reg_map as mips_reg_map
2625
from qiling.loader.elf import AUX
2726

27+
from .utils import QlGdbUtils
28+
2829
GDB_SIGNAL_INT = 2
2930
GDB_SIGNAL_SEGV = 11
3031
GDB_SIGNAL_GILL = 4
@@ -35,21 +36,17 @@
3536

3637
class QlGdb(QlDebugger, object):
3738
"""docstring for Debugsession"""
38-
def __init__(self, ql, ip, port):
39+
def __init__(self, ql: Qiling, ip: str = '127.0.01', port: int = 9999):
3940
super(QlGdb, self).__init__(ql)
41+
4042
self.ql = ql
4143
self.last_pkt = None
42-
self.exe_abspath = (os.path.abspath(self.ql.argv[0]))
43-
self.rootfs_abspath = (os.path.abspath(self.ql.rootfs))
44+
self.exe_abspath = os.path.abspath(self.ql.argv[0])
45+
self.rootfs_abspath = os.path.abspath(self.ql.rootfs)
4446
self.gdb = QlGdbUtils()
4547

46-
if ip == None:
47-
ip = '127.0.0.1'
48-
49-
if port == None:
50-
port = 9999
51-
else:
52-
port = int(port)
48+
if type(port) is str:
49+
port = int(port, 0)
5350

5451
self.ip = ip
5552
self.port = port
@@ -83,18 +80,23 @@ def __init__(self, ql, ip, port):
8380
QL_ARCH.MIPS : list({**mips_reg_map}.keys()),
8481
}
8582

86-
def addr_to_str(self, addr, short=False, endian="big"):
87-
if self.ql.archbit == 64 and short == False:
88-
addr = (hex(int.from_bytes(self.ql.pack64(addr), byteorder=endian)))
89-
addr = '{:0>16}'.format(addr[2:])
90-
elif self.ql.archbit == 32 or short == True:
91-
addr = (hex(int.from_bytes(self.ql.pack32(addr), byteorder=endian)))
92-
addr = ('{:0>8}'.format(addr[2:]))
93-
elif self.ql.archbit == 16 or short == True:
94-
addr = (hex(int.from_bytes(self.ql.pack32(addr), byteorder=endian)))
95-
addr = ('{:0>8}'.format(addr[2:]))
96-
addr = str(addr)
97-
return addr
83+
def addr_to_str(self, addr: int, short: bool = False, endian: Literal['little', 'big'] = 'big') -> str:
84+
# a hacky way to divide archbits by 2 if short, and leave it unchanged if not
85+
nbits = self.ql.archbit // (int(short) + 1)
86+
87+
if nbits == 64:
88+
s = f'{int.from_bytes(self.ql.pack64(addr), byteorder=endian):016x}'
89+
90+
elif nbits == 32:
91+
s = f'{int.from_bytes(self.ql.pack32(addr), byteorder=endian):08x}'
92+
93+
elif nbits == 16:
94+
s = f'{int.from_bytes(self.ql.pack16(addr), byteorder=endian):04x}'
95+
96+
else:
97+
raise RuntimeError
98+
99+
return s
98100

99101
def bin_to_escstr(self, rawbin):
100102
rawbin_escape = ""
@@ -221,13 +223,13 @@ def handle_g(subcmd):
221223
s += tmp
222224

223225
elif self.ql.archtype == QL_ARCH.ARM:
224-
mode = self.ql.arch.check_thumb()
226+
225227

226228
# r0-r12,sp,lr,pc,cpsr ,see https://sourceware.org/git/?p=binutils-gdb.git;a=blob;f=gdb/arch/arm.h;h=fa589fd0582c0add627a068e6f4947a909c45e86;hb=HEAD#l127
227229
for reg in self.tables[QL_ARCH.ARM][:16] + [self.tables[QL_ARCH.ARM][25]]:
228-
r = self.ql.reg.read(reg)
229-
if mode == UC_MODE_THUMB and reg == "pc":
230-
r += 1
230+
# if reg is pc, make sure to take thumb mode into account
231+
r = self.ql.arch.get_pc() if reg == "pc" else self.ql.reg.read(reg)
232+
231233
tmp = self.addr_to_str(r)
232234
s += tmp
233235

@@ -505,106 +507,43 @@ def handle_q(subcmd):
505507
elif subcmd.startswith('Xfer:auxv:read::'):
506508
if self.ql.code:
507509
return
508-
if self.ql.ostype in (QL_OS.LINUX, QL_OS.FREEBSD) :
509-
if self.ql.archbit == 64:
510-
ANNEX = "00000000000000"
511-
AT_SYSINFO_EHDR = "0000000000000000" # System-supplied DSO's ELF header
512-
ID_AT_HWCAP = "1000000000000000"
513-
ID_AT_PAGESZ = "0600000000000000"
514-
ID_AT_CLKTCK = "1100000000000000"
515-
AT_CLKTCK = "6400000000000000" # Frequency of times() 100
516-
ID_AT_PHDR = "0300000000000000"
517-
ID_AT_PHENT = "0400000000000000"
518-
ID_AT_PHNUM = "0500000000000000"
519-
ID_AT_BASE = "0700000000000000"
520-
ID_AT_FLAGS = "0800000000000000"
521-
ID_AT_ENTRY = "0900000000000000"
522-
ID_AT_UID = "0b00000000000000"
523-
ID_AT_EUID = "0c00000000000000"
524-
ID_AT_GID = "0d00000000000000"
525-
ID_AT_EGID = "0e00000000000000"
526-
ID_AT_SECURE = "1700000000000000"
527-
AT_SECURE = "0000000000000000"
528-
ID_AT_RANDOM = "1900000000000000"
529-
ID_AT_HWCAP2 = "1a00000000000000"
530-
AT_HWCAP2 = "0000000000000000"
531-
ID_AT_EXECFN = "1f00000000000000"
532-
AT_EXECFN = "0000000000000000" # File name of executable
533-
ID_AT_PLATFORM = "0f00000000000000"
534-
ID_AT_NULL = "0000000000000000"
535-
AT_NULL = "0000000000000000"
536-
537-
elif self.ql.archbit == 32:
538-
ANNEX = "000000"
539-
AT_SYSINFO_EHDR = "00000000" # System-supplied DSO's ELF header
540-
ID_AT_HWCAP = "10000000"
541-
ID_AT_PAGESZ = "06000000"
542-
ID_AT_CLKTCK = "11000000"
543-
AT_CLKTCK = "64000000" # Frequency of times() 100
544-
ID_AT_PHDR = "03000000"
545-
ID_AT_PHENT = "04000000"
546-
ID_AT_PHNUM = "05000000"
547-
ID_AT_BASE = "07000000"
548-
ID_AT_FLAGS = "08000000"
549-
ID_AT_ENTRY = "09000000"
550-
ID_AT_UID = "0b000000"
551-
ID_AT_EUID = "0c000000"
552-
ID_AT_GID = "0d000000"
553-
ID_AT_EGID = "0e000000"
554-
ID_AT_SECURE = "17000000"
555-
AT_SECURE = "00000000"
556-
ID_AT_RANDOM = "19000000"
557-
ID_AT_HWCAP2 = "1a000000"
558-
AT_HWCAP2 = "00000000"
559-
ID_AT_EXECFN = "1f000000"
560-
AT_EXECFN = "00000000" # File name of executable
561-
ID_AT_PLATFORM = "0f000000"
562-
ID_AT_NULL = "00000000"
563-
AT_NULL = "00000000"
564-
565-
AT_HWCAP = self.addr_to_str(self.ql.loader.aux_vec[AUX.AT_HWCAP]) # mock cpuid 0x1f8bfbff
566-
AT_PAGESZ = self.addr_to_str(self.ql.loader.aux_vec[AUX.AT_PAGESZ]) # System page size, fixed in qiling
567-
AT_PHDR = self.addr_to_str(self.ql.loader.aux_vec[AUX.AT_PHDR]) # Program headers for program
568-
AT_PHENT = self.addr_to_str(self.ql.loader.aux_vec[AUX.AT_PHENT]) # Size of program header entry
569-
AT_PHNUM = self.addr_to_str(self.ql.loader.aux_vec[AUX.AT_PHNUM]) # Number of program headers
570-
AT_BASE = self.addr_to_str(self.ql.loader.aux_vec[AUX.AT_BASE]) # Base address of interpreter
571-
AT_FLAGS = self.addr_to_str(self.ql.loader.aux_vec[AUX.AT_FLAGS])
572-
AT_ENTRY = self.addr_to_str(self.ql.loader.aux_vec[AUX.AT_ENTRY]) # Entry point of program
573-
AT_UID = self.addr_to_str(self.ql.loader.aux_vec[AUX.AT_UID]) # UID from ql.profile
574-
AT_EUID = self.addr_to_str(self.ql.loader.aux_vec[AUX.AT_EUID]) # UID from ql.profile
575-
AT_GID = self.addr_to_str(self.ql.loader.aux_vec[AUX.AT_GID]) # UID from ql.profile
576-
AT_EGID = self.addr_to_str(self.ql.loader.aux_vec[AUX.AT_EGID]) # UID from ql.profile
577-
AT_RANDOM = self.addr_to_str(self.ql.loader.aux_vec[AUX.AT_RANDOM]) # Address of 16 random bytes
578-
AT_PLATFORM = self.addr_to_str(self.ql.loader.aux_vec[AUX.AT_PLATFORM]) # String identifying platform
579-
580-
auxvdata_c = (
581-
ANNEX + AT_SYSINFO_EHDR +
582-
ID_AT_HWCAP + AT_HWCAP +
583-
ID_AT_PAGESZ + AT_PAGESZ +
584-
ID_AT_CLKTCK + AT_CLKTCK +
585-
ID_AT_PHDR + AT_PHDR +
586-
ID_AT_PHENT + AT_PHENT +
587-
ID_AT_PHNUM + AT_PHNUM +
588-
ID_AT_BASE + AT_BASE +
589-
ID_AT_FLAGS + AT_FLAGS +
590-
ID_AT_ENTRY + AT_ENTRY +
591-
ID_AT_UID + AT_UID +
592-
ID_AT_EUID + AT_EUID +
593-
ID_AT_GID + AT_GID +
594-
ID_AT_EGID + AT_EGID +
595-
ID_AT_SECURE + AT_SECURE +
596-
ID_AT_RANDOM + AT_RANDOM +
597-
ID_AT_HWCAP2 + AT_HWCAP2 +
598-
ID_AT_EXECFN + AT_EXECFN +
599-
ID_AT_PLATFORM + AT_PLATFORM +
600-
ID_AT_NULL + AT_NULL
601-
)
602-
603-
auxvdata = self.bin_to_escstr(unhexlify(auxvdata_c))
604-
#self.send(b'l!%s' % auxvdata)
510+
511+
if self.ql.ostype in (QL_OS.LINUX, QL_OS.FREEBSD):
512+
def __read_auxv() -> Iterator[int]:
513+
auxv_entries = (
514+
AUX.AT_HWCAP,
515+
AUX.AT_PAGESZ,
516+
AUX.AT_CLKTCK,
517+
AUX.AT_PHDR,
518+
AUX.AT_PHENT,
519+
AUX.AT_PHNUM,
520+
AUX.AT_BASE,
521+
AUX.AT_FLAGS,
522+
AUX.AT_ENTRY,
523+
AUX.AT_UID,
524+
AUX.AT_EUID,
525+
AUX.AT_GID,
526+
AUX.AT_EGID,
527+
AUX.AT_SECURE,
528+
AUX.AT_RANDOM,
529+
AUX.AT_HWCAP2,
530+
AUX.AT_EXECFN,
531+
AUX.AT_PLATFORM,
532+
AUX.AT_NULL
533+
)
534+
535+
for e in auxv_entries:
536+
yield e.value
537+
yield self.ql.loader.aux_vec[e]
538+
539+
annex = self.addr_to_str(0)[:-2]
540+
sysinfo_ehdr = self.addr_to_str(0)
541+
542+
auxvdata_c = unhexlify(''.join([annex, sysinfo_ehdr] + [self.addr_to_str(val) for val in __read_auxv()]))
543+
auxvdata = self.bin_to_escstr(auxvdata_c)
605544
else:
606545
auxvdata = b""
607-
546+
608547
self.send(b'l!%s' % auxvdata)
609548

610549
elif subcmd.startswith('Xfer:exec-file:read:'):

qiling/loader/elf.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -335,6 +335,7 @@ def __push_str(top: int, s: str) -> int:
335335

336336
new_stack = randstraddr = __push_str(new_stack, 'a' * 16)
337337
new_stack = cpustraddr = __push_str(new_stack, 'i686')
338+
new_stack = execfn = __push_str(new_stack, argv[0])
338339

339340
# store aux vector data for gdb use
340341
elf_phdr = load_address + elffile['e_phoff']
@@ -369,6 +370,8 @@ def __push_str(top: int, s: str) -> int:
369370
(AUX.AT_HWCAP, elf_hwcap),
370371
(AUX.AT_CLKTCK, 100),
371372
(AUX.AT_RANDOM, randstraddr),
373+
(AUX.AT_HWCAP2, 0),
374+
(AUX.AT_EXECFN, execfn),
372375
(AUX.AT_PLATFORM, cpustraddr),
373376
(AUX.AT_SECURE, 0),
374377
(AUX.AT_NULL, 0)

qiling/utils.py

Lines changed: 10 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -420,25 +420,21 @@ def component_setup(component_type, component_name, ql):
420420
return ql_get_module_function(f"qiling.{component_type}.{component_name}", function_name)(ql)
421421

422422

423-
def debugger_setup(debugger, ql):
424-
# default remote server
425-
remotedebugsrv = "gdb"
426-
debug_opts = [None, None]
423+
def debugger_setup(options, ql):
424+
if options is True:
425+
options = 'gdb'
427426

428-
if debugger != True and type(debugger) is str:
429-
debug_opts = debugger.split(":")
427+
if type(options) is str:
428+
objname, *args = options.split(':')
430429

431-
if len(debug_opts) == 2 and debug_opts[0] != "qdb":
432-
pass
433-
else:
434-
remotedebugsrv, *debug_opts = debug_opts
430+
if debugger_convert(objname) not in enum_values(QL_DEBUGGER):
431+
raise QlErrorOutput('Debugger not supported')
435432

436-
if debugger_convert(remotedebugsrv) not in enum_values(QL_DEBUGGER):
437-
raise QlErrorOutput("Error: Debugger not supported")
433+
obj = ql_get_module_function(f'qiling.debugger.{objname}.{objname}', f'Ql{str.capitalize(objname)}')
438434

439-
debugsession = ql_get_module_function(f"qiling.debugger.{remotedebugsrv}.{remotedebugsrv}", f"Ql{str.capitalize(remotedebugsrv)}")
435+
return obj(ql, *args)
440436

441-
return debugsession(ql, *debug_opts)
437+
return None
442438

443439
def arch_setup(archtype, ql):
444440
if not ql_is_valid_arch(archtype):

0 commit comments

Comments
 (0)