Skip to content

Commit 095e8ea

Browse files
committed
add blob loader
1 parent 44c305f commit 095e8ea

File tree

5 files changed

+167
-2
lines changed

5 files changed

+167
-2
lines changed

qiling/const.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ class QL_OS(IntEnum):
3333
EVM = 207
3434
QNX = 208
3535
MCU = 209
36+
BARE = 210
3637

3738
class QL_VERBOSE(IntEnum):
3839
OFF = 0
@@ -65,6 +66,7 @@ class QL_INTERCEPT(IntEnum):
6566
QL_OS_BAREMETAL = (QL_OS.MCU,)
6667
QL_OS_INTERPRETER = (QL_OS.EVM,)
6768
QL_OS_ALL = QL_OS_POSIX + QL_OS_NONPID + (QL_OS.WINDOWS,)
69+
QL_OS_BARE_RTOS = (QL_OS.BARE,)
6870

6971
QL_HOOK_BLOCK = 0b0001
7072
QL_CALL_BLOCK = 0b0010
@@ -90,6 +92,7 @@ def __reverse_enum(e: Type[Enum]) -> Mapping[str, Any]:
9092
QL_OS.DOS : "DOS",
9193
QL_OS.EVM : "EVM",
9294
QL_OS.MCU : "MCU",
95+
QL_OS.BARE : "BLOB"
9396
}
9497

9598
arch_os_map = {

qiling/core.py

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919
from .hw.hw import QlHwManager
2020
from .loader.loader import QlLoader
2121

22-
from .const import QL_ARCH_ENDIAN, QL_ENDIAN, QL_VERBOSE, QL_OS_INTERPRETER, QL_OS_BAREMETAL, QL_OS_ALL
22+
from .const import QL_ARCH_ENDIAN, QL_ENDIAN, QL_VERBOSE, QL_OS_INTERPRETER, QL_OS_BAREMETAL, QL_OS_ALL, QL_OS_BARE_RTOS
2323
from .exception import QlErrorFileNotFound, QlErrorArch, QlErrorOsType
2424
from .utils import *
2525
from .core_struct import QlCoreStructs
@@ -194,7 +194,7 @@ def __init__(
194194
##############
195195
# Components #
196196
##############
197-
if self.gpos or self.baremetal:
197+
if self.gpos or self.baremetal or self.blob:
198198
self._mem = component_setup("os", "memory", self)
199199
self._reg = component_setup("arch", "register", self)
200200

@@ -490,6 +490,15 @@ def gpos(self) -> bool:
490490
"""
491491
return self.ostype in QL_OS_ALL
492492

493+
@property
494+
def blob(self) -> bool:
495+
""" Static linked bare binary type
496+
- U-Boot, VxWorks, eCos
497+
498+
Type: bool
499+
"""
500+
return self.ostype in QL_OS_BARE_RTOS
501+
493502
@property
494503
def platform_os(self):
495504
""" Specify current platform os where Qiling runs on.

qiling/loader/blob.py

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
#!/usr/bin/env python3
2+
#
3+
# Cross Platform and Multi Architecture Advanced Binary Emulation Framework
4+
#
5+
6+
from qiling import Qiling
7+
from qiling.cc import QlCC, intel, arm, mips
8+
from qiling.const import QL_ARCH
9+
from qiling.loader.loader import QlLoader
10+
from qiling.os.fcall import QlFunctionCall
11+
from qiling.os.memory import QlMemoryHeap
12+
from qiling.os.os import QlOs
13+
14+
15+
class QLOsBare(QlOs):
16+
""" QLOsBare for bare barines.
17+
18+
For bare binary such as u-boot, it's ready to be mapped and executed directly,
19+
where there is(may be) no concept of os? Currently, some functionalities such as
20+
resolve_fcall_params(), heap or add_fs_mapper() are based on os. To keep the
21+
consistence of api usage, QLOsBare is introduced and placed at its loader temporarily.
22+
"""
23+
def __init__(self, ql: Qiling):
24+
super(QLOsBare, self).__init__(ql)
25+
26+
self.ql = ql
27+
28+
cc: QlCC = {
29+
QL_ARCH.X86 : intel.cdecl,
30+
QL_ARCH.X8664 : intel.amd64,
31+
QL_ARCH.ARM : arm.aarch32,
32+
QL_ARCH.ARM64 : arm.aarch64,
33+
QL_ARCH.MIPS : mips.mipso32
34+
}[ql.archtype](ql)
35+
36+
self.fcall = QlFunctionCall(ql, cc)
37+
38+
def run(self):
39+
self.entry_point = self.ql.entry_point if self.ql.entry_point else self.ql.loader.load_address
40+
self.exit_point = self.ql.exit_point if self.ql.exit_point else self.ql.loader.load_address + len(self.ql.code)
41+
42+
self.ql.emu_start(self.entry_point, self.exit_point, self.ql.timeout, self.ql.count)
43+
44+
class QlLoaderBLOB(QlLoader):
45+
def __init__(self, ql: Qiling):
46+
super().__init__(ql)
47+
48+
self.load_address = 0
49+
50+
def run(self):
51+
# setup bare os
52+
self.ql._os = QLOsBare(self.ql)
53+
54+
self.load_address = self.ql.os.entry_point # for consistency
55+
56+
self.ql.mem.map(self.ql.os.entry_point, self.ql.os.code_ram_size, info="[code]")
57+
self.ql.mem.write(self.ql.os.entry_point, self.ql.code)
58+
59+
heap_address = self.ql.os.entry_point + self.ql.os.code_ram_size
60+
heap_size = int(self.ql.os.profile.get("CODE", "heap_size"), 16)
61+
self.ql.os.heap = QlMemoryHeap(self.ql, heap_address, heap_address + heap_size)
62+
63+
self.ql.reg.arch_sp = heap_address - 0x1000
64+
65+
return

tests/profiles/uboot_bin.ql

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
[CODE]
2+
ram_size = 0xa00000
3+
entry_point = 0x80800000
4+
heap_size = 0x300000
5+
6+
7+
[LOG]
8+
# log directory output
9+
# usage: dir = qlog
10+
dir =
11+
# split log file, use with multithread
12+
split = False
13+
14+
15+
[MISC]
16+
# append string into different logs
17+
# maily for multiple times Ql run with one file
18+
# usage: append = test1
19+
append =
20+
current_path = /

tests/test_blob.py

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
#!/usr/bin/env python3
2+
#
3+
# Cross Platform and Multi Architecture Advanced Binary Emulation Framework
4+
#
5+
6+
import sys, unittest
7+
sys.path.append("..")
8+
9+
from qiling.core import Qiling
10+
from qiling.const import QL_VERBOSE
11+
from qiling.os.const import STRING
12+
13+
class BlobTest(unittest.TestCase):
14+
def test_uboot_arm(self):
15+
def my_getenv(ql, *args, **kwargs):
16+
env = {"ID": b"000000000000000", "ethaddr": b"11:22:33:44:55:66"}
17+
params = ql.os.resolve_fcall_params({'key': STRING})
18+
value = env.get(params["key"], b"")
19+
20+
value_addr = ql.os.heap.alloc(len(value))
21+
ql.mem.write(value_addr, value)
22+
23+
ql.reg.r0 = value_addr
24+
ql.reg.arch_pc = ql.reg.lr
25+
26+
def check_password(ql, *args, **kwargs):
27+
passwd_output = ql.mem.read(ql.reg.r0, ql.reg.r2)
28+
passwd_input = ql.mem.read(ql.reg.r1, ql.reg.r2)
29+
self.assertEqual(passwd_output, passwd_input)
30+
31+
def partial_run_init(ql):
32+
# argv prepare
33+
ql.reg.arch_sp -= 0x30
34+
arg0_ptr = ql.reg.arch_sp
35+
ql.mem.write(arg0_ptr, b"kaimendaji")
36+
37+
ql.reg.arch_sp -= 0x10
38+
arg1_ptr = ql.reg.arch_sp
39+
ql.mem.write(arg1_ptr, b"013f1f")
40+
41+
ql.reg.arch_sp -= 0x20
42+
argv_ptr = ql.reg.arch_sp
43+
ql.mem.write(argv_ptr, ql.pack(arg0_ptr))
44+
ql.mem.write(argv_ptr + ql.pointersize, ql.pack(arg1_ptr))
45+
46+
ql.reg.r2 = 2
47+
ql.reg.r3 = argv_ptr
48+
49+
print("ARM uboot bin")
50+
51+
with open("../examples/rootfs/blob/u-boot.bin.img", "rb") as f:
52+
uboot_code = f.read()
53+
54+
ql = Qiling(code=uboot_code[0x40:], archtype="arm", ostype="bare", profile="profiles/uboot_bin.ql", verbose=QL_VERBOSE.DEBUG)
55+
56+
image_base_addr = ql.loader.load_address
57+
ql.hook_address(my_getenv, image_base_addr + 0x13AC0)
58+
ql.hook_address(check_password, image_base_addr + 0x48634)
59+
60+
partial_run_init(ql)
61+
62+
ql.run(image_base_addr + 0x486B4, image_base_addr + 0x48718)
63+
64+
del ql
65+
66+
67+
if __name__ == "__main__":
68+
unittest.main()

0 commit comments

Comments
 (0)