mirror of
https://github.com/qilingframework/qiling.git
synced 2026-10-12 02:21:40 +00:00
Merge 'dev' into next
This commit is contained in:
@@ -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
|
||||
|
||||
|
||||
@@ -53,6 +53,8 @@
|
||||
- madprogrammer
|
||||
- danielmoos
|
||||
- sigeryang
|
||||
- bet4it
|
||||
- nullableVoidPtr
|
||||
|
||||
|
||||
#### Legacy Core Developers
|
||||
|
||||
@@ -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
@@ -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
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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}
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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()
|
||||
|
||||
+1
-1
Submodule examples/rootfs updated: 00760d170e...15c8d84287
+5
-1
@@ -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()
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
@@ -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']
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
# NOTE: use "-dev" for dev branch
|
||||
#__version__ = "1.4.3"
|
||||
__version__ = "1.4.3" + "-dev"
|
||||
+6
-12
@@ -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:
|
||||
|
||||
@@ -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
@@ -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
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
@@ -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
@@ -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)
|
||||
|
||||
@@ -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
@@ -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'
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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:
|
||||
|
||||
@@ -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
@@ -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)
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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),
|
||||
|
||||
@@ -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
@@ -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]
|
||||
|
||||
@@ -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
File diff suppressed because it is too large
Load Diff
@@ -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)
|
||||
|
||||
@@ -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>
|
||||
@@ -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,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>
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
@@ -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']
|
||||
@@ -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:
|
||||
|
||||
@@ -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):
|
||||
|
||||
@@ -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)])}")
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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):
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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]
|
||||
|
||||
@@ -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):
|
||||
|
||||
@@ -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
File diff suppressed because it is too large
Load Diff
+188
@@ -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']
|
||||
+13
-7
@@ -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)
|
||||
|
||||
@@ -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
@@ -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)
|
||||
|
||||
@@ -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()
|
||||
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
@@ -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)
|
||||
|
||||
@@ -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
@@ -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
|
||||
|
||||
@@ -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):
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
@@ -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
|
||||
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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)
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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
@@ -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
Reference in New Issue
Block a user