diff --git a/examples/hello_arm_uboot.py b/examples/hello_arm_uboot.py
index 9544fe0ee..f97ff6eff 100644
--- a/examples/hello_arm_uboot.py
+++ b/examples/hello_arm_uboot.py
@@ -8,68 +8,82 @@
from qiling.core import Qiling
from qiling.const import QL_ARCH, QL_OS, QL_VERBOSE
-from qiling.os.const import STRING
+from qiling.os.const import STRING, SIZE_T, POINTER
-def get_kaimendaji_password():
- def my_getenv(ql: Qiling):
- env = {
- "ID" : b"000000000000000",
- "ethaddr" : b"11:22:33:44:55:66"
- }
+def my_getenv(ql: Qiling):
+ env = {
+ "ID" : b"000000000000000",
+ "ethaddr" : b"11:22:33:44:55:66"
+ }
- params = ql.os.resolve_fcall_params({'key': STRING})
- value = env.get(params["key"], b"")
+ params = ql.os.resolve_fcall_params({'key': STRING})
+ value = env.get(params["key"], b"")
- value_addr = ql.os.heap.alloc(len(value))
- ql.mem.write(value_addr, value)
+ value_addr = ql.os.heap.alloc(len(value))
+ ql.mem.write(value_addr, value)
- ql.arch.regs.r0 = value_addr
- ql.arch.regs.arch_pc = ql.arch.regs.lr
+ ql.arch.regs.r0 = value_addr
+ ql.arch.regs.arch_pc = ql.arch.regs.lr
- def get_password(ql: Qiling):
- password_raw = ql.mem.read(ql.arch.regs.r0, ql.arch.regs.r2)
- password = ''
- for item in password_raw:
- if 0 <= item <= 9:
- password += chr(item + 48)
- else:
- password += chr(item + 87)
+def get_password(ql: Qiling):
+ # we land on a memcmp call, where the real password is being compared to
+ # the one provided by the user. we can follow the arguments to read the
+ # real password
- print("The password is: %s" % password)
+ params = ql.os.resolve_fcall_params({
+ 'ptr1': POINTER, # points to real password
+ 'ptr2': POINTER, # points to user provided password
+ 'size': SIZE_T # comparison length
+ })
- def partial_run_init(ql: Qiling):
- # argv prepare
- ql.arch.regs.arch_sp -= 0x30
- arg0_ptr = ql.arch.regs.arch_sp
- ql.mem.write(arg0_ptr, b"kaimendaji")
+ ptr1 = params['ptr1']
+ size = params['size']
- ql.arch.regs.arch_sp -= 0x10
- arg1_ptr = ql.arch.regs.arch_sp
- ql.mem.write(arg1_ptr, b"000000") # arbitrary password
+ password_raw = ql.mem.read(ptr1, size)
- ql.arch.regs.arch_sp -= 0x20
- argv_ptr = ql.arch.regs.arch_sp
- ql.mem.write_ptr(argv_ptr, arg0_ptr)
- ql.mem.write_ptr(argv_ptr + ql.arch.pointersize, arg1_ptr)
+ def __hex_digit(ch: int) -> str:
+ off = ord('0') if ch in range(10) else ord('a') - 10
- ql.arch.regs.r2 = 2
- ql.arch.regs.r3 = argv_ptr
+ return chr(ch + off)
- with open("../examples/rootfs/blob/u-boot.bin.img", "rb") as f:
- uboot_code = f.read()
+ # should be: "013f1f"
+ password = "".join(__hex_digit(ch) for ch in password_raw)
- ql = Qiling(code=uboot_code[0x40:], archtype=QL_ARCH.ARM, ostype=QL_OS.BLOB, profile="uboot_bin.ql", verbose=QL_VERBOSE.OFF)
+ print(f'The password is: {password}')
- image_base_addr = ql.loader.load_address
- ql.hook_address(my_getenv, image_base_addr + 0x13AC0)
- ql.hook_address(get_password, image_base_addr + 0x48634)
- partial_run_init(ql)
+def partial_run_init(ql: Qiling):
+ # argv prepare
+ ql.arch.regs.arch_sp -= 0x30
+ arg0_ptr = ql.arch.regs.arch_sp
+ ql.mem.write(arg0_ptr, b"kaimendaji")
+
+ ql.arch.regs.arch_sp -= 0x10
+ arg1_ptr = ql.arch.regs.arch_sp
+ ql.mem.write(arg1_ptr, b"000000") # arbitrary password
- ql.run(image_base_addr + 0x486B4, image_base_addr + 0x48718)
+ ql.arch.regs.arch_sp -= 0x20
+ argv_ptr = ql.arch.regs.arch_sp
+ ql.mem.write_ptr(argv_ptr, arg0_ptr)
+ ql.mem.write_ptr(argv_ptr + ql.arch.pointersize, arg1_ptr)
+
+ ql.arch.regs.r2 = 2
+ ql.arch.regs.r3 = argv_ptr
if __name__ == "__main__":
- get_kaimendaji_password()
+ with open("../examples/rootfs/blob/u-boot.bin.img", "rb") as f:
+ uboot_code = f.read()
+
+ ql = Qiling(code=uboot_code[0x40:], archtype=QL_ARCH.ARM, ostype=QL_OS.BLOB, profile="uboot_bin.ql", verbose=QL_VERBOSE.DEBUG)
+
+ imgbase = ql.loader.images[0].base
+
+ ql.hook_address(my_getenv, imgbase + 0x13AC0)
+ ql.hook_address(get_password, imgbase + 0x48634)
+
+ partial_run_init(ql)
+
+ ql.run(imgbase + 0x486B4, imgbase + 0x48718)
diff --git a/examples/uboot_bin.ql b/examples/uboot_bin.ql
index b7f7216c8..c33a7d238 100644
--- a/examples/uboot_bin.ql
+++ b/examples/uboot_bin.ql
@@ -1,5 +1,6 @@
[CODE]
ram_size = 0xa00000
+load_address = 0x80800000
entry_point = 0x80800000
heap_size = 0x300000
diff --git a/qiling/debugger/gdb/xml/arm/arm-m-profile.xml b/qiling/debugger/gdb/xml/cortex_m/arm-m-profile.xml
similarity index 81%
rename from qiling/debugger/gdb/xml/arm/arm-m-profile.xml
rename to qiling/debugger/gdb/xml/cortex_m/arm-m-profile.xml
index f0584a206..a07071502 100644
--- a/qiling/debugger/gdb/xml/arm/arm-m-profile.xml
+++ b/qiling/debugger/gdb/xml/cortex_m/arm-m-profile.xml
@@ -25,4 +25,10 @@
-
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/qiling/debugger/gdb/xml/cortex_m/target.xml b/qiling/debugger/gdb/xml/cortex_m/target.xml
new file mode 100644
index 000000000..635912398
--- /dev/null
+++ b/qiling/debugger/gdb/xml/cortex_m/target.xml
@@ -0,0 +1,12 @@
+
+
+
+
+
+ armv7-m
+
+
\ No newline at end of file
diff --git a/qiling/debugger/gdb/xmlregs.py b/qiling/debugger/gdb/xmlregs.py
index f569cd22c..e1abf08d5 100644
--- a/qiling/debugger/gdb/xmlregs.py
+++ b/qiling/debugger/gdb/xmlregs.py
@@ -13,14 +13,21 @@
reg_map_q as arm_regs_q,
reg_map_s as arm_regs_s
)
+
+from qiling.arch.cortex_m_const import (
+ reg_map as cortex_m_regs
+)
+
from qiling.arch.arm64_const import (
reg_map as arm64_regs,
reg_map_v as arm64_regs_v,
reg_map_fp as arm64_reg_map_fp
)
+
from qiling.arch.mips_const import (
reg_map as mips_regs_gpr
)
+
from qiling.arch.x86_const import (
reg_map_32 as x86_regs_32,
reg_map_64 as x86_regs_64,
@@ -133,7 +140,7 @@ def __load_regsmap(archtype: QL_ARCH, xmltree: ElementTree.ElementTree) -> Seque
QL_ARCH.X86: dict(**x86_regs_32, **x86_regs_misc, **x86_regs_cr, **x86_regs_st, **x86_regs_xmm),
QL_ARCH.X8664: dict(**x86_regs_64, **x86_regs_misc, **x86_regs_cr, **x86_regs_st, **x86_regs_xmm, **x86_regs_ymm),
QL_ARCH.ARM: dict(**arm_regs, **arm_regs_vfp, **arm_regs_q, **arm_regs_s),
- QL_ARCH.CORTEX_M: arm_regs,
+ QL_ARCH.CORTEX_M: dict(**cortex_m_regs),
QL_ARCH.ARM64: dict(**arm64_regs, **arm64_regs_v, **arm64_reg_map_fp),
QL_ARCH.MIPS: dict(**mips_regs_gpr)
}[archtype]
diff --git a/qiling/debugger/qdb/context.py b/qiling/debugger/qdb/context.py
index 349197544..344f7563a 100644
--- a/qiling/debugger/qdb/context.py
+++ b/qiling/debugger/qdb/context.py
@@ -57,7 +57,7 @@ def disasm(self, address: int, detail: bool = False) -> InsnLike:
"""Helper function for disassembling.
"""
- insn_bytes = self.read_insn(address)
+ insn_bytes = self.read_insn(address) or b''
insn = None
if insn_bytes:
@@ -75,7 +75,7 @@ def disasm_lite(self, address: int) -> Tuple[int, int, str, str]:
A tuple of: instruction address, size, mnemonic and operands
"""
- insn_bytes = self.read_insn(address)
+ insn_bytes = self.read_insn(address) or b''
insn = None
if insn_bytes:
diff --git a/qiling/debugger/qdb/qdb.py b/qiling/debugger/qdb/qdb.py
index ae942139e..7182b46da 100644
--- a/qiling/debugger/qdb/qdb.py
+++ b/qiling/debugger/qdb/qdb.py
@@ -136,10 +136,7 @@ def __bp_handler(ql: Qiling, address: int, size: int):
with self.__set_temp(self.ql, 'verbose', QL_VERBOSE.DISABLED):
self.ql.os.run()
- if self.ql.os.type is QL_OS.BLOB:
- self.ql.loader.entry_point = self.ql.loader.load_address
-
- elif init_hook:
+ if init_hook:
for each_hook in init_hook:
self.do_breakpoint(each_hook)
diff --git a/qiling/loader/blob.py b/qiling/loader/blob.py
index b8831a552..f17b80a9d 100644
--- a/qiling/loader/blob.py
+++ b/qiling/loader/blob.py
@@ -15,7 +15,8 @@ def __init__(self, ql: Qiling):
self.load_address = 0
def run(self):
- self.load_address = self.ql.os.entry_point # for consistency
+ self.load_address = self.ql.os.load_address
+ self.entry_point = self.ql.os.entry_point
code_begins = self.load_address
code_size = self.ql.os.code_ram_size
@@ -28,8 +29,10 @@ def run(self):
self.images.append(Image(code_begins, code_ends, 'blob_code'))
# FIXME: heap starts above end of ram??
+ # FIXME: heap should be allocated by OS, not loader
heap_base = code_ends
heap_size = int(self.ql.os.profile.get("CODE", "heap_size"), 16)
self.ql.os.heap = QlMemoryHeap(self.ql, heap_base, heap_base + heap_size)
+ # FIXME: stack pointer should be a configurable profile setting
self.ql.arch.regs.arch_sp = code_ends - 0x1000
diff --git a/qiling/os/blob/blob.py b/qiling/os/blob/blob.py
index 02e6f94d3..e4a022562 100644
--- a/qiling/os/blob/blob.py
+++ b/qiling/os/blob/blob.py
@@ -9,6 +9,7 @@
from qiling.os.fcall import QlFunctionCall
from qiling.os.os import QlOs
+
class QlOsBlob(QlOs):
""" QlOsBlob for bare barines.
@@ -21,7 +22,7 @@ class QlOsBlob(QlOs):
type = QL_OS.BLOB
def __init__(self, ql: Qiling):
- super(QlOsBlob, self).__init__(ql)
+ super().__init__(ql)
self.ql = ql
@@ -39,11 +40,14 @@ def __init__(self, ql: Qiling):
self.fcall = QlFunctionCall(ql, cc)
def run(self):
- if self.ql.entry_point:
+ # if entry point was set explicitly, override the default one
+ if self.ql.entry_point is not None:
self.entry_point = self.ql.entry_point
- self.exit_point = self.ql.loader.load_address + len(self.ql.code)
- if self.ql.exit_point:
+ self.exit_point = self.load_address + len(self.ql.code)
+
+ # if exit point was set explicitly, override the default one
+ if self.ql.exit_point is not None:
self.exit_point = self.ql.exit_point
self.ql.emu_start(self.entry_point, self.exit_point, self.ql.timeout, self.ql.count)
diff --git a/qiling/os/os.py b/qiling/os/os.py
index 636e089c4..dd9f38564 100644
--- a/qiling/os/os.py
+++ b/qiling/os/os.py
@@ -89,6 +89,7 @@ def __init__(self, ql: Qiling, resolvers: Mapping[Any, Resolver] = {}):
if self.ql.code:
# this shellcode entrypoint does not work for windows
# windows shellcode entry point will comes from pe loader
+ self.load_address = self.profile.getint('CODE', 'load_address')
self.entry_point = self.profile.getint('CODE', 'entry_point')
self.code_ram_size = self.profile.getint('CODE', 'ram_size')
diff --git a/qiling/profiles/linux.ql b/qiling/profiles/linux.ql
index eac82348b..de828a32d 100644
--- a/qiling/profiles/linux.ql
+++ b/qiling/profiles/linux.ql
@@ -1,6 +1,7 @@
[CODE]
# ram_size 0xa00000 is 10MB
ram_size = 0xa00000
+load_address = 0x1000000
entry_point = 0x1000000
diff --git a/qiling/profiles/windows.ql b/qiling/profiles/windows.ql
index 15cc2f39b..ac2cc6684 100644
--- a/qiling/profiles/windows.ql
+++ b/qiling/profiles/windows.ql
@@ -23,6 +23,7 @@ KI_USER_SHARED_DATA = 0x7ffe0000
[CODE]
# ram_size 0xa00000 is 10MB
ram_size = 0xa00000
+load_address = 0x1000000
entry_point = 0x1000000
[KERNEL]
diff --git a/tests/__init__.py b/tests/__init__.py
new file mode 100644
index 000000000..e69de29bb
diff --git a/tests/profiles/uboot_bin.ql b/tests/profiles/uboot_bin.ql
index b7f7216c8..c33a7d238 100644
--- a/tests/profiles/uboot_bin.ql
+++ b/tests/profiles/uboot_bin.ql
@@ -1,5 +1,6 @@
[CODE]
ram_size = 0xa00000
+load_address = 0x80800000
entry_point = 0x80800000
heap_size = 0x300000
diff --git a/tests/test_blob.py b/tests/test_blob.py
index bc191dc16..33e35751a 100644
--- a/tests/test_blob.py
+++ b/tests/test_blob.py
@@ -10,13 +10,17 @@
from qiling.core import Qiling
from qiling.const import QL_ARCH, QL_OS, QL_VERBOSE
-from qiling.os.const import STRING
+from qiling.os.const import STRING, POINTER, SIZE_T
class BlobTest(unittest.TestCase):
def test_uboot_arm(self):
- def my_getenv(ql, *args, **kwargs):
- env = {"ID": b"000000000000000", "ethaddr": b"11:22:33:44:55:66"}
+ def my_getenv(ql: Qiling):
+ env = {
+ "ID": b"000000000000000",
+ "ethaddr": b"11:22:33:44:55:66"
+ }
+
params = ql.os.resolve_fcall_params({'key': STRING})
value = env.get(params["key"], b"")
@@ -26,12 +30,23 @@ def my_getenv(ql, *args, **kwargs):
ql.arch.regs.r0 = value_addr
ql.arch.regs.arch_pc = ql.arch.regs.lr
- def check_password(ql, *args, **kwargs):
- passwd_output = ql.mem.read(ql.arch.regs.r0, ql.arch.regs.r2)
- passwd_input = ql.mem.read(ql.arch.regs.r1, ql.arch.regs.r2)
- self.assertEqual(passwd_output, passwd_input)
+ def check_password(ql: Qiling):
+ params = ql.os.resolve_fcall_params({
+ 'ptr1': POINTER, # points to real password
+ 'ptr2': POINTER, # points to user provided password
+ 'size': SIZE_T # comparison length
+ })
+
+ ptr1 = params['ptr1']
+ ptr2 = params['ptr2']
+ size = params['size']
+
+ real_password = ql.mem.read(ptr1, size)
+ user_password = ql.mem.read(ptr2, size)
- def partial_run_init(ql):
+ self.assertSequenceEqual(real_password, user_password, seq_type=bytearray)
+
+ def partial_run_init(ql: Qiling):
# argv prepare
ql.arch.regs.arch_sp -= 0x30
arg0_ptr = ql.arch.regs.arch_sp
@@ -56,13 +71,14 @@ def partial_run_init(ql):
ql = Qiling(code=uboot_code[0x40:], archtype=QL_ARCH.ARM, ostype=QL_OS.BLOB, profile="profiles/uboot_bin.ql", verbose=QL_VERBOSE.DEBUG)
- image_base_addr = ql.loader.load_address
- ql.hook_address(my_getenv, image_base_addr + 0x13AC0)
- ql.hook_address(check_password, image_base_addr + 0x48634)
+ imgbase = ql.loader.images[0].base
+
+ ql.hook_address(my_getenv, imgbase + 0x13AC0)
+ ql.hook_address(check_password, imgbase + 0x48634)
partial_run_init(ql)
- ql.run(image_base_addr + 0x486B4, image_base_addr + 0x48718)
+ ql.run(imgbase + 0x486B4, imgbase + 0x48718)
del ql