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
104 changes: 59 additions & 45 deletions examples/hello_arm_uboot.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)
1 change: 1 addition & 0 deletions examples/uboot_bin.ql
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
[CODE]
ram_size = 0xa00000
load_address = 0x80800000
entry_point = 0x80800000
heap_size = 0x300000

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,4 +25,10 @@
<reg name="pc" bitsize="32" type="code_ptr"/>

<reg name="xpsr" bitsize="32" regnum="25"/>
</feature>
<reg name="msp" bitsize="32"/>
<reg name="psp" bitsize="32"/>
<reg name="primask" bitsize="32"/>
<reg name="basepri" bitsize="32"/>
<reg name="faultmask" bitsize="32"/>
<reg name="control" bitsize="32"/>
</feature>
12 changes: 12 additions & 0 deletions qiling/debugger/gdb/xml/cortex_m/target.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<?xml version="1.0"?>
<!-- Copyright (C) 2009-2016 Free Software Foundation, Inc.

*!Copying and distribution of this file, with or without modification,
*!are permitted in any medium without royalty provided the copyright
*!notice and this notice are preserved. -->

<!DOCTYPE target SYSTEM "gdb-target.dtd">
<target xmlns:xi="http://www.w3.org/2001/XInclude">
<architecture>armv7-m</architecture>
<xi:include href="arm-m-profile.xml"/>
</target>
9 changes: 8 additions & 1 deletion qiling/debugger/gdb/xmlregs.py
Comment thread
elicn marked this conversation as resolved.
Comment thread
elicn marked this conversation as resolved.
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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]
Expand Down
4 changes: 2 additions & 2 deletions qiling/debugger/qdb/context.py
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand All @@ -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:
Expand Down
5 changes: 1 addition & 4 deletions qiling/debugger/qdb/qdb.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)

Expand Down
5 changes: 4 additions & 1 deletion qiling/loader/blob.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
12 changes: 8 additions & 4 deletions qiling/os/blob/blob.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
from qiling.os.fcall import QlFunctionCall
from qiling.os.os import QlOs


class QlOsBlob(QlOs):
""" QlOsBlob for bare barines.

Expand All @@ -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

Expand All @@ -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)
1 change: 1 addition & 0 deletions qiling/os/os.py
Original file line number Diff line number Diff line change
Expand Up @@ -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')

Expand Down
1 change: 1 addition & 0 deletions qiling/profiles/linux.ql
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
[CODE]
# ram_size 0xa00000 is 10MB
ram_size = 0xa00000
load_address = 0x1000000
entry_point = 0x1000000


Expand Down
1 change: 1 addition & 0 deletions qiling/profiles/windows.ql
Original file line number Diff line number Diff line change
Expand Up @@ -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]
Expand Down
Empty file added tests/__init__.py
Empty file.
1 change: 1 addition & 0 deletions tests/profiles/uboot_bin.ql
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
[CODE]
ram_size = 0xa00000
load_address = 0x80800000
entry_point = 0x80800000
heap_size = 0x300000

Expand Down
40 changes: 28 additions & 12 deletions tests/test_blob.py
Original file line number Diff line number Diff line change
Expand Up @@ -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"")

Expand All @@ -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
Expand All @@ -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

Expand Down