mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2025-10-04 10:19:23 +02:00
GP-4209: GhidraTime-MSTTD integration. Type hints for (most) Python agents.
This commit is contained in:
parent
deb49d5322
commit
21a1602579
93 changed files with 6453 additions and 4118 deletions
|
@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|||
|
||||
[project]
|
||||
name = "ghidradbg"
|
||||
version = "11.3"
|
||||
version = "11.4"
|
||||
authors = [
|
||||
{ name="Ghidra Development Team" },
|
||||
]
|
||||
|
@ -17,7 +17,7 @@ classifiers = [
|
|||
"Operating System :: OS Independent",
|
||||
]
|
||||
dependencies = [
|
||||
"ghidratrace==11.3",
|
||||
"ghidratrace==11.4",
|
||||
"pybag>=2.2.12"
|
||||
]
|
||||
|
||||
|
@ -26,7 +26,7 @@ dependencies = [
|
|||
"Bug Tracker" = "https://github.com/NationalSecurityAgency/ghidra/issues"
|
||||
|
||||
[tool.setuptools.package-data]
|
||||
ghidradbg = ["*.tlb"]
|
||||
ghidradbg = ["*.tlb", "py.typed"]
|
||||
|
||||
[tool.setuptools]
|
||||
include-package-data = true
|
||||
|
|
|
@ -1,17 +1,17 @@
|
|||
## ###
|
||||
# IP: GHIDRA
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
# IP: GHIDRA
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
##
|
||||
|
||||
# NOTE: libraries must precede EVERYTHING, esp pybag and DbgMod
|
||||
|
|
|
@ -13,13 +13,15 @@
|
|||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
##
|
||||
from typing import Dict, List, Optional, Tuple
|
||||
|
||||
from ghidratrace.client import Address, RegVal
|
||||
from pybag import pydbg
|
||||
|
||||
from . import util
|
||||
|
||||
|
||||
language_map = {
|
||||
language_map: Dict[str, List[str]] = {
|
||||
'AARCH64': ['AARCH64:LE:64:AppleSilicon'],
|
||||
'ARM': ['ARM:LE:32:v8'],
|
||||
'Itanium': [],
|
||||
|
@ -31,25 +33,25 @@ language_map = {
|
|||
'SH4': ['SuperH4:LE:32:default'],
|
||||
}
|
||||
|
||||
data64_compiler_map = {
|
||||
data64_compiler_map: Dict[Optional[str], str] = {
|
||||
None: 'pointer64',
|
||||
}
|
||||
|
||||
x86_compiler_map = {
|
||||
x86_compiler_map: Dict[Optional[str], str] = {
|
||||
'windows': 'windows',
|
||||
'Cygwin': 'windows',
|
||||
'default': 'windows',
|
||||
}
|
||||
|
||||
default_compiler_map = {
|
||||
default_compiler_map: Dict[Optional[str], str] = {
|
||||
'windows': 'default',
|
||||
}
|
||||
|
||||
windows_compiler_map = {
|
||||
windows_compiler_map: Dict[Optional[str], str] = {
|
||||
'windows': 'windows',
|
||||
}
|
||||
|
||||
compiler_map = {
|
||||
compiler_map : Dict[str, Dict[Optional[str], str]]= {
|
||||
'DATA:BE:64:default': data64_compiler_map,
|
||||
'DATA:LE:64:default': data64_compiler_map,
|
||||
'x86:LE:32:default': x86_compiler_map,
|
||||
|
@ -62,11 +64,11 @@ compiler_map = {
|
|||
}
|
||||
|
||||
|
||||
def get_arch():
|
||||
def get_arch() -> str:
|
||||
try:
|
||||
type = util.dbg.get_actual_processor_type()
|
||||
except Exception:
|
||||
print("Error getting actual processor type.")
|
||||
except Exception as e:
|
||||
print(f"Error getting actual processor type: {e}")
|
||||
return "Unknown"
|
||||
if type is None:
|
||||
return "x86_64"
|
||||
|
@ -76,25 +78,25 @@ def get_arch():
|
|||
return "AARCH64"
|
||||
if type == 0x014c:
|
||||
return "x86"
|
||||
if type == 0x0160: # R3000 BE
|
||||
if type == 0x0160: # R3000 BE
|
||||
return "MIPS-BE"
|
||||
if type == 0x0162: # R3000 LE
|
||||
if type == 0x0162: # R3000 LE
|
||||
return "MIPS"
|
||||
if type == 0x0166: # R4000 LE
|
||||
if type == 0x0166: # R4000 LE
|
||||
return "MIPS"
|
||||
if type == 0x0168: # R10000 LE
|
||||
if type == 0x0168: # R10000 LE
|
||||
return "MIPS"
|
||||
if type == 0x0169: # WCE v2 LE
|
||||
if type == 0x0169: # WCE v2 LE
|
||||
return "MIPS"
|
||||
if type == 0x0266: # MIPS 16
|
||||
if type == 0x0266: # MIPS 16
|
||||
return "MIPS"
|
||||
if type == 0x0366: # MIPS FPU
|
||||
if type == 0x0366: # MIPS FPU
|
||||
return "MIPS"
|
||||
if type == 0x0466: # MIPS FPU16
|
||||
if type == 0x0466: # MIPS FPU16
|
||||
return "MIPS"
|
||||
if type == 0x0184: # Alpha AXP
|
||||
if type == 0x0184: # Alpha AXP
|
||||
return "Alpha"
|
||||
if type == 0x0284: # Aplha 64
|
||||
if type == 0x0284: # Aplha 64
|
||||
return "Alpha"
|
||||
if type >= 0x01a2 and type < 0x01a6:
|
||||
return "SH"
|
||||
|
@ -102,17 +104,17 @@ def get_arch():
|
|||
return "SH4"
|
||||
if type == 0x01a6:
|
||||
return "SH5"
|
||||
if type == 0x01c0: # ARM LE
|
||||
if type == 0x01c0: # ARM LE
|
||||
return "ARM"
|
||||
if type == 0x01c2: # ARM Thumb/Thumb-2 LE
|
||||
if type == 0x01c2: # ARM Thumb/Thumb-2 LE
|
||||
return "ARM"
|
||||
if type == 0x01c4: # ARM Thumb-2 LE
|
||||
if type == 0x01c4: # ARM Thumb-2 LE
|
||||
return "ARM"
|
||||
if type == 0x01d3: # AM33
|
||||
if type == 0x01d3: # AM33
|
||||
return "ARM"
|
||||
if type == 0x01f0 or type == 0x1f1: # PPC
|
||||
if type == 0x01f0 or type == 0x1f1: # PPC
|
||||
return "PPC"
|
||||
if type == 0x0200:
|
||||
if type == 0x0200:
|
||||
return "Itanium"
|
||||
if type == 0x0520:
|
||||
return "Infineon"
|
||||
|
@ -120,23 +122,23 @@ def get_arch():
|
|||
return "CEF"
|
||||
if type == 0x0EBC:
|
||||
return "EFI"
|
||||
if type == 0x8664: # AMD64 (K8)
|
||||
if type == 0x8664: # AMD64 (K8)
|
||||
return "x86_64"
|
||||
if type == 0x9041: # M32R
|
||||
if type == 0x9041: # M32R
|
||||
return "M32R"
|
||||
if type == 0xC0EE:
|
||||
return "CEE"
|
||||
return "Unknown"
|
||||
|
||||
|
||||
def get_endian():
|
||||
def get_endian() -> str:
|
||||
parm = util.get_convenience_variable('endian')
|
||||
if parm != 'auto':
|
||||
return parm
|
||||
return 'little'
|
||||
|
||||
|
||||
def get_osabi():
|
||||
def get_osabi() -> str:
|
||||
parm = util.get_convenience_variable('osabi')
|
||||
if not parm in ['auto', 'default']:
|
||||
return parm
|
||||
|
@ -150,7 +152,7 @@ def get_osabi():
|
|||
return "windows"
|
||||
|
||||
|
||||
def compute_ghidra_language():
|
||||
def compute_ghidra_language() -> str:
|
||||
# First, check if the parameter is set
|
||||
lang = util.get_convenience_variable('ghidra-language')
|
||||
if lang != 'auto':
|
||||
|
@ -175,7 +177,7 @@ def compute_ghidra_language():
|
|||
return 'DATA' + lebe + '64:default'
|
||||
|
||||
|
||||
def compute_ghidra_compiler(lang):
|
||||
def compute_ghidra_compiler(lang: str) -> str:
|
||||
# First, check if the parameter is set
|
||||
comp = util.get_convenience_variable('ghidra-compiler')
|
||||
if comp != 'auto':
|
||||
|
@ -197,7 +199,7 @@ def compute_ghidra_compiler(lang):
|
|||
return 'default'
|
||||
|
||||
|
||||
def compute_ghidra_lcsp():
|
||||
def compute_ghidra_lcsp() -> Tuple[str, str]:
|
||||
lang = compute_ghidra_language()
|
||||
comp = compute_ghidra_compiler(lang)
|
||||
return lang, comp
|
||||
|
@ -205,10 +207,10 @@ def compute_ghidra_lcsp():
|
|||
|
||||
class DefaultMemoryMapper(object):
|
||||
|
||||
def __init__(self, defaultSpace):
|
||||
def __init__(self, defaultSpace: str) -> None:
|
||||
self.defaultSpace = defaultSpace
|
||||
|
||||
def map(self, proc: int, offset: int):
|
||||
def map(self, proc: int, offset: int) -> Tuple[str, Address]:
|
||||
space = self.defaultSpace
|
||||
return self.defaultSpace, Address(space, offset)
|
||||
|
||||
|
@ -220,10 +222,10 @@ class DefaultMemoryMapper(object):
|
|||
|
||||
DEFAULT_MEMORY_MAPPER = DefaultMemoryMapper('ram')
|
||||
|
||||
memory_mappers = {}
|
||||
memory_mappers: Dict[str, DefaultMemoryMapper] = {}
|
||||
|
||||
|
||||
def compute_memory_mapper(lang):
|
||||
def compute_memory_mapper(lang: str) -> DefaultMemoryMapper:
|
||||
if not lang in memory_mappers:
|
||||
return DEFAULT_MEMORY_MAPPER
|
||||
return memory_mappers[lang]
|
||||
|
@ -231,16 +233,15 @@ def compute_memory_mapper(lang):
|
|||
|
||||
class DefaultRegisterMapper(object):
|
||||
|
||||
def __init__(self, byte_order):
|
||||
def __init__(self, byte_order: str) -> None:
|
||||
if not byte_order in ['big', 'little']:
|
||||
raise ValueError("Invalid byte_order: {}".format(byte_order))
|
||||
self.byte_order = byte_order
|
||||
self.union_winners = {}
|
||||
|
||||
def map_name(self, proc, name):
|
||||
def map_name(self, proc: int, name: str):
|
||||
return name
|
||||
|
||||
def map_value(self, proc, name, value):
|
||||
def map_value(self, proc: int, name: str, value: int):
|
||||
try:
|
||||
# TODO: this seems half-baked
|
||||
av = value.to_bytes(8, "big")
|
||||
|
@ -249,10 +250,10 @@ class DefaultRegisterMapper(object):
|
|||
.format(name, value, type(value)))
|
||||
return RegVal(self.map_name(proc, name), av)
|
||||
|
||||
def map_name_back(self, proc, name):
|
||||
def map_name_back(self, proc: int, name: str) -> str:
|
||||
return name
|
||||
|
||||
def map_value_back(self, proc, name, value):
|
||||
def map_value_back(self, proc: int, name: str, value: bytes):
|
||||
return RegVal(self.map_name_back(proc, name), value)
|
||||
|
||||
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -1,17 +1,17 @@
|
|||
## ###
|
||||
# IP: GHIDRA
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
# IP: GHIDRA
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
##
|
||||
import os
|
||||
|
||||
|
|
|
@ -1,17 +1,17 @@
|
|||
## ###
|
||||
# IP: GHIDRA
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
# IP: GHIDRA
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
##
|
||||
from ctypes import *
|
||||
|
||||
|
|
|
@ -1,17 +1,17 @@
|
|||
## ###
|
||||
# IP: GHIDRA
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
# IP: GHIDRA
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
##
|
||||
from ctypes import *
|
||||
|
||||
|
|
|
@ -1,17 +1,17 @@
|
|||
## ###
|
||||
# IP: GHIDRA
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
# IP: GHIDRA
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
##
|
||||
from ctypes import *
|
||||
|
||||
|
|
|
@ -1,17 +1,17 @@
|
|||
## ###
|
||||
# IP: GHIDRA
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
# IP: GHIDRA
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
##
|
||||
from ctypes import *
|
||||
|
||||
|
|
|
@ -44,7 +44,7 @@ class ModelIterator(object):
|
|||
next = (self._index, mo.ModelObject(object))
|
||||
self._index += 1
|
||||
return next
|
||||
|
||||
|
||||
index = mo.ModelObject(indexer)
|
||||
ival = index.GetIntrinsicValue()
|
||||
if ival is None:
|
||||
|
|
|
@ -37,9 +37,8 @@ class ModelMethod(object):
|
|||
metadata = POINTER(DbgMod.IKeyStore)()
|
||||
try:
|
||||
self._method.Call(byref(object), argcount, byref(arguments),
|
||||
byref(result), byref(metadata))
|
||||
byref(result), byref(metadata))
|
||||
except COMError as ce:
|
||||
return None
|
||||
|
||||
return mo.ModelObject(result)
|
||||
|
||||
|
|
|
@ -1,17 +1,17 @@
|
|||
## ###
|
||||
# IP: GHIDRA
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
# IP: GHIDRA
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
##
|
||||
from ctypes import *
|
||||
from enum import Enum
|
||||
|
@ -221,10 +221,10 @@ class ModelObject(object):
|
|||
return None
|
||||
self.dconcept = StringDisplayableConcept(dconcept)
|
||||
return self.dconcept.ToDisplayString(self)
|
||||
|
||||
|
||||
# This does NOT work - returns a null pointer for value. Why?
|
||||
# One possibility: casting is not a valid way to obtain an IModelMethod
|
||||
#
|
||||
#
|
||||
# def ToDisplayString0(self):
|
||||
# map = self.GetAttributes()
|
||||
# method = map["ToDisplayString"]
|
||||
|
@ -338,11 +338,10 @@ class ModelObject(object):
|
|||
next = map[element]
|
||||
else:
|
||||
next = next.GetKeyValue(element)
|
||||
#if next is None:
|
||||
# if next is None:
|
||||
# print(f"{element} not found")
|
||||
return next
|
||||
|
||||
|
||||
def GetValue(self):
|
||||
value = self.GetIntrinsicValue()
|
||||
if value is None:
|
||||
|
@ -350,4 +349,3 @@ class ModelObject(object):
|
|||
if value.vt == 0xd:
|
||||
return None
|
||||
return value.value
|
||||
|
||||
|
|
|
@ -1,17 +1,17 @@
|
|||
## ###
|
||||
# IP: GHIDRA
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
# IP: GHIDRA
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
##
|
||||
from ctypes import *
|
||||
|
||||
|
|
|
@ -13,12 +13,12 @@
|
|||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
##
|
||||
from ghidradbg import arch, commands, util
|
||||
from ghidratrace import sch
|
||||
from ghidratrace.client import Client, Address, AddressRange, TraceObject
|
||||
from ghidratrace.client import Client, Address, AddressRange, Trace, TraceObject
|
||||
|
||||
PAGE_SIZE = 4096
|
||||
|
||||
from ghidradbg import arch, commands, util
|
||||
|
||||
SESSION_PATH = 'Sessions[0]'
|
||||
PROCESSES_PATH = SESSION_PATH + '.ExdiProcesses'
|
||||
|
@ -42,106 +42,97 @@ SECTIONS_ADD_PATTERN = '.Sections'
|
|||
SECTION_KEY_PATTERN = '[{secname}]'
|
||||
SECTION_ADD_PATTERN = SECTIONS_ADD_PATTERN + SECTION_KEY_PATTERN
|
||||
|
||||
|
||||
@util.dbg.eng_thread
|
||||
def ghidra_trace_put_processes_exdi():
|
||||
"""
|
||||
Put the list of processes into the trace's processes list.
|
||||
"""
|
||||
def ghidra_trace_put_processes_exdi() -> None:
|
||||
"""Put the list of processes into the trace's processes list."""
|
||||
|
||||
radix = util.get_convenience_variable('output-radix')
|
||||
commands.STATE.require_tx()
|
||||
with commands.STATE.client.batch() as b:
|
||||
put_processes_exdi(commands.STATE, radix)
|
||||
trace, tx = commands.STATE.require_tx()
|
||||
with trace.client.batch() as b:
|
||||
put_processes_exdi(trace, radix)
|
||||
|
||||
|
||||
@util.dbg.eng_thread
|
||||
def ghidra_trace_put_regions_exdi():
|
||||
"""
|
||||
Read the memory map, if applicable, and write to the trace's Regions
|
||||
"""
|
||||
def ghidra_trace_put_regions_exdi() -> None:
|
||||
"""Read the memory map, if applicable, and write to the trace's Regions."""
|
||||
|
||||
commands.STATE.require_tx()
|
||||
with commands.STATE.client.batch() as b:
|
||||
put_regions_exdi(commands.STATE)
|
||||
trace, tx = commands.STATE.require_tx()
|
||||
with trace.client.batch() as b:
|
||||
put_regions_exdi(trace)
|
||||
|
||||
|
||||
@util.dbg.eng_thread
|
||||
def ghidra_trace_put_kmodules_exdi():
|
||||
"""
|
||||
Gather object files, if applicable, and write to the trace's Modules
|
||||
"""
|
||||
def ghidra_trace_put_kmodules_exdi() -> None:
|
||||
"""Gather object files, if applicable, and write to the trace's Modules."""
|
||||
|
||||
commands.STATE.require_tx()
|
||||
with commands.STATE.client.batch() as b:
|
||||
put_kmodules_exdi(commands.STATE)
|
||||
trace, tx = commands.STATE.require_tx()
|
||||
with trace.client.batch() as b:
|
||||
put_kmodules_exdi(trace)
|
||||
|
||||
|
||||
@util.dbg.eng_thread
|
||||
def ghidra_trace_put_threads_exdi(pid):
|
||||
"""
|
||||
Put the current process's threads into the Ghidra trace
|
||||
"""
|
||||
def ghidra_trace_put_threads_exdi(pid: int) -> None:
|
||||
"""Put the current process's threads into the Ghidra trace."""
|
||||
|
||||
radix = util.get_convenience_variable('output-radix')
|
||||
commands.STATE.require_tx()
|
||||
with commands.STATE.client.batch() as b:
|
||||
put_threads_exdi(commands.STATE, pid, radix)
|
||||
trace, tx = commands.STATE.require_tx()
|
||||
with trace.client.batch() as b:
|
||||
put_threads_exdi(trace, pid, radix)
|
||||
|
||||
|
||||
@util.dbg.eng_thread
|
||||
def ghidra_trace_put_all_exdi():
|
||||
"""
|
||||
Put everything currently selected into the Ghidra trace
|
||||
"""
|
||||
def ghidra_trace_put_all_exdi() -> None:
|
||||
"""Put everything currently selected into the Ghidra trace."""
|
||||
|
||||
radix = util.get_convenience_variable('output-radix')
|
||||
commands.STATE.require_tx()
|
||||
with commands.STATE.client.batch() as b:
|
||||
trace, tx = commands.STATE.require_tx()
|
||||
with trace.client.batch() as b:
|
||||
if util.dbg.use_generics == False:
|
||||
put_processes_exdi(commands.STATE, radix)
|
||||
put_regions_exdi(commands.STATE)
|
||||
put_kmodules_exdi(commands.STATE)
|
||||
put_processes_exdi(trace, radix)
|
||||
put_regions_exdi(trace)
|
||||
put_kmodules_exdi(trace)
|
||||
|
||||
|
||||
@util.dbg.eng_thread
|
||||
def put_processes_exdi(state, radix):
|
||||
def put_processes_exdi(trace: Trace, radix: int) -> None:
|
||||
radix = util.get_convenience_variable('output-radix')
|
||||
keys = []
|
||||
result = util.dbg._base.cmd("!process 0 0")
|
||||
lines = list(x for x in result.splitlines() if "DeepFreeze" not in x)
|
||||
count = int((len(lines)-2)/5)
|
||||
for i in range(0,count):
|
||||
l1 = lines[i*5+1].strip().split() # PROCESS
|
||||
l2 = lines[i*5+2].strip().split() # SessionId, Cid, Peb: ParentId
|
||||
l3 = lines[i*5+3].strip().split() # DirBase, ObjectTable, HandleCount
|
||||
l4 = lines[i*5+4].strip().split() # Image
|
||||
for i in range(0, count):
|
||||
l1 = lines[i*5+1].strip().split() # PROCESS
|
||||
l2 = lines[i*5+2].strip().split() # SessionId, Cid, Peb: ParentId
|
||||
l3 = lines[i*5+3].strip().split() # DirBase, ObjectTable, HandleCount
|
||||
l4 = lines[i*5+4].strip().split() # Image
|
||||
id = int(l2[3], 16)
|
||||
name = l4[1]
|
||||
ppath = PROCESS_PATTERN.format(pid=id)
|
||||
procobj = state.trace.create_object(ppath)
|
||||
procobj = trace.create_object(ppath)
|
||||
keys.append(PROCESS_KEY_PATTERN.format(pid=id))
|
||||
pidstr = ('0x{:x}' if radix ==
|
||||
pidstr = ('0x{:x}' if radix ==
|
||||
16 else '0{:o}' if radix == 8 else '{}').format(id)
|
||||
procobj.set_value('PID', id)
|
||||
procobj.set_value('Name', name)
|
||||
procobj.set_value('_display', '[{}] {}'.format(pidstr, name))
|
||||
(base, addr) = commands.map_address(int(l1[1],16))
|
||||
procobj.set_value('EPROCESS', addr, schema="ADDRESS")
|
||||
(base, addr) = commands.map_address(int(l2[5],16))
|
||||
procobj.set_value('PEB', addr, schema="ADDRESS")
|
||||
(base, addr) = commands.map_address(int(l3[1],16))
|
||||
procobj.set_value('DirBase', addr, schema="ADDRESS")
|
||||
(base, addr) = commands.map_address(int(l3[3],16))
|
||||
procobj.set_value('ObjectTable', addr, schema="ADDRESS")
|
||||
#procobj.set_value('ObjectTable', l3[3])
|
||||
tcobj = state.trace.create_object(ppath+".Threads")
|
||||
(base, addr) = commands.map_address(int(l1[1], 16))
|
||||
procobj.set_value('EPROCESS', addr, schema=sch.ADDRESS)
|
||||
(base, addr) = commands.map_address(int(l2[5], 16))
|
||||
procobj.set_value('PEB', addr, schema=sch.ADDRESS)
|
||||
(base, addr) = commands.map_address(int(l3[1], 16))
|
||||
procobj.set_value('DirBase', addr, schema=sch.ADDRESS)
|
||||
(base, addr) = commands.map_address(int(l3[3], 16))
|
||||
procobj.set_value('ObjectTable', addr, schema=sch.ADDRESS)
|
||||
# procobj.set_value('ObjectTable', l3[3])
|
||||
tcobj = trace.create_object(ppath+".Threads")
|
||||
procobj.insert()
|
||||
tcobj.insert()
|
||||
state.trace.proxy_object_path(PROCESSES_PATH).retain_values(keys)
|
||||
trace.proxy_object_path(PROCESSES_PATH).retain_values(keys)
|
||||
|
||||
|
||||
@util.dbg.eng_thread
|
||||
def put_regions_exdi(state):
|
||||
def put_regions_exdi(trace: Trace) -> None:
|
||||
radix = util.get_convenience_variable('output-radix')
|
||||
keys = []
|
||||
result = util.dbg._base.cmd("!address")
|
||||
|
@ -153,33 +144,33 @@ def put_regions_exdi(state):
|
|||
continue
|
||||
if init == False:
|
||||
continue
|
||||
fields = l.strip().replace('`','').split() # PROCESS
|
||||
fields = l.strip().replace('`', '').split() # PROCESS
|
||||
if len(fields) < 4:
|
||||
continue
|
||||
start = fields[0]
|
||||
#finish = fields[1]
|
||||
# finish = fields[1]
|
||||
length = fields[2]
|
||||
type = fields[3]
|
||||
(sbase, saddr) = commands.map_address(int(start,16))
|
||||
#(fbase, faddr) = commands.map_address(int(finish,16))
|
||||
rng = saddr.extend(int(length,16))
|
||||
(sbase, saddr) = commands.map_address(int(start, 16))
|
||||
# (fbase, faddr) = commands.map_address(int(finish,16))
|
||||
rng = saddr.extend(int(length, 16))
|
||||
rpath = REGION_PATTERN.format(start=start)
|
||||
keys.append(REGION_KEY_PATTERN.format(start=start))
|
||||
regobj = state.trace.create_object(rpath)
|
||||
regobj.set_value('Range', rng, schema="RANGE")
|
||||
regobj = trace.create_object(rpath)
|
||||
regobj.set_value('Range', rng, schema=sch.RANGE)
|
||||
regobj.set_value('Size', length)
|
||||
regobj.set_value('Type', type)
|
||||
regobj.set_value('_readable', True)
|
||||
regobj.set_value('_writable', True)
|
||||
regobj.set_value('_executable', True)
|
||||
regobj.set_value('_display', '[{}] {}'.format(
|
||||
start, type))
|
||||
start, type))
|
||||
regobj.insert()
|
||||
state.trace.proxy_object_path(MEMORY_PATH).retain_values(keys)
|
||||
trace.proxy_object_path(MEMORY_PATH).retain_values(keys)
|
||||
|
||||
|
||||
@util.dbg.eng_thread
|
||||
def put_kmodules_exdi(state):
|
||||
def put_kmodules_exdi(trace: Trace) -> None:
|
||||
radix = util.get_convenience_variable('output-radix')
|
||||
keys = []
|
||||
result = util.dbg._base.cmd("lm")
|
||||
|
@ -190,32 +181,33 @@ def put_kmodules_exdi(state):
|
|||
continue
|
||||
if "Unloaded" in l:
|
||||
continue
|
||||
fields = l.strip().replace('`','').split()
|
||||
fields = l.strip().replace('`', '').split()
|
||||
if len(fields) < 3:
|
||||
continue
|
||||
start = fields[0]
|
||||
finish = fields[1]
|
||||
name = fields[2]
|
||||
sname = name.replace('.sys','').replace('.dll','')
|
||||
(sbase, saddr) = commands.map_address(int(start,16))
|
||||
(fbase, faddr) = commands.map_address(int(finish,16))
|
||||
sz = faddr.offset - saddr.offset
|
||||
sname = name.replace('.sys', '').replace('.dll', '')
|
||||
(sbase, saddr) = commands.map_address(int(start, 16))
|
||||
(fbase, faddr) = commands.map_address(int(finish, 16))
|
||||
sz = faddr.offset - saddr.offset
|
||||
rng = saddr.extend(sz)
|
||||
mpath = KMODULE_PATTERN.format(modpath=sname)
|
||||
keys.append(KMODULE_KEY_PATTERN.format(modpath=sname))
|
||||
modobj = commands.STATE.trace.create_object(mpath)
|
||||
modobj = trace.create_object(mpath)
|
||||
modobj.set_value('Name', name)
|
||||
modobj.set_value('Base', saddr, schema="ADDRESS")
|
||||
modobj.set_value('Range', rng, schema="RANGE")
|
||||
modobj.set_value('Base', saddr, schema=sch.ADDRESS)
|
||||
modobj.set_value('Range', rng, schema=sch.RANGE)
|
||||
modobj.set_value('Size', hex(sz))
|
||||
modobj.insert()
|
||||
state.trace.proxy_object_path(KMODULES_PATH).retain_values(keys)
|
||||
trace.proxy_object_path(KMODULES_PATH).retain_values(keys)
|
||||
|
||||
|
||||
@util.dbg.eng_thread
|
||||
def put_threads_exdi(state, pid, radix):
|
||||
def put_threads_exdi(trace: Trace, pid: int, radix: int) -> None:
|
||||
radix = util.get_convenience_variable('output-radix')
|
||||
pidstr = ('0x{:x}' if radix == 16 else '0{:o}' if radix == 8 else '{}').format(pid)
|
||||
pidstr = ('0x{:x}' if radix == 16 else '0{:o}' if radix ==
|
||||
8 else '{}').format(pid)
|
||||
keys = []
|
||||
result = util.dbg._base.cmd("!process "+hex(pid)+" 4")
|
||||
lines = result.split("\n")
|
||||
|
@ -223,15 +215,15 @@ def put_threads_exdi(state, pid, radix):
|
|||
l = l.strip()
|
||||
if "THREAD" not in l:
|
||||
continue
|
||||
fields = l.split()
|
||||
cid = fields[3] # pid.tid (decimal)
|
||||
tid = int(cid.split('.')[1],16)
|
||||
fields = l.split()
|
||||
cid = fields[3] # pid.tid (decimal)
|
||||
tid = int(cid.split('.')[1], 16)
|
||||
tidstr = ('0x{:x}' if radix ==
|
||||
16 else '0{:o}' if radix == 8 else '{}').format(tid)
|
||||
tpath = THREAD_PATTERN.format(pid=pid, tnum=tid)
|
||||
tobj = commands.STATE.trace.create_object(tpath)
|
||||
tobj = trace.create_object(tpath)
|
||||
keys.append(THREAD_KEY_PATTERN.format(tnum=tidstr))
|
||||
tobj = state.trace.create_object(tpath)
|
||||
tobj = trace.create_object(tpath)
|
||||
tobj.set_value('PID', pidstr)
|
||||
tobj.set_value('TID', tidstr)
|
||||
tobj.set_value('_display', '[{}]'.format(tidstr))
|
||||
|
@ -240,5 +232,5 @@ def put_threads_exdi(state, pid, radix):
|
|||
tobj.set_value('Win32Thread', fields[7])
|
||||
tobj.set_value('State', fields[8])
|
||||
tobj.insert()
|
||||
commands.STATE.trace.proxy_object_path(
|
||||
THREADS_PATTERN.format(pid=pidstr)).retain_values(keys)
|
||||
trace.proxy_object_path(THREADS_PATTERN.format(
|
||||
pid=pidstr)).retain_values(keys)
|
||||
|
|
|
@ -16,15 +16,17 @@
|
|||
import re
|
||||
|
||||
from ghidratrace import sch
|
||||
from ghidratrace.client import MethodRegistry, ParamDesc, Address, AddressRange
|
||||
from ghidratrace.client import (MethodRegistry, ParamDesc, Address,
|
||||
AddressRange, TraceObject)
|
||||
from ghidradbg import util, commands, methods
|
||||
from ghidradbg.methods import REGISTRY, SESSIONS_PATTERN, SESSION_PATTERN, extre
|
||||
|
||||
from . import exdi_commands
|
||||
|
||||
XPROCESSES_PATTERN = extre(SESSION_PATTERN, '\.ExdiProcesses')
|
||||
XPROCESS_PATTERN = extre(XPROCESSES_PATTERN, '\[(?P<procnum>\\d*)\]')
|
||||
XTHREADS_PATTERN = extre(XPROCESS_PATTERN, '\.Threads')
|
||||
XPROCESSES_PATTERN = extre(SESSION_PATTERN, '\\.ExdiProcesses')
|
||||
XPROCESS_PATTERN = extre(XPROCESSES_PATTERN, '\\[(?P<procnum>\\d*)\\]')
|
||||
XTHREADS_PATTERN = extre(XPROCESS_PATTERN, '\\.Threads')
|
||||
|
||||
|
||||
def find_pid_by_pattern(pattern, object, err_msg):
|
||||
mat = pattern.fullmatch(object.path)
|
||||
|
@ -38,16 +40,23 @@ def find_pid_by_obj(object):
|
|||
return find_pid_by_pattern(XTHREADS_PATTERN, object, "an ExdiThreadsContainer")
|
||||
|
||||
|
||||
class ExdiProcessContainer(TraceObject):
|
||||
pass
|
||||
|
||||
|
||||
class ExdiThreadContainer(TraceObject):
|
||||
pass
|
||||
|
||||
|
||||
@REGISTRY.method(action='refresh', display="Refresh Target Processes")
|
||||
def refresh_exdi_processes(node: sch.Schema('ExdiProcessContainer')):
|
||||
def refresh_exdi_processes(node: ExdiProcessContainer) -> None:
|
||||
"""Refresh the list of processes in the target kernel."""
|
||||
with commands.open_tracked_tx('Refresh Processes'):
|
||||
exdi_commands.ghidra_trace_put_processes_exdi()
|
||||
|
||||
|
||||
@REGISTRY.method(action='refresh', display="Refresh Process Threads")
|
||||
def refresh_exdi_threads(node: sch.Schema('ExdiThreadContainer')):
|
||||
def refresh_exdi_threads(node: ExdiThreadContainer) -> None:
|
||||
"""Refresh the list of threads in the process."""
|
||||
pid = find_pid_by_obj(node)
|
||||
with commands.open_tracked_tx('Refresh Threads'):
|
||||
|
|
|
@ -13,11 +13,14 @@
|
|||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
##
|
||||
from bisect import bisect_left, bisect_right
|
||||
from dataclasses import dataclass, field
|
||||
import functools
|
||||
import sys
|
||||
import threading
|
||||
import time
|
||||
import traceback
|
||||
from typing import Any, Callable, Collection, Dict, Optional, TypeVar, cast
|
||||
|
||||
from comtypes.hresult import S_OK
|
||||
from pybag import pydbg
|
||||
|
@ -26,6 +29,8 @@ from pybag.dbgeng import exception
|
|||
from pybag.dbgeng.callbacks import EventHandler
|
||||
from pybag.dbgeng.idebugbreakpoint import DebugBreakpoint
|
||||
|
||||
from ghidratrace.client import Schedule
|
||||
|
||||
from . import commands, util
|
||||
from .exdi import exdi_commands
|
||||
|
||||
|
@ -33,36 +38,33 @@ from .exdi import exdi_commands
|
|||
ALL_EVENTS = 0xFFFF
|
||||
|
||||
|
||||
class HookState(object):
|
||||
__slots__ = ('installed', 'mem_catchpoint')
|
||||
|
||||
def __init__(self):
|
||||
self.installed = False
|
||||
self.mem_catchpoint = None
|
||||
@dataclass(frozen=False)
|
||||
class HookState:
|
||||
installed = False
|
||||
mem_catchpoint = None
|
||||
|
||||
|
||||
class ProcessState(object):
|
||||
__slots__ = ('first', 'regions', 'modules', 'threads',
|
||||
'breaks', 'watches', 'visited', 'waiting')
|
||||
@dataclass(frozen=False)
|
||||
class ProcessState:
|
||||
first = True
|
||||
# For things we can detect changes to between stops
|
||||
regions = False
|
||||
modules = False
|
||||
threads = False
|
||||
breaks = False
|
||||
watches = False
|
||||
# For frames and threads that have already been synced since last stop
|
||||
visited: set[Any] = field(default_factory=set)
|
||||
waiting = False
|
||||
|
||||
def __init__(self):
|
||||
self.first = True
|
||||
# For things we can detect changes to between stops
|
||||
self.regions = False
|
||||
self.modules = False
|
||||
self.threads = False
|
||||
self.breaks = False
|
||||
self.watches = False
|
||||
# For frames and threads that have already been synced since last stop
|
||||
self.visited = set()
|
||||
self.waiting = False
|
||||
|
||||
def record(self, description=None, snap=None):
|
||||
def record(self, description: Optional[str] = None,
|
||||
time: Optional[Schedule] = None) -> None:
|
||||
# print("RECORDING")
|
||||
first = self.first
|
||||
self.first = False
|
||||
trace = commands.STATE.require_trace()
|
||||
if description is not None:
|
||||
commands.STATE.trace.snapshot(description, snap=snap)
|
||||
trace.snapshot(description, time=time)
|
||||
if first:
|
||||
if util.is_kernel():
|
||||
commands.create_generic("Sessions")
|
||||
|
@ -73,7 +75,7 @@ class ProcessState(object):
|
|||
commands.put_threads()
|
||||
if util.is_trace():
|
||||
commands.init_ttd()
|
||||
#commands.put_events()
|
||||
# commands.put_events()
|
||||
if self.threads:
|
||||
commands.put_threads()
|
||||
self.threads = False
|
||||
|
@ -93,46 +95,46 @@ class ProcessState(object):
|
|||
self.visited.add(hashable_frame)
|
||||
if first or self.regions:
|
||||
if util.is_exdi():
|
||||
exdi_commands.put_regions_exdi(commands.STATE)
|
||||
exdi_commands.put_regions_exdi(trace)
|
||||
commands.put_regions()
|
||||
self.regions = False
|
||||
if first or self.modules:
|
||||
if util.is_exdi():
|
||||
exdi_commands.put_kmodules_exdi(commands.STATE)
|
||||
exdi_commands.put_kmodules_exdi(trace)
|
||||
commands.put_modules()
|
||||
self.modules = False
|
||||
if first or self.breaks:
|
||||
commands.put_breakpoints()
|
||||
self.breaks = False
|
||||
|
||||
def record_continued(self):
|
||||
def record_continued(self) -> None:
|
||||
commands.put_processes(running=True)
|
||||
commands.put_threads(running=True)
|
||||
|
||||
def record_exited(self, exit_code, description=None, snap=None):
|
||||
def record_exited(self, exit_code: int, description: Optional[str] = None,
|
||||
time: Optional[Schedule] = None) -> None:
|
||||
# print("RECORD_EXITED")
|
||||
trace = commands.STATE.require_trace()
|
||||
if description is not None:
|
||||
commands.STATE.trace.snapshot(description, snap=snap)
|
||||
trace.snapshot(description, time=time)
|
||||
proc = util.selected_process()
|
||||
ipath = commands.PROCESS_PATTERN.format(procnum=proc)
|
||||
procobj = commands.STATE.trace.proxy_object_path(ipath)
|
||||
procobj = trace.proxy_object_path(ipath)
|
||||
procobj.set_value('Exit Code', exit_code)
|
||||
procobj.set_value('State', 'TERMINATED')
|
||||
|
||||
|
||||
class BrkState(object):
|
||||
__slots__ = ('break_loc_counts',)
|
||||
@dataclass(frozen=False)
|
||||
class BrkState:
|
||||
break_loc_counts: Dict[int, int] = field(default_factory=dict)
|
||||
|
||||
def __init__(self):
|
||||
self.break_loc_counts = {}
|
||||
|
||||
def update_brkloc_count(self, b, count):
|
||||
def update_brkloc_count(self, b: DebugBreakpoint, count: int) -> None:
|
||||
self.break_loc_counts[b.GetID()] = count
|
||||
|
||||
def get_brkloc_count(self, b):
|
||||
def get_brkloc_count(self, b: DebugBreakpoint) -> int:
|
||||
return self.break_loc_counts.get(b.GetID(), 0)
|
||||
|
||||
def del_brkloc_count(self, b):
|
||||
def del_brkloc_count(self, b: DebugBreakpoint) -> int:
|
||||
if b not in self.break_loc_counts:
|
||||
return 0 # TODO: Print a warning?
|
||||
count = self.break_loc_counts[b.GetID()]
|
||||
|
@ -142,35 +144,37 @@ class BrkState(object):
|
|||
|
||||
HOOK_STATE = HookState()
|
||||
BRK_STATE = BrkState()
|
||||
PROC_STATE = {}
|
||||
PROC_STATE: Dict[int, ProcessState] = {}
|
||||
|
||||
|
||||
def log_errors(func):
|
||||
'''
|
||||
Wrap a function in a try-except that prints and reraises the
|
||||
exception.
|
||||
C = TypeVar('C', bound=Callable)
|
||||
|
||||
|
||||
def log_errors(func: C) -> C:
|
||||
"""Wrap a function in a try-except that prints and reraises the exception.
|
||||
|
||||
This is needed because pybag and/or the COM wrappers do not print
|
||||
exceptions that occur during event callbacks.
|
||||
'''
|
||||
"""
|
||||
@functools.wraps(func)
|
||||
def _func(*args, **kwargs):
|
||||
def _func(*args, **kwargs) -> Any:
|
||||
try:
|
||||
return func(*args, **kwargs)
|
||||
except:
|
||||
traceback.print_exc()
|
||||
raise
|
||||
return _func
|
||||
return cast(C, _func)
|
||||
|
||||
|
||||
@log_errors
|
||||
def on_state_changed(*args):
|
||||
# print("ON_STATE_CHANGED")
|
||||
# print(args)
|
||||
def on_state_changed(*args) -> int:
|
||||
# print(f"---ON_STATE_CHANGED:{args}---")
|
||||
if args[0] == DbgEng.DEBUG_CES_CURRENT_THREAD:
|
||||
return on_thread_selected(args)
|
||||
on_thread_selected(args)
|
||||
return S_OK
|
||||
elif args[0] == DbgEng.DEBUG_CES_BREAKPOINTS:
|
||||
return on_breakpoint_modified(args)
|
||||
on_breakpoint_modified(args)
|
||||
return S_OK
|
||||
elif args[0] == DbgEng.DEBUG_CES_RADIX:
|
||||
util.set_convenience_variable('output-radix', args[1])
|
||||
return S_OK
|
||||
|
@ -179,27 +183,30 @@ def on_state_changed(*args):
|
|||
proc = util.selected_process()
|
||||
if args[1] & DbgEng.DEBUG_STATUS_INSIDE_WAIT:
|
||||
if proc in PROC_STATE:
|
||||
# Process may have exited (so deleted) first
|
||||
# Process may have exited (so deleted) first
|
||||
PROC_STATE[proc].waiting = True
|
||||
return S_OK
|
||||
if proc in PROC_STATE:
|
||||
# Process may have exited (so deleted) first.
|
||||
PROC_STATE[proc].waiting = False
|
||||
trace = commands.STATE.trace
|
||||
with commands.STATE.client.batch():
|
||||
trace = commands.STATE.require_trace()
|
||||
with trace.client.batch():
|
||||
with trace.open_tx("State changed proc {}".format(proc)):
|
||||
commands.put_state(proc)
|
||||
if args[1] == DbgEng.DEBUG_STATUS_BREAK:
|
||||
return on_stop(args)
|
||||
on_stop(args)
|
||||
return S_OK
|
||||
elif args[1] == DbgEng.DEBUG_STATUS_NO_DEBUGGEE:
|
||||
return on_exited(proc)
|
||||
on_exited(proc)
|
||||
return S_OK
|
||||
else:
|
||||
return on_cont(args)
|
||||
on_cont(args)
|
||||
return S_OK
|
||||
return S_OK
|
||||
|
||||
|
||||
@log_errors
|
||||
def on_debuggee_changed(*args):
|
||||
def on_debuggee_changed(*args) -> int:
|
||||
# print("ON_DEBUGGEE_CHANGED: args={}".format(args))
|
||||
# sys.stdout.flush()
|
||||
trace = commands.STATE.trace
|
||||
|
@ -213,20 +220,20 @@ def on_debuggee_changed(*args):
|
|||
|
||||
|
||||
@log_errors
|
||||
def on_session_status_changed(*args):
|
||||
def on_session_status_changed(*args) -> None:
|
||||
# print("ON_STATUS_CHANGED: args={}".format(args))
|
||||
trace = commands.STATE.trace
|
||||
if trace is None:
|
||||
return
|
||||
if args[0] == DbgEng.DEBUG_SESSION_ACTIVE or args[0] == DbgEng.DEBUG_SESSION_REBOOT:
|
||||
with commands.STATE.client.batch():
|
||||
with trace.client.batch():
|
||||
with trace.open_tx("New Session {}".format(util.selected_process())):
|
||||
commands.put_processes()
|
||||
return DbgEng.DEBUG_STATUS_GO
|
||||
|
||||
|
||||
@log_errors
|
||||
def on_symbol_state_changed(*args):
|
||||
def on_symbol_state_changed(*args) -> None:
|
||||
# print("ON_SYMBOL_STATE_CHANGED")
|
||||
proc = util.selected_process()
|
||||
if proc not in PROC_STATE:
|
||||
|
@ -240,31 +247,31 @@ def on_symbol_state_changed(*args):
|
|||
|
||||
|
||||
@log_errors
|
||||
def on_system_error(*args):
|
||||
def on_system_error(*args) -> None:
|
||||
print("ON_SYSTEM_ERROR: args={}".format(args))
|
||||
# print(hex(args[0]))
|
||||
trace = commands.STATE.trace
|
||||
if trace is None:
|
||||
return
|
||||
with commands.STATE.client.batch():
|
||||
with trace.client.batch():
|
||||
with trace.open_tx("System Error {}".format(util.selected_process())):
|
||||
commands.put_processes()
|
||||
return DbgEng.DEBUG_STATUS_BREAK
|
||||
|
||||
|
||||
@log_errors
|
||||
def on_new_process(*args):
|
||||
def on_new_process(*args) -> None:
|
||||
# print("ON_NEW_PROCESS")
|
||||
trace = commands.STATE.trace
|
||||
if trace is None:
|
||||
return
|
||||
with commands.STATE.client.batch():
|
||||
with trace.client.batch():
|
||||
with trace.open_tx("New Process {}".format(util.selected_process())):
|
||||
commands.put_processes()
|
||||
return DbgEng.DEBUG_STATUS_BREAK
|
||||
|
||||
|
||||
def on_process_selected():
|
||||
def on_process_selected() -> None:
|
||||
# print("PROCESS_SELECTED")
|
||||
proc = util.selected_process()
|
||||
if proc not in PROC_STATE:
|
||||
|
@ -272,14 +279,14 @@ def on_process_selected():
|
|||
trace = commands.STATE.trace
|
||||
if trace is None:
|
||||
return
|
||||
with commands.STATE.client.batch():
|
||||
with trace.client.batch():
|
||||
with trace.open_tx("Process {} selected".format(proc)):
|
||||
PROC_STATE[proc].record()
|
||||
commands.activate()
|
||||
|
||||
|
||||
@log_errors
|
||||
def on_process_deleted(*args):
|
||||
def on_process_deleted(*args) -> None:
|
||||
# print("ON_PROCESS_DELETED")
|
||||
exit_code = args[0]
|
||||
proc = util.selected_process()
|
||||
|
@ -289,14 +296,14 @@ def on_process_deleted(*args):
|
|||
trace = commands.STATE.trace
|
||||
if trace is None:
|
||||
return
|
||||
with commands.STATE.client.batch():
|
||||
with trace.client.batch():
|
||||
with trace.open_tx("Process {} deleted".format(proc)):
|
||||
commands.put_processes() # TODO: Could just delete the one....
|
||||
return DbgEng.DEBUG_STATUS_BREAK
|
||||
|
||||
|
||||
@log_errors
|
||||
def on_threads_changed(*args):
|
||||
def on_threads_changed(*args) -> None:
|
||||
# print("ON_THREADS_CHANGED")
|
||||
proc = util.selected_process()
|
||||
if proc not in PROC_STATE:
|
||||
|
@ -305,7 +312,7 @@ def on_threads_changed(*args):
|
|||
return DbgEng.DEBUG_STATUS_GO
|
||||
|
||||
|
||||
def on_thread_selected(*args):
|
||||
def on_thread_selected(*args) -> None:
|
||||
# print("THREAD_SELECTED: args={}".format(args))
|
||||
# sys.stdout.flush()
|
||||
nthrd = args[0][1]
|
||||
|
@ -315,7 +322,7 @@ def on_thread_selected(*args):
|
|||
trace = commands.STATE.trace
|
||||
if trace is None:
|
||||
return
|
||||
with commands.STATE.client.batch():
|
||||
with trace.client.batch():
|
||||
with trace.open_tx("Thread {}.{} selected".format(nproc, nthrd)):
|
||||
commands.put_state(nproc)
|
||||
state = PROC_STATE[nproc]
|
||||
|
@ -326,7 +333,7 @@ def on_thread_selected(*args):
|
|||
commands.activate()
|
||||
|
||||
|
||||
def on_register_changed(regnum):
|
||||
def on_register_changed(regnum) -> None:
|
||||
# print("REGISTER_CHANGED")
|
||||
proc = util.selected_process()
|
||||
if proc not in PROC_STATE:
|
||||
|
@ -334,13 +341,13 @@ def on_register_changed(regnum):
|
|||
trace = commands.STATE.trace
|
||||
if trace is None:
|
||||
return
|
||||
with commands.STATE.client.batch():
|
||||
with trace.client.batch():
|
||||
with trace.open_tx("Register {} changed".format(regnum)):
|
||||
commands.putreg()
|
||||
commands.activate()
|
||||
|
||||
|
||||
def on_memory_changed(space):
|
||||
def on_memory_changed(space) -> None:
|
||||
if space != DbgEng.DEBUG_DATA_SPACE_VIRTUAL:
|
||||
return
|
||||
proc = util.selected_process()
|
||||
|
@ -352,12 +359,12 @@ def on_memory_changed(space):
|
|||
# Not great, but invalidate the whole space
|
||||
# UI will only re-fetch what it needs
|
||||
# But, some observations will not be recovered
|
||||
with commands.STATE.client.batch():
|
||||
with trace.client.batch():
|
||||
with trace.open_tx("Memory changed"):
|
||||
commands.putmem_state(0, 2**64, 'unknown')
|
||||
|
||||
|
||||
def on_cont(*args):
|
||||
def on_cont(*args) -> None:
|
||||
# print("ON CONT")
|
||||
proc = util.selected_process()
|
||||
if proc not in PROC_STATE:
|
||||
|
@ -366,56 +373,55 @@ def on_cont(*args):
|
|||
if trace is None:
|
||||
return
|
||||
state = PROC_STATE[proc]
|
||||
with commands.STATE.client.batch():
|
||||
with trace.client.batch():
|
||||
with trace.open_tx("Continued"):
|
||||
state.record_continued()
|
||||
return DbgEng.DEBUG_STATUS_GO
|
||||
|
||||
|
||||
def on_stop(*args):
|
||||
# print("ON STOP")
|
||||
def on_stop(*args) -> None:
|
||||
proc = util.selected_process()
|
||||
if proc not in PROC_STATE:
|
||||
# print("not in state")
|
||||
return
|
||||
trace = commands.STATE.trace
|
||||
if trace is None:
|
||||
# print("no trace")
|
||||
return
|
||||
state = PROC_STATE[proc]
|
||||
state.visited.clear()
|
||||
snap = update_position()
|
||||
with commands.STATE.client.batch():
|
||||
time = update_position()
|
||||
with trace.client.batch():
|
||||
with trace.open_tx("Stopped"):
|
||||
state.record("Stopped", snap)
|
||||
description = util.compute_description(time, "Stopped")
|
||||
state.record(description, time)
|
||||
commands.put_event_thread()
|
||||
commands.activate()
|
||||
|
||||
|
||||
def update_position():
|
||||
"""Update the position"""
|
||||
cursor = util.get_cursor()
|
||||
if cursor is None:
|
||||
def update_position() -> Optional[Schedule]:
|
||||
"""Update the position."""
|
||||
posobj = util.get_object("State.DebuggerVariables.curthread.TTD.Position")
|
||||
if posobj is None:
|
||||
return None
|
||||
pos = cursor.get_position()
|
||||
pos = util.pos2split(posobj)
|
||||
lpos = util.get_last_position()
|
||||
rng = range(pos.major, lpos.major)
|
||||
if pos.major > lpos.major:
|
||||
rng = range(lpos.major, pos.major)
|
||||
for i in rng:
|
||||
type = util.get_event_type(i)
|
||||
if type == "modload" or type == "modunload":
|
||||
on_modules_changed()
|
||||
break
|
||||
for i in rng:
|
||||
type = util.get_event_type(i)
|
||||
if type == "threadcreated" or type == "threadterm":
|
||||
on_threads_changed()
|
||||
util.set_last_position(pos)
|
||||
return util.pos2snap(pos)
|
||||
if lpos is None:
|
||||
return util.split2schedule(pos)
|
||||
|
||||
|
||||
def on_exited(proc):
|
||||
minpos, maxpos = (lpos, pos) if lpos < pos else (pos, lpos)
|
||||
evts = list(util.ttd.evttypes.keys())
|
||||
minidx = bisect_left(evts, minpos)
|
||||
maxidx = bisect_right(evts, maxpos)
|
||||
types = set(util.ttd.evttypes[p] for p in evts[minidx:maxidx])
|
||||
if "modload" in types or "modunload" in types:
|
||||
on_modules_changed()
|
||||
if "threadcreated" in types or "threadterm" in types:
|
||||
on_threads_changed()
|
||||
|
||||
util.set_last_position(pos)
|
||||
return util.split2schedule(pos)
|
||||
|
||||
|
||||
def on_exited(proc) -> None:
|
||||
# print("ON EXITED")
|
||||
if proc not in PROC_STATE:
|
||||
# print("not in state")
|
||||
|
@ -427,14 +433,14 @@ def on_exited(proc):
|
|||
state.visited.clear()
|
||||
exit_code = util.GetExitCode()
|
||||
description = "Exited with code {}".format(exit_code)
|
||||
with commands.STATE.client.batch():
|
||||
with trace.client.batch():
|
||||
with trace.open_tx(description):
|
||||
state.record_exited(exit_code, description)
|
||||
commands.activate()
|
||||
|
||||
|
||||
@log_errors
|
||||
def on_modules_changed(*args):
|
||||
def on_modules_changed(*args) -> None:
|
||||
# print("ON_MODULES_CHANGED")
|
||||
proc = util.selected_process()
|
||||
if proc not in PROC_STATE:
|
||||
|
@ -443,7 +449,7 @@ def on_modules_changed(*args):
|
|||
return DbgEng.DEBUG_STATUS_GO
|
||||
|
||||
|
||||
def on_breakpoint_created(bp):
|
||||
def on_breakpoint_created(bp) -> None:
|
||||
# print("ON_BREAKPOINT_CREATED")
|
||||
proc = util.selected_process()
|
||||
if proc not in PROC_STATE:
|
||||
|
@ -453,15 +459,14 @@ def on_breakpoint_created(bp):
|
|||
if trace is None:
|
||||
return
|
||||
ibpath = commands.PROC_BREAKS_PATTERN.format(procnum=proc)
|
||||
with commands.STATE.client.batch():
|
||||
with trace.client.batch():
|
||||
with trace.open_tx("Breakpoint {} created".format(bp.GetId())):
|
||||
ibobj = trace.create_object(ibpath)
|
||||
# Do not use retain_values or it'll remove other locs
|
||||
commands.put_single_breakpoint(bp, ibobj, proc, [])
|
||||
ibobj.insert()
|
||||
|
||||
|
||||
def on_breakpoint_modified(*args):
|
||||
def on_breakpoint_modified(*args) -> None:
|
||||
# print("BREAKPOINT_MODIFIED")
|
||||
proc = util.selected_process()
|
||||
if proc not in PROC_STATE:
|
||||
|
@ -481,7 +486,7 @@ def on_breakpoint_modified(*args):
|
|||
return on_breakpoint_created(bp)
|
||||
|
||||
|
||||
def on_breakpoint_deleted(bpid):
|
||||
def on_breakpoint_deleted(bpid) -> None:
|
||||
proc = util.selected_process()
|
||||
if proc not in PROC_STATE:
|
||||
return
|
||||
|
@ -490,68 +495,68 @@ def on_breakpoint_deleted(bpid):
|
|||
if trace is None:
|
||||
return
|
||||
bpath = commands.PROC_BREAK_PATTERN.format(procnum=proc, breaknum=bpid)
|
||||
with commands.STATE.client.batch():
|
||||
with trace.client.batch():
|
||||
with trace.open_tx("Breakpoint {} deleted".format(bpid)):
|
||||
trace.proxy_object_path(bpath).remove(tree=True)
|
||||
|
||||
|
||||
@log_errors
|
||||
def on_breakpoint_hit(*args):
|
||||
def on_breakpoint_hit(*args) -> None:
|
||||
# print("ON_BREAKPOINT_HIT: args={}".format(args))
|
||||
return DbgEng.DEBUG_STATUS_BREAK
|
||||
|
||||
|
||||
@log_errors
|
||||
def on_exception(*args):
|
||||
def on_exception(*args) -> None:
|
||||
# print("ON_EXCEPTION: args={}".format(args))
|
||||
return DbgEng.DEBUG_STATUS_BREAK
|
||||
|
||||
|
||||
@util.dbg.eng_thread
|
||||
def install_hooks():
|
||||
# print("Installing hooks")
|
||||
if HOOK_STATE.installed:
|
||||
return
|
||||
HOOK_STATE.installed = True
|
||||
def install_hooks() -> None:
|
||||
# print("Installing hooks")
|
||||
if HOOK_STATE.installed:
|
||||
return
|
||||
HOOK_STATE.installed = True
|
||||
|
||||
events = util.dbg._base.events
|
||||
|
||||
if util.is_remote():
|
||||
events.engine_state(handler=on_state_changed_async)
|
||||
events.debuggee_state(handler=on_debuggee_changed_async)
|
||||
events.session_status(handler=on_session_status_changed_async)
|
||||
events.symbol_state(handler=on_symbol_state_changed_async)
|
||||
events.system_error(handler=on_system_error_async)
|
||||
events = util.dbg._base.events
|
||||
|
||||
events.create_process(handler=on_new_process_async)
|
||||
events.exit_process(handler=on_process_deleted_async)
|
||||
events.create_thread(handler=on_threads_changed_async)
|
||||
events.exit_thread(handler=on_threads_changed_async)
|
||||
events.module_load(handler=on_modules_changed_async)
|
||||
events.unload_module(handler=on_modules_changed_async)
|
||||
if util.is_remote():
|
||||
events.engine_state(handler=on_state_changed_async)
|
||||
events.debuggee_state(handler=on_debuggee_changed_async)
|
||||
events.session_status(handler=on_session_status_changed_async)
|
||||
events.symbol_state(handler=on_symbol_state_changed_async)
|
||||
events.system_error(handler=on_system_error_async)
|
||||
|
||||
events.breakpoint(handler=on_breakpoint_hit_async)
|
||||
events.exception(handler=on_exception_async)
|
||||
else:
|
||||
events.engine_state(handler=on_state_changed)
|
||||
events.debuggee_state(handler=on_debuggee_changed)
|
||||
events.session_status(handler=on_session_status_changed)
|
||||
events.symbol_state(handler=on_symbol_state_changed)
|
||||
events.system_error(handler=on_system_error)
|
||||
events.create_process(handler=on_new_process_async)
|
||||
events.exit_process(handler=on_process_deleted_async)
|
||||
events.create_thread(handler=on_threads_changed_async)
|
||||
events.exit_thread(handler=on_threads_changed_async)
|
||||
events.module_load(handler=on_modules_changed_async)
|
||||
events.unload_module(handler=on_modules_changed_async)
|
||||
|
||||
events.create_process(handler=on_new_process)
|
||||
events.exit_process(handler=on_process_deleted)
|
||||
events.create_thread(handler=on_threads_changed)
|
||||
events.exit_thread(handler=on_threads_changed)
|
||||
events.module_load(handler=on_modules_changed)
|
||||
events.unload_module(handler=on_modules_changed)
|
||||
events.breakpoint(handler=on_breakpoint_hit_async)
|
||||
events.exception(handler=on_exception_async)
|
||||
else:
|
||||
events.engine_state(handler=on_state_changed)
|
||||
events.debuggee_state(handler=on_debuggee_changed)
|
||||
events.session_status(handler=on_session_status_changed)
|
||||
events.symbol_state(handler=on_symbol_state_changed)
|
||||
events.system_error(handler=on_system_error)
|
||||
|
||||
events.breakpoint(handler=on_breakpoint_hit)
|
||||
events.exception(handler=on_exception)
|
||||
events.create_process(handler=on_new_process)
|
||||
events.exit_process(handler=on_process_deleted)
|
||||
events.create_thread(handler=on_threads_changed)
|
||||
events.exit_thread(handler=on_threads_changed)
|
||||
events.module_load(handler=on_modules_changed)
|
||||
events.unload_module(handler=on_modules_changed)
|
||||
|
||||
events.breakpoint(handler=on_breakpoint_hit)
|
||||
events.exception(handler=on_exception)
|
||||
|
||||
|
||||
@util.dbg.eng_thread
|
||||
def remove_hooks():
|
||||
def remove_hooks() -> None:
|
||||
# print("Removing hooks")
|
||||
if not HOOK_STATE.installed:
|
||||
return
|
||||
|
@ -559,14 +564,14 @@ def remove_hooks():
|
|||
util.dbg._base._reset_callbacks()
|
||||
|
||||
|
||||
def enable_current_process():
|
||||
def enable_current_process() -> None:
|
||||
# print("Enable current process")
|
||||
proc = util.selected_process()
|
||||
# print("proc: {}".format(proc))
|
||||
PROC_STATE[proc] = ProcessState()
|
||||
|
||||
|
||||
def disable_current_process():
|
||||
def disable_current_process() -> None:
|
||||
proc = util.selected_process()
|
||||
if proc in PROC_STATE:
|
||||
# Silently ignore already disabled
|
||||
|
@ -574,56 +579,55 @@ def disable_current_process():
|
|||
|
||||
|
||||
@log_errors
|
||||
def on_state_changed_async(*args):
|
||||
util.dbg.run_async(on_state_changed, *args)
|
||||
def on_state_changed_async(*args) -> None:
|
||||
util.dbg.run_async(on_state_changed, *args)
|
||||
|
||||
|
||||
@log_errors
|
||||
def on_debuggee_changed_async(*args):
|
||||
util.dbg.run_async(on_debuggee_changed, *args)
|
||||
def on_debuggee_changed_async(*args) -> None:
|
||||
util.dbg.run_async(on_debuggee_changed, *args)
|
||||
|
||||
|
||||
@log_errors
|
||||
def on_session_status_changed_async(*args):
|
||||
util.dbg.run_async(on_session_status_changed, *args)
|
||||
def on_session_status_changed_async(*args) -> None:
|
||||
util.dbg.run_async(on_session_status_changed, *args)
|
||||
|
||||
|
||||
@log_errors
|
||||
def on_symbol_state_changed_async(*args):
|
||||
util.dbg.run_async(on_symbol_state_changed, *args)
|
||||
def on_symbol_state_changed_async(*args) -> None:
|
||||
util.dbg.run_async(on_symbol_state_changed, *args)
|
||||
|
||||
|
||||
@log_errors
|
||||
def on_system_error_async(*args):
|
||||
util.dbg.run_async(on_system_error, *args)
|
||||
def on_system_error_async(*args) -> None:
|
||||
util.dbg.run_async(on_system_error, *args)
|
||||
|
||||
|
||||
@log_errors
|
||||
def on_new_process_async(*args):
|
||||
util.dbg.run_async(on_new_process, *args)
|
||||
def on_new_process_async(*args) -> None:
|
||||
util.dbg.run_async(on_new_process, *args)
|
||||
|
||||
|
||||
@log_errors
|
||||
def on_process_deleted_async(*args):
|
||||
util.dbg.run_async(on_process_deleted, *args)
|
||||
def on_process_deleted_async(*args) -> None:
|
||||
util.dbg.run_async(on_process_deleted, *args)
|
||||
|
||||
|
||||
@log_errors
|
||||
def on_threads_changed_async(*args):
|
||||
util.dbg.run_async(on_threads_changed, *args)
|
||||
def on_threads_changed_async(*args) -> None:
|
||||
util.dbg.run_async(on_threads_changed, *args)
|
||||
|
||||
|
||||
@log_errors
|
||||
def on_modules_changed_async(*args):
|
||||
util.dbg.run_async(on_modules_changed, *args)
|
||||
def on_modules_changed_async(*args) -> None:
|
||||
util.dbg.run_async(on_modules_changed, *args)
|
||||
|
||||
|
||||
@log_errors
|
||||
def on_breakpoint_hit_async(*args):
|
||||
util.dbg.run_async(on_breakpoint_hit, *args)
|
||||
def on_breakpoint_hit_async(*args) -> None:
|
||||
util.dbg.run_async(on_breakpoint_hit, *args)
|
||||
|
||||
|
||||
@log_errors
|
||||
def on_exception_async(*args):
|
||||
util.dbg.run_async(on_exception, *args)
|
||||
|
||||
def on_exception_async(*args) -> None:
|
||||
util.dbg.run_async(on_exception, *args)
|
||||
|
|
|
@ -1,17 +1,17 @@
|
|||
## ###
|
||||
# IP: GHIDRA
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
# IP: GHIDRA
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
##
|
||||
import ctypes
|
||||
import os
|
||||
|
|
|
@ -18,44 +18,48 @@ from contextlib import redirect_stdout
|
|||
from io import StringIO
|
||||
import re
|
||||
import sys
|
||||
from typing import Annotated, Any, Dict, Optional
|
||||
|
||||
from ghidratrace import sch
|
||||
from ghidratrace.client import MethodRegistry, ParamDesc, Address, AddressRange
|
||||
from ghidratrace.client import (MethodRegistry, ParamDesc, Address,
|
||||
AddressRange, Schedule, TraceObject)
|
||||
from pybag import pydbg
|
||||
from pybag.dbgeng import core as DbgEng, exception
|
||||
|
||||
from . import util, commands
|
||||
|
||||
|
||||
REGISTRY = MethodRegistry(ThreadPoolExecutor(
|
||||
max_workers=1, thread_name_prefix='MethodRegistry'))
|
||||
|
||||
|
||||
def extre(base, ext):
|
||||
def extre(base: re.Pattern, ext: str) -> re.Pattern:
|
||||
return re.compile(base.pattern + ext)
|
||||
|
||||
|
||||
AVAILABLE_PATTERN = re.compile('Available\[(?P<pid>\\d*)\]')
|
||||
WATCHPOINT_PATTERN = re.compile('Watchpoints\[(?P<watchnum>\\d*)\]')
|
||||
BREAKPOINT_PATTERN = re.compile('Breakpoints\[(?P<breaknum>\\d*)\]')
|
||||
BREAK_LOC_PATTERN = extre(BREAKPOINT_PATTERN, '\[(?P<locnum>\\d*)\]')
|
||||
WATCHPOINT_PATTERN = re.compile('Watchpoints\\[(?P<watchnum>\\d*)\\]')
|
||||
BREAKPOINT_PATTERN = re.compile('Breakpoints\\[(?P<breaknum>\\d*)\\]')
|
||||
BREAK_LOC_PATTERN = extre(BREAKPOINT_PATTERN, '\\[(?P<locnum>\\d*)\\]')
|
||||
SESSIONS_PATTERN = re.compile('Sessions')
|
||||
SESSION_PATTERN = extre(SESSIONS_PATTERN, '\[(?P<snum>\\d*)\]')
|
||||
PROCESSES_PATTERN = extre(SESSION_PATTERN, '\.Processes')
|
||||
PROCESS_PATTERN = extre(PROCESSES_PATTERN, '\[(?P<procnum>\\d*)\]')
|
||||
PROC_BREAKS_PATTERN = extre(PROCESS_PATTERN, '\.Debug.Breakpoints')
|
||||
PROC_BREAKBPT_PATTERN = extre(PROC_BREAKS_PATTERN, '\[(?P<breaknum>\\d*)\]')
|
||||
ENV_PATTERN = extre(PROCESS_PATTERN, '\.Environment')
|
||||
THREADS_PATTERN = extre(PROCESS_PATTERN, '\.Threads')
|
||||
THREAD_PATTERN = extre(THREADS_PATTERN, '\[(?P<tnum>\\d*)\]')
|
||||
STACK_PATTERN = extre(THREAD_PATTERN, '\.Stack.Frames')
|
||||
FRAME_PATTERN = extre(STACK_PATTERN, '\[(?P<level>\\d*)\]')
|
||||
REGS_PATTERN0 = extre(THREAD_PATTERN, '.Registers')
|
||||
REGS_PATTERN = extre(FRAME_PATTERN, '.Registers')
|
||||
MEMORY_PATTERN = extre(PROCESS_PATTERN, '\.Memory')
|
||||
MODULES_PATTERN = extre(PROCESS_PATTERN, '\.Modules')
|
||||
SESSION_PATTERN = extre(SESSIONS_PATTERN, '\\[(?P<snum>\\d*)\\]')
|
||||
AVAILABLE_PATTERN = extre(SESSION_PATTERN, '\\.Available\\[(?P<pid>\\d*)\\]')
|
||||
PROCESSES_PATTERN = extre(SESSION_PATTERN, '\\.Processes')
|
||||
PROCESS_PATTERN = extre(PROCESSES_PATTERN, '\\[(?P<procnum>\\d*)\\]')
|
||||
PROC_BREAKS_PATTERN = extre(PROCESS_PATTERN, '\\.Debug.Breakpoints')
|
||||
PROC_BREAKBPT_PATTERN = extre(PROC_BREAKS_PATTERN, '\\[(?P<breaknum>\\d*)\\]')
|
||||
ENV_PATTERN = extre(PROCESS_PATTERN, '\\.Environment')
|
||||
THREADS_PATTERN = extre(PROCESS_PATTERN, '\\.Threads')
|
||||
THREAD_PATTERN = extre(THREADS_PATTERN, '\\[(?P<tnum>\\d*)\\]')
|
||||
STACK_PATTERN = extre(THREAD_PATTERN, '\\.Stack.Frames')
|
||||
FRAME_PATTERN = extre(STACK_PATTERN, '\\[(?P<level>\\d*)\\]')
|
||||
REGS_PATTERN0 = extre(THREAD_PATTERN, '\\.Registers')
|
||||
REGS_PATTERN = extre(FRAME_PATTERN, '\\.Registers')
|
||||
MEMORY_PATTERN = extre(PROCESS_PATTERN, '\\.Memory')
|
||||
MODULES_PATTERN = extre(PROCESS_PATTERN, '\\.Modules')
|
||||
|
||||
|
||||
def find_availpid_by_pattern(pattern, object, err_msg):
|
||||
def find_availpid_by_pattern(pattern: re.Pattern, object: TraceObject,
|
||||
err_msg: str) -> int:
|
||||
mat = pattern.fullmatch(object.path)
|
||||
if mat is None:
|
||||
raise TypeError(f"{object} is not {err_msg}")
|
||||
|
@ -63,17 +67,18 @@ def find_availpid_by_pattern(pattern, object, err_msg):
|
|||
return pid
|
||||
|
||||
|
||||
def find_availpid_by_obj(object):
|
||||
return find_availpid_by_pattern(AVAILABLE_PATTERN, object, "an Available")
|
||||
def find_availpid_by_obj(object: TraceObject) -> int:
|
||||
return find_availpid_by_pattern(AVAILABLE_PATTERN, object, "an Attachable")
|
||||
|
||||
|
||||
def find_proc_by_num(id):
|
||||
def find_proc_by_num(id: int) -> int:
|
||||
if id != util.selected_process():
|
||||
util.select_process(id)
|
||||
return util.selected_process()
|
||||
|
||||
|
||||
def find_proc_by_pattern(object, pattern, err_msg):
|
||||
def find_proc_by_pattern(object: TraceObject, pattern: re.Pattern,
|
||||
err_msg: str) -> int:
|
||||
mat = pattern.fullmatch(object.path)
|
||||
if mat is None:
|
||||
raise TypeError(f"{object} is not {err_msg}")
|
||||
|
@ -81,43 +86,39 @@ def find_proc_by_pattern(object, pattern, err_msg):
|
|||
return find_proc_by_num(procnum)
|
||||
|
||||
|
||||
def find_proc_by_obj(object):
|
||||
def find_proc_by_obj(object: TraceObject) -> int:
|
||||
return find_proc_by_pattern(object, PROCESS_PATTERN, "an Process")
|
||||
|
||||
|
||||
def find_proc_by_procbreak_obj(object):
|
||||
def find_proc_by_procbreak_obj(object: TraceObject) -> int:
|
||||
return find_proc_by_pattern(object, PROC_BREAKS_PATTERN,
|
||||
"a BreakpointLocationContainer")
|
||||
|
||||
|
||||
def find_proc_by_procwatch_obj(object):
|
||||
return find_proc_by_pattern(object, PROC_WATCHES_PATTERN,
|
||||
"a WatchpointContainer")
|
||||
|
||||
|
||||
def find_proc_by_env_obj(object):
|
||||
def find_proc_by_env_obj(object: TraceObject) -> int:
|
||||
return find_proc_by_pattern(object, ENV_PATTERN, "an Environment")
|
||||
|
||||
|
||||
def find_proc_by_threads_obj(object):
|
||||
def find_proc_by_threads_obj(object: TraceObject) -> int:
|
||||
return find_proc_by_pattern(object, THREADS_PATTERN, "a ThreadContainer")
|
||||
|
||||
|
||||
def find_proc_by_mem_obj(object):
|
||||
def find_proc_by_mem_obj(object: TraceObject) -> int:
|
||||
return find_proc_by_pattern(object, MEMORY_PATTERN, "a Memory")
|
||||
|
||||
|
||||
def find_proc_by_modules_obj(object):
|
||||
def find_proc_by_modules_obj(object: TraceObject) -> int:
|
||||
return find_proc_by_pattern(object, MODULES_PATTERN, "a ModuleContainer")
|
||||
|
||||
|
||||
def find_thread_by_num(id):
|
||||
def find_thread_by_num(id: int) -> Optional[int]:
|
||||
if id != util.selected_thread():
|
||||
util.select_thread(id)
|
||||
return util.selected_thread()
|
||||
|
||||
|
||||
def find_thread_by_pattern(pattern, object, err_msg):
|
||||
def find_thread_by_pattern(pattern: re.Pattern, object: TraceObject,
|
||||
err_msg: str) -> Optional[int]:
|
||||
mat = pattern.fullmatch(object.path)
|
||||
if mat is None:
|
||||
raise TypeError(f"{object} is not {err_msg}")
|
||||
|
@ -127,27 +128,29 @@ def find_thread_by_pattern(pattern, object, err_msg):
|
|||
return find_thread_by_num(tnum)
|
||||
|
||||
|
||||
def find_thread_by_obj(object):
|
||||
def find_thread_by_obj(object: TraceObject) -> Optional[int]:
|
||||
return find_thread_by_pattern(THREAD_PATTERN, object, "a Thread")
|
||||
|
||||
|
||||
def find_thread_by_stack_obj(object):
|
||||
def find_thread_by_stack_obj(object: TraceObject) -> Optional[int]:
|
||||
return find_thread_by_pattern(STACK_PATTERN, object, "a Stack")
|
||||
|
||||
|
||||
def find_thread_by_regs_obj(object):
|
||||
return find_thread_by_pattern(REGS_PATTERN0, object, "a RegisterValueContainer")
|
||||
def find_thread_by_regs_obj(object: TraceObject) -> Optional[int]:
|
||||
return find_thread_by_pattern(REGS_PATTERN0, object,
|
||||
"a RegisterValueContainer")
|
||||
|
||||
|
||||
@util.dbg.eng_thread
|
||||
def find_frame_by_level(level):
|
||||
def find_frame_by_level(level: int) -> DbgEng._DEBUG_STACK_FRAME:
|
||||
for f in util.dbg._base.backtrace_list():
|
||||
if f.FrameNumber == level:
|
||||
return f
|
||||
# return dbg().backtrace_list()[level]
|
||||
|
||||
|
||||
def find_frame_by_pattern(pattern, object, err_msg):
|
||||
def find_frame_by_pattern(pattern: re.Pattern, object: TraceObject,
|
||||
err_msg: str) -> DbgEng._DEBUG_STACK_FRAME:
|
||||
mat = pattern.fullmatch(object.path)
|
||||
if mat is None:
|
||||
raise TypeError(f"{object} is not {err_msg}")
|
||||
|
@ -159,11 +162,11 @@ def find_frame_by_pattern(pattern, object, err_msg):
|
|||
return find_frame_by_level(level)
|
||||
|
||||
|
||||
def find_frame_by_obj(object):
|
||||
def find_frame_by_obj(object: TraceObject) -> DbgEng._DEBUG_STACK_FRAME:
|
||||
return find_frame_by_pattern(FRAME_PATTERN, object, "a StackFrame")
|
||||
|
||||
|
||||
def find_bpt_by_number(breaknum):
|
||||
def find_bpt_by_number(breaknum: int) -> DbgEng.IDebugBreakpoint:
|
||||
try:
|
||||
bp = dbg()._control.GetBreakpointById(breaknum)
|
||||
return bp
|
||||
|
@ -171,7 +174,8 @@ def find_bpt_by_number(breaknum):
|
|||
raise KeyError(f"Breakpoints[{breaknum}] does not exist")
|
||||
|
||||
|
||||
def find_bpt_by_pattern(pattern, object, err_msg):
|
||||
def find_bpt_by_pattern(pattern: re.Pattern, object: TraceObject,
|
||||
err_msg: str) -> DbgEng.IDebugBreakpoint:
|
||||
mat = pattern.fullmatch(object.path)
|
||||
if mat is None:
|
||||
raise TypeError(f"{object} is not {err_msg}")
|
||||
|
@ -179,16 +183,80 @@ def find_bpt_by_pattern(pattern, object, err_msg):
|
|||
return find_bpt_by_number(breaknum)
|
||||
|
||||
|
||||
def find_bpt_by_obj(object):
|
||||
def find_bpt_by_obj(object: TraceObject) -> DbgEng.IDebugBreakpoint:
|
||||
return find_bpt_by_pattern(PROC_BREAKBPT_PATTERN, object, "a BreakpointSpec")
|
||||
|
||||
|
||||
shared_globals = dict()
|
||||
shared_globals: Dict[str, Any] = dict()
|
||||
|
||||
|
||||
@REGISTRY.method
|
||||
class Session(TraceObject):
|
||||
pass
|
||||
|
||||
|
||||
class AvailableContainer(TraceObject):
|
||||
pass
|
||||
|
||||
|
||||
class BreakpointContainer(TraceObject):
|
||||
pass
|
||||
|
||||
|
||||
class ProcessContainer(TraceObject):
|
||||
pass
|
||||
|
||||
|
||||
class Environment(TraceObject):
|
||||
pass
|
||||
|
||||
|
||||
class ThreadContainer(TraceObject):
|
||||
pass
|
||||
|
||||
|
||||
class Stack(TraceObject):
|
||||
pass
|
||||
|
||||
|
||||
class RegisterValueContainer(TraceObject):
|
||||
pass
|
||||
|
||||
|
||||
class Memory(TraceObject):
|
||||
pass
|
||||
|
||||
|
||||
class ModuleContainer(TraceObject):
|
||||
pass
|
||||
|
||||
|
||||
class State(TraceObject):
|
||||
pass
|
||||
|
||||
|
||||
class Process(TraceObject):
|
||||
pass
|
||||
|
||||
|
||||
class Thread(TraceObject):
|
||||
pass
|
||||
|
||||
|
||||
class StackFrame(TraceObject):
|
||||
pass
|
||||
|
||||
|
||||
class Attachable(TraceObject):
|
||||
pass
|
||||
|
||||
|
||||
class BreakpointSpec(TraceObject):
|
||||
pass
|
||||
|
||||
|
||||
@REGISTRY.method()
|
||||
# @util.dbg.eng_thread
|
||||
def execute(cmd: str, to_string: bool=False):
|
||||
def execute(cmd: str, to_string: bool = False):
|
||||
"""Execute a Python3 command or script."""
|
||||
# print("***{}***".format(cmd))
|
||||
# sys.stderr.flush()
|
||||
|
@ -205,59 +273,58 @@ def execute(cmd: str, to_string: bool=False):
|
|||
@REGISTRY.method(action='evaluate', display='Evaluate')
|
||||
# @util.dbg.eng_thread
|
||||
def evaluate(
|
||||
session: sch.Schema('Session'),
|
||||
expr: ParamDesc(str, display='Expr')):
|
||||
session: Session,
|
||||
expr: Annotated[str, ParamDesc(display='Expr')]) -> str:
|
||||
"""Evaluate a Python3 expression."""
|
||||
return str(eval(expr, shared_globals))
|
||||
|
||||
|
||||
@REGISTRY.method(action='refresh', display="Refresh", condition=util.dbg.use_generics)
|
||||
def refresh_generic(node: sch.OBJECT):
|
||||
@REGISTRY.method(action='refresh', display="Refresh",
|
||||
condition=util.dbg.use_generics)
|
||||
def refresh_generic(node: TraceObject) -> None:
|
||||
"""List the children for a generic node."""
|
||||
with commands.open_tracked_tx('Refresh Generic'):
|
||||
commands.ghidra_trace_put_generic(node)
|
||||
|
||||
|
||||
@REGISTRY.method(action='refresh', display='Refresh Available')
|
||||
def refresh_available(node: sch.Schema('AvailableContainer')):
|
||||
def refresh_available(node: AvailableContainer) -> None:
|
||||
"""List processes on pydbg's host system."""
|
||||
with commands.open_tracked_tx('Refresh Available'):
|
||||
commands.ghidra_trace_put_available()
|
||||
|
||||
|
||||
@REGISTRY.method(action='refresh', display='Refresh Breakpoints')
|
||||
def refresh_breakpoints(node: sch.Schema('BreakpointContainer')):
|
||||
"""
|
||||
Refresh the list of breakpoints (including locations for the current
|
||||
process).
|
||||
"""
|
||||
def refresh_breakpoints(node: BreakpointContainer) -> None:
|
||||
"""Refresh the list of breakpoints (including locations for the current
|
||||
process)."""
|
||||
with commands.open_tracked_tx('Refresh Breakpoints'):
|
||||
commands.ghidra_trace_put_breakpoints()
|
||||
|
||||
|
||||
@REGISTRY.method(action='refresh', display='Refresh Processes')
|
||||
def refresh_processes(node: sch.Schema('ProcessContainer')):
|
||||
def refresh_processes(node: ProcessContainer) -> None:
|
||||
"""Refresh the list of processes."""
|
||||
with commands.open_tracked_tx('Refresh Processes'):
|
||||
commands.ghidra_trace_put_processes()
|
||||
|
||||
|
||||
@REGISTRY.method(action='refresh', display='Refresh Environment')
|
||||
def refresh_environment(node: sch.Schema('Environment')):
|
||||
def refresh_environment(node: Environment) -> None:
|
||||
"""Refresh the environment descriptors (arch, os, endian)."""
|
||||
with commands.open_tracked_tx('Refresh Environment'):
|
||||
commands.ghidra_trace_put_environment()
|
||||
|
||||
|
||||
@REGISTRY.method(action='refresh', display='Refresh Threads')
|
||||
def refresh_threads(node: sch.Schema('ThreadContainer')):
|
||||
def refresh_threads(node: ThreadContainer) -> None:
|
||||
"""Refresh the list of threads in the process."""
|
||||
with commands.open_tracked_tx('Refresh Threads'):
|
||||
commands.ghidra_trace_put_threads()
|
||||
|
||||
|
||||
@REGISTRY.method(action='refresh', display='Refresh Stack')
|
||||
def refresh_stack(node: sch.Schema('Stack')):
|
||||
def refresh_stack(node: Stack) -> None:
|
||||
"""Refresh the backtrace for the thread."""
|
||||
tnum = find_thread_by_stack_obj(node)
|
||||
util.reset_frames()
|
||||
|
@ -268,55 +335,67 @@ def refresh_stack(node: sch.Schema('Stack')):
|
|||
|
||||
|
||||
@REGISTRY.method(action='refresh', display='Refresh Registers')
|
||||
def refresh_registers(node: sch.Schema('RegisterValueContainer')):
|
||||
"""Refresh the register values for the selected frame"""
|
||||
def refresh_registers(node: RegisterValueContainer) -> None:
|
||||
"""Refresh the register values for the selected frame."""
|
||||
tnum = find_thread_by_regs_obj(node)
|
||||
with commands.open_tracked_tx('Refresh Registers'):
|
||||
commands.ghidra_trace_putreg()
|
||||
|
||||
|
||||
@REGISTRY.method(action='refresh', display='Refresh Memory')
|
||||
def refresh_mappings(node: sch.Schema('Memory')):
|
||||
def refresh_mappings(node: Memory) -> None:
|
||||
"""Refresh the list of memory regions for the process."""
|
||||
with commands.open_tracked_tx('Refresh Memory Regions'):
|
||||
commands.ghidra_trace_put_regions()
|
||||
|
||||
|
||||
@REGISTRY.method(action='refresh', display='Refresh Modules')
|
||||
def refresh_modules(node: sch.Schema('ModuleContainer')):
|
||||
"""
|
||||
Refresh the modules and sections list for the process.
|
||||
def refresh_modules(node: ModuleContainer) -> None:
|
||||
"""Refresh the modules and sections list for the process.
|
||||
|
||||
This will refresh the sections for all modules, not just the selected one.
|
||||
This will refresh the sections for all modules, not just the
|
||||
selected one.
|
||||
"""
|
||||
with commands.open_tracked_tx('Refresh Modules'):
|
||||
commands.ghidra_trace_put_modules()
|
||||
|
||||
|
||||
@REGISTRY.method(action='refresh', display='Refresh Events')
|
||||
def refresh_events(node: sch.Schema('State')):
|
||||
"""
|
||||
Refresh the events list for a trace.
|
||||
"""
|
||||
def refresh_events(node: State) -> None:
|
||||
"""Refresh the events list for a trace."""
|
||||
with commands.open_tracked_tx('Refresh Events'):
|
||||
commands.ghidra_trace_put_events(node)
|
||||
commands.ghidra_trace_put_events()
|
||||
|
||||
|
||||
@util.dbg.eng_thread
|
||||
def do_maybe_activate_time(time: Optional[str]) -> None:
|
||||
if time is not None:
|
||||
sch: Schedule = Schedule.parse(time)
|
||||
dbg().cmd(f"!tt " + util.schedule2ss(sch), quiet=False)
|
||||
dbg().wait()
|
||||
|
||||
|
||||
@REGISTRY.method(action='activate')
|
||||
def activate_process(process: sch.Schema('Process')):
|
||||
def activate_process(process: Process,
|
||||
time: Optional[str] = None) -> None:
|
||||
"""Switch to the process."""
|
||||
do_maybe_activate_time(time)
|
||||
find_proc_by_obj(process)
|
||||
|
||||
|
||||
@REGISTRY.method(action='activate')
|
||||
def activate_thread(thread: sch.Schema('Thread')):
|
||||
def activate_thread(thread: Thread,
|
||||
time: Optional[str] = None) -> None:
|
||||
"""Switch to the thread."""
|
||||
do_maybe_activate_time(time)
|
||||
find_thread_by_obj(thread)
|
||||
|
||||
|
||||
@REGISTRY.method(action='activate')
|
||||
def activate_frame(frame: sch.Schema('StackFrame')):
|
||||
def activate_frame(frame: StackFrame,
|
||||
time: Optional[str] = None) -> None:
|
||||
"""Select the frame."""
|
||||
do_maybe_activate_time(time)
|
||||
f = find_frame_by_obj(frame)
|
||||
util.select_frame(f.FrameNumber)
|
||||
with commands.open_tracked_tx('Refresh Stack'):
|
||||
|
@ -327,7 +406,7 @@ def activate_frame(frame: sch.Schema('StackFrame')):
|
|||
|
||||
@REGISTRY.method(action='delete')
|
||||
@util.dbg.eng_thread
|
||||
def remove_process(process: sch.Schema('Process')):
|
||||
def remove_process(process: Process) -> None:
|
||||
"""Remove the process."""
|
||||
find_proc_by_obj(process)
|
||||
dbg().detach_proc()
|
||||
|
@ -336,15 +415,15 @@ def remove_process(process: sch.Schema('Process')):
|
|||
@REGISTRY.method(action='connect', display='Connect')
|
||||
@util.dbg.eng_thread
|
||||
def target(
|
||||
session: sch.Schema('Session'),
|
||||
cmd: ParamDesc(str, display='Command')):
|
||||
session: Session,
|
||||
cmd: Annotated[str, ParamDesc(display='Command')]) -> None:
|
||||
"""Connect to a target machine or process."""
|
||||
dbg().attach_kernel(cmd)
|
||||
|
||||
|
||||
@REGISTRY.method(action='attach', display='Attach')
|
||||
@util.dbg.eng_thread
|
||||
def attach_obj(target: sch.Schema('Attachable')):
|
||||
def attach_obj(target: Attachable) -> None:
|
||||
"""Attach the process to the given target."""
|
||||
pid = find_availpid_by_obj(target)
|
||||
dbg().attach_proc(pid)
|
||||
|
@ -353,82 +432,90 @@ def attach_obj(target: sch.Schema('Attachable')):
|
|||
@REGISTRY.method(action='attach', display='Attach by pid')
|
||||
@util.dbg.eng_thread
|
||||
def attach_pid(
|
||||
session: sch.Schema('Session'),
|
||||
pid: ParamDesc(str, display='PID')):
|
||||
session: Session,
|
||||
pid: Annotated[int, ParamDesc(display='PID')]) -> None:
|
||||
"""Attach the process to the given target."""
|
||||
dbg().attach_proc(int(pid))
|
||||
dbg().attach_proc(pid)
|
||||
|
||||
|
||||
@REGISTRY.method(action='attach', display='Attach by name')
|
||||
@util.dbg.eng_thread
|
||||
def attach_name(
|
||||
session: sch.Schema('Session'),
|
||||
name: ParamDesc(str, display='Name')):
|
||||
session: Session,
|
||||
name: Annotated[str, ParamDesc(display='Name')]) -> None:
|
||||
"""Attach the process to the given target."""
|
||||
dbg().attach_proc(name)
|
||||
|
||||
|
||||
@REGISTRY.method(action='detach', display='Detach')
|
||||
@util.dbg.eng_thread
|
||||
def detach(process: sch.Schema('Process')):
|
||||
def detach(process: Process) -> None:
|
||||
"""Detach the process's target."""
|
||||
dbg().detach_proc()
|
||||
|
||||
|
||||
@REGISTRY.method(action='launch', display='Launch')
|
||||
def launch_loader(
|
||||
session: sch.Schema('Session'),
|
||||
file: ParamDesc(str, display='File'),
|
||||
args: ParamDesc(str, display='Arguments')=''):
|
||||
"""
|
||||
Start a native process with the given command line, stopping at the ntdll initial breakpoint.
|
||||
"""
|
||||
session: Session,
|
||||
file: Annotated[str, ParamDesc(display='File')],
|
||||
args: Annotated[str, ParamDesc(display='Arguments')] = '',
|
||||
timeout: Annotated[int, ParamDesc(display='Timeout')] = -1,
|
||||
wait: Annotated[bool, ParamDesc(
|
||||
display='Wait',
|
||||
description='Perform the initial WaitForEvents')] = False) -> None:
|
||||
"""Start a native process with the given command line, stopping at the
|
||||
ntdll initial breakpoint."""
|
||||
command = file
|
||||
if args != None:
|
||||
command += " " + args
|
||||
commands.ghidra_trace_create(command=file, start_trace=False)
|
||||
commands.ghidra_trace_create(command=command, start_trace=False,
|
||||
timeout=timeout, wait=wait)
|
||||
|
||||
|
||||
@REGISTRY.method(action='launch', display='LaunchEx')
|
||||
def launch(
|
||||
session: sch.Schema('Session'),
|
||||
file: ParamDesc(str, display='File'),
|
||||
args: ParamDesc(str, display='Arguments')='',
|
||||
initial_break: ParamDesc(bool, display='Initial Break')=True,
|
||||
timeout: ParamDesc(int, display='Timeout')=-1):
|
||||
"""
|
||||
Run a native process with the given command line.
|
||||
"""
|
||||
session: Session,
|
||||
file: Annotated[str, ParamDesc(display='File')],
|
||||
args: Annotated[str, ParamDesc(display='Arguments')] = '',
|
||||
initial_break: Annotated[bool, ParamDesc(
|
||||
display='Initial Break')] = True,
|
||||
timeout: Annotated[int, ParamDesc(display='Timeout')] = -1,
|
||||
wait: Annotated[bool, ParamDesc(
|
||||
display='Wait',
|
||||
description='Perform the initial WaitForEvents')] = False) -> None:
|
||||
"""Run a native process with the given command line."""
|
||||
command = file
|
||||
if args != None:
|
||||
command += " " + args
|
||||
commands.ghidra_trace_create(
|
||||
command, initial_break=initial_break, timeout=timeout, start_trace=False)
|
||||
commands.ghidra_trace_create(command=command, start_trace=False,
|
||||
initial_break=initial_break,
|
||||
timeout=timeout, wait=wait)
|
||||
|
||||
|
||||
@REGISTRY.method
|
||||
@REGISTRY.method()
|
||||
@util.dbg.eng_thread
|
||||
def kill(process: sch.Schema('Process')):
|
||||
def kill(process: Process) -> None:
|
||||
"""Kill execution of the process."""
|
||||
commands.ghidra_trace_kill()
|
||||
|
||||
|
||||
@REGISTRY.method(action='resume', display="Go")
|
||||
def go(process: sch.Schema('Process')):
|
||||
def go(process: Process) -> None:
|
||||
"""Continue execution of the process."""
|
||||
util.dbg.run_async(lambda: dbg().go())
|
||||
|
||||
|
||||
@REGISTRY.method(action='step_ext', display='Go (backwards)', icon='icon.debugger.resume.back', condition=util.dbg.IS_TRACE)
|
||||
@REGISTRY.method(action='step_ext', display='Go (backwards)',
|
||||
icon='icon.debugger.resume.back', condition=util.dbg.IS_TRACE)
|
||||
@util.dbg.eng_thread
|
||||
def go_back(thread: sch.Schema('Process')):
|
||||
def go_back(process: Process) -> None:
|
||||
"""Continue execution of the process backwards."""
|
||||
dbg().cmd("g-")
|
||||
dbg().wait()
|
||||
|
||||
|
||||
@REGISTRY.method
|
||||
def interrupt(process: sch.Schema('Process')):
|
||||
@REGISTRY.method()
|
||||
def interrupt(process: Process) -> None:
|
||||
"""Interrupt the execution of the debugged program."""
|
||||
# SetInterrupt is reentrant, so bypass the thread checks
|
||||
util.dbg._protected_base._control.SetInterrupt(
|
||||
|
@ -436,53 +523,64 @@ def interrupt(process: sch.Schema('Process')):
|
|||
|
||||
|
||||
@REGISTRY.method(action='step_into')
|
||||
def step_into(thread: sch.Schema('Thread'), n: ParamDesc(int, display='N')=1):
|
||||
def step_into(thread: Thread,
|
||||
n: Annotated[int, ParamDesc(display='N')] = 1) -> None:
|
||||
"""Step one instruction exactly."""
|
||||
find_thread_by_obj(thread)
|
||||
util.dbg.run_async(lambda: dbg().stepi(n))
|
||||
|
||||
|
||||
@REGISTRY.method(action='step_over')
|
||||
def step_over(thread: sch.Schema('Thread'), n: ParamDesc(int, display='N')=1):
|
||||
def step_over(thread: Thread,
|
||||
n: Annotated[int, ParamDesc(display='N')] = 1) -> None:
|
||||
"""Step one instruction, but proceed through subroutine calls."""
|
||||
find_thread_by_obj(thread)
|
||||
util.dbg.run_async(lambda: dbg().stepo(n))
|
||||
|
||||
|
||||
@REGISTRY.method(action='step_ext', display='Step Into (backwards)', icon='icon.debugger.step.back.into', condition=util.dbg.IS_TRACE)
|
||||
@REGISTRY.method(action='step_ext', display='Step Into (backwards)',
|
||||
icon='icon.debugger.step.back.into',
|
||||
condition=util.dbg.IS_TRACE)
|
||||
@util.dbg.eng_thread
|
||||
def step_back_into(thread: sch.Schema('Thread'), n: ParamDesc(int, display='N')=1):
|
||||
def step_back_into(thread: Thread,
|
||||
n: Annotated[int, ParamDesc(display='N')] = 1) -> None:
|
||||
"""Step one instruction backward exactly."""
|
||||
dbg().cmd("t- " + str(n))
|
||||
dbg().wait()
|
||||
|
||||
|
||||
@REGISTRY.method(action='step_ext', display='Step Over (backwards)', icon='icon.debugger.step.back.over', condition=util.dbg.IS_TRACE)
|
||||
@REGISTRY.method(action='step_ext', display='Step Over (backwards)',
|
||||
icon='icon.debugger.step.back.over',
|
||||
condition=util.dbg.IS_TRACE)
|
||||
@util.dbg.eng_thread
|
||||
def step_back_over(thread: sch.Schema('Thread'), n: ParamDesc(int, display='N')=1):
|
||||
def step_back_over(thread: Thread,
|
||||
n: Annotated[int, ParamDesc(display='N')] = 1) -> None:
|
||||
"""Step one instruction backward, but proceed through subroutine calls."""
|
||||
dbg().cmd("p- " + str(n))
|
||||
dbg().wait()
|
||||
|
||||
|
||||
@REGISTRY.method(action='step_out')
|
||||
def step_out(thread: sch.Schema('Thread')):
|
||||
def step_out(thread: Thread) -> None:
|
||||
"""Execute until the current stack frame returns."""
|
||||
find_thread_by_obj(thread)
|
||||
util.dbg.run_async(lambda: dbg().stepout())
|
||||
|
||||
|
||||
@REGISTRY.method(action='step_to', display='Step To')
|
||||
def step_to(thread: sch.Schema('Thread'), address: Address, max=None):
|
||||
def step_to(thread: Thread, address: Address,
|
||||
max: Optional[int] = None) -> None:
|
||||
"""Continue execution up to the given address."""
|
||||
find_thread_by_obj(thread)
|
||||
# TODO: The address may need mapping.
|
||||
util.dbg.run_async(lambda: dbg().stepto(address.offset, max))
|
||||
|
||||
|
||||
@REGISTRY.method(action='go_to_time', display='Go To (event)', condition=util.dbg.IS_TRACE)
|
||||
@REGISTRY.method(action='go_to_time', display='Go To (event)',
|
||||
condition=util.dbg.IS_TRACE)
|
||||
@util.dbg.eng_thread
|
||||
def go_to_time(node: sch.Schema('State'), evt: ParamDesc(str, display='Event')):
|
||||
def go_to_time(node: State,
|
||||
evt: Annotated[str, ParamDesc(display='Event')]) -> None:
|
||||
"""Reset the trace to a specific time."""
|
||||
dbg().cmd("!tt " + evt)
|
||||
dbg().wait()
|
||||
|
@ -490,7 +588,7 @@ def go_to_time(node: sch.Schema('State'), evt: ParamDesc(str, display='Event')):
|
|||
|
||||
@REGISTRY.method(action='break_sw_execute')
|
||||
@util.dbg.eng_thread
|
||||
def break_address(process: sch.Schema('Process'), address: Address):
|
||||
def break_address(process: Process, address: Address) -> None:
|
||||
"""Set a breakpoint."""
|
||||
find_proc_by_obj(process)
|
||||
dbg().bp(expr=address.offset)
|
||||
|
@ -498,7 +596,7 @@ def break_address(process: sch.Schema('Process'), address: Address):
|
|||
|
||||
@REGISTRY.method(action='break_ext', display='Set Breakpoint')
|
||||
@util.dbg.eng_thread
|
||||
def break_expression(expression: str):
|
||||
def break_expression(expression: str) -> None:
|
||||
"""Set a breakpoint."""
|
||||
# TODO: Escape?
|
||||
dbg().bp(expr=expression)
|
||||
|
@ -506,7 +604,7 @@ def break_expression(expression: str):
|
|||
|
||||
@REGISTRY.method(action='break_hw_execute')
|
||||
@util.dbg.eng_thread
|
||||
def break_hw_address(process: sch.Schema('Process'), address: Address):
|
||||
def break_hw_address(process: Process, address: Address) -> None:
|
||||
"""Set a hardware-assisted breakpoint."""
|
||||
find_proc_by_obj(process)
|
||||
dbg().ba(expr=address.offset)
|
||||
|
@ -514,44 +612,46 @@ def break_hw_address(process: sch.Schema('Process'), address: Address):
|
|||
|
||||
@REGISTRY.method(action='break_ext', display='Set Hardware Breakpoint')
|
||||
@util.dbg.eng_thread
|
||||
def break_hw_expression(expression: str):
|
||||
def break_hw_expression(expression: str) -> None:
|
||||
"""Set a hardware-assisted breakpoint."""
|
||||
dbg().ba(expr=expression)
|
||||
|
||||
|
||||
@REGISTRY.method(action='break_read')
|
||||
@util.dbg.eng_thread
|
||||
def break_read_range(process: sch.Schema('Process'), range: AddressRange):
|
||||
def break_read_range(process: Process, range: AddressRange) -> None:
|
||||
"""Set a read breakpoint."""
|
||||
find_proc_by_obj(process)
|
||||
dbg().ba(expr=range.min, size=range.length(), access=DbgEng.DEBUG_BREAK_READ)
|
||||
dbg().ba(expr=range.min, size=range.length(),
|
||||
access=DbgEng.DEBUG_BREAK_READ)
|
||||
|
||||
|
||||
@REGISTRY.method(action='break_ext', display='Set Read Breakpoint')
|
||||
@util.dbg.eng_thread
|
||||
def break_read_expression(expression: str):
|
||||
def break_read_expression(expression: str) -> None:
|
||||
"""Set a read breakpoint."""
|
||||
dbg().ba(expr=expression, access=DbgEng.DEBUG_BREAK_READ)
|
||||
|
||||
|
||||
@REGISTRY.method(action='break_write')
|
||||
@util.dbg.eng_thread
|
||||
def break_write_range(process: sch.Schema('Process'), range: AddressRange):
|
||||
def break_write_range(process: Process, range: AddressRange) -> None:
|
||||
"""Set a write breakpoint."""
|
||||
find_proc_by_obj(process)
|
||||
dbg().ba(expr=range.min, size=range.length(), access=DbgEng.DEBUG_BREAK_WRITE)
|
||||
dbg().ba(expr=range.min, size=range.length(),
|
||||
access=DbgEng.DEBUG_BREAK_WRITE)
|
||||
|
||||
|
||||
@REGISTRY.method(action='break_ext', display='Set Write Breakpoint')
|
||||
@util.dbg.eng_thread
|
||||
def break_write_expression(expression: str):
|
||||
def break_write_expression(expression: str) -> None:
|
||||
"""Set a write breakpoint."""
|
||||
dbg().ba(expr=expression, access=DbgEng.DEBUG_BREAK_WRITE)
|
||||
|
||||
|
||||
@REGISTRY.method(action='break_access')
|
||||
@util.dbg.eng_thread
|
||||
def break_access_range(process: sch.Schema('Process'), range: AddressRange):
|
||||
def break_access_range(process: Process, range: AddressRange) -> None:
|
||||
"""Set an access breakpoint."""
|
||||
find_proc_by_obj(process)
|
||||
dbg().ba(expr=range.min, size=range.length(),
|
||||
|
@ -560,14 +660,15 @@ def break_access_range(process: sch.Schema('Process'), range: AddressRange):
|
|||
|
||||
@REGISTRY.method(action='break_ext', display='Set Access Breakpoint')
|
||||
@util.dbg.eng_thread
|
||||
def break_access_expression(expression: str):
|
||||
def break_access_expression(expression: str) -> None:
|
||||
"""Set an access breakpoint."""
|
||||
dbg().ba(expr=expression, access=DbgEng.DEBUG_BREAK_READ | DbgEng.DEBUG_BREAK_WRITE)
|
||||
dbg().ba(expr=expression,
|
||||
access=DbgEng.DEBUG_BREAK_READ | DbgEng.DEBUG_BREAK_WRITE)
|
||||
|
||||
|
||||
@REGISTRY.method(action='toggle', display='Toggle Breakpoint')
|
||||
@util.dbg.eng_thread
|
||||
def toggle_breakpoint(breakpoint: sch.Schema('BreakpointSpec'), enabled: bool):
|
||||
def toggle_breakpoint(breakpoint: BreakpointSpec, enabled: bool) -> None:
|
||||
"""Toggle a breakpoint."""
|
||||
bpt = find_bpt_by_obj(breakpoint)
|
||||
if enabled:
|
||||
|
@ -578,50 +679,53 @@ def toggle_breakpoint(breakpoint: sch.Schema('BreakpointSpec'), enabled: bool):
|
|||
|
||||
@REGISTRY.method(action='delete', display='Delete Breakpoint')
|
||||
@util.dbg.eng_thread
|
||||
def delete_breakpoint(breakpoint: sch.Schema('BreakpointSpec')):
|
||||
def delete_breakpoint(breakpoint: BreakpointSpec) -> None:
|
||||
"""Delete a breakpoint."""
|
||||
bpt = find_bpt_by_obj(breakpoint)
|
||||
dbg().cmd("bc {}".format(bpt.GetId()))
|
||||
|
||||
|
||||
@REGISTRY.method
|
||||
@REGISTRY.method()
|
||||
@util.dbg.eng_thread
|
||||
def read_mem(process: sch.Schema('Process'), range: AddressRange):
|
||||
def read_mem(process: Process, range: AddressRange) -> None:
|
||||
"""Read memory."""
|
||||
# print("READ_MEM: process={}, range={}".format(process, range))
|
||||
nproc = find_proc_by_obj(process)
|
||||
offset_start = process.trace.memory_mapper.map_back(
|
||||
offset_start = process.trace.extra.require_mm().map_back(
|
||||
nproc, Address(range.space, range.min))
|
||||
with commands.open_tracked_tx('Read Memory'):
|
||||
result = commands.put_bytes(
|
||||
offset_start, offset_start + range.length() - 1, pages=True, display_result=False)
|
||||
offset_start, offset_start + range.length() - 1, pages=True,
|
||||
display_result=False)
|
||||
if result['count'] == 0:
|
||||
commands.putmem_state(
|
||||
offset_start, offset_start + range.length() - 1, 'error')
|
||||
|
||||
|
||||
@REGISTRY.method
|
||||
@REGISTRY.method()
|
||||
@util.dbg.eng_thread
|
||||
def write_mem(process: sch.Schema('Process'), address: Address, data: bytes):
|
||||
def write_mem(process: Process, address: Address, data: bytes) -> None:
|
||||
"""Write memory."""
|
||||
nproc = find_proc_by_obj(process)
|
||||
offset = process.trace.memory_mapper.map_back(nproc, address)
|
||||
offset = process.trace.extra.required_mm().map_back(nproc, address)
|
||||
dbg().write(offset, data)
|
||||
|
||||
|
||||
@REGISTRY.method
|
||||
@REGISTRY.method()
|
||||
@util.dbg.eng_thread
|
||||
def write_reg(frame: sch.Schema('StackFrame'), name: str, value: bytes):
|
||||
def write_reg(frame: StackFrame, name: str, value: bytes) -> None:
|
||||
"""Write a register."""
|
||||
util.select_frame()
|
||||
f = find_frame_by_obj(frame)
|
||||
util.select_frame(f.FrameNumber)
|
||||
nproc = pydbg.selected_process()
|
||||
dbg().reg._set_register(name, value)
|
||||
|
||||
|
||||
@REGISTRY.method(display='Refresh Events (custom)', condition=util.dbg.IS_TRACE)
|
||||
@util.dbg.eng_thread
|
||||
def refresh_events_custom(node: sch.Schema('State'), cmd: ParamDesc(str, display='Cmd'),
|
||||
prefix: ParamDesc(str, display='Prefix')="dx -r2 @$cursession.TTD"):
|
||||
def refresh_events_custom(node: State,
|
||||
cmd: Annotated[str, ParamDesc(display='Cmd')],
|
||||
prefix: Annotated[str, ParamDesc(display='Prefix')] = "dx -r2 @$cursession.TTD") -> None:
|
||||
"""Parse TTD objects generated from a LINQ command."""
|
||||
with commands.open_tracked_tx('Put Events (custom)'):
|
||||
commands.ghidra_trace_put_events_custom(prefix, cmd)
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
<context>
|
||||
<schema name="DbgRoot" canonical="yes" elementResync="NEVER" attributeResync="NEVER">
|
||||
<interface name="EventScope" />
|
||||
<attribute name="Sessions" schema="SessionContainer" required="yes" fixed="yes" />
|
||||
<attribute name="Settings" schema="ANY" />
|
||||
<attribute name="State" schema="State" />
|
||||
|
@ -16,7 +17,6 @@
|
|||
</schema>
|
||||
<schema name="Session" elementResync="NEVER" attributeResync="NEVER">
|
||||
<interface name="Activatable" />
|
||||
<interface name="EventScope" />
|
||||
<interface name="FocusScope" />
|
||||
<interface name="Aggregate" />
|
||||
<element schema="VOID" />
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
<context>
|
||||
<schema name="DbgRoot" canonical="yes" elementResync="NEVER" attributeResync="NEVER">
|
||||
<interface name="EventScope" />
|
||||
<attribute name="Sessions" schema="SessionContainer" required="yes" fixed="yes" />
|
||||
<attribute name="Settings" schema="ANY" />
|
||||
<attribute name="State" schema="ANY" />
|
||||
|
@ -16,7 +17,6 @@
|
|||
</schema>
|
||||
<schema name="Session" elementResync="NEVER" attributeResync="NEVER">
|
||||
<interface name="Activatable" />
|
||||
<interface name="EventScope" />
|
||||
<interface name="FocusScope" />
|
||||
<interface name="Aggregate" />
|
||||
<element schema="VOID" />
|
||||
|
|
|
@ -13,10 +13,16 @@
|
|||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
##
|
||||
from comtypes.automation import VARIANT
|
||||
|
||||
from ghidratrace.client import Schedule
|
||||
from .dbgmodel.imodelobject import ModelObject
|
||||
from capstone import CsInsn
|
||||
from _winapi import STILL_ACTIVE
|
||||
from collections import namedtuple
|
||||
from concurrent.futures import Future
|
||||
import concurrent.futures
|
||||
from ctypes import *
|
||||
from ctypes import POINTER, byref, c_ulong, c_ulonglong, create_string_buffer
|
||||
import functools
|
||||
import io
|
||||
import os
|
||||
|
@ -25,11 +31,14 @@ import re
|
|||
import sys
|
||||
import threading
|
||||
import traceback
|
||||
from typing import Any, Callable, Dict, Iterable, List, Optional, Sequence, Tuple, TypeVar, Union, cast
|
||||
|
||||
from comtypes import CoClass, GUID
|
||||
import comtypes
|
||||
from comtypes.gen import DbgMod
|
||||
from comtypes.hresult import S_OK, S_FALSE
|
||||
from ghidradbg.dbgmodel.ihostdatamodelaccess import HostDataModelAccess
|
||||
from ghidradbg.dbgmodel.imodelmethod import ModelMethod
|
||||
from pybag import pydbg, userdbg, kerneldbg, crashdbg
|
||||
from pybag.dbgeng import core as DbgEng
|
||||
from pybag.dbgeng import exception
|
||||
|
@ -37,9 +46,7 @@ from pybag.dbgeng import util as DbgUtil
|
|||
from pybag.dbgeng.callbacks import DbgEngCallbacks
|
||||
from pybag.dbgeng.idebugclient import DebugClient
|
||||
|
||||
from ghidradbg.dbgmodel.ihostdatamodelaccess import HostDataModelAccess
|
||||
from ghidradbg.dbgmodel.imodelmethod import ModelMethod
|
||||
from _winapi import STILL_ACTIVE
|
||||
DESCRIPTION_PATTERN = '[{major:X}:{minor:X}] {type}'
|
||||
|
||||
DbgVersion = namedtuple('DbgVersion', ['full', 'name', 'dotted', 'arch'])
|
||||
|
||||
|
@ -132,23 +139,27 @@ class DebuggeeRunningException(BaseException):
|
|||
pass
|
||||
|
||||
|
||||
T = TypeVar('T')
|
||||
|
||||
|
||||
class DbgExecutor(object):
|
||||
|
||||
def __init__(self, ghidra_dbg):
|
||||
def __init__(self, ghidra_dbg: 'GhidraDbg') -> None:
|
||||
self._ghidra_dbg = ghidra_dbg
|
||||
self._work_queue = queue.SimpleQueue()
|
||||
self._work_queue: queue.SimpleQueue = queue.SimpleQueue()
|
||||
self._thread = _Worker(ghidra_dbg._new_base,
|
||||
self._work_queue, ghidra_dbg._dispatch_events)
|
||||
self._thread.start()
|
||||
self._executing = False
|
||||
|
||||
def submit(self, fn, / , *args, **kwargs):
|
||||
def submit(self, fn: Callable[..., T], /, *args, **kwargs) -> Future[T]:
|
||||
f = self._submit_no_exit(fn, *args, **kwargs)
|
||||
self._ghidra_dbg.exit_dispatch()
|
||||
return f
|
||||
|
||||
def _submit_no_exit(self, fn, / , *args, **kwargs):
|
||||
f = Future()
|
||||
def _submit_no_exit(self, fn: Callable[..., T], /,
|
||||
*args, **kwargs) -> Future[T]:
|
||||
f: Future[T] = Future()
|
||||
if self._executing and self._ghidra_dbg.IS_REMOTE == False:
|
||||
f.set_exception(DebuggeeRunningException("Debuggee is Running"))
|
||||
return f
|
||||
|
@ -156,7 +167,7 @@ class DbgExecutor(object):
|
|||
self._work_queue.put(w)
|
||||
return f
|
||||
|
||||
def _clear_queue(self):
|
||||
def _clear_queue(self) -> None:
|
||||
while True:
|
||||
try:
|
||||
work_item = self._work_queue.get_nowait()
|
||||
|
@ -165,12 +176,12 @@ class DbgExecutor(object):
|
|||
work_item.future.set_exception(
|
||||
DebuggeeRunningException("Debuggee is Running"))
|
||||
|
||||
def _state_execute(self):
|
||||
def _state_execute(self) -> None:
|
||||
self._executing = True
|
||||
if self._ghidra_dbg.IS_REMOTE == False:
|
||||
self._clear_queue()
|
||||
|
||||
def _state_break(self):
|
||||
def _state_break(self) -> None:
|
||||
self._executing = False
|
||||
|
||||
|
||||
|
@ -201,9 +212,12 @@ class AllDbg(pydbg.DebuggerBase):
|
|||
load_dump = crashdbg.CrashDbg.load_dump
|
||||
|
||||
|
||||
C = TypeVar('C', bound=Callable[..., Any])
|
||||
|
||||
|
||||
class GhidraDbg(object):
|
||||
|
||||
def __init__(self):
|
||||
def __init__(self) -> None:
|
||||
self._queue = DbgExecutor(self)
|
||||
self._thread = self._queue._thread
|
||||
# Wait for the executor to be operational before getting base
|
||||
|
@ -245,10 +259,10 @@ class GhidraDbg(object):
|
|||
setattr(self, name, self.eng_thread(getattr(base, name)))
|
||||
self.IS_KERNEL = False
|
||||
self.IS_EXDI = False
|
||||
self.IS_REMOTE = os.getenv('OPT_CONNECT_STRING') is not None
|
||||
self.IS_TRACE = os.getenv('USE_TTD') == "true"
|
||||
|
||||
def _new_base(self):
|
||||
self.IS_REMOTE: bool = os.getenv('OPT_CONNECT_STRING') is not None
|
||||
self.IS_TRACE: bool = os.getenv('USE_TTD') == "true"
|
||||
|
||||
def _new_base(self) -> None:
|
||||
remote = os.getenv('OPT_CONNECT_STRING')
|
||||
if remote is not None:
|
||||
remote_client = DbgEng.DebugConnect(remote)
|
||||
|
@ -256,8 +270,8 @@ class GhidraDbg(object):
|
|||
self._protected_base = AllDbg(client=debug_client)
|
||||
else:
|
||||
self._protected_base = AllDbg()
|
||||
|
||||
def _generate_client(self, original):
|
||||
|
||||
def _generate_client(self, original: DebugClient) -> DebugClient:
|
||||
cli = POINTER(DbgEng.IDebugClient)()
|
||||
cliptr = POINTER(POINTER(DbgEng.IDebugClient))(cli)
|
||||
hr = original.CreateClient(cliptr)
|
||||
|
@ -265,13 +279,13 @@ class GhidraDbg(object):
|
|||
return DebugClient(client=cli)
|
||||
|
||||
@property
|
||||
def _base(self):
|
||||
def _base(self) -> AllDbg:
|
||||
if threading.current_thread() is not self._thread:
|
||||
raise WrongThreadException("Was {}. Want {}".format(
|
||||
threading.current_thread(), self._thread))
|
||||
return self._protected_base
|
||||
|
||||
def run(self, fn, *args, **kwargs):
|
||||
def run(self, fn: Callable[..., T], *args, **kwargs) -> T:
|
||||
# TODO: Remove this check?
|
||||
if hasattr(self, '_thread') and threading.current_thread() is self._thread:
|
||||
raise WrongThreadException()
|
||||
|
@ -283,64 +297,60 @@ class GhidraDbg(object):
|
|||
except concurrent.futures.TimeoutError:
|
||||
pass
|
||||
|
||||
def run_async(self, fn, *args, **kwargs):
|
||||
def run_async(self, fn: Callable[..., T], *args, **kwargs) -> Future[T]:
|
||||
return self._queue.submit(fn, *args, **kwargs)
|
||||
|
||||
@staticmethod
|
||||
def check_thread(func):
|
||||
'''
|
||||
For methods inside of GhidraDbg, ensure it runs on the dbgeng
|
||||
thread
|
||||
'''
|
||||
def check_thread(func: C) -> C:
|
||||
"""For methods inside of GhidraDbg, ensure it runs on the dbgeng
|
||||
thread."""
|
||||
|
||||
@functools.wraps(func)
|
||||
def _func(self, *args, **kwargs):
|
||||
def _func(self, *args, **kwargs) -> Any:
|
||||
if threading.current_thread() is self._thread:
|
||||
return func(self, *args, **kwargs)
|
||||
else:
|
||||
return self.run(func, self, *args, **kwargs)
|
||||
|
||||
return _func
|
||||
return cast(C, _func)
|
||||
|
||||
def eng_thread(self, func):
|
||||
'''
|
||||
For methods and functions outside of GhidraDbg, ensure it
|
||||
runs on this GhidraDbg's dbgeng thread
|
||||
'''
|
||||
def eng_thread(self, func: C) -> C:
|
||||
"""For methods and functions outside of GhidraDbg, ensure it runs on
|
||||
this GhidraDbg's dbgeng thread."""
|
||||
|
||||
@functools.wraps(func)
|
||||
def _func(*args, **kwargs):
|
||||
def _func(*args, **kwargs) -> Any:
|
||||
if threading.current_thread() is self._thread:
|
||||
return func(*args, **kwargs)
|
||||
else:
|
||||
return self.run(func, *args, **kwargs)
|
||||
|
||||
return _func
|
||||
return cast(C, _func)
|
||||
|
||||
def _ces_exec_status(self, argument):
|
||||
def _ces_exec_status(self, argument: int):
|
||||
if argument & 0xfffffff == DbgEng.DEBUG_STATUS_BREAK:
|
||||
self._queue._state_break()
|
||||
else:
|
||||
self._queue._state_execute()
|
||||
|
||||
@check_thread
|
||||
def _install_stdin(self):
|
||||
def _install_stdin(self) -> None:
|
||||
self.input = StdInputCallbacks(self)
|
||||
self._base._client.SetInputCallbacks(self.input)
|
||||
|
||||
# Manually decorated to preserve undecorated
|
||||
def _dispatch_events(self, timeout=DbgEng.WAIT_INFINITE):
|
||||
def _dispatch_events(self, timeout: int = DbgEng.WAIT_INFINITE) -> None:
|
||||
# NB: pybag's impl doesn't heed standalone
|
||||
self._protected_base._client.DispatchCallbacks(timeout)
|
||||
|
||||
dispatch_events = check_thread(_dispatch_events)
|
||||
|
||||
# no check_thread. Must allow reentry
|
||||
def exit_dispatch(self):
|
||||
def exit_dispatch(self) -> None:
|
||||
self._protected_base._client.ExitDispatch()
|
||||
|
||||
@check_thread
|
||||
def cmd(self, cmdline, quiet=True):
|
||||
def cmd(self, cmdline: str, quiet: bool = True) -> str:
|
||||
# NB: pybag's impl always captures.
|
||||
# Here, we let it print without capture if quiet is False
|
||||
if quiet:
|
||||
|
@ -356,20 +366,20 @@ class GhidraDbg(object):
|
|||
return self._base._control.Execute(cmdline)
|
||||
|
||||
@check_thread
|
||||
def return_input(self, input):
|
||||
def return_input(self, input: str) -> None:
|
||||
# TODO: Contribute fix upstream (check_hr -> check_err)
|
||||
# return self._base._control.ReturnInput(input.encode())
|
||||
hr = self._base._control._ctrl.ReturnInput(input.encode())
|
||||
exception.check_err(hr)
|
||||
|
||||
def interrupt(self):
|
||||
def interrupt(self) -> None:
|
||||
# Contribute upstream?
|
||||
# NOTE: This can be called from any thread
|
||||
self._protected_base._control.SetInterrupt(
|
||||
DbgEng.DEBUG_INTERRUPT_ACTIVE)
|
||||
|
||||
@check_thread
|
||||
def get_current_system_id(self):
|
||||
def get_current_system_id(self) -> int:
|
||||
# TODO: upstream?
|
||||
sys_id = c_ulong()
|
||||
hr = self._base._systems._sys.GetCurrentSystemId(byref(sys_id))
|
||||
|
@ -377,7 +387,7 @@ class GhidraDbg(object):
|
|||
return sys_id.value
|
||||
|
||||
@check_thread
|
||||
def get_prompt_text(self):
|
||||
def get_prompt_text(self) -> str:
|
||||
# TODO: upstream?
|
||||
size = c_ulong()
|
||||
hr = self._base._control._ctrl.GetPromptText(None, 0, byref(size))
|
||||
|
@ -386,12 +396,12 @@ class GhidraDbg(object):
|
|||
return prompt_buf.value.decode()
|
||||
|
||||
@check_thread
|
||||
def get_actual_processor_type(self):
|
||||
def get_actual_processor_type(self) -> int:
|
||||
return self._base._control.GetActualProcessorType()
|
||||
|
||||
@property
|
||||
@check_thread
|
||||
def pid(self):
|
||||
def pid(self) -> Optional[int]:
|
||||
try:
|
||||
if is_kernel():
|
||||
return 0
|
||||
|
@ -403,17 +413,12 @@ class GhidraDbg(object):
|
|||
|
||||
class TTDState(object):
|
||||
|
||||
def __init__(self):
|
||||
self._cursor = None
|
||||
self._first = None
|
||||
self._last = None
|
||||
self._lastmajor = None
|
||||
self._lastpos = None
|
||||
self.breakpoints = []
|
||||
self.events = {}
|
||||
self.evttypes = {}
|
||||
self.starts = {}
|
||||
self.stops = {}
|
||||
def __init__(self) -> None:
|
||||
self._first: Optional[Tuple[int, int]] = None
|
||||
self._last: Optional[Tuple[int, int]] = None
|
||||
self._lastpos: Optional[Tuple[int, int]] = None
|
||||
self.evttypes: Dict[Tuple[int, int], str] = {}
|
||||
self.MAX_STEP: int
|
||||
|
||||
|
||||
dbg = GhidraDbg()
|
||||
|
@ -421,16 +426,16 @@ ttd = TTDState()
|
|||
|
||||
|
||||
@dbg.eng_thread
|
||||
def compute_pydbg_ver():
|
||||
def compute_pydbg_ver() -> DbgVersion:
|
||||
pat = re.compile(
|
||||
'(?P<name>.*Debugger.*) Version (?P<dotted>[\\d\\.]*) (?P<arch>\\w*)')
|
||||
blurb = dbg.cmd('version')
|
||||
matches = [pat.match(l) for l in blurb.splitlines() if pat.match(l)]
|
||||
matches_opt = [pat.match(l) for l in blurb.splitlines()]
|
||||
matches = [m for m in matches_opt if m is not None]
|
||||
if len(matches) == 0:
|
||||
return DbgVersion('Unknown', 'Unknown', '0', 'Unknown')
|
||||
m = matches[0]
|
||||
return DbgVersion(full=m.group(), **m.groupdict())
|
||||
name, dotted_and_arch = full.split(' Version ')
|
||||
|
||||
|
||||
DBG_VERSION = compute_pydbg_ver()
|
||||
|
@ -441,26 +446,27 @@ def get_target():
|
|||
|
||||
|
||||
@dbg.eng_thread
|
||||
def disassemble1(addr):
|
||||
return DbgUtil.disassemble_instruction(dbg._base.bitness(), addr, dbg.read(addr, 15))
|
||||
def disassemble1(addr: int) -> CsInsn:
|
||||
data = dbg.read(addr, 15) # type:ignore
|
||||
return DbgUtil.disassemble_instruction(dbg._base.bitness(), addr, data)
|
||||
|
||||
|
||||
def get_inst(addr):
|
||||
def get_inst(addr: int) -> str:
|
||||
return str(disassemble1(addr))
|
||||
|
||||
|
||||
def get_inst_sz(addr):
|
||||
def get_inst_sz(addr: int) -> int:
|
||||
return int(disassemble1(addr).size)
|
||||
|
||||
|
||||
@dbg.eng_thread
|
||||
def get_breakpoints():
|
||||
def get_breakpoints() -> Iterable[Tuple[str, str, str, str, str]]:
|
||||
ids = [bpid for bpid in dbg._base.breakpoints]
|
||||
offset_set = []
|
||||
expr_set = []
|
||||
prot_set = []
|
||||
width_set = []
|
||||
stat_set = []
|
||||
offset_set: List[str] = []
|
||||
expr_set: List[str] = []
|
||||
prot_set: List[str] = []
|
||||
width_set: List[str] = []
|
||||
stat_set: List[str] = []
|
||||
for bpid in ids:
|
||||
try:
|
||||
bp = dbg._base._control.GetBreakpointById(bpid)
|
||||
|
@ -496,7 +502,7 @@ def get_breakpoints():
|
|||
|
||||
|
||||
@dbg.eng_thread
|
||||
def selected_process():
|
||||
def selected_process() -> int:
|
||||
try:
|
||||
if is_exdi():
|
||||
return 0
|
||||
|
@ -504,7 +510,8 @@ def selected_process():
|
|||
do = dbg._base._systems.GetCurrentProcessDataOffset()
|
||||
id = c_ulong()
|
||||
offset = c_ulonglong(do)
|
||||
nproc = dbg._base._systems._sys.GetProcessIdByDataOffset(offset, byref(id))
|
||||
nproc = dbg._base._systems._sys.GetProcessIdByDataOffset(
|
||||
offset, byref(id))
|
||||
return id.value
|
||||
if dbg.use_generics:
|
||||
return dbg._base._systems.GetCurrentProcessSystemId()
|
||||
|
@ -515,7 +522,7 @@ def selected_process():
|
|||
|
||||
|
||||
@dbg.eng_thread
|
||||
def selected_process_space():
|
||||
def selected_process_space() -> int:
|
||||
try:
|
||||
if is_exdi():
|
||||
return 0
|
||||
|
@ -528,7 +535,7 @@ def selected_process_space():
|
|||
|
||||
|
||||
@dbg.eng_thread
|
||||
def selected_thread():
|
||||
def selected_thread() -> Optional[int]:
|
||||
try:
|
||||
if is_kernel():
|
||||
return 0
|
||||
|
@ -540,7 +547,7 @@ def selected_thread():
|
|||
|
||||
|
||||
@dbg.eng_thread
|
||||
def selected_frame():
|
||||
def selected_frame() -> Optional[int]:
|
||||
try:
|
||||
line = dbg.cmd('.frame').strip()
|
||||
if not line:
|
||||
|
@ -553,40 +560,47 @@ def selected_frame():
|
|||
return None
|
||||
|
||||
|
||||
def require(t: Optional[T]) -> T:
|
||||
if t is None:
|
||||
raise ValueError("Unexpected None")
|
||||
return t
|
||||
|
||||
|
||||
@dbg.eng_thread
|
||||
def select_process(id: int):
|
||||
def select_process(id: int) -> None:
|
||||
if is_kernel():
|
||||
# TODO: Ideally this should get the data offset from the id and then call
|
||||
# SetImplicitProcessDataOffset
|
||||
return
|
||||
if dbg.use_generics:
|
||||
id = get_proc_id(id)
|
||||
id = require(get_proc_id(id))
|
||||
return dbg._base._systems.SetCurrentProcessId(id)
|
||||
|
||||
|
||||
@dbg.eng_thread
|
||||
def select_thread(id: int):
|
||||
def select_thread(id: int) -> None:
|
||||
if is_kernel():
|
||||
# TODO: Ideally this should get the data offset from the id and then call
|
||||
# SetImplicitThreadDataOffset
|
||||
return
|
||||
if dbg.use_generics:
|
||||
id = get_thread_id(id)
|
||||
id = require(get_thread_id(id))
|
||||
return dbg._base._systems.SetCurrentThreadId(id)
|
||||
|
||||
|
||||
@dbg.eng_thread
|
||||
def select_frame(id: int):
|
||||
def select_frame(id: int) -> str:
|
||||
return dbg.cmd('.frame /c {}'.format(id))
|
||||
|
||||
|
||||
@dbg.eng_thread
|
||||
def reset_frames():
|
||||
def reset_frames() -> str:
|
||||
return dbg.cmd('.cxr')
|
||||
|
||||
|
||||
@dbg.eng_thread
|
||||
def parse_and_eval(expr, type=None):
|
||||
def parse_and_eval(expr: Union[str, int],
|
||||
type: Optional[int] = None) -> Union[int, float, bytes]:
|
||||
if isinstance(expr, int):
|
||||
return expr
|
||||
# TODO: This could be contributed upstream
|
||||
|
@ -617,20 +631,22 @@ def parse_and_eval(expr, type=None):
|
|||
return value.u.F82Bytes
|
||||
if type == DbgEng.DEBUG_VALUE_FLOAT128:
|
||||
return value.u.F128Bytes
|
||||
raise ValueError(
|
||||
f"Could not evaluate '{expr}' or convert result '{value}'")
|
||||
|
||||
|
||||
@dbg.eng_thread
|
||||
def get_pc():
|
||||
def get_pc() -> int:
|
||||
return dbg._base.reg.get_pc()
|
||||
|
||||
|
||||
@dbg.eng_thread
|
||||
def get_sp():
|
||||
def get_sp() -> int:
|
||||
return dbg._base.reg.get_sp()
|
||||
|
||||
|
||||
@dbg.eng_thread
|
||||
def GetProcessIdsByIndex(count=0):
|
||||
def GetProcessIdsByIndex(count: int = 0) -> Tuple[List[int], List[int]]:
|
||||
# TODO: This could be contributed upstream?
|
||||
if count == 0:
|
||||
try:
|
||||
|
@ -643,11 +659,11 @@ def GetProcessIdsByIndex(count=0):
|
|||
hr = dbg._base._systems._sys.GetProcessIdsByIndex(
|
||||
0, count, ids, sysids)
|
||||
exception.check_err(hr)
|
||||
return (tuple(ids), tuple(sysids))
|
||||
return (list(ids), list(sysids))
|
||||
|
||||
|
||||
@dbg.eng_thread
|
||||
def GetCurrentProcessExecutableName():
|
||||
def GetCurrentProcessExecutableName() -> str:
|
||||
# TODO: upstream?
|
||||
_dbg = dbg._base
|
||||
size = c_ulong()
|
||||
|
@ -659,17 +675,15 @@ def GetCurrentProcessExecutableName():
|
|||
size = exesize
|
||||
hr = _dbg._systems._sys.GetCurrentProcessExecutableName(buffer, size, None)
|
||||
exception.check_err(hr)
|
||||
buffer = buffer[:size.value]
|
||||
buffer = buffer.rstrip(b'\x00')
|
||||
return buffer
|
||||
return buffer.value.decode()
|
||||
|
||||
|
||||
@dbg.eng_thread
|
||||
def GetCurrentProcessPeb():
|
||||
def GetCurrentProcessPeb() -> int:
|
||||
# TODO: upstream?
|
||||
_dbg = dbg._base
|
||||
offset = c_ulonglong()
|
||||
if dbg.is_kernel():
|
||||
if is_kernel():
|
||||
hr = _dbg._systems._sys.GetCurrentProcessDataOffset(byref(offset))
|
||||
else:
|
||||
hr = _dbg._systems._sys.GetCurrentProcessPeb(byref(offset))
|
||||
|
@ -678,7 +692,7 @@ def GetCurrentProcessPeb():
|
|||
|
||||
|
||||
@dbg.eng_thread
|
||||
def GetCurrentThreadTeb():
|
||||
def GetCurrentThreadTeb() -> int:
|
||||
# TODO: upstream?
|
||||
_dbg = dbg._base
|
||||
offset = c_ulonglong()
|
||||
|
@ -691,7 +705,7 @@ def GetCurrentThreadTeb():
|
|||
|
||||
|
||||
@dbg.eng_thread
|
||||
def GetExitCode():
|
||||
def GetExitCode() -> int:
|
||||
# TODO: upstream?
|
||||
if is_kernel():
|
||||
return STILL_ACTIVE
|
||||
|
@ -704,8 +718,9 @@ def GetExitCode():
|
|||
|
||||
|
||||
@dbg.eng_thread
|
||||
def process_list(running=False):
|
||||
"""Get the list of all processes"""
|
||||
def process_list(running: bool = False) -> Union[
|
||||
Iterable[Tuple[int, str, int]], Iterable[Tuple[int]]]:
|
||||
"""Get the list of all processes."""
|
||||
_dbg = dbg._base
|
||||
ids, sysids = GetProcessIdsByIndex()
|
||||
pebs = []
|
||||
|
@ -725,12 +740,16 @@ def process_list(running=False):
|
|||
return zip(sysids)
|
||||
finally:
|
||||
if not running and curid is not None:
|
||||
_dbg._systems.SetCurrentProcessId(curid)
|
||||
try:
|
||||
_dbg._systems.SetCurrentProcessId(curid)
|
||||
except Exception as e:
|
||||
print(f"Couldn't restore current process: {e}")
|
||||
|
||||
|
||||
@dbg.eng_thread
|
||||
def thread_list(running=False):
|
||||
"""Get the list of all threads"""
|
||||
def thread_list(running: bool = False) -> Union[
|
||||
Iterable[Tuple[int, int, str]], Iterable[Tuple[int]]]:
|
||||
"""Get the list of all threads."""
|
||||
_dbg = dbg._base
|
||||
try:
|
||||
ids, sysids = _dbg._systems.GetThreadIdsByIndex()
|
||||
|
@ -758,8 +777,8 @@ def thread_list(running=False):
|
|||
|
||||
|
||||
@dbg.eng_thread
|
||||
def get_proc_id(pid):
|
||||
"""Get the list of all processes"""
|
||||
def get_proc_id(pid: int) -> Optional[int]:
|
||||
"""Get the id for the given system process id."""
|
||||
# TODO: Implement GetProcessIdBySystemId and replace this logic
|
||||
_dbg = dbg._base
|
||||
map = {}
|
||||
|
@ -773,18 +792,18 @@ def get_proc_id(pid):
|
|||
return None
|
||||
|
||||
|
||||
def full_mem():
|
||||
def full_mem() -> List[DbgEng._MEMORY_BASIC_INFORMATION64]:
|
||||
info = DbgEng._MEMORY_BASIC_INFORMATION64()
|
||||
info.BaseAddress = 0
|
||||
info.RegionSize = (1 << 64) - 1
|
||||
info.Protect = 0xFFF
|
||||
info.Name = "full memory"
|
||||
return [ info ]
|
||||
return [info]
|
||||
|
||||
|
||||
@dbg.eng_thread
|
||||
def get_thread_id(tid):
|
||||
"""Get the list of all threads"""
|
||||
def get_thread_id(tid: int) -> Optional[int]:
|
||||
"""Get the id for the given system thread id."""
|
||||
# TODO: Implement GetThreadIdBySystemId and replace this logic
|
||||
_dbg = dbg._base
|
||||
map = {}
|
||||
|
@ -799,8 +818,8 @@ def get_thread_id(tid):
|
|||
|
||||
|
||||
@dbg.eng_thread
|
||||
def open_trace_or_dump(filename):
|
||||
"""Open a trace or dump file"""
|
||||
def open_trace_or_dump(filename: Union[str, bytes]) -> None:
|
||||
"""Open a trace or dump file."""
|
||||
_cli = dbg._base._client._cli
|
||||
if isinstance(filename, str):
|
||||
filename = filename.encode()
|
||||
|
@ -808,7 +827,7 @@ def open_trace_or_dump(filename):
|
|||
exception.check_err(hr)
|
||||
|
||||
|
||||
def split_path(pathString):
|
||||
def split_path(pathString: str) -> List[str]:
|
||||
list = []
|
||||
segs = pathString.split(".")
|
||||
for s in segs:
|
||||
|
@ -823,23 +842,23 @@ def split_path(pathString):
|
|||
return list
|
||||
|
||||
|
||||
def IHostDataModelAccess():
|
||||
return HostDataModelAccess(
|
||||
dbg._base._client._cli.QueryInterface(interface=DbgMod.IHostDataModelAccess))
|
||||
def IHostDataModelAccess() -> HostDataModelAccess:
|
||||
return HostDataModelAccess(dbg._base._client._cli.QueryInterface(
|
||||
interface=DbgMod.IHostDataModelAccess))
|
||||
|
||||
|
||||
def IModelMethod(method_ptr):
|
||||
return ModelMethod(
|
||||
method_ptr.GetIntrinsicValue().value.QueryInterface(interface=DbgMod.IModelMethod))
|
||||
def IModelMethod(method_ptr) -> ModelMethod:
|
||||
return ModelMethod(method_ptr.GetIntrinsicValue().value.QueryInterface(
|
||||
interface=DbgMod.IModelMethod))
|
||||
|
||||
|
||||
@dbg.eng_thread
|
||||
def get_object(relpath):
|
||||
"""Get the list of all threads"""
|
||||
def get_object(relpath: str) -> Optional[ModelObject]:
|
||||
"""Get the model object at the given path."""
|
||||
_cli = dbg._base._client._cli
|
||||
access = HostDataModelAccess(_cli.QueryInterface(
|
||||
interface=DbgMod.IHostDataModelAccess))
|
||||
(mgr, host) = access.GetDataModel()
|
||||
mgr, host = access.GetDataModel()
|
||||
root = mgr.GetRootNamespace()
|
||||
pathstr = "Debugger"
|
||||
if relpath != '':
|
||||
|
@ -850,11 +869,13 @@ def get_object(relpath):
|
|||
|
||||
|
||||
@dbg.eng_thread
|
||||
def get_method(context_path, method_name):
|
||||
"""Get the list of all threads"""
|
||||
def get_method(context_path: str, method_name: str) -> Optional[ModelMethod]:
|
||||
"""Get method for the given object (path) and name."""
|
||||
obj = get_object(context_path)
|
||||
if obj is None:
|
||||
return None
|
||||
keys = obj.EnumerateKeys()
|
||||
(k, v) = keys.GetNext()
|
||||
k, v = keys.GetNext()
|
||||
while k is not None:
|
||||
if k.value == method_name:
|
||||
break
|
||||
|
@ -865,24 +886,24 @@ def get_method(context_path, method_name):
|
|||
|
||||
|
||||
@dbg.eng_thread
|
||||
def get_attributes(obj):
|
||||
"""Get the list of attributes"""
|
||||
def get_attributes(obj: ModelObject) -> Dict[str, ModelObject]:
|
||||
"""Get the list of attributes."""
|
||||
if obj is None:
|
||||
return None
|
||||
return obj.GetAttributes()
|
||||
|
||||
|
||||
@dbg.eng_thread
|
||||
def get_elements(obj):
|
||||
"""Get the list of elements"""
|
||||
def get_elements(obj: ModelObject) -> List[Tuple[int, ModelObject]]:
|
||||
"""Get the list of elements."""
|
||||
if obj is None:
|
||||
return None
|
||||
return obj.GetElements()
|
||||
|
||||
|
||||
@dbg.eng_thread
|
||||
def get_kind(obj):
|
||||
"""Get the kind"""
|
||||
def get_kind(obj) -> Optional[int]:
|
||||
"""Get the kind."""
|
||||
if obj is None:
|
||||
return None
|
||||
kind = obj.GetKind()
|
||||
|
@ -891,65 +912,66 @@ def get_kind(obj):
|
|||
return obj.GetKind().value
|
||||
|
||||
|
||||
@dbg.eng_thread
|
||||
def get_type(obj):
|
||||
"""Get the type"""
|
||||
if obj is None:
|
||||
return None
|
||||
return obj.GetTypeKind()
|
||||
# DOESN'T WORK YET
|
||||
# @dbg.eng_thread
|
||||
# def get_type(obj: ModelObject):
|
||||
# """Get the type."""
|
||||
# if obj is None:
|
||||
# return None
|
||||
# return obj.GetTypeKind()
|
||||
|
||||
|
||||
@dbg.eng_thread
|
||||
def get_value(obj):
|
||||
"""Get the value"""
|
||||
def get_value(obj: ModelObject) -> Any:
|
||||
"""Get the value."""
|
||||
if obj is None:
|
||||
return None
|
||||
return obj.GetValue()
|
||||
|
||||
|
||||
@dbg.eng_thread
|
||||
def get_intrinsic_value(obj):
|
||||
"""Get the intrinsic value"""
|
||||
def get_intrinsic_value(obj: ModelObject) -> VARIANT:
|
||||
"""Get the intrinsic value."""
|
||||
if obj is None:
|
||||
return None
|
||||
return obj.GetIntrinsicValue()
|
||||
|
||||
|
||||
@dbg.eng_thread
|
||||
def get_target_info(obj):
|
||||
"""Get the target info"""
|
||||
def get_target_info(obj: ModelObject) -> ModelObject:
|
||||
"""Get the target info."""
|
||||
if obj is None:
|
||||
return None
|
||||
return obj.GetTargetInfo()
|
||||
|
||||
|
||||
@dbg.eng_thread
|
||||
def get_type_info(obj):
|
||||
"""Get the type info"""
|
||||
def get_type_info(obj: ModelObject) -> ModelObject:
|
||||
"""Get the type info."""
|
||||
if obj is None:
|
||||
return None
|
||||
return obj.GetTypeInfo()
|
||||
|
||||
|
||||
@dbg.eng_thread
|
||||
def get_name(obj):
|
||||
"""Get the name"""
|
||||
def get_name(obj: ModelObject) -> str:
|
||||
"""Get the name."""
|
||||
if obj is None:
|
||||
return None
|
||||
return obj.GetName().value
|
||||
|
||||
|
||||
@dbg.eng_thread
|
||||
def to_display_string(obj):
|
||||
"""Get the display string"""
|
||||
def to_display_string(obj: ModelObject) -> str:
|
||||
"""Get the display string."""
|
||||
if obj is None:
|
||||
return None
|
||||
return obj.ToDisplayString()
|
||||
|
||||
|
||||
@dbg.eng_thread
|
||||
def get_location(obj):
|
||||
"""Get the location"""
|
||||
def get_location(obj: ModelObject) -> Optional[str]:
|
||||
"""Get the location."""
|
||||
if obj is None:
|
||||
return None
|
||||
try:
|
||||
|
@ -961,10 +983,10 @@ def get_location(obj):
|
|||
return None
|
||||
|
||||
|
||||
conv_map = {}
|
||||
conv_map: Dict[str, str] = {}
|
||||
|
||||
|
||||
def get_convenience_variable(id):
|
||||
def get_convenience_variable(id: str) -> Any:
|
||||
if id not in conv_map:
|
||||
return "auto"
|
||||
val = conv_map[id]
|
||||
|
@ -973,77 +995,89 @@ def get_convenience_variable(id):
|
|||
return val
|
||||
|
||||
|
||||
def get_cursor():
|
||||
return ttd._cursor
|
||||
|
||||
|
||||
def get_last_position():
|
||||
def get_last_position() -> Optional[Tuple[int, int]]:
|
||||
return ttd._lastpos
|
||||
|
||||
|
||||
def set_last_position(pos):
|
||||
def set_last_position(pos: Tuple[int, int]) -> None:
|
||||
ttd._lastpos = pos
|
||||
|
||||
|
||||
def get_event_type(rng):
|
||||
if ttd.evttypes.__contains__(rng):
|
||||
return ttd.evttypes[rng]
|
||||
def get_event_type(pos: Tuple[int, int]) -> Optional[str]:
|
||||
if ttd.evttypes.__contains__(pos):
|
||||
return ttd.evttypes[pos]
|
||||
return None
|
||||
|
||||
|
||||
def pos2snap(pos):
|
||||
pmap = get_attributes(pos)
|
||||
major = get_value(pmap["Sequence"])
|
||||
minor = get_value(pmap["Steps"])
|
||||
return mm2snap(major, minor)
|
||||
def split2schedule(pos: Tuple[int, int]) -> Schedule:
|
||||
major, minor = pos
|
||||
return mm2schedule(major, minor)
|
||||
|
||||
|
||||
def mm2snap(major, minor):
|
||||
def schedule2split(time: Schedule) -> Tuple[int, int]:
|
||||
return time.snap, time.steps
|
||||
|
||||
|
||||
def mm2schedule(major: int, minor: int) -> Schedule:
|
||||
index = int(major)
|
||||
if index < 0 or index >= ttd.MAX_STEP:
|
||||
return int(ttd._lastmajor) # << 32
|
||||
snap = index # << 32 + int(minor)
|
||||
return snap
|
||||
if index < 0 or hasattr(ttd, 'MAX_STEP') and index >= ttd.MAX_STEP:
|
||||
return Schedule(require(ttd._last)[0])
|
||||
if index >= 1 << 63:
|
||||
return Schedule((1 << 63) - 1)
|
||||
return Schedule(index, minor)
|
||||
|
||||
|
||||
def pos2split(pos):
|
||||
def pos2split(pos: ModelObject) -> Tuple[int, int]:
|
||||
pmap = get_attributes(pos)
|
||||
major = get_value(pmap["Sequence"])
|
||||
minor = get_value(pmap["Steps"])
|
||||
return (major, minor)
|
||||
|
||||
|
||||
def set_convenience_variable(id, value):
|
||||
def schedule2ss(time: Schedule) -> str:
|
||||
return f'{time.snap:x}:{time.steps:x}'
|
||||
|
||||
|
||||
def compute_description(time: Optional[Schedule], fallback: str) -> str:
|
||||
if time is None:
|
||||
return fallback
|
||||
evt_type = get_event_type(schedule2split(time))
|
||||
evt_str = evt_type or fallback
|
||||
return DESCRIPTION_PATTERN.format(major=time.snap, minor=time.steps,
|
||||
type=evt_str)
|
||||
|
||||
|
||||
def set_convenience_variable(id: str, value: Any) -> None:
|
||||
conv_map[id] = value
|
||||
|
||||
|
||||
def set_kernel(value):
|
||||
|
||||
|
||||
def set_kernel(value: bool) -> None:
|
||||
dbg.IS_KERNEL = value
|
||||
|
||||
|
||||
def is_kernel():
|
||||
|
||||
|
||||
def is_kernel() -> bool:
|
||||
return dbg.IS_KERNEL
|
||||
|
||||
|
||||
def set_exdi(value):
|
||||
def set_exdi(value: bool) -> None:
|
||||
dbg.IS_EXDI = value
|
||||
|
||||
|
||||
def is_exdi():
|
||||
|
||||
|
||||
def is_exdi() -> bool:
|
||||
return dbg.IS_EXDI
|
||||
|
||||
|
||||
def set_remote(value):
|
||||
def set_remote(value: bool) -> None:
|
||||
dbg.IS_REMOTE = value
|
||||
|
||||
|
||||
def is_remote():
|
||||
|
||||
|
||||
def is_remote() -> bool:
|
||||
return dbg.IS_REMOTE
|
||||
|
||||
|
||||
def set_trace(value):
|
||||
|
||||
|
||||
def set_trace(value: bool) -> None:
|
||||
dbg.IS_TRACE = value
|
||||
|
||||
|
||||
def is_trace():
|
||||
|
||||
|
||||
def is_trace() -> bool:
|
||||
return dbg.IS_TRACE
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue