mirror of
https://github.com/qilingframework/qiling.git
synced 2026-10-12 02:21:40 +00:00
Merge remote-tracking branch 'qiling.io/dev' into fix_thumb_emu
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
|
||||
|
||||
|
||||
+3
-2
@@ -23,7 +23,7 @@
|
||||
#### CI, Website,Documentations, Logo & Swags
|
||||
|
||||
- FOO Kevin (chfl4gs) <chfl4gs_at_qiling_io>
|
||||
- SU muchen (miraisuu) <suu_at_iling_io>
|
||||
- SU muchen (miraisuu) <suu_at_qiling_io>
|
||||
|
||||
|
||||
#### Key Contributors (in no particular order)
|
||||
@@ -36,6 +36,7 @@
|
||||
- Mark Jansen (learn-more)
|
||||
- cq674350529
|
||||
- bkerler (viperbjk)
|
||||
- bet4it
|
||||
|
||||
|
||||
#### Contributors (in no particular order)
|
||||
@@ -53,7 +54,7 @@
|
||||
- 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,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,7 +12,7 @@
|
||||
|
||||
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-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/)
|
||||
|
||||
+1
-1
Submodule examples/rootfs updated: 76780c9e91...15c8d84287
@@ -57,6 +57,7 @@ CALL :collect_dll32 kdcom.dll
|
||||
CALL :collect_dll32 kernel32.dll
|
||||
CALL :collect_dll32 KernelBase.dll
|
||||
CALL :collect_dll32 mpr.dll
|
||||
CALL :collect_dll32 mscoree.dll
|
||||
CALL :collect_dll32 msvcp_win.dll
|
||||
CALL :collect_dll32 msvcp60.dll
|
||||
CALL :collect_dll32 msvcr120_clr0400.dll, msvcr110.dll
|
||||
@@ -83,7 +84,6 @@ CALL :collect_dll32 version.dll
|
||||
CALL :collect_dll32 win32u.dll
|
||||
CALL :collect_dll32 winhttp.dll
|
||||
CALL :collect_dll32 wininet.dll
|
||||
CALL :collect_dll32 wininet.dll
|
||||
CALL :collect_dll32 winmm.dll
|
||||
CALL :collect_dll32 ws2_32.dll
|
||||
CALL :collect_dll32 wsock32.dll
|
||||
@@ -103,16 +103,24 @@ CALL :collect_dll64 advapi32.dll
|
||||
CALL :collect_dll64 gdi32.dll
|
||||
CALL :collect_dll64 kernel32.dll
|
||||
CALL :collect_dll64 KernelBase.dll
|
||||
CALL :collect_dll64 mscoree.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
|
||||
|
||||
@@ -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='*')
|
||||
|
||||
+6
-1
@@ -1,4 +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]
|
||||
|
||||
+18
-4
@@ -3,10 +3,13 @@
|
||||
# Cross Platform and Multi Architecture Advanced Binary Emulation Framework
|
||||
|
||||
|
||||
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 *
|
||||
from ..arch import QlArch
|
||||
from .vm.evm import QlArchEVMEmulator
|
||||
from .hooks import monkeypath_core_hooks
|
||||
|
||||
class QlArchEVM(QlArch):
|
||||
type = QL_ARCH.EVM
|
||||
@@ -16,7 +19,8 @@ class QlArchEVM(QlArch):
|
||||
super(QlArchEVM, self).__init__(ql)
|
||||
self.evm = QlArchEVMEmulator(self.ql)
|
||||
|
||||
monkeypath_core_hooks(self.ql)
|
||||
monkeypatch_core_hooks(self.ql)
|
||||
monkeypatch_core_methods(self.ql)
|
||||
|
||||
def run(self, msg):
|
||||
return self.evm.vm.execute_message(msg)
|
||||
@@ -40,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)
|
||||
|
||||
@@ -63,8 +63,8 @@ def __evm_hook_del(ql, hret):
|
||||
if not hooks_list:
|
||||
del evm_hooks_info.hook_addr_dict[h.addr]
|
||||
|
||||
def monkeypath_core_hooks(ql):
|
||||
"""Monkeypath core hooks for evm
|
||||
def monkeypatch_core_hooks(ql):
|
||||
"""Monkeypatch core hooks for evm
|
||||
"""
|
||||
|
||||
ql.hook_code = types.MethodType(__evm_hook_code, ql)
|
||||
|
||||
+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
|
||||
}
|
||||
|
||||
@@ -102,12 +102,6 @@ class QlRegisterManager:
|
||||
|
||||
return self.uc.reg_write(self.uc_pc, value)
|
||||
|
||||
@property
|
||||
def arch_pc_name(self) -> str:
|
||||
"""Get the architectural program counter register name.
|
||||
"""
|
||||
|
||||
return next(k for k, v in self.register_mapping.items() if v == self.uc_pc)
|
||||
|
||||
@property
|
||||
def arch_sp(self) -> int:
|
||||
|
||||
+4
-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,
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
|
||||
from abc import abstractmethod
|
||||
from typing import Optional
|
||||
|
||||
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:
|
||||
@@ -32,7 +33,7 @@ class GDTArray:
|
||||
|
||||
self.mem.write(self.base + (index * self.entsize), data)
|
||||
|
||||
def get_next_free(self, start: int = None, end: int = None) -> int:
|
||||
def get_next_free(self, start: Optional[int] = None, end: Optional[int] = None) -> int:
|
||||
# The Linux kernel determines whether the segment is empty by judging whether the content in the current GDT segment is 0.
|
||||
null_entry = b'\x00' * self.entsize
|
||||
|
||||
@@ -50,8 +51,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))
|
||||
@@ -110,7 +113,7 @@ class GDTManager:
|
||||
def set_entry(self, index: int, data: bytes) -> None:
|
||||
self.array[index] = data
|
||||
|
||||
def get_free_idx(self, start: int = None, end: int = None) -> int:
|
||||
def get_free_idx(self, start: Optional[int] = None, end: Optional[int] = None) -> int:
|
||||
return self.array.get_next_free(start, end)
|
||||
|
||||
|
||||
|
||||
+7
-3
@@ -60,9 +60,8 @@ class QL_STOP(Flag):
|
||||
|
||||
QL_ARCH_INTERPRETER = (QL_ARCH.EVM,)
|
||||
|
||||
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_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
|
||||
@@ -83,3 +82,8 @@ 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'
|
||||
]
|
||||
|
||||
+20
-19
@@ -30,22 +30,22 @@ 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
|
||||
):
|
||||
@@ -100,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
|
||||
@@ -414,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:
|
||||
@@ -540,17 +537,21 @@ 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 = select_debugger(self._debugger)
|
||||
@@ -565,7 +566,7 @@ class Qiling(QlCoreHooks, QlCoreStructs):
|
||||
if self.count <= 0:
|
||||
self.count = -1
|
||||
|
||||
self.arch.run(count=self.count, end=self.exit_point)
|
||||
self.arch.run(count=self.count, end=self.exit_point)
|
||||
else:
|
||||
self.write_exit_trap()
|
||||
# emulate the binary
|
||||
|
||||
+345
-31
@@ -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,10 +234,10 @@ class QlCoreHooks:
|
||||
###############
|
||||
# Class Hooks #
|
||||
###############
|
||||
def _ql_hook_internal(self, hook_type, callback, user_data=None, *args) -> int:
|
||||
def _ql_hook_internal(self, hook_type: int, callback: Callable, context: Any, *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)
|
||||
|
||||
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:
|
||||
@@ -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,51 +318,202 @@ 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):
|
||||
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):
|
||||
def hook_address(self, callback: AddressHookCallback, address: int, user_data: Any = None) -> HookRet:
|
||||
"""Intercept execution from a certain memory 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:
|
||||
@@ -287,37 +524,114 @@ 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):
|
||||
return self.ql_hook(UC_HOOK_INSN, callback, user_data, begin, end, arg1)
|
||||
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.
|
||||
|
||||
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, hret: HookRet):
|
||||
def hook_del(self, hret: HookRet) -> None:
|
||||
"""Unregister an existing hook and release its resources.
|
||||
|
||||
Args:
|
||||
hret : hook handle
|
||||
"""
|
||||
|
||||
h = hret.obj
|
||||
hook_type = hret.type
|
||||
|
||||
|
||||
+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)])}")
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -12,8 +12,8 @@ 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.os.type)]
|
||||
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.strings:
|
||||
|
||||
@@ -97,7 +97,7 @@ def __to_trace_line(record: TraceRecord, symsmap: Mapping[int, str] = {}) -> str
|
||||
"""
|
||||
|
||||
if op.type == CS_OP_REG:
|
||||
return insn.reg_name(op.value.reg)
|
||||
return insn.reg_name(op.value.reg) or '?'
|
||||
|
||||
elif op.type == CS_OP_IMM:
|
||||
imm = op.value.imm
|
||||
@@ -113,6 +113,7 @@ def __to_trace_line(record: TraceRecord, symsmap: Mapping[int, str] = {}) -> str
|
||||
disp = mem.disp
|
||||
|
||||
ea = base + index * scale + disp
|
||||
seg = f'{insn.reg_name(mem.segment)}:' if mem.segment else ''
|
||||
|
||||
# we construct the string representation for each operand; denote memory
|
||||
# dereferenes with the appropriate 'ptr' prefix. the 'lea' instruction is
|
||||
@@ -131,7 +132,7 @@ def __to_trace_line(record: TraceRecord, symsmap: Mapping[int, str] = {}) -> str
|
||||
|
||||
qualifier = f'{ptr} ptr '
|
||||
|
||||
return f'{qualifier}[{__resolve(ea) or f"{ea:#x}"}]'
|
||||
return f'{qualifier}{seg}[{__resolve(ea) or f"{ea:#x}"}]'
|
||||
|
||||
# unexpected op type
|
||||
raise RuntimeError
|
||||
|
||||
+29
-26
@@ -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
|
||||
@@ -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
|
||||
@@ -365,7 +368,7 @@ class QlLoaderELF(QlLoader):
|
||||
_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]")
|
||||
|
||||
@@ -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):
|
||||
|
||||
+14
-8
@@ -31,10 +31,10 @@ class QlPeCacheEntry(NamedTuple):
|
||||
class QlPeCache:
|
||||
@staticmethod
|
||||
def cache_filename(path: str) -> str:
|
||||
dirname, basename = ntpath.split(path)
|
||||
dirname, basename = os.path.split(path)
|
||||
|
||||
# canonicalize basename while preserving the path
|
||||
path = ntpath.join(dirname, basename.casefold())
|
||||
path = os.path.join(dirname, basename.casefold())
|
||||
|
||||
return f'{path}.cache2'
|
||||
|
||||
@@ -248,7 +248,12 @@ class Process:
|
||||
# in case of a dll loaded from a hooked API call, failures would not be
|
||||
# recoverable and we have to give up its DllMain.
|
||||
if not self.ql.os.PE_RUN:
|
||||
|
||||
# temporarily set PE_RUN to allow proper fcall unwinding during
|
||||
# execution of DllMain
|
||||
self.ql.os.PE_RUN = True
|
||||
self.call_dll_entrypoint(dll, dll_base, dll_len, dll_name)
|
||||
self.ql.os.PE_RUN = False
|
||||
|
||||
self.ql.log.info(f'Done loading {dll_name}')
|
||||
|
||||
@@ -622,7 +627,9 @@ class Process:
|
||||
user_shared_data_obj = KUSER_SHARED_DATA()
|
||||
user_shared_data_size = ctypes.sizeof(KUSER_SHARED_DATA)
|
||||
|
||||
self.ql.mem.map(addr, self.ql.mem.align_up(user_shared_data_size))
|
||||
# TODO: initialize key fields in this structure
|
||||
|
||||
self.ql.mem.map(addr, self.ql.mem.align_up(user_shared_data_size), info='[kuser shared]')
|
||||
self.ql.mem.write(addr, bytes(user_shared_data_obj))
|
||||
|
||||
def init_security_cookie(self, pe: pefile.PE, image_base: int):
|
||||
@@ -657,6 +664,7 @@ class QlLoaderPE(QlLoader, Process):
|
||||
self.sys_dlls = (
|
||||
'ntdll.dll',
|
||||
'kernel32.dll',
|
||||
'mscoree.dll',
|
||||
'ucrtbase.dll'
|
||||
)
|
||||
|
||||
@@ -695,7 +703,6 @@ class QlLoaderPE(QlLoader, Process):
|
||||
cmdline = ntpath.join(self.ql.os.userprofile, 'Desktop', self.ql.targetname)
|
||||
cmdargs = ' '.join(f'"{arg}"' if ' ' in arg else arg for arg in self.argv[1:])
|
||||
|
||||
self.filepath = bytes(f'{cmdline}\x00', "utf-8")
|
||||
self.cmdline = bytes(f'{cmdline} {cmdargs}\x00', "utf-8")
|
||||
|
||||
self.load(pe)
|
||||
@@ -722,14 +729,13 @@ class QlLoaderPE(QlLoader, Process):
|
||||
self.ql.log.info(f'Loading {self.path} to {image_base:#x}')
|
||||
self.ql.log.info(f'PE entry point at {self.entry_point:#x}')
|
||||
|
||||
self.ql.mem.map(image_base, image_size, info=f'[{image_name}]')
|
||||
self.ql.mem.map(image_base, image_size, info=f'{image_name}')
|
||||
self.images.append(Image(image_base, image_base + pe.NT_HEADERS.OPTIONAL_HEADER.SizeOfImage, os.path.abspath(self.path)))
|
||||
|
||||
if self.is_driver:
|
||||
self.init_driver_object()
|
||||
self.init_registry_path()
|
||||
self.init_eprocess()
|
||||
self.init_ki_user_shared_data()
|
||||
|
||||
# set IRQ Level in CR8 to PASSIVE_LEVEL
|
||||
self.ql.arch.regs.write(UC_X86_REG_CR8, 0)
|
||||
@@ -748,6 +754,8 @@ class QlLoaderPE(QlLoader, Process):
|
||||
# add image to ldr table
|
||||
self.add_ldr_data_table_entry(image_name)
|
||||
|
||||
self.init_ki_user_shared_data()
|
||||
|
||||
pe.parse_data_directories()
|
||||
|
||||
# done manipulating pe file; write its contents into memory
|
||||
@@ -817,8 +825,6 @@ class QlLoaderPE(QlLoader, Process):
|
||||
self.ql.os.fcall.call_native(self.entry_point, args, None)
|
||||
|
||||
elif pe is None:
|
||||
self.filepath = b""
|
||||
|
||||
self.ql.mem.map(self.entry_point, self.ql.os.code_ram_size, info="[shellcode]")
|
||||
|
||||
self.init_teb()
|
||||
|
||||
+2
-1
@@ -7,6 +7,7 @@ import copy
|
||||
import logging
|
||||
import os
|
||||
import re
|
||||
import weakref
|
||||
|
||||
from typing import Optional, TextIO
|
||||
|
||||
@@ -38,7 +39,7 @@ class QlBaseFormatter(logging.Formatter):
|
||||
|
||||
def __init__(self, ql, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
self.ql = ql
|
||||
self.ql = weakref.proxy(ql)
|
||||
|
||||
def get_level_tag(self, level: str) -> str:
|
||||
return self.__level_tag[level]
|
||||
|
||||
@@ -58,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)
|
||||
|
||||
+16
-1
@@ -2,7 +2,7 @@
|
||||
#
|
||||
# Cross Platform and Multi Architecture Advanced Binary Emulation Framework
|
||||
|
||||
from typing import Any, Callable, Iterable, MutableMapping, Optional, Mapping, Tuple, Sequence
|
||||
from typing import Any, Callable, Iterable, Iterator, MutableMapping, Optional, Mapping, Tuple, Sequence
|
||||
|
||||
from qiling import Qiling
|
||||
from qiling.cc import QlCC
|
||||
@@ -50,6 +50,21 @@ class QlFunctionCall:
|
||||
# let the user override default accessors or add custom ones
|
||||
self.accessors.update(accessors)
|
||||
|
||||
def readEllipsis(self, ptypes: Sequence[Any]) -> Iterator[int]:
|
||||
"""
|
||||
"""
|
||||
|
||||
default = self.accessors[PARAM_INTN]
|
||||
|
||||
# count skipped slots
|
||||
si = sum(self.accessors.get(typ, default)[2] for typ in ptypes)
|
||||
|
||||
while True:
|
||||
read, _, nslots = default
|
||||
|
||||
yield read(si)
|
||||
si += nslots
|
||||
|
||||
def readParams(self, ptypes: Sequence[Any]) -> Sequence[int]:
|
||||
"""Walk the function parameters list and get their values.
|
||||
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -31,12 +31,10 @@ def hook_printk(ql: Qiling, address: int, params):
|
||||
return 0
|
||||
|
||||
level = int(format[1]) if format[1].isdigit() else 4
|
||||
nargs = format.count("%")
|
||||
ptypes = (POINTER, ) + (PARAM_INTN, ) * nargs
|
||||
args = ql.os.fcall.readParams(ptypes)[1:]
|
||||
args = ql.os.fcall.readEllipsis(params.values())
|
||||
|
||||
count = ql.os.utils.printf(f'{PRINTK_LEVEL[level]} {format[2:]}', args, wstring=False)
|
||||
ql.os.utils.update_ellipsis(params, args)
|
||||
count, upd_args = ql.os.utils.printf(f'{PRINTK_LEVEL[level]} {format[2:]}', args, wstring=False)
|
||||
upd_args(params)
|
||||
|
||||
return count
|
||||
|
||||
|
||||
@@ -14,6 +14,7 @@ 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.linux.procfs import QlProcFS
|
||||
from qiling.os.posix.posix import QlOsPosix
|
||||
|
||||
from . import futex
|
||||
@@ -118,6 +119,14 @@ class QlOsLinux(QlOsPosix):
|
||||
if getattr(self.fd[i], 'close_on_exec', 0):
|
||||
self.fd[i] = None
|
||||
|
||||
|
||||
def setup_procfs(self):
|
||||
self.fs_mapper.add_fs_mapping(r'/proc/self/auxv', QlProcFS.self_auxv(self))
|
||||
self.fs_mapper.add_fs_mapping(r'/proc/self/cmdline', QlProcFS.self_cmdline(self))
|
||||
self.fs_mapper.add_fs_mapping(r'/proc/self/environ', QlProcFS.self_environ(self))
|
||||
self.fs_mapper.add_fs_mapping(r'/proc/self/exe', QlProcFS.self_exe(self))
|
||||
|
||||
|
||||
def hook_syscall(self, ql, intno = None):
|
||||
return self.load_syscall()
|
||||
|
||||
@@ -133,6 +142,10 @@ class QlOsLinux(QlOsPosix):
|
||||
|
||||
|
||||
def run(self):
|
||||
# do not set-up procfs for drivers and shellcode
|
||||
if not self.ql.code and not self.ql.loader.is_driver:
|
||||
self.setup_procfs()
|
||||
|
||||
if self.ql.exit_point is not None:
|
||||
self.exit_point = self.ql.exit_point
|
||||
|
||||
|
||||
@@ -0,0 +1,119 @@
|
||||
|
||||
from typing import TYPE_CHECKING, AnyStr, Optional, Sized
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from qiling.os.linux.linux import QlOsLinux
|
||||
|
||||
|
||||
class QlFileSeekable:
|
||||
|
||||
def __init__(self):
|
||||
self.buff: Sized
|
||||
self.pos = 0
|
||||
|
||||
def seek(self, offset: int, whence: int) -> int:
|
||||
assert whence in (0, 1, 2)
|
||||
|
||||
# SEEK_SET
|
||||
if whence == 0:
|
||||
pos = offset
|
||||
|
||||
# SEEK_CUR
|
||||
elif whence == 1:
|
||||
pos = self.pos + offset
|
||||
|
||||
# SEEK_END
|
||||
elif whence == 2:
|
||||
pos = len(self.buff) + offset
|
||||
|
||||
# make sure pos is within reasonabe boundaries
|
||||
self.pos = min(max(pos, 0), len(self.buff))
|
||||
|
||||
return self.pos
|
||||
|
||||
def ftell(self) -> int:
|
||||
return self.pos
|
||||
|
||||
|
||||
class QlFileReadable:
|
||||
|
||||
def __init__(self, *, content: Optional[bytearray] = None):
|
||||
self.buff = content or bytearray()
|
||||
self.pos = 0
|
||||
|
||||
def read(self, length: int = -1) -> bytes:
|
||||
if length == -1:
|
||||
length = len(self.buff)
|
||||
|
||||
content = self.buff[self.pos:length]
|
||||
self.pos = min(self.pos + length, len(self.buff))
|
||||
|
||||
return bytes(content)
|
||||
|
||||
|
||||
class QlFileProcFS(QlFileReadable, QlFileSeekable):
|
||||
|
||||
def __init__(self, content: bytearray):
|
||||
QlFileReadable.__init__(self, content=content)
|
||||
QlFileSeekable.__init__(self)
|
||||
|
||||
def __enter__(self):
|
||||
return self
|
||||
|
||||
def __exit__(self, exc_type, exc_value, exc_tb):
|
||||
self.close()
|
||||
|
||||
def close(self):
|
||||
pass
|
||||
|
||||
|
||||
class QlProcFS:
|
||||
|
||||
@staticmethod
|
||||
def self_auxv(os: 'QlOsLinux') -> QlFileProcFS:
|
||||
nbytes = os.ql.arch.bits // 8
|
||||
|
||||
auxv_addr = os.ql.loader.auxv
|
||||
null_entry = bytes(nbytes * 2)
|
||||
|
||||
auxv_data = bytearray()
|
||||
|
||||
# keep reading until AUXV.AT_NULL is reached
|
||||
while not auxv_data.endswith(null_entry):
|
||||
auxv_data.extend(os.ql.mem.read(auxv_addr, nbytes))
|
||||
auxv_addr += nbytes
|
||||
|
||||
auxv_data.extend(os.ql.mem.read(auxv_addr, nbytes))
|
||||
auxv_addr += nbytes
|
||||
|
||||
return QlFileProcFS(content=auxv_data)
|
||||
|
||||
|
||||
@staticmethod
|
||||
def self_cmdline(os: 'QlOsLinux') -> QlFileProcFS:
|
||||
entries = (arg.encode('utf-8') for arg in os.ql.argv)
|
||||
cmdline = bytearray(b'\x00'.join(entries) + b'\x00')
|
||||
|
||||
return QlFileProcFS(content=cmdline)
|
||||
|
||||
|
||||
@staticmethod
|
||||
def self_environ(os: 'QlOsLinux') -> QlFileProcFS:
|
||||
def __to_bytes(s: AnyStr) -> bytes:
|
||||
if isinstance(s, str):
|
||||
return s.encode('utf-8')
|
||||
|
||||
return s
|
||||
|
||||
entries = (b'='.join((__to_bytes(k), __to_bytes(v))) for k, v in os.ql.env.items())
|
||||
environ = bytearray(b'\x00'.join(entries) + b'\x00')
|
||||
|
||||
return QlFileProcFS(content=environ)
|
||||
|
||||
|
||||
@staticmethod
|
||||
def self_exe(os: 'QlOsLinux') -> QlFileProcFS:
|
||||
with open(os.ql.path, 'rb') as exefile:
|
||||
content = bytearray(exefile.read())
|
||||
|
||||
return QlFileProcFS(content=content)
|
||||
+14
-13
@@ -11,10 +11,10 @@ from abc import abstractmethod
|
||||
from unicorn.unicorn import UcError
|
||||
|
||||
from qiling import Qiling
|
||||
from qiling.const import QL_ARCH
|
||||
from qiling.os.thread import *
|
||||
from qiling.arch.x86_const import *
|
||||
from qiling.exception import QlErrorExecutionStop
|
||||
from qiling.os.path import QlOsPath
|
||||
|
||||
LINUX_THREAD_ID = 2000
|
||||
|
||||
@@ -38,7 +38,6 @@ class QlLinuxThread(QlThread):
|
||||
self._start_address = start_address
|
||||
self._status = THREAD_STATUS_RUNNING
|
||||
self._return_val = 0
|
||||
self.path = self.ql.os.path
|
||||
self._log_file_fd = None
|
||||
self._sched_cb = None
|
||||
|
||||
@@ -87,6 +86,8 @@ class QlLinuxThread(QlThread):
|
||||
if self._set_child_tid_address != None:
|
||||
self.ql.mem.write_ptr(self._set_child_tid_address, self.id, 4)
|
||||
|
||||
self.ql.os.thread_management.add_thread(self)
|
||||
|
||||
@property
|
||||
def ql(self):
|
||||
return self._ql
|
||||
@@ -135,14 +136,6 @@ class QlLinuxThread(QlThread):
|
||||
def return_val(self, rv):
|
||||
self._return_val = rv
|
||||
|
||||
@property
|
||||
def path(self):
|
||||
return self._path
|
||||
|
||||
@path.setter
|
||||
def path(self, p: QlOsPath):
|
||||
self._path = QlOsPath(self.ql.rootfs, p.cwd, self.ql.os.type)
|
||||
|
||||
@property
|
||||
def log_file_fd(self):
|
||||
return self._log_file_fd
|
||||
@@ -295,7 +288,6 @@ class QlLinuxThread(QlThread):
|
||||
# Caveat:
|
||||
# Don't use thread id to identify the thread object.
|
||||
new_thread = self.ql.os.thread_class.spawn(self._ql, self._start_address, self._exit_point, self._saved_context, set_child_tid_addr = None, thread_id = self._thread_id)
|
||||
new_thread._path = self._path
|
||||
new_thread._return_val = self._return_val
|
||||
new_thread._robust_list_head_len = self._robust_list_head_len
|
||||
new_thread._robust_list_head_ptr = self._robust_list_head_ptr
|
||||
@@ -562,10 +554,15 @@ class QlLinuxThreadManagement:
|
||||
t.stop()
|
||||
if t in self.threads:
|
||||
self.threads.remove(t)
|
||||
self.ql.log.debug(f"[Thread Manager] Thread IDs: { {t.id for t in self.threads} }")
|
||||
# Exit the world.
|
||||
if t == self.main_thread:
|
||||
self.stop()
|
||||
|
||||
def add_thread(self, t):
|
||||
self.threads.add(t)
|
||||
self.ql.log.debug(f"[Thread Manager] Thread IDs: { {t.id for t in self.threads} }")
|
||||
|
||||
def _clear_queued_msg(self):
|
||||
try:
|
||||
msg_before_main_thread = self.ql._msg_before_main_thread
|
||||
@@ -597,8 +594,12 @@ class QlLinuxThreadManagement:
|
||||
def stop(self):
|
||||
self.ql.log.debug("[Thread Manager] Stop the world.")
|
||||
self.ql.emu_stop()
|
||||
for t in self.threads:
|
||||
gevent.kill(t)
|
||||
global LINUX_THREAD_ID
|
||||
LINUX_THREAD_ID = 2000
|
||||
while len(self.threads) != 0:
|
||||
t = self.threads.pop()
|
||||
self.ql.log.debug(f"[Thread Manager] Thread IDs: { {t.id for t in self.threads} }")
|
||||
self.stop_thread(t)
|
||||
|
||||
def run(self):
|
||||
previous_thread = self._prepare_lib_patch()
|
||||
|
||||
@@ -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
|
||||
|
||||
+6
-4
@@ -101,15 +101,17 @@ class QlFsMapper:
|
||||
if self.has_mapping(path):
|
||||
return self._open_mapping_ql_file(path, openflags, openmode)
|
||||
|
||||
real_path = self.path.transform_to_real_path(path)
|
||||
return ql_file.open(real_path, openflags, openmode)
|
||||
host_path = self.path.virtual_to_host_path(path)
|
||||
|
||||
return ql_file.open(host_path, openflags, openmode)
|
||||
|
||||
def open(self, path: str, openmode: str):
|
||||
if self.has_mapping(path):
|
||||
return self._open_mapping(path, openmode)
|
||||
|
||||
real_path = self.path.transform_to_real_path(path)
|
||||
return open(real_path, openmode)
|
||||
host_path = self.path.virtual_to_host_path(path)
|
||||
|
||||
return open(host_path, openmode)
|
||||
|
||||
def _parse_path(self, p: Union[os.PathLike, str]) -> str:
|
||||
fspath = getattr(p, '__fspath__', None)
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
|
||||
+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:
|
||||
|
||||
+3
-2
@@ -252,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)
|
||||
|
||||
+18
-1
@@ -42,9 +42,9 @@ class QlOsPath:
|
||||
self.cwd = cwd
|
||||
|
||||
# <TEMPORARY>
|
||||
# temporary aliases for backward compatibility
|
||||
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
|
||||
@@ -258,6 +258,23 @@ class QlOsPath:
|
||||
else:
|
||||
return True
|
||||
|
||||
def host_to_virtual_path(self, hostpath: str) -> str:
|
||||
"""Convert a host path to its corresponding virtual path relative to rootfs.
|
||||
|
||||
Args:
|
||||
hostpath : host path
|
||||
|
||||
Returns: the corresponding virtual path
|
||||
|
||||
Raises: `ValueError` in case the host path does not map to a rootfs location
|
||||
"""
|
||||
|
||||
resolved = Path(hostpath).resolve(strict=False)
|
||||
virtpath = self._cwd_anchor / resolved.relative_to(self._rootfs_path)
|
||||
|
||||
return str(virtpath)
|
||||
|
||||
|
||||
def virtual_abspath(self, virtpath: str) -> str:
|
||||
"""Convert a relative virtual path to an absolute virtual path based
|
||||
on the current working directory.
|
||||
|
||||
@@ -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()}
|
||||
|
||||
@@ -3,133 +3,136 @@
|
||||
# Cross Platform and Multi Architecture Advanced Binary Emulation Framework
|
||||
#
|
||||
import os
|
||||
|
||||
from qiling.exception import *
|
||||
from socket import socket, AddressFamily, SocketKind
|
||||
from typing import Union
|
||||
|
||||
try:
|
||||
import fcntl
|
||||
except ImportError:
|
||||
pass
|
||||
import socket
|
||||
|
||||
class ql_socket:
|
||||
def __init__(self, socket):
|
||||
def __init__(self, socket: socket):
|
||||
self.__fd = socket.fileno()
|
||||
self.__socket = socket
|
||||
|
||||
def __getstate__(self, *args, **kwargs):
|
||||
|
||||
_state = self.__dict__.copy()
|
||||
|
||||
_state["_ql_socket__socket"] = {
|
||||
"family": self.__dict__["_ql_socket__socket"].family,
|
||||
"type": self.__dict__["_ql_socket__socket"].type,
|
||||
"proto": self.__dict__["_ql_socket__socket"].proto,
|
||||
"laddr": self.__dict__["_ql_socket__socket"].getsockname(),
|
||||
}
|
||||
fname = f'_{self.__class__.__name__}__socket'
|
||||
sock = self.__dict__[fname]
|
||||
|
||||
_state[fname] = {
|
||||
"family" : sock.family,
|
||||
"type" : sock.type,
|
||||
"proto" : sock.proto,
|
||||
"laddr" : sock.getsockname(),
|
||||
}
|
||||
|
||||
return _state
|
||||
|
||||
def __setstate__(self, state):
|
||||
self.__dict__ = state
|
||||
|
||||
@classmethod
|
||||
def open(self, socket_domain, socket_type, socket_protocol, opts=None):
|
||||
s = socket.socket(socket_domain, socket_type, socket_protocol)
|
||||
if opts:
|
||||
s.setsockopt(*opts)
|
||||
return self(s)
|
||||
|
||||
def read(self, read_len):
|
||||
return os.read(self.__fd, read_len)
|
||||
|
||||
def write(self, write_buf):
|
||||
return os.write(self.__fd, write_buf)
|
||||
|
||||
def fileno(self):
|
||||
@classmethod
|
||||
def open(cls, domain: Union[AddressFamily, int], socktype: Union[SocketKind, int], protocol: int):
|
||||
s = socket(domain, socktype, protocol)
|
||||
|
||||
return cls(s)
|
||||
|
||||
def read(self, length: int) -> bytes:
|
||||
return os.read(self.__fd, length)
|
||||
|
||||
def write(self, data: bytes) -> int:
|
||||
return os.write(self.__fd, data)
|
||||
|
||||
def fileno(self) -> int:
|
||||
return self.__fd
|
||||
|
||||
def close(self):
|
||||
return os.close(self.__fd)
|
||||
|
||||
def fcntl(self, fcntl_cmd, fcntl_arg):
|
||||
|
||||
def close(self) -> None:
|
||||
os.close(self.__fd)
|
||||
|
||||
def fcntl(self, cmd, arg):
|
||||
try:
|
||||
return fcntl.fcntl(self.__fd, fcntl_cmd, fcntl_arg)
|
||||
return fcntl.fcntl(self.__fd, cmd, arg)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
def ioctl(self, ioctl_cmd, ioctl_arg):
|
||||
def ioctl(self, cmd, arg):
|
||||
try:
|
||||
return fcntl.ioctl(self.__fd, ioctl_cmd, ioctl_arg)
|
||||
return fcntl.ioctl(self.__fd, cmd, arg)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
def dup(self):
|
||||
new_s = self.__socket.dup()
|
||||
new_ql_socket = ql_socket(new_s)
|
||||
return new_ql_socket
|
||||
pass
|
||||
|
||||
def connect(self, connect_addr):
|
||||
return self.__socket.connect(connect_addr)
|
||||
|
||||
def shutdown(self, shutdown_how):
|
||||
return self.__socket.shutdown(shutdown_how)
|
||||
|
||||
def bind(self, bind_addr):
|
||||
return self.__socket.bind(bind_addr)
|
||||
|
||||
def listen(self, listen_num):
|
||||
return self.__socket.listen(listen_num)
|
||||
def dup(self) -> 'ql_socket':
|
||||
new_s = self.__socket.dup()
|
||||
|
||||
return ql_socket(new_s)
|
||||
|
||||
def connect(self, address) -> None:
|
||||
self.__socket.connect(address)
|
||||
|
||||
def shutdown(self, how: int) -> None:
|
||||
return self.__socket.shutdown(how)
|
||||
|
||||
def bind(self, address) -> None:
|
||||
return self.__socket.bind(address)
|
||||
|
||||
def listen(self, backlog: int) -> None:
|
||||
return self.__socket.listen(backlog)
|
||||
|
||||
def getsockname(self):
|
||||
return self.__socket.getsockname()
|
||||
|
||||
|
||||
def getpeername(self):
|
||||
return self.__socket.getpeername()
|
||||
|
||||
def getsockopt(self, level, optname, optlen):
|
||||
return self.__socket.getsockopt(level, optname, optlen)
|
||||
def getsockopt(self, level: int, optname: int, buflen: int):
|
||||
return self.__socket.getsockopt(level, optname, buflen)
|
||||
|
||||
def setsockopt(self, level, optname, optval, optlen):
|
||||
if optval is not None:
|
||||
return self.__socket.setsockopt(level, optname, optval)
|
||||
def setsockopt(self, level: int, optname: int, value: Union[int, bytes, None], optlen: int = 0) -> None:
|
||||
if value is None:
|
||||
self.__socket.setsockopt(level, optname, None, optlen)
|
||||
else:
|
||||
return self.__socket.setsockopt(level, optname, None, optval)
|
||||
|
||||
self.__socket.setsockopt(level, optname, value)
|
||||
|
||||
def accept(self):
|
||||
try:
|
||||
con, addr = self.__socket.accept()
|
||||
new_ql_socket = ql_socket(con)
|
||||
except BlockingIOError:
|
||||
# For support non-blocking sockets
|
||||
return None, None
|
||||
return new_ql_socket, addr
|
||||
|
||||
def recv(self, recv_len, recv_flags):
|
||||
return self.__socket.recv(recv_len, recv_flags)
|
||||
|
||||
def send(self, send_buf, send_flags):
|
||||
return self.__socket.send(send_buf, send_flags)
|
||||
addr = None
|
||||
new_ql_socket = None
|
||||
else:
|
||||
new_ql_socket = ql_socket(con)
|
||||
|
||||
def recvmsg(self, bufsize, ancbufsize, flags):
|
||||
return new_ql_socket, addr
|
||||
|
||||
def recv(self, bufsize: int, flags: int) -> bytes:
|
||||
return self.__socket.recv(bufsize, flags)
|
||||
|
||||
def send(self, data, flags: int) -> int:
|
||||
return self.__socket.send(data, flags)
|
||||
|
||||
def recvmsg(self, bufsize: int, ancbufsize: int, flags: int):
|
||||
return self.__socket.recvmsg(bufsize, ancbufsize, flags)
|
||||
|
||||
def recvfrom(self, recvfrom_len, recvfrom_flags):
|
||||
return self.__socket.recvfrom(recvfrom_len, recvfrom_flags)
|
||||
def recvfrom(self, bufsize: int, flags: int):
|
||||
return self.__socket.recvfrom(bufsize, flags)
|
||||
|
||||
def sendto(self, sendto_buf, sendto_flags, sendto_addr):
|
||||
return self.__socket.sendto(sendto_buf, sendto_flags, sendto_addr)
|
||||
|
||||
@property
|
||||
def family(self):
|
||||
def family(self) -> AddressFamily:
|
||||
return self.__socket.family
|
||||
|
||||
@property
|
||||
def socktype(self):
|
||||
def socktype(self) -> SocketKind:
|
||||
return self.__socket.type
|
||||
|
||||
@property
|
||||
def socket(self):
|
||||
def socket(self) -> socket:
|
||||
return self.__socket
|
||||
|
||||
# def __getattr__(self,name):
|
||||
@@ -139,41 +142,40 @@ class ql_socket:
|
||||
# raise AttributeError("A instance has no attribute '%s'" % name)
|
||||
|
||||
class ql_pipe:
|
||||
def __init__(self, fd):
|
||||
def __init__(self, fd: int):
|
||||
self.__fd = fd
|
||||
|
||||
@classmethod
|
||||
def open(self):
|
||||
def open(cls):
|
||||
r, w = os.pipe()
|
||||
return (self(r), self(w))
|
||||
|
||||
def read(self, read_len):
|
||||
return os.read(self.__fd, read_len)
|
||||
|
||||
def write(self, write_buf):
|
||||
return os.write(self.__fd, write_buf)
|
||||
|
||||
def fileno(self):
|
||||
return (cls(r), cls(w))
|
||||
|
||||
def read(self, length: int) -> bytes:
|
||||
return os.read(self.__fd, length)
|
||||
|
||||
def write(self, data: bytes) -> int:
|
||||
return os.write(self.__fd, data)
|
||||
|
||||
def fileno(self) -> int:
|
||||
return self.__fd
|
||||
|
||||
def close(self):
|
||||
return os.close(self.__fd)
|
||||
|
||||
def fcntl(self, fcntl_cmd, fcntl_arg):
|
||||
def close(self) -> None:
|
||||
os.close(self.__fd)
|
||||
|
||||
def fcntl(self, cmd, arg):
|
||||
try:
|
||||
return fcntl.fcntl(self.__fd, fcntl_cmd, fcntl_arg)
|
||||
return fcntl.fcntl(self.__fd, cmd, arg)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
def ioctl(self, ioctl_cmd, ioctl_arg):
|
||||
def ioctl(self, cmd, arg):
|
||||
try:
|
||||
return fcntl.ioctl(self.__fd, ioctl_cmd, ioctl_arg)
|
||||
return fcntl.ioctl(self.__fd, cmd, arg)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
def dup(self):
|
||||
pass
|
||||
|
||||
def dup(self) -> 'ql_pipe':
|
||||
new_fd = os.dup(self.__fd)
|
||||
new_ql_pipe = ql_pipe(new_fd)
|
||||
return new_ql_pipe
|
||||
|
||||
|
||||
return ql_pipe(new_fd)
|
||||
|
||||
@@ -72,7 +72,7 @@ class QlFileDes:
|
||||
def __len__(self):
|
||||
return len(self.__fds)
|
||||
|
||||
def __getitem__(self, idx: int):
|
||||
def __getitem__(self, idx: Union[slice, int]):
|
||||
return self.__fds[idx]
|
||||
|
||||
def __setitem__(self, idx: int, val: Optional[IO]):
|
||||
|
||||
@@ -7,9 +7,11 @@ from .futex import *
|
||||
from .ioctl import *
|
||||
from .mman import *
|
||||
from .net import *
|
||||
from .personality import *
|
||||
from .poll import *
|
||||
from .prctl import *
|
||||
from .ptrace import *
|
||||
from .random import *
|
||||
from .resource import *
|
||||
from .sched import *
|
||||
from .select import *
|
||||
@@ -25,4 +27,3 @@ from .uio import *
|
||||
from .unistd import *
|
||||
from .utsname import *
|
||||
from .wait import *
|
||||
from .random import *
|
||||
|
||||
@@ -131,8 +131,8 @@ def ql_syscall_fcntl(ql: Qiling, fd: int, cmd: int, arg: int):
|
||||
if arg not in range(NR_OPEN):
|
||||
regreturn = -EINVAL
|
||||
|
||||
for idx, val in enumerate(ql.os.fd, arg):
|
||||
if val is None:
|
||||
for idx in range(arg, len(ql.os.fd)):
|
||||
if ql.os.fd[idx] is None:
|
||||
ql.os.fd[idx] = f.dup()
|
||||
regreturn = idx
|
||||
break
|
||||
@@ -173,11 +173,13 @@ def ql_syscall_fcntl64(ql: Qiling, fd: int, cmd: int, arg: int):
|
||||
if arg not in range(NR_OPEN):
|
||||
regreturn = -1
|
||||
|
||||
for idx, val in enumerate(ql.os.fd, arg):
|
||||
if val is None:
|
||||
for idx in range(arg, len(ql.os.fd)):
|
||||
if ql.os.fd[idx] is None:
|
||||
ql.os.fd[idx] = f.dup()
|
||||
regreturn = idx
|
||||
break
|
||||
else:
|
||||
regreturn = -1
|
||||
|
||||
elif cmd == F_GETFL:
|
||||
regreturn = 2
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
#!/usr/bin/env python3
|
||||
#
|
||||
# Cross Platform and Multi Architecture Advanced Binary Emulation Framework
|
||||
#
|
||||
|
||||
import os
|
||||
|
||||
|
||||
from qiling import Qiling
|
||||
from qiling.const import *
|
||||
|
||||
def ql_syscall_personality(ql: Qiling, persona: int):
|
||||
regreturn = 0
|
||||
return regreturn
|
||||
@@ -19,9 +19,16 @@ import os
|
||||
from qiling import Qiling
|
||||
|
||||
def __getrlimit_common(ql: Qiling, res: int, rlim: int) -> int:
|
||||
rlimit = resource.getrlimit(res)
|
||||
ql.mem.write(rlim, ql.pack32s(rlimit[0]) + ql.pack32s(rlimit[1]))
|
||||
|
||||
RLIMIT_STACK = 3
|
||||
if res == RLIMIT_STACK:
|
||||
if ql.arch.bits == 64:
|
||||
stack_size = int(ql.os.profile.get("OS64", "stack_size"), 16)
|
||||
elif ql.arch.bits == 32:
|
||||
stack_size = int(ql.os.profile.get("OS32", "stack_size"), 16)
|
||||
rlimit = (stack_size, -1)
|
||||
else:
|
||||
rlimit = resource.getrlimit(res)
|
||||
ql.mem.write(rlim, ql.pack64s(rlimit[0]) + ql.pack64s(rlimit[1]))
|
||||
return 0
|
||||
|
||||
def ql_syscall_ugetrlimit(ql: Qiling, res: int, rlim: int):
|
||||
@@ -30,23 +37,29 @@ def ql_syscall_ugetrlimit(ql: Qiling, res: int, rlim: int):
|
||||
def ql_syscall_getrlimit(ql: Qiling, res: int, rlim: int):
|
||||
return __getrlimit_common(ql, res, rlim)
|
||||
|
||||
def ql_syscall_setrlimit(ql: Qiling, setrlimit_resource: int, setrlimit_rlim: int):
|
||||
def ql_syscall_setrlimit(ql: Qiling, res: int, rlim: int):
|
||||
# maybe we can nop the setrlimit
|
||||
tmp_rlim = (ql.unpack32s(ql.mem.read(setrlimit_rlim, 4)), ql.unpack32s(ql.mem.read(setrlimit_rlim + 4, 4)))
|
||||
resource.setrlimit(setrlimit_resource, tmp_rlim)
|
||||
tmp_rlim = (ql.unpack32s(ql.mem.read(rlim, 4)), ql.unpack32s(ql.mem.read(rlim + 4, 4)))
|
||||
resource.setrlimit(res, tmp_rlim)
|
||||
|
||||
return 0
|
||||
|
||||
def ql_syscall_prlimit64(ql: Qiling, pid: int, res: int, new_limit: int, old_limit: int):
|
||||
# setrlimit() and getrlimit()
|
||||
if pid == 0 and new_limit == 0:
|
||||
rlim = resource.getrlimit(res)
|
||||
ql.mem.write(old_limit, ql.packs(rlim[0]) + ql.packs(rlim[1]))
|
||||
|
||||
return 0
|
||||
try:
|
||||
rlim = resource.getrlimit(res)
|
||||
ql.mem.write(old_limit, ql.packs(rlim[0]) + ql.packs(rlim[1]))
|
||||
return 0
|
||||
except:
|
||||
return -1
|
||||
|
||||
# set other process which pid != 0
|
||||
return -1
|
||||
|
||||
def ql_syscall_getpriority(ql: Qiling, getpriority_which: int, getpriority_who: int):
|
||||
return os.getpriority(getpriority_which, getpriority_who)
|
||||
def ql_syscall_getpriority(ql: Qiling, which: int, who: int):
|
||||
try:
|
||||
regreturn = os.getpriority(which, who)
|
||||
except:
|
||||
regreturn = -1
|
||||
return regreturn
|
||||
|
||||
@@ -82,7 +82,6 @@ def ql_syscall_clone(ql: Qiling, flags: int, child_stack: int, parent_tidptr: in
|
||||
set_child_tid_addr = child_tidptr
|
||||
|
||||
th = ql.os.thread_class.spawn(ql, ql.arch.regs.arch_pc + 2, ql.os.exit_point, set_child_tid_addr = set_child_tid_addr)
|
||||
th.path = f_th.path
|
||||
ql.log.debug(f'{str(th)} created')
|
||||
|
||||
if flags & CLONE_PARENT_SETTID == CLONE_PARENT_SETTID:
|
||||
|
||||
@@ -69,38 +69,44 @@ def ql_bin_to_ip(ip):
|
||||
|
||||
def ql_syscall_socket(ql: Qiling, socket_domain, socket_type, socket_protocol):
|
||||
idx = next((i for i in range(NR_OPEN) if ql.os.fd[i] is None), -1)
|
||||
regreturn = idx
|
||||
|
||||
if idx != -1:
|
||||
emu_socket_value = socket_type
|
||||
|
||||
# ql_socket.open should use host platform based socket_type.
|
||||
try:
|
||||
emu_socket_value = socket_type
|
||||
emu_socket_type = socket_type_mapping(socket_type, ql.arch.type, ql.os.type)
|
||||
socket_type = getattr(socket, emu_socket_type)
|
||||
ql.log.debug(f'Convert emu_socket_type {emu_socket_type}:{emu_socket_value} to host platform based socket_type {emu_socket_type}:{socket_type}')
|
||||
|
||||
except AttributeError:
|
||||
ql.log.error(f'Cannot convert emu_socket_type {emu_socket_type}:{emu_socket_value} to host platform based socket_type')
|
||||
raise
|
||||
|
||||
except Exception:
|
||||
except KeyError:
|
||||
ql.log.error(f'Cannot convert emu_socket_type {emu_socket_value} to host platform based socket_type')
|
||||
raise
|
||||
|
||||
try:
|
||||
if ql.verbose >= QL_VERBOSE.DEBUG: # set REUSEADDR options under debug mode
|
||||
ql.os.fd[idx] = ql_socket.open(socket_domain, socket_type, socket_protocol, (socket.SOL_SOCKET, socket.SO_REUSEADDR, 1))
|
||||
else:
|
||||
ql.os.fd[idx] = ql_socket.open(socket_domain, socket_type, socket_protocol)
|
||||
except OSError as e: # May raise error: Protocol not supported
|
||||
socket_type = getattr(socket, emu_socket_type)
|
||||
except AttributeError:
|
||||
ql.log.error(f'Cannot convert emu_socket_type {emu_socket_type}:{emu_socket_value} to host platform based socket_type')
|
||||
raise
|
||||
|
||||
ql.log.debug(f'Convert emu_socket_type {emu_socket_type}:{emu_socket_value} to host platform based socket_type {emu_socket_type}:{socket_type}')
|
||||
|
||||
try:
|
||||
sock = ql_socket.open(socket_domain, socket_type, socket_protocol)
|
||||
|
||||
# set REUSEADDR options under debug mode
|
||||
if ql.verbose >= QL_VERBOSE.DEBUG:
|
||||
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
|
||||
|
||||
ql.os.fd[idx] = sock
|
||||
|
||||
# May raise error: Protocol not supported
|
||||
except OSError as e:
|
||||
ql.log.debug(f'{e}: {socket_domain=}, {socket_type=}, {socket_protocol=}')
|
||||
regreturn = -1
|
||||
idx = -1
|
||||
|
||||
socket_type = socket_type_mapping(socket_type, ql.arch.type, ql.os.type)
|
||||
socket_domain = socket_domain_mapping(socket_domain, ql.arch.type, ql.os.type)
|
||||
ql.log.debug("socket(%s, %s, %s) = %d" % (socket_domain, socket_type, socket_protocol, regreturn))
|
||||
ql.log.debug("socket(%s, %s, %s) = %d" % (socket_domain, socket_type, socket_protocol, idx))
|
||||
|
||||
return regreturn
|
||||
return idx
|
||||
|
||||
|
||||
def ql_syscall_connect(ql: Qiling, connect_sockfd, connect_addr, connect_addrlen):
|
||||
@@ -146,29 +152,31 @@ def ql_syscall_getsockopt(ql: Qiling, sockfd, level, optname, optval_addr, optle
|
||||
|
||||
try:
|
||||
optlen = min(ql.unpack32s(ql.mem.read(optlen_addr, 4)), 1024)
|
||||
|
||||
if optlen < 0:
|
||||
return -EINVAL
|
||||
|
||||
emu_level = level
|
||||
|
||||
try:
|
||||
emu_level = level
|
||||
emu_level_name = socket_level_mapping(emu_level, ql.arch.type, ql.os.type)
|
||||
except KeyError:
|
||||
ql.log.error(f"Can't convert emu_level {emu_level} to host platform based level")
|
||||
raise
|
||||
|
||||
try:
|
||||
level = getattr(socket, emu_level_name)
|
||||
ql.log.debug("Convert emu_level {}:{} to host platform based level {}:{}".format(
|
||||
emu_level_name, emu_level, emu_level_name, level))
|
||||
|
||||
except AttributeError:
|
||||
ql.log.error("Can't convert emu_level {}:{} to host platform based emu_level".format(
|
||||
emu_level_name, emu_level))
|
||||
ql.log.error(f"Can't convert emu_level {emu_level_name}:{emu_level} to host platform based emu_level")
|
||||
raise
|
||||
|
||||
except Exception:
|
||||
ql.log.error("Can't convert emu_level {} to host platform based level".format(emu_level))
|
||||
raise
|
||||
ql.log.debug(f"Convert emu_level {emu_level_name}:{emu_level} to host platform based level {emu_level_name}:{level}")
|
||||
|
||||
emu_opt = optname
|
||||
|
||||
try:
|
||||
emu_opt = optname
|
||||
|
||||
emu_level_name = socket_level_mapping(emu_level, ql.arch.type, ql.os.type)
|
||||
|
||||
# emu_opt_name is based on level
|
||||
if emu_level_name == "IPPROTO_IP":
|
||||
emu_opt_name = socket_ip_option_mapping(emu_opt, ql.arch.type, ql.os.type)
|
||||
@@ -180,18 +188,17 @@ def ql_syscall_getsockopt(ql: Qiling, sockfd, level, optname, optval_addr, optle
|
||||
if emu_opt_name.endswith("_NEW") or emu_opt_name.endswith("_OLD"):
|
||||
emu_opt_name = emu_opt_name[:-4]
|
||||
|
||||
except KeyError:
|
||||
ql.log.error(f"Can't convert emu_optname {emu_opt} to host platform based optname")
|
||||
raise
|
||||
|
||||
try:
|
||||
optname = getattr(socket, emu_opt_name)
|
||||
ql.log.debug("Convert emu_optname {}:{} to host platform based optname {}:{}".format(
|
||||
emu_opt_name, emu_opt, emu_opt_name, optname))
|
||||
|
||||
except AttributeError:
|
||||
ql.log.error("Can't convert emu_optname {}:{} to host platform based emu_optname".format(
|
||||
emu_opt_name, emu_opt))
|
||||
ql.log.error(f"Can't convert emu_optname {emu_opt_name}:{emu_opt} to host platform based emu_optname")
|
||||
raise
|
||||
|
||||
except Exception:
|
||||
ql.log.error("Can't convert emu_optname {} to host platform based optname".format(emu_opt))
|
||||
raise
|
||||
ql.log.debug(f"Convert emu_optname {emu_opt_name}:{emu_opt} to host platform based optname {emu_opt_name}:{optname}")
|
||||
|
||||
optval = ql.os.fd[sockfd].getsockopt(level, optname, optlen)
|
||||
ql.mem.write(optval_addr, optval)
|
||||
@@ -210,26 +217,27 @@ def ql_syscall_setsockopt(ql: Qiling, sockfd, level, optname, optval_addr, optle
|
||||
ql.os.fd[sockfd].setsockopt(level, optname, None, optlen)
|
||||
else:
|
||||
try:
|
||||
emu_level = level
|
||||
|
||||
try:
|
||||
emu_level = level
|
||||
emu_level_name = socket_level_mapping(emu_level, ql.arch.type, ql.os.type)
|
||||
except KeyError:
|
||||
ql.log.error(f"Can't convert emu_level {emu_level} to host platform based level")
|
||||
raise
|
||||
|
||||
try:
|
||||
level = getattr(socket, emu_level_name)
|
||||
ql.log.debug("Convert emu_level {}:{} to host platform based level {}:{}".format(
|
||||
emu_level_name, emu_level, emu_level_name, level))
|
||||
|
||||
except AttributeError:
|
||||
ql.log.error("Can't convert emu_level {}:{} to host platform based emu_level".format(
|
||||
emu_level_name, emu_level))
|
||||
ql.log.error(f"Can't convert emu_level {emu_level_name}:{emu_level} to host platform based emu_level")
|
||||
raise
|
||||
|
||||
except Exception:
|
||||
ql.log.error("Can't convert emu_level {} to host platform based level".format(emu_level))
|
||||
raise
|
||||
ql.log.debug(f"Convert emu_level {emu_level_name}:{emu_level} to host platform based level {emu_level_name}:{level}")
|
||||
|
||||
emu_opt = optname
|
||||
|
||||
try:
|
||||
emu_opt = optname
|
||||
|
||||
emu_level_name = socket_level_mapping(emu_level, ql.arch.type, ql.os.type)
|
||||
|
||||
# emu_opt_name is based on level
|
||||
if emu_level_name == "IPPROTO_IP":
|
||||
emu_opt_name = socket_ip_option_mapping(emu_opt, ql.arch.type, ql.os.type)
|
||||
@@ -241,18 +249,17 @@ def ql_syscall_setsockopt(ql: Qiling, sockfd, level, optname, optval_addr, optle
|
||||
if emu_opt_name.endswith("_NEW") or emu_opt_name.endswith("_OLD"):
|
||||
emu_opt_name = emu_opt_name[:-4]
|
||||
|
||||
except KeyError:
|
||||
ql.log.error(f"Can't convert emu_optname {emu_opt} to host platform based optname")
|
||||
raise
|
||||
|
||||
try:
|
||||
optname = getattr(socket, emu_opt_name)
|
||||
ql.log.debug("Convert emu_optname {}:{} to host platform based optname {}:{}".format(
|
||||
emu_opt_name, emu_opt, emu_opt_name, optname))
|
||||
|
||||
except AttributeError:
|
||||
ql.log.error("Can't convert emu_optname {}:{} to host platform based emu_optname".format(
|
||||
emu_opt_name, emu_opt))
|
||||
ql.log.error(f"Can't convert emu_optname {emu_opt_name}:{emu_opt} to host platform based emu_optname")
|
||||
raise
|
||||
|
||||
except Exception:
|
||||
ql.log.error("Can't convert emu_optname {} to host platform based optname".format(emu_opt))
|
||||
raise
|
||||
ql.log.debug(f"Convert emu_optname {emu_opt_name}:{emu_opt} to host platform based optname {emu_opt_name}:{optname}")
|
||||
|
||||
optval = ql.mem.read(optval_addr, optlen)
|
||||
ql.os.fd[sockfd].setsockopt(level, optname, optval, None)
|
||||
@@ -304,13 +311,13 @@ def ql_syscall_bind(ql: Qiling, bind_fd, bind_addr, bind_addrlen):
|
||||
|
||||
# need a proper fix, for now ipv4 comes first
|
||||
elif sin_family == 2 and ql.os.bindtolocalhost == True:
|
||||
ql.os.fd[bind_fd].bind(('127.0.0.1', port))
|
||||
host = "127.0.0.1"
|
||||
ql.os.fd[bind_fd].bind((host, port))
|
||||
|
||||
# IPv4 should comes first
|
||||
elif ql.os.ipv6 == True and sin_family == 10 and ql.os.bindtolocalhost == True:
|
||||
ql.os.fd[bind_fd].bind(('::1', port))
|
||||
host = "::1"
|
||||
ql.os.fd[bind_fd].bind((host, port))
|
||||
|
||||
elif ql.os.bindtolocalhost == False:
|
||||
ql.os.fd[bind_fd].bind((host, port))
|
||||
|
||||
@@ -1208,11 +1208,11 @@ def transform_path(ql: Qiling, dirfd: int, path: int, flags: int = 0):
|
||||
then the target file is the one referred to by the file descriptor dirfd.
|
||||
"""
|
||||
|
||||
dirfd = ql.unpacks(ql.pack(dirfd))
|
||||
dirfd = ql.unpack32s(ql.pack32(dirfd & (1<<32)-1))
|
||||
path = ql.os.utils.read_cstring(path)
|
||||
|
||||
if path.startswith('/'):
|
||||
return None, os.path.join(ql.rootfs, path)
|
||||
return None, os.path.join(ql.rootfs, path.lstrip('/'))
|
||||
|
||||
if dirfd == AT_FDCWD:
|
||||
return None, ql.os.path.transform_to_real_path(path)
|
||||
@@ -1225,15 +1225,26 @@ def transform_path(ql: Qiling, dirfd: int, path: int, flags: int = 0):
|
||||
|
||||
|
||||
def ql_syscall_chmod(ql: Qiling, filename: int, mode: int):
|
||||
file_path = ql.os.utils.read_cstring(filename)
|
||||
real_path = ql.os.path.transform_to_real_path(file_path)
|
||||
try:
|
||||
os.chmod(real_path, mode)
|
||||
regreturn = 0
|
||||
except:
|
||||
regreturn = -1
|
||||
ql.log.debug(f'chmod("{ql.os.utils.read_cstring(filename)}", {mode:d}) = 0')
|
||||
|
||||
return 0
|
||||
return regreturn
|
||||
|
||||
def ql_syscall_fchmod(ql: Qiling, fd: int, mode: int):
|
||||
if fd not in range(NR_OPEN) or ql.os.fd[fd] is None:
|
||||
return -EBADF
|
||||
|
||||
return 0
|
||||
try:
|
||||
os.fchmod(ql.os.fd[fd].fileno(), mode)
|
||||
regreturn = 0
|
||||
except:
|
||||
regreturn = -1
|
||||
ql.log.debug("fchmod(%d, %d) = %d" % (fd, mode, regreturn))
|
||||
return regreturn
|
||||
|
||||
def ql_syscall_fstatat64(ql: Qiling, dirfd: int, path: int, buf_ptr: int, flags: int):
|
||||
dirfd, real_path = transform_path(ql, dirfd, path, flags)
|
||||
@@ -1460,6 +1471,7 @@ def ql_syscall_mknodat(ql: Qiling, dirfd: int, path: int, mode: int, dev: int):
|
||||
except:
|
||||
regreturn = -1
|
||||
|
||||
ql.log.debug("mknodat(%d, %s, 0%o, %d) = %d" % (dirfd, real_path, mode, dev, regreturn))
|
||||
return regreturn
|
||||
|
||||
|
||||
@@ -1474,6 +1486,7 @@ def ql_syscall_mkdir(ql: Qiling, pathname: int, mode: int):
|
||||
except:
|
||||
regreturn = -1
|
||||
|
||||
ql.log.debug("mkdir(%s, 0%o) = %d" % (real_path, mode, regreturn))
|
||||
return regreturn
|
||||
|
||||
def ql_syscall_rmdir(ql: Qiling, pathname: int):
|
||||
|
||||
@@ -25,7 +25,7 @@ def ql_syscall_exit(ql: Qiling, code: int):
|
||||
if ql.multithread:
|
||||
def _sched_cb_exit(cur_thread):
|
||||
ql.log.debug(f"[Thread {cur_thread.get_id()}] Terminated")
|
||||
cur_thread.stop()
|
||||
ql.os.thread_management.stop_thread(cur_thread)
|
||||
cur_thread.exit_code = code
|
||||
|
||||
td = ql.os.thread_management.cur_thread
|
||||
@@ -43,7 +43,7 @@ def ql_syscall_exit_group(ql: Qiling, code: int):
|
||||
if ql.multithread:
|
||||
def _sched_cb_exit(cur_thread):
|
||||
ql.log.debug(f"[Thread {cur_thread.get_id()}] Terminated")
|
||||
cur_thread.stop()
|
||||
ql.os.thread_management.stop_thread(cur_thread)
|
||||
cur_thread.exit_code = code
|
||||
|
||||
td = ql.os.thread_management.cur_thread
|
||||
@@ -116,6 +116,27 @@ def ql_syscall_capset(ql: Qiling, hdrp: int, datap: int):
|
||||
def ql_syscall_kill(ql: Qiling, pid: int, sig: int):
|
||||
return 0
|
||||
|
||||
|
||||
def ql_syscall_fsync(ql: Qiling, fd: int):
|
||||
try:
|
||||
os.fsync(ql.os.fd[fd].fileno())
|
||||
regreturn = 0
|
||||
except:
|
||||
regreturn = -1
|
||||
ql.log.debug("fsync(%d) = %d" % (fd, regreturn))
|
||||
return regreturn
|
||||
|
||||
|
||||
def ql_syscall_fdatasync(ql: Qiling, fd: int):
|
||||
try:
|
||||
os.fdatasync(ql.os.fd[fd].fileno())
|
||||
regreturn = 0
|
||||
except:
|
||||
regreturn = -1
|
||||
ql.log.debug("fdatasync(%d) = %d" % (fd, regreturn))
|
||||
return regreturn
|
||||
|
||||
|
||||
def ql_syscall_faccessat(ql: Qiling, dfd: int, filename: int, mode: int):
|
||||
access_path = ql.os.utils.read_cstring(filename)
|
||||
real_path = ql.os.path.transform_to_real_path(access_path)
|
||||
@@ -189,13 +210,14 @@ def ql_syscall_brk(ql: Qiling, inp: int):
|
||||
# otherwise, just return current brk_address
|
||||
|
||||
if inp:
|
||||
new_brk_addr = ((inp + 0xfff) // 0x1000) * 0x1000
|
||||
cur_brk_addr = ql.loader.brk_address
|
||||
new_brk_addr = ql.mem.align_up(inp)
|
||||
|
||||
if inp > ql.loader.brk_address: # increase current brk_address if inp is greater
|
||||
ql.mem.map(ql.loader.brk_address, new_brk_addr - ql.loader.brk_address, info="[brk]")
|
||||
if inp > cur_brk_addr: # increase current brk_address if inp is greater
|
||||
ql.mem.map(cur_brk_addr, new_brk_addr - cur_brk_addr, info="[brk]")
|
||||
|
||||
elif inp < ql.loader.brk_address: # shrink current bkr_address to inp if its smaller
|
||||
ql.mem.unmap(new_brk_addr, ql.loader.brk_address - new_brk_addr)
|
||||
elif inp < cur_brk_addr: # shrink current bkr_address to inp if its smaller
|
||||
ql.mem.unmap(new_brk_addr, cur_brk_addr - new_brk_addr)
|
||||
|
||||
ql.loader.brk_address = new_brk_addr
|
||||
|
||||
@@ -316,23 +338,25 @@ def ql_syscall_write(ql: Qiling, fd: int, buf: int, count: int):
|
||||
def ql_syscall_readlink(ql: Qiling, path_name: int, path_buff: int, path_buffsize: int):
|
||||
pathname = ql.os.utils.read_cstring(path_name)
|
||||
# pathname = str(pathname, 'utf-8', errors="ignore")
|
||||
real_path = ql.os.path.transform_to_link_path(pathname)
|
||||
relative_path = ql.os.path.transform_to_relative_path(pathname)
|
||||
host_path = ql.os.path.virtual_to_host_path(pathname)
|
||||
virt_path = ql.os.path.virtual_abspath(pathname)
|
||||
|
||||
if not os.path.exists(real_path):
|
||||
regreturn = -1
|
||||
# cover procfs psaudo files first
|
||||
# TODO: /proc/self/root, /proc/self/cwd
|
||||
if virt_path == r'/proc/self/exe':
|
||||
p = ql.os.path.host_to_virtual_path(ql.path)
|
||||
p = p.encode('utf-8')
|
||||
|
||||
elif relative_path == r'/proc/self/exe':
|
||||
localpath = os.path.abspath(ql.path)
|
||||
localpath = bytes(localpath, 'utf-8') + b'\x00'
|
||||
ql.mem.write(path_buff, p + b'\x00')
|
||||
regreturn = len(p)
|
||||
|
||||
ql.mem.write(path_buff, localpath)
|
||||
regreturn = len(localpath) - 1
|
||||
|
||||
else:
|
||||
elif os.path.exists(host_path):
|
||||
regreturn = 0
|
||||
|
||||
ql.log.debug("readlink(%s, 0x%x, 0x%x) = %d" % (relative_path, path_buff, path_buffsize, regreturn))
|
||||
else:
|
||||
regreturn = -1
|
||||
|
||||
ql.log.debug('readlink("%s", 0x%x, 0x%x) = %d' % (virt_path, path_buff, path_buffsize, regreturn))
|
||||
|
||||
return regreturn
|
||||
|
||||
@@ -354,20 +378,17 @@ def ql_syscall_getcwd(ql: Qiling, path_buff: int, path_buffsize: int):
|
||||
|
||||
def ql_syscall_chdir(ql: Qiling, path_name: int):
|
||||
pathname = ql.os.utils.read_cstring(path_name)
|
||||
real_path = ql.os.path.transform_to_real_path(pathname)
|
||||
relative_path = ql.os.path.transform_to_relative_path(pathname)
|
||||
host_path = ql.os.path.virtual_to_host_path(pathname)
|
||||
virt_path = ql.os.path.virtual_abspath(pathname)
|
||||
|
||||
if os.path.exists(real_path) and os.path.isdir(real_path):
|
||||
if ql.os.thread_management:
|
||||
ql.os.thread_management.cur_thread.path.cwd = relative_path
|
||||
else:
|
||||
ql.os.path.cwd = relative_path
|
||||
if os.path.exists(host_path) and os.path.isdir(host_path):
|
||||
ql.os.path.cwd = virt_path
|
||||
|
||||
regreturn = 0
|
||||
ql.log.debug("chdir(%s) = %d"% (relative_path, regreturn))
|
||||
ql.log.debug("chdir(%s) = %d"% (virt_path, regreturn))
|
||||
else:
|
||||
regreturn = -1
|
||||
ql.log.warning("chdir(%s) = %d : not found" % (relative_path, regreturn))
|
||||
ql.log.warning("chdir(%s) = %d : not found" % (virt_path, regreturn))
|
||||
|
||||
return regreturn
|
||||
|
||||
@@ -375,20 +396,25 @@ def ql_syscall_chdir(ql: Qiling, path_name: int):
|
||||
def ql_syscall_readlinkat(ql: Qiling, dfd: int, path: int, buf: int, bufsize: int):
|
||||
pathname = ql.os.utils.read_cstring(path)
|
||||
# pathname = str(pathname, 'utf-8', errors="ignore")
|
||||
real_path = ql.os.path.transform_to_link_path(pathname)
|
||||
relative_path = ql.os.path.transform_to_relative_path(pathname)
|
||||
host_path = ql.os.path.virtual_to_host_path(pathname)
|
||||
virt_path = ql.os.path.virtual_abspath(pathname)
|
||||
|
||||
if not os.path.exists(real_path):
|
||||
# cover procfs psaudo files first
|
||||
# TODO: /proc/self/root, /proc/self/cwd
|
||||
if virt_path == r'/proc/self/exe':
|
||||
p = ql.os.path.host_to_virtual_path(ql.path)
|
||||
p = p.encode('utf-8')
|
||||
|
||||
ql.mem.write(buf, p + b'\x00')
|
||||
regreturn = len(p)
|
||||
|
||||
elif os.path.exists(host_path):
|
||||
regreturn = 0
|
||||
|
||||
else:
|
||||
regreturn = -1
|
||||
|
||||
elif relative_path == r'/proc/self/exe':
|
||||
localpath = os.path.abspath(ql.path)
|
||||
localpath = bytes(localpath, 'utf-8') + b'\x00'
|
||||
|
||||
ql.mem.write(buf, localpath)
|
||||
regreturn = len(localpath) -1
|
||||
else:
|
||||
regreturn = 0
|
||||
ql.log.debug('readlinkat(%d, "%s", 0x%x, 0x%x) = %d' % (dfd, virt_path, buf, bufsize, regreturn))
|
||||
|
||||
return regreturn
|
||||
|
||||
@@ -534,8 +560,6 @@ def ql_syscall_dup3(ql: Qiling, fd: int, newfd: int, flags: int):
|
||||
|
||||
def ql_syscall_set_tid_address(ql: Qiling, tidptr: int):
|
||||
if ql.os.thread_management:
|
||||
ql.os.thread_management.cur_thread.set_clear_child_tid_addr(tidptr)
|
||||
|
||||
regreturn = ql.os.thread_management.cur_thread.id
|
||||
else:
|
||||
regreturn = os.getpid()
|
||||
|
||||
+1
-7
@@ -45,12 +45,6 @@ class QlOsStats:
|
||||
for key, values in self.strings.items():
|
||||
ret.append(f'{key}: {", ".join(str(word) for word in values)}')
|
||||
|
||||
ret.extend(QlOsStats._banner('registry keys accessed'))
|
||||
|
||||
for key, values in self.syscalls.items():
|
||||
ret.append(f'{key}:')
|
||||
ret.extend(f' {json.dumps(value):s}' for value in values)
|
||||
|
||||
return ret
|
||||
|
||||
def log_api_call(self, address: int, name: str, params: Mapping, retval: Any, retaddr: int) -> None:
|
||||
@@ -107,7 +101,7 @@ class QlWinStats(QlOsStats):
|
||||
|
||||
ret.extend(QlOsStats._banner('registry keys accessed'))
|
||||
|
||||
for key, values in self.syscalls.items():
|
||||
for key, values in self.registry.items():
|
||||
ret.append(f'{key}:')
|
||||
ret.extend(f' {json.dumps(value):s}' for value in values)
|
||||
|
||||
|
||||
+7
-7
@@ -3,20 +3,20 @@
|
||||
# Cross Platform and Multi Architecture Advanced Binary Emulation Framework
|
||||
#
|
||||
|
||||
from abc import ABCMeta, abstractmethod
|
||||
from abc import abstractmethod
|
||||
from gevent import Greenlet
|
||||
from qiling.const import *
|
||||
from .const import *
|
||||
|
||||
from qiling import Qiling
|
||||
|
||||
class QlThread(Greenlet):
|
||||
__metaclass__=ABCMeta
|
||||
|
||||
def __init__(self, ql):
|
||||
super(QlThread, self).__init__()
|
||||
def __init__(self, ql: Qiling):
|
||||
super().__init__()
|
||||
|
||||
self.ql = ql
|
||||
self.log_file_fd = None
|
||||
|
||||
def __str__(self):
|
||||
def __str__(self) -> str:
|
||||
return f"QlThread {self.get_id()}"
|
||||
|
||||
# the common functions which are used in qiling core.
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
import csv
|
||||
from typing import Mapping
|
||||
from os import path
|
||||
import inspect
|
||||
from pathlib import Path
|
||||
|
||||
def __init_guids_db() -> Mapping[str, str]:
|
||||
"""Initialize GUIDs dictionary from a local database.
|
||||
"""
|
||||
|
||||
csv_path = path.dirname(path.abspath(__file__))
|
||||
csv_path = path.join(csv_path, 'guids.csv')
|
||||
csv_path = Path(inspect.getfile(inspect.currentframe())).parent / 'guids.csv'
|
||||
|
||||
with open(csv_path) as guids_file:
|
||||
with csv_path.open('r') as guids_file:
|
||||
guids_reader = csv.reader(guids_file)
|
||||
|
||||
return dict(tuple(entry) for entry in guids_reader)
|
||||
|
||||
@@ -197,7 +197,9 @@ class QlOsUefi(QlOs):
|
||||
self.emit_stack()
|
||||
|
||||
self.ql.log.error(f'Memory map:')
|
||||
self.ql.mem.show_mapinfo()
|
||||
for info_line in self.ql.mem.get_formatted_mapinfo():
|
||||
self.ql.log.error(info_line)
|
||||
|
||||
|
||||
def set_api(self, target: str, handler: Callable, intercept: QL_INTERCEPT = QL_INTERCEPT.CALL):
|
||||
super().set_api(f'hook_{target}', handler, intercept)
|
||||
|
||||
+104
-20
@@ -7,12 +7,16 @@
|
||||
This module is intended for general purpose functions that are only used in qiling.os
|
||||
"""
|
||||
|
||||
from typing import MutableMapping, Union, Sequence, MutableSequence, Tuple
|
||||
import ctypes
|
||||
from typing import Callable, Iterable, Iterator, List, MutableMapping, Sequence, Tuple, TypeVar, Union
|
||||
from uuid import UUID
|
||||
|
||||
from qiling import Qiling
|
||||
from qiling.const import QL_VERBOSE
|
||||
|
||||
# TODO: separate windows-specific implementation
|
||||
from qiling.os.windows.structs import make_unicode_string
|
||||
|
||||
class QlOsUtils:
|
||||
|
||||
ELLIPSIS_PREF = r'__qlva_'
|
||||
@@ -101,39 +105,119 @@ class QlOsUtils:
|
||||
else:
|
||||
self.ql.log.info(log)
|
||||
|
||||
def __common_printf(self, format: str, args: MutableSequence, wstring: bool):
|
||||
fmtstr = format.split("%")[1:]
|
||||
def __common_printf(self, format: str, va_args: Iterator[int], wstring: bool) -> Tuple[str, Sequence[int]]:
|
||||
import re
|
||||
|
||||
# https://docs.microsoft.com/en-us/cpp/c-runtime-library/format-specification-syntax-printf-and-wprintf-functions
|
||||
# %[flags][width][.precision][size]type
|
||||
fmtstr = re.compile(r'''%
|
||||
(?P<follows>%|
|
||||
(?P<flags>[-+0 #]+)?
|
||||
(?P<width>[*]|[0-9]+)?
|
||||
(?:.(?P<precision>[*]|[0-9]+))?
|
||||
(?P<size>hh|ll|I32|I64|[hjltwzIL])?
|
||||
(?P<type>[diopuaAcCeEfFgGsSxXZ])
|
||||
)
|
||||
''', re.VERBOSE)
|
||||
|
||||
T = TypeVar('T')
|
||||
|
||||
def __dup(iterator: Iterator[T], out: List[T]) -> Iterator[T]:
|
||||
"""A wrapper iterator to record iterator elements as they are being yielded.
|
||||
"""
|
||||
|
||||
for elem in iterator:
|
||||
out.append(elem)
|
||||
yield elem
|
||||
|
||||
repl_args = [] # processed arguments
|
||||
orig_args = [] # original arguments
|
||||
|
||||
va_list = __dup(va_args, orig_args)
|
||||
|
||||
read_string = self.read_wstring if wstring else self.read_cstring
|
||||
|
||||
for i, f in enumerate(fmtstr):
|
||||
if f.startswith("s"):
|
||||
args[i] = read_string(args[i])
|
||||
def __repl(m: re.Match) -> str:
|
||||
"""Convert printf format string tokens into Python's.
|
||||
"""
|
||||
|
||||
out = format.replace(r'%llx', r'%x')
|
||||
out = out.replace(r'%p', r'%#x')
|
||||
if m['follows'] == '%':
|
||||
return '%%'
|
||||
|
||||
return out % tuple(args)
|
||||
else:
|
||||
flags = m['flags'] or ''
|
||||
|
||||
def va_list(self, format: str, ptr: int) -> MutableSequence[int]:
|
||||
count = format.count("%")
|
||||
fill = ' ' if ' ' in flags else ''
|
||||
align = '<' if '-' in flags else ''
|
||||
sign = '+' if '+' in flags else ''
|
||||
pound = '#' if '#' in flags else ''
|
||||
zeros = '0' if '0' in flags else ''
|
||||
|
||||
return [self.ql.mem.read_ptr(ptr + i * self.ql.arch.pointersize) for i in range(count)]
|
||||
width = m['width'] or ''
|
||||
|
||||
def sprintf(self, buff: int, format: str, args: MutableSequence, wstring: bool = False) -> int:
|
||||
out = self.__common_printf(format, args, wstring)
|
||||
if width == '*':
|
||||
width = f'{next(va_list)}'
|
||||
|
||||
prec = m['precision'] or ''
|
||||
|
||||
if prec == '*':
|
||||
prec = f'{next(va_list)}'
|
||||
|
||||
if prec:
|
||||
prec = f'.{prec}'
|
||||
|
||||
typ = m['type']
|
||||
arg = next(va_list)
|
||||
|
||||
if typ in 'sS':
|
||||
typ = 's'
|
||||
arg = read_string(arg)
|
||||
|
||||
elif typ == 'Z':
|
||||
# note: ANSI_STRING and UNICODE_STRING have identical layout
|
||||
strcls = make_unicode_string(self.ql.arch.bits).__class__
|
||||
data = self.ql.mem.read(arg, ctypes.sizeof(strcls))
|
||||
strobj = strcls.from_buffer_copy(data)
|
||||
|
||||
typ = 's'
|
||||
arg = read_string(strobj.Buffer)
|
||||
|
||||
elif typ == 'p':
|
||||
pound = '#'
|
||||
typ = 'x'
|
||||
|
||||
repl_args.append(arg)
|
||||
|
||||
return f'%{fill}{align}{sign}{pound}{zeros}{width}{prec}{typ}'
|
||||
|
||||
out = fmtstr.sub(__repl, format)
|
||||
|
||||
return out % tuple(repl_args), orig_args
|
||||
|
||||
def va_list(self, ptr: int) -> Iterator[int]:
|
||||
while True:
|
||||
yield self.ql.mem.read_ptr(ptr)
|
||||
|
||||
ptr += self.ql.arch.pointersize
|
||||
|
||||
def sprintf(self, buff: int, format: str, va_args: Iterator[int], wstring: bool = False) -> Tuple[int, Callable]:
|
||||
out, args = self.__common_printf(format, va_args, wstring)
|
||||
enc = 'utf-16le' if wstring else 'utf-8'
|
||||
|
||||
self.ql.mem.write(buff, (out + '\x00').encode(enc))
|
||||
|
||||
return len(out)
|
||||
return len(out), self.__update_ellipsis(args)
|
||||
|
||||
def printf(self, format: str, args: MutableSequence, wstring: bool = False) -> int:
|
||||
out = self.__common_printf(format, args, wstring)
|
||||
def printf(self, format: str, va_args: Iterator[int], wstring: bool = False) -> Tuple[int, Callable]:
|
||||
out, args = self.__common_printf(format, va_args, wstring)
|
||||
enc = 'utf-8'
|
||||
|
||||
self.ql.os.stdout.write(out.encode(enc))
|
||||
|
||||
return len(out)
|
||||
return len(out), self.__update_ellipsis(args)
|
||||
|
||||
def update_ellipsis(self, params: MutableMapping, args: Sequence) -> None:
|
||||
params.update((f'{QlOsUtils.ELLIPSIS_PREF}{i}', a) for i, a in enumerate(args))
|
||||
def __update_ellipsis(self, args: Iterable[int]) -> Callable[[MutableMapping], None]:
|
||||
def __do_update(params: MutableMapping) -> None:
|
||||
params.update((f'{QlOsUtils.ELLIPSIS_PREF}{i}', a) for i, a in enumerate(args))
|
||||
|
||||
return __do_update
|
||||
@@ -111,6 +111,7 @@ LPSTR = POINTER
|
||||
LPSYSTEMTIME = POINTER
|
||||
LPSYSTEM_INFO = POINTER
|
||||
LPTHREAD_START_ROUTINE = POINTER
|
||||
LPTIME_ZONE_INFORMATION = POINTER
|
||||
LPTOP_LEVEL_EXCEPTION_FILTER= POINTER
|
||||
LPUNKNOWN = POINTER
|
||||
LPVOID = POINTER
|
||||
|
||||
@@ -1,16 +1,17 @@
|
||||
#!/usr/bin/env python3
|
||||
#
|
||||
#
|
||||
# Cross Platform and Multi Architecture Advanced Binary Emulation Framework
|
||||
#
|
||||
# A Simple Windows Clipboard Simulation
|
||||
|
||||
from typing import TYPE_CHECKING, Optional
|
||||
|
||||
from qiling.os.windows.const import *
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from qiling.os.windows.windows import QlOsWindows
|
||||
|
||||
NOT_LOCKED = -1
|
||||
ERROR_CLIPBOARD_NOT_OPEN = 0x58a
|
||||
|
||||
class Clipboard:
|
||||
|
||||
@@ -19,9 +20,12 @@ class Clipboard:
|
||||
self.data = b"Default Clipboard Data"
|
||||
self.os = os
|
||||
|
||||
# Valid formats taken from https://doxygen.reactos.org/d8/dd6/base_
|
||||
# 2applications_2mstsc_2constants_8h_source.html
|
||||
self.formats = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 128, 129, 130, 131, 142, 512, 767, 768, 1023]
|
||||
self.formats = [
|
||||
CF_TEXT, CF_BITMAP, CF_METAFILEPICT, CF_SYLK, CF_DIF, CF_TIFF, CF_OEMTEXT, CF_DIB,
|
||||
CF_PALETTE, CF_PENDATA, CF_RIFF, CF_WAVE, CF_UNICODETEXT, CF_ENHMETAFILE, CF_HDROP,
|
||||
CF_LOCALE, CF_MAX, CF_OWNERDISPLAY, CF_DSPTEXT, CF_DSPBITMAP, CF_DSPMETAFILEPICT,
|
||||
CF_DSPENHMETAFILE, CF_PRIVATEFIRST, CF_PRIVATELAST, CF_GDIOBJFIRST, CF_GDIOBJLAST
|
||||
]
|
||||
|
||||
def open(self, h_wnd: int) -> bool:
|
||||
"""Lock clipboard to hWnd if not already locked.
|
||||
@@ -50,23 +54,35 @@ class Clipboard:
|
||||
def format_available(self, fmt: int) -> bool:
|
||||
return fmt in self.formats
|
||||
|
||||
def set_data(self, fmt: int, data: bytes) -> int:
|
||||
if fmt not in self.formats:
|
||||
return 0
|
||||
|
||||
def set_data(self, format: int, address: int) -> int:
|
||||
hWnd = self.os.thread_manager.cur_thread.id
|
||||
|
||||
if self.locked_by != hWnd:
|
||||
self.os.last_error = ERROR_CLIPBOARD_NOT_OPEN
|
||||
return 0
|
||||
|
||||
self.data = data
|
||||
def __handle_text(a: int) -> bytes:
|
||||
return self.os.utils.read_cstring(a).encode()
|
||||
|
||||
# BUG: this should be the handle of the clipboard object
|
||||
return 1
|
||||
def __handle_uctext(a: int) -> bytes:
|
||||
return self.os.utils.read_wstring(a).encode()
|
||||
|
||||
def get_data(self, fmt: int) -> Optional[bytes]:
|
||||
if fmt not in self.formats:
|
||||
# TODO: support more clipboard formats
|
||||
format_handlers = {
|
||||
CF_TEXT : __handle_text,
|
||||
CF_UNICODETEXT : __handle_uctext
|
||||
}
|
||||
|
||||
if format not in format_handlers:
|
||||
return 0
|
||||
|
||||
self.data = format_handlers[format](address)
|
||||
|
||||
# TODO: should create a handle for the clipboard object?
|
||||
return address
|
||||
|
||||
def get_data(self, format: int) -> Optional[bytes]:
|
||||
if format not in self.formats:
|
||||
return None
|
||||
|
||||
hWnd = self.os.thread_manager.cur_thread.id
|
||||
|
||||
+108
-37
@@ -19,7 +19,9 @@ ERROR_ALREADY_EXISTS = 0xB7
|
||||
ERROR_MORE_DATA = 0xEA
|
||||
ERROR_NO_MORE_ITEMS = 0x103
|
||||
ERROR_NOT_OWNER = 0x120
|
||||
ERROR_OLD_WIN_VERSION = 0X47E
|
||||
ERROR_NO_UNICODE_TRANSLATION = 0x459
|
||||
ERROR_OLD_WIN_VERSION = 0x47E
|
||||
ERROR_CLIPBOARD_NOT_OPEN = 0x58a
|
||||
# ...
|
||||
|
||||
# https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-erref/596a1078-e883-4972-9bbc-49e60bebca55
|
||||
@@ -35,25 +37,12 @@ STATUS_NO_YIELD_PERFORMED = 0x40000024
|
||||
|
||||
INVALID_HANDLE_VALUE = -1
|
||||
|
||||
STD_INPUT_HANDLE = 0xfffffff6
|
||||
STD_OUTPUT_HANDLE = 0xfffffff5
|
||||
STD_ERROR_HANDLE = 0xfffffff4
|
||||
STD_INPUT_HANDLE = 0xfffffff6 # -10
|
||||
STD_OUTPUT_HANDLE = 0xfffffff5 # -11
|
||||
STD_ERROR_HANDLE = 0xfffffff4 # -12
|
||||
|
||||
# Registry Type
|
||||
|
||||
# Predefined Keys
|
||||
REG_KEYS = {
|
||||
0x80000000: "HKEY_CLASSES_ROOT",
|
||||
0x80000005: "HKEY_CURRENT_CONFIG",
|
||||
0x80000001: "HKEY_CURRENT_USER",
|
||||
0x80000007: "HKEY_CURRENT_USER_LOCAL_SETTINGS",
|
||||
0x80000002: "HKEY_LOCAL_MACHINE",
|
||||
0x80000004: "HKEY_PERFORMANCE_DATA",
|
||||
0x80000060: "HKEY_PERFORMANCE_NLSTEXT",
|
||||
0x80000050: "HKEY_PERFORMANCE_TEXT",
|
||||
0x80000003: "HKEY_USERS"
|
||||
}
|
||||
|
||||
REG_TYPES = {
|
||||
"REG_NONE": Registry.RegNone,
|
||||
"REG_SZ": Registry.RegSZ,
|
||||
@@ -611,17 +600,23 @@ PF_XSAVE_ENABLED = 0x17
|
||||
# ...
|
||||
|
||||
|
||||
# documented PROCESSINFOCLASS values
|
||||
# https://docs.microsoft.com/en-us/windows/win32/procthread/zwqueryinformationprocess
|
||||
# https://www.pinvoke.net/default.aspx/ntdll/PROCESSINFOCLASS.html
|
||||
ProcessBasicInformation = 0
|
||||
ProcessDebugPort = 7
|
||||
ProcessExecuteFlags = 0x22
|
||||
ProcessWow64Information = 26
|
||||
ProcessImageFileName = 27
|
||||
ProcessBreakOnTermination = 29
|
||||
ProcessProtectionInformation = 61
|
||||
ProcessDebugObjectHandle = 0x1E
|
||||
ProcessDebugFlags = 0x1F
|
||||
|
||||
# more PROCESSINFOCLASS values
|
||||
# https://www.pinvoke.net/default.aspx/ntdll/PROCESSINFOCLASS.html
|
||||
ProcessDebugObjectHandle = 30
|
||||
ProcessDebugFlags = 31
|
||||
ProcessExecuteFlags = 34
|
||||
ProcessImageInformation = 37
|
||||
ProcessMitigationPolicy = 52
|
||||
ProcessFaultInformation = 63
|
||||
|
||||
# https://www.geoffchappell.com/studies/windows/km/ntoskrnl/api/ps/psquery/class.htm
|
||||
ThreadBasicInformation = 0x0
|
||||
@@ -632,26 +627,79 @@ ThreadHideFromDebugger = 0x11
|
||||
# ...
|
||||
|
||||
|
||||
# message box types:
|
||||
# https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-messageboxw
|
||||
|
||||
MB_ABORTRETRYIGNORE = 0x000000020
|
||||
MB_CANCELTRYCONTINUE = 0x000000060
|
||||
MB_HELP = 0x000040000
|
||||
MB_OK = 0x000000000
|
||||
MB_OKCANCEL = 0x000000010
|
||||
MB_RETRYCANCEL = 0x000000050
|
||||
MB_YESNO = 0x000000040
|
||||
MB_YESNOCANCEL = 0x000000030
|
||||
MB_OK = 0x00000000
|
||||
MB_OKCANCEL = 0x00000001
|
||||
MB_ABORTRETRYIGNORE = 0x00000002
|
||||
MB_YESNOCANCEL = 0x00000003
|
||||
MB_YESNO = 0x00000004
|
||||
MB_RETRYCANCEL = 0x00000005
|
||||
MB_CANCELTRYCONTINUE = 0x00000006
|
||||
|
||||
IDABORT = 3
|
||||
IDCANCEL = 2
|
||||
IDCONTINUE = 11
|
||||
IDIGNORE = 5
|
||||
IDNO = 7
|
||||
IDOK = 1
|
||||
IDRETRY = 4
|
||||
MB_ICONERROR = 0x00000010
|
||||
MB_ICONQUESTION = 0x00000020
|
||||
MB_ICONWARNING = 0x00000030
|
||||
MB_ICONINFORMATION = 0x00000040
|
||||
|
||||
MB_DEFBUTTON1 = 0x00000000
|
||||
MB_DEFBUTTON2 = 0x00000100
|
||||
MB_DEFBUTTON3 = 0x00000200
|
||||
MB_DEFBUTTON4 = 0x00000300
|
||||
|
||||
MB_APPLMODAL = 0x00000000
|
||||
MB_SYSTEMMODAL = 0x00001000
|
||||
MB_TASKMODAL = 0x00002000
|
||||
MB_HELP = 0x00004000
|
||||
|
||||
MB_SETFOREGROUND = 0x00010000
|
||||
MB_DEFAULT_DESKTOP_ONLY = 0x00020000
|
||||
MB_TOPMOST = 0x00040000
|
||||
MB_RIGHT = 0x00080000
|
||||
MB_RTLREADING = 0x00100000
|
||||
MB_SERVICE_NOTIFICATION = 0x00200000
|
||||
|
||||
# message box responses:
|
||||
IDOK = 1
|
||||
IDCANCEL = 2
|
||||
IDABORT = 3
|
||||
IDRETRY = 4
|
||||
IDIGNORE = 5
|
||||
IDYES = 6
|
||||
IDNO = 7
|
||||
IDTRYAGAIN = 10
|
||||
IDYES = 6
|
||||
IDCONTINUE = 11
|
||||
|
||||
# clipboard data types
|
||||
# https://doxygen.reactos.org/d8/dd6/base_2applications_2mstsc_2constants_8h_source.html
|
||||
|
||||
CF_TEXT = 1
|
||||
CF_BITMAP = 2
|
||||
CF_METAFILEPICT = 3
|
||||
CF_SYLK = 4
|
||||
CF_DIF = 5
|
||||
CF_TIFF = 6
|
||||
CF_OEMTEXT = 7
|
||||
CF_DIB = 8
|
||||
CF_PALETTE = 9
|
||||
CF_PENDATA = 10
|
||||
CF_RIFF = 11
|
||||
CF_WAVE = 12
|
||||
CF_UNICODETEXT = 13
|
||||
CF_ENHMETAFILE = 14
|
||||
CF_HDROP = 15
|
||||
CF_LOCALE = 16
|
||||
CF_MAX = 17
|
||||
CF_OWNERDISPLAY = 128
|
||||
CF_DSPTEXT = 129
|
||||
CF_DSPBITMAP = 130
|
||||
CF_DSPMETAFILEPICT = 131
|
||||
CF_DSPENHMETAFILE = 142
|
||||
CF_PRIVATEFIRST = 512
|
||||
CF_PRIVATELAST = 767
|
||||
CF_GDIOBJFIRST = 768
|
||||
CF_GDIOBJLAST = 1023
|
||||
|
||||
# https://docs.microsoft.com/en-us/windows/win32/api/libloaderapi/nf-libloaderapi-setdefaultdlldirectories
|
||||
|
||||
@@ -709,3 +757,26 @@ OF_SHARE_EXCLUSIVE = 0x00000010
|
||||
OF_SHARE_DENY_WRITE = 0x00000020
|
||||
OF_SHARE_DENY_READ = 0x00000030
|
||||
OF_SHARE_DENY_NONE = 0x00000040
|
||||
|
||||
# code pages identifiers
|
||||
CP_ACP = 0
|
||||
CP_OEMCP = 1
|
||||
CP_THREAD_ACP = 3
|
||||
CP_UTF16 = 1200
|
||||
CP_UTF16BE = 1201
|
||||
CP_ASCII = 20127
|
||||
CP_UTF7 = 65000
|
||||
CP_UTF8 = 65001
|
||||
|
||||
# conversion types
|
||||
WC_DISCARDNS = 0x0010
|
||||
WC_SEPCHARS = 0x0020
|
||||
WC_DEFAULTCHAR = 0x0040
|
||||
WC_ERR_INVALID_CHARS = 0x0080
|
||||
WC_COMPOSITECHECK = 0x0200
|
||||
WC_NO_BEST_FIT_CHARS = 0x0400
|
||||
|
||||
TIME_ZONE_ID_INVALID = -1
|
||||
TIME_ZONE_ID_UNKNOWN = 0
|
||||
TIME_ZONE_ID_STANDARD = 1
|
||||
TIME_ZONE_ID_DAYLIGHT = 2
|
||||
|
||||
@@ -14,27 +14,35 @@ def __RegOpenKey(ql: Qiling, address: int, params):
|
||||
lpSubKey = params["lpSubKey"]
|
||||
phkResult = params["phkResult"]
|
||||
|
||||
if hKey not in REG_KEYS:
|
||||
handle = ql.os.handle_manager.get(hKey)
|
||||
|
||||
if handle is None or not handle.name.startswith('HKEY'):
|
||||
return ERROR_FILE_NOT_FOUND
|
||||
|
||||
s_hKey = REG_KEYS[hKey]
|
||||
key = s_hKey + "\\" + lpSubKey
|
||||
params["hKey"] = handle.name
|
||||
|
||||
# Keys in the profile are saved as KEY\PARAM = VALUE, so i just want to check that the key is the same
|
||||
keys_profile = [key.rsplit("\\", 1)[0] for key in ql.os.profile["REGISTRY"].keys()]
|
||||
if key.lower() in keys_profile:
|
||||
ql.log.debug("Using profile for key of %s" % key)
|
||||
ql.os.registry_manager.access(key)
|
||||
else:
|
||||
if not ql.os.registry_manager.exists(key):
|
||||
ql.log.debug("Value key %s not present" % key)
|
||||
return ERROR_FILE_NOT_FOUND
|
||||
if lpSubKey:
|
||||
key = f'{handle.name}\\{lpSubKey}'
|
||||
|
||||
# Keys in the profile are saved as KEY\PARAM = VALUE, so i just want to check that the key is the same
|
||||
keys_profile = [entry.casefold() for entry in ql.os.profile["REGISTRY"].keys()]
|
||||
|
||||
if key.casefold() in keys_profile:
|
||||
ql.log.debug("Using profile for key of %s" % key)
|
||||
ql.os.registry_manager.access(key)
|
||||
|
||||
else:
|
||||
if not ql.os.registry_manager.exists(key):
|
||||
ql.log.debug("Value key %s not present" % key)
|
||||
return ERROR_FILE_NOT_FOUND
|
||||
|
||||
# new handle
|
||||
handle = Handle(obj=key)
|
||||
ql.os.handle_manager.append(handle)
|
||||
|
||||
if phkResult:
|
||||
ql.mem.write_ptr(phkResult, handle.id)
|
||||
|
||||
# new handle
|
||||
new_handle = Handle(obj=key)
|
||||
ql.os.handle_manager.append(new_handle)
|
||||
if phkResult != 0:
|
||||
ql.mem.write_ptr(phkResult, new_handle.id)
|
||||
return ERROR_SUCCESS
|
||||
|
||||
def __RegQueryValue(ql: Qiling, address: int, params, wstring: bool):
|
||||
@@ -84,35 +92,34 @@ def __RegQueryValue(ql: Qiling, address: int, params, wstring: bool):
|
||||
return ret
|
||||
|
||||
def __RegCreateKey(ql: Qiling, address: int, params):
|
||||
ret = ERROR_SUCCESS
|
||||
|
||||
hKey = params["hKey"]
|
||||
lpSubKey = params["lpSubKey"]
|
||||
phkResult = params["phkResult"]
|
||||
|
||||
if hKey not in REG_KEYS:
|
||||
handle = ql.os.handle_manager.get(hKey)
|
||||
|
||||
if handle is None or not handle.name.startswith('HKEY'):
|
||||
return ERROR_FILE_NOT_FOUND
|
||||
|
||||
s_hKey = REG_KEYS[hKey]
|
||||
params["hKey"] = s_hKey
|
||||
params["hKey"] = handle.name
|
||||
|
||||
keyname = f'{s_hKey}\\{lpSubKey}'
|
||||
if lpSubKey:
|
||||
keyname = f'{handle.name}\\{lpSubKey}'
|
||||
|
||||
if not ql.os.registry_manager.exists(keyname):
|
||||
ql.os.registry_manager.create(keyname)
|
||||
ret = ERROR_SUCCESS
|
||||
if not ql.os.registry_manager.exists(keyname):
|
||||
ql.os.registry_manager.create(keyname)
|
||||
|
||||
# new handle
|
||||
if ret == ERROR_SUCCESS:
|
||||
new_handle = Handle(obj=keyname)
|
||||
ql.os.handle_manager.append(new_handle)
|
||||
if phkResult != 0:
|
||||
ql.mem.write_ptr(phkResult, new_handle.id)
|
||||
else:
|
||||
# elicn: is this even reachable?
|
||||
new_handle = 0
|
||||
handle = ql.os.handle_manager.search_by_obj(keyname)
|
||||
|
||||
return ret
|
||||
# make sure we have a handle for this keyname
|
||||
if handle is None:
|
||||
handle = Handle(obj=keyname)
|
||||
ql.os.handle_manager.append(handle)
|
||||
|
||||
if phkResult:
|
||||
ql.mem.write_ptr(phkResult, handle.id)
|
||||
|
||||
return ERROR_SUCCESS
|
||||
|
||||
def __RegSetValue(ql: Qiling, address: int, params, wstring: bool):
|
||||
hKey = params["hKey"]
|
||||
@@ -458,11 +465,17 @@ def hook_GetTokenInformation(ql: Qiling, address: int, params):
|
||||
TokenInformationLength = params["TokenInformationLength"]
|
||||
ReturnLength = params["ReturnLength"]
|
||||
|
||||
token = ql.os.handle_manager.get(TokenHandle).obj
|
||||
handle = ql.os.handle_manager.get(TokenHandle)
|
||||
|
||||
if handle is None:
|
||||
ql.os.last_error = ERROR_INVALID_HANDLE
|
||||
return 0
|
||||
|
||||
token = handle.obj
|
||||
information_value = token.get(TokenInformationClass)
|
||||
|
||||
ql.mem.write_ptr(ReturnLength, len(information_value), 4)
|
||||
return_size = ql.mem.read_ptr(ReturnLength, 4)
|
||||
return_size = len(information_value)
|
||||
ql.mem.write_ptr(ReturnLength, return_size, 4)
|
||||
|
||||
ql.log.debug("The target is checking for its permissions")
|
||||
|
||||
|
||||
@@ -23,3 +23,4 @@ from .consoleapi2 import *
|
||||
from .consoleapi3 import *
|
||||
from .debugapi import *
|
||||
from .psapi import *
|
||||
from .timezoneapi import *
|
||||
|
||||
@@ -22,10 +22,13 @@ def hook_SetUnhandledExceptionFilter(ql: Qiling, address: int, params):
|
||||
if handle is None:
|
||||
handle = Handle(name="TopLevelExceptionHandler", obj=addr)
|
||||
ql.os.handle_manager.append(handle)
|
||||
prev_filter = 0
|
||||
|
||||
else:
|
||||
prev_filter = handle.obj
|
||||
handle.obj = addr
|
||||
|
||||
return 0
|
||||
return prev_filter
|
||||
|
||||
# _Post_equals_last_error_ DWORD GetLastError();
|
||||
@winsdkapi(cc=STDCALL, params={})
|
||||
@@ -73,11 +76,19 @@ def hook_SetErrorMode(ql: Qiling, address: int, params):
|
||||
'lpArguments' : POINTER
|
||||
})
|
||||
def hook_RaiseException(ql: Qiling, address: int, params):
|
||||
func_addr = ql.os.handle_manager.search("TopLevelExceptionHandler").obj
|
||||
nNumberOfArguments = params['nNumberOfArguments']
|
||||
lpArguments = params['lpArguments']
|
||||
|
||||
ql.os.fcall.call_native(func_addr, [], None)
|
||||
handle = ql.os.handle_manager.search("TopLevelExceptionHandler")
|
||||
|
||||
return 0
|
||||
if handle is None:
|
||||
ql.log.warning(f'RaiseException: top level exception handler not found')
|
||||
return
|
||||
|
||||
exception_handler = handle.obj
|
||||
args = [(PARAM_INTN, ql.mem.read_ptr(lpArguments + i * ql.arch.pointersize)) for i in range(nNumberOfArguments)] if lpArguments else []
|
||||
|
||||
ql.os.fcall.call_native(exception_handler, args, None)
|
||||
|
||||
# PVOID AddVectoredExceptionHandler(
|
||||
# ULONG First,
|
||||
|
||||
@@ -26,18 +26,13 @@ from qiling.os.windows.structs import Win32FindData
|
||||
def hook_GetFileType(ql: Qiling, address: int, params):
|
||||
hFile = params["hFile"]
|
||||
|
||||
if hFile in (STD_INPUT_HANDLE, STD_OUTPUT_HANDLE, STD_ERROR_HANDLE):
|
||||
ret = FILE_TYPE_CHAR
|
||||
else:
|
||||
obj = ql.os.handle_manager.get(hFile)
|
||||
handle = ql.os.handle_manager.get(hFile)
|
||||
|
||||
if obj is None:
|
||||
raise QlErrorNotImplemented("API not implemented")
|
||||
else:
|
||||
# technically is not always a type_char but.. almost
|
||||
ret = FILE_TYPE_CHAR
|
||||
if handle is None:
|
||||
raise QlErrorNotImplemented("API not implemented")
|
||||
|
||||
return ret
|
||||
# technically is not always a type_char but.. almost
|
||||
return FILE_TYPE_CHAR
|
||||
|
||||
# HANDLE FindFirstFileA(
|
||||
# LPCSTR lpFileName,
|
||||
@@ -156,28 +151,16 @@ def hook_ReadFile(ql: Qiling, address: int, params):
|
||||
nNumberOfBytesToRead = params["nNumberOfBytesToRead"]
|
||||
lpNumberOfBytesRead = params["lpNumberOfBytesRead"]
|
||||
|
||||
if hFile == STD_INPUT_HANDLE:
|
||||
if ql.os.automatize_input:
|
||||
# TODO maybe insert a good random generation input
|
||||
s = (b"A" * (nNumberOfBytesToRead - 1)) + b"\x00"
|
||||
else:
|
||||
ql.log.debug("Insert input")
|
||||
s = ql.os.stdin.read(nNumberOfBytesToRead)
|
||||
handle = ql.os.handle_manager.get(hFile)
|
||||
|
||||
slen = len(s)
|
||||
read_len = slen
|
||||
if handle is None:
|
||||
ql.os.last_error = ERROR_INVALID_HANDLE
|
||||
return 0
|
||||
|
||||
if slen > nNumberOfBytesToRead:
|
||||
s = s[:nNumberOfBytesToRead]
|
||||
read_len = nNumberOfBytesToRead
|
||||
data = handle.obj.read(nNumberOfBytesToRead)
|
||||
|
||||
ql.mem.write(lpBuffer, s)
|
||||
ql.mem.write_ptr(lpNumberOfBytesRead, read_len, 4)
|
||||
else:
|
||||
f = ql.os.handle_manager.get(hFile).obj
|
||||
data = f.read(nNumberOfBytesToRead)
|
||||
ql.mem.write(lpBuffer, data)
|
||||
ql.mem.write_ptr(lpNumberOfBytesRead, len(data), 4)
|
||||
ql.mem.write(lpBuffer, data)
|
||||
ql.mem.write_ptr(lpNumberOfBytesRead, len(data), 4)
|
||||
|
||||
return 1
|
||||
|
||||
@@ -201,24 +184,20 @@ def hook_WriteFile(ql: Qiling, address: int, params):
|
||||
nNumberOfBytesToWrite = params["nNumberOfBytesToWrite"]
|
||||
lpNumberOfBytesWritten = params["lpNumberOfBytesWritten"]
|
||||
|
||||
handle = ql.os.handle_manager.get(hFile)
|
||||
|
||||
if handle is None:
|
||||
ql.os.last_error = ERROR_INVALID_HANDLE
|
||||
return 0
|
||||
|
||||
fobj = handle.obj
|
||||
data = ql.mem.read(lpBuffer, nNumberOfBytesToWrite)
|
||||
|
||||
if hFile == STD_OUTPUT_HANDLE:
|
||||
s = ql.mem.read(lpBuffer, nNumberOfBytesToWrite)
|
||||
ql.os.stdout.write(s)
|
||||
ql.os.stats.log_string(s.decode())
|
||||
ql.mem.write_ptr(lpNumberOfBytesWritten, nNumberOfBytesToWrite, 4)
|
||||
else:
|
||||
f = ql.os.handle_manager.get(hFile)
|
||||
ql.os.stats.log_string(data.decode())
|
||||
|
||||
if f is None:
|
||||
# Invalid handle
|
||||
ql.os.last_error = ERROR_INVALID_HANDLE
|
||||
return 0
|
||||
else:
|
||||
f = f.obj
|
||||
|
||||
buffer = ql.mem.read(lpBuffer, nNumberOfBytesToWrite)
|
||||
nNumberOfBytesWritten = f.write(bytes(buffer))
|
||||
ql.mem.write_ptr(lpNumberOfBytesWritten, nNumberOfBytesWritten, 4)
|
||||
written = fobj.write(bytes(data))
|
||||
ql.mem.write_ptr(lpNumberOfBytesWritten, written, 4)
|
||||
|
||||
return 1
|
||||
|
||||
|
||||
@@ -48,12 +48,12 @@ def hook_CloseHandle(ql: Qiling, address: int, params):
|
||||
if handle is None:
|
||||
ql.os.last_error = ERROR_INVALID_HANDLE
|
||||
return 0
|
||||
else:
|
||||
if handle.permissions is not None and handle.permissions & HANDLE_FLAG_PROTECT_FROM_CLOSE >= 1:
|
||||
# FIXME: add error
|
||||
return 0
|
||||
else:
|
||||
ql.os.handle_manager.delete(value)
|
||||
|
||||
if handle.permissions is not None and handle.permissions & HANDLE_FLAG_PROTECT_FROM_CLOSE:
|
||||
# FIXME: add error
|
||||
return 0
|
||||
|
||||
ql.os.handle_manager.delete(value)
|
||||
|
||||
return 1
|
||||
|
||||
|
||||
@@ -18,12 +18,10 @@ def _GetModuleHandle(ql: Qiling, address: int, params):
|
||||
if lpModuleName == 0:
|
||||
ret = ql.loader.pe_image_address
|
||||
else:
|
||||
lpModuleName = lpModuleName.lower()
|
||||
|
||||
if not has_lib_ext(lpModuleName):
|
||||
lpModuleName = f'{lpModuleName}.dll'
|
||||
|
||||
image = ql.loader.get_image_by_name(lpModuleName)
|
||||
image = ql.loader.get_image_by_name(lpModuleName, casefold=True)
|
||||
|
||||
if image:
|
||||
ret = image.base
|
||||
@@ -69,6 +67,35 @@ def hook_GetModuleHandleExW(ql: Qiling, address: int, params):
|
||||
|
||||
return res
|
||||
|
||||
def __GetModuleFileName(ql: Qiling, address: int, params, *, wide: bool):
|
||||
hModule = params["hModule"]
|
||||
lpFilename = params["lpFilename"]
|
||||
nSize = params["nSize"]
|
||||
|
||||
if not hModule:
|
||||
if ql.code:
|
||||
raise QlErrorNotImplemented('cannot retrieve module file name in shellcode mode')
|
||||
|
||||
hModule = ql.loader.pe_image_address
|
||||
|
||||
hpath = next((image.path for image in ql.loader.images if image.base == hModule), None)
|
||||
|
||||
if hpath is None:
|
||||
ql.os.last_error = ERROR_INVALID_HANDLE
|
||||
return 0
|
||||
|
||||
encname = 'utf-16le' if wide else 'latin'
|
||||
vpath = ql.os.path.host_to_virtual_path(hpath)
|
||||
truncated = vpath[:nSize - 1] + '\x00'
|
||||
encoded = truncated.encode(encname)
|
||||
|
||||
if len(vpath) + 1 > nSize:
|
||||
ql.os.last_error = ERROR_INSUFFICIENT_BUFFER
|
||||
|
||||
ql.mem.write(lpFilename, encoded)
|
||||
|
||||
return min(len(vpath), nSize)
|
||||
|
||||
# DWORD GetModuleFileNameA(
|
||||
# HMODULE hModule,
|
||||
# LPSTR lpFilename,
|
||||
@@ -80,30 +107,7 @@ def hook_GetModuleHandleExW(ql: Qiling, address: int, params):
|
||||
'nSize' : DWORD
|
||||
})
|
||||
def hook_GetModuleFileNameA(ql: Qiling, address: int, params):
|
||||
hModule = params["hModule"]
|
||||
lpFilename = params["lpFilename"]
|
||||
nSize = params["nSize"]
|
||||
ret = 0
|
||||
|
||||
# GetModuleHandle can return pe_image_address as handle, and GetModuleFileName will try to retrieve it.
|
||||
# Pretty much 0 and pe_image_address value should do the same operations
|
||||
if not ql.code and (hModule == 0 or hModule == ql.loader.pe_image_address):
|
||||
filename = ql.loader.filepath
|
||||
filename_len = len(filename)
|
||||
|
||||
if filename_len > nSize - 1:
|
||||
filename = ql.loader.filepath[:nSize - 1]
|
||||
ret = nSize
|
||||
else:
|
||||
ret = filename_len
|
||||
|
||||
ql.mem.write(lpFilename, filename + b"\x00")
|
||||
|
||||
else:
|
||||
ql.log.debug("hModule %x" % hModule)
|
||||
raise QlErrorNotImplemented("API not implemented")
|
||||
|
||||
return ret
|
||||
return __GetModuleFileName(ql, address, params, wide=False)
|
||||
|
||||
# DWORD GetModuleFileNameW(
|
||||
# HMODULE hModule,
|
||||
@@ -116,30 +120,7 @@ def hook_GetModuleFileNameA(ql: Qiling, address: int, params):
|
||||
'nSize' : DWORD
|
||||
})
|
||||
def hook_GetModuleFileNameW(ql: Qiling, address: int, params):
|
||||
hModule = params["hModule"]
|
||||
lpFilename = params["lpFilename"]
|
||||
nSize = params["nSize"]
|
||||
ret = 0
|
||||
|
||||
# GetModuleHandle can return pe_image_address as handle, and GetModuleFileName will try to retrieve it.
|
||||
# Pretty much 0 and pe_image_address value should do the same operations
|
||||
if not ql.code and (hModule == 0 or hModule == ql.loader.pe_image_address):
|
||||
filename = ql.loader.filepath.decode('ascii').encode('utf-16le')
|
||||
filename_len = len(filename)
|
||||
|
||||
if filename_len > nSize - 1:
|
||||
filename = ql.loader.filepath[:nSize - 1]
|
||||
ret = nSize
|
||||
else:
|
||||
ret = filename_len
|
||||
|
||||
ql.mem.write(lpFilename, filename + b"\x00")
|
||||
|
||||
else:
|
||||
ql.log.debug("hModule %x" % hModule)
|
||||
raise QlErrorNotImplemented("API not implemented")
|
||||
|
||||
return ret
|
||||
return __GetModuleFileName(ql, address, params, wide=True)
|
||||
|
||||
# FARPROC GetProcAddress(
|
||||
# HMODULE hModule,
|
||||
@@ -188,9 +169,11 @@ def hook_GetProcAddress(ql: Qiling, address: int, params):
|
||||
def _LoadLibrary(ql: Qiling, address: int, params):
|
||||
lpLibFileName = params["lpLibFileName"]
|
||||
|
||||
if not ql.code and lpLibFileName == ql.loader.filepath.decode():
|
||||
# Loading self
|
||||
return ql.loader.pe_image_address
|
||||
# TODO: this searches only by basename; do we need to search by full path as well?
|
||||
dll = ql.loader.get_image_by_name(lpLibFileName, casefold=True)
|
||||
|
||||
if dll is not None:
|
||||
return dll.base
|
||||
|
||||
return ql.loader.load_dll(lpLibFileName)
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ from qiling.os.windows.api import *
|
||||
from qiling.os.windows.const import *
|
||||
from qiling.os.windows.fncc import *
|
||||
|
||||
from qiling.os.windows.thread import QlWindowsThread
|
||||
from qiling.os.windows.thread import QlWindowsThread, THREAD_STATUS
|
||||
from qiling.os.windows.handle import Handle
|
||||
from qiling.os.windows.structs import Token, StartupInfo
|
||||
|
||||
@@ -164,20 +164,13 @@ def hook_CreateThread(ql: Qiling, address: int, params):
|
||||
dwCreationFlags = params["dwCreationFlags"]
|
||||
lpThreadId = params["lpThreadId"]
|
||||
|
||||
# new thread obj
|
||||
new_thread = QlWindowsThread(ql)
|
||||
|
||||
if dwCreationFlags & CREATE_SUSPENDED == CREATE_SUSPENDED:
|
||||
thread_status = QlWindowsThread.READY
|
||||
if dwCreationFlags & CREATE_SUSPENDED:
|
||||
thread_status = THREAD_STATUS.READY
|
||||
else:
|
||||
thread_status = QlWindowsThread.RUNNING
|
||||
thread_status = THREAD_STATUS.RUNNING
|
||||
|
||||
# create new thread
|
||||
thread_id = new_thread.create(
|
||||
lpStartAddress,
|
||||
lpParameter,
|
||||
thread_status
|
||||
)
|
||||
new_thread = QlWindowsThread.create(ql, dwStackSize, lpStartAddress, lpParameter, thread_status)
|
||||
|
||||
# append the new thread to ThreadManager
|
||||
ql.os.thread_manager.append(new_thread)
|
||||
|
||||
@@ -7,6 +7,7 @@ from qiling import Qiling
|
||||
from qiling.os.windows.api import *
|
||||
from qiling.os.windows.fncc import *
|
||||
from qiling.os.windows.utils import cmp
|
||||
from qiling.os.windows.const import *
|
||||
|
||||
# BOOL GetStringTypeW(
|
||||
# DWORD dwInfoType,
|
||||
@@ -43,6 +44,42 @@ def hook_GetStringTypeExA(ql: Qiling, address: int, params):
|
||||
# TODO: implement
|
||||
return 1
|
||||
|
||||
def __encoding_name(codepage: int) -> str:
|
||||
'''Get python encoding name from codepage value.
|
||||
|
||||
@see: https://docs.python.org/3.8/library/codecs.html#standard-encodings
|
||||
'''
|
||||
|
||||
# mapping of special codepage values to encodings
|
||||
encodings = {
|
||||
# available only on windows hosts
|
||||
CP_ACP : 'mbcs',
|
||||
CP_OEMCP : 'oem',
|
||||
CP_THREAD_ACP : 'mbcs',
|
||||
|
||||
# avaiable everywhere
|
||||
CP_UTF16 : 'utf-16',
|
||||
CP_UTF16BE : 'utf-16be',
|
||||
CP_ASCII : 'ascii',
|
||||
CP_UTF7 : 'utf-7',
|
||||
CP_UTF8 : 'utf-8'
|
||||
}
|
||||
|
||||
if codepage in encodings:
|
||||
encname = encodings[codepage]
|
||||
|
||||
else:
|
||||
encname = f'cp{codepage}'
|
||||
|
||||
# encoding might break on the hosting system; test it and
|
||||
# fallback to windows-1252 ('western') if it fails
|
||||
try:
|
||||
_ = '\x00'.encode(encname)
|
||||
except LookupError:
|
||||
encname = 'cp1252'
|
||||
|
||||
return encname
|
||||
|
||||
# int WideCharToMultiByte(
|
||||
# UINT CodePage,
|
||||
# DWORD dwFlags,
|
||||
@@ -56,7 +93,7 @@ def hook_GetStringTypeExA(ql: Qiling, address: int, params):
|
||||
@winsdkapi(cc=STDCALL, params={
|
||||
'CodePage' : UINT,
|
||||
'dwFlags' : DWORD,
|
||||
'lpWideCharStr' : WSTRING, # LPCWCH
|
||||
'lpWideCharStr' : POINTER, # WSTRING
|
||||
'cchWideChar' : INT,
|
||||
'lpMultiByteStr' : LPSTR,
|
||||
'cbMultiByte' : INT,
|
||||
@@ -64,16 +101,54 @@ def hook_GetStringTypeExA(ql: Qiling, address: int, params):
|
||||
'lpUsedDefaultChar' : LPBOOL
|
||||
})
|
||||
def hook_WideCharToMultiByte(ql: Qiling, address: int, params):
|
||||
CodePage = params['CodePage']
|
||||
dwFlags = params['dwFlags']
|
||||
lpWideCharStr = params['lpWideCharStr']
|
||||
cchWideChar = params['cchWideChar']
|
||||
lpMultiByteStr = params['lpMultiByteStr']
|
||||
cbMultiByte = params["cbMultiByte"]
|
||||
s_lpWideCharStr = params["lpWideCharStr"]
|
||||
lpMultiByteStr = params["lpMultiByteStr"]
|
||||
|
||||
s = (s_lpWideCharStr + "\x00").encode("utf-16le")
|
||||
if not cchWideChar:
|
||||
# TODO: set last error
|
||||
return 0
|
||||
|
||||
if cbMultiByte != 0 and lpMultiByteStr != 0:
|
||||
ql.mem.write(lpMultiByteStr, s)
|
||||
# -1 indicates the string is null-terminated. the string is
|
||||
# read along with its null-terminator
|
||||
elif cchWideChar == 0xffffffff:
|
||||
wcstr = bytearray()
|
||||
ch = 1
|
||||
|
||||
return len(s)
|
||||
while ch != b'\x00\x00':
|
||||
ch = ql.mem.read(lpWideCharStr, 2)
|
||||
wcstr.extend(ch)
|
||||
|
||||
lpWideCharStr += 2
|
||||
|
||||
# read exactly cbMultiByte bytes. that may or may not include
|
||||
# a null-terminator
|
||||
else:
|
||||
wcstr = ql.mem.read(lpWideCharStr, cchWideChar * 2)
|
||||
|
||||
decoded = wcstr.decode('utf-16le')
|
||||
|
||||
encname = __encoding_name(CodePage)
|
||||
errors = 'strict' if dwFlags & WC_ERR_INVALID_CHARS else 'replace'
|
||||
|
||||
try:
|
||||
encoded = decoded.encode(encname, errors)
|
||||
except UnicodeEncodeError:
|
||||
ql.os.last_error = ERROR_NO_UNICODE_TRANSLATION
|
||||
return 0
|
||||
|
||||
if not cbMultiByte:
|
||||
return len(encoded)
|
||||
|
||||
if not lpMultiByteStr:
|
||||
return 0
|
||||
|
||||
ql.mem.write(lpMultiByteStr, encoded[:cbMultiByte])
|
||||
|
||||
return min(len(encoded), cbMultiByte)
|
||||
|
||||
# int MultiByteToWideChar(
|
||||
# UINT CodePage,
|
||||
@@ -86,18 +161,53 @@ def hook_WideCharToMultiByte(ql: Qiling, address: int, params):
|
||||
@winsdkapi(cc=STDCALL, params={
|
||||
'CodePage' : UINT,
|
||||
'dwFlags' : DWORD,
|
||||
'lpMultiByteStr' : WSTRING, # LPCCH
|
||||
'lpMultiByteStr' : POINTER, # STRING,
|
||||
'cbMultiByte' : INT,
|
||||
'lpWideCharStr' : LPWSTR,
|
||||
'cchWideChar' : INT
|
||||
})
|
||||
def hook_MultiByteToWideChar(ql: Qiling, address: int, params):
|
||||
wide_str = (params['lpMultiByteStr'] + "\x00").encode('utf-16le')
|
||||
CodePage = params['CodePage']
|
||||
lpMultiByteStr = params['lpMultiByteStr']
|
||||
cbMultiByte = params['cbMultiByte']
|
||||
lpWideCharStr = params['lpWideCharStr']
|
||||
cchWideChar = params['cchWideChar']
|
||||
|
||||
if params['cchWideChar'] != 0:
|
||||
ql.mem.write(params['lpWideCharStr'], wide_str)
|
||||
if not cbMultiByte:
|
||||
# TODO: set last error
|
||||
return 0
|
||||
|
||||
return len(wide_str)
|
||||
# -1 indicates the string is null-terminated. the string is
|
||||
# read along with its null-terminator
|
||||
elif cbMultiByte == 0xffffffff:
|
||||
mbstr = bytearray()
|
||||
ch = 1
|
||||
|
||||
while ch != b'\x00':
|
||||
ch = ql.mem.read(lpMultiByteStr, 1)
|
||||
mbstr.extend(ch)
|
||||
|
||||
lpMultiByteStr += 1
|
||||
|
||||
# read exactly cbMultiByte bytes. that may or may not include
|
||||
# a null-terminator
|
||||
else:
|
||||
mbstr = ql.mem.read(lpMultiByteStr, cbMultiByte)
|
||||
|
||||
# use specified code page to translate bytes into string
|
||||
encname = __encoding_name(CodePage)
|
||||
decoded = mbstr.decode(encname)
|
||||
|
||||
# this is a dry-run; just return the amount of chars
|
||||
if not cchWideChar:
|
||||
return len(decoded)
|
||||
|
||||
if not lpWideCharStr:
|
||||
return 0
|
||||
|
||||
ql.mem.write(lpWideCharStr, decoded[:cchWideChar].encode('utf-16le'))
|
||||
|
||||
return min(len(decoded), cchWideChar)
|
||||
|
||||
def __CompareString(ql: Qiling, address: int, params) -> int:
|
||||
lpString1 = params["lpString1"]
|
||||
|
||||
@@ -145,15 +145,17 @@ def hook_WaitForMultipleObjects(ql: Qiling, address: int, params):
|
||||
return 0
|
||||
|
||||
def __OpenMutex(ql: Qiling, address: int, params):
|
||||
lpName = params["lpName"]
|
||||
|
||||
if not lpName:
|
||||
return 0
|
||||
|
||||
# The name can have a "Global" or "Local" prefix to explicitly open an object in the global or session namespace.
|
||||
# It can also have no prefix
|
||||
try:
|
||||
_type, name = params["lpName"].split("\\")
|
||||
except ValueError:
|
||||
name = params["lpName"]
|
||||
_type = ""
|
||||
_type, name = lpName.split("\\") if '\\' in lpName else ('', lpName)
|
||||
|
||||
handle = ql.os.handle_manager.search(name)
|
||||
|
||||
if _type == "Global":
|
||||
# if is global is a Windows lock. We always return a valid handle because we have no way to emulate them
|
||||
# example sample: Gandcrab e42431d37561cc695de03b85e8e99c9e31321742
|
||||
@@ -175,11 +177,13 @@ def __OpenMutex(ql: Qiling, address: int, params):
|
||||
raise QlErrorNotImplemented("API not implemented")
|
||||
|
||||
def __CreateMutex(ql: Qiling, address: int, params):
|
||||
try:
|
||||
_type, name = params["lpName"].split("\\")
|
||||
except ValueError:
|
||||
name = params["lpName"]
|
||||
_type = ""
|
||||
lpName = params["lpName"]
|
||||
owning = params["bInitialOwner"]
|
||||
|
||||
if not lpName:
|
||||
return 0
|
||||
|
||||
_type, name = lpName.split("\\") if '\\' in lpName else ('', lpName)
|
||||
|
||||
handle = ql.os.handle_manager.search(name)
|
||||
|
||||
@@ -187,7 +191,6 @@ def __CreateMutex(ql: Qiling, address: int, params):
|
||||
# ql.os.last_error = ERROR_ALREADY_EXISTS
|
||||
return 0
|
||||
|
||||
owning = params["bInitialOwner"]
|
||||
mutex = Mutex(name, _type)
|
||||
|
||||
if owning:
|
||||
@@ -279,30 +282,30 @@ def hook_ReleaseMutex(ql: Qiling, address: int, params):
|
||||
|
||||
def __CreateEvent(ql: Qiling, address: int, params):
|
||||
# Implementation seems similar enough to Mutex to just use it
|
||||
lpName = params["lpName"]
|
||||
|
||||
try:
|
||||
namespace, name = params["lpName"].split("\\")
|
||||
except ValueError:
|
||||
name = params["lpName"]
|
||||
namespace = ""
|
||||
if lpName:
|
||||
namespace, name = lpName.split('\\') if '\\' in lpName else ('', lpName)
|
||||
handle = ql.os.handle_manager.search(name)
|
||||
|
||||
handle = ql.os.handle_manager.search(name)
|
||||
if handle is not None:
|
||||
ql.os.last_error = ERROR_ALREADY_EXISTS
|
||||
return handle.id
|
||||
|
||||
if handle is not None:
|
||||
ql.os.last_error = ERROR_ALREADY_EXISTS
|
||||
# FIXME: fail with a nullptr?
|
||||
# return 0
|
||||
else:
|
||||
mutex = Mutex(name, namespace)
|
||||
# TODO: should be None instead of empty strings?
|
||||
namespace = ''
|
||||
name = ''
|
||||
|
||||
if params['bInitialState']:
|
||||
mutex.lock()
|
||||
mutex = Mutex(name, namespace)
|
||||
|
||||
handle = Handle(obj=mutex, name=name)
|
||||
ql.os.handle_manager.append(handle)
|
||||
if params['bInitialState']:
|
||||
mutex.lock()
|
||||
|
||||
# FIXME: shouldn't it be 'id' instead of 'ID'?
|
||||
return handle.ID
|
||||
handle = Handle(obj=mutex, name=name)
|
||||
ql.os.handle_manager.append(handle)
|
||||
|
||||
return handle.id
|
||||
|
||||
# HANDLE CreateEventA(
|
||||
# LPSECURITY_ATTRIBUTES lpEventAttributes,
|
||||
|
||||
@@ -10,7 +10,7 @@ from qiling import Qiling
|
||||
from qiling.os.windows.api import *
|
||||
from qiling.os.windows.const import *
|
||||
from qiling.os.windows.fncc import *
|
||||
from qiling.os.windows.structs import SystemInfo, SystemTime
|
||||
from qiling.os.windows.structs import FILETIME, SystemInfo, SystemTime
|
||||
|
||||
# NOT_BUILD_WINDOWS_DEPRECATE DWORD GetVersion(
|
||||
# );
|
||||
@@ -81,8 +81,20 @@ def hook_GetLocalTime(ql: Qiling, address: int, params):
|
||||
'lpSystemTimeAsFileTime' : LPFILETIME
|
||||
})
|
||||
def hook_GetSystemTimeAsFileTime(ql: Qiling, address: int, params):
|
||||
# TODO
|
||||
pass
|
||||
ptr = params['lpSystemTimeAsFileTime']
|
||||
|
||||
epoch = datetime(1601, 1, 1)
|
||||
elapsed = datetime.now() - epoch
|
||||
|
||||
# number of 100-nanosecond intervals since Jan 1, 1601 utc
|
||||
# where: (10 ** 9) / 100 -> (10 ** 7)
|
||||
hnano = int(elapsed.total_seconds() * (10 ** 7))
|
||||
|
||||
mask = (1 << 32) - 1
|
||||
hi = (hnano >> 32) & mask
|
||||
lo = (hnano >> 0) & mask
|
||||
|
||||
ql.mem.write(ptr, bytes(FILETIME(lo, hi)))
|
||||
|
||||
# DWORD GetTickCount(
|
||||
# );
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
#!/usr/bin/env python3
|
||||
#
|
||||
# Cross Platform and Multi Architecture Advanced Binary Emulation Framework
|
||||
#
|
||||
|
||||
from qiling import Qiling
|
||||
from qiling.os.windows.api import *
|
||||
from qiling.os.windows.const import *
|
||||
from qiling.os.windows.fncc import *
|
||||
|
||||
# DWORD GetTimeZoneInformation(
|
||||
# [out] LPTIME_ZONE_INFORMATION lpTimeZoneInformation
|
||||
# );
|
||||
@winsdkapi(cc=STDCALL, params={
|
||||
'lpTimeZoneInformation' : LPTIME_ZONE_INFORMATION
|
||||
})
|
||||
def hook_GetTimeZoneInformation(ql: Qiling, address: int, params):
|
||||
# TODO: implement this later. fail for now
|
||||
return TIME_ZONE_ID_INVALID
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user