Skip to content

Commit 09f2599

Browse files
authored
Merge pull request #5 from qilingframework/dev
Dev
2 parents adc18e5 + 99d676f commit 09f2599

File tree

199 files changed

+6691
-3797
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

199 files changed

+6691
-3797
lines changed

ChangeLog

Lines changed: 22 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,19 +2,34 @@ This file details the changelog of Qiling Framework.
22

33

44
------------------------------------
5-
[Version 1.4.2]: Jan 29th, 2022
5+
[Version 1.4.2]: Feb 13th, 2022
66

77
New features:
8-
-
9-
-
8+
- Add stm32f103 support (#1087)
9+
- Add Arduino Due (SAM3X8E) Support (#1090)
1010

1111
Improvements:
12-
-
13-
-
12+
- ARM exception handler improvements (#1056)
13+
- UEFI improvements (#1061)
14+
- Qdb improvements (#1058)
15+
- Update rich api in evm dbgcui (#1062)
16+
- Add security coockies back into PE loader for kernel driver (#1063)
17+
- Fix ql_open_flag_mapping for Linux binary emulation on Windows (#1064)
18+
- Minor changes and fixes to the tracing module (#1065)
19+
- Fix unicornafl for linux_x8664 fuzzing example (#1068)
20+
- Fuzzing improvements (#1075)
21+
- Add fix and example for openat path traversion (#1076)
22+
- Fix _CreateFileA params issue (#1079)
1423

1524
Contributors:
16-
-
17-
-
25+
- nmantani
26+
- hardik05
27+
- cla7aye15I4nd
28+
- ucgJhe
29+
- elicn
30+
- wtdcode
31+
- kabeor
32+
- xwings
1833

1934

2035
------------------------------------

README.md

Lines changed: 26 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -88,55 +88,55 @@ Please see [setup guide](https://docs.qiling.io/en/latest/install/) file for how
8888

8989
#### Examples
9090

91-
- Below example shows how to use Qiling framework to emulate a Windows EXE on a Linux machine
91+
- The example below shows how to use Qiling framework in the most striaghtforward way to emulate a Windows executable.
9292

9393
```python
94-
from qiling import *
95-
96-
# sandbox to emulate the EXE
97-
def my_sandbox(path, rootfs):
98-
# setup Qiling engine
99-
ql = Qiling(path, rootfs)
100-
# now emulate the EXE
101-
ql.run()
94+
from qiling import Qiling
10295

10396
if __name__ == "__main__":
104-
# execute Windows EXE under our rootfs
105-
my_sandbox(["examples/rootfs/x86_windows/bin/x86_hello.exe"], "examples/rootfs/x86_windows")
97+
# initialize Qiling instance, specifying the executable to emulate and the emulated system root.
98+
# note that the current working directory is assumed to be Qiling home
99+
ql = Qiling([r'examples/rootfs/x86_windows/bin/x86_hello.exe'], r'examples/rootfs/x86_windows')
100+
101+
# start emulation
102+
ql.run()
106103
```
107104

108-
- Below example shows how to use Qiling framework to dynamically patch a Windows crackme, make it always display "Congratulation" dialog
105+
- The following example shows how a Windows crackme may be patched dynamically to make it always display the "Congratulation" dialog.
109106

110107
```python
111-
from qiling import *
108+
from qiling import Qiling
109+
110+
def force_call_dialog_func(ql: Qiling):
111+
# get DialogFunc address from current stack frame
112+
lpDialogFunc = ql.stack_read(-8)
112113

113-
def force_call_dialog_func(ql):
114-
# get DialogFunc address
115-
lpDialogFunc = ql.unpack32(ql.mem.read(ql.reg.esp - 0x8, 4))
116114
# setup stack memory for DialogFunc
117115
ql.stack_push(0)
118-
ql.stack_push(1001)
119-
ql.stack_push(273)
116+
ql.stack_push(1001) # IDS_APPNAME
117+
ql.stack_push(0x111) # WM_COMMAND
120118
ql.stack_push(0)
119+
120+
# push return address
121121
ql.stack_push(0x0401018)
122-
# force EIP to DialogFunc
123-
ql.reg.eip = lpDialogFunc
122+
123+
# resume emulation from DialogFunc address
124+
ql.arch.regs.eip = lpDialogFunc
124125

125126

126-
def my_sandbox(path, rootfs):
127-
ql = Qiling(path, rootfs)
127+
if __name__ == "__main__":
128+
# initialize Qiling instance
129+
ql = Qiling([r'rootfs/x86_windows/bin/Easy_CrackMe.exe'], r'rootfs/x86_windows')
130+
128131
# NOP out some code
129132
ql.patch(0x004010B5, b'\x90\x90')
130133
ql.patch(0x004010CD, b'\x90\x90')
131134
ql.patch(0x0040110B, b'\x90\x90')
132135
ql.patch(0x00401112, b'\x90\x90')
136+
133137
# hook at an address with a callback
134138
ql.hook_address(force_call_dialog_func, 0x00401016)
135139
ql.run()
136-
137-
138-
if __name__ == "__main__":
139-
my_sandbox(["rootfs/x86_windows/bin/Easy_CrackMe.exe"], "rootfs/x86_windows")
140140
```
141141

142142
The below Youtube video shows how the above example works.

examples/crackme_x86_linux.py

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -16,14 +16,11 @@
1616

1717
class Solver:
1818
def __init__(self, invalid: bytes):
19-
mock_stdin = pipe.SimpleInStream(sys.stdin.fileno())
20-
mock_stdout = pipe.NullOutStream(sys.stdout.fileno())
21-
2219
# create a silent qiling instance
23-
self.ql = Qiling([rf"{ROOTFS}/bin/crackme_linux"], ROOTFS,
24-
verbose=QL_VERBOSE.OFF, # thwart qiling logger output
25-
stdin=mock_stdin, # take over the input to the program using a fake stdin
26-
stdout=mock_stdout) # disregard program output
20+
self.ql = Qiling([rf"{ROOTFS}/bin/crackme_linux"], ROOTFS, verbose=QL_VERBOSE.OFF)
21+
22+
self.ql.os.stdin = pipe.SimpleInStream(sys.stdin.fileno()) # take over the input to the program using a fake stdin
23+
self.ql.os.stdout = pipe.NullOutStream(sys.stdout.fileno()) # disregard program output
2724

2825
# execute program until it reaches the 'main' function
2926
self.ql.run(end=0x0804851b)
@@ -32,7 +29,7 @@ def __init__(self, invalid: bytes):
3229
#
3330
# since the emulation halted upon entering 'main', its return address is there on
3431
# the stack. we use it to limit the emulation till function returns
35-
self.replay_starts = self.ql.reg.arch_pc
32+
self.replay_starts = self.ql.arch.regs.arch_pc
3633
self.replay_ends = self.ql.stack_read(0)
3734

3835
# instead of restarting the whole program every time a new flag character is guessed,

examples/crackme_x86_windows.py

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -14,13 +14,10 @@ def instruction_count(ql: Qiling, address: int, size: int, user_data):
1414
user_data[0] += 1
1515

1616
def get_count(flag):
17-
mock_stdin = pipe.SimpleStringBuffer()
18-
mock_stdout = pipe.SimpleStringBuffer()
17+
ql = Qiling(["rootfs/x86_windows/bin/crackme.exe"], "rootfs/x86_windows", verbose=QL_VERBOSE.OFF)
1918

20-
ql = Qiling(["rootfs/x86_windows/bin/crackme.exe"], "rootfs/x86_windows",
21-
verbose=QL_VERBOSE.OFF,
22-
stdin=mock_stdin,
23-
stdout=mock_stdout)
19+
ql.os.stdin = pipe.SimpleStringBuffer()
20+
ql.os.stdout = pipe.SimpleStringBuffer()
2421

2522
ql.os.stdin.write(bytes("".join(flag) + "\n", 'utf-8'))
2623
count = [0]

examples/crackme_x86_windows_auto.py

Lines changed: 57 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -8,26 +8,64 @@
88

99
from qiling import Qiling
1010
from qiling.extensions import pipe
11+
from qiling.const import QL_INTERCEPT, QL_VERBOSE
12+
from qiling.os.windows.api import HWND, UINT, LONG
13+
14+
def hook_DialogBoxParamA_onexit(ql: Qiling, address: int, params, retval: int):
15+
# extract lpDialogFunc value
16+
# [see arguments list at 'qiling/os/windows/dlls/user32.py' -> 'hook_DialogBoxParamA']
17+
lpDialogFunc = params['lpDialogFunc']
18+
19+
def call_DialogFunc(ql: Qiling):
20+
# we would like to resume from the exact same address that used to invoke
21+
# this hook. in order to prevent an endless loop of hook invocations, we
22+
# remove the hook through its handle.
23+
hh.remove()
24+
25+
WM_COMMAND = 0x111
26+
IDS_APPNAME = 1001
27+
28+
# [steps #3 and #4]
29+
# set up the arguments and call the address passed through the lpDialogFunc
30+
# param. make sure it resumes back to where we were.
31+
ql.os.fcall.call_native(lpDialogFunc, (
32+
(HWND, 0),
33+
(UINT, WM_COMMAND),
34+
(UINT, IDS_APPNAME),
35+
(LONG, 0),
36+
), ql.arch.regs.arch_pc)
37+
38+
# get DialogBoxParamA return address; should be the first item on the stack
39+
retaddr = ql.arch.stack_read(0)
40+
41+
# we would like to call DialogFunc as soon as DialogBoxParamA returns, so we
42+
# hook its return address. once it returns, 'call_DialogFunc' will be invoked.
43+
hh = ql.hook_address(call_DialogFunc, retaddr)
44+
45+
def our_sandbox(path: str, rootfs: str):
46+
ql = Qiling([path], rootfs, verbose=QL_VERBOSE.DEFAULT)
47+
48+
# this crackme's logic lies within the function passed to DialogBoxParamA through
49+
# the lpDialogFunc parameter. normally DialogBoxParamA would call the function
50+
# passed through that parameter, but Qiling's implementation for it doesn't do
51+
# that.
52+
#
53+
# to solve this crackme and force the "success" dialog to show, we will:
54+
# 1. set up a mock stdin and feed it with the correct flag
55+
# 1. hook DialogBoxParamA to see where its lpDialogFunc param points to
56+
# 2. set up a valid set of arguments DialogFunc expects to see
57+
# 3. call it and see it greets us with a "success" message
58+
59+
# [step #1]
60+
# set up a mock stdin and feed it with mocked keystrokes
61+
ql.os.stdin = pipe.SimpleInStream(sys.stdin.fileno())
62+
ql.os.stdin.write(b'Ea5yR3versing\n')
63+
64+
# [step #2]
65+
# intercept DialogBoxParamA on exit
66+
ql.os.set_api('DialogBoxParamA', hook_DialogBoxParamA_onexit, QL_INTERCEPT.EXIT)
1167

12-
def force_call_dialog_func(ql: Qiling):
13-
# get DialogFunc address
14-
lpDialogFunc = ql.unpack32(ql.mem.read(ql.reg.esp - 0x8, 4))
15-
# setup stack for DialogFunc
16-
ql.stack_push(0)
17-
ql.stack_push(1001)
18-
ql.stack_push(273)
19-
ql.stack_push(0)
20-
ql.stack_push(0x0401018)
21-
# force EIP to DialogFunc
22-
ql.reg.eip = lpDialogFunc
23-
24-
def our_sandbox(path, rootfs):
25-
ql = Qiling(path, rootfs, stdin=pipe.SimpleInStream(sys.stdin.fileno()))
26-
27-
ql.os.stdin.write(b"Ea5yR3versing\n")
28-
ql.hook_address(force_call_dialog_func, 0x00401016)
2968
ql.run()
3069

3170
if __name__ == "__main__":
32-
# Flag is : Ea5yR3versing
33-
our_sandbox(["rootfs/x86_windows/bin/Easy_CrackMe.exe"], "rootfs/x86_windows")
71+
our_sandbox(r"rootfs/x86_windows/bin/Easy_CrackMe.exe", r"rootfs/x86_windows")

examples/crackme_x86_windows_setcallback.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,15 +10,15 @@
1010

1111
def force_call_dialog_func(ql: Qiling):
1212
# get DialogFunc address
13-
lpDialogFunc = ql.unpack32(ql.mem.read(ql.reg.esp - 0x8, 4))
13+
lpDialogFunc = ql.unpack32(ql.mem.read(ql.arch.regs.esp - 0x8, 4))
1414
# setup stack for DialogFunc
1515
ql.stack_push(0)
1616
ql.stack_push(1001)
1717
ql.stack_push(273)
1818
ql.stack_push(0)
1919
ql.stack_push(0x0401018)
2020
# force EIP to DialogFunc
21-
ql.reg.eip = lpDialogFunc
21+
ql.arch.regs.eip = lpDialogFunc
2222

2323
def my_sandbox(path, rootfs):
2424
ql = Qiling(path, rootfs)

examples/crackme_x86_windows_unpatch.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,15 +10,15 @@
1010

1111
def force_call_dialog_func(ql: Qiling):
1212
# get DialogFunc address
13-
lpDialogFunc = ql.unpack32(ql.mem.read(ql.reg.esp - 0x8, 4))
13+
lpDialogFunc = ql.unpack32(ql.mem.read(ql.arch.regs.esp - 0x8, 4))
1414
# setup stack for DialogFunc
1515
ql.stack_push(0)
1616
ql.stack_push(1001)
1717
ql.stack_push(273)
1818
ql.stack_push(0)
1919
ql.stack_push(0x0401018)
2020
# force EIP to DialogFunc
21-
ql.reg.eip = lpDialogFunc
21+
ql.arch.regs.eip = lpDialogFunc
2222

2323
def our_sandbox(path, rootfs):
2424
ql = Qiling(path, rootfs)

examples/doogie_8086_crack.py

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -119,7 +119,7 @@ def echo_key(ql: Qiling, key):
119119

120120
def show_once(ql: Qiling, key):
121121
klen = len(key)
122-
ql.reg.ax = klen
122+
ql.arch.regs.ax = klen
123123
ql.mem.write(0x87F4, key)
124124
# Partial exectution to skip input reading
125125
ql.run(begin=0x801B, end=0x803d)
@@ -133,7 +133,7 @@ def third_stage(keys):
133133
"rootfs/8086",
134134
console=False)
135135
ql.add_fs_mapper(0x80, QlDisk("rootfs/8086/doogie/doogie.DOS_MBR", 0x80))
136-
ql.set_api((0x1a, 4), set_required_datetime, QL_INTERCEPT.EXIT)
136+
ql.os.set_api((0x1a, 4), set_required_datetime, QL_INTERCEPT.EXIT)
137137
hk = ql.hook_code(stop, begin=0x8018, end=0x8018)
138138
ql.run()
139139
ql.hook_del(hk)
@@ -172,10 +172,10 @@ def read_until_zero(ql: Qiling, addr):
172172

173173
def set_required_datetime(ql: Qiling):
174174
ql.log.info("Setting Feburary 06, 1990")
175-
ql.reg.ch = BIN2BCD(19)
176-
ql.reg.cl = BIN2BCD(1990%100)
177-
ql.reg.dh = BIN2BCD(2)
178-
ql.reg.dl = BIN2BCD(6)
175+
ql.arch.regs.ch = BIN2BCD(19)
176+
ql.arch.regs.cl = BIN2BCD(1990%100)
177+
ql.arch.regs.dh = BIN2BCD(2)
178+
ql.arch.regs.dl = BIN2BCD(6)
179179

180180
def stop(ql, addr, data):
181181
ql.emu_stop()
@@ -187,7 +187,7 @@ def first_stage():
187187
console=False)
188188
ql.add_fs_mapper(0x80, QlDisk("rootfs/8086/doogie/doogie.DOS_MBR", 0x80))
189189
# Doogie suggests that the datetime should be 1990-02-06.
190-
ql.set_api((0x1a, 4), set_required_datetime, QL_INTERCEPT.EXIT)
190+
ql.os.set_api((0x1a, 4), set_required_datetime, QL_INTERCEPT.EXIT)
191191
# A workaround to stop the program.
192192
hk = ql.hook_code(stop, begin=0x8018, end=0x8018)
193193
ql.run()

examples/extensions/idaplugin/custom_script.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,10 @@ def __init__(self):
55
pass
66

77
def _show_context(self, ql:Qiling):
8-
registers = [ k for k in ql.reg.register_mapping.keys() if type(k) is str ]
8+
registers = [ k for k in ql.arch.regs.register_mapping.keys() if type(k) is str ]
99
for idx in range(0, len(registers), 3):
1010
regs = registers[idx:idx+3]
11-
s = "\t".join(map(lambda v: f"{v:4}: {ql.reg.__getattr__(v):016x}", regs))
11+
s = "\t".join(map(lambda v: f"{v:4}: {ql.arch.regs.__getattr__(v):016x}", regs))
1212
ql.log.info(s)
1313

1414
def custom_prepare(self, ql:Qiling):

0 commit comments

Comments
 (0)