Merge remote-tracking branch 'qiling.io/dev' into fix_thumb_emu

This commit is contained in:
ucgJhe
2022-07-08 00:02:40 +00:00
120 changed files with 3901 additions and 2274 deletions
+2
View File
@@ -5,12 +5,14 @@
jobs:
deploy:
runs-on: ubuntu-latest
if: github.repository_owner == 'qilingframework'
steps:
- uses: actions/checkout@v2
with:
fetch-depth: 0
- uses: xwings/sync-repo-action@master
with:
run: git config --global --add safe.directory *
ssh_private_key: ${{ secrets.GITEE_KEY }}
target_repo: ssh://[email protected]/qilingframework/qiling.git
+3 -2
View File
@@ -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
+28 -1
View File
@@ -1,7 +1,14 @@
This file details the changelog of Qiling Framework.
------------------------------------
[Version 1.4.3]: April 7th, 2022
[Version 1.4.4]: July XX, 2022
------------------------------------
[Version 1.4.3]: June 1st, 2022
New features:
- Introduce PowerPC architecture support (#1140)
Improvements:
- Fix fuzzing for tendaac15 (#1096)
@@ -10,11 +17,22 @@ Improvements:
- Minor PE Loader fix (#1104)
- Minor quality changes (#1106)
- Fix cacheflush syscall typo (#1115)
- Improvements and fixes for Windows and PE (#1118)
- Add vm_context to EVM hooks (#1119)
- Load interpreter segments with correct perms and vaddr (#1120)
- Fix mistakes in fuzz_x8664_linux binary (#1121)
- Add EVM ABI helpers, fix EVM DBG stack view (#1123)
- Fix regression caused by missing exception handling when opening socket (#1124)
- CI improvement (#1128 #1134)
- Add macho load command 'LC_LOAD_WEAK_DYLIB' support (#1133)
- Fix breakage of non-Windows binary emulation on Windows host (#1143)
- Remove misused region bound check of unmap_all (#1144)
- Change deprecated interfaces of IDA (#1145)
- Use importlib to retrieve package version (#1146)
- New and improved gdbserver (#1148)
- Rewrite package data reading (#1150)
- Misc improvements (#1154)
- Fix memory exhaustion problem caused by the logger (#1161)
Contributors:
- wtdcode
@@ -22,9 +40,18 @@ Contributors:
- elicn
- xwings
- cq674350529
- elicn
- TheZ3ro
- bet4it
- chinggg
- kabeor
- chfl4gs
- profiles
- OlfillasOdikno
- nmantan
- machinewu
- nullableVoidPtr
- Phat3
------------------------------------
-5
View File
@@ -1,5 +0,0 @@
recursive-include qiling/debugger/gdb/xml *
recursive-include qiling/extensions/windows_sdk/defs *
recursive-include qiling/profiles *
include qiling/os/uefi/guids.csv
include qiling/os/qnx/syspage.bin
+1 -1
View File
@@ -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/)
+9 -1
View File
@@ -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
-1
View File
@@ -115,7 +115,6 @@ if __name__ == "__main__":
parser.add_argument("-t", "--trace", help="Enable full trace", action='store_true', default=False)
parser.add_argument("-R", "--root", help="rootfs", default=None)
parser.add_argument("-d", "--dump", help="Directory to dump memory regions to", default="dump")
#parser.add_argument("-a", "--automatize_input", help="Automatize writes on standard input", default=False)
parser.add_argument("-p ", "--profile", help="customized profile",
default="qiling/profiles/windows.ql")
parser.add_argument('input', nargs='*')
+6 -1
View File
@@ -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']
-3
View File
@@ -1,3 +0,0 @@
# NOTE: use "-dev" for dev branch
#__version__ = "1.4.3"
__version__ = "1.4.3" + "-dev"
+6 -12
View File
@@ -41,7 +41,11 @@ class QlArchARM(QlArch):
@cached_property
def regs(self) -> QlRegisterManager:
regs_map = arm_const.reg_map
regs_map = dict(
**arm_const.reg_map,
**arm_const.reg_vfp
)
pc_reg = 'pc'
sp_reg = 'sp'
@@ -53,17 +57,7 @@ class QlArchARM(QlArch):
@property
def endian(self) -> QL_ENDIAN:
# FIXME: ARM is a bi-endian architecture which allows flipping core endianess
# while running. endianess is tested in runtime through CPSR[9], however unicorn
# doesn't reflect the endianess correctly through that bit.
# @see: https://github.com/unicorn-engine/unicorn/issues/1542
#
# we work around this by using the initial endianess configuration, even though
# it might have been changed since.
#
# return QL_ENDIAN.EB if self.regs.cpsr & (1 << 9) else QL_ENDIAN.EL
return self._init_endian
return QL_ENDIAN.EB if self.regs.cpsr & (1 << 9) else QL_ENDIAN.EL
@property
def effective_pc(self) -> int:
+7 -1
View File
@@ -26,7 +26,13 @@ class QlArchARM64(QlArch):
def regs(self) -> QlRegisterManager:
regs_map = dict(
**arm64_const.reg_map,
**arm64_const.reg_map_w
**arm64_const.reg_map_b,
**arm64_const.reg_map_d,
**arm64_const.reg_map_h,
**arm64_const.reg_map_q,
**arm64_const.reg_map_s,
**arm64_const.reg_map_w,
**arm64_const.reg_map_v
)
pc_reg = 'pc'
+279 -69
View File
@@ -1,79 +1,289 @@
#!/usr/bin/env python3
#
#
# Cross Platform and Multi Architecture Advanced Binary Emulation Framework
#
from unicorn.arm64_const import *
reg_map = {
"x0": UC_ARM64_REG_X0,
"x1": UC_ARM64_REG_X1,
"x2": UC_ARM64_REG_X2,
"x3": UC_ARM64_REG_X3,
"x4": UC_ARM64_REG_X4,
"x5": UC_ARM64_REG_X5,
"x6": UC_ARM64_REG_X6,
"x7": UC_ARM64_REG_X7,
"x8": UC_ARM64_REG_X8,
"x9": UC_ARM64_REG_X9,
"x10": UC_ARM64_REG_X10,
"x11": UC_ARM64_REG_X11,
"x12": UC_ARM64_REG_X12,
"x13": UC_ARM64_REG_X13,
"x14": UC_ARM64_REG_X14,
"x15": UC_ARM64_REG_X15,
"x16": UC_ARM64_REG_X16,
"x17": UC_ARM64_REG_X17,
"x18": UC_ARM64_REG_X18,
"x19": UC_ARM64_REG_X19,
"x20": UC_ARM64_REG_X20,
"x21": UC_ARM64_REG_X21,
"x22": UC_ARM64_REG_X22,
"x23": UC_ARM64_REG_X23,
"x24": UC_ARM64_REG_X24,
"x25": UC_ARM64_REG_X25,
"x26": UC_ARM64_REG_X26,
"x27": UC_ARM64_REG_X27,
"x28": UC_ARM64_REG_X28,
"x29": UC_ARM64_REG_X29,
"x30": UC_ARM64_REG_X30,
"sp": UC_ARM64_REG_SP,
"pc": UC_ARM64_REG_PC,
"lr": UC_ARM64_REG_LR,
"cpacr_el1": UC_ARM64_REG_CPACR_EL1,
"tpidr_el0": UC_ARM64_REG_TPIDR_EL0,
"x0": UC_ARM64_REG_X0,
"x1": UC_ARM64_REG_X1,
"x2": UC_ARM64_REG_X2,
"x3": UC_ARM64_REG_X3,
"x4": UC_ARM64_REG_X4,
"x5": UC_ARM64_REG_X5,
"x6": UC_ARM64_REG_X6,
"x7": UC_ARM64_REG_X7,
"x8": UC_ARM64_REG_X8,
"x9": UC_ARM64_REG_X9,
"x10": UC_ARM64_REG_X10,
"x11": UC_ARM64_REG_X11,
"x12": UC_ARM64_REG_X12,
"x13": UC_ARM64_REG_X13,
"x14": UC_ARM64_REG_X14,
"x15": UC_ARM64_REG_X15,
"x16": UC_ARM64_REG_X16,
"x17": UC_ARM64_REG_X17,
"x18": UC_ARM64_REG_X18,
"x19": UC_ARM64_REG_X19,
"x20": UC_ARM64_REG_X20,
"x21": UC_ARM64_REG_X21,
"x22": UC_ARM64_REG_X22,
"x23": UC_ARM64_REG_X23,
"x24": UC_ARM64_REG_X24,
"x25": UC_ARM64_REG_X25,
"x26": UC_ARM64_REG_X26,
"x27": UC_ARM64_REG_X27,
"x28": UC_ARM64_REG_X28,
"x29": UC_ARM64_REG_X29,
"x30": UC_ARM64_REG_X30,
"sp": UC_ARM64_REG_SP,
"pc": UC_ARM64_REG_PC,
"lr": UC_ARM64_REG_LR,
"cpacr_el1": UC_ARM64_REG_CPACR_EL1,
"tpidr_el0": UC_ARM64_REG_TPIDR_EL0
}
reg_map_b = {
"b0" : UC_ARM64_REG_B0,
"b1" : UC_ARM64_REG_B1,
"b2" : UC_ARM64_REG_B2,
"b3" : UC_ARM64_REG_B3,
"b4" : UC_ARM64_REG_B4,
"b5" : UC_ARM64_REG_B5,
"b6" : UC_ARM64_REG_B6,
"b7" : UC_ARM64_REG_B7,
"b8" : UC_ARM64_REG_B8,
"b9" : UC_ARM64_REG_B9,
"b10" : UC_ARM64_REG_B10,
"b11" : UC_ARM64_REG_B11,
"b12" : UC_ARM64_REG_B12,
"b13" : UC_ARM64_REG_B13,
"b14" : UC_ARM64_REG_B14,
"b15" : UC_ARM64_REG_B15,
"b16" : UC_ARM64_REG_B16,
"b17" : UC_ARM64_REG_B17,
"b18" : UC_ARM64_REG_B18,
"b19" : UC_ARM64_REG_B19,
"b20" : UC_ARM64_REG_B20,
"b21" : UC_ARM64_REG_B21,
"b22" : UC_ARM64_REG_B22,
"b23" : UC_ARM64_REG_B23,
"b24" : UC_ARM64_REG_B24,
"b25" : UC_ARM64_REG_B25,
"b26" : UC_ARM64_REG_B26,
"b27" : UC_ARM64_REG_B27,
"b28" : UC_ARM64_REG_B28,
"b29" : UC_ARM64_REG_B29,
"b30" : UC_ARM64_REG_B30,
"b31" : UC_ARM64_REG_B31
}
reg_map_d = {
"d0" : UC_ARM64_REG_D0,
"d1" : UC_ARM64_REG_D1,
"d2" : UC_ARM64_REG_D2,
"d3" : UC_ARM64_REG_D3,
"d4" : UC_ARM64_REG_D4,
"d5" : UC_ARM64_REG_D5,
"d6" : UC_ARM64_REG_D6,
"d7" : UC_ARM64_REG_D7,
"d8" : UC_ARM64_REG_D8,
"d9" : UC_ARM64_REG_D9,
"d10" : UC_ARM64_REG_D10,
"d11" : UC_ARM64_REG_D11,
"d12" : UC_ARM64_REG_D12,
"d13" : UC_ARM64_REG_D13,
"d14" : UC_ARM64_REG_D14,
"d15" : UC_ARM64_REG_D15,
"d16" : UC_ARM64_REG_D16,
"d17" : UC_ARM64_REG_D17,
"d18" : UC_ARM64_REG_D18,
"d19" : UC_ARM64_REG_D19,
"d20" : UC_ARM64_REG_D20,
"d21" : UC_ARM64_REG_D21,
"d22" : UC_ARM64_REG_D22,
"d23" : UC_ARM64_REG_D23,
"d24" : UC_ARM64_REG_D24,
"d25" : UC_ARM64_REG_D25,
"d26" : UC_ARM64_REG_D26,
"d27" : UC_ARM64_REG_D27,
"d28" : UC_ARM64_REG_D28,
"d29" : UC_ARM64_REG_D29,
"d30" : UC_ARM64_REG_D30,
"d31" : UC_ARM64_REG_D31
}
reg_map_h = {
"h0" : UC_ARM64_REG_H0,
"h1" : UC_ARM64_REG_H1,
"h2" : UC_ARM64_REG_H2,
"h3" : UC_ARM64_REG_H3,
"h4" : UC_ARM64_REG_H4,
"h5" : UC_ARM64_REG_H5,
"h6" : UC_ARM64_REG_H6,
"h7" : UC_ARM64_REG_H7,
"h8" : UC_ARM64_REG_H8,
"h9" : UC_ARM64_REG_H9,
"h10" : UC_ARM64_REG_H10,
"h11" : UC_ARM64_REG_H11,
"h12" : UC_ARM64_REG_H12,
"h13" : UC_ARM64_REG_H13,
"h14" : UC_ARM64_REG_H14,
"h15" : UC_ARM64_REG_H15,
"h16" : UC_ARM64_REG_H16,
"h17" : UC_ARM64_REG_H17,
"h18" : UC_ARM64_REG_H18,
"h19" : UC_ARM64_REG_H19,
"h20" : UC_ARM64_REG_H20,
"h21" : UC_ARM64_REG_H21,
"h22" : UC_ARM64_REG_H22,
"h23" : UC_ARM64_REG_H23,
"h24" : UC_ARM64_REG_H24,
"h25" : UC_ARM64_REG_H25,
"h26" : UC_ARM64_REG_H26,
"h27" : UC_ARM64_REG_H27,
"h28" : UC_ARM64_REG_H28,
"h29" : UC_ARM64_REG_H29,
"h30" : UC_ARM64_REG_H30,
"h31" : UC_ARM64_REG_H31
}
reg_map_q = {
"q0" : UC_ARM64_REG_Q0,
"q1" : UC_ARM64_REG_Q1,
"q2" : UC_ARM64_REG_Q2,
"q3" : UC_ARM64_REG_Q3,
"q4" : UC_ARM64_REG_Q4,
"q5" : UC_ARM64_REG_Q5,
"q6" : UC_ARM64_REG_Q6,
"q7" : UC_ARM64_REG_Q7,
"q8" : UC_ARM64_REG_Q8,
"q9" : UC_ARM64_REG_Q9,
"q10" : UC_ARM64_REG_Q10,
"q11" : UC_ARM64_REG_Q11,
"q12" : UC_ARM64_REG_Q12,
"q13" : UC_ARM64_REG_Q13,
"q14" : UC_ARM64_REG_Q14,
"q15" : UC_ARM64_REG_Q15,
"q16" : UC_ARM64_REG_Q16,
"q17" : UC_ARM64_REG_Q17,
"q18" : UC_ARM64_REG_Q18,
"q19" : UC_ARM64_REG_Q19,
"q20" : UC_ARM64_REG_Q20,
"q21" : UC_ARM64_REG_Q21,
"q22" : UC_ARM64_REG_Q22,
"q23" : UC_ARM64_REG_Q23,
"q24" : UC_ARM64_REG_Q24,
"q25" : UC_ARM64_REG_Q25,
"q26" : UC_ARM64_REG_Q26,
"q27" : UC_ARM64_REG_Q27,
"q28" : UC_ARM64_REG_Q28,
"q29" : UC_ARM64_REG_Q29,
"q30" : UC_ARM64_REG_Q30,
"q31" : UC_ARM64_REG_Q31
}
reg_map_s = {
"s0" : UC_ARM64_REG_S0,
"s1" : UC_ARM64_REG_S1,
"s2" : UC_ARM64_REG_S2,
"s3" : UC_ARM64_REG_S3,
"s4" : UC_ARM64_REG_S4,
"s5" : UC_ARM64_REG_S5,
"s6" : UC_ARM64_REG_S6,
"s7" : UC_ARM64_REG_S7,
"s8" : UC_ARM64_REG_S8,
"s9" : UC_ARM64_REG_S9,
"s10" : UC_ARM64_REG_S10,
"s11" : UC_ARM64_REG_S11,
"s12" : UC_ARM64_REG_S12,
"s13" : UC_ARM64_REG_S13,
"s14" : UC_ARM64_REG_S14,
"s15" : UC_ARM64_REG_S15,
"s16" : UC_ARM64_REG_S16,
"s17" : UC_ARM64_REG_S17,
"s18" : UC_ARM64_REG_S18,
"s19" : UC_ARM64_REG_S19,
"s20" : UC_ARM64_REG_S20,
"s21" : UC_ARM64_REG_S21,
"s22" : UC_ARM64_REG_S22,
"s23" : UC_ARM64_REG_S23,
"s24" : UC_ARM64_REG_S24,
"s25" : UC_ARM64_REG_S25,
"s26" : UC_ARM64_REG_S26,
"s27" : UC_ARM64_REG_S27,
"s28" : UC_ARM64_REG_S28,
"s29" : UC_ARM64_REG_S29,
"s30" : UC_ARM64_REG_S30,
"s31" : UC_ARM64_REG_S31
}
reg_map_w = {
"w0" : UC_ARM64_REG_W0,
"w1" : UC_ARM64_REG_W1,
"w2" : UC_ARM64_REG_W2,
"w3" : UC_ARM64_REG_W3,
"w4" : UC_ARM64_REG_W4,
"w5" : UC_ARM64_REG_W5,
"w6" : UC_ARM64_REG_W6,
"w7" : UC_ARM64_REG_W7,
"w8" : UC_ARM64_REG_W8,
"w9" : UC_ARM64_REG_W9,
"w10" : UC_ARM64_REG_W10,
"w11" : UC_ARM64_REG_W11,
"w12" : UC_ARM64_REG_W12,
"w13" : UC_ARM64_REG_W13,
"w14" : UC_ARM64_REG_W14,
"w15" : UC_ARM64_REG_W15,
"w16" : UC_ARM64_REG_W16,
"w17" : UC_ARM64_REG_W17,
"w18" : UC_ARM64_REG_W18,
"w19" : UC_ARM64_REG_W19,
"w20" : UC_ARM64_REG_W20,
"w21" : UC_ARM64_REG_W21,
"w22" : UC_ARM64_REG_W22,
"w23" : UC_ARM64_REG_W23,
"w24" : UC_ARM64_REG_W24,
"w25" : UC_ARM64_REG_W25,
"w26" : UC_ARM64_REG_W26,
"w27" : UC_ARM64_REG_W27,
"w28" : UC_ARM64_REG_W28,
"w29" : UC_ARM64_REG_W29,
"w30" : UC_ARM64_REG_W30,
}
"w0" : UC_ARM64_REG_W0,
"w1" : UC_ARM64_REG_W1,
"w2" : UC_ARM64_REG_W2,
"w3" : UC_ARM64_REG_W3,
"w4" : UC_ARM64_REG_W4,
"w5" : UC_ARM64_REG_W5,
"w6" : UC_ARM64_REG_W6,
"w7" : UC_ARM64_REG_W7,
"w8" : UC_ARM64_REG_W8,
"w9" : UC_ARM64_REG_W9,
"w10" : UC_ARM64_REG_W10,
"w11" : UC_ARM64_REG_W11,
"w12" : UC_ARM64_REG_W12,
"w13" : UC_ARM64_REG_W13,
"w14" : UC_ARM64_REG_W14,
"w15" : UC_ARM64_REG_W15,
"w16" : UC_ARM64_REG_W16,
"w17" : UC_ARM64_REG_W17,
"w18" : UC_ARM64_REG_W18,
"w19" : UC_ARM64_REG_W19,
"w20" : UC_ARM64_REG_W20,
"w21" : UC_ARM64_REG_W21,
"w22" : UC_ARM64_REG_W22,
"w23" : UC_ARM64_REG_W23,
"w24" : UC_ARM64_REG_W24,
"w25" : UC_ARM64_REG_W25,
"w26" : UC_ARM64_REG_W26,
"w27" : UC_ARM64_REG_W27,
"w28" : UC_ARM64_REG_W28,
"w29" : UC_ARM64_REG_W29,
"w30" : UC_ARM64_REG_W30
}
reg_map_v = {
"v0" : UC_ARM64_REG_V0,
"v1" : UC_ARM64_REG_V1,
"v2" : UC_ARM64_REG_V2,
"v3" : UC_ARM64_REG_V3,
"v4" : UC_ARM64_REG_V4,
"v5" : UC_ARM64_REG_V5,
"v6" : UC_ARM64_REG_V6,
"v7" : UC_ARM64_REG_V7,
"v8" : UC_ARM64_REG_V8,
"v9" : UC_ARM64_REG_V9,
"v10" : UC_ARM64_REG_V10,
"v11" : UC_ARM64_REG_V11,
"v12" : UC_ARM64_REG_V12,
"v13" : UC_ARM64_REG_V13,
"v14" : UC_ARM64_REG_V14,
"v15" : UC_ARM64_REG_V15,
"v16" : UC_ARM64_REG_V16,
"v17" : UC_ARM64_REG_V17,
"v18" : UC_ARM64_REG_V18,
"v19" : UC_ARM64_REG_V19,
"v20" : UC_ARM64_REG_V20,
"v21" : UC_ARM64_REG_V21,
"v22" : UC_ARM64_REG_V22,
"v23" : UC_ARM64_REG_V23,
"v24" : UC_ARM64_REG_V24,
"v25" : UC_ARM64_REG_V25,
"v26" : UC_ARM64_REG_V26,
"v27" : UC_ARM64_REG_V27,
"v28" : UC_ARM64_REG_V28,
"v29" : UC_ARM64_REG_V29,
"v30" : UC_ARM64_REG_V30,
"v31" : UC_ARM64_REG_V31
}
+57 -33
View File
@@ -4,39 +4,63 @@
#
from unicorn.arm_const import *
from enum import IntEnum
reg_map = {
"r0": UC_ARM_REG_R0,
"r1": UC_ARM_REG_R1,
"r2": UC_ARM_REG_R2,
"r3": UC_ARM_REG_R3,
"r4": UC_ARM_REG_R4,
"r5": UC_ARM_REG_R5,
"r6": UC_ARM_REG_R6,
"r7": UC_ARM_REG_R7,
"r8": UC_ARM_REG_R8,
"r9": UC_ARM_REG_R9,
"r10": UC_ARM_REG_R10,
"r11": UC_ARM_REG_R11,
"r12": UC_ARM_REG_R12,
"sp": UC_ARM_REG_SP,
"lr": UC_ARM_REG_LR,
"pc": UC_ARM_REG_PC,
# CPSR needs to be at offset 25 for GDB, see https://sourceware.org/git/?p=binutils-gdb.git;a=blob;f=gdb/arch/arm.h;h=fa589fd0582c0add627a068e6f4947a909c45e86;hb=HEAD#l34
# The fp registers inbetween have become obsolete
"f0": UC_ARM_REG_INVALID,
"f1": UC_ARM_REG_INVALID,
"f2": UC_ARM_REG_INVALID,
"f3": UC_ARM_REG_INVALID,
"f4": UC_ARM_REG_INVALID,
"f5": UC_ARM_REG_INVALID,
"f6": UC_ARM_REG_INVALID,
"f7": UC_ARM_REG_INVALID,
"fps": UC_ARM_REG_INVALID,
"cpsr": UC_ARM_REG_CPSR,
"c1_c0_2": UC_ARM_REG_C1_C0_2,
"c13_c0_3": UC_ARM_REG_C13_C0_3,
"fpexc": UC_ARM_REG_FPEXC,
"r0": UC_ARM_REG_R0,
"r1": UC_ARM_REG_R1,
"r2": UC_ARM_REG_R2,
"r3": UC_ARM_REG_R3,
"r4": UC_ARM_REG_R4,
"r5": UC_ARM_REG_R5,
"r6": UC_ARM_REG_R6,
"r7": UC_ARM_REG_R7,
"r8": UC_ARM_REG_R8,
"r9": UC_ARM_REG_R9,
"r10": UC_ARM_REG_R10,
"r11": UC_ARM_REG_R11,
"r12": UC_ARM_REG_R12,
"sp": UC_ARM_REG_SP,
"lr": UC_ARM_REG_LR,
"pc": UC_ARM_REG_PC,
"cpsr": UC_ARM_REG_CPSR,
"c1_c0_2": UC_ARM_REG_C1_C0_2,
"c13_c0_3": UC_ARM_REG_C13_C0_3,
"fpexc": UC_ARM_REG_FPEXC
}
reg_vfp = {
"d0" : UC_ARM_REG_D0,
"d1" : UC_ARM_REG_D1,
"d2" : UC_ARM_REG_D2,
"d3" : UC_ARM_REG_D3,
"d4" : UC_ARM_REG_D4,
"d5" : UC_ARM_REG_D5,
"d6" : UC_ARM_REG_D6,
"d7" : UC_ARM_REG_D7,
"d8" : UC_ARM_REG_D8,
"d9" : UC_ARM_REG_D9,
"d10" : UC_ARM_REG_D10,
"d11" : UC_ARM_REG_D11,
"d12" : UC_ARM_REG_D12,
"d13" : UC_ARM_REG_D13,
"d14" : UC_ARM_REG_D14,
"d15" : UC_ARM_REG_D15,
"d16" : UC_ARM_REG_D16,
"d17" : UC_ARM_REG_D17,
"d18" : UC_ARM_REG_D18,
"d19" : UC_ARM_REG_D19,
"d20" : UC_ARM_REG_D20,
"d21" : UC_ARM_REG_D21,
"d22" : UC_ARM_REG_D22,
"d23" : UC_ARM_REG_D23,
"d24" : UC_ARM_REG_D24,
"d25" : UC_ARM_REG_D25,
"d26" : UC_ARM_REG_D26,
"d27" : UC_ARM_REG_D27,
"d28" : UC_ARM_REG_D28,
"d29" : UC_ARM_REG_D29,
"d30" : UC_ARM_REG_D30,
"d31" : UC_ARM_REG_D31,
"fpscr" : UC_ARM_REG_FPSCR
}
+4 -3
View File
@@ -1,4 +1,5 @@
import os
import inspect
from pathlib import Path
import re
import logging
import json
@@ -92,8 +93,8 @@ def analysis_func_sign(insns:list, engine_num=1):
class signatures_engine_1:
@staticmethod
def find_signature(sign):
path = os.path.split(os.path.realpath(__file__))[0] + '/signatures.json'
with open(path) as data_file:
path = Path(inspect.getfile(inspect.getframe())).parent / 'signatures.json'
with path.open('r') as data_file:
data = json.load(data_file)
list_name = [name for name, hexa in data.items() if hexa == sign]
+18 -4
View File
@@ -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)
+2 -2
View File
@@ -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
View File
@@ -37,7 +37,8 @@ class QlArchMIPS(QlArch):
def regs(self) -> QlRegisterManager:
regs_map = dict(
**mips_const.reg_map,
**mips_const.reg_map_afpr128
**mips_const.reg_map_afpr128,
**mips_const.reg_map_fpu
)
pc_reg = 'pc'
+69 -1
View File
@@ -6,6 +6,39 @@
from unicorn.mips_const import *
reg_map = {
"r0" : UC_MIPS_REG_0,
"r1" : UC_MIPS_REG_1,
"r2" : UC_MIPS_REG_2,
"r3" : UC_MIPS_REG_3,
"r4" : UC_MIPS_REG_4,
"r5" : UC_MIPS_REG_5,
"r6" : UC_MIPS_REG_6,
"r7" : UC_MIPS_REG_7,
"r8" : UC_MIPS_REG_8,
"r9" : UC_MIPS_REG_9,
"r10" : UC_MIPS_REG_10,
"r11" : UC_MIPS_REG_11,
"r12" : UC_MIPS_REG_12,
"r13" : UC_MIPS_REG_13,
"r14" : UC_MIPS_REG_14,
"r15" : UC_MIPS_REG_15,
"r16" : UC_MIPS_REG_16,
"r17" : UC_MIPS_REG_17,
"r18" : UC_MIPS_REG_18,
"r19" : UC_MIPS_REG_19,
"r20" : UC_MIPS_REG_20,
"r21" : UC_MIPS_REG_21,
"r22" : UC_MIPS_REG_22,
"r23" : UC_MIPS_REG_23,
"r24" : UC_MIPS_REG_24,
"r25" : UC_MIPS_REG_25,
"r26" : UC_MIPS_REG_26,
"r27" : UC_MIPS_REG_27,
"r28" : UC_MIPS_REG_28,
"r29" : UC_MIPS_REG_29,
"r30" : UC_MIPS_REG_30,
"r31" : UC_MIPS_REG_31,
"zero": UC_MIPS_REG_ZERO,
"at": UC_MIPS_REG_AT,
"v0": UC_MIPS_REG_V0,
@@ -49,4 +82,39 @@ reg_map = {
reg_map_afpr128 = {
"cp0_config3" : UC_MIPS_REG_CP0_CONFIG3,
"cp0_userlocal": UC_MIPS_REG_CP0_USERLOCAL,
}
}
reg_map_fpu = {
"f0" : UC_MIPS_REG_F0,
"f1" : UC_MIPS_REG_F1,
"f2" : UC_MIPS_REG_F2,
"f3" : UC_MIPS_REG_F3,
"f4" : UC_MIPS_REG_F4,
"f5" : UC_MIPS_REG_F5,
"f6" : UC_MIPS_REG_F6,
"f7" : UC_MIPS_REG_F7,
"f8" : UC_MIPS_REG_F8,
"f9" : UC_MIPS_REG_F9,
"f10" : UC_MIPS_REG_F10,
"f11" : UC_MIPS_REG_F11,
"f12" : UC_MIPS_REG_F12,
"f13" : UC_MIPS_REG_F13,
"f14" : UC_MIPS_REG_F14,
"f15" : UC_MIPS_REG_F15,
"f16" : UC_MIPS_REG_F16,
"f17" : UC_MIPS_REG_F17,
"f18" : UC_MIPS_REG_F18,
"f19" : UC_MIPS_REG_F19,
"f20" : UC_MIPS_REG_F20,
"f21" : UC_MIPS_REG_F21,
"f22" : UC_MIPS_REG_F22,
"f23" : UC_MIPS_REG_F23,
"f24" : UC_MIPS_REG_F24,
"f25" : UC_MIPS_REG_F25,
"f26" : UC_MIPS_REG_F26,
"f27" : UC_MIPS_REG_F27,
"f28" : UC_MIPS_REG_F28,
"f29" : UC_MIPS_REG_F29,
"f30" : UC_MIPS_REG_F30,
"f31" : UC_MIPS_REG_F31
}
-6
View File
@@ -102,12 +102,6 @@ class QlRegisterManager:
return self.uc.reg_write(self.uc_pc, value)
@property
def arch_pc_name(self) -> str:
"""Get the architectural program counter register name.
"""
return next(k for k, v in self.register_mapping.items() if v == self.uc_pc)
@property
def arch_sp(self) -> int:
+4 -34
View File
@@ -4,14 +4,11 @@
#
from functools import cached_property
from typing import Union
from unicorn import Uc, UC_ARCH_X86, UC_MODE_16, UC_MODE_32, UC_MODE_64
from capstone import Cs, CS_ARCH_X86, CS_MODE_16, CS_MODE_32, CS_MODE_64
from keystone import Ks, KS_ARCH_X86, KS_MODE_16, KS_MODE_32, KS_MODE_64
from unicorn.x86_const import UC_X86_REG_EFLAGS
from qiling.arch.arch import QlArch
from qiling.arch.msr import QlMsrManager
from qiling.arch.register import QlRegisterManager
@@ -112,7 +109,10 @@ class QlArchX8664(QlArchIntel):
**x86_const.reg_map_64_b,
**x86_const.reg_map_64_w,
**x86_const.reg_map_64_d,
**x86_const.reg_map_seg_base
**x86_const.reg_map_seg_base,
**x86_const.reg_map_xmm,
**x86_const.reg_map_ymm,
**x86_const.reg_map_zmm
)
pc_reg = 'rip'
@@ -126,33 +126,3 @@ class QlArchX8664(QlArchIntel):
@cached_property
def assembler(self) -> Ks:
return Ks(KS_ARCH_X86, KS_MODE_64)
# TODO: generalize this
def __reg_bits(self, register: int) -> int:
# all regs in reg_map_misc are 16 bits except of eflags
if register == UC_X86_REG_EFLAGS:
return 32
regmaps = (
(x86_const.reg_map_8, 8),
(x86_const.reg_map_16, 16),
(x86_const.reg_map_32, 32),
(x86_const.reg_map_64, 64),
(x86_const.reg_map_misc, 16),
(x86_const.reg_map_cr, 64), # 32 bits for x86
(x86_const.reg_map_st, 32),
(x86_const.reg_map_seg_base, 64), # 32 bits for x86
)
return next((rsize for rmap, rsize in regmaps if register in rmap.values()), 0)
# note: this method was not generalized for all archs since it requires a bookkeeping
# of all registers, while it is used only by gdb and only for x86-64
def reg_bits(self, reg: Union[str, int]) -> int:
"""Get register size in bits.
"""
if type(reg) is str:
reg = self.regs.register_mapping[reg]
return self.__reg_bits(reg)
+1 -1
View File
@@ -177,7 +177,7 @@ reg_map_st = {
}
reg_map_misc = {
"ef": UC_X86_REG_EFLAGS,
"eflags": UC_X86_REG_EFLAGS,
"cs": UC_X86_REG_CS,
"ss": UC_X86_REG_SS,
"ds": UC_X86_REG_DS,
+8 -5
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -8,7 +8,8 @@
# handling hooks #
##############################################
from typing import Callable, MutableMapping, MutableSequence
from typing import Any, Callable, MutableMapping, MutableSequence, Protocol
from typing import TYPE_CHECKING
from unicorn import Uc
from unicorn.unicorn_const import *
@@ -18,6 +19,73 @@ from .utils import catch_KeyboardInterrupt
from .const import QL_HOOK_BLOCK
from .exception import QlErrorCoreHook
if TYPE_CHECKING:
from qiling import Qiling
class MemHookCallback(Protocol):
def __call__(self, __ql: 'Qiling', __access: int, __address: int, __size: int, __value: int, *__context: Any) -> Any:
"""Memory access hook callback.
Args:
__ql : the associated qiling instance
__access : the intercepted memory access type, one of UC_HOOK_MEM_* constants
__addr : the target memory location
__size : size of intercepted memory access
__value : the value to write, for write operations, 0 for others
__context : additional context passed on hook creation. if no context was passed, this argument should be omitted
Returns:
an integer with `QL_HOOK_BLOCK` mask set to block execution of remaining hooks
(if any) or `None`
"""
pass
class TraceHookCalback(Protocol):
def __call__(self, __ql: 'Qiling', __address: int, __size: int, *__context: Any) -> Any:
"""Execution hook callback.
Args:
__ql : the associated qiling instance
__address : address of the instruction to be executed
__size : instruction size
__context : additional context passed on hook creation. if no context was passed, this argument should be omitted
Returns:
an integer with `QL_HOOK_BLOCK` mask set to block execution of remaining hooks
(if any) or `None`
"""
pass
class AddressHookCallback(Protocol):
def __call__(self, __ql: 'Qiling', *__context: Any) -> Any:
"""Address hook callback.
Args:
__ql : the associated qiling instance
__context : additional context passed on hook creation. if no context was passed, this argument should be omitted
Returns:
an integer with `QL_HOOK_BLOCK` mask set to block execution of remaining hooks
(if any) or `None`
"""
pass
class InterruptHookCallback(Protocol):
def __call__(self, __ql: 'Qiling', intno: int, *__context: Any) -> Any:
"""Interrupt hook callback.
Args:
__ql : the associated qiling instance
__intno : the intercepted interrupt number
__context : additional context passed on hook creation. if no context was passed, this argument should be omitted
Returns:
an integer with `QL_HOOK_BLOCK` mask set to block execution of remaining hooks
(if any) or `None`
"""
pass
# Don't assume self is Qiling.
class QlCoreHooks:
def __init__(self, uc: Uc):
@@ -37,6 +105,9 @@ class QlCoreHooks:
# Callback definitions #
########################
def _hook_intr_cb(self, uc: Uc, intno: int, pack_data) -> None:
"""Interrupt hooks dispatcher.
"""
ql, hook_type = pack_data
handled = False
@@ -60,15 +131,18 @@ class QlCoreHooks:
def _hook_insn_cb(self, uc: Uc, *args):
ql, hook_type = args[-1]
"""Instruction hooks dispatcher.
"""
*hook_args, (ql, insn_type) = args
retval = None
if hook_type in self._insn_hook:
hooks_list = self._insn_hook[hook_type]
if insn_type in self._insn_hook:
hooks_list = self._insn_hook[insn_type]
for hook in hooks_list:
if hook.bound_check(ql.arch.regs.arch_pc):
ret = hook.call(ql, *args[:-1])
ret = hook.call(ql, *hook_args)
if type(ret) is tuple:
ret, retval = ret
@@ -81,13 +155,16 @@ class QlCoreHooks:
def _hook_trace_cb(self, uc: Uc, addr: int, size: int, pack_data) -> None:
"""Code and block hooks dispatcher.
"""
ql, hook_type = pack_data
if hook_type in self._hook:
hooks_list = self._hook[hook_type]
for hook in hooks_list:
if hook.bound_check(ql.arch.regs.arch_pc):
if hook.bound_check(addr, size):
ret = hook.call(ql, addr, size)
if type(ret) is int and ret & QL_HOOK_BLOCK:
@@ -95,6 +172,9 @@ class QlCoreHooks:
def _hook_mem_cb(self, uc: Uc, access: int, addr: int, size: int, value: int, pack_data):
"""Memory access hooks dispatcher.
"""
ql, hook_type = pack_data
handled = False
@@ -116,6 +196,9 @@ class QlCoreHooks:
def _hook_insn_invalid_cb(self, uc: Uc, pack_data) -> None:
"""Invalid instruction hooks dispatcher.
"""
ql, hook_type = pack_data
handled = False
@@ -134,6 +217,9 @@ class QlCoreHooks:
def _hook_addr_cb(self, uc: Uc, addr: int, size: int, pack_data):
"""Address hooks dispatcher.
"""
ql = pack_data
if addr in self._addr_hook:
@@ -148,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
View File
File diff suppressed because it is too large Load Diff
+37 -78
View File
@@ -3,117 +3,76 @@
# Cross Platform and Multi Architecture Advanced Binary Emulation Framework
#
from typing import Optional
from qiling import Qiling
from qiling.const import QL_ARCH
# this code is partially based on uDbg
# @see: https://github.com/iGio90/uDdbg
PROMPT = r'gdb>'
class QlGdbUtils:
def __init__(self):
self.ql: Qiling
self.current_address = 0x0
self.current_address_size = 0x0
self.last_bp = 0x0
self.entry_point = None
self.exit_point = None
self.soft_bp = False
self.has_soft_bp = False
self.bp_list = []
# self.mapping = []
self.breakpoint_count = 0x0
self.skip_bp_count = 0x0
def initialize(self, ql: Qiling, hook_address: int, exit_point: int, mappings=[]):
def __init__(self, ql: Qiling, entry_point: int, exit_point: int):
self.ql = ql
if ql.baremetal:
self.current_address = self.entry_point
else:
self.current_address = self.entry_point = ql.os.entry_point
self.exit_point = exit_point
# self.mapping = mappings
self.bp_list = []
self.last_bp = None
def __entry_point_hook(ql: Qiling):
ql.hook_del(ep_hret)
ql.hook_code(self.dbg_hook)
ql.log.info(f'{PROMPT} stopped at entry point: {ql.arch.regs.arch_pc:#x}')
ql.stop()
ql.log.info(f'{PROMPT} Stop at entry point: {ql.arch.regs.arch_pc:#x}')
ep_hret = ql.hook_address(__entry_point_hook, hook_address)
# set a one-time hook to be dispatched upon reaching program entry point.
# that hook will be used to set up the breakpoint handling hook
ep_hret = ql.hook_address(__entry_point_hook, entry_point)
def dbg_hook(self, ql: Qiling, address: int, size: int):
"""Modified this function for qiling.gdbserver by kabeor from https://github.com/iGio90/uDdbg
"""
if ql.arch.type == QL_ARCH.ARM and ql.arch.is_thumb:
address += 1
try:
if ql.arch.type == QL_ARCH.ARM:
if ql.arch.is_thumb:
address += 1
# resuming emulation after hitting a breakpoint will re-enter this hook.
# avoid an endless hooking loop by detecting and skipping this case
if address == self.last_bp:
self.last_bp = None
# self.mapping.append([(hex(address))])
self.current_address = address
hit_soft_bp = False
elif address in self.bp_list:
self.last_bp = address
if self.soft_bp == True:
self.soft_bp = False
hit_soft_bp = True
# Breakpoints are always added without the LSB, even in Thumb, so they should be checked like this as well
if ((address & ~1) in self.bp_list and (address & ~1) != self.last_bp) or self.has_soft_bp == True:
if self.skip_bp_count > 0:
self.skip_bp_count -= 1
else:
self.breakpoint_count += 1
ql.stop()
self.last_bp = address
ql.log.info(f'{PROMPT} Breakpoint found, stop at address: {address:#x}')
elif address == self.last_bp:
self.last_bp = 0x0
self.has_soft_bp = hit_soft_bp
if self.current_address + size == self.exit_point:
ql.log.debug(f'{PROMPT} emulation entrypoint at {self.entry_point:#x}')
ql.log.debug(f'{PROMPT} emulation exitpoint at {self.exit_point:#x}')
except KeyboardInterrupt:
ql.log.info(f'{PROMPT} Paused at {address:#x}, instruction size = {size:d}')
ql.log.info(f'{PROMPT} breakpoint hit, stopped at {address:#x}')
ql.stop()
except:
raise
# # TODO: not sure what this is about
# if address + size == self.exit_point:
# ql.log.debug(f'{PROMPT} emulation entrypoint at {self.entry_point:#x}')
# ql.log.debug(f'{PROMPT} emulation exitpoint at {self.exit_point:#x}')
def bp_insert(self, addr: int):
if addr not in self.bp_list:
self.bp_list.append(addr)
self.ql.log.info(f'{PROMPT} Breakpoint added at: {addr:#x}')
self.ql.log.info(f'{PROMPT} breakpoint added at {addr:#x}')
def bp_remove(self, addr: int, type = None, len = None):
def bp_remove(self, addr: int):
self.bp_list.remove(addr)
self.ql.log.info(f'{PROMPT} Breakpoint removed at: {addr:#x}')
self.ql.log.info(f'{PROMPT} breakpoint removed from {addr:#x}')
def resume_emu(self, address: int = None, skip_bp: int = 0):
"""Modified this function for qiling.gdbserver by kabeor from https://github.com/iGio90/uDdbg
"""
def resume_emu(self, address: Optional[int] = None, steps: int = 0):
if address is None:
address = self.ql.arch.regs.arch_pc
if address is not None:
if self.ql.arch.type == QL_ARCH.ARM:
if self.ql.arch.is_thumb:
address += 1
if self.ql.arch.type == QL_ARCH.ARM and self.ql.arch.is_thumb:
address += 1
self.current_address = address
op = f'stepping {steps} instructions' if steps else 'resuming'
self.ql.log.info(f'{PROMPT} {op} from {address:#x}')
self.skip_bp_count = skip_bp
if self.exit_point is not None:
self.ql.log.info(f'{PROMPT} Resume at: {self.current_address:#x}')
self.ql.emu_start(self.current_address, self.exit_point)
self.ql.emu_start(address, self.exit_point, count=steps)
+6 -1
View File
@@ -1 +1,6 @@
<?xml version="1.0"?><!DOCTYPE target SYSTEM "gdb-target.dtd"><target><architecture>i8086</architecture><xi:include href="i386-32bit.xml"/></target>
<?xml version="1.0"?>
<!DOCTYPE target SYSTEM "gdb-target.dtd">
<target xmlns:xi="http://www.w3.org/2001/XInclude">
<architecture>i8086</architecture>
<xi:include href="i386-32bit.xml"/>
</target>
+24 -24
View File
@@ -1,31 +1,31 @@
<?xml version="1.0"?>
<!-- Copyright (C) 2007-2016 Free Software Foundation, Inc.
<!-- Copyright (C) 2007-2020 Free Software Foundation, Inc.
*!Copying and distribution of this file, with or without modification,
*!are permitted in any medium without royalty provided the copyright
*!notice and this notice are preserved. -->
Copying and distribution of this file, with or without modification,
are permitted in any medium without royalty provided the copyright
notice and this notice are preserved. -->
<!DOCTYPE feature SYSTEM "gdb-target.dtd">
<feature name="org.gnu.gdb.arm.core">
<reg name="r0" bitsize="32" type="uint32"/>
<reg name="r1" bitsize="32" type="uint32"/>
<reg name="r2" bitsize="32" type="uint32"/>
<reg name="r3" bitsize="32" type="uint32"/>
<reg name="r4" bitsize="32" type="uint32"/>
<reg name="r5" bitsize="32" type="uint32"/>
<reg name="r6" bitsize="32" type="uint32"/>
<reg name="r7" bitsize="32" type="uint32"/>
<reg name="r8" bitsize="32" type="uint32"/>
<reg name="r9" bitsize="32" type="uint32"/>
<reg name="r10" bitsize="32" type="uint32"/>
<reg name="r11" bitsize="32" type="uint32"/>
<reg name="r12" bitsize="32" type="uint32"/>
<reg name="sp" bitsize="32" type="data_ptr"/>
<reg name="lr" bitsize="32"/>
<reg name="pc" bitsize="32" type="code_ptr"/>
<reg name="r0" bitsize="32" type="uint32"/>
<reg name="r1" bitsize="32" type="uint32"/>
<reg name="r2" bitsize="32" type="uint32"/>
<reg name="r3" bitsize="32" type="uint32"/>
<reg name="r4" bitsize="32" type="uint32"/>
<reg name="r5" bitsize="32" type="uint32"/>
<reg name="r6" bitsize="32" type="uint32"/>
<reg name="r7" bitsize="32" type="uint32"/>
<reg name="r8" bitsize="32" type="uint32"/>
<reg name="r9" bitsize="32" type="uint32"/>
<reg name="r10" bitsize="32" type="uint32"/>
<reg name="r11" bitsize="32" type="uint32"/>
<reg name="r12" bitsize="32" type="uint32"/>
<reg name="sp" bitsize="32" type="data_ptr"/>
<reg name="lr" bitsize="32"/>
<reg name="pc" bitsize="32" type="code_ptr"/>
<!-- The CPSR is register 25, rather than register 16, because
*" the FPA registers historically were placed between the PC
*" and the CPSR in the "g" packet. -->
<reg name="cpsr" bitsize="32" regnum="25"/>
<!-- The CPSR is register 25, rather than register 16, because
the FPA registers historically were placed between the PC
and the CPSR in the "g" packet. -->
<reg name="cpsr" bitsize="32" regnum="25"/>
</feature>
@@ -1,39 +0,0 @@
<?xml version="1.0"?>
<!-- Copyright (C) 2019-2020 Free Software Foundation, Inc.
Copying and distribution of this file, with or without modification,
are permitted in any medium without royalty provided the copyright
notice and this notice are preserved. -->
<!DOCTYPE feature SYSTEM "gdb-target.dtd">
<feature name="org.gnu.gdb.arm.m-profile">
<reg name="r0" bitsize="32"/>
<reg name="r1" bitsize="32"/>
<reg name="r2" bitsize="32"/>
<reg name="r3" bitsize="32"/>
<reg name="r4" bitsize="32"/>
<reg name="r5" bitsize="32"/>
<reg name="r6" bitsize="32"/>
<reg name="r7" bitsize="32"/>
<reg name="r8" bitsize="32"/>
<reg name="r9" bitsize="32"/>
<reg name="r10" bitsize="32"/>
<reg name="r11" bitsize="32"/>
<reg name="r12" bitsize="32"/>
<reg name="sp" bitsize="32" type="data_ptr"/>
<reg name="lr" bitsize="32"/>
<reg name="pc" bitsize="32" type="code_ptr"/>
<!-- Slack for unused FPA registers (f0-f7 + fps). -->
<reg name="" bitsize="96" type="arm_fpa_ext" regnum="16"/>
<reg name="" bitsize="96" type="arm_fpa_ext"/>
<reg name="" bitsize="96" type="arm_fpa_ext"/>
<reg name="" bitsize="96" type="arm_fpa_ext"/>
<reg name="" bitsize="96" type="arm_fpa_ext"/>
<reg name="" bitsize="96" type="arm_fpa_ext"/>
<reg name="" bitsize="96" type="arm_fpa_ext"/>
<reg name="" bitsize="96" type="arm_fpa_ext"/>
<reg name="" bitsize="32"/>
<reg name="xpsr" bitsize="32" regnum="25"/>
</feature>
@@ -7,7 +7,7 @@
<!DOCTYPE feature SYSTEM "gdb-target.dtd">
<feature name="org.gnu.gdb.arm.m-profile">
<reg name="r0" bitsize="32"/>
<reg name="r0" bitsize="32" regnum="0"/>
<reg name="r1" bitsize="32"/>
<reg name="r2" bitsize="32"/>
<reg name="r3" bitsize="32"/>
@@ -23,5 +23,6 @@
<reg name="sp" bitsize="32" type="data_ptr"/>
<reg name="lr" bitsize="32"/>
<reg name="pc" bitsize="32" type="code_ptr"/>
<reg name="xpsr" bitsize="32" regnum="25"/>
</feature>
+6 -6
View File
@@ -6,9 +6,9 @@
*!notice and this notice are preserved. -->
<!DOCTYPE target SYSTEM "gdb-target.dtd">
<target>
<architecture>arm</architecture>
<xi:include href="arm-core.xml"/>
<xi:include href="arm-vfpv3.xml"/>
<feature name="org.gnu.gdb.arm.neon"/>
</target>
<target xmlns:xi="http://www.w3.org/2001/XInclude">
<architecture>arm</architecture>
<xi:include href="arm-core.xml"/>
<xi:include href="arm-vfpv3.xml"/>
<feature name="org.gnu.gdb.arm.neon"/>
</target>
+55 -55
View File
@@ -1,67 +1,67 @@
<?xml version="1.0"?>
<!-- Copyright (C) 2009-2016 Free Software Foundation, Inc.
*!Contributed by ARM Ltd.
Contributed by ARM Ltd.
*!Copying and distribution of this file, with or without modification,
*!are permitted in any medium without royalty provided the copyright
*!notice and this notice are preserved. -->
Copying and distribution of this file, with or without modification,
are permitted in any medium without royalty provided the copyright
notice and this notice are preserved. -->
<!DOCTYPE feature SYSTEM "gdb-target.dtd">
<feature name="org.gnu.gdb.aarch64.core">
<reg name="x0" bitsize="64"/>
<reg name="x1" bitsize="64"/>
<reg name="x2" bitsize="64"/>
<reg name="x3" bitsize="64"/>
<reg name="x4" bitsize="64"/>
<reg name="x5" bitsize="64"/>
<reg name="x6" bitsize="64"/>
<reg name="x7" bitsize="64"/>
<reg name="x8" bitsize="64"/>
<reg name="x9" bitsize="64"/>
<reg name="x10" bitsize="64"/>
<reg name="x11" bitsize="64"/>
<reg name="x12" bitsize="64"/>
<reg name="x13" bitsize="64"/>
<reg name="x14" bitsize="64"/>
<reg name="x15" bitsize="64"/>
<reg name="x16" bitsize="64"/>
<reg name="x17" bitsize="64"/>
<reg name="x18" bitsize="64"/>
<reg name="x19" bitsize="64"/>
<reg name="x20" bitsize="64"/>
<reg name="x21" bitsize="64"/>
<reg name="x22" bitsize="64"/>
<reg name="x23" bitsize="64"/>
<reg name="x24" bitsize="64"/>
<reg name="x25" bitsize="64"/>
<reg name="x26" bitsize="64"/>
<reg name="x27" bitsize="64"/>
<reg name="x28" bitsize="64"/>
<reg name="x29" bitsize="64"/>
<reg name="x30" bitsize="64"/>
<reg name="sp" bitsize="64" type="data_ptr"/>
<reg name="x0" bitsize="64"/>
<reg name="x1" bitsize="64"/>
<reg name="x2" bitsize="64"/>
<reg name="x3" bitsize="64"/>
<reg name="x4" bitsize="64"/>
<reg name="x5" bitsize="64"/>
<reg name="x6" bitsize="64"/>
<reg name="x7" bitsize="64"/>
<reg name="x8" bitsize="64"/>
<reg name="x9" bitsize="64"/>
<reg name="x10" bitsize="64"/>
<reg name="x11" bitsize="64"/>
<reg name="x12" bitsize="64"/>
<reg name="x13" bitsize="64"/>
<reg name="x14" bitsize="64"/>
<reg name="x15" bitsize="64"/>
<reg name="x16" bitsize="64"/>
<reg name="x17" bitsize="64"/>
<reg name="x18" bitsize="64"/>
<reg name="x19" bitsize="64"/>
<reg name="x20" bitsize="64"/>
<reg name="x21" bitsize="64"/>
<reg name="x22" bitsize="64"/>
<reg name="x23" bitsize="64"/>
<reg name="x24" bitsize="64"/>
<reg name="x25" bitsize="64"/>
<reg name="x26" bitsize="64"/>
<reg name="x27" bitsize="64"/>
<reg name="x28" bitsize="64"/>
<reg name="x29" bitsize="64"/>
<reg name="x30" bitsize="64"/>
<reg name="sp" bitsize="64" type="data_ptr"/>
<reg name="pc" bitsize="64" type="code_ptr"/>
<reg name="pc" bitsize="64" type="code_ptr"/>
<flags id="cpsr_flags" size="4">
* <field name="SP" start="0" end="0"/>
* <field name="" start="1" end="1"/>
* <field name="EL" start="2" end="3"/>
* <field name="nRW" start="4" end="4"/>
* <field name="" start="5" end="5"/>
* <field name="F" start="6" end="6"/>
* <field name="I" start="7" end="7"/>
* <field name="A" start="8" end="8"/>
* <field name="D" start="9" end="9"/>
<flags id="cpsr_flags" size="4">
<field name="SP" start="0" end="0"/>
<field name="" start="1" end="1"/>
<field name="EL" start="2" end="3"/>
<field name="nRW" start="4" end="4"/>
<field name="" start="5" end="5"/>
<field name="F" start="6" end="6"/>
<field name="I" start="7" end="7"/>
<field name="A" start="8" end="8"/>
<field name="D" start="9" end="9"/>
* <field name="IL" start="20" end="20"/>
* <field name="SS" start="21" end="21"/>
<field name="IL" start="20" end="20"/>
<field name="SS" start="21" end="21"/>
* <field name="V" start="28" end="28"/>
* <field name="C" start="29" end="29"/>
* <field name="Z" start="30" end="30"/>
* <field name="N" start="31" end="31"/>
</flags>
<reg name="cpsr" bitsize="32" type="cpsr_flags"/>
<field name="V" start="28" end="28"/>
<field name="C" start="29" end="29"/>
<field name="Z" start="30" end="30"/>
<field name="N" start="31" end="31"/>
</flags>
<reg name="cpsr" bitsize="32" type="cpsr_flags"/>
</feature>
+86 -79
View File
@@ -1,86 +1,93 @@
<?xml version="1.0"?>
<!-- Copyright (C) 2009-2016 Free Software Foundation, Inc.
*!Contributed by ARM Ltd.
Contributed by ARM Ltd.
*!Copying and distribution of this file, with or without modification,
*!are permitted in any medium without royalty provided the copyright
*!notice and this notice are preserved. -->
Copying and distribution of this file, with or without modification,
are permitted in any medium without royalty provided the copyright
notice and this notice are preserved. -->
<!DOCTYPE feature SYSTEM "gdb-target.dtd">
<feature name="org.gnu.gdb.aarch64.fpu">
<vector id="v2d" type="ieee_double" count="2"/>
<vector id="v2u" type="uint64" count="2"/>
<vector id="v2i" type="int64" count="2"/>
<vector id="v4f" type="ieee_single" count="4"/>
<vector id="v4u" type="uint32" count="4"/>
<vector id="v4i" type="int32" count="4"/>
<vector id="v8u" type="uint16" count="8"/>
<vector id="v8i" type="int16" count="8"/>
<vector id="v16u" type="uint8" count="16"/>
<vector id="v16i" type="int8" count="16"/>
<vector id="v1u" type="uint128" count="1"/>
<vector id="v1i" type="int128" count="1"/>
<union id="vnd">
* <field name="f" type="v2d"/>
* <field name="u" type="v2u"/>
* <field name="s" type="v2i"/>
</union>
<union id="vns">
* <field name="f" type="v4f"/>
* <field name="u" type="v4u"/>
* <field name="s" type="v4i"/>
</union>
<union id="vnh">
* <field name="u" type="v8u"/>
* <field name="s" type="v8i"/>
</union>
<union id="vnb">
* <field name="u" type="v16u"/>
* <field name="s" type="v16i"/>
</union>
<union id="vnq">
* <field name="u" type="v1u"/>
* <field name="s" type="v1i"/>
</union>
<union id="aarch64v">
* <field name="d" type="vnd"/>
* <field name="s" type="vns"/>
* <field name="h" type="vnh"/>
* <field name="b" type="vnb"/>
* <field name="q" type="vnq"/>
</union>
<reg name="v0" bitsize="128" type="aarch64v" regnum="34"/>
<reg name="v1" bitsize="128" type="aarch64v" />
<reg name="v2" bitsize="128" type="aarch64v" />
<reg name="v3" bitsize="128" type="aarch64v" />
<reg name="v4" bitsize="128" type="aarch64v" />
<reg name="v5" bitsize="128" type="aarch64v" />
<reg name="v6" bitsize="128" type="aarch64v" />
<reg name="v7" bitsize="128" type="aarch64v" />
<reg name="v8" bitsize="128" type="aarch64v" />
<reg name="v9" bitsize="128" type="aarch64v" />
<reg name="v10" bitsize="128" type="aarch64v"/>
<reg name="v11" bitsize="128" type="aarch64v"/>
<reg name="v12" bitsize="128" type="aarch64v"/>
<reg name="v13" bitsize="128" type="aarch64v"/>
<reg name="v14" bitsize="128" type="aarch64v"/>
<reg name="v15" bitsize="128" type="aarch64v"/>
<reg name="v16" bitsize="128" type="aarch64v"/>
<reg name="v17" bitsize="128" type="aarch64v"/>
<reg name="v18" bitsize="128" type="aarch64v"/>
<reg name="v19" bitsize="128" type="aarch64v"/>
<reg name="v20" bitsize="128" type="aarch64v"/>
<reg name="v21" bitsize="128" type="aarch64v"/>
<reg name="v22" bitsize="128" type="aarch64v"/>
<reg name="v23" bitsize="128" type="aarch64v"/>
<reg name="v24" bitsize="128" type="aarch64v"/>
<reg name="v25" bitsize="128" type="aarch64v"/>
<reg name="v26" bitsize="128" type="aarch64v"/>
<reg name="v27" bitsize="128" type="aarch64v"/>
<reg name="v28" bitsize="128" type="aarch64v"/>
<reg name="v29" bitsize="128" type="aarch64v"/>
<reg name="v30" bitsize="128" type="aarch64v"/>
<reg name="v31" bitsize="128" type="aarch64v"/>
<reg name="fpsr" bitsize="32"/>
<reg name="fpcr" bitsize="32"/>
<vector id="v2d" type="ieee_double" count="2"/>
<vector id="v2u" type="uint64" count="2"/>
<vector id="v2i" type="int64" count="2"/>
<vector id="v4f" type="ieee_single" count="4"/>
<vector id="v4u" type="uint32" count="4"/>
<vector id="v4i" type="int32" count="4"/>
<vector id="v8u" type="uint16" count="8"/>
<vector id="v8i" type="int16" count="8"/>
<vector id="v16u" type="uint8" count="16"/>
<vector id="v16i" type="int8" count="16"/>
<vector id="v1u" type="uint128" count="1"/>
<vector id="v1i" type="int128" count="1"/>
<union id="vnd">
<field name="f" type="v2d"/>
<field name="u" type="v2u"/>
<field name="s" type="v2i"/>
</union>
<union id="vns">
<field name="f" type="v4f"/>
<field name="u" type="v4u"/>
<field name="s" type="v4i"/>
</union>
<union id="vnh">
<field name="u" type="v8u"/>
<field name="s" type="v8i"/>
</union>
<union id="vnb">
<field name="u" type="v16u"/>
<field name="s" type="v16i"/>
</union>
<union id="vnq">
<field name="u" type="v1u"/>
<field name="s" type="v1i"/>
</union>
<union id="aarch64v">
<field name="d" type="vnd"/>
<field name="s" type="vns"/>
<field name="h" type="vnh"/>
<field name="b" type="vnb"/>
<field name="q" type="vnq"/>
</union>
<reg name="v0" bitsize="128" type="aarch64v" regnum="34"/>
<reg name="v1" bitsize="128" type="aarch64v" />
<reg name="v2" bitsize="128" type="aarch64v" />
<reg name="v3" bitsize="128" type="aarch64v" />
<reg name="v4" bitsize="128" type="aarch64v" />
<reg name="v5" bitsize="128" type="aarch64v" />
<reg name="v6" bitsize="128" type="aarch64v" />
<reg name="v7" bitsize="128" type="aarch64v" />
<reg name="v8" bitsize="128" type="aarch64v" />
<reg name="v9" bitsize="128" type="aarch64v" />
<reg name="v10" bitsize="128" type="aarch64v"/>
<reg name="v11" bitsize="128" type="aarch64v"/>
<reg name="v12" bitsize="128" type="aarch64v"/>
<reg name="v13" bitsize="128" type="aarch64v"/>
<reg name="v14" bitsize="128" type="aarch64v"/>
<reg name="v15" bitsize="128" type="aarch64v"/>
<reg name="v16" bitsize="128" type="aarch64v"/>
<reg name="v17" bitsize="128" type="aarch64v"/>
<reg name="v18" bitsize="128" type="aarch64v"/>
<reg name="v19" bitsize="128" type="aarch64v"/>
<reg name="v20" bitsize="128" type="aarch64v"/>
<reg name="v21" bitsize="128" type="aarch64v"/>
<reg name="v22" bitsize="128" type="aarch64v"/>
<reg name="v23" bitsize="128" type="aarch64v"/>
<reg name="v24" bitsize="128" type="aarch64v"/>
<reg name="v25" bitsize="128" type="aarch64v"/>
<reg name="v26" bitsize="128" type="aarch64v"/>
<reg name="v27" bitsize="128" type="aarch64v"/>
<reg name="v28" bitsize="128" type="aarch64v"/>
<reg name="v29" bitsize="128" type="aarch64v"/>
<reg name="v30" bitsize="128" type="aarch64v"/>
<reg name="v31" bitsize="128" type="aarch64v"/>
<reg name="fpsr" bitsize="32"/>
<reg name="fpcr" bitsize="32"/>
</feature>
+9 -8
View File
@@ -1,14 +1,15 @@
<?xml version="1.0"?>
<!-- Copyright (C) 2009-2016 Free Software Foundation, Inc.
*!Contributed by ARM Ltd.
Contributed by ARM Ltd.
*!Copying and distribution of this file, with or without modification,
*!are permitted in any medium without royalty provided the copyright
*!notice and this notice are preserved. -->
Copying and distribution of this file, with or without modification,
are permitted in any medium without royalty provided the copyright
notice and this notice are preserved. -->
<!DOCTYPE target SYSTEM "gdb-target.dtd">
<target>
<architecture>aarch64</architecture>
<xi:include href="aarch64-core.xml"/>
<xi:include href="aarch64-fpu.xml"/>
<target xmlns:xi="http://www.w3.org/2001/XInclude">
<architecture>aarch64</architecture>
<xi:include href="aarch64-core.xml"/>
<xi:include href="aarch64-fpu.xml"/>
</target>
+10 -9
View File
@@ -6,14 +6,15 @@
*!notice and this notice are preserved. -->
<!DOCTYPE target SYSTEM "gdb-target.dtd">
<target>
<architecture>mips</architecture>
<osabi>GNU/Linux</osabi>
<xi:include href="mips-cpu.xml"/>
<xi:include href="mips-cp0.xml"/>
<xi:include href="mips-fpu.xml"/>
<target xmlns:xi="http://www.w3.org/2001/XInclude">
<architecture>mips</architecture>
<osabi>GNU/Linux</osabi>
<feature name="org.gnu.gdb.mips.linux">
* <reg name="restart" bitsize="32" group="system"/>
</feature>
<xi:include href="mips-cpu.xml"/>
<xi:include href="mips-cp0.xml"/>
<xi:include href="mips-fpu.xml"/>
<feature name="org.gnu.gdb.mips.linux">
<reg name="restart" bitsize="32" group="system"/>
</feature>
</target>
+52 -52
View File
@@ -7,59 +7,59 @@
<!DOCTYPE feature SYSTEM "gdb-target.dtd">
<feature name="org.gnu.gdb.i386.core">
<flags id="i386_eflags" size="4">
<field name="CF" start="0" end="0"/>
<field name="" start="1" end="1"/>
<field name="PF" start="2" end="2"/>
<field name="AF" start="4" end="4"/>
<field name="ZF" start="6" end="6"/>
<field name="SF" start="7" end="7"/>
<field name="TF" start="8" end="8"/>
<field name="IF" start="9" end="9"/>
<field name="DF" start="10" end="10"/>
<field name="OF" start="11" end="11"/>
<field name="NT" start="14" end="14"/>
<field name="RF" start="16" end="16"/>
<field name="VM" start="17" end="17"/>
<field name="AC" start="18" end="18"/>
<field name="VIF" start="19" end="19"/>
<field name="VIP" start="20" end="20"/>
<field name="ID" start="21" end="21"/>
</flags>
<flags id="i386_eflags" size="4">
<field name="CF" start="0" end="0"/>
<field name="" start="1" end="1"/>
<field name="PF" start="2" end="2"/>
<field name="AF" start="4" end="4"/>
<field name="ZF" start="6" end="6"/>
<field name="SF" start="7" end="7"/>
<field name="TF" start="8" end="8"/>
<field name="IF" start="9" end="9"/>
<field name="DF" start="10" end="10"/>
<field name="OF" start="11" end="11"/>
<field name="NT" start="14" end="14"/>
<field name="RF" start="16" end="16"/>
<field name="VM" start="17" end="17"/>
<field name="AC" start="18" end="18"/>
<field name="VIF" start="19" end="19"/>
<field name="VIP" start="20" end="20"/>
<field name="ID" start="21" end="21"/>
</flags>
<reg name="eax" bitsize="32" type="int32"/>
<reg name="ecx" bitsize="32" type="int32"/>
<reg name="edx" bitsize="32" type="int32"/>
<reg name="ebx" bitsize="32" type="int32"/>
<reg name="esp" bitsize="32" type="data_ptr"/>
<reg name="ebp" bitsize="32" type="data_ptr"/>
<reg name="esi" bitsize="32" type="int32"/>
<reg name="edi" bitsize="32" type="int32"/>
<reg name="eax" bitsize="32" type="int32"/>
<reg name="ecx" bitsize="32" type="int32"/>
<reg name="edx" bitsize="32" type="int32"/>
<reg name="ebx" bitsize="32" type="int32"/>
<reg name="esp" bitsize="32" type="data_ptr"/>
<reg name="ebp" bitsize="32" type="data_ptr"/>
<reg name="esi" bitsize="32" type="int32"/>
<reg name="edi" bitsize="32" type="int32"/>
<reg name="eip" bitsize="32" type="code_ptr"/>
<reg name="eflags" bitsize="32" type="i386_eflags"/>
<reg name="cs" bitsize="32" type="int32"/>
<reg name="ss" bitsize="32" type="int32"/>
<reg name="ds" bitsize="32" type="int32"/>
<reg name="es" bitsize="32" type="int32"/>
<reg name="fs" bitsize="32" type="int32"/>
<reg name="gs" bitsize="32" type="int32"/>
<reg name="eip" bitsize="32" type="code_ptr"/>
<reg name="eflags" bitsize="32" type="i386_eflags"/>
<reg name="cs" bitsize="32" type="int32"/>
<reg name="ss" bitsize="32" type="int32"/>
<reg name="ds" bitsize="32" type="int32"/>
<reg name="es" bitsize="32" type="int32"/>
<reg name="fs" bitsize="32" type="int32"/>
<reg name="gs" bitsize="32" type="int32"/>
<reg name="st0" bitsize="80" type="i387_ext"/>
<reg name="st1" bitsize="80" type="i387_ext"/>
<reg name="st2" bitsize="80" type="i387_ext"/>
<reg name="st3" bitsize="80" type="i387_ext"/>
<reg name="st4" bitsize="80" type="i387_ext"/>
<reg name="st5" bitsize="80" type="i387_ext"/>
<reg name="st6" bitsize="80" type="i387_ext"/>
<reg name="st7" bitsize="80" type="i387_ext"/>
<reg name="st0" bitsize="80" type="i387_ext"/>
<reg name="st1" bitsize="80" type="i387_ext"/>
<reg name="st2" bitsize="80" type="i387_ext"/>
<reg name="st3" bitsize="80" type="i387_ext"/>
<reg name="st4" bitsize="80" type="i387_ext"/>
<reg name="st5" bitsize="80" type="i387_ext"/>
<reg name="st6" bitsize="80" type="i387_ext"/>
<reg name="st7" bitsize="80" type="i387_ext"/>
<reg name="fctrl" bitsize="32" type="int" group="float"/>
<reg name="fstat" bitsize="32" type="int" group="float"/>
<reg name="ftag" bitsize="32" type="int" group="float"/>
<reg name="fiseg" bitsize="32" type="int" group="float"/>
<reg name="fioff" bitsize="32" type="int" group="float"/>
<reg name="foseg" bitsize="32" type="int" group="float"/>
<reg name="fooff" bitsize="32" type="int" group="float"/>
<reg name="fop" bitsize="32" type="int" group="float"/>
</feature>
<reg name="fctrl" bitsize="32" type="int" group="float"/>
<reg name="fstat" bitsize="32" type="int" group="float"/>
<reg name="ftag" bitsize="32" type="int" group="float"/>
<reg name="fiseg" bitsize="32" type="int" group="float"/>
<reg name="fioff" bitsize="32" type="int" group="float"/>
<reg name="foseg" bitsize="32" type="int" group="float"/>
<reg name="fooff" bitsize="32" type="int" group="float"/>
<reg name="fop" bitsize="32" type="int" group="float"/>
</feature>
+33 -31
View File
@@ -7,39 +7,41 @@
<!DOCTYPE feature SYSTEM "gdb-target.dtd">
<feature name="org.gnu.gdb.i386.mpx">
<struct id="br128">
<field name="lbound" type="uint64"/>
<field name="ubound_raw" type="uint64"/>
</struct>
<struct id="br128">
<field name="lbound" type="uint64"/>
<field name="ubound_raw" type="uint64"/>
</struct>
<struct id="_bndstatus" size="8">
<field name="bde" start="2" end="31"/>
<field name="error" start="0" end="1"/>
</struct>
<struct id="_bndstatus" size="8">
<field name="bde" start="2" end="31"/>
<field name="error" start="0" end="1"/>
</struct>
<union id="status">
<field name="raw" type="data_ptr"/>
<field name="status" type="_bndstatus"/>
</union>
<union id="status">
<field name="raw" type="data_ptr"/>
<field name="status" type="_bndstatus"/>
</union>
<struct id="_bndcfgu" size="8">
<field name="base" start="12" end="31" />
<field name="reserved" start="2" end="11"/>
<!-- Explicitly set the type here, otherwise it defaults to bool.
Perhaps this should be uint32, but the container type has size 8. -->
<field name="preserved" start="1" end="1" type="uint64"/>
<field name="enabled" start="0" end="0" type="uint64"/>
</struct>
<struct id="_bndcfgu" size="8">
<field name="base" start="12" end="31" />
<field name="reserved" start="2" end="11"/>
<!--
Explicitly set the type here, otherwise it defaults to bool.
Perhaps this should be uint32, but the container type has size 8.
-->
<field name="preserved" start="1" end="1" type="uint64"/>
<field name="enabled" start="0" end="0" type="uint64"/>
</struct>
<union id="cfgu">
<field name="raw" type="data_ptr"/>
<field name="config" type="_bndcfgu"/>
</union>
<union id="cfgu">
<field name="raw" type="data_ptr"/>
<field name="config" type="_bndcfgu"/>
</union>
<reg name="bnd0raw" bitsize="128" type="br128"/>
<reg name="bnd1raw" bitsize="128" type="br128"/>
<reg name="bnd2raw" bitsize="128" type="br128"/>
<reg name="bnd3raw" bitsize="128" type="br128"/>
<reg name="bndcfgu" bitsize="64" type="cfgu"/>
<reg name="bndstatus" bitsize="64" type="status"/>
</feature>
<reg name="bnd0raw" bitsize="128" type="br128"/>
<reg name="bnd1raw" bitsize="128" type="br128"/>
<reg name="bnd2raw" bitsize="128" type="br128"/>
<reg name="bnd3raw" bitsize="128" type="br128"/>
<reg name="bndcfgu" bitsize="64" type="cfgu"/>
<reg name="bndstatus" bitsize="64" type="status"/>
</feature>
+2 -4
View File
@@ -7,7 +7,5 @@
<!DOCTYPE feature SYSTEM "gdb-target.dtd">
<feature name="org.gnu.gdb.i386.pkeys">
<reg name="pkru" bitsize="32" type="uint32"/>
</feature>
<reg name="pkru" bitsize="32" type="uint32"/>
</feature>
+43 -41
View File
@@ -7,46 +7,48 @@
<!DOCTYPE feature SYSTEM "gdb-target.dtd">
<feature name="org.gnu.gdb.i386.sse">
<vector id="v4f" type="ieee_single" count="4"/>
<vector id="v2d" type="ieee_double" count="2"/>
<vector id="v16i8" type="int8" count="16"/>
<vector id="v8i16" type="int16" count="8"/>
<vector id="v4i32" type="int32" count="4"/>
<vector id="v2i64" type="int64" count="2"/>
<union id="vec128">
<field name="v4_float" type="v4f"/>
<field name="v2_double" type="v2d"/>
<field name="v16_int8" type="v16i8"/>
<field name="v8_int16" type="v8i16"/>
<field name="v4_int32" type="v4i32"/>
<field name="v2_int64" type="v2i64"/>
<field name="uint128" type="uint128"/>
</union>
<flags id="i386_mxcsr" size="4">
<field name="IE" start="0" end="0"/>
<field name="DE" start="1" end="1"/>
<field name="ZE" start="2" end="2"/>
<field name="OE" start="3" end="3"/>
<field name="UE" start="4" end="4"/>
<field name="PE" start="5" end="5"/>
<field name="DAZ" start="6" end="6"/>
<field name="IM" start="7" end="7"/>
<field name="DM" start="8" end="8"/>
<field name="ZM" start="9" end="9"/>
<field name="OM" start="10" end="10"/>
<field name="UM" start="11" end="11"/>
<field name="PM" start="12" end="12"/>
<field name="FZ" start="15" end="15"/>
</flags>
<vector id="v4f" type="ieee_single" count="4"/>
<vector id="v2d" type="ieee_double" count="2"/>
<vector id="v16i8" type="int8" count="16"/>
<vector id="v8i16" type="int16" count="8"/>
<vector id="v4i32" type="int32" count="4"/>
<vector id="v2i64" type="int64" count="2"/>
<reg name="xmm0" bitsize="128" type="vec128" regnum="32"/>
<reg name="xmm1" bitsize="128" type="vec128"/>
<reg name="xmm2" bitsize="128" type="vec128"/>
<reg name="xmm3" bitsize="128" type="vec128"/>
<reg name="xmm4" bitsize="128" type="vec128"/>
<reg name="xmm5" bitsize="128" type="vec128"/>
<reg name="xmm6" bitsize="128" type="vec128"/>
<reg name="xmm7" bitsize="128" type="vec128"/>
<union id="vec128">
<field name="v4_float" type="v4f"/>
<field name="v2_double" type="v2d"/>
<field name="v16_int8" type="v16i8"/>
<field name="v8_int16" type="v8i16"/>
<field name="v4_int32" type="v4i32"/>
<field name="v2_int64" type="v2i64"/>
<field name="uint128" type="uint128"/>
</union>
<reg name="mxcsr" bitsize="32" type="i386_mxcsr" group="vector"/>
</feature>
<flags id="i386_mxcsr" size="4">
<field name="IE" start="0" end="0"/>
<field name="DE" start="1" end="1"/>
<field name="ZE" start="2" end="2"/>
<field name="OE" start="3" end="3"/>
<field name="UE" start="4" end="4"/>
<field name="PE" start="5" end="5"/>
<field name="DAZ" start="6" end="6"/>
<field name="IM" start="7" end="7"/>
<field name="DM" start="8" end="8"/>
<field name="ZM" start="9" end="9"/>
<field name="OM" start="10" end="10"/>
<field name="UM" start="11" end="11"/>
<field name="PM" start="12" end="12"/>
<field name="FZ" start="15" end="15"/>
</flags>
<reg name="xmm0" bitsize="128" type="vec128" regnum="32"/>
<reg name="xmm1" bitsize="128" type="vec128"/>
<reg name="xmm2" bitsize="128" type="vec128"/>
<reg name="xmm3" bitsize="128" type="vec128"/>
<reg name="xmm4" bitsize="128" type="vec128"/>
<reg name="xmm5" bitsize="128" type="vec128"/>
<reg name="xmm6" bitsize="128" type="vec128"/>
<reg name="xmm7" bitsize="128" type="vec128"/>
<reg name="mxcsr" bitsize="32" type="i386_mxcsr" group="vector"/>
</feature>
+9 -9
View File
@@ -1,11 +1,11 @@
<?xml version="1.0"?>
<!DOCTYPE target SYSTEM "gdb-target.dtd">
<target><architecture>i386</architecture>
<osabi>GNU/Linux</osabi>
<xi:include href="32bit-core.xml"/>
<xi:include href="32bit-sse.xml"/>
<xi:include href="32bit-linux.xml"/>
<xi:include href="32bit-segments.xml"/>
<xi:include href="32bit-avx.xml"/>
<xi:include href="32bit-mpx.xml"/>
</target>
<target xmlns:xi="http://www.w3.org/2001/XInclude">
<architecture>i386</architecture>
<osabi>GNU/Linux</osabi>
<xi:include href="32bit-core.xml"/>
<xi:include href="32bit-sse.xml"/>
<xi:include href="32bit-linux.xml"/>
<xi:include href="32bit-avx.xml"/>
</target>
+13 -1
View File
@@ -1 +1,13 @@
<?xml version="1.0"?><!DOCTYPE target SYSTEM "gdb-target.dtd"><target><architecture>i386:x86-64</architecture><osabi>GNU/Linux</osabi><xi:include href="64bit-core.xml"/><xi:include href="64bit-sse.xml"/><xi:include href="64bit-linux.xml"/><xi:include href="64bit-segments.xml"/><xi:include href="64bit-avx.xml"/><xi:include href="64bit-mpx.xml"/></target>
<?xml version="1.0"?>
<!DOCTYPE target SYSTEM "gdb-target.dtd">
<target xmlns:xi="http://www.w3.org/2001/XInclude">
<architecture>i386:x86-64</architecture>
<osabi>GNU/Linux</osabi>
<xi:include href="64bit-core.xml"/>
<xi:include href="64bit-sse.xml"/>
<xi:include href="64bit-linux.xml"/>
<xi:include href="64bit-segments.xml"/>
<xi:include href="64bit-avx.xml"/>
<xi:include href="64bit-mpx.xml"/>
</target>
+150
View File
@@ -0,0 +1,150 @@
#!/usr/bin/env python3
#
# Cross Platform and Multi Architecture Advanced Binary Emulation Framework
#
from typing import Iterator, Mapping, Optional, Sequence, Tuple
from pathlib import PurePath
from xml.etree import ElementTree, ElementInclude
from qiling.arch.arm_const import reg_map as arm_regs
from qiling.arch.arm_const import reg_vfp as arm_regs_vfp
from qiling.arch.arm64_const import reg_map as arm64_regs
from qiling.arch.arm64_const import reg_map_v as arm64_regs_v
from qiling.arch.mips_const import reg_map as mips_regs_gpr
from qiling.arch.mips_const import reg_map_fpu as mips_regs_fpu
from qiling.arch.x86_const import reg_map_32 as x86_regs_32
from qiling.arch.x86_const import reg_map_64 as x86_regs_64
from qiling.arch.x86_const import reg_map_misc as x86_regs_misc
from qiling.arch.x86_const import reg_map_cr as x86_regs_cr
from qiling.arch.x86_const import reg_map_st as x86_regs_st
from qiling.arch.x86_const import reg_map_xmm as x86_regs_xmm
from qiling.arch.x86_const import reg_map_ymm as x86_regs_ymm
from qiling.const import QL_ARCH, QL_OS
RegEntry = Tuple[Optional[int], int, int]
class QlGdbFeatures:
def __init__(self, archtype: QL_ARCH, ostype: QL_OS):
xmltree = QlGdbFeatures.__load_target_xml(archtype, ostype)
regsmap = QlGdbFeatures.__load_regsmap(archtype, xmltree)
self.xmltree = xmltree
self.regsmap = regsmap
def tostring(self) -> str:
root = self.xmltree.getroot()
return ElementTree.tostring(root, encoding='unicode', xml_declaration=True)
@staticmethod
def __get_xml_path(archtype: QL_ARCH) -> Tuple[str, PurePath]:
import inspect
p = PurePath(inspect.getfile(QlGdbFeatures))
basedir = p.parent / 'xml' / archtype.name.lower()
filename = basedir / 'target.xml'
return str(filename), basedir
@staticmethod
def __load_target_xml(archtype: QL_ARCH, ostype: QL_OS) -> ElementTree.ElementTree:
filename, base_url = QlGdbFeatures.__get_xml_path(archtype)
tree = ElementTree.parse(filename)
# NOTE: this is needed to load xinclude hrefs relative to the main xml file. starting
# from python 3.9 ElementInclude.include has an argument for that called 'base_url'.
# this is a workaround for earlier python versions such as 3.8
# <WORKAROUND>
def my_loader(base: PurePath):
def __wrapped(href: str, parse, encoding=None):
abshref = base / href
return ElementInclude.default_loader(str(abshref), parse, encoding)
return __wrapped
# </WORKAROUND>
# inline all xi:include elements
ElementInclude.include(tree.getroot(), loader=my_loader(base_url))
# patch xml osabi element with the appropriate abi tag
osabi = tree.find('osabi')
if osabi is not None:
# NOTE: the 'Windows' abi tag is supported starting from gdb 10.
# earlier gdb versions use 'Cygwin' instead
abitag = {
QL_OS.LINUX : 'GNU/Linux',
QL_OS.FREEBSD : 'FreeBSD',
QL_OS.MACOS : 'Darwin',
QL_OS.WINDOWS : 'Windows',
QL_OS.UEFI : 'Windows',
QL_OS.DOS : 'Windows',
QL_OS.QNX : 'QNX-Neutrino'
}.get(ostype, 'unknown')
osabi.text = abitag
return tree
@staticmethod
def __walk_xml_regs(xmltree: ElementTree.ElementTree) -> Iterator[Tuple[int, str, int]]:
regnum = -1
for reg in xmltree.iter('reg'):
# if regnum is not specified, assume it follows the previous one
regnum = int(reg.get('regnum', regnum + 1))
name = reg.attrib['name']
bitsize = reg.attrib['bitsize']
yield regnum, name, int(bitsize)
@staticmethod
def __load_regsmap(archtype: QL_ARCH, xmltree: ElementTree.ElementTree) -> Sequence[RegEntry]:
"""Initialize registers map using available target XML files.
Args:
archtype: target architecture type
Returns: a list representing registers data
"""
# retreive the relevant set of registers; their order of appearance is not
# important as it is determined by the info read from the xml files
ucregs: Mapping[str, int] = {
QL_ARCH.A8086 : dict(**x86_regs_32, **x86_regs_misc, **x86_regs_cr, **x86_regs_st),
QL_ARCH.X86 : dict(**x86_regs_32, **x86_regs_misc, **x86_regs_cr, **x86_regs_st, **x86_regs_xmm),
QL_ARCH.X8664 : dict(**x86_regs_64, **x86_regs_misc, **x86_regs_cr, **x86_regs_st, **x86_regs_xmm, **x86_regs_ymm),
QL_ARCH.ARM : dict(**arm_regs, **arm_regs_vfp),
QL_ARCH.CORTEX_M : arm_regs,
QL_ARCH.ARM64 : dict(**arm64_regs, **arm64_regs_v),
QL_ARCH.MIPS : dict(**mips_regs_gpr, **mips_regs_fpu)
}[archtype]
regsinfo = sorted(QlGdbFeatures.__walk_xml_regs(xmltree))
# pre-allocate regmap and occupy it with null entries
last_regnum = regsinfo[-1][0]
regmap: Sequence[RegEntry] = [(None, 0, 0)] * (last_regnum + 1)
pos = 0
for regnum, name, bitsize in sorted(regsinfo):
# reg value size in nibbles
nibbles = bitsize // 4
regmap[regnum] = (ucregs.get(name), pos, nibbles)
# value position of next reg
pos += nibbles
return regmap
__all__ = ['RegEntry', 'QlGdbFeatures']
+2 -2
View File
@@ -21,7 +21,7 @@ class ArchX86(Arch):
"eax", "ebx", "ecx", "edx",
"esp", "ebp", "esi", "edi",
"eip", "ss", "cs", "ds", "es",
"fs", "gs", "ef",
"fs", "gs", "eflags",
)
def read_insn(self, address: int) -> bytes:
@@ -34,7 +34,7 @@ class ArchX86(Arch):
@staticmethod
def get_flags(bits: int) -> Mapping[str, bool]:
"""
get flags from ql.reg.ef
get flags from ql.reg.eflags
"""
return {
@@ -91,7 +91,7 @@ class BranchPredictorX86(BranchPredictor, ArchX86):
}
if line.mnemonic in jump_table:
eflags = self.get_flags(self.ql.arch.regs.ef).values()
eflags = self.get_flags(self.ql.arch.regs.eflags).values()
prophecy.going = jump_table.get(line.mnemonic)(*eflags)
elif line.mnemonic in jump_reg_table:
+2 -2
View File
@@ -389,7 +389,7 @@ class CtxManager_X86(CtxManager):
"eax", "ebx", "ecx", "edx",
"esp", "ebp", "esi", "edi",
"eip", "ss", "cs", "ds", "es",
"fs", "gs", "ef",
"fs", "gs", "eflags",
)
@context_printer("[ REGISTERS ]")
def context_reg(self, saved_reg_dump):
@@ -416,7 +416,7 @@ class CtxManager_X86(CtxManager):
lines += line
print(lines.format(*cur_regs.values()))
print(color.GREEN, "EFLAGS: [CF: {flags[CF]}, PF: {flags[PF]}, AF: {flags[AF]}, ZF: {flags[ZF]}, SF: {flags[SF]}, OF: {flags[OF]}]".format(flags=get_x86_eflags(self.ql.arch.regs.ef)), color.END, sep="")
print(color.GREEN, "EFLAGS: [CF: {flags[CF]}, PF: {flags[PF]}, AF: {flags[AF]}, ZF: {flags[ZF]}, SF: {flags[SF]}, OF: {flags[OF]}]".format(flags=get_x86_eflags(self.ql.arch.regs.eflags)), color.END, sep="")
@context_printer("[ DISASM ]", footer=True)
def context_asm(self):
+3 -1
View File
@@ -353,7 +353,9 @@ class QlQdb(cmd.Cmd, QlDebugger):
show some runtime information
"""
self.ql.mem.show_mapinfo()
for info_line in self.ql.mem.get_formatted_mapinfo():
self.ql.log.info(info_line)
qdb_print(QDB_MSG.INFO, f"Breakpoints: {[hex(addr) for addr in self.bp_list.keys()]}")
if self.rr:
qdb_print(QDB_MSG.INFO, f"Snapshots: {len([st for st in self.rr.layers if isinstance(st, self.rr.DiffedState)])}")
+1 -1
View File
@@ -22,7 +22,7 @@ class ContextRenderX86(ContextRender, ArchX86):
cur_regs = self.dump_regs()
diff_reg = self.reg_diff(cur_regs, saved_reg_dump)
self.render_regs_dump(cur_regs, diff_reg=diff_reg)
print(color.GREEN, "EFLAGS: [CF: {flags[CF]}, PF: {flags[PF]}, AF: {flags[AF]}, ZF: {flags[ZF]}, SF: {flags[SF]}, OF: {flags[OF]}]".format(flags=self.get_flags(self.ql.arch.regs.ef)), color.END, sep="")
print(color.GREEN, "EFLAGS: [CF: {flags[CF]}, PF: {flags[PF]}, AF: {flags[AF]}, ZF: {flags[ZF]}, SF: {flags[SF]}, OF: {flags[OF]}]".format(flags=self.get_flags(self.ql.arch.regs.eflags)), color.END, sep="")
@Render.divider_printer("[ DISASM ]")
def context_asm(self):
+3 -3
View File
@@ -333,7 +333,7 @@ class IDA:
@staticmethod
def get_ql_arch_string():
info = IDA.get_info_structure()
proc = info.get_procName().lower()
proc = info.procname.lower()
result = None
if proc == "metapc":
result = "x86"
@@ -805,7 +805,7 @@ class QlEmuMisc:
self.action_type = action
def activate(self, ctx):
if ctx.form_type == BWN_DISASM:
if ctx.widget_type == BWN_DISASM:
self.action_handler.ql_handle_menu_action(self.action_type)
return 1
@@ -878,7 +878,7 @@ class QlEmuQiling:
def __init__(self):
self.path = None
self.rootfs = None
self.ql: Qiling
self.ql: Qiling = None
self.status = None
self.exit_addr = None
self.baseaddr = None
+2 -2
View File
@@ -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:
+3 -2
View File
@@ -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
View File
@@ -29,7 +29,7 @@ from qiling.os.linux.kernel_api.kernel_api import hook_sys_open, hook_sys_read,
# auxiliary vector types
# see: https://man7.org/linux/man-pages/man3/getauxval.3.html
class AUX(IntEnum):
class AUXV(IntEnum):
AT_NULL = 0
AT_IGNORE = 1
AT_EXECFD = 2
@@ -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]")
+1
View File
@@ -45,6 +45,7 @@ LC_DYLD_INFO_ONLY = 0x80000022
LC_LOAD_DYLINKER = 0x0000000E
LC_MAIN = 0x80000028
LC_LOAD_DYLIB = 0x0000000C
LC_LOAD_WEAK_DYLIB = 0x80000018
LC_ENCRYPTION_INFO_64 = 0x0000002C
LC_BUILD_VERSION = 0x00000032
LC_DYLD_EXPORTS_TRIE = 0x80000033
@@ -39,6 +39,7 @@ class LoadCommand:
LC_LOAD_DYLINKER : LoadDylinker,
LC_MAIN : LoadMain,
LC_LOAD_DYLIB : LoadDyLib,
LC_LOAD_WEAK_DYLIB : LoadWeakDyLib,
LC_ENCRYPTION_INFO_64 : LoadEncryptionInfo64,
LC_DYLD_EXPORTS_TRIE : LoadDyldExportTrie,
LC_DYLD_CHAINED_FIXUPS : LoadDyldChainedFixups,
@@ -255,6 +256,11 @@ class LoadDyLib(LoadCommand):
pass
class LoadWeakDyLib(LoadDyLib):
def __init__(self, data):
super().__init__(data)
class LoadUnixThread(LoadCommand):
def __init__(self, data):
+14 -8
View File
@@ -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
View File
@@ -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]
+2 -2
View File
@@ -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
View File
@@ -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.
+7 -6
View File
@@ -10,24 +10,25 @@ from gevent.event import Event
from queue import Queue
class QlLinuxFutexManagement:
FUTEX_BITSET_MATCH_ANY = 0xffffffff
def __init__(self):
self._wait_list = {}
@property
def wait_list(self):
return self._wait_list
def futex_wait(self, ql, uaddr, t, val, bitset=FUTEX_BITSET_MATCH_ANY):
EAGAIN = 11
def _sched_wait_event(cur_thread):
ql.log.debug(f"Wait for notifications.")
event.wait()
uaddr_value = ql.unpack32(ql.mem.read(uaddr, 4))
if uaddr_value != val:
ql.log.debug(f"uaddr: {hex(uaddr_value)} != {hex(val)}")
return -1
return -EAGAIN
ql.emu_stop()
if uaddr not in self.wait_list.keys():
self.wait_list[uaddr] = Queue()
@@ -35,7 +36,7 @@ class QlLinuxFutexManagement:
self.wait_list[uaddr].put((bitset, t, event))
t.sched_cb = _sched_wait_event
return 0
def get_futex_wake_list(self, ql, addr, number, bitset=FUTEX_BITSET_MATCH_ANY):
wakes = []
if addr not in self.wait_list or number == 0:
+3 -5
View File
@@ -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
+13
View File
@@ -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
+119
View File
@@ -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
View File
@@ -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()
+2 -2
View File
@@ -188,5 +188,5 @@ def page_align_end(addr, page_size):
def set_eflags_cf(ql, target_cf):
ql.arch.regs.ef = ( ql.arch.regs.ef & 0xfffffffe ) | target_cf
return ql.arch.regs.ef
ql.arch.regs.eflags = ( ql.arch.regs.eflags & 0xfffffffe ) | target_cf
return ql.arch.regs.eflags
+6 -4
View File
@@ -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)
+1
View File
@@ -0,0 +1 @@
+74 -56
View File
@@ -4,12 +4,11 @@
#
import os, re
from typing import Any, Callable, List, MutableSequence, Optional, Sequence, Tuple
from typing import Any, Callable, Iterator, List, Mapping, MutableSequence, Optional, Pattern, Sequence, Tuple, Union
from unicorn import UC_PROT_NONE, UC_PROT_READ, UC_PROT_WRITE, UC_PROT_EXEC, UC_PROT_ALL
from qiling import Qiling
from qiling.const import *
from qiling.exception import *
# tuple: range start, range end, permissions mask, range label, is mmio?
@@ -36,7 +35,7 @@ class QlMemoryManager:
}
if ql.arch.bits not in bit_stuff:
raise QlErrorStructConversion("Unsupported Qiling archtecture for memory manager")
raise QlErrorStructConversion("Unsupported Qiling architecture for memory manager")
max_addr = bit_stuff[ql.arch.bits]
@@ -128,8 +127,8 @@ class QlMemoryManager:
self.map_info = tmp_map_info
def change_mapinfo(self, mem_s: int, mem_e: int, mem_p: int = None, mem_info: str = None):
tmp_map_info: MapInfoEntry = None
def change_mapinfo(self, mem_s: int, mem_e: int, mem_p: Optional[int] = None, mem_info: Optional[str] = None):
tmp_map_info: Optional[MapInfoEntry] = None
info_idx: int = None
for idx, map_info in enumerate(self.map_info):
@@ -150,12 +149,12 @@ class QlMemoryManager:
if mem_info is not None:
self.map_info[info_idx] = (tmp_map_info[0], tmp_map_info[1], tmp_map_info[2], mem_info, tmp_map_info[4])
def get_mapinfo(self) -> Sequence[Tuple[int, int, str, str, Optional[str]]]:
def get_mapinfo(self) -> Sequence[Tuple[int, int, str, str, str]]:
"""Get memory map info.
Returns: A sequence of 5-tuples representing the memory map entries. Each
tuple contains range start, range end, permissions, range label and path of
containing image (or None if not contained by any image)
containing image (or an empty string if not contained by any image)
"""
def __perms_mapping(ps: int) -> str:
@@ -167,21 +166,21 @@ class QlMemoryManager:
return ''.join(val if idx & ps else '-' for idx, val in perms_d.items())
def __process(lbound: int, ubound: int, perms: int, label: str, is_mmio: bool) -> Tuple[int, int, str, str, Optional[str]]:
def __process(lbound: int, ubound: int, perms: int, label: str, is_mmio: bool) -> Tuple[int, int, str, str, str]:
perms_str = __perms_mapping(perms)
if hasattr(self.ql, 'loader'):
image = self.ql.loader.find_containing_image(lbound)
container = image.path if image and not is_mmio else None
container = image.path if image and not is_mmio else ''
else:
container = None
container = ''
return (lbound, ubound, perms_str, label, container)
return tuple(__process(*entry) for entry in self.map_info)
def show_mapinfo(self):
"""Emit memory map info in a nicely formatted table.
def get_formatted_mapinfo(self) -> Sequence[str]:
"""Get memory map info in a nicely formatted table.
"""
mapinfo = self.get_mapinfo()
@@ -193,12 +192,17 @@ class QlMemoryManager:
len_addr = max(grouped[0])
len_label = max(grouped[1])
# emit title row
self.ql.log.info(f'{"Start":{len_addr}s} {"End":{len_addr}s} {"Perm":5s} {"Label":{len_label}s} {"Image"}')
# pre-allocate table
table = [''] * (len(mapinfo) + 1)
# emit table rows
for lbound, ubound, perms, label, container in mapinfo:
self.ql.log.info(f'{lbound:0{len_addr}x} - {ubound:0{len_addr}x} {perms:5s} {label:{len_label}s} {container or ""}')
# add title row
table[0] = f'{"Start":{len_addr}s} {"End":{len_addr}s} {"Perm":5s} {"Label":{len_label}s} {"Image"}'
# add table rows
for i, (lbound, ubound, perms, label, container) in enumerate(mapinfo, 1):
table[i] = f'{lbound:0{len_addr}x} - {ubound:0{len_addr}x} {perms:5s} {label:{len_label}s} {container}'
return table
# TODO: relying on the label string is risky; find a more reliable method
def get_lib_base(self, filename: str) -> Optional[int]:
@@ -211,7 +215,7 @@ class QlMemoryManager:
return next((lbound for lbound, info in stripped if os.path.basename(info) == filename), None)
def align(self, value: int, alignment: int = None) -> int:
def align(self, value: int, alignment: Optional[int] = None) -> int:
"""Align a value down to the specified alignment boundary. If `value` is already
aligned, the same value is returned. Commonly used to determine the base address
of the enclosing page.
@@ -233,7 +237,7 @@ class QlMemoryManager:
# round down to nearest alignment
return value & ~(alignment - 1)
def align_up(self, value: int, alignment: int = None) -> int:
def align_up(self, value: int, alignment: Optional[int] = None) -> int:
"""Align a value up to the specified alignment boundary. If `value` is already
aligned, the same value is returned. Commonly used to determine the end address
of the enlosing page.
@@ -281,7 +285,7 @@ class QlMemoryManager:
self.ql.log.debug(f'restoring memory range: {lbound:#08x} {ubound:#08x} {label}')
size = ubound - lbound
if not self.is_mapped(lbound, size):
if self.is_available(lbound, size):
self.ql.log.debug(f'mapping {lbound:#08x} {ubound:#08x}, mapsize = {size:#x}')
self.map(lbound, size, perms, label)
@@ -306,13 +310,13 @@ class QlMemoryManager:
return self.ql.uc.mem_read(addr, size)
def read_ptr(self, addr: int, size: int=None) -> int:
def read_ptr(self, addr: int, size: int = 0) -> int:
"""Read an integer value from a memory address.
Bytes read will be unpacked using emulated architecture properties.
Args:
addr: memory address to read
size: pointer size (in bytes): either 1, 2, 4, 8, or None for arch native size
size: pointer size (in bytes): either 1, 2, 4, 8, or 0 for arch native size
Returns: integer value stored at the specified memory address
"""
@@ -342,14 +346,14 @@ class QlMemoryManager:
self.ql.uc.mem_write(addr, data)
def write_ptr(self, addr: int, value: int, size: int=None) -> None:
def write_ptr(self, addr: int, value: int, size: int = 0) -> None:
"""Write an integer value to a memory address.
Bytes written will be packed using emulated architecture properties.
Args:
addr: target memory address
value: integer value to write
size: pointer size (in bytes): either 1, 2, 4, 8, or None for arch native size
size: pointer size (in bytes): either 1, 2, 4, 8, or 0 for arch native size
"""
if not size:
@@ -367,18 +371,18 @@ class QlMemoryManager:
self.write(addr, __pack(value))
def search(self, needle: bytes, begin: int = None, end: int = None) -> Sequence[int]:
def search(self, needle: Union[bytes, Pattern[bytes]], begin: Optional[int] = None, end: Optional[int] = None) -> Sequence[int]:
"""Search for a sequence of bytes in memory.
Args:
needle: bytes sequence to look for
needle: bytes sequence or regex pattern to look for
begin: search starting address (or None to start at lowest avaiable address)
end: search ending address (or None to end at highest avaiable address)
Returns: addresses of all matches
"""
# if starting point not set, search from the first mapped region
# if starting point not set, search from the first mapped region
if begin is None:
begin = self.map_info[0][0]
@@ -392,6 +396,10 @@ class QlMemoryManager:
ranges = [(max(begin, lbound), min(ubound, end)) for lbound, ubound, _, _, is_mmio in self.map_info if not (end < lbound or ubound < begin or is_mmio)]
results = []
# if needle is a bytes sequence use it verbatim, not as a pattern
if type(needle) is bytes:
needle = re.escape(needle)
for lbound, ubound in ranges:
haystack = self.read(lbound, ubound - lbound)
local_results = (match.start(0) + lbound for match in re.finditer(needle, haystack))
@@ -414,17 +422,41 @@ class QlMemoryManager:
if (addr, addr + size) in self.mmio_cbs:
del self.mmio_cbs[(addr, addr+size)]
def unmap_all(self):
def unmap_all(self) -> None:
"""Reclaim the entire memory space.
"""
for begin, end, _ in self.ql.uc.mem_regions():
if begin and end:
self.unmap(begin, end - begin + 1)
self.unmap(begin, end - begin + 1)
def __mapped_regions(self) -> Iterator[Tuple[int, int]]:
"""Iterate through all mapped memory regions, consolidating adjacent regions
together to a continuous one. Protection bits and labels are ignored.
"""
if not self.map_info:
return
iter_memmap = iter(self.map_info)
p_lbound, p_ubound, _, _, _ = next(iter_memmap)
# map_info is assumed to contain non-overlapping regions sorted by lbound
for lbound, ubound, _, _, _ in iter_memmap:
if lbound == p_ubound:
p_ubound = ubound
else:
yield (p_lbound, p_ubound)
p_lbound = lbound
p_ubound = ubound
yield (p_lbound, p_ubound)
def is_available(self, addr: int, size: int) -> bool:
"""Query whether the memory range starting at `addr` and is of length of `size` bytes
can be allocated.
is available for allocation.
Returns: True if it can be allocated, False otherwise
"""
@@ -435,37 +467,23 @@ class QlMemoryManager:
end = addr + size
# make sure neither begin nor end are enclosed within a mapped range, or entirely enclosing one
return not any((lbound <= begin < ubound) or (lbound < end <= ubound) or (begin <= lbound < ubound <= end) for lbound, ubound, _, _, _ in self.map_info)
return not any((lbound <= begin < ubound) or (lbound < end <= ubound) or (begin <= lbound < ubound <= end) for lbound, ubound in self.__mapped_regions())
def is_mapped(self, addr: int, size: int) -> bool:
"""Query whether the memory range starting at `addr` and is of length of `size` bytes
is mapped, either partially or entirely.
is fully mapped.
Returns: True if any part of the specified memory range is taken, False otherwise
Returns: True if the specified memory range is taken fully, False otherwise
"""
return not self.is_available(addr, size)
assert size > 0, 'expected a positive size value'
def is_free(self, address, size):
'''
The main function of is_free first must fufull is_mapped condition.
then, check for is the mapped range empty, either fill with 0xFF or 0x00
Returns true if mapped range is empty else return Flase
If not not mapped, map it and return true
'''
if self.is_mapped(address, size) == True:
address_end = (address + size)
while address < address_end:
mem_read = self.ql.mem.read(address, 0x1)
if (mem_read[0] != 0x00) and (mem_read[0] != 0xFF):
return False
address += 1
return True
else:
return True
begin = addr
end = addr + size
return any((lbound <= begin < end <= ubound) for lbound, ubound in self.__mapped_regions())
def find_free_space(self, size: int, minaddr: int = None, maxaddr: int = None, align: int = None) -> int:
def find_free_space(self, size: int, minaddr: Optional[int] = None, maxaddr: Optional[int] = None, align: Optional[int] = None) -> int:
"""Locate an unallocated memory that is large enough to contain a range in size of
`size` and based at `minaddr`.
@@ -510,7 +528,7 @@ class QlMemoryManager:
raise QlOutOfMemory('Out Of Memory')
def map_anywhere(self, size: int, minaddr: int = None, maxaddr: int = None, align: int = None, perms: int = UC_PROT_ALL, info: str = None) -> int:
def map_anywhere(self, size: int, minaddr: Optional[int] = None, maxaddr: Optional[int] = None, align: Optional[int] = None, perms: int = UC_PROT_ALL, info: Optional[str] = None) -> int:
"""Map a region anywhere in memory.
Args:
@@ -545,7 +563,7 @@ class QlMemoryManager:
self.change_mapinfo(aligned_address, aligned_address + aligned_size, mem_p = perms)
def map(self, addr: int, size: int, perms: int = UC_PROT_ALL, info: str = None):
def map(self, addr: int, size: int, perms: int = UC_PROT_ALL, info: Optional[str] = None):
"""Map a new memory range.
Args:
@@ -724,7 +742,7 @@ class QlMemoryHeap:
self.current_alloc = 0
self.current_use = 0
def _find(self, addr: int, inuse: bool = None) -> Optional[Chunk]:
def _find(self, addr: int, inuse: Optional[bool] = None) -> Optional[Chunk]:
"""Find a chunk starting at a specified address.
Args:
+3 -2
View File
@@ -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
View File
@@ -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.
+4 -3
View File
@@ -3,11 +3,12 @@
# Cross Platform and Multi Architecture Advanced Binary Emulation Framework
#
from typing import MutableSequence
from .const import *
from typing import Mapping, MutableSequence
from qiling import Qiling
from qiling.const import *
from qiling.const import QL_ARCH, QL_OS
from .const import *
def _invert_dict(d: Mapping) -> Mapping:
return { v:k for k, v in d.items()}
+98 -96
View File
@@ -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)
+1 -1
View File
@@ -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]):
+2 -1
View File
@@ -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 *
+6 -4
View File
@@ -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
+14
View File
@@ -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
+25 -12
View File
@@ -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
-1
View File
@@ -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:
+65 -58
View File
@@ -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))
+19 -6
View File
@@ -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):
+65 -41
View File
@@ -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
View File
@@ -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
View File
@@ -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.
+4 -4
View File
@@ -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)
+3 -1
View File
@@ -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
View File
@@ -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
+1
View File
@@ -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
+30 -14
View File
@@ -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
View File
@@ -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
+52 -39
View File
@@ -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,
+24 -45
View File
@@ -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
+6 -6
View File
@@ -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
+37 -54
View File
@@ -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)
+122 -12
View File
@@ -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"]
+31 -28
View File
@@ -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,
+15 -3
View File
@@ -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