Merge 'dev' into next

This commit is contained in:
dataisland
2022-06-17 17:00:35 +08:00
parent 8b05c8c4e4
commit b6a697dca2
150 changed files with 7645 additions and 5706 deletions
+2
View File
@@ -5,12 +5,14 @@
jobs:
deploy:
runs-on: ubuntu-latest
if: github.repository_owner == 'qilingframework'
steps:
- uses: actions/checkout@v2
with:
fetch-depth: 0
- uses: xwings/sync-repo-action@master
with:
run: git config --global --add safe.directory *
ssh_private_key: ${{ secrets.GITEE_KEY }}
target_repo: ssh://[email protected]/qilingframework/qiling.git
+2
View File
@@ -53,6 +53,8 @@
- madprogrammer
- danielmoos
- sigeryang
- bet4it
- nullableVoidPtr
#### Legacy Core Developers
+28 -1
View File
@@ -1,7 +1,14 @@
This file details the changelog of Qiling Framework.
------------------------------------
[Version 1.4.3]: April 7th, 2022
[Version 1.4.4]: July XX, 2022
------------------------------------
[Version 1.4.3]: June 1st, 2022
New features:
- Introduce PowerPC architecture support (#1140)
Improvements:
- Fix fuzzing for tendaac15 (#1096)
@@ -10,11 +17,22 @@ Improvements:
- Minor PE Loader fix (#1104)
- Minor quality changes (#1106)
- Fix cacheflush syscall typo (#1115)
- Improvements and fixes for Windows and PE (#1118)
- Add vm_context to EVM hooks (#1119)
- Load interpreter segments with correct perms and vaddr (#1120)
- Fix mistakes in fuzz_x8664_linux binary (#1121)
- Add EVM ABI helpers, fix EVM DBG stack view (#1123)
- Fix regression caused by missing exception handling when opening socket (#1124)
- CI improvement (#1128 #1134)
- Add macho load command 'LC_LOAD_WEAK_DYLIB' support (#1133)
- Fix breakage of non-Windows binary emulation on Windows host (#1143)
- Remove misused region bound check of unmap_all (#1144)
- Change deprecated interfaces of IDA (#1145)
- Use importlib to retrieve package version (#1146)
- New and improved gdbserver (#1148)
- Rewrite package data reading (#1150)
- Misc improvements (#1154)
- Fix memory exhaustion problem caused by the logger (#1161)
Contributors:
- wtdcode
@@ -22,9 +40,18 @@ Contributors:
- elicn
- xwings
- cq674350529
- elicn
- TheZ3ro
- bet4it
- chinggg
- kabeor
- chfl4gs
- profiles
- OlfillasOdikno
- nmantan
- machinewu
- nullableVoidPtr
- Phat3
------------------------------------
+1 -1
View File
@@ -23,7 +23,7 @@ WORKDIR /qiling
RUN apt-get update \
&& apt-get install -y --no-install-recommends unzip apt-utils \
&& rm -rf /var/lib/apt/lists/* \
&& pip3 install wheels/*.whl \
&& pip3 install --no-deps wheels/*.whl \
&& rm -rf wheels
ENV HOME /qiling
-5
View File
@@ -1,5 +0,0 @@
recursive-include qiling/debugger/gdb/xml *
recursive-include qiling/extensions/windows_sdk/defs *
recursive-include qiling/profiles *
include qiling/os/uefi/guids.csv
include qiling/os/qnx/syspage.bin
+2 -2
View File
@@ -12,8 +12,8 @@
Qiling is an advanced binary emulation framework, with the following features:
- Emulate multi-platforms: Windows, MacOS, Linux, BSD, UEFI, DOS, MBR, Ethereum Virtual Machine
- Emulate multi-architectures: X86, X86_64, Arm, Arm64, MIPS, 8086
- Emulate multi-platforms: Windows, MacOS, Linux, Android, BSD, UEFI, DOS, MBR, Ethereum Virtual Machine
- Emulate multi-architectures: 8086, X86, X86_64, ARM, ARM64, MIPS, RISCV, PowerPC
- Support multiple file formats: PE, MachO, ELF, COM, MBR
- Support Windows Driver (.sys), Linux Kernel Module (.ko) & MacOS Kernel (.kext) via [Demigod](https://groundx.io/demigod/)
- Emulates & sandbox code in an isolated environment
+16 -9
View File
@@ -89,21 +89,26 @@ class Solver:
return False
def progress(msg: str) -> None:
print(msg, end='\r', file=sys.stderr, flush=True)
def main():
idx_list = (1, 4, 2, 0, 3)
flag = [0] * len(idx_list)
flag = bytearray(b'*****')
indices = (1, 4, 2, 0, 3)
solver = Solver(bytes(flag))
# all possible flag characters (may be reduced to uppercase and digits to save time)
charset = string.printable
for idx in idx_list:
progress('Initializing...')
solver = Solver(flag)
# bruteforce all possible flag characters
for ch in string.printable:
flag[idx] = ord(ch)
for i in indices:
for ch in charset:
flag[i] = ord(ch)
print(f'Guessing... [{"".join(chr(ch) if ch else "_" for ch in flag)}]', end='\r', file=sys.stderr, flush=True)
progress(f'Guessing... {flag.decode()}')
if solver.replay(bytes(flag)):
if solver.replay(flag):
break
else:
@@ -113,3 +118,5 @@ def main():
if __name__ == "__main__":
main()
# expected flag: L1NUX
+97 -33
View File
@@ -10,47 +10,111 @@ from qiling import Qiling
from qiling.const import QL_VERBOSE
from qiling.extensions import pipe
def instruction_count(ql: Qiling, address: int, size: int, user_data):
user_data[0] += 1
ROOTFS = r"rootfs/x86_windows"
def get_count(flag):
ql = Qiling(["rootfs/x86_windows/bin/crackme.exe"], "rootfs/x86_windows", verbose=QL_VERBOSE.OFF)
class Solver:
def __init__(self, invalid: bytes):
# create a silent qiling instance
self.ql = Qiling([rf"{ROOTFS}/bin/crackme.exe"], ROOTFS, verbose=QL_VERBOSE.OFF)
ql.os.stdin = pipe.SimpleStringBuffer()
ql.os.stdout = pipe.SimpleStringBuffer()
self.ql.os.stdin = pipe.SimpleInStream(sys.stdin.fileno()) # take over the input to the program using a fake stdin
self.ql.os.stdout = pipe.NullOutStream(sys.stdout.fileno()) # disregard program output
ql.os.stdin.write(bytes("".join(flag) + "\n", 'utf-8'))
count = [0]
# execute program until it reaches the part that asks for input
self.ql.run(end=0x401030)
ql.hook_code(instruction_count, count)
ql.run()
# record replay starting and ending points.
#
# since the emulation halted upon entering a function, its return address is there on
# the stack. we use it to limit the emulation till function returns
self.replay_starts = self.ql.arch.regs.arch_pc
self.replay_ends = self.ql.stack_read(0)
print(ql.os.stdout.read().decode('utf-8'), end='')
print(" ============ count: %d ============ " % count[0])
# instead of restarting the whole program every time a new flag character is guessed,
# we will restore its state to the latest point possible, fast-forwarding a good
# amount of start-up code that is not affected by the input.
#
# here we save the state just when the read input function is about to be called so we
# could use it to jumpstart the initialization part and get to the read input immediately
self.jumpstart = self.ql.save() or {}
return count[0]
# calibrate the replay instruction count by running the code with an invalid input
# first. the instruction count returned from the calibration process will be then
# used as a baseline for consequent replays
self.best_icount = self.__run(invalid)
def solve():
# BJWXB_CTF{C5307D46-E70E-4038-B6F9-8C3F698B7C53}
prefix = list("BJWXB_CTF{C5307D46-E70E-4038-B6F9-8C3F698B7C")
flag = list("\x00" * 100)
base = get_count(prefix + flag)
i = 0
def __run(self, input: bytes) -> int:
icount = [0]
try:
for i in range(len(flag)):
for j in "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-{}":
flag[i] = j
data = get_count(prefix + flag)
if data > base:
base = data
print("\n\n\n>>> FLAG: " + "".join(prefix + flag) + "\n\n\n")
break
if flag[i] == "}":
def __count_instructions(ql: Qiling, address: int, size: int):
icount[0] += 1
# set a hook to fire up every time an instruction is about to execute
hobj = self.ql.hook_code(__count_instructions)
# feed stdin with input
self.ql.os.stdin.write(input + b'\n')
# resume emulation till function returns
self.ql.run(begin=self.replay_starts, end=self.replay_ends)
hobj.remove()
return icount[0]
def replay(self, input: bytes) -> bool:
"""Restore state and replay with a new input.
Returns an indication to execution progress: `True` if a progress
was made, `False` otherwise
"""
# restore program's state back to the starting point
self.ql.restore(self.jumpstart)
# resume emulation and count emulated instructions
curr_icount = self.__run(input)
# the larger part of the input is correct, the more instructions are expected to be executed. this is true
# for traditional loop-based validations like strcmp or memcmp which bails as soon as a mismatch is found:
# more correct characters mean more loop iterations - thus more executed instructions.
#
# if we got a higher instruction count, it means we made a progress in the right direction
if curr_icount > self.best_icount:
self.best_icount = curr_icount
return True
return False
def progress(msg: str) -> None:
print(msg, end='\r', file=sys.stderr, flush=True)
def main():
flag = bytearray(b'BJWXB_CTF{********-****-****-****-************}')
indices = (i for i, ch in enumerate(flag) if ch == ord('*'))
# uppercase hex digits
charset = '0123456789ABCDEF'
progress('Initializing...')
solver = Solver(flag)
for i in indices:
for ch in charset:
flag[i] = ord(ch)
progress(f'Guessing... {flag.decode()}')
if solver.replay(flag):
break
print("SOLVED!!!")
except KeyboardInterrupt:
print("STOP: KeyboardInterrupt")
else:
raise RuntimeError('no match found')
print(f'\nFlag found!')
if __name__ == "__main__":
solve()
main()
# expected flag: BJWXB_CTF{C5307D46-E70E-4038-B6F9-8C3F698B7C53}
+24 -17
View File
@@ -9,30 +9,37 @@ sys.path.append("..")
from qiling import Qiling
from qiling.const import QL_VERBOSE
def my_syscall_write(ql: Qiling, write_fd, write_buf, write_count, *args, **kw):
regreturn = 0
# customized system calls always use the same arguments list as the original ones,
# but with a Qiling instance as first argument
def my_syscall_write(ql: Qiling, fd: int, buf: int, count: int):
try:
buf = ql.mem.read(write_buf, write_count)
ql.log.info("\n+++++++++\nmy write(%d,%x,%i) = %d\n+++++++++" % (write_fd, write_buf, write_count, regreturn))
ql.os.fd[write_fd].write(buf)
regreturn = write_count
# read data from emulated memory
data = ql.mem.read(buf, count)
# select the emulated file object that corresponds to the requested
# file descriptor
fobj = ql.os.fd[fd]
# write the data into the file object, if it supports write operations
if hasattr(fobj, 'write'):
fobj.write(data)
except:
regreturn = -1
ql.log.info("\n+++++++++\nmy write(%d,%x,%i) = %d\n+++++++++" % (write_fd, write_buf, write_count, regreturn))
ret = -1
else:
ret = count
return regreturn
ql.log.info(f'my_syscall_write({fd}, {buf:#x}, {count}) = {ret}')
return ret
if __name__ == "__main__":
ql = Qiling(["rootfs/arm_linux/bin/arm_hello"], "rootfs/arm_linux", verbose=QL_VERBOSE.DEBUG)
# Custom syscall handler by syscall name or syscall number.
# Known issue: If the syscall func is not be implemented in qiling, qiling does
# not know which func should be replaced.
# In that case, you must specify syscall by its number.
ql = Qiling([r'rootfs/arm_linux/bin/arm_hello'], r'rootfs/arm_linux', verbose=QL_VERBOSE.DEBUG)
# replacing a system call with a custom implementation.
# note that system calls may be referred to either by their name or number.
ql.os.set_syscall(0x04, my_syscall_write)
# set syscall by syscall name
#ql.os.set_syscall("write", my_syscall_write)
# an possible alternative: refer to a syscall by its name
#ql.os.set_syscall('write', my_syscall_write)
ql.run()
+6 -5
View File
@@ -9,12 +9,13 @@ sys.path.append("..")
from qiling import Qiling
from qiling.const import QL_VERBOSE
def run_sandbox(path, rootfs, verbose):
ql = Qiling(path, rootfs, verbose = verbose)
if __name__ == "__main__":
ql = Qiling([r'rootfs/arm_linux/bin/arm_hello'], r'rootfs/arm_linux', verbose=QL_VERBOSE.DEBUG)
ql.debugger = "qdb" # enable qdb without options
# other possible alternatives:
# ql.debugger = "qdb::rr" # switch on record and replay with rr
# ql.debugger = "qdb:0x1030c" # enable qdb and setup breakpoin at 0x1030c
ql.run()
if __name__ == "__main__":
run_sandbox(["rootfs/arm_linux/bin/arm_hello"], "rootfs/arm_linux", QL_VERBOSE.DEBUG)
ql.run()
+5 -1
View File
@@ -151,8 +151,12 @@ def hook_StartServiceA(ql: Qiling, address: int, params):
if service_handle.name in ql.os.services:
service_path = ql.os.services[service_handle.name]
service_path = ql.os.path.transform_to_real_path(service_path)
ql.amsint32_driver = Qiling([service_path], ql.rootfs, verbose=QL_VERBOSE.DEBUG)
init_unseen_symbols(ql.amsint32_driver, ql.amsint32_driver.loader.dlls["ntoskrnl.exe"]+0xb7695, b"NtTerminateProcess", 0, "ntoskrnl.exe")
ntoskrnl = ql.amsint32_driver.loader.get_image_by_name("ntoskrnl.exe")
assert ntoskrnl, 'ntoskernl.exe was not loaded'
init_unseen_symbols(ql.amsint32_driver, ntoskrnl.base+0xb7695, b"NtTerminateProcess", 0, "ntoskrnl.exe")
#ql.amsint32_driver.debugger= ":9999"
try:
ql.amsint32_driver.load()
+157 -90
View File
@@ -1,90 +1,157 @@
@echo off
REM
REM Example batch script to copy windows required DLLs and registry
REM
mkdir examples\rootfs\x86_windows\Windows\System32
mkdir examples\rootfs\x86_windows\Windows\System32\drivers
mkdir examples\rootfs\x86_windows\Windows\registry
mkdir examples\rootfs\x8664_windows\Windows\System32
mkdir examples\rootfs\x8664_windows\Windows\SysWOW64
mkdir examples\rootfs\x8664_windows\Windows\registry
REM
REM Registry
REM
echo f | xcopy /f /y C:\Users\Default\NTUSER.DAT examples\rootfs\x8664_windows\Windows\registry\NTUSER.DAT
reg save hklm\system examples\rootfs\x8664_windows\Windows\registry\SYSTEM
reg save hklm\security examples\rootfs\x8664_windows\Windows\registry\SECURITY
reg save hklm\software examples\rootfs\x8664_windows\Windows\registry\SOFTWARE
reg save hklm\hardware examples\rootfs\x8664_windows\Windows\registry\HARDWARE
reg save hklm\SAM examples\rootfs\x8664_windows\Windows\registry\SAM
xcopy /d /y examples\rootfs\x8664_windows\Windows\registry\* examples\rootfs\x86_windows\Windows\registry\
REM
REM Dlls
REM
if exist %WINDIR%\SysWOW64\advapi32.dll xcopy /f /y %WINDIR%\SysWOW64\advapi32.dll "examples\rootfs\x86_windows\Windows\System32\"
if exist %WINDIR%\SysWOW64\rpcrt4.dll xcopy /f /y %WINDIR%\SysWOW64\rpcrt4.dll "examples\rootfs\x86_windows\Windows\System32\"
if exist %WINDIR%\SysWOW64\crypt32.dll xcopy /f /y %WINDIR%\SysWOW64\crypt32.dll "examples\rootfs\x86_windows\Windows\System32\"
if exist %WINDIR%\SysWOW64\iphlpapi.dll xcopy /f /y %WINDIR%\SysWOW64\iphlpapi.dll "examples\rootfs\x86_windows\Windows\System32\"
if exist %WINDIR%\SysWOW64\kernel32.dll xcopy /f /y %WINDIR%\SysWOW64\kernel32.dll "examples\rootfs\x86_windows\Windows\System32\"
if exist %WINDIR%\SysWOW64\KernelBase.dll xcopy /f /y %WINDIR%\SysWOW64\KernelBase.dll "examples\rootfs\x86_windows\Windows\System32\"
if exist %WINDIR%\SysWOW64\mpr.dll xcopy /f /y %WINDIR%\SysWOW64\mpr.dll "examples\rootfs\x86_windows\Windows\System32\"
if exist %WINDIR%\SysWOW64\msvcp60.dll xcopy /f /y %WINDIR%\SysWOW64\msvcp60.dll "examples\rootfs\x86_windows\Windows\System32\"
if exist %WINDIR%\SysWOW64\msvcrt.dll xcopy /f /y %WINDIR%\SysWOW64\msvcrt.dll "examples\rootfs\x86_windows\Windows\System32\"
if exist %WINDIR%\SysWOW64\netapi32.dll xcopy /f /y %WINDIR%\SysWOW64\netapi32.dll "examples\rootfs\x86_windows\Windows\System32\"
if exist %WINDIR%\SysWOW64\ntdll.dll xcopy /f /y %WINDIR%\SysWOW64\ntdll.dll "examples\rootfs\x86_windows\Windows\System32\"
if exist %WINDIR%\SysWOW64\ole32.dll xcopy /f /y %WINDIR%\SysWOW64\ole32.dll "examples\rootfs\x86_windows\Windows\System32\"
if exist %WINDIR%\SysWOW64\urlmon.dll xcopy /f /y %WINDIR%\SysWOW64\urlmon.dll "examples\rootfs\x86_windows\Windows\System32\"
if exist %WINDIR%\SysWOW64\user32.dll xcopy /f /y %WINDIR%\SysWOW64\user32.dll "examples\rootfs\x86_windows\Windows\System32\"
if exist %WINDIR%\SysWOW64\wsock32.dll xcopy /f /y %WINDIR%\SysWOW64\wsock32.dll "examples\rootfs\x86_windows\Windows\System32\"
if exist %WINDIR%\SysWOW64\version.dll xcopy /f /y %WINDIR%\SysWOW64\version.dll "examples\rootfs\x86_windows\Windows\System32\"
if exist %WINDIR%\SysWOW64\winmm.dll xcopy /f /y %WINDIR%\SysWOW64\winmm.dll "examples\rootfs\x86_windows\Windows\System32\"
if exist %WINDIR%\SysWOW64\comctl32.dll xcopy /f /y %WINDIR%\SysWOW64\comctl32.dll "examples\rootfs\x86_windows\Windows\System32\"
if exist %WINDIR%\SysWOW64\wininet.dll xcopy /f /y %WINDIR%\SysWOW64\wininet.dll "examples\rootfs\x86_windows\Windows\System32\"
if exist %WINDIR%\SysWOW64\psapi.dll xcopy /f /y %WINDIR%\SysWOW64\psapi.dll "examples\rootfs\x86_windows\Windows\System32\"
if exist %WINDIR%\SysWOW64\userenv.dll xcopy /f /y %WINDIR%\SysWOW64\userenv.dll "examples\rootfs\x86_windows\Windows\System32\"
if exist %WINDIR%\SysWOW64\uxtheme.dll xcopy /f /y %WINDIR%\SysWOW64\uxtheme.dll "examples\rootfs\x86_windows\Windows\System32\"
if exist %WINDIR%\SysWOW64\gdi32.dll xcopy /f /y %WINDIR%\SysWOW64\gdi32.dll "examples\rootfs\x86_windows\Windows\System32\"
if exist %WINDIR%\SysWOW64\comdlg32.dll xcopy /f /y %WINDIR%\SysWOW64\comdlg32.dll "examples\rootfs\x86_windows\Windows\System32\"
if exist %WINDIR%\SysWOW64\shell32.dll xcopy /f /y %WINDIR%\SysWOW64\shell32.dll "examples\rootfs\x86_windows\Windows\System32\"
if exist %WINDIR%\SysWOW64\oleaut32.dll xcopy /f /y %WINDIR%\SysWOW64\oleaut32.dll "examples\rootfs\x86_windows\Windows\System32\"
if exist %WINDIR%\SysWOW64\vcruntime140.dll xcopy /f /y %WINDIR%\SysWOW64\vcruntime140.dll "examples\rootfs\x86_windows\Windows\System32\"
if exist %WINDIR%\SysWOW64\winhttp.dll xcopy /f /y %WINDIR%\SysWOW64\winhttp.dll "examples\rootfs\x86_windows\Windows\System32\"
if exist %WINDIR%\SysWOW64\wininet.dll xcopy /f /y %WINDIR%\SysWOW64\wininet.dll "examples\rootfs\x86_windows\Windows\System32\"
if exist %WINDIR%\SysWOW64\ws2_32.dll xcopy /f /y %WINDIR%\SysWOW64\ws2_32.dll "examples\rootfs\x86_windows\Windows\System32\"
if exist %WINDIR%\SysWOW64\msvcr120_clr0400.dll echo f | xcopy /f /y %WINDIR%\SysWOW64\msvcr120_clr0400.dll "examples\rootfs\x86_windows\Windows\System32\msvcr110.dll"
if exist %WINDIR%\SysWOW64\downlevel\api-ms-win-crt-stdio-l1-1-0.dll xcopy /f /y %WINDIR%\SysWOW64\downlevel\api-ms-win-crt-stdio-l1-1-0.dll "examples\rootfs\x86_windows\Windows\System32\"
if exist %WINDIR%\SysWOW64\downlevel\api-ms-win-crt-runtime-l1-1-0.dll xcopy /f /y %WINDIR%\SysWOW64\downlevel\api-ms-win-crt-runtime-l1-1-0.dll "examples\rootfs\x86_windows\Windows\System32\"
if exist %WINDIR%\SysWOW64\downlevel\api-ms-win-crt-math-l1-1-0.dll xcopy /f /y %WINDIR%\SysWOW64\downlevel\api-ms-win-crt-math-l1-1-0.dll "examples\rootfs\x86_windows\Windows\System32\"
if exist %WINDIR%\SysWOW64\downlevel\api-ms-win-crt-locale-l1-1-0.dll xcopy /f /y %WINDIR%\SysWOW64\downlevel\api-ms-win-crt-locale-l1-1-0.dll "examples\rootfs\x86_windows\Windows\System32\"
if exist %WINDIR%\SysWOW64\downlevel\api-ms-win-crt-heap-l1-1-0.dll xcopy /f /y %WINDIR%\SysWOW64\downlevel\api-ms-win-crt-heap-l1-1-0.dll "examples\rootfs\x86_windows\Windows\System32\"
if exist %WINDIR%\SysWOW64\downlevel\api-ms-win-core-synch-l1-2-0.dll xcopy /f /y %WINDIR%\SysWOW64\downlevel\api-ms-win-core-synch-l1-2-0.dll "examples\rootfs\x86_windows\Windows\System32\"
if exist %WINDIR%\SysWOW64\downlevel\api-ms-win-core-fibers-l1-1-1.dll xcopy /f /y %WINDIR%\SysWOW64\downlevel\api-ms-win-core-fibers-l1-1-1.dll "examples\rootfs\x86_windows\Windows\System32\"
if exist %WINDIR%\SysWOW64\downlevel\api-ms-win-core-localization-l1-2-1.dll xcopy /f /y %WINDIR%\SysWOW64\downlevel\api-ms-win-core-localization-l1-2-1.dll "examples\rootfs\x86_windows\Windows\System32\"
if exist %WINDIR%\SysWOW64\downlevel\api-ms-win-core-sysinfo-l1-2-1.dll xcopy /f /y %WINDIR%\SysWOW64\downlevel\api-ms-win-core-sysinfo-l1-2-1.dll "examples\rootfs\x86_windows\Windows\System32\"
if exist %WINDIR%\SysWOW64\shlwapi.dll xcopy /f /y %WINDIR%\SysWOW64\shlwapi.dll "examples\rootfs\x86_windows\Windows\System32\"
if exist %WINDIR%\SysWOW64\setupapi.dll xcopy /f /y %WINDIR%\SysWOW64\setupapi.dll "examples\rootfs\x86_windows\Windows\System32\"
if exist %WINDIR%\System32\ntoskrnl.exe xcopy /f /y %WINDIR%\System32\ntoskrnl.exe "examples\rootfs\x86_windows\Windows\System32\"
if exist %WINDIR%\winsxs\amd64_microsoft-windows-printing-xpsprint_31bf3856ad364e35_10.0.17763.194_none_20349c5a971eb293\XpsPrint.dll xcopy /f /y %WINDIR%\winsxs\amd64_microsoft-windows-printing-xpsprint_31bf3856ad364e35_10.0.17763.194_none_20349c5a971eb293\XpsPrint.dll "examples\rootfs\x86_windows\Windows\System32\"
if exist %WINDIR%\System32\ntoskrnl.exe xcopy /f /y %WINDIR%\System32\ntoskrnl.exe "examples\rootfs\x8664_windows\Windows\System32\"
if exist %WINDIR%\System32\advapi32.dll xcopy /f /y %WINDIR%\System32\advapi32.dll "examples\rootfs\x8664_windows\Windows\System32\"
if exist %WINDIR%\System32\kernel32.dll xcopy /f /y %WINDIR%\System32\kernel32.dll "examples\rootfs\x8664_windows\Windows\System32\"
if exist %WINDIR%\System32\KernelBase.dll xcopy /f /y %WINDIR%\System32\KernelBase.dll "examples\rootfs\x8664_windows\Windows\System32\"
if exist %WINDIR%\System32\msvcrt.dll xcopy /f /y %WINDIR%\System32\msvcrt.dll "examples\rootfs\x8664_windows\Windows\System32\"
if exist %WINDIR%\System32\ntdll.dll xcopy /f /y %WINDIR%\System32\ntdll.dll "examples\rootfs\x8664_windows\Windows\System32\"
if exist %WINDIR%\System32\urlmon.dll xcopy /f /y %WINDIR%\System32\urlmon.dll "examples\rootfs\x8664_windows\Windows\System32\"
if exist %WINDIR%\System32\user32.dll xcopy /f /y %WINDIR%\System32\user32.dll "examples\rootfs\x8664_windows\Windows\System32\"
if exist %WINDIR%\System32\ws2_32.dll xcopy /f /y %WINDIR%\System32\ws2_32.dll "examples\rootfs\x8664_windows\Windows\System32\"
if exist %WINDIR%\System32\vcruntime140.dll xcopy /f /y %WINDIR%\System32\vcruntime140.dll "examples\rootfs\x8664_windows\Windows\System32\"
if exist %WINDIR%\System32\downlevel\api-ms-win-crt-stdio-l1-1-0.dll xcopy /f /y %WINDIR%\System32\downlevel\api-ms-win-crt-stdio-l1-1-0.dll "examples\rootfs\x8664_windows\Windows\System32\"
if exist %WINDIR%\System32\downlevel\api-ms-win-crt-runtime-l1-1-0.dll xcopy /f /y %WINDIR%\System32\downlevel\api-ms-win-crt-runtime-l1-1-0.dll "examples\rootfs\x8664_windows\Windows\System32\"
if exist %WINDIR%\System32\downlevel\api-ms-win-crt-math-l1-1-0.dll xcopy /f /y %WINDIR%\System32\downlevel\api-ms-win-crt-math-l1-1-0.dll "examples\rootfs\x8664_windows\Windows\System32\"
if exist %WINDIR%\System32\downlevel\api-ms-win-crt-locale-l1-1-0.dll xcopy /f /y %WINDIR%\System32\downlevel\api-ms-win-crt-locale-l1-1-0.dll "examples\rootfs\x8664_windows\Windows\System32\"
if exist %WINDIR%\System32\downlevel\api-ms-win-crt-heap-l1-1-0.dll xcopy /f /y %WINDIR%\System32\downlevel\api-ms-win-crt-heap-l1-1-0.dll "examples\rootfs\x8664_windows\Windows\System32\"
if exist %WINDIR%\System32\vcruntime140d.dll xcopy /f /y %WINDIR%\System32\vcruntime140d.dll "examples\rootfs\x8664_windows\Windows\System32\"
if exist %WINDIR%\System32\ucrtbased.dll xcopy /f /y %WINDIR%\System32\ucrtbased.dll "examples\rootfs\x8664_windows\Windows\System32\"
exit /b
@ECHO OFF
::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
:: Create the emulated Windows directory structure and registry ::
::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
:: Host system directories
SET SYSDIR32="%WINDIR%\SysWOW64"
SET SYSDIR64="%WINDIR%\System32"
:: Qiling rootfs directories
SET QL_WINDIR32="examples\rootfs\x86_windows\Windows"
SET QL_WINDIR64="examples\rootfs\x8664_windows\Windows"
SET QL_SYSDIR32="%QL_WINDIR32%\System32"
SET QL_SYSDIR64="%QL_WINDIR64%\System32"
SET QL_REGDIR32="%QL_WINDIR32%\registry"
SET QL_REGDIR64="%QL_WINDIR64%\registry"
:: Create emulated Windows directory structure
MKDIR %QL_REGDIR32%
MKDIR %QL_SYSDIR32%
MKDIR "%QL_SYSDIR32%\drivers"
MKDIR "%QL_WINDIR32%\Temp"
MKDIR %QL_REGDIR64%
MKDIR %QL_SYSDIR64%
MKDIR "%QL_SYSDIR64%\drivers"
MKDIR "%QL_WINDIR64%\Temp"
:: Generate emulated Windows registry (requires Administrator privileges)
REG SAVE HKLM\SYSTEM %QL_REGDIR64%\SYSTEM /Y
REG SAVE HKLM\SECURITY %QL_REGDIR64%\SECURITY /Y
REG SAVE HKLM\SOFTWARE %QL_REGDIR64%\SOFTWARE /Y
REG SAVE HKLM\HARDWARE %QL_REGDIR64%\HARDWARE /Y
REG SAVE HKLM\SAM %QL_REGDIR64%\SAM /Y
COPY /B /Y C:\Users\Default\NTUSER.DAT "%QL_REGDIR64%\NTUSER.DAT"
:: Duplicate generated registry
XCOPY /F /D /Y %QL_REGDIR64%\* %QL_REGDIR32%
:: Collect 32-bit DLL files
CALL :collect_dll32 advapi32.dll
CALL :collect_dll32 bcrypt.dll
CALL :collect_dll32 cfgmgr32.dll
CALL :collect_dll32 ci.dll
CALL :collect_dll32 combase.dll
CALL :collect_dll32 comctl32.dll
CALL :collect_dll32 comdlg32.dll
CALL :collect_dll32 crypt32.dll
CALL :collect_dll32 cryptbase.dll
CALL :collect_dll32 gdi32.dll
CALL :collect_dll32 hal.dll
CALL :collect_dll32 iphlpapi.dll
CALL :collect_dll32 kdcom.dll
CALL :collect_dll32 kernel32.dll
CALL :collect_dll32 KernelBase.dll
CALL :collect_dll32 mpr.dll
CALL :collect_dll32 msvcp_win.dll
CALL :collect_dll32 msvcp60.dll
CALL :collect_dll32 msvcr120_clr0400.dll, msvcr110.dll
CALL :collect_dll32 msvcrt.dll
CALL :collect_dll32 netapi32.dll
CALL :collect_dll32 ntdll.dll
CALL :collect_dll32 ole32.dll
CALL :collect_dll32 oleaut32.dll
CALL :collect_dll32 psapi.dll
CALL :collect_dll32 rpcrt4.dll
CALL :collect_dll32 sechost.dll
CALL :collect_dll32 setupapi.dll
CALL :collect_dll32 shell32.dll
CALL :collect_dll32 shlwapi.dll
CALL :collect_dll32 sspicli.dll
CALL :collect_dll32 ucrtbase.dll
CALL :collect_dll32 ucrtbased.dll
CALL :collect_dll32 urlmon.dll
CALL :collect_dll32 user32.dll
CALL :collect_dll32 userenv.dll
CALL :collect_dll32 uxtheme.dll
CALL :collect_dll32 vcruntime140.dll
CALL :collect_dll32 version.dll
CALL :collect_dll32 win32u.dll
CALL :collect_dll32 winhttp.dll
CALL :collect_dll32 wininet.dll
CALL :collect_dll32 winmm.dll
CALL :collect_dll32 ws2_32.dll
CALL :collect_dll32 wsock32.dll
CALL :collect_dll32 downlevel\api-ms-win-core-fibers-l1-1-1.dll
CALL :collect_dll32 downlevel\api-ms-win-core-localization-l1-2-1.dll
CALL :collect_dll32 downlevel\api-ms-win-core-synch-l1-2-0.dll
CALL :collect_dll32 downlevel\api-ms-win-core-sysinfo-l1-2-1.dll
CALL :collect_dll32 downlevel\api-ms-win-crt-heap-l1-1-0.dll
CALL :collect_dll32 downlevel\api-ms-win-crt-locale-l1-1-0.dll
CALL :collect_dll32 downlevel\api-ms-win-crt-math-l1-1-0.dll
CALL :collect_dll32 downlevel\api-ms-win-crt-runtime-l1-1-0.dll
CALL :collect_dll32 downlevel\api-ms-win-crt-stdio-l1-1-0.dll
:: Collect 64-bit DLL files
CALL :collect_dll64 advapi32.dll
CALL :collect_dll64 gdi32.dll
CALL :collect_dll64 kernel32.dll
CALL :collect_dll64 KernelBase.dll
CALL :collect_dll64 msvcrt.dll
CALL :collect_dll64 ntdll.dll
CALL :collect_dll64 ntoskrnl.exe
CALL :collect_dll64 msvcp_win.dll
CALL :collect_dll64 ucrtbase.dll
CALL :collect_dll64 ucrtbased.dll
CALL :collect_dll64 urlmon.dll
CALL :collect_dll64 rpcrt4.dll
CALL :collect_dll64 sechost.dll
CALL :collect_dll64 shell32.dll
CALL :collect_dll64 shlwapi.dll
CALL :collect_dll64 user32.dll
CALL :collect_dll64 vcruntime140.dll
CALL :collect_dll64 vcruntime140d.dll
CALL :collect_dll64 win32u.dll
CALL :collect_dll64 winhttp.dll
CALL :collect_dll64 wininet.dll
CALL :collect_dll64 ws2_32.dll
CALL :collect_dll64 downlevel\api-ms-win-crt-heap-l1-1-0.dll
CALL :collect_dll64 downlevel\api-ms-win-crt-locale-l1-1-0.dll
CALL :collect_dll64 downlevel\api-ms-win-crt-math-l1-1-0.dll
CALL :collect_dll64 downlevel\api-ms-win-crt-runtime-l1-1-0.dll
CALL :collect_dll64 downlevel\api-ms-win-crt-stdio-l1-1-0.dll
:: Collect extras
CALL :collect %SYSDIR64%, ntoskrnl.exe, %QL_SYSDIR32%
:: All done!
EXIT /B 0
:: Functions definitions
:normpath
SET %1=%~dpfn2
EXIT /B
:collect
CALL :normpath SRC, %~1\%~2
CALL :normpath DST, %~3\%~4
IF EXIST %SRC% (
ECHO %SRC% -^> %DST%
COPY /B /Y "%SRC%" "%DST%" >NUL
)
EXIT /B
:collect_dll64
CALL :collect %SYSDIR64%, %~1, %QL_SYSDIR64%, %~2
EXIT /B
:collect_dll32
CALL :collect %SYSDIR32%, %~1, %QL_SYSDIR32%, %~2
EXIT /B
-1
View File
@@ -115,7 +115,6 @@ if __name__ == "__main__":
parser.add_argument("-t", "--trace", help="Enable full trace", action='store_true', default=False)
parser.add_argument("-R", "--root", help="rootfs", default=None)
parser.add_argument("-d", "--dump", help="Directory to dump memory regions to", default="dump")
#parser.add_argument("-a", "--automatize_input", help="Automatize writes on standard input", default=False)
parser.add_argument("-p ", "--profile", help="customized profile",
default="qiling/profiles/windows.ql")
parser.add_argument('input', nargs='*')
+8 -1
View File
@@ -1,2 +1,9 @@
import importlib.metadata
from .core import Qiling
from .__version__ import __version__
try:
__version__ = importlib.metadata.version(__package__ or __name__)
except importlib.metadata.PackageNotFoundError:
__version__ = "0.0.0"
__all__ = ['Qiling']
-3
View File
@@ -1,3 +0,0 @@
# NOTE: use "-dev" for dev branch
#__version__ = "1.4.3"
__version__ = "1.4.3" + "-dev"
+6 -12
View File
@@ -41,7 +41,11 @@ class QlArchARM(QlArch):
@cached_property
def regs(self) -> QlRegisterManager:
regs_map = arm_const.reg_map
regs_map = dict(
**arm_const.reg_map,
**arm_const.reg_vfp
)
pc_reg = 'pc'
sp_reg = 'sp'
@@ -53,17 +57,7 @@ class QlArchARM(QlArch):
@property
def endian(self) -> QL_ENDIAN:
# FIXME: ARM is a bi-endian architecture which allows flipping core endianess
# while running. endianess is tested in runtime through CPSR[9], however unicorn
# doesn't reflect the endianess correctly through that bit.
# @see: https://github.com/unicorn-engine/unicorn/issues/1542
#
# we work around this by using the initial endianess configuration, even though
# it might have been changed since.
#
# return QL_ENDIAN.EB if self.regs.cpsr & (1 << 9) else QL_ENDIAN.EL
return self._init_endian
return QL_ENDIAN.EB if self.regs.cpsr & (1 << 9) else QL_ENDIAN.EL
@property
def effective_pc(self) -> int:
+7 -1
View File
@@ -26,7 +26,13 @@ class QlArchARM64(QlArch):
def regs(self) -> QlRegisterManager:
regs_map = dict(
**arm64_const.reg_map,
**arm64_const.reg_map_w
**arm64_const.reg_map_b,
**arm64_const.reg_map_d,
**arm64_const.reg_map_h,
**arm64_const.reg_map_q,
**arm64_const.reg_map_s,
**arm64_const.reg_map_w,
**arm64_const.reg_map_v
)
pc_reg = 'pc'
+279 -69
View File
@@ -1,79 +1,289 @@
#!/usr/bin/env python3
#
#
# Cross Platform and Multi Architecture Advanced Binary Emulation Framework
#
from unicorn.arm64_const import *
reg_map = {
"x0": UC_ARM64_REG_X0,
"x1": UC_ARM64_REG_X1,
"x2": UC_ARM64_REG_X2,
"x3": UC_ARM64_REG_X3,
"x4": UC_ARM64_REG_X4,
"x5": UC_ARM64_REG_X5,
"x6": UC_ARM64_REG_X6,
"x7": UC_ARM64_REG_X7,
"x8": UC_ARM64_REG_X8,
"x9": UC_ARM64_REG_X9,
"x10": UC_ARM64_REG_X10,
"x11": UC_ARM64_REG_X11,
"x12": UC_ARM64_REG_X12,
"x13": UC_ARM64_REG_X13,
"x14": UC_ARM64_REG_X14,
"x15": UC_ARM64_REG_X15,
"x16": UC_ARM64_REG_X16,
"x17": UC_ARM64_REG_X17,
"x18": UC_ARM64_REG_X18,
"x19": UC_ARM64_REG_X19,
"x20": UC_ARM64_REG_X20,
"x21": UC_ARM64_REG_X21,
"x22": UC_ARM64_REG_X22,
"x23": UC_ARM64_REG_X23,
"x24": UC_ARM64_REG_X24,
"x25": UC_ARM64_REG_X25,
"x26": UC_ARM64_REG_X26,
"x27": UC_ARM64_REG_X27,
"x28": UC_ARM64_REG_X28,
"x29": UC_ARM64_REG_X29,
"x30": UC_ARM64_REG_X30,
"sp": UC_ARM64_REG_SP,
"pc": UC_ARM64_REG_PC,
"lr": UC_ARM64_REG_LR,
"cpacr_el1": UC_ARM64_REG_CPACR_EL1,
"tpidr_el0": UC_ARM64_REG_TPIDR_EL0,
"x0": UC_ARM64_REG_X0,
"x1": UC_ARM64_REG_X1,
"x2": UC_ARM64_REG_X2,
"x3": UC_ARM64_REG_X3,
"x4": UC_ARM64_REG_X4,
"x5": UC_ARM64_REG_X5,
"x6": UC_ARM64_REG_X6,
"x7": UC_ARM64_REG_X7,
"x8": UC_ARM64_REG_X8,
"x9": UC_ARM64_REG_X9,
"x10": UC_ARM64_REG_X10,
"x11": UC_ARM64_REG_X11,
"x12": UC_ARM64_REG_X12,
"x13": UC_ARM64_REG_X13,
"x14": UC_ARM64_REG_X14,
"x15": UC_ARM64_REG_X15,
"x16": UC_ARM64_REG_X16,
"x17": UC_ARM64_REG_X17,
"x18": UC_ARM64_REG_X18,
"x19": UC_ARM64_REG_X19,
"x20": UC_ARM64_REG_X20,
"x21": UC_ARM64_REG_X21,
"x22": UC_ARM64_REG_X22,
"x23": UC_ARM64_REG_X23,
"x24": UC_ARM64_REG_X24,
"x25": UC_ARM64_REG_X25,
"x26": UC_ARM64_REG_X26,
"x27": UC_ARM64_REG_X27,
"x28": UC_ARM64_REG_X28,
"x29": UC_ARM64_REG_X29,
"x30": UC_ARM64_REG_X30,
"sp": UC_ARM64_REG_SP,
"pc": UC_ARM64_REG_PC,
"lr": UC_ARM64_REG_LR,
"cpacr_el1": UC_ARM64_REG_CPACR_EL1,
"tpidr_el0": UC_ARM64_REG_TPIDR_EL0
}
reg_map_b = {
"b0" : UC_ARM64_REG_B0,
"b1" : UC_ARM64_REG_B1,
"b2" : UC_ARM64_REG_B2,
"b3" : UC_ARM64_REG_B3,
"b4" : UC_ARM64_REG_B4,
"b5" : UC_ARM64_REG_B5,
"b6" : UC_ARM64_REG_B6,
"b7" : UC_ARM64_REG_B7,
"b8" : UC_ARM64_REG_B8,
"b9" : UC_ARM64_REG_B9,
"b10" : UC_ARM64_REG_B10,
"b11" : UC_ARM64_REG_B11,
"b12" : UC_ARM64_REG_B12,
"b13" : UC_ARM64_REG_B13,
"b14" : UC_ARM64_REG_B14,
"b15" : UC_ARM64_REG_B15,
"b16" : UC_ARM64_REG_B16,
"b17" : UC_ARM64_REG_B17,
"b18" : UC_ARM64_REG_B18,
"b19" : UC_ARM64_REG_B19,
"b20" : UC_ARM64_REG_B20,
"b21" : UC_ARM64_REG_B21,
"b22" : UC_ARM64_REG_B22,
"b23" : UC_ARM64_REG_B23,
"b24" : UC_ARM64_REG_B24,
"b25" : UC_ARM64_REG_B25,
"b26" : UC_ARM64_REG_B26,
"b27" : UC_ARM64_REG_B27,
"b28" : UC_ARM64_REG_B28,
"b29" : UC_ARM64_REG_B29,
"b30" : UC_ARM64_REG_B30,
"b31" : UC_ARM64_REG_B31
}
reg_map_d = {
"d0" : UC_ARM64_REG_D0,
"d1" : UC_ARM64_REG_D1,
"d2" : UC_ARM64_REG_D2,
"d3" : UC_ARM64_REG_D3,
"d4" : UC_ARM64_REG_D4,
"d5" : UC_ARM64_REG_D5,
"d6" : UC_ARM64_REG_D6,
"d7" : UC_ARM64_REG_D7,
"d8" : UC_ARM64_REG_D8,
"d9" : UC_ARM64_REG_D9,
"d10" : UC_ARM64_REG_D10,
"d11" : UC_ARM64_REG_D11,
"d12" : UC_ARM64_REG_D12,
"d13" : UC_ARM64_REG_D13,
"d14" : UC_ARM64_REG_D14,
"d15" : UC_ARM64_REG_D15,
"d16" : UC_ARM64_REG_D16,
"d17" : UC_ARM64_REG_D17,
"d18" : UC_ARM64_REG_D18,
"d19" : UC_ARM64_REG_D19,
"d20" : UC_ARM64_REG_D20,
"d21" : UC_ARM64_REG_D21,
"d22" : UC_ARM64_REG_D22,
"d23" : UC_ARM64_REG_D23,
"d24" : UC_ARM64_REG_D24,
"d25" : UC_ARM64_REG_D25,
"d26" : UC_ARM64_REG_D26,
"d27" : UC_ARM64_REG_D27,
"d28" : UC_ARM64_REG_D28,
"d29" : UC_ARM64_REG_D29,
"d30" : UC_ARM64_REG_D30,
"d31" : UC_ARM64_REG_D31
}
reg_map_h = {
"h0" : UC_ARM64_REG_H0,
"h1" : UC_ARM64_REG_H1,
"h2" : UC_ARM64_REG_H2,
"h3" : UC_ARM64_REG_H3,
"h4" : UC_ARM64_REG_H4,
"h5" : UC_ARM64_REG_H5,
"h6" : UC_ARM64_REG_H6,
"h7" : UC_ARM64_REG_H7,
"h8" : UC_ARM64_REG_H8,
"h9" : UC_ARM64_REG_H9,
"h10" : UC_ARM64_REG_H10,
"h11" : UC_ARM64_REG_H11,
"h12" : UC_ARM64_REG_H12,
"h13" : UC_ARM64_REG_H13,
"h14" : UC_ARM64_REG_H14,
"h15" : UC_ARM64_REG_H15,
"h16" : UC_ARM64_REG_H16,
"h17" : UC_ARM64_REG_H17,
"h18" : UC_ARM64_REG_H18,
"h19" : UC_ARM64_REG_H19,
"h20" : UC_ARM64_REG_H20,
"h21" : UC_ARM64_REG_H21,
"h22" : UC_ARM64_REG_H22,
"h23" : UC_ARM64_REG_H23,
"h24" : UC_ARM64_REG_H24,
"h25" : UC_ARM64_REG_H25,
"h26" : UC_ARM64_REG_H26,
"h27" : UC_ARM64_REG_H27,
"h28" : UC_ARM64_REG_H28,
"h29" : UC_ARM64_REG_H29,
"h30" : UC_ARM64_REG_H30,
"h31" : UC_ARM64_REG_H31
}
reg_map_q = {
"q0" : UC_ARM64_REG_Q0,
"q1" : UC_ARM64_REG_Q1,
"q2" : UC_ARM64_REG_Q2,
"q3" : UC_ARM64_REG_Q3,
"q4" : UC_ARM64_REG_Q4,
"q5" : UC_ARM64_REG_Q5,
"q6" : UC_ARM64_REG_Q6,
"q7" : UC_ARM64_REG_Q7,
"q8" : UC_ARM64_REG_Q8,
"q9" : UC_ARM64_REG_Q9,
"q10" : UC_ARM64_REG_Q10,
"q11" : UC_ARM64_REG_Q11,
"q12" : UC_ARM64_REG_Q12,
"q13" : UC_ARM64_REG_Q13,
"q14" : UC_ARM64_REG_Q14,
"q15" : UC_ARM64_REG_Q15,
"q16" : UC_ARM64_REG_Q16,
"q17" : UC_ARM64_REG_Q17,
"q18" : UC_ARM64_REG_Q18,
"q19" : UC_ARM64_REG_Q19,
"q20" : UC_ARM64_REG_Q20,
"q21" : UC_ARM64_REG_Q21,
"q22" : UC_ARM64_REG_Q22,
"q23" : UC_ARM64_REG_Q23,
"q24" : UC_ARM64_REG_Q24,
"q25" : UC_ARM64_REG_Q25,
"q26" : UC_ARM64_REG_Q26,
"q27" : UC_ARM64_REG_Q27,
"q28" : UC_ARM64_REG_Q28,
"q29" : UC_ARM64_REG_Q29,
"q30" : UC_ARM64_REG_Q30,
"q31" : UC_ARM64_REG_Q31
}
reg_map_s = {
"s0" : UC_ARM64_REG_S0,
"s1" : UC_ARM64_REG_S1,
"s2" : UC_ARM64_REG_S2,
"s3" : UC_ARM64_REG_S3,
"s4" : UC_ARM64_REG_S4,
"s5" : UC_ARM64_REG_S5,
"s6" : UC_ARM64_REG_S6,
"s7" : UC_ARM64_REG_S7,
"s8" : UC_ARM64_REG_S8,
"s9" : UC_ARM64_REG_S9,
"s10" : UC_ARM64_REG_S10,
"s11" : UC_ARM64_REG_S11,
"s12" : UC_ARM64_REG_S12,
"s13" : UC_ARM64_REG_S13,
"s14" : UC_ARM64_REG_S14,
"s15" : UC_ARM64_REG_S15,
"s16" : UC_ARM64_REG_S16,
"s17" : UC_ARM64_REG_S17,
"s18" : UC_ARM64_REG_S18,
"s19" : UC_ARM64_REG_S19,
"s20" : UC_ARM64_REG_S20,
"s21" : UC_ARM64_REG_S21,
"s22" : UC_ARM64_REG_S22,
"s23" : UC_ARM64_REG_S23,
"s24" : UC_ARM64_REG_S24,
"s25" : UC_ARM64_REG_S25,
"s26" : UC_ARM64_REG_S26,
"s27" : UC_ARM64_REG_S27,
"s28" : UC_ARM64_REG_S28,
"s29" : UC_ARM64_REG_S29,
"s30" : UC_ARM64_REG_S30,
"s31" : UC_ARM64_REG_S31
}
reg_map_w = {
"w0" : UC_ARM64_REG_W0,
"w1" : UC_ARM64_REG_W1,
"w2" : UC_ARM64_REG_W2,
"w3" : UC_ARM64_REG_W3,
"w4" : UC_ARM64_REG_W4,
"w5" : UC_ARM64_REG_W5,
"w6" : UC_ARM64_REG_W6,
"w7" : UC_ARM64_REG_W7,
"w8" : UC_ARM64_REG_W8,
"w9" : UC_ARM64_REG_W9,
"w10" : UC_ARM64_REG_W10,
"w11" : UC_ARM64_REG_W11,
"w12" : UC_ARM64_REG_W12,
"w13" : UC_ARM64_REG_W13,
"w14" : UC_ARM64_REG_W14,
"w15" : UC_ARM64_REG_W15,
"w16" : UC_ARM64_REG_W16,
"w17" : UC_ARM64_REG_W17,
"w18" : UC_ARM64_REG_W18,
"w19" : UC_ARM64_REG_W19,
"w20" : UC_ARM64_REG_W20,
"w21" : UC_ARM64_REG_W21,
"w22" : UC_ARM64_REG_W22,
"w23" : UC_ARM64_REG_W23,
"w24" : UC_ARM64_REG_W24,
"w25" : UC_ARM64_REG_W25,
"w26" : UC_ARM64_REG_W26,
"w27" : UC_ARM64_REG_W27,
"w28" : UC_ARM64_REG_W28,
"w29" : UC_ARM64_REG_W29,
"w30" : UC_ARM64_REG_W30,
}
"w0" : UC_ARM64_REG_W0,
"w1" : UC_ARM64_REG_W1,
"w2" : UC_ARM64_REG_W2,
"w3" : UC_ARM64_REG_W3,
"w4" : UC_ARM64_REG_W4,
"w5" : UC_ARM64_REG_W5,
"w6" : UC_ARM64_REG_W6,
"w7" : UC_ARM64_REG_W7,
"w8" : UC_ARM64_REG_W8,
"w9" : UC_ARM64_REG_W9,
"w10" : UC_ARM64_REG_W10,
"w11" : UC_ARM64_REG_W11,
"w12" : UC_ARM64_REG_W12,
"w13" : UC_ARM64_REG_W13,
"w14" : UC_ARM64_REG_W14,
"w15" : UC_ARM64_REG_W15,
"w16" : UC_ARM64_REG_W16,
"w17" : UC_ARM64_REG_W17,
"w18" : UC_ARM64_REG_W18,
"w19" : UC_ARM64_REG_W19,
"w20" : UC_ARM64_REG_W20,
"w21" : UC_ARM64_REG_W21,
"w22" : UC_ARM64_REG_W22,
"w23" : UC_ARM64_REG_W23,
"w24" : UC_ARM64_REG_W24,
"w25" : UC_ARM64_REG_W25,
"w26" : UC_ARM64_REG_W26,
"w27" : UC_ARM64_REG_W27,
"w28" : UC_ARM64_REG_W28,
"w29" : UC_ARM64_REG_W29,
"w30" : UC_ARM64_REG_W30
}
reg_map_v = {
"v0" : UC_ARM64_REG_V0,
"v1" : UC_ARM64_REG_V1,
"v2" : UC_ARM64_REG_V2,
"v3" : UC_ARM64_REG_V3,
"v4" : UC_ARM64_REG_V4,
"v5" : UC_ARM64_REG_V5,
"v6" : UC_ARM64_REG_V6,
"v7" : UC_ARM64_REG_V7,
"v8" : UC_ARM64_REG_V8,
"v9" : UC_ARM64_REG_V9,
"v10" : UC_ARM64_REG_V10,
"v11" : UC_ARM64_REG_V11,
"v12" : UC_ARM64_REG_V12,
"v13" : UC_ARM64_REG_V13,
"v14" : UC_ARM64_REG_V14,
"v15" : UC_ARM64_REG_V15,
"v16" : UC_ARM64_REG_V16,
"v17" : UC_ARM64_REG_V17,
"v18" : UC_ARM64_REG_V18,
"v19" : UC_ARM64_REG_V19,
"v20" : UC_ARM64_REG_V20,
"v21" : UC_ARM64_REG_V21,
"v22" : UC_ARM64_REG_V22,
"v23" : UC_ARM64_REG_V23,
"v24" : UC_ARM64_REG_V24,
"v25" : UC_ARM64_REG_V25,
"v26" : UC_ARM64_REG_V26,
"v27" : UC_ARM64_REG_V27,
"v28" : UC_ARM64_REG_V28,
"v29" : UC_ARM64_REG_V29,
"v30" : UC_ARM64_REG_V30,
"v31" : UC_ARM64_REG_V31
}
+57 -33
View File
@@ -4,39 +4,63 @@
#
from unicorn.arm_const import *
from enum import IntEnum
reg_map = {
"r0": UC_ARM_REG_R0,
"r1": UC_ARM_REG_R1,
"r2": UC_ARM_REG_R2,
"r3": UC_ARM_REG_R3,
"r4": UC_ARM_REG_R4,
"r5": UC_ARM_REG_R5,
"r6": UC_ARM_REG_R6,
"r7": UC_ARM_REG_R7,
"r8": UC_ARM_REG_R8,
"r9": UC_ARM_REG_R9,
"r10": UC_ARM_REG_R10,
"r11": UC_ARM_REG_R11,
"r12": UC_ARM_REG_R12,
"sp": UC_ARM_REG_SP,
"lr": UC_ARM_REG_LR,
"pc": UC_ARM_REG_PC,
# CPSR needs to be at offset 25 for GDB, see https://sourceware.org/git/?p=binutils-gdb.git;a=blob;f=gdb/arch/arm.h;h=fa589fd0582c0add627a068e6f4947a909c45e86;hb=HEAD#l34
# The fp registers inbetween have become obsolete
"f0": UC_ARM_REG_INVALID,
"f1": UC_ARM_REG_INVALID,
"f2": UC_ARM_REG_INVALID,
"f3": UC_ARM_REG_INVALID,
"f4": UC_ARM_REG_INVALID,
"f5": UC_ARM_REG_INVALID,
"f6": UC_ARM_REG_INVALID,
"f7": UC_ARM_REG_INVALID,
"fps": UC_ARM_REG_INVALID,
"cpsr": UC_ARM_REG_CPSR,
"c1_c0_2": UC_ARM_REG_C1_C0_2,
"c13_c0_3": UC_ARM_REG_C13_C0_3,
"fpexc": UC_ARM_REG_FPEXC,
"r0": UC_ARM_REG_R0,
"r1": UC_ARM_REG_R1,
"r2": UC_ARM_REG_R2,
"r3": UC_ARM_REG_R3,
"r4": UC_ARM_REG_R4,
"r5": UC_ARM_REG_R5,
"r6": UC_ARM_REG_R6,
"r7": UC_ARM_REG_R7,
"r8": UC_ARM_REG_R8,
"r9": UC_ARM_REG_R9,
"r10": UC_ARM_REG_R10,
"r11": UC_ARM_REG_R11,
"r12": UC_ARM_REG_R12,
"sp": UC_ARM_REG_SP,
"lr": UC_ARM_REG_LR,
"pc": UC_ARM_REG_PC,
"cpsr": UC_ARM_REG_CPSR,
"c1_c0_2": UC_ARM_REG_C1_C0_2,
"c13_c0_3": UC_ARM_REG_C13_C0_3,
"fpexc": UC_ARM_REG_FPEXC
}
reg_vfp = {
"d0" : UC_ARM_REG_D0,
"d1" : UC_ARM_REG_D1,
"d2" : UC_ARM_REG_D2,
"d3" : UC_ARM_REG_D3,
"d4" : UC_ARM_REG_D4,
"d5" : UC_ARM_REG_D5,
"d6" : UC_ARM_REG_D6,
"d7" : UC_ARM_REG_D7,
"d8" : UC_ARM_REG_D8,
"d9" : UC_ARM_REG_D9,
"d10" : UC_ARM_REG_D10,
"d11" : UC_ARM_REG_D11,
"d12" : UC_ARM_REG_D12,
"d13" : UC_ARM_REG_D13,
"d14" : UC_ARM_REG_D14,
"d15" : UC_ARM_REG_D15,
"d16" : UC_ARM_REG_D16,
"d17" : UC_ARM_REG_D17,
"d18" : UC_ARM_REG_D18,
"d19" : UC_ARM_REG_D19,
"d20" : UC_ARM_REG_D20,
"d21" : UC_ARM_REG_D21,
"d22" : UC_ARM_REG_D22,
"d23" : UC_ARM_REG_D23,
"d24" : UC_ARM_REG_D24,
"d25" : UC_ARM_REG_D25,
"d26" : UC_ARM_REG_D26,
"d27" : UC_ARM_REG_D27,
"d28" : UC_ARM_REG_D28,
"d29" : UC_ARM_REG_D29,
"d30" : UC_ARM_REG_D30,
"d31" : UC_ARM_REG_D31,
"fpscr" : UC_ARM_REG_FPSCR
}
+4 -3
View File
@@ -1,4 +1,5 @@
import os
import inspect
from pathlib import Path
import re
import logging
import json
@@ -92,8 +93,8 @@ def analysis_func_sign(insns:list, engine_num=1):
class signatures_engine_1:
@staticmethod
def find_signature(sign):
path = os.path.split(os.path.realpath(__file__))[0] + '/signatures.json'
with open(path) as data_file:
path = Path(inspect.getfile(inspect.getframe())).parent / 'signatures.json'
with path.open('r') as data_file:
data = json.load(data_file)
list_name = [name for name, hexa in data.items() if hexa == sign]
+19 -3
View File
@@ -3,10 +3,13 @@
# Cross Platform and Multi Architecture Advanced Binary Emulation Framework
from qiling.const import *
from ..arch import QlArch
from .vm.evm import QlArchEVMEmulator
import types
from qiling.arch.arch import QlArch
from qiling.arch.evm.hooks import monkeypatch_core_hooks
from qiling.arch.evm.vm.evm import QlArchEVMEmulator
from qiling.arch.evm.vm.message import Message
from qiling.const import *
class QlArchEVM(QlArch):
type = QL_ARCH.EVM
@@ -16,6 +19,9 @@ class QlArchEVM(QlArch):
super(QlArchEVM, self).__init__(ql)
self.evm = QlArchEVMEmulator(self.ql)
monkeypatch_core_hooks(self.ql)
monkeypatch_core_methods(self.ql)
def run(self, msg):
return self.evm.vm.execute_message(msg)
@@ -38,3 +44,13 @@ class QlArchEVM(QlArch):
@property
def endian(self) -> QL_ENDIAN:
return QL_ENDIAN.EL
def __evm_run(self, code: Message):
return self.arch.run(code)
def monkeypatch_core_methods(ql):
"""Monkeypatch core methods for evm
"""
ql.run = types.MethodType(__evm_run, ql)
+53 -51
View File
@@ -2,70 +2,72 @@
#
# Cross Platform and Multi Architecture Advanced Binary Emulation Framework
import types
from enum import IntEnum
from typing import MutableMapping, MutableSequence
from qiling.core_hooks_types import Hook, HookAddr, HookIntr, HookRet
class EVM_HOOK(IntEnum):
CODE = (1 << 0)
ADDR = (1 << 1)
INSN = (1 << 2)
class QlArchEVMHooks:
def __init__(self) -> None:
super().__init__()
self.hook_code_list = []
self.hook_insn_list = []
self.hook_addr_dict = {}
self.hook_code_list: MutableSequence[Hook] = []
self.hook_insn_list: MutableSequence[HookIntr] = []
self.hook_addr_dict: MutableMapping[int, MutableSequence[HookAddr]] = {}
evm_hooks_info = QlArchEVMHooks()
def _ql_evm_hook(ql, hook_type, h, *args):
base_type = [
"HOOK_CODE",
"HOOK_INSN",
"HOOK_ADDR"
]
if hook_type in base_type:
if hook_type in ["HOOK_CODE"]:
evm_hooks_info.hook_code_list.append(h)
elif hook_type in ["HOOK_INSN"]:
evm_hooks_info.hook_insn_list.append(h)
elif hook_type in ["HOOK_ADDR"]:
address = args[0]
if address not in evm_hooks_info.hook_addr_dict.keys():
evm_hooks_info.hook_addr_dict[address] = []
evm_hooks_info.hook_addr_dict[address].append(h)
def ql_evm_hooks(ql, hook_type, callback, user_data=None, begin=1, end=0, *args):
def __evm_hook_code(ql, callback, user_data=None, begin=1, end=0):
h = Hook(callback, user_data, begin, end)
_ql_evm_hook(ql, hook_type, h, *args)
return HookRet(ql, hook_type, h)
evm_hooks_info.hook_code_list.append(h)
def evm_hook_insn(ql, hook_type, callback, intno, user_data=None, begin=1, end=0):
return HookRet(ql, EVM_HOOK.CODE, h)
def __evm_hook_insn(ql, callback, intno, user_data=None, begin=1, end=0):
h = HookIntr(callback, intno, user_data)
_ql_evm_hook(ql, hook_type, h)
return HookRet(ql, hook_type, h)
evm_hooks_info.hook_insn_list.append(h)
def evm_hook_address(ql, hook_type, h, address):
_ql_evm_hook(ql, hook_type, h, address)
return HookRet(ql, hook_type, h)
return HookRet(ql, EVM_HOOK.INSN, h)
def evm_hook_del(hook_type, h):
base_type = [
"HOOK_CODE",
"HOOK_INSN",
"HOOK_ADDR"
]
def __evm_hook_address(ql, callback, address, user_data=None):
h = HookAddr(callback, address, user_data)
if isinstance(h, HookAddr):
if h.addr in evm_hooks_info.hook_addr_dict.keys():
if h in evm_hooks_info.hook_addr_dict[h.addr]:
evm_hooks_info.hook_addr_dict[h.addr].remove(h)
if len(evm_hooks_info.hook_addr_dict[h.addr]) == 0:
del evm_hooks_info.hook_addr_dict[h.addr]
if address not in evm_hooks_info.hook_addr_dict:
evm_hooks_info.hook_addr_dict[address] = []
if hook_type in base_type:
if hook_type in ["HOOK_CODE"]:
evm_hooks_info.hook_code_list.remove(h)
elif hook_type in ["HOOK_INSN"]:
evm_hooks_info.hook_insn_list.remove(h)
evm_hooks_info.hook_addr_dict[address].append(h)
return HookRet(ql, EVM_HOOK.ADDR, h)
def __evm_hook_del(ql, hret):
h = hret.obj
hook_type = hret.type
if hook_type == EVM_HOOK.CODE:
evm_hooks_info.hook_code_list.remove(h)
elif hook_type == EVM_HOOK.INSN:
evm_hooks_info.hook_insn_list.remove(h)
elif hook_type == EVM_HOOK.ADDR:
if h.addr in evm_hooks_info.hook_addr_dict:
hooks_list = evm_hooks_info.hook_addr_dict[h.addr]
if h in hooks_list:
hooks_list.remove(h)
if not hooks_list:
del evm_hooks_info.hook_addr_dict[h.addr]
def monkeypatch_core_hooks(ql):
"""Monkeypatch core hooks for evm
"""
ql.hook_code = types.MethodType(__evm_hook_code, ql)
ql.hook_address = types.MethodType(__evm_hook_address, ql)
ql.hook_insn = types.MethodType(__evm_hook_insn, ql)
ql.hook_del = types.MethodType(__evm_hook_del, ql)
+2 -2
View File
@@ -6,7 +6,7 @@
import os, time
import cmd2
from rich import print as rprint
from ..hooks import evm_hook_insn
from ..hooks import evm_hooks_info
from .disassembler import EVMDisasm
from ..analysis.signatures import analysis_func_sign
from .utils import analysis_bytecode, bytecode_to_bytes
@@ -38,7 +38,7 @@ class EVMDebugger(cmd2.Cmd):
load_bytecode, runtime_code, aux_data, constructor_args = analysis_bytecode(self.executor.vm_context.msg.code)
insns = EVMDisasm().disasm(bytecode_to_bytes(runtime_code), evm_hook_insn)
insns = EVMDisasm().disasm(bytecode_to_bytes(runtime_code), evm_hooks_info)
self.func_sign = analysis_func_sign(insns, engine_num=2)
self.cli_output()
+2 -1
View File
@@ -37,7 +37,8 @@ class QlArchMIPS(QlArch):
def regs(self) -> QlRegisterManager:
regs_map = dict(
**mips_const.reg_map,
**mips_const.reg_map_afpr128
**mips_const.reg_map_afpr128,
**mips_const.reg_map_fpu
)
pc_reg = 'pc'
+69 -1
View File
@@ -6,6 +6,39 @@
from unicorn.mips_const import *
reg_map = {
"r0" : UC_MIPS_REG_0,
"r1" : UC_MIPS_REG_1,
"r2" : UC_MIPS_REG_2,
"r3" : UC_MIPS_REG_3,
"r4" : UC_MIPS_REG_4,
"r5" : UC_MIPS_REG_5,
"r6" : UC_MIPS_REG_6,
"r7" : UC_MIPS_REG_7,
"r8" : UC_MIPS_REG_8,
"r9" : UC_MIPS_REG_9,
"r10" : UC_MIPS_REG_10,
"r11" : UC_MIPS_REG_11,
"r12" : UC_MIPS_REG_12,
"r13" : UC_MIPS_REG_13,
"r14" : UC_MIPS_REG_14,
"r15" : UC_MIPS_REG_15,
"r16" : UC_MIPS_REG_16,
"r17" : UC_MIPS_REG_17,
"r18" : UC_MIPS_REG_18,
"r19" : UC_MIPS_REG_19,
"r20" : UC_MIPS_REG_20,
"r21" : UC_MIPS_REG_21,
"r22" : UC_MIPS_REG_22,
"r23" : UC_MIPS_REG_23,
"r24" : UC_MIPS_REG_24,
"r25" : UC_MIPS_REG_25,
"r26" : UC_MIPS_REG_26,
"r27" : UC_MIPS_REG_27,
"r28" : UC_MIPS_REG_28,
"r29" : UC_MIPS_REG_29,
"r30" : UC_MIPS_REG_30,
"r31" : UC_MIPS_REG_31,
"zero": UC_MIPS_REG_ZERO,
"at": UC_MIPS_REG_AT,
"v0": UC_MIPS_REG_V0,
@@ -49,4 +82,39 @@ reg_map = {
reg_map_afpr128 = {
"cp0_config3" : UC_MIPS_REG_CP0_CONFIG3,
"cp0_userlocal": UC_MIPS_REG_CP0_USERLOCAL,
}
}
reg_map_fpu = {
"f0" : UC_MIPS_REG_F0,
"f1" : UC_MIPS_REG_F1,
"f2" : UC_MIPS_REG_F2,
"f3" : UC_MIPS_REG_F3,
"f4" : UC_MIPS_REG_F4,
"f5" : UC_MIPS_REG_F5,
"f6" : UC_MIPS_REG_F6,
"f7" : UC_MIPS_REG_F7,
"f8" : UC_MIPS_REG_F8,
"f9" : UC_MIPS_REG_F9,
"f10" : UC_MIPS_REG_F10,
"f11" : UC_MIPS_REG_F11,
"f12" : UC_MIPS_REG_F12,
"f13" : UC_MIPS_REG_F13,
"f14" : UC_MIPS_REG_F14,
"f15" : UC_MIPS_REG_F15,
"f16" : UC_MIPS_REG_F16,
"f17" : UC_MIPS_REG_F17,
"f18" : UC_MIPS_REG_F18,
"f19" : UC_MIPS_REG_F19,
"f20" : UC_MIPS_REG_F20,
"f21" : UC_MIPS_REG_F21,
"f22" : UC_MIPS_REG_F22,
"f23" : UC_MIPS_REG_F23,
"f24" : UC_MIPS_REG_F24,
"f25" : UC_MIPS_REG_F25,
"f26" : UC_MIPS_REG_F26,
"f27" : UC_MIPS_REG_F27,
"f28" : UC_MIPS_REG_F28,
"f29" : UC_MIPS_REG_F29,
"f30" : UC_MIPS_REG_F30,
"f31" : UC_MIPS_REG_F31
}
+51
View File
@@ -0,0 +1,51 @@
#!/usr/bin/env python3
#
# Cross Platform and Multi Architecture Advanced Binary Emulation Framework
#
from functools import cached_property
from unicorn import Uc, UC_ARCH_PPC, UC_MODE_PPC32, UC_MODE_BIG_ENDIAN
from capstone import Cs, CS_ARCH_PPC, CS_MODE_32, CS_MODE_BIG_ENDIAN
from keystone import Ks, KS_ARCH_PPC, KS_MODE_PPC32, KS_MODE_BIG_ENDIAN
from qiling import Qiling
from qiling.arch.arch import QlArch
from qiling.arch import ppc_const
from qiling.arch.register import QlRegisterManager
from qiling.const import QL_ARCH, QL_ENDIAN
class QlArchPPC(QlArch):
type = QL_ARCH.PPC
bits = 32
@cached_property
def uc(self) -> Uc:
return Uc(UC_ARCH_PPC, UC_MODE_PPC32 + UC_MODE_BIG_ENDIAN)
@cached_property
def regs(self) -> QlRegisterManager:
regs_map = dict(
**ppc_const.reg_map,
**ppc_const.reg_float_map
)
pc_reg = 'pc'
sp_reg = 'r1'
return QlRegisterManager(self.uc, regs_map, pc_reg, sp_reg)
@cached_property
def disassembler(self) -> Cs:
return Cs(CS_ARCH_PPC, CS_MODE_32 + CS_MODE_BIG_ENDIAN)
@cached_property
def assembler(self) -> Ks:
return Ks(KS_ARCH_PPC, KS_MODE_PPC32 + KS_MODE_BIG_ENDIAN)
@property
def endian(self) -> QL_ENDIAN:
return QL_ENDIAN.EB
def enable_float(self):
self.regs.msr = self.regs.msr | ppc_const.MSR.FP
+128
View File
@@ -0,0 +1,128 @@
#!/usr/bin/env python3
#
# Cross Platform and Multi Architecture Advanced Binary Emulation Framework
#
from unicorn.ppc_const import *
from enum import IntEnum
reg_map = {
"r0": UC_PPC_REG_0,
"r1": UC_PPC_REG_1,
"r2": UC_PPC_REG_2,
"r3": UC_PPC_REG_3,
"r4": UC_PPC_REG_4,
"r5": UC_PPC_REG_5,
"r6": UC_PPC_REG_6,
"r7": UC_PPC_REG_7,
"r8": UC_PPC_REG_8,
"r9": UC_PPC_REG_9,
"r10": UC_PPC_REG_10,
"r11": UC_PPC_REG_11,
"r12": UC_PPC_REG_12,
"r13": UC_PPC_REG_13,
"r14": UC_PPC_REG_14,
"r15": UC_PPC_REG_15,
"r16": UC_PPC_REG_16,
"r17": UC_PPC_REG_17,
"r18": UC_PPC_REG_18,
"r19": UC_PPC_REG_19,
"r20": UC_PPC_REG_20,
"r21": UC_PPC_REG_21,
"r22": UC_PPC_REG_22,
"r23": UC_PPC_REG_23,
"r24": UC_PPC_REG_24,
"r25": UC_PPC_REG_25,
"r26": UC_PPC_REG_26,
"r27": UC_PPC_REG_27,
"r28": UC_PPC_REG_28,
"r29": UC_PPC_REG_29,
"r30": UC_PPC_REG_30,
"r31": UC_PPC_REG_31,
"pc": UC_PPC_REG_PC,
"msr": UC_PPC_REG_MSR,
"cr": UC_PPC_REG_CR0,
"lr": UC_PPC_REG_LR,
"ctr": UC_PPC_REG_CTR,
"xer": UC_PPC_REG_XER,
}
reg_float_map = {
"f0": UC_PPC_REG_FPR0,
"f1": UC_PPC_REG_FPR1,
"f2": UC_PPC_REG_FPR2,
"f3": UC_PPC_REG_FPR3,
"f4": UC_PPC_REG_FPR4,
"f5": UC_PPC_REG_FPR5,
"f6": UC_PPC_REG_FPR6,
"f7": UC_PPC_REG_FPR7,
"f8": UC_PPC_REG_FPR8,
"f9": UC_PPC_REG_FPR9,
"f10": UC_PPC_REG_FPR10,
"f11": UC_PPC_REG_FPR11,
"f12": UC_PPC_REG_FPR12,
"f13": UC_PPC_REG_FPR13,
"f14": UC_PPC_REG_FPR14,
"f15": UC_PPC_REG_FPR15,
"f16": UC_PPC_REG_FPR16,
"f17": UC_PPC_REG_FPR17,
"f18": UC_PPC_REG_FPR18,
"f19": UC_PPC_REG_FPR19,
"f20": UC_PPC_REG_FPR20,
"f21": UC_PPC_REG_FPR21,
"f22": UC_PPC_REG_FPR22,
"f23": UC_PPC_REG_FPR23,
"f24": UC_PPC_REG_FPR24,
"f25": UC_PPC_REG_FPR25,
"f26": UC_PPC_REG_FPR26,
"f27": UC_PPC_REG_FPR27,
"f28": UC_PPC_REG_FPR28,
"f29": UC_PPC_REG_FPR29,
"f30": UC_PPC_REG_FPR30,
"f31": UC_PPC_REG_FPR31,
}
class MSR(IntEnum):
SF = 1 << 63
TAG = 1 << 62
ISF = 1 << 61
HV = 1 << 60
TS0 = 1 << 34
TS1 = 1 << 33
TM = 1 << 32
CM = 1 << 31
ICM = 1 << 30
GS = 1 << 28
UCLE = 1 << 26
VR = 1 << 25
SPE = 1 << 25
AP = 1 << 23
VSX = 1 << 23
SA = 1 << 22
KEY = 1 << 19
POW = 1 << 18
TGPR = 1 << 17
CE = 1 << 17
ILE = 1 << 16
EE = 1 << 15
PR = 1 << 14
FP = 1 << 13
ME = 1 << 12
FE0 = 1 << 11
SE = 1 << 10
DWE = 1 << 10
UBLE = 1 << 10
BE = 1 << 9
DE = 1 << 9
FE1 = 1 << 8
AL = 1 << 7
EP = 1 << 6
IR = 1 << 5
DR = 1 << 4
IS = 1 << 5
DS = 1 << 4
PE = 1 << 3
PX = 1 << 2
PMM = 1 << 2
RI = 1 << 1
LE = 1 << 0
-6
View File
@@ -102,12 +102,6 @@ class QlRegisterManager:
return self.uc.reg_write(self.uc_pc, value)
@property
def arch_pc_name(self) -> str:
"""Get the architectural program counter register name.
"""
return next(k for k, v in self.register_mapping.items() if v == self.uc_pc)
@property
def arch_sp(self) -> int:
+4 -3
View File
@@ -11,8 +11,8 @@ from typing import Tuple
from os.path import basename
from functools import lru_cache
from keystone import (Ks, KS_ARCH_ARM, KS_ARCH_ARM64, KS_ARCH_MIPS, KS_ARCH_X86,
KS_MODE_ARM, KS_MODE_THUMB, KS_MODE_MIPS32, KS_MODE_16, KS_MODE_32, KS_MODE_64,
from keystone import (Ks, KS_ARCH_ARM, KS_ARCH_ARM64, KS_ARCH_MIPS, KS_ARCH_X86, KS_ARCH_PPC,
KS_MODE_ARM, KS_MODE_THUMB, KS_MODE_MIPS32, KS_MODE_PPC32, KS_MODE_16, KS_MODE_32, KS_MODE_64,
KS_MODE_LITTLE_ENDIAN, KS_MODE_BIG_ENDIAN)
from qiling import Qiling
@@ -104,7 +104,8 @@ def assembler(arch: QL_ARCH, endianess: QL_ENDIAN, is_thumb: bool) -> Ks:
QL_ARCH.MIPS : (KS_ARCH_MIPS, KS_MODE_MIPS32 + endian),
QL_ARCH.A8086 : (KS_ARCH_X86, KS_MODE_16),
QL_ARCH.X86 : (KS_ARCH_X86, KS_MODE_32),
QL_ARCH.X8664 : (KS_ARCH_X86, KS_MODE_64)
QL_ARCH.X8664 : (KS_ARCH_X86, KS_MODE_64),
QL_ARCH.PPC : (KS_ARCH_PPC, KS_MODE_PPC32 + KS_MODE_BIG_ENDIAN)
}
if arch in asm_map:
+4 -34
View File
@@ -4,14 +4,11 @@
#
from functools import cached_property
from typing import Union
from unicorn import Uc, UC_ARCH_X86, UC_MODE_16, UC_MODE_32, UC_MODE_64
from capstone import Cs, CS_ARCH_X86, CS_MODE_16, CS_MODE_32, CS_MODE_64
from keystone import Ks, KS_ARCH_X86, KS_MODE_16, KS_MODE_32, KS_MODE_64
from unicorn.x86_const import UC_X86_REG_EFLAGS
from qiling.arch.arch import QlArch
from qiling.arch.msr import QlMsrManager
from qiling.arch.register import QlRegisterManager
@@ -112,7 +109,10 @@ class QlArchX8664(QlArchIntel):
**x86_const.reg_map_64_b,
**x86_const.reg_map_64_w,
**x86_const.reg_map_64_d,
**x86_const.reg_map_seg_base
**x86_const.reg_map_seg_base,
**x86_const.reg_map_xmm,
**x86_const.reg_map_ymm,
**x86_const.reg_map_zmm
)
pc_reg = 'rip'
@@ -126,33 +126,3 @@ class QlArchX8664(QlArchIntel):
@cached_property
def assembler(self) -> Ks:
return Ks(KS_ARCH_X86, KS_MODE_64)
# TODO: generalize this
def __reg_bits(self, register: int) -> int:
# all regs in reg_map_misc are 16 bits except of eflags
if register == UC_X86_REG_EFLAGS:
return 32
regmaps = (
(x86_const.reg_map_8, 8),
(x86_const.reg_map_16, 16),
(x86_const.reg_map_32, 32),
(x86_const.reg_map_64, 64),
(x86_const.reg_map_misc, 16),
(x86_const.reg_map_cr, 64), # 32 bits for x86
(x86_const.reg_map_st, 32),
(x86_const.reg_map_seg_base, 64), # 32 bits for x86
)
return next((rsize for rmap, rsize in regmaps if register in rmap.values()), 0)
# note: this method was not generalized for all archs since it requires a bookkeeping
# of all registers, while it is used only by gdb and only for x86-64
def reg_bits(self, reg: Union[str, int]) -> int:
"""Get register size in bits.
"""
if type(reg) is str:
reg = self.regs.register_mapping[reg]
return self.__reg_bits(reg)
+1 -1
View File
@@ -177,7 +177,7 @@ reg_map_st = {
}
reg_map_misc = {
"ef": UC_X86_REG_EFLAGS,
"eflags": UC_X86_REG_EFLAGS,
"cs": UC_X86_REG_CS,
"ss": UC_X86_REG_SS,
"ds": UC_X86_REG_DS,
+5 -3
View File
@@ -4,7 +4,7 @@ from abc import abstractmethod
from qiling import Qiling
from qiling.arch.x86 import QlArchIntel
from qiling.arch.x86_const import *
from qiling.exception import QlGDTError
from qiling.exception import QlGDTError, QlMemoryMappedError
from qiling.os.memory import QlMemoryManager
class GDTArray:
@@ -50,8 +50,10 @@ class GDTManager:
def __init__(self, ql: Qiling, base = QL_X86_GDT_ADDR, limit = QL_X86_GDT_LIMIT, num_entries = 16):
ql.log.debug(f'Mapping GDT at {base:#x} with limit {limit:#x}')
if not ql.mem.is_mapped(base, limit):
ql.mem.map(base, limit, info="[GDT]")
if not ql.mem.is_available(base, limit):
raise QlMemoryMappedError('cannot map GDT, memory location is taken')
ql.mem.map(base, limit, info="[GDT]")
# setup GDT by writing to GDTR
ql.arch.regs.write(UC_X86_REG_GDTR, (0, base, limit, 0x0))
+10 -12
View File
@@ -2,7 +2,7 @@
#
# Cross Platform and Multi Architecture Advanced Binary Emulation Framework
from typing import Callable, Tuple
from typing import Callable, Sequence, Tuple
from qiling.arch.arch import QlArch
@@ -26,7 +26,7 @@ class QlCC:
raise NotImplementedError
def getRawParam(self, slot: int, argbits: int = None) -> int:
def getRawParam(self, slot: int, argbits: int = 0) -> int:
"""Read a value of native size from the specified argument slot.
Note that argument slots and argument indexes are not the same. Though they often correlate
@@ -41,7 +41,7 @@ class QlCC:
raise NotImplementedError
def setRawParam(self, slot: int, value: int, argbits: int = None) -> None:
def setRawParam(self, slot: int, value: int, argbits: int = 0) -> None:
"""Replace the value in the specified argument slot.
Note that argument slots and argument indexes are not the same. Though they often correlate
@@ -107,19 +107,17 @@ class QlCommonBaseCC(QlCC):
of the QlCC interface.
"""
_argregs = ()
_retreg: int
_argregs: Sequence
_shadow = 0
_retaddr_on_stack = True
def __init__(self, arch: QlArch, retreg: int):
def __init__(self, arch: QlArch):
super().__init__(arch)
# native address size in bytes
self._asize = self.arch.pointersize
# return value register
self._retreg = retreg
def __access_param(self, index: int, stack_access: Callable, reg_access: Callable) -> Tuple[Callable, int]:
"""[private] Generic accessor to function call parameters by their index.
@@ -150,17 +148,17 @@ class QlCommonBaseCC(QlCC):
else:
return reg_access, reg
def getRawParam(self, index: int, argbits: int = None) -> int:
def getRawParam(self, index: int, argbits: int = 0) -> int:
read, loc = self.__access_param(index, self.arch.stack_read, self.arch.regs.read)
mask = (0 if argbits is None else (1 << argbits)) - 1
mask = (argbits and (1 << argbits)) - 1
return read(loc) & mask
def setRawParam(self, index: int, value: int, argbits: int = None) -> None:
def setRawParam(self, index: int, value: int, argbits: int = 0) -> None:
write, loc = self.__access_param(index, self.arch.stack_write, self.arch.regs.write)
mask = (0 if argbits is None else (1 << argbits)) - 1
mask = (argbits and (1 << argbits)) - 1
write(loc, value & mask)
+2 -7
View File
@@ -8,7 +8,6 @@ from unicorn.arm64_const import (
UC_ARM64_REG_X4, UC_ARM64_REG_X5, UC_ARM64_REG_X6, UC_ARM64_REG_X7
)
from qiling.arch.arch import QlArch
from qiling.cc import QlCommonBaseCC
class QlArmBaseCC(QlCommonBaseCC):
@@ -29,13 +28,9 @@ class QlArmBaseCC(QlCommonBaseCC):
return self.arch.stack_pop()
class aarch64(QlArmBaseCC):
_retreg = UC_ARM64_REG_X0
_argregs = (UC_ARM64_REG_X0, UC_ARM64_REG_X1, UC_ARM64_REG_X2, UC_ARM64_REG_X3, UC_ARM64_REG_X4, UC_ARM64_REG_X5, UC_ARM64_REG_X6, UC_ARM64_REG_X7) + (None, ) * 8
def __init__(self, arch: QlArch) -> None:
super().__init__(arch, UC_ARM64_REG_X0)
class aarch32(QlArmBaseCC):
_retreg = UC_ARM_REG_R0
_argregs = (UC_ARM_REG_R0, UC_ARM_REG_R1, UC_ARM_REG_R2, UC_ARM_REG_R3) + (None, ) * 12
def __init__(self, arch: QlArch) -> None:
super().__init__(arch, UC_ARM_REG_R0)
+8 -14
View File
@@ -3,12 +3,11 @@
# Cross Platform and Multi Architecture Advanced Binary Emulation Framework
from unicorn.x86_const import (
UC_X86_REG_AX, UC_X86_REG_EAX, UC_X86_REG_RAX, UC_X86_REG_RCX,
UC_X86_REG_RDI, UC_X86_REG_RDX, UC_X86_REG_RSI, UC_X86_REG_R8,
UC_X86_REG_R9, UC_X86_REG_R10
UC_X86_REG_EAX, UC_X86_REG_RAX, UC_X86_REG_RCX, UC_X86_REG_RDI,
UC_X86_REG_RDX, UC_X86_REG_RSI, UC_X86_REG_R8, UC_X86_REG_R9,
UC_X86_REG_R10
)
from qiling.arch.arch import QlArch
from qiling.cc import QlCommonBaseCC
class QlIntelBaseCC(QlCommonBaseCC):
@@ -16,15 +15,6 @@ class QlIntelBaseCC(QlCommonBaseCC):
Supports arguments passing over registers and stack.
"""
def __init__(self, arch: QlArch):
retreg = {
16: UC_X86_REG_AX,
32: UC_X86_REG_EAX,
64: UC_X86_REG_RAX
}[arch.bits]
super().__init__(arch, retreg)
def setReturnAddress(self, addr: int) -> None:
self.arch.stack_push(addr)
@@ -36,6 +26,8 @@ class QlIntel64(QlIntelBaseCC):
"""Calling convention base class for Intel-based 64-bit systems.
"""
_retreg = UC_X86_REG_RAX
@staticmethod
def getNumSlots(argbits: int) -> int:
return max(argbits, 64) // 64
@@ -44,11 +36,13 @@ class QlIntel32(QlIntelBaseCC):
"""Calling convention base class for Intel-based 32-bit systems.
"""
_retreg = UC_X86_REG_EAX
@staticmethod
def getNumSlots(argbits: int) -> int:
return max(argbits, 32) // 32
def getRawParam(self, slot: int, nbits: int = None) -> int:
def getRawParam(self, slot: int, nbits: int = 0) -> int:
__super_getparam = super().getRawParam
if nbits == 64:
+1 -4
View File
@@ -4,17 +4,14 @@
from unicorn.mips_const import UC_MIPS_REG_V0, UC_MIPS_REG_A0, UC_MIPS_REG_A1, UC_MIPS_REG_A2, UC_MIPS_REG_A3
from qiling.arch.arch import QlArch
from qiling.cc import QlCommonBaseCC
class mipso32(QlCommonBaseCC):
_retreg = UC_MIPS_REG_V0
_argregs = (UC_MIPS_REG_A0, UC_MIPS_REG_A1, UC_MIPS_REG_A2, UC_MIPS_REG_A3) + (None, ) * 12
_shadow = 4
_retaddr_on_stack = False
def __init__(self, arch: QlArch):
super().__init__(arch, UC_MIPS_REG_V0)
@staticmethod
def getNumSlots(argbits: int):
return 1
+23
View File
@@ -0,0 +1,23 @@
#!/usr/bin/env python3
#
# Cross Platform and Multi Architecture Advanced Binary Emulation Framework
from qiling.cc import QlCommonBaseCC
from unicorn.ppc_const import (
UC_PPC_REG_3, UC_PPC_REG_4, UC_PPC_REG_5,
UC_PPC_REG_6, UC_PPC_REG_7, UC_PPC_REG_8,
)
class ppc(QlCommonBaseCC):
"""Default calling convention for PPC
First 6 arguments are passed in regs, the rest are passed on the stack.
"""
_retreg = UC_PPC_REG_3
_argregs = (UC_PPC_REG_3, UC_PPC_REG_4, UC_PPC_REG_5, UC_PPC_REG_6, UC_PPC_REG_7, UC_PPC_REG_8) + (None, ) * 10
@staticmethod
def getNumSlots(argbits: int):
return 1
+4 -8
View File
@@ -2,25 +2,21 @@
#
# Cross Platform and Multi Architecture Advanced Binary Emulation Framework
from qiling.arch.arch import QlArch
from qiling.cc import QlCommonBaseCC
from unicorn.riscv_const import (
UC_RISCV_REG_A0, UC_RISCV_REG_A1, UC_RISCV_REG_A2,
UC_RISCV_REG_A3, UC_RISCV_REG_A4, UC_RISCV_REG_A5,
UC_RISCV_REG_A0, UC_RISCV_REG_A1, UC_RISCV_REG_A2,
UC_RISCV_REG_A3, UC_RISCV_REG_A4, UC_RISCV_REG_A5
)
class riscv(QlCommonBaseCC):
"""Default calling convention for RISCV
First 6 arguments are passed in regs, the rest are passed on the stack.
First 6 arguments are passed in regs, the rest are passed on the stack.
"""
_retreg = UC_RISCV_REG_A0
_argregs = (UC_RISCV_REG_A0, UC_RISCV_REG_A1, UC_RISCV_REG_A2, UC_RISCV_REG_A3, UC_RISCV_REG_A4, UC_RISCV_REG_A5) + (None, ) * 10
def __init__(self, arch: QlArch):
super().__init__(arch, UC_RISCV_REG_A0)
@staticmethod
def getNumSlots(argbits: int):
return 1
+19 -25
View File
@@ -4,7 +4,7 @@
#
from enum import Enum, Flag, IntEnum
from typing import Any, Mapping, Type
from typing import Mapping, Type, TypeVar
class QL_ENDIAN(IntEnum):
EL = 1
@@ -21,6 +21,7 @@ class QL_ARCH(IntEnum):
CORTEX_M = 109
RISCV = 110
RISCV64 = 111
PPC = 112
class QL_OS(IntEnum):
LINUX = 201
@@ -57,39 +58,32 @@ class QL_STOP(Flag):
STACK_POINTER = (1 << 0)
EXIT_TRAP = (1 << 1)
QL_OS_NONPID = (QL_OS.DOS, QL_OS.UEFI)
QL_OS_POSIX = (QL_OS.LINUX, QL_OS.FREEBSD, QL_OS.MACOS, QL_OS.QNX)
QL_OS_BAREMETAL = (QL_OS.MCU,)
QL_OS_INTERPRETER = (QL_OS.EVM,)
QL_ARCH_INTERPRETER = (QL_ARCH.EVM,)
QL_OS_POSIX = (QL_OS.LINUX, QL_OS.FREEBSD, QL_OS.MACOS, QL_OS.QNX)
QL_OS_BAREMETAL = (QL_OS.MCU,)
QL_HOOK_BLOCK = 0b0001
QL_CALL_BLOCK = 0b0010
def __reverse_enum(e: Type[Enum]) -> Mapping[str, Any]:
'''Create a reverse mapping for an enum.
T = TypeVar('T', bound=Enum)
def __casefold_enum(e: Type[T]) -> Mapping[str, T]:
'''Create a casefolded mapping of an enum to allow case-insensitive lookup.
'''
return dict((v.name.lower(), v) for v in e.__members__.values())
return dict((k.casefold(), v) for k, v in e._member_map_.items())
debugger_map: Mapping[str, QL_DEBUGGER] = __reverse_enum(QL_DEBUGGER)
arch_map : Mapping[str, QL_ARCH] = __reverse_enum(QL_ARCH)
os_map : Mapping[str, QL_OS] = __reverse_enum(QL_OS)
verbose_map : Mapping[str, QL_VERBOSE] = __reverse_enum(QL_VERBOSE)
loader_map = {
QL_OS.LINUX : "ELF",
QL_OS.FREEBSD : "ELF",
QL_OS.QNX : "ELF",
QL_OS.MACOS : "MACHO",
QL_OS.WINDOWS : "PE",
QL_OS.UEFI : "PE_UEFI",
QL_OS.DOS : "DOS",
QL_OS.EVM : "EVM",
QL_OS.MCU : "MCU",
QL_OS.BLOB : "BLOB"
}
debugger_map = __casefold_enum(QL_DEBUGGER)
arch_map = __casefold_enum(QL_ARCH)
os_map = __casefold_enum(QL_OS)
verbose_map = __casefold_enum(QL_VERBOSE)
arch_os_map = {
QL_ARCH.EVM : QL_OS.EVM,
QL_ARCH.CORTEX_M : QL_OS.MCU
}
__all__ = [
'QL_ENDIAN', 'QL_ARCH', 'QL_OS', 'QL_VERBOSE', 'QL_DEBUGGER', 'QL_INTERCEPT', 'QL_STOP',
'QL_ARCH_INTERPRETER', 'QL_OS_POSIX', 'QL_OS_BAREMETAL', 'QL_HOOK_BLOCK', 'QL_CALL_BLOCK'
]
+78 -92
View File
@@ -3,51 +3,49 @@
# Cross Platform and Multi Architecture Advanced Binary Emulation Framework
#
from configparser import ConfigParser
import os, pickle
from typing import TYPE_CHECKING, Any, AnyStr, List, Mapping, MutableMapping, Optional, Sequence, Tuple, Union
# See https://stackoverflow.com/questions/39740632/python-type-hinting-without-cyclic-imports
from typing import AnyStr, List, Mapping, MutableMapping, Sequence, Union
from typing import TYPE_CHECKING
from unicorn.unicorn import Uc
if TYPE_CHECKING:
from unicorn.unicorn import Uc
from configparser import ConfigParser
from logging import Logger
from .arch.arch import QlArch
from .os.os import QlOs
from .os.memory import QlMemoryManager
from .hw.hw import QlHwManager
from .loader.loader import QlLoader
from .const import QL_ARCH, QL_ENDIAN, QL_OS, QL_STOP, QL_VERBOSE, QL_OS_INTERPRETER, QL_OS_BAREMETAL
from .const import QL_ARCH, QL_ENDIAN, QL_OS, QL_STOP, QL_VERBOSE, QL_ARCH_INTERPRETER, QL_OS_BAREMETAL
from .exception import QlErrorFileNotFound, QlErrorArch, QlErrorOsType
from .host import QlHost
from .log import *
from .utils import *
from .core_struct import QlCoreStructs
from .core_hooks import QlCoreHooks
from .__version__ import __version__
# Mixin Pattern
class Qiling(QlCoreHooks, QlCoreStructs):
def __init__(
self,
argv: Sequence[str] = None,
rootfs: str = None,
rootfs: str = r'.',
env: MutableMapping[AnyStr, AnyStr] = {},
code: bytes = None,
ostype: Union[str, QL_OS] = None,
archtype: Union[str, QL_ARCH] = None,
verbose: QL_VERBOSE = QL_VERBOSE.DEFAULT,
profile: str = None,
console=True,
console: bool = True,
log_file=None,
log_override=None,
log_plain=False,
multithread = False,
log_plain: bool = False,
multithread: bool = False,
filter = None,
stop: QL_STOP = QL_STOP.NONE,
*,
endian: QL_ENDIAN = None,
endian: Optional[QL_ENDIAN] = None,
thumb: bool = False,
libcache: bool = False
):
@@ -62,11 +60,8 @@ class Qiling(QlCoreHooks, QlCoreStructs):
self._env = env
self._code = code
self._multithread = multithread
self._log_file_fd = None
self._log_filter = None
self._filter = filter
self._internal_exception = None
self._uc = None
self._stop_options = stop
##################################
@@ -105,10 +100,7 @@ class Qiling(QlCoreHooks, QlCoreStructs):
################
# rootfs setup #
################
if rootfs is None:
rootfs = '.'
elif not os.path.exists(rootfs):
if not os.path.exists(rootfs):
raise QlErrorFileNotFound(f'Target rootfs not found: "{rootfs}"')
self._rootfs = rootfs
@@ -122,7 +114,7 @@ class Qiling(QlCoreHooks, QlCoreStructs):
archtype = arch_convert(archtype)
if type(ostype) is str:
ostype = ostype_convert(ostype)
ostype = os_convert(ostype)
# if provided arch was invalid or not provided, guess arch and os
if archtype is None:
@@ -141,61 +133,58 @@ class Qiling(QlCoreHooks, QlCoreStructs):
ostype = arch_os_convert(archtype)
# arch should have been determined by now; fail if not
if archtype is None or not ql_is_valid_arch(archtype):
raise QlErrorArch(f'Uknown or unsupported architecture: "{archtype}"')
if type(archtype) is not QL_ARCH:
raise QlErrorArch(f'Unknown or unsupported architecture: "{archtype}"')
# os should have been determined by now; fail if not
if ostype is None or not ql_is_valid_ostype(ostype):
if type(ostype) is not QL_OS:
raise QlErrorOsType(f'Unknown or unsupported operating system: "{ostype}"')
# if endianess is still undetermined, set it to little-endian.
# this setting is ignored for architectures with predfined endianess
# this setting is ignored for architectures with predefined endianess
if endian is None:
endian = QL_ENDIAN.EL
# make sure args were canonicalized successfully
assert type(archtype) is QL_ARCH
assert type(ostype) is QL_OS
assert type(endian) is QL_ENDIAN
self._arch = arch_setup(archtype, endian, thumb, self)
self._ostype = ostype
self.uc = self.arch.uc
self._arch = select_arch(archtype, endian, thumb)(self)
# Once we finish setting up arch, we can init QlCoreStructs and QlCoreHooks
QlCoreStructs.__init__(self, self.arch.endian, self.arch.bits)
if not self.interpreter:
QlCoreStructs.__init__(self, self.arch.endian, self.arch.bits)
QlCoreHooks.__init__(self, self.uc)
##########
# Logger #
##########
self._log_file_fd, self._log_filter = ql_setup_logger(self, log_file, console, self._filter, log_override, log_plain)
self._log_file_fd = setup_logger(self, log_file, console, log_override, log_plain)
self.filter = filter
self.verbose = verbose
###########
# Profile #
###########
self._profile = profile_setup(self, self.ostype, profile)
self.log.debug(f'Profile: {profile or "default"}')
self._profile = profile_setup(ostype, profile)
##########
# Loader #
##########
self._loader = loader_setup(self, self.ostype, libcache)
self._loader = select_loader(ostype, libcache)(self)
##############
# Components #
##############
if not self.interpreter:
self._mem = component_setup("os", "memory", self)
self._os = os_setup(self.ostype, self)
self._mem = select_component('os', 'memory')(self)
self._os = select_os(ostype)(self)
if self.baremetal:
self._hw = select_component('hw', 'hw')(self)
# Run the loader
self.loader.run()
self._init_stop_guard()
self._init_stop_guard()
#####################
# Qiling Components #
@@ -243,7 +232,7 @@ class Qiling(QlCoreHooks, QlCoreStructs):
return self._os
@property
def log(self) -> logging.Logger:
def log(self) -> "Logger":
""" Returns the logger this Qiling instance uses.
You can override this log by passing `log_override=your_log` to Qiling.__init__
@@ -271,7 +260,7 @@ class Qiling(QlCoreHooks, QlCoreStructs):
return self._multithread
@property
def profile(self) -> ConfigParser:
def profile(self) -> "ConfigParser":
""" Program profile. See qiling/profiles/*.ql for details.
Note: Please pass None or the path string to Qiling.__init__.
@@ -307,25 +296,6 @@ class Qiling(QlCoreHooks, QlCoreStructs):
"""
return self._env
@property
def ostype(self) -> QL_OS:
""" The emulated os type.
Note: Please pass None or one of the strings below to Qiling.__init__.
If you use shellcode, you must specify ostype and archtype manually.
Type: int.
Values:
- "macos" : macOS.
- "darwin" : an alias to "macos".
- "freebsd" : FreeBSD
- "windows" : Windows
- "uefi" : UEFI
- "dos" : DOS
Example: Qiling(code=b"\x90", ostype="macos", archtype="x8664")
"""
return self._ostype
@property
def code(self) -> bytes:
""" The shellcode to execute.
@@ -361,7 +331,7 @@ class Qiling(QlCoreHooks, QlCoreStructs):
Type: bool
"""
return self.ostype in QL_OS_INTERPRETER
return self.arch.type in QL_ARCH_INTERPRETER
@property
def baremetal(self) -> bool:
@@ -370,7 +340,12 @@ class Qiling(QlCoreHooks, QlCoreStructs):
Type: bool
"""
return self.ostype in QL_OS_BAREMETAL
# os is not initialized for interpreter archs
if self.interpreter:
return False
return self.os.type in QL_OS_BAREMETAL
@property
def host(self) -> QlHost:
@@ -394,8 +369,8 @@ class Qiling(QlCoreHooks, QlCoreStructs):
Values:
`QL_VERBOSE.DISABLED`: turn off logging
`QL_VERBOSE.OFF` : mask off anything below warnings, errors and critical severity
`QL_VERBOSE.DEFAULT` : info logging level: default verbosity
`QL_VERBOSE.DEBUG` : debug logging level: higher verbosity
`QL_VERBOSE.DEFAULT` : info logging level; default verbosity
`QL_VERBOSE.DEBUG` : debug logging level; higher verbosity
`QL_VERBOSE.DISASM` : debug verbosity along with disassembly trace (slow!)
`QL_VERBOSE.DUMP` : disassembly trace along with cpu context dump
"""
@@ -405,7 +380,7 @@ class Qiling(QlCoreHooks, QlCoreStructs):
def verbose(self, v: QL_VERBOSE):
self._verbose = v
self.log.setLevel(ql_resolve_logger_level(v))
self.log.setLevel(resolve_logger_level(v))
self.arch.utils.setup_output(v)
@property
@@ -436,8 +411,8 @@ class Qiling(QlCoreHooks, QlCoreStructs):
return self._debug_stop
@debug_stop.setter
def debug_stop(self, ds):
self._debug_stop = ds
def debug_stop(self, enabled: bool):
self._debug_stop = enabled
@property
def debugger(self) -> bool:
@@ -466,28 +441,32 @@ class Qiling(QlCoreHooks, QlCoreStructs):
Example: - Qiling(filter=r'^exit')
- ql.filter = r'^open'
"""
return self._filter
lf = self._log_filter
return '' if lf is None else lf._filter.pattern
@filter.setter
def filter(self, ft):
self._filter = ft
if self._log_filter is None:
self._log_filter = RegexFilter(ft)
self.log.addFilter(self._log_filter)
def filter(self, regex: Optional[str]):
if regex is None:
if self._log_filter is not None:
self.log.removeFilter(self._log_filter)
else:
self._log_filter.update_filter(ft)
if self._log_filter is None:
self._log_filter = RegexFilter(regex)
self.log.addFilter(self._log_filter)
self._log_filter.update_filter(regex)
@property
def uc(self) -> Uc:
def uc(self) -> 'Uc':
""" Raw uc instance.
Type: Ucgit
Type: Uc
"""
return self._uc
@uc.setter
def uc(self, u):
self._uc = u
return self.arch.uc
@property
def stop_options(self) -> QL_STOP:
@@ -558,20 +537,27 @@ class Qiling(QlCoreHooks, QlCoreStructs):
# Qiling APIS #
###############
# Emulate the binary from begin until @end, with timeout in @timeout and
# number of emulated instructions in @count
def run(self, begin=None, end=None, timeout=0, count=0, code=None):
def run(self, begin: Optional[int] = None, end: Optional[int] = None, timeout: int = 0, count: int = 0):
"""Start binary emulation.
Args:
begin : emulation starting address
end : emulation ending address
timeout : limit emulation to a specific amount of time (microseconds); unlimited by default
count : limit emulation to a specific amount of instructions; unlimited by default
"""
# replace the original entry point, exit point, timeout and count
self.entry_point = begin
self.exit_point = end
self.timeout = timeout
self.count = count
if self.interpreter:
return self.arch.run(code)
self.count = count
# init debugger (if set)
debugger = debugger_setup(self._debugger, self)
debugger = select_debugger(self._debugger)
if debugger:
debugger = debugger(self)
# patch binary
self.do_bin_patch()
+357 -76
View File
@@ -8,7 +8,8 @@
# handling hooks #
##############################################
from typing import Callable, MutableMapping, MutableSequence
from typing import Any, Callable, MutableMapping, MutableSequence, Protocol
from typing import TYPE_CHECKING
from unicorn import Uc
from unicorn.unicorn_const import *
@@ -18,6 +19,73 @@ from .utils import catch_KeyboardInterrupt
from .const import QL_HOOK_BLOCK
from .exception import QlErrorCoreHook
if TYPE_CHECKING:
from qiling import Qiling
class MemHookCallback(Protocol):
def __call__(self, __ql: 'Qiling', __access: int, __address: int, __size: int, __value: int, *__context: Any) -> Any:
"""Memory access hook callback.
Args:
__ql : the associated qiling instance
__access : the intercepted memory access type, one of UC_HOOK_MEM_* constants
__addr : the target memory location
__size : size of intercepted memory access
__value : the value to write, for write operations, 0 for others
__context : additional context passed on hook creation. if no context was passed, this argument should be omitted
Returns:
an integer with `QL_HOOK_BLOCK` mask set to block execution of remaining hooks
(if any) or `None`
"""
pass
class TraceHookCalback(Protocol):
def __call__(self, __ql: 'Qiling', __address: int, __size: int, *__context: Any) -> Any:
"""Execution hook callback.
Args:
__ql : the associated qiling instance
__address : address of the instruction to be executed
__size : instruction size
__context : additional context passed on hook creation. if no context was passed, this argument should be omitted
Returns:
an integer with `QL_HOOK_BLOCK` mask set to block execution of remaining hooks
(if any) or `None`
"""
pass
class AddressHookCallback(Protocol):
def __call__(self, __ql: 'Qiling', *__context: Any) -> Any:
"""Address hook callback.
Args:
__ql : the associated qiling instance
__context : additional context passed on hook creation. if no context was passed, this argument should be omitted
Returns:
an integer with `QL_HOOK_BLOCK` mask set to block execution of remaining hooks
(if any) or `None`
"""
pass
class InterruptHookCallback(Protocol):
def __call__(self, __ql: 'Qiling', intno: int, *__context: Any) -> Any:
"""Interrupt hook callback.
Args:
__ql : the associated qiling instance
__intno : the intercepted interrupt number
__context : additional context passed on hook creation. if no context was passed, this argument should be omitted
Returns:
an integer with `QL_HOOK_BLOCK` mask set to block execution of remaining hooks
(if any) or `None`
"""
pass
# Don't assume self is Qiling.
class QlCoreHooks:
def __init__(self, uc: Uc):
@@ -37,6 +105,9 @@ class QlCoreHooks:
# Callback definitions #
########################
def _hook_intr_cb(self, uc: Uc, intno: int, pack_data) -> None:
"""Interrupt hooks dispatcher.
"""
ql, hook_type = pack_data
handled = False
@@ -60,15 +131,18 @@ class QlCoreHooks:
def _hook_insn_cb(self, uc: Uc, *args):
ql, hook_type = args[-1]
"""Instruction hooks dispatcher.
"""
*hook_args, (ql, insn_type) = args
retval = None
if hook_type in self._insn_hook:
hooks_list = self._insn_hook[hook_type]
if insn_type in self._insn_hook:
hooks_list = self._insn_hook[insn_type]
for hook in hooks_list:
if hook.bound_check(ql.arch.regs.arch_pc):
ret = hook.call(ql, *args[:-1])
ret = hook.call(ql, *hook_args)
if type(ret) is tuple:
ret, retval = ret
@@ -81,13 +155,16 @@ class QlCoreHooks:
def _hook_trace_cb(self, uc: Uc, addr: int, size: int, pack_data) -> None:
"""Code and block hooks dispatcher.
"""
ql, hook_type = pack_data
if hook_type in self._hook:
hooks_list = self._hook[hook_type]
for hook in hooks_list:
if hook.bound_check(ql.arch.regs.arch_pc):
if hook.bound_check(addr, size):
ret = hook.call(ql, addr, size)
if type(ret) is int and ret & QL_HOOK_BLOCK:
@@ -95,6 +172,9 @@ class QlCoreHooks:
def _hook_mem_cb(self, uc: Uc, access: int, addr: int, size: int, value: int, pack_data):
"""Memory access hooks dispatcher.
"""
ql, hook_type = pack_data
handled = False
@@ -116,6 +196,9 @@ class QlCoreHooks:
def _hook_insn_invalid_cb(self, uc: Uc, pack_data) -> None:
"""Invalid instruction hooks dispatcher.
"""
ql, hook_type = pack_data
handled = False
@@ -134,6 +217,9 @@ class QlCoreHooks:
def _hook_addr_cb(self, uc: Uc, addr: int, size: int, pack_data):
"""Address hooks dispatcher.
"""
ql = pack_data
if addr in self._addr_hook:
@@ -148,15 +234,15 @@ class QlCoreHooks:
###############
# Class Hooks #
###############
def _ql_hook_internal(self, hook_type, callback, user_data=None, *args) -> int:
_callback = (catch_KeyboardInterrupt(self))(callback)
# pack user_data & callback for wrapper _callback
return self._h_uc.hook_add(hook_type, _callback, (self, user_data), 1, 0, *args)
def _ql_hook_internal(self, hook_type: int, callback: Callable, context: Any, *args) -> int:
_callback = catch_KeyboardInterrupt(self, callback)
return self._h_uc.hook_add(hook_type, _callback, (self, context), 1, 0, *args)
def _ql_hook_addr_internal(self, callback: Callable, address: int) -> int:
_callback = (catch_KeyboardInterrupt(self))(callback)
# pack user_data & callback for wrapper _callback
_callback = catch_KeyboardInterrupt(self, callback)
return self._h_uc.hook_add(UC_HOOK_CODE, _callback, self, address, address)
@@ -175,7 +261,7 @@ class QlCoreHooks:
ins_t = args[0]
if ins_t not in self._insn_hook_fuc:
self._insn_hook_fuc[ins_t] = self._ql_hook_internal(t, self._hook_insn_cb, ins_t, *args)
self._insn_hook_fuc[ins_t] = self._ql_hook_internal(t, self._hook_insn_cb, ins_t, ins_t)
if ins_t not in self._insn_hook:
self._insn_hook[ins_t] = []
@@ -232,60 +318,203 @@ class QlCoreHooks:
handler(t)
def ql_hook(self, hook_type: int, callback: Callable, user_data=None, begin=1, end=0, *args) -> HookRet:
def ql_hook(self, hook_type: int, callback: Callable, user_data: Any = None, begin: int = 1, end: int = 0, *args) -> HookRet:
"""Intercept certain emulation events within a specified range.
Args:
hook_type : event type to intercept; this argument is used as a bitmap and may encode multiple
events to hook with the same calback. see UC_HOOK_* constants for available events
callback : a method to call upon interception; callback signature may vary
depending on the hooked event type
user_data : an additional context to pass to callback (default: `None`)
begin : start of memory range to watch
end : end of memory range to watch
Notes:
If `begin` and `end` are not specified, the entire memory space will be watched.
Returns:
Hook handle
"""
hook = Hook(callback, user_data, begin, end)
self._ql_hook(hook_type, hook, *args)
return HookRet(self, hook_type, hook)
def hook_code(self, callback, user_data=None, begin=1, end=0):
if self.interpreter:
from .arch.evm.hooks import ql_evm_hooks
return ql_evm_hooks(self, 'HOOK_CODE', callback, user_data, begin, end)
def hook_code(self, callback: TraceHookCalback, user_data: Any = None, begin: int = 1, end: int = 0) -> HookRet:
"""Intercept assembly instructions before they get executed.
Args:
callback : a method to call upon interception
user_data : an additional context to pass to callback (default: `None`)
begin : start of memory range to watch
end : end of memory range to watch
Notes:
If `begin` and `end` are not specified, the entire memory space will be watched.
Returns:
Hook handle
"""
return self.ql_hook(UC_HOOK_CODE, callback, user_data, begin, end)
# TODO: remove; this is a special case of hook_intno(-1)
def hook_intr(self, callback, user_data=None, begin=1, end=0):
return self.ql_hook(UC_HOOK_INTR, callback, user_data, begin, end)
def hook_block(self, callback, user_data=None, begin=1, end=0):
def hook_block(self, callback: TraceHookCalback, user_data: Any = None, begin: int = 1, end: int = 0) -> HookRet:
"""Intercept landings in new basic blocks in a specified range.
Args:
callback : a method to call upon interception
user_data : an additional context to pass to callback (default: `None`)
begin : start of memory range to watch
end : end of memory range to watch
Notes:
If `begin` and `end` are not specified, the entire memory space will be watched.
Returns:
Hook handle
"""
return self.ql_hook(UC_HOOK_BLOCK, callback, user_data, begin, end)
def hook_mem_unmapped(self, callback, user_data=None, begin=1, end=0):
def hook_mem_unmapped(self, callback: MemHookCallback, user_data: Any = None, begin: int = 1, end: int = 0) -> HookRet:
"""Intercept illegal accesses to unmapped memory in a specified range.
Args:
callback : a method to call upon interception
user_data : an additional context to pass to callback (default: `None`)
begin : start of memory range to watch
end : end of memory range to watch
Notes:
If `begin` and `end` are not specified, the entire memory space will be watched.
Returns:
Hook handle
"""
return self.ql_hook(UC_HOOK_MEM_UNMAPPED, callback, user_data, begin, end)
def hook_mem_read_invalid(self, callback, user_data=None, begin=1, end=0):
def hook_mem_read_invalid(self, callback: MemHookCallback, user_data: Any = None, begin: int = 1, end: int = 0) -> HookRet:
"""Intercept illegal reading attempts from a specified range.
Args:
callback : a method to call upon interception
user_data : an additional context to pass to callback (default: `None`)
begin : start of memory range to watch
end : end of memory range to watch
Notes:
If `begin` and `end` are not specified, the entire memory space will be watched.
Returns:
Hook handle
"""
return self.ql_hook(UC_HOOK_MEM_READ_INVALID, callback, user_data, begin, end)
def hook_mem_write_invalid(self, callback, user_data=None, begin=1, end=0):
def hook_mem_write_invalid(self, callback: MemHookCallback, user_data: Any = None, begin: int = 1, end: int = 0) -> HookRet:
"""Intercept illegal writing attempts to a specified range.
Args:
callback : a method to call upon interception
user_data : an additional context to pass to callback (default: `None`)
begin : start of memory range to watch
end : end of memory range to watch
Notes:
If `begin` and `end` are not specified, the entire memory space will be watched.
Returns:
Hook handle
"""
return self.ql_hook(UC_HOOK_MEM_WRITE_INVALID, callback, user_data, begin, end)
def hook_mem_fetch_invalid(self, callback, user_data=None, begin=1, end=0):
def hook_mem_fetch_invalid(self, callback: MemHookCallback, user_data: Any = None, begin: int = 1, end: int = 0) -> HookRet:
"""Intercept illegal code fetching attempts from a specified range.
Args:
callback : a method to call upon interception
user_data : an additional context to pass to callback (default: `None`)
begin : start of memory range to watch
end : end of memory range to watch
Notes:
If `begin` and `end` are not specified, the entire memory space will be watched.
Returns:
Hook handle
"""
return self.ql_hook(UC_HOOK_MEM_FETCH_INVALID, callback, user_data, begin, end)
def hook_mem_valid(self, callback, user_data=None, begin=1, end=0):
def hook_mem_valid(self, callback: MemHookCallback, user_data: Any = None, begin: int = 1, end: int = 0) -> HookRet:
"""Intercept benign memory accesses within a specified range.
This is equivalent to hooking memory reads, writes and fetches.
Args:
callback : a method to call upon interception
user_data : an additional context to pass to callback (default: `None`)
begin : start of memory range to watch
end : end of memory range to watch
Notes:
If `begin` and `end` are not specified, the entire memory space will be watched.
Returns:
Hook handle
"""
return self.ql_hook(UC_HOOK_MEM_VALID, callback, user_data, begin, end)
def hook_mem_invalid(self, callback, user_data=None, begin=1, end=0):
def hook_mem_invalid(self, callback: MemHookCallback, user_data: Any = None, begin: int = 1, end: int = 0) -> HookRet:
"""Intercept invalid memory accesses within a specified range.
This is equivalent to hooking invalid memory reads, writes and fetches.
Args:
callback : a method to call upon interception
user_data : an additional context to pass to callback (default: `None`)
begin : start of memory range to watch
end : end of memory range to watch
Notes:
If `begin` and `end` are not specified, the entire memory space will be watched.
Returns:
Hook handle
"""
return self.ql_hook(UC_HOOK_MEM_INVALID, callback, user_data, begin, end)
# a convenient API to set callback for a single address
def hook_address(self, callback, address, user_data=None):
hook = HookAddr(callback, address, user_data)
def hook_address(self, callback: AddressHookCallback, address: int, user_data: Any = None) -> HookRet:
"""Intercept execution from a certain memory address.
if self.interpreter:
from .arch.evm.hooks import evm_hook_address
return evm_hook_address(self, 'HOOK_ADDR', hook, address)
Args:
callback : a method to call upon interception
address : memory location to watch
user_data : an additional context to pass to callback (default: `None`)
Returns:
Hook handle
"""
hook = HookAddr(callback, address, user_data)
if address not in self._addr_hook_fuc:
self._addr_hook_fuc[address] = self._ql_hook_addr_internal(self._hook_addr_cb, address)
@@ -295,80 +524,132 @@ class QlCoreHooks:
self._addr_hook[address].append(hook)
return HookRet(self, None, hook)
# note: assuming 0 is not a valid hook type
return HookRet(self, 0, hook)
def get_hook_address(self, address):
return self._addr_hook.get(address, [])
def hook_intno(self, callback: InterruptHookCallback, intno: int, user_data: Any = None) -> HookRet:
"""Intercept interrupts.
Args:
callback : a method to call upon interception
intono : interrupt vector number to intercept, or -1 for any
user_data : an additional context to pass to callback (default: `None`)
Returns:
Hook handle
"""
def hook_intno(self, callback, intno, user_data=None):
hook = HookIntr(callback, intno, user_data)
self._ql_hook(UC_HOOK_INTR, hook)
return HookRet(self, UC_HOOK_INTR, hook)
def hook_mem_read(self, callback, user_data=None, begin=1, end=0):
def hook_mem_read(self, callback: MemHookCallback, user_data: Any = None, begin: int = 1, end: int = 0) -> HookRet:
"""Intercept benign memory reads from a specified range.
Args:
callback : a method to call upon interception
user_data : an additional context to pass to callback (default: `None`)
begin : start of memory range to watch
end : end of memory range to watch
Notes:
If `begin` and `end` are not specified, the entire memory space will be watched.
Returns:
Hook handle
"""
return self.ql_hook(UC_HOOK_MEM_READ, callback, user_data, begin, end)
def hook_mem_write(self, callback, user_data=None, begin=1, end=0):
def hook_mem_write(self, callback: MemHookCallback, user_data: Any = None, begin: int = 1, end: int = 0) -> HookRet:
"""Intercept benign memory writes to a specified range.
Args:
callback : a method to call upon interception
user_data : an additional context to pass to callback (default: `None`)
begin : start of memory range to watch
end : end of memory range to watch
Notes:
If `begin` and `end` are not specified, the entire memory space will be watched.
Returns:
Hook handle
"""
return self.ql_hook(UC_HOOK_MEM_WRITE, callback, user_data, begin, end)
def hook_mem_fetch(self, callback, user_data=None, begin=1, end=0):
def hook_mem_fetch(self, callback: MemHookCallback, user_data: Any = None, begin: int = 1, end: int = 0) -> HookRet:
"""Intercept benign code fetches from a specified range.
Args:
callback : a method to call upon interception
user_data : an additional context to pass to callback (default: `None`)
begin : start of memory range to watch
end : end of memory range to watch
Notes:
If `begin` and `end` are not specified, the entire memory space will be watched.
Returns:
Hook handle
"""
return self.ql_hook(UC_HOOK_MEM_FETCH, callback, user_data, begin, end)
def hook_insn(self, callback, arg1, user_data=None, begin=1, end=0):
if self.interpreter:
from .arch.evm.hooks import evm_hook_insn
return evm_hook_insn(self, 'HOOK_INSN', callback, arg1, user_data, begin, end)
def hook_insn(self, callback, insn_type: int, user_data: Any = None, begin: int = 1, end: int = 0) -> HookRet:
"""Intercept execution of a certain instruction type within a specified range.
return self.ql_hook(UC_HOOK_INSN, callback, user_data, begin, end, arg1)
Args:
callback : a method to call upon interception; the callback arguments list differs
based on the instruction type
insn_type : instruction type to intercept
user_data : an additional context to pass to callback (default: `None`)
begin : start of memory range to watch
end : end of memory range to watch
Notes:
- The set of supported instruction types is very limited and defined by unicorn.
- If `begin` and `end` are not specified, the entire memory space will be watched.
Returns:
Hook handle
"""
return self.ql_hook(UC_HOOK_INSN, callback, user_data, begin, end, insn_type)
def hook_del(self, *args):
if len(args) != 1 and len(args) != 2:
return
def hook_del(self, hret: HookRet) -> None:
"""Unregister an existing hook and release its resources.
if isinstance(args[0], HookRet):
args[0].remove()
return
Args:
hret : hook handle
"""
hook_type, h = args
h = hret.obj
hook_type = hret.type
if self.interpreter:
from .arch.evm.hooks import evm_hook_del
return evm_hook_del(hook_type, h)
def __remove(hooks_map, handles_map, key: int) -> None:
if key in hooks_map:
hooks_list = hooks_map[key]
def __handle_common(t: int) -> None:
if t in self._hook:
if h in self._hook[t]:
del self._hook[t][self._hook[t].index(h)]
if h in hooks_list:
hooks_list.remove(h)
if len(self._hook[t]) == 0:
self._h_uc.hook_del(self._hook_fuc[t])
del self._hook_fuc[t]
if not hooks_list:
uc_handle = handles_map.pop(key)
def __handle_insn(t: int) -> None:
if t in self._insn_hook:
if h in self._insn_hook[t]:
del self._insn_hook[t][self._insn_hook[t].index(h)]
self._h_uc.hook_del(uc_handle)
if len(self._insn_hook[t]) == 0:
self._h_uc.hook_del(self._insn_hook_fuc[t])
del self._insn_hook_fuc[t]
def __handle_addr(t: int) -> None:
if t in self._addr_hook:
if h in self._addr_hook[t]:
del self._addr_hook[t][self._addr_hook[t].index(h)]
if len(self._addr_hook[t]) == 0:
self._h_uc.hook_del(self._addr_hook_fuc[t])
del self._addr_hook_fuc[t]
__handle_common = lambda k: __remove(self._hook, self._hook_fuc, k)
__handle_insn = lambda i: __remove(self._insn_hook, self._insn_hook_fuc, i)
__handle_addr = lambda a: __remove(self._addr_hook, self._addr_hook_fuc, a)
type_handlers = (
(UC_HOOK_INTR, __handle_common),
+6 -6
View File
@@ -46,11 +46,11 @@ class HookIntr(Hook):
class HookRet:
def __init__(self, ql, t, h):
self._ql = ql
self._t = t
self._h = h
def __init__(self, ql, hook_type: int, hook_obj: Hook):
self.type = hook_type
self.obj = hook_obj
self.__remove = ql.hook_del
def remove(self):
self._ql.hook_del(self._t, self._h)
def remove(self) -> None:
self.__remove(self)
+20 -18
View File
@@ -10,10 +10,13 @@
##############################################
import struct
from typing import Union
from .const import QL_ENDIAN
from .exception import QlErrorStructConversion
ReadableBuffer = Union[bytes, bytearray, memoryview]
# Don't assume self is Qiling.
class QlCoreStructs:
def __init__(self, endian: QL_ENDIAN, bit: int):
@@ -30,12 +33,11 @@ class QlCoreStructs:
self._fmt32s = f'{modifier}i'
self._fmt64 = f'{modifier}Q'
self._fmt64s = f'{modifier}q'
handlers = {
64 : (self.pack64, self.pack64s, self.unpack64, self.unpack64s),
32 : (self.pack32, self.pack32s, self.unpack32, self.unpack32s),
16 : (self.pack16, self.pack16s, self.unpack16, self.unpack16s),
1 : ( None, None, None, None)
}
if bit not in handlers:
@@ -48,50 +50,50 @@ class QlCoreStructs:
self.unpack = up
self.unpacks = ups
def pack64(self, x):
def pack64(self, x: int, /) -> bytes:
return struct.pack(self._fmt64, x)
def pack64s(self, x):
def pack64s(self, x: int, /) -> bytes:
return struct.pack(self._fmt64s, x)
def unpack64(self, x):
def unpack64(self, x: ReadableBuffer, /) -> int:
return struct.unpack(self._fmt64, x)[0]
def unpack64s(self, x):
def unpack64s(self, x: ReadableBuffer, /) -> int:
return struct.unpack(self._fmt64s, x)[0]
def pack32(self, x):
def pack32(self, x: int, /) -> bytes:
return struct.pack(self._fmt32, x)
def pack32s(self, x):
def pack32s(self, x: int, /) -> bytes:
return struct.pack(self._fmt32s, x)
def unpack32(self, x):
def unpack32(self, x: ReadableBuffer, /) -> int:
return struct.unpack(self._fmt32, x)[0]
def unpack32s(self, x):
def unpack32s(self, x: ReadableBuffer, /) -> int:
return struct.unpack(self._fmt32s, x)[0]
def pack16(self, x):
def pack16(self, x: int, /) -> bytes:
return struct.pack(self._fmt16, x)
def pack16s(self, x):
def pack16s(self, x: int, /) -> bytes:
return struct.pack(self._fmt16s, x)
def unpack16(self, x):
def unpack16(self, x: ReadableBuffer, /) -> int:
return struct.unpack(self._fmt16, x)[0]
def unpack16s(self, x):
def unpack16s(self, x: ReadableBuffer, /) -> int:
return struct.unpack(self._fmt16s, x)[0]
def pack8(self, x):
def pack8(self, x: int, /) -> bytes:
return struct.pack(self._fmt8, x)
def pack8s(self, x):
def pack8s(self, x: int, /) -> bytes:
return struct.pack(self._fmt8s, x)
def unpack8(self, x):
def unpack8(self, x: ReadableBuffer, /) -> int:
return struct.unpack(self._fmt8, x)[0]
def unpack8s(self, x):
def unpack8s(self, x: ReadableBuffer, /) -> int:
return struct.unpack(self._fmt8s, x)[0]
+1 -1
View File
@@ -17,7 +17,7 @@ class QlDisassember():
def disasm_all_lines(self):
disasm_result = []
if self.ql.ostype == QL_OS.LINUX:
if self.ql.os.type == QL_OS.LINUX:
disasm_result = self.disasm_elf()
return disasm_result
+904 -751
View File
File diff suppressed because it is too large Load Diff
+37 -78
View File
@@ -3,117 +3,76 @@
# Cross Platform and Multi Architecture Advanced Binary Emulation Framework
#
from typing import Optional
from qiling import Qiling
from qiling.const import QL_ARCH
# this code is partially based on uDbg
# @see: https://github.com/iGio90/uDdbg
PROMPT = r'gdb>'
class QlGdbUtils:
def __init__(self):
self.ql: Qiling
self.current_address = 0x0
self.current_address_size = 0x0
self.last_bp = 0x0
self.entry_point = None
self.exit_point = None
self.soft_bp = False
self.has_soft_bp = False
self.bp_list = []
# self.mapping = []
self.breakpoint_count = 0x0
self.skip_bp_count = 0x0
def initialize(self, ql: Qiling, hook_address: int, exit_point: int, mappings=[]):
def __init__(self, ql: Qiling, entry_point: int, exit_point: int):
self.ql = ql
if ql.baremetal:
self.current_address = self.entry_point
else:
self.current_address = self.entry_point = ql.os.entry_point
self.exit_point = exit_point
# self.mapping = mappings
self.bp_list = []
self.last_bp = None
def __entry_point_hook(ql: Qiling):
ql.hook_del(ep_hret)
ql.hook_code(self.dbg_hook)
ql.log.info(f'{PROMPT} stopped at entry point: {ql.arch.regs.arch_pc:#x}')
ql.stop()
ql.log.info(f'{PROMPT} Stop at entry point: {ql.arch.regs.arch_pc:#x}')
ep_hret = ql.hook_address(__entry_point_hook, hook_address)
# set a one-time hook to be dispatched upon reaching program entry point.
# that hook will be used to set up the breakpoint handling hook
ep_hret = ql.hook_address(__entry_point_hook, entry_point)
def dbg_hook(self, ql: Qiling, address: int, size: int):
"""Modified this function for qiling.gdbserver by kabeor from https://github.com/iGio90/uDdbg
"""
if ql.arch.type == QL_ARCH.ARM and ql.arch.is_thumb:
address += 1
try:
if ql.arch.type == QL_ARCH.ARM:
if ql.arch.is_thumb:
address += 1
# resuming emulation after hitting a breakpoint will re-enter this hook.
# avoid an endless hooking loop by detecting and skipping this case
if address == self.last_bp:
self.last_bp = None
# self.mapping.append([(hex(address))])
self.current_address = address
hit_soft_bp = False
elif address in self.bp_list:
self.last_bp = address
if self.soft_bp == True:
self.soft_bp = False
hit_soft_bp = True
# Breakpoints are always added without the LSB, even in Thumb, so they should be checked like this as well
if ((address & ~1) in self.bp_list and (address & ~1) != self.last_bp) or self.has_soft_bp == True:
if self.skip_bp_count > 0:
self.skip_bp_count -= 1
else:
self.breakpoint_count += 1
ql.stop()
self.last_bp = address
ql.log.info(f'{PROMPT} Breakpoint found, stop at address: {address:#x}')
elif address == self.last_bp:
self.last_bp = 0x0
self.has_soft_bp = hit_soft_bp
if self.current_address + size == self.exit_point:
ql.log.debug(f'{PROMPT} emulation entrypoint at {self.entry_point:#x}')
ql.log.debug(f'{PROMPT} emulation exitpoint at {self.exit_point:#x}')
except KeyboardInterrupt:
ql.log.info(f'{PROMPT} Paused at {address:#x}, instruction size = {size:d}')
ql.log.info(f'{PROMPT} breakpoint hit, stopped at {address:#x}')
ql.stop()
except:
raise
# # TODO: not sure what this is about
# if address + size == self.exit_point:
# ql.log.debug(f'{PROMPT} emulation entrypoint at {self.entry_point:#x}')
# ql.log.debug(f'{PROMPT} emulation exitpoint at {self.exit_point:#x}')
def bp_insert(self, addr: int):
if addr not in self.bp_list:
self.bp_list.append(addr)
self.ql.log.info(f'{PROMPT} Breakpoint added at: {addr:#x}')
self.ql.log.info(f'{PROMPT} breakpoint added at {addr:#x}')
def bp_remove(self, addr: int, type = None, len = None):
def bp_remove(self, addr: int):
self.bp_list.remove(addr)
self.ql.log.info(f'{PROMPT} Breakpoint removed at: {addr:#x}')
self.ql.log.info(f'{PROMPT} breakpoint removed from {addr:#x}')
def resume_emu(self, address: int = None, skip_bp: int = 0):
"""Modified this function for qiling.gdbserver by kabeor from https://github.com/iGio90/uDdbg
"""
def resume_emu(self, address: Optional[int] = None, steps: int = 0):
if address is None:
address = self.ql.arch.regs.arch_pc
if address is not None:
if self.ql.arch.type == QL_ARCH.ARM:
if self.ql.arch.is_thumb:
address += 1
if self.ql.arch.type == QL_ARCH.ARM and self.ql.arch.is_thumb:
address += 1
self.current_address = address
op = f'stepping {steps} instructions' if steps else 'resuming'
self.ql.log.info(f'{PROMPT} {op} from {address:#x}')
self.skip_bp_count = skip_bp
if self.exit_point is not None:
self.ql.log.info(f'{PROMPT} Resume at: {self.current_address:#x}')
self.ql.emu_start(self.current_address, self.exit_point)
self.ql.emu_start(address, self.exit_point, count=steps)
+6 -1
View File
@@ -1 +1,6 @@
<?xml version="1.0"?><!DOCTYPE target SYSTEM "gdb-target.dtd"><target><architecture>i8086</architecture><xi:include href="i386-32bit.xml"/></target>
<?xml version="1.0"?>
<!DOCTYPE target SYSTEM "gdb-target.dtd">
<target xmlns:xi="http://www.w3.org/2001/XInclude">
<architecture>i8086</architecture>
<xi:include href="i386-32bit.xml"/>
</target>
+24 -24
View File
@@ -1,31 +1,31 @@
<?xml version="1.0"?>
<!-- Copyright (C) 2007-2016 Free Software Foundation, Inc.
<!-- Copyright (C) 2007-2020 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. -->
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 feature SYSTEM "gdb-target.dtd">
<feature name="org.gnu.gdb.arm.core">
<reg name="r0" bitsize="32" type="uint32"/>
<reg name="r1" bitsize="32" type="uint32"/>
<reg name="r2" bitsize="32" type="uint32"/>
<reg name="r3" bitsize="32" type="uint32"/>
<reg name="r4" bitsize="32" type="uint32"/>
<reg name="r5" bitsize="32" type="uint32"/>
<reg name="r6" bitsize="32" type="uint32"/>
<reg name="r7" bitsize="32" type="uint32"/>
<reg name="r8" bitsize="32" type="uint32"/>
<reg name="r9" bitsize="32" type="uint32"/>
<reg name="r10" bitsize="32" type="uint32"/>
<reg name="r11" bitsize="32" type="uint32"/>
<reg name="r12" bitsize="32" type="uint32"/>
<reg name="sp" bitsize="32" type="data_ptr"/>
<reg name="lr" bitsize="32"/>
<reg name="pc" bitsize="32" type="code_ptr"/>
<reg name="r0" bitsize="32" type="uint32"/>
<reg name="r1" bitsize="32" type="uint32"/>
<reg name="r2" bitsize="32" type="uint32"/>
<reg name="r3" bitsize="32" type="uint32"/>
<reg name="r4" bitsize="32" type="uint32"/>
<reg name="r5" bitsize="32" type="uint32"/>
<reg name="r6" bitsize="32" type="uint32"/>
<reg name="r7" bitsize="32" type="uint32"/>
<reg name="r8" bitsize="32" type="uint32"/>
<reg name="r9" bitsize="32" type="uint32"/>
<reg name="r10" bitsize="32" type="uint32"/>
<reg name="r11" bitsize="32" type="uint32"/>
<reg name="r12" bitsize="32" type="uint32"/>
<reg name="sp" bitsize="32" type="data_ptr"/>
<reg name="lr" bitsize="32"/>
<reg name="pc" bitsize="32" type="code_ptr"/>
<!-- The CPSR is register 25, rather than register 16, because
*" the FPA registers historically were placed between the PC
*" and the CPSR in the "g" packet. -->
<reg name="cpsr" bitsize="32" regnum="25"/>
<!-- The CPSR is register 25, rather than register 16, because
the FPA registers historically were placed between the PC
and the CPSR in the "g" packet. -->
<reg name="cpsr" bitsize="32" regnum="25"/>
</feature>
@@ -1,39 +0,0 @@
<?xml version="1.0"?>
<!-- Copyright (C) 2019-2020 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 feature SYSTEM "gdb-target.dtd">
<feature name="org.gnu.gdb.arm.m-profile">
<reg name="r0" bitsize="32"/>
<reg name="r1" bitsize="32"/>
<reg name="r2" bitsize="32"/>
<reg name="r3" bitsize="32"/>
<reg name="r4" bitsize="32"/>
<reg name="r5" bitsize="32"/>
<reg name="r6" bitsize="32"/>
<reg name="r7" bitsize="32"/>
<reg name="r8" bitsize="32"/>
<reg name="r9" bitsize="32"/>
<reg name="r10" bitsize="32"/>
<reg name="r11" bitsize="32"/>
<reg name="r12" bitsize="32"/>
<reg name="sp" bitsize="32" type="data_ptr"/>
<reg name="lr" bitsize="32"/>
<reg name="pc" bitsize="32" type="code_ptr"/>
<!-- Slack for unused FPA registers (f0-f7 + fps). -->
<reg name="" bitsize="96" type="arm_fpa_ext" regnum="16"/>
<reg name="" bitsize="96" type="arm_fpa_ext"/>
<reg name="" bitsize="96" type="arm_fpa_ext"/>
<reg name="" bitsize="96" type="arm_fpa_ext"/>
<reg name="" bitsize="96" type="arm_fpa_ext"/>
<reg name="" bitsize="96" type="arm_fpa_ext"/>
<reg name="" bitsize="96" type="arm_fpa_ext"/>
<reg name="" bitsize="96" type="arm_fpa_ext"/>
<reg name="" bitsize="32"/>
<reg name="xpsr" bitsize="32" regnum="25"/>
</feature>
@@ -7,7 +7,7 @@
<!DOCTYPE feature SYSTEM "gdb-target.dtd">
<feature name="org.gnu.gdb.arm.m-profile">
<reg name="r0" bitsize="32"/>
<reg name="r0" bitsize="32" regnum="0"/>
<reg name="r1" bitsize="32"/>
<reg name="r2" bitsize="32"/>
<reg name="r3" bitsize="32"/>
@@ -23,5 +23,6 @@
<reg name="sp" bitsize="32" type="data_ptr"/>
<reg name="lr" bitsize="32"/>
<reg name="pc" bitsize="32" type="code_ptr"/>
<reg name="xpsr" bitsize="32" regnum="25"/>
</feature>
+6 -6
View File
@@ -6,9 +6,9 @@
*!notice and this notice are preserved. -->
<!DOCTYPE target SYSTEM "gdb-target.dtd">
<target>
<architecture>arm</architecture>
<xi:include href="arm-core.xml"/>
<xi:include href="arm-vfpv3.xml"/>
<feature name="org.gnu.gdb.arm.neon"/>
</target>
<target xmlns:xi="http://www.w3.org/2001/XInclude">
<architecture>arm</architecture>
<xi:include href="arm-core.xml"/>
<xi:include href="arm-vfpv3.xml"/>
<feature name="org.gnu.gdb.arm.neon"/>
</target>
+55 -55
View File
@@ -1,67 +1,67 @@
<?xml version="1.0"?>
<!-- Copyright (C) 2009-2016 Free Software Foundation, Inc.
*!Contributed by ARM Ltd.
Contributed by ARM Ltd.
*!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. -->
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 feature SYSTEM "gdb-target.dtd">
<feature name="org.gnu.gdb.aarch64.core">
<reg name="x0" bitsize="64"/>
<reg name="x1" bitsize="64"/>
<reg name="x2" bitsize="64"/>
<reg name="x3" bitsize="64"/>
<reg name="x4" bitsize="64"/>
<reg name="x5" bitsize="64"/>
<reg name="x6" bitsize="64"/>
<reg name="x7" bitsize="64"/>
<reg name="x8" bitsize="64"/>
<reg name="x9" bitsize="64"/>
<reg name="x10" bitsize="64"/>
<reg name="x11" bitsize="64"/>
<reg name="x12" bitsize="64"/>
<reg name="x13" bitsize="64"/>
<reg name="x14" bitsize="64"/>
<reg name="x15" bitsize="64"/>
<reg name="x16" bitsize="64"/>
<reg name="x17" bitsize="64"/>
<reg name="x18" bitsize="64"/>
<reg name="x19" bitsize="64"/>
<reg name="x20" bitsize="64"/>
<reg name="x21" bitsize="64"/>
<reg name="x22" bitsize="64"/>
<reg name="x23" bitsize="64"/>
<reg name="x24" bitsize="64"/>
<reg name="x25" bitsize="64"/>
<reg name="x26" bitsize="64"/>
<reg name="x27" bitsize="64"/>
<reg name="x28" bitsize="64"/>
<reg name="x29" bitsize="64"/>
<reg name="x30" bitsize="64"/>
<reg name="sp" bitsize="64" type="data_ptr"/>
<reg name="x0" bitsize="64"/>
<reg name="x1" bitsize="64"/>
<reg name="x2" bitsize="64"/>
<reg name="x3" bitsize="64"/>
<reg name="x4" bitsize="64"/>
<reg name="x5" bitsize="64"/>
<reg name="x6" bitsize="64"/>
<reg name="x7" bitsize="64"/>
<reg name="x8" bitsize="64"/>
<reg name="x9" bitsize="64"/>
<reg name="x10" bitsize="64"/>
<reg name="x11" bitsize="64"/>
<reg name="x12" bitsize="64"/>
<reg name="x13" bitsize="64"/>
<reg name="x14" bitsize="64"/>
<reg name="x15" bitsize="64"/>
<reg name="x16" bitsize="64"/>
<reg name="x17" bitsize="64"/>
<reg name="x18" bitsize="64"/>
<reg name="x19" bitsize="64"/>
<reg name="x20" bitsize="64"/>
<reg name="x21" bitsize="64"/>
<reg name="x22" bitsize="64"/>
<reg name="x23" bitsize="64"/>
<reg name="x24" bitsize="64"/>
<reg name="x25" bitsize="64"/>
<reg name="x26" bitsize="64"/>
<reg name="x27" bitsize="64"/>
<reg name="x28" bitsize="64"/>
<reg name="x29" bitsize="64"/>
<reg name="x30" bitsize="64"/>
<reg name="sp" bitsize="64" type="data_ptr"/>
<reg name="pc" bitsize="64" type="code_ptr"/>
<reg name="pc" bitsize="64" type="code_ptr"/>
<flags id="cpsr_flags" size="4">
* <field name="SP" start="0" end="0"/>
* <field name="" start="1" end="1"/>
* <field name="EL" start="2" end="3"/>
* <field name="nRW" start="4" end="4"/>
* <field name="" start="5" end="5"/>
* <field name="F" start="6" end="6"/>
* <field name="I" start="7" end="7"/>
* <field name="A" start="8" end="8"/>
* <field name="D" start="9" end="9"/>
<flags id="cpsr_flags" size="4">
<field name="SP" start="0" end="0"/>
<field name="" start="1" end="1"/>
<field name="EL" start="2" end="3"/>
<field name="nRW" start="4" end="4"/>
<field name="" start="5" end="5"/>
<field name="F" start="6" end="6"/>
<field name="I" start="7" end="7"/>
<field name="A" start="8" end="8"/>
<field name="D" start="9" end="9"/>
* <field name="IL" start="20" end="20"/>
* <field name="SS" start="21" end="21"/>
<field name="IL" start="20" end="20"/>
<field name="SS" start="21" end="21"/>
* <field name="V" start="28" end="28"/>
* <field name="C" start="29" end="29"/>
* <field name="Z" start="30" end="30"/>
* <field name="N" start="31" end="31"/>
</flags>
<reg name="cpsr" bitsize="32" type="cpsr_flags"/>
<field name="V" start="28" end="28"/>
<field name="C" start="29" end="29"/>
<field name="Z" start="30" end="30"/>
<field name="N" start="31" end="31"/>
</flags>
<reg name="cpsr" bitsize="32" type="cpsr_flags"/>
</feature>
+86 -79
View File
@@ -1,86 +1,93 @@
<?xml version="1.0"?>
<!-- Copyright (C) 2009-2016 Free Software Foundation, Inc.
*!Contributed by ARM Ltd.
Contributed by ARM Ltd.
*!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. -->
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 feature SYSTEM "gdb-target.dtd">
<feature name="org.gnu.gdb.aarch64.fpu">
<vector id="v2d" type="ieee_double" count="2"/>
<vector id="v2u" type="uint64" count="2"/>
<vector id="v2i" type="int64" count="2"/>
<vector id="v4f" type="ieee_single" count="4"/>
<vector id="v4u" type="uint32" count="4"/>
<vector id="v4i" type="int32" count="4"/>
<vector id="v8u" type="uint16" count="8"/>
<vector id="v8i" type="int16" count="8"/>
<vector id="v16u" type="uint8" count="16"/>
<vector id="v16i" type="int8" count="16"/>
<vector id="v1u" type="uint128" count="1"/>
<vector id="v1i" type="int128" count="1"/>
<union id="vnd">
* <field name="f" type="v2d"/>
* <field name="u" type="v2u"/>
* <field name="s" type="v2i"/>
</union>
<union id="vns">
* <field name="f" type="v4f"/>
* <field name="u" type="v4u"/>
* <field name="s" type="v4i"/>
</union>
<union id="vnh">
* <field name="u" type="v8u"/>
* <field name="s" type="v8i"/>
</union>
<union id="vnb">
* <field name="u" type="v16u"/>
* <field name="s" type="v16i"/>
</union>
<union id="vnq">
* <field name="u" type="v1u"/>
* <field name="s" type="v1i"/>
</union>
<union id="aarch64v">
* <field name="d" type="vnd"/>
* <field name="s" type="vns"/>
* <field name="h" type="vnh"/>
* <field name="b" type="vnb"/>
* <field name="q" type="vnq"/>
</union>
<reg name="v0" bitsize="128" type="aarch64v" regnum="34"/>
<reg name="v1" bitsize="128" type="aarch64v" />
<reg name="v2" bitsize="128" type="aarch64v" />
<reg name="v3" bitsize="128" type="aarch64v" />
<reg name="v4" bitsize="128" type="aarch64v" />
<reg name="v5" bitsize="128" type="aarch64v" />
<reg name="v6" bitsize="128" type="aarch64v" />
<reg name="v7" bitsize="128" type="aarch64v" />
<reg name="v8" bitsize="128" type="aarch64v" />
<reg name="v9" bitsize="128" type="aarch64v" />
<reg name="v10" bitsize="128" type="aarch64v"/>
<reg name="v11" bitsize="128" type="aarch64v"/>
<reg name="v12" bitsize="128" type="aarch64v"/>
<reg name="v13" bitsize="128" type="aarch64v"/>
<reg name="v14" bitsize="128" type="aarch64v"/>
<reg name="v15" bitsize="128" type="aarch64v"/>
<reg name="v16" bitsize="128" type="aarch64v"/>
<reg name="v17" bitsize="128" type="aarch64v"/>
<reg name="v18" bitsize="128" type="aarch64v"/>
<reg name="v19" bitsize="128" type="aarch64v"/>
<reg name="v20" bitsize="128" type="aarch64v"/>
<reg name="v21" bitsize="128" type="aarch64v"/>
<reg name="v22" bitsize="128" type="aarch64v"/>
<reg name="v23" bitsize="128" type="aarch64v"/>
<reg name="v24" bitsize="128" type="aarch64v"/>
<reg name="v25" bitsize="128" type="aarch64v"/>
<reg name="v26" bitsize="128" type="aarch64v"/>
<reg name="v27" bitsize="128" type="aarch64v"/>
<reg name="v28" bitsize="128" type="aarch64v"/>
<reg name="v29" bitsize="128" type="aarch64v"/>
<reg name="v30" bitsize="128" type="aarch64v"/>
<reg name="v31" bitsize="128" type="aarch64v"/>
<reg name="fpsr" bitsize="32"/>
<reg name="fpcr" bitsize="32"/>
<vector id="v2d" type="ieee_double" count="2"/>
<vector id="v2u" type="uint64" count="2"/>
<vector id="v2i" type="int64" count="2"/>
<vector id="v4f" type="ieee_single" count="4"/>
<vector id="v4u" type="uint32" count="4"/>
<vector id="v4i" type="int32" count="4"/>
<vector id="v8u" type="uint16" count="8"/>
<vector id="v8i" type="int16" count="8"/>
<vector id="v16u" type="uint8" count="16"/>
<vector id="v16i" type="int8" count="16"/>
<vector id="v1u" type="uint128" count="1"/>
<vector id="v1i" type="int128" count="1"/>
<union id="vnd">
<field name="f" type="v2d"/>
<field name="u" type="v2u"/>
<field name="s" type="v2i"/>
</union>
<union id="vns">
<field name="f" type="v4f"/>
<field name="u" type="v4u"/>
<field name="s" type="v4i"/>
</union>
<union id="vnh">
<field name="u" type="v8u"/>
<field name="s" type="v8i"/>
</union>
<union id="vnb">
<field name="u" type="v16u"/>
<field name="s" type="v16i"/>
</union>
<union id="vnq">
<field name="u" type="v1u"/>
<field name="s" type="v1i"/>
</union>
<union id="aarch64v">
<field name="d" type="vnd"/>
<field name="s" type="vns"/>
<field name="h" type="vnh"/>
<field name="b" type="vnb"/>
<field name="q" type="vnq"/>
</union>
<reg name="v0" bitsize="128" type="aarch64v" regnum="34"/>
<reg name="v1" bitsize="128" type="aarch64v" />
<reg name="v2" bitsize="128" type="aarch64v" />
<reg name="v3" bitsize="128" type="aarch64v" />
<reg name="v4" bitsize="128" type="aarch64v" />
<reg name="v5" bitsize="128" type="aarch64v" />
<reg name="v6" bitsize="128" type="aarch64v" />
<reg name="v7" bitsize="128" type="aarch64v" />
<reg name="v8" bitsize="128" type="aarch64v" />
<reg name="v9" bitsize="128" type="aarch64v" />
<reg name="v10" bitsize="128" type="aarch64v"/>
<reg name="v11" bitsize="128" type="aarch64v"/>
<reg name="v12" bitsize="128" type="aarch64v"/>
<reg name="v13" bitsize="128" type="aarch64v"/>
<reg name="v14" bitsize="128" type="aarch64v"/>
<reg name="v15" bitsize="128" type="aarch64v"/>
<reg name="v16" bitsize="128" type="aarch64v"/>
<reg name="v17" bitsize="128" type="aarch64v"/>
<reg name="v18" bitsize="128" type="aarch64v"/>
<reg name="v19" bitsize="128" type="aarch64v"/>
<reg name="v20" bitsize="128" type="aarch64v"/>
<reg name="v21" bitsize="128" type="aarch64v"/>
<reg name="v22" bitsize="128" type="aarch64v"/>
<reg name="v23" bitsize="128" type="aarch64v"/>
<reg name="v24" bitsize="128" type="aarch64v"/>
<reg name="v25" bitsize="128" type="aarch64v"/>
<reg name="v26" bitsize="128" type="aarch64v"/>
<reg name="v27" bitsize="128" type="aarch64v"/>
<reg name="v28" bitsize="128" type="aarch64v"/>
<reg name="v29" bitsize="128" type="aarch64v"/>
<reg name="v30" bitsize="128" type="aarch64v"/>
<reg name="v31" bitsize="128" type="aarch64v"/>
<reg name="fpsr" bitsize="32"/>
<reg name="fpcr" bitsize="32"/>
</feature>
+9 -8
View File
@@ -1,14 +1,15 @@
<?xml version="1.0"?>
<!-- Copyright (C) 2009-2016 Free Software Foundation, Inc.
*!Contributed by ARM Ltd.
Contributed by ARM Ltd.
*!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. -->
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>
<architecture>aarch64</architecture>
<xi:include href="aarch64-core.xml"/>
<xi:include href="aarch64-fpu.xml"/>
<target xmlns:xi="http://www.w3.org/2001/XInclude">
<architecture>aarch64</architecture>
<xi:include href="aarch64-core.xml"/>
<xi:include href="aarch64-fpu.xml"/>
</target>
+10 -9
View File
@@ -6,14 +6,15 @@
*!notice and this notice are preserved. -->
<!DOCTYPE target SYSTEM "gdb-target.dtd">
<target>
<architecture>mips</architecture>
<osabi>GNU/Linux</osabi>
<xi:include href="mips-cpu.xml"/>
<xi:include href="mips-cp0.xml"/>
<xi:include href="mips-fpu.xml"/>
<target xmlns:xi="http://www.w3.org/2001/XInclude">
<architecture>mips</architecture>
<osabi>GNU/Linux</osabi>
<feature name="org.gnu.gdb.mips.linux">
* <reg name="restart" bitsize="32" group="system"/>
</feature>
<xi:include href="mips-cpu.xml"/>
<xi:include href="mips-cp0.xml"/>
<xi:include href="mips-fpu.xml"/>
<feature name="org.gnu.gdb.mips.linux">
<reg name="restart" bitsize="32" group="system"/>
</feature>
</target>
+52 -52
View File
@@ -7,59 +7,59 @@
<!DOCTYPE feature SYSTEM "gdb-target.dtd">
<feature name="org.gnu.gdb.i386.core">
<flags id="i386_eflags" size="4">
<field name="CF" start="0" end="0"/>
<field name="" start="1" end="1"/>
<field name="PF" start="2" end="2"/>
<field name="AF" start="4" end="4"/>
<field name="ZF" start="6" end="6"/>
<field name="SF" start="7" end="7"/>
<field name="TF" start="8" end="8"/>
<field name="IF" start="9" end="9"/>
<field name="DF" start="10" end="10"/>
<field name="OF" start="11" end="11"/>
<field name="NT" start="14" end="14"/>
<field name="RF" start="16" end="16"/>
<field name="VM" start="17" end="17"/>
<field name="AC" start="18" end="18"/>
<field name="VIF" start="19" end="19"/>
<field name="VIP" start="20" end="20"/>
<field name="ID" start="21" end="21"/>
</flags>
<flags id="i386_eflags" size="4">
<field name="CF" start="0" end="0"/>
<field name="" start="1" end="1"/>
<field name="PF" start="2" end="2"/>
<field name="AF" start="4" end="4"/>
<field name="ZF" start="6" end="6"/>
<field name="SF" start="7" end="7"/>
<field name="TF" start="8" end="8"/>
<field name="IF" start="9" end="9"/>
<field name="DF" start="10" end="10"/>
<field name="OF" start="11" end="11"/>
<field name="NT" start="14" end="14"/>
<field name="RF" start="16" end="16"/>
<field name="VM" start="17" end="17"/>
<field name="AC" start="18" end="18"/>
<field name="VIF" start="19" end="19"/>
<field name="VIP" start="20" end="20"/>
<field name="ID" start="21" end="21"/>
</flags>
<reg name="eax" bitsize="32" type="int32"/>
<reg name="ecx" bitsize="32" type="int32"/>
<reg name="edx" bitsize="32" type="int32"/>
<reg name="ebx" bitsize="32" type="int32"/>
<reg name="esp" bitsize="32" type="data_ptr"/>
<reg name="ebp" bitsize="32" type="data_ptr"/>
<reg name="esi" bitsize="32" type="int32"/>
<reg name="edi" bitsize="32" type="int32"/>
<reg name="eax" bitsize="32" type="int32"/>
<reg name="ecx" bitsize="32" type="int32"/>
<reg name="edx" bitsize="32" type="int32"/>
<reg name="ebx" bitsize="32" type="int32"/>
<reg name="esp" bitsize="32" type="data_ptr"/>
<reg name="ebp" bitsize="32" type="data_ptr"/>
<reg name="esi" bitsize="32" type="int32"/>
<reg name="edi" bitsize="32" type="int32"/>
<reg name="eip" bitsize="32" type="code_ptr"/>
<reg name="eflags" bitsize="32" type="i386_eflags"/>
<reg name="cs" bitsize="32" type="int32"/>
<reg name="ss" bitsize="32" type="int32"/>
<reg name="ds" bitsize="32" type="int32"/>
<reg name="es" bitsize="32" type="int32"/>
<reg name="fs" bitsize="32" type="int32"/>
<reg name="gs" bitsize="32" type="int32"/>
<reg name="eip" bitsize="32" type="code_ptr"/>
<reg name="eflags" bitsize="32" type="i386_eflags"/>
<reg name="cs" bitsize="32" type="int32"/>
<reg name="ss" bitsize="32" type="int32"/>
<reg name="ds" bitsize="32" type="int32"/>
<reg name="es" bitsize="32" type="int32"/>
<reg name="fs" bitsize="32" type="int32"/>
<reg name="gs" bitsize="32" type="int32"/>
<reg name="st0" bitsize="80" type="i387_ext"/>
<reg name="st1" bitsize="80" type="i387_ext"/>
<reg name="st2" bitsize="80" type="i387_ext"/>
<reg name="st3" bitsize="80" type="i387_ext"/>
<reg name="st4" bitsize="80" type="i387_ext"/>
<reg name="st5" bitsize="80" type="i387_ext"/>
<reg name="st6" bitsize="80" type="i387_ext"/>
<reg name="st7" bitsize="80" type="i387_ext"/>
<reg name="st0" bitsize="80" type="i387_ext"/>
<reg name="st1" bitsize="80" type="i387_ext"/>
<reg name="st2" bitsize="80" type="i387_ext"/>
<reg name="st3" bitsize="80" type="i387_ext"/>
<reg name="st4" bitsize="80" type="i387_ext"/>
<reg name="st5" bitsize="80" type="i387_ext"/>
<reg name="st6" bitsize="80" type="i387_ext"/>
<reg name="st7" bitsize="80" type="i387_ext"/>
<reg name="fctrl" bitsize="32" type="int" group="float"/>
<reg name="fstat" bitsize="32" type="int" group="float"/>
<reg name="ftag" bitsize="32" type="int" group="float"/>
<reg name="fiseg" bitsize="32" type="int" group="float"/>
<reg name="fioff" bitsize="32" type="int" group="float"/>
<reg name="foseg" bitsize="32" type="int" group="float"/>
<reg name="fooff" bitsize="32" type="int" group="float"/>
<reg name="fop" bitsize="32" type="int" group="float"/>
</feature>
<reg name="fctrl" bitsize="32" type="int" group="float"/>
<reg name="fstat" bitsize="32" type="int" group="float"/>
<reg name="ftag" bitsize="32" type="int" group="float"/>
<reg name="fiseg" bitsize="32" type="int" group="float"/>
<reg name="fioff" bitsize="32" type="int" group="float"/>
<reg name="foseg" bitsize="32" type="int" group="float"/>
<reg name="fooff" bitsize="32" type="int" group="float"/>
<reg name="fop" bitsize="32" type="int" group="float"/>
</feature>
+33 -31
View File
@@ -7,39 +7,41 @@
<!DOCTYPE feature SYSTEM "gdb-target.dtd">
<feature name="org.gnu.gdb.i386.mpx">
<struct id="br128">
<field name="lbound" type="uint64"/>
<field name="ubound_raw" type="uint64"/>
</struct>
<struct id="br128">
<field name="lbound" type="uint64"/>
<field name="ubound_raw" type="uint64"/>
</struct>
<struct id="_bndstatus" size="8">
<field name="bde" start="2" end="31"/>
<field name="error" start="0" end="1"/>
</struct>
<struct id="_bndstatus" size="8">
<field name="bde" start="2" end="31"/>
<field name="error" start="0" end="1"/>
</struct>
<union id="status">
<field name="raw" type="data_ptr"/>
<field name="status" type="_bndstatus"/>
</union>
<union id="status">
<field name="raw" type="data_ptr"/>
<field name="status" type="_bndstatus"/>
</union>
<struct id="_bndcfgu" size="8">
<field name="base" start="12" end="31" />
<field name="reserved" start="2" end="11"/>
<!-- Explicitly set the type here, otherwise it defaults to bool.
Perhaps this should be uint32, but the container type has size 8. -->
<field name="preserved" start="1" end="1" type="uint64"/>
<field name="enabled" start="0" end="0" type="uint64"/>
</struct>
<struct id="_bndcfgu" size="8">
<field name="base" start="12" end="31" />
<field name="reserved" start="2" end="11"/>
<!--
Explicitly set the type here, otherwise it defaults to bool.
Perhaps this should be uint32, but the container type has size 8.
-->
<field name="preserved" start="1" end="1" type="uint64"/>
<field name="enabled" start="0" end="0" type="uint64"/>
</struct>
<union id="cfgu">
<field name="raw" type="data_ptr"/>
<field name="config" type="_bndcfgu"/>
</union>
<union id="cfgu">
<field name="raw" type="data_ptr"/>
<field name="config" type="_bndcfgu"/>
</union>
<reg name="bnd0raw" bitsize="128" type="br128"/>
<reg name="bnd1raw" bitsize="128" type="br128"/>
<reg name="bnd2raw" bitsize="128" type="br128"/>
<reg name="bnd3raw" bitsize="128" type="br128"/>
<reg name="bndcfgu" bitsize="64" type="cfgu"/>
<reg name="bndstatus" bitsize="64" type="status"/>
</feature>
<reg name="bnd0raw" bitsize="128" type="br128"/>
<reg name="bnd1raw" bitsize="128" type="br128"/>
<reg name="bnd2raw" bitsize="128" type="br128"/>
<reg name="bnd3raw" bitsize="128" type="br128"/>
<reg name="bndcfgu" bitsize="64" type="cfgu"/>
<reg name="bndstatus" bitsize="64" type="status"/>
</feature>
+2 -4
View File
@@ -7,7 +7,5 @@
<!DOCTYPE feature SYSTEM "gdb-target.dtd">
<feature name="org.gnu.gdb.i386.pkeys">
<reg name="pkru" bitsize="32" type="uint32"/>
</feature>
<reg name="pkru" bitsize="32" type="uint32"/>
</feature>
+43 -41
View File
@@ -7,46 +7,48 @@
<!DOCTYPE feature SYSTEM "gdb-target.dtd">
<feature name="org.gnu.gdb.i386.sse">
<vector id="v4f" type="ieee_single" count="4"/>
<vector id="v2d" type="ieee_double" count="2"/>
<vector id="v16i8" type="int8" count="16"/>
<vector id="v8i16" type="int16" count="8"/>
<vector id="v4i32" type="int32" count="4"/>
<vector id="v2i64" type="int64" count="2"/>
<union id="vec128">
<field name="v4_float" type="v4f"/>
<field name="v2_double" type="v2d"/>
<field name="v16_int8" type="v16i8"/>
<field name="v8_int16" type="v8i16"/>
<field name="v4_int32" type="v4i32"/>
<field name="v2_int64" type="v2i64"/>
<field name="uint128" type="uint128"/>
</union>
<flags id="i386_mxcsr" size="4">
<field name="IE" start="0" end="0"/>
<field name="DE" start="1" end="1"/>
<field name="ZE" start="2" end="2"/>
<field name="OE" start="3" end="3"/>
<field name="UE" start="4" end="4"/>
<field name="PE" start="5" end="5"/>
<field name="DAZ" start="6" end="6"/>
<field name="IM" start="7" end="7"/>
<field name="DM" start="8" end="8"/>
<field name="ZM" start="9" end="9"/>
<field name="OM" start="10" end="10"/>
<field name="UM" start="11" end="11"/>
<field name="PM" start="12" end="12"/>
<field name="FZ" start="15" end="15"/>
</flags>
<vector id="v4f" type="ieee_single" count="4"/>
<vector id="v2d" type="ieee_double" count="2"/>
<vector id="v16i8" type="int8" count="16"/>
<vector id="v8i16" type="int16" count="8"/>
<vector id="v4i32" type="int32" count="4"/>
<vector id="v2i64" type="int64" count="2"/>
<reg name="xmm0" bitsize="128" type="vec128" regnum="32"/>
<reg name="xmm1" bitsize="128" type="vec128"/>
<reg name="xmm2" bitsize="128" type="vec128"/>
<reg name="xmm3" bitsize="128" type="vec128"/>
<reg name="xmm4" bitsize="128" type="vec128"/>
<reg name="xmm5" bitsize="128" type="vec128"/>
<reg name="xmm6" bitsize="128" type="vec128"/>
<reg name="xmm7" bitsize="128" type="vec128"/>
<union id="vec128">
<field name="v4_float" type="v4f"/>
<field name="v2_double" type="v2d"/>
<field name="v16_int8" type="v16i8"/>
<field name="v8_int16" type="v8i16"/>
<field name="v4_int32" type="v4i32"/>
<field name="v2_int64" type="v2i64"/>
<field name="uint128" type="uint128"/>
</union>
<reg name="mxcsr" bitsize="32" type="i386_mxcsr" group="vector"/>
</feature>
<flags id="i386_mxcsr" size="4">
<field name="IE" start="0" end="0"/>
<field name="DE" start="1" end="1"/>
<field name="ZE" start="2" end="2"/>
<field name="OE" start="3" end="3"/>
<field name="UE" start="4" end="4"/>
<field name="PE" start="5" end="5"/>
<field name="DAZ" start="6" end="6"/>
<field name="IM" start="7" end="7"/>
<field name="DM" start="8" end="8"/>
<field name="ZM" start="9" end="9"/>
<field name="OM" start="10" end="10"/>
<field name="UM" start="11" end="11"/>
<field name="PM" start="12" end="12"/>
<field name="FZ" start="15" end="15"/>
</flags>
<reg name="xmm0" bitsize="128" type="vec128" regnum="32"/>
<reg name="xmm1" bitsize="128" type="vec128"/>
<reg name="xmm2" bitsize="128" type="vec128"/>
<reg name="xmm3" bitsize="128" type="vec128"/>
<reg name="xmm4" bitsize="128" type="vec128"/>
<reg name="xmm5" bitsize="128" type="vec128"/>
<reg name="xmm6" bitsize="128" type="vec128"/>
<reg name="xmm7" bitsize="128" type="vec128"/>
<reg name="mxcsr" bitsize="32" type="i386_mxcsr" group="vector"/>
</feature>
+9 -9
View File
@@ -1,11 +1,11 @@
<?xml version="1.0"?>
<!DOCTYPE target SYSTEM "gdb-target.dtd">
<target><architecture>i386</architecture>
<osabi>GNU/Linux</osabi>
<xi:include href="32bit-core.xml"/>
<xi:include href="32bit-sse.xml"/>
<xi:include href="32bit-linux.xml"/>
<xi:include href="32bit-segments.xml"/>
<xi:include href="32bit-avx.xml"/>
<xi:include href="32bit-mpx.xml"/>
</target>
<target xmlns:xi="http://www.w3.org/2001/XInclude">
<architecture>i386</architecture>
<osabi>GNU/Linux</osabi>
<xi:include href="32bit-core.xml"/>
<xi:include href="32bit-sse.xml"/>
<xi:include href="32bit-linux.xml"/>
<xi:include href="32bit-avx.xml"/>
</target>
+13 -1
View File
@@ -1 +1,13 @@
<?xml version="1.0"?><!DOCTYPE target SYSTEM "gdb-target.dtd"><target><architecture>i386:x86-64</architecture><osabi>GNU/Linux</osabi><xi:include href="64bit-core.xml"/><xi:include href="64bit-sse.xml"/><xi:include href="64bit-linux.xml"/><xi:include href="64bit-segments.xml"/><xi:include href="64bit-avx.xml"/><xi:include href="64bit-mpx.xml"/></target>
<?xml version="1.0"?>
<!DOCTYPE target SYSTEM "gdb-target.dtd">
<target xmlns:xi="http://www.w3.org/2001/XInclude">
<architecture>i386:x86-64</architecture>
<osabi>GNU/Linux</osabi>
<xi:include href="64bit-core.xml"/>
<xi:include href="64bit-sse.xml"/>
<xi:include href="64bit-linux.xml"/>
<xi:include href="64bit-segments.xml"/>
<xi:include href="64bit-avx.xml"/>
<xi:include href="64bit-mpx.xml"/>
</target>
+150
View File
@@ -0,0 +1,150 @@
#!/usr/bin/env python3
#
# Cross Platform and Multi Architecture Advanced Binary Emulation Framework
#
from typing import Iterator, Mapping, Optional, Sequence, Tuple
from pathlib import PurePath
from xml.etree import ElementTree, ElementInclude
from qiling.arch.arm_const import reg_map as arm_regs
from qiling.arch.arm_const import reg_vfp as arm_regs_vfp
from qiling.arch.arm64_const import reg_map as arm64_regs
from qiling.arch.arm64_const import reg_map_v as arm64_regs_v
from qiling.arch.mips_const import reg_map as mips_regs_gpr
from qiling.arch.mips_const import reg_map_fpu as mips_regs_fpu
from qiling.arch.x86_const import reg_map_32 as x86_regs_32
from qiling.arch.x86_const import reg_map_64 as x86_regs_64
from qiling.arch.x86_const import reg_map_misc as x86_regs_misc
from qiling.arch.x86_const import reg_map_cr as x86_regs_cr
from qiling.arch.x86_const import reg_map_st as x86_regs_st
from qiling.arch.x86_const import reg_map_xmm as x86_regs_xmm
from qiling.arch.x86_const import reg_map_ymm as x86_regs_ymm
from qiling.const import QL_ARCH, QL_OS
RegEntry = Tuple[Optional[int], int, int]
class QlGdbFeatures:
def __init__(self, archtype: QL_ARCH, ostype: QL_OS):
xmltree = QlGdbFeatures.__load_target_xml(archtype, ostype)
regsmap = QlGdbFeatures.__load_regsmap(archtype, xmltree)
self.xmltree = xmltree
self.regsmap = regsmap
def tostring(self) -> str:
root = self.xmltree.getroot()
return ElementTree.tostring(root, encoding='unicode', xml_declaration=True)
@staticmethod
def __get_xml_path(archtype: QL_ARCH) -> Tuple[str, PurePath]:
import inspect
p = PurePath(inspect.getfile(QlGdbFeatures))
basedir = p.parent / 'xml' / archtype.name.lower()
filename = basedir / 'target.xml'
return str(filename), basedir
@staticmethod
def __load_target_xml(archtype: QL_ARCH, ostype: QL_OS) -> ElementTree.ElementTree:
filename, base_url = QlGdbFeatures.__get_xml_path(archtype)
tree = ElementTree.parse(filename)
# NOTE: this is needed to load xinclude hrefs relative to the main xml file. starting
# from python 3.9 ElementInclude.include has an argument for that called 'base_url'.
# this is a workaround for earlier python versions such as 3.8
# <WORKAROUND>
def my_loader(base: PurePath):
def __wrapped(href: str, parse, encoding=None):
abshref = base / href
return ElementInclude.default_loader(str(abshref), parse, encoding)
return __wrapped
# </WORKAROUND>
# inline all xi:include elements
ElementInclude.include(tree.getroot(), loader=my_loader(base_url))
# patch xml osabi element with the appropriate abi tag
osabi = tree.find('osabi')
if osabi is not None:
# NOTE: the 'Windows' abi tag is supported starting from gdb 10.
# earlier gdb versions use 'Cygwin' instead
abitag = {
QL_OS.LINUX : 'GNU/Linux',
QL_OS.FREEBSD : 'FreeBSD',
QL_OS.MACOS : 'Darwin',
QL_OS.WINDOWS : 'Windows',
QL_OS.UEFI : 'Windows',
QL_OS.DOS : 'Windows',
QL_OS.QNX : 'QNX-Neutrino'
}.get(ostype, 'unknown')
osabi.text = abitag
return tree
@staticmethod
def __walk_xml_regs(xmltree: ElementTree.ElementTree) -> Iterator[Tuple[int, str, int]]:
regnum = -1
for reg in xmltree.iter('reg'):
# if regnum is not specified, assume it follows the previous one
regnum = int(reg.get('regnum', regnum + 1))
name = reg.attrib['name']
bitsize = reg.attrib['bitsize']
yield regnum, name, int(bitsize)
@staticmethod
def __load_regsmap(archtype: QL_ARCH, xmltree: ElementTree.ElementTree) -> Sequence[RegEntry]:
"""Initialize registers map using available target XML files.
Args:
archtype: target architecture type
Returns: a list representing registers data
"""
# retreive the relevant set of registers; their order of appearance is not
# important as it is determined by the info read from the xml files
ucregs: Mapping[str, int] = {
QL_ARCH.A8086 : dict(**x86_regs_32, **x86_regs_misc, **x86_regs_cr, **x86_regs_st),
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),
QL_ARCH.CORTEX_M : arm_regs,
QL_ARCH.ARM64 : dict(**arm64_regs, **arm64_regs_v),
QL_ARCH.MIPS : dict(**mips_regs_gpr, **mips_regs_fpu)
}[archtype]
regsinfo = sorted(QlGdbFeatures.__walk_xml_regs(xmltree))
# pre-allocate regmap and occupy it with null entries
last_regnum = regsinfo[-1][0]
regmap: Sequence[RegEntry] = [(None, 0, 0)] * (last_regnum + 1)
pos = 0
for regnum, name, bitsize in sorted(regsinfo):
# reg value size in nibbles
nibbles = bitsize // 4
regmap[regnum] = (ucregs.get(name), pos, nibbles)
# value position of next reg
pos += nibbles
return regmap
__all__ = ['RegEntry', 'QlGdbFeatures']
+2 -2
View File
@@ -21,7 +21,7 @@ class ArchX86(Arch):
"eax", "ebx", "ecx", "edx",
"esp", "ebp", "esi", "edi",
"eip", "ss", "cs", "ds", "es",
"fs", "gs", "ef",
"fs", "gs", "eflags",
)
def read_insn(self, address: int) -> bytes:
@@ -34,7 +34,7 @@ class ArchX86(Arch):
@staticmethod
def get_flags(bits: int) -> Mapping[str, bool]:
"""
get flags from ql.reg.ef
get flags from ql.reg.eflags
"""
return {
@@ -91,7 +91,7 @@ class BranchPredictorX86(BranchPredictor, ArchX86):
}
if line.mnemonic in jump_table:
eflags = self.get_flags(self.ql.arch.regs.ef).values()
eflags = self.get_flags(self.ql.arch.regs.eflags).values()
prophecy.going = jump_table.get(line.mnemonic)(*eflags)
elif line.mnemonic in jump_reg_table:
+2 -2
View File
@@ -389,7 +389,7 @@ class CtxManager_X86(CtxManager):
"eax", "ebx", "ecx", "edx",
"esp", "ebp", "esi", "edi",
"eip", "ss", "cs", "ds", "es",
"fs", "gs", "ef",
"fs", "gs", "eflags",
)
@context_printer("[ REGISTERS ]")
def context_reg(self, saved_reg_dump):
@@ -416,7 +416,7 @@ class CtxManager_X86(CtxManager):
lines += line
print(lines.format(*cur_regs.values()))
print(color.GREEN, "EFLAGS: [CF: {flags[CF]}, PF: {flags[PF]}, AF: {flags[AF]}, ZF: {flags[ZF]}, SF: {flags[SF]}, OF: {flags[OF]}]".format(flags=get_x86_eflags(self.ql.arch.regs.ef)), color.END, sep="")
print(color.GREEN, "EFLAGS: [CF: {flags[CF]}, PF: {flags[PF]}, AF: {flags[AF]}, ZF: {flags[ZF]}, SF: {flags[SF]}, OF: {flags[OF]}]".format(flags=get_x86_eflags(self.ql.arch.regs.eflags)), color.END, sep="")
@context_printer("[ DISASM ]", footer=True)
def context_asm(self):
+3 -1
View File
@@ -353,7 +353,9 @@ class QlQdb(cmd.Cmd, QlDebugger):
show some runtime information
"""
self.ql.mem.show_mapinfo()
for info_line in self.ql.mem.get_formatted_mapinfo():
self.ql.log.info(info_line)
qdb_print(QDB_MSG.INFO, f"Breakpoints: {[hex(addr) for addr in self.bp_list.keys()]}")
if self.rr:
qdb_print(QDB_MSG.INFO, f"Snapshots: {len([st for st in self.rr.layers if isinstance(st, self.rr.DiffedState)])}")
+5 -1
View File
@@ -34,7 +34,11 @@ class Render:
def decorator(context_dumper):
def wrapper(*args, **kwargs):
width, height = os.get_terminal_size()
try:
width, _ = os.get_terminal_size()
except OSError:
width = 130
bar = (width - len(field_name)) // 2 - 1
print(ruler * bar, field_name, ruler * bar)
context_dumper(*args, **kwargs)
+1 -1
View File
@@ -22,7 +22,7 @@ class ContextRenderX86(ContextRender, ArchX86):
cur_regs = self.dump_regs()
diff_reg = self.reg_diff(cur_regs, saved_reg_dump)
self.render_regs_dump(cur_regs, diff_reg=diff_reg)
print(color.GREEN, "EFLAGS: [CF: {flags[CF]}, PF: {flags[PF]}, AF: {flags[AF]}, ZF: {flags[ZF]}, SF: {flags[SF]}, OF: {flags[OF]}]".format(flags=self.get_flags(self.ql.arch.regs.ef)), color.END, sep="")
print(color.GREEN, "EFLAGS: [CF: {flags[CF]}, PF: {flags[PF]}, AF: {flags[AF]}, ZF: {flags[ZF]}, SF: {flags[SF]}, OF: {flags[OF]}]".format(flags=self.get_flags(self.ql.arch.regs.eflags)), color.END, sep="")
@Render.divider_printer("[ DISASM ]")
def context_asm(self):
+4 -4
View File
@@ -333,7 +333,7 @@ class IDA:
@staticmethod
def get_ql_arch_string():
info = IDA.get_info_structure()
proc = info.get_procName().lower()
proc = info.procname.lower()
result = None
if proc == "metapc":
result = "x86"
@@ -805,7 +805,7 @@ class QlEmuMisc:
self.action_type = action
def activate(self, ctx):
if ctx.form_type == BWN_DISASM:
if ctx.widget_type == BWN_DISASM:
self.action_handler.ql_handle_menu_action(self.action_type)
return 1
@@ -878,7 +878,7 @@ class QlEmuQiling:
def __init__(self):
self.path = None
self.rootfs = None
self.ql: Qiling
self.ql: Qiling = None
self.status = None
self.exit_addr = None
self.baseaddr = None
@@ -893,7 +893,7 @@ class QlEmuQiling:
self.ql.os.stderr = QlEmuMisc.QLStdIO('stderr', sys.__stderr__.fileno())
self.exit_addr = self.ql.os.exit_point
if self.ql.ostype == QL_OS.LINUX:
if self.ql.os.type == QL_OS.LINUX:
f = open(self.ql.path, 'rb')
elffile = ELFFile(f)
elf_header = elffile.header
+5 -5
View File
@@ -12,11 +12,11 @@ class Report:
def __init__(self, ql):
self.filename = ql.argv
self.rootfs = ql.rootfs
self.arch = list(arch_map.keys())[list(arch_map.values()).index(ql.arch.type)]
self.os = list(os_map.keys())[list(os_map.values()).index(ql.ostype)]
self.arch = ql.arch.type.name
self.os = ql.os.type.name
self.env = ql.env
self.strings = set()
for string in ql.os.stats.appeared_strings:
for string in ql.os.stats.strings:
strings = string.split(" ")
self.strings |= set(strings)
self.profile = {}
@@ -31,7 +31,7 @@ class Report:
class WindowsReport(Report):
def __init__(self, ql):
super().__init__(ql)
self.dlls = ql.loader.dlls
self.teb_address = ql.loader.TEB.base
self.peb_address = ql.loader.PEB.base
self.ldr_address = ql.loader.LDR.base
@@ -54,7 +54,7 @@ class WindowsReport(Report):
def generate_report(ql, pretty_print=False) -> dict:
if ql.ostype == QL_OS.WINDOWS:
if ql.os.type == QL_OS.WINDOWS:
report = WindowsReport(ql)
else:
report = Report(ql)
+2 -2
View File
@@ -21,7 +21,7 @@ class QlHost:
system = platform.system()
return utils.ostype_convert(system.lower())
return utils.os_convert(system)
@cached_property
def arch(self) -> Optional[QL_ARCH]:
@@ -30,4 +30,4 @@ class QlHost:
machine = platform.machine()
return utils.arch_convert(machine.lower())
return utils.arch_convert(machine)
+31 -28
View File
@@ -29,7 +29,7 @@ from qiling.os.linux.kernel_api.kernel_api import hook_sys_open, hook_sys_read,
# auxiliary vector types
# see: https://man7.org/linux/man-pages/man3/getauxval.3.html
class AUX(IntEnum):
class AUXV(IntEnum):
AT_NULL = 0
AT_IGNORE = 1
AT_EXECFD = 2
@@ -123,7 +123,7 @@ class QlLoaderELF(QlLoader):
self.ql.arch.regs.arch_sp = self.stack_address
# No idea why.
if self.ql.ostype == QL_OS.FREEBSD:
if self.ql.os.type == QL_OS.FREEBSD:
# self.ql.arch.regs.rbp = self.stack_address + 0x40
self.ql.arch.regs.rdi = self.stack_address
self.ql.arch.regs.r14 = self.stack_address
@@ -317,36 +317,39 @@ class QlLoaderELF(QlLoader):
elf_hwcap = 0xd7b81f
# setup aux vector
aux_entries = (
(AUX.AT_PHDR, elf_phdr),
(AUX.AT_PHENT, elf_phent),
(AUX.AT_PHNUM, elf_phnum),
(AUX.AT_PAGESZ, self.ql.mem.pagesize),
(AUX.AT_BASE, interp_address),
(AUX.AT_FLAGS, 0),
(AUX.AT_ENTRY, self.elf_entry),
(AUX.AT_UID, self.ql.os.uid),
(AUX.AT_EUID, self.ql.os.euid),
(AUX.AT_GID, self.ql.os.gid),
(AUX.AT_EGID, self.ql.os.egid),
(AUX.AT_HWCAP, elf_hwcap),
(AUX.AT_CLKTCK, 100),
(AUX.AT_RANDOM, randstraddr),
(AUX.AT_HWCAP2, 0),
(AUX.AT_EXECFN, execfn),
(AUX.AT_PLATFORM, cpustraddr),
(AUX.AT_SECURE, 0),
(AUX.AT_NULL, 0)
auxv_entries = (
(AUXV.AT_HWCAP, elf_hwcap),
(AUXV.AT_PAGESZ, self.ql.mem.pagesize),
(AUXV.AT_CLKTCK, 100),
(AUXV.AT_PHDR, elf_phdr),
(AUXV.AT_PHENT, elf_phent),
(AUXV.AT_PHNUM, elf_phnum),
(AUXV.AT_BASE, interp_address),
(AUXV.AT_FLAGS, 0),
(AUXV.AT_ENTRY, self.elf_entry),
(AUXV.AT_UID, self.ql.os.uid),
(AUXV.AT_EUID, self.ql.os.euid),
(AUXV.AT_GID, self.ql.os.gid),
(AUXV.AT_EGID, self.ql.os.egid),
(AUXV.AT_SECURE, 0),
(AUXV.AT_RANDOM, randstraddr),
(AUXV.AT_HWCAP2, 0),
(AUXV.AT_EXECFN, execfn),
(AUXV.AT_PLATFORM, cpustraddr),
(AUXV.AT_NULL, 0)
)
# add all aux entries
for key, val in aux_entries:
elf_table.extend(self.ql.pack(key) + self.ql.pack(val))
bytes_before_auxv = len(elf_table)
# add all auxv entries
for key, val in auxv_entries:
elf_table.extend(self.ql.pack(key))
elf_table.extend(self.ql.pack(val))
new_stack = self.ql.mem.align(new_stack - len(elf_table), 0x10)
self.ql.mem.write(new_stack, bytes(elf_table))
self.aux_vec = dict(aux_entries)
self.auxv = new_stack + bytes_before_auxv
self.stack_address = new_stack
self.load_address = load_address
@@ -361,11 +364,11 @@ class QlLoaderELF(QlLoader):
self.skip_exit_check = (self.elf_entry != self.entry_point)
# map vsyscall section for some specific needs
if self.ql.arch.type == QL_ARCH.X8664 and self.ql.ostype == QL_OS.LINUX:
if self.ql.arch.type == QL_ARCH.X8664 and self.ql.os.type == QL_OS.LINUX:
_vsyscall_addr = int(self.profile.get('vsyscall_address'), 0)
_vsyscall_size = int(self.profile.get('vsyscall_size'), 0)
if not self.ql.mem.is_mapped(_vsyscall_addr, _vsyscall_size):
if self.ql.mem.is_available(_vsyscall_addr, _vsyscall_size):
# initialize with int3 instructions then insert syscall entry
# each syscall should be 1KiB away
self.ql.mem.map(_vsyscall_addr, _vsyscall_size, info="[vsyscall]")
+19 -1
View File
@@ -3,11 +3,15 @@
# Cross Platform and Multi Architecture Advanced Binary Emulation Framework
#
import os
from typing import Any, Mapping, MutableSequence, NamedTuple, Optional
from qiling import Qiling
Image = NamedTuple('Image', (('base', int), ('end', int), ('path', str)))
class Image(NamedTuple):
base: int
end: int
path: str
class QlLoader:
def __init__(self, ql: Qiling):
@@ -25,6 +29,20 @@ class QlLoader:
return next((image for image in self.images if image.base <= address < image.end), None)
def get_image_by_name(self, name: str, *, casefold: bool = False) -> Optional[Image]:
"""Retrieve an image by its basename.
Args:
name : image base name
casefold : whether name matching should be case-insensitive (default is case-sensitive)
Returns: image object whose basename match to the one given, or `None` if not found
"""
cf = str.casefold if casefold else lambda s: s
return next((image for image in self.images if cf(os.path.basename(image.path)) == cf(name)), None)
def save(self) -> Mapping[str, Any]:
saved_state = {
'images': [tuple(img) for img in self.images]
+1
View File
@@ -45,6 +45,7 @@ LC_DYLD_INFO_ONLY = 0x80000022
LC_LOAD_DYLINKER = 0x0000000E
LC_MAIN = 0x80000028
LC_LOAD_DYLIB = 0x0000000C
LC_LOAD_WEAK_DYLIB = 0x80000018
LC_ENCRYPTION_INFO_64 = 0x0000002C
LC_BUILD_VERSION = 0x00000032
LC_DYLD_EXPORTS_TRIE = 0x80000033
@@ -39,6 +39,7 @@ class LoadCommand:
LC_LOAD_DYLINKER : LoadDylinker,
LC_MAIN : LoadMain,
LC_LOAD_DYLIB : LoadDyLib,
LC_LOAD_WEAK_DYLIB : LoadWeakDyLib,
LC_ENCRYPTION_INFO_64 : LoadEncryptionInfo64,
LC_DYLD_EXPORTS_TRIE : LoadDyldExportTrie,
LC_DYLD_CHAINED_FIXUPS : LoadDyldChainedFixups,
@@ -255,6 +256,11 @@ class LoadDyLib(LoadCommand):
pass
class LoadWeakDyLib(LoadDyLib):
def __init__(self, data):
super().__init__(data)
class LoadUnixThread(LoadCommand):
def __init__(self, data):
-3
View File
@@ -9,7 +9,6 @@ from elftools.elf.elffile import ELFFile
from qiling.const import *
from qiling.core import Qiling
from qiling.utils import component_setup
from .loader import QlLoader
@@ -63,8 +62,6 @@ class QlLoaderMCU(QlLoader):
self.load_address = 0
self.filetype = self.guess_filetype()
self.ql._hw = component_setup("hw", "hw", self.ql)
if self.filetype == 'elf':
with open(self.ql.path, 'rb') as infile:
self.elf = ELFFile(io.BytesIO(infile.read()))
+664 -452
View File
File diff suppressed because it is too large Load Diff
+188
View File
@@ -0,0 +1,188 @@
#!/usr/bin/env python3
#
# Cross Platform and Multi Architecture Advanced Binary Emulation Framework
#
import copy
import logging
import os
import re
import weakref
from typing import Optional, TextIO
from qiling.const import QL_VERBOSE
QL_INSTANCE_ID = 114514
FMT_STR = '%(levelname)s\t%(message)s'
class COLOR:
WHITE = '\033[37m'
CRIMSON = '\033[31m'
RED = '\033[91m'
GREEN = '\033[92m'
YELLOW = '\033[93m'
BLUE = '\033[94m'
MAGENTA = '\033[95m'
CYAN = '\033[96m'
ENDC = '\033[0m'
class QlBaseFormatter(logging.Formatter):
__level_tag = {
'WARNING' : '[!]',
'INFO' : '[=]',
'DEBUG' : '[+]',
'CRITICAL' : '[x]',
'ERROR' : '[x]'
}
def __init__(self, ql, *args, **kwargs):
super().__init__(*args, **kwargs)
self.ql = weakref.proxy(ql)
def get_level_tag(self, level: str) -> str:
return self.__level_tag[level]
def get_thread_tag(self, thread: str) -> str:
return thread
def format(self, record: logging.LogRecord):
# In case we have multiple formatters, we have to keep a copy of the record.
record = copy.copy(record)
# early logging may access ql.os when it is not yet set
try:
cur_thread = self.ql.os.thread_management.cur_thread
except AttributeError:
tid = f''
else:
tid = self.get_thread_tag(str(cur_thread))
level = self.get_level_tag(record.levelname)
record.levelname = f'{level} {tid}'
return super().format(record)
class QlColoredFormatter(QlBaseFormatter):
__level_color = {
'WARNING' : COLOR.YELLOW,
'INFO' : COLOR.BLUE,
'DEBUG' : COLOR.MAGENTA,
'CRITICAL' : COLOR.CRIMSON,
'ERROR' : COLOR.RED
}
def get_level_tag(self, level: str) -> str:
s = super().get_level_tag(level)
return f'{self.__level_color[level]}{s}{COLOR.ENDC}'
def get_thread_tag(self, tid: str) -> str:
s = super().get_thread_tag(tid)
return f'{COLOR.GREEN}{s}{COLOR.ENDC}'
class RegexFilter(logging.Filter):
def update_filter(self, regexp: str):
self._filter = re.compile(regexp)
def filter(self, record: logging.LogRecord):
msg = record.getMessage()
return self._filter.match(msg) is not None
def resolve_logger_level(verbose: QL_VERBOSE) -> int:
return {
QL_VERBOSE.DISABLED : logging.CRITICAL,
QL_VERBOSE.OFF : logging.WARNING,
QL_VERBOSE.DEFAULT : logging.INFO,
QL_VERBOSE.DEBUG : logging.DEBUG,
QL_VERBOSE.DISASM : logging.DEBUG,
QL_VERBOSE.DUMP : logging.DEBUG
}[verbose]
def __is_color_terminal(stream: TextIO) -> bool:
"""Determine whether standard output is attached to a color terminal.
see: https://stackoverflow.com/questions/53574442/how-to-reliably-test-color-capability-of-an-output-terminal-in-python3
"""
def __handle_nt(fd: int) -> bool:
import ctypes
import msvcrt
ENABLE_VIRTUAL_TERMINAL_PROCESSING = 4
kernel32 = ctypes.WinDLL('kernel32', use_last_error=True)
hstdout = msvcrt.get_osfhandle(fd)
mode = ctypes.c_ulong()
return kernel32.GetConsoleMode(hstdout, ctypes.byref(mode)) and (mode.value & ENABLE_VIRTUAL_TERMINAL_PROCESSING != 0)
def __handle_posix(fd: int) -> bool:
import curses
try:
curses.setupterm(fd=fd)
except curses.error:
return True
else:
return curses.tigetnum('colors') > 0
def __default(_: int) -> bool:
return True
handlers = {
'nt' : __handle_nt,
'posix' : __handle_posix
}
handler = handlers.get(os.name, __default)
return handler(stream.fileno())
def setup_logger(ql, log_file: Optional[str], console: bool, log_override: Optional[logging.Logger], log_plain: bool):
global QL_INSTANCE_ID
# If there is an override for our logger, then use it.
if log_override is not None:
log = log_override
else:
# We should leave the root logger untouched.
log = logging.getLogger(f'qiling{QL_INSTANCE_ID}')
QL_INSTANCE_ID += 1
# Disable propagation to avoid duplicate output.
log.propagate = False
# Clear all handlers and filters.
log.handlers = []
log.filters = []
# Do we have console output?
if console:
handler = logging.StreamHandler()
if log_plain or not __is_color_terminal(handler.stream):
formatter = QlBaseFormatter(ql, FMT_STR)
else:
formatter = QlColoredFormatter(ql, FMT_STR)
handler.setFormatter(formatter)
log.addHandler(handler)
else:
handler = logging.NullHandler()
log.addHandler(handler)
# Do we have to write log to a file?
if log_file is not None:
handler = logging.FileHandler(log_file)
formatter = QlBaseFormatter(ql, FMT_STR)
handler.setFormatter(formatter)
log.addHandler(handler)
log.setLevel(logging.INFO)
return log
__all__ = ['RegexFilter', 'setup_logger', 'resolve_logger_level']
View File
+13 -7
View File
@@ -4,8 +4,8 @@
#
from qiling import Qiling
from qiling.cc import QlCC, intel, arm, mips
from qiling.const import QL_ARCH
from qiling.cc import QlCC, intel, arm, mips, riscv, ppc
from qiling.const import QL_ARCH, QL_OS
from qiling.os.fcall import QlFunctionCall
from qiling.os.os import QlOs
@@ -17,17 +17,23 @@ class QlOsBlob(QlOs):
resolve_fcall_params(), heap or add_fs_mapper() are based on os. To keep the
consistence of api usage, QlOsBlob is introduced and placed at its loader temporarily.
"""
type = QL_OS.BLOB
def __init__(self, ql: Qiling):
super(QlOsBlob, self).__init__(ql)
self.ql = ql
cc: QlCC = {
QL_ARCH.X86 : intel.cdecl,
QL_ARCH.X8664 : intel.amd64,
QL_ARCH.ARM : arm.aarch32,
QL_ARCH.ARM64 : arm.aarch64,
QL_ARCH.MIPS : mips.mipso32
QL_ARCH.X86 : intel.cdecl,
QL_ARCH.X8664 : intel.amd64,
QL_ARCH.ARM : arm.aarch32,
QL_ARCH.ARM64 : arm.aarch64,
QL_ARCH.MIPS : mips.mipso32,
QL_ARCH.RISCV : riscv.riscv,
QL_ARCH.RISCV64 : riscv.riscv,
QL_ARCH.PPC : ppc.ppc,
}[ql.arch.type](ql.arch)
self.fcall = QlFunctionCall(ql, cc)
+5 -3
View File
@@ -10,7 +10,7 @@ from datetime import datetime
from unicorn import UcError
from qiling import Qiling
from qiling.const import QL_INTERCEPT
from qiling.const import QL_OS, QL_INTERCEPT
from qiling.os.os import QlOs
from .interrupts import handlers
@@ -29,6 +29,8 @@ class Flags(IntEnum):
IOPL = (3 << 12) # io privilege
class QlOsDos(QlOs):
type = QL_OS.DOS
def __init__(self, ql: Qiling):
super(QlOsDos, self).__init__(ql)
@@ -56,10 +58,10 @@ class QlOsDos(QlOs):
curses.endwin()
def set_flag_value(self, fl: Flags, val: int) -> None:
self.ql.arch.regs.ef = self.ql.arch.regs.ef & (~fl) | (fl * val)
self.ql.arch.regs.eflags = self.ql.arch.regs.eflags & (~fl) | (fl * val)
def test_flags(self, fl):
return self.ql.arch.regs.ef & fl == fl
return self.ql.arch.regs.eflags & fl == fl
def set_cf(self):
self.set_flag_value(Flags.CF, 0b1)
+2 -2
View File
@@ -32,8 +32,8 @@ class QlFunctionCall:
self.cc = cc
def __make_accessor(nbits: int) -> Accessor:
reader = lambda si: cc.getRawParam(si, nbits or None)
writer = lambda si, val: cc.setRawParam(si, val, nbits or None)
reader = lambda si: cc.getRawParam(si, nbits)
writer = lambda si, val: cc.setRawParam(si, val, nbits)
nslots = cc.getNumSlots(nbits)
return (reader, writer, nslots)
+4 -1
View File
@@ -7,9 +7,12 @@ from unicorn import UcError
from unicorn.x86_const import UC_X86_INS_SYSCALL
from qiling.arch.x86_utils import GDTManager, SegmentManager86
from qiling.const import QL_OS
from qiling.os.posix.posix import QlOsPosix
class QlOsFreebsd(QlOsPosix):
type = QL_OS.FREEBSD
def __init__(self, ql):
super(QlOsFreebsd, self).__init__(ql)
@@ -27,7 +30,7 @@ class QlOsFreebsd(QlOsPosix):
self.ql.hook_insn(self.hook_syscall, UC_X86_INS_SYSCALL)
def hook_syscall(self, intno= None):
def hook_syscall(self, ql):
return self.load_syscall()
+21 -24
View File
@@ -79,6 +79,10 @@ class HookFunc:
elif self.ql.arch.type == QL_ARCH.ARM64:
return self.ql.arch.regs.x30
# PPC
elif self.ql.arch.type== QL_ARCH.PPC:
return self.ql.arch.regs.lr
# X86
elif self.ql.arch.type == QL_ARCH.X86:
return self.ql.unpack(self.ql.mem.read(self.ql.arch.regs.esp, self.ql.arch.pointersize))
@@ -98,6 +102,10 @@ class HookFunc:
elif self.ql.arch.type == QL_ARCH.MIPS:
pass
# PPC
elif self.ql.arch.type== QL_ARCH.PPC:
pass
# ARM64
elif self.ql.arch.type == QL_ARCH.ARM64:
pass
@@ -121,6 +129,10 @@ class HookFunc:
elif self.ql.arch.type == QL_ARCH.MIPS:
self.ql.arch.regs.ra = addr
# PPC
elif self.ql.arch.type== QL_ARCH.PPC:
self.ql.arch.regs.lr = addr
# ARM64
elif self.ql.arch.type == QL_ARCH.ARM64:
self.ql.arch.stack_write(0, addr)
@@ -173,29 +185,6 @@ class HookFunc:
else:
self.context_fixup()
def ret(self):
# ARM
if self.ql.arch.type == QL_ARCH.ARM:
self.ql.arch.regs.arch_pc = self.ret_pc
# MIPS32
elif self.ql.arch.type == QL_ARCH.MIPS:
self.ql.arch.regs.arch_pc = self.ret_pc
# ARM64
elif self.ql.arch.type == QL_ARCH.ARM64:
self.ql.arch.regs.arch_pc = self.ret_pc
# X86
elif self.ql.arch.type == QL_ARCH.X86:
self.ql.arch.regs.arch_pc = self.ret_pc
# X8664
elif self.ql.arch.type == QL_ARCH.X8664:
self.ql.arch.regs.arch_pc = self.ret_pc
else:
raise
def call_exit(self):
# if self.ql.arch.type == QL_ARCH.ARM or self.ql.arch.type == QL_ARCH.ARM64:
# self.ql.arch.regs.arch_pc = self.ql.arch.regs.arch_pc + 4
@@ -211,7 +200,7 @@ class HookFunc:
else:
onexit_cb(self.ql, onexit_userdata)
self.ret()
self.ql.arch.regs.arch_pc = self.ret_pc
class HookFuncRel(HookFunc):
@@ -614,6 +603,14 @@ class FunctionHook:
ins = b'\x00\x01'
self.add_function_hook = self.add_function_hook_relocation
# PowerPC
elif self.ql.arch.type== QL_ARCH.PPC:
self.GLOB_DAT = 21
self.JMP_SLOT = 22
# nop
ins = b'\x60\x00\x00\x00'
self.add_function_hook = self.add_function_hook_relocation
self._parse()
if self.rel != None:
self.show_relocation(self.rel)
+7 -6
View File
@@ -10,24 +10,25 @@ from gevent.event import Event
from queue import Queue
class QlLinuxFutexManagement:
FUTEX_BITSET_MATCH_ANY = 0xffffffff
def __init__(self):
self._wait_list = {}
@property
def wait_list(self):
return self._wait_list
def futex_wait(self, ql, uaddr, t, val, bitset=FUTEX_BITSET_MATCH_ANY):
EAGAIN = 11
def _sched_wait_event(cur_thread):
ql.log.debug(f"Wait for notifications.")
event.wait()
uaddr_value = ql.unpack32(ql.mem.read(uaddr, 4))
if uaddr_value != val:
ql.log.debug(f"uaddr: {hex(uaddr_value)} != {hex(val)}")
return -1
return -EAGAIN
ql.emu_stop()
if uaddr not in self.wait_list.keys():
self.wait_list[uaddr] = Queue()
@@ -35,7 +36,7 @@ class QlLinuxFutexManagement:
self.wait_list[uaddr].put((bitset, t, event))
t.sched_cb = _sched_wait_event
return 0
def get_futex_wake_list(self, ql, addr, number, bitset=FUTEX_BITSET_MATCH_ANY):
wakes = []
if addr not in self.wait_list or number == 0:
+21 -19
View File
@@ -3,7 +3,6 @@
# Cross Platform and Multi Architecture Advanced Binary Emulation Framework
#
from typing import Callable
from unicorn import UcError
from unicorn.x86_const import UC_X86_INS_SYSCALL
@@ -11,30 +10,32 @@ from qiling import Qiling
from qiling.arch.x86_const import GS_SEGMENT_ADDR, GS_SEGMENT_SIZE
from qiling.arch.x86_utils import GDTManager, SegmentManager86, SegmentManager64
from qiling.arch import arm_utils
from qiling.cc import QlCC, intel, arm, mips, riscv
from qiling.const import QL_ARCH, QL_INTERCEPT
from qiling.cc import QlCC, intel, arm, mips, riscv, ppc
from qiling.const import QL_ARCH, QL_OS
from qiling.os.fcall import QlFunctionCall
from qiling.os.const import *
from qiling.os.posix.const import NR_OPEN
from qiling.os.posix.posix import QlOsPosix
from . import futex
from . import thread
class QlOsLinux(QlOsPosix):
type = QL_OS.LINUX
def __init__(self, ql: Qiling):
super(QlOsLinux, self).__init__(ql)
self.ql = ql
cc: QlCC = {
QL_ARCH.X86 : intel.cdecl,
QL_ARCH.X8664 : intel.amd64,
QL_ARCH.ARM : arm.aarch32,
QL_ARCH.ARM64 : arm.aarch64,
QL_ARCH.MIPS : mips.mipso32,
QL_ARCH.RISCV : riscv.riscv,
QL_ARCH.RISCV64: riscv.riscv,
QL_ARCH.X86 : intel.cdecl,
QL_ARCH.X8664 : intel.amd64,
QL_ARCH.ARM : arm.aarch32,
QL_ARCH.ARM64 : arm.aarch64,
QL_ARCH.MIPS : mips.mipso32,
QL_ARCH.RISCV : riscv.riscv,
QL_ARCH.RISCV64 : riscv.riscv,
QL_ARCH.PPC : ppc.ppc,
}[ql.arch.type](ql.arch)
self.fcall = QlFunctionCall(ql, cc)
@@ -107,6 +108,11 @@ class QlOsLinux(QlOsPosix):
self.ql.hook_intno(self.hook_syscall, 8)
self.thread_class = None
elif self.ql.arch.type == QL_ARCH.PPC:
self.ql.arch.enable_float()
self.ql.hook_intno(self.hook_syscall, 8)
self.thread_class = None
# on fork or execve, do not inherit opened files tagged as 'close on exec'
for i in range(len(self.fd)):
if getattr(self.fd[i], 'close_on_exec', 0):
@@ -116,10 +122,6 @@ class QlOsLinux(QlOsPosix):
return self.load_syscall()
def add_function_hook(self, fn: str, cb: Callable, intercept: QL_INTERCEPT):
self.ql.os.function_hook.add_function_hook(fn, cb, intercept)
def register_function_after_load(self, function):
if function not in self.function_after_load_list:
self.function_after_load_list.append(function)
@@ -161,9 +163,9 @@ class QlOsLinux(QlOsPosix):
self.ql.emu_start(self.ql.loader.elf_entry, self.exit_point, self.ql.timeout, self.ql.count)
except UcError:
# TODO: this is bad We need a better approach for this
#if self.ql.output != QL_OUTPUT.DEBUG:
# return
self.emu_error()
raise
# display summary
for entry in self.stats.summary():
self.ql.log.debug(entry)
+421 -2
View File
@@ -18,8 +18,9 @@ def get_syscall_mapper(archtype: QL_ARCH):
QL_ARCH.X8664 : x8664_syscall_table,
QL_ARCH.X86 : x86_syscall_table,
QL_ARCH.MIPS : mips_syscall_table,
QL_ARCH.RISCV : riscv32_syscall_table,
QL_ARCH.RISCV64 : riscv64_syscall_table
QL_ARCH.RISCV : riscv32_syscall_table,
QL_ARCH.RISCV64 : riscv64_syscall_table,
QL_ARCH.PPC : ppc_syscall_table
}[archtype]
def __mapper(syscall_num: int) -> str:
@@ -2550,3 +2551,421 @@ riscv64_syscall_table = {
446: "landlock_restrict_self",
448: "process_mrelease",
}
ppc_syscall_table = {
0: "restart_syscall",
1: "exit",
2: "fork",
3: "read",
4: "write",
5: "open",
6: "close",
7: "waitpid",
8: "creat",
9: "link",
10: "unlink",
11: "execve",
12: "chdir",
13: "time",
14: "mknod",
15: "chmod",
16: "lchown",
17: "break",
18: "oldstat",
19: "lseek",
20: "getpid",
21: "mount",
22: "umount",
23: "setuid",
24: "getuid",
25: "stime",
26: "ptrace",
27: "alarm",
28: "oldfstat",
29: "pause",
30: "utime",
31: "stty",
32: "gtty",
33: "access",
34: "nice",
35: "ftime",
36: "sync",
37: "kill",
38: "rename",
39: "mkdir",
40: "rmdir",
41: "dup",
42: "pipe",
43: "times",
44: "prof",
45: "brk",
46: "setgid",
47: "getgid",
48: "signal",
49: "geteuid",
50: "getegid",
51: "acct",
52: "umount2",
53: "lock",
54: "ioctl",
55: "fcntl",
56: "mpx",
57: "setpgid",
58: "ulimit",
59: "oldolduname",
60: "umask",
61: "chroot",
62: "ustat",
63: "dup2",
64: "getppid",
65: "getpgrp",
66: "setsid",
67: "sigaction",
68: "sgetmask",
69: "ssetmask",
70: "setreuid",
71: "setregid",
72: "sigsuspend",
73: "sigpending",
74: "sethostname",
75: "setrlimit",
76: "getrlimit",
77: "getrusage",
78: "gettimeofday",
79: "settimeofday",
80: "getgroups",
81: "setgroups",
82: "select",
83: "symlink",
84: "oldlstat",
85: "readlink",
86: "uselib",
87: "swapon",
88: "reboot",
89: "readdir",
90: "mmap",
91: "munmap",
92: "truncate",
93: "ftruncate",
94: "fchmod",
95: "fchown",
96: "getpriority",
97: "setpriority",
98: "profil",
99: "statfs",
100: "fstatfs",
101: "ioperm",
102: "socketcall",
103: "syslog",
104: "setitimer",
105: "getitimer",
106: "stat",
107: "lstat",
108: "fstat",
109: "olduname",
110: "iopl",
111: "vhangup",
112: "idle",
113: "vm86",
114: "wait4",
115: "swapoff",
116: "sysinfo",
117: "ipc",
118: "fsync",
119: "sigreturn",
120: "clone",
121: "setdomainname",
122: "uname",
123: "modify_ldt",
124: "adjtimex",
125: "mprotect",
126: "sigprocmask",
127: "create_module",
128: "init_module",
129: "delete_module",
130: "get_kernel_syms",
131: "quotactl",
132: "getpgid",
133: "fchdir",
134: "bdflush",
135: "sysfs",
136: "personality",
137: "afs_syscall",
138: "setfsuid",
139: "setfsgid",
140: "_llseek",
141: "getdents",
142: "_newselect",
143: "flock",
144: "msync",
145: "readv",
146: "writev",
147: "getsid",
148: "fdatasync",
149: "_sysctl",
150: "mlock",
151: "munlock",
152: "mlockall",
153: "munlockall",
154: "sched_setparam",
155: "sched_getparam",
156: "sched_setscheduler",
157: "sched_getscheduler",
158: "sched_yield",
159: "sched_get_priority_max",
160: "sched_get_priority_min",
161: "sched_rr_get_interval",
162: "nanosleep",
163: "mremap",
164: "setresuid",
165: "getresuid",
166: "query_module",
167: "poll",
168: "nfsservctl",
169: "setresgid",
170: "getresgid",
171: "prctl",
172: "rt_sigreturn",
173: "rt_sigaction",
174: "rt_sigprocmask",
175: "rt_sigpending",
176: "rt_sigtimedwait",
177: "rt_sigqueueinfo",
178: "rt_sigsuspend",
179: "pread64",
180: "pwrite64",
181: "chown",
182: "getcwd",
183: "capget",
184: "capset",
185: "sigaltstack",
186: "sendfile",
187: "getpmsg",
188: "putpmsg",
189: "vfork",
190: "ugetrlimit",
191: "readahead",
192: "mmap2",
193: "truncate64",
194: "ftruncate64",
195: "stat64",
196: "lstat64",
197: "fstat64",
198: "pciconfig_read",
199: "pciconfig_write",
200: "pciconfig_iobase",
201: "multiplexer",
202: "getdents64",
203: "pivot_root",
204: "fcntl64",
205: "madvise",
206: "mincore",
207: "gettid",
208: "tkill",
209: "setxattr",
210: "lsetxattr",
211: "fsetxattr",
212: "getxattr",
213: "lgetxattr",
214: "fgetxattr",
215: "listxattr",
216: "llistxattr",
217: "flistxattr",
218: "removexattr",
219: "lremovexattr",
220: "fremovexattr",
221: "futex",
222: "sched_setaffinity",
223: "sched_getaffinity",
225: "tuxcall",
226: "sendfile64",
227: "io_setup",
228: "io_destroy",
229: "io_getevents",
230: "io_submit",
231: "io_cancel",
232: "set_tid_address",
233: "fadvise64",
234: "exit_group",
235: "lookup_dcookie",
236: "epoll_create",
237: "epoll_ctl",
238: "epoll_wait",
239: "remap_file_pages",
240: "timer_create",
241: "timer_settime",
242: "timer_gettime",
243: "timer_getoverrun",
244: "timer_delete",
245: "clock_settime",
246: "clock_gettime",
247: "clock_getres",
248: "clock_nanosleep",
249: "swapcontext",
250: "tgkill",
251: "utimes",
252: "statfs64",
253: "fstatfs64",
254: "fadvise64_64",
255: "rtas",
256: "sys_debug_setcontext",
258: "migrate_pages",
259: "mbind",
260: "get_mempolicy",
261: "set_mempolicy",
262: "mq_open",
263: "mq_unlink",
264: "mq_timedsend",
265: "mq_timedreceive",
266: "mq_notify",
267: "mq_getsetattr",
268: "kexec_load",
269: "add_key",
270: "request_key",
271: "keyctl",
272: "waitid",
273: "ioprio_set",
274: "ioprio_get",
275: "inotify_init",
276: "inotify_add_watch",
277: "inotify_rm_watch",
278: "spu_run",
279: "spu_create",
280: "pselect6",
281: "ppoll",
282: "unshare",
283: "splice",
284: "tee",
285: "vmsplice",
286: "openat",
287: "mkdirat",
288: "mknodat",
289: "fchownat",
290: "futimesat",
291: "fstatat64",
292: "unlinkat",
293: "renameat",
294: "linkat",
295: "symlinkat",
296: "readlinkat",
297: "fchmodat",
298: "faccessat",
299: "get_robust_list",
300: "set_robust_list",
301: "move_pages",
302: "getcpu",
303: "epoll_pwait",
304: "utimensat",
305: "signalfd",
306: "timerfd_create",
307: "eventfd",
308: "sync_file_range2",
309: "fallocate",
310: "subpage_prot",
311: "timerfd_settime",
312: "timerfd_gettime",
313: "signalfd4",
314: "eventfd2",
315: "epoll_create1",
316: "dup3",
317: "pipe2",
318: "inotify_init1",
319: "perf_event_open",
320: "preadv",
321: "pwritev",
322: "rt_tgsigqueueinfo",
323: "fanotify_init",
324: "fanotify_mark",
325: "prlimit64",
326: "socket",
327: "bind",
328: "connect",
329: "listen",
330: "accept",
331: "getsockname",
332: "getpeername",
333: "socketpair",
334: "send",
335: "sendto",
336: "recv",
337: "recvfrom",
338: "shutdown",
339: "setsockopt",
340: "getsockopt",
341: "sendmsg",
342: "recvmsg",
343: "recvmmsg",
344: "accept4",
345: "name_to_handle_at",
346: "open_by_handle_at",
347: "clock_adjtime",
348: "syncfs",
349: "sendmmsg",
350: "setns",
351: "process_vm_readv",
352: "process_vm_writev",
353: "finit_module",
354: "kcmp",
355: "sched_setattr",
356: "sched_getattr",
357: "renameat2",
358: "seccomp",
359: "getrandom",
360: "memfd_create",
361: "bpf",
362: "execveat",
363: "switch_endian",
364: "userfaultfd",
365: "membarrier",
378: "mlock2",
379: "copy_file_range",
380: "preadv2",
381: "pwritev2",
382: "kexec_file_load",
383: "statx",
384: "pkey_alloc",
385: "pkey_free",
386: "pkey_mprotect",
387: "rseq",
388: "io_pgetevents",
393: "semget",
394: "semctl",
395: "shmget",
396: "shmctl",
397: "shmat",
398: "shmdt",
399: "msgget",
400: "msgsnd",
401: "msgrcv",
402: "msgctl",
403: "clock_gettime64",
404: "clock_settime64",
405: "clock_adjtime64",
406: "clock_getres_time64",
407: "clock_nanosleep_time64",
408: "timer_gettime64",
409: "timer_settime64",
410: "timerfd_gettime64",
411: "timerfd_settime64",
412: "utimensat_time64",
413: "pselect6_time64",
414: "ppoll_time64",
416: "io_pgetevents_time64",
417: "recvmmsg_time64",
418: "mq_timedsend_time64",
419: "mq_timedreceive_time64",
420: "semtimedop_time64",
421: "rt_sigtimedwait_time64",
422: "futex_time64",
423: "sched_rr_get_interval_time64",
424: "pidfd_send_signal",
425: "io_uring_setup",
426: "io_uring_enter",
427: "io_uring_register",
428: "open_tree",
429: "move_mount",
430: "fsopen",
431: "fsconfig",
432: "fsmount",
433: "fspick",
}
+31 -40
View File
@@ -11,24 +11,29 @@ from datetime import datetime
from math import floor
import ctypes
class timespec(ctypes.Structure):
_fields_ = [
("tv_sec", ctypes.c_uint64),
("tv_nsec", ctypes.c_int64)
]
def __get_timespec_struct(archbits: int):
long = getattr(ctypes, f'c_int{archbits}')
ulong = getattr(ctypes, f'c_uint{archbits}')
_pack_ = 8
class timespec(ctypes.Structure):
_pack_ = archbits // 8
_fields_ = (
('tv_sec', ulong),
('tv_nsec', long)
)
# Temporary dirty fix.
# TODO: Pack ctypes.Structure according to ql.arch.type and ql.ostype?
class timespec32(ctypes.Structure):
_fields_ = [
("tv_sec", ctypes.c_uint32),
("tv_nsec", ctypes.c_int32)
]
return timespec
def __get_timespec_obj(archbits: int):
now = datetime.now().timestamp()
tv_sec = floor(now)
tv_nsec = floor((now - floor(now)) * 1e6)
ts_cls = __get_timespec_struct(archbits)
return ts_cls(tv_sec=tv_sec, tv_nsec=tv_nsec)
_pack_ = 4
def ql_syscall_set_thread_area(ql: Qiling, u_info_addr: int):
if ql.arch.type == QL_ARCH.X86:
@@ -62,39 +67,25 @@ def ql_syscall_set_thread_area(ql: Qiling, u_info_addr: int):
return 0
def ql_syscall_set_tls(ql, address, *args, **kw):
def ql_syscall_set_tls(ql: Qiling, address: int):
if ql.arch.type == QL_ARCH.ARM:
ql.arch.regs.c13_c0_3 = address
ql.mem.write_ptr(ql.arch.arm_get_tls_addr + 16, address, 4)
ql.arch.regs.r0 = address
ql.log.debug("settls(0x%x)" % address)
def ql_syscall_clock_gettime(ql, clock_gettime_clock_id, clock_gettime_timespec, *args, **kw):
now = datetime.now().timestamp()
tv_sec = floor(now)
tv_nsec = floor((now - floor(now)) * 1e6)
if ql.arch.type == QL_ARCH.X8664:
tp = timespec(tv_sec= tv_sec, tv_nsec=tv_nsec)
else:
tp = timespec32(tv_sec= tv_sec, tv_nsec=tv_nsec)
ql.mem.write(clock_gettime_timespec, bytes(tp))
def ql_syscall_clock_gettime(ql: Qiling, clock_id: int, tp: int):
ts_obj = __get_timespec_obj(ql.arch.bits)
ql.mem.write(tp, bytes(ts_obj))
ql.log.debug("clock_gettime(clock_id = %d, timespec = 0x%x)" % (clock_gettime_clock_id, clock_gettime_timespec))
return 0
def ql_syscall_gettimeofday(ql, gettimeofday_tv, gettimeofday_tz, *args, **kw):
now = datetime.now().timestamp()
tv_sec = floor(now)
tv_nsec = floor((now - floor(now)) * 1e6)
if ql.arch.type == QL_ARCH.X8664:
tp = timespec(tv_sec= tv_sec, tv_nsec=tv_nsec)
else:
tp = timespec32(tv_sec= tv_sec, tv_nsec=tv_nsec)
def ql_syscall_gettimeofday(ql: Qiling, tv: int, tz: int):
if tv:
ts_obj = __get_timespec_obj(ql.arch.bits)
ql.mem.write(tv, bytes(ts_obj))
if gettimeofday_tv != 0:
ql.mem.write(gettimeofday_tv, bytes(tp))
if gettimeofday_tz != 0:
ql.mem.write(gettimeofday_tz, b'\x00' * 8)
regreturn = 0
return regreturn
if tz:
ql.mem.write(tz, b'\x00' * 8)
return 0
+3 -3
View File
@@ -14,7 +14,7 @@ from qiling import Qiling
from qiling.os.thread import *
from qiling.arch.x86_const import *
from qiling.exception import QlErrorExecutionStop
from qiling.os.path import QlPathManager
from qiling.os.path import QlOsPath
LINUX_THREAD_ID = 2000
@@ -140,8 +140,8 @@ class QlLinuxThread(QlThread):
return self._path
@path.setter
def path(self, p):
self._path = QlPathManager(self._ql, p.cwd)
def path(self, p: QlOsPath):
self._path = QlOsPath(self.ql.rootfs, p.cwd, self.ql.os.type)
@property
def log_file_fd(self):
+5 -3
View File
@@ -11,7 +11,7 @@ from unicorn.x86_const import UC_X86_INS_SYSCALL
from qiling import Qiling
from qiling.arch.x86_utils import GDTManager, SegmentManager64
from qiling.cc import intel
from qiling.const import QL_ARCH, QL_VERBOSE
from qiling.const import QL_ARCH, QL_OS, QL_VERBOSE
from qiling.os.fcall import QlFunctionCall
from qiling.os.posix.posix import QlOsPosix
from qiling.os.macos.events.macos import QlMacOSEvManager
@@ -20,6 +20,8 @@ from qiling.os.macos.events.macos_structs import mac_policy_list_t
from qiling.os.macos.structs import kmod_info_t, POINTER64
class QlOsMacos(QlOsPosix):
type = QL_OS.MACOS
def __init__(self, ql: Qiling):
super(QlOsMacos, self).__init__(ql)
@@ -157,11 +159,11 @@ class QlOsMacos(QlOsPosix):
self.ql.hook_insn(self.hook_syscall, UC_X86_INS_SYSCALL)
def hook_syscall(self, intno= None, int = None):
def hook_syscall(self, ql, intno = None):
return self.load_syscall()
def hook_sigtrap(self, intno= None, int = None):
def hook_sigtrap(self, ql, intno):
self.ql.log.info("Trap Found")
self.emu_error()
exit(1)
+2 -2
View File
@@ -188,5 +188,5 @@ def page_align_end(addr, page_size):
def set_eflags_cf(ql, target_cf):
ql.arch.regs.ef = ( ql.arch.regs.ef & 0xfffffffe ) | target_cf
return ql.arch.regs.ef
ql.arch.regs.eflags = ( ql.arch.regs.eflags & 0xfffffffe ) | target_cf
return ql.arch.regs.eflags
+2 -2
View File
@@ -7,7 +7,7 @@ import inspect
import os
from typing import Any, MutableMapping, Union
from .path import QlPathManager
from .path import QlOsPath
from .filestruct import ql_file
# All mapped objects should inherit this class.
@@ -59,7 +59,7 @@ class QlFsMappedObject:
class QlFsMapper:
def __init__(self, path: QlPathManager):
def __init__(self, path: QlOsPath):
self._mapping: MutableMapping[str, Any] = {}
self.path = path
+5 -3
View File
@@ -3,8 +3,9 @@
# Cross Platform and Multi Architecture Advanced Binary Emulation Framework
#
from qiling.os.os import QlOs
from qiling.const import QL_OS
from qiling.const import QL_ARCH
from qiling.os.os import QlOs
from qiling.extensions.multitask import MultiTaskUnicorn, UnicornTask
from unicorn import UC_ERR_OK
@@ -31,9 +32,10 @@ class MCUTask(UnicornTask):
class QlOsMcu(QlOs):
type = QL_OS.MCU
def __init__(self, ql):
super(QlOsMcu, self).__init__(ql)
self.ql = ql
self.runable = True
self.fast_mode = False
+74 -56
View File
@@ -4,12 +4,11 @@
#
import os, re
from typing import Any, Callable, List, MutableSequence, Optional, Sequence, Tuple
from typing import Any, Callable, Iterator, List, Mapping, MutableSequence, Optional, Pattern, Sequence, Tuple, Union
from unicorn import UC_PROT_NONE, UC_PROT_READ, UC_PROT_WRITE, UC_PROT_EXEC, UC_PROT_ALL
from qiling import Qiling
from qiling.const import *
from qiling.exception import *
# tuple: range start, range end, permissions mask, range label, is mmio?
@@ -36,7 +35,7 @@ class QlMemoryManager:
}
if ql.arch.bits not in bit_stuff:
raise QlErrorStructConversion("Unsupported Qiling archtecture for memory manager")
raise QlErrorStructConversion("Unsupported Qiling architecture for memory manager")
max_addr = bit_stuff[ql.arch.bits]
@@ -128,8 +127,8 @@ class QlMemoryManager:
self.map_info = tmp_map_info
def change_mapinfo(self, mem_s: int, mem_e: int, mem_p: int = None, mem_info: str = None):
tmp_map_info: MapInfoEntry = None
def change_mapinfo(self, mem_s: int, mem_e: int, mem_p: Optional[int] = None, mem_info: Optional[str] = None):
tmp_map_info: Optional[MapInfoEntry] = None
info_idx: int = None
for idx, map_info in enumerate(self.map_info):
@@ -150,12 +149,12 @@ class QlMemoryManager:
if mem_info is not None:
self.map_info[info_idx] = (tmp_map_info[0], tmp_map_info[1], tmp_map_info[2], mem_info, tmp_map_info[4])
def get_mapinfo(self) -> Sequence[Tuple[int, int, str, str, Optional[str]]]:
def get_mapinfo(self) -> Sequence[Tuple[int, int, str, str, str]]:
"""Get memory map info.
Returns: A sequence of 5-tuples representing the memory map entries. Each
tuple contains range start, range end, permissions, range label and path of
containing image (or None if not contained by any image)
containing image (or an empty string if not contained by any image)
"""
def __perms_mapping(ps: int) -> str:
@@ -167,21 +166,21 @@ class QlMemoryManager:
return ''.join(val if idx & ps else '-' for idx, val in perms_d.items())
def __process(lbound: int, ubound: int, perms: int, label: str, is_mmio: bool) -> Tuple[int, int, str, str, Optional[str]]:
def __process(lbound: int, ubound: int, perms: int, label: str, is_mmio: bool) -> Tuple[int, int, str, str, str]:
perms_str = __perms_mapping(perms)
if hasattr(self.ql, 'loader'):
image = self.ql.loader.find_containing_image(lbound)
container = image.path if image and not is_mmio else None
container = image.path if image and not is_mmio else ''
else:
container = None
container = ''
return (lbound, ubound, perms_str, label, container)
return tuple(__process(*entry) for entry in self.map_info)
def show_mapinfo(self):
"""Emit memory map info in a nicely formatted table.
def get_formatted_mapinfo(self) -> Sequence[str]:
"""Get memory map info in a nicely formatted table.
"""
mapinfo = self.get_mapinfo()
@@ -193,12 +192,17 @@ class QlMemoryManager:
len_addr = max(grouped[0])
len_label = max(grouped[1])
# emit title row
self.ql.log.info(f'{"Start":{len_addr}s} {"End":{len_addr}s} {"Perm":5s} {"Label":{len_label}s} {"Image"}')
# pre-allocate table
table = [''] * (len(mapinfo) + 1)
# emit table rows
for lbound, ubound, perms, label, container in mapinfo:
self.ql.log.info(f'{lbound:0{len_addr}x} - {ubound:0{len_addr}x} {perms:5s} {label:{len_label}s} {container or ""}')
# add title row
table[0] = f'{"Start":{len_addr}s} {"End":{len_addr}s} {"Perm":5s} {"Label":{len_label}s} {"Image"}'
# add table rows
for i, (lbound, ubound, perms, label, container) in enumerate(mapinfo, 1):
table[i] = f'{lbound:0{len_addr}x} - {ubound:0{len_addr}x} {perms:5s} {label:{len_label}s} {container}'
return table
# TODO: relying on the label string is risky; find a more reliable method
def get_lib_base(self, filename: str) -> Optional[int]:
@@ -211,7 +215,7 @@ class QlMemoryManager:
return next((lbound for lbound, info in stripped if os.path.basename(info) == filename), None)
def align(self, value: int, alignment: int = None) -> int:
def align(self, value: int, alignment: Optional[int] = None) -> int:
"""Align a value down to the specified alignment boundary. If `value` is already
aligned, the same value is returned. Commonly used to determine the base address
of the enclosing page.
@@ -233,7 +237,7 @@ class QlMemoryManager:
# round down to nearest alignment
return value & ~(alignment - 1)
def align_up(self, value: int, alignment: int = None) -> int:
def align_up(self, value: int, alignment: Optional[int] = None) -> int:
"""Align a value up to the specified alignment boundary. If `value` is already
aligned, the same value is returned. Commonly used to determine the end address
of the enlosing page.
@@ -281,7 +285,7 @@ class QlMemoryManager:
self.ql.log.debug(f'restoring memory range: {lbound:#08x} {ubound:#08x} {label}')
size = ubound - lbound
if not self.is_mapped(lbound, size):
if self.is_available(lbound, size):
self.ql.log.debug(f'mapping {lbound:#08x} {ubound:#08x}, mapsize = {size:#x}')
self.map(lbound, size, perms, label)
@@ -306,13 +310,13 @@ class QlMemoryManager:
return self.ql.uc.mem_read(addr, size)
def read_ptr(self, addr: int, size: int=None) -> int:
def read_ptr(self, addr: int, size: int = 0) -> int:
"""Read an integer value from a memory address.
Bytes read will be unpacked using emulated architecture properties.
Args:
addr: memory address to read
size: pointer size (in bytes): either 1, 2, 4, 8, or None for arch native size
size: pointer size (in bytes): either 1, 2, 4, 8, or 0 for arch native size
Returns: integer value stored at the specified memory address
"""
@@ -342,14 +346,14 @@ class QlMemoryManager:
self.ql.uc.mem_write(addr, data)
def write_ptr(self, addr: int, value: int, size: int=None) -> None:
def write_ptr(self, addr: int, value: int, size: int = 0) -> None:
"""Write an integer value to a memory address.
Bytes written will be packed using emulated architecture properties.
Args:
addr: target memory address
value: integer value to write
size: pointer size (in bytes): either 1, 2, 4, 8, or None for arch native size
size: pointer size (in bytes): either 1, 2, 4, 8, or 0 for arch native size
"""
if not size:
@@ -367,18 +371,18 @@ class QlMemoryManager:
self.write(addr, __pack(value))
def search(self, needle: bytes, begin: int = None, end: int = None) -> Sequence[int]:
def search(self, needle: Union[bytes, Pattern[bytes]], begin: Optional[int] = None, end: Optional[int] = None) -> Sequence[int]:
"""Search for a sequence of bytes in memory.
Args:
needle: bytes sequence to look for
needle: bytes sequence or regex pattern to look for
begin: search starting address (or None to start at lowest avaiable address)
end: search ending address (or None to end at highest avaiable address)
Returns: addresses of all matches
"""
# if starting point not set, search from the first mapped region
# if starting point not set, search from the first mapped region
if begin is None:
begin = self.map_info[0][0]
@@ -392,6 +396,10 @@ class QlMemoryManager:
ranges = [(max(begin, lbound), min(ubound, end)) for lbound, ubound, _, _, is_mmio in self.map_info if not (end < lbound or ubound < begin or is_mmio)]
results = []
# if needle is a bytes sequence use it verbatim, not as a pattern
if type(needle) is bytes:
needle = re.escape(needle)
for lbound, ubound in ranges:
haystack = self.read(lbound, ubound - lbound)
local_results = (match.start(0) + lbound for match in re.finditer(needle, haystack))
@@ -414,17 +422,41 @@ class QlMemoryManager:
if (addr, addr + size) in self.mmio_cbs:
del self.mmio_cbs[(addr, addr+size)]
def unmap_all(self):
def unmap_all(self) -> None:
"""Reclaim the entire memory space.
"""
for begin, end, _ in self.ql.uc.mem_regions():
if begin and end:
self.unmap(begin, end - begin + 1)
self.unmap(begin, end - begin + 1)
def __mapped_regions(self) -> Iterator[Tuple[int, int]]:
"""Iterate through all mapped memory regions, consolidating adjacent regions
together to a continuous one. Protection bits and labels are ignored.
"""
if not self.map_info:
return
iter_memmap = iter(self.map_info)
p_lbound, p_ubound, _, _, _ = next(iter_memmap)
# map_info is assumed to contain non-overlapping regions sorted by lbound
for lbound, ubound, _, _, _ in iter_memmap:
if lbound == p_ubound:
p_ubound = ubound
else:
yield (p_lbound, p_ubound)
p_lbound = lbound
p_ubound = ubound
yield (p_lbound, p_ubound)
def is_available(self, addr: int, size: int) -> bool:
"""Query whether the memory range starting at `addr` and is of length of `size` bytes
can be allocated.
is available for allocation.
Returns: True if it can be allocated, False otherwise
"""
@@ -435,37 +467,23 @@ class QlMemoryManager:
end = addr + size
# make sure neither begin nor end are enclosed within a mapped range, or entirely enclosing one
return not any((lbound <= begin < ubound) or (lbound < end <= ubound) or (begin <= lbound < ubound <= end) for lbound, ubound, _, _, _ in self.map_info)
return not any((lbound <= begin < ubound) or (lbound < end <= ubound) or (begin <= lbound < ubound <= end) for lbound, ubound in self.__mapped_regions())
def is_mapped(self, addr: int, size: int) -> bool:
"""Query whether the memory range starting at `addr` and is of length of `size` bytes
is mapped, either partially or entirely.
is fully mapped.
Returns: True if any part of the specified memory range is taken, False otherwise
Returns: True if the specified memory range is taken fully, False otherwise
"""
return not self.is_available(addr, size)
assert size > 0, 'expected a positive size value'
def is_free(self, address, size):
'''
The main function of is_free first must fufull is_mapped condition.
then, check for is the mapped range empty, either fill with 0xFF or 0x00
Returns true if mapped range is empty else return Flase
If not not mapped, map it and return true
'''
if self.is_mapped(address, size) == True:
address_end = (address + size)
while address < address_end:
mem_read = self.ql.mem.read(address, 0x1)
if (mem_read[0] != 0x00) and (mem_read[0] != 0xFF):
return False
address += 1
return True
else:
return True
begin = addr
end = addr + size
return any((lbound <= begin < end <= ubound) for lbound, ubound in self.__mapped_regions())
def find_free_space(self, size: int, minaddr: int = None, maxaddr: int = None, align: int = None) -> int:
def find_free_space(self, size: int, minaddr: Optional[int] = None, maxaddr: Optional[int] = None, align: Optional[int] = None) -> int:
"""Locate an unallocated memory that is large enough to contain a range in size of
`size` and based at `minaddr`.
@@ -510,7 +528,7 @@ class QlMemoryManager:
raise QlOutOfMemory('Out Of Memory')
def map_anywhere(self, size: int, minaddr: int = None, maxaddr: int = None, align: int = None, perms: int = UC_PROT_ALL, info: str = None) -> int:
def map_anywhere(self, size: int, minaddr: Optional[int] = None, maxaddr: Optional[int] = None, align: Optional[int] = None, perms: int = UC_PROT_ALL, info: Optional[str] = None) -> int:
"""Map a region anywhere in memory.
Args:
@@ -545,7 +563,7 @@ class QlMemoryManager:
self.change_mapinfo(aligned_address, aligned_address + aligned_size, mem_p = perms)
def map(self, addr: int, size: int, perms: int = UC_PROT_ALL, info: str = None):
def map(self, addr: int, size: int, perms: int = UC_PROT_ALL, info: Optional[str] = None):
"""Map a new memory range.
Args:
@@ -724,7 +742,7 @@ class QlMemoryHeap:
self.current_alloc = 0
self.current_use = 0
def _find(self, addr: int, inuse: bool = None) -> Optional[Chunk]:
def _find(self, addr: int, inuse: Optional[bool] = None) -> Optional[Chunk]:
"""Find a chunk starting at a specified address.
Args:
+16 -19
View File
@@ -4,7 +4,7 @@
#
import sys
from typing import Any, Iterable, Optional, Callable, Mapping, Sequence, TextIO, Tuple
from typing import Any, Hashable, Iterable, Optional, Callable, Mapping, Sequence, TextIO, Tuple
from unicorn import UcError
@@ -15,10 +15,13 @@ from qiling.os.fcall import QlFunctionCall, TypedArg
from .filestruct import ql_file
from .mapper import QlFsMapper
from .utils import QlOsStats, QlOsUtils
from .path import QlPathManager
from .stats import QlOsStats
from .utils import QlOsUtils
from .path import QlOsPath
class QlOs:
type: QL_OS
Resolver = Callable[[int], Any]
def __init__(self, ql: Qiling, resolvers: Mapping[Any, Resolver] = {}):
@@ -37,10 +40,10 @@ class QlOs:
self.profile = self.ql.profile
self.exit_code = 0
if not ql.baremetal:
if self.type in QL_OS_POSIX + (QL_OS.WINDOWS, QL_OS.DOS):
cwd = self.profile.get("MISC", "current_path")
self.path = QlPathManager(ql, cwd)
self.path = QlOsPath(ql.rootfs, cwd, self.type)
self.fs_mapper = QlFsMapper(self.path)
self.user_defined_api = {
@@ -201,26 +204,19 @@ class QlOs:
return retval
# TODO: separate this method into os-specific functionalities, instead of 'if-else'
def set_api(self, api_name: str, intercept_function: Callable, intercept: QL_INTERCEPT = QL_INTERCEPT.CALL):
"""Either replace or hook OS API with a custom one.
def set_api(self, target: Hashable, handler: Callable, intercept: QL_INTERCEPT = QL_INTERCEPT.CALL):
"""Either hook or replace an OS API with a custom one.
Args:
api_name: target API name
intercept_function: function to call
target: target API identifier
handler: function to call
intercept:
`QL_INTERCEPT.CALL` : run handler instead of the existing target implementation
`QL_INTERCEPT.ENTER`: run handler before the target API is called
`QL_INTERCEPT.EXIT` : run handler after the target API is called
"""
if self.ql.ostype == QL_OS.UEFI:
api_name = f'hook_{api_name}'
if (self.ql.ostype in (QL_OS.WINDOWS, QL_OS.UEFI, QL_OS.DOS)) or (self.ql.ostype in (QL_OS_POSIX) and self.ql.loader.is_driver):
self.user_defined_api[intercept][api_name] = intercept_function
else:
self.add_function_hook(api_name, intercept_function, intercept)
self.user_defined_api[intercept][target] = handler
# os main method; derivatives must implement one of their own
def run(self) -> None:
@@ -256,5 +252,6 @@ class QlOs:
finally:
self.ql.log.error(f'PC = {pc:#0{self.ql.arch.pointersize * 2 + 2}x}{pc_info}\n')
self.ql.log.info(f'Memory map:')
self.ql.mem.show_mapinfo()
self.ql.log.error(f'Memory map:')
for info_line in self.ql.mem.get_formatted_mapinfo():
self.ql.log.error(info_line)
+295 -125
View File
@@ -3,160 +3,330 @@
# Cross Platform and Multi Architecture Advanced Binary Emulation Framework
#
import os
from typing import Union
from pathlib import Path, PurePath, PurePosixPath, PureWindowsPath
from typing import Optional, Union
from pathlib import Path, PurePosixPath, PureWindowsPath
from qiling import Qiling
from qiling.const import QL_OS, QL_OS_POSIX
# OH-MY-WIN32 !!!
# Some codes from cygwin.
#
# Basic guide:
# We should only handle "normal" paths like "C:\Windows\System32" and "bin/a.exe" for users.
# For UNC paths like '\\.\PHYSICALDRIVE0" and "\\Server\Share", they should be implemented
# by users via fs mapping interface.
class QlPathManager:
def __init__(self, ql: Qiling, cwd: str):
self.ql = ql
self._cwd = cwd
AnyPurePath = Union[PurePosixPath, PureWindowsPath]
class QlOsPath:
"""Virtual to host path manipulations helper.
"""
def __init__(self, rootfs: str, cwd: str, emulos: QL_OS) -> None:
"""Initialize a path manipulation object.
Args:
rootfs : host path to serve as the virtual root directory
cwd : virtual current working directory
emuls : emulated operating system
"""
nt_path_os = (QL_OS.WINDOWS, QL_OS.DOS)
posix_path_os = QL_OS_POSIX
# rootfs is a local directory on the host, and expected to exist
self._rootfs_path = Path(rootfs).resolve(strict=True)
# determine how virtual paths should be handled
if emulos in nt_path_os:
self.PureVirtualPath = PureWindowsPath
elif emulos in posix_path_os:
self.PureVirtualPath = PurePosixPath
else:
raise ValueError(f'unexpected os type: {emulos}')
self.cwd = cwd
# <TEMPORARY>
self.transform_to_relative_path = self.virtual_abspath
self.transform_to_real_path = self.virtual_to_host_path
self.transform_to_link_path = self.virtual_to_host_path
# </TEMPORARY>
@staticmethod
def __strip_parent_refs(path: AnyPurePath) -> AnyPurePath:
"""Strip leading parent dir references, if any.
"""
if path.parts:
pardir = r'..'
while path.parts[0] == pardir:
path = path.relative_to(pardir)
return path
@property
def cwd(self) -> str:
return self._cwd
return str(self._cwd_anchor / self._cwd_vpath)
@cwd.setter
def cwd(self, c: str) -> None:
if not c.startswith('/'):
self.ql.log.warning(f'Sanity check: path does not start with a forward slash "/"')
def cwd(self, virtpath: str) -> None:
vpath = self.PureVirtualPath(virtpath)
self._cwd = c
if not vpath.is_absolute():
raise ValueError(f'current working directory must be an absolute path: {virtpath}')
@staticmethod
def normalize(path: Union[Path, PurePath]) -> Union[Path, PurePath]:
# expected types: PosixPath, PurePosixPath, WindowsPath, PureWindowsPath
assert isinstance(path, (Path, PurePath)), f'did not expect {type(path).__name__!r} here'
# extract the virtual path anchor so we can append cwd path to rootfs later on.
# however, we will still need the anchor to provide full virtual paths
cwd_anchor = self.PureVirtualPath(vpath.anchor)
cwd_vpath = vpath.relative_to(cwd_anchor)
normalized_path = type(path)()
cwd_vpath = QlOsPath.__strip_parent_refs(cwd_vpath)
# remove anchor (necessary for Windows UNC paths) and convert to relative path
if path.is_absolute():
path = path.relative_to(path.anchor)
self._cwd_anchor = cwd_anchor
self._cwd_vpath = cwd_vpath
for p in path.parts:
if p == '.':
continue
def __virtual_abspath(self, virtpath: Union[str, AnyPurePath]) -> AnyPurePath:
"""Get the absolute virtual path representation of a virtual path.
This method does not follow symbolic links or parent directory references.
if p == '..':
normalized_path = normalized_path.parent
continue
Args:
virtpath: virtual path to resolve, either relative or absolute
normalized_path /= p
Returns: An absolute virtual path
"""
return normalized_path
vpath = self.PureVirtualPath(virtpath)
@staticmethod
def convert_win32_to_posix(rootfs: Union[str, Path], cwd: str, path: str) -> Path:
_rootfs = Path(rootfs)
_cwd = PurePosixPath(cwd[1:])
if vpath.is_absolute():
return vpath
# rebase on top the current working directory. in case vpath is an absolute
# path, cwd will be discarded
absvpath = self._cwd_vpath / vpath
# referencing root's parent directory should circle back to root.
# remove any leading parent dir references absvpath might have
absvpath = QlOsPath.__strip_parent_refs(absvpath)
return self._cwd_anchor / absvpath
def __resolved_vsymlink(self, basepath: Path, name: str):
"""Attempt to resolve a virtual symbolic link.
A virtual symbolic link points to a location within the virtual file
system, not to be confused with paths on the host file system.
For example:
a vsymlink that points to '/var/tmp' should be resolved to
'my/rootfs/path/var/tmp' on the host
"""
fullpath = self._rootfs_path / basepath / name
vpath = None
if fullpath.is_symlink():
resolved = fullpath.resolve(strict=False)
try:
# the resolve method turns fullpath into an absolute path on the host,
# but we need it to be an absolute virtual path. to convert the host
# path to virtual we have to make sure it resides within rootfs.
#
# if the host path is indeed under rootfs, we can rebase the virtual
# portion on top of rootfs and return an absolute virtual path.
#
# returning an absolute path will discard the accumulated vpath.
vpath = self._cwd_anchor / resolved.relative_to(self._rootfs_path)
except ValueError:
# failing to convert the host path into a virtual one means that either
# the symbolic link is already an absolute virtual path, or it is pointing
# to an external directory on the host file system, which means it does
# not reflect a virtual path.
#
# on both cases we may return the path as-is, discarding the accumulated
# vpath.
#
# that, however, may not work in case the host filesystem and the virtual
# one are not of the same type (e.g. a virtual Windows path on top of a
# Linux host): the absolute path will not discard the accumulated vpath
# and will be appended - which is a corner case we do not know how to
# handle efficiently.
vpath = resolved
return vpath
# this will work only if hosting os = virtual os
def __virtual_resolve(self, virtpath: Union[str, AnyPurePath]) -> AnyPurePath:
"""Resolve a virtual path, including symbolic links and directory
references it might include. Path must not include circular symbolic
links.
Args:
virtpath: virtual path to resolve, either relative or absolute
Returns: An absolute virtual path
"""
vpath = self.PureVirtualPath(virtpath)
# if not already, turn vpath into an absolute path
if not vpath.is_absolute():
vpath = self.__virtual_abspath(vpath)
# accumulate paths as we progress through the resolution process.
#
# since symlink inspection and resolution can only be done on an
# actual file system, each step in the progress has to be translated
# into its correpsonding host path. that is the reason we keep track
# on the acumulated host path in parallel to the virtual one
#
# note: the reason we do not set acc_hpath to rootfs is to prevent
# parent dir refs from traversing beyond rootfs directory.
acc_hpath = Path()
acc_vpath = self.PureVirtualPath(vpath.anchor)
# eliminate virtual path's anchor to allow us accumulate its
# parts on top of rootfs
vpath = vpath.relative_to(vpath.anchor)
for part in vpath.parts:
if part == '..':
acc_hpath = acc_hpath.parent
acc_vpath = acc_vpath.parent
# Things are complicated here.
# See https://docs.microsoft.com/zh-cn/windows/win32/fileio/naming-a-file?redirectedfrom=MSDN
if PureWindowsPath(path).is_absolute():
if (len(path) >= 2 and path.startswith(r'\\')) or \
(len(path) >= 3 and path[0].isalpha() and path[1:3] == ':\\'): # \\.\PhysicalDrive0 or \\Server\Share\Directory or X:\
# UNC path should be handled in fs mapping. If not, append it to rootfs directly.
pw = PureWindowsPath(path)
result = _rootfs / QlPathManager.normalize(pw)
else:
# code should never reach here.
result = _rootfs / QlPathManager.normalize(path)
else:
if len(path) >= 3 and path[:3] == r'\\?' or path[:3] == r'\??': # \??\ or \\?\ or \Device\..
# Similair to \\.\, it should be handled in fs mapping.
pw = PureWindowsPath(path)
result = _rootfs / QlPathManager.normalize(_cwd / pw.relative_to(pw.anchor).as_posix())
else:
# a normal relative path
result = _rootfs / QlPathManager.normalize(_cwd / PureWindowsPath(path).as_posix())
# if this is a symlink attempt to resolve it
vtemp = self.__resolved_vsymlink(acc_hpath, part)
return result
# not a symlink; accumulate path part
if vtemp is None:
acc_hpath = acc_hpath / part
acc_vpath = acc_vpath / part
else:
# rebase it on top of the accumulated virtual path
new_vpath = acc_vpath / vtemp
# recursively resolve the new virtual path we got
vres = self.__virtual_resolve(new_vpath)
acc_hpath = Path(vres)
acc_vpath = vres
return acc_vpath
def __virtual_to_host_path(self, virtpath: Union[str, AnyPurePath]) -> Path:
"""Convert a virtual path to its corresponding path on the host.
This method partialy normalizes the virtual path and does not resolve
references to parent directories neither virtual symbolic links
"""
absvpath = self.__virtual_abspath(virtpath)
# remove path anchor to allow path to be rebased
vpath = absvpath.relative_to(absvpath.anchor)
# rebase virtual path on top of rootfs to get the host path
return self._rootfs_path / vpath
def __is_safe_host_path(self, hostpath: Path, strict: bool = False) -> bool:
"""Sanitize the specified host path and make sure it does not traverse out
of the rootfs directory hierarchy.
Args:
hostpath : a local path to sanitize
strict : whether to raise an error if target path does not exist
Returns: whether the path is safe to use
"""
# canonicalization before assertion: resolve any relative path references and
# symbolic links that may exist.
#
# in case strict is set to True and the path does not exist, a FileNotFoundError
# is raised. this error is left for the user to catch and handle
hostpath = hostpath.resolve(strict=strict)
try:
# to prevent path-traversal issues we have to make sure hostpath ended up
# as a subpath of rootfs. the following method will fail if that is not
# the case
_ = hostpath.relative_to(self._rootfs_path)
except ValueError:
return False
else:
return True
def virtual_abspath(self, virtpath: str) -> str:
"""Convert a relative virtual path to an absolute virtual path based
on the current working directory.
Args:
virtpath : relative virtual path
Returns: the absolute virtual path
"""
absvpath = self.__virtual_abspath(virtpath)
return str(absvpath)
def virtual_to_host_path(self, virtpath: str) -> str:
"""Convert a virtual path to its corresponding path on the hosting system.
Args:
virtpath : path on the emulated system. the path may be either absolute
or relative
Returns: the corresponding path on the hosting system
"""
absvpath = self.__virtual_resolve(virtpath)
hostpath = self.__virtual_to_host_path(absvpath)
return str(hostpath)
def is_safe_host_path(self, hostpath: str) -> bool:
hpath = Path(hostpath)
return self.__is_safe_host_path(hpath, strict=False)
@staticmethod
def convert_posix_to_win32(rootfs: Union[str, Path], cwd: str, path: str) -> Path:
_rootfs = Path(rootfs)
_cwd = PurePosixPath(cwd[1:])
_path = PurePosixPath(path)
def __host_casefold_path(hostpath: str) -> Optional[str]:
# assuming posix host
p = PurePosixPath(hostpath)
norm = Path(p.anchor)
if _path.is_absolute():
return _rootfs / QlPathManager.normalize(_path)
else:
return _rootfs / QlPathManager.normalize(_cwd / _path)
for elem in p.relative_to(norm).parts:
folded = elem.casefold()
@staticmethod
def convert_for_native_os(rootfs: Union[str, Path], cwd: str, path: str) -> Path:
_rootfs = Path(rootfs)
_cwd = PurePosixPath(cwd[1:])
_path = Path(path)
try:
norm = next(entry for entry in norm.iterdir() if entry.name.casefold() == folded)
except StopIteration:
return None
if _path.is_absolute():
return _rootfs / QlPathManager.normalize(_path)
else:
return _rootfs / QlPathManager.normalize(_cwd / _path.as_posix())
return str(norm)
def convert_path(self, rootfs: Union[str, Path], cwd: str, path: str) -> Path:
emulated_os = self.ql.ostype
hosting_os = self.ql.host.os
def host_casefold_path(self, hostpath: str) -> Optional[str]:
"""As opposed to POSIX paths, NT paths are case insensitive and may be specified
in multiple ways. When emulating an NT file system on top of POSIX one, virtual
NT paths and files might not be found because they are specified in a different
case than the one that is actually used on the hosting POSIX system.
# emulated os and hosting platform are of the same type
if (emulated_os == hosting_os) or (emulated_os in QL_OS_POSIX and hosting_os in QL_OS_POSIX):
return QlPathManager.convert_for_native_os(rootfs, cwd, path)
This method translates a case insensitive path into the actual case sensitive
name that is used on the hosting POSIX file system.
elif emulated_os in QL_OS_POSIX and hosting_os == QL_OS.WINDOWS:
return QlPathManager.convert_posix_to_win32(rootfs, cwd, path)
Args:
hostpath: a path on the host, case insensitive
elif emulated_os == QL_OS.WINDOWS and hosting_os in QL_OS_POSIX:
return QlPathManager.convert_win32_to_posix(rootfs, cwd, path)
Returns: the corresponding path on the host system, or None if the path does not
exist
"""
else:
return QlPathManager.convert_for_native_os(rootfs, cwd, path)
# only relevant if the emulated file system is NT-based
if self.PureVirtualPath is PureWindowsPath:
return QlOsPath.__host_casefold_path(hostpath)
def transform_to_link_path(self, path: str) -> str:
real_path = self.convert_path(self.ql.rootfs, self.cwd, path)
return hostpath
return str(real_path.absolute())
def transform_to_real_path(self, path: str) -> str:
# TODO: We really need a virtual file system.
real_path = self.convert_path(self.ql.rootfs, self.cwd, path)
if os.path.islink(real_path):
link_path = Path(os.readlink(real_path))
real_path = self.convert_path(os.path.dirname(real_path), "/", link_path)
if os.path.islink(real_path):
real_path = self.transform_to_real_path(real_path)
# resolve multilevel symbolic link
if not os.path.exists(real_path):
path_dirs = link_path.parts
if link_path.is_absolute():
path_dirs = path_dirs[1:]
for i in range(len(path_dirs) - 1):
path_prefix = os.path.sep.join(path_dirs[:i+1])
real_path_prefix = self.transform_to_real_path(path_prefix)
path_remain = os.path.sep.join(path_dirs[i+1:])
real_path = Path(os.path.join(real_path_prefix, path_remain))
if os.path.exists(real_path):
break
return str(real_path.absolute())
# The `relative path` here refers to the path which is relative to the rootfs.
def transform_to_relative_path(self, path: str) -> str:
return str(Path(self.cwd) / path)
+18
View File
@@ -515,6 +515,24 @@ linux_riscv_open_flags = {
'O_LARGEFILE': None,
}
linux_ppc_open_flags = {
'O_RDONLY': 0x0,
'O_WRONLY': 0x1,
'O_RDWR': 0x2,
'O_NONBLOCK': 0x800,
'O_APPEND': 0x400,
'O_ASYNC': 0x2000,
'O_SYNC': 0x101000,
'O_NOFOLLOW': 0x8000,
'O_CREAT': 0x40,
'O_TRUNC': 0x200,
'O_EXCL': 0x80,
'O_NOCTTY': 0x100,
'O_DIRECTORY': 0x4000,
'O_BINARY' : None,
'O_LARGEFILE': 0x10000,
}
freebsd_x86_open_flags = {
'O_RDONLY': 0x0,
'O_WRONLY': 0x1,
+24 -16
View File
@@ -3,11 +3,12 @@
# Cross Platform and Multi Architecture Advanced Binary Emulation Framework
#
from typing import MutableSequence
from .const import *
from typing import Mapping, MutableSequence
from qiling import Qiling
from qiling.const import *
from qiling.const import QL_ARCH, QL_OS
from .const import *
def _invert_dict(d: Mapping) -> Mapping:
return { v:k for k, v in d.items()}
@@ -34,22 +35,27 @@ def _constant_mapping(bits: int, d_map: Mapping[str, int], ret: MutableSequence[
def ql_open_flag_mapping(ql: Qiling, flags):
def flag_mapping(flags, mapping_name, mapping_from, mapping_to):
def flag_mapping(flags, mapping_name, mapping_from, mapping_to, host_os, virt_os):
ret = 0
for n in mapping_name:
if mapping_from[n] is None or mapping_to[n] is None:
continue
if (flags & mapping_from[n]) == mapping_from[n]:
ret = ret | mapping_to[n]
if (host_os == QL_OS.WINDOWS and virt_os != QL_OS.WINDOWS):
ret = ret | mapping_to['O_BINARY']
return ret
f = {}
t = {}
if ql.host.os == None:
host_os = ql.host.os
virt_os = ql.os.type
if host_os is None:
return flags
if ql.ostype == QL_OS.LINUX:
if virt_os == QL_OS.LINUX:
if ql.arch.type in (QL_ARCH.X86, QL_ARCH.X8664):
f = linux_x86_open_flags
elif ql.arch.type in (QL_ARCH.ARM, QL_ARCH.ARM64):
@@ -58,30 +64,32 @@ def ql_open_flag_mapping(ql: Qiling, flags):
f = linux_mips_open_flags
elif ql.arch.type in (QL_ARCH.RISCV, QL_ARCH.RISCV64):
f = linux_riscv_open_flags
elif ql.arch.type == QL_ARCH.PPC:
f = linux_ppc_open_flags
elif ql.ostype == QL_OS.MACOS:
elif virt_os == QL_OS.MACOS:
if ql.arch.type in (QL_ARCH.X86, QL_ARCH.X8664):
f = macos_x86_open_flags
elif ql.ostype == QL_OS.FREEBSD:
elif virt_os == QL_OS.FREEBSD:
f = freebsd_x86_open_flags
elif ql.ostype == QL_OS.WINDOWS:
elif virt_os == QL_OS.WINDOWS:
f = windows_x86_open_flags
elif ql.ostype == QL_OS.QNX:
elif virt_os == QL_OS.QNX:
f = qnx_arm64_open_flags
if ql.host.os == QL_OS.LINUX:
if host_os == QL_OS.LINUX:
t = linux_x86_open_flags
elif ql.host.os == QL_OS.MACOS:
elif host_os == QL_OS.MACOS:
t = macos_x86_open_flags
elif ql.host.os == QL_OS.FREEBSD:
elif host_os == QL_OS.FREEBSD:
t = freebsd_x86_open_flags
elif ql.host.os == QL_OS.WINDOWS:
elif host_os == QL_OS.WINDOWS:
t = windows_x86_open_flags
if f == t:
return flags
return flag_mapping(flags, open_flags_name, f, t)
return flag_mapping(flags, open_flags_name, f, t, host_os, virt_os)
def mmap_flag_mapping(flags):
+69 -19
View File
@@ -4,27 +4,30 @@
#
from inspect import signature, Parameter
from typing import TextIO, Union, Callable
from typing import TextIO, Union, Callable, IO, List, Optional
from unicorn.arm64_const import UC_ARM64_REG_X8, UC_ARM64_REG_X16
from unicorn.arm_const import UC_ARM_REG_R7
from unicorn.arm_const import (
UC_ARM_REG_R0, UC_ARM_REG_R1, UC_ARM_REG_R2, UC_ARM_REG_R3,
UC_ARM_REG_R4, UC_ARM_REG_R5, UC_ARM_REG_R7, UC_ARM_REG_R12
)
from unicorn.mips_const import UC_MIPS_REG_V0
from unicorn.x86_const import UC_X86_REG_EAX, UC_X86_REG_RAX
from unicorn.x86_const import (
UC_X86_REG_EAX, UC_X86_REG_EBX, UC_X86_REG_ECX, UC_X86_REG_EDX,
UC_X86_REG_ESI, UC_X86_REG_EDI, UC_X86_REG_EBP, UC_X86_REG_RDI,
UC_X86_REG_RSI, UC_X86_REG_RDX, UC_X86_REG_R10, UC_X86_REG_R8,
UC_X86_REG_R9, UC_X86_REG_RAX
)
from unicorn.riscv_const import UC_RISCV_REG_A7
from unicorn.ppc_const import UC_PPC_REG_0
from qiling import Qiling
from qiling.cc import QlCC, intel, arm, mips, riscv
from qiling.cc import QlCC, intel, arm, mips, riscv, ppc
from qiling.const import QL_ARCH, QL_OS, QL_INTERCEPT
from qiling.exception import QlErrorSyscallNotFound
from qiling.os.os import QlOs
from qiling.os.posix.const import errors
from qiling.utils import QlFileDes, ostype_convert_str, ql_get_module_function, ql_syscall_mapping_function
from qiling.os.posix.syscall import *
from qiling.os.linux.syscall import *
from qiling.os.macos.syscall import *
from qiling.os.freebsd.syscall import *
from qiling.os.qnx.syscall import *
from qiling.os.posix.const import NR_OPEN, errors
from qiling.utils import ql_get_module, ql_get_module_function
SYSCALL_PREF: str = f'ql_syscall_'
@@ -58,6 +61,36 @@ class riscv32(riscv.riscv):
class riscv64(riscv.riscv):
pass
class ppc(ppc.ppc):
pass
class QlFileDes:
def __init__(self):
self.__fds: List[Optional[IO]] = [None] * NR_OPEN
def __len__(self):
return len(self.__fds)
def __getitem__(self, idx: Union[slice, int]):
return self.__fds[idx]
def __setitem__(self, idx: int, val: Optional[IO]):
self.__fds[idx] = val
def __iter__(self):
return iter(self.__fds)
def __repr__(self):
return repr(self.__fds)
def save(self):
return self.__fds
def restore(self, fds):
self.__fds = fds
class QlOsPosix(QlOs):
def __init__(self, ql: Qiling):
@@ -86,14 +119,15 @@ class QlOsPosix(QlOs):
QL_ARCH.X86 : UC_X86_REG_EAX,
QL_ARCH.X8664 : UC_X86_REG_RAX,
QL_ARCH.RISCV : UC_RISCV_REG_A7,
QL_ARCH.RISCV64 : UC_RISCV_REG_A7
QL_ARCH.RISCV64 : UC_RISCV_REG_A7,
QL_ARCH.PPC : UC_PPC_REG_0
}[self.ql.arch.type]
# handle some special cases
if (self.ql.arch.type == QL_ARCH.ARM64) and (self.ql.ostype == QL_OS.MACOS):
if (self.ql.arch.type == QL_ARCH.ARM64) and (self.type == QL_OS.MACOS):
self.__syscall_id_reg = UC_ARM64_REG_X16
elif (self.ql.arch.type == QL_ARCH.ARM) and (self.ql.ostype == QL_OS.QNX):
elif (self.ql.arch.type == QL_ARCH.ARM) and (self.type == QL_OS.QNX):
self.__syscall_id_reg = UC_ARM_REG_R12
# TODO: use abstract to access __syscall_cc and __syscall_id_reg by defining a system call class
@@ -104,11 +138,12 @@ class QlOsPosix(QlOs):
QL_ARCH.X86 : intel32,
QL_ARCH.X8664 : intel64,
QL_ARCH.RISCV : riscv32,
QL_ARCH.RISCV64 : riscv64
QL_ARCH.RISCV64 : riscv64,
QL_ARCH.PPC : ppc
}[self.ql.arch.type](self.ql.arch)
# select syscall mapping function based on emulated OS and architecture
self.syscall_mapper = ql_syscall_mapping_function(self.ql.ostype, self.ql.arch.type)
self.syscall_mapper = self.__get_syscall_mapper(self.ql.arch.type)
self._fd = QlFileDes()
@@ -121,6 +156,14 @@ class QlOsPosix(QlOs):
self._shms = {}
def __get_syscall_mapper(self, archtype: QL_ARCH):
qlos_path = f'.os.{self.type.name.lower()}.map_syscall'
qlos_func = 'get_syscall_mapper'
func = ql_get_module_function(qlos_path, qlos_func)
return func(archtype)
@QlOs.stdin.setter
def stdin(self, stream: TextIO) -> None:
self._stdin = stream
@@ -162,6 +205,13 @@ class QlOsPosix(QlOs):
self.posix_syscall_hooks[intercept][target] = handler
def set_api(self, target: str, handler: Callable, intercept: QL_INTERCEPT = QL_INTERCEPT.CALL):
if self.ql.loader.is_driver:
super().set_api(target, handler, intercept)
else:
self.function_hook.add_function_hook(target, handler, intercept)
@staticmethod
def getNameFromErrorCode(ret: int) -> str:
"""Return the hex representation of a return value and if possible
@@ -195,9 +245,9 @@ class QlOsPosix(QlOs):
if not syscall_hook:
def __get_os_module(osname: str):
return ql_get_module_function(f'qiling.os.{osname.lower()}', 'syscall')
return ql_get_module(f'.os.{osname.lower()}.syscall')
os_syscalls = __get_os_module(ostype_convert_str(self.ql.ostype))
os_syscalls = __get_os_module(self.type.name)
posix_syscalls = __get_os_module('posix')
# look in os-specific and posix syscall hooks

Some files were not shown because too many files have changed in this diff Show More