update to ida 7.6, add builds
This commit is contained in:
855
idasdk76/allmake.mak
Normal file
855
idasdk76/allmake.mak
Normal file
@@ -0,0 +1,855 @@
|
||||
#
|
||||
# Common part of make files for IDA.
|
||||
#
|
||||
|
||||
# find directory of allmake.mak:
|
||||
IDA:=$(dir $(lastword $(MAKEFILE_LIST)))
|
||||
|
||||
# define the version number we are building
|
||||
IDAVER_MAJOR:=7
|
||||
IDAVER_MINOR:=6
|
||||
# 760
|
||||
IDAVERDECIMAL:=$(IDAVER_MAJOR)$(IDAVER_MINOR)0
|
||||
# 7.6
|
||||
IDAVERDOTTED:=$(IDAVER_MAJOR).$(IDAVER_MINOR)
|
||||
|
||||
# only 32-bit cygwin make is support on Windows
|
||||
ifeq ($(OS),Windows_NT)
|
||||
ifneq ($(MAKE_HOST),i686-pc-cygwin)
|
||||
$(error Only 32-bit cygwin make is supported on Windows. Make sure you are not using 64-bit cygwin, msys, msys2, or any other version of win32 make)
|
||||
endif
|
||||
endif
|
||||
|
||||
# if no targets are defined, default to host OS
|
||||
ifeq ($(or $(__ANDROID__),$(__ANDROID_X86__),$(__ARMLINUX__),$(__LINUX__),$(__MAC__),$(__NT__)),)
|
||||
ifeq ($(OS),Windows_NT)
|
||||
__NT__=1
|
||||
else
|
||||
UNAME_S := $(shell uname -s)
|
||||
ifeq ($(UNAME_S),Linux)
|
||||
__LINUX__=1
|
||||
endif
|
||||
ifeq ($(UNAME_S),Darwin)
|
||||
__MAC__=1
|
||||
endif
|
||||
endif
|
||||
endif
|
||||
|
||||
# only one build target may be defined
|
||||
ifneq ($(__ANDROID__)$(__ANDROID_X86__)$(__ARMLINUX__)$(__LINUX__)$(__MAC__)$(__NT__),1)
|
||||
$(error Only one build target may be defined (__ANDROID__, __ANDROID_X86__, __ARMLINUX__, __LINUX__, __MAC__, or __NT__))
|
||||
endif
|
||||
|
||||
# detect build configuration
|
||||
# Note: will set one of M, MM, MMH, M32, MO, MMO, MMHO, MO32, MSO, MMSO, or MSO32
|
||||
BUILD_CONFIG-1 := M
|
||||
BUILD_CONFIG-$(__EA64__) += M
|
||||
BUILD_CONFIG-$(USE_STATIC_RUNTIME) += S
|
||||
BUILD_CONFIG-$(IDAHOME) += H
|
||||
BUILD_CONFIG-$(NDEBUG) += O
|
||||
BUILD_CONFIG-$(__X86__) += 32
|
||||
empty :=
|
||||
space := $(empty) $(empty)
|
||||
comma := ,
|
||||
BUILD_CONFIG := $(subst $(space),,$(BUILD_CONFIG-1))
|
||||
$(BUILD_CONFIG) := 1
|
||||
|
||||
# definition of a single \n character (empty lines are important!)
|
||||
define newline
|
||||
|
||||
|
||||
endef
|
||||
|
||||
|
||||
# support arm64 macOS11 builds
|
||||
ifeq ($(and $(__MAC__),$(__ARM__)),1)
|
||||
ifdef __X86__
|
||||
$(error 32-bit ARM builds are not supported on mac.)
|
||||
endif
|
||||
# create a shortcut for convenience
|
||||
__APPLE_SILICON__=1
|
||||
endif
|
||||
|
||||
|
||||
ifdef __ARM__
|
||||
PROCDEF = __ARM__
|
||||
TARGET_PROCESSOR_NAME-1=arm
|
||||
TARGET_PROCESSOR_NAME-$(__MAC__)=arm64
|
||||
TARGET_PROCESSOR_NAME=$(TARGET_PROCESSOR_NAME-1)
|
||||
else ifndef __X86__
|
||||
ARCH_FLAGS = -m64
|
||||
TARGET_PROCESSOR_NAME=x64
|
||||
else
|
||||
ARCH_FLAGS = -m32
|
||||
TARGET_PROCESSOR_NAME=x86
|
||||
endif
|
||||
|
||||
# define some variables to simplify build system
|
||||
ifndef __X86__
|
||||
__X64__ = 1
|
||||
ifndef __EA64__
|
||||
__X32__ = 1
|
||||
endif
|
||||
endif
|
||||
ifndef __NT__
|
||||
__UNIX__ = 1
|
||||
endif
|
||||
|
||||
ifndef IDAHOME
|
||||
IDAADV = 1
|
||||
endif
|
||||
|
||||
# define SYSNAME
|
||||
SYSNAME-$(__LINUX__) = linux
|
||||
SYSNAME-$(__MAC__) = mac
|
||||
SYSNAME-$(__NT__) = win
|
||||
SYSNAME = $(SYSNAME-1)
|
||||
|
||||
# path functions (depending on host OS)
|
||||
ifeq ($(OS),Windows_NT)
|
||||
# define: convert unix path to dos path by replacing slashes by backslashes
|
||||
dospath=$(subst /,\\,$(1))
|
||||
else
|
||||
# define: dospath does not do anything in unix
|
||||
dospath=$(1)
|
||||
endif
|
||||
# define: return 1 if path exists, 0 otherwise
|
||||
ls=$(if $(wildcard $(1)),1,0)
|
||||
|
||||
# define: logical negation
|
||||
not = $(if $(1),,1)
|
||||
|
||||
# define: greater or equal
|
||||
gte = $(if $(filter-out $(1),$(word 2,$(sort $(1) $(2)))),,1)
|
||||
|
||||
include $(IDA)defaults.mk
|
||||
|
||||
#############################################################################
|
||||
ifdef __NT__
|
||||
COMPILER_NAME=vc
|
||||
|
||||
# Visual C++ Toolchain and Windows SDK paths
|
||||
# Note: see comments in defaults.mk for more information about these
|
||||
# variables.
|
||||
|
||||
# This function searches for a specified path, converts it to a 8.3
|
||||
# path with forward slashes as separator, and exports it as an
|
||||
# environment variable. This way, subcalls to make do not need to
|
||||
# call $(shell) again.
|
||||
define require_path
|
||||
$$(if $(strip $$($(1))),,$$(eval $(1):=$$(subst \,/,$$(shell cygpath -d $(2) 2>/dev/null))))
|
||||
$$(if $(strip $$($(1))),,$$(error Could not find $(3) in $(2)$$(newline)*** See defaults.mk and "Visual C++ Toolchain and Windows SDK paths" in allmake.mak))
|
||||
$$(eval export $(1))
|
||||
endef
|
||||
|
||||
# This function fixes variables imported from defaults.mk/vcvars.bat
|
||||
# by ensuring that they are surrounded by quotes and by removing the
|
||||
# trailing backslash.
|
||||
fix_var=$(1):='$$(patsubst %\,%,$$(patsubst '%,%,$$(patsubst %',%,$$(patsubst "%,%,$$(patsubst %",%,$$($(1)))))))'
|
||||
|
||||
# Note: these cfg files are created in makeenv_vc.mak
|
||||
ifdef __XPCOMPAT__
|
||||
-include $(IDA)vs17paths_xp.cfg
|
||||
else
|
||||
-include $(IDA)vs17paths.cfg
|
||||
endif
|
||||
|
||||
# Visual C++ 2017 Install Directory
|
||||
ifndef MSVC_ROOT
|
||||
ifneq (,$(findstring Microsoft$(space)Visual$(space)Studio$(space),$(VCINSTALLDIR)))
|
||||
ifeq (,$(findstring 2017,$(VCINSTALLDIR)))
|
||||
$(error Please check your system environment variable VCInstallDir [$(VCINSTALLDIR)].$(newline)It seems to be pointing to an old version of Visual Studio (and not version 2017).$(newline)You may override it in defaults.mk.)
|
||||
endif
|
||||
endif
|
||||
$(eval $(call fix_var,VCINSTALLDIR))
|
||||
$(eval $(call require_path,MSVC_ROOT,$(VCINSTALLDIR),Visual C++ 2017 Install Directory))
|
||||
export MSVC_ROOT
|
||||
endif
|
||||
|
||||
# Visual C++ 2017 Tools Version
|
||||
ifndef MSVC_TOOLSVER
|
||||
ifndef VCToolsVersion
|
||||
# Try to obtain version from Microsoft.VCToolsVersion.default.txt
|
||||
MSVC_TOOLSVER_PATH = $(MSVC_ROOT)/Auxiliary/Build/Microsoft.VCToolsVersion.default.txt
|
||||
VCToolsVersion := $(shell cat $(MSVC_TOOLSVER_PATH) 2> /dev/null)
|
||||
ifeq (,$(VCToolsVersion))
|
||||
# If that failed, try to detect latest version from the directory names
|
||||
VCToolsVersion := $(notdir $(lastword $(sort $(wildcard $(MSVC_ROOT)/Tools/MSVC/14.*))))
|
||||
endif
|
||||
ifeq (,$(VCToolsVersion))
|
||||
$(error Could not find Visual C++ 2017 Tools Version in $(MSVC_TOOLSVER_PATH))
|
||||
endif
|
||||
endif
|
||||
$(eval $(call fix_var,VCToolsVersion))
|
||||
MSVC_TOOLSVER := $(VCToolsVersion)
|
||||
export MSVC_TOOLSVER
|
||||
endif
|
||||
|
||||
# Final Visual C++ 2017 Tools path
|
||||
$(eval $(call require_path,MSVC_PATH,$(MSVC_ROOT)/Tools/MSVC/$(MSVC_TOOLSVER),Visual C++ 2017 Tools))
|
||||
|
||||
MSVC_BIN-X86 ?= $(MSVC_PATH)/bin/HostX86/x86
|
||||
MSVC_BIN-X64 ?= $(MSVC_PATH)/bin/HostX64/x64
|
||||
ifdef __X86__
|
||||
MSVC_BIN ?= $(MSVC_BIN-X86)
|
||||
else
|
||||
MSVC_BIN ?= $(MSVC_BIN-X64)
|
||||
endif
|
||||
MSVC_INCLUDE ?= $(MSVC_PATH)/Include
|
||||
|
||||
# Windows SDK Install Directory
|
||||
ifndef WSDK_PATH
|
||||
$(eval $(call fix_var,WindowsSdkDir))
|
||||
$(eval $(call require_path,WSDK_PATH,$(WindowsSdkDir),Windows SDK Install Directory))
|
||||
export WSDK_PATH
|
||||
endif
|
||||
|
||||
# Windows SDK Version
|
||||
ifndef WSDK_VER
|
||||
ifndef WindowsSDKVersion
|
||||
# Detect the latest version of the Windows SDK
|
||||
WSDK_VER_PATH = $(WSDK_PATH)/Include/10.*
|
||||
WindowsSDKVersion := $(notdir $(lastword $(sort $(wildcard $(WSDK_VER_PATH)))))
|
||||
ifeq (,$(WindowsSDKVersion))
|
||||
$(error Could not find Windows SDK Version in $(WSDK_VER_PATH))
|
||||
endif
|
||||
endif
|
||||
$(eval $(call fix_var,WindowsSDKVersion))
|
||||
WSDK_VER := $(WindowsSDKVersion)
|
||||
export WSDK_VER
|
||||
endif
|
||||
|
||||
# Windows SDK Include/Lib paths
|
||||
INCLUDE_UCRT_PATH ?= $(WSDK_PATH)/Include/$(WSDK_VER)/ucrt
|
||||
LIB_UCRT_PATH ?= $(WSDK_PATH)/Lib/$(WSDK_VER)/ucrt
|
||||
$(eval $(call require_path,INCLUDE_UCRT,$(INCLUDE_UCRT_PATH),Windows SDK Include/ucrt))
|
||||
$(eval $(call require_path,LIB_UCRT,$(LIB_UCRT_PATH),Windows SDK Lib/ucrt))
|
||||
|
||||
ifdef __XPCOMPAT__
|
||||
$(eval $(call require_path,INCLUDE_MSSDK71,$(MSSDK71_PATH)/Include,Microsoft SDK Include))
|
||||
$(eval $(call require_path,LIB_MSSDK71,$(MSSDK71_PATH)/Lib,Microsoft SDK Lib))
|
||||
$(eval $(call require_path,SDK_BIN,$(MSSDK71_PATH)/Bin,Microsoft SDK Bin))
|
||||
else
|
||||
INCLUDE_SHARED_PATH ?= $(WSDK_PATH)/Include/$(WSDK_VER)/shared
|
||||
INCLUDE_UM_PATH ?= $(WSDK_PATH)/Include/$(WSDK_VER)/um
|
||||
LIB_UM_PATH ?= $(WSDK_PATH)/Lib/$(WSDK_VER)/um
|
||||
SDK_BIN_PATH ?= $(WSDK_PATH)/Bin/$(WSDK_VER)/
|
||||
|
||||
$(eval $(call require_path,INCLUDE_SHARED,$(INCLUDE_SHARED_PATH),Windows SDK Include/shared))
|
||||
$(eval $(call require_path,INCLUDE_UM,$(INCLUDE_UM_PATH),Windows SDK Include/um))
|
||||
$(eval $(call require_path,LIB_UM,$(LIB_UM_PATH),Windows SDK Lib/um))
|
||||
$(eval $(call require_path,SDK_BIN,$(SDK_BIN_PATH),Windows SDK Bin))
|
||||
endif
|
||||
|
||||
# Export INCLUDE as an environment variable so it may be used by cl.
|
||||
ifndef INCLUDE
|
||||
ifdef __XPCOMPAT__
|
||||
INCLUDE = $(MSVC_INCLUDE);$(INCLUDE_UCRT);$(INCLUDE_MSSDK71)
|
||||
else
|
||||
INCLUDE = $(MSVC_INCLUDE);$(INCLUDE_UCRT);$(INCLUDE_UM);$(INCLUDE_SHARED)
|
||||
endif
|
||||
export INCLUDE
|
||||
endif
|
||||
|
||||
# Export LIB as an environment variable so it may be used by cl/link.
|
||||
ifndef LIB
|
||||
ifdef __XPCOMPAT__
|
||||
ifdef __X86__
|
||||
LIB = $(MSVC_PATH)/lib/x86;$(LIB_UCRT)/x86;$(LIB_MSSDK71)
|
||||
else
|
||||
LIB = $(MSVC_PATH)/lib/x64;$(LIB_UCRT)/x64;$(LIB_MSSDK71)/x64
|
||||
endif
|
||||
else
|
||||
ifdef __X86__
|
||||
LIB = $(MSVC_PATH)/lib/x86;$(LIB_UCRT)/x86;$(LIB_UM)/x86
|
||||
else
|
||||
LIB = $(MSVC_PATH)/lib/x64;$(LIB_UCRT)/x64;$(LIB_UM)/x64
|
||||
endif
|
||||
endif
|
||||
export LIB
|
||||
endif
|
||||
|
||||
# If a Visual Studio Command Prompt is used, make sure the target
|
||||
# architecture is correct.
|
||||
ifdef VSCMD_ARG_TGT_ARCH
|
||||
ifneq ($(VSCMD_ARG_TGT_ARCH),$(TARGET_PROCESSOR_NAME))
|
||||
ifdef __X86__
|
||||
EXPECTED_ARCH = x86
|
||||
else
|
||||
EXPECTED_ARCH = x64
|
||||
endif
|
||||
LOWERCASE_BUILD_CONFIG := $(subst M,m,$(subst S,s,$(subst O,o,$(BUILD_CONFIG))))
|
||||
$(error Please use the correct Visual Studio Command Prompt for the target architecture$(newline)*** The target architecture for '$(LOWERCASE_BUILD_CONFIG)' is $(EXPECTED_ARCH), and the architecture for the current Visual Studio Command Prompt is $(VSCMD_ARG_TGT_ARCH)))
|
||||
endif
|
||||
endif
|
||||
#############################################################################
|
||||
else ifdef __LINUX__
|
||||
COMPILER_NAME=gcc
|
||||
PTHR_SWITCH=-pthread
|
||||
STDLIBS += -lrt -lpthread -lc
|
||||
#############################################################################
|
||||
else ifdef __MAC__
|
||||
COMPILER_NAME=clang
|
||||
STDLIBS += -lpthread -liconv
|
||||
ARCH_FLAGS-$(__X64__) = -arch x86_64
|
||||
ARCH_FLAGS-$(__X86__) = -arch i386
|
||||
ARCH_FLAGS-$(__ARM__) = -arch arm64
|
||||
ARCH_FLAGS += $(ARCH_FLAGS-1)
|
||||
# The following value is defined in defaults.mk.
|
||||
ARCH_FLAGS += -mmacosx-version-min=$(MACOSX_DEPLOYMENT_TARGET)
|
||||
ifndef MACSDK
|
||||
MACSDK := $(shell /usr/bin/xcrun --sdk macosx --show-sdk-path)
|
||||
ifeq ($(MACSDK),)
|
||||
$(error Could not find MacOSX SDK)
|
||||
endif
|
||||
export MACSDK
|
||||
endif
|
||||
ARCH_FLAGS += -isysroot $(MACSDK)
|
||||
endif
|
||||
|
||||
#############################################################################
|
||||
# toolchain-specific variables
|
||||
|
||||
ifneq (,$(filter $(COMPILER_NAME),gcc clang))
|
||||
# file extensions
|
||||
A = .a
|
||||
B = $(SUFF64)
|
||||
O = .o
|
||||
II = .i
|
||||
# toolchain output switches
|
||||
OBJSW = -o # with space
|
||||
OUTAR =
|
||||
OUTII = -o # with space
|
||||
OUTSW = -o # with space
|
||||
ifdef __MAC__
|
||||
OUTMAP = -Wl,-map,
|
||||
else
|
||||
OUTMAP = -Wl,-Map,
|
||||
endif
|
||||
# misc switches
|
||||
AROPT = rc
|
||||
CPPONLY = -E
|
||||
FORCEC = -xc
|
||||
NORTTI = -fno-rtti
|
||||
ifdef __MAC__
|
||||
OUTDLL = -dynamiclib
|
||||
else
|
||||
OUTDLL = --shared
|
||||
endif
|
||||
# utilities
|
||||
CCACHE-$(USE_CCACHE) = ccache
|
||||
ifeq ($(COMPILER_NAME),clang)
|
||||
_CC = clang
|
||||
_CXX = clang++
|
||||
else
|
||||
_CC = gcc
|
||||
_CXX = g++
|
||||
ifdef USE_GOLD
|
||||
GOLD = -fuse-ld=gold
|
||||
endif
|
||||
endif
|
||||
AR = $(CROSS_PREFIX)ar$(HOST_EXE) $(AROPT)
|
||||
CC = $(CCACHE-1) $(CROSS_PREFIX)$(_CC)$(HOST_EXE) $(ARCH_FLAGS)
|
||||
CCL = $(CROSS_PREFIX)$(_CXX)$(HOST_EXE) $(ARCH_FLAGS) $(GOLD)
|
||||
CXX = $(CCACHE-1) $(CROSS_PREFIX)$(_CXX)$(HOST_EXE) $(ARCH_FLAGS)
|
||||
else ifeq ($(COMPILER_NAME),vc)
|
||||
# file extensions
|
||||
A = .lib
|
||||
B = $(SUFF64).exe
|
||||
O = .obj
|
||||
II = .i
|
||||
# toolchain output switches
|
||||
OBJSW = /Fo
|
||||
OUTAR = /OUT:
|
||||
OUTII = /Fi
|
||||
OUTSW = /OUT:
|
||||
OUTMAP = /map:
|
||||
# misc switches
|
||||
CPPONLY = /P
|
||||
FORCEC = /TC
|
||||
NOLOGO = /nologo
|
||||
NORTTI = /GR-
|
||||
OUTDLL = /DLL
|
||||
# utilities
|
||||
AR = $(MSVC_BIN)/lib.exe $(NOLOGO)
|
||||
CC = $(MSVC_BIN)/cl.exe $(NOLOGO)
|
||||
CCL = $(MSVC_BIN)/link.exe $(NOLOGO)
|
||||
CXX = $(CC)
|
||||
endif
|
||||
|
||||
##############################################################################
|
||||
# target-specific cflags/ldflags
|
||||
ifneq (,$(filter $(COMPILER_NAME),gcc clang))
|
||||
|
||||
# system cflags
|
||||
CC_DEFS += $(PROCDEF)
|
||||
ifdef __MAC__
|
||||
CC_DEFS += __MAC__
|
||||
else
|
||||
CC_DEFS += __LINUX__
|
||||
endif
|
||||
|
||||
ifdef __APPLE_SILICON__
|
||||
CC_DEFS += __APPLE_SILICON__
|
||||
endif
|
||||
|
||||
# pic-related flags
|
||||
# Note: this variable may be overridden in other parts of the build
|
||||
PIC = -fPIC
|
||||
|
||||
ifdef __MAC__
|
||||
LDPIE = $(PIC) -Wl,-pie
|
||||
else
|
||||
LDPIE = $(PIC) -pie
|
||||
endif
|
||||
|
||||
# common cflags
|
||||
CC_DEFS += $(DEF64)
|
||||
CC_DEFS += $(DEFX86)
|
||||
|
||||
CC_F += $(PIC)
|
||||
CC_F += -fdiagnostics-show-option
|
||||
CC_F += -fno-strict-aliasing
|
||||
CC_F += -fvisibility=hidden
|
||||
CC_F += -fwrapv
|
||||
ifneq ($(COMPILER_NAME),clang)
|
||||
CC_F += -fno-delete-null-pointer-checks
|
||||
endif
|
||||
|
||||
CC_INCP += $(I)
|
||||
|
||||
CC_W += -Wall
|
||||
CC_W += -Wextra
|
||||
CC_W += -Wformat=2
|
||||
CC_W += -Werror=format-security
|
||||
CC_W += -Werror=format-nonliteral
|
||||
CC_W += -Wshadow
|
||||
CC_W += -Wunused
|
||||
|
||||
CC_WNO += -Wno-format-y2k
|
||||
CC_WNO += -Wno-missing-field-initializers
|
||||
CC_WNO += -Wno-sign-compare
|
||||
|
||||
CC_X += -g
|
||||
CC_X += -pipe
|
||||
|
||||
# enable c++11
|
||||
CXXSTD = -std=c++11
|
||||
|
||||
CXX_F += -fvisibility-inlines-hidden
|
||||
CXX_WNO += -Wno-invalid-offsetof
|
||||
|
||||
# system-specific cflags
|
||||
ifeq ($(COMPILER_NAME),clang) # mac/android
|
||||
# 'cc -dumpversion' always reports 4.2.1 for clang
|
||||
# https://stackoverflow.com/questions/12893731/why-does-clang-dumpversion-report-4-2-1
|
||||
|
||||
# clang is extra picky - need to add some warning supressions
|
||||
# must eventually get rid of most of these
|
||||
CC_WNO += -Wno-char-subscripts
|
||||
CC_WNO += -Wno-dynamic-class-memaccess
|
||||
CC_WNO += -Wno-int-to-pointer-cast
|
||||
CC_WNO += -Wno-invalid-source-encoding
|
||||
CC_WNO += -Wno-logical-not-parentheses
|
||||
CC_WNO += -Wno-logical-op-parentheses
|
||||
CC_WNO += -Wno-null-conversion
|
||||
CC_WNO += -Wno-nullability-completeness
|
||||
CC_WNO += -Wno-parentheses-equality
|
||||
CC_WNO += -Wno-self-assign
|
||||
CC_WNO += -Wno-unused-const-variable
|
||||
CC_WNO += -Wno-unused-function
|
||||
CC_WNO += -Wno-unused-private-field
|
||||
CC_WNO += -Wno-unused-variable
|
||||
CC_WNO += -Wno-varargs
|
||||
|
||||
CC_F += -fno-caret-diagnostics
|
||||
else # (arm)linux
|
||||
|
||||
# get gcc version
|
||||
ifndef _GCC_VERSION
|
||||
_GCC_VERSION:=$(wordlist 1,2,$(subst ., ,$(shell $(CC) -dumpversion)))
|
||||
export _GCC_VERSION
|
||||
endif
|
||||
GCC_VERSION=$(firstword $(_GCC_VERSION)).$(lastword $(_GCC_VERSION))
|
||||
|
||||
CC_WNO += -Wno-unused-local-typedefs
|
||||
CC_F += -fno-diagnostics-show-caret
|
||||
|
||||
CC_DEFS-$(call gte,$(GCC_VERSION),5.0) += _GLIBCXX_USE_CXX11_ABI=0
|
||||
CC_W-$(call gte,$(GCC_VERSION),7.0) += -Wimplicit-fallthrough=0
|
||||
CXX_WNO-$(call gte,$(GCC_VERSION),8.0) += -Wno-class-memaccess
|
||||
|
||||
# suppress warning about ABI change in GCC 4.4
|
||||
CC_WNO-$(__ARMLINUX__) += -Wno-psabi
|
||||
endif
|
||||
|
||||
# optimization cflags
|
||||
ifdef NDEBUG
|
||||
CC_F += -fdata-sections
|
||||
CC_F += -ffunction-sections
|
||||
ifndef __ASAN__
|
||||
CC_F += -fomit-frame-pointer
|
||||
endif
|
||||
# stack protector
|
||||
ifdef __TARGET_MAC_HOST_LINUX__
|
||||
# disable stack protector for our osxcross toolchain (we check
|
||||
# against __TARGET_MAC_HOST_LINUX__ since it is hard to check
|
||||
# for version number in clang).
|
||||
else ifeq ($(call gte,$(GCC_VERSION),4.9),1)
|
||||
CC_F += -fstack-protector-strong
|
||||
else
|
||||
CC_F += -fstack-protector
|
||||
endif
|
||||
CC_DEFS += NDEBUG
|
||||
CC_DEFS += _FORTIFY_SOURCE=2
|
||||
else
|
||||
CC_DEFS += _DEBUG
|
||||
endif
|
||||
|
||||
# system-specific ldflags
|
||||
ifdef __LINUX__
|
||||
LDFLAGS += -Wl,--build-id
|
||||
LDFLAGS += -Wl,--gc-sections
|
||||
LDFLAGS += -Wl,--warn-shared-textrel
|
||||
|
||||
NO_UNDEFS = -Wl,--no-undefined
|
||||
DLL_W += $(NO_UNDEFS)
|
||||
else ifdef __MAC__
|
||||
LDFLAGS += -Wl,-dead_strip
|
||||
|
||||
ifndef __TARGET_MAC_HOST_LINUX__
|
||||
DLL_X += -compatibility_version 1.0
|
||||
DLL_X += -current_version 1.0
|
||||
DLL_X += -single_module
|
||||
endif
|
||||
endif
|
||||
|
||||
# common linker/compiler flags
|
||||
ifdef NDEBUG
|
||||
CCOPT += -O2
|
||||
ifdef __LINUX__
|
||||
LDOPT += -Wl,-O1
|
||||
endif
|
||||
endif
|
||||
|
||||
# AddressSanitizer flags
|
||||
ifdef __ASAN__
|
||||
CC_DEFS += __ASAN__
|
||||
CC_F += -fno-omit-frame-pointer
|
||||
CC_F += -fsanitize=address
|
||||
LDFLAGS += -fsanitize=address
|
||||
export LSAN_OPTIONS=suppressions=$(IDA)etc/bin/known_leaks.txt:detect_leaks=0
|
||||
endif
|
||||
|
||||
# final compiler flags
|
||||
CC_F += $(CC_F-1)
|
||||
CC_W += $(CC_W-1)
|
||||
CC_WNO += $(CC_WNO-1)
|
||||
CXX_WNO += $(CXX_WNO-1)
|
||||
CC_DEFS += $(CC_DEFS-1)
|
||||
CC_INCP += $(CC_INCP-1)
|
||||
CC_D += $(addprefix -D,$(CC_DEFS))
|
||||
CC_I += $(addprefix -I,$(CC_INCP))
|
||||
|
||||
# the -Wno-* flags must come after the -W enabling flags
|
||||
WARNS = $(sort $(CC_W)) $(sort $(CC_WNO))
|
||||
|
||||
CFLAGS += $(sort $(CC_X))
|
||||
CFLAGS += $(CCOPT)
|
||||
CFLAGS += $(sort $(CC_I))
|
||||
CFLAGS += $(sort $(CC_D))
|
||||
CFLAGS += $(sort $(CC_F))
|
||||
CFLAGS += $(WARNS)
|
||||
CFLAGS += $(PTHR_SWITCH)
|
||||
|
||||
# for warning suppression, override the WARNS variable with NOWARNS:
|
||||
# $(TARGET): WARNS = $(NOWARNS)
|
||||
NOWARNS = -w
|
||||
|
||||
# dll linker flags
|
||||
DLLFLAGS += $(DLL_W) $(DLL_X)
|
||||
|
||||
else ifeq ($(COMPILER_NAME),vc)
|
||||
# for warning suppression, override the WARNS variable with NOWARNS:
|
||||
# $(TARGET): WARNS = $(NOWARNS)
|
||||
NOWARNS = -w -wd4702 -wd4738
|
||||
|
||||
# optimization ldflags
|
||||
LDOPT += /DEBUG
|
||||
ifdef NDEBUG
|
||||
LDOPT += /INCREMENTAL:NO /OPT:ICF /OPT:REF
|
||||
endif
|
||||
|
||||
# set c runtime to use
|
||||
ifdef NDEBUG
|
||||
ifdef USE_STATIC_RUNTIME
|
||||
RUNTIME_LIBSW = /MT
|
||||
else
|
||||
RUNTIME_LIBSW = /MD
|
||||
endif
|
||||
else
|
||||
ifdef USE_STATIC_RUNTIME
|
||||
RUNTIME_LIBSW = /MTd
|
||||
else
|
||||
RUNTIME_LIBSW = /MDd
|
||||
endif
|
||||
endif
|
||||
|
||||
# PDB options
|
||||
PDBFLAGS = /PDB:$(PDBDIR)/
|
||||
ifdef NDEBUG
|
||||
PDBFLAGS += /PDBALTPATH:%_PDB%
|
||||
endif
|
||||
# Generate debug info (old style)
|
||||
PDBFORMAT = /Z7
|
||||
|
||||
# final compiler flags
|
||||
CC_DEFS += $(DEF64)
|
||||
CC_DEFS += $(DEFX86)
|
||||
CC_DEFS += $(CC_DEFS-1)
|
||||
CC_INCP += $(CC_INCP-1)
|
||||
CC_D += $(addprefix -D,$(CC_DEFS))
|
||||
CC_I += $(addprefix -I,$(CC_INCP))
|
||||
|
||||
CFGFILE = @$(IDA)$(SYSDIR).cfg
|
||||
CFLAGS += $(CFGFILE)
|
||||
CFLAGS += $(RUNTIME_LIBSW)
|
||||
CFLAGS += $(PDBFORMAT)
|
||||
CFLAGS += /Brepro
|
||||
CFLAGS += $(sort $(CC_I))
|
||||
CFLAGS += $(sort $(CC_D))
|
||||
CFLAGS += $(sort $(CC_F))
|
||||
CFLAGS += $(WARNS)
|
||||
|
||||
# final linker flags
|
||||
LDFLAGS += /Brepro
|
||||
LDFLAGS += $(PDBFLAGS)
|
||||
LDFLAGS += /ERRORREPORT:QUEUE
|
||||
ifdef __X86__
|
||||
LDFLAGS += /LARGEADDRESSAWARE
|
||||
endif
|
||||
|
||||
ifdef __XPCOMPAT__
|
||||
XPSUBSYS-$(__X64__) = /SUBSYSTEM:CONSOLE,5.02
|
||||
XPSUBSYS-$(__X86__) = /SUBSYSTEM:CONSOLE,5.01
|
||||
LDFLAGS += $(XPSUBSYS-1)
|
||||
endif
|
||||
|
||||
endif
|
||||
|
||||
# to enable obsolete functions, disable the NO_OBSOLETE_FUNCS variable:
|
||||
# $(TARGET): NO_OBSOLETE_FUNCS =
|
||||
NO_OBSOLETE_FUNCS = NO_OBSOLETE_FUNCS
|
||||
CC_DEFS += $(NO_OBSOLETE_FUNCS)
|
||||
|
||||
CXXFLAGS += $(CXXSTD)
|
||||
CXXFLAGS += $(CFLAGS)
|
||||
CXXFLAGS += $(sort $(CXX_F))
|
||||
CXXFLAGS += $(sort $(CXX_WNO))
|
||||
CXXFLAGS += $(UFLAGS)
|
||||
|
||||
LDFLAGS += $(LDOPT)
|
||||
|
||||
#############################################################################
|
||||
ifdef __X86__
|
||||
DEFX86 = __X86__
|
||||
endif
|
||||
|
||||
ifdef __EA64__
|
||||
SUFF64=64
|
||||
ADRSIZE=64
|
||||
DEF64 = __EA64__
|
||||
else
|
||||
ADRSIZE=32
|
||||
endif
|
||||
|
||||
ifdef NDEBUG
|
||||
OPTSUF=_opt
|
||||
endif
|
||||
|
||||
ifdef IDAHOME
|
||||
EXTRASUF1:=_home
|
||||
IDAHOME_PROCESSORS=pc arm ppc mips mc68k
|
||||
else ifdef DEMO_OR_FREE
|
||||
ifdef DEMO
|
||||
EXTRASUF1:=_demo
|
||||
else
|
||||
EXTRASUF1:=_free
|
||||
endif
|
||||
IDADEMO_PROCESSOR=pc
|
||||
IDADEMO_LOADERS=pe elf mach-o
|
||||
else
|
||||
ifdef USE_STATIC_RUNTIME
|
||||
EXTRASUF1:=_s
|
||||
endif
|
||||
ifdef __ASAN__
|
||||
EXTRASUF2:=_asan
|
||||
endif
|
||||
ifdef __FUZZER__
|
||||
EXTRASUF3:=_afl
|
||||
endif
|
||||
endif
|
||||
EXTRASUF=$(EXTRASUF1)$(EXTRASUF2)$(EXTRASUF3)
|
||||
|
||||
#############################################################################
|
||||
SYSDIR=$(TARGET_PROCESSOR_NAME)_$(SYSNAME)_$(COMPILER_NAME)_$(ADRSIZE)$(OPTSUF)$(EXTRASUF)
|
||||
# libraries directory
|
||||
LIBDIR=$(IDA)lib/$(TARGET_PROCESSOR_NAME)_$(SYSNAME)_$(COMPILER_NAME)_$(ADRSIZE)$(EXTRASUF)
|
||||
# object files directory (using ?= to allow overriding)
|
||||
OBJDIR?=obj/$(SYSDIR)
|
||||
# PDB files directory
|
||||
PDBDIR=$(IDA)pdb/$(TARGET_PROCESSOR_NAME)_$(SYSNAME)_$(COMPILER_NAME)_$(ADRSIZE)$(EXTRASUF)
|
||||
# output directory for target platform
|
||||
R=$(IDA)bin/
|
||||
# input directory with existing build utilities
|
||||
RS=$(IDA)bin/
|
||||
# _ida.hlp placed in main tool directory
|
||||
HI=$(RS)
|
||||
# help source
|
||||
HS=.hls
|
||||
# help headers
|
||||
HH=.hhp
|
||||
# include,help and other directories are common for all platforms and compilers:
|
||||
I =$(IDA)include/
|
||||
C =$(R)cfg/
|
||||
RI=$(R)idc/
|
||||
F=$(OBJDIR)/
|
||||
L=$(LIBDIR)/
|
||||
|
||||
DUMB=$(L)dumb$(O)
|
||||
HELP=$(L)help$(O)
|
||||
HLIB=$(HI)_ida.hlp
|
||||
|
||||
# to be used like this:
|
||||
# $(L)va$(A): $(call lib, $(VA_OBJS))
|
||||
lib=$(1); $(strip $(QARf)$(AR) $(OUTAR)$$@ $$^)
|
||||
|
||||
# to be used like this: $(call _link_exe, target, objs, libs)
|
||||
_link_exe=$(strip $(QCCL)$(CCL) $(OUTSW)$(1) $(2) $(3) $(LDFLAGS) $(STDLIBS))
|
||||
|
||||
# to be used like this: $(call link_exe, objs, libs)
|
||||
link_exe=$(call _link_exe,$@,$(1),$(2))
|
||||
|
||||
# to be used like this: $(call _link_dll, target, objs, libs)
|
||||
_link_dll=$(strip $(QCCL)$(CCL) $(OUTDLL) $(DLLFLAGS) $(OUTSW)$(1) $(2) $(3) $(LDFLAGS) $(STDLIBS))
|
||||
|
||||
# to be used like this: $(call link_dll, objs, libs)
|
||||
link_dll=$(call _link_dll,$@,$(1),$(2))
|
||||
|
||||
# to be used like this: $(call link_dumb, target, libs, objs)
|
||||
link_dumb=$(3) $(patsubst %,$(L)%$(A),$(2)); $(strip $(QCCLf)$(CCL) $(OUTSW)$(1) $(LDFLAGS) $(3) $(patsubst %,$(L)%$(A),$(2)) $(STDLIBS))
|
||||
|
||||
# to be used like this:
|
||||
# target: $(call dumb_target, libs, objs) extra_ldflags
|
||||
dumb_target=$(call link_dumb,$$@,$(1),$(2) $(DUMB))
|
||||
|
||||
# to be used like this:
|
||||
# $(R)%$(B): $(F)%$(O) $(call dumb_pattern, libs, objs) extra_ldflags
|
||||
dumb_pattern=$(call link_dumb,$$@ $$<,$(1),$(2) $(DUMB))
|
||||
|
||||
# to be used like this:
|
||||
# OBJS += $(call objs,obj1 obj2 obj3 ...)
|
||||
objs=$(addprefix $(F),$(addsuffix $(O),$(1)))
|
||||
|
||||
# output name for module dll
|
||||
module_dll=$(BIN_PATH)$(1)$(SUFF64)$(DLLEXT)
|
||||
|
||||
# output name for server executable
|
||||
server_exe=$(R)dbgsrv/$(1)
|
||||
|
||||
ifeq ($(or $(M),$(MM),$(MMH),$(MO),$(MMO),$(MMHO)),1)
|
||||
BUILD_IDA = 1
|
||||
endif
|
||||
ifeq ($(or $(M32),$(MM),$(MO32),$(MMO)),1)
|
||||
BUILD_DBGSRV = 1
|
||||
endif
|
||||
|
||||
# target-os specific variables
|
||||
ifdef __NT__
|
||||
DLLEXT=.dll
|
||||
else ifdef __MAC__
|
||||
DLLEXT=.dylib
|
||||
else
|
||||
DLLEXT=.so
|
||||
endif
|
||||
|
||||
# build system commands
|
||||
ifeq ($(OS),Windows_NT)
|
||||
CP=cp -f --preserve=all
|
||||
MKDIR=-@mkdir
|
||||
AWK=gawk
|
||||
else
|
||||
CP=cp -f
|
||||
MKDIR=-@mkdir 2>/dev/null
|
||||
AWK=awk
|
||||
endif
|
||||
RM=rm -f
|
||||
MV=mv
|
||||
|
||||
# used to silence some makefile commands
|
||||
# run 'make Q=' to prevent commands from being silenced
|
||||
Q?=@
|
||||
|
||||
# some makefiles rebuild targets when the makefile itself changes.
|
||||
# this makes debugging makefiles a pain.
|
||||
# run 'make MAKEFILE_DEP=' to disable this behaviour.
|
||||
MAKEFILE_DEP?=makefile
|
||||
|
||||
# libida-related
|
||||
# Note: $(IDALIB) should be used in the dependency list
|
||||
# $(LINKIDA) should be used in the link command
|
||||
ifdef __NT__
|
||||
# Note: on Windows, ida.lib does not have a "64" suffix for ea64
|
||||
IDALIB = $(L)ida$(A)
|
||||
LINKIDA = $(IDALIB)
|
||||
else
|
||||
IDALIB = $(L)libida$(SUFF64)$(DLLEXT)
|
||||
LINKIDA = -L$(L) -lida$(SUFF64)
|
||||
endif
|
||||
|
||||
# simplify command echo
|
||||
ifdef IDAMAKE_SIMPLIFY
|
||||
ifeq ($(Q),@)
|
||||
DO_IDAMAKE_SIMPLIFY=1
|
||||
endif
|
||||
endif
|
||||
|
||||
ifdef DO_IDAMAKE_SIMPLIFY
|
||||
ifdef IDAMAKE_SIMPLIFY_NO_COLOR
|
||||
qcolor=$(1)
|
||||
else
|
||||
ifeq ($(OS),Windows_NT)
|
||||
qcolor=-e #
|
||||
endif
|
||||
qcolor+="\033[1;34m$(1)\033[0m"
|
||||
endif
|
||||
QCXX = @echo $(call qcolor,compile) $< && #
|
||||
QCC = @echo $(call qcolor,compile) $< && #
|
||||
QASM = @echo $(call qcolor,asm) $< && #
|
||||
QARf = @echo $(call qcolor,lib) $$@ && #
|
||||
QCCL = @echo $(call qcolor,link) $@ && #
|
||||
QCCLf = @echo $(call qcolor,link) $$@ && #
|
||||
endif
|
||||
|
||||
# simple build rules
|
||||
CONLY?=-c
|
||||
|
||||
$(F)%$(O): %.cpp
|
||||
$(strip $(QCXX)$(CXX) $(CXXFLAGS) $(NORTTI) $(CONLY) $(OBJSW)$@ $<)
|
||||
|
||||
$(F)%$(O): %.c
|
||||
$(strip $(QCC)$(CC) $(CFLAGS) $(CONLY) $(OBJSW)$@ $(FORCEC) $<)
|
||||
|
||||
$(C)%.cfg: %.cfg
|
||||
$(CP) $? $@
|
||||
|
||||
# http://www.cmcrossroads.com/article/printing-value-makefile-variable
|
||||
print-%:
|
||||
@echo $* = "$($*)"
|
||||
@echo $*\'s origin is $(origin $*)
|
||||
|
||||
#############################################################################
|
||||
.PHONY: all test cfg includes
|
||||
|
||||
# Force make to delete the target if the rule to build it fails
|
||||
.DELETE_ON_ERROR:
|
||||
363
idasdk76/dbg/arm_debmod.cpp
Normal file
363
idasdk76/dbg/arm_debmod.cpp
Normal file
@@ -0,0 +1,363 @@
|
||||
#include <pro.h>
|
||||
#include <nalt.hpp>
|
||||
#include "arm_debmod.h"
|
||||
|
||||
#ifdef ENABLE_LOWCNDS
|
||||
inline bool has_armv5(void) { return true; }
|
||||
static arm_debmod_t *ssmod; // pointer to the current debugger module
|
||||
#endif
|
||||
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
arm_debmod_t::arm_debmod_t(void)
|
||||
{
|
||||
static const uchar bpt[] = ARM_BPT_CODE;
|
||||
bpt_code.append(bpt, sizeof(bpt));
|
||||
sp_idx = R_SP;
|
||||
pc_idx = R_PC;
|
||||
lr_idx = R_LR;
|
||||
sr_idx = R_PSR;
|
||||
nregs = qnumber(arm_registers);
|
||||
|
||||
set_platform("linux");
|
||||
|
||||
reset_hwbpts();
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
int arm_debmod_t::getn_hwbpts_supported(void)
|
||||
{
|
||||
if ( hwbpt_count < 0 )
|
||||
{
|
||||
// unknown, ask the subclass how many hwbpts are supported
|
||||
hwbpt_count = _getn_hwbpts_supported();
|
||||
if ( hwbpt_count < 0 )
|
||||
hwbpt_count = 0;
|
||||
else
|
||||
hwbpt_count = qmin(hwbpt_count, ARM_MAX_HWBPTS);
|
||||
}
|
||||
return hwbpt_count;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
int arm_debmod_t::getn_watchpoints_supported(void)
|
||||
{
|
||||
if ( watch_count < 0 )
|
||||
{
|
||||
// unknown, ask the subclass how many watchpoints are supported
|
||||
watch_count = _getn_watchpoints_supported();
|
||||
if ( watch_count < 0 )
|
||||
watch_count = 0;
|
||||
else
|
||||
watch_count = qmin(watch_count, ARM_MAX_WATCHPOINTS);
|
||||
}
|
||||
return watch_count;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
int arm_debmod_t::find_hwbpt_slot(int *out, ea_t ea)
|
||||
{
|
||||
int slot = -1;
|
||||
for ( int i = 0, n = getn_hwbpts_supported(); i < n; i++ )
|
||||
{
|
||||
if ( hwbpt_slots[i].addr == ea )
|
||||
return BPT_BAD_ADDR; // duplicate
|
||||
|
||||
if ( hwbpt_slots[i].addr == BADADDR && slot < 0 )
|
||||
slot = i;
|
||||
}
|
||||
|
||||
if ( slot < 0 )
|
||||
return BPT_TOO_MANY;
|
||||
|
||||
if ( out != nullptr )
|
||||
*out = slot;
|
||||
|
||||
return BPT_OK;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
int arm_debmod_t::find_watchpoint_slot(int *out, ea_t ea)
|
||||
{
|
||||
int slot = -1;
|
||||
for ( int i = 0, n = getn_watchpoints_supported(); i < n; i++ )
|
||||
{
|
||||
if ( watch_slots[i].addr == ea )
|
||||
return BPT_BAD_ADDR; // duplicate
|
||||
|
||||
if ( watch_slots[i].addr == BADADDR && slot < 0 )
|
||||
slot = i;
|
||||
}
|
||||
|
||||
if ( slot < 0 )
|
||||
return BPT_TOO_MANY;
|
||||
|
||||
if ( out != nullptr )
|
||||
*out = slot;
|
||||
|
||||
return BPT_OK;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
int idaapi arm_debmod_t::dbg_is_ok_bpt(bpttype_t type, ea_t ea, int /*len*/)
|
||||
{
|
||||
switch ( type )
|
||||
{
|
||||
case BPT_SOFT:
|
||||
break;
|
||||
|
||||
case BPT_EXEC:
|
||||
{
|
||||
if ( getn_hwbpts_supported() <= 0 )
|
||||
return BPT_BAD_TYPE;
|
||||
|
||||
int code = find_hwbpt_slot(nullptr, ea);
|
||||
if ( code != BPT_OK )
|
||||
return code;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
{
|
||||
if ( getn_watchpoints_supported() <= 0 )
|
||||
return BPT_BAD_TYPE;
|
||||
|
||||
int code = find_watchpoint_slot(nullptr, ea);
|
||||
if ( code != BPT_OK )
|
||||
return code;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return BPT_OK;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
bool arm_debmod_t::add_hwbpt(bpttype_t type, ea_t ea, int len)
|
||||
{
|
||||
int slot;
|
||||
bool ok = false;
|
||||
switch ( type )
|
||||
{
|
||||
case BPT_EXEC:
|
||||
if ( find_hwbpt_slot(&slot, ea) == BPT_OK )
|
||||
{
|
||||
hwbpt_slots[slot].type = type;
|
||||
hwbpt_slots[slot].addr = ea;
|
||||
hwbpt_slots[slot].ctrl = get_hwbpt_ctrl_bits(type, ea, len);
|
||||
ok = true;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
if ( find_watchpoint_slot(&slot, ea) == BPT_OK )
|
||||
{
|
||||
watch_slots[slot].type = type;
|
||||
watch_slots[slot].addr = ea;
|
||||
watch_slots[slot].ctrl = get_watchpoint_ctrl_bits(type, ea, len);
|
||||
ok = true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
return ok && refresh_hwbpts();
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
bool arm_debmod_t::del_hwbpt(ea_t ea, bpttype_t type)
|
||||
{
|
||||
bool ok = false;
|
||||
switch ( type )
|
||||
{
|
||||
case BPT_EXEC:
|
||||
for ( int i = 0, n = getn_hwbpts_supported(); i < n; i++ )
|
||||
{
|
||||
if ( hwbpt_slots[i].addr == ea )
|
||||
{
|
||||
hwbpt_slots[i].clear();
|
||||
ok = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
for ( int i = 0, n = getn_watchpoints_supported(); i < n; i++ )
|
||||
{
|
||||
if ( watch_slots[i].addr == ea )
|
||||
{
|
||||
watch_slots[i].clear();
|
||||
ok = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
return ok && refresh_hwbpts();
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
void arm_debmod_t::reset_hwbpts(void)
|
||||
{
|
||||
hwbpt_count = -1;
|
||||
watch_count = -1;
|
||||
|
||||
for ( size_t i = 0; i < ARM_MAX_HWBPTS; i++ )
|
||||
hwbpt_slots[i].clear();
|
||||
|
||||
for ( size_t i = 0; i < ARM_MAX_WATCHPOINTS; i++ )
|
||||
watch_slots[i].clear();
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
void arm_debmod_t::cleanup_hwbpts(void)
|
||||
{
|
||||
reset_hwbpts();
|
||||
refresh_hwbpts();
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
int arm_debmod_t::finalize_appcall_stack(
|
||||
call_context_t &ctx,
|
||||
regval_map_t ®s,
|
||||
bytevec_t &/*stk*/)
|
||||
{
|
||||
regs[lr_idx].ival = ctx.ctrl_ea;
|
||||
// return addrsize as the adjustment factor to add to sp
|
||||
// we do not need the return address, that's why we ignore the first 4
|
||||
// bytes of the prepared stack image
|
||||
return debapp_attrs.addrsize;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
int arm_debmod_t::get_regidx(const char *regname, int *clsmask)
|
||||
{
|
||||
return arm_get_regidx(clsmask, regname);
|
||||
}
|
||||
|
||||
#ifdef ENABLE_LOWCNDS
|
||||
//--------------------------------------------------------------------------
|
||||
static const regval_t &idaapi arm_getreg(const char *name, const regval_t *regvals)
|
||||
{
|
||||
int idx = ssmod->get_regidx(name, NULL);
|
||||
QASSERT(30182, idx >= 0 && idx < ssmod->nregs);
|
||||
return regvals[idx];
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
static uint32 idaapi arm_get_long(ea_t ea)
|
||||
{
|
||||
uint32 v = -1;
|
||||
ssmod->dbg_read_memory(ea, &v, sizeof(v), NULL);
|
||||
return v;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
static uint16 idaapi arm_get_word(ea_t ea)
|
||||
{
|
||||
uint16 v = -1;
|
||||
ssmod->dbg_read_memory(ea, &v, sizeof(v), NULL);
|
||||
return v;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
static uint8 idaapi arm_get_byte(ea_t ea)
|
||||
{
|
||||
uint8 v = -1;
|
||||
ssmod->dbg_read_memory(ea, &v, sizeof(v), NULL);
|
||||
return v;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
// stripped down version of get_dtype_size()
|
||||
static size_t idaapi arm_get_dtype_size(op_dtype_t dtype)
|
||||
{
|
||||
switch ( dtype )
|
||||
{
|
||||
case dt_byte: return 1; // 8 bit
|
||||
case dt_word:
|
||||
case dt_half: return 2; // 16 bit
|
||||
case dt_dword:
|
||||
case dt_float: return 4; // 4 byte
|
||||
case dt_qword:
|
||||
case dt_double: return 8; // 8 byte
|
||||
default: return 0;
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// since arm does not have a single step facility, we have to emulate it
|
||||
// with a temporary breakpoint.
|
||||
drc_t arm_debmod_t::dbg_perform_single_step(debug_event_t *dev, const insn_t &insn)
|
||||
{
|
||||
// read register values
|
||||
regvals_t values;
|
||||
values.resize(nregs);
|
||||
drc_t drc = dbg_read_registers(dev->tid, ARM_RC_GENERAL, values.begin(), NULL);
|
||||
if ( drc <= DRC_NONE )
|
||||
return drc;
|
||||
|
||||
static const opinfo_helpers_t oh =
|
||||
{
|
||||
arm_getreg,
|
||||
arm_get_byte,
|
||||
arm_get_word,
|
||||
arm_get_long,
|
||||
arm_get_dtype_size,
|
||||
NULL, // has_insn_cf_chg not needed
|
||||
};
|
||||
|
||||
// calculate the address of the next executed instruction
|
||||
lock_begin();
|
||||
ssmod = this;
|
||||
ea_t next = calc_next_exec_insn(insn, values.begin(), oh, false); // TODO pass is_mprofile parameter
|
||||
ssmod = NULL;
|
||||
lock_end();
|
||||
|
||||
// BADADDR means that the execution flow is linear
|
||||
if ( next == BADADDR )
|
||||
{
|
||||
next = insn.ea + insn.size;
|
||||
if ( (values[sr_idx].ival & BIT5) != 0 ) // thumb?
|
||||
next |= 1;
|
||||
}
|
||||
|
||||
// safety check: self jumping instruction cannot be single stepped
|
||||
if ( (next & ~1) == insn.ea )
|
||||
return DRC_FAILED;
|
||||
|
||||
// add a breakpoint there
|
||||
update_bpt_info_t ubi;
|
||||
ubi.ea = next;
|
||||
ubi.type = BPT_SOFT;
|
||||
ubi.code = 0;
|
||||
int nbpts;
|
||||
drc = dbg_update_bpts(&nbpts, &ubi, 1, 0, NULL);
|
||||
if ( drc != DRC_OK || nbpts == 0 )
|
||||
return drc != DRC_OK ? drc : DRC_FAILED;
|
||||
|
||||
drc = resume_app_and_get_event(dev);
|
||||
|
||||
// clean up: delete the temporary breakpoint
|
||||
ubi.ea &= ~1; // del_bpt requires an even address
|
||||
drc_t drc2 = dbg_update_bpts(&nbpts, &ubi, 0, 1, NULL);
|
||||
if ( drc2 != DRC_OK || nbpts == 0 )
|
||||
{
|
||||
msg("%a: failed to remove single step bpt?!\n", ubi.ea);
|
||||
drc = drc2 != DRC_OK ? drc2 : DRC_FAILED;
|
||||
}
|
||||
// the caller expects to see STEP after us:
|
||||
if ( drc == DRC_OK )
|
||||
dev->set_eid(STEP);
|
||||
return drc;
|
||||
}
|
||||
|
||||
#endif // ENABLE_LOWCNDS
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
void arm_debmod_t::adjust_swbpt(ea_t *p_ea, int *p_len)
|
||||
{
|
||||
ea_t &ea = *p_ea;
|
||||
if ( (ea & 1) != 0 ) // T bit is set, use a thumb breakpoint
|
||||
{
|
||||
ea--;
|
||||
*p_len = 2;
|
||||
}
|
||||
}
|
||||
77
idasdk76/dbg/arm_debmod.h
Normal file
77
idasdk76/dbg/arm_debmod.h
Normal file
@@ -0,0 +1,77 @@
|
||||
#ifndef __ARM_DEBMOD__
|
||||
#define __ARM_DEBMOD__
|
||||
|
||||
#include "arm_regs.hpp"
|
||||
#include "deb_arm.hpp"
|
||||
#include "debmod.h"
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
struct arm_hwbpt_info_t
|
||||
{
|
||||
bpttype_t type; // BPT_...
|
||||
ea_t addr; // target address
|
||||
ea_t ctrl; // control value
|
||||
|
||||
arm_hwbpt_info_t(void) { clear(); }
|
||||
|
||||
void clear(void)
|
||||
{
|
||||
type = BPT_DEFAULT;
|
||||
addr = BADADDR;
|
||||
ctrl = 0;
|
||||
}
|
||||
};
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
class arm_debmod_t : public debmod_t
|
||||
{
|
||||
typedef debmod_t inherited;
|
||||
|
||||
protected:
|
||||
int hwbpt_count;
|
||||
int watch_count;
|
||||
|
||||
arm_hwbpt_info_t hwbpt_slots[ARM_MAX_HWBPTS];
|
||||
arm_hwbpt_info_t watch_slots[ARM_MAX_WATCHPOINTS];
|
||||
|
||||
int lr_idx;
|
||||
int sr_idx;
|
||||
|
||||
public:
|
||||
arm_debmod_t(void);
|
||||
|
||||
void reset_hwbpts(void);
|
||||
void cleanup_hwbpts(void);
|
||||
|
||||
int getn_hwbpts_supported(void);
|
||||
int getn_watchpoints_supported(void);
|
||||
|
||||
int find_hwbpt_slot(int *slot, ea_t ea);
|
||||
int find_watchpoint_slot(int *slot, ea_t ea);
|
||||
|
||||
bool add_hwbpt(bpttype_t type, ea_t ea, int len);
|
||||
bool del_hwbpt(ea_t ea, bpttype_t type);
|
||||
|
||||
// subclasses must implement these functions to properly support hardware breakpoints/watchpoints
|
||||
virtual int _getn_hwbpts_supported(void) newapi { return -1; }
|
||||
virtual int _getn_watchpoints_supported(void) newapi { return -1; }
|
||||
virtual ea_t get_hwbpt_ctrl_bits(bpttype_t, ea_t, int) newapi { return 0; }
|
||||
virtual ea_t get_watchpoint_ctrl_bits(bpttype_t, ea_t, int) newapi { return 0; }
|
||||
virtual bool refresh_hwbpts(void) newapi { return false; }
|
||||
|
||||
// overridden base class functions
|
||||
virtual int idaapi dbg_is_ok_bpt(bpttype_t type, ea_t ea, int len) override;
|
||||
virtual int finalize_appcall_stack(call_context_t &ctx, regval_map_t ®s, bytevec_t &stk) override;
|
||||
|
||||
virtual int get_regidx(const char *regname, int *clsmask) override;
|
||||
virtual void adjust_swbpt(ea_t *p_ea, int *p_len) override;
|
||||
|
||||
protected:
|
||||
#ifdef ENABLE_LOWCNDS
|
||||
virtual drc_t dbg_perform_single_step(debug_event_t *dev, const insn_t &insn) override;
|
||||
#endif
|
||||
};
|
||||
|
||||
bool is_32bit_thumb_insn(uint16 code);
|
||||
|
||||
#endif
|
||||
101
idasdk76/dbg/arm_local_impl.cpp
Normal file
101
idasdk76/dbg/arm_local_impl.cpp
Normal file
@@ -0,0 +1,101 @@
|
||||
#include <idp.hpp>
|
||||
#include <dbg.hpp>
|
||||
#include <loader.hpp>
|
||||
#include <segregs.hpp>
|
||||
#include <segment.hpp>
|
||||
|
||||
#include "deb_arm.hpp"
|
||||
|
||||
#include "arm_regs.cpp"
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
int is_arm_valid_bpt(bpttype_t type, ea_t ea, int len)
|
||||
{
|
||||
switch ( type )
|
||||
{
|
||||
case BPT_SOFT:
|
||||
if ( (ea & 1) != 0 )
|
||||
return BPT_BAD_ADDR;
|
||||
break;
|
||||
|
||||
case BPT_EXEC:
|
||||
if ( (ea & 3) != 0 )
|
||||
return BPT_BAD_ALIGN;
|
||||
break;
|
||||
|
||||
default:
|
||||
if ( (ea & 7) != 0 )
|
||||
return BPT_BAD_ALIGN;
|
||||
if ( len < 1 || len > 8 )
|
||||
return BPT_BAD_LEN;
|
||||
break;
|
||||
}
|
||||
|
||||
return BPT_OK;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// if bit0 is set, ensure that thumb mode
|
||||
// if bit0 is clear, ensure that arm mode
|
||||
static void handle_arm_thumb_modes(ea_t ea)
|
||||
{
|
||||
bool should_be_thumb = (ea & 1) != 0;
|
||||
bool is_thumb = processor_t::get_code16_mode(ea);
|
||||
if ( should_be_thumb != is_thumb )
|
||||
processor_t::set_code16_mode(ea, should_be_thumb);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
static easet_t pending_addresses;
|
||||
|
||||
static ssize_t idaapi dbg_callback(void *, int code, va_list)
|
||||
{
|
||||
// we apply thumb/arm switches when the process is suspended.
|
||||
// it is quite late (normally we should do it as soon as the corresponding
|
||||
// segment is created) but i did not manage to make it work.
|
||||
// in the segm_added event the addresses are not enabled yet,
|
||||
// so switching modes fails.
|
||||
if ( code == dbg_suspend_process && !pending_addresses.empty() )
|
||||
{
|
||||
for ( easet_t::iterator p=pending_addresses.begin();
|
||||
p != pending_addresses.end();
|
||||
++p )
|
||||
{
|
||||
handle_arm_thumb_modes(*p);
|
||||
}
|
||||
pending_addresses.clear();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// For ARM processors the low bit means 1-thumb, 0-arm mode.
|
||||
// The following function goes over the address list and sets the mode
|
||||
// in IDA database according to bit0. It also resets bit0 for all addresses.
|
||||
void set_arm_thumb_modes(ea_t *addrs, int qty)
|
||||
{
|
||||
for ( int i=0; i < qty; i++ )
|
||||
{
|
||||
ea_t ea = addrs[i];
|
||||
segment_t *s = getseg(ea);
|
||||
if ( s == NULL )
|
||||
pending_addresses.insert(ea);
|
||||
else
|
||||
handle_arm_thumb_modes(ea);
|
||||
|
||||
addrs[i] = ea & ~1;
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
void processor_specific_init(void)
|
||||
{
|
||||
hook_to_notification_point(HT_DBG, dbg_callback);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
void processor_specific_term(void)
|
||||
{
|
||||
unhook_from_notification_point(HT_DBG, dbg_callback);
|
||||
pending_addresses.clear();
|
||||
}
|
||||
259
idasdk76/dbg/arm_regs.cpp
Normal file
259
idasdk76/dbg/arm_regs.cpp
Normal file
@@ -0,0 +1,259 @@
|
||||
|
||||
#include "arm_regs.hpp"
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
// NOTE: keep in sync with register_class_arm_t
|
||||
const char *arm_register_classes[] =
|
||||
{
|
||||
"General registers",
|
||||
"VFP registers",
|
||||
"NEON registers",
|
||||
NULL
|
||||
};
|
||||
|
||||
#ifndef __EA64__
|
||||
//-------------------------------------------------------------------------
|
||||
static const char *const psr[] =
|
||||
{
|
||||
"MODE", // 0
|
||||
"MODE", // 1
|
||||
"MODE", // 2
|
||||
"MODE", // 3
|
||||
"MODE", // 4
|
||||
"T", // 5
|
||||
"F", // 6
|
||||
"I", // 7
|
||||
"A", // 8
|
||||
"E", // 9
|
||||
"IT", // 10
|
||||
"IT", // 11
|
||||
"IT", // 12
|
||||
"IT", // 13
|
||||
"IT", // 14
|
||||
"IT", // 15
|
||||
"GE", // 16
|
||||
"GE", // 17
|
||||
"GE", // 18
|
||||
"GE", // 19
|
||||
NULL, // 20
|
||||
NULL, // 21
|
||||
NULL, // 22
|
||||
NULL, // 23
|
||||
"J", // 24
|
||||
"IT2", // 25 additional bits of IT
|
||||
"IT2", // 26 additional bits of IT
|
||||
"Q", // 27
|
||||
"V", // 28
|
||||
"C", // 29
|
||||
"Z", // 30
|
||||
"N", // 31
|
||||
};
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
static const char *const vfp_format[] =
|
||||
{
|
||||
"VFP_1_double",
|
||||
};
|
||||
#else
|
||||
//-------------------------------------------------------------------------
|
||||
static const char *const psr[] =
|
||||
{
|
||||
"M", // 0 AArch32 mode that an exception was taken from
|
||||
"M", // 1
|
||||
"M", // 2
|
||||
"M", // 3
|
||||
"M", // 4 Execution state that the exception was taken from
|
||||
"T", // 5 T32 Instruction set state bit
|
||||
"F", // 6 FIQ mask bit
|
||||
"I", // 7 IRQ mask bit
|
||||
"A", // 8 Asynchronous data abort mask bit
|
||||
"E", // 9 Endianness Execution State bit
|
||||
"IT", // 10 If-Then
|
||||
"IT", // 11
|
||||
"IT", // 12
|
||||
"IT", // 13
|
||||
"IT", // 14
|
||||
"IT", // 15
|
||||
"GE", // 16 Greater than or Equal flags
|
||||
"GE", // 17
|
||||
"GE", // 18
|
||||
"GE", // 19
|
||||
"IL", // 20 Illegal Execution State bit
|
||||
NULL, // 21
|
||||
NULL, // 22
|
||||
NULL, // 23
|
||||
NULL, // 24
|
||||
"IT2", // 25 If-Then
|
||||
"IT2", // 26
|
||||
"Q", // 27 Cumulative saturation bit
|
||||
"V", // 28 oVerflow condition flag
|
||||
"C", // 29 Carry condition flag
|
||||
"Z", // 30 Zero condition flag
|
||||
"N", // 31 Negative condition flag
|
||||
};
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
static const char *const neon_formats[] =
|
||||
{
|
||||
"NEON_16_bytes",
|
||||
"NEON_8_words",
|
||||
"NEON_4_dwords",
|
||||
"NEON_2_qwords",
|
||||
"NEON_4_floats",
|
||||
"NEON_2_doubles",
|
||||
};
|
||||
#endif
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
// NOTE: keep in sync with register_arm_t
|
||||
register_info_t arm_registers[] =
|
||||
{
|
||||
#ifndef __EA64__
|
||||
// General registers
|
||||
{ "R0", REGISTER_ADDRESS, ARM_RC_GENERAL, dt_dword, NULL, 0 },
|
||||
{ "R1", REGISTER_ADDRESS, ARM_RC_GENERAL, dt_dword, NULL, 0 },
|
||||
{ "R2", REGISTER_ADDRESS, ARM_RC_GENERAL, dt_dword, NULL, 0 },
|
||||
{ "R3", REGISTER_ADDRESS, ARM_RC_GENERAL, dt_dword, NULL, 0 },
|
||||
{ "R4", REGISTER_ADDRESS, ARM_RC_GENERAL, dt_dword, NULL, 0 },
|
||||
{ "R5", REGISTER_ADDRESS, ARM_RC_GENERAL, dt_dword, NULL, 0 },
|
||||
{ "R6", REGISTER_ADDRESS, ARM_RC_GENERAL, dt_dword, NULL, 0 },
|
||||
{ "R7", REGISTER_ADDRESS, ARM_RC_GENERAL, dt_dword, NULL, 0 },
|
||||
{ "R8", REGISTER_ADDRESS, ARM_RC_GENERAL, dt_dword, NULL, 0 },
|
||||
{ "R9", REGISTER_ADDRESS, ARM_RC_GENERAL, dt_dword, NULL, 0 },
|
||||
{ "R10", REGISTER_ADDRESS, ARM_RC_GENERAL, dt_dword, NULL, 0 },
|
||||
{ "R11", REGISTER_ADDRESS, ARM_RC_GENERAL, dt_dword, NULL, 0 },
|
||||
{ "R12", REGISTER_ADDRESS|REGISTER_FP, ARM_RC_GENERAL, dt_dword, NULL, 0 },
|
||||
{ "SP", REGISTER_ADDRESS|REGISTER_SP, ARM_RC_GENERAL, dt_dword, NULL, 0 },
|
||||
{ "LR", REGISTER_ADDRESS, ARM_RC_GENERAL, dt_dword, NULL, 0 },
|
||||
{ "PC", REGISTER_ADDRESS|REGISTER_IP, ARM_RC_GENERAL, dt_dword, NULL, 0 },
|
||||
{ "PSR", 0, ARM_RC_GENERAL, dt_dword, psr, 0xF800007F },
|
||||
// VFP registers
|
||||
{ "D0", REGISTER_CUSTFMT, ARM_RC_VFP, dt_qword, vfp_format, 0 },
|
||||
{ "D1", REGISTER_CUSTFMT, ARM_RC_VFP, dt_qword, vfp_format, 0 },
|
||||
{ "D2", REGISTER_CUSTFMT, ARM_RC_VFP, dt_qword, vfp_format, 0 },
|
||||
{ "D3", REGISTER_CUSTFMT, ARM_RC_VFP, dt_qword, vfp_format, 0 },
|
||||
{ "D4", REGISTER_CUSTFMT, ARM_RC_VFP, dt_qword, vfp_format, 0 },
|
||||
{ "D5", REGISTER_CUSTFMT, ARM_RC_VFP, dt_qword, vfp_format, 0 },
|
||||
{ "D6", REGISTER_CUSTFMT, ARM_RC_VFP, dt_qword, vfp_format, 0 },
|
||||
{ "D7", REGISTER_CUSTFMT, ARM_RC_VFP, dt_qword, vfp_format, 0 },
|
||||
{ "D8", REGISTER_CUSTFMT, ARM_RC_VFP, dt_qword, vfp_format, 0 },
|
||||
{ "D9", REGISTER_CUSTFMT, ARM_RC_VFP, dt_qword, vfp_format, 0 },
|
||||
{ "D10", REGISTER_CUSTFMT, ARM_RC_VFP, dt_qword, vfp_format, 0 },
|
||||
{ "D11", REGISTER_CUSTFMT, ARM_RC_VFP, dt_qword, vfp_format, 0 },
|
||||
{ "D12", REGISTER_CUSTFMT, ARM_RC_VFP, dt_qword, vfp_format, 0 },
|
||||
{ "D13", REGISTER_CUSTFMT, ARM_RC_VFP, dt_qword, vfp_format, 0 },
|
||||
{ "D14", REGISTER_CUSTFMT, ARM_RC_VFP, dt_qword, vfp_format, 0 },
|
||||
{ "D15", REGISTER_CUSTFMT, ARM_RC_VFP, dt_qword, vfp_format, 0 },
|
||||
{ "D16", REGISTER_CUSTFMT, ARM_RC_VFP, dt_qword, vfp_format, 0 },
|
||||
{ "D17", REGISTER_CUSTFMT, ARM_RC_VFP, dt_qword, vfp_format, 0 },
|
||||
{ "D18", REGISTER_CUSTFMT, ARM_RC_VFP, dt_qword, vfp_format, 0 },
|
||||
{ "D19", REGISTER_CUSTFMT, ARM_RC_VFP, dt_qword, vfp_format, 0 },
|
||||
{ "D20", REGISTER_CUSTFMT, ARM_RC_VFP, dt_qword, vfp_format, 0 },
|
||||
{ "D21", REGISTER_CUSTFMT, ARM_RC_VFP, dt_qword, vfp_format, 0 },
|
||||
{ "D22", REGISTER_CUSTFMT, ARM_RC_VFP, dt_qword, vfp_format, 0 },
|
||||
{ "D23", REGISTER_CUSTFMT, ARM_RC_VFP, dt_qword, vfp_format, 0 },
|
||||
{ "D24", REGISTER_CUSTFMT, ARM_RC_VFP, dt_qword, vfp_format, 0 },
|
||||
{ "D25", REGISTER_CUSTFMT, ARM_RC_VFP, dt_qword, vfp_format, 0 },
|
||||
{ "D26", REGISTER_CUSTFMT, ARM_RC_VFP, dt_qword, vfp_format, 0 },
|
||||
{ "D27", REGISTER_CUSTFMT, ARM_RC_VFP, dt_qword, vfp_format, 0 },
|
||||
{ "D28", REGISTER_CUSTFMT, ARM_RC_VFP, dt_qword, vfp_format, 0 },
|
||||
{ "D29", REGISTER_CUSTFMT, ARM_RC_VFP, dt_qword, vfp_format, 0 },
|
||||
{ "D30", REGISTER_CUSTFMT, ARM_RC_VFP, dt_qword, vfp_format, 0 },
|
||||
{ "D31", REGISTER_CUSTFMT, ARM_RC_VFP, dt_qword, vfp_format, 0 },
|
||||
{ "FPSCR", 0, ARM_RC_VFP, dt_dword, NULL, 0 },
|
||||
#else
|
||||
// General registers
|
||||
{ "X0", REGISTER_ADDRESS, ARM_RC_GENERAL, dt_qword, NULL, 0 },
|
||||
{ "X1", REGISTER_ADDRESS, ARM_RC_GENERAL, dt_qword, NULL, 0 },
|
||||
{ "X2", REGISTER_ADDRESS, ARM_RC_GENERAL, dt_qword, NULL, 0 },
|
||||
{ "X3", REGISTER_ADDRESS, ARM_RC_GENERAL, dt_qword, NULL, 0 },
|
||||
{ "X4", REGISTER_ADDRESS, ARM_RC_GENERAL, dt_qword, NULL, 0 },
|
||||
{ "X5", REGISTER_ADDRESS, ARM_RC_GENERAL, dt_qword, NULL, 0 },
|
||||
{ "X6", REGISTER_ADDRESS, ARM_RC_GENERAL, dt_qword, NULL, 0 },
|
||||
{ "X7", REGISTER_ADDRESS, ARM_RC_GENERAL, dt_qword, NULL, 0 },
|
||||
{ "X8", REGISTER_ADDRESS, ARM_RC_GENERAL, dt_qword, NULL, 0 },
|
||||
{ "X9", REGISTER_ADDRESS, ARM_RC_GENERAL, dt_qword, NULL, 0 },
|
||||
{ "X10", REGISTER_ADDRESS, ARM_RC_GENERAL, dt_qword, NULL, 0 },
|
||||
{ "X11", REGISTER_ADDRESS, ARM_RC_GENERAL, dt_qword, NULL, 0 },
|
||||
{ "X12", REGISTER_ADDRESS, ARM_RC_GENERAL, dt_qword, NULL, 0 },
|
||||
{ "X13", REGISTER_ADDRESS, ARM_RC_GENERAL, dt_qword, NULL, 0 },
|
||||
{ "X14", REGISTER_ADDRESS, ARM_RC_GENERAL, dt_qword, NULL, 0 },
|
||||
{ "X15", REGISTER_ADDRESS, ARM_RC_GENERAL, dt_qword, NULL, 0 },
|
||||
{ "X16", REGISTER_ADDRESS, ARM_RC_GENERAL, dt_qword, NULL, 0 },
|
||||
{ "X17", REGISTER_ADDRESS, ARM_RC_GENERAL, dt_qword, NULL, 0 },
|
||||
{ "X18", REGISTER_ADDRESS, ARM_RC_GENERAL, dt_qword, NULL, 0 },
|
||||
{ "X19", REGISTER_ADDRESS, ARM_RC_GENERAL, dt_qword, NULL, 0 },
|
||||
{ "X20", REGISTER_ADDRESS, ARM_RC_GENERAL, dt_qword, NULL, 0 },
|
||||
{ "X21", REGISTER_ADDRESS, ARM_RC_GENERAL, dt_qword, NULL, 0 },
|
||||
{ "X22", REGISTER_ADDRESS, ARM_RC_GENERAL, dt_qword, NULL, 0 },
|
||||
{ "X23", REGISTER_ADDRESS, ARM_RC_GENERAL, dt_qword, NULL, 0 },
|
||||
{ "X24", REGISTER_ADDRESS, ARM_RC_GENERAL, dt_qword, NULL, 0 },
|
||||
{ "X25", REGISTER_ADDRESS, ARM_RC_GENERAL, dt_qword, NULL, 0 },
|
||||
{ "X26", REGISTER_ADDRESS, ARM_RC_GENERAL, dt_qword, NULL, 0 },
|
||||
{ "X27", REGISTER_ADDRESS, ARM_RC_GENERAL, dt_qword, NULL, 0 },
|
||||
{ "X28", REGISTER_ADDRESS, ARM_RC_GENERAL, dt_qword, NULL, 0 },
|
||||
{ "X29", REGISTER_ADDRESS|REGISTER_FP, ARM_RC_GENERAL, dt_qword, NULL, 0 },
|
||||
{ "X30", REGISTER_ADDRESS, ARM_RC_GENERAL, dt_qword, NULL, 0 },
|
||||
{ "SP", REGISTER_ADDRESS|REGISTER_SP, ARM_RC_GENERAL, dt_qword, NULL, 0 },
|
||||
{ "PC", REGISTER_ADDRESS|REGISTER_IP, ARM_RC_GENERAL, dt_qword, NULL, 0 },
|
||||
{ "PSR", 0, ARM_RC_GENERAL, dt_dword, psr, 0xF8000000 },
|
||||
{ "V0", REGISTER_CUSTFMT, ARM_RC_NEON, dt_byte16, neon_formats, 0 },
|
||||
{ "V1", REGISTER_CUSTFMT, ARM_RC_NEON, dt_byte16, neon_formats, 0 },
|
||||
{ "V2", REGISTER_CUSTFMT, ARM_RC_NEON, dt_byte16, neon_formats, 0 },
|
||||
{ "V3", REGISTER_CUSTFMT, ARM_RC_NEON, dt_byte16, neon_formats, 0 },
|
||||
{ "V4", REGISTER_CUSTFMT, ARM_RC_NEON, dt_byte16, neon_formats, 0 },
|
||||
{ "V5", REGISTER_CUSTFMT, ARM_RC_NEON, dt_byte16, neon_formats, 0 },
|
||||
{ "V6", REGISTER_CUSTFMT, ARM_RC_NEON, dt_byte16, neon_formats, 0 },
|
||||
{ "V7", REGISTER_CUSTFMT, ARM_RC_NEON, dt_byte16, neon_formats, 0 },
|
||||
{ "V8", REGISTER_CUSTFMT, ARM_RC_NEON, dt_byte16, neon_formats, 0 },
|
||||
{ "V9", REGISTER_CUSTFMT, ARM_RC_NEON, dt_byte16, neon_formats, 0 },
|
||||
{ "V10", REGISTER_CUSTFMT, ARM_RC_NEON, dt_byte16, neon_formats, 0 },
|
||||
{ "V11", REGISTER_CUSTFMT, ARM_RC_NEON, dt_byte16, neon_formats, 0 },
|
||||
{ "V12", REGISTER_CUSTFMT, ARM_RC_NEON, dt_byte16, neon_formats, 0 },
|
||||
{ "V13", REGISTER_CUSTFMT, ARM_RC_NEON, dt_byte16, neon_formats, 0 },
|
||||
{ "V14", REGISTER_CUSTFMT, ARM_RC_NEON, dt_byte16, neon_formats, 0 },
|
||||
{ "V15", REGISTER_CUSTFMT, ARM_RC_NEON, dt_byte16, neon_formats, 0 },
|
||||
{ "V16", REGISTER_CUSTFMT, ARM_RC_NEON, dt_byte16, neon_formats, 0 },
|
||||
{ "V17", REGISTER_CUSTFMT, ARM_RC_NEON, dt_byte16, neon_formats, 0 },
|
||||
{ "V18", REGISTER_CUSTFMT, ARM_RC_NEON, dt_byte16, neon_formats, 0 },
|
||||
{ "V19", REGISTER_CUSTFMT, ARM_RC_NEON, dt_byte16, neon_formats, 0 },
|
||||
{ "V20", REGISTER_CUSTFMT, ARM_RC_NEON, dt_byte16, neon_formats, 0 },
|
||||
{ "V21", REGISTER_CUSTFMT, ARM_RC_NEON, dt_byte16, neon_formats, 0 },
|
||||
{ "V22", REGISTER_CUSTFMT, ARM_RC_NEON, dt_byte16, neon_formats, 0 },
|
||||
{ "V23", REGISTER_CUSTFMT, ARM_RC_NEON, dt_byte16, neon_formats, 0 },
|
||||
{ "V24", REGISTER_CUSTFMT, ARM_RC_NEON, dt_byte16, neon_formats, 0 },
|
||||
{ "V25", REGISTER_CUSTFMT, ARM_RC_NEON, dt_byte16, neon_formats, 0 },
|
||||
{ "V26", REGISTER_CUSTFMT, ARM_RC_NEON, dt_byte16, neon_formats, 0 },
|
||||
{ "V27", REGISTER_CUSTFMT, ARM_RC_NEON, dt_byte16, neon_formats, 0 },
|
||||
{ "V28", REGISTER_CUSTFMT, ARM_RC_NEON, dt_byte16, neon_formats, 0 },
|
||||
{ "V29", REGISTER_CUSTFMT, ARM_RC_NEON, dt_byte16, neon_formats, 0 },
|
||||
{ "V30", REGISTER_CUSTFMT, ARM_RC_NEON, dt_byte16, neon_formats, 0 },
|
||||
{ "V31", REGISTER_CUSTFMT, ARM_RC_NEON, dt_byte16, neon_formats, 0 },
|
||||
{ "FPSR", 0, ARM_RC_NEON, dt_dword, NULL, 0 },
|
||||
{ "FPCR", 0, ARM_RC_NEON, dt_dword, NULL, 0 },
|
||||
#endif
|
||||
};
|
||||
CASSERT(qnumber(arm_registers) == ARM_NREGS);
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
int arm_get_regidx(int *clsmask, const char *regname)
|
||||
{
|
||||
for ( size_t i = 0; i < qnumber(arm_registers); i++ )
|
||||
{
|
||||
if ( strieq(regname, arm_registers[i].name) )
|
||||
{
|
||||
if ( clsmask != NULL )
|
||||
*clsmask = arm_registers[i].register_class;
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
int arm_get_regclass(int idx)
|
||||
{
|
||||
if ( idx >= 0 && idx < qnumber(arm_registers) )
|
||||
return arm_registers[idx].register_class;
|
||||
return 0;
|
||||
}
|
||||
182
idasdk76/dbg/arm_regs.hpp
Normal file
182
idasdk76/dbg/arm_regs.hpp
Normal file
@@ -0,0 +1,182 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <pro.h>
|
||||
#include <idd.hpp>
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
#if defined(__LINUX__) && defined(__ARM__) && !defined(__EA64__)
|
||||
# define __HAVE_ARM_VFP__
|
||||
#endif
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
#if defined(__MAC__) && defined(__ARM__)
|
||||
# define __HAVE_ARM_NEON__
|
||||
#endif
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
// NOTE: keep in sync with arm_register_classes
|
||||
enum register_class_arm_t
|
||||
{
|
||||
ARM_RC_GENERAL = 0x01, // General registers
|
||||
ARM_RC_VFP = 0x02, // VFP registers
|
||||
ARM_RC_NEON = 0x04, // NEON registers
|
||||
ARM_RC_ALL = ARM_RC_GENERAL
|
||||
#ifdef __HAVE_ARM_VFP__
|
||||
| ARM_RC_VFP
|
||||
#endif
|
||||
#ifdef __HAVE_ARM_NEON__
|
||||
| ARM_RC_NEON
|
||||
#endif
|
||||
};
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
// NOTE: keep in sync with arm_registers
|
||||
enum register_arm_t
|
||||
{
|
||||
#ifndef __EA64__
|
||||
// General registers
|
||||
R_R0,
|
||||
R_R1,
|
||||
R_R2,
|
||||
R_R3,
|
||||
R_R4,
|
||||
R_R5,
|
||||
R_R6,
|
||||
R_R7,
|
||||
R_R8,
|
||||
R_R9,
|
||||
R_R10,
|
||||
R_R11,
|
||||
R_R12,
|
||||
R_SP,
|
||||
R_LR,
|
||||
R_PC,
|
||||
R_PSR,
|
||||
// VFP registers
|
||||
R_D0,
|
||||
R_D1,
|
||||
R_D2,
|
||||
R_D3,
|
||||
R_D4,
|
||||
R_D5,
|
||||
R_D6,
|
||||
R_D7,
|
||||
R_D8,
|
||||
R_D9,
|
||||
R_D10,
|
||||
R_D11,
|
||||
R_D12,
|
||||
R_D13,
|
||||
R_D14,
|
||||
R_D15,
|
||||
R_D16,
|
||||
R_D17,
|
||||
R_D18,
|
||||
R_D19,
|
||||
R_D20,
|
||||
R_D21,
|
||||
R_D22,
|
||||
R_D23,
|
||||
R_D24,
|
||||
R_D25,
|
||||
R_D26,
|
||||
R_D27,
|
||||
R_D28,
|
||||
R_D29,
|
||||
R_D30,
|
||||
R_D31,
|
||||
R_FPSCR,
|
||||
#else
|
||||
// General registers
|
||||
R_R0,
|
||||
R_R1,
|
||||
R_R2,
|
||||
R_R3,
|
||||
R_R4,
|
||||
R_R5,
|
||||
R_R6,
|
||||
R_R7,
|
||||
R_R8,
|
||||
R_R9,
|
||||
R_R10,
|
||||
R_R11,
|
||||
R_R12,
|
||||
R_R13,
|
||||
R_R14,
|
||||
R_R15,
|
||||
R_R16,
|
||||
R_R17,
|
||||
R_R18,
|
||||
R_R19,
|
||||
R_R20,
|
||||
R_R21,
|
||||
R_R22,
|
||||
R_R23,
|
||||
R_R24,
|
||||
R_R25,
|
||||
R_R26,
|
||||
R_R27,
|
||||
R_R28,
|
||||
R_R29,
|
||||
R_LR,
|
||||
R_SP,
|
||||
R_PC,
|
||||
R_PSR,
|
||||
// NEON registers
|
||||
R_V0,
|
||||
R_V1,
|
||||
R_V2,
|
||||
R_V3,
|
||||
R_V4,
|
||||
R_V5,
|
||||
R_V6,
|
||||
R_V7,
|
||||
R_V8,
|
||||
R_V9,
|
||||
R_V10,
|
||||
R_V11,
|
||||
R_V12,
|
||||
R_V13,
|
||||
R_V14,
|
||||
R_V15,
|
||||
R_V16,
|
||||
R_V17,
|
||||
R_V18,
|
||||
R_V19,
|
||||
R_V20,
|
||||
R_V21,
|
||||
R_V22,
|
||||
R_V23,
|
||||
R_V24,
|
||||
R_V25,
|
||||
R_V26,
|
||||
R_V27,
|
||||
R_V28,
|
||||
R_V29,
|
||||
R_V30,
|
||||
R_V31,
|
||||
R_FPSR,
|
||||
R_FPCR,
|
||||
#endif
|
||||
};
|
||||
|
||||
// Number of registers in arm and aarch64
|
||||
#define ARM64_NREGS 68
|
||||
#define ARM32_NREGS 50
|
||||
|
||||
#ifdef __EA64__
|
||||
#define ARM_NREGS ARM64_NREGS
|
||||
#else
|
||||
#define ARM_NREGS ARM32_NREGS
|
||||
#endif
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
extern const char *arm_register_classes[];
|
||||
extern register_info_t arm_registers[ARM_NREGS];
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
int arm_get_regidx(int *clsmask, const char *regname);
|
||||
int arm_get_regclass(int idx);
|
||||
906
idasdk76/dbg/bin_search.cpp
Normal file
906
idasdk76/dbg/bin_search.cpp
Normal file
@@ -0,0 +1,906 @@
|
||||
#include <pro.h>
|
||||
#include "debmod.h"
|
||||
|
||||
//#define TEST
|
||||
#ifdef TEST
|
||||
static uchar memory[256];
|
||||
static const int PAGESZ = 4;
|
||||
static ssize_t read_page(ea_t ea, void *buf, size_t size, qstring *)
|
||||
{
|
||||
QASSERT(1517, (size % PAGESZ) == 0);
|
||||
if ( ea >= sizeof(memory) )
|
||||
return -1;
|
||||
memcpy(buf, &memory[ea], size);
|
||||
return size;
|
||||
}
|
||||
#else
|
||||
static const int PAGESZ = 4096;
|
||||
#define read_page(ea, buf, size, errbuf) mod->dbg_read_memory(ea, buf, size, errbuf)
|
||||
#endif
|
||||
static const int PAGE_HB = 1000; // page heartbeat counter
|
||||
static const int TIME_HB = (RECV_TIMEOUT_PERIOD/1000) / 2;
|
||||
// time period between heartbeats
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// memrchr is unavailable under Windows and MAC
|
||||
#if defined(_WIN32) || defined(__MAC__)
|
||||
// fixme: we need more optimized version
|
||||
static void *local_memrchr(const void *s, int c, size_t n)
|
||||
{
|
||||
const unsigned char *start = (const unsigned char *)s;
|
||||
const unsigned char *end = start + n - 1;
|
||||
while ( end >= start )
|
||||
{
|
||||
if ( *end == c )
|
||||
return (void *)end;
|
||||
end--;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
#else
|
||||
#define local_memrchr memrchr
|
||||
#endif
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
class matcher_t
|
||||
{
|
||||
protected:
|
||||
struct partmatch_t
|
||||
{
|
||||
ea_t match_ea; // starting address of the match
|
||||
size_t ptn_idx; // index of the pattern
|
||||
size_t ptn_off; // offset inside the pattern
|
||||
};
|
||||
typedef qlist<partmatch_t> partmatches_t;
|
||||
|
||||
// constructor arguments
|
||||
ea_t *found_ea;
|
||||
debmod_t *mod;
|
||||
const compiled_binpat_vec_t &ptns;
|
||||
int srch_flags;
|
||||
qstring *errbuf; //lint !e958
|
||||
|
||||
uchar page[PAGESZ];
|
||||
ea_t page_ea;
|
||||
partmatches_t pmatches;
|
||||
ea_t failed_ea;
|
||||
|
||||
// cache
|
||||
intvec_t simple_ptns; // indexes of patterns w/o a mask and the search is case sensitive
|
||||
intvec_t complex_ptns; // other patterns
|
||||
|
||||
// heartbeat
|
||||
uint32 last_hb; // time in secs of the last heartbeat
|
||||
|
||||
matcher_t(
|
||||
ea_t *_found_ea,
|
||||
debmod_t *_mod,
|
||||
const compiled_binpat_vec_t &_ptns,
|
||||
int _srch_flags,
|
||||
qstring *_errbuf)
|
||||
: found_ea(_found_ea),
|
||||
mod(_mod),
|
||||
ptns(_ptns),
|
||||
srch_flags(_srch_flags),
|
||||
errbuf(_errbuf),
|
||||
page_ea(BADADDR),
|
||||
failed_ea(BADADDR)
|
||||
{
|
||||
for ( int i=0; i < ptns.size(); ++i )
|
||||
{
|
||||
const compiled_binpat_t &ptn = ptns[i];
|
||||
if ( ptn.bytes.empty() )
|
||||
continue;
|
||||
if ( sense_case() && ptn.all_bytes_defined() ) // TODO: && !inf_is_wide_high_byte_first() - for servers
|
||||
simple_ptns.push_back(i);
|
||||
else
|
||||
complex_ptns.push_back(i);
|
||||
}
|
||||
memset(page, 0, sizeof(page));
|
||||
last_hb = get_secs(qtime64());
|
||||
}
|
||||
|
||||
bool sense_case(void) const { return (srch_flags & BIN_SEARCH_CASE) != 0; }
|
||||
bool check_break(void) const { return (srch_flags & BIN_SEARCH_NOBREAK) == 0; }
|
||||
|
||||
bool test_cancelled(void) const
|
||||
{
|
||||
struct ida_local tester_t : public exec_request_t
|
||||
{
|
||||
virtual ~tester_t() {}
|
||||
virtual int idaapi execute(void)
|
||||
{
|
||||
return user_cancelled();
|
||||
}
|
||||
};
|
||||
tester_t tester;
|
||||
return static_cast<bool>(execute_sync(tester, MFF_FAST));
|
||||
}
|
||||
|
||||
void send_heartbeat(size_t *page_counter)
|
||||
{
|
||||
*page_counter += 1;
|
||||
if ( *page_counter >= PAGE_HB )
|
||||
{
|
||||
*page_counter = 0;
|
||||
uint32 now = get_secs(qtime64());
|
||||
if ( now - last_hb >= TIME_HB )
|
||||
{
|
||||
mod->dmsg(""); // heartbeat
|
||||
last_hb = now;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
virtual ~matcher_t()
|
||||
{
|
||||
found_ea = NULL;
|
||||
mod = NULL;
|
||||
errbuf = NULL;
|
||||
}
|
||||
|
||||
virtual drc_t walk_memory_ranges(const ea_t srch_start_ea, const ea_t srch_end_ea) = 0;
|
||||
|
||||
drc_t search_memory_range(ea_t range_start_ea, ea_t range_end_ea)
|
||||
{
|
||||
if ( range_is_unreadable(range_start_ea) )
|
||||
{
|
||||
#ifndef TEST
|
||||
mod->debdeb("dbg_bin_search memory range %a..%a is unreadable, skip it\n", range_start_ea, range_end_ea);
|
||||
#endif
|
||||
return DRC_FAILED;
|
||||
}
|
||||
|
||||
return find(range_start_ea, range_end_ea);
|
||||
}
|
||||
|
||||
virtual drc_t find(ea_t start_ea, ea_t end_ea) = 0;
|
||||
|
||||
bool match_pattern(
|
||||
const uchar *page_ptr,
|
||||
const compiled_binpat_t &ptn,
|
||||
size_t ptn_off,
|
||||
size_t nbytes) const
|
||||
{
|
||||
const uchar *image = ptn.bytes.begin() + ptn_off;
|
||||
const uchar *mask = ptn.all_bytes_defined() ? NULL : ptn.mask.begin() + ptn_off;
|
||||
for ( int i=0; i < nbytes; ++i, ++page_ptr, ++image )
|
||||
{
|
||||
uchar c = *page_ptr;
|
||||
if ( mask != NULL )
|
||||
{
|
||||
if ( mask == SKIP_FF_MASK )
|
||||
{
|
||||
if ( *image == 0xFF )
|
||||
continue;
|
||||
}
|
||||
else
|
||||
{
|
||||
if ( mask[i] == 0 )
|
||||
continue;
|
||||
}
|
||||
}
|
||||
uchar c2 = *image;
|
||||
if ( sense_case() ? (c != c2) : (qtoupper(c) != qtoupper(c2)) )
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
ea_t get_failed_address(void) const { return failed_ea; }
|
||||
|
||||
private:
|
||||
bool range_is_unreadable(ea_t range_start_ea)
|
||||
{
|
||||
uchar dummy;
|
||||
return mod->dbg_read_memory(range_start_ea, &dummy, sizeof(dummy), NULL) != sizeof(dummy);
|
||||
}
|
||||
};
|
||||
|
||||
typedef janitor_t<matcher_t *> matcher_janitor_t;
|
||||
template <> inline matcher_janitor_t::~janitor_t()
|
||||
{
|
||||
delete resource;
|
||||
resource = NULL;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
class forward_matcher_t : public matcher_t
|
||||
{
|
||||
size_t last_off;
|
||||
|
||||
public:
|
||||
forward_matcher_t(
|
||||
ea_t *_found_ea,
|
||||
debmod_t *_mod,
|
||||
const compiled_binpat_vec_t &_ptns,
|
||||
int _srch_flags,
|
||||
qstring *_errbuf)
|
||||
: matcher_t(_found_ea, _mod, _ptns, _srch_flags, _errbuf),
|
||||
last_off(PAGESZ)
|
||||
{}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
virtual drc_t walk_memory_ranges(const ea_t srch_start_ea, const ea_t srch_end_ea) override
|
||||
{
|
||||
meminfo_vec_t::const_iterator p=mod->old_ranges.begin();
|
||||
ea_t range_start_ea = BADADDR;
|
||||
for ( ; p < mod->old_ranges.end(); ++p )
|
||||
{
|
||||
if ( p->contains(srch_start_ea) )
|
||||
{
|
||||
range_start_ea = srch_start_ea;
|
||||
break;
|
||||
}
|
||||
if ( p->start_ea > srch_start_ea )
|
||||
break;
|
||||
}
|
||||
if ( range_start_ea == BADADDR )
|
||||
{
|
||||
if ( p == mod->old_ranges.end() || p->start_ea >= srch_end_ea )
|
||||
return DRC_FAILED; // not found
|
||||
range_start_ea = p->start_ea;
|
||||
}
|
||||
ea_t range_end_ea = srch_end_ea < p->end_ea ? srch_end_ea : p->end_ea;
|
||||
drc_t drc = search_memory_range(range_start_ea, range_end_ea);
|
||||
if ( drc != DRC_FAILED ) // not found
|
||||
return drc;
|
||||
|
||||
for ( ++p; p < mod->old_ranges.end() && srch_end_ea >= p->end_ea; ++p )
|
||||
{
|
||||
range_start_ea = p->start_ea;
|
||||
range_end_ea = srch_end_ea < p->end_ea ? srch_end_ea : p->end_ea;
|
||||
drc = search_memory_range(range_start_ea, range_end_ea);
|
||||
if ( drc != DRC_FAILED ) // not found
|
||||
return drc;
|
||||
}
|
||||
|
||||
return DRC_FAILED; // not found
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// find patterns in [start_ea, end_ea)
|
||||
virtual drc_t find(ea_t start_ea, ea_t end_ea) override
|
||||
{
|
||||
page_ea = align_down(start_ea, PAGESZ);
|
||||
ea_t page_off = start_ea - page_ea;
|
||||
size_t page_counter = 0;
|
||||
while ( page_ea < end_ea )
|
||||
{
|
||||
if ( check_break() && test_cancelled() )
|
||||
return DRC_ERROR;
|
||||
if ( read_page(page_ea, page, sizeof(page), errbuf) != sizeof(page) )
|
||||
{
|
||||
failed_ea = page_ea;
|
||||
return DRC_ERROR;
|
||||
}
|
||||
last_off = end_ea - page_ea;
|
||||
if ( last_off > PAGESZ )
|
||||
last_off = PAGESZ;
|
||||
// handle partial matches first
|
||||
for ( partmatches_t::iterator p=pmatches.begin(); p != pmatches.end(); )
|
||||
{
|
||||
switch ( finalize_partial_match(*p) )
|
||||
{
|
||||
case DRC_OK: // found a match
|
||||
return DRC_OK;
|
||||
default:
|
||||
case DRC_FAILED: // definitely failed
|
||||
p = pmatches.erase(p);
|
||||
break;
|
||||
case DRC_NONE: // need to continue matching
|
||||
++p;
|
||||
break;
|
||||
}
|
||||
}
|
||||
// try to find new matches
|
||||
if ( match_simple_patterns(page_off) )
|
||||
return DRC_OK;
|
||||
if ( !complex_ptns.empty() )
|
||||
{
|
||||
while ( page_off < last_off )
|
||||
{
|
||||
if ( match_at(page_off) )
|
||||
return DRC_OK;
|
||||
page_off++;
|
||||
}
|
||||
}
|
||||
page_ea += PAGESZ; // advance to the next page
|
||||
page_off = 0;
|
||||
send_heartbeat(&page_counter);
|
||||
}
|
||||
return DRC_FAILED;
|
||||
}
|
||||
|
||||
private:
|
||||
//--------------------------------------------------------------------------
|
||||
// try to match complex patterns at PAGE_OFF
|
||||
// too long patterns that do not fit the page will be matched partially
|
||||
// if the partial match is ok, we will remember them
|
||||
bool match_at(ea_t page_off)
|
||||
{
|
||||
const uchar *page_ptr = page + page_off;
|
||||
size_t rest = last_off - page_off;
|
||||
for ( intvec_t::const_iterator p=complex_ptns.begin();
|
||||
p != complex_ptns.end();
|
||||
++p )
|
||||
{
|
||||
const int &i = *p;
|
||||
const compiled_binpat_t &ptn = ptns[i];
|
||||
size_t vecsize = ptn.bytes.size();
|
||||
size_t nbytes = qmin(rest, vecsize);
|
||||
if ( !match_pattern(page_ptr, ptn, 0, nbytes) )
|
||||
continue;
|
||||
if ( vecsize <= rest )
|
||||
{
|
||||
*found_ea = page_ea + page_off;
|
||||
return true; // fits the page, a simple comparison is enough
|
||||
}
|
||||
// remember partial match
|
||||
partmatch_t pm;
|
||||
pm.match_ea = page_ea + page_off;
|
||||
pm.ptn_idx = i;
|
||||
pm.ptn_off = nbytes;
|
||||
pmatches.push_back(pm);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// try to match simple patterns inside the page
|
||||
// the partial match is processed as described above
|
||||
bool match_simple_patterns(ea_t page_off)
|
||||
{
|
||||
for ( intvec_t::const_iterator p=simple_ptns.begin();
|
||||
p != simple_ptns.end();
|
||||
++p )
|
||||
{
|
||||
const int &i = *p;
|
||||
const uchar *page_ptr = page + page_off;
|
||||
size_t rest = last_off - page_off;
|
||||
const bytevec_t &ptn_bytes = ptns[i].bytes;
|
||||
size_t ptn_sz = ptn_bytes.size();
|
||||
uchar ptn_ch = ptn_bytes[0];
|
||||
|
||||
const uchar *pold = page_ptr;
|
||||
while ( rest > 0 )
|
||||
{
|
||||
const uchar *pnew = (uchar *)memchr(pold, ptn_ch, rest);
|
||||
if ( pnew == NULL )
|
||||
break;
|
||||
rest -= (pnew - pold);
|
||||
size_t nbytes = qmin(rest, ptn_sz);
|
||||
if ( memcmp(pnew, ptn_bytes.begin(), nbytes) == 0 )
|
||||
{
|
||||
ea_t matched_ea = page_ea + (pnew - page);
|
||||
if ( ptn_sz <= rest )
|
||||
{
|
||||
*found_ea = matched_ea;
|
||||
return true;
|
||||
}
|
||||
// remember partial match
|
||||
partmatch_t pm;
|
||||
pm.match_ea = matched_ea;
|
||||
pm.ptn_idx = i;
|
||||
pm.ptn_off = nbytes;
|
||||
pmatches.push_back(pm);
|
||||
}
|
||||
pold = pnew + 1;
|
||||
rest -= 1;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// try to finalize a partial match by matching the next part of the
|
||||
// long pattern against the start of the PAGE. patterns that are still
|
||||
// too long for matching may produce new partial matches.
|
||||
drc_t finalize_partial_match(partmatch_t &pm)
|
||||
{
|
||||
const compiled_binpat_t &ptn = ptns[pm.ptn_idx];
|
||||
size_t vecsize = ptn.bytes.size();
|
||||
size_t ptn_rest = vecsize - pm.ptn_off;
|
||||
size_t nbytes = qmin(ptn_rest, last_off);
|
||||
if ( !match_pattern(page, ptn, pm.ptn_off, nbytes) )
|
||||
return DRC_FAILED;
|
||||
if ( ptn_rest <= last_off )
|
||||
{
|
||||
*found_ea = pm.match_ea;
|
||||
return DRC_OK; // finalized the match
|
||||
}
|
||||
if ( last_off != PAGESZ )
|
||||
return DRC_FAILED;
|
||||
// remember a new partial match
|
||||
pm.ptn_off += PAGESZ;
|
||||
return DRC_NONE;
|
||||
}
|
||||
};
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
class backward_matcher_t : public matcher_t
|
||||
{
|
||||
ea_t page_off;
|
||||
|
||||
public:
|
||||
backward_matcher_t(
|
||||
ea_t *_found_ea,
|
||||
debmod_t *_mod,
|
||||
const compiled_binpat_vec_t &_ptns,
|
||||
int _srch_flags,
|
||||
qstring *_errbuf)
|
||||
: matcher_t(_found_ea, _mod, _ptns, _srch_flags, _errbuf),
|
||||
page_off(0)
|
||||
{}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
virtual drc_t walk_memory_ranges(const ea_t srch_start_ea, const ea_t srch_end_ea) override
|
||||
{
|
||||
meminfo_vec_t::const_iterator p=mod->old_ranges.end() - 1;
|
||||
ea_t range_end_ea = BADADDR;
|
||||
for ( ; p >= mod->old_ranges.begin(); --p )
|
||||
{
|
||||
if ( p->start_ea < srch_end_ea )
|
||||
{
|
||||
range_end_ea = srch_end_ea < p->end_ea ? srch_end_ea : p->end_ea;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if ( range_end_ea == BADADDR )
|
||||
return DRC_FAILED; // not found
|
||||
ea_t range_start_ea = p->contains(srch_start_ea) ? srch_start_ea : p->start_ea;
|
||||
drc_t drc = search_memory_range(range_start_ea, range_end_ea);
|
||||
if ( drc != DRC_FAILED ) // not found
|
||||
return drc;
|
||||
|
||||
for ( --p; p >= mod->old_ranges.begin() && srch_start_ea < p->end_ea; --p )
|
||||
{
|
||||
range_end_ea = p->end_ea;
|
||||
range_start_ea = p->contains(srch_start_ea) ? srch_start_ea : p->start_ea;
|
||||
drc = search_memory_range(range_start_ea, range_end_ea);
|
||||
if ( drc != DRC_FAILED ) // not found
|
||||
return drc;
|
||||
}
|
||||
|
||||
return DRC_FAILED; // not found
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// find patterns in [start_ea, end_ea)
|
||||
virtual drc_t find(ea_t start_ea, ea_t end_ea) override
|
||||
{
|
||||
page_ea = align_down(end_ea - 1, PAGESZ);
|
||||
ea_t last_off = end_ea - page_ea;
|
||||
size_t page_counter = 0;
|
||||
while ( start_ea < page_ea + PAGESZ )
|
||||
{
|
||||
if ( check_break() && test_cancelled() )
|
||||
return DRC_ERROR;
|
||||
if ( read_page(page_ea, page, sizeof(page), errbuf) != sizeof(page) )
|
||||
{
|
||||
failed_ea = page_ea;
|
||||
return DRC_ERROR;
|
||||
}
|
||||
page_off = page_ea < start_ea ? start_ea - page_ea : 0;
|
||||
// handle partial matches first
|
||||
for ( partmatches_t::iterator p=pmatches.begin(); p != pmatches.end(); )
|
||||
{
|
||||
switch ( finalize_partial_match(*p) )
|
||||
{
|
||||
case DRC_OK: // found a match
|
||||
return DRC_OK;
|
||||
default:
|
||||
case DRC_FAILED: // definitely failed
|
||||
p = pmatches.erase(p);
|
||||
break;
|
||||
case DRC_NONE: // need to continue matching
|
||||
++p;
|
||||
break;
|
||||
}
|
||||
}
|
||||
// try to find new matches
|
||||
if ( match_simple_patterns(last_off) )
|
||||
return DRC_OK;
|
||||
if ( !complex_ptns.empty() )
|
||||
{
|
||||
while ( page_off < last_off )
|
||||
{
|
||||
if ( match_before(last_off) )
|
||||
return DRC_OK;
|
||||
last_off--;
|
||||
}
|
||||
}
|
||||
page_ea -= PAGESZ; // advance to the next page
|
||||
last_off = PAGESZ;
|
||||
send_heartbeat(&page_counter);
|
||||
}
|
||||
return DRC_FAILED;
|
||||
}
|
||||
|
||||
private:
|
||||
//--------------------------------------------------------------------------
|
||||
// try to match all patterns before LAST_OFF
|
||||
// too long patterns that do not fit the page will be matched partially
|
||||
// if the partial match is ok, we will remember them
|
||||
bool match_before(ea_t last_off)
|
||||
{
|
||||
size_t rest = last_off - page_off;
|
||||
for ( intvec_t::const_iterator p=complex_ptns.begin();
|
||||
p != complex_ptns.end();
|
||||
++p )
|
||||
{
|
||||
const int &i = *p;
|
||||
const compiled_binpat_t &ptn = ptns[i];
|
||||
size_t vecsize = ptn.bytes.size();
|
||||
size_t nbytes = qmin(rest, vecsize);
|
||||
if ( !match_pattern(page+last_off-nbytes, ptn, vecsize-nbytes, nbytes) )
|
||||
continue;
|
||||
if ( vecsize <= rest )
|
||||
{
|
||||
*found_ea = page_ea + last_off - nbytes;
|
||||
return true; // fits the page, a simple comparison is enough
|
||||
}
|
||||
// remember partial match
|
||||
partmatch_t pm;
|
||||
pm.match_ea = page_ea + last_off - vecsize;
|
||||
pm.ptn_idx = i;
|
||||
pm.ptn_off = nbytes;
|
||||
pmatches.push_back(pm);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// try to match simple patterns inside the page
|
||||
// the partial match is processed as described above
|
||||
bool match_simple_patterns(ea_t last_off)
|
||||
{
|
||||
const uchar *page_ptr = page + page_off;
|
||||
|
||||
for ( intvec_t::const_iterator q=simple_ptns.begin();
|
||||
q != simple_ptns.end();
|
||||
++q )
|
||||
{
|
||||
const int &i = *q;
|
||||
size_t rest = last_off - page_off;
|
||||
const bytevec_t &ptn_bytes = ptns[i].bytes;
|
||||
size_t ptn_sz = ptn_bytes.size();
|
||||
uchar ptn_ch = ptn_bytes[ptn_sz-1];
|
||||
|
||||
while ( rest > 0 )
|
||||
{
|
||||
const uchar *p = (uchar *)local_memrchr(page_ptr, ptn_ch, rest);
|
||||
if ( p == NULL )
|
||||
break;
|
||||
rest = p + 1 - page_ptr;
|
||||
size_t nbytes = qmin(rest, ptn_sz);
|
||||
if ( memcmp(p + 1 - nbytes, &ptn_bytes[ptn_sz - nbytes], nbytes) == 0 )
|
||||
{
|
||||
ea_t matched_ea = page_ea + (p + 1 - page) - ptn_sz;
|
||||
if ( ptn_sz <= rest )
|
||||
{
|
||||
*found_ea = matched_ea;
|
||||
return true;
|
||||
}
|
||||
// remember partial match
|
||||
partmatch_t pm;
|
||||
pm.match_ea = matched_ea;
|
||||
pm.ptn_idx = i;
|
||||
pm.ptn_off = nbytes;
|
||||
pmatches.push_back(pm);
|
||||
}
|
||||
rest -= 1;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// try to finalize a partial match by matching the previous part of the
|
||||
// long pattern against the end of the PAGE. patterns that are still
|
||||
// too long for matching may produce new partial matches.
|
||||
drc_t finalize_partial_match(partmatch_t &pm)
|
||||
{
|
||||
const compiled_binpat_t &ptn = ptns[pm.ptn_idx];
|
||||
size_t vecsize = ptn.bytes.size();
|
||||
size_t ptn_rest = vecsize - pm.ptn_off;
|
||||
size_t nbytes = qmin(ptn_rest, PAGESZ - page_off);
|
||||
if ( !match_pattern(page + PAGESZ - nbytes, ptn, ptn_rest - nbytes, nbytes) )
|
||||
return DRC_FAILED;
|
||||
if ( ptn_rest <= PAGESZ - page_off )
|
||||
{
|
||||
*found_ea = pm.match_ea;
|
||||
return DRC_OK; // finalized the match
|
||||
}
|
||||
if ( page_off != 0 )
|
||||
return DRC_FAILED;
|
||||
// remember a new partial match
|
||||
pm.ptn_off += PAGESZ;
|
||||
return DRC_NONE;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
#ifndef TEST
|
||||
//--------------------------------------------------------------------------
|
||||
// Note:
|
||||
// The input search range can include the unreadable memory regions.
|
||||
// For example, "[vvar]" on Linux.
|
||||
// read_memory() returns 0 when trying to read from such region.
|
||||
// These regions must be skipped.
|
||||
drc_t idaapi debmod_t::dbg_bin_search(
|
||||
ea_t *found_ea,
|
||||
ea_t start_ea,
|
||||
ea_t end_ea,
|
||||
const compiled_binpat_vec_t &ptns,
|
||||
int srch_flags,
|
||||
qstring *errbuf)
|
||||
{
|
||||
static int forbidden = -1;
|
||||
if ( forbidden == -1 )
|
||||
forbidden = qgetenv("IDA_IDB_BIN_SEARCH", NULL);
|
||||
if ( forbidden )
|
||||
return DRC_NONE;
|
||||
|
||||
debdeb("dbg_bin_search %a..%a\n", start_ea, end_ea);
|
||||
|
||||
if ( start_ea >= end_ea || ptns.empty() || old_ranges.empty() )
|
||||
return DRC_NONE;
|
||||
|
||||
//lint -esym(429,matcher) has not been freed
|
||||
matcher_t *matcher = NULL;
|
||||
matcher_janitor_t matcher_janitor(matcher);
|
||||
|
||||
bool search_backward = (srch_flags & BIN_SEARCH_BACKWARD) != 0;
|
||||
if ( search_backward )
|
||||
matcher = new backward_matcher_t(found_ea, this, ptns, srch_flags, errbuf);
|
||||
else
|
||||
matcher = new forward_matcher_t(found_ea, this, ptns, srch_flags, errbuf);
|
||||
|
||||
drc_t drc = matcher->walk_memory_ranges(start_ea, end_ea);
|
||||
if ( drc != DRC_ERROR )
|
||||
return drc; //-V773 without releasing the 'matcher' pointer
|
||||
|
||||
ea_t failed_ea = matcher->get_failed_address();
|
||||
if ( failed_ea != BADADDR )
|
||||
{
|
||||
debdeb("dbg_bin_search failed to read memory at %a\n", failed_ea);
|
||||
if ( errbuf != NULL && errbuf->empty() )
|
||||
errbuf->sprnt("Failed to read memory at %a\n", failed_ea);
|
||||
}
|
||||
|
||||
return DRC_ERROR;
|
||||
}
|
||||
|
||||
#else // TEST
|
||||
//--------------------------------------------------------------------------
|
||||
drc_t binary_search(
|
||||
ea_t *found_ea,
|
||||
ea_t start_ea,
|
||||
ea_t end_ea,
|
||||
const compiled_binpat_vec_t &ptns,
|
||||
int srch_flags,
|
||||
qstring *errbuf)
|
||||
{
|
||||
matcher_t *matcher;
|
||||
if ( (srch_flags & BIN_SEARCH_BACKWARD) != 0 )
|
||||
matcher = new backward_matcher_t(found_ea, NULL, ptns, srch_flags, errbuf);
|
||||
else
|
||||
matcher = new forward_matcher_t(found_ea, NULL, ptns, srch_flags, errbuf);
|
||||
drc_t drc = matcher->find(start_ea, end_ea);
|
||||
delete matcher;
|
||||
return drc;
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
inline bool cmpbytes(
|
||||
const uchar *ptr,
|
||||
uchar b,
|
||||
const uchar *pptr,
|
||||
size_t ptnsize,
|
||||
bool sense_case)
|
||||
{
|
||||
if ( sense_case )
|
||||
return *ptr == b && memcmp(ptr+1, pptr, ptnsize) == 0; //lint !e670
|
||||
if ( qtoupper(b) != qtoupper(*ptr) )
|
||||
return false;
|
||||
++ptr;
|
||||
for ( int i=0; i < ptnsize; ++i, ++ptr, ++pptr )
|
||||
{
|
||||
if ( qtoupper(*ptr) != qtoupper(*pptr) )
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
void *memmem(
|
||||
const void *buf,
|
||||
size_t bufsize,
|
||||
const void *ptn,
|
||||
size_t ptnsize,
|
||||
bool sense_case)
|
||||
{
|
||||
if ( int(ptnsize) <= 0 || int(bufsize) < 0 || ptnsize > bufsize )
|
||||
return NULL;
|
||||
const uchar *ptr = (const uchar *)buf;
|
||||
const uchar *const end = ptr + bufsize - ptnsize + 1;
|
||||
const uchar *pptr = (const uchar *)ptn;
|
||||
uchar b = *pptr++;
|
||||
ptnsize--;
|
||||
while ( ptr < end )
|
||||
{
|
||||
if ( cmpbytes(ptr, b, pptr, ptnsize, sense_case) )
|
||||
return (void *)ptr;
|
||||
++ptr;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
void *memmemr(
|
||||
const void *buf,
|
||||
size_t bufsize,
|
||||
const void *ptn,
|
||||
size_t ptnsize,
|
||||
bool sense_case)
|
||||
{
|
||||
if ( int(ptnsize) <= 0 || int(bufsize) < 0 || ptnsize > bufsize )
|
||||
return NULL;
|
||||
const uchar *ptr = (const uchar *)buf + bufsize - ptnsize;
|
||||
const uchar *const start = (const uchar *)buf;
|
||||
const uchar *pptr = (const uchar *)ptn;
|
||||
uchar b = *pptr++;
|
||||
ptnsize--;
|
||||
while ( start <= ptr )
|
||||
{
|
||||
if ( cmpbytes(ptr, b, pptr, ptnsize, sense_case) )
|
||||
return (void *)ptr;
|
||||
--ptr;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
drc_t simple_binary_search(
|
||||
eavec_t *found_eas,
|
||||
ea_t start_ea,
|
||||
ea_t end_ea,
|
||||
const compiled_binpat_vec_t &ptns,
|
||||
int srch_flags,
|
||||
qstring * /*errbuf*/)
|
||||
{
|
||||
if ( start_ea >= end_ea || start_ea > sizeof(memory) )
|
||||
return DRC_FAILED;
|
||||
bool sense_case = (srch_flags & BIN_SEARCH_CASE) != 0;
|
||||
found_eas->clear();
|
||||
bool backward = (srch_flags & BIN_SEARCH_BACKWARD) != 0;
|
||||
for ( compiled_binpat_vec_t::const_iterator p=ptns.begin();
|
||||
p != ptns.end();
|
||||
++p )
|
||||
{
|
||||
const bytevec_t &vec = p->bytes;
|
||||
uchar *start = memory + start_ea;
|
||||
asize_t nbytes = qmin(sizeof(memory)-start_ea, end_ea-start_ea);
|
||||
uchar *f = (uchar *)(backward
|
||||
? memmemr(start, nbytes, vec.begin(), vec.size(), sense_case)
|
||||
: memmem(start, nbytes, vec.begin(), vec.size(), sense_case));
|
||||
if ( f == NULL )
|
||||
continue;
|
||||
ea_t idx = f - memory;
|
||||
if ( idx >= end_ea )
|
||||
continue;
|
||||
found_eas->push_back(idx);
|
||||
}
|
||||
return found_eas->empty() ? DRC_FAILED : DRC_OK;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
static bool check(qstring *found1_s, const eavec_t &found1, ea_t found2)
|
||||
{
|
||||
bool ok = found1.empty() && found2 == BADADDR;
|
||||
for ( int k=0; k < found1.size(); ++k )
|
||||
{
|
||||
if ( found1[k] == found2 )
|
||||
ok = true;
|
||||
if ( k > 0 )
|
||||
found1_s->append(',');
|
||||
found1_s->cat_sprnt("%a", found1[k]);
|
||||
}
|
||||
if ( found1_s->empty() )
|
||||
found1_s->sprnt("%a", BADADDR);
|
||||
return ok;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
bool sense_case = false;
|
||||
int max_ptns = 3;
|
||||
for ( int i=1; i < argc; ++i )
|
||||
{
|
||||
char argch = argv[i][0];
|
||||
if ( argch == 'C' )
|
||||
{
|
||||
sense_case = true;
|
||||
}
|
||||
else if ( '0' < argch && argch <= '9' )
|
||||
{
|
||||
max_ptns = argch - '0';
|
||||
}
|
||||
}
|
||||
msg("Test bin_search with %d pattern[s] and %s\n",
|
||||
max_ptns,
|
||||
sense_case ? "case sensitive" : "case ignored");
|
||||
|
||||
for ( int i=0; i < sizeof(memory); i++ )
|
||||
memory[i] = i;//rand();
|
||||
|
||||
for ( int i=0; i < 1000000; i++ )
|
||||
{
|
||||
// prepare a pattern for searching
|
||||
compiled_binpat_vec_t ptns;
|
||||
int ptns_cnt = max_ptns == 1 ? 1 : (rand() % max_ptns) + 1;
|
||||
ptns.resize(ptns_cnt);
|
||||
qstring out;
|
||||
for ( int c=0; c < ptns.size(); c++ )
|
||||
{
|
||||
size_t off = rand() % sizeof(memory);
|
||||
size_t len = (rand() % sizeof(memory)/20) + 1;
|
||||
if ( (rand() % 50) == 0 )
|
||||
len += 8;
|
||||
compiled_binpat_t &pat = ptns[c];
|
||||
pat.bytes.resize(len, 0xFF);
|
||||
size_t copyable = qmin(sizeof(memory)-off, len);
|
||||
memcpy(pat.bytes.begin(), &memory[off], copyable);
|
||||
if ( c > 0 )
|
||||
out.append(",");
|
||||
out.cat_sprnt("%X:%X", int(off), int(len));
|
||||
// if some rare cases make the pattern possibly insearchable
|
||||
if ( (rand() % 50) == 0 )
|
||||
{
|
||||
pat.bytes[0] = 0xAA;
|
||||
out.append("-");
|
||||
}
|
||||
}
|
||||
ea_t start_ea = rand() % sizeof(memory);
|
||||
ea_t end_ea = start_ea + (rand() % sizeof(memory));
|
||||
if ( end_ea > sizeof(memory) )
|
||||
end_ea = sizeof(memory); // no need to test out of memory
|
||||
int flags = sense_case ? BIN_SEARCH_CASE : BIN_SEARCH_NOCASE;
|
||||
|
||||
eavec_t found1;
|
||||
simple_binary_search(&found1, start_ea, end_ea, ptns, flags|BIN_SEARCH_FORWARD, NULL);
|
||||
|
||||
ea_t found2 = BADADDR;
|
||||
binary_search(&found2, start_ea, end_ea, ptns, flags|BIN_SEARCH_FORWARD, NULL);
|
||||
|
||||
qstring found1_s;
|
||||
bool ok = check(&found1_s, found1, found2);
|
||||
msg("%3d find (%s) in (%a..%a) => %s %a\n", i, out.c_str(), start_ea, end_ea, found1_s.c_str(), found2);
|
||||
if ( !ok )
|
||||
{
|
||||
msg("FAILED!\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
found1.clear();
|
||||
simple_binary_search(&found1, start_ea, end_ea, ptns, flags|BIN_SEARCH_BACKWARD, NULL);
|
||||
|
||||
found2 = BADADDR;
|
||||
binary_search(&found2, start_ea, end_ea, ptns, flags|BIN_SEARCH_BACKWARD, NULL);
|
||||
|
||||
found1_s.qclear();
|
||||
ok = check(&found1_s, found1, found2);
|
||||
msg("%3d findr(%s) in (%a..%a) => %s %a\n", i, out.c_str(), start_ea, end_ea, found1_s.c_str(), found2);
|
||||
if ( !ok )
|
||||
{
|
||||
msg("FAILED!\n");
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
msg("OK\n");
|
||||
return 0;
|
||||
}
|
||||
#endif // TEST
|
||||
34
idasdk76/dbg/bochs/bochsext.h
Normal file
34
idasdk76/dbg/bochs/bochsext.h
Normal file
@@ -0,0 +1,34 @@
|
||||
/*
|
||||
* Interactive disassembler (IDA).
|
||||
* ALL RIGHTS RESERVED.
|
||||
* Copyright (c) 1990-2021 Hex-Rays
|
||||
*
|
||||
*
|
||||
* This file defines the Bochs Debugger module extension functions.
|
||||
* Use debugger_t->get_debmod_extensions() to retrieve this structure.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef __BOCHSEXT__
|
||||
#define __BOCHSEXT__
|
||||
|
||||
#pragma pack(push, 1)
|
||||
|
||||
#define BOCHSEXT_VER 1
|
||||
|
||||
struct bochsext_t
|
||||
{
|
||||
// the structure version
|
||||
uint32 version;
|
||||
|
||||
// Sends an arbitrary command to Bochs internal debugger
|
||||
// cmd - command to send
|
||||
// out - pointer to qstring that will hold the output of the command
|
||||
// Returns: true if ok; false if failed to send command to bochs or receive
|
||||
// a reply
|
||||
bool (idaapi *send_command)(const char *cmd, qstring *out);
|
||||
};
|
||||
|
||||
#pragma pack(pop)
|
||||
|
||||
#endif
|
||||
62
idasdk76/dbg/bochs/ctrl/bochsys.h
Normal file
62
idasdk76/dbg/bochs/ctrl/bochsys.h
Normal file
@@ -0,0 +1,62 @@
|
||||
/*
|
||||
* Interactive disassembler (IDA).
|
||||
* ALL RIGHTS RESERVED.
|
||||
* Copyright (c) 1990-2021 Hex-Rays
|
||||
*
|
||||
*
|
||||
* This file defines the functions prototypes that are exported by bochsys.dll
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef __BOCHSYS_DLL__
|
||||
#define __BOCHSYS_DLL__
|
||||
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#include <windows.h>
|
||||
|
||||
#define BX_CALLCONV WINAPI
|
||||
|
||||
typedef wchar_t wchar16_t;
|
||||
//CASSERT(sizeof(wchar16_t) == 2);
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// These functions are similar to MS Windows functions. Please refer
|
||||
// to the SDK documentation for more information on how to use them.
|
||||
extern FARPROC WINAPI BxGetProcAddress(HMODULE hMod, LPCSTR ProcName);
|
||||
extern HMODULE WINAPI BxGetModuleHandleA(LPCSTR ModuleFileName);
|
||||
extern DWORD WINAPI BxGetModuleFileNameA(HINSTANCE hModule, LPCSTR lpFilename, DWORD nSize);
|
||||
extern DWORD WINAPI BxGetModuleFileNameW(HINSTANCE hModule, LPWSTR lpFilename, DWORD nSize);
|
||||
extern HMODULE WINAPI BxLoadLibraryA(LPCTSTR lpFileName);
|
||||
extern LPVOID WINAPI BxVirtualAlloc(LPVOID lpAddress, SIZE_T dwSize, DWORD flAllocationType, DWORD flProtect);
|
||||
extern BOOL WINAPI BxVirtualFree(LPVOID lpAddress, SIZE_T dwSize, DWORD dwFreeType);
|
||||
extern DWORD WINAPI BxExitProcess(DWORD);
|
||||
extern DWORD WINAPI BxGetTickCount(VOID);
|
||||
extern BOOL WINAPI BxVirtualProtect(LPVOID lpAddress, SIZE_T dwSize, DWORD flNewProtect, PDWORD lpflOldProtect);
|
||||
extern DWORD WINAPI BxWin32SetLastError(DWORD ErrorCode);
|
||||
extern DWORD WINAPI BxWin32GetLastError(VOID);
|
||||
extern LPCSTR WINAPI BxWin32GetCommandLineA(VOID);
|
||||
extern LPWSTR WINAPI BxWin32GetCommandLineW(VOID);
|
||||
extern LPCSTR WINAPI BxWin32GetEnvironmentStringsA(VOID);
|
||||
extern LPWSTR WINAPI BxWin32GetEnvironmentStringsW(VOID);
|
||||
extern LPVOID WINAPI BxWin32TlsGetValue(DWORD dwTlsIndex);
|
||||
extern BOOL WINAPI BxWin32TlsSetValue(DWORD dwTlsIndex,LPVOID lpTlsValue);
|
||||
extern BOOL WINAPI BxWin32TlsFree(DWORD dwTlsIndex);
|
||||
extern DWORD WINAPI BxWin32TlsAlloc(VOID);
|
||||
extern DWORD WINAPI BxWin32FlsAlloc(VOID);
|
||||
extern char * WINAPI BxStrCpyA(char *Dst, char *Src);
|
||||
extern wchar16_t * WINAPI BxStrCpyW(wchar16_t *Dst, wchar16_t *Src);
|
||||
extern char * WINAPI BxStrCatA(char *Dst, char *Src);
|
||||
extern wchar16_t * WINAPI BxStrCatW(wchar16_t *Dst, wchar16_t *Src);
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// Installs an exception handler. Only one exception handler
|
||||
// can be installed at one time. You need to uninstall one
|
||||
// before reinstalling another.
|
||||
// These two functions will return non-zero on success.
|
||||
typedef DWORD (*PEXCEPTION_HANDLER)(PEXCEPTION_RECORD, struct _EXCEPTION_REGISTRATION_RECORD *, PCONTEXT,struct _EXCEPTION_REGISTRATION_RECORD **);
|
||||
|
||||
extern DWORD WINAPI BxInstallSEH(PEXCEPTION_HANDLER Handler);
|
||||
extern DWORD WINAPI BxUninstallSEH();
|
||||
|
||||
#endif
|
||||
271
idasdk76/dbg/bochs/ctrl/startup.idc
Normal file
271
idasdk76/dbg/bochs/ctrl/startup.idc
Normal file
@@ -0,0 +1,271 @@
|
||||
// This file contains bochs startup and exit procedures bochs_startup() and bochs_exit()
|
||||
// The former is called when the process starts
|
||||
// The latter is called when the process is about to exit
|
||||
// Both functions cause IDA debugger to suspend if they return a non zero value
|
||||
|
||||
// This section declares which DLLs will be available:
|
||||
// * Use the "stub" to mark a dll for stubbing
|
||||
|
||||
// * Use the "load" to mark a dll to be loaded as is
|
||||
// The "load" keyword has an additional attributes called "R0UserEntry"
|
||||
// This attribute is used to designate an exported function that will be called from ring0
|
||||
// Such a mechanism is useful to extend bochsys kernel or even replace it after it is loaded
|
||||
// One simple application is to modify the IDT and add R3 callable interrupts into your kernel
|
||||
|
||||
// Only lines containing three forward slashes ("/") are processed:
|
||||
|
||||
/// stub ntdll.dll
|
||||
/// stub kernel32.dll
|
||||
/// stub user32.dll
|
||||
/// stub shell32.dll
|
||||
/// stub shlwapi.dll
|
||||
/// stub urlmon.dll
|
||||
/// stub advapi32.dll
|
||||
/// stub mswsock.dll
|
||||
/// stub wininet.dll
|
||||
/// stub msvcrt.dll
|
||||
/// stub gdi32.dll
|
||||
/// stub ole32.dll
|
||||
/// stub wsock32.dll
|
||||
/// stub ws2_32.dll
|
||||
|
||||
// Define our own environment variables.
|
||||
// (add triple slashes to enable)
|
||||
// env path=c:\games\bin;d:\bin\asdf\
|
||||
// env userprofile=c:\games\
|
||||
|
||||
// Define your dependency mappings
|
||||
// (add triple slashes to enable the following lines)
|
||||
// map /home/guest/sys_dlls/user32.dll=d:\winnt\system32\user32.dll
|
||||
// map /home/guest/sys_dlls/shell32.dll=d:\winnt\system32\shell32.dll
|
||||
// map /home/guest/sys_dlls/kernel32.dll=d:\winnt\system32\kernel32.dll
|
||||
// map /home/guest/sys_dlls/shlwapi.dll=d:\winnt\system32\shlwapi.dll
|
||||
// map /home/guest/sys_dlls/urlmon.dll=d:\winnt\system32\urlmon.dll
|
||||
// map /home/guest/sys_dlls/mswsock.dll=d:\winnt\system32\mswsock.dll
|
||||
// map /home/guest/sys_dlls/wininet.dll=d:\winnt\system32\wininet.dll
|
||||
// map /home/guest/sys_dlls/msvcrt.dll=d:\winnt\system32\msvcrt.dll
|
||||
// map /home/guest/sys_dlls/gdi32.dll=d:\winnt\system32\gdi32.dll
|
||||
// map /home/guest/sys_dlls/ntdll.dll=d:\winnt\system32\ntdll.dll
|
||||
// map /home/guest/sys_dlls/advapi32.dll=d:\winnt\system32\advapi32.dll
|
||||
|
||||
// Define additional DLL path
|
||||
// (add triple slashes to enable the following lines)
|
||||
// path /home/guest/sys_dlls/=c:\winnt\system32\
|
||||
|
||||
// Bochs debugger plugin also allows you to specify the DLL path through the environment variable IDABXPATHMAP
|
||||
// (It is possible to specify more than one key/value pair by separating them with a semi-colon)
|
||||
// For example:
|
||||
// $ export IDABXPATHMAP="/home/guest/sys_dlls/=c:/winnt/system32/;/home/user2/other_dlls/=c:/program files/common files/3rd party/"
|
||||
// Similarly, one can specify the environment variables through the environment variable IDABXENVMAP
|
||||
// (it is possible to specify more than one key/value pair by separating them with a "++")
|
||||
// For example:
|
||||
// $ export IDABXENVMAP="TMP=c:/Users/Guest/Temp++SystemDrive=C:++windir=c:/windows/"
|
||||
//
|
||||
// Please note that the forward slashes "/" in the value part of the key/value pair will always be replaced with a backslash
|
||||
|
||||
//
|
||||
// The following are oneshot options. Once set they cannot be unset.
|
||||
// To define them simply preceed the option name with triple slashes.
|
||||
// - nosearchpath: Disables SearchPath() use for finding DLLs (this option is applicable on MS Windows only).
|
||||
// By turning this option, Bochs plugin will try to load DLLs from the current directory.
|
||||
// It useful for loading certain (old or new) versions of system DLLs instead of the ones currently installed
|
||||
// on the system.
|
||||
// - noactivationcontext: Disables the use of "Activation Context" (this option is applicable on MS Windows only).
|
||||
//
|
||||
|
||||
//
|
||||
// For loading drivers, you may uncomment the following stub definition(s):
|
||||
//
|
||||
// stub ntoskrnl.exe
|
||||
|
||||
// For example: to load a dll as is: load mydll.dll
|
||||
// For example: to load a dll as is and specify a user R0 entry: load mydll.dll R0UserEntry=MyExportedFunc
|
||||
|
||||
#include <idc.idc>
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// IDC scripts that will be available during the debugging session
|
||||
|
||||
// MS Windows related functions
|
||||
// ------------------------------
|
||||
// BochsVirtXXXX functions allocate/free virtual memory in the emulated session.
|
||||
// The "size" parameter is always rounded to a page.
|
||||
//
|
||||
|
||||
//
|
||||
// Allocate virtual memory
|
||||
// This function emulates the VirtualAlloc function from MS Windows
|
||||
// addr - the preferred address for the allocation. Zero means no preference
|
||||
// size - size of the block to allocate
|
||||
// writable - should be allocated memory be wrtiable?
|
||||
// Currently only read/write page protections are supported
|
||||
// Returns: the address of the allocated block or zero
|
||||
//
|
||||
// long BochsVirtAlloc(long addr, long size, bool writable);
|
||||
//
|
||||
|
||||
//
|
||||
// Change protection of memory page
|
||||
// This function emulates the VirtualProtect function from MS Windows
|
||||
// addr - the desired address to change protection.
|
||||
// size - size of the block
|
||||
// attr - the new page attribute:
|
||||
// 0 = Read only
|
||||
// 1 = Read/Write
|
||||
// Returns: the old protection value or -1 on failure
|
||||
//
|
||||
// long BochsVirtProtect(long addr, long size, long attr);
|
||||
//
|
||||
|
||||
|
||||
//
|
||||
// Free virtual memory
|
||||
// This function emulates the VirtualFree function from MS Windows
|
||||
// addr - the address of previously allocated memory block
|
||||
// size - the size of the block. If zero, then the entire block at addr
|
||||
// will be freed.
|
||||
// Returns: success
|
||||
//
|
||||
// bool BochsVirtFree(long addr, long size);
|
||||
//
|
||||
|
||||
//
|
||||
// Returns the base address of a given module name
|
||||
// module_name - The name of the module.
|
||||
// The name can be full path or filename with extension, or simply filename without extension
|
||||
// Returns: zero if it fails
|
||||
//
|
||||
// long BochsGetModuleHandle(string module_name);
|
||||
//
|
||||
|
||||
//
|
||||
// Returns a procedure's address
|
||||
// This function calls the internal GetProcAddress to resolve function addresses.
|
||||
// hmod - the module handle
|
||||
// procname - name of the procedure inside the module
|
||||
// Returns: the zero if procedure not found, otherwise the address
|
||||
//
|
||||
// long BochsGetProcAddress(long hmod, string procname);
|
||||
//
|
||||
|
||||
//
|
||||
// Returns the module name given its base address
|
||||
// module_handle: the base address of a given module
|
||||
// Returns: empty string if module was not found
|
||||
//
|
||||
// string BochsGetModuleFileName(long module_handle)
|
||||
//
|
||||
|
||||
//
|
||||
// Returns the command line value passed to the application
|
||||
//
|
||||
// string BochsGetCommandLine()
|
||||
//
|
||||
|
||||
//
|
||||
// Set last error code
|
||||
// This function emulates the SetLastError function from MS Windows.
|
||||
// It writes the specified code to TIB.
|
||||
// error_code - new error code to set
|
||||
// Returns: success
|
||||
//
|
||||
// success BochsWin32SetLastError(long error_num);
|
||||
//
|
||||
|
||||
//
|
||||
// Other functions:
|
||||
// -------------------
|
||||
//
|
||||
|
||||
//
|
||||
// Sends arbitrary commands to the internal debugger of BOCHS. The output is returned as a string.
|
||||
// This is useful for example to send some commands to BOCHS that are not exposed via the GUI of IDA.
|
||||
// command: the command you want to send
|
||||
// Returns: output string or empty string if it failed
|
||||
//
|
||||
// string send_dbg_command(string command)
|
||||
//
|
||||
|
||||
//
|
||||
// Retrieves the parameter value passed to an IDC script that is implementing a given API.
|
||||
// This same function can be implemented with this expression: #define BX_GETPARAM(n) get_wide_dword(esp+4*(n+1))
|
||||
// arg_num: the argument number (starting by one)
|
||||
// Returns: the value or zero in case it fails
|
||||
//
|
||||
// string BochsGetParam(long arg_num)
|
||||
//
|
||||
|
||||
//
|
||||
// Calls a function inside Bochs
|
||||
// This function can call functions inside Bochs. Very useful if you want to call
|
||||
// functions in the user's code. The arguments are pushed from right to left.
|
||||
// func_ptr - The address of the function to be called
|
||||
// argN - a set of dwords that contain the arguments.
|
||||
// Arguments can be numbers or pointers
|
||||
// Returns: success
|
||||
//
|
||||
// long BochsCall(long func_ptr, arg1, arg2, ...);
|
||||
//
|
||||
|
||||
|
||||
//
|
||||
// These functions will return the total physical memory amount and the remaining free
|
||||
// memory in bytes.
|
||||
// Returns: memory size in bytes
|
||||
//
|
||||
// long BochsGetFreeMem()
|
||||
// long BochsGetMaxMem()
|
||||
//
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
static BochsPatchDbgDword(ea, dv)
|
||||
{
|
||||
auto i;
|
||||
for (i=0;i<4;i++)
|
||||
{
|
||||
patch_dbg_byte(ea, dv & 0xFF);
|
||||
ea = ea + 1;
|
||||
dv = dv >> 8;
|
||||
}
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Utility function that can be used as a conditional breakpoint condition
|
||||
// in order to skip to the next instruction w/o suspending IDA
|
||||
static bochs_skipnext()
|
||||
{
|
||||
Eip = next_head(eip, BADADDR);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Utility function that can be used as a conditional breakpoint condition
|
||||
// in order to execute the contents of the comments at the bp location
|
||||
static bochs_execidc_comments()
|
||||
{
|
||||
exec_idc(Comment(eip));
|
||||
return 0;
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Utility function used to dump registers. The output can be used as a comment
|
||||
// with the bochs_execidc_comments() bp condition
|
||||
static bochs_dump_registers()
|
||||
{
|
||||
msg("eax=0x%x;ebx=0x%x;ecx=0x%x;edx=0x%x;esi=0x%x;edi=0x%x;ebp=0x%x;", eax, ebx, ecx, edx, esi, edi, ebp);
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
static bochs_startup()
|
||||
{
|
||||
msg("Bochs debugger has been initialized.\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
static bochs_exit()
|
||||
{
|
||||
msg("Bochs debugger has been terminated.\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
2
idasdk76/dbg/bochs/sdk/api_user32.idc
Normal file
2
idasdk76/dbg/bochs/sdk/api_user32.idc
Normal file
@@ -0,0 +1,2 @@
|
||||
|
||||
///func=MessageBoxA entry=bxtest.MyMessageBox
|
||||
89
idasdk76/dbg/bochs/sdk/bxtest.c
Normal file
89
idasdk76/dbg/bochs/sdk/bxtest.c
Normal file
@@ -0,0 +1,89 @@
|
||||
#include "bochsys.h"
|
||||
#include <windows.h>
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// dummy entry point so that linker does not use entrypoints from CRT
|
||||
DWORD WINAPI Entry(DWORD a, DWORD b, DWORD c)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// This function will be called by bochsys.dll from R0 before switching to R3
|
||||
// This is even called before TLS callbacks
|
||||
void WINAPI MyR0Entry(VOID)
|
||||
{
|
||||
__asm
|
||||
{
|
||||
nop
|
||||
mov dx, 0378h
|
||||
in eax, dx
|
||||
nop
|
||||
nop
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
DWORD MyHandler(
|
||||
PEXCEPTION_RECORD rec,
|
||||
struct _EXCEPTION_REGISTRATION_RECORD *reg,
|
||||
PCONTEXT ctx,
|
||||
struct _EXCEPTION_REGISTRATION_RECORD **reg2)
|
||||
{
|
||||
ctx->Eip += 2;
|
||||
return ExceptionContinueExecution;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
void BuggyFunction()
|
||||
{
|
||||
BxInstallSEH(MyHandler);
|
||||
__asm
|
||||
{
|
||||
xor eax, eax
|
||||
mov eax, [eax]
|
||||
}
|
||||
BxUninstallSEH();
|
||||
}
|
||||
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// In this function, BxXXXXXX functions are used from the bochsys library
|
||||
int __stdcall MyMessageBox(
|
||||
HWND hWnd,
|
||||
LPCTSTR lpText,
|
||||
LPCTSTR lpCaption,
|
||||
UINT uType)
|
||||
{
|
||||
char *p;
|
||||
int i;
|
||||
|
||||
// Allocate memory
|
||||
p = BxVirtualAlloc(0, 0x1000, MEM_COMMIT, PAGE_READWRITE);
|
||||
|
||||
// Fill the memory
|
||||
for (i=1;i<=0x1000;i++)
|
||||
*p++ = i & 0xFF;
|
||||
|
||||
// Resolve an entry and call it
|
||||
(VOID (__stdcall *)(int, int)) BxGetProcAddress(BxLoadLibraryA("kernel32.dll"), "Beep")(5, 1);
|
||||
|
||||
// Call a function that might cause an exception
|
||||
BuggyFunction();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// In this function we import from user32 and kernel32
|
||||
// (because VirtualAlloc->BxVirtualAlloc and MessageBoxA->bxtest.MyMessageBox are redirected and implemented)
|
||||
int __stdcall MyRoutine(
|
||||
HWND hWnd,
|
||||
LPCTSTR lpText,
|
||||
LPCTSTR lpCaption,
|
||||
UINT uType)
|
||||
{
|
||||
VirtualAlloc(0, 0x1000, MEM_COMMIT, PAGE_READWRITE);
|
||||
MessageBoxA(0, "hey", "info", MB_OK);
|
||||
return 0;
|
||||
}
|
||||
4
idasdk76/dbg/bochs/sdk/bxtest.def
Normal file
4
idasdk76/dbg/bochs/sdk/bxtest.def
Normal file
@@ -0,0 +1,4 @@
|
||||
EXPORTS
|
||||
MyMessageBox
|
||||
MyRoutine
|
||||
MyR0Entry
|
||||
BIN
idasdk76/dbg/bochs/sdk/bxtest.dll
Normal file
BIN
idasdk76/dbg/bochs/sdk/bxtest.dll
Normal file
Binary file not shown.
11
idasdk76/dbg/bochs/sdk/compile.bat
Normal file
11
idasdk76/dbg/bochs/sdk/compile.bat
Normal file
@@ -0,0 +1,11 @@
|
||||
@echo off
|
||||
"C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\bin\cl.exe" -c /Zl /Gd /Tc bxtest.c "/IC:/Program Files (x86)/Windows Kits/8.1/Include/um" "/IC:/Program Files (x86)/Windows Kits/8.1/Include/shared" "/IC:/PROGRA~2/WI3CF2~1/10/Include/10.0.10150.0/ucrt" /I"C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\include"
|
||||
if errorlevel 1 goto end
|
||||
"C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\bin\link.exe" bxtest.obj bochsys.lib kernel32.lib user32.lib /OUT:bxtest.dll /ENTRY:Entry /def:bxtest.def /DRIVER /SAFESEH:NO /NODEFAULTLIB /SUBSYSTEM:WINDOWS /LIBPATH:"C:\Program Files\Microsoft Visual Studio 14.0\VC\Lib" /LIBPATH:"C:/Program Files (x86)/Windows Kits/8.1/Lib/winv6.3/um/x86"
|
||||
if errorlevel 1 goto end
|
||||
|
||||
if exist bxtest.obj del bxtest.obj
|
||||
if exist bxtest.exp del bxtest.exp
|
||||
if exist bxtest.lib del bxtest.lib
|
||||
|
||||
:end
|
||||
90
idasdk76/dbg/bochs/sdk/readme.txt
Normal file
90
idasdk76/dbg/bochs/sdk/readme.txt
Normal file
@@ -0,0 +1,90 @@
|
||||
|
||||
Custom DLLs for emulated MS Windows environment
|
||||
-----------------------------------------------
|
||||
|
||||
This directory contains files that demonstrate how to build a custom DLL
|
||||
for the PE loader of the Bochs debugger.
|
||||
|
||||
compile.bat shows how to build a custom DLL with the MS compiler/linker.
|
||||
The general rule is not to link with runtime libraries, but linking with
|
||||
import libraries is ok.
|
||||
|
||||
"bxtest.c" demonstrates how to call functions in bochsys.dll.
|
||||
"bochsys.h" has the list of functions that can be called from custom DLLs.
|
||||
"bochsys.lib" is the corresponding import library
|
||||
|
||||
Custom DLLs must be mentioned in plugins\bochs\startup.idc.
|
||||
For that please add a line like this:
|
||||
|
||||
/// load bxtest.dll
|
||||
|
||||
This will cause the DLL to be present in the memory space of the debugged process.
|
||||
For the custom DLL to be useful, its exported functions should be connected
|
||||
to API function names. For example, the following line redirects MessageBoxA
|
||||
to bxtest.MyMessageBox:
|
||||
|
||||
/// func=MessageBoxA entry=bxtest.MyMessageBox
|
||||
|
||||
The exact format of the startup.idc file is explained in its header.
|
||||
|
||||
On the other hand, it is also possible to write a custom DLL that replaces system
|
||||
DLLs like kernel32.dll or user32.dll.
|
||||
|
||||
|
||||
The "load" command has an additional parameter "R0UserEntry=MyR0Entry" used as:
|
||||
///load bxtest.dll R0UserEntry=MyR0Entry
|
||||
|
||||
Which means that bxtest.dll should be loaded into the process memory and
|
||||
that this DLL has an exported entry that should be called by bochsys from ring0.
|
||||
Such a facility is ideal if you're looking to replace or enhance bochsys's kernel.
|
||||
|
||||
To test how MessageBoxA is redirected to MyMessageBox, please follow these
|
||||
steps:
|
||||
|
||||
- compile and link bxtest.dll with compile.bat
|
||||
(we provide ready-to-use bxtest.dll for your convenience, so you
|
||||
skip this step)
|
||||
|
||||
- add two lines mentioned above to startup.idc and api_user32.idc respectively
|
||||
|
||||
- load test.pe into IDA and select Bochs debugger
|
||||
|
||||
- run it and single step into the MessageBoxA function
|
||||
|
||||
With any questions, please contact us at support@hex-rays.com
|
||||
|
||||
|
||||
Bochs plugin debugger extensions
|
||||
-----------------------------------
|
||||
|
||||
Bochs extensions allow for accessing extended debugger functionality.
|
||||
|
||||
To get and use the extensions, query the currently loaded debugger using
|
||||
get_debmod_extensions(). Usually it returns a pointer to a structure with
|
||||
pointers to functions. Please follow this example:
|
||||
|
||||
#include "bochsext.h"
|
||||
|
||||
void idaapi run(int)
|
||||
{
|
||||
if ( dbg == NULL )
|
||||
{
|
||||
msg("dbg == NULL\n");
|
||||
return;
|
||||
}
|
||||
const bochsext_t *ext = (const bochsext_t *)dbg->get_debmod_extensions();
|
||||
if ( ext == NULL )
|
||||
{
|
||||
msg("no debugger extensions!\n");
|
||||
return;
|
||||
}
|
||||
|
||||
// dump 10 bytes from physical memory at 0x0
|
||||
qstring out;
|
||||
if ( !ext->send_command("xp /10mb 0x0\r\n", &out) )
|
||||
{
|
||||
msg("failed to send command!\n");
|
||||
return;
|
||||
}
|
||||
msg("->result=%s\n", out.c_str());
|
||||
}
|
||||
36
idasdk76/dbg/bochs/sdk/test.asm
Normal file
36
idasdk76/dbg/bochs/sdk/test.asm
Normal file
@@ -0,0 +1,36 @@
|
||||
; #########################################################################
|
||||
|
||||
.386
|
||||
.model flat, stdcall
|
||||
option casemap :none ; case sensitive
|
||||
|
||||
; #########################################################################
|
||||
|
||||
include d:\masm32\include\windows.inc
|
||||
include d:\masm32\include\user32.inc
|
||||
include d:\masm32\include\kernel32.inc
|
||||
|
||||
includelib d:\masm32\lib\user32.lib
|
||||
includelib d:\masm32\lib\kernel32.lib
|
||||
|
||||
; #########################################################################
|
||||
; --------------------------------------------------------
|
||||
.data
|
||||
szDlgTitle db "Minimum MASM",0
|
||||
szMsg db " --- Assembler Pure and Simple --- ",0
|
||||
.code
|
||||
start:
|
||||
; --------------------------------------------------------
|
||||
; script
|
||||
push MB_OK
|
||||
push offset szDlgTitle
|
||||
push offset szMsg
|
||||
push 0
|
||||
call MessageBox
|
||||
|
||||
; --------------------------------------------------------
|
||||
; idacall
|
||||
push -2
|
||||
call ExitProcess
|
||||
|
||||
end start
|
||||
BIN
idasdk76/dbg/bochs/sdk/test.pe
Normal file
BIN
idasdk76/dbg/bochs/sdk/test.pe
Normal file
Binary file not shown.
857
idasdk76/dbg/common_local_impl.cpp
Normal file
857
idasdk76/dbg/common_local_impl.cpp
Normal file
@@ -0,0 +1,857 @@
|
||||
//
|
||||
// This file is included from other files, do not directly compile it.
|
||||
// It contains the debugger_t structure definition and a few other helper functions
|
||||
//
|
||||
|
||||
#include <loader.hpp>
|
||||
#include <segregs.hpp>
|
||||
#include <network.hpp>
|
||||
|
||||
bool plugin_inited;
|
||||
bool debugger_inited;
|
||||
|
||||
#define IS_GDB_DEBUGGER (DEBUGGER_ID == DEBUGGER_ID_GDB_USER || DEBUGGER_ID == DEBUGGER_ID_ARM_IPHONE_USER || DEBUGGER_ID == DEBUGGER_ID_XNU_USER)
|
||||
|
||||
#if TARGET_PROCESSOR == PLFM_386
|
||||
#define REGISTERS x86_registers
|
||||
#define REGISTERS_SIZE qnumber(x86_registers)
|
||||
#define REGISTER_CLASSES x86_register_classes
|
||||
#define REGISTER_CLASSES_DEFAULT X86_RC_GENERAL
|
||||
#define READ_REGISTERS x86_read_registers
|
||||
#define WRITE_REGISTER x86_write_register
|
||||
#if !IS_GDB_DEBUGGER
|
||||
#define is_valid_bpt is_x86_valid_bpt
|
||||
#endif
|
||||
#define BPT_CODE X86_BPT_CODE
|
||||
#define BPT_CODE_SIZE X86_BPT_SIZE
|
||||
#elif TARGET_PROCESSOR == PLFM_ARM
|
||||
#define REGISTERS arm_registers
|
||||
#define REGISTERS_SIZE qnumber(arm_registers)
|
||||
#define REGISTER_CLASSES arm_register_classes
|
||||
#define REGISTER_CLASSES_DEFAULT ARM_RC_GENERAL
|
||||
#define READ_REGISTERS s_read_registers
|
||||
#define WRITE_REGISTER s_write_register
|
||||
#if !IS_GDB_DEBUGGER
|
||||
#define is_valid_bpt is_arm_valid_bpt
|
||||
#else
|
||||
#define is_valid_bpt gdb_valid_bpt
|
||||
#endif
|
||||
#define BPT_CODE ARM_BPT_CODE
|
||||
#define BPT_CODE_SIZE ARM_BPT_SIZE
|
||||
#elif TARGET_PROCESSOR == PLFM_DALVIK
|
||||
#define BPT_CODE { 0 }
|
||||
#define BPT_CODE_SIZE 0
|
||||
#define READ_REGISTERS s_read_registers
|
||||
#define WRITE_REGISTER s_write_register
|
||||
#define is_valid_bpt is_dalvik_valid_bpt
|
||||
#elif IS_GDB_DEBUGGER
|
||||
#define REGISTERS NULL
|
||||
#define REGISTERS_SIZE 0
|
||||
#define REGISTER_CLASSES NULL
|
||||
#define REGISTER_CLASSES_DEFAULT 0
|
||||
#define READ_REGISTERS simple_read_registers
|
||||
#define WRITE_REGISTER simple_write_register
|
||||
#define is_valid_bpt gdb_valid_bpt
|
||||
#define BPT_CODE { 0 }
|
||||
#define BPT_CODE_SIZE 0
|
||||
#else
|
||||
#error This processor is not supported yet
|
||||
#endif
|
||||
|
||||
static const uchar bpt_code[] = BPT_CODE;
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
int idaapi is_ok_bpt(bpttype_t type, ea_t ea, int len)
|
||||
{
|
||||
int ret = is_valid_bpt(type, ea, len);
|
||||
if ( ret != BPT_OK )
|
||||
return ret;
|
||||
else
|
||||
return g_dbgmod.dbg_is_ok_bpt(type, ea, len);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// For ARM, we have to set the low bit of the address to 1 for thumb mode
|
||||
#if DEBUGGER_ID == DEBUGGER_ID_ARM_LINUX_USER
|
||||
static drc_t idaapi arm_update_bpts(
|
||||
int *nbpts,
|
||||
update_bpt_info_t *bpts,
|
||||
int nadd,
|
||||
int ndel,
|
||||
qstring *errbuf)
|
||||
{
|
||||
// This function is called from debthread, but to use get_sreg() we must
|
||||
// switch to the mainthread
|
||||
struct ida_local arm_bptea_fixer_t : public exec_request_t
|
||||
{
|
||||
update_bpt_info_t *bpts;
|
||||
update_bpt_info_t *e;
|
||||
qvector<ea_t *> thumb_mode;
|
||||
virtual int idaapi execute(void) override
|
||||
{
|
||||
for ( update_bpt_info_t *b=bpts; b != e; b++ )
|
||||
{
|
||||
if ( b->type == BPT_SOFT && get_sreg(b->ea, ARM_T) == 1 )
|
||||
{
|
||||
b->ea++; // odd address means that thumb bpt must be set
|
||||
thumb_mode.push_back(&b->ea);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
arm_bptea_fixer_t(update_bpt_info_t *p1, update_bpt_info_t *p2)
|
||||
: bpts(p1), e(p2) {}
|
||||
};
|
||||
arm_bptea_fixer_t abf(bpts, bpts+nadd);
|
||||
execute_sync(abf, MFF_READ);
|
||||
|
||||
drc_t drc = s_update_bpts(nbpts, bpts, nadd, ndel, errbuf);
|
||||
|
||||
// reset the odd bit because the addresses are required by the caller
|
||||
for ( int i=0; i < abf.thumb_mode.size(); i++ )
|
||||
(*abf.thumb_mode[i])--;
|
||||
|
||||
return drc;
|
||||
}
|
||||
#define s_update_bpts arm_update_bpts
|
||||
#endif
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
static drc_t idaapi update_bpts(
|
||||
int *nbpts,
|
||||
update_bpt_info_t *bpts,
|
||||
int nadd,
|
||||
int ndel,
|
||||
qstring *errbuf)
|
||||
{
|
||||
bool valid_bpt_exists = false;
|
||||
update_bpt_info_t *e = bpts + nadd;
|
||||
for ( update_bpt_info_t *b=bpts; b != e; b++ )
|
||||
{
|
||||
if ( b->code == BPT_SKIP )
|
||||
continue;
|
||||
|
||||
b->code = is_valid_bpt(b->type, b->ea, b->size);
|
||||
if ( b->code == BPT_OK )
|
||||
valid_bpt_exists = true;
|
||||
}
|
||||
|
||||
if ( !valid_bpt_exists && ndel == 0 )
|
||||
{
|
||||
if ( nbpts != NULL )
|
||||
*nbpts = 0;
|
||||
return DRC_OK; // none of bpts is writable
|
||||
}
|
||||
|
||||
drc_t drc = s_update_bpts(nbpts, bpts, nadd, ndel, errbuf);
|
||||
return drc;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
#ifndef REMOTE_DEBUGGER
|
||||
// another copy of this function (for remote debugging) is defined in dbg_rpc_handler.cpp
|
||||
int send_ioctl(
|
||||
void *,
|
||||
int fn,
|
||||
const void *buf,
|
||||
size_t size,
|
||||
void **poutbuf,
|
||||
ssize_t *poutsize)
|
||||
{
|
||||
return g_dbgmod.handle_ioctl(fn, buf, size, poutbuf, poutsize);
|
||||
}
|
||||
#endif
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
THREAD_SAFE int debmod_t::send_debug_names_to_ida(
|
||||
ea_t *addrs,
|
||||
const char *const *names,
|
||||
int qty)
|
||||
{
|
||||
return ::send_debug_names_to_ida(addrs, names, qty);
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
THREAD_SAFE int send_debug_names_to_ida(
|
||||
ea_t *addrs,
|
||||
const char *const *names,
|
||||
int qty)
|
||||
{
|
||||
struct debug_name_handler_t : public exec_request_t
|
||||
{
|
||||
ea_t *addrs;
|
||||
const char *const *names;
|
||||
int qty;
|
||||
debug_name_handler_t(ea_t *_addrs, const char *const *_names, int _qty)
|
||||
: addrs(_addrs), names(_names), qty(_qty) {}
|
||||
int idaapi execute(void) override
|
||||
{
|
||||
set_arm_thumb_modes(addrs, qty);
|
||||
return set_debug_names(addrs, names, qty);
|
||||
}
|
||||
};
|
||||
debug_name_handler_t dnh(addrs, names, qty);
|
||||
return execute_sync(dnh, MFF_WRITE);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
THREAD_SAFE int debmod_t::send_debug_event_to_ida(
|
||||
const debug_event_t *ev,
|
||||
int rqflags)
|
||||
{
|
||||
return ::send_debug_event_to_ida(ev, rqflags);
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
THREAD_SAFE int send_debug_event_to_ida(
|
||||
const debug_event_t *ev,
|
||||
int rqflags)
|
||||
{
|
||||
return handle_debug_event(ev, rqflags);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
THREAD_SAFE int import_dll(const import_request_t &req)
|
||||
{
|
||||
struct dll_importer_t : public exec_request_t
|
||||
{
|
||||
const import_request_t &req;
|
||||
dll_importer_t(const import_request_t &_req) : req(_req) {}
|
||||
int idaapi execute(void) override
|
||||
{
|
||||
return g_dbgmod.import_dll(req) ? 0 : 1;
|
||||
}
|
||||
};
|
||||
dll_importer_t di(req);
|
||||
return execute_sync(di, MFF_WRITE);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
#if TARGET_PROCESSOR != PLFM_ARM
|
||||
void set_arm_thumb_modes(ea_t * /*addrs*/, int /*qty*/)
|
||||
{
|
||||
}
|
||||
#endif
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// installs or uninstalls debugger specific idc functions
|
||||
bool add_idc_funcs(const ext_idcfunc_t efuncs[], size_t nfuncs, bool reg)
|
||||
{
|
||||
if ( reg )
|
||||
{
|
||||
for ( int i=0; i < nfuncs; i++ )
|
||||
if ( !add_idc_func(efuncs[i]) )
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
for ( int i=0; i < nfuncs; i++ )
|
||||
if ( !del_idc_func(efuncs[i].name) )
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
static drc_t init_debugger(
|
||||
const char *hostname,
|
||||
int port_num,
|
||||
const char *password,
|
||||
qstring *errbuf)
|
||||
{
|
||||
g_dbgmod.dbg_set_debugging((debug & IDA_DEBUG_DEBUGGER) != 0);
|
||||
|
||||
if ( !s_open_remote(hostname, port_num, password, errbuf) )
|
||||
return DRC_FAILED;
|
||||
|
||||
uint32_t flags2 = 0;
|
||||
drc_t drc = s_init(&flags2, errbuf);
|
||||
if ( drc != DRC_OK )
|
||||
{
|
||||
s_close_remote();
|
||||
return drc;
|
||||
}
|
||||
|
||||
debugger.flags2 |= flags2;
|
||||
#if defined(REMOTE_DEBUGGER) && !defined(NO_OPEN_FILE)
|
||||
setflag(debugger.flags2, DBG_HAS_OPEN_FILE, true);
|
||||
#endif
|
||||
#ifdef HAVE_UPDATE_CALL_STACK
|
||||
setflag(debugger.flags2, DBG_HAS_UPDATE_CALL_STACK, true);
|
||||
#endif
|
||||
#ifdef HAVE_APPCALL
|
||||
setflag(debugger.flags2, DBG_HAS_APPCALL, true);
|
||||
#endif
|
||||
#ifdef HAVE_MAP_ADDRESS
|
||||
setflag(debugger.flags2, DBG_HAS_MAP_ADDRESS, true);
|
||||
#endif
|
||||
debugger_inited = true;
|
||||
processor_specific_init();
|
||||
register_idc_funcs(true);
|
||||
init_dbg_idcfuncs(true);
|
||||
#if DEBUGGER_ID == DEBUGGER_ID_X86_IA32_WIN32_USER || DEBUGGER_ID == DEBUGGER_ID_X86_IA32_BOCHS
|
||||
install_x86seh_menu();
|
||||
#endif
|
||||
return DRC_OK;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
static drc_t term_debugger(void)
|
||||
{
|
||||
if ( debugger_inited )
|
||||
{
|
||||
debugger_inited = false;
|
||||
#if DEBUGGER_ID == DEBUGGER_ID_X86_IA32_WIN32_USER || DEBUGGER_ID == DEBUGGER_ID_X86_IA32_BOCHS
|
||||
remove_x86seh_menu();
|
||||
#endif
|
||||
init_dbg_idcfuncs(false);
|
||||
register_idc_funcs(false);
|
||||
processor_specific_term();
|
||||
g_dbgmod.dbg_term();
|
||||
return s_close_remote();
|
||||
}
|
||||
return DRC_FAILED;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
static ssize_t idaapi idd_notify(void *, int msgid, va_list va)
|
||||
{
|
||||
int retcode = DRC_NONE;
|
||||
qstring *errbuf;
|
||||
|
||||
switch ( msgid )
|
||||
{
|
||||
case debugger_t::ev_init_debugger:
|
||||
{
|
||||
const char *hostname = va_arg(va, const char *);
|
||||
int portnum = va_arg(va, int);
|
||||
const char *password = va_arg(va, const char *);
|
||||
errbuf = va_arg(va, qstring *);
|
||||
QASSERT(1522, errbuf != NULL);
|
||||
retcode = init_debugger(hostname, portnum, password, errbuf);
|
||||
}
|
||||
break;
|
||||
|
||||
case debugger_t::ev_term_debugger:
|
||||
retcode = term_debugger();
|
||||
break;
|
||||
|
||||
case debugger_t::ev_get_processes:
|
||||
{
|
||||
procinfo_vec_t *procs = va_arg(va, procinfo_vec_t *);
|
||||
errbuf = va_arg(va, qstring *);
|
||||
retcode = g_dbgmod.dbg_get_processes(procs, errbuf);
|
||||
}
|
||||
break;
|
||||
|
||||
case debugger_t::ev_start_process:
|
||||
{
|
||||
const char *path = va_arg(va, const char *);
|
||||
const char *args = va_arg(va, const char *);
|
||||
const char *startdir = va_arg(va, const char *);
|
||||
uint32 dbg_proc_flags = va_arg(va, uint32);
|
||||
const char *input_path = va_arg(va, const char *);
|
||||
uint32 input_file_crc32 = va_arg(va, uint32);
|
||||
errbuf = va_arg(va, qstring *);
|
||||
retcode = s_start_process(path,
|
||||
args,
|
||||
startdir,
|
||||
dbg_proc_flags,
|
||||
input_path,
|
||||
input_file_crc32,
|
||||
errbuf);
|
||||
}
|
||||
break;
|
||||
|
||||
case debugger_t::ev_attach_process:
|
||||
{
|
||||
pid_t pid = va_argi(va, pid_t);
|
||||
int event_id = va_arg(va, int);
|
||||
uint32 dbg_proc_flags = va_arg(va, uint32);
|
||||
errbuf = va_arg(va, qstring *);
|
||||
retcode = s_attach_process(pid, event_id, dbg_proc_flags, errbuf);
|
||||
}
|
||||
break;
|
||||
|
||||
case debugger_t::ev_detach_process:
|
||||
retcode = g_dbgmod.dbg_detach_process();
|
||||
break;
|
||||
|
||||
case debugger_t::ev_get_debapp_attrs:
|
||||
{
|
||||
debapp_attrs_t *out_pattrs = va_arg(va, debapp_attrs_t *);
|
||||
g_dbgmod.dbg_get_debapp_attrs(out_pattrs);
|
||||
retcode = DRC_OK;
|
||||
}
|
||||
break;
|
||||
|
||||
case debugger_t::ev_rebase_if_required_to:
|
||||
{
|
||||
ea_t new_base = va_arg(va, ea_t);
|
||||
rebase_if_required_to(new_base);
|
||||
retcode = DRC_OK;
|
||||
}
|
||||
break;
|
||||
|
||||
case debugger_t::ev_request_pause:
|
||||
errbuf = va_arg(va, qstring *);
|
||||
retcode = g_dbgmod.dbg_prepare_to_pause_process(errbuf);
|
||||
break;
|
||||
|
||||
case debugger_t::ev_exit_process:
|
||||
errbuf = va_arg(va, qstring *);
|
||||
retcode = g_dbgmod.dbg_exit_process(errbuf);
|
||||
break;
|
||||
|
||||
case debugger_t::ev_get_debug_event:
|
||||
{
|
||||
gdecode_t *code = va_arg(va, gdecode_t *);
|
||||
debug_event_t *event = va_arg(va, debug_event_t *);
|
||||
int timeout_ms = va_arg(va, int);
|
||||
*code = g_dbgmod.dbg_get_debug_event(event, timeout_ms);
|
||||
retcode = DRC_OK;
|
||||
}
|
||||
break;
|
||||
|
||||
case debugger_t::ev_resume:
|
||||
{
|
||||
debug_event_t *event = va_arg(va, debug_event_t *);
|
||||
retcode = g_dbgmod.dbg_continue_after_event(event);
|
||||
}
|
||||
break;
|
||||
|
||||
case debugger_t::ev_set_exception_info:
|
||||
{
|
||||
exception_info_t *info = va_arg(va, exception_info_t *);
|
||||
int qty = va_arg(va, int);
|
||||
g_dbgmod.dbg_set_exception_info(info, qty);
|
||||
retcode = DRC_OK;
|
||||
}
|
||||
break;
|
||||
|
||||
case debugger_t::ev_suspended:
|
||||
{
|
||||
bool dlls_added = va_argi(va, bool);
|
||||
thread_name_vec_t *thr_names = va_arg(va, thread_name_vec_t *);
|
||||
s_stopped_at_debug_event(thr_names, dlls_added);
|
||||
retcode = DRC_OK;
|
||||
}
|
||||
break;
|
||||
|
||||
case debugger_t::ev_thread_suspend:
|
||||
{
|
||||
thid_t tid = va_argi(va, thid_t);
|
||||
retcode = g_dbgmod.dbg_thread_suspend(tid);
|
||||
}
|
||||
break;
|
||||
|
||||
case debugger_t::ev_thread_continue:
|
||||
{
|
||||
thid_t tid = va_argi(va, thid_t);
|
||||
retcode = g_dbgmod.dbg_thread_continue(tid);
|
||||
}
|
||||
break;
|
||||
|
||||
case debugger_t::ev_set_resume_mode:
|
||||
{
|
||||
thid_t tid = va_argi(va, thid_t);
|
||||
resume_mode_t resmod = va_argi(va, resume_mode_t);
|
||||
retcode = g_dbgmod.dbg_set_resume_mode(tid, resmod);
|
||||
}
|
||||
break;
|
||||
|
||||
case debugger_t::ev_read_registers:
|
||||
{
|
||||
thid_t tid = va_argi(va, thid_t);
|
||||
int clsmask = va_arg(va, int);
|
||||
regval_t *values = va_arg(va, regval_t *);
|
||||
errbuf = va_arg(va, qstring *);
|
||||
retcode = READ_REGISTERS(tid, clsmask, values, errbuf);
|
||||
}
|
||||
break;
|
||||
|
||||
case debugger_t::ev_write_register:
|
||||
{
|
||||
thid_t tid = va_argi(va, thid_t);
|
||||
int regidx = va_arg(va, int);
|
||||
const regval_t *value = va_arg(va, const regval_t *);
|
||||
errbuf = va_arg(va, qstring *);
|
||||
retcode = WRITE_REGISTER(tid, regidx, value, errbuf);
|
||||
}
|
||||
break;
|
||||
|
||||
case debugger_t::ev_thread_get_sreg_base:
|
||||
{
|
||||
ea_t *answer = va_arg(va, ea_t *);
|
||||
thid_t tid = va_argi(va, thid_t);
|
||||
int sreg_value = va_arg(va, int);
|
||||
errbuf = va_arg(va, qstring *);
|
||||
retcode = g_dbgmod.dbg_thread_get_sreg_base(answer, tid, sreg_value, errbuf);
|
||||
}
|
||||
break;
|
||||
|
||||
case debugger_t::ev_get_memory_info:
|
||||
{
|
||||
meminfo_vec_t *ranges = va_arg(va, meminfo_vec_t *);
|
||||
errbuf = va_arg(va, qstring *);
|
||||
retcode = g_dbgmod.dbg_get_memory_info(*ranges, errbuf);
|
||||
}
|
||||
break;
|
||||
|
||||
case debugger_t::ev_read_memory:
|
||||
{
|
||||
size_t *nbytes = va_arg(va, size_t *);
|
||||
ea_t ea = va_arg(va, ea_t);
|
||||
void *buffer = va_arg(va, void *);
|
||||
size_t size = va_arg(va, size_t);
|
||||
errbuf = va_arg(va, qstring *);
|
||||
ssize_t code = g_dbgmod.dbg_read_memory(ea, buffer, size, errbuf);
|
||||
*nbytes = code >= 0 ? code : 0;
|
||||
retcode = code >= 0 ? DRC_OK : DRC_NOPROC;
|
||||
}
|
||||
break;
|
||||
|
||||
case debugger_t::ev_write_memory:
|
||||
{
|
||||
size_t *nbytes = va_arg(va, size_t *);
|
||||
ea_t ea = va_arg(va, ea_t);
|
||||
const void *buffer = va_arg(va, void *);
|
||||
size_t size = va_arg(va, size_t);
|
||||
errbuf = va_arg(va, qstring *);
|
||||
ssize_t code = g_dbgmod.dbg_write_memory(ea, buffer, size, errbuf);
|
||||
*nbytes = code >= 0 ? code : 0;
|
||||
retcode = code >= 0 ? DRC_OK : DRC_NOPROC;
|
||||
}
|
||||
break;
|
||||
|
||||
case debugger_t::ev_check_bpt:
|
||||
{
|
||||
int *bptvc = va_arg(va, int *);
|
||||
bpttype_t type = va_argi(va, bpttype_t);
|
||||
ea_t ea = va_arg(va, ea_t);
|
||||
int len = va_arg(va, int);
|
||||
*bptvc = is_ok_bpt(type, ea, len);
|
||||
retcode = DRC_OK;
|
||||
}
|
||||
break;
|
||||
|
||||
case debugger_t::ev_update_bpts:
|
||||
{
|
||||
int *nbpts = va_arg(va, int *);
|
||||
update_bpt_info_t *bpts = va_arg(va, update_bpt_info_t *);
|
||||
int nadd = va_arg(va, int);
|
||||
int ndel = va_arg(va, int);
|
||||
errbuf = va_arg(va, qstring *);
|
||||
retcode = update_bpts(nbpts, bpts, nadd, ndel, errbuf);
|
||||
}
|
||||
break;
|
||||
|
||||
case debugger_t::ev_update_lowcnds:
|
||||
{
|
||||
int *nupdated = va_arg(va, int *);
|
||||
const lowcnd_t *lowcnds = va_arg(va, const lowcnd_t *);
|
||||
int nlowcnds = va_arg(va, int);
|
||||
errbuf = va_arg(va, qstring *);
|
||||
retcode = g_dbgmod.dbg_update_lowcnds(nupdated, lowcnds, nlowcnds, errbuf);
|
||||
}
|
||||
break;
|
||||
|
||||
case debugger_t::ev_open_file:
|
||||
{
|
||||
const char *file = va_arg(va, const char *);
|
||||
uint64 *fsize = va_arg(va, uint64 *);
|
||||
bool readonly = va_argi(va, bool);
|
||||
retcode = g_dbgmod.dbg_open_file(file, fsize, readonly);
|
||||
}
|
||||
break;
|
||||
|
||||
case debugger_t::ev_close_file:
|
||||
{
|
||||
int fn = va_arg(va, int);
|
||||
g_dbgmod.dbg_close_file(fn);
|
||||
retcode = DRC_OK;
|
||||
}
|
||||
break;
|
||||
|
||||
case debugger_t::ev_read_file:
|
||||
{
|
||||
int fn = va_arg(va, int);
|
||||
qoff64_t off = va_arg(va, qoff64_t);
|
||||
void *buf = va_arg(va, void *);
|
||||
size_t size = va_arg(va, size_t);
|
||||
retcode = g_dbgmod.dbg_read_file(fn, off, buf, size);
|
||||
}
|
||||
break;
|
||||
|
||||
case debugger_t::ev_write_file:
|
||||
{
|
||||
int fn = va_arg(va, int);
|
||||
qoff64_t off = va_arg(va, qoff64_t);
|
||||
const void *buf = va_arg(va, const void *);
|
||||
size_t size = va_arg(va, size_t);
|
||||
retcode = g_dbgmod.dbg_write_file(fn, off, buf, size);
|
||||
}
|
||||
break;
|
||||
|
||||
case debugger_t::ev_map_address:
|
||||
{
|
||||
ea_t *mapped = va_arg(va, ea_t *);
|
||||
ea_t ea = va_arg(va, ea_t);
|
||||
const regval_t *regs = va_arg(va, const regval_t *);
|
||||
int regnum = va_arg(va, int);
|
||||
*mapped = g_dbgmod.map_address(ea, regs, regnum);
|
||||
return DRC_OK;
|
||||
}
|
||||
break;
|
||||
|
||||
#ifdef GET_DEBMOD_EXTS
|
||||
case debugger_t::ev_get_debmod_extensions:
|
||||
{
|
||||
const void **ext = va_arg(va, const void **);
|
||||
*ext = GET_DEBMOD_EXTS();
|
||||
retcode = DRC_OK;
|
||||
}
|
||||
break;
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_UPDATE_CALL_STACK
|
||||
case debugger_t::ev_update_call_stack:
|
||||
{
|
||||
thid_t tid = va_argi(va, thid_t);
|
||||
call_stack_t *trace = va_arg(va, call_stack_t *);
|
||||
retcode = g_dbgmod.dbg_update_call_stack(tid, trace);
|
||||
}
|
||||
break;
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_APPCALL
|
||||
case debugger_t::ev_appcall:
|
||||
{
|
||||
ea_t *blob_ea = va_arg(va, ea_t *);
|
||||
ea_t func_ea = va_arg(va, ea_t);
|
||||
thid_t tid = va_arg(va, thid_t);
|
||||
const func_type_data_t *fti = va_arg(va, const func_type_data_t *);
|
||||
int nargs = va_arg(va, int);
|
||||
const regobjs_t *regargs = va_arg(va, const regobjs_t *);
|
||||
relobj_t *stkargs = va_arg(va, relobj_t *);
|
||||
regobjs_t *retregs = va_arg(va, regobjs_t *);
|
||||
errbuf = va_arg(va, qstring *);
|
||||
debug_event_t *event = va_arg(va, debug_event_t *);
|
||||
int opts = va_arg(va, int);
|
||||
qnotused(nargs);
|
||||
*blob_ea = g_dbgmod.dbg_appcall(func_ea, tid, fti->stkargs, regargs, stkargs, retregs, errbuf, event, opts);
|
||||
retcode = DRC_OK;
|
||||
}
|
||||
break;
|
||||
|
||||
case debugger_t::ev_cleanup_appcall:
|
||||
{
|
||||
thid_t tid = va_argi(va, thid_t);
|
||||
retcode = g_dbgmod.dbg_cleanup_appcall(tid);
|
||||
}
|
||||
break;
|
||||
#endif
|
||||
|
||||
case debugger_t::ev_eval_lowcnd:
|
||||
{
|
||||
thid_t tid = va_argi(va, thid_t);
|
||||
ea_t ea = va_arg(va, ea_t);
|
||||
errbuf = va_arg(va, qstring *);
|
||||
retcode = g_dbgmod.dbg_eval_lowcnd(tid, ea, errbuf);
|
||||
}
|
||||
break;
|
||||
|
||||
case debugger_t::ev_send_ioctl:
|
||||
{
|
||||
int fn = va_arg(va, int);
|
||||
const void *buf = va_arg(va, const void *);
|
||||
size_t size = va_arg(va, size_t);
|
||||
void **poutbuf = va_arg(va, void **);
|
||||
ssize_t *poutsize = va_arg(va, ssize_t *);
|
||||
retcode = g_dbgmod.handle_ioctl(fn, buf, size, poutbuf, poutsize);
|
||||
}
|
||||
break;
|
||||
|
||||
case debugger_t::ev_dbg_enable_trace:
|
||||
{
|
||||
thid_t tid = va_arg(va, thid_t);
|
||||
bool enable = va_argi(va, bool);
|
||||
int trace_flags = va_arg(va, int);
|
||||
retcode = g_dbgmod.dbg_enable_trace(tid, enable, trace_flags) ? DRC_OK : DRC_NONE;
|
||||
}
|
||||
break;
|
||||
|
||||
case debugger_t::ev_is_tracing_enabled:
|
||||
{
|
||||
thid_t tid = va_arg(va, thid_t);
|
||||
int tracebit = va_arg(va, int);
|
||||
retcode = g_dbgmod.dbg_is_tracing_enabled(tid, tracebit) ? DRC_OK : DRC_NONE;
|
||||
}
|
||||
break;
|
||||
|
||||
case debugger_t::ev_rexec:
|
||||
{
|
||||
const char *cmdline = va_arg(va, const char *);
|
||||
retcode = g_dbgmod.dbg_rexec(cmdline);
|
||||
}
|
||||
break;
|
||||
|
||||
#ifdef HAVE_GET_SRCINFO_PATH
|
||||
case debugger_t::ev_get_srcinfo_path:
|
||||
{
|
||||
qstring *path = va_arg(va, qstring *);
|
||||
ea_t base = va_arg(va, ea_t);
|
||||
bool ok = g_dbgmod.dbg_get_srcinfo_path(path, base);
|
||||
retcode = ok ? DRC_OK : DRC_NONE;
|
||||
}
|
||||
break;
|
||||
#endif
|
||||
|
||||
case debugger_t::ev_bin_search:
|
||||
{
|
||||
ea_t *ea = va_arg(va, ea_t *);
|
||||
ea_t start_ea = va_arg(va, ea_t);
|
||||
ea_t end_ea = va_arg(va, ea_t);
|
||||
const compiled_binpat_vec_t *ptns = va_arg(va, const compiled_binpat_vec_t *);
|
||||
int srch_flags = va_arg(va, int);
|
||||
errbuf = va_arg(va, qstring *);
|
||||
if ( ptns != NULL )
|
||||
retcode = g_dbgmod.dbg_bin_search(ea, start_ea, end_ea, *ptns, srch_flags, errbuf);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return retcode;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// Initialize debugger plugin
|
||||
static plugmod_t *idaapi init(void)
|
||||
{
|
||||
// copy of the definitions from loader.hpp
|
||||
// we will delete them after improving the debuggers to use PLUGIN_MULTI.
|
||||
#define PLUGIN_SKIP nullptr
|
||||
#define PLUGIN_KEEP ((plugmod_t *)2)
|
||||
|
||||
if ( init_plugin() )
|
||||
{
|
||||
update_idd_registers();
|
||||
dbg = &debugger;
|
||||
plugin_inited = true;
|
||||
return PLUGIN_KEEP;
|
||||
}
|
||||
return PLUGIN_SKIP;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// Terminate debugger plugin
|
||||
static void idaapi term(void)
|
||||
{
|
||||
if ( plugin_inited )
|
||||
{
|
||||
term_plugin();
|
||||
plugin_inited = false;
|
||||
}
|
||||
// we're being unloaded, clear the 'dbg' pointer if it's ours
|
||||
if ( dbg == &debugger )
|
||||
dbg = NULL;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// The plugin method - usually is not used for debugger plugins
|
||||
static bool idaapi run(size_t arg)
|
||||
{
|
||||
#ifdef HAVE_PLUGIN_RUN
|
||||
plugin_run(int(arg));
|
||||
#else
|
||||
qnotused(arg);
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
//
|
||||
// DEBUGGER DESCRIPTION BLOCK
|
||||
//
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
#ifdef SET_DBG_OPTIONS
|
||||
# define S_SET_DBG_OPTIONS s_set_dbg_options
|
||||
#else
|
||||
# define S_SET_DBG_OPTIONS NULL
|
||||
# define SET_DBG_OPTIONS NULL
|
||||
#endif
|
||||
|
||||
#ifndef S_FILETYPE
|
||||
# define S_FILETYPE 0
|
||||
#endif
|
||||
|
||||
// DBG_HAS_SET_RESUME_MODE must be set before init_debugger.
|
||||
// typically arm has no single step mechanism, arm64 macOS11 is an exception.
|
||||
#if TARGET_PROCESSOR == PLFM_ARM && DEBUGGER_ID != DEBUGGER_ID_ARM_MACOS_USER
|
||||
# define S_DBG_HAS_SET_RESUME_MODE 0
|
||||
#else
|
||||
# define S_DBG_HAS_SET_RESUME_MODE DBG_HAS_SET_RESUME_MODE
|
||||
#endif
|
||||
|
||||
#ifndef DEBUGGER_RESMOD
|
||||
# define DEBUGGER_RESMOD 0
|
||||
#endif
|
||||
|
||||
debugger_t debugger =
|
||||
{
|
||||
IDD_INTERFACE_VERSION,
|
||||
DEBUGGER_NAME,
|
||||
DEBUGGER_ID,
|
||||
PROCESSOR_NAME,
|
||||
DEBUGGER_FLAGS, // flags
|
||||
DBG_HAS_ATTACH_PROCESS
|
||||
| DBG_HAS_REQUEST_PAUSE
|
||||
| DBG_HAS_SET_EXCEPTION_INFO
|
||||
| DBG_HAS_THREAD_SUSPEND
|
||||
| DBG_HAS_THREAD_CONTINUE
|
||||
| S_DBG_HAS_SET_RESUME_MODE
|
||||
| DBG_HAS_THREAD_GET_SREG_BASE
|
||||
| DBG_HAS_CHECK_BPT
|
||||
| DBG_HAS_REXEC, // flags2
|
||||
|
||||
REGISTER_CLASSES,
|
||||
REGISTER_CLASSES_DEFAULT,
|
||||
REGISTERS,
|
||||
REGISTERS_SIZE,
|
||||
|
||||
MEMORY_PAGE_SIZE,
|
||||
|
||||
bpt_code,
|
||||
sizeof(bpt_code),
|
||||
S_FILETYPE,
|
||||
DEBUGGER_RESMOD,
|
||||
|
||||
S_SET_DBG_OPTIONS,
|
||||
idd_notify,
|
||||
};
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
//
|
||||
// PLUGIN DESCRIPTION BLOCK
|
||||
//
|
||||
//--------------------------------------------------------------------------
|
||||
plugin_t PLUGIN =
|
||||
{
|
||||
IDP_INTERFACE_VERSION,
|
||||
PLUGIN_HIDE|PLUGIN_DBG, // plugin flags
|
||||
init, // initialize
|
||||
|
||||
term, // terminate. this pointer may be NULL.
|
||||
|
||||
run, // invoke plugin
|
||||
|
||||
comment, // long comment about the plugin
|
||||
// it could appear in the status line
|
||||
// or as a hint
|
||||
|
||||
comment, // multiline help about the plugin
|
||||
|
||||
wanted_name, // the preferred short name of the plugin
|
||||
"" // the preferred hotkey to run the plugin
|
||||
};
|
||||
258
idasdk76/dbg/common_stub_impl.cpp
Normal file
258
idasdk76/dbg/common_stub_impl.cpp
Normal file
@@ -0,0 +1,258 @@
|
||||
//
|
||||
// This file is included from other files, do not directly compile it.
|
||||
// It contains the implementation of debugger plugin callback functions
|
||||
//
|
||||
|
||||
#include <err.h>
|
||||
#include <name.hpp>
|
||||
#include <expr.hpp>
|
||||
#include <segment.hpp>
|
||||
#include <typeinf.hpp>
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
//lint -esym(714, rebase_or_warn) not referenced
|
||||
int rebase_or_warn(ea_t base, ea_t new_base)
|
||||
{
|
||||
int code = rebase_program(new_base - base, MSF_FIXONCE);
|
||||
if ( code != MOVE_SEGM_OK )
|
||||
{
|
||||
msg("Failed to rebase program, error code %d\n", code);
|
||||
warning("IDA failed to rebase the program.\n"
|
||||
"Most likely it happened because of the debugger\n"
|
||||
"segments created to reflect the real memory state.\n\n"
|
||||
"Please stop the debugger and rebase the program manually.\n"
|
||||
"For that, please select the whole program and\n"
|
||||
"use Edit, Segments, Rebase program with delta 0x%08a",
|
||||
new_base - base);
|
||||
}
|
||||
return code;
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
void idaapi s_stopped_at_debug_event(thread_name_vec_t *thr_names, bool dlls_added)
|
||||
{
|
||||
// Let the debugger module populate the names
|
||||
g_dbgmod.dbg_stopped_at_debug_event(NULL, dlls_added, thr_names);
|
||||
if ( dlls_added )
|
||||
{
|
||||
#ifndef RPC_CLIENT
|
||||
// Pass the debug names to the kernel
|
||||
g_dbgmod.set_debug_names();
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// This code is compiled for local debuggers (like win32_user.plw)
|
||||
#ifndef RPC_CLIENT
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
AS_PRINTF(3,0) ssize_t dvmsg(int code, rpc_engine_t *, const char *format, va_list va)
|
||||
{
|
||||
if ( code == 0 )
|
||||
return vmsg(format, va);
|
||||
if ( code > 0 )
|
||||
vwarning(format, va);
|
||||
else
|
||||
verror(format, va);
|
||||
return 0;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
AS_PRINTF(2,0) void dmsg(rpc_engine_t *rpc, const char *format, va_list va)
|
||||
{
|
||||
dvmsg(0, rpc, format, va);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
AS_PRINTF(2,0) void derror(rpc_engine_t *rpc, const char *format, va_list va)
|
||||
{
|
||||
dvmsg(-1, rpc, format, va);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
AS_PRINTF(2,0) void dwarning(rpc_engine_t *rpc, const char *format, va_list va)
|
||||
{
|
||||
dvmsg(1, rpc, format, va);
|
||||
}
|
||||
|
||||
#endif // end of 'local debugger' code
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
bool lock_begin(void)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
bool lock_end(void)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
void report_idc_error(
|
||||
rpc_engine_t *,
|
||||
ea_t ea,
|
||||
error_t code,
|
||||
ssize_t errval,
|
||||
const char *errprm)
|
||||
{
|
||||
// Copy errval/errprm to the locations expected by qstrerror()
|
||||
if ( errprm != NULL && errprm != get_error_string(0) )
|
||||
QPRM(1, errprm);
|
||||
else if ( code == eOS )
|
||||
errno = errval;
|
||||
else
|
||||
set_error_data(0, errval);
|
||||
|
||||
warning("AUTOHIDE NONE\n%a: %s", ea, qstrerror(code));
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
int for_all_debuggers(debmod_visitor_t &v)
|
||||
{
|
||||
return v.visit(&g_dbgmod);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
drc_t idaapi s_write_register(thid_t tid, int reg_idx, const regval_t *value, qstring *errbuf)
|
||||
{
|
||||
return g_dbgmod.dbg_write_register(tid, reg_idx, value, errbuf);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
drc_t idaapi s_read_registers(thid_t tid, int clsmask, regval_t *values, qstring *errbuf)
|
||||
{
|
||||
return g_dbgmod.dbg_read_registers(tid, clsmask, values, errbuf);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
drc_t idaapi s_update_bpts(
|
||||
int *nbpts,
|
||||
update_bpt_info_t *bpts,
|
||||
int nadd,
|
||||
int ndel,
|
||||
qstring *errbuf)
|
||||
{
|
||||
return g_dbgmod.dbg_update_bpts(nbpts, bpts, nadd, ndel, errbuf);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
static void update_idd_registers(void)
|
||||
{
|
||||
size_t nregs = g_dbgmod.idaregs.nregs();
|
||||
if ( nregs > 0 )
|
||||
{
|
||||
// register classes
|
||||
debugger.regclasses = g_dbgmod.idaregs.regclasses();
|
||||
debugger.default_regclasses = 1; // TODO 1 is the general register set
|
||||
|
||||
// registers
|
||||
debugger.nregs = nregs;
|
||||
debugger.registers = g_dbgmod.idaregs.registers();
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
drc_t s_init(uint32_t *flags2, qstring *errbuf)
|
||||
{
|
||||
g_dbgmod.debugger_flags = debugger.flags;
|
||||
drc_t retcode = g_dbgmod.dbg_init(flags2, errbuf);
|
||||
if ( retcode > DRC_NONE )
|
||||
update_idd_registers();
|
||||
return retcode;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
static drc_t s_attach_process(
|
||||
pid_t process_id,
|
||||
int event_id,
|
||||
int flags,
|
||||
qstring *errbuf)
|
||||
{
|
||||
drc_t retcode = g_dbgmod.dbg_attach_process(process_id, event_id, flags, errbuf);
|
||||
if ( retcode > DRC_NONE )
|
||||
update_idd_registers();
|
||||
return retcode;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
static drc_t s_start_process(
|
||||
const char *path,
|
||||
const char *args,
|
||||
const char *startdir,
|
||||
int flags,
|
||||
const char *input_path,
|
||||
uint32 input_file_crc32,
|
||||
qstring *errbuf)
|
||||
{
|
||||
drc_t retcode = g_dbgmod.dbg_start_process(path,
|
||||
args,
|
||||
startdir,
|
||||
flags,
|
||||
input_path,
|
||||
input_file_crc32,
|
||||
errbuf);
|
||||
if ( retcode > DRC_NONE )
|
||||
update_idd_registers();
|
||||
return retcode;
|
||||
}
|
||||
|
||||
#ifdef SET_DBG_OPTIONS
|
||||
//--------------------------------------------------------------------------
|
||||
// forward declaration
|
||||
const char *idaapi SET_DBG_OPTIONS(
|
||||
const char *keyword,
|
||||
int pri,
|
||||
int value_type,
|
||||
const void *value);
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
static const char *idaapi s_set_dbg_options(
|
||||
const char *keyword,
|
||||
int pri,
|
||||
int value_type,
|
||||
const void *value)
|
||||
{
|
||||
const char *ret = SET_DBG_OPTIONS(keyword, pri, value_type, value);
|
||||
update_idd_registers();
|
||||
return ret;
|
||||
}
|
||||
#endif
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
#ifdef REMOTE_DEBUGGER
|
||||
bool s_open_remote(const char *hostname, int port_number, const char *password, qstring *errbuf)
|
||||
{
|
||||
return g_dbgmod.open_remote(hostname, port_number, password, errbuf);
|
||||
}
|
||||
drc_t s_close_remote(void)
|
||||
{
|
||||
return g_dbgmod.close_remote();
|
||||
}
|
||||
#else
|
||||
bool s_open_remote(const char *, int, const char *, qstring *)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
drc_t s_close_remote(void)
|
||||
{
|
||||
return DRC_OK;
|
||||
}
|
||||
#endif
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// Local debuggers must call setup_lowcnd_regfuncs() in order to handle
|
||||
// register read/write requests from low level bpts.
|
||||
void init_dbg_idcfuncs(bool init)
|
||||
{
|
||||
#if !defined(ENABLE_LOWCNDS) \
|
||||
|| defined(REMOTE_DEBUGGER) \
|
||||
|| DEBUGGER_ID == DEBUGGER_ID_X86_IA32_BOCHS
|
||||
qnotused(init);
|
||||
#else
|
||||
setup_lowcnd_regfuncs(init ? idc_get_reg_value : NULL,
|
||||
init ? idc_set_reg_value : NULL);
|
||||
#endif
|
||||
}
|
||||
57
idasdk76/dbg/dbg_pe_hlp.cpp
Normal file
57
idasdk76/dbg/dbg_pe_hlp.cpp
Normal file
@@ -0,0 +1,57 @@
|
||||
#if defined(__NT__) && !defined(__X86__)
|
||||
//--------------------------------------------------------------------------
|
||||
static bool path_start_match(const char *s, const char *f)
|
||||
{
|
||||
if ( f == NULL || s == NULL || *f == 0 || *s == 0 )
|
||||
return false;
|
||||
if ( s == f )
|
||||
return true;
|
||||
qstring t1(s);
|
||||
qstring t2(f);
|
||||
size_t l1 = t1.length();
|
||||
size_t l2 = t2.length();
|
||||
t1.replace("\\", "/");
|
||||
t2.replace("\\", "/");
|
||||
if ( t1[l1-1] == '/' )
|
||||
--l1;
|
||||
if ( t2[l2-1] == '/' )
|
||||
--l2;
|
||||
if ( l1 > l2
|
||||
|| l1 < l2 && t2[l1] != '/' )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return memicmp(t1.c_str(), t2.c_str(), l1) == 0;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
static void replace_system32(char *path, size_t sz)
|
||||
{
|
||||
char spath[MAXSTR];
|
||||
spath[0] = 0;
|
||||
GetSystemDirectoryA(spath, sizeof(spath));
|
||||
if ( spath[0] == 0 || !path_start_match(spath, path) )
|
||||
return;
|
||||
char wpath[MAXSTR];
|
||||
wpath[0] = 0;
|
||||
GetSystemWow64Directory(wpath, sizeof(wpath));
|
||||
if ( wpath[0] == 0 || path_start_match(wpath, spath) )
|
||||
return;
|
||||
size_t len = strlen(wpath);
|
||||
if ( wpath[len-1] == '/' || wpath[len-1] == '\\' )
|
||||
wpath[len-1] = 0;
|
||||
len = strlen(spath);
|
||||
if ( spath[len-1] == '/' || spath[len-1] == '\\' )
|
||||
{
|
||||
--len;
|
||||
path[len] = 0;
|
||||
}
|
||||
qstring n;
|
||||
n.sprnt("%s%s", wpath, &path[len]);
|
||||
qstrncpy(path, n.c_str(), sz);
|
||||
}
|
||||
#else
|
||||
#define replace_system32(PATH, SZ) do {} while ( false )
|
||||
#endif
|
||||
|
||||
|
||||
246
idasdk76/dbg/dbg_rpc_client.cpp
Normal file
246
idasdk76/dbg/dbg_rpc_client.cpp
Normal file
@@ -0,0 +1,246 @@
|
||||
|
||||
// This file is included in the debugger stub that runs on the computer with IDA
|
||||
|
||||
#include <pro.h>
|
||||
#include <name.hpp>
|
||||
#include <diskio.hpp>
|
||||
|
||||
#include "dbg_rpc_client.h"
|
||||
#include "dbg_rpc_hlp.h"
|
||||
#include "debmod.h"
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// check and send to the remote server the specified stub
|
||||
// do it only if its crc does not match the specified crc
|
||||
// this function runs on the local machine with ida interface
|
||||
static uchar *sync_stub(const char *fname, uint32 crc, size_t *psize)
|
||||
{
|
||||
bool complain = true;
|
||||
uchar *retval = NULL;
|
||||
char path[QMAXPATH];
|
||||
if ( getsysfile(path, sizeof(path), fname, NULL) != NULL )
|
||||
{
|
||||
linput_t *li = open_linput(path, false);
|
||||
if ( li != NULL )
|
||||
{
|
||||
int64 size = qlsize(li);
|
||||
if ( size > 0 )
|
||||
{
|
||||
bytevec_t buf;
|
||||
buf.resize(size);
|
||||
if ( qlread(li, buf.begin(), size) == size )
|
||||
{
|
||||
complain = false;
|
||||
if ( calc_crc32(0, buf.begin(), size) != crc )
|
||||
{
|
||||
*psize = size;
|
||||
retval = buf.extract();
|
||||
}
|
||||
else
|
||||
{
|
||||
msg("Kernel debugger stub is up to date...\n");
|
||||
*psize = 1; // signal ok
|
||||
}
|
||||
}
|
||||
}
|
||||
close_linput(li);
|
||||
}
|
||||
}
|
||||
if ( complain )
|
||||
warning("AUTOHIDE NONE\nCould not find/read debugger stub %s", fname);
|
||||
return retval;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
dbg_rpc_client_t::dbg_rpc_client_t(idarpc_stream_t *_irs)
|
||||
: dbg_rpc_engine_t(/*is_client=*/ true),
|
||||
client_irs(_irs)
|
||||
{
|
||||
pending_event.clear_all();
|
||||
verbose = false;
|
||||
recv_timeout = RECV_TIMEOUT_PERIOD;
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
void dbg_rpc_client_t::my_update_wait_dialog(
|
||||
const char *message,
|
||||
const rpc_packet_t *rp)
|
||||
{
|
||||
if ( send_request_data.wait_dialog_displayed )
|
||||
{
|
||||
if ( rp->code != send_request_data.code )
|
||||
replace_wait_box("%s", message);
|
||||
}
|
||||
else
|
||||
{
|
||||
show_wait_box("%s", message);
|
||||
send_request_data.wait_dialog_displayed = true;
|
||||
}
|
||||
send_request_data.code = rp->code;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// requests received from the server.
|
||||
// here the client handles certain server -> client requests
|
||||
bytevec_t dbg_rpc_client_t::on_send_request_interrupt(const rpc_packet_t *rp)
|
||||
{
|
||||
memory_deserializer_t mmdsr(rp+1, rp->length);
|
||||
bytevec_t req = prepare_rpc_packet(RPC_OK);
|
||||
|
||||
switch ( rp->code )
|
||||
{
|
||||
// send_debug_names_to_ida() is thread safe
|
||||
case RPC_SET_DEBUG_NAMES:
|
||||
{
|
||||
my_update_wait_dialog("Downloading Symbols", rp);
|
||||
int qty = mmdsr.unpack_dd();
|
||||
ea_t *addrs = OPERATOR_NEW(ea_t, qty);
|
||||
char **names = OPERATOR_NEW(char *, qty);
|
||||
qstring name;
|
||||
ea_t old = 0;
|
||||
for ( int i=0; i < qty; i++ )
|
||||
{
|
||||
adiff_t o2 = mmdsr.unpack_ea64();
|
||||
if ( mmdsr.unpack_dd() )
|
||||
o2 = -o2;
|
||||
old += o2;
|
||||
addrs[i] = old;
|
||||
int oldlen = mmdsr.unpack_dd();
|
||||
QASSERT(1203, oldlen >= 0 && oldlen <= name.length());
|
||||
// keep the prefix
|
||||
name.resize(oldlen);
|
||||
if ( !mmdsr.unpack_str(&name) )
|
||||
INTERR(1294);
|
||||
names[i] = qstrdup(name.c_str());
|
||||
}
|
||||
int result = send_debug_names_to_ida(addrs, names, qty);
|
||||
verb(("set_debug_name(qty=%d) => %d\n", qty, result));
|
||||
req.pack_dd(result);
|
||||
for ( int i=0; i < qty; i++ )
|
||||
qfree(names[i]);
|
||||
delete [] addrs;
|
||||
delete [] names;
|
||||
}
|
||||
break;
|
||||
|
||||
// import_dll() is thread safe
|
||||
case RPC_IMPORT_DLL:
|
||||
{
|
||||
my_update_wait_dialog("Importing DLLs", rp);
|
||||
ea_t base = mmdsr.unpack_ea64();
|
||||
const char *path = mmdsr.unpack_str();
|
||||
int n = mmdsr.unpack_dd();
|
||||
const void *bytes = mmdsr.unpack_obj_inplace(n);
|
||||
bytevec_t uuid(bytes, n);
|
||||
int result = import_dll(import_request_t(base, path, uuid));
|
||||
verb(("import_dll(base=%a, path=%s) => %d\n", base, path, result));
|
||||
req.pack_dd(result);
|
||||
}
|
||||
break;
|
||||
|
||||
// send_debug_event_to_ida() is thread safe
|
||||
case RPC_HANDLE_DEBUG_EVENT:
|
||||
{
|
||||
debug_event_t ev;
|
||||
extract_debug_event(&ev, mmdsr);
|
||||
int rqflags = mmdsr.unpack_dd();
|
||||
int code = send_debug_event_to_ida(&ev, rqflags);
|
||||
req.pack_dd(code);
|
||||
}
|
||||
break;
|
||||
|
||||
// sync_stub() is thread safe
|
||||
case RPC_SYNC_STUB:
|
||||
{
|
||||
const char *fname = mmdsr.unpack_str();
|
||||
uint32 crc = mmdsr.unpack_dd();
|
||||
|
||||
// security problem: the debugger server should not be able to
|
||||
// read an arbitrary file on the local computer. therefore we completely
|
||||
// ignore the file name and use a hardcoded name.
|
||||
qnotused(fname);
|
||||
fname = "ida_kdstub.dll";
|
||||
|
||||
size_t size = 0;
|
||||
uchar *contents = sync_stub(fname, crc, &size);
|
||||
req.pack_dd((uint32)size);
|
||||
if ( contents != NULL )
|
||||
{
|
||||
req.append(contents, size);
|
||||
qfree(contents);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
// msg/error/warning are thread safe
|
||||
case RPC_ERROR:
|
||||
case RPC_MSG:
|
||||
case RPC_WARNING:
|
||||
{
|
||||
const char *str = mmdsr.unpack_str();
|
||||
if ( *str != '\0' )
|
||||
{
|
||||
if ( rp->code == RPC_MSG )
|
||||
msg("%s", str);
|
||||
else if ( rp->code == RPC_ERROR )
|
||||
error("%s", str);
|
||||
else
|
||||
warning("%s", str);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
// no external functions are called
|
||||
case RPC_EVENT:
|
||||
{
|
||||
extract_debug_event(&pending_event, mmdsr);
|
||||
has_pending_event = true;
|
||||
req = prepare_rpc_packet(RPC_EVOK);
|
||||
verbev(("got event, storing it and sending RPC_EVOK\n"));
|
||||
}
|
||||
break;
|
||||
|
||||
// i doubt that this code is used on the client side
|
||||
// ioctl_handler is NULL
|
||||
case RPC_IOCTL:
|
||||
{
|
||||
int code = handle_ioctl_packet(req, mmdsr.ptr, mmdsr.end);
|
||||
if ( code != RPC_OK )
|
||||
return prepare_rpc_packet((uchar)code);
|
||||
}
|
||||
break;
|
||||
|
||||
// report_idc_error() is thread safe
|
||||
case RPC_REPORT_IDC_ERROR:
|
||||
{
|
||||
ea_t ea = mmdsr.unpack_ea64();
|
||||
error_t code = mmdsr.unpack_dd();
|
||||
const char *errprm;
|
||||
ssize_t errval;
|
||||
if ( mmdsr.unpack_db() )
|
||||
{
|
||||
errprm = mmdsr.unpack_str();
|
||||
errval = (ssize_t)errprm;
|
||||
}
|
||||
else
|
||||
{
|
||||
errprm = NULL;
|
||||
errval = mmdsr.unpack_ea64();
|
||||
}
|
||||
report_idc_error(NULL, ea, code, errval, errprm);
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
return prepare_rpc_packet(RPC_UNK);
|
||||
}
|
||||
return req;
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
void dbg_rpc_client_t::on_send_request_end(const rpc_packet_t *)
|
||||
{
|
||||
if ( send_request_data.wait_dialog_displayed )
|
||||
hide_wait_box();
|
||||
send_request_data.reset();
|
||||
}
|
||||
35
idasdk76/dbg/dbg_rpc_client.h
Normal file
35
idasdk76/dbg/dbg_rpc_client.h
Normal file
@@ -0,0 +1,35 @@
|
||||
#ifndef __RPC_CLIENT__
|
||||
#define __RPC_CLIENT__
|
||||
|
||||
#include "dbg_rpc_engine.h"
|
||||
|
||||
class dbg_rpc_client_t: public dbg_rpc_engine_t
|
||||
{
|
||||
protected:
|
||||
debug_event_t pending_event;
|
||||
idarpc_stream_t *client_irs;
|
||||
bool verbose;
|
||||
|
||||
struct send_request_data_t
|
||||
{
|
||||
uchar code;
|
||||
bool wait_dialog_displayed;
|
||||
|
||||
send_request_data_t() { reset(); }
|
||||
void reset() { code = uchar(-1); wait_dialog_displayed = false; }
|
||||
};
|
||||
send_request_data_t send_request_data;
|
||||
|
||||
void my_update_wait_dialog(const char *message, const rpc_packet_t *rp);
|
||||
|
||||
virtual bytevec_t on_send_request_interrupt(const rpc_packet_t *rp) override;
|
||||
virtual void on_send_request_end(const rpc_packet_t *result) override;
|
||||
|
||||
public:
|
||||
dbg_rpc_client_t(idarpc_stream_t *irs);
|
||||
virtual ~dbg_rpc_client_t() {}
|
||||
|
||||
virtual idarpc_stream_t *get_irs() const override { return client_irs; }
|
||||
};
|
||||
|
||||
#endif
|
||||
166
idasdk76/dbg/dbg_rpc_engine.cpp
Normal file
166
idasdk76/dbg/dbg_rpc_engine.cpp
Normal file
@@ -0,0 +1,166 @@
|
||||
|
||||
#include <network.hpp>
|
||||
|
||||
#include "dbg_rpc_engine.h"
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
#ifdef TESTABLE_BUILD
|
||||
static const rpc_packet_type_desc_t dbg_rpc_packet_t_descs[] =
|
||||
{
|
||||
{ RPC_OK, "RPC_OK", nullptr },
|
||||
{ RPC_UNK, "RPC_UNK", nullptr },
|
||||
{ RPC_MEM, "RPC_MEM", nullptr },
|
||||
|
||||
{ RPC_OPEN, "RPC_OPEN", nullptr },
|
||||
{ RPC_EVENT, "RPC_EVENT", nullptr },
|
||||
{ RPC_EVOK, "RPC_EVOK", nullptr },
|
||||
{ RPC_CANCELLED, "RPC_CANCELLED", nullptr },
|
||||
|
||||
{ RPC_INIT, "RPC_INIT", nullptr },
|
||||
{ RPC_TERM, "RPC_TERM", nullptr },
|
||||
{ RPC_GET_PROCESSES, "RPC_GET_PROCESSES", nullptr },
|
||||
{ RPC_START_PROCESS, "RPC_START_PROCESS", nullptr },
|
||||
{ RPC_EXIT_PROCESS, "RPC_EXIT_PROCESS", nullptr },
|
||||
{ RPC_ATTACH_PROCESS, "RPC_ATTACH_PROCESS", nullptr },
|
||||
{ RPC_DETACH_PROCESS, "RPC_DETACH_PROCESS", nullptr },
|
||||
{ RPC_GET_DEBUG_EVENT, "RPC_GET_DEBUG_EVENT", nullptr },
|
||||
{ RPC_PREPARE_TO_PAUSE_PROCESS, "RPC_PREPARE_TO_PAUSE_PROCESS", nullptr },
|
||||
{ RPC_STOPPED_AT_DEBUG_EVENT, "RPC_STOPPED_AT_DEBUG_EVENT", nullptr },
|
||||
{ RPC_CONTINUE_AFTER_EVENT, "RPC_CONTINUE_AFTER_EVENT", nullptr },
|
||||
{ RPC_TH_SUSPEND, "RPC_TH_SUSPEND", nullptr },
|
||||
{ RPC_TH_CONTINUE, "RPC_TH_CONTINUE", nullptr },
|
||||
{ RPC_SET_RESUME_MODE, "RPC_SET_RESUME_MODE", nullptr },
|
||||
{ RPC_GET_MEMORY_INFO, "RPC_GET_MEMORY_INFO", nullptr },
|
||||
{ RPC_READ_MEMORY, "RPC_READ_MEMORY", nullptr },
|
||||
{ RPC_WRITE_MEMORY, "RPC_WRITE_MEMORY", nullptr },
|
||||
{ RPC_UPDATE_BPTS, "RPC_UPDATE_BPTS", nullptr },
|
||||
{ RPC_UPDATE_LOWCNDS, "RPC_UPDATE_LOWCNDS", nullptr },
|
||||
{ RPC_EVAL_LOWCND, "RPC_EVAL_LOWCND", nullptr },
|
||||
{ RPC_ISOK_BPT, "RPC_ISOK_BPT", nullptr },
|
||||
{ RPC_READ_REGS, "RPC_READ_REGS", nullptr },
|
||||
{ RPC_WRITE_REG, "RPC_WRITE_REG", nullptr },
|
||||
{ RPC_GET_SREG_BASE, "RPC_GET_SREG_BASE", nullptr },
|
||||
{ RPC_SET_EXCEPTION_INFO, "RPC_SET_EXCEPTION_INFO", nullptr },
|
||||
|
||||
{ RPC_OPEN_FILE, "RPC_OPEN_FILE", nullptr },
|
||||
{ RPC_CLOSE_FILE, "RPC_CLOSE_FILE", nullptr },
|
||||
{ RPC_READ_FILE, "RPC_READ_FILE", nullptr },
|
||||
{ RPC_WRITE_FILE, "RPC_WRITE_FILE", nullptr },
|
||||
{ RPC_IOCTL, "RPC_IOCTL", nullptr },
|
||||
{ RPC_UPDATE_CALL_STACK, "RPC_UPDATE_CALL_STACK", nullptr },
|
||||
{ RPC_APPCALL, "RPC_APPCALL", nullptr },
|
||||
{ RPC_CLEANUP_APPCALL, "RPC_CLEANUP_APPCALL", nullptr },
|
||||
{ RPC_REXEC, "RPC_REXEC", nullptr },
|
||||
{ RPC_GET_SCATTERED_IMAGE, "RPC_GET_SCATTERED_IMAGE", nullptr },
|
||||
{ RPC_GET_IMAGE_UUID, "RPC_GET_IMAGE_UUID", nullptr },
|
||||
{ RPC_GET_SEGM_START, "RPC_GET_SEGM_START", nullptr },
|
||||
{ RPC_BIN_SEARCH, "RPC_BIN_SEARCH", nullptr },
|
||||
|
||||
{ RPC_SET_DEBUG_NAMES, "RPC_SET_DEBUG_NAMES", nullptr },
|
||||
{ RPC_SYNC_STUB, "RPC_SYNC_STUB", nullptr },
|
||||
{ RPC_ERROR, "RPC_ERROR", nullptr },
|
||||
{ RPC_MSG, "RPC_MSG", nullptr },
|
||||
{ RPC_WARNING, "RPC_WARNING", nullptr },
|
||||
{ RPC_HANDLE_DEBUG_EVENT, "RPC_HANDLE_DEBUG_EVENT", nullptr },
|
||||
{ RPC_REPORT_IDC_ERROR, "RPC_REPORT_IDC_ERROR", nullptr },
|
||||
{ RPC_IMPORT_DLL, "RPC_IMPORT_DLL", nullptr },
|
||||
};
|
||||
#endif
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
dbg_rpc_engine_t::dbg_rpc_engine_t(bool _is_client)
|
||||
: rpc_engine_t(_is_client),
|
||||
has_pending_event(false),
|
||||
poll_debug_events(false)
|
||||
{
|
||||
#ifdef TESTABLE_BUILD
|
||||
register_packet_type_descs(
|
||||
dbg_rpc_packet_t_descs,
|
||||
qnumber(dbg_rpc_packet_t_descs));
|
||||
#endif
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// sends a request and waits for a reply
|
||||
// may occasionally send another request based on the reply
|
||||
rpc_packet_t *dbg_rpc_engine_t::send_request_and_receive_reply(bytevec_t &req, int flags)
|
||||
{
|
||||
bool displayed = false;
|
||||
rpc_packet_t *result = NULL;
|
||||
|
||||
while ( true )
|
||||
{
|
||||
if ( displayed && user_cancelled() )
|
||||
req = prepare_rpc_packet(RPC_CANCELLED);
|
||||
|
||||
if ( !req.empty() )
|
||||
{
|
||||
int code = send_data(req);
|
||||
if ( code != 0 || (flags & PREQ_GET_EVENT) != 0 )
|
||||
break;
|
||||
|
||||
rpc_packet_t *reqp = (rpc_packet_t *)req.begin();
|
||||
if ( reqp->code == RPC_ERROR )
|
||||
qexit(1); // sent error packet, may die now
|
||||
}
|
||||
|
||||
rpc_packet_t *rp = recv_packet();
|
||||
if ( rp == NULL )
|
||||
break;
|
||||
|
||||
switch ( rp->code )
|
||||
{
|
||||
case RPC_UNK:
|
||||
dwarning("rpc: remote did not understand our request");
|
||||
goto FAILURE;
|
||||
case RPC_MEM:
|
||||
dwarning("rpc: no remote memory");
|
||||
goto FAILURE;
|
||||
case RPC_CANCELLED:
|
||||
msg("rpc: user cancelled the operation\n");
|
||||
goto FAILURE;
|
||||
case RPC_OK:
|
||||
result = rp;
|
||||
goto END;
|
||||
default:
|
||||
// other replies are passed to the handler
|
||||
break;
|
||||
}
|
||||
|
||||
if ( !logged_in )
|
||||
{
|
||||
lprintf("Exploit packet has been detected and ignored\n");
|
||||
FAILURE:
|
||||
qfree(rp);
|
||||
break;
|
||||
}
|
||||
// handle actual command in the request
|
||||
// FIXME: use a better function name
|
||||
req = on_send_request_interrupt(rp);
|
||||
qfree(rp);
|
||||
}
|
||||
|
||||
END:
|
||||
on_send_request_end(result);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
int dbg_rpc_engine_t::_send_request_get_int_result(
|
||||
bytevec_t &req,
|
||||
int failure_code,
|
||||
qstring *errbuf)
|
||||
{
|
||||
rpc_packet_t *rp = send_request_and_receive_reply(req);
|
||||
if ( rp == NULL )
|
||||
return failure_code;
|
||||
|
||||
memory_deserializer_t mmdsr(rp+1, rp->length);
|
||||
int rc = mmdsr.unpack_dd();
|
||||
if ( rc < 0 && errbuf != NULL )
|
||||
*errbuf = mmdsr.unpack_str();
|
||||
|
||||
qfree(rp);
|
||||
return rc;
|
||||
}
|
||||
37
idasdk76/dbg/dbg_rpc_engine.h
Normal file
37
idasdk76/dbg/dbg_rpc_engine.h
Normal file
@@ -0,0 +1,37 @@
|
||||
#ifndef __DBG_RPC_ENGINE__
|
||||
#define __DBG_RPC_ENGINE__
|
||||
|
||||
#include <pro.h>
|
||||
#include <idd.hpp>
|
||||
#include <network.hpp>
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
class dbg_rpc_engine_t : public rpc_engine_t
|
||||
{
|
||||
public:
|
||||
bool has_pending_event;
|
||||
bool poll_debug_events;
|
||||
|
||||
dbg_rpc_engine_t(bool _is_client);
|
||||
|
||||
#define PREQ_MUST_LOGIN 0x01
|
||||
#define PREQ_GET_EVENT 0x02
|
||||
rpc_packet_t *send_request_and_receive_reply(bytevec_t &pkt, int flags);
|
||||
|
||||
virtual rpc_packet_t *send_request_and_receive_reply(bytevec_t &pkt) override
|
||||
{
|
||||
return send_request_and_receive_reply(pkt, 0);
|
||||
}
|
||||
|
||||
protected:
|
||||
int send_request_get_long_result(bytevec_t &pkt) { return _send_request_get_int_result(pkt, -1, NULL); }
|
||||
drc_t send_request_get_drc_result(bytevec_t &pkt, qstring *errbuf) { return drc_t(_send_request_get_int_result(pkt, DRC_NETERR, errbuf)); }
|
||||
|
||||
virtual bytevec_t on_send_request_interrupt(const rpc_packet_t *rp) = 0;
|
||||
virtual void on_send_request_end(const rpc_packet_t *result) newapi { qnotused(result); }
|
||||
|
||||
private:
|
||||
int _send_request_get_int_result(bytevec_t &pkt, int failure_code, qstring *errbuf);
|
||||
};
|
||||
|
||||
#endif // __DBG_RPC_ENGINE__
|
||||
1423
idasdk76/dbg/dbg_rpc_handler.cpp
Normal file
1423
idasdk76/dbg/dbg_rpc_handler.cpp
Normal file
File diff suppressed because it is too large
Load Diff
65
idasdk76/dbg/dbg_rpc_handler.h
Normal file
65
idasdk76/dbg/dbg_rpc_handler.h
Normal file
@@ -0,0 +1,65 @@
|
||||
#ifndef __DBG_RPC_HANDLER__
|
||||
#define __DBG_RPC_HANDLER__
|
||||
|
||||
#include <network.hpp>
|
||||
|
||||
#include "dbg_rpc_engine.h"
|
||||
#define MIN_SERVER_IOCTL 0x01000000
|
||||
|
||||
#include "debmod.h"
|
||||
|
||||
struct dbgsrv_dispatcher_t;
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
class dbg_rpc_handler_t
|
||||
: public client_handler_t,
|
||||
public dbg_rpc_engine_t
|
||||
{
|
||||
public:
|
||||
dbg_rpc_handler_t(idarpc_stream_t *_irs, dbgsrv_dispatcher_t *_dispatcher);
|
||||
virtual ~dbg_rpc_handler_t();
|
||||
|
||||
virtual bool handle() override;
|
||||
virtual void shutdown_gracefully(int signum) override;
|
||||
|
||||
private:
|
||||
debug_event_t ev;
|
||||
debug_event_t pending_event;
|
||||
debmod_t *dbg_mod;
|
||||
dbgsrv_dispatcher_t *dispatcher;
|
||||
DECLARE_UNCOPYABLE(dbg_rpc_handler_t);
|
||||
void append_start_or_attach(bytevec_t &req, drc_t drc, const qstring &errbuf) const;
|
||||
int poll_events(int timeout_ms);
|
||||
void extract_path_and_arch(
|
||||
const char **out_file_path,
|
||||
int *out_arch,
|
||||
int *out_is_be,
|
||||
memory_deserializer_t &mmdsr) const;
|
||||
int on_recv_packet_progress(bool *performed);
|
||||
|
||||
protected:
|
||||
void rpc_update_lowcnds(bytevec_t &rcmd, memory_deserializer_t &mmdsr);
|
||||
int rpc_update_bpts(bytevec_t &rcmd, memory_deserializer_t &mmdsr);
|
||||
drc_t rpc_attach_process(qstring *errbuf, memory_deserializer_t &mmdsr);
|
||||
bool check_broken_connection(pid_t pid);
|
||||
virtual int handle_server_ioctl(int fn, void **out, ssize_t *outsz, memory_deserializer_t &mmdsr) newapi;
|
||||
virtual bytevec_t on_send_request_interrupt(const rpc_packet_t *rp) override;
|
||||
|
||||
public:
|
||||
void set_debugger_instance(debmod_t *instance);
|
||||
debmod_t *get_debugger_instance();
|
||||
void prepare_broken_connection();
|
||||
bool rpc_sync_stub(const char *server_stub_name, const char *ida_stub_name);
|
||||
int send_debug_names_to_ida(ea_t *ea, const char *const *names, int qty);
|
||||
int send_debug_event_to_ida(const debug_event_t *ev, int rqflags);
|
||||
void process_import_requests(const import_infos_t &infos);
|
||||
virtual idarpc_stream_t *get_irs() const override { return irs; }
|
||||
virtual bool get_broken_connection(void) override;
|
||||
virtual void set_broken_connection(void) override;
|
||||
int kill_process(void); // returns 0-ok, >0-failed, after that many seconds
|
||||
};
|
||||
|
||||
// defined only in the single threaded version of the server:
|
||||
extern dbg_rpc_handler_t *g_global_server;
|
||||
|
||||
#endif // __DBG_RPC_HANDLER__
|
||||
70
idasdk76/dbg/dbg_rpc_handler_ioctls.h
Normal file
70
idasdk76/dbg/dbg_rpc_handler_ioctls.h
Normal file
@@ -0,0 +1,70 @@
|
||||
#ifndef __DBG_RPC_HANDLER_IOCTLS__
|
||||
#define __DBG_RPC_HANDLER_IOCTLS__
|
||||
|
||||
// Note: the dbg_rpc_handler_t implementation will consider all
|
||||
// IOCTL IDs >= 0x01000000 as being server IOCTLs, and those will
|
||||
// consequently *not* be passed to the debugger module.
|
||||
#define MIN_SERVER_IOCTL 0x01000000
|
||||
|
||||
#define DWARF_RPCSRV_IOCTL_OK 0
|
||||
#define DWARF_RPCSRV_IOCTL_ERROR -1
|
||||
|
||||
enum rpcsrv_ioctl_t
|
||||
{
|
||||
// Get DWARF sections information.
|
||||
//
|
||||
// client->server
|
||||
// (unpacked) char * : file_path (on the server's disk.)
|
||||
// (packed) uint32 : processor ID (as in: ph.id)
|
||||
// server->client
|
||||
// (unpacked) byte : DWARF info found
|
||||
// (packed) uint32 : is_64 (0 - no, !=0 - yes)
|
||||
// (packed) uint32 : is_msb (0 - no, !=0 - yes)
|
||||
// (packed) uint64 : declared load address
|
||||
// (packed) uint32 : number of DWARF section infos.
|
||||
// (packed) sec info : DWARF section info, N times.
|
||||
// Returns: 0 - ok
|
||||
// !=0 - error (text in output buffer.)
|
||||
//
|
||||
// The structure of a "sec info" is:
|
||||
// (packed) uint64 address_in_memory
|
||||
// (packed) uint64 size (in bytes)
|
||||
// (packed) uint16 section_index
|
||||
// (unpacked) char * section_name
|
||||
rpcsrv_ioctl_dwarf_secinfo = MIN_SERVER_IOCTL + 1,
|
||||
|
||||
// Get DWARF section data.
|
||||
//
|
||||
// client->server
|
||||
// (unpacked) char * : file_path (on the server's disk.)
|
||||
// (packed) uint32 : processor ID (as in: ph.id)
|
||||
// (packed) uint16 : DWARF section index (as returned by 'rpcsrv_ioctl_dwarf_secinfo')
|
||||
// server->client
|
||||
// (unpacked) byte * : DWARF section data.
|
||||
// Returns: 0 - ok
|
||||
// !=0 - error
|
||||
rpcsrv_ioctl_dwarf_secdata,
|
||||
|
||||
#if defined(TESTABLE_BUILD)
|
||||
// Set path to look for ELF/DWARF companion files, per-PID
|
||||
//
|
||||
// This is strictly meant for testing, where tests can store
|
||||
// files in unusual places on the leasing debug server's volume.
|
||||
//
|
||||
// client->server
|
||||
// (packed) uint32 : the PID
|
||||
// (unpacked) char * : directory path (on the server's disk.)
|
||||
// server->client
|
||||
// Nothing
|
||||
// Returns: 0 - ok
|
||||
// != 0 - error
|
||||
rpcsrv_ioctl_set_elf_debug_file_directory_for_pid = 2 * MIN_SERVER_IOCTL + 1,
|
||||
#endif
|
||||
};
|
||||
|
||||
#if defined(TESTABLE_BUILD) && defined(__LINUX__)
|
||||
void set_elf_debug_file_directory_for_pid(int pid, const char *path);
|
||||
const char *get_elf_debug_file_directory_for_pid(int pid);
|
||||
#endif
|
||||
|
||||
#endif // __DBG_RPC_HANDLER_IOCTLS__
|
||||
367
idasdk76/dbg/dbg_rpc_hlp.cpp
Normal file
367
idasdk76/dbg/dbg_rpc_hlp.cpp
Normal file
@@ -0,0 +1,367 @@
|
||||
|
||||
#include <segment.hpp>
|
||||
#include <typeinf.hpp>
|
||||
|
||||
#include "dbg_rpc_hlp.h"
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
void append_memory_info(bytevec_t &s, const memory_info_t *meminf)
|
||||
{
|
||||
s.pack_ea64(meminf->sbase);
|
||||
s.pack_ea64(meminf->start_ea - (meminf->sbase << 4));
|
||||
s.pack_ea64(meminf->size());
|
||||
s.pack_dd(meminf->perm | (meminf->bitness<<4));
|
||||
s.pack_str(meminf->name.c_str());
|
||||
s.pack_str(meminf->sclass.c_str());
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
void extract_memory_info(memory_info_t *meminf, memory_deserializer_t &mmdsr)
|
||||
{
|
||||
meminf->sbase = mmdsr.unpack_ea64();
|
||||
meminf->start_ea = ea_t(meminf->sbase << 4) + mmdsr.unpack_ea64();
|
||||
meminf->end_ea = meminf->start_ea + mmdsr.unpack_ea64();
|
||||
int v = mmdsr.unpack_dd();
|
||||
meminf->perm = uchar(v) & SEGPERM_MAXVAL;
|
||||
meminf->bitness = uchar(v>>4);
|
||||
meminf->name = mmdsr.unpack_str();
|
||||
meminf->sclass = mmdsr.unpack_str();
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
void append_scattered_segm(bytevec_t &s, const scattered_segm_t *ss)
|
||||
{
|
||||
s.pack_ea64(ss->start_ea);
|
||||
s.pack_ea64(ss->end_ea);
|
||||
s.pack_str(ss->name.c_str());
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
void extract_scattered_segm(scattered_segm_t *ss, memory_deserializer_t &mmdsr)
|
||||
{
|
||||
ss->start_ea = mmdsr.unpack_ea64();
|
||||
ss->end_ea = mmdsr.unpack_ea64();
|
||||
ss->name = mmdsr.unpack_str();
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
void append_process_info_vec(bytevec_t &s, const procinfo_vec_t *procs)
|
||||
{
|
||||
size_t size = procs->size();
|
||||
s.pack_dd(size);
|
||||
for ( size_t i = 0; i < size; i++ )
|
||||
{
|
||||
const process_info_t &pi = procs->at(i);
|
||||
s.pack_dd(pi.pid);
|
||||
s.pack_str(pi.name.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
void extract_process_info_vec(procinfo_vec_t *procs, memory_deserializer_t &mmdsr)
|
||||
{
|
||||
size_t size = mmdsr.unpack_dd();
|
||||
for ( size_t i = 0; i < size; i++ )
|
||||
{
|
||||
process_info_t &pi = procs->push_back();
|
||||
pi.pid = mmdsr.unpack_dd();
|
||||
pi.name = mmdsr.unpack_str();
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
void append_module_info(bytevec_t &s, const modinfo_t *modinf)
|
||||
{
|
||||
s.pack_str(modinf->name);
|
||||
s.pack_ea64(modinf->base);
|
||||
s.pack_ea64(modinf->size);
|
||||
s.pack_ea64(modinf->rebase_to);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
void extract_module_info(modinfo_t *modinf, memory_deserializer_t &mmdsr)
|
||||
{
|
||||
modinf->name = mmdsr.unpack_str();
|
||||
modinf->base = mmdsr.unpack_ea64();
|
||||
modinf->size = mmdsr.unpack_ea64();
|
||||
modinf->rebase_to = mmdsr.unpack_ea64();
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
void append_exception(bytevec_t &s, const excinfo_t *e)
|
||||
{
|
||||
s.pack_dd(e->code);
|
||||
s.pack_dd(e->can_cont);
|
||||
s.pack_ea64(e->ea);
|
||||
s.pack_str(e->info);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
void extract_exception(excinfo_t *exc, memory_deserializer_t &mmdsr)
|
||||
{
|
||||
exc->code = mmdsr.unpack_dd();
|
||||
exc->can_cont = mmdsr.unpack_dd() != 0;
|
||||
exc->ea = mmdsr.unpack_ea64();
|
||||
exc->info = mmdsr.unpack_str();
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
void extract_debug_event(debug_event_t *ev, memory_deserializer_t &mmdsr)
|
||||
{
|
||||
ev->set_eid(event_id_t(mmdsr.unpack_dd()));
|
||||
ev->pid = mmdsr.unpack_dd();
|
||||
ev->tid = mmdsr.unpack_dd();
|
||||
ev->ea = mmdsr.unpack_ea64();
|
||||
ev->handled = mmdsr.unpack_dd() != 0;
|
||||
switch ( ev->eid() )
|
||||
{
|
||||
case NO_EVENT: // Not an interesting event
|
||||
case STEP: // One instruction executed
|
||||
case PROCESS_DETACHED: // Detached from process
|
||||
default:
|
||||
break;
|
||||
case PROCESS_STARTED: // New process started
|
||||
case PROCESS_ATTACHED: // Attached to running process
|
||||
case LIB_LOADED: // New library loaded
|
||||
extract_module_info(&ev->modinfo(), mmdsr);
|
||||
break;
|
||||
case PROCESS_EXITED: // Process stopped
|
||||
case THREAD_EXITED: // Thread stopped
|
||||
ev->exit_code() = mmdsr.unpack_dd();
|
||||
break;
|
||||
case BREAKPOINT: // Breakpoint reached
|
||||
extract_breakpoint(&ev->bpt(), mmdsr);
|
||||
break;
|
||||
case EXCEPTION: // Exception
|
||||
extract_exception(&ev->exc(), mmdsr);
|
||||
break;
|
||||
case THREAD_STARTED: // New thread started
|
||||
case LIB_UNLOADED: // Library unloaded
|
||||
case INFORMATION: // User-defined information
|
||||
ev->info() = mmdsr.unpack_str();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
void append_debug_event(bytevec_t &s, const debug_event_t *ev)
|
||||
{
|
||||
s.pack_dd(ev->eid());
|
||||
s.pack_dd(ev->pid);
|
||||
s.pack_dd(ev->tid);
|
||||
s.pack_ea64(ev->ea);
|
||||
s.pack_dd(ev->handled);
|
||||
switch ( ev->eid() )
|
||||
{
|
||||
case NO_EVENT: // Not an interesting event
|
||||
case STEP: // One instruction executed
|
||||
case PROCESS_DETACHED: // Detached from process
|
||||
default:
|
||||
break;
|
||||
case PROCESS_STARTED: // New process started
|
||||
case PROCESS_ATTACHED: // Attached to running process
|
||||
case LIB_LOADED: // New library loaded
|
||||
append_module_info(s, &ev->modinfo());
|
||||
break;
|
||||
case PROCESS_EXITED: // Process stopped
|
||||
case THREAD_EXITED: // Thread stopped
|
||||
s.pack_dd(ev->exit_code());
|
||||
break;
|
||||
case BREAKPOINT: // Breakpoint reached
|
||||
append_breakpoint(s, &ev->bpt());
|
||||
break;
|
||||
case EXCEPTION: // Exception
|
||||
append_exception(s, &ev->exc());
|
||||
break;
|
||||
case THREAD_STARTED: // New thread started
|
||||
case LIB_UNLOADED: // Library unloaded
|
||||
case INFORMATION: // User-defined information
|
||||
s.pack_str(ev->info());
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
exception_info_t *extract_exception_info(int qty, memory_deserializer_t &mmdsr)
|
||||
{
|
||||
exception_info_t *extable = NULL;
|
||||
if ( qty > 0 )
|
||||
{
|
||||
extable = OPERATOR_NEW(exception_info_t, qty);
|
||||
for ( int i=0; i < qty; i++ )
|
||||
{
|
||||
extable[i].code = mmdsr.unpack_dd();
|
||||
extable[i].flags = mmdsr.unpack_dd();
|
||||
extable[i].name = mmdsr.unpack_str();
|
||||
extable[i].desc = mmdsr.unpack_str();
|
||||
}
|
||||
}
|
||||
return extable;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
void append_exception_info(bytevec_t &s, const exception_info_t *table, int qty)
|
||||
{
|
||||
for ( int i=0; i < qty; i++ )
|
||||
{
|
||||
s.pack_dd(table[i].code);
|
||||
s.pack_dd(table[i].flags);
|
||||
s.pack_str(table[i].name.c_str());
|
||||
s.pack_str(table[i].desc.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
void extract_call_stack(call_stack_t *trace, memory_deserializer_t &mmdsr)
|
||||
{
|
||||
int n = mmdsr.unpack_dd();
|
||||
trace->resize(n);
|
||||
for ( int i=0; i < n; i++ )
|
||||
{
|
||||
call_stack_info_t &ci = (*trace)[i];
|
||||
ci.callea = mmdsr.unpack_ea64();
|
||||
ci.funcea = mmdsr.unpack_ea64();
|
||||
ci.fp = mmdsr.unpack_ea64();
|
||||
ci.funcok = mmdsr.unpack_dd() != 0;
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
void append_call_stack(bytevec_t &s, const call_stack_t &trace)
|
||||
{
|
||||
int n = trace.size();
|
||||
s.pack_dd(n);
|
||||
for ( int i=0; i < n; i++ )
|
||||
{
|
||||
const call_stack_info_t &ci = trace[i];
|
||||
s.pack_ea64(ci.callea);
|
||||
s.pack_ea64(ci.funcea);
|
||||
s.pack_ea64(ci.fp);
|
||||
s.pack_dd(ci.funcok);
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
void extract_regobjs(
|
||||
regobjs_t *regargs,
|
||||
bool with_values,
|
||||
memory_deserializer_t &mmdsr)
|
||||
{
|
||||
int n = mmdsr.unpack_dd();
|
||||
regargs->resize(n);
|
||||
for ( int i=0; i < n; i++ )
|
||||
{
|
||||
regobj_t &ro = (*regargs)[i];
|
||||
ro.regidx = mmdsr.unpack_dd();
|
||||
int size = mmdsr.unpack_dd();
|
||||
ro.value.resize(size);
|
||||
if ( with_values )
|
||||
{
|
||||
ro.relocate = mmdsr.unpack_dd();
|
||||
mmdsr.unpack_obj(ro.value.begin(), size);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
static void extract_relobj(relobj_t *stkargs, memory_deserializer_t &mmdsr)
|
||||
{
|
||||
int n = mmdsr.unpack_dd();
|
||||
stkargs->resize(n);
|
||||
mmdsr.unpack_obj(stkargs->begin(), n);
|
||||
|
||||
stkargs->base = mmdsr.unpack_ea64();
|
||||
|
||||
n = mmdsr.unpack_dd();
|
||||
stkargs->ri.resize(n);
|
||||
mmdsr.unpack_obj(stkargs->ri.begin(), n);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
void extract_appcall(
|
||||
regobjs_t *regargs,
|
||||
relobj_t *stkargs,
|
||||
regobjs_t *retregs,
|
||||
memory_deserializer_t &mmdsr)
|
||||
{
|
||||
extract_regobjs(regargs, true, mmdsr);
|
||||
extract_relobj(stkargs, mmdsr);
|
||||
if ( retregs != NULL )
|
||||
extract_regobjs(retregs, false, mmdsr);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
void append_regobjs(bytevec_t &s, const regobjs_t ®args, bool with_values)
|
||||
{
|
||||
s.pack_dd(regargs.size());
|
||||
for ( size_t i=0; i < regargs.size(); i++ )
|
||||
{
|
||||
const regobj_t &ro = regargs[i];
|
||||
s.pack_dd(ro.regidx);
|
||||
s.pack_dd(ro.value.size());
|
||||
if ( with_values )
|
||||
{
|
||||
s.pack_dd(ro.relocate);
|
||||
s.append(ro.value.begin(), ro.value.size());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
static void append_relobj(bytevec_t &s, const relobj_t &stkargs)
|
||||
{
|
||||
s.pack_buf(stkargs.begin(), stkargs.size());
|
||||
s.pack_ea64(stkargs.base);
|
||||
s.pack_buf(stkargs.ri.begin(), stkargs.ri.size());
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
void append_appcall(
|
||||
bytevec_t &s,
|
||||
const regobjs_t ®args,
|
||||
const relobj_t &stkargs,
|
||||
const regobjs_t *retregs)
|
||||
{
|
||||
append_regobjs(s, regargs, true);
|
||||
append_relobj(s, stkargs);
|
||||
if ( retregs != NULL )
|
||||
append_regobjs(s, *retregs, false);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
void append_regvals(bytevec_t &s, const regval_t *values, int n, const uchar *regmap)
|
||||
{
|
||||
for ( int i=0; i < n; i++ )
|
||||
if ( regmap == NULL || test_bit(regmap, i) )
|
||||
append_regval(s, values[i]);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
void extract_debapp_attrs(debapp_attrs_t *attrs, memory_deserializer_t &mmdsr)
|
||||
{
|
||||
attrs->addrsize = mmdsr.unpack_dd();
|
||||
attrs->platform = mmdsr.unpack_str();
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
void append_debapp_attrs(bytevec_t &s, const debapp_attrs_t &attrs)
|
||||
{
|
||||
s.pack_dd(attrs.addrsize);
|
||||
s.pack_str(attrs.platform.c_str());
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
void extract_dynamic_register_set(
|
||||
dynamic_register_set_t *idaregs,
|
||||
memory_deserializer_t &mmdsr)
|
||||
{
|
||||
deserialize_dynamic_register_set(idaregs, mmdsr);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
void append_dynamic_register_set(
|
||||
bytevec_t &s,
|
||||
dynamic_register_set_t &idaregs)
|
||||
{
|
||||
serialize_dynamic_register_set(&s, idaregs);
|
||||
}
|
||||
80
idasdk76/dbg/dbg_rpc_hlp.h
Normal file
80
idasdk76/dbg/dbg_rpc_hlp.h
Normal file
@@ -0,0 +1,80 @@
|
||||
#ifndef __DBG_RPC_HLP__
|
||||
#define __DBG_RPC_HLP__
|
||||
|
||||
#include <pro.h>
|
||||
#include <range.hpp>
|
||||
#include <idd.hpp>
|
||||
#include <network.hpp>
|
||||
|
||||
void append_regvals(bytevec_t &s, const regval_t *values, int n, const uchar *regmap);
|
||||
|
||||
void append_debug_event(bytevec_t &s, const debug_event_t *ev);
|
||||
void extract_debug_event(debug_event_t *ev, memory_deserializer_t &mmdsr);
|
||||
|
||||
void extract_exception(excinfo_t *exc, memory_deserializer_t &mmdsr);
|
||||
void append_exception(bytevec_t &s, const excinfo_t *e);
|
||||
|
||||
inline void append_breakpoint(bytevec_t &s, const bptaddr_t *info)
|
||||
{
|
||||
s.pack_ea64(info->hea);
|
||||
s.pack_ea64(info->kea);
|
||||
}
|
||||
inline void extract_breakpoint(bptaddr_t *info, memory_deserializer_t &mmdsr)
|
||||
{
|
||||
info->hea = mmdsr.unpack_ea64();
|
||||
info->kea = mmdsr.unpack_ea64();
|
||||
}
|
||||
void extract_module_info(modinfo_t *info, memory_deserializer_t &mmdsr);
|
||||
void append_module_info(bytevec_t &s, const modinfo_t *info);
|
||||
void extract_process_info_vec(procinfo_vec_t *procs, memory_deserializer_t &mmdsr);
|
||||
void append_process_info_vec(bytevec_t &s, const procinfo_vec_t *procs);
|
||||
|
||||
void extract_call_stack(call_stack_t *trace, memory_deserializer_t &mmdsr);
|
||||
void append_call_stack(bytevec_t &s, const call_stack_t &trace);
|
||||
|
||||
void extract_regobjs(regobjs_t *regargs, bool with_values, memory_deserializer_t &mmdsr);
|
||||
void append_regobjs(bytevec_t &s, const regobjs_t ®args, bool with_values);
|
||||
|
||||
void extract_appcall(
|
||||
regobjs_t *regargs,
|
||||
relobj_t *stkargs,
|
||||
regobjs_t *retregs,
|
||||
memory_deserializer_t &mmdsr);
|
||||
|
||||
void append_appcall(
|
||||
bytevec_t &s,
|
||||
const regobjs_t ®args,
|
||||
const relobj_t &stkargs,
|
||||
const regobjs_t *retregs);
|
||||
|
||||
void extract_debapp_attrs(
|
||||
debapp_attrs_t *attrs,
|
||||
memory_deserializer_t &mmdsr);
|
||||
void append_debapp_attrs(bytevec_t &s, const debapp_attrs_t &attrs);
|
||||
|
||||
void extract_dynamic_register_set(
|
||||
dynamic_register_set_t *idaregs,
|
||||
memory_deserializer_t &mmdsr);
|
||||
void append_dynamic_register_set(
|
||||
bytevec_t &s,
|
||||
dynamic_register_set_t &idaregs);
|
||||
|
||||
|
||||
inline void append_type(bytevec_t &s, const type_t *str)
|
||||
{
|
||||
s.pack_str((char *)str);
|
||||
}
|
||||
void append_type(bytevec_t &s, const tinfo_t &tif);
|
||||
void extract_type(tinfo_t *tif, memory_deserializer_t &mmdsr);
|
||||
|
||||
void extract_memory_info(memory_info_t *info, memory_deserializer_t &mmdsr);
|
||||
void append_memory_info(bytevec_t &s, const memory_info_t *info);
|
||||
|
||||
void extract_scattered_segm(scattered_segm_t *ss, memory_deserializer_t &mmdsr);
|
||||
void append_scattered_segm(bytevec_t &s, const scattered_segm_t *ss);
|
||||
|
||||
void append_exception_info(bytevec_t &s, const exception_info_t *table, int qty);
|
||||
exception_info_t *extract_exception_info(int qty, memory_deserializer_t &mmdsr);
|
||||
|
||||
|
||||
#endif // __DBG_RPC_HLP__
|
||||
22
idasdk76/dbg/deb_arm.hpp
Normal file
22
idasdk76/dbg/deb_arm.hpp
Normal file
@@ -0,0 +1,22 @@
|
||||
#ifndef __DEB_ARM__
|
||||
#define __DEB_ARM__
|
||||
|
||||
#include <ua.hpp>
|
||||
#include <idd.hpp>
|
||||
|
||||
#define MEMORY_PAGE_SIZE 0x1000
|
||||
#define ARM_BPT_CODE { 0xF0, 0x01, 0xF0, 0xE7 } // und #10
|
||||
#define AARCH64_BPT_CODE { 0x00, 0x00, 0x20, 0xD4 } // brk #0
|
||||
|
||||
#define ARM_BPT_SIZE 4 // size of BPT instruction
|
||||
|
||||
#define ARM_T 20 // number of virtual T segment register in IDA
|
||||
// it controls thumb/arm mode.
|
||||
|
||||
#define ARM_MAX_HWBPTS 16
|
||||
#define ARM_MAX_WATCHPOINTS 16
|
||||
|
||||
int is_arm_valid_bpt(bpttype_t type, ea_t ea, int len);
|
||||
|
||||
#endif
|
||||
|
||||
47
idasdk76/dbg/deb_pc.hpp
Normal file
47
idasdk76/dbg/deb_pc.hpp
Normal file
@@ -0,0 +1,47 @@
|
||||
#ifndef __DEB_PC__
|
||||
#define __DEB_PC__
|
||||
|
||||
#include <ua.hpp>
|
||||
#include <range.hpp>
|
||||
#include <idd.hpp>
|
||||
|
||||
#define MEMORY_PAGE_SIZE 0x1000
|
||||
#define X86_BPT_CODE { 0xCC }
|
||||
#define MAX_BPT 4 // maximal number of hardware breakpoints
|
||||
#define X86_BPT_SIZE 1 // size of 0xCC instruction
|
||||
#define EFLAGS_TRAP_FLAG 0x00000100
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
drc_t idaapi x86_read_registers(thid_t thread_id, int clsmask, regval_t *values, qstring *errbuf);
|
||||
drc_t idaapi x86_write_register(thid_t thread_id, int regidx, const regval_t *value, qstring *errbuf);
|
||||
int is_x86_valid_bpt(bpttype_t type, ea_t ea, int len);
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
inline int check_x86_hwbpt(bpttype_t type, ea_t ea, int len)
|
||||
{
|
||||
if ( type != BPT_RDWR // type is good?
|
||||
&& type != BPT_WRITE
|
||||
&& type != BPT_EXEC )
|
||||
{
|
||||
return BPT_BAD_TYPE;
|
||||
}
|
||||
|
||||
if ( len != 1 // is length good?
|
||||
&& (type == BPT_EXEC // instruction hardware breakpoint only accepts the len of one byte
|
||||
|| (len != 2 && len != 4
|
||||
#ifndef __X86__
|
||||
&& len != 8
|
||||
#endif
|
||||
)) )
|
||||
{
|
||||
return BPT_BAD_LEN;
|
||||
}
|
||||
|
||||
if ( (ea & (len-1)) != 0 ) // is alignment good?
|
||||
return BPT_BAD_ALIGN;
|
||||
|
||||
return BPT_OK;
|
||||
}
|
||||
|
||||
|
||||
#endif
|
||||
1333
idasdk76/dbg/debmod.cpp
Normal file
1333
idasdk76/dbg/debmod.cpp
Normal file
File diff suppressed because it is too large
Load Diff
739
idasdk76/dbg/debmod.h
Normal file
739
idasdk76/dbg/debmod.h
Normal file
@@ -0,0 +1,739 @@
|
||||
#ifndef __DEBUGGER_MODULE__
|
||||
#define __DEBUGGER_MODULE__
|
||||
|
||||
//
|
||||
//
|
||||
// This is the base debmod_t class definition
|
||||
// From this class all debugger code must inherite and specialize
|
||||
//
|
||||
// Some OS specific functions must be implemented:
|
||||
// bool init_subsystem();
|
||||
// bool term_subsystem();
|
||||
// debmod_t *create_debug_session(void *);
|
||||
// int create_thread(thread_cb_t thread_cb, void *context);
|
||||
//
|
||||
|
||||
#include <deque>
|
||||
|
||||
#include <pro.h>
|
||||
#include <idd.hpp>
|
||||
#include <network.hpp>
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
extern debugger_t debugger;
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
struct name_info_t
|
||||
{
|
||||
eavec_t addrs;
|
||||
qvector<char *> names;
|
||||
void clear(void)
|
||||
{
|
||||
addrs.clear();
|
||||
names.clear();
|
||||
}
|
||||
};
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// Extended process info
|
||||
struct ext_process_info_t : public process_info_t
|
||||
{
|
||||
int addrsize; // process bitness 32bit - 4, 64bit - 8, 0 - unknown
|
||||
qstring ext_name; // human-readable name (e.g. with command line agrs)
|
||||
void copy_to(process_info_t *dst)
|
||||
{
|
||||
dst->pid = pid;
|
||||
dst->name = ext_name.empty() ? name : ext_name;
|
||||
}
|
||||
};
|
||||
typedef qvector<ext_process_info_t> procvec_t;
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// Very simple class to store pending events
|
||||
enum queue_pos_t
|
||||
{
|
||||
IN_FRONT,
|
||||
IN_BACK
|
||||
};
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
struct pagebpt_data_t
|
||||
{
|
||||
ea_t ea; // address of the bpt as specified by the user
|
||||
ea_t page_ea; // real address of the bpt as written to the process
|
||||
int user_len; // breakpoint length as specified by the user
|
||||
int aligned_len; // breakpoint length aligned to the page size
|
||||
int real_len; // real length of the breakpoint as written to the process
|
||||
uint32 old_prot; // old page protections (before writing the bpt to the process)
|
||||
// if 0, the bpt has not been written to the process yet.
|
||||
uint32 new_prot; // new page protections (when the bpt is active)
|
||||
bpttype_t type; // breakpoint type
|
||||
};
|
||||
|
||||
// Information about page breakpoints is stored in this data structure.
|
||||
// The map is indexed by the page start address (not the address specified
|
||||
// by the user!)
|
||||
typedef std::map<ea_t, pagebpt_data_t> page_bpts_t; // page_ea -> bpt info
|
||||
typedef qvector<page_bpts_t::iterator> pbpt_iterators_t; // list of iterators into page_bpts_t
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// set of addresses
|
||||
typedef std::set<ea_t> easet_t;
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
class idc_value_t;
|
||||
class rpc_engine_t;
|
||||
error_t idaapi idc_get_reg_value(idc_value_t *argv, idc_value_t *r);
|
||||
error_t idaapi idc_set_reg_value(idc_value_t *argv, idc_value_t *r);
|
||||
void report_idc_error(rpc_engine_t *rpc, ea_t ea, error_t code, ssize_t errval, const char *errprm);
|
||||
|
||||
// IDC function name that is exported by a debugger module
|
||||
// to allow scripts to send debugger commands
|
||||
#define IDC_SENDDBG_CMD "send_dbg_command"
|
||||
#define IDC_READ_MSR "read_msr"
|
||||
#define IDC_WRITE_MSR "write_msr"
|
||||
#define IDC_STEP_BACK "step_back"
|
||||
#define IDC_SET_TEV "set_current_tev"
|
||||
#define IDC_GET_TEV "get_current_tev"
|
||||
|
||||
// A macro to convert a pointer to ea_t without sign extension.
|
||||
#define EA_T(ptr) (ea_t)(size_t)(ptr)
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
//-V:debmod_bpt_t:730 not all members of a class are initialized inside the constructor: saved
|
||||
struct debmod_bpt_t
|
||||
{
|
||||
ea_t ea;
|
||||
uchar saved[8]; // size of the biggest supported bpt size (PPC64)
|
||||
uchar nsaved;
|
||||
int bid; // (epoc) breakpoint id (from TRK)
|
||||
debmod_bpt_t() : ea(BADADDR), nsaved(0), bid(0) {}
|
||||
debmod_bpt_t(ea_t _ea, uchar _nsaved) : ea(_ea), nsaved(_nsaved), bid(0) { QASSERT(1796, nsaved < sizeof(saved)); }
|
||||
};
|
||||
typedef std::map<ea_t, debmod_bpt_t> debmodbpt_map_t;
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
struct eventlist_t : public std::deque<debug_event_t>
|
||||
{
|
||||
public:
|
||||
// save a pending event
|
||||
void enqueue(const debug_event_t &ev, queue_pos_t pos)
|
||||
{
|
||||
if ( pos != IN_BACK )
|
||||
push_front(ev);
|
||||
else
|
||||
push_back(ev);
|
||||
}
|
||||
|
||||
// retrieve a pending event
|
||||
bool retrieve(debug_event_t *event)
|
||||
{
|
||||
if ( empty() )
|
||||
return false;
|
||||
// get the first event and return it
|
||||
*event = front();
|
||||
pop_front();
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
int send_ioctl(rpc_engine_t *rpc, int fn, const void *buf, size_t size, void **poutbuf, ssize_t *poutsize);
|
||||
int send_debug_names_to_ida(ea_t *addrs, const char *const *names, int qty);
|
||||
int send_debug_event_to_ida(const debug_event_t *ev, int rqflags);
|
||||
void set_arm_thumb_modes(ea_t *addrs, int qty);
|
||||
char *debug_event_str(const debug_event_t *ev, char *buf, size_t bufsize);
|
||||
char *debug_event_str(const debug_event_t *ev); // returns static buf
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// describes a dll to be imported
|
||||
struct import_request_t
|
||||
{
|
||||
ea_t base;
|
||||
qstring path;
|
||||
bytevec_t uuid;
|
||||
import_request_t(ea_t _base, const qstring &_path, const bytevec_t &_uuid)
|
||||
: base(_base), path(_path), uuid(_uuid) {}
|
||||
};
|
||||
DECLARE_TYPE_AS_MOVABLE(import_request_t);
|
||||
typedef qvector<import_request_t> import_infos_t;
|
||||
int import_dll(const import_request_t &req);
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
struct regctx_t;
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
struct regctx_entry_t
|
||||
{
|
||||
enum type_t
|
||||
{
|
||||
IVAL = 0,
|
||||
FVAL = 1,
|
||||
DATA = 2,
|
||||
FUNC = 3,
|
||||
} type;
|
||||
int reg_class;
|
||||
int reg_idx;
|
||||
union
|
||||
{
|
||||
struct
|
||||
{
|
||||
size_t offset_in_ctx;
|
||||
size_t size_in_ctx;
|
||||
size_t reg_size;
|
||||
};
|
||||
struct
|
||||
{
|
||||
void (*read_func)(const regctx_t *, regval_t *, void *);
|
||||
void (*write_func)(regctx_t *, const regval_t *, void *);
|
||||
void *user_data;
|
||||
};
|
||||
};
|
||||
|
||||
uint64_t truncate_ival(uint64_t ival) const
|
||||
{
|
||||
switch ( reg_size )
|
||||
{
|
||||
case 1: ival = uint8_t(ival); break;
|
||||
case 2: ival = uint16_t(ival); break;
|
||||
case 4: ival = uint32_t(ival); break;
|
||||
}
|
||||
return ival;
|
||||
}
|
||||
|
||||
void read(regval_t *value, const uint8_t *ptr) const
|
||||
{
|
||||
if ( type == regctx_entry_t::FUNC )
|
||||
{
|
||||
read_func((regctx_t *) ptr, value, user_data);
|
||||
}
|
||||
else
|
||||
{
|
||||
const uint8_t *p = ptr + offset_in_ctx;
|
||||
switch ( type )
|
||||
{
|
||||
case regctx_entry_t::IVAL:
|
||||
{
|
||||
uint64_t ival = 0;
|
||||
switch ( size_in_ctx )
|
||||
{
|
||||
case 1: ival = *(uint8_t *)p; break;
|
||||
case 2: ival = *(uint16_t *)p; break;
|
||||
case 4: ival = *(uint32_t *)p; break;
|
||||
case 8: ival = *(uint64_t *)p; break;
|
||||
}
|
||||
ival = truncate_ival(ival);
|
||||
value->ival = ival;
|
||||
}
|
||||
break;
|
||||
case regctx_entry_t::FVAL:
|
||||
// TODO: for x86, the value is converted to IEEE format in
|
||||
// x86_read_registers(). clean this up somehow.
|
||||
memcpy(&value->fval, p, size_in_ctx);
|
||||
value->rvtype = RVT_FLOAT;
|
||||
break;
|
||||
case regctx_entry_t::DATA:
|
||||
value->set_bytes(p, size_in_ctx);
|
||||
break;
|
||||
case regctx_entry_t::FUNC:
|
||||
// never happens; makes compiler happy.
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool patch(uint8_t *ptr, const regval_t *value) const
|
||||
{
|
||||
if ( type == regctx_entry_t::FUNC )
|
||||
{
|
||||
write_func((regctx_t *) ptr, value, user_data);
|
||||
}
|
||||
else
|
||||
{
|
||||
uint8_t *p = ptr + offset_in_ctx;
|
||||
switch ( type )
|
||||
{
|
||||
case regctx_entry_t::IVAL:
|
||||
{
|
||||
uint64_t ival = value->ival;
|
||||
ival = truncate_ival(ival);
|
||||
switch ( size_in_ctx )
|
||||
{
|
||||
case 1: *(uint8_t *)p = ival; break;
|
||||
case 2: *(uint16_t *)p = ival; break;
|
||||
case 4: *(uint32_t *)p = ival; break;
|
||||
case 8: *(uint64_t *)p = ival; break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case regctx_entry_t::FVAL:
|
||||
// TODO: for x86, the value is converted from IEEE format in
|
||||
// x86_write_register(). clean this up somehow.
|
||||
memcpy(p, &value->fval, size_in_ctx);
|
||||
break;
|
||||
case regctx_entry_t::DATA:
|
||||
if ( value->get_data_size() != size_in_ctx )
|
||||
return false;
|
||||
memcpy(p, value->get_data(), value->get_data_size());
|
||||
break;
|
||||
case regctx_entry_t::FUNC:
|
||||
// never happens; makes compiler happy.
|
||||
break;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
};
|
||||
DECLARE_TYPE_AS_MOVABLE(regctx_entry_t);
|
||||
typedef qvector<regctx_entry_t> reg_ctx_entries_t;
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
struct regctx_base_t
|
||||
{
|
||||
dynamic_register_set_t &idaregs; // linked to debmod_t's variable of the same name
|
||||
thid_t tid;
|
||||
int clsmask;
|
||||
|
||||
reg_ctx_entries_t entries;
|
||||
|
||||
void setup(thid_t _tid, int _clsmask=0)
|
||||
{
|
||||
tid = _tid;
|
||||
clsmask = _clsmask;
|
||||
}
|
||||
|
||||
void setup_reg(int dyn_reg_idx)
|
||||
{
|
||||
clsmask |= entries[dyn_reg_idx].reg_class;
|
||||
}
|
||||
|
||||
regctx_base_t(dynamic_register_set_t &_idaregs)
|
||||
: idaregs(_idaregs)
|
||||
{
|
||||
setup(0, 0);
|
||||
}
|
||||
|
||||
void add_idareg(register_info_t &ri)
|
||||
{
|
||||
idaregs.add_register(ri.name,
|
||||
ri.flags,
|
||||
ri.dtype,
|
||||
ri.register_class,
|
||||
ri.bit_strings,
|
||||
ri.default_bit_strings_mask);
|
||||
}
|
||||
|
||||
size_t add_entry(
|
||||
regctx_entry_t::type_t type,
|
||||
register_info_t &ri,
|
||||
size_t offset_in_ctx,
|
||||
size_t size_in_ctx,
|
||||
size_t reg_size)
|
||||
{
|
||||
size_t dyn_reg_idx = entries.size();
|
||||
regctx_entry_t &entry = entries.push_back();
|
||||
entry.type = type;
|
||||
entry.reg_class = ri.register_class;
|
||||
entry.reg_idx = dyn_reg_idx;
|
||||
entry.offset_in_ctx = offset_in_ctx;
|
||||
entry.size_in_ctx = size_in_ctx;
|
||||
entry.reg_size = reg_size;
|
||||
add_idareg(ri);
|
||||
return dyn_reg_idx;
|
||||
}
|
||||
|
||||
size_t add_ival(register_info_t &ri, size_t offset_in_ctx, size_t size_in_ctx)
|
||||
{
|
||||
QASSERT(1797, size_in_ctx <= sizeof(regval_t::ival));
|
||||
size_t reg_size = ri.dtype == dt_word ? 2
|
||||
: ri.dtype == dt_dword ? 4
|
||||
: ri.dtype == dt_qword ? 8
|
||||
: 0;
|
||||
QASSERT(1798, reg_size != 0);
|
||||
return add_entry(regctx_entry_t::IVAL, ri, offset_in_ctx, size_in_ctx, reg_size);
|
||||
}
|
||||
|
||||
size_t add_fval(register_info_t &ri, size_t offset_in_ctx, size_t size_in_ctx)
|
||||
{
|
||||
QASSERT(1799, size_in_ctx <= sizeof(regval_t::fval));
|
||||
return add_entry(regctx_entry_t::FVAL, ri, offset_in_ctx, size_in_ctx, size_in_ctx);
|
||||
}
|
||||
|
||||
size_t add_data(register_info_t &ri, size_t offset_in_ctx, size_t size_in_ctx)
|
||||
{
|
||||
return add_entry(regctx_entry_t::DATA, ri, offset_in_ctx, size_in_ctx, size_in_ctx);
|
||||
}
|
||||
|
||||
size_t add_func(
|
||||
register_info_t &ri,
|
||||
void (*read_func)(const regctx_t *, regval_t *, void *),
|
||||
void (*write_func)(regctx_t *, const regval_t *, void *),
|
||||
void *user_data=nullptr)
|
||||
{
|
||||
size_t dyn_reg_idx = entries.size();
|
||||
regctx_entry_t &entry = entries.push_back();
|
||||
entry.type = regctx_entry_t::FUNC;
|
||||
entry.reg_class = ri.register_class;
|
||||
entry.reg_idx = dyn_reg_idx;
|
||||
entry.read_func = read_func;
|
||||
entry.write_func = write_func;
|
||||
entry.user_data = user_data;
|
||||
add_idareg(ri);
|
||||
return dyn_reg_idx;
|
||||
}
|
||||
|
||||
void read_all(regval_t *values)
|
||||
{
|
||||
for ( const regctx_entry_t &entry: entries )
|
||||
if ( (clsmask & entry.reg_class) != 0 )
|
||||
entry.read(&values[entry.reg_idx], (const uint8_t *) this);
|
||||
}
|
||||
|
||||
bool patch(int dyn_reg_idx, const regval_t *value)
|
||||
{
|
||||
return entries[dyn_reg_idx].patch((uint8_t *) this, value);
|
||||
}
|
||||
};
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// Main class to represent a debugger module
|
||||
class debmod_t
|
||||
{
|
||||
protected:
|
||||
// processor_t &ph;
|
||||
typedef std::map<int, regval_t> regval_map_t;
|
||||
qvector<exception_info_t> exceptions;
|
||||
name_info_t dn_names;
|
||||
// Pending events. currently used only to store
|
||||
// exceptions that happen while attaching
|
||||
eventlist_t events;
|
||||
// The last event received via a successful get_debug_event()
|
||||
debug_event_t last_event;
|
||||
|
||||
// debugged process attributes (may be changed after process start/attach)
|
||||
debapp_attrs_t debapp_attrs;
|
||||
|
||||
procvec_t proclist;
|
||||
|
||||
// appcall contexts
|
||||
struct call_context_t
|
||||
{
|
||||
regvals_t saved_regs;
|
||||
ea_t sp;
|
||||
ea_t ctrl_ea;
|
||||
bool regs_spoiled;
|
||||
call_context_t() : sp(BADADDR), ctrl_ea(BADADDR), regs_spoiled(false) {}
|
||||
};
|
||||
typedef qstack<call_context_t> call_contexts_t;
|
||||
typedef std::map<thid_t, call_contexts_t> appcalls_t;
|
||||
appcalls_t appcalls;
|
||||
|
||||
int send_ioctl(int fn, const void *buf, size_t size, void **poutbuf, ssize_t *poutsize)
|
||||
{
|
||||
return ::send_ioctl(rpc, fn, buf, size, poutbuf, poutsize);
|
||||
}
|
||||
// If an IDC error occurs: we cannot prepare an error message on the server
|
||||
// side because we do not have access to error strings (they are in ida.hlp).
|
||||
// We pass the error code to IDA (with eventual arguments) so it can prepare
|
||||
// a nice error message for the user
|
||||
void report_idc_error(ea_t ea, error_t code, ssize_t errval, const char *errprm)
|
||||
{
|
||||
return ::report_idc_error(rpc, ea, code, errval, errprm);
|
||||
}
|
||||
|
||||
typedef std::map<ea_t, lowcnd_t> lowcnds_t;
|
||||
lowcnds_t cndmap;
|
||||
eavec_t handling_lowcnds;
|
||||
bool evaluate_and_handle_lowcnd(debug_event_t *event, int elc_flags=0);
|
||||
bool handle_lowcnd(lowcnd_t *lc, debug_event_t *event, int elc_flags);
|
||||
#define ELC_KEEP_EIP 0x0001 // do not reset eip before stepping
|
||||
#define ELC_KEEP_SUSP 0x0002 // keep suspended state, do not resume after stepping
|
||||
|
||||
// helper functions for programmatical single stepping
|
||||
virtual drc_t dbg_perform_single_step(debug_event_t *event, const insn_t &ins);
|
||||
virtual int dbg_freeze_threads_except(thid_t) { return 0; }
|
||||
virtual int dbg_thaw_threads_except(thid_t) { return 0; }
|
||||
drc_t resume_app_and_get_event(debug_event_t *dev);
|
||||
void set_platform(const char *platform_name);
|
||||
|
||||
// return number of processes, -1 - not implemented
|
||||
virtual int idaapi get_process_list(procvec_t *proclist, qstring *errbuf);
|
||||
|
||||
public:
|
||||
// initialized by dbg_init()
|
||||
int debugger_flags;
|
||||
meminfo_vec_t old_ranges;
|
||||
rpc_engine_t *rpc;
|
||||
bool debug_debugger;
|
||||
// Is dynamic library?
|
||||
bool is_dll;
|
||||
|
||||
// indexes of sp and program counter registers.
|
||||
// Must be initialized by derived classes.
|
||||
int sp_idx;
|
||||
int pc_idx;
|
||||
|
||||
// index of frame pointer register
|
||||
int fp_idx;
|
||||
|
||||
// Total number of registers.
|
||||
// Must be initialized by derived classes.
|
||||
int nregs;
|
||||
|
||||
// Breakpoint code.
|
||||
// Must be initialized by derived classes.
|
||||
bytevec_t bpt_code;
|
||||
|
||||
qstring input_file_path;
|
||||
|
||||
page_bpts_t page_bpts;
|
||||
|
||||
// will either send as msg/warning/error through callui,
|
||||
// or through 'rpc' if it is present.
|
||||
DEFINE_ALL_NOTIFICATION_FUNCTIONS(rpc);
|
||||
AS_PRINTF(3,0) static ssize_t dvnotif(int code, rpc_engine_t *rpc, const char *format, va_list va);
|
||||
|
||||
bool broken_connection;
|
||||
pid_t pid;
|
||||
|
||||
debmodbpt_map_t bpts;
|
||||
update_bpt_vec_t deleted_bpts; // deleted bpts in the last update_bpts() call
|
||||
// if bpt EA was deleted then bpt raised in the same place for the
|
||||
// same thread does not belong to IDA
|
||||
bool is_ida_bpt(ea_t ea, thid_t tid)
|
||||
{
|
||||
if ( bpts.find(ea) != bpts.end() )
|
||||
return true;
|
||||
update_bpt_info_t b;
|
||||
b.ea = ea;
|
||||
return deleted_bpts.find(b) != deleted_bpts.end()
|
||||
&& (last_event.eid() != BREAKPOINT
|
||||
|| last_event.ea != ea
|
||||
|| last_event.tid != tid);
|
||||
}
|
||||
|
||||
static bool reuse_broken_connections;
|
||||
|
||||
//------------------------------------
|
||||
// Dynamic register set for debugger_t
|
||||
//------------------------------------
|
||||
dynamic_register_set_t idaregs;
|
||||
|
||||
//------------------------------------
|
||||
// Constructors and destructors
|
||||
//------------------------------------
|
||||
debmod_t();
|
||||
virtual ~debmod_t() { cleanup(); }
|
||||
|
||||
//------------------------------------
|
||||
// Debug names methods
|
||||
//------------------------------------
|
||||
void clear_debug_names();
|
||||
name_info_t *get_debug_names();
|
||||
void save_debug_name(ea_t ea, const char *name);
|
||||
int set_debug_names();
|
||||
int send_debug_names_to_ida(ea_t *addrs, const char *const *names, int qty);
|
||||
int send_debug_event_to_ida(const debug_event_t *ev, int rqflags);
|
||||
ea_t find_debug_name(const char *name) const;
|
||||
//------------------------------------
|
||||
// Utility methods
|
||||
//------------------------------------
|
||||
void cleanup(void);
|
||||
AS_PRINTF(2, 3) void debdeb(const char *format, ...);
|
||||
AS_PRINTF(2, 3) bool deberr(const char *format, ...);
|
||||
bool same_as_oldmemcfg(const meminfo_vec_t &ranges) const;
|
||||
void save_oldmemcfg(const meminfo_vec_t &ranges);
|
||||
bool continue_after_last_event(bool handled = true);
|
||||
lowcnd_t *get_failed_lowcnd(thid_t tid, ea_t ea);
|
||||
page_bpts_t::iterator find_page_bpt(ea_t ea, int size=1);
|
||||
bool del_page_bpt(ea_t ea, bpttype_t type);
|
||||
void enable_page_bpts(bool enable);
|
||||
ea_t calc_page_base(ea_t ea) { return align_down(ea, dbg_memory_page_size()); }
|
||||
void log_exception(const debug_event_t *ev, const exception_info_t *ei);
|
||||
uint64 probe_file_size(int fn, uint64 step);
|
||||
void set_input_path(const char *input_path);
|
||||
int read_bpt_orgbytes(bytevec_t *buf, ea_t ea, int len);
|
||||
|
||||
//------------------------------------
|
||||
// Shared methods
|
||||
//------------------------------------
|
||||
virtual bool check_input_file_crc32(uint32 orig_crc);
|
||||
virtual const exception_info_t *find_exception(int code);
|
||||
virtual bool get_exception_name(int code, char *buf, size_t bufsize);
|
||||
virtual drc_t idaapi dbg_get_processes(procinfo_vec_t *info, qstring *errbuf);
|
||||
|
||||
//------------------------------------
|
||||
// Methods to be implemented
|
||||
//------------------------------------
|
||||
virtual void idaapi dbg_set_debugging(bool _debug_debugger) = 0;
|
||||
virtual drc_t idaapi dbg_init(uint32_t *flags2, qstring *errbuf) = 0;
|
||||
virtual void idaapi dbg_term(void) = 0;
|
||||
virtual drc_t idaapi dbg_detach_process(void) = 0;
|
||||
virtual drc_t idaapi dbg_start_process(
|
||||
const char *path,
|
||||
const char *args,
|
||||
const char *startdir,
|
||||
int flags,
|
||||
const char *input_path,
|
||||
uint32 input_file_crc32,
|
||||
qstring *errbuf) = 0;
|
||||
virtual gdecode_t idaapi dbg_get_debug_event(debug_event_t *event, int timeout_msecs) = 0;
|
||||
virtual drc_t idaapi dbg_attach_process(pid_t process_id, int event_id, int flags, qstring *errbuf) = 0;
|
||||
virtual drc_t idaapi dbg_prepare_to_pause_process(qstring *errbuf) = 0;
|
||||
virtual drc_t idaapi dbg_exit_process(qstring *errbuf) = 0;
|
||||
virtual drc_t idaapi dbg_continue_after_event(const debug_event_t *event) = 0;
|
||||
virtual void idaapi dbg_set_exception_info(const exception_info_t *info, int qty);
|
||||
virtual void idaapi dbg_stopped_at_debug_event(import_infos_t *infos, bool dlls_added, thread_name_vec_t *thr_names) = 0;
|
||||
virtual drc_t idaapi dbg_thread_suspend(thid_t thread_id) = 0;
|
||||
virtual drc_t idaapi dbg_thread_continue(thid_t thread_id) = 0;
|
||||
virtual drc_t idaapi dbg_set_resume_mode(thid_t thread_id, resume_mode_t resmod) = 0;
|
||||
virtual drc_t idaapi dbg_read_registers(
|
||||
thid_t thread_id,
|
||||
int clsmask,
|
||||
regval_t *values,
|
||||
qstring *errbuf) = 0;
|
||||
virtual drc_t idaapi dbg_write_register(
|
||||
thid_t thread_id,
|
||||
int reg_idx,
|
||||
const regval_t *value,
|
||||
qstring *errbuf) = 0;
|
||||
virtual drc_t idaapi dbg_thread_get_sreg_base(ea_t *ea, thid_t thread_id, int sreg_value, qstring *errbuf) = 0;
|
||||
virtual ea_t idaapi map_address(ea_t ea, const regval_t *, int /* regnum */) { return ea; }
|
||||
virtual drc_t idaapi dbg_get_memory_info(meminfo_vec_t &ranges, qstring *errbuf) = 0;
|
||||
virtual int idaapi dbg_get_scattered_image(scattered_image_t & /*si*/, ea_t /*base*/) { return -1; }
|
||||
virtual bool idaapi dbg_get_image_uuid(bytevec_t * /*uuid*/, ea_t /*base*/) { return false; }
|
||||
virtual ea_t idaapi dbg_get_segm_start(ea_t /*base*/, const qstring & /*segname*/) { return BADADDR; }
|
||||
virtual ssize_t idaapi dbg_read_memory(ea_t ea, void *buffer, size_t size, qstring *errbuf) = 0;
|
||||
virtual ssize_t idaapi dbg_write_memory(ea_t ea, const void *buffer, size_t size, qstring *errbuf) = 0;
|
||||
virtual int idaapi dbg_is_ok_bpt(bpttype_t type, ea_t ea, int len) = 0;
|
||||
// for swbpts, len may be -1 (unknown size, for example arm/thumb mode) or bpt opcode length
|
||||
// dbg_add_bpt returns 2 if it adds a page bpt
|
||||
virtual int idaapi dbg_add_bpt(bytevec_t *orig_bytes, bpttype_t type, ea_t ea, int len) = 0;
|
||||
virtual int idaapi dbg_del_bpt(bpttype_t type, ea_t ea, const uchar *orig_bytes, int len) = 0;
|
||||
virtual drc_t idaapi dbg_update_bpts(int *nbpts, update_bpt_info_t *bpts, int nadd, int ndel, qstring *errbuf);
|
||||
virtual int idaapi dbg_add_page_bpt(bpttype_t /*type*/, ea_t /*ea*/, int /*size*/) { return 0; }
|
||||
virtual bool idaapi dbg_enable_page_bpt(page_bpts_t::iterator /*p*/, bool /*enable*/) { return false; }
|
||||
virtual drc_t idaapi dbg_update_lowcnds(int *nupdated, const lowcnd_t *lowcnds, int nlowcnds, qstring *errbuf);
|
||||
virtual drc_t idaapi dbg_eval_lowcnd(thid_t tid, ea_t ea, qstring *errbuf);
|
||||
virtual int idaapi dbg_open_file(const char * /*file*/, uint64 * /*fsize*/, bool /*readonly*/) { return -1; }
|
||||
virtual void idaapi dbg_close_file(int /*fn*/) {}
|
||||
virtual ssize_t idaapi dbg_read_file(int /*fn*/, qoff64_t /*off*/, void * /*buf*/, size_t /*size*/) { return 0; }
|
||||
virtual ssize_t idaapi dbg_write_file(int /*fn*/, qoff64_t /*off*/, const void * /*buf*/, size_t /*size*/) { return 0; }
|
||||
virtual int idaapi handle_ioctl(
|
||||
int /*fn*/,
|
||||
const void * /*buf*/,
|
||||
size_t /*size*/,
|
||||
void ** /*outbuf*/,
|
||||
ssize_t * /*outsize*/)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
virtual int idaapi get_system_specific_errno(void) const; // this code must be acceptable by winerr()
|
||||
virtual drc_t idaapi dbg_update_call_stack(thid_t, call_stack_t *) { return DRC_NONE; }
|
||||
virtual ea_t idaapi dbg_appcall(
|
||||
ea_t /*func_ea*/,
|
||||
thid_t /*tid*/,
|
||||
int /*stkarg_nbytes*/,
|
||||
const struct regobjs_t * /*regargs*/,
|
||||
struct relobj_t * /*stkargs*/,
|
||||
struct regobjs_t * /*retregs*/,
|
||||
qstring *errbuf,
|
||||
debug_event_t * /*event*/,
|
||||
int /*flags*/);
|
||||
virtual drc_t idaapi dbg_cleanup_appcall(thid_t /*tid*/);
|
||||
virtual bool idaapi write_registers(
|
||||
thid_t /*tid*/,
|
||||
int /*start*/,
|
||||
int /*count*/,
|
||||
const regval_t * /*values*/)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
// finalize appcall stack image
|
||||
// input: stack image contains the return address at the beginning
|
||||
virtual int finalize_appcall_stack(call_context_t &, regval_map_t &, bytevec_t &) { return 0; }
|
||||
virtual ea_t calc_appcall_stack(const regvals_t ®vals);
|
||||
virtual bool should_stop_appcall(thid_t tid, const debug_event_t *event, ea_t ea);
|
||||
virtual bool should_suspend_at_exception(const debug_event_t *event, const exception_info_t *ei);
|
||||
virtual bool preprocess_appcall_cleanup(thid_t, call_context_t &) { return true; }
|
||||
virtual int get_regidx(const char *regname, int *clsmask) = 0;
|
||||
virtual uint32 dbg_memory_page_size(void) { return 0x1000; }
|
||||
virtual bool idaapi dbg_prepare_broken_connection(void) { return false; }
|
||||
virtual bool idaapi dbg_continue_broken_connection(pid_t) { old_ranges.clear(); return true; }
|
||||
virtual bool idaapi dbg_enable_trace(thid_t, bool, int) { return false; }
|
||||
virtual bool idaapi dbg_is_tracing_enabled(thid_t, int) { return false; }
|
||||
virtual int idaapi dbg_rexec(const char *cmdline);
|
||||
virtual void adjust_swbpt(ea_t *, int *) {}
|
||||
virtual void dbg_get_debapp_attrs(debapp_attrs_t *out_pattrs) const;
|
||||
virtual bool idaapi dbg_get_srcinfo_path(qstring * /*path*/, ea_t /*base*/) const { return false; }
|
||||
virtual bool import_dll(const import_request_t & /*req*/) { return false; }
|
||||
virtual drc_t idaapi dbg_bin_search(
|
||||
ea_t *ea,
|
||||
ea_t start_ea,
|
||||
ea_t end_ea,
|
||||
const compiled_binpat_vec_t &ptns,
|
||||
int srch_flags,
|
||||
qstring *errbuf);
|
||||
|
||||
bool restore_broken_breakpoints(void);
|
||||
void set_addr_size(int size) { debapp_attrs.addrsize = size; }
|
||||
void set_endianness(bool is_be) { debapp_attrs.is_be = is_be; }
|
||||
};
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
//
|
||||
// Some functions, per OS implemented
|
||||
//
|
||||
bool init_subsystem();
|
||||
bool term_subsystem();
|
||||
debmod_t *create_debug_session(void *params);
|
||||
|
||||
//
|
||||
// Processor specific init/term
|
||||
//
|
||||
void processor_specific_init(void);
|
||||
void processor_specific_term(void);
|
||||
|
||||
// Perform an action on all existing debugger modules
|
||||
struct debmod_visitor_t
|
||||
{
|
||||
virtual int visit(debmod_t *debmod) = 0;
|
||||
};
|
||||
int for_all_debuggers(debmod_visitor_t &v);
|
||||
|
||||
|
||||
//
|
||||
// Utility functions
|
||||
//
|
||||
|
||||
// Common method between MacOS and Linux to launch a process
|
||||
drc_t idaapi maclnx_launch_process(
|
||||
debmod_t *debmod,
|
||||
const char *path,
|
||||
const char *args,
|
||||
const char *startdir,
|
||||
int flags,
|
||||
const char *input_path,
|
||||
uint32 input_file_crc32,
|
||||
void **child_pid,
|
||||
qstring *errbuf);
|
||||
|
||||
bool add_idc_funcs(const struct ext_idcfunc_t funcs[], size_t nfuncs, bool reg);
|
||||
|
||||
//
|
||||
// Externs
|
||||
//
|
||||
extern debmod_t *idc_debmod;
|
||||
extern thid_t idc_thread;
|
||||
extern bool ignore_sigint;
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
// server.cpp
|
||||
bool lock_begin();
|
||||
bool lock_end();
|
||||
|
||||
// bool srv_lock_begin(void);
|
||||
// bool srv_lock_end(void);
|
||||
|
||||
#endif
|
||||
292
idasdk76/dbg/exceptions.cfg
Normal file
292
idasdk76/dbg/exceptions.cfg
Normal file
@@ -0,0 +1,292 @@
|
||||
; This file describes the exception definitions.
|
||||
; The definitions will be used for new databases.
|
||||
|
||||
; for each exception:
|
||||
; code stop_at pass_to_app notification name description
|
||||
;
|
||||
; code is the exception code
|
||||
; stop_at is one of stop or nostop - should the debugger suspend
|
||||
; the process when the exception occurs
|
||||
; pass_to_app is one of app or mask - should the debugger pass
|
||||
; the exception to the application or not
|
||||
; notification is one of warn/log/silent
|
||||
; name is the exception short name
|
||||
; description is the exception description displayed to the user
|
||||
|
||||
; Windows exceptions
|
||||
.win32,bochs_win32
|
||||
0xC0000005 stop mask warn EXCEPTION_ACCESS_VIOLATION The instruction at 0x%a referenced memory at 0x%a. The memory could not be %s
|
||||
0x80000002 stop mask warn EXCEPTION_DATATYPE_MISALIGNMENT A datatype misalignment error was detected in a load or store instruction
|
||||
0x80000003 stop mask warn EXCEPTION_BREAKPOINT Software breakpoint exception
|
||||
0x80000004 stop mask warn EXCEPTION_SINGLE_STEP Single step exception
|
||||
0xC000008C stop mask warn EXCEPTION_ARRAY_BOUNDS_EXCEEDED Array bounds exceeded
|
||||
0xC000008D stop mask warn EXCEPTION_FLT_DENORMAL_OPERAND Floating point denormal operand
|
||||
0xC000008E stop mask warn EXCEPTION_FLT_DIVIDE_BY_ZERO Floating point divide by zero
|
||||
0xC000008F stop mask warn EXCEPTION_FLT_INEXACT_RESULT Floating point inexact result
|
||||
0xC0000090 stop mask warn EXCEPTION_FLT_INVALID_OPERATION Floating point invalid operation
|
||||
0xC0000091 stop mask warn EXCEPTION_FLT_OVERFLOW Floating point overflow
|
||||
0xC0000092 stop mask warn EXCEPTION_FLT_STACK_CHECK Floating point stack check
|
||||
0xC0000093 stop mask warn EXCEPTION_FLT_UNDERFLOW Floating point underflow
|
||||
0xC0000094 stop mask warn EXCEPTION_INT_DIVIDE_BY_ZERO Integer divide by zero
|
||||
0xC0000095 stop mask warn EXCEPTION_INT_OVERFLOW Integer overflow
|
||||
0xC0000096 stop mask warn EXCEPTION_PRIV_INSTRUCTION Priveleged instruction
|
||||
0xC0000006 stop mask warn EXCEPTION_IN_PAGE_ERROR The instruction at "0x%a" referenced memory at "0x%a". The required data was not placed into memory because of an I/O error status of "0x%a"
|
||||
0xC000001D stop mask warn EXCEPTION_ILLEGAL_INSTRUCTION An attempt was made to execute an illegal instruction
|
||||
0xC0000025 stop mask warn EXCEPTION_NONCONTINUABLE_EXCEPTION Windows cannot continue from this exception
|
||||
0xC00000FD stop mask warn EXCEPTION_STACK_OVERFLOW A new guard page for the stack cannot be created (stack overflow)
|
||||
0xC0000026 stop mask warn EXCEPTION_INVALID_DISPOSITION An invalid exception disposition was returned by an exception handler
|
||||
0x80000001 stop mask warn EXCEPTION_GUARD_PAGE A page of memory that marks the end of a data structure such as a stack or an array has been accessed
|
||||
0xC0000008 stop mask warn EXCEPTION_INVALID_HANDLE An invalid HANDLE was specified
|
||||
0xEEFFACE stop mask warn EXCEPTION_BCC_FATAL Fatal unhandled exception in the BCC compiled program
|
||||
0xEEDFAE6 stop mask warn EXCEPTION_BCC_NORMAL Unhandled exception in the BCC compiled program
|
||||
0x40010005 stop mask warn DBG_CONTROL_C CTRL+C was input to console process
|
||||
0x40010008 stop mask warn DBG_CONTROL_BREAK CTRL+BREAK was input to console process
|
||||
0xE06D7363 stop mask warn EXCEPTION_MSC_CPLUSPLUS Microsoft C++ exception
|
||||
0xE0434F4D stop mask warn EXCEPTION_MANAGED_NET Managed .NET exception
|
||||
0xE0434352 stop mask warn EXCEPTION_MANAGED_NET_V4 Managed .NET exception (V4+)
|
||||
0x4000001E stop mask warn EXCEPTION_WX86_SINGLE_STEP Single step exception (x86 emulation)
|
||||
0x4000001F stop mask warn EXCEPTION_WX86_BREAKPOINT Software breakpoint exception (x86 emulation)
|
||||
0x406D1388 nostop mask log MS_VC_EXCEPTION SetThreadName
|
||||
|
||||
; Linux exceptions
|
||||
.linux
|
||||
1 stop mask warn SIGHUP Hangup
|
||||
2 stop mask warn SIGINT Interrupt
|
||||
3 stop mask warn SIGQUIT Quit
|
||||
4 stop mask warn SIGILL Illegal instruction
|
||||
5 stop mask warn SIGTRAP Trace trap
|
||||
6 stop mask warn SIGABRT Abort
|
||||
7 stop mask warn SIGBUS BUS error
|
||||
8 stop mask warn SIGFPE Floating-point exception
|
||||
9 stop mask warn SIGKILL Kill unblockable
|
||||
10 stop mask warn SIGUSR1 User-defined signal 1
|
||||
11 stop mask warn SIGSEGV Segmentation violation
|
||||
12 stop mask warn SIGUSR2 User-defined signal 2
|
||||
13 stop mask warn SIGPIPE Broken pipe
|
||||
14 stop mask warn SIGALRM Alarm clock
|
||||
15 stop mask warn SIGTERM Termination
|
||||
16 stop mask warn SIGSTKFLT Stack fault
|
||||
17 stop mask warn SIGCHLD Child status has changed
|
||||
18 stop mask warn SIGCONT Continue
|
||||
19 stop mask warn SIGSTOP Stop unblockable
|
||||
20 stop mask warn SIGTSTP Keyboard stop
|
||||
21 stop mask warn SIGTTIN Background read from tty
|
||||
22 stop mask warn SIGTTOU Background write to tty
|
||||
23 stop mask warn SIGURG Urgent condition on socket
|
||||
24 stop mask warn SIGXCPU CPU limit exceeded
|
||||
25 stop mask warn SIGXFSZ File size limit exceeded
|
||||
26 stop mask warn SIGVTALRM Virtual alarm clock
|
||||
27 stop mask warn SIGPROF Profiling alarm clock
|
||||
28 stop mask warn SIGWINCH Window size change
|
||||
29 stop mask warn SIGIO I/O now possible
|
||||
30 stop mask warn SIGPWR Power failure restart
|
||||
31 stop mask warn SIGSYS Bad system call
|
||||
|
||||
; Mac OS X/iphone exceptions
|
||||
.macosx,ios,xnu
|
||||
1 stop mask warn SIGHUP terminal line hangup
|
||||
2 stop mask warn SIGINT interrupt program
|
||||
3 stop mask warn SIGQUIT quit program
|
||||
4 stop mask warn SIGILL illegal instruction
|
||||
5 stop mask warn SIGTRAP trace trap
|
||||
6 stop mask warn SIGABRT abort program
|
||||
7 stop mask warn SIGEMT emulate instruction executed
|
||||
8 stop mask warn SIGFPE floating-point exception
|
||||
9 stop mask warn SIGKILL kill program
|
||||
10 stop mask warn SIGBUS bus error
|
||||
11 stop mask warn SIGSEGV segmentation violation
|
||||
12 stop mask warn SIGSYS non-existent system call invoked
|
||||
13 stop mask warn SIGPIPE write on a pipe with no reader
|
||||
14 stop mask warn SIGALRM real-time timer expired
|
||||
15 stop mask warn SIGTERM software termination signal
|
||||
16 stop mask warn SIGURG urgent condition present on socket
|
||||
17 stop mask warn SIGSTOP stop
|
||||
18 stop mask warn SIGTSTP stop signal generated from keyboard
|
||||
19 stop mask warn SIGCONT continue after stop
|
||||
20 stop mask warn SIGCHLD child status has changed
|
||||
21 stop mask warn SIGTTIN background read attempted from control terminal
|
||||
22 stop mask warn SIGTTOU background write attempted to control terminal
|
||||
23 stop mask warn SIGIO I/O is possible on a descriptor
|
||||
24 stop mask warn SIGXCPU cpu time limit exceeded
|
||||
25 stop mask warn SIGXFSZ file size limit exceeded
|
||||
26 stop mask warn SIGVTALRM virtual time alarm
|
||||
27 stop mask warn SIGPROF profiling timer alarm
|
||||
28 stop mask warn SIGWINCH Window size change
|
||||
29 stop mask warn SIGINFO status request from keyboard
|
||||
30 stop mask warn SIGUSR1 User defined signal 1
|
||||
31 stop mask warn SIGUSR2 User defined signal 2
|
||||
;additional iphone exceptions
|
||||
145 stop mask warn EXC_BAD_ACCESS Bad memory access
|
||||
146 stop mask warn EXC_BAD_INSTRUCTION Bad instruction
|
||||
147 stop mask warn EXC_ARITHMETIC Arithmetic exception
|
||||
148 stop mask warn EXC_EMULATION Emulation exception
|
||||
149 stop mask warn EXC_SOFTWARE Software exception
|
||||
150 stop mask warn EXC_BREAKPOINT Breakpoint exception
|
||||
|
||||
; Bochs IA-32 emulator: raw exceptions
|
||||
; (in addition to MS Windows exceptions defined above)
|
||||
.bochs_win32
|
||||
0x00 stop mask warn DIVIDE_BY_ZERO Divide by zero
|
||||
0x01 stop mask warn SINGLE_STEP Single step
|
||||
0x03 stop mask warn BREAKPOINT Breakpoint
|
||||
0x04 stop mask warn INTO Interrupt on overflow
|
||||
0x06 stop mask warn INVALID_OPCODE Invalid opcode
|
||||
0x0C stop mask warn STACK_EXCEPTION Stack exception
|
||||
0x0D stop mask warn GENERAL_PROTECTION_FAULT General protection fault
|
||||
0x0E stop mask warn PAGE_FAULT Page fault at 0x%a, error code %a
|
||||
0x10 stop mask warn FLOATING_POINT_ERROR Floating point error
|
||||
|
||||
.gdb
|
||||
; gdb signals
|
||||
1 stop mask warn SIGHUP Hangup
|
||||
2 stop mask warn SIGINT Interrupt
|
||||
3 stop mask warn SIGQUIT Quit
|
||||
4 stop mask warn SIGILL Illegal instruction
|
||||
5 stop mask warn SIGTRAP Trace/breakpoint trap
|
||||
6 stop mask warn SIGABRT Aborted
|
||||
7 stop mask warn SIGEMT Emulation trap
|
||||
8 stop mask warn SIGFPE Arithmetic exception
|
||||
9 stop mask warn SIGKILL Killed
|
||||
10 stop mask warn SIGBUS Bus error
|
||||
11 stop mask warn SIGSEGV Segmentation fault
|
||||
12 stop mask warn SIGSYS Bad system call
|
||||
13 stop mask warn SIGPIPE Broken pipe
|
||||
14 stop mask warn SIGALRM Alarm clock
|
||||
15 stop mask warn SIGTERM Terminated
|
||||
16 stop mask warn SIGURG Urgent I/O condition
|
||||
17 stop mask warn SIGSTOP Stopped (signal)
|
||||
18 stop mask warn SIGTSTP Stopped (user)
|
||||
19 stop mask warn SIGCONT Continued
|
||||
20 stop mask warn SIGCHLD Child status changed
|
||||
21 stop mask warn SIGTTIN Stopped (tty input)
|
||||
22 stop mask warn SIGTTOU Stopped (tty output)
|
||||
23 stop mask warn SIGIO I/O possible
|
||||
24 stop mask warn SIGXCPU CPU time limit exceeded
|
||||
25 stop mask warn SIGXFSZ File size limit exceeded
|
||||
26 stop mask warn SIGVTALRM Virtual timer expired
|
||||
27 stop mask warn SIGPROF Profiling timer expired
|
||||
28 stop mask warn SIGWINCH Window size changed
|
||||
29 stop mask warn SIGLOST Resource lost
|
||||
30 stop mask warn SIGUSR1 User defined signal 1
|
||||
31 stop mask warn SIGUSR2 User defined signal 2
|
||||
32 stop mask warn SIGPWR Power fail/restart
|
||||
33 stop mask warn SIGPOLL Pollable event occurred
|
||||
34 stop mask warn SIGWIND SIGWIND
|
||||
35 stop mask warn SIGPHONE SIGPHONE
|
||||
36 stop mask warn SIGWAITING Process's LWPs are blocked
|
||||
37 stop mask warn SIGLWP Signal LWP
|
||||
38 stop mask warn SIGDANGER Swap space dangerously low
|
||||
39 stop mask warn SIGGRANT Monitor mode granted
|
||||
40 stop mask warn SIGRETRACT Need to relinquish monitor mode
|
||||
41 stop mask warn SIGMSG Monitor mode data available
|
||||
42 stop mask warn SIGSOUND Sound completed
|
||||
43 stop mask warn SIGSAK Secure attention
|
||||
44 stop mask warn SIGPRIO SIGPRIO
|
||||
45 stop mask warn SIG33 Real-time event 33
|
||||
46 stop mask warn SIG34 Real-time event 34
|
||||
47 stop mask warn SIG35 Real-time event 35
|
||||
48 stop mask warn SIG36 Real-time event 36
|
||||
49 stop mask warn SIG37 Real-time event 37
|
||||
50 stop mask warn SIG38 Real-time event 38
|
||||
51 stop mask warn SIG39 Real-time event 39
|
||||
52 stop mask warn SIG40 Real-time event 40
|
||||
53 stop mask warn SIG41 Real-time event 41
|
||||
54 stop mask warn SIG42 Real-time event 42
|
||||
55 stop mask warn SIG43 Real-time event 43
|
||||
56 stop mask warn SIG44 Real-time event 44
|
||||
57 stop mask warn SIG45 Real-time event 45
|
||||
58 stop mask warn SIG46 Real-time event 46
|
||||
59 stop mask warn SIG47 Real-time event 47
|
||||
60 stop mask warn SIG48 Real-time event 48
|
||||
61 stop mask warn SIG49 Real-time event 49
|
||||
62 stop mask warn SIG50 Real-time event 50
|
||||
63 stop mask warn SIG51 Real-time event 51
|
||||
64 stop mask warn SIG52 Real-time event 52
|
||||
65 stop mask warn SIG53 Real-time event 53
|
||||
66 stop mask warn SIG54 Real-time event 54
|
||||
67 stop mask warn SIG55 Real-time event 55
|
||||
68 stop mask warn SIG56 Real-time event 56
|
||||
69 stop mask warn SIG57 Real-time event 57
|
||||
70 stop mask warn SIG58 Real-time event 58
|
||||
71 stop mask warn SIG59 Real-time event 59
|
||||
72 stop mask warn SIG60 Real-time event 60
|
||||
73 stop mask warn SIG61 Real-time event 61
|
||||
74 stop mask warn SIG62 Real-time event 62
|
||||
75 stop mask warn SIG63 Real-time event 63
|
||||
76 stop mask warn SIGCANCEL LWP internal signal
|
||||
77 stop mask warn SIG32 Real-time event 32
|
||||
78 stop mask warn SIG64 Real-time event 64
|
||||
79 stop mask warn SIG65 Real-time event 65
|
||||
80 stop mask warn SIG66 Real-time event 66
|
||||
81 stop mask warn SIG67 Real-time event 67
|
||||
82 stop mask warn SIG68 Real-time event 68
|
||||
83 stop mask warn SIG69 Real-time event 69
|
||||
84 stop mask warn SIG70 Real-time event 70
|
||||
85 stop mask warn SIG71 Real-time event 71
|
||||
86 stop mask warn SIG72 Real-time event 72
|
||||
87 stop mask warn SIG73 Real-time event 73
|
||||
88 stop mask warn SIG74 Real-time event 74
|
||||
89 stop mask warn SIG75 Real-time event 75
|
||||
90 stop mask warn SIG76 Real-time event 76
|
||||
91 stop mask warn SIG77 Real-time event 77
|
||||
92 stop mask warn SIG78 Real-time event 78
|
||||
93 stop mask warn SIG79 Real-time event 79
|
||||
94 stop mask warn SIG80 Real-time event 80
|
||||
95 stop mask warn SIG81 Real-time event 81
|
||||
96 stop mask warn SIG82 Real-time event 82
|
||||
97 stop mask warn SIG83 Real-time event 83
|
||||
98 stop mask warn SIG84 Real-time event 84
|
||||
99 stop mask warn SIG85 Real-time event 85
|
||||
100 stop mask warn SIG86 Real-time event 86
|
||||
101 stop mask warn SIG87 Real-time event 87
|
||||
102 stop mask warn SIG88 Real-time event 88
|
||||
103 stop mask warn SIG89 Real-time event 89
|
||||
104 stop mask warn SIG90 Real-time event 90
|
||||
105 stop mask warn SIG91 Real-time event 91
|
||||
106 stop mask warn SIG92 Real-time event 92
|
||||
107 stop mask warn SIG93 Real-time event 93
|
||||
108 stop mask warn SIG94 Real-time event 94
|
||||
109 stop mask warn SIG95 Real-time event 95
|
||||
110 stop mask warn SIG96 Real-time event 96
|
||||
111 stop mask warn SIG97 Real-time event 97
|
||||
112 stop mask warn SIG98 Real-time event 98
|
||||
113 stop mask warn SIG99 Real-time event 99
|
||||
114 stop mask warn SIG100 Real-time event 100
|
||||
115 stop mask warn SIG101 Real-time event 101
|
||||
116 stop mask warn SIG102 Real-time event 102
|
||||
117 stop mask warn SIG103 Real-time event 103
|
||||
118 stop mask warn SIG104 Real-time event 104
|
||||
119 stop mask warn SIG105 Real-time event 105
|
||||
120 stop mask warn SIG106 Real-time event 106
|
||||
121 stop mask warn SIG107 Real-time event 107
|
||||
122 stop mask warn SIG108 Real-time event 108
|
||||
123 stop mask warn SIG109 Real-time event 109
|
||||
124 stop mask warn SIG110 Real-time event 110
|
||||
125 stop mask warn SIG111 Real-time event 111
|
||||
126 stop mask warn SIG112 Real-time event 112
|
||||
127 stop mask warn SIG113 Real-time event 113
|
||||
128 stop mask warn SIG114 Real-time event 114
|
||||
129 stop mask warn SIG115 Real-time event 115
|
||||
130 stop mask warn SIG116 Real-time event 116
|
||||
131 stop mask warn SIG117 Real-time event 117
|
||||
132 stop mask warn SIG118 Real-time event 118
|
||||
133 stop mask warn SIG119 Real-time event 119
|
||||
134 stop mask warn SIG120 Real-time event 120
|
||||
135 stop mask warn SIG121 Real-time event 121
|
||||
136 stop mask warn SIG122 Real-time event 122
|
||||
137 stop mask warn SIG123 Real-time event 123
|
||||
138 stop mask warn SIG124 Real-time event 124
|
||||
139 stop mask warn SIG125 Real-time event 125
|
||||
140 stop mask warn SIG126 Real-time event 126
|
||||
141 stop mask warn SIG127 Real-time event 127
|
||||
142 stop mask warn SIGINFO Information request
|
||||
145 stop mask warn EXC_BAD_ACCESS Could not access memory
|
||||
146 stop mask warn EXC_BAD_INSTRUCTION Illegal instruction/operand
|
||||
147 stop mask warn EXC_ARITHMETIC Arithmetic exception
|
||||
148 stop mask warn EXC_EMULATION Emulation instruction
|
||||
149 stop mask warn EXC_SOFTWARE Software generated exception
|
||||
150 stop mask warn EXC_BREAKPOINT Breakpoint
|
||||
151 stop mask warn SIGLIBRT librt internal signal
|
||||
|
||||
.dalvik
|
||||
1 nostop mask warn java.lang.Throwable The superclass of all errors and exceptions in the Java language
|
||||
88
idasdk76/dbg/linux/android.cpp
Normal file
88
idasdk76/dbg/linux/android.cpp
Normal file
@@ -0,0 +1,88 @@
|
||||
/*
|
||||
Android specific functions.
|
||||
*/
|
||||
|
||||
// r_brk():
|
||||
// B0003050 70 47 BX LR
|
||||
static const uchar bxlr_thumb[] = { 0x70, 0x47 };
|
||||
|
||||
#define LINKER "/system/bin/linker"
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// on android /system/bin/linker comes without any symbols.
|
||||
// Since there is no other way, we scan the data segment for
|
||||
// dcd 1, ?, r_brk, 0, 0
|
||||
// r_debug is located very close to the beginning of the data segment,
|
||||
// we should find it fine. In any case, we check only the first 4KB.
|
||||
bool linux_debmod_t::add_android_shlib_bpt(const meminfo_vec_t &miv, bool attaching)
|
||||
{
|
||||
// find read/writable linker memory range
|
||||
meminfo_vec_t::const_iterator p;
|
||||
ea_t linker_base = BADADDR;
|
||||
for ( p=miv.begin(); p != miv.end(); ++p )
|
||||
{
|
||||
if ( p->name == LINKER )
|
||||
{
|
||||
if ( linker_base == BADADDR )
|
||||
linker_base = p->start_ea;
|
||||
// assume the data segment to be readable and writable
|
||||
if ( (p->perm & 6) == 6 )
|
||||
break;
|
||||
}
|
||||
}
|
||||
if ( p == miv.end() )
|
||||
{
|
||||
msg("Failed to find data segment of " LINKER "\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
// read max 2KB
|
||||
uint32 buf[2048];
|
||||
int nbytes = qmin(p->size(), sizeof(buf));
|
||||
ea_t dataseg = p->start_ea;
|
||||
nbytes = dbg_read_memory(dataseg, buf, nbytes, NULL);
|
||||
|
||||
uint32 *ptr = buf;
|
||||
for ( int i=0; i < nbytes/4-5; i++, ptr++ )
|
||||
{
|
||||
if ( ptr[0] == 1 // version
|
||||
&& (attaching || ptr[1] == 0) // r_map, 0 at the beginning
|
||||
&& (ptr[2] & 1) != 0 && ptr[2] < dataseg // r_brk (Thumb pointer)
|
||||
&& (attaching || ptr[3] == 0) // r_state: RT_CONSISTENT
|
||||
&& ptr[4] == 0 ) // linker baseaddr: always zero?
|
||||
{
|
||||
ea_t r_brk = ptr[2] & ~1;
|
||||
// check if linker is not relocated yet
|
||||
if ( r_brk < linker_base )
|
||||
r_brk += linker_base; // adjust address
|
||||
uchar opcode[2];
|
||||
if ( dbg_read_memory(r_brk, opcode, 2, NULL) == 2
|
||||
&& memcmp(opcode, bxlr_thumb, 2) == 0 )
|
||||
{
|
||||
// found it!
|
||||
if ( add_internal_bp(shlib_bpt, r_brk+1) )
|
||||
{
|
||||
dmsg("found r_debug (r_brk=%a)\n", r_brk);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
msg("Failed to find r_debug in " LINKER "\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// Android reports shared objects without any path. Try to find full path.
|
||||
void linux_debmod_t::make_android_abspath(qstring *in_out_path)
|
||||
{
|
||||
if ( qisabspath(in_out_path->c_str()) )
|
||||
return;
|
||||
|
||||
// Apparently /proc didn't return an absolute path. Check /system/lib.
|
||||
// Normally we should not arrive here, this is just for safety.
|
||||
char path[QMAXPATH];
|
||||
qmakepath(path, sizeof(path), "/system/lib", in_out_path->c_str(), NULL);
|
||||
if ( qfileexist(path) )
|
||||
*in_out_path = path;
|
||||
}
|
||||
63
idasdk76/dbg/linux/android.hpp
Normal file
63
idasdk76/dbg/linux/android.hpp
Normal file
@@ -0,0 +1,63 @@
|
||||
/*
|
||||
|
||||
Android specific definitions
|
||||
|
||||
*/
|
||||
|
||||
#ifndef __ANDROID_HPP
|
||||
#define __ANDROID_HPP
|
||||
|
||||
// Android NDK lacks link.h, so we have to use our own definitions
|
||||
|
||||
#include <android/api-level.h>
|
||||
#include "dbg_rpc_handler.h"
|
||||
|
||||
struct link_map
|
||||
{
|
||||
uintptr_t l_addr;
|
||||
char * l_name;
|
||||
uintptr_t l_ld;
|
||||
struct link_map * l_next;
|
||||
struct link_map * l_prev;
|
||||
};
|
||||
|
||||
struct r_debug
|
||||
{
|
||||
int32_t r_version;
|
||||
struct link_map * r_map;
|
||||
int32_t r_brk;
|
||||
// Values for r_state
|
||||
enum
|
||||
{
|
||||
RT_CONSISTENT,
|
||||
RT_ADD,
|
||||
RT_DELETE
|
||||
};
|
||||
int32_t r_state;
|
||||
uintptr_t r_ldbase;
|
||||
};
|
||||
|
||||
// pread64 is missing
|
||||
#define pread64 pread
|
||||
|
||||
// thread_db.h lacks many definitions as well:
|
||||
|
||||
#define TD_MIN_EVENT_NUM 0
|
||||
#define TD_MAX_EVENT_NUM 16
|
||||
#define td_eventismember(set, n) (((set)->events & (1 << (n))) != 0)
|
||||
typedef void *prgregset_t;
|
||||
typedef void *prfpregset_t;
|
||||
typedef int td_thr_type_e;
|
||||
extern "C"
|
||||
{
|
||||
inline td_err_e td_init(void) { return TD_OK; }
|
||||
inline td_err_e td_thr_set_event(const td_thrhandle_t *, td_thr_events_t *) { return TD_OK; }
|
||||
inline td_err_e td_thr_setsigpending(const td_thrhandle_t *, unsigned char, const sigset_t *) { return TD_OK; }
|
||||
}
|
||||
|
||||
// Other missing definitions:
|
||||
typedef int32 __ptrace_request;
|
||||
|
||||
#define user_regs pt_regs
|
||||
|
||||
#endif // define __ANDROID_HPP
|
||||
35
idasdk76/dbg/linux/armlinux_stub.cpp
Normal file
35
idasdk76/dbg/linux/armlinux_stub.cpp
Normal file
@@ -0,0 +1,35 @@
|
||||
#define REMOTE_DEBUGGER
|
||||
#define RPC_CLIENT
|
||||
|
||||
static const char wanted_name[] = "Remote ARM Linux/Android debugger";
|
||||
#define DEBUGGER_NAME "armlinux"
|
||||
#define PROCESSOR_NAME "arm"
|
||||
#define DEFAULT_PLATFORM_NAME "linux"
|
||||
#define TARGET_PROCESSOR PLFM_ARM
|
||||
#define DEBUGGER_ID DEBUGGER_ID_ARM_LINUX_USER
|
||||
#define DEBUGGER_FLAGS (DBG_FLAG_REMOTE \
|
||||
| DBG_FLAG_SMALLBLKS \
|
||||
| DBG_FLAG_LOWCNDS \
|
||||
| DBG_FLAG_DEBTHREAD \
|
||||
| DBG_FLAG_PREFER_SWBPTS)
|
||||
#define HAVE_APPCALL
|
||||
#define S_FILETYPE f_ELF
|
||||
|
||||
#include <pro.h>
|
||||
#include <idp.hpp>
|
||||
#include <idd.hpp>
|
||||
#include <ua.hpp>
|
||||
#include <range.hpp>
|
||||
#include <loader.hpp>
|
||||
#include <kernwin.hpp>
|
||||
#include <network.hpp>
|
||||
|
||||
#include "dbg_rpc_client.h"
|
||||
#include "rpc_debmod.h"
|
||||
|
||||
rpc_debmod_t g_dbgmod(DEFAULT_PLATFORM_NAME);
|
||||
#include "common_stub_impl.cpp"
|
||||
|
||||
#include "arm_local_impl.cpp"
|
||||
#include "linux_local_impl.cpp"
|
||||
#include "common_local_impl.cpp"
|
||||
3693
idasdk76/dbg/linux/linux_debmod.cpp
Normal file
3693
idasdk76/dbg/linux/linux_debmod.cpp
Normal file
File diff suppressed because it is too large
Load Diff
352
idasdk76/dbg/linux/linux_debmod.h
Normal file
352
idasdk76/dbg/linux/linux_debmod.h
Normal file
@@ -0,0 +1,352 @@
|
||||
#ifndef __LINUX_DEBUGGER_MODULE__
|
||||
#define __LINUX_DEBUGGER_MODULE__
|
||||
|
||||
#include <signal.h>
|
||||
#include <sys/ptrace.h>
|
||||
#include <sys/wait.h>
|
||||
#include <sys/user.h>
|
||||
|
||||
#include "linuxbase_debmod.h"
|
||||
|
||||
extern "C"
|
||||
{
|
||||
#include <thread_db.h>
|
||||
}
|
||||
|
||||
#include <deque>
|
||||
|
||||
typedef int HANDLE;
|
||||
typedef uint32 DWORD;
|
||||
#define INVALID_HANDLE_VALUE (-1)
|
||||
|
||||
using std::pair;
|
||||
using std::make_pair;
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
typedef std::map<ea_t, qstring> ea2name_t;
|
||||
typedef std::map<qstring, ea_t> name2ea_t;
|
||||
|
||||
#ifndef WCONTINUED
|
||||
#define WCONTINUED 8
|
||||
#endif
|
||||
|
||||
#ifndef __WALL
|
||||
#define __WALL 0x40000000
|
||||
#endif
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// image information
|
||||
struct image_info_t
|
||||
{
|
||||
image_info_t() : base(BADADDR), size(0), dl_crc(0) {}
|
||||
image_info_t(
|
||||
ea_t _base,
|
||||
asize_t _size,
|
||||
const qstring &_fname,
|
||||
const qstring &_soname)
|
||||
: base(_base), size(_size), fname(_fname), soname(_soname), dl_crc(0) {}
|
||||
ea_t base;
|
||||
asize_t size; // image size, currently 0
|
||||
qstring fname;
|
||||
qstring soname;
|
||||
ea2name_t names;
|
||||
qstring buildid;
|
||||
qstring debuglink;
|
||||
uint32 dl_crc;
|
||||
};
|
||||
typedef std::map<ea_t, image_info_t> images_t; // key: image base address
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
enum thstate_t
|
||||
{
|
||||
RUNNING, // running
|
||||
STOPPED, // waiting to be resumed after qwait
|
||||
DYING, // we got a notification that the thread is about to die
|
||||
DEAD, // dead thread; ignore any signals from it
|
||||
};
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// thread information
|
||||
struct thread_info_t
|
||||
{
|
||||
thread_info_t(int t)
|
||||
: tid(t), suspend_count(0), user_suspend(0), child_signum(0), single_step(false),
|
||||
state(STOPPED), waiting_sigstop(false), got_pending_status(false), pending_status(0) {}
|
||||
int tid;
|
||||
int suspend_count;
|
||||
int user_suspend;
|
||||
int child_signum;
|
||||
bool single_step;
|
||||
thstate_t state;
|
||||
bool waiting_sigstop;
|
||||
bool got_pending_status;
|
||||
int pending_status;
|
||||
qstring name; // thread name
|
||||
bool is_running(void) const
|
||||
{
|
||||
return state == RUNNING && !waiting_sigstop && !got_pending_status;
|
||||
}
|
||||
};
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
typedef std::map<HANDLE, thread_info_t> threads_t; // (tid -> info)
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
enum ps_err_e
|
||||
{
|
||||
PS_OK, /* Success. */
|
||||
PS_ERR, /* Generic error. */
|
||||
PS_BADPID, /* Bad process handle. */
|
||||
PS_BADLID, /* Bad LWP id. */
|
||||
PS_BADADDR, /* Bad address. */
|
||||
PS_NOSYM, /* Symbol not found. */
|
||||
PS_NOFREGS /* FPU register set not available. */
|
||||
};
|
||||
struct ps_prochandle
|
||||
{
|
||||
pid_t pid;
|
||||
};
|
||||
|
||||
#ifndef UINT32_C
|
||||
# define UINT32_C uint32
|
||||
#endif
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
struct internal_bpt
|
||||
{
|
||||
ea_t bpt_addr;
|
||||
uchar saved[BPT_CODE_SIZE];
|
||||
uchar nsaved;
|
||||
internal_bpt(): bpt_addr(0), nsaved(0) {} //-V730 Not all members of a class are initialized
|
||||
};
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
struct mapfp_entry_t
|
||||
{
|
||||
ea_t ea1;
|
||||
ea_t ea2;
|
||||
ea_t offset;
|
||||
uint64 inode;
|
||||
char perm[8];
|
||||
char device[8];
|
||||
uint8 bitness; // Number of bits in segment addresses (0-16bit, 1-32bit, 2-64bit)
|
||||
qstring fname;
|
||||
bool empty(void) const { return ea1 >= ea2; }
|
||||
};
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
struct chk_signal_info_t
|
||||
{
|
||||
pid_t pid;
|
||||
int status;
|
||||
int timeout_ms;
|
||||
|
||||
chk_signal_info_t(int _timeout_ms)
|
||||
{
|
||||
timeout_ms = _timeout_ms;
|
||||
pid = 0;
|
||||
status = 0;
|
||||
}
|
||||
};
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
enum attach_mode_t
|
||||
{
|
||||
AMT_NO_ATTACH,
|
||||
AMT_ATTACH_NORMAL,
|
||||
AMT_ATTACH_BROKEN
|
||||
};
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
class linux_debmod_t: public linuxbase_debmod_t
|
||||
{
|
||||
typedef linuxbase_debmod_t inherited;
|
||||
|
||||
// thread_db related data and functions:
|
||||
struct ps_prochandle prochandle;
|
||||
td_thragent_t *ta;
|
||||
|
||||
internal_bpt birth_bpt; // thread created
|
||||
internal_bpt death_bpt; // thread exited
|
||||
internal_bpt shlib_bpt; // shared lib list changed
|
||||
bool complained_shlib_bpt;
|
||||
|
||||
void make_android_abspath(qstring *in_out_path);
|
||||
bool add_android_shlib_bpt(const meminfo_vec_t &miv, bool attaching);
|
||||
|
||||
bool add_internal_bp(internal_bpt &bp, ea_t addr);
|
||||
bool erase_internal_bp(internal_bpt &bp);
|
||||
|
||||
bool tdb_enable_event(td_event_e event, internal_bpt *bp);
|
||||
void tdb_update_threads(void);
|
||||
bool tdb_new(void);
|
||||
void tdb_delete(void);
|
||||
void tdb_handle_messages(int pid);
|
||||
void dead_thread(int tid, thstate_t state);
|
||||
void store_pending_signal(int pid, int status);
|
||||
|
||||
bool activate_multithreading();
|
||||
|
||||
// procfs
|
||||
void procfs_collect_threads(void);
|
||||
bool attach_collected_thread(unsigned long lwp);
|
||||
bool get_thread_name(qstring *thr_name, thid_t tid);
|
||||
void update_thread_names(thread_name_vec_t *thr_names);
|
||||
|
||||
// list of debug names not yet sent to IDA
|
||||
name_info_t pending_names;
|
||||
name_info_t nptl_names;
|
||||
|
||||
pid_t check_for_signal(int *status, int pid, int timeout_ms) const;
|
||||
|
||||
int find_largest_addrsize(const meminfo_vec_t &miv);
|
||||
void _import_dll(image_info_t &ii);
|
||||
void _import_symbols_from_file(name_info_t *out, image_info_t &ii);
|
||||
|
||||
public:
|
||||
easet_t dlls_to_import; // list of dlls to import information from
|
||||
images_t dlls; // list of loaded DLLs
|
||||
threads_t threads;
|
||||
qvector<thid_t> deleted_threads;
|
||||
qvector<thid_t> seen_threads; // thread was born and appeared too early
|
||||
|
||||
// debugged process information
|
||||
HANDLE process_handle;
|
||||
HANDLE thread_handle;
|
||||
bool threads_collected = false;
|
||||
|
||||
bool exited; // Did the process exit?
|
||||
|
||||
easet_t removed_bpts; // removed breakpoints
|
||||
|
||||
FILE *mapfp; // map file handle
|
||||
|
||||
int npending_signals; // number of pending signals
|
||||
bool may_run;
|
||||
bool requested_to_suspend;
|
||||
bool in_event; // IDA kernel is handling a debugger event
|
||||
|
||||
qstring interp;
|
||||
|
||||
qstring exe_path; // name of the executable file
|
||||
|
||||
ea_t nptl_base; // base of 'libpthread.so'
|
||||
|
||||
regctx_t *reg_ctx;
|
||||
|
||||
linux_debmod_t();
|
||||
~linux_debmod_t();
|
||||
|
||||
void init_reg_ctx(void);
|
||||
void term_reg_ctx(void);
|
||||
|
||||
thread_info_t &add_thread(int tid);
|
||||
void del_thread(int tid);
|
||||
thread_info_t *get_thread(thid_t tid);
|
||||
bool retrieve_pending_signal(pid_t *pid, int *status);
|
||||
int get_debug_event(debug_event_t *event, int timeout_ms);
|
||||
bool del_pending_event(event_id_t id, const char *module_name);
|
||||
void enqueue_event(const debug_event_t &ev, queue_pos_t pos);
|
||||
bool suspend_all_threads(void);
|
||||
bool resume_all_threads(void);
|
||||
int dbg_freeze_threads(thid_t tid, bool exclude=true);
|
||||
int dbg_thaw_threads(thid_t tid, bool exclude=true);
|
||||
void set_thread_state(thread_info_t &ti, thstate_t state) const;
|
||||
bool resume_app(thid_t tid);
|
||||
bool has_pending_events(void);
|
||||
bool read_asciiz(tid_t tid, ea_t ea, char *buf, size_t bufsize, bool suspend=false);
|
||||
int _read_memory(int tid, ea_t ea, void *buffer, int size, bool suspend=false);
|
||||
int _write_memory(int tid, ea_t ea, const void *buffer, int size, bool suspend=false);
|
||||
void add_dll(ea_t base, asize_t size, const char *modname, const char *soname);
|
||||
asize_t calc_module_size(const meminfo_vec_t &miv, const memory_info_t *mi) const;
|
||||
void enum_names(const char *libpath=NULL);
|
||||
bool add_shlib_bpt(const meminfo_vec_t &miv, bool attaching);
|
||||
bool gen_library_events(int tid);
|
||||
bool emulate_retn(int tid);
|
||||
void cleanup(void);
|
||||
bool handle_process_start(pid_t pid, attach_mode_t attaching);
|
||||
drc_t get_memory_info(meminfo_vec_t &areas, bool suspend);
|
||||
bool set_hwbpts(HANDLE hThread) const;
|
||||
virtual bool refresh_hwbpts() override;
|
||||
void handle_dll_movements(const meminfo_vec_t &miv);
|
||||
bool idaapi thread_get_fs_base(thid_t tid, int reg_idx, ea_t *pea) const;
|
||||
bool read_mapping(mapfp_entry_t *me);
|
||||
bool get_soname(const char *fname, qstring *soname) const;
|
||||
ea_t find_pending_name(const char *name);
|
||||
bool handle_hwbpt(debug_event_t *event);
|
||||
bool thread_is_known(const td_thrinfo_t &info) const;
|
||||
bool listen_thread_events(const td_thrinfo_t &info, const td_thrhandle_t *th_p);
|
||||
void attach_to_thread(const td_thrinfo_t &info);
|
||||
bool attach_to_thread(int tid, ea_t ea);
|
||||
void handle_extended_wait(bool *handled, const chk_signal_info_t &csi);
|
||||
bool finish_attaching(int tid, ea_t ea, bool use_ip);
|
||||
bool check_for_new_events(chk_signal_info_t *csi, bool *event_prepared);
|
||||
|
||||
//
|
||||
virtual void idaapi dbg_set_debugging(bool _debug_debugger) override;
|
||||
virtual drc_t idaapi dbg_init(uint32_t *flags2, qstring *errbuf) override;
|
||||
virtual void idaapi dbg_term(void) override;
|
||||
virtual drc_t idaapi dbg_detach_process(void) override;
|
||||
virtual drc_t idaapi dbg_start_process(
|
||||
const char *path,
|
||||
const char *args,
|
||||
const char *startdir,
|
||||
int flags,
|
||||
const char *input_path,
|
||||
uint32 input_file_crc32,
|
||||
qstring *errbuf) override;
|
||||
virtual gdecode_t idaapi dbg_get_debug_event(debug_event_t *event, int timeout_ms) override;
|
||||
virtual drc_t idaapi dbg_attach_process(pid_t process_id, int event_id, int flags, qstring *errbuf) override;
|
||||
virtual drc_t idaapi dbg_prepare_to_pause_process(qstring *errbuf) override;
|
||||
virtual drc_t idaapi dbg_exit_process(qstring *errbuf) override;
|
||||
virtual drc_t idaapi dbg_continue_after_event(const debug_event_t *event) override;
|
||||
virtual void idaapi dbg_stopped_at_debug_event(import_infos_t *infos, bool dlls_added, thread_name_vec_t *thr_names) override;
|
||||
virtual drc_t idaapi dbg_thread_suspend(thid_t thread_id) override;
|
||||
virtual drc_t idaapi dbg_thread_continue(thid_t thread_id) override;
|
||||
virtual drc_t idaapi dbg_set_resume_mode(thid_t thread_id, resume_mode_t resmod) override;
|
||||
virtual drc_t idaapi dbg_read_registers(
|
||||
thid_t thread_id,
|
||||
int clsmask,
|
||||
regval_t *values,
|
||||
qstring *errbuf) override;
|
||||
virtual drc_t idaapi dbg_write_register(
|
||||
thid_t thread_id,
|
||||
int reg_idx,
|
||||
const regval_t *value,
|
||||
qstring *errbuf) override;
|
||||
virtual drc_t idaapi dbg_thread_get_sreg_base(ea_t *ea, thid_t thread_id, int sreg_value, qstring *errbuf) override;
|
||||
virtual drc_t idaapi dbg_get_memory_info(meminfo_vec_t &areas, qstring *errbuf) override;
|
||||
virtual ssize_t idaapi dbg_read_memory(ea_t ea, void *buffer, size_t size, qstring *errbuf) override;
|
||||
virtual ssize_t idaapi dbg_write_memory(ea_t ea, const void *buffer, size_t size, qstring *errbuf) override;
|
||||
virtual int idaapi dbg_add_bpt(bytevec_t *orig_bytes, bpttype_t type, ea_t ea, int len) override;
|
||||
virtual int idaapi dbg_del_bpt(bpttype_t type, ea_t ea, const uchar *orig_bytes, int len) override;
|
||||
virtual int idaapi handle_ioctl(int fn, const void *buf, size_t size, void **outbuf, ssize_t *outsize) override;
|
||||
virtual bool idaapi write_registers(
|
||||
thid_t tid,
|
||||
int start,
|
||||
int count,
|
||||
const regval_t *values) override;
|
||||
virtual int dbg_freeze_threads_except(thid_t tid) override { return dbg_freeze_threads(tid); }
|
||||
virtual int dbg_thaw_threads_except(thid_t tid) override { return dbg_thaw_threads(tid); }
|
||||
|
||||
virtual bool idaapi dbg_continue_broken_connection(pid_t pid) override;
|
||||
virtual bool idaapi dbg_prepare_broken_connection(void) override;
|
||||
|
||||
// thread_db
|
||||
void display_thrinfo(thid_t tid);
|
||||
void display_all_threads();
|
||||
|
||||
void cleanup_breakpoints(void);
|
||||
void cleanup_signals(void);
|
||||
|
||||
bool fix_instruction_pointer(void) const;
|
||||
#ifdef __ARM__
|
||||
virtual void adjust_swbpt(ea_t *p_ea, int *p_len) override;
|
||||
#endif
|
||||
|
||||
#ifdef LDEB
|
||||
void log(thid_t tid, const char *format, ...);
|
||||
#endif
|
||||
};
|
||||
|
||||
#endif
|
||||
15
idasdk76/dbg/linux/linux_debmod.script
Normal file
15
idasdk76/dbg/linux/linux_debmod.script
Normal file
@@ -0,0 +1,15 @@
|
||||
{
|
||||
global:
|
||||
PLUGIN;
|
||||
ps_pglobal_lookup;
|
||||
ps_pdread;
|
||||
ps_pdwrite;
|
||||
ps_lgetregs;
|
||||
ps_lsetregs;
|
||||
ps_lgetfpregs;
|
||||
ps_lsetfpregs;
|
||||
ps_getpid;
|
||||
ps_get_thread_area;
|
||||
local:
|
||||
*;
|
||||
};
|
||||
66
idasdk76/dbg/linux/linux_local_impl.cpp
Normal file
66
idasdk76/dbg/linux/linux_local_impl.cpp
Normal file
@@ -0,0 +1,66 @@
|
||||
#include <loader.hpp>
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// installs or uninstalls debugger specific idc functions
|
||||
inline bool register_idc_funcs(bool)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
void idaapi rebase_if_required_to(ea_t new_base)
|
||||
{
|
||||
ea_t base = get_imagebase();
|
||||
if ( base != BADADDR && new_base != BADADDR && base != new_base )
|
||||
rebase_or_warn(base, new_base);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
static bool init_plugin(void)
|
||||
{
|
||||
#ifndef RPC_CLIENT
|
||||
if ( !init_subsystem() )
|
||||
return false;
|
||||
#endif
|
||||
|
||||
bool ok = false;
|
||||
do
|
||||
{
|
||||
if ( !netnode::inited() || is_miniidb() || inf_is_snapshot() )
|
||||
{
|
||||
#ifdef __LINUX__
|
||||
// local debugger is available if we are running under Linux
|
||||
return true;
|
||||
#else
|
||||
// for other systems only the remote debugger is available
|
||||
if ( debugger.is_remote() )
|
||||
return true;
|
||||
break; // failed
|
||||
#endif
|
||||
}
|
||||
|
||||
if ( inf_get_filetype() != f_ELF )
|
||||
break;
|
||||
processor_t &ph = PH;
|
||||
if ( ph.id != TARGET_PROCESSOR && ph.id != -1 )
|
||||
break;
|
||||
|
||||
ok = true;
|
||||
} while ( false );
|
||||
#ifndef RPC_CLIENT
|
||||
if ( !ok )
|
||||
term_subsystem();
|
||||
#endif
|
||||
return ok;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
inline void term_plugin(void)
|
||||
{
|
||||
#ifndef RPC_CLIENT
|
||||
term_subsystem();
|
||||
#endif
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
static const char comment[] = "Userland linux debugger plugin.";
|
||||
34
idasdk76/dbg/linux/linux_stub.cpp
Normal file
34
idasdk76/dbg/linux/linux_stub.cpp
Normal file
@@ -0,0 +1,34 @@
|
||||
#define REMOTE_DEBUGGER
|
||||
#define RPC_CLIENT
|
||||
|
||||
static const char wanted_name[] = "Remote Linux debugger";
|
||||
#define DEBUGGER_NAME "linux"
|
||||
#define PROCESSOR_NAME "metapc"
|
||||
#define DEFAULT_PLATFORM_NAME "linux"
|
||||
#define TARGET_PROCESSOR PLFM_386
|
||||
#define DEBUGGER_ID DEBUGGER_ID_X86_IA32_LINUX_USER
|
||||
#define DEBUGGER_FLAGS (DBG_FLAG_REMOTE \
|
||||
| DBG_FLAG_LOWCNDS \
|
||||
| DBG_FLAG_DEBTHREAD)
|
||||
#define DEBUGGER_RESMOD (DBG_RESMOD_STEP_INTO)
|
||||
#define HAVE_APPCALL
|
||||
#define S_FILETYPE f_ELF
|
||||
|
||||
#include <pro.h>
|
||||
#include <idp.hpp>
|
||||
#include <idd.hpp>
|
||||
#include <ua.hpp>
|
||||
#include <range.hpp>
|
||||
#include <loader.hpp>
|
||||
#include <kernwin.hpp>
|
||||
#include <network.hpp>
|
||||
|
||||
#include "dbg_rpc_client.h"
|
||||
#include "rpc_debmod.h"
|
||||
|
||||
rpc_debmod_t g_dbgmod(DEFAULT_PLATFORM_NAME);
|
||||
#include "common_stub_impl.cpp"
|
||||
|
||||
#include "pc_local_impl.cpp"
|
||||
#include "linux_local_impl.cpp"
|
||||
#include "common_local_impl.cpp"
|
||||
1355
idasdk76/dbg/linux/linux_threads.cpp
Normal file
1355
idasdk76/dbg/linux/linux_threads.cpp
Normal file
File diff suppressed because it is too large
Load Diff
26
idasdk76/dbg/linux/linux_user.cpp
Normal file
26
idasdk76/dbg/linux/linux_user.cpp
Normal file
@@ -0,0 +1,26 @@
|
||||
|
||||
static const char wanted_name[] = "Local Linux debugger";
|
||||
#define DEBUGGER_NAME "linux"
|
||||
#define PROCESSOR_NAME "metapc"
|
||||
#define TARGET_PROCESSOR PLFM_386
|
||||
#define DEBUGGER_ID DEBUGGER_ID_X86_IA32_LINUX_USER
|
||||
#define DEBUGGER_FLAGS (DBG_FLAG_LOWCNDS \
|
||||
| DBG_FLAG_DEBTHREAD)
|
||||
#define DEBUGGER_RESMOD (DBG_RESMOD_STEP_INTO)
|
||||
#define HAVE_APPCALL
|
||||
#define S_FILETYPE f_ELF
|
||||
|
||||
#include <fpro.h>
|
||||
#include <idd.hpp>
|
||||
#include <ua.hpp>
|
||||
#include <range.hpp>
|
||||
#include <loader.hpp>
|
||||
#include "linux_debmod.h"
|
||||
|
||||
linux_debmod_t g_dbgmod;
|
||||
bool ignore_sigint = false;
|
||||
#include "common_stub_impl.cpp"
|
||||
|
||||
#include "pc_local_impl.cpp"
|
||||
#include "linux_local_impl.cpp"
|
||||
#include "common_local_impl.cpp"
|
||||
8
idasdk76/dbg/linux/linux_wait.cpp
Normal file
8
idasdk76/dbg/linux/linux_wait.cpp
Normal file
@@ -0,0 +1,8 @@
|
||||
|
||||
#include <pro.h>
|
||||
#include "linux_debmod.h"
|
||||
//--------------------------------------------------------------------------
|
||||
pid_t linux_debmod_t::check_for_signal(int *status, int _pid, int timeout_ms) const
|
||||
{
|
||||
return qwait_timed(status, _pid, __WALL | WCONTINUED, timeout_ms);
|
||||
}
|
||||
146
idasdk76/dbg/linux/linuxbase_debmod.cpp
Normal file
146
idasdk76/dbg/linux/linuxbase_debmod.cpp
Normal file
@@ -0,0 +1,146 @@
|
||||
#include <fpro.h>
|
||||
#include <prodir.h>
|
||||
#include <diskio.hpp>
|
||||
#include "linuxbase_debmod.h"
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
static inline const char *str_bitness(int bitness)
|
||||
{
|
||||
switch ( bitness )
|
||||
{
|
||||
case 8:
|
||||
return "[64]";
|
||||
case 4:
|
||||
return "[32]";
|
||||
default:
|
||||
return "[x]";
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
static void build_process_ext_name(ext_process_info_t *pinfo)
|
||||
{
|
||||
pinfo->ext_name = str_bitness(pinfo->addrsize);
|
||||
|
||||
char buf[QMAXPATH];
|
||||
qsnprintf(buf, sizeof(buf), "/proc/%u/cmdline", pinfo->pid);
|
||||
|
||||
FILE *cmdfp = qfopen(buf, "r");
|
||||
if ( cmdfp == nullptr )
|
||||
return;
|
||||
|
||||
int size = qfread(cmdfp, buf, sizeof(buf));
|
||||
buf[size] = '\0';
|
||||
qfclose(cmdfp);
|
||||
|
||||
#ifdef __ANDROID__
|
||||
while ( size >= 0 && buf[size] == '\0' )
|
||||
size--;
|
||||
size++;
|
||||
#endif
|
||||
|
||||
// arguments are separated by '\0'
|
||||
for ( int i=0; i < size; )
|
||||
{
|
||||
const char *in = &buf[i];
|
||||
qstring arg = in;
|
||||
quote_cmdline_arg(&arg);
|
||||
pinfo->ext_name.append(" ");
|
||||
pinfo->ext_name.append(arg);
|
||||
|
||||
i += strlen(in) + 1;
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// Returns the file name assciated with pid
|
||||
bool idaapi linuxbase_debmod_t::get_exec_fname(
|
||||
int _pid,
|
||||
char *buf,
|
||||
size_t bufsize)
|
||||
{
|
||||
char path[QMAXPATH];
|
||||
qsnprintf(path, sizeof(path), "/proc/%u/exe", _pid);
|
||||
int len = readlink(path, buf, bufsize-1);
|
||||
if ( len > 0 )
|
||||
{
|
||||
buf[len] = '\0';
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
// ESXi keeps the real file name inside /proc/PID/exe (which is not a link)
|
||||
FILE *fp = qfopen(path, "r");
|
||||
if ( fp != NULL )
|
||||
{
|
||||
len = qfread(fp, buf, bufsize);
|
||||
qfclose(fp);
|
||||
if ( len > 1 && len < bufsize && buf[0] == '/' ) // sanity check
|
||||
{
|
||||
buf[len] = '\0';
|
||||
return true;
|
||||
}
|
||||
}
|
||||
buf[0] = '\0';
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// Get process bitness: 32bit - 4, 64bit - 8, 0 - unknown
|
||||
int idaapi linuxbase_debmod_t::get_process_bitness(int _pid)
|
||||
{
|
||||
char fname[QMAXPATH];
|
||||
qsnprintf(fname, sizeof(fname), "/proc/%u/maps", _pid);
|
||||
FILE *mapfp = fopenRT(fname);
|
||||
if ( mapfp == NULL )
|
||||
return 0;
|
||||
|
||||
int bitness = 4;
|
||||
qstring line;
|
||||
while ( qgetline(&line, mapfp) >= 0 )
|
||||
{
|
||||
if ( line.empty() )
|
||||
continue;
|
||||
ea_t ea1;
|
||||
ea_t ea2;
|
||||
if ( qsscanf(line.begin(), "%a-%a ", &ea1, &ea2) == 2 )
|
||||
{
|
||||
size_t pos = line.find('-');
|
||||
if ( pos != qstring::npos && pos > 8 )
|
||||
{
|
||||
bitness = 8;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
qfclose(mapfp);
|
||||
return bitness;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
int idaapi linuxbase_debmod_t::get_process_list(procvec_t *list, qstring *)
|
||||
{
|
||||
int mypid = getpid();
|
||||
list->clear();
|
||||
qffblk64_t fb;
|
||||
for ( int code = qfindfirst("/proc/*", &fb, FA_DIREC);
|
||||
code == 0;
|
||||
code = qfindnext(&fb) )
|
||||
{
|
||||
if ( !qisdigit(fb.ff_name[0]) )
|
||||
continue;
|
||||
ext_process_info_t pinfo;
|
||||
pinfo.pid = atoi(fb.ff_name);
|
||||
if ( pinfo.pid == mypid )
|
||||
continue;
|
||||
char buf[MAXSTR];
|
||||
if ( !get_exec_fname(pinfo.pid, buf, sizeof(buf)) )
|
||||
continue; // we skip the process because we cannot debug it anyway
|
||||
pinfo.name = buf;
|
||||
pinfo.addrsize = get_process_bitness(pinfo.pid);
|
||||
build_process_ext_name(&pinfo);
|
||||
list->push_back(pinfo);
|
||||
}
|
||||
return list->size();
|
||||
}
|
||||
30
idasdk76/dbg/linux/linuxbase_debmod.h
Normal file
30
idasdk76/dbg/linux/linuxbase_debmod.h
Normal file
@@ -0,0 +1,30 @@
|
||||
#ifndef __LINUXBASE_HPP__
|
||||
#define __LINUXBASE_HPP__
|
||||
|
||||
#include "debmod.h"
|
||||
|
||||
// Base class for linux modules
|
||||
|
||||
#ifdef __ARM__
|
||||
# define BASE_DEBUGGER_MODULE arm_debmod_t
|
||||
# include "arm_debmod.h"
|
||||
# define BPT_CODE_SIZE ARM_BPT_SIZE
|
||||
#else
|
||||
# define BASE_DEBUGGER_MODULE pc_debmod_t
|
||||
# include "pc_debmod.h"
|
||||
# define BPT_CODE_SIZE X86_BPT_SIZE
|
||||
#endif
|
||||
|
||||
class linuxbase_debmod_t: public BASE_DEBUGGER_MODULE
|
||||
{
|
||||
typedef BASE_DEBUGGER_MODULE inherited;
|
||||
protected:
|
||||
// return number of processes, -1 - not implemented
|
||||
virtual int idaapi get_process_list(procvec_t *proclist, qstring *errbuf) override;
|
||||
// return the file name assciated with pid
|
||||
virtual bool idaapi get_exec_fname(int pid, char *buf, size_t bufsize) newapi;
|
||||
// get process bitness: 32bit - 4, 64bit - 8, 0 - unknown
|
||||
virtual int idaapi get_process_bitness(int pid) newapi;
|
||||
};
|
||||
|
||||
#endif
|
||||
164
idasdk76/dbg/linux/makefile
Normal file
164
idasdk76/dbg/linux/makefile
Normal file
@@ -0,0 +1,164 @@
|
||||
include ../../allmake.mak
|
||||
|
||||
GOALS-$(BUILD_IDA) += modules # target in $(IDA)module.mak
|
||||
GOALS-$(BUILD_DBGSRV) += server # target in $(IDA)dbg/server.mak
|
||||
.PHONY: $(GOALS-1)
|
||||
all: $(GOALS-1)
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
ifdef __LINUX__
|
||||
SERVER = linux_server$(SUFF64)
|
||||
endif
|
||||
ifdef SERVER
|
||||
SERVERS += $(call server_exe,$(SERVER))
|
||||
endif
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
USER = $(call module_dll,linux_user)
|
||||
ifeq ($(and $(BUILD_IDA),$(__LINUX__)),1)
|
||||
MODULES += $(USER)
|
||||
endif
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
# we explicitly added our module targets
|
||||
NO_DEFAULT_TARGETS = 1
|
||||
|
||||
# NOTE: all MODULES must be defined before including plugin.mak.
|
||||
include ../plugin.mak
|
||||
# NOTE: target-specific rules and dependencies that use variable
|
||||
# expansion to name the target (such as "$(MODULE): [...]") must
|
||||
# come after including plugin.mak
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
# select OBJS common to user plugin and debugger server
|
||||
ifeq ($(or $(__LINUX__),$(__ANDROID__),$(__ANDROID_X86__),$(__ARMLINUX__)),1)
|
||||
BUILD_LINUX:=1
|
||||
endif
|
||||
|
||||
BASE_OBJS-$(BUILD_LINUX) += $(F)linuxbase_debmod$(O)
|
||||
BASE_OBJS-$(BUILD_LINUX) += $(F)linux_debmod$(O)
|
||||
BASE_OBJS-$(BUILD_LINUX) += $(F)linux_wait$(O)
|
||||
|
||||
BASE_OBJS += $(BASE_OBJS-1) $(F)symelf$(O)
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
ifdef __LINUX__
|
||||
SERVER_LDFLAGS += -Wl,--version-script=linux_debmod.script
|
||||
SERVER_LDFLAGS += $(OUTMAP)$(F)$(@F).map
|
||||
SERVER_STDLIBS += -lthread_db -lrt -lc -lpthread -ldl
|
||||
endif
|
||||
SERVER_OBJS += $(BASE_OBJS)
|
||||
|
||||
# suppress warnings for libthread_db.c
|
||||
$(F)libthread_db$(O): WARNS = $(NOWARNS)
|
||||
|
||||
include ../server.mak
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
STUB_OBJS += $(F)linux_stub$(O)
|
||||
$(STUB): MODULE_OBJS += $(STUB_OBJS)
|
||||
$(STUB): $(STUB_OBJS)
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
USER_OBJS += $(F)linux_user$(O)
|
||||
USER_OBJS += $(BASE_OBJS)
|
||||
$(USER): MODULE_OBJS += $(USER_OBJS)
|
||||
$(USER): $(USER_OBJS)
|
||||
$(USER): DEFFILE = linux_debmod.script
|
||||
$(USER): STDLIBS += -ldl
|
||||
$(USER): STDLIBS += -lthread_db
|
||||
|
||||
ifeq ($(COMPILER_NAME),gcc)
|
||||
$(USER): LDFLAGS += -Wl,--export-dynamic
|
||||
endif
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
include $(IDA)objdir.mak
|
||||
|
||||
# MAKEDEP dependency list ------------------
|
||||
$(F)armlinux_stub$(O): $(I)bitrange.hpp $(I)bytes.hpp $(I)config.hpp \
|
||||
$(I)dbg.hpp $(I)err.h $(I)expr.hpp $(I)fpro.h \
|
||||
$(I)funcs.hpp $(I)ida.hpp $(I)idd.hpp $(I)idp.hpp \
|
||||
$(I)kernwin.hpp $(I)lines.hpp $(I)llong.hpp \
|
||||
$(I)loader.hpp $(I)nalt.hpp $(I)name.hpp $(I)netnode.hpp \
|
||||
$(I)network.hpp $(I)pro.h $(I)range.hpp $(I)segment.hpp \
|
||||
$(I)segregs.hpp $(I)typeinf.hpp $(I)ua.hpp $(I)xref.hpp \
|
||||
../arm_local_impl.cpp ../arm_regs.cpp ../arm_regs.hpp \
|
||||
../common_local_impl.cpp ../common_stub_impl.cpp \
|
||||
../dbg_rpc_client.h ../dbg_rpc_engine.h ../deb_arm.hpp \
|
||||
../debmod.h ../rpc_debmod.h armlinux_stub.cpp \
|
||||
linux_local_impl.cpp
|
||||
$(F)libthread_db$(O): libthread_db.c
|
||||
$(F)linux_debmod$(O): $(I)bitrange.hpp $(I)bytes.hpp $(I)config.hpp \
|
||||
$(I)diskio.hpp $(I)err.h $(I)fpro.h $(I)funcs.hpp \
|
||||
$(I)ida.hpp $(I)idd.hpp $(I)idp.hpp $(I)kernwin.hpp \
|
||||
$(I)lines.hpp $(I)llong.hpp $(I)loader.hpp $(I)nalt.hpp \
|
||||
$(I)name.hpp $(I)netnode.hpp $(I)network.hpp $(I)pro.h \
|
||||
$(I)prodir.h $(I)range.hpp $(I)segment.hpp $(I)ua.hpp \
|
||||
$(I)xref.hpp ../../plugins/dwarf/look_for_debug_file.cpp \
|
||||
../arm_debmod.h ../arm_regs.hpp ../dbg_rpc_engine.h \
|
||||
../dbg_rpc_handler.h ../dbg_rpc_handler_ioctls.h \
|
||||
../deb_arm.hpp ../deb_pc.hpp ../debmod.h ../pc_debmod.h \
|
||||
../pc_regs.hpp android.cpp android.hpp linux_debmod.cpp \
|
||||
linux_debmod.h linux_threads.cpp linuxbase_debmod.h \
|
||||
symelf.hpp
|
||||
$(F)linux_stub$(O): $(I)bitrange.hpp $(I)bytes.hpp $(I)config.hpp \
|
||||
$(I)dbg.hpp $(I)err.h $(I)expr.hpp $(I)fpro.h \
|
||||
$(I)funcs.hpp $(I)ida.hpp $(I)idd.hpp $(I)idp.hpp \
|
||||
$(I)kernwin.hpp $(I)lines.hpp $(I)llong.hpp \
|
||||
$(I)loader.hpp $(I)nalt.hpp $(I)name.hpp $(I)netnode.hpp \
|
||||
$(I)network.hpp $(I)pro.h $(I)range.hpp $(I)segment.hpp \
|
||||
$(I)segregs.hpp $(I)typeinf.hpp $(I)ua.hpp $(I)xref.hpp \
|
||||
../common_local_impl.cpp ../common_stub_impl.cpp \
|
||||
../dbg_rpc_client.h ../dbg_rpc_engine.h ../deb_pc.hpp \
|
||||
../debmod.h ../pc_local_impl.cpp ../pc_regs.hpp \
|
||||
../rpc_debmod.h linux_local_impl.cpp linux_stub.cpp
|
||||
$(F)linux_user$(O): $(I)bitrange.hpp $(I)bytes.hpp $(I)config.hpp \
|
||||
$(I)dbg.hpp $(I)err.h $(I)expr.hpp $(I)fpro.h \
|
||||
$(I)funcs.hpp $(I)ida.hpp $(I)idd.hpp $(I)idp.hpp \
|
||||
$(I)kernwin.hpp $(I)lines.hpp $(I)llong.hpp \
|
||||
$(I)loader.hpp $(I)nalt.hpp $(I)name.hpp $(I)netnode.hpp \
|
||||
$(I)network.hpp $(I)pro.h $(I)range.hpp $(I)segment.hpp \
|
||||
$(I)segregs.hpp $(I)typeinf.hpp $(I)ua.hpp $(I)xref.hpp \
|
||||
../arm_debmod.h ../common_local_impl.cpp \
|
||||
../common_stub_impl.cpp ../deb_arm.hpp ../deb_pc.hpp \
|
||||
../debmod.h ../pc_debmod.h ../pc_local_impl.cpp \
|
||||
../pc_regs.hpp linux_debmod.h linux_local_impl.cpp \
|
||||
linux_user.cpp linuxbase_debmod.h
|
||||
$(F)linux_wait$(O): $(I)bytes.hpp $(I)ida.hpp $(I)idd.hpp $(I)kernwin.hpp \
|
||||
$(I)lines.hpp $(I)llong.hpp $(I)nalt.hpp $(I)netnode.hpp \
|
||||
$(I)network.hpp $(I)pro.h $(I)range.hpp $(I)ua.hpp \
|
||||
$(I)xref.hpp ../arm_debmod.h ../deb_arm.hpp \
|
||||
../deb_pc.hpp ../debmod.h ../pc_debmod.h ../pc_regs.hpp \
|
||||
linux_debmod.h linux_wait.cpp linuxbase_debmod.h
|
||||
$(F)linuxbase_debmod$(O): $(I)bytes.hpp $(I)diskio.hpp $(I)fpro.h \
|
||||
$(I)ida.hpp $(I)idd.hpp $(I)kernwin.hpp $(I)lines.hpp \
|
||||
$(I)llong.hpp $(I)nalt.hpp $(I)netnode.hpp \
|
||||
$(I)network.hpp $(I)pro.h $(I)prodir.h $(I)range.hpp \
|
||||
$(I)ua.hpp $(I)xref.hpp ../arm_debmod.h ../deb_arm.hpp \
|
||||
../deb_pc.hpp ../debmod.h ../pc_debmod.h ../pc_regs.hpp \
|
||||
linuxbase_debmod.cpp linuxbase_debmod.h
|
||||
$(F)symelf$(O) : $(I)auto.hpp $(I)bitrange.hpp $(I)bytes.hpp \
|
||||
$(I)config.hpp $(I)diskio.hpp $(I)entry.hpp \
|
||||
$(I)fixup.hpp $(I)fpro.h $(I)funcs.hpp \
|
||||
$(I)ida.hpp $(I)idd.hpp $(I)idp.hpp $(I)kernwin.hpp \
|
||||
$(I)lines.hpp $(I)llong.hpp $(I)loader.hpp $(I)nalt.hpp \
|
||||
$(I)name.hpp $(I)netnode.hpp $(I)network.hpp \
|
||||
$(I)offset.hpp $(I)pro.h $(I)range.hpp $(I)segment.hpp \
|
||||
$(I)segregs.hpp $(I)ua.hpp $(I)xref.hpp \
|
||||
../../ldr/elf/../idaldr.h ../../ldr/elf/common.cpp \
|
||||
../../ldr/elf/elf.h ../../ldr/elf/elfbase.h \
|
||||
\
|
||||
\
|
||||
\
|
||||
\
|
||||
\
|
||||
\
|
||||
\
|
||||
\
|
||||
\
|
||||
\
|
||||
\
|
||||
\
|
||||
../../ldr/elf/reader.cpp \
|
||||
../debmod.h symelf.cpp symelf.hpp
|
||||
239
idasdk76/dbg/linux/symelf.cpp
Normal file
239
idasdk76/dbg/linux/symelf.cpp
Normal file
@@ -0,0 +1,239 @@
|
||||
|
||||
// read elf symbols
|
||||
|
||||
#include <fpro.h>
|
||||
#include <kernwin.hpp>
|
||||
#include <diskio.hpp>
|
||||
#include "../../ldr/elf/elfbase.h"
|
||||
#include "../../ldr/elf/elf.h"
|
||||
#include "debmod.h"
|
||||
#include "symelf.hpp"
|
||||
|
||||
#include "../../ldr/elf/common.cpp"
|
||||
#include "../../ldr/elf/reader.cpp"
|
||||
|
||||
inline uint32 low(uint32 x) { return x; }
|
||||
|
||||
uval_t imagebase;
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
//lint -e{1764} could be declared const ref
|
||||
static int handle_symbol(
|
||||
reader_t &reader,
|
||||
int shndx,
|
||||
int _info,
|
||||
uint32 st_name,
|
||||
uval_t st_value,
|
||||
slice_type_t slice_type,
|
||||
symbol_visitor_t &sv)
|
||||
{
|
||||
if ( shndx == SHN_UNDEF
|
||||
|| shndx == SHN_LOPROC
|
||||
|| shndx == SHN_HIPROC
|
||||
|| shndx == SHN_ABS )
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
int type = ELF_ST_TYPE(_info);
|
||||
if ( type != STT_OBJECT && type != STT_FUNC )
|
||||
return 0;
|
||||
|
||||
if ( st_name == 0 )
|
||||
return 0;
|
||||
|
||||
if ( imagebase != uval_t(-1) )
|
||||
st_value -= imagebase;
|
||||
|
||||
qstring name;
|
||||
reader.get_name(&name, slice_type, st_name);
|
||||
return sv.visit_symbol(st_value, name.c_str());
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
static int load_symbols(
|
||||
reader_t &reader,
|
||||
const elf_shdr_t §ion,
|
||||
slice_type_t slice_type,
|
||||
symbol_visitor_t &sv)
|
||||
{
|
||||
int code = 0;
|
||||
sym_rel *sym;
|
||||
buffered_input_t<sym_rel> symbols_input(reader, section);
|
||||
for ( elf_sym_idx_t i = 0; code == 0 && symbols_input.next(sym); ++i )
|
||||
{
|
||||
if ( i == 0 ) // skip _UNDEF
|
||||
continue;
|
||||
|
||||
code = handle_symbol(reader,
|
||||
sym->original.st_shndx,
|
||||
sym->original.st_info,
|
||||
sym->original.st_name,
|
||||
sym->original.st_value,
|
||||
slice_type,
|
||||
sv);
|
||||
}
|
||||
return code;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
static bool map_pht(reader_t &reader)
|
||||
{
|
||||
if ( !reader.read_program_headers() )
|
||||
return false;
|
||||
|
||||
imagebase = reader.pheaders.get_image_base();
|
||||
return true;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
static bool silent_handler(const reader_t &reader, reader_t::errcode_t code, ...)
|
||||
{
|
||||
return reader.is_warning(code); // resume after warnings
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
static int _load_elf_symbols(linput_t *li, symbol_visitor_t &sv)
|
||||
{
|
||||
reader_t reader(li);
|
||||
reader.set_handler(silent_handler);
|
||||
if ( !reader.read_ident() || !reader.read_header() )
|
||||
return -1;
|
||||
|
||||
const elf_ident_t &ident = reader.get_ident();
|
||||
uint8 elf_class = ident.elf_class;
|
||||
if ( elf_class != ELFCLASS32 && elf_class != ELFCLASS64 )
|
||||
return -1;
|
||||
|
||||
uint8 elf_data_ord = ident.bytesex;
|
||||
if ( elf_data_ord != ELFDATA2LSB && elf_data_ord != ELFDATA2MSB )
|
||||
return -1;
|
||||
|
||||
section_headers_t §ions = reader.sections;
|
||||
dynamic_linking_tables_t dlt;
|
||||
|
||||
int code = 0;
|
||||
elf_ehdr_t &header = reader.get_header();
|
||||
if ( header.has_pht() && !map_pht(reader) )
|
||||
return -1;
|
||||
|
||||
reader.read_section_headers();
|
||||
|
||||
// Try and acquire dynamic linking tables info.
|
||||
dlt = reader.sections.get_dynamic_linking_tables_info();
|
||||
if ( !dlt.is_valid() )
|
||||
dlt = reader.pheaders.get_dynamic_linking_tables_info();
|
||||
|
||||
// Parse dynamic info if available
|
||||
dynamic_info_t di;
|
||||
if ( dlt.is_valid() )
|
||||
{
|
||||
reader_t::dyninfo_tags_t dyninfo_tags;
|
||||
dyninfo_tags.reserve(10);
|
||||
if ( reader.read_dynamic_info_tags(&dyninfo_tags, dlt)
|
||||
&& reader.parse_dynamic_info(&di, dyninfo_tags)
|
||||
&& (sv.velf & VISIT_DYNINFO) != 0 )
|
||||
{
|
||||
reader.set_di_strtab(reader.dyn_strtab, di.strtab());
|
||||
typedef reader_t::dyninfo_tags_t::const_iterator const_it;
|
||||
for ( const_it dyn = dyninfo_tags.begin();
|
||||
dyn != dyninfo_tags.end();
|
||||
++dyn )
|
||||
{
|
||||
qstring name;
|
||||
switch ( dyn->d_tag )
|
||||
{
|
||||
case DT_SONAME:
|
||||
case DT_RPATH:
|
||||
case DT_RUNPATH:
|
||||
case DT_NEEDED:
|
||||
reader.get_name(&name, reader.dyn_strtab, uint32(dyn->d_un));
|
||||
break;
|
||||
}
|
||||
if ( sv.visit_dyninfo(dyn->d_tag, name.c_str(), dyn->d_un) != 0 )
|
||||
break;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
if ( (sv.velf & VISIT_INTERP) != 0 )
|
||||
{
|
||||
elf_shdr_t *interp_sh = reader.sections.get_wks(WKS_INTERP);
|
||||
if ( interp_sh != NULL )
|
||||
{
|
||||
qstring name;
|
||||
reader.get_string_at(&name, interp_sh->sh_offset);
|
||||
code = sv.visit_interp(name.c_str());
|
||||
if ( code != 0 )
|
||||
return code;
|
||||
}
|
||||
}
|
||||
|
||||
if ( (sv.velf & VISIT_SYMBOLS) != 0 )
|
||||
{
|
||||
elf_shndx_t symtab = sections.get_index(WKS_SYMTAB);
|
||||
elf_shndx_t dynsym = sections.get_index(WKS_DYNSYM);
|
||||
elf_shdr_t fake_section;
|
||||
if ( symtab != 0 || dynsym != 0 )
|
||||
{
|
||||
// Loading symbols
|
||||
if ( symtab != 0 )
|
||||
code = load_symbols(reader, *sections.getn(symtab), SLT_SYMTAB, sv);
|
||||
if ( code == 0 && dynsym != 0 )
|
||||
code = load_symbols(reader, *sections.getn(dynsym), SLT_DYNSYM, sv);
|
||||
}
|
||||
else if ( di.fill_section_header(&fake_section, DIT_SYMTAB) )
|
||||
{
|
||||
code = load_symbols(reader, fake_section, SLT_DYNSYM, sv);
|
||||
}
|
||||
}
|
||||
|
||||
notes_t notes(&reader);
|
||||
if ( (sv.velf & VISIT_BUILDID) != 0 && reader.read_notes(¬es) )
|
||||
{
|
||||
qstring id;
|
||||
if ( notes.get_build_id(&id) )
|
||||
{
|
||||
code = sv.visit_buildid(id.c_str());
|
||||
if ( code != 0 )
|
||||
return code;
|
||||
}
|
||||
}
|
||||
|
||||
if ( (sv.velf & VISIT_DBGLINK ) != 0 )
|
||||
{
|
||||
uint32 crc;
|
||||
qstring debuglink;
|
||||
if ( sections.is_initialized()
|
||||
&& sections.read_gnu_debuglink(&debuglink, &crc) )
|
||||
{
|
||||
code = sv.visit_debuglink(debuglink.c_str(), crc);
|
||||
if ( code != 0 )
|
||||
return code;
|
||||
}
|
||||
}
|
||||
|
||||
return code;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
static int load_linput_elf_symbols(linput_t *li, symbol_visitor_t &sv)
|
||||
{
|
||||
if ( li == NULL )
|
||||
return -1;
|
||||
int code;
|
||||
// there is thread unsafe code in elf handling, so use locks
|
||||
lock_begin();
|
||||
{
|
||||
code = _load_elf_symbols(li, sv);
|
||||
}
|
||||
lock_end();
|
||||
close_linput(li);
|
||||
return code;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
int load_elf_symbols(const char *fname, symbol_visitor_t &sv, bool remote)
|
||||
{
|
||||
return load_linput_elf_symbols(open_linput(fname, remote), sv);
|
||||
}
|
||||
30
idasdk76/dbg/linux/symelf.hpp
Normal file
30
idasdk76/dbg/linux/symelf.hpp
Normal file
@@ -0,0 +1,30 @@
|
||||
// read symbols from an elf file
|
||||
#ifndef __SYMELF__
|
||||
#define __SYMELF__
|
||||
|
||||
struct symbol_visitor_t
|
||||
{
|
||||
symbol_visitor_t(int visit_flags) : velf(visit_flags) {}
|
||||
|
||||
int velf;
|
||||
#define VISIT_SYMBOLS 0x0001
|
||||
#define VISIT_INTERP 0x0002
|
||||
#define VISIT_DYNINFO 0x0004
|
||||
#define VISIT_SEGMENTS 0x0008
|
||||
#define VISIT_BUILDID 0x0010
|
||||
#define VISIT_DBGLINK 0x0020
|
||||
|
||||
// any callback returns nonzero - stop enumeration
|
||||
virtual int visit_symbol(ea_t /*ea*/, const char * /*name*/) { return 0; }
|
||||
virtual int visit_interp(const char * /*name*/) { return 0; }
|
||||
virtual int visit_segment(ea_t /*start*/, size_t /*size*/, const char * /*name*/) { return 0; }
|
||||
virtual int visit_dyninfo(uint64 /*tag*/, const char * /*name*/, uint64 /*value*/) { return 0; }
|
||||
virtual int visit_buildid(const char * /*Build ID*/) { return 0; }
|
||||
virtual int visit_debuglink(const char * /*debug*/, uint32 /*crc*/) { return 0; }
|
||||
};
|
||||
|
||||
// returns -1 on errors
|
||||
// otherwise returns the non-zero code returned by the visitor or 0
|
||||
int load_elf_symbols(const char *fname, symbol_visitor_t &sv, bool remote=false);
|
||||
|
||||
#endif
|
||||
60
idasdk76/dbg/makefile
Normal file
60
idasdk76/dbg/makefile
Normal file
@@ -0,0 +1,60 @@
|
||||
include ../allmake.mak
|
||||
|
||||
# the following platforms only build the servers, and not the plugins.
|
||||
ifneq ($(or $(__ANDROID__),$(__ANDROID_X86__),$(__ARMLINUX__),$(USE_STATIC_RUNTIME)),1)
|
||||
DBG_PLUGIN = $(L)dbg_plugin$(A)
|
||||
CONFIGS += exceptions.cfg
|
||||
endif
|
||||
|
||||
BASE_LIBS += $(L)dbg_rpc$(A)
|
||||
BASE_LIBS += $(L)dbg_server$(A)
|
||||
BASE_LIBS += $(DBG_PLUGIN)
|
||||
BASE_LIBS += $(L)dbg_proc$(A)
|
||||
GOALS += $(addprefix $(C),$(CONFIGS))
|
||||
|
||||
ALLDIRS += linux
|
||||
# ALLDIRS += mac
|
||||
ALLDIRS += win32
|
||||
|
||||
# default target
|
||||
all: $(ALLDIRS) $(GOALS)
|
||||
|
||||
# dependencies
|
||||
$(ALLDIRS): $(BASE_LIBS)
|
||||
|
||||
# recipes for subdirs
|
||||
.PHONY: $(ALLDIRS)
|
||||
$(sort $(ALLDIRS)):
|
||||
$(Q)$(MAKE) -C $@
|
||||
|
||||
clean::
|
||||
$(foreach dir,$(ALLDIRS),$(MAKE) -C $(dir) clean;)
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
RPC_OBJS += $(F)dbg_rpc_engine$(O)
|
||||
RPC_OBJS += $(F)dbg_rpc_hlp$(O)
|
||||
$(L)dbg_rpc$(A): $(call lib, $(RPC_OBJS))
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
SERVER_OBJS += $(F)bin_search$(O)
|
||||
SERVER_OBJS += $(F)dbg_rpc_handler$(O)
|
||||
SERVER_OBJS += $(F)debmod$(O)
|
||||
SERVER_OBJS += $(F)server$(O)
|
||||
$(L)dbg_server$(A): $(call lib, $(SERVER_OBJS))
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
PLUGIN_OBJS += $(F)bin_search$(O)
|
||||
PLUGIN_OBJS += $(F)dbg_rpc_client$(O)
|
||||
PLUGIN_OBJS += $(F)debmod$(O)
|
||||
PLUGIN_OBJS += $(F)rpc_debmod$(O)
|
||||
$(L)dbg_plugin$(A): $(call lib, $(PLUGIN_OBJS))
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
PROC_OBJS += $(F)arm_debmod$(O)
|
||||
PROC_OBJS += $(F)arm_regs$(O)
|
||||
PROC_OBJS += $(F)pc_debmod$(O)
|
||||
PROC_OBJS += $(F)pc_regs$(O)
|
||||
$(L)dbg_proc$(A): $(call lib, $(PROC_OBJS))
|
||||
|
||||
include $(IDA)objdir.mak
|
||||
|
||||
304
idasdk76/dbg/pc_debmod.cpp
Normal file
304
idasdk76/dbg/pc_debmod.cpp
Normal file
@@ -0,0 +1,304 @@
|
||||
#ifdef __NT__
|
||||
#include <windows.h>
|
||||
#endif
|
||||
#include <pro.h>
|
||||
#include <ua.hpp>
|
||||
#include "pc_debmod.h"
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
//lint -esym(1566,pc_debmod_t::hwbpt_ea,pc_debmod_t::hwbpt_type)
|
||||
//lint -esym(1566,pc_debmod_t::dr6,pc_debmod_t::dr7)
|
||||
pc_debmod_t::pc_debmod_t()
|
||||
{
|
||||
static const uchar bpt[] = X86_BPT_CODE;
|
||||
bpt_code.append(bpt, sizeof(bpt));
|
||||
sp_idx = R_ESP;
|
||||
pc_idx = R_EIP;
|
||||
sr_idx = R_EFLAGS;
|
||||
fs_idx = R_FS;
|
||||
gs_idx = R_GS;
|
||||
cs_idx = R_CS;
|
||||
ds_idx = R_DS;
|
||||
es_idx = R_ES;
|
||||
ss_idx = R_SS;
|
||||
nregs = X86_NREGS;
|
||||
|
||||
cleanup_hwbpts();
|
||||
set_platform(get_local_platform());
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
int pc_debmod_t::get_regidx(const char *regname, int *clsmask)
|
||||
{
|
||||
return x86_get_regidx(clsmask, regname);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
int idaapi pc_debmod_t::dbg_is_ok_bpt(bpttype_t type, ea_t ea, int /* len */)
|
||||
{
|
||||
if ( type == BPT_SOFT )
|
||||
return BPT_OK;
|
||||
|
||||
return find_hwbpt_slot(ea, type) == -1 ? BPT_TOO_MANY : BPT_OK;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// returns -1 if something is wrong
|
||||
int pc_debmod_t::find_hwbpt_slot(ea_t ea, bpttype_t type) const
|
||||
{
|
||||
for ( int i=0; i < MAX_BPT; i++ )
|
||||
{
|
||||
if ( hwbpt_ea[i] == ea && hwbpt_type[i] == type ) // another breakpoint is here
|
||||
return -1;
|
||||
if ( hwbpt_ea[i] == BADADDR ) // empty slot found
|
||||
return i;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
bool pc_debmod_t::add_hwbpt(bpttype_t type, ea_t ea, int len)
|
||||
{
|
||||
int i = find_hwbpt_slot(ea, type); // get slot number
|
||||
if ( i != -1 )
|
||||
{
|
||||
hwbpt_ea[i] = ea;
|
||||
hwbpt_type[i] = type;
|
||||
if ( type == BPT_EXEC )
|
||||
type = 0; // exec bpts are encoded with 0 on x86
|
||||
|
||||
// length code used by the processor
|
||||
int lenc = (len == 2) ? 1
|
||||
: (len == 4) ? 3
|
||||
: (len == 8) ? 2
|
||||
: 0;
|
||||
|
||||
dr7 |= (1 << (i*2)); // enable local breakpoint
|
||||
dr7 |= (type << (16+(i*4))); // set breakpoint type
|
||||
dr7 |= (lenc << (18+(i*4))); // set breakpoint length
|
||||
|
||||
return refresh_hwbpts();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
bool pc_debmod_t::del_hwbpt(ea_t ea, bpttype_t type)
|
||||
{
|
||||
for ( int i=0; i < MAX_BPT; i++ )
|
||||
{
|
||||
if ( hwbpt_ea[i] == ea && hwbpt_type[i] == type )
|
||||
{
|
||||
hwbpt_ea[i] = BADADDR; // clean the address
|
||||
dr7 &= ~(3 << (i*2)); // clean the enable bits
|
||||
dr7 &= ~(0xF << (i*4+16)); // clean the length and type
|
||||
return refresh_hwbpts();
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
#ifdef __NT__
|
||||
//--------------------------------------------------------------------------
|
||||
// Set hardware breakpoint for one thread
|
||||
bool pc_debmod_t::set_hwbpts(HANDLE hThread)
|
||||
{
|
||||
// sure_suspend_thread(ti);
|
||||
CONTEXT Context;
|
||||
Context.ContextFlags = CONTEXT_DEBUG_REGISTERS | CONTEXT_CONTROL;
|
||||
|
||||
BOOL ok = GetThreadContext(hThread, &Context);
|
||||
if ( !ok )
|
||||
{
|
||||
deberr("GetThreadContext");
|
||||
return false;
|
||||
}
|
||||
Context.Dr0 = hwbpt_ea[0];
|
||||
Context.Dr1 = hwbpt_ea[1];
|
||||
Context.Dr2 = hwbpt_ea[2];
|
||||
Context.Dr3 = hwbpt_ea[3];
|
||||
Context.Dr6 = 0;
|
||||
Context.Dr7 = dr7;
|
||||
|
||||
ok = SetThreadContext(hThread, &Context);
|
||||
if ( !ok )
|
||||
{
|
||||
deberr("SetThreadContext");
|
||||
}
|
||||
// sure_resume_thread(ti);
|
||||
return ok != FALSE;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
ea_t pc_debmod_t::is_hwbpt_triggered(thid_t id, bool is_stepping)
|
||||
{
|
||||
CONTEXT Context;
|
||||
Context.ContextFlags = CONTEXT_DEBUG_REGISTERS | CONTEXT_CONTROL;
|
||||
HANDLE h = get_thread_handle(id);
|
||||
if ( GetThreadContext(h, &Context) )
|
||||
{
|
||||
for ( int i=0; i < MAX_BPT; i++ )
|
||||
{
|
||||
if ( (Context.Dr7 & uint32(1 << (i*2)))
|
||||
&& (Context.Dr6 & uint32(1 << i)) ) // Local hardware breakpoint 'i'
|
||||
{
|
||||
ULONG_PTR *dr = NULL;
|
||||
switch ( i )
|
||||
{
|
||||
case 0: dr = &Context.Dr0; break;
|
||||
case 1: dr = &Context.Dr1; break;
|
||||
case 2: dr = &Context.Dr2; break;
|
||||
case 3: dr = &Context.Dr3; break;
|
||||
}
|
||||
if ( dr == NULL )
|
||||
break;
|
||||
if ( hwbpt_ea[i] == *dr )
|
||||
{
|
||||
set_hwbpts(h); // Clear the status bits
|
||||
// do not report exec breakpoint if it occurs while we are stepping
|
||||
if ( is_stepping && hwbpt_type[i] == BPT_EXEC )
|
||||
break;
|
||||
return hwbpt_ea[i];
|
||||
}
|
||||
//? TRACING else
|
||||
// debdeb("System hardware breakpoint at %08X ???\n", *dr); //?
|
||||
// what to do ?:
|
||||
// reset it, and continue as if no event were received ?
|
||||
// send it to IDA, and let the user setup a "stop on non-debugger hardware breakpoint" option ?
|
||||
}
|
||||
}
|
||||
}
|
||||
return BADADDR;
|
||||
}
|
||||
#endif // ifdef __NT__
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
void pc_debmod_t::cleanup_hwbpts()
|
||||
{
|
||||
for ( int i=0; i < MAX_BPT; i++ )
|
||||
{
|
||||
hwbpt_ea[i] = BADADDR;
|
||||
hwbpt_type[i] = bpttype_t(0);
|
||||
}
|
||||
dr6 = 0;
|
||||
dr7 = 0x100; // exact local breakpoints
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
ea_t pc_debmod_t::calc_appcall_stack(const regvals_t ®vals)
|
||||
{
|
||||
ea_t ea = inherited::calc_appcall_stack(regvals);
|
||||
#ifndef __X86__
|
||||
// do not touch the red zone (used by gcc)
|
||||
ea = ea > 128 ? ea - 128 : BADADDR;
|
||||
#endif
|
||||
return ea;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
int pc_debmod_t::finalize_appcall_stack(
|
||||
call_context_t &,
|
||||
regval_map_t &,
|
||||
bytevec_t &stk)
|
||||
{
|
||||
// pc-specific: add endless loop, so user does not execute unwanted code
|
||||
// after manual appcall. we do not really need to write bpt,
|
||||
// but it is easy to include it here than skip it.
|
||||
// previously we reserved 'addrsize' bytes on the stack for this purpose,
|
||||
// and we use 3 of them.
|
||||
static const uchar bpt_and_loop[] = { 0xCC, 0xEB, 0xFE };
|
||||
stk.append(bpt_and_loop, sizeof(bpt_and_loop));
|
||||
return 0;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
bool pc_debmod_t::should_stop_appcall(
|
||||
thid_t tid,
|
||||
const debug_event_t *event,
|
||||
ea_t ea)
|
||||
{
|
||||
if ( inherited::should_stop_appcall(tid, event, ea) )
|
||||
return true;
|
||||
|
||||
// Check if the current instruction is a "RET" and then dereferences
|
||||
// the contents of SP to find the return address. IF it matches, it is
|
||||
// time to stop
|
||||
regvals_t regs;
|
||||
regs.resize(X86_NREGS);
|
||||
do
|
||||
{
|
||||
// Start by reading registers
|
||||
if ( dbg_read_registers(tid, X86_RC_GENERAL, regs.begin(), NULL) != DRC_OK )
|
||||
break;
|
||||
|
||||
// Get the opcodes
|
||||
uchar opcode;
|
||||
if ( dbg_read_memory((ea_t)regs[pc_idx].ival, &opcode, 1, NULL) != 1 )
|
||||
break;
|
||||
// Check for "RET" and "RET n"
|
||||
if ( opcode != 0xC3 && opcode != 0xC2 )
|
||||
break;
|
||||
|
||||
// Dereference value at ESP
|
||||
ea_t at_sp = BADADDR;
|
||||
if ( dbg_read_memory((ea_t)regs[sp_idx].ival, &at_sp, sizeof(at_sp), NULL) != sizeof(at_sp) )
|
||||
break;
|
||||
return ea == at_sp; // time to stop!
|
||||
} while ( false );
|
||||
return false;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
bool pc_debmod_t::preprocess_appcall_cleanup(thid_t, call_context_t &ctx)
|
||||
{
|
||||
// Linux 2.6.24-19 has a bug(?):
|
||||
// it doesn't clear trace flag after single-stepping
|
||||
// so if we single-step and then make an appcall, we would restore eflags with TF set
|
||||
// but next time we resume the program, kernel thinks that TF was set by the user
|
||||
// and doesn't clear it, and so our appcall stops immediately
|
||||
// to prevent that, we'll always clear trace flag before restoring eflags
|
||||
if ( ctx.saved_regs.size() > sr_idx )
|
||||
ctx.saved_regs[sr_idx].ival &= ~0x100;
|
||||
return true; // success
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
void pc_debmod_t::read_fpu_registers(
|
||||
regval_t *values,
|
||||
int clsmask,
|
||||
const void *fptr,
|
||||
size_t step)
|
||||
{
|
||||
const uchar *vptr = (const uchar *)fptr;
|
||||
for ( int i=0; i < 8; i++,vptr+=step )
|
||||
{
|
||||
if ( (clsmask & X86_RC_FPU) != 0 )
|
||||
{
|
||||
regval_t *v = &values[R_ST0+i];
|
||||
memcpy(&v->fval, vptr, 10); //-V512 v->fval underflow
|
||||
v->rvtype = RVT_FLOAT;
|
||||
}
|
||||
if ( (clsmask & X86_RC_MMX) != 0 )
|
||||
values[R_MMX0+i].set_bytes(vptr, 8);
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
const char *pc_debmod_t::get_local_platform()
|
||||
{
|
||||
#ifdef __NT__
|
||||
# define LOCAL_PLATFORM "win32"
|
||||
#else
|
||||
# ifdef __MAC__
|
||||
# define LOCAL_PLATFORM "macosx"
|
||||
# else
|
||||
# ifdef __LINUX__
|
||||
# define LOCAL_PLATFORM "linux"
|
||||
# else
|
||||
# define LOCAL_PLATFORM "PC_UNDEFINED"
|
||||
# endif
|
||||
# endif
|
||||
#endif
|
||||
return LOCAL_PLATFORM;
|
||||
}
|
||||
52
idasdk76/dbg/pc_debmod.h
Normal file
52
idasdk76/dbg/pc_debmod.h
Normal file
@@ -0,0 +1,52 @@
|
||||
#ifndef __PC_DEBUGGER_MODULE__
|
||||
#define __PC_DEBUGGER_MODULE__
|
||||
|
||||
#ifdef __NT__
|
||||
# include <windows.h>
|
||||
#endif
|
||||
|
||||
#include "pc_regs.hpp"
|
||||
#include "deb_pc.hpp"
|
||||
#include "debmod.h"
|
||||
|
||||
class pc_debmod_t: public debmod_t
|
||||
{
|
||||
typedef debmod_t inherited;
|
||||
protected:
|
||||
// Hardware breakpoints
|
||||
ea_t hwbpt_ea[MAX_BPT];
|
||||
bpttype_t hwbpt_type[MAX_BPT];
|
||||
uint32 dr6;
|
||||
uint32 dr7;
|
||||
|
||||
int sr_idx;
|
||||
int fs_idx;
|
||||
int gs_idx;
|
||||
int cs_idx;
|
||||
int ds_idx;
|
||||
int es_idx;
|
||||
int ss_idx;
|
||||
|
||||
public:
|
||||
pc_debmod_t();
|
||||
void cleanup_hwbpts();
|
||||
virtual bool refresh_hwbpts() newapi { return false; }
|
||||
int find_hwbpt_slot(ea_t ea, bpttype_t type) const;
|
||||
bool del_hwbpt(ea_t ea, bpttype_t type);
|
||||
bool add_hwbpt(bpttype_t type, ea_t ea, int len);
|
||||
static const char *get_local_platform();
|
||||
#ifdef __NT__
|
||||
virtual bool set_hwbpts(HANDLE hThread) newapi;
|
||||
ea_t is_hwbpt_triggered(thid_t id, bool is_stepping);
|
||||
virtual HANDLE get_thread_handle(thid_t /*tid*/) newapi { return INVALID_HANDLE_VALUE; }
|
||||
#endif
|
||||
virtual int idaapi dbg_is_ok_bpt(bpttype_t type, ea_t ea, int len) override;
|
||||
virtual int finalize_appcall_stack(call_context_t &, regval_map_t &, bytevec_t &stk) override;
|
||||
virtual ea_t calc_appcall_stack(const regvals_t ®vals) override;
|
||||
virtual bool should_stop_appcall(thid_t tid, const debug_event_t *event, ea_t ea) override;
|
||||
virtual bool preprocess_appcall_cleanup(thid_t tid, call_context_t &ctx) override;
|
||||
virtual int get_regidx(const char *regname, int *clsmask) override;
|
||||
static void read_fpu_registers(regval_t *values, int clsmask, const void *fptr, size_t step);
|
||||
};
|
||||
|
||||
#endif
|
||||
113
idasdk76/dbg/pc_local_impl.cpp
Normal file
113
idasdk76/dbg/pc_local_impl.cpp
Normal file
@@ -0,0 +1,113 @@
|
||||
// x86-specific code (compiled only on IDA side, never on the server side)
|
||||
|
||||
#include <dbg.hpp>
|
||||
#include "pc_regs.hpp"
|
||||
#include "deb_pc.hpp"
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
//
|
||||
// DEBUGGER REGISTER AND INSTRUCTION INFORMATIONS
|
||||
//
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
#if 0
|
||||
static void DEBUG_REGVALS(regval_t *values)
|
||||
{
|
||||
for ( int i = 0; i < qnumber(registers); i++ )
|
||||
{
|
||||
msg("%s = ", registers[i].name);
|
||||
switch ( registers[i].dtyp )
|
||||
{
|
||||
case dt_qword: msg("%016LX\n", values[i].ival); break;
|
||||
case dt_dword: msg("%08X\n", values[i].ival); break;
|
||||
case dt_word: msg("%04X\n", values[i].ival); break;
|
||||
case dt_tbyte:
|
||||
for ( int j = 0; j < sizeof(regval_t); j++ )
|
||||
{
|
||||
if ( j == 10 )
|
||||
msg(" - "); // higher bytes are not used by x86 floats
|
||||
msg("%02X ", ((unsigned char*)&values[i])[j]);
|
||||
}
|
||||
// msg("%02X ", (unsigned short)values[i].fval[j]);
|
||||
msg("\n");
|
||||
break;
|
||||
}
|
||||
}
|
||||
msg("\n");
|
||||
}
|
||||
#endif
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
drc_t idaapi x86_read_registers(
|
||||
thid_t thread_id,
|
||||
int clsmask,
|
||||
regval_t *values,
|
||||
qstring *errbuf)
|
||||
{
|
||||
drc_t drc = s_read_registers(thread_id, clsmask, values, errbuf);
|
||||
if ( drc == DRC_OK )
|
||||
{
|
||||
// FPU related registers
|
||||
if ( (clsmask & X86_RC_FPU) != 0 )
|
||||
{
|
||||
for ( size_t i = 0; i < debugger.nregs; i++ )
|
||||
{
|
||||
const register_info_t &ri = debugger.regs(i);
|
||||
if ( ri.register_class == X86_RC_FPU && ri.dtype == dt_tbyte )
|
||||
{
|
||||
int rc = processor_t::realcvt(&values[i].fval, &values[i].fval, 004); // load long double
|
||||
if ( rc == 0 )
|
||||
break; // realcvt not implemented
|
||||
else if ( rc < 0 ) // error
|
||||
values[i].fval.clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return drc;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
drc_t idaapi x86_write_register(
|
||||
thid_t thread_id,
|
||||
int reg_idx,
|
||||
const regval_t *value,
|
||||
qstring *errbuf)
|
||||
{
|
||||
regval_t rv = *value;
|
||||
// FPU related registers
|
||||
const register_info_t &ri = debugger.regs(reg_idx);
|
||||
if ( ri.register_class == X86_RC_FPU && ri.dtype == dt_tbyte )
|
||||
{
|
||||
uchar fn[10];
|
||||
int code = processor_t::realcvt(fn, &rv.fval, 014); // store long double //-V536 octal
|
||||
if ( code == REAL_ERROR_FPOVER )
|
||||
memcpy(&rv.fval, fn, 10); //-V512 rv.fval underflow
|
||||
}
|
||||
return s_write_register(thread_id, reg_idx, &rv, errbuf);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
int is_x86_valid_bpt(bpttype_t type, ea_t ea, int len)
|
||||
{
|
||||
if ( type != BPT_SOFT )
|
||||
{
|
||||
if ( (debugger.flags & DBG_FLAG_ANYSIZE_HWBPT) == 0 )
|
||||
return check_x86_hwbpt(type, ea, len);
|
||||
|
||||
if ( type == 0 )
|
||||
return BPT_BAD_TYPE;
|
||||
}
|
||||
return BPT_OK;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
void processor_specific_init(void)
|
||||
{
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
void processor_specific_term(void)
|
||||
{
|
||||
}
|
||||
329
idasdk76/dbg/pc_regs.cpp
Normal file
329
idasdk76/dbg/pc_regs.cpp
Normal file
@@ -0,0 +1,329 @@
|
||||
|
||||
#include "pc_regs.hpp"
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
// NOTE: keep in sync with register_class_x86_t
|
||||
const char *x86_register_classes[] =
|
||||
{
|
||||
"General registers",
|
||||
"Segment registers",
|
||||
"FPU registers",
|
||||
"MMX registers",
|
||||
"XMM registers",
|
||||
"YMM registers",
|
||||
NULL
|
||||
};
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
static const char *const eflags[] =
|
||||
{
|
||||
"CF", // 0
|
||||
NULL, // 1
|
||||
"PF", // 2
|
||||
NULL, // 3
|
||||
"AF", // 4
|
||||
NULL, // 5
|
||||
"ZF", // 6
|
||||
"SF", // 7
|
||||
"TF", // 8
|
||||
"IF", // 9
|
||||
"DF", // 10
|
||||
"OF", // 11
|
||||
"IOPL", // 12
|
||||
"IOPL", // 13
|
||||
"NT", // 14
|
||||
NULL, // 15
|
||||
"RF", // 16
|
||||
"VM", // 17
|
||||
"AC", // 18
|
||||
"VIF", // 19
|
||||
"VIP", // 20
|
||||
"ID", // 21
|
||||
NULL, // 22
|
||||
NULL, // 23
|
||||
NULL, // 24
|
||||
NULL, // 25
|
||||
NULL, // 26
|
||||
NULL, // 27
|
||||
NULL, // 28
|
||||
NULL, // 29
|
||||
NULL, // 30
|
||||
NULL // 31
|
||||
};
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
static const char *const ctrlflags[] =
|
||||
{
|
||||
"CTRL.IM",
|
||||
"CTRL.DM",
|
||||
"CTRL.ZM",
|
||||
"CTRL.OM",
|
||||
"CTRL.UM",
|
||||
"CTRL.PM",
|
||||
NULL,
|
||||
NULL,
|
||||
"CTRL.PC",
|
||||
"CTRL.PC",
|
||||
"CTRL.RC",
|
||||
"CTRL.RC",
|
||||
"CTRL.X",
|
||||
NULL,
|
||||
NULL,
|
||||
NULL
|
||||
};
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
static const char *const statflags[] =
|
||||
{
|
||||
"STAT.IE",
|
||||
"STAT.DE",
|
||||
"STAT.ZE",
|
||||
"STAT.OE",
|
||||
"STAT.UE",
|
||||
"STAT.PE",
|
||||
"STAT.SF",
|
||||
"STAT.ES",
|
||||
"STAT.C0",
|
||||
"STAT.C1",
|
||||
"STAT.C2",
|
||||
"STAT.TOP",
|
||||
"STAT.TOP",
|
||||
"STAT.TOP",
|
||||
"STAT.C3",
|
||||
"STAT.B"
|
||||
};
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
static const char *const tagsflags[] =
|
||||
{
|
||||
"TAG0",
|
||||
"TAG0",
|
||||
"TAG1",
|
||||
"TAG1",
|
||||
"TAG2",
|
||||
"TAG2",
|
||||
"TAG3",
|
||||
"TAG3",
|
||||
"TAG4",
|
||||
"TAG4",
|
||||
"TAG5",
|
||||
"TAG5",
|
||||
"TAG6",
|
||||
"TAG6",
|
||||
"TAG7",
|
||||
"TAG7"
|
||||
};
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
static const char *const xmm_format[] =
|
||||
{
|
||||
"XMM_4_floats",
|
||||
};
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
static const char *const ymm_format[] =
|
||||
{
|
||||
"YMM_8_floats",
|
||||
};
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
static const char *const mmx_format[] =
|
||||
{
|
||||
"MMX_8_bytes",
|
||||
};
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
static const char *const mxcsr_bits[] =
|
||||
{
|
||||
"IE", // 0 Invalid Operation Flag
|
||||
"DE", // 1 Denormal Flag
|
||||
"ZE", // 2 Divide-by-Zero Flag
|
||||
"OE", // 3 Overflow Flag
|
||||
"UE", // 4 Underflow Flag
|
||||
"PE", // 5 Precision Flag
|
||||
"DAZ", // 6 Denormals Are Zeros*
|
||||
"IM", // 7 Invalid Operation Mask
|
||||
"DM", // 8 Denormal Operation Mask
|
||||
"ZM", // 9 Divide-by-Zero Mask
|
||||
"OM", // 10 Overflow Mask
|
||||
"UM", // 11 Underflow Mask
|
||||
"PM", // 12 Precision Mask
|
||||
"RC", // 13 Rounding Control
|
||||
"RC", // 14 Rounding Control
|
||||
"FZ", // 15 Flush to Zero
|
||||
NULL, // 16
|
||||
NULL, // 17
|
||||
NULL, // 18
|
||||
NULL, // 19
|
||||
NULL, // 20
|
||||
NULL, // 21
|
||||
NULL, // 22
|
||||
NULL, // 23
|
||||
NULL, // 24
|
||||
NULL, // 25
|
||||
NULL, // 26
|
||||
NULL, // 27
|
||||
NULL, // 28
|
||||
NULL, // 29
|
||||
NULL, // 30
|
||||
NULL // 31
|
||||
};
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
// General registers
|
||||
#ifdef __EA64__
|
||||
register_info_t r_rax = { "RAX", REGISTER_ADDRESS, X86_RC_GENERAL, dt_qword, NULL, 0 };
|
||||
register_info_t r_rbx = { "RBX", REGISTER_ADDRESS, X86_RC_GENERAL, dt_qword, NULL, 0 };
|
||||
register_info_t r_rcx = { "RCX", REGISTER_ADDRESS, X86_RC_GENERAL, dt_qword, NULL, 0 };
|
||||
register_info_t r_rdx = { "RDX", REGISTER_ADDRESS, X86_RC_GENERAL, dt_qword, NULL, 0 };
|
||||
register_info_t r_rsi = { "RSI", REGISTER_ADDRESS, X86_RC_GENERAL, dt_qword, NULL, 0 };
|
||||
register_info_t r_rdi = { "RDI", REGISTER_ADDRESS, X86_RC_GENERAL, dt_qword, NULL, 0 };
|
||||
register_info_t r_rbp = { "RBP", REGISTER_ADDRESS|REGISTER_FP, X86_RC_GENERAL, dt_qword, NULL, 0 };
|
||||
register_info_t r_rsp = { "RSP", REGISTER_ADDRESS|REGISTER_SP, X86_RC_GENERAL, dt_qword, NULL, 0 };
|
||||
register_info_t r_rip = { "RIP", REGISTER_ADDRESS|REGISTER_IP, X86_RC_GENERAL, dt_qword, NULL, 0 };
|
||||
register_info_t r_r8 = { "R8", REGISTER_ADDRESS, X86_RC_GENERAL, dt_qword, NULL, 0 };
|
||||
register_info_t r_r9 = { "R9", REGISTER_ADDRESS, X86_RC_GENERAL, dt_qword, NULL, 0 };
|
||||
register_info_t r_r10 = { "R10", REGISTER_ADDRESS, X86_RC_GENERAL, dt_qword, NULL, 0 };
|
||||
register_info_t r_r11 = { "R11", REGISTER_ADDRESS, X86_RC_GENERAL, dt_qword, NULL, 0 };
|
||||
register_info_t r_r12 = { "R12", REGISTER_ADDRESS, X86_RC_GENERAL, dt_qword, NULL, 0 };
|
||||
register_info_t r_r13 = { "R13", REGISTER_ADDRESS, X86_RC_GENERAL, dt_qword, NULL, 0 };
|
||||
register_info_t r_r14 = { "R14", REGISTER_ADDRESS, X86_RC_GENERAL, dt_qword, NULL, 0 };
|
||||
register_info_t r_r15 = { "R15", REGISTER_ADDRESS, X86_RC_GENERAL, dt_qword, NULL, 0 };
|
||||
#endif
|
||||
register_info_t r_eax = { "EAX", REGISTER_ADDRESS, X86_RC_GENERAL, dt_dword, NULL, 0 };
|
||||
register_info_t r_ebx = { "EBX", REGISTER_ADDRESS, X86_RC_GENERAL, dt_dword, NULL, 0 };
|
||||
register_info_t r_ecx = { "ECX", REGISTER_ADDRESS, X86_RC_GENERAL, dt_dword, NULL, 0 };
|
||||
register_info_t r_edx = { "EDX", REGISTER_ADDRESS, X86_RC_GENERAL, dt_dword, NULL, 0 };
|
||||
register_info_t r_esi = { "ESI", REGISTER_ADDRESS, X86_RC_GENERAL, dt_dword, NULL, 0 };
|
||||
register_info_t r_edi = { "EDI", REGISTER_ADDRESS, X86_RC_GENERAL, dt_dword, NULL, 0 };
|
||||
register_info_t r_ebp = { "EBP", REGISTER_ADDRESS|REGISTER_FP, X86_RC_GENERAL, dt_dword, NULL, 0 };
|
||||
register_info_t r_esp = { "ESP", REGISTER_ADDRESS|REGISTER_SP, X86_RC_GENERAL, dt_dword, NULL, 0 };
|
||||
register_info_t r_eip = { "EIP", REGISTER_ADDRESS|REGISTER_IP, X86_RC_GENERAL, dt_dword, NULL, 0 };
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
// NOTE: keep in sync with register_x86_t
|
||||
register_info_t x86_registers[] =
|
||||
{
|
||||
// FPU registers
|
||||
{ "ST0", 0, X86_RC_FPU, dt_tbyte, NULL, 0 },
|
||||
{ "ST1", 0, X86_RC_FPU, dt_tbyte, NULL, 0 },
|
||||
{ "ST2", 0, X86_RC_FPU, dt_tbyte, NULL, 0 },
|
||||
{ "ST3", 0, X86_RC_FPU, dt_tbyte, NULL, 0 },
|
||||
{ "ST4", 0, X86_RC_FPU, dt_tbyte, NULL, 0 },
|
||||
{ "ST5", 0, X86_RC_FPU, dt_tbyte, NULL, 0 },
|
||||
{ "ST6", 0, X86_RC_FPU, dt_tbyte, NULL, 0 },
|
||||
{ "ST7", 0, X86_RC_FPU, dt_tbyte, NULL, 0 },
|
||||
{ "CTRL", 0, X86_RC_FPU, dt_word, ctrlflags, 0x1F3F },
|
||||
{ "STAT", 0, X86_RC_FPU, dt_word, statflags, 0xFFFF },
|
||||
{ "TAGS", 0, X86_RC_FPU, dt_word, tagsflags, 0xFFFF },
|
||||
// Segment registers
|
||||
{ "CS", REGISTER_CS|REGISTER_NOLF, X86_RC_SEGMENTS, dt_word, NULL, 0 },
|
||||
{ "DS", REGISTER_NOLF, X86_RC_SEGMENTS, dt_word, NULL, 0 },
|
||||
{ "ES", 0, X86_RC_SEGMENTS, dt_word, NULL, 0 },
|
||||
{ "FS", REGISTER_NOLF, X86_RC_SEGMENTS, dt_word, NULL, 0 },
|
||||
{ "GS", REGISTER_NOLF, X86_RC_SEGMENTS, dt_word, NULL, 0 },
|
||||
{ "SS", REGISTER_SS, X86_RC_SEGMENTS, dt_word, NULL, 0 },
|
||||
// General registers
|
||||
#ifdef __EA64__
|
||||
r_rax,
|
||||
r_rbx,
|
||||
r_rcx,
|
||||
r_rdx,
|
||||
r_rsi,
|
||||
r_rdi,
|
||||
r_rbp,
|
||||
r_rsp,
|
||||
r_rip,
|
||||
r_r8,
|
||||
r_r9,
|
||||
r_r10,
|
||||
r_r11,
|
||||
r_r12,
|
||||
r_r13,
|
||||
r_r14,
|
||||
r_r15,
|
||||
#else
|
||||
r_eax,
|
||||
r_ebx,
|
||||
r_ecx,
|
||||
r_edx,
|
||||
r_esi,
|
||||
r_edi,
|
||||
r_ebp,
|
||||
r_esp,
|
||||
r_eip,
|
||||
#endif
|
||||
{ "EFL", 0, X86_RC_GENERAL, dt_dword, eflags, 0x00000FD5 }, // OF|DF|IF|TF|SF|ZF|AF|PF|CF
|
||||
// XMM registers
|
||||
{ "XMM0", REGISTER_CUSTFMT, X86_RC_XMM, dt_byte16, xmm_format, 0 },
|
||||
{ "XMM1", REGISTER_CUSTFMT, X86_RC_XMM, dt_byte16, xmm_format, 0 },
|
||||
{ "XMM2", REGISTER_CUSTFMT, X86_RC_XMM, dt_byte16, xmm_format, 0 },
|
||||
{ "XMM3", REGISTER_CUSTFMT, X86_RC_XMM, dt_byte16, xmm_format, 0 },
|
||||
{ "XMM4", REGISTER_CUSTFMT, X86_RC_XMM, dt_byte16, xmm_format, 0 },
|
||||
{ "XMM5", REGISTER_CUSTFMT, X86_RC_XMM, dt_byte16, xmm_format, 0 },
|
||||
{ "XMM6", REGISTER_CUSTFMT, X86_RC_XMM, dt_byte16, xmm_format, 0 },
|
||||
{ "XMM7", REGISTER_CUSTFMT, X86_RC_XMM, dt_byte16, xmm_format, 0 },
|
||||
#ifdef __EA64__
|
||||
{ "XMM8", REGISTER_CUSTFMT, X86_RC_XMM, dt_byte16, xmm_format, 0 },
|
||||
{ "XMM9", REGISTER_CUSTFMT, X86_RC_XMM, dt_byte16, xmm_format, 0 },
|
||||
{ "XMM10", REGISTER_CUSTFMT, X86_RC_XMM, dt_byte16, xmm_format, 0 },
|
||||
{ "XMM11", REGISTER_CUSTFMT, X86_RC_XMM, dt_byte16, xmm_format, 0 },
|
||||
{ "XMM12", REGISTER_CUSTFMT, X86_RC_XMM, dt_byte16, xmm_format, 0 },
|
||||
{ "XMM13", REGISTER_CUSTFMT, X86_RC_XMM, dt_byte16, xmm_format, 0 },
|
||||
{ "XMM14", REGISTER_CUSTFMT, X86_RC_XMM, dt_byte16, xmm_format, 0 },
|
||||
{ "XMM15", REGISTER_CUSTFMT, X86_RC_XMM, dt_byte16, xmm_format, 0 },
|
||||
#endif
|
||||
{ "MXCSR", 0, X86_RC_XMM, dt_dword, mxcsr_bits, 0xFFFF },
|
||||
// MMX registers
|
||||
{ "MM0", REGISTER_CUSTFMT, X86_RC_MMX, dt_qword, mmx_format, 0 },
|
||||
{ "MM1", REGISTER_CUSTFMT, X86_RC_MMX, dt_qword, mmx_format, 0 },
|
||||
{ "MM2", REGISTER_CUSTFMT, X86_RC_MMX, dt_qword, mmx_format, 0 },
|
||||
{ "MM3", REGISTER_CUSTFMT, X86_RC_MMX, dt_qword, mmx_format, 0 },
|
||||
{ "MM4", REGISTER_CUSTFMT, X86_RC_MMX, dt_qword, mmx_format, 0 },
|
||||
{ "MM5", REGISTER_CUSTFMT, X86_RC_MMX, dt_qword, mmx_format, 0 },
|
||||
{ "MM6", REGISTER_CUSTFMT, X86_RC_MMX, dt_qword, mmx_format, 0 },
|
||||
{ "MM7", REGISTER_CUSTFMT, X86_RC_MMX, dt_qword, mmx_format, 0 },
|
||||
// YMM registers
|
||||
{ "YMM0", REGISTER_CUSTFMT, X86_RC_YMM, dt_byte32, ymm_format, 0 },
|
||||
{ "YMM1", REGISTER_CUSTFMT, X86_RC_YMM, dt_byte32, ymm_format, 0 },
|
||||
{ "YMM2", REGISTER_CUSTFMT, X86_RC_YMM, dt_byte32, ymm_format, 0 },
|
||||
{ "YMM3", REGISTER_CUSTFMT, X86_RC_YMM, dt_byte32, ymm_format, 0 },
|
||||
{ "YMM4", REGISTER_CUSTFMT, X86_RC_YMM, dt_byte32, ymm_format, 0 },
|
||||
{ "YMM5", REGISTER_CUSTFMT, X86_RC_YMM, dt_byte32, ymm_format, 0 },
|
||||
{ "YMM6", REGISTER_CUSTFMT, X86_RC_YMM, dt_byte32, ymm_format, 0 },
|
||||
{ "YMM7", REGISTER_CUSTFMT, X86_RC_YMM, dt_byte32, ymm_format, 0 },
|
||||
#ifdef __EA64__
|
||||
{ "YMM8", REGISTER_CUSTFMT, X86_RC_YMM, dt_byte32, ymm_format, 0 },
|
||||
{ "YMM9", REGISTER_CUSTFMT, X86_RC_YMM, dt_byte32, ymm_format, 0 },
|
||||
{ "YMM10", REGISTER_CUSTFMT, X86_RC_YMM, dt_byte32, ymm_format, 0 },
|
||||
{ "YMM11", REGISTER_CUSTFMT, X86_RC_YMM, dt_byte32, ymm_format, 0 },
|
||||
{ "YMM12", REGISTER_CUSTFMT, X86_RC_YMM, dt_byte32, ymm_format, 0 },
|
||||
{ "YMM13", REGISTER_CUSTFMT, X86_RC_YMM, dt_byte32, ymm_format, 0 },
|
||||
{ "YMM14", REGISTER_CUSTFMT, X86_RC_YMM, dt_byte32, ymm_format, 0 },
|
||||
{ "YMM15", REGISTER_CUSTFMT, X86_RC_YMM, dt_byte32, ymm_format, 0 },
|
||||
#endif
|
||||
};
|
||||
CASSERT(qnumber(x86_registers) == X86_NREGS);
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
int x86_get_regidx(int *clsmask, const char *regname)
|
||||
{
|
||||
for ( size_t i = 0; i < qnumber(x86_registers); i++ )
|
||||
{
|
||||
if ( strieq(regname, x86_registers[i].name) )
|
||||
{
|
||||
if ( clsmask != NULL )
|
||||
*clsmask = x86_registers[i].register_class;
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
int x86_get_regclass(int idx)
|
||||
{
|
||||
if ( idx >= 0 && idx < qnumber(x86_registers) )
|
||||
return x86_registers[idx].register_class;
|
||||
return 0;
|
||||
}
|
||||
172
idasdk76/dbg/pc_regs.hpp
Normal file
172
idasdk76/dbg/pc_regs.hpp
Normal file
@@ -0,0 +1,172 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <pro.h>
|
||||
#include <idd.hpp>
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
// NOTE: keep in sync with x86_register_classes
|
||||
enum register_class_x86_t
|
||||
{
|
||||
X86_RC_GENERAL = 0x01, // General registers
|
||||
X86_RC_SEGMENTS = 0x02, // Segment registers
|
||||
X86_RC_FPU = 0x04, // FPU registers
|
||||
X86_RC_MMX = 0x08, // MMX registers
|
||||
X86_RC_XMM = 0x10, // XMM registers
|
||||
X86_RC_YMM = 0x20, // YMM registers
|
||||
X86_RC_ALL = X86_RC_GENERAL
|
||||
| X86_RC_SEGMENTS
|
||||
| X86_RC_FPU
|
||||
| X86_RC_MMX
|
||||
| X86_RC_XMM
|
||||
| X86_RC_YMM
|
||||
};
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
// NOTE: keep in sync with x86_registers
|
||||
enum register_x86_t
|
||||
{
|
||||
// FPU registers
|
||||
R_ST0,
|
||||
R_ST1,
|
||||
R_ST2,
|
||||
R_ST3,
|
||||
R_ST4,
|
||||
R_ST5,
|
||||
R_ST6,
|
||||
R_ST7,
|
||||
R_CTRL,
|
||||
R_STAT,
|
||||
R_TAGS,
|
||||
// Segment registers
|
||||
R_CS,
|
||||
R_DS,
|
||||
R_ES,
|
||||
R_FS,
|
||||
R_GS,
|
||||
R_SS,
|
||||
// General registers
|
||||
R_EAX,
|
||||
R_EBX,
|
||||
R_ECX,
|
||||
R_EDX,
|
||||
R_ESI,
|
||||
R_EDI,
|
||||
R_EBP,
|
||||
R_ESP,
|
||||
R_EIP,
|
||||
#ifdef __EA64__
|
||||
R_R8,
|
||||
R_R9,
|
||||
R_R10,
|
||||
R_R11,
|
||||
R_R12,
|
||||
R_R13,
|
||||
R_R14,
|
||||
R_R15,
|
||||
#endif
|
||||
R_EFLAGS,
|
||||
// XMM registers
|
||||
R_XMM0,
|
||||
R_XMM1,
|
||||
R_XMM2,
|
||||
R_XMM3,
|
||||
R_XMM4,
|
||||
R_XMM5,
|
||||
R_XMM6,
|
||||
R_XMM7,
|
||||
#ifndef __EA64__
|
||||
R_LAST_XMM = R_XMM7,
|
||||
#else
|
||||
R_XMM8,
|
||||
R_XMM9,
|
||||
R_XMM10,
|
||||
R_XMM11,
|
||||
R_XMM12,
|
||||
R_XMM13,
|
||||
R_XMM14,
|
||||
R_XMM15,
|
||||
R_LAST_XMM = R_XMM15,
|
||||
#endif
|
||||
R_MXCSR,
|
||||
// MMX registers
|
||||
R_MMX0,
|
||||
R_MMX1,
|
||||
R_MMX2,
|
||||
R_MMX3,
|
||||
R_MMX4,
|
||||
R_MMX5,
|
||||
R_MMX6,
|
||||
R_MMX7,
|
||||
// YMM registers
|
||||
R_YMM0,
|
||||
R_YMM1,
|
||||
R_YMM2,
|
||||
R_YMM3,
|
||||
R_YMM4,
|
||||
R_YMM5,
|
||||
R_YMM6,
|
||||
R_YMM7,
|
||||
#ifndef __EA64__
|
||||
R_LAST_YMM = R_YMM7,
|
||||
#else
|
||||
R_YMM8,
|
||||
R_YMM9,
|
||||
R_YMM10,
|
||||
R_YMM11,
|
||||
R_YMM12,
|
||||
R_YMM13,
|
||||
R_YMM14,
|
||||
R_YMM15,
|
||||
R_LAST_YMM = R_YMM15,
|
||||
#endif
|
||||
};
|
||||
|
||||
// Number of registers in x86 and x64
|
||||
#define X86_X64_NREGS 76
|
||||
#define X86_X86_NREGS 52
|
||||
|
||||
#ifdef __EA64__
|
||||
#define X86_NREGS X86_X64_NREGS
|
||||
#else
|
||||
#define X86_NREGS X86_X86_NREGS
|
||||
#endif
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
// General registers
|
||||
#ifdef __EA64__
|
||||
extern register_info_t r_rax;
|
||||
extern register_info_t r_rbx;
|
||||
extern register_info_t r_rcx;
|
||||
extern register_info_t r_rdx;
|
||||
extern register_info_t r_rsi;
|
||||
extern register_info_t r_rdi;
|
||||
extern register_info_t r_rbp;
|
||||
extern register_info_t r_rsp;
|
||||
extern register_info_t r_rip;
|
||||
extern register_info_t r_r8;
|
||||
extern register_info_t r_r9;
|
||||
extern register_info_t r_r10;
|
||||
extern register_info_t r_r11;
|
||||
extern register_info_t r_r12;
|
||||
extern register_info_t r_r13;
|
||||
extern register_info_t r_r14;
|
||||
extern register_info_t r_r15;
|
||||
#endif
|
||||
extern register_info_t r_eax;
|
||||
extern register_info_t r_ebx;
|
||||
extern register_info_t r_ecx;
|
||||
extern register_info_t r_edx;
|
||||
extern register_info_t r_esi;
|
||||
extern register_info_t r_edi;
|
||||
extern register_info_t r_ebp;
|
||||
extern register_info_t r_esp;
|
||||
extern register_info_t r_eip;
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
extern const char *x86_register_classes[];
|
||||
extern register_info_t x86_registers[X86_NREGS];
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
int x86_get_regidx(int *clsmask, const char *regname);
|
||||
int x86_get_regclass(int idx);
|
||||
18
idasdk76/dbg/plugin.mak
Normal file
18
idasdk76/dbg/plugin.mak
Normal file
@@ -0,0 +1,18 @@
|
||||
|
||||
ifneq ($(wildcard ../../parse),)
|
||||
CC_DEFS += ENABLE_LOWCNDS
|
||||
endif
|
||||
CC_INCP += ..
|
||||
|
||||
include ../../plugins/plugin.mak
|
||||
|
||||
PLUGIN_LIBS += $(L)dbg_plugin$(A)
|
||||
PLUGIN_LIBS += $(L)dbg_rpc$(A)
|
||||
PLUGIN_LIBS += $(L)dbg_proc$(A)
|
||||
PLUGIN_LIBS += $(L)network$(A)
|
||||
$(MODULES): LIBS += $(PLUGIN_LIBS)
|
||||
$(MODULES): $(PLUGIN_LIBS)
|
||||
|
||||
ifeq ($(or $(__LINUX__),$(__MAC__)),1)
|
||||
$(MODULES): STDLIBS += -ldl
|
||||
endif
|
||||
996
idasdk76/dbg/rpc_debmod.cpp
Normal file
996
idasdk76/dbg/rpc_debmod.cpp
Normal file
@@ -0,0 +1,996 @@
|
||||
|
||||
#include <segment.hpp>
|
||||
#include <err.h>
|
||||
#include <network.hpp>
|
||||
|
||||
#include "rpc_debmod.h"
|
||||
#include "dbg_rpc_hlp.h"
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
inline drc_t unpack_drc(memory_deserializer_t &mmdsr)
|
||||
{
|
||||
return drc_t(mmdsr.unpack_dd());
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
rpc_debmod_t::rpc_debmod_t(const char *default_platform)
|
||||
: dbg_rpc_client_t(NULL)
|
||||
{
|
||||
nregs = debugger.nregs;
|
||||
for ( int i=0; i < nregs; i++ )
|
||||
{
|
||||
const register_info_t &ri = debugger.regs(i);
|
||||
if ( (ri.flags & REGISTER_SP) != 0 )
|
||||
sp_idx = i;
|
||||
if ( (ri.flags & REGISTER_IP) != 0 )
|
||||
pc_idx = i;
|
||||
}
|
||||
bpt_code.append(debugger.bpt_bytes, debugger.bpt_size);
|
||||
rpc = this;
|
||||
|
||||
set_platform(default_platform);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
int idaapi rpc_debmod_t::handle_ioctl( //-V524 equivalent to 'send_ioctl'
|
||||
int fn,
|
||||
const void *buf,
|
||||
size_t size,
|
||||
void **poutbuf,
|
||||
ssize_t *poutsize)
|
||||
{
|
||||
return rpc_engine_t::send_ioctl(fn, buf, size, poutbuf, poutsize);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
inline int get_expected_addrsize(void)
|
||||
{
|
||||
if ( is_miniidb() )
|
||||
#ifdef __EA64__
|
||||
return 8;
|
||||
#else
|
||||
return 4;
|
||||
#endif
|
||||
return inf_is_64bit() ? 8 : 4;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
bool idaapi rpc_debmod_t::open_remote(
|
||||
const char *hostname,
|
||||
int port_number,
|
||||
const char *password,
|
||||
qstring *errbuf)
|
||||
{
|
||||
if ( hostname[0] == '\0' )
|
||||
{
|
||||
if ( errbuf != NULL )
|
||||
*errbuf = "Please specify the hostname in Debugger, Process options";
|
||||
return false;
|
||||
}
|
||||
|
||||
rpc_packet_t *rp = NULL;
|
||||
network_error = false;
|
||||
client_irs = irs_new();
|
||||
if ( !irs_init_client(client_irs, hostname, port_number) )
|
||||
{
|
||||
FAILURE:
|
||||
if ( rp != NULL )
|
||||
qfree(rp);
|
||||
|
||||
if ( errbuf != NULL )
|
||||
*errbuf = irs_strerror(client_irs);
|
||||
irs_term(&client_irs);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
rp = recv_packet();
|
||||
if ( rp == NULL || rp->code != RPC_OPEN ) // is this an ida debugger server?
|
||||
{
|
||||
dbg_rpc_client_t::dwarning("ICON ERROR\nAUTOHIDE NONE\n"
|
||||
"Bogus or irresponsive remote server");
|
||||
goto FAILURE;
|
||||
}
|
||||
|
||||
memory_deserializer_t mmdsr(rp+1, rp->length);
|
||||
int version = mmdsr.unpack_dd();
|
||||
int remote_debugger_id = mmdsr.unpack_dd();
|
||||
int easize = mmdsr.unpack_dd();
|
||||
qstring errstr;
|
||||
if ( version != IDD_INTERFACE_VERSION )
|
||||
errstr.sprnt("protocol version is %d, expected %d", version, IDD_INTERFACE_VERSION);
|
||||
else if ( remote_debugger_id != debugger.id )
|
||||
errstr.sprnt("debugger id is %d, expected %d (%s)", remote_debugger_id, debugger.id, debugger.name);
|
||||
else if ( easize < get_expected_addrsize() )
|
||||
errstr.sprnt("address size is %d bytes, expected at least %d", easize, get_expected_addrsize());
|
||||
if ( !errstr.empty() )
|
||||
{
|
||||
bytevec_t req = prepare_rpc_packet(RPC_OK);
|
||||
req.pack_dd(false);
|
||||
send_data(req);
|
||||
warning("ICON ERROR\nAUTOHIDE NONE\n"
|
||||
"Incompatible debugging server:\n"
|
||||
"%s", errstr.c_str());
|
||||
goto FAILURE;
|
||||
}
|
||||
qfree(rp);
|
||||
|
||||
bytevec_t req = prepare_rpc_packet(RPC_OK);
|
||||
req.pack_dd(true);
|
||||
req.pack_str(password);
|
||||
send_data(req);
|
||||
|
||||
rp = recv_packet();
|
||||
if ( rp == NULL || rp->code != RPC_OK )
|
||||
goto FAILURE;
|
||||
|
||||
memory_deserializer_t mmdsr2(rp+1, rp->length);
|
||||
bool password_ok = mmdsr2.unpack_dd() != 0;
|
||||
if ( !password_ok ) // is this an ida debugger server?
|
||||
{
|
||||
warning("ICON ERROR\nAUTOHIDE NONE\n"
|
||||
"Bad password");
|
||||
goto FAILURE;
|
||||
}
|
||||
|
||||
qfree(rp);
|
||||
return true;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
int idaapi rpc_debmod_t::dbg_add_bpt(bytevec_t *, bpttype_t, ea_t, int)
|
||||
{
|
||||
INTERR(30114);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
int idaapi rpc_debmod_t::dbg_del_bpt(bpttype_t, ea_t, const uchar *, int)
|
||||
{
|
||||
INTERR(30115);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
drc_t idaapi rpc_debmod_t::dbg_update_lowcnds(
|
||||
int *nupdated,
|
||||
const lowcnd_t *lowcnds,
|
||||
int nlowcnds,
|
||||
qstring *errbuf)
|
||||
{
|
||||
ea_t ea = 0;
|
||||
bytevec_t req = prepare_rpc_packet(RPC_UPDATE_LOWCNDS);
|
||||
req.pack_dd(nlowcnds);
|
||||
const lowcnd_t *lc = lowcnds;
|
||||
for ( int i=0; i < nlowcnds; i++, lc++ )
|
||||
{
|
||||
req.pack_ea64(lc->ea-ea); ea = lc->ea;
|
||||
req.pack_str(lc->cndbody);
|
||||
if ( !lc->cndbody.empty() )
|
||||
{
|
||||
req.pack_dd(lc->type);
|
||||
if ( lc->type != BPT_SOFT )
|
||||
req.pack_dd(lc->size);
|
||||
req.pack_db(lc->orgbytes.size());
|
||||
req.append(lc->orgbytes.begin(), lc->orgbytes.size());
|
||||
req.pack_ea64(lc->cmd.ea);
|
||||
if ( lc->cmd.ea != BADADDR )
|
||||
req.append(&lc->cmd, sizeof(lc->cmd));
|
||||
}
|
||||
}
|
||||
|
||||
rpc_packet_t *rp = send_request_and_receive_reply(req);
|
||||
if ( rp == NULL )
|
||||
return DRC_NETERR;
|
||||
|
||||
memory_deserializer_t mmdsr(rp+1, rp->length);
|
||||
drc_t drc = unpack_drc(mmdsr);
|
||||
int ret_nupdated = mmdsr.unpack_dd();
|
||||
if ( nupdated != NULL )
|
||||
*nupdated = ret_nupdated;
|
||||
|
||||
if ( errbuf != NULL && drc != DRC_NONE )
|
||||
*errbuf = mmdsr.unpack_str();
|
||||
|
||||
qfree(rp);
|
||||
return drc;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
drc_t idaapi rpc_debmod_t::dbg_eval_lowcnd(thid_t tid, ea_t ea, qstring *errbuf)
|
||||
{
|
||||
bytevec_t req = prepare_rpc_packet(RPC_EVAL_LOWCND);
|
||||
req.pack_dd(tid);
|
||||
req.pack_ea64(ea);
|
||||
return send_request_get_drc_result(req, errbuf);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
drc_t idaapi rpc_debmod_t::dbg_update_bpts(
|
||||
int *nbpts,
|
||||
update_bpt_info_t *ubpts,
|
||||
int nadd,
|
||||
int ndel,
|
||||
qstring *errbuf)
|
||||
{
|
||||
int skipped = 0;
|
||||
update_bpt_info_t *b;
|
||||
update_bpt_info_t *bend = ubpts + nadd;
|
||||
for ( b=ubpts; b != bend; b++ )
|
||||
if ( b->code != BPT_OK )
|
||||
skipped++;
|
||||
if ( skipped == nadd && ndel == 0 )
|
||||
{
|
||||
if ( nbpts != NULL )
|
||||
*nbpts = 0; // no bpts to update
|
||||
return DRC_OK;
|
||||
}
|
||||
|
||||
bytevec_t req = prepare_rpc_packet(RPC_UPDATE_BPTS);
|
||||
req.pack_dd(nadd-skipped);
|
||||
req.pack_dd(ndel);
|
||||
ea_t ea = 0;
|
||||
for ( b=ubpts; b != bend; b++ )
|
||||
{
|
||||
if ( b->code == BPT_OK )
|
||||
{
|
||||
req.pack_ea64(b->ea-ea); ea = b->ea;
|
||||
req.pack_dd(b->size);
|
||||
req.pack_dd(b->type);
|
||||
req.pack_dd(b->pid);
|
||||
req.pack_dd(b->tid);
|
||||
}
|
||||
}
|
||||
|
||||
ea = 0;
|
||||
bend += ndel;
|
||||
for ( ; b != bend; b++ )
|
||||
{
|
||||
req.pack_ea64(b->ea-ea); ea = b->ea;
|
||||
req.pack_db(b->orgbytes.size());
|
||||
req.append(b->orgbytes.begin(), b->orgbytes.size());
|
||||
req.pack_dd(b->type);
|
||||
req.pack_dd(b->pid);
|
||||
req.pack_dd(b->tid);
|
||||
}
|
||||
|
||||
rpc_packet_t *rp = send_request_and_receive_reply(req);
|
||||
if ( rp == NULL )
|
||||
return DRC_NETERR;
|
||||
|
||||
memory_deserializer_t mmdsr(rp+1, rp->length);
|
||||
drc_t drc = unpack_drc(mmdsr);
|
||||
int ret_nbpts = mmdsr.unpack_dd();
|
||||
if ( nbpts != NULL )
|
||||
*nbpts = ret_nbpts;
|
||||
bend = ubpts + nadd;
|
||||
for ( b=ubpts; b != bend; b++ )
|
||||
{
|
||||
if ( b->code == BPT_OK )
|
||||
{
|
||||
b->code = mmdsr.unpack_db();
|
||||
if ( b->code == BPT_OK && b->type == BPT_SOFT )
|
||||
{
|
||||
uchar len = mmdsr.unpack_db();
|
||||
b->orgbytes.resize(len);
|
||||
mmdsr.unpack_obj(b->orgbytes.begin(), len);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bend += ndel;
|
||||
for ( ; b != bend; b++ )
|
||||
b->code = mmdsr.unpack_db();
|
||||
|
||||
if ( errbuf != NULL && drc != DRC_NONE )
|
||||
*errbuf = mmdsr.unpack_str();
|
||||
return drc;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
drc_t idaapi rpc_debmod_t::dbg_thread_get_sreg_base(ea_t *ea, thid_t tid, int sreg_value, qstring *errbuf)
|
||||
{
|
||||
bytevec_t req = prepare_rpc_packet(RPC_GET_SREG_BASE);
|
||||
req.pack_dd(tid);
|
||||
req.pack_dd(sreg_value);
|
||||
|
||||
rpc_packet_t *rp = send_request_and_receive_reply(req);
|
||||
if ( rp == NULL )
|
||||
return DRC_NETERR;
|
||||
|
||||
memory_deserializer_t mmdsr(rp+1, rp->length);
|
||||
drc_t drc = unpack_drc(mmdsr);
|
||||
if ( drc == DRC_OK )
|
||||
*ea = mmdsr.unpack_ea64();
|
||||
else if ( errbuf != NULL )
|
||||
*errbuf = mmdsr.unpack_str();
|
||||
|
||||
qfree(rp);
|
||||
return drc;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
void idaapi rpc_debmod_t::dbg_set_exception_info(const exception_info_t *table, int qty)
|
||||
{
|
||||
bytevec_t req = prepare_rpc_packet(RPC_SET_EXCEPTION_INFO);
|
||||
req.pack_dd(qty);
|
||||
append_exception_info(req, table, qty);
|
||||
|
||||
qfree(send_request_and_receive_reply(req));
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
int idaapi rpc_debmod_t::dbg_open_file(const char *file, uint64 *fsize, bool readonly)
|
||||
{
|
||||
bytevec_t req = prepare_rpc_packet(RPC_OPEN_FILE);
|
||||
req.pack_str(file);
|
||||
req.pack_dd(readonly);
|
||||
|
||||
rpc_packet_t *rp = send_request_and_receive_reply(req);
|
||||
if ( rp == NULL )
|
||||
return -1;
|
||||
|
||||
memory_deserializer_t mmdsr(rp+1, rp->length);
|
||||
int fn = mmdsr.unpack_dd();
|
||||
if ( fn != -1 )
|
||||
{
|
||||
if ( fsize != NULL && readonly )
|
||||
*fsize = mmdsr.unpack_dq();
|
||||
}
|
||||
else
|
||||
{
|
||||
qerrcode(mmdsr.unpack_dd());
|
||||
}
|
||||
qfree(rp);
|
||||
return fn;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
void idaapi rpc_debmod_t::dbg_close_file(int fn)
|
||||
{
|
||||
bytevec_t req = prepare_rpc_packet(RPC_CLOSE_FILE);
|
||||
req.pack_dd(fn);
|
||||
|
||||
qfree(send_request_and_receive_reply(req));
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
ssize_t idaapi rpc_debmod_t::dbg_read_file(int fn, qoff64_t off, void *buf, size_t size)
|
||||
{
|
||||
bytevec_t req = prepare_rpc_packet(RPC_READ_FILE);
|
||||
req.pack_dd(fn);
|
||||
req.pack_dq(off);
|
||||
req.pack_dd((uint32)size);
|
||||
|
||||
rpc_packet_t *rp = send_request_and_receive_reply(req);
|
||||
if ( rp == NULL )
|
||||
return -1;
|
||||
|
||||
memory_deserializer_t mmdsr(rp+1, rp->length);
|
||||
int32 rsize = mmdsr.unpack_dd();
|
||||
if ( size != rsize )
|
||||
qerrcode(mmdsr.unpack_dd());
|
||||
|
||||
if ( rsize > 0 )
|
||||
{
|
||||
QASSERT(1204, rsize <= size);
|
||||
mmdsr.unpack_obj(buf, rsize);
|
||||
}
|
||||
qfree(rp);
|
||||
return rsize;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
ssize_t idaapi rpc_debmod_t::dbg_write_file(int fn, qoff64_t off, const void *buf, size_t size)
|
||||
{
|
||||
bytevec_t req = prepare_rpc_packet(RPC_WRITE_FILE);
|
||||
req.pack_dd(fn);
|
||||
req.pack_dq(off);
|
||||
req.pack_buf(buf, size);
|
||||
|
||||
rpc_packet_t *rp = send_request_and_receive_reply(req);
|
||||
if ( rp == NULL )
|
||||
return -1;
|
||||
|
||||
memory_deserializer_t mmdsr(rp+1, rp->length);
|
||||
int32 rsize = mmdsr.unpack_dd();
|
||||
if ( size != rsize )
|
||||
qerrcode(mmdsr.unpack_dd());
|
||||
|
||||
qfree(rp);
|
||||
return rsize;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
int idaapi rpc_debmod_t::dbg_is_ok_bpt(bpttype_t type, ea_t ea, int len)
|
||||
{
|
||||
bytevec_t req = prepare_rpc_packet(RPC_ISOK_BPT);
|
||||
req.pack_dd(type);
|
||||
req.pack_ea64(ea);
|
||||
req.pack_dd(len+1);
|
||||
|
||||
return send_request_get_long_result(req);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
void idaapi rpc_debmod_t::dbg_set_debugging(bool _debug_debugger)
|
||||
{
|
||||
debug_debugger = _debug_debugger;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
drc_t idaapi rpc_debmod_t::dbg_init(uint32_t *_flags2, qstring *errbuf)
|
||||
{
|
||||
has_pending_event = false;
|
||||
poll_debug_events = false;
|
||||
|
||||
bytevec_t req = prepare_rpc_packet(RPC_INIT);
|
||||
req.pack_dd(debugger.flags);
|
||||
req.pack_dd(debug_debugger);
|
||||
|
||||
rpc_packet_t *rp = send_request_and_receive_reply(req);
|
||||
if ( rp == nullptr )
|
||||
return DRC_NETERR;
|
||||
|
||||
memory_deserializer_t mmdsr(rp+1, rp->length);
|
||||
drc_t drc = unpack_drc(mmdsr);
|
||||
uint32_t flags2 = mmdsr.unpack_dd();
|
||||
if ( _flags2 != nullptr )
|
||||
*_flags2 = flags2;
|
||||
if ( drc != DRC_OK && errbuf != nullptr )
|
||||
*errbuf = mmdsr.unpack_str();
|
||||
|
||||
qfree(rp);
|
||||
return drc;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
void idaapi rpc_debmod_t::dbg_term(void)
|
||||
{
|
||||
bytevec_t req = prepare_rpc_packet(RPC_TERM);
|
||||
|
||||
qfree(send_request_and_receive_reply(req));
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
drc_t idaapi rpc_debmod_t::dbg_get_processes(procinfo_vec_t *procs, qstring *errbuf)
|
||||
{
|
||||
bytevec_t req = prepare_rpc_packet(RPC_GET_PROCESSES);
|
||||
|
||||
rpc_packet_t *rp = send_request_and_receive_reply(req);
|
||||
if ( rp == NULL )
|
||||
return DRC_NETERR;
|
||||
memory_deserializer_t mmdsr(rp+1, rp->length);
|
||||
|
||||
procs->qclear();
|
||||
drc_t drc = unpack_drc(mmdsr);
|
||||
if ( drc == DRC_OK )
|
||||
extract_process_info_vec(procs, mmdsr);
|
||||
else if ( errbuf != NULL )
|
||||
*errbuf = mmdsr.unpack_str();
|
||||
|
||||
qfree(rp);
|
||||
return drc;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
drc_t idaapi rpc_debmod_t::dbg_detach_process(void)
|
||||
{
|
||||
return get_drc(RPC_DETACH_PROCESS);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
drc_t idaapi rpc_debmod_t::dbg_start_process(
|
||||
const char *path,
|
||||
const char *args,
|
||||
const char *startdir,
|
||||
int flags,
|
||||
const char *input_path,
|
||||
uint32 input_file_crc32,
|
||||
qstring *errbuf)
|
||||
{
|
||||
if ( inf_test_mode() )
|
||||
flags |= DBG_HIDE_WINDOW;
|
||||
bytevec_t req = prepare_rpc_packet(RPC_START_PROCESS);
|
||||
req.pack_str(path);
|
||||
req.pack_str(args);
|
||||
req.pack_str(startdir);
|
||||
req.pack_dd(flags);
|
||||
req.pack_str(input_path);
|
||||
req.pack_dd(input_file_crc32);
|
||||
|
||||
return process_start_or_attach(req, errbuf);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
gdecode_t idaapi rpc_debmod_t::dbg_get_debug_event(debug_event_t *event, int timeout_ms)
|
||||
{
|
||||
if ( has_pending_event )
|
||||
{
|
||||
verbev(("get_debug_event => has pending event, returning it\n"));
|
||||
*event = pending_event;
|
||||
has_pending_event = false;
|
||||
poll_debug_events = false;
|
||||
return GDE_ONE_EVENT;
|
||||
}
|
||||
|
||||
gdecode_t result = GDE_NO_EVENT;
|
||||
if ( poll_debug_events )
|
||||
{
|
||||
// do we have something waiting?
|
||||
if ( irs_ready(client_irs, timeout_ms) > 0 )
|
||||
{
|
||||
verbev(("get_debug_event => remote has a packet for us\n"));
|
||||
// get the packet - it can RPC_EVENT or RPC_MSG/RPC_WARNING/RPC_ERROR
|
||||
bytevec_t empty;
|
||||
rpc_packet_t *rp = send_request_and_receive_reply(empty, PREQ_GET_EVENT);
|
||||
verbev(("get_debug_event => processed remote event, has=%d\n", has_pending_event));
|
||||
if ( rp != NULL )
|
||||
{
|
||||
warning("rpc: event protocol error (rp=%p has_event=%d)", rp, has_pending_event);
|
||||
return GDE_ERROR;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
verbev(("get_debug_event => first time, send GET_DEBUG_EVENT\n"));
|
||||
bytevec_t req = prepare_rpc_packet(RPC_GET_DEBUG_EVENT);
|
||||
req.pack_dd(timeout_ms);
|
||||
|
||||
rpc_packet_t *rp = send_request_and_receive_reply(req);
|
||||
if ( rp == NULL )
|
||||
return GDE_ERROR;
|
||||
memory_deserializer_t mmdsr(rp+1, rp->length);
|
||||
|
||||
result = gdecode_t(mmdsr.unpack_dd());
|
||||
if ( result >= GDE_ONE_EVENT )
|
||||
extract_debug_event(event, mmdsr);
|
||||
else
|
||||
poll_debug_events = true;
|
||||
verbev(("get_debug_event => remote said %d, poll=%d now\n", result, poll_debug_events));
|
||||
qfree(rp);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
drc_t idaapi rpc_debmod_t::dbg_attach_process(pid_t _pid, int event_id, int flags, qstring *errbuf)
|
||||
{
|
||||
bytevec_t req = prepare_rpc_packet(RPC_ATTACH_PROCESS);
|
||||
req.pack_dd(_pid);
|
||||
req.pack_dd(event_id);
|
||||
req.pack_dd(flags);
|
||||
return process_start_or_attach(req, errbuf);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
drc_t idaapi rpc_debmod_t::dbg_prepare_to_pause_process(qstring *errbuf)
|
||||
{
|
||||
return get_drc(RPC_PREPARE_TO_PAUSE_PROCESS, errbuf);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
drc_t idaapi rpc_debmod_t::dbg_exit_process(qstring *errbuf)
|
||||
{
|
||||
return get_drc(RPC_EXIT_PROCESS, errbuf);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
drc_t idaapi rpc_debmod_t::dbg_continue_after_event(const debug_event_t *event)
|
||||
{
|
||||
bytevec_t req = prepare_rpc_packet(RPC_CONTINUE_AFTER_EVENT);
|
||||
append_debug_event(req, event);
|
||||
|
||||
return send_request_get_drc_result(req, NULL);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
void idaapi rpc_debmod_t::dbg_stopped_at_debug_event(
|
||||
import_infos_t *,
|
||||
bool dlls_added,
|
||||
thread_name_vec_t *thr_names)
|
||||
{
|
||||
bytevec_t req = prepare_rpc_packet(RPC_STOPPED_AT_DEBUG_EVENT);
|
||||
req.pack_db(dlls_added);
|
||||
bool ask_thr_names = thr_names != NULL;
|
||||
req.pack_db(ask_thr_names);
|
||||
|
||||
rpc_packet_t *rp = send_request_and_receive_reply(req);
|
||||
if ( rp == NULL )
|
||||
return;
|
||||
|
||||
if ( ask_thr_names )
|
||||
{
|
||||
memory_deserializer_t mmdsr(rp+1, rp->length);
|
||||
uint32 n = mmdsr.unpack_dd();
|
||||
thr_names->resize(n);
|
||||
for ( uint32 i=0; i < n; ++i )
|
||||
{
|
||||
thread_name_t &tn = (*thr_names)[i];
|
||||
tn.tid = mmdsr.unpack_dd();
|
||||
tn.name = mmdsr.unpack_str();
|
||||
}
|
||||
}
|
||||
|
||||
qfree(rp);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
drc_t idaapi rpc_debmod_t::dbg_thread_suspend(thid_t tid)
|
||||
{
|
||||
return get_drc_int(RPC_TH_SUSPEND, tid);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
drc_t idaapi rpc_debmod_t::dbg_thread_continue(thid_t tid)
|
||||
{
|
||||
return get_drc_int(RPC_TH_CONTINUE, tid);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
drc_t idaapi rpc_debmod_t::dbg_set_resume_mode(thid_t tid, resume_mode_t resmod)
|
||||
{
|
||||
bytevec_t req = prepare_rpc_packet(RPC_SET_RESUME_MODE);
|
||||
req.pack_dd(tid);
|
||||
req.pack_dd(resmod);
|
||||
|
||||
return send_request_get_drc_result(req, NULL);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// prepare bitmap of registers belonging to the specified classes
|
||||
// return size of the bitmap in bits (always the total number of registers)
|
||||
static int calc_regmap(bytevec_t *regmap, int clsmask)
|
||||
{
|
||||
int nregs = debugger.nregs;
|
||||
regmap->resize((nregs+7)/8, 0);
|
||||
for ( int i=0; i < nregs; i++ )
|
||||
if ( (debugger.regs(i).register_class & clsmask) != 0 )
|
||||
regmap->set_bit(i);
|
||||
return nregs;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
drc_t idaapi rpc_debmod_t::dbg_read_registers(
|
||||
thid_t tid,
|
||||
int clsmask,
|
||||
regval_t *values,
|
||||
qstring *errbuf)
|
||||
{
|
||||
bytevec_t req = prepare_rpc_packet(RPC_READ_REGS);
|
||||
req.pack_dd(tid);
|
||||
req.pack_dd(clsmask);
|
||||
// append additional information about the class structure
|
||||
bytevec_t regmap;
|
||||
int n_regs = calc_regmap(®map, clsmask);
|
||||
req.pack_dd(n_regs);
|
||||
req.append(regmap.begin(), regmap.size());
|
||||
|
||||
rpc_packet_t *rp = send_request_and_receive_reply(req);
|
||||
if ( rp == NULL )
|
||||
return DRC_NETERR;
|
||||
|
||||
memory_deserializer_t mmdsr(rp+1, rp->length);
|
||||
drc_t drc = unpack_drc(mmdsr);
|
||||
if ( drc == DRC_OK )
|
||||
unpack_regvals(values, n_regs, regmap.begin(), mmdsr);
|
||||
else if ( errbuf != NULL )
|
||||
*errbuf = mmdsr.unpack_str();
|
||||
qfree(rp);
|
||||
return drc;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
drc_t idaapi rpc_debmod_t::dbg_write_register(
|
||||
thid_t tid,
|
||||
int reg_idx,
|
||||
const regval_t *value,
|
||||
qstring *errbuf)
|
||||
{
|
||||
bytevec_t req = prepare_rpc_packet(RPC_WRITE_REG);
|
||||
req.pack_dd(tid);
|
||||
req.pack_dd(reg_idx);
|
||||
append_regvals(req, value, 1, NULL);
|
||||
|
||||
return send_request_get_drc_result(req, errbuf);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
drc_t idaapi rpc_debmod_t::dbg_get_memory_info(meminfo_vec_t &areas, qstring *errbuf)
|
||||
{
|
||||
bytevec_t req = prepare_rpc_packet(RPC_GET_MEMORY_INFO);
|
||||
|
||||
rpc_packet_t *rp = send_request_and_receive_reply(req);
|
||||
if ( rp == NULL )
|
||||
return DRC_NETERR;
|
||||
|
||||
memory_deserializer_t mmdsr(rp+1, rp->length);
|
||||
drc_t drc = drc_t(mmdsr.unpack_dd() + DRC_IDBSEG);
|
||||
if ( drc > DRC_NONE )
|
||||
{
|
||||
int n = mmdsr.unpack_dd();
|
||||
areas.resize(n);
|
||||
for ( int i=0; i < n; i++ )
|
||||
extract_memory_info(&areas[i], mmdsr);
|
||||
}
|
||||
else if ( errbuf != NULL )
|
||||
{
|
||||
*errbuf = mmdsr.unpack_str();
|
||||
}
|
||||
qfree(rp);
|
||||
return drc;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
int idaapi rpc_debmod_t::dbg_get_scattered_image(scattered_image_t &si, ea_t base)
|
||||
{
|
||||
bytevec_t req = prepare_rpc_packet(RPC_GET_SCATTERED_IMAGE);
|
||||
req.pack_ea64(base);
|
||||
|
||||
rpc_packet_t *rp = send_request_and_receive_reply(req);
|
||||
if ( rp == NULL )
|
||||
return false;
|
||||
|
||||
memory_deserializer_t mmdsr(rp+1, rp->length);
|
||||
int result = mmdsr.unpack_dd() - 2;
|
||||
if ( result > 0 )
|
||||
{
|
||||
int n = mmdsr.unpack_dd();
|
||||
si.resize(n);
|
||||
for ( int i=0; i < n; i++ )
|
||||
extract_scattered_segm(&si[i], mmdsr);
|
||||
}
|
||||
qfree(rp);
|
||||
return result;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
bool idaapi rpc_debmod_t::dbg_get_image_uuid(bytevec_t *uuid, ea_t base)
|
||||
{
|
||||
bytevec_t req = prepare_rpc_packet(RPC_GET_IMAGE_UUID);
|
||||
req.pack_ea64(base);
|
||||
|
||||
rpc_packet_t *rp = send_request_and_receive_reply(req);
|
||||
if ( rp == NULL )
|
||||
return false;
|
||||
|
||||
memory_deserializer_t mmdsr(rp+1, rp->length);
|
||||
bool result = mmdsr.unpack_dd() != 0;
|
||||
if ( result )
|
||||
{
|
||||
int n = mmdsr.unpack_dd();
|
||||
uuid->append(mmdsr.ptr, n);
|
||||
}
|
||||
qfree(rp);
|
||||
return result;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
ea_t idaapi rpc_debmod_t::dbg_get_segm_start(ea_t base, const qstring &segname)
|
||||
{
|
||||
bytevec_t req = prepare_rpc_packet(RPC_GET_SEGM_START);
|
||||
req.pack_ea64(base);
|
||||
req.pack_str(segname.c_str());
|
||||
|
||||
rpc_packet_t *rp = send_request_and_receive_reply(req);
|
||||
if ( rp == NULL )
|
||||
return false;
|
||||
|
||||
memory_deserializer_t mmdsr(rp+1, rp->length);
|
||||
ea_t result = mmdsr.unpack_ea64();
|
||||
qfree(rp);
|
||||
return result;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
ssize_t idaapi rpc_debmod_t::dbg_read_memory(ea_t ea, void *buffer, size_t size, qstring *errbuf)
|
||||
{
|
||||
bytevec_t req = prepare_rpc_packet(RPC_READ_MEMORY);
|
||||
req.pack_ea64(ea);
|
||||
req.pack_dd((uint32)size);
|
||||
|
||||
rpc_packet_t *rp = send_request_and_receive_reply(req);
|
||||
if ( rp == NULL )
|
||||
return -1;
|
||||
|
||||
memory_deserializer_t mmdsr(rp+1, rp->length);
|
||||
int result = mmdsr.unpack_dd();
|
||||
if ( result > 0 )
|
||||
{
|
||||
QASSERT(1205, result <= size);
|
||||
mmdsr.unpack_obj(buffer, result);
|
||||
}
|
||||
else if ( errbuf != NULL )
|
||||
{
|
||||
*errbuf = mmdsr.unpack_str();
|
||||
}
|
||||
qfree(rp);
|
||||
return result;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
ssize_t idaapi rpc_debmod_t::dbg_write_memory(ea_t ea, const void *buffer, size_t size, qstring *errbuf)
|
||||
{
|
||||
bytevec_t req = prepare_rpc_packet(RPC_WRITE_MEMORY);
|
||||
req.pack_ea64(ea);
|
||||
req.pack_buf(buffer, size);
|
||||
|
||||
rpc_packet_t *rp = send_request_and_receive_reply(req);
|
||||
if ( rp == NULL )
|
||||
return -1;
|
||||
|
||||
memory_deserializer_t mmdsr(rp+1, rp->length);
|
||||
int result = mmdsr.unpack_dd();
|
||||
if ( errbuf != NULL && result <= 0 )
|
||||
*errbuf = mmdsr.unpack_str();
|
||||
qfree(rp);
|
||||
return result;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
drc_t idaapi rpc_debmod_t::dbg_update_call_stack(thid_t tid, call_stack_t *trace)
|
||||
{
|
||||
bytevec_t req = prepare_rpc_packet(RPC_UPDATE_CALL_STACK);
|
||||
req.pack_dd(tid);
|
||||
|
||||
rpc_packet_t *rp = send_request_and_receive_reply(req);
|
||||
if ( rp == NULL )
|
||||
return DRC_NETERR;
|
||||
|
||||
memory_deserializer_t mmdsr(rp+1, rp->length);
|
||||
drc_t drc = unpack_drc(mmdsr);
|
||||
if ( drc == DRC_OK )
|
||||
extract_call_stack(trace, mmdsr);
|
||||
qfree(rp);
|
||||
return drc;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
ea_t idaapi rpc_debmod_t::dbg_appcall(
|
||||
ea_t func_ea,
|
||||
thid_t tid,
|
||||
int stkarg_nbytes,
|
||||
const struct regobjs_t *regargs,
|
||||
struct relobj_t *stkargs,
|
||||
struct regobjs_t *retregs,
|
||||
qstring *errbuf,
|
||||
debug_event_t *event,
|
||||
int flags)
|
||||
{
|
||||
bytevec_t req = prepare_rpc_packet(RPC_APPCALL);
|
||||
req.pack_ea64(func_ea);
|
||||
req.pack_dd(tid);
|
||||
req.pack_dd(stkarg_nbytes);
|
||||
req.pack_dd(flags);
|
||||
regobjs_t *rr = (flags & APPCALL_MANUAL) == 0 ? retregs : NULL;
|
||||
append_appcall(req, *regargs, *stkargs, rr);
|
||||
|
||||
rpc_packet_t *rp = send_request_and_receive_reply(req);
|
||||
if ( rp == NULL )
|
||||
return BADADDR;
|
||||
|
||||
memory_deserializer_t mmdsr(rp+1, rp->length);
|
||||
ea_t sp = mmdsr.unpack_ea64();
|
||||
if ( sp == BADADDR )
|
||||
{
|
||||
if ( (flags & APPCALL_DEBEV) != 0 )
|
||||
extract_debug_event(event, mmdsr);
|
||||
if ( errbuf != NULL )
|
||||
*errbuf = mmdsr.unpack_str();
|
||||
}
|
||||
else if ( (flags & APPCALL_MANUAL) == 0 )
|
||||
{
|
||||
if ( retregs != NULL )
|
||||
extract_regobjs(retregs, true, mmdsr);
|
||||
}
|
||||
qfree(rp);
|
||||
return sp;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
drc_t idaapi rpc_debmod_t::dbg_cleanup_appcall(thid_t tid)
|
||||
{
|
||||
bytevec_t req = prepare_rpc_packet(RPC_CLEANUP_APPCALL);
|
||||
req.pack_dd(tid);
|
||||
return send_request_get_drc_result(req, NULL);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
int idaapi rpc_debmod_t::dbg_rexec(const char *cmdline)
|
||||
{
|
||||
bytevec_t req = prepare_rpc_packet(RPC_REXEC);
|
||||
req.pack_str(cmdline);
|
||||
return send_request_get_long_result(req);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
drc_t idaapi rpc_debmod_t::dbg_bin_search(
|
||||
ea_t *pea,
|
||||
ea_t start_ea,
|
||||
ea_t end_ea,
|
||||
const compiled_binpat_vec_t &ptns,
|
||||
int srch_flags,
|
||||
qstring *errbuf)
|
||||
{
|
||||
bytevec_t req = prepare_rpc_packet(RPC_BIN_SEARCH);
|
||||
req.pack_ea64(start_ea);
|
||||
req.pack_ea64(end_ea);
|
||||
// compiled_binpat_vec_t
|
||||
int sz = ptns.size();
|
||||
req.pack_dd(sz);
|
||||
for ( compiled_binpat_vec_t::const_iterator p=ptns.begin();
|
||||
p != ptns.end();
|
||||
++p )
|
||||
{ // compiled_binpat_t
|
||||
sz = p->bytes.size();
|
||||
req.pack_buf(p->bytes.begin(), sz);
|
||||
sz = p->mask.size();
|
||||
req.pack_buf(p->mask.begin(), sz);
|
||||
sz = p->strlits.size();
|
||||
req.pack_dd(sz);
|
||||
for ( int i=0; i < sz; ++i )
|
||||
{
|
||||
req.pack_ea64(p->strlits[i].start_ea);
|
||||
req.pack_ea64(p->strlits[i].end_ea);
|
||||
}
|
||||
req.pack_dd(p->encidx);
|
||||
}
|
||||
req.pack_dd(srch_flags);
|
||||
|
||||
rpc_packet_t *rp = send_request_and_receive_reply(req);
|
||||
if ( rp == NULL )
|
||||
return DRC_NETERR;
|
||||
|
||||
memory_deserializer_t mmdsr(rp+1, rp->length);
|
||||
drc_t drc = unpack_drc(mmdsr);
|
||||
if ( drc == DRC_OK )
|
||||
{
|
||||
if ( pea != NULL )
|
||||
*pea = mmdsr.unpack_ea64();
|
||||
}
|
||||
else if ( drc != DRC_FAILED ) // DRC_FAILED means not found
|
||||
{
|
||||
if ( errbuf != NULL )
|
||||
*errbuf = mmdsr.unpack_str();
|
||||
}
|
||||
|
||||
qfree(rp);
|
||||
return drc;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
drc_t rpc_debmod_t::close_remote()
|
||||
{
|
||||
bytevec_t req = prepare_rpc_packet(RPC_OK);
|
||||
send_data(req);
|
||||
irs_term(&client_irs);
|
||||
network_error = false;
|
||||
return DRC_OK;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
int idaapi rpc_debmod_t::get_system_specific_errno(void) const
|
||||
{
|
||||
return irs_get_error(client_irs);
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
drc_t rpc_debmod_t::process_start_or_attach(bytevec_t &req, qstring *errbuf)
|
||||
{
|
||||
rpc_packet_t *rp = send_request_and_receive_reply(req);
|
||||
if ( rp == NULL )
|
||||
return DRC_NETERR;
|
||||
|
||||
memory_deserializer_t mmdsr(rp+1, rp->length);
|
||||
drc_t drc = unpack_drc(mmdsr);
|
||||
if ( drc > DRC_NONE )
|
||||
{
|
||||
extract_debapp_attrs(&debapp_attrs, mmdsr);
|
||||
extract_dynamic_register_set(&idaregs, mmdsr);
|
||||
}
|
||||
else if ( errbuf != NULL )
|
||||
{
|
||||
*errbuf = mmdsr.unpack_str();
|
||||
}
|
||||
qfree(rp);
|
||||
return drc;
|
||||
}
|
||||
121
idasdk76/dbg/rpc_debmod.h
Normal file
121
idasdk76/dbg/rpc_debmod.h
Normal file
@@ -0,0 +1,121 @@
|
||||
#ifndef __RPC_DEBUGGER_MODULE__
|
||||
#define __RPC_DEBUGGER_MODULE__
|
||||
|
||||
#define pack_ea DONT_USE_pack_ea_USE_pack_ea64_INSTEAD
|
||||
#define unpack_ea DONT_USE_unpack_ea_USE_unpack_ea64_INSTEAD
|
||||
|
||||
#include "debmod.h"
|
||||
#include "dbg_rpc_client.h"
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
class rpc_debmod_t
|
||||
: public debmod_t,
|
||||
public dbg_rpc_client_t
|
||||
{
|
||||
drc_t process_start_or_attach(bytevec_t &req, qstring *errbuf);
|
||||
|
||||
public:
|
||||
rpc_debmod_t(const char *default_platform = NULL);
|
||||
virtual bool idaapi open_remote(const char *hostname, int port_number, const char *password, qstring *errbuf) newapi;
|
||||
drc_t close_remote();
|
||||
|
||||
int send_ioctl(int fn, const void *buf, size_t size, void **poutbuf, ssize_t *poutsize)
|
||||
{
|
||||
return rpc_engine_t::send_ioctl(fn, buf, size, poutbuf, poutsize);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
inline int getint(ushort code)
|
||||
{
|
||||
bytevec_t req = prepare_rpc_packet((uchar)code);
|
||||
return send_request_get_long_result(req);
|
||||
}
|
||||
drc_t get_drc_int(uchar code, int x)
|
||||
{
|
||||
bytevec_t req = prepare_rpc_packet(code);
|
||||
req.pack_dd(x);
|
||||
return send_request_get_drc_result(req, NULL);
|
||||
}
|
||||
inline drc_t get_drc(ushort code, qstring *errbuf=NULL)
|
||||
{
|
||||
bytevec_t req = prepare_rpc_packet((uchar)code);
|
||||
return send_request_get_drc_result(req, errbuf);
|
||||
}
|
||||
|
||||
//
|
||||
virtual void idaapi dbg_set_debugging(bool _debug_debugger) override;
|
||||
virtual drc_t idaapi dbg_init(uint32_t *flags2, qstring *errbuf) override;
|
||||
virtual void idaapi dbg_term(void) override;
|
||||
virtual drc_t idaapi dbg_get_processes(procinfo_vec_t *procs, qstring *errbuf) override;
|
||||
virtual drc_t idaapi dbg_detach_process(void) override;
|
||||
virtual drc_t idaapi dbg_start_process(
|
||||
const char *path,
|
||||
const char *args,
|
||||
const char *startdir,
|
||||
int flags,
|
||||
const char *input_path,
|
||||
uint32 input_file_crc32,
|
||||
qstring *errbuf) override;
|
||||
virtual gdecode_t idaapi dbg_get_debug_event(debug_event_t *event, int timeout_ms) override;
|
||||
virtual drc_t idaapi dbg_attach_process(pid_t process_id, int event_id, int flags, qstring *errbuf) override;
|
||||
virtual drc_t idaapi dbg_prepare_to_pause_process(qstring *errbuf) override;
|
||||
virtual drc_t idaapi dbg_exit_process(qstring *errbuf) override;
|
||||
virtual drc_t idaapi dbg_continue_after_event(const debug_event_t *event) override;
|
||||
virtual void idaapi dbg_set_exception_info(const exception_info_t *info, int qty) override;
|
||||
virtual void idaapi dbg_stopped_at_debug_event(import_infos_t *infos, bool dlls_added, thread_name_vec_t *thr_names) override;
|
||||
virtual drc_t idaapi dbg_thread_suspend(thid_t thread_id) override;
|
||||
virtual drc_t idaapi dbg_thread_continue(thid_t thread_id) override;
|
||||
virtual drc_t idaapi dbg_set_resume_mode(thid_t thread_id, resume_mode_t resmod) override;
|
||||
virtual drc_t idaapi dbg_read_registers(
|
||||
thid_t thread_id,
|
||||
int clsmask,
|
||||
regval_t *values,
|
||||
qstring *errbuf) override;
|
||||
virtual drc_t idaapi dbg_write_register(
|
||||
thid_t thread_id,
|
||||
int reg_idx,
|
||||
const regval_t *value,
|
||||
qstring *errbuf) override;
|
||||
virtual drc_t idaapi dbg_thread_get_sreg_base(ea_t *ea, thid_t thread_id, int sreg_value, qstring *errbuf) override;
|
||||
virtual drc_t idaapi dbg_get_memory_info(meminfo_vec_t &areas, qstring *errbuf) override;
|
||||
virtual int idaapi dbg_get_scattered_image(scattered_image_t &si, ea_t base) override;
|
||||
virtual bool idaapi dbg_get_image_uuid(bytevec_t *uuid, ea_t base) override;
|
||||
virtual ea_t idaapi dbg_get_segm_start(ea_t base, const qstring &segname) override;
|
||||
virtual ssize_t idaapi dbg_read_memory(ea_t ea, void *buffer, size_t size, qstring *errbuf) override;
|
||||
virtual ssize_t idaapi dbg_write_memory(ea_t ea, const void *buffer, size_t size, qstring *errbuf) override;
|
||||
virtual int idaapi dbg_is_ok_bpt(bpttype_t type, ea_t ea, int len) override;
|
||||
virtual int idaapi dbg_add_bpt(bytevec_t *orig_bytes, bpttype_t type, ea_t ea, int len) override;
|
||||
virtual int idaapi dbg_del_bpt(bpttype_t type, ea_t ea, const uchar *orig_bytes, int len) override;
|
||||
virtual drc_t idaapi dbg_update_bpts(int *nbpts, update_bpt_info_t *bpts, int nadd, int ndel, qstring *errbuf) override;
|
||||
virtual drc_t idaapi dbg_update_lowcnds(int *nupdated, const lowcnd_t *lowcnds, int nlowcnds, qstring *errbuf) override;
|
||||
virtual drc_t idaapi dbg_eval_lowcnd(thid_t tid, ea_t ea, qstring *errbuf) override;
|
||||
virtual int idaapi dbg_open_file(const char *file, uint64 *fsize, bool readonly) override;
|
||||
virtual void idaapi dbg_close_file(int fn) override;
|
||||
virtual ssize_t idaapi dbg_read_file(int fn, qoff64_t off, void *buf, size_t size) override;
|
||||
virtual ssize_t idaapi dbg_write_file(int fn, qoff64_t off, const void *buf, size_t size) override;
|
||||
virtual int idaapi handle_ioctl(int fn, const void *buf, size_t size, void **outbuf, ssize_t *outsize) override;
|
||||
virtual int idaapi get_system_specific_errno(void) const override;
|
||||
virtual drc_t idaapi dbg_update_call_stack(thid_t, call_stack_t *) override;
|
||||
virtual ea_t idaapi dbg_appcall(
|
||||
ea_t func_ea,
|
||||
thid_t tid,
|
||||
int stkarg_nbytes,
|
||||
const struct regobjs_t *regargs,
|
||||
struct relobj_t *stkargs,
|
||||
struct regobjs_t *retregs,
|
||||
qstring *errbuf,
|
||||
debug_event_t *event,
|
||||
int flags) override;
|
||||
virtual drc_t idaapi dbg_cleanup_appcall(thid_t tid) override;
|
||||
virtual int get_regidx(const char *, int *) override { INTERR(30116); }
|
||||
virtual int idaapi dbg_rexec(const char *cmdline) override;
|
||||
virtual drc_t idaapi dbg_bin_search(
|
||||
ea_t *ea,
|
||||
ea_t start_ea,
|
||||
ea_t end_ea,
|
||||
const compiled_binpat_vec_t &ptns,
|
||||
int srch_flags,
|
||||
qstring *errbuf) override;
|
||||
};
|
||||
|
||||
#endif
|
||||
224
idasdk76/dbg/server.cpp
Normal file
224
idasdk76/dbg/server.cpp
Normal file
@@ -0,0 +1,224 @@
|
||||
/*
|
||||
IDA remote debugger server
|
||||
*/
|
||||
|
||||
#ifdef _WIN32
|
||||
// We use the deprecated inet_ntoa() function for Windows XP compatibility.
|
||||
//lint -e750 local macro '' not referenced
|
||||
#define _WINSOCK_DEPRECATED_NO_WARNINGS
|
||||
#endif
|
||||
|
||||
#include <pro.h>
|
||||
#include <expr.hpp>
|
||||
#include "server.h"
|
||||
|
||||
// Provide dummy versions for tinfo copy/clear. Debugger servers do not use them
|
||||
#if !defined(__NT__)
|
||||
void ida_export copy_tinfo_t(tinfo_t *, const tinfo_t &) {}
|
||||
void ida_export clear_tinfo_t(tinfo_t *) {}
|
||||
#endif
|
||||
|
||||
// We don't have a kernel. Provide envvar-based debug file directory retrieval.
|
||||
#if defined(__LINUX__)
|
||||
static bool _elf_debug_file_directory_resolved = false;
|
||||
static qstring _elf_debug_file_directory;
|
||||
idaman const char *ida_export get_elf_debug_file_directory()
|
||||
{
|
||||
if ( !_elf_debug_file_directory_resolved )
|
||||
{
|
||||
if ( !qgetenv("DEBUG_FILE_DIRECTORY", &_elf_debug_file_directory) )
|
||||
qgetenv("ELF_DEBUG_FILE_DIRECTORY", &_elf_debug_file_directory);
|
||||
if ( _elf_debug_file_directory.empty() )
|
||||
_elf_debug_file_directory = "/usr/lib/debug";
|
||||
_elf_debug_file_directory_resolved = true;
|
||||
}
|
||||
return _elf_debug_file_directory.c_str();
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
#ifdef TESTABLE_BUILD
|
||||
static std::map<int,qstring> _pid_elf_debug_file_directories;
|
||||
void set_elf_debug_file_directory_for_pid(int pid, const char *path)
|
||||
{
|
||||
_pid_elf_debug_file_directories[pid] = path;
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
const char *get_elf_debug_file_directory_for_pid(int pid)
|
||||
{
|
||||
const char *found = NULL;
|
||||
std::map<int,qstring>::const_iterator it = _pid_elf_debug_file_directories.find(pid);
|
||||
if ( it != _pid_elf_debug_file_directories.end() )
|
||||
found = it->second.begin();
|
||||
else
|
||||
found = get_elf_debug_file_directory();
|
||||
return found;
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
|
||||
//lint -esym(714, dump_udt) not referenced
|
||||
void dump_udt(const char *, const struct udt_type_data_t &) {}
|
||||
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// SERVER GLOBAL VARIABLES
|
||||
#ifdef __SINGLE_THREADED_SERVER__
|
||||
dbgsrv_dispatcher_t dispatcher(false);
|
||||
|
||||
static bool init_lock(void) { return true; }
|
||||
bool lock_begin(void) { return true; }
|
||||
bool lock_end(void) { return true; }
|
||||
#else
|
||||
dbgsrv_dispatcher_t dispatcher(true);
|
||||
|
||||
static qmutex_t g_mutex = NULL;
|
||||
static bool init_lock(void) { g_mutex = qmutex_create(); return g_mutex != NULL; }
|
||||
bool lock_begin(void) { return qmutex_lock(g_mutex); }
|
||||
bool lock_end(void) { return qmutex_unlock(g_mutex); }
|
||||
#endif
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
dbg_rpc_handler_t *g_global_server = NULL;
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// perform an action (func) on all debuggers
|
||||
int for_all_debuggers(debmod_visitor_t &v)
|
||||
{
|
||||
int code = 0;
|
||||
dispatcher.clients_list->lock();
|
||||
{
|
||||
client_handlers_list_t::storage_t::iterator it;
|
||||
for ( it = dispatcher.clients_list->storage.begin();
|
||||
it != dispatcher.clients_list->storage.end();
|
||||
++it )
|
||||
{
|
||||
dbg_rpc_handler_t *h = (dbg_rpc_handler_t *) it->first;
|
||||
code = v.visit(h->get_debugger_instance());
|
||||
if ( code != 0 )
|
||||
break;
|
||||
}
|
||||
}
|
||||
dispatcher.clients_list->unlock();
|
||||
return code;
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
dbgsrv_dispatcher_t::dbgsrv_dispatcher_t(bool multi_threaded)
|
||||
: base_dispatcher_t(multi_threaded),
|
||||
broken_conns_supported(false),
|
||||
on_broken_conn(BCH_DEFAULT)
|
||||
{
|
||||
port_number = DEBUGGER_PORT_NUMBER;
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
void dbgsrv_dispatcher_t::collect_cliopts(cliopts_t *out)
|
||||
{
|
||||
struct ida_local ns_t
|
||||
{
|
||||
static void _set_dpassword(const char *value, void *ud)
|
||||
{
|
||||
((dbgsrv_dispatcher_t *) ud)->server_password = value;
|
||||
}
|
||||
static void _set_broken_connections_keep_debmod(const char *, void *ud)
|
||||
{
|
||||
((dbgsrv_dispatcher_t *) ud)->on_broken_conn = BCH_KEEP_DEBMOD;
|
||||
}
|
||||
static void _set_closing_session_kill_debuggee(const char *, void *ud)
|
||||
{
|
||||
((dbgsrv_dispatcher_t *) ud)->on_broken_conn = BCH_KILL_PROCESS;
|
||||
}
|
||||
};
|
||||
|
||||
static const cliopt_t cliopts[] =
|
||||
{
|
||||
{ 'P', "password", "Password", ns_t::_set_dpassword, 1 },
|
||||
};
|
||||
// the following options are valid only if broken connections are supported
|
||||
static const cliopt_t bc_cliopts[] =
|
||||
{
|
||||
{
|
||||
'k',
|
||||
"on-broken-connection-keep-session",
|
||||
"Keep debugger session alive when connection breaks",
|
||||
ns_t::_set_broken_connections_keep_debmod,
|
||||
0,
|
||||
},
|
||||
{
|
||||
'K',
|
||||
"on-stop-kill-process",
|
||||
"Kill debuggee when closing session",
|
||||
ns_t::_set_closing_session_kill_debuggee,
|
||||
0,
|
||||
},
|
||||
};
|
||||
|
||||
base_dispatcher_t::collect_cliopts(out);
|
||||
for ( size_t i = 0; i < qnumber(cliopts); ++i )
|
||||
out->push_back(cliopts[i]);
|
||||
|
||||
if ( broken_conns_supported )
|
||||
for ( size_t i = 0; i < qnumber(bc_cliopts); ++i )
|
||||
out->push_back(bc_cliopts[i]);
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
client_handler_t *dbgsrv_dispatcher_t::new_client_handler(idarpc_stream_t *_irs)
|
||||
{
|
||||
dbg_rpc_handler_t *h = new dbg_rpc_handler_t(_irs, this);
|
||||
h->verbose = verbose;
|
||||
void *params = NULL;
|
||||
#if defined(__LINUX__) && defined(TESTABLE_BUILD)
|
||||
params = (void *) get_elf_debug_file_directory_for_pid; //lint !e611 cast between pointer to function type '' and pointer to object type 'void *'
|
||||
#endif
|
||||
h->set_debugger_instance(create_debug_session(params));
|
||||
g_global_server = h;
|
||||
return h;
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
void dbgsrv_dispatcher_t::shutdown_gracefully(int signum)
|
||||
{
|
||||
base_dispatcher_t::shutdown_gracefully(signum);
|
||||
term_subsystem();
|
||||
}
|
||||
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// debugger remote server - TCP/IP mode
|
||||
int NT_CDECL main(int argc, const char *argv[])
|
||||
{
|
||||
#ifdef ENABLE_LOWCNDS
|
||||
init_idc();
|
||||
#endif
|
||||
|
||||
// call the debugger module to initialize its subsystem once
|
||||
if ( !init_lock() || !init_subsystem() )
|
||||
{
|
||||
lprintf("Could not initialize subsystem!");
|
||||
return -1;
|
||||
}
|
||||
|
||||
qstring password;
|
||||
if ( qgetenv("IDA_DBGSRV_PASSWD", &password) )
|
||||
dispatcher.server_password = password;
|
||||
|
||||
int ida_major = IDA_SDK_VERSION / 100;
|
||||
#if (IDA_SDK_VERSION % 10) == 0
|
||||
int ida_minor = (IDA_SDK_VERSION % 100)/10; // 740 -> 4
|
||||
#else
|
||||
int ida_minor = IDA_SDK_VERSION % 100; // 741 -> 41
|
||||
#endif
|
||||
|
||||
lprintf("IDA " SYSTEM SYSBITS " remote debug server(" __SERVER_TYPE__ ") "
|
||||
"v%d.%d.%d. Hex-Rays (c) 2004-2021\n",
|
||||
ida_major, ida_minor, IDD_INTERFACE_VERSION);
|
||||
|
||||
dispatcher.broken_conns_supported = debmod_t::reuse_broken_connections;
|
||||
cliopts_t cliopts(lprintf);
|
||||
dispatcher.collect_cliopts(&cliopts);
|
||||
cliopts.apply(argc, argv, &dispatcher);
|
||||
dispatcher.install_signal_handlers();
|
||||
dispatcher.dispatch();
|
||||
}
|
||||
62
idasdk76/dbg/server.h
Normal file
62
idasdk76/dbg/server.h
Normal file
@@ -0,0 +1,62 @@
|
||||
#ifndef SERVER_H
|
||||
#define SERVER_H
|
||||
|
||||
#include <network.hpp>
|
||||
|
||||
#ifdef __NT__
|
||||
//# ifndef SIGHUP
|
||||
//# define SIGHUP 1
|
||||
//# endif
|
||||
# define DEBUGGER_ID DEBUGGER_ID_X86_IA32_WIN32_USER
|
||||
#else // not NT, i.e. UNIX
|
||||
# if defined(__LINUX__)
|
||||
# if defined(__ARM__)
|
||||
# define DEBUGGER_ID DEBUGGER_ID_ARM_LINUX_USER
|
||||
# else
|
||||
# define DEBUGGER_ID DEBUGGER_ID_X86_IA32_LINUX_USER
|
||||
# endif
|
||||
# elif defined(__MAC__)
|
||||
# if defined(__ARM__)
|
||||
# define DEBUGGER_ID DEBUGGER_ID_ARM_MACOS_USER
|
||||
# else
|
||||
# define DEBUGGER_ID DEBUGGER_ID_X86_IA32_MACOSX_USER
|
||||
# endif
|
||||
# endif
|
||||
# include <sys/socket.h>
|
||||
# include <netinet/in.h>
|
||||
#endif // !__NT__
|
||||
|
||||
enum broken_conn_hndl_t
|
||||
{
|
||||
BCH_DEFAULT,
|
||||
BCH_KEEP_DEBMOD,
|
||||
BCH_KILL_PROCESS,
|
||||
};
|
||||
|
||||
struct dbgsrv_dispatcher_t : public base_dispatcher_t
|
||||
{
|
||||
qstring server_password;
|
||||
bool broken_conns_supported;
|
||||
broken_conn_hndl_t on_broken_conn;
|
||||
|
||||
dbgsrv_dispatcher_t(bool multi_threaded);
|
||||
|
||||
virtual void collect_cliopts(cliopts_t *out) override;
|
||||
virtual client_handler_t *new_client_handler(idarpc_stream_t *irs) override;
|
||||
|
||||
virtual void shutdown_gracefully(int signum) override;
|
||||
};
|
||||
|
||||
#include "debmod.h"
|
||||
#include "dbg_rpc_hlp.h"
|
||||
#include "dbg_rpc_handler.h"
|
||||
|
||||
// // sizeof(ea_t)==8 and sizeof(size_t)==4 servers cannot be used to debug 64-bit
|
||||
// // applications. but to debug 32-bit applications, simple 32-bit servers
|
||||
// // are enough and can work with both 32-bit and 64-bit versions of ida.
|
||||
// // so, there is no need to build sizeof(ea_t)==8 and sizeof(size_t)==4 servers
|
||||
// #if defined(__EA64__) == defined(__X86__)
|
||||
// #error "Mixed mode servers do not make sense, they should not be compiled"
|
||||
// #endif
|
||||
|
||||
#endif
|
||||
17
idasdk76/dbg/server.mak
Normal file
17
idasdk76/dbg/server.mak
Normal file
@@ -0,0 +1,17 @@
|
||||
|
||||
SERVER_LIBS += $(L)dbg_server$(A)
|
||||
SERVER_LIBS += $(L)dbg_rpc$(A)
|
||||
SERVER_LIBS += $(L)dbg_proc$(A)
|
||||
SERVER_LIBS += $(L)network$(A)
|
||||
SERVER_LIBS += $(DUMB)
|
||||
SERVER_LIBS += $(L)unicode$(A)
|
||||
SERVER_LIBS += $(L)pro$(A)
|
||||
SERVER_LIBS += $(L)compress$(A)
|
||||
|
||||
server: $(SERVERS)
|
||||
$(SERVERS): LDFLAGS += $(SERVER_LDFLAGS)
|
||||
$(SERVERS): STDLIBS += $(SERVER_STDLIBS)
|
||||
$(SERVERS): $(SERVER_OBJS) $(SERVER_LIBS)
|
||||
$(call link_exe, $(SERVER_OBJS), $(SERVER_LIBS))
|
||||
$(CHECKSYMS_CMD)
|
||||
$(SERVER_POSTACTION)
|
||||
176
idasdk76/dbg/win32/makefile
Normal file
176
idasdk76/dbg/win32/makefile
Normal file
@@ -0,0 +1,176 @@
|
||||
include ../../allmake.mak
|
||||
|
||||
GOALS-$(BUILD_IDA) += modules # target in $(IDA)module.mak
|
||||
GOALS-$(BUILD_DBGSRV) += server # target in $(IDA)dbg/server.mak
|
||||
.PHONY: $(GOALS-1)
|
||||
all: $(GOALS-1)
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
ifdef __NT__
|
||||
ifndef __X86__
|
||||
SERVER = win64_remote$(B)
|
||||
else
|
||||
SERVER = win32_remote$(B)
|
||||
endif
|
||||
endif
|
||||
ifdef SERVER
|
||||
SERVERS += $(call server_exe,$(SERVER))
|
||||
endif
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
STUB = $(call module_dll,win32_stub)
|
||||
ifdef BUILD_IDA
|
||||
ifeq ($(or $(IDAHOME),$(DEMO_OR_FREE)),)
|
||||
MODULES += $(STUB)
|
||||
endif
|
||||
endif
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
USER = $(call module_dll,win32_user)
|
||||
ifeq ($(and $(BUILD_IDA),$(__NT__)),1)
|
||||
MODULES += $(USER)
|
||||
endif
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
# we explicitly added our module targets
|
||||
NO_DEFAULT_TARGETS = 1
|
||||
|
||||
# NOTE: all MODULES must be defined before including plugin.mak.
|
||||
include ../plugin.mak
|
||||
# NOTE: target-specific rules and dependencies that use variable
|
||||
# expansion to name the target (such as "$(MODULE): [...]") must
|
||||
# come after including plugin.mak
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
# select OBJS common to user plugin and debugger server
|
||||
BASE_OBJS-$(__NT__) += $(F)win32_debmod$(O)
|
||||
BASE_OBJS-$(__NT__) += $(F)win32_util$(O)
|
||||
BASE_OBJS-$(__NT__) += $(F)winbase_debmod$(O)
|
||||
BASE_OBJS += $(BASE_OBJS-1)
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
SERVER_OBJS += $(F)win32_server$(O)
|
||||
SERVER_OBJS += $(F)tilfuncs$(O)
|
||||
SERVER_OBJS += $(BASE_OBJS)
|
||||
|
||||
SERVER_STDLIBS += ole32.lib
|
||||
SERVER_STDLIBS += oleaut32.lib
|
||||
|
||||
include ../server.mak
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
STUB_OBJS += $(F)win32_stub$(O)
|
||||
STUB_OBJS += $(F)w32sehch$(O)
|
||||
$(STUB): MODULE_OBJS += $(STUB_OBJS)
|
||||
$(STUB): $(STUB_OBJS)
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
USER_OBJS += $(F)win32_user$(O)
|
||||
USER_OBJS += $(F)w32sehch$(O)
|
||||
USER_OBJS += $(BASE_OBJS)
|
||||
$(USER): MODULE_OBJS += $(USER_OBJS)
|
||||
$(USER): $(USER_OBJS)
|
||||
$(USER): STDLIBS += user32.lib
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
include $(IDA)objdir.mak
|
||||
|
||||
# MAKEDEP dependency list ------------------
|
||||
$(F)tilfuncs$(O): $(I)bitrange.hpp $(I)bytes.hpp $(I)config.hpp \
|
||||
$(I)diskio.hpp $(I)err.h $(I)expr.hpp $(I)fpro.h \
|
||||
$(I)funcs.hpp $(I)ida.hpp $(I)idd.hpp $(I)idp.hpp \
|
||||
$(I)ins/pc.hpp $(I)intel.hpp $(I)kernwin.hpp \
|
||||
$(I)lines.hpp $(I)llong.hpp $(I)nalt.hpp $(I)name.hpp \
|
||||
$(I)netnode.hpp $(I)network.hpp $(I)pro.h $(I)range.hpp \
|
||||
$(I)segment.hpp $(I)typeinf.hpp $(I)ua.hpp $(I)xref.hpp \
|
||||
../../ldr/pe/cor.h ../../ldr/pe/corerror.h \
|
||||
../../ldr/pe/corhdr.h ../../ldr/pe/mycor.h \
|
||||
../../ldr/pe/pe.h ../../plugins/pdb/common.cpp \
|
||||
../../plugins/pdb/cvconst.h ../../plugins/pdb/dbghelp.h \
|
||||
../../plugins/pdb/dia2.h ../../plugins/pdb/idaaccess.hpp \
|
||||
../../plugins/pdb/msdia.cpp ../../plugins/pdb/msdia.hpp \
|
||||
../../plugins/pdb/pdb.hpp \
|
||||
../../plugins/pdb/pdbaccess.hpp \
|
||||
../../plugins/pdb/pdbida.hpp \
|
||||
../../plugins/pdb/pdblocal.cpp \
|
||||
../../plugins/pdb/pdblocal.hpp \
|
||||
../../plugins/pdb/varser.hpp ../debmod.h tilfuncs.cpp \
|
||||
tilfuncs.hpp
|
||||
$(F)w32sehch$(O): $(I)bitrange.hpp $(I)bytes.hpp $(I)config.hpp \
|
||||
$(I)dbg.hpp $(I)fpro.h $(I)funcs.hpp $(I)ida.hpp \
|
||||
$(I)idd.hpp $(I)idp.hpp $(I)kernwin.hpp $(I)lines.hpp \
|
||||
$(I)llong.hpp $(I)loader.hpp $(I)nalt.hpp $(I)name.hpp \
|
||||
$(I)netnode.hpp $(I)pro.h $(I)range.hpp $(I)segment.hpp \
|
||||
$(I)ua.hpp $(I)xref.hpp w32sehch.cpp w32sehch.h
|
||||
$(F)win32_debmod$(O): $(I)auto.hpp $(I)bitrange.hpp $(I)bytes.hpp \
|
||||
$(I)config.hpp $(I)dbg.hpp $(I)diskio.hpp $(I)entry.hpp \
|
||||
$(I)err.h $(I)exehdr.h $(I)fixup.hpp $(I)fpro.h \
|
||||
$(I)funcs.hpp $(I)ida.hpp $(I)idd.hpp \
|
||||
$(I)idp.hpp $(I)kernwin.hpp $(I)lines.hpp $(I)llong.hpp \
|
||||
$(I)loader.hpp $(I)nalt.hpp $(I)name.hpp $(I)netnode.hpp \
|
||||
$(I)network.hpp $(I)offset.hpp $(I)pro.h $(I)prodir.h \
|
||||
$(I)range.hpp $(I)segment.hpp $(I)segregs.hpp $(I)ua.hpp \
|
||||
$(I)xref.hpp ../../ldr/pe/../idaldr.h \
|
||||
../../ldr/pe/common.cpp ../../ldr/pe/common.h \
|
||||
../../ldr/pe/pe.h ../dbg_pe_hlp.cpp ../deb_pc.hpp \
|
||||
../debmod.h ../pc_debmod.h ../pc_regs.hpp \
|
||||
win32_debmod.cpp win32_debmod.h win32_debmod_impl.cpp \
|
||||
win32_rpc.h win32_undoc.h win32_util.hpp \
|
||||
winbase_debmod.h
|
||||
$(F)win32_server$(O): $(I)bitrange.hpp $(I)bytes.hpp $(I)config.hpp \
|
||||
$(I)fpro.h $(I)funcs.hpp $(I)ida.hpp $(I)idd.hpp \
|
||||
$(I)idp.hpp $(I)kernwin.hpp $(I)lines.hpp $(I)llong.hpp \
|
||||
$(I)nalt.hpp $(I)name.hpp $(I)netnode.hpp \
|
||||
$(I)network.hpp $(I)pro.h $(I)range.hpp $(I)segment.hpp \
|
||||
$(I)typeinf.hpp $(I)ua.hpp $(I)xref.hpp \
|
||||
../../ldr/pe/cor.h ../../ldr/pe/corerror.h \
|
||||
../../ldr/pe/corhdr.h ../../ldr/pe/mycor.h \
|
||||
../../ldr/pe/pe.h ../../plugins/pdb/cvconst.h \
|
||||
../../plugins/pdb/dia2.h ../../plugins/pdb/idaaccess.hpp \
|
||||
../../plugins/pdb/msdia.hpp ../../plugins/pdb/pdb.hpp \
|
||||
../../plugins/pdb/pdbaccess.hpp \
|
||||
../../plugins/pdb/pdbida.hpp \
|
||||
../../plugins/pdb/pdblocal.hpp ../dbg_rpc_hlp.h \
|
||||
../deb_pc.hpp ../debmod.h ../pc_debmod.h ../pc_regs.hpp \
|
||||
tilfuncs.hpp win32_debmod.h win32_rpc.h win32_server.cpp \
|
||||
win32_util.hpp winbase_debmod.h
|
||||
$(F)win32_stub$(O): $(I)../ldr/pe/pe.h $(I)../plugins/pdb/pdb.hpp \
|
||||
$(I)bitrange.hpp $(I)bytes.hpp $(I)config.hpp \
|
||||
$(I)dbg.hpp $(I)err.h $(I)expr.hpp $(I)fpro.h \
|
||||
$(I)funcs.hpp $(I)ida.hpp $(I)idd.hpp $(I)idp.hpp \
|
||||
$(I)kernwin.hpp $(I)lines.hpp $(I)llong.hpp \
|
||||
$(I)loader.hpp $(I)nalt.hpp $(I)name.hpp $(I)netnode.hpp \
|
||||
$(I)network.hpp $(I)pro.h $(I)range.hpp $(I)segment.hpp \
|
||||
$(I)segregs.hpp $(I)typeinf.hpp $(I)ua.hpp $(I)xref.hpp \
|
||||
../../ldr/pe/pe.h ../common_local_impl.cpp \
|
||||
../common_stub_impl.cpp ../dbg_rpc_client.h \
|
||||
../dbg_rpc_engine.h ../dbg_rpc_hlp.h ../deb_pc.hpp \
|
||||
../debmod.h ../pc_local_impl.cpp ../pc_regs.hpp \
|
||||
../rpc_debmod.h w32sehch.h win32_local_impl.cpp \
|
||||
win32_rpc.h win32_stub.cpp
|
||||
$(F)win32_user$(O): $(I)../ldr/pe/pe.h $(I)../plugins/pdb/pdb.hpp \
|
||||
$(I)bitrange.hpp $(I)bytes.hpp $(I)config.hpp \
|
||||
$(I)dbg.hpp $(I)err.h $(I)expr.hpp $(I)fpro.h \
|
||||
$(I)funcs.hpp $(I)ida.hpp $(I)idd.hpp $(I)idp.hpp \
|
||||
$(I)kernwin.hpp $(I)lines.hpp $(I)llong.hpp \
|
||||
$(I)loader.hpp $(I)nalt.hpp $(I)name.hpp $(I)netnode.hpp \
|
||||
$(I)network.hpp $(I)pro.h $(I)range.hpp $(I)segment.hpp \
|
||||
$(I)segregs.hpp $(I)typeinf.hpp $(I)ua.hpp $(I)xref.hpp \
|
||||
../../ldr/pe/pe.h ../common_local_impl.cpp \
|
||||
../common_stub_impl.cpp ../dbg_rpc_hlp.h ../deb_pc.hpp \
|
||||
../debmod.h ../pc_debmod.h ../pc_local_impl.cpp \
|
||||
../pc_regs.hpp w32sehch.h win32_debmod.h \
|
||||
win32_local_impl.cpp win32_rpc.h win32_server_stub.cpp \
|
||||
win32_user.cpp win32_util.hpp winbase_debmod.h
|
||||
$(F)win32_util$(O): $(I)bytes.hpp $(I)ida.hpp $(I)idd.hpp $(I)kernwin.hpp \
|
||||
$(I)lines.hpp $(I)llong.hpp $(I)nalt.hpp $(I)netnode.hpp \
|
||||
$(I)network.hpp $(I)pro.h $(I)range.hpp $(I)segment.hpp \
|
||||
$(I)ua.hpp $(I)xref.hpp ../deb_pc.hpp ../debmod.h \
|
||||
../pc_debmod.h ../pc_regs.hpp win32_util.cpp \
|
||||
win32_util.hpp winbase_debmod.h
|
||||
$(F)winbase_debmod$(O): $(I)bytes.hpp $(I)ida.hpp $(I)idd.hpp \
|
||||
$(I)kernwin.hpp $(I)lines.hpp $(I)llong.hpp $(I)nalt.hpp \
|
||||
$(I)netnode.hpp $(I)network.hpp $(I)pro.h $(I)range.hpp \
|
||||
$(I)segment.hpp $(I)ua.hpp $(I)xref.hpp ../deb_pc.hpp \
|
||||
../debmod.h ../pc_debmod.h ../pc_regs.hpp win32_util.hpp \
|
||||
winbase_debmod.cpp winbase_debmod.h
|
||||
338
idasdk76/dbg/win32/w32sehch.cpp
Normal file
338
idasdk76/dbg/win32/w32sehch.cpp
Normal file
@@ -0,0 +1,338 @@
|
||||
#include <pro.h>
|
||||
#include <name.hpp>
|
||||
#include <kernwin.hpp>
|
||||
#include <dbg.hpp>
|
||||
#include <loader.hpp>
|
||||
#include "w32sehch.h"
|
||||
|
||||
static int req_id = -1;
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
// represents data to store on x86seh_chooser_t
|
||||
struct x86seh_entry_t
|
||||
{
|
||||
uint32 handler = 0; // address of SEH handler
|
||||
uint32 stack = 0; // address of SEH chain on the stack
|
||||
x86seh_entry_t(uint32 _handler, uint32 _stack)
|
||||
: handler(_handler), stack(_stack) {}
|
||||
|
||||
bool operator ==(const x86seh_entry_t &e) const { return handler == e.handler && stack == e.handler; }
|
||||
};
|
||||
DECLARE_TYPE_AS_MOVABLE(x86seh_entry_t);
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
// non-modal exception handler chooser
|
||||
struct x86seh_chooser_t : public chooser_t
|
||||
{
|
||||
protected:
|
||||
qvector<x86seh_entry_t> list;
|
||||
qstring title_;
|
||||
thid_t tid;
|
||||
|
||||
static const int widths_[];
|
||||
static const char *const header_[];
|
||||
enum { ICON = 144 };
|
||||
|
||||
public:
|
||||
// this object must be allocated using `new`
|
||||
x86seh_chooser_t(thid_t tid);
|
||||
virtual ~x86seh_chooser_t()
|
||||
{
|
||||
unhook_from_notification_point(
|
||||
HT_DBG,
|
||||
dbg_handler, const_cast<char *>(title));
|
||||
}
|
||||
ssize_t choose(uint32 addr = uint32(-1)) //lint !e1511 member hides non-virtual member
|
||||
{
|
||||
return ::choose(this, &addr);
|
||||
}
|
||||
|
||||
virtual const void *get_obj_id(size_t *len) const override
|
||||
{
|
||||
*len = sizeof(tid);
|
||||
return &tid;
|
||||
}
|
||||
|
||||
virtual size_t idaapi get_count() const override { return list.size(); }
|
||||
virtual void idaapi get_row(
|
||||
qstrvec_t *cols,
|
||||
int *icon_,
|
||||
chooser_item_attrs_t *attrs,
|
||||
size_t n) const override;
|
||||
virtual cbret_t idaapi enter(size_t n) override;
|
||||
|
||||
// calculate the location of the item,
|
||||
virtual ssize_t idaapi get_item_index(const void *item_data) const override;
|
||||
virtual bool idaapi init() override;
|
||||
virtual cbret_t idaapi refresh(ssize_t n) override;
|
||||
|
||||
ea_t get_stack_addr(int n) const;
|
||||
|
||||
protected:
|
||||
static ssize_t idaapi dbg_handler(void *ud, int notif_code, va_list va);
|
||||
};
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
const int x86seh_chooser_t::widths_[] =
|
||||
{
|
||||
CHCOL_HEX | 10, // Address
|
||||
30, // Name
|
||||
10, // Stack
|
||||
};
|
||||
const char *const x86seh_chooser_t::header_[] =
|
||||
{
|
||||
"Address", // 0
|
||||
"Name", // 1
|
||||
"Stack", // 2
|
||||
};
|
||||
|
||||
static const char seh_widget_title[] = "Structured exception handlers";
|
||||
//-------------------------------------------------------------------------
|
||||
inline x86seh_chooser_t::x86seh_chooser_t(thid_t tid_)
|
||||
: chooser_t(CH_NOBTNS | CH_FORCE_DEFAULT | CH_CAN_REFRESH,
|
||||
qnumber(widths_), widths_, header_),
|
||||
tid(tid_)
|
||||
{
|
||||
title_.sprnt("[%04X] - %s", tid, seh_widget_title);
|
||||
title = title_.c_str();
|
||||
CASSERT(qnumber(widths_) == qnumber(header_));
|
||||
icon = ICON;
|
||||
|
||||
hook_to_notification_point(
|
||||
HT_DBG,
|
||||
dbg_handler, const_cast<char *>(title));
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
void idaapi x86seh_chooser_t::get_row(
|
||||
qstrvec_t *cols_,
|
||||
int *,
|
||||
chooser_item_attrs_t *,
|
||||
size_t n) const
|
||||
{
|
||||
// assert: n < list.size()
|
||||
uint32 addr = list[n].handler;
|
||||
|
||||
qstrvec_t &cols = *cols_;
|
||||
cols[0].sprnt("%08X", addr);
|
||||
get_nice_colored_name(&cols[1], addr, GNCN_NOCOLOR | GNCN_NOLABEL);
|
||||
// set Stack column data
|
||||
cols[2].sprnt("%08X", list[n].stack);
|
||||
CASSERT(qnumber(header_) == 3);
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
chooser_t::cbret_t idaapi x86seh_chooser_t::enter(size_t n)
|
||||
{
|
||||
// assert: n < list.size()
|
||||
ea_t ea = ea_t(list[n].handler);
|
||||
if ( !is_code(get_flags(ea)) )
|
||||
create_insn(ea);
|
||||
jumpto(ea);
|
||||
return cbret_t(); // nothing changed
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
ssize_t idaapi x86seh_chooser_t::get_item_index(const void *item_data) const
|
||||
{
|
||||
if ( list.empty() )
|
||||
return NO_SELECTION;
|
||||
|
||||
const x86seh_entry_t item = *(const x86seh_entry_t *)item_data;
|
||||
if ( item.handler == uint32(-1) )
|
||||
return 0; // first item by default
|
||||
|
||||
// find `item_script` in the list
|
||||
const x86seh_entry_t *p = list.find(item);
|
||||
if ( p != list.end() )
|
||||
return p - list.begin();
|
||||
return 0; // first item by default
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
bool idaapi x86seh_chooser_t::init()
|
||||
{
|
||||
// rebuild the handlers list
|
||||
uint64 fs_sel;
|
||||
ea_t fs_base;
|
||||
uint32 excr_ea;
|
||||
list.clear();
|
||||
if ( !get_reg_val("fs", &fs_sel)
|
||||
|| internal_get_sreg_base(&fs_base, tid, int(fs_sel)) <= DRC_NONE
|
||||
|| read_dbg_memory(fs_base, &excr_ea, sizeof(excr_ea)) != sizeof(excr_ea) )
|
||||
{
|
||||
warning("Failed to build the SEH list for thread %08X", tid);
|
||||
return false; // do not show the empty chooser
|
||||
}
|
||||
|
||||
struct EXC_REG_RECORD
|
||||
{
|
||||
uint32 prev;
|
||||
uint32 handler;
|
||||
};
|
||||
EXC_REG_RECORD rec;
|
||||
std::set<uint32> seen;
|
||||
while ( excr_ea != 0xffffffff )
|
||||
{
|
||||
if ( read_dbg_memory(excr_ea, &rec, sizeof(rec)) != sizeof(rec) )
|
||||
break;
|
||||
|
||||
if ( !seen.insert(excr_ea).second )
|
||||
{
|
||||
msg("Circular SEH record has been detected\n");
|
||||
break;
|
||||
}
|
||||
|
||||
list.push_back(x86seh_entry_t(rec.handler, excr_ea));
|
||||
excr_ea = rec.prev;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
chooser_t::cbret_t idaapi x86seh_chooser_t::refresh(ssize_t n)
|
||||
{
|
||||
uint32 item_addr = uint32(-1);
|
||||
if ( n >= 0 && n < list.size() )
|
||||
item_addr = list[n].handler; // remember the currently selected handler
|
||||
|
||||
init();
|
||||
|
||||
if ( n < 0 )
|
||||
return NO_SELECTION;
|
||||
ssize_t idx = get_item_index(&item_addr);
|
||||
// no need to adjust `idx` as get_item_index() returns first item by
|
||||
// default
|
||||
return idx;
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
ea_t x86seh_chooser_t::get_stack_addr(int n) const
|
||||
{
|
||||
if ( n < list.size() )
|
||||
return ea_t(list[n].stack);
|
||||
|
||||
return BADADDR;
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
ssize_t idaapi x86seh_chooser_t::dbg_handler(void *ud, int code, va_list)
|
||||
{
|
||||
if ( code == dbg_suspend_process )
|
||||
{
|
||||
const char *ttl = static_cast<const char *>(ud);
|
||||
refresh_chooser(ttl);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
struct stkview_ah_t : public action_handler_t
|
||||
{
|
||||
virtual int idaapi activate(action_activation_ctx_t *ctx) override
|
||||
{
|
||||
if ( !ctx->chooser_selection.empty() ) // should always be the case
|
||||
{
|
||||
size_t idx = ctx->chooser_selection[0];
|
||||
const x86seh_chooser_t *c = (x86seh_chooser_t *) ctx->source.chooser;
|
||||
if ( c == nullptr )
|
||||
return 0;
|
||||
|
||||
ea_t ea = c->get_stack_addr(idx);
|
||||
if ( ea != BADADDR )
|
||||
{
|
||||
TWidget *w = find_widget("Stack view");
|
||||
if ( w != nullptr )
|
||||
{
|
||||
activate_widget(w, true);
|
||||
jumpto(ea);
|
||||
}
|
||||
}
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
virtual action_state_t idaapi update(action_update_ctx_t *ctx) override
|
||||
{
|
||||
return ::qstrstr(ctx->widget_title.c_str(), seh_widget_title) != nullptr
|
||||
? AST_ENABLE_FOR_WIDGET
|
||||
: AST_DISABLE_FOR_WIDGET;
|
||||
}
|
||||
};
|
||||
static stkview_ah_t stkview_ah;
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
struct show_window_ah_t : public action_handler_t
|
||||
{
|
||||
virtual int idaapi activate(action_activation_ctx_t *) override
|
||||
{
|
||||
thid_t tid = get_current_thread();
|
||||
x86seh_chooser_t *ch = new x86seh_chooser_t(tid);
|
||||
bool ok = ch->choose() == 0;
|
||||
if ( ok )
|
||||
{
|
||||
TWidget *w = find_widget(ch->title);
|
||||
if ( w != nullptr )
|
||||
{
|
||||
#define ACTION_NAME "x86seh:Stack"
|
||||
const action_desc_t stkview_ah_action = ACTION_DESC_LITERAL_OWNER(
|
||||
ACTION_NAME,
|
||||
"Follow in stack view",
|
||||
&stkview_ah,
|
||||
&PLUGIN,
|
||||
nullptr,
|
||||
nullptr,
|
||||
-1,
|
||||
ADF_OT_PLUGIN);
|
||||
register_action(stkview_ah_action);
|
||||
attach_action_to_popup(
|
||||
w,
|
||||
nullptr, // make permanent
|
||||
ACTION_NAME);
|
||||
}
|
||||
}
|
||||
return ok; //-V773 The function was exited without releasing the 'ch' pointer.
|
||||
} //lint !e429 Custodial pointer 'ch' has not been freed or returned
|
||||
|
||||
virtual action_state_t idaapi update(action_update_ctx_t *) override
|
||||
{
|
||||
return AST_ENABLE;
|
||||
}
|
||||
};
|
||||
static show_window_ah_t show_window_ah;
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
void remove_x86seh_menu()
|
||||
{
|
||||
if ( req_id != -1 )
|
||||
{
|
||||
cancel_exec_request(req_id);
|
||||
req_id = -1;
|
||||
}
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
void install_x86seh_menu()
|
||||
{
|
||||
// HACK: We queue this request because commdbg apparently enables the debug menus
|
||||
// just after calling init_debugger().
|
||||
struct uireq_install_menu_t: public ui_request_t
|
||||
{
|
||||
virtual bool idaapi run() override
|
||||
{
|
||||
if ( !inf_is_64bit() )
|
||||
{
|
||||
register_and_attach_to_menu(
|
||||
"Debugger/Debugger windows/Stack trace",
|
||||
"dbg:sehList", "SEH list", NULL, SETMENU_APP,
|
||||
&show_window_ah,
|
||||
&PLUGIN,
|
||||
ADF_OT_PLUGIN);
|
||||
}
|
||||
req_id = -1;
|
||||
return false;
|
||||
}
|
||||
};
|
||||
req_id = execute_ui_requests(new uireq_install_menu_t, NULL);
|
||||
}
|
||||
7
idasdk76/dbg/win32/w32sehch.h
Normal file
7
idasdk76/dbg/win32/w32sehch.h
Normal file
@@ -0,0 +1,7 @@
|
||||
#ifndef __W32SEHCH__
|
||||
#define __W32SEHCH__
|
||||
|
||||
void install_x86seh_menu();
|
||||
void remove_x86seh_menu();
|
||||
|
||||
#endif
|
||||
3519
idasdk76/dbg/win32/win32_debmod.cpp
Normal file
3519
idasdk76/dbg/win32/win32_debmod.cpp
Normal file
File diff suppressed because it is too large
Load Diff
427
idasdk76/dbg/win32/win32_debmod.h
Normal file
427
idasdk76/dbg/win32/win32_debmod.h
Normal file
@@ -0,0 +1,427 @@
|
||||
#ifndef __WIN32_DEBUGGER_MODULE__
|
||||
#define __WIN32_DEBUGGER_MODULE__
|
||||
|
||||
#include <windows.h>
|
||||
#include <Tlhelp32.h>
|
||||
#include "../../ldr/pe/pe.h"
|
||||
#include "winbase_debmod.h"
|
||||
|
||||
//-V::720 It is advised to utilize the 'SuspendThread' function only when developing a debugger
|
||||
|
||||
// Type definitions
|
||||
|
||||
class win32_debmod_t;
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// image information
|
||||
struct image_info_t
|
||||
{
|
||||
image_info_t() { memset(this, 0, sizeof(*this)); }
|
||||
image_info_t(win32_debmod_t *);
|
||||
image_info_t(win32_debmod_t *, ea_t _base, uint32 _imagesize, const qstring &_name);
|
||||
image_info_t(win32_debmod_t *, const LOAD_DLL_DEBUG_INFO &i, uint32 _imagesize, const char *_name);
|
||||
image_info_t(win32_debmod_t *, const modinfo_t &m);
|
||||
|
||||
win32_debmod_t *sess;
|
||||
ea_t base;
|
||||
uval_t imagesize;
|
||||
qstring name;
|
||||
LOAD_DLL_DEBUG_INFO dll_info;
|
||||
};
|
||||
|
||||
// key: image base address
|
||||
typedef std::map<ea_t, image_info_t> images_t;
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
struct context_holder_t
|
||||
{
|
||||
bytevec_t buffer;
|
||||
PCONTEXT ptr;
|
||||
|
||||
context_holder_t() : ptr(NULL) {}
|
||||
};
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
struct context_helper_t
|
||||
{
|
||||
typedef DWORD64 (WINAPI *PGETENABLEDXSTATEFEATURES)();
|
||||
PGETENABLEDXSTATEFEATURES pfnGetEnabledXStateFeatures;
|
||||
|
||||
typedef BOOL (WINAPI *PINITIALIZECONTEXT)(PVOID Buffer, DWORD ContextFlags, PCONTEXT *Context, PDWORD ContextLength);
|
||||
PINITIALIZECONTEXT pfnInitializeContext;
|
||||
|
||||
typedef BOOL (WINAPI *PGETXSTATEFEATURESMASK)(PCONTEXT Context, PDWORD64 FeatureMask);
|
||||
PGETXSTATEFEATURESMASK pfnGetXStateFeaturesMask;
|
||||
|
||||
typedef PVOID (WINAPI *LOCATEXSTATEFEATURE)(PCONTEXT Context, DWORD FeatureId, PDWORD Length);
|
||||
LOCATEXSTATEFEATURE pfnLocateXStateFeature;
|
||||
|
||||
typedef BOOL (WINAPI *SETXSTATEFEATURESMASK)(PCONTEXT Context, DWORD64 FeatureMask);
|
||||
SETXSTATEFEATURESMASK pfnSetXStateFeaturesMask;
|
||||
|
||||
typedef BOOL (WINAPI *COPYCONTEXT)(PCONTEXT Destination, DWORD ContextFlags, PCONTEXT Source);
|
||||
COPYCONTEXT pfnCopyContext;
|
||||
|
||||
int xstate_context_size;
|
||||
bool get_xstate_context_size(int *out_ctxsz);
|
||||
context_helper_t() { clear(); }
|
||||
bool create_context(context_holder_t *out, int *ctxflags);
|
||||
bool xstate_helpers_loaded() const { return xstate_context_size > 0; }
|
||||
void clear();
|
||||
};
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// thread information
|
||||
struct thread_info_t : public CREATE_THREAD_DEBUG_INFO
|
||||
{
|
||||
thread_info_t(
|
||||
win32_debmod_t *dm,
|
||||
const CREATE_THREAD_DEBUG_INFO &i,
|
||||
thid_t t,
|
||||
wow64_state_t wow64_state);
|
||||
win32_debmod_t *debmod;
|
||||
thid_t tid; // thread id
|
||||
int suspend_count;
|
||||
ea_t bpt_ea;
|
||||
int flags;
|
||||
#define THR_TRACING 0x0001 // expecting a STEP event
|
||||
#define THR_WOW64 0x0002 // is wow64 process?
|
||||
#define THR_NEWNAME 0x0004 // thread was renamed
|
||||
ea_t callgate_ea;
|
||||
qstring name;
|
||||
|
||||
bool read_context(context_holder_t *out, int clsmask);
|
||||
bool write_context(int clsmask, CONTEXT &ctx);
|
||||
bool toggle_tbit(bool set_tbit);
|
||||
bool is_tracing(void) const { return (flags & THR_TRACING) != 0; }
|
||||
bool is_wow64(void) const { return (flags & THR_WOW64) != 0; }
|
||||
void set_tracing(void) { flags |= THR_TRACING; }
|
||||
void clr_tracing(void) { flags &= ~THR_TRACING; }
|
||||
bool is_new_name(void) const { return (flags & THR_NEWNAME) != 0; }
|
||||
void clr_new_name(void) { flags &= ~THR_NEWNAME; }
|
||||
void set_new_name(void) { flags |= THR_NEWNAME; }
|
||||
};
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
inline thread_info_t::thread_info_t(
|
||||
win32_debmod_t *dm,
|
||||
const CREATE_THREAD_DEBUG_INFO &i,
|
||||
thid_t t,
|
||||
wow64_state_t wow64_state)
|
||||
: CREATE_THREAD_DEBUG_INFO(i), tid(t), suspend_count(0), bpt_ea(BADADDR),
|
||||
debmod(dm),
|
||||
flags(wow64_state > 0 ? THR_WOW64 : 0),
|
||||
callgate_ea(0)
|
||||
{
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// Check if the context structure has valid values at the specified portion
|
||||
// portion is a conbination of CONTEXT_... bitmasks
|
||||
inline bool has_portion(const CONTEXT &ctx, int portion)
|
||||
{
|
||||
return (ctx.ContextFlags & portion & 0xFFFF) != 0;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// (tid -> info)
|
||||
struct threads_t: public std::map<DWORD, thread_info_t>
|
||||
{
|
||||
thread_info_t *get(DWORD tid)
|
||||
{
|
||||
const iterator it = find(tid);
|
||||
if ( it == end() )
|
||||
return NULL;
|
||||
return &it->second;
|
||||
}
|
||||
};
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
typedef qvector<thread_info_t> threadvec_t;
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// structure for the internal breakpoint information for threads
|
||||
struct internal_bpt_info_t
|
||||
{
|
||||
int count; // number of times this breakpoint is 'set'
|
||||
uchar orig_bytes[BPT_CODE_SIZE]; // original byte values
|
||||
};
|
||||
typedef std::map<ea_t, internal_bpt_info_t> bpt_info_t;
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
typedef int (*process_cb_t)(debmod_t *, PROCESSENTRY32 *pe32, void *ud);
|
||||
typedef int (*module_cb_t)(debmod_t *, MODULEENTRY32 *me32, void *ud);
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
// A live PDB session, that will be used remotely (typically by non-windows machines).
|
||||
struct pdb_remote_session_t;
|
||||
void close_pdb_remote_session(pdb_remote_session_t *);
|
||||
|
||||
// Wow64-specific events
|
||||
#ifndef STATUS_WX86_BREAKPOINT
|
||||
# define STATUS_WX86_BREAKPOINT 0x4000001f
|
||||
#endif
|
||||
#ifndef STATUS_WX86_SINGLE_STEP
|
||||
# define STATUS_WX86_SINGLE_STEP 0x4000001e
|
||||
#endif
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
struct machine_thread_state_t;
|
||||
struct machine_float_state_t;
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
class win32_debmod_t : public winbase_debmod_t
|
||||
{
|
||||
typedef winbase_debmod_t inherited;
|
||||
|
||||
regctx_t *reg_ctx;
|
||||
|
||||
gdecode_t get_debug_event(debug_event_t *event, int timeout_ms);
|
||||
void check_thread(bool must_be_main_thread) const;
|
||||
void add_thread(const CREATE_THREAD_DEBUG_INFO &thr_info, thid_t tid);
|
||||
void install_callgate_workaround(thread_info_t *ti, const debug_event_t *event);
|
||||
int describe_stack_segment(
|
||||
thid_t tid,
|
||||
images_t &thr_ranges,
|
||||
images_t &cls_ranges,
|
||||
const _NT_TIB &tib,
|
||||
const char *pref);
|
||||
void update_thread_names(thread_name_vec_t *thr_names);
|
||||
|
||||
bool get_pe_exports_from_path(
|
||||
const char *path,
|
||||
linput_t *li,
|
||||
ea_t imagebase,
|
||||
name_info_t &ni,
|
||||
const char *exported_name=NULL) const;
|
||||
void _term_reg_ctx(void);
|
||||
|
||||
public:
|
||||
// debugged process information
|
||||
qstring process_path;
|
||||
HANDLE thread_handle;
|
||||
HANDLE redirin_handle;
|
||||
HANDLE redirout_handle;
|
||||
attach_status_t attach_status;
|
||||
HANDLE attach_evid;
|
||||
int8 expecting_debug_break;
|
||||
bool stop_at_ntdll_bpts;
|
||||
|
||||
images_t curproc; // image of the running process
|
||||
images_t dlls; // list of loaded DLLs
|
||||
images_t images; // list of detected PE images
|
||||
images_t thread_ranges; // list of ranges related to threads
|
||||
images_t class_ranges; // list of ranges related to class names
|
||||
|
||||
easet_t dlls_to_import; // list of dlls to import information from
|
||||
modinfo_t binary_to_import; // executable to import information from
|
||||
|
||||
bpt_info_t thread_bpts;
|
||||
|
||||
threads_t threads;
|
||||
|
||||
// ID of a thread for which we must emulate a STEP event on XP (using a breakpoint)
|
||||
thid_t winxp_step_thread;
|
||||
|
||||
CREATE_PROCESS_DEBUG_INFO cpdi;
|
||||
|
||||
debug_event_t *in_event; // current debug event
|
||||
bool fake_suspend_event;
|
||||
bool exiting;
|
||||
bool pause_requested;
|
||||
procinfo_vec_t processes;
|
||||
|
||||
// threads suspended by the fiber created for restoring broken connections
|
||||
threadvec_t _suspended_threads;
|
||||
// event to wait until the broken connection is completely restored
|
||||
HANDLE broken_event_handle;
|
||||
context_helper_t context_helper;
|
||||
|
||||
// Module specific methods, to be implemented
|
||||
virtual void idaapi dbg_set_debugging(bool _debug_debugger) override;
|
||||
virtual drc_t idaapi dbg_init(uint32_t *flags2, qstring *errbuf) override;
|
||||
virtual void idaapi dbg_term(void) override;
|
||||
virtual drc_t idaapi dbg_detach_process(void) override;
|
||||
virtual drc_t idaapi dbg_start_process(
|
||||
const char *path,
|
||||
const char *args,
|
||||
const char *startdir,
|
||||
int flags,
|
||||
const char *input_path,
|
||||
uint32 input_file_crc32,
|
||||
qstring *errbuf) override;
|
||||
virtual gdecode_t idaapi dbg_get_debug_event(debug_event_t *event, int timeout_ms) override;
|
||||
virtual drc_t idaapi dbg_attach_process(
|
||||
pid_t process_id,
|
||||
int event_id,
|
||||
int flags,
|
||||
qstring *errbuf) override;
|
||||
virtual drc_t idaapi dbg_prepare_to_pause_process(qstring *errbuf) override;
|
||||
virtual drc_t idaapi dbg_exit_process(qstring *errbuf) override;
|
||||
virtual drc_t idaapi dbg_continue_after_event(const debug_event_t *event) override;
|
||||
virtual void idaapi dbg_stopped_at_debug_event(
|
||||
import_infos_t *infos,
|
||||
bool dlls_added,
|
||||
thread_name_vec_t *thr_names) override;
|
||||
virtual drc_t idaapi dbg_thread_suspend(thid_t thread_id) override;
|
||||
virtual drc_t idaapi dbg_thread_continue(thid_t thread_id) override;
|
||||
virtual drc_t idaapi dbg_set_resume_mode(thid_t thread_id, resume_mode_t resmod) override;
|
||||
virtual drc_t idaapi dbg_read_registers(
|
||||
thid_t thread_id,
|
||||
int clsmask,
|
||||
regval_t *values,
|
||||
qstring *errbuf) override;
|
||||
virtual drc_t idaapi dbg_write_register(
|
||||
thid_t thread_id,
|
||||
int reg_idx,
|
||||
const regval_t *value,
|
||||
qstring *errbuf) override;
|
||||
|
||||
virtual drc_t idaapi dbg_thread_get_sreg_base(ea_t *ea, thid_t thread_id, int sreg_value, qstring *errbuf) override;
|
||||
virtual drc_t idaapi dbg_get_memory_info(meminfo_vec_t &ranges, qstring *errbuf) override;
|
||||
virtual ssize_t idaapi dbg_read_memory(ea_t ea, void *buffer, size_t size, qstring *errbuf) override;
|
||||
virtual ssize_t idaapi dbg_write_memory(ea_t ea, const void *buffer, size_t size, qstring *errbuf) override;
|
||||
virtual int idaapi dbg_add_bpt(bytevec_t *orig_bytes, bpttype_t type, ea_t ea, int len) override;
|
||||
virtual int idaapi dbg_del_bpt(bpttype_t type, ea_t ea, const uchar *orig_bytes, int len) override;
|
||||
virtual int idaapi handle_ioctl(int fn, const void *buf, size_t size, void **outbuf, ssize_t *outsize) override;
|
||||
//
|
||||
win32_debmod_t();
|
||||
~win32_debmod_t() { cleanup(); _term_reg_ctx(); }
|
||||
|
||||
virtual void init_reg_ctx(void) override;
|
||||
virtual void term_reg_ctx(void) override;
|
||||
|
||||
bool get_thread_state(
|
||||
context_holder_t *out_ctxh,
|
||||
machine_thread_state_t *out_regs,
|
||||
machine_float_state_t *out_floats,
|
||||
thid_t tid,
|
||||
int clsmask);
|
||||
bool set_thread_state(
|
||||
const machine_thread_state_t ®s,
|
||||
const machine_float_state_t &floats,
|
||||
const context_holder_t &ctxh,
|
||||
thid_t tid,
|
||||
int clsmask);
|
||||
|
||||
void handle_pdb_thread_request(void *data);
|
||||
uint32 calc_imagesize(eanat_t base);
|
||||
bool get_filename_for(
|
||||
char *buf,
|
||||
size_t bufsize,
|
||||
eanat_t image_name_ea,
|
||||
bool use_unicode,
|
||||
eanat_t image_base);
|
||||
ea_t get_dll_export(
|
||||
const images_t &dlls,
|
||||
ea_t imagebase,
|
||||
const char *exported_name);
|
||||
|
||||
bool create_process(
|
||||
const char *path,
|
||||
const char *args,
|
||||
const char *startdir,
|
||||
bool is_gui,
|
||||
bool hide_window,
|
||||
PROCESS_INFORMATION *ProcessInformation);
|
||||
|
||||
void show_debug_event(const DEBUG_EVENT &ev);
|
||||
|
||||
ssize_t _read_memory(eanat_t ea, void *buffer, size_t size, bool suspend = false);
|
||||
ssize_t _write_memory(eanat_t ea, const void *buffer, size_t size, bool suspend = false);
|
||||
|
||||
int rdmsr(int reg, uint64 *value);
|
||||
int wrmsr(int reg, uint64 value);
|
||||
int kldbgdrv_access_msr(struct SYSDBG_MSR *msr, bool write);
|
||||
|
||||
// !! OVERWRITTEN METHODS !!
|
||||
bool refresh_hwbpts();
|
||||
|
||||
// Utility methods
|
||||
gdecode_t handle_exception(debug_event_t *event,
|
||||
const EXCEPTION_RECORD &er,
|
||||
bool was_thread_bpt,
|
||||
bool firsttime);
|
||||
ssize_t access_memory(eanat_t ea, void *buffer, ssize_t size, bool write, bool suspend);
|
||||
inline void resume_all_threads(bool raw = false);
|
||||
inline void suspend_all_threads(bool raw = false);
|
||||
size_t add_dll(image_info_t &ii);
|
||||
bool module_present(const char *modname);
|
||||
HANDLE get_thread_handle(thid_t tid);
|
||||
static int get_dmi_cb(debmod_t *sess, MODULEENTRY32 *me32, void *ud);
|
||||
void get_debugged_module_info(modinfo_t *dmi);
|
||||
int for_each_module(DWORD pid, module_cb_t module_cb, void *ud);
|
||||
bool myCloseHandle(HANDLE &h);
|
||||
void cleanup(void);
|
||||
void restore_original_bytes(ea_t ea, bool really_restore = true);
|
||||
int save_original_bytes(ea_t ea);
|
||||
bool set_thread_bpt(thread_info_t &ti, ea_t ea);
|
||||
bool del_thread_bpt(thread_info_t &ti, ea_t ea);
|
||||
bool del_thread_bpts(ea_t ea);
|
||||
bool has_bpt_at(ea_t ea);
|
||||
bool can_access(ea_t addr);
|
||||
ea_t get_kernel_bpt_ea(ea_t ea, thid_t tid);
|
||||
void create_attach_event(debug_event_t *event, bool attached);
|
||||
void create_start_event(debug_event_t *event);
|
||||
bool check_for_hwbpt(debug_event_t *event, bool is_stepping=false);
|
||||
ea_t get_region_info(ea_t ea, memory_info_t *info);
|
||||
bool get_dll_exports(
|
||||
const images_t &dlls,
|
||||
ea_t imagebase,
|
||||
name_info_t &ni,
|
||||
const char *exported_name = NULL);
|
||||
bool get_filename_from_process(
|
||||
eanat_t name_ea,
|
||||
bool is_unicode,
|
||||
char *buf,
|
||||
size_t bufsize);
|
||||
bool get_debug_string(const DEBUG_EVENT &ev, char *buf, size_t bufsize);
|
||||
int add_thread_ranges(
|
||||
thid_t tid,
|
||||
images_t &thread_ranges,
|
||||
images_t &class_ranges);
|
||||
ea_t get_pe_header(eanat_t imagebase, peheader_t *nh);
|
||||
bool get_pe_export_name_from_process(
|
||||
eanat_t imagebase,
|
||||
char *name,
|
||||
size_t namesize);
|
||||
|
||||
void show_exception_record(const EXCEPTION_RECORD &er, int level=0);
|
||||
|
||||
eanat_t pstos0(eanat_t ea);
|
||||
eanat_t s0tops(eanat_t ea);
|
||||
|
||||
bool prepare_to_stop_process(debug_event_t *, const threads_t &);
|
||||
bool disable_hwbpts();
|
||||
bool enable_hwbpts();
|
||||
bool may_write(ea_t ea);
|
||||
LPVOID correct_exe_image_base(LPVOID base);
|
||||
bool clear_tbit(thread_info_t &th);
|
||||
void enqueue_event(const debug_event_t &ev, queue_pos_t pos);
|
||||
|
||||
void suspend_running_threads(threadvec_t &suspended);
|
||||
void resume_suspended_threads(threadvec_t suspended) const;
|
||||
bool reopen_threads(void);
|
||||
|
||||
virtual bool idaapi write_registers(
|
||||
thid_t thread_id,
|
||||
int start,
|
||||
int count,
|
||||
const regval_t *values) override;
|
||||
|
||||
virtual bool idaapi dbg_prepare_broken_connection(void) override;
|
||||
virtual bool idaapi dbg_continue_broken_connection(pid_t pid) override;
|
||||
|
||||
qvector<pdb_remote_session_t*> pdb_remote_sessions;
|
||||
pdb_remote_session_t *get_pdb_session(int id);
|
||||
void delete_pdb_session(int id);
|
||||
|
||||
protected:
|
||||
virtual int dbg_freeze_threads_except(thid_t tid) override;
|
||||
virtual int dbg_thaw_threads_except(thid_t tid) override;
|
||||
};
|
||||
|
||||
ea_t s0tops(ea_t ea);
|
||||
|
||||
#endif
|
||||
|
||||
717
idasdk76/dbg/win32/win32_debmod_impl.cpp
Normal file
717
idasdk76/dbg/win32/win32_debmod_impl.cpp
Normal file
@@ -0,0 +1,717 @@
|
||||
//
|
||||
//
|
||||
// This file contains win32 specific implementations of win32_debmod class
|
||||
//
|
||||
//
|
||||
|
||||
#include <diskio.hpp>
|
||||
#include "win32_rpc.h"
|
||||
#include "win32_undoc.h"
|
||||
|
||||
#include "dbg_pe_hlp.cpp"
|
||||
|
||||
struct impfunc_t
|
||||
{
|
||||
const char *name;
|
||||
void *fptr;
|
||||
};
|
||||
#define IMPFUNC(x) { TEXT(#x), &x }
|
||||
|
||||
//lint -esym(843,ntdll) -esym(844,ntdll) could be const
|
||||
static HMODULE ntdll = NULL;
|
||||
static NtSystemDebugControl_t *NtSystemDebugControl;
|
||||
static NtLoadDriver_t *NtLoadDriver;
|
||||
static NtUnloadDriver_t *NtUnloadDriver;
|
||||
static RtlAdjustPrivilege_t *RtlAdjustPrivilege;
|
||||
static NtCreateFile_t *NtCreateFile;
|
||||
static NtDeviceIoControlFile_t *NtDeviceIoControlFile;
|
||||
|
||||
static const impfunc_t ntfuncs[] =
|
||||
{
|
||||
IMPFUNC(NtSystemDebugControl),
|
||||
IMPFUNC(NtLoadDriver),
|
||||
IMPFUNC(NtUnloadDriver),
|
||||
IMPFUNC(RtlAdjustPrivilege),
|
||||
IMPFUNC(NtCreateFile),
|
||||
IMPFUNC(NtDeviceIoControlFile),
|
||||
};
|
||||
|
||||
// To read MSRs, we use a local kernel debugger driver provided by Microsoft.
|
||||
//lint -esym(843,DriverHandle,DriverPath,DriverName) could be const
|
||||
//lint -esym(844,DriverHandle) could be pointing to const
|
||||
static HANDLE DriverHandle = NULL;
|
||||
static UNICODE_STRING DriverPath = RTL_CONSTANT_STRING(L"\\REGISTRY\\MACHINE\\SYSTEM\\CURRENTCONTROLSET\\SERVICES\\kldbgdrv");
|
||||
static UNICODE_STRING DriverName = RTL_CONSTANT_STRING(L"\\Device\\kldbgdrv");
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// PE COMMON HELPER FUNCTIONS
|
||||
#define lread myread // since we can't use loader_failure()
|
||||
inline void myread(linput_t *li, void *buf, size_t size)
|
||||
{
|
||||
int bytes_read = qlread(li, buf, size);
|
||||
if ( bytes_read != size )
|
||||
{
|
||||
int saved_code = qerrcode();
|
||||
const char *errmsg = qerrstr();
|
||||
uint64 pos = qltell(li) - bytes_read;
|
||||
static const char *const format =
|
||||
"Read error: %s\n"
|
||||
"(file position 0x%" FMT_64 "X, wanted 0x%" FMT_Z "X bytes, read 0x%X)";
|
||||
|
||||
error(format,
|
||||
saved_code ? errmsg : "read past end of file",
|
||||
pos,
|
||||
size,
|
||||
bytes_read);
|
||||
}
|
||||
}
|
||||
|
||||
#include "../../ldr/pe/common.cpp"
|
||||
|
||||
#define GetMappedFileName_Name "GetMappedFileNameW"
|
||||
#define GetModuleFileNameEx_Name "GetModuleFileNameExW"
|
||||
|
||||
// function prototypes
|
||||
typedef DWORD (WINAPI *GetMappedFileName_t)(HANDLE hProcess, LPVOID lpv, LPWSTR lpFilename, DWORD nSize);
|
||||
typedef DWORD (WINAPI *GetModuleFileNameEx_t)(HANDLE hProcess, HMODULE hModule, LPWSTR lpFilename, DWORD nSize);
|
||||
|
||||
// functions pointers
|
||||
//lint -esym(843,_GetMappedFileName,_GetModuleFileNameEx) could be const
|
||||
static GetMappedFileName_t _GetMappedFileName = NULL;
|
||||
static GetModuleFileNameEx_t _GetModuleFileNameEx = NULL;
|
||||
|
||||
// dynamic linking information for PSAPI functions
|
||||
//lint -esym(843,hPSAPI) -esym(844,hPSAPI) could be const
|
||||
static HMODULE hPSAPI = NULL;
|
||||
|
||||
// dw32 support
|
||||
//lint -esym(843,system_teb_size) could be const
|
||||
static DWORD system_teb_size = MEMORY_PAGE_SIZE;
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
LPVOID win32_debmod_t::correct_exe_image_base(LPVOID base)
|
||||
{
|
||||
return base;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
eanat_t win32_debmod_t::s0tops(eanat_t ea)
|
||||
{
|
||||
return ea;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
eanat_t win32_debmod_t::pstos0(eanat_t ea)
|
||||
{
|
||||
return ea;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
bool win32_debmod_t::prepare_to_stop_process(debug_event_t *, const threads_t &)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
bool win32_debmod_t::disable_hwbpts()
|
||||
{
|
||||
for ( page_bpts_t::iterator p = page_bpts.begin(); p != page_bpts.end(); ++p )
|
||||
dbg_enable_page_bpt(p, false);
|
||||
return true;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
bool win32_debmod_t::enable_hwbpts()
|
||||
{
|
||||
for ( page_bpts_t::iterator p = page_bpts.begin(); p != page_bpts.end(); ++p )
|
||||
dbg_enable_page_bpt(p, true);
|
||||
return true;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
bool win32_debmod_t::may_write(ea_t /*ea*/)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
int win32_debmod_t::describe_stack_segment(
|
||||
thid_t tid,
|
||||
images_t &thr_ranges,
|
||||
images_t &cls_ranges,
|
||||
const _NT_TIB &tib,
|
||||
const char *pref) // "x64" for x64 part of wow64 processes
|
||||
{
|
||||
int cnt = 1;
|
||||
char name[MAXSTR];
|
||||
asize_t size = EA_T(tib.StackBase) - EA_T(tib.StackLimit);
|
||||
qsnprintf(name, sizeof(name), "%sStack[%08X]", pref, tid);
|
||||
image_info_t ii_stack(this, EA_T(tib.StackLimit), size, name);
|
||||
thr_ranges.insert(std::make_pair(ii_stack.base, ii_stack));
|
||||
ii_stack.name = "STACK";
|
||||
cls_ranges.insert(std::make_pair(ii_stack.base, ii_stack));
|
||||
// verify a Stack PAGE_GUARD page exists
|
||||
ea_t ea_guard = ii_stack.base - MEMORY_PAGE_SIZE;
|
||||
MEMORY_BASIC_INFORMATION MemoryBasicInformation;
|
||||
if ( VirtualQueryEx(process_handle, (LPCVOID)(size_t)ea_guard,
|
||||
&MemoryBasicInformation, sizeof(MemoryBasicInformation)) )
|
||||
{
|
||||
if ( MemoryBasicInformation.Protect & PAGE_GUARD ) // a Stack PAGE_GUARD exists
|
||||
{
|
||||
qsnprintf(name, sizeof(name), "%sStack_PAGE_GUARD[%08X]", pref, tid);
|
||||
image_info_t ii_guard(this, ea_guard, MEMORY_PAGE_SIZE, name);
|
||||
thr_ranges.insert(std::make_pair(ii_guard.base, ii_guard));
|
||||
ii_guard.name = "STACK";
|
||||
cls_ranges.insert(std::make_pair(ii_guard.base, ii_guard));
|
||||
cnt++;
|
||||
}
|
||||
}
|
||||
return cnt;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
int win32_debmod_t::add_thread_ranges(
|
||||
thid_t tid,
|
||||
images_t &thr_ranges,
|
||||
images_t &cls_ranges)
|
||||
{
|
||||
thread_info_t *ti = threads.get(tid);
|
||||
if ( ti == NULL )
|
||||
return 0;
|
||||
|
||||
// This structure is specific to NT, but stack related records are Win9X compatible
|
||||
_NT_TIB tib;
|
||||
ea_t ea_tib = EA_T(ti->lpThreadLocalBase);
|
||||
if ( _read_memory(ea_tib, &tib, sizeof(tib)) != sizeof(tib) ) // read the TIB
|
||||
return 0;
|
||||
|
||||
// additional test: we verify that TIB->Self contains the TIB's linear address
|
||||
if ( EA_T(tib.Self) != ea_tib )
|
||||
return false;
|
||||
|
||||
// add TIB range
|
||||
char name[MAXSTR];
|
||||
qsnprintf(name, sizeof(name), "TIB[%08X]", tid);
|
||||
// we suppose the whole page is reserved for the TIB
|
||||
image_info_t ii_tib(this, ea_tib, system_teb_size, name);
|
||||
thr_ranges.insert(std::make_pair(ii_tib.base, ii_tib));
|
||||
|
||||
int cnt = 0;
|
||||
const char *pref = "";
|
||||
if ( check_wow64_process() == WOW64_YES )
|
||||
{
|
||||
// Note: This works for Windows versions <= 8.1
|
||||
// The offset of the 32-bit TEB address within the 64-bit TEB is 0.
|
||||
// This can be used to directly access the 32-bit TEB of a WOW64 thread
|
||||
ea_t wow64_tib_ea = *(uint32*)&tib;
|
||||
struct _NT_TIB32
|
||||
{
|
||||
DWORD ExceptionList;
|
||||
DWORD StackBase;
|
||||
DWORD StackLimit;
|
||||
DWORD SubSystemTib;
|
||||
DWORD FiberData;
|
||||
DWORD ArbitraryUserPointer;
|
||||
DWORD Self;
|
||||
};
|
||||
_NT_TIB32 tib32;
|
||||
if ( _read_memory(wow64_tib_ea, &tib32, sizeof(tib32)) == sizeof(tib32) )
|
||||
{
|
||||
_NT_TIB tib2;
|
||||
tib2.StackBase = (PVOID)(eanat_t)tib32.StackBase;
|
||||
tib2.StackLimit = (PVOID)(eanat_t)tib32.StackLimit;
|
||||
cnt += describe_stack_segment(tid, thr_ranges, cls_ranges, tib2, pref);
|
||||
}
|
||||
pref = "x64";
|
||||
}
|
||||
|
||||
// add stack range
|
||||
cnt += describe_stack_segment(tid, thr_ranges, cls_ranges, tib, pref);
|
||||
return cnt;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// Get PE header
|
||||
// In: ea=DLL imagebase, nh=buffer to keep the answer
|
||||
// child==true:ea is an address in the child process
|
||||
// child==false:ea is an address in the the debugger itself
|
||||
// Returns: offset to the headers, BADADDR means failure
|
||||
ea_t win32_debmod_t::get_pe_header(eanat_t ea, peheader_t *nh)
|
||||
{
|
||||
uint32 offset = 0;
|
||||
uint32 magic;
|
||||
if ( _read_memory(ea, &magic, sizeof(magic)) != sizeof(magic) )
|
||||
return BADADDR;
|
||||
if ( ushort(magic) == MC2('M','Z') )
|
||||
{
|
||||
if ( _read_memory(ea+PE_PTROFF, &offset, sizeof(offset)) != sizeof(offset) )
|
||||
return BADADDR;
|
||||
}
|
||||
peheader64_t pe64;
|
||||
if ( _read_memory(ea+offset, &pe64, sizeof(pe64)) != sizeof(pe64) )
|
||||
return BADADDR;
|
||||
if ( !pe64_to_pe(*nh, pe64, true, true) )
|
||||
return BADADDR;
|
||||
if ( nh->signature != PEEXE_ID )
|
||||
return BADADDR;
|
||||
return offset;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// calculate dll image size
|
||||
// since we could not find anything nice, we just look
|
||||
// at the beginning of the DLL module in the memory and extract
|
||||
// correct value from the file header
|
||||
uint32 win32_debmod_t::calc_imagesize(eanat_t base)
|
||||
{
|
||||
peheader_t nh;
|
||||
ea_t peoff = get_pe_header(base, &nh);
|
||||
if ( peoff == BADADDR )
|
||||
return 0;
|
||||
return nh.imagesize;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
bool win32_debmod_t::create_process(
|
||||
const char *path,
|
||||
const char *args,
|
||||
const char *startdir,
|
||||
bool is_gui,
|
||||
bool hide_window,
|
||||
PROCESS_INFORMATION *ProcessInformation)
|
||||
{
|
||||
linput_t *li = open_linput(path, false);
|
||||
if ( li == NULL )
|
||||
return false;
|
||||
pe_loader_t pl;
|
||||
pl.read_header(li, true);
|
||||
close_linput(li);
|
||||
|
||||
#ifndef __EA64__
|
||||
if ( pl.pe.is_pe_plus() )
|
||||
{
|
||||
dwarning("AUTOHIDE NONE\nPlease use ida64 to debug 64-bit applications");
|
||||
SetLastError(ERROR_NOT_SUPPORTED);
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef __X86__
|
||||
if ( pl.pe.is_pe_plus() )
|
||||
{
|
||||
static const char server_name[] = "win64_remote64.exe";
|
||||
if ( ask_yn(ASKBTN_YES,
|
||||
"AUTOHIDE REGISTRY\nHIDECANCEL\n"
|
||||
"Debugging 64-bit applications is only possible with the %s server.\n"
|
||||
"Launch it now?",
|
||||
server_name) == ASKBTN_YES )
|
||||
{
|
||||
do
|
||||
{
|
||||
// Switch to the remote win32 debugger
|
||||
if ( !load_debugger("win32_stub", true) )
|
||||
{
|
||||
warning("Failed to switch to the remote windows debugger!");
|
||||
break;
|
||||
}
|
||||
|
||||
// Form the server path
|
||||
char server_exe[QMAXPATH];
|
||||
qmakepath(server_exe, sizeof(server_exe), idadir(NULL), server_name, NULL);
|
||||
|
||||
// Try to launch the server
|
||||
STARTUPINFO si = { sizeof(si) };
|
||||
PROCESS_INFORMATION pi;
|
||||
if ( hide_window )
|
||||
{
|
||||
si.dwFlags |= STARTF_USESHOWWINDOW;
|
||||
si.wShowWindow = SW_HIDE; /* SW_FORCEMINIMIZE ? */
|
||||
}
|
||||
if ( !::CreateProcess(server_exe, NULL, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi) )
|
||||
{
|
||||
warning("Failed to run the 64-bit remote server!");
|
||||
break;
|
||||
}
|
||||
|
||||
// Set the remote debugging options: localhost
|
||||
set_remote_debugger("localhost", "", -1);
|
||||
|
||||
// Notify the user
|
||||
info("Debugging server has been started, please try debugging the program again.");
|
||||
} while ( false );
|
||||
}
|
||||
SetLastError(ERROR_NOT_SUPPORTED);
|
||||
return false;
|
||||
}
|
||||
#endif // __X86__
|
||||
|
||||
// Empty directory means our directory
|
||||
if ( startdir != NULL && startdir[0] == '\0' )
|
||||
startdir = NULL;
|
||||
|
||||
// Args passed as empty string?
|
||||
if ( args != NULL && args[0] == '\0' )
|
||||
args = NULL;
|
||||
|
||||
launch_process_params_t lpp;
|
||||
lpp.flags |= LP_TRACE | LP_PATH_WITH_ARGS;
|
||||
if ( !is_gui )
|
||||
lpp.flags |= LP_NEW_CONSOLE;
|
||||
if ( hide_window )
|
||||
lpp.flags |= LP_HIDE_WINDOW;
|
||||
|
||||
lpp.path = path;
|
||||
lpp.args = args;
|
||||
lpp.startdir = startdir;
|
||||
lpp.info = ProcessInformation;
|
||||
|
||||
qstring errbuf;
|
||||
if ( launch_process(lpp, &errbuf) == NULL )
|
||||
{
|
||||
dwarning("AUTOHIDE NONE\n%s", errbuf.c_str());
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
void term_win32_subsystem(void)
|
||||
{
|
||||
if ( hPSAPI != NULL )
|
||||
{
|
||||
FreeLibrary(hPSAPI);
|
||||
hPSAPI = NULL;
|
||||
}
|
||||
if ( DriverHandle != NULL )
|
||||
{
|
||||
CloseHandle(DriverHandle);
|
||||
DriverHandle = NULL;
|
||||
NtUnloadDriver(&DriverPath);
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
void init_win32_subsystem(void)
|
||||
{
|
||||
ntdll = GetModuleHandle(TEXT("ntdll.dll"));
|
||||
if ( ntdll != NULL )
|
||||
{
|
||||
for ( int i=0; i < qnumber(ntfuncs); i++ )
|
||||
*(FARPROC*)ntfuncs[i].fptr = GetProcAddress(ntdll, ntfuncs[i].name);
|
||||
}
|
||||
|
||||
// load the library
|
||||
hPSAPI = LoadLibrary(TEXT("psapi.dll"));
|
||||
if ( hPSAPI != NULL )
|
||||
{
|
||||
// find the needed functions
|
||||
*(FARPROC*)&_GetMappedFileName = GetProcAddress(hPSAPI, TEXT(GetMappedFileName_Name));
|
||||
*(FARPROC*)&_GetModuleFileNameEx = GetProcAddress(hPSAPI, TEXT(GetModuleFileNameEx_Name));
|
||||
if ( _GetMappedFileName == NULL )
|
||||
{
|
||||
FreeLibrary(hPSAPI);
|
||||
hPSAPI = NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
bool win32_debmod_t::can_access(ea_t addr)
|
||||
{
|
||||
char dummy;
|
||||
return access_memory(addr, &dummy, 1, false, false) == 1;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// return the address of all names exported by a DLL in 'ni'
|
||||
// if 'exported_name' is given, only the address of this exported name will be returned in 'ni'
|
||||
bool win32_debmod_t::get_pe_exports_from_path(
|
||||
const char *path,
|
||||
linput_t *li,
|
||||
ea_t imagebase,
|
||||
name_info_t &ni,
|
||||
const char *exported_name) const
|
||||
{
|
||||
// prepare nice name prefix for exported functions names
|
||||
char prefix[MAXSTR];
|
||||
qstrncpy(prefix, qbasename(path), sizeof(prefix));
|
||||
char *ptr = strrchr(prefix, '.');
|
||||
if ( ptr != NULL )
|
||||
*ptr = '\0';
|
||||
qstrlwr(prefix);
|
||||
|
||||
pe_loader_t pl;
|
||||
if ( !pl.read_header(li) )
|
||||
return false;
|
||||
|
||||
struct export_reader_t : public pe_export_visitor_t
|
||||
{
|
||||
const char *prefix;
|
||||
ea_t imagebase;
|
||||
name_info_t ∋
|
||||
const char *exported_name;
|
||||
export_reader_t(const char *pfx, ea_t base, name_info_t &_ni, const char *exname)
|
||||
: prefix(pfx), imagebase(base), ni(_ni), exported_name(exname) {}
|
||||
int idaapi visit_export(uint32 rva, uint32 ord, const char *name, const char *)
|
||||
{
|
||||
ea_t fulladdr = imagebase + rva;
|
||||
if ( exported_name != NULL )
|
||||
{
|
||||
if ( strcmp(exported_name, name) == 0 )
|
||||
{
|
||||
ni.addrs.push_back(fulladdr);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
qstring n2;
|
||||
if ( name[0] == '\0' )
|
||||
n2.sprnt("%s_%u", prefix, ord);
|
||||
else
|
||||
n2.sprnt("%s_%s", prefix, name);
|
||||
ni.addrs.push_back(fulladdr);
|
||||
ni.names.push_back(n2.extract());
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
};
|
||||
export_reader_t er(prefix, imagebase, ni, exported_name);
|
||||
return pl.process_exports(li, er) >= 0;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// return the address of all names exported by a DLL in 'ni'
|
||||
// if 'exported_name' is given, only the address of this exported name will be returned in 'ni'
|
||||
bool win32_debmod_t::get_dll_exports(
|
||||
const images_t &loaded_dlls,
|
||||
ea_t imagebase,
|
||||
name_info_t &ni,
|
||||
const char *exported_name)
|
||||
{
|
||||
char prefix[MAXSTR];
|
||||
images_t::const_iterator p = loaded_dlls.find(imagebase);
|
||||
if ( p == loaded_dlls.end() )
|
||||
{
|
||||
dwarning("get_dll_exports: can't find dll name for imagebase %a", imagebase);
|
||||
return false;
|
||||
}
|
||||
char dname[MAXSTR];
|
||||
const char *dllname = p->second.name.c_str();
|
||||
qstrncpy(dname, dllname, sizeof(dname));
|
||||
if ( debapp_attrs.addrsize == 4 )
|
||||
replace_system32(dname, MAXSTR);
|
||||
linput_t *li = open_linput(dname, false);
|
||||
if ( li == NULL )
|
||||
{
|
||||
// sysWOW64: ntdll32.dll does not exist but there is a file called ntdll.dll
|
||||
if ( stricmp(qbasename(dllname), "ntdll32.dll") != 0 )
|
||||
return false;
|
||||
if ( qisabspath(dllname) )
|
||||
{
|
||||
qstrncpy(prefix, dllname, sizeof(prefix));
|
||||
char *fname = qbasename(prefix);
|
||||
qstrncpy(fname, "ntdll.dll", sizeof(prefix)-(fname-prefix));
|
||||
dllname = prefix;
|
||||
}
|
||||
else
|
||||
{
|
||||
#ifdef __X86__
|
||||
// TODO: On X86 there might by file redirection active on a X64 host
|
||||
// Therefore we will load on such system always 32 bit DLL, as we can
|
||||
// access 64 bit one without disabling the redirection
|
||||
dllname = "C:\\Windows\\System32\\ntdll.dll";
|
||||
#else
|
||||
#ifndef __EA64__
|
||||
dllname = "C:\\Windows\\SysWOW64\\ntdll.dll";
|
||||
#else
|
||||
dllname = debapp_attrs.addrsize != 4 ? "C:\\Windows\\System32\\ntdll.dll" : "C:\\Windows\\SysWOW64\\ntdll.dll";
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
li = open_linput(dllname, false);
|
||||
if ( li == NULL )
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ok = get_pe_exports_from_path(dllname, li, imagebase, ni, exported_name);
|
||||
close_linput(li);
|
||||
return ok;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// get name from export directory in PE image in debugged process
|
||||
bool win32_debmod_t::get_pe_export_name_from_process(
|
||||
eanat_t imagebase,
|
||||
char *name,
|
||||
size_t namesize)
|
||||
{
|
||||
peheader_t pe;
|
||||
ea_t peoff = get_pe_header(imagebase, &pe);
|
||||
if ( peoff != BADADDR && pe.expdir.rva != 0 )
|
||||
{
|
||||
eanat_t ea = imagebase + pe.expdir.rva;
|
||||
peexpdir_t expdir;
|
||||
if ( _read_memory(ea, &expdir, sizeof(expdir)) == sizeof(expdir) )
|
||||
{
|
||||
ea = imagebase + expdir.dllname;
|
||||
name[0] = '\0';
|
||||
_read_memory(ea, name, namesize); // don't check the return code because
|
||||
// we might have read more than necessary
|
||||
if ( name[0] != '\0' )
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// Read/write a model specific register using the driver provided by WinDbg.
|
||||
// The following requirements are imposed by this code:
|
||||
// - debugger module should be run with admin privileges
|
||||
// - System must be loaded with /debug switch (use bcdedit.exe to turn it on)
|
||||
// - Windbg local kernel debugging should be used at least once
|
||||
// This code is based on a sample kindly provided by Alex Ionescu.
|
||||
int win32_debmod_t::kldbgdrv_access_msr(SYSDBG_MSR *msr, bool write)
|
||||
{
|
||||
NTSTATUS code;
|
||||
IO_STATUS_BLOCK IoStatusBlock;
|
||||
if ( DriverHandle == NULL )
|
||||
{
|
||||
//
|
||||
// Acquire 'load driver' privilege
|
||||
//
|
||||
BOOLEAN Old;
|
||||
code = RtlAdjustPrivilege(SE_LOAD_DRIVER_PRIVILEGE, TRUE, FALSE, &Old);
|
||||
if ( FAILED(code) )
|
||||
{
|
||||
dwarning("AUTOHIDE NONE\n"
|
||||
"Failed to acquire 'load driver' privilege, please run as admin!\n"
|
||||
"Error: %s\n", winerr(code));
|
||||
return code;
|
||||
}
|
||||
|
||||
//
|
||||
// And need this for the driver to accept our commands
|
||||
// Additionally, system must be booted in /DEBUG mode
|
||||
//
|
||||
code = RtlAdjustPrivilege(SE_DEBUG_PRIVILEGE, TRUE, FALSE, &Old);
|
||||
if ( FAILED(code) )
|
||||
{
|
||||
dwarning("AUTOHIDE NONE\n"
|
||||
"Failed to acquire 'debug' privilege, is system booted in /debug mode?\n"
|
||||
"Error: %s\n", winerr(code));
|
||||
return code;
|
||||
}
|
||||
|
||||
//
|
||||
// Now load the driver
|
||||
//
|
||||
code = NtLoadDriver(&DriverPath);
|
||||
if ( FAILED(code) && code != STATUS_IMAGE_ALREADY_LOADED )
|
||||
{
|
||||
dwarning("AUTOHIDE NONE\n"
|
||||
"Failed to load 'kldbgdrv', please use local kernel debugging at least once!\n"
|
||||
"Error: %s\n", winerr(code));
|
||||
return code;
|
||||
}
|
||||
|
||||
//
|
||||
// Open a handle to it
|
||||
//
|
||||
OBJECT_ATTRIBUTES ObjectAttributes;
|
||||
InitializeObjectAttributes(&ObjectAttributes, &DriverName, OBJ_CASE_INSENSITIVE, NULL, NULL);
|
||||
code = NtCreateFile(&DriverHandle,
|
||||
GENERIC_READ | GENERIC_WRITE,
|
||||
&ObjectAttributes,
|
||||
&IoStatusBlock,
|
||||
NULL,
|
||||
FILE_ATTRIBUTE_NORMAL,
|
||||
0,
|
||||
FILE_CREATE,
|
||||
FILE_NON_DIRECTORY_FILE,
|
||||
NULL,
|
||||
0);
|
||||
if ( FAILED(code) )
|
||||
{
|
||||
dwarning("AUTOHIDE NONE\n"
|
||||
"Failed to open 'kldbgdrv'\n"
|
||||
"Error: %s\n", winerr(code));
|
||||
return code;
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Package the input parameters into the private structure
|
||||
//
|
||||
KLDD_DATA_DEBUG_CONTROL KldDebugCommand;
|
||||
KldDebugCommand.Command = write ? SysDbgWriteMsr : SysDbgReadMsr;
|
||||
KldDebugCommand.InputBuffer = msr;
|
||||
KldDebugCommand.InputBufferLength = sizeof(*msr);
|
||||
|
||||
//
|
||||
// Send the request -- output isn't packaged, just specify directly the buffer
|
||||
//
|
||||
code = NtDeviceIoControlFile(DriverHandle,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
&IoStatusBlock,
|
||||
KLDD_CODE_DEBUG_CONTROL,
|
||||
&KldDebugCommand,
|
||||
sizeof(KldDebugCommand),
|
||||
msr,
|
||||
sizeof(*msr));
|
||||
if ( FAILED(code) )
|
||||
{
|
||||
dwarning("AUTOHIDE NONE\n"
|
||||
"Failed to access model specific register, is system booted in /debug mode?\n"
|
||||
"Error: %s\n", winerr(code));
|
||||
return code;
|
||||
}
|
||||
|
||||
// all ok!
|
||||
return code;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
int win32_debmod_t::rdmsr(int reg, uint64 *value)
|
||||
{
|
||||
SYSDBG_MSR msr;
|
||||
msr.reg = reg;
|
||||
msr.value = 0; // shut up the compiler
|
||||
|
||||
NTSTATUS code;
|
||||
if ( NtSystemDebugControl == NULL )
|
||||
code = STATUS_NOT_IMPLEMENTED;
|
||||
else
|
||||
code = NtSystemDebugControl(SysDbgReadMsr, &msr, sizeof(msr), &msr, sizeof(msr), 0);
|
||||
|
||||
// if failed to read it with SystemDebugControl, try the driver
|
||||
if ( FAILED(code) )
|
||||
code = kldbgdrv_access_msr(&msr, false);
|
||||
|
||||
if ( SUCCEEDED(code) )
|
||||
*value = msr.value;
|
||||
return code;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
int win32_debmod_t::wrmsr(int reg, uint64 value)
|
||||
{
|
||||
SYSDBG_MSR msr;
|
||||
msr.reg = reg;
|
||||
msr.value = value;
|
||||
|
||||
NTSTATUS code;
|
||||
if ( NtSystemDebugControl == NULL )
|
||||
code = STATUS_NOT_IMPLEMENTED;
|
||||
else
|
||||
code = NtSystemDebugControl(SysDbgWriteMsr, &msr, sizeof(msr), NULL, 0, 0);
|
||||
|
||||
// if failed to write it with SystemDebugControl, try the driver
|
||||
if ( FAILED(code) )
|
||||
code = kldbgdrv_access_msr(&msr, true);
|
||||
|
||||
return code;
|
||||
}
|
||||
208
idasdk76/dbg/win32/win32_local_impl.cpp
Normal file
208
idasdk76/dbg/win32/win32_local_impl.cpp
Normal file
@@ -0,0 +1,208 @@
|
||||
#ifndef __NT__
|
||||
#define EXCEPTION_ACCESS_VIOLATION STATUS_ACCESS_VIOLATION
|
||||
#define EXCEPTION_DATATYPE_MISALIGNMENT STATUS_DATATYPE_MISALIGNMENT
|
||||
#define EXCEPTION_BREAKPOINT STATUS_BREAKPOINT
|
||||
#define EXCEPTION_SINGLE_STEP STATUS_SINGLE_STEP
|
||||
#define EXCEPTION_ARRAY_BOUNDS_EXCEEDED STATUS_ARRAY_BOUNDS_EXCEEDED
|
||||
#define EXCEPTION_FLT_DENORMAL_OPERAND STATUS_FLOAT_DENORMAL_OPERAND
|
||||
#define EXCEPTION_FLT_DIVIDE_BY_ZERO STATUS_FLOAT_DIVIDE_BY_ZERO
|
||||
#define EXCEPTION_FLT_INEXACT_RESULT STATUS_FLOAT_INEXACT_RESULT
|
||||
#define EXCEPTION_FLT_INVALID_OPERATION STATUS_FLOAT_INVALID_OPERATION
|
||||
#define EXCEPTION_FLT_OVERFLOW STATUS_FLOAT_OVERFLOW
|
||||
#define EXCEPTION_FLT_STACK_CHECK STATUS_FLOAT_STACK_CHECK
|
||||
#define EXCEPTION_FLT_UNDERFLOW STATUS_FLOAT_UNDERFLOW
|
||||
#define EXCEPTION_INT_DIVIDE_BY_ZERO STATUS_INTEGER_DIVIDE_BY_ZERO
|
||||
#define EXCEPTION_INT_OVERFLOW STATUS_INTEGER_OVERFLOW
|
||||
#define EXCEPTION_PRIV_INSTRUCTION STATUS_PRIVILEGED_INSTRUCTION
|
||||
#define EXCEPTION_IN_PAGE_ERROR STATUS_IN_PAGE_ERROR
|
||||
#define EXCEPTION_ILLEGAL_INSTRUCTION STATUS_ILLEGAL_INSTRUCTION
|
||||
#define EXCEPTION_NONCONTINUABLE_EXCEPTION STATUS_NONCONTINUABLE_EXCEPTION
|
||||
#define EXCEPTION_STACK_OVERFLOW STATUS_STACK_OVERFLOW
|
||||
#define EXCEPTION_INVALID_DISPOSITION STATUS_INVALID_DISPOSITION
|
||||
#define EXCEPTION_GUARD_PAGE STATUS_GUARD_PAGE_VIOLATION
|
||||
#define EXCEPTION_INVALID_HANDLE STATUS_INVALID_HANDLE
|
||||
#define CONTROL_C_EXIT STATUS_CONTROL_C_EXIT
|
||||
#define DBG_CONTROL_C 0x40010005L
|
||||
#define DBG_CONTROL_BREAK 0x40010008L
|
||||
#define STATUS_GUARD_PAGE_VIOLATION 0x80000001L
|
||||
#define STATUS_DATATYPE_MISALIGNMENT 0x80000002L
|
||||
#define STATUS_BREAKPOINT 0x80000003L
|
||||
#define STATUS_SINGLE_STEP 0x80000004L
|
||||
#define STATUS_ACCESS_VIOLATION 0xC0000005L
|
||||
#define STATUS_IN_PAGE_ERROR 0xC0000006L
|
||||
#define STATUS_INVALID_HANDLE 0xC0000008L
|
||||
#define STATUS_NO_MEMORY 0xC0000017L
|
||||
#define STATUS_ILLEGAL_INSTRUCTION 0xC000001DL
|
||||
#define STATUS_NONCONTINUABLE_EXCEPTION 0xC0000025L
|
||||
#define STATUS_INVALID_DISPOSITION 0xC0000026L
|
||||
#define STATUS_ARRAY_BOUNDS_EXCEEDED 0xC000008CL
|
||||
#define STATUS_FLOAT_DENORMAL_OPERAND 0xC000008DL
|
||||
#define STATUS_FLOAT_DIVIDE_BY_ZERO 0xC000008EL
|
||||
#define STATUS_FLOAT_INEXACT_RESULT 0xC000008FL
|
||||
#define STATUS_FLOAT_INVALID_OPERATION 0xC0000090L
|
||||
#define STATUS_FLOAT_OVERFLOW 0xC0000091L
|
||||
#define STATUS_FLOAT_STACK_CHECK 0xC0000092L
|
||||
#define STATUS_FLOAT_UNDERFLOW 0xC0000093L
|
||||
#define STATUS_INTEGER_DIVIDE_BY_ZERO 0xC0000094L
|
||||
#define STATUS_INTEGER_OVERFLOW 0xC0000095L
|
||||
#define STATUS_PRIVILEGED_INSTRUCTION 0xC0000096L
|
||||
#define STATUS_STACK_OVERFLOW 0xC00000FDL
|
||||
#define STATUS_CONTROL_C_EXIT 0xC000013AL
|
||||
#define STATUS_FLOAT_MULTIPLE_FAULTS 0xC00002B4L
|
||||
#define STATUS_FLOAT_MULTIPLE_TRAPS 0xC00002B5L
|
||||
#define STATUS_REG_NAT_CONSUMPTION 0xC00002C9L
|
||||
#define SUCCEEDED(x) (x >= 0)
|
||||
#define FAILED(x) (x < 0)
|
||||
#endif
|
||||
|
||||
#include <expr.hpp>
|
||||
#include <loader.hpp>
|
||||
#include "../ldr/pe/pe.h"
|
||||
#include "../plugins/pdb/pdb.hpp"
|
||||
#include "win32_rpc.h"
|
||||
#include "dbg_rpc_hlp.h"
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
static const char idc_win32_rdmsr_args[] = { VT_LONG, 0 };
|
||||
static error_t idaapi idc_win32_rdmsr(idc_value_t *argv, idc_value_t *res)
|
||||
{
|
||||
uint64 value = 0; // shut up the compiler
|
||||
uval_t reg = argv[0].num;
|
||||
#ifdef RPC_CLIENT
|
||||
void *out = NULL;
|
||||
ssize_t outsize;
|
||||
int code = g_dbgmod.send_ioctl(WIN32_IOCTL_RDMSR, ®, sizeof(reg), &out, &outsize);
|
||||
if ( SUCCEEDED(code) && outsize == sizeof(value) )
|
||||
value = *(uint64*)out;
|
||||
qfree(out);
|
||||
#else
|
||||
int code = g_dbgmod.rdmsr(reg, &value);
|
||||
#endif
|
||||
if ( FAILED(code) )
|
||||
{
|
||||
res->num = code;
|
||||
return set_qerrno(eExecThrow); // read error, raise exception
|
||||
}
|
||||
res->set_int64(value);
|
||||
return eOk;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
static const char idc_win32_wrmsr_args[] = { VT_LONG, VT_INT64, 0 };
|
||||
static error_t idaapi idc_win32_wrmsr(idc_value_t *argv, idc_value_t *res)
|
||||
{
|
||||
win32_wrmsr_t msr;
|
||||
msr.reg = argv[0].num;
|
||||
msr.value = argv[1].i64;
|
||||
#ifdef RPC_CLIENT
|
||||
res->num = g_dbgmod.send_ioctl(WIN32_IOCTL_WRMSR, &msr, sizeof(msr), NULL, NULL);
|
||||
#else
|
||||
res->num = g_dbgmod.wrmsr(msr.reg, msr.value);
|
||||
#endif
|
||||
return eOk;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// Installs or uninstalls debugger specific idc functions
|
||||
static bool register_idc_funcs(bool reg)
|
||||
{
|
||||
static const ext_idcfunc_t idcfuncs[] =
|
||||
{
|
||||
{ IDC_READ_MSR, idc_win32_rdmsr, idc_win32_rdmsr_args, NULL, 0, 0 },
|
||||
{ IDC_WRITE_MSR, idc_win32_wrmsr, idc_win32_wrmsr_args, NULL, 0, 0 },
|
||||
};
|
||||
return add_idc_funcs(idcfuncs, qnumber(idcfuncs), reg);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
void idaapi rebase_if_required_to(ea_t new_base)
|
||||
{
|
||||
netnode penode(PE_NODE);
|
||||
ea_t currentbase = new_base;
|
||||
ea_t imagebase = ea_t(penode.altval(PE_ALT_IMAGEBASE)); // loading address (usually pe.imagebase)
|
||||
|
||||
if ( imagebase == 0 )
|
||||
{
|
||||
if ( !is_miniidb() )
|
||||
warning("AUTOHIDE DATABASE\n"
|
||||
"IDA could not automatically determine if the program should be\n"
|
||||
"rebased in the database because the database format is too old and\n"
|
||||
"doesn't contain enough information.\n"
|
||||
"Create a new database if you want automated rebasing to work properly.\n"
|
||||
"Note you can always manually rebase the program by using the\n"
|
||||
"Edit, Segments, Rebase program command.");
|
||||
}
|
||||
else if ( imagebase != currentbase )
|
||||
{
|
||||
rebase_or_warn(imagebase, currentbase);
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
bool read_pe_header(peheader_t *pe)
|
||||
{
|
||||
netnode penode(PE_NODE);
|
||||
return penode.valobj(pe, sizeof(peheader_t)) > 0;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// Initialize Win32 debugger plugin
|
||||
static bool win32_init_plugin(void)
|
||||
{
|
||||
// Remote debugger? Then nothing to initialize locally
|
||||
#ifndef RPC_CLIENT
|
||||
if ( !init_subsystem() )
|
||||
return false;
|
||||
#endif
|
||||
if ( !netnode::inited() || is_miniidb() || inf_is_snapshot() )
|
||||
{
|
||||
#ifndef __NT__
|
||||
// local debugger is available if we are running under Windows
|
||||
// for other systems only the remote debugger is available
|
||||
if ( !debugger.is_remote() )
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
else
|
||||
{
|
||||
if ( inf_get_filetype() != f_PE )
|
||||
return false; // only PE files
|
||||
|
||||
processor_t &ph = PH;
|
||||
if ( ph.id != TARGET_PROCESSOR && ph.id != -1 )
|
||||
return false;
|
||||
|
||||
// find out the pe header
|
||||
peheader_t pe;
|
||||
if ( !read_pe_header(&pe) )
|
||||
return false;
|
||||
|
||||
// debug only gui, console, or unknown applications
|
||||
if ( pe.subsys != PES_WINGUI // Windows GUI
|
||||
&& pe.subsys != PES_WINCHAR // Windows Character
|
||||
&& pe.subsys != PES_UNKNOWN ) // Unknown
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
inline void win32_term_plugin(void)
|
||||
{
|
||||
#ifndef RPC_CLIENT
|
||||
term_subsystem();
|
||||
#endif
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
struct pdb_remote_session_t;
|
||||
void close_pdb_remote_session(pdb_remote_session_t *)
|
||||
{
|
||||
}
|
||||
|
||||
#ifndef HAVE_PLUGIN_COMMENTS
|
||||
//--------------------------------------------------------------------------
|
||||
static const char comment[] = "Userland win32 debugger plugin";
|
||||
#endif
|
||||
203
idasdk76/dbg/win32/win32_rpc.h
Normal file
203
idasdk76/dbg/win32/win32_rpc.h
Normal file
@@ -0,0 +1,203 @@
|
||||
|
||||
#ifndef WIN32_RPC_H
|
||||
#define WIN32_RPC_H
|
||||
|
||||
// IOCTL codes for the win32 debugger
|
||||
|
||||
#define WIN32_IOCTL_RDMSR 0 // read model specific register
|
||||
#define WIN32_IOCTL_WRMSR 1 // write model specific register
|
||||
#define WIN32_IOCTL_READFILE 2 // server->client: read bytes from the input file
|
||||
// uint64 offset;
|
||||
// uint32 length;
|
||||
// returns: 1 - ok
|
||||
// -2 - error (text in output buffer)
|
||||
|
||||
// Open file for PDB retrieval.
|
||||
//
|
||||
// This operation will *typically* require that executable data
|
||||
// be provided to the underlying MS PDB "DIA" dll. Therefore,
|
||||
// there is _no_ way (currently) that this operation will
|
||||
// immediately return something relevant. The client must
|
||||
// poll for _OPERATION_COMPLETE-ness.
|
||||
//
|
||||
// client->server
|
||||
// (unpacked) compiler_info_t: compiler_info
|
||||
// (packed) uint64 : base_address
|
||||
// (unpacked) char * : input_file
|
||||
// (unpacked) char * : user symbols path
|
||||
// server->client
|
||||
// (packed) uint32 : session handle
|
||||
#define WIN32_IOCTL_PDB_OPEN 3
|
||||
|
||||
// Close PDB 'session', previously opened with _PDB_OPEN.
|
||||
//
|
||||
// client->server
|
||||
// (packed) uint32 : session handle
|
||||
// server->client
|
||||
// void
|
||||
#define WIN32_IOCTL_PDB_CLOSE 4
|
||||
|
||||
// Fetch the data for one symbol.
|
||||
//
|
||||
// Synchronous operation.
|
||||
//
|
||||
// client->server
|
||||
// (packed) uint32 : session handle
|
||||
// (packed) uint64 : symbol ID
|
||||
// server->client
|
||||
// (unpacked) uint32: The integer value 1.
|
||||
// (serialized) data: Packed symbol data (once).
|
||||
#define WIN32_IOCTL_PDB_FETCH_SYMBOL 5
|
||||
|
||||
// Fetch the data for the children of a symbol.
|
||||
//
|
||||
// Synchronous operation.
|
||||
//
|
||||
// client->server
|
||||
// (packed) uint32 : session handle
|
||||
// (packed) uint64 : symbol ID
|
||||
// (packed) uint32 : children type (a SymTagEnum)
|
||||
// server->client
|
||||
// (unpacked) uint32: Number of symbols whose data
|
||||
// has been fetched.
|
||||
// (serialized) data: Packed symbol data (N times).
|
||||
#define WIN32_IOCTL_PDB_FETCH_CHILDREN 6
|
||||
|
||||
// Is the current operation complete?
|
||||
//
|
||||
// Depending on the type of the operation, the contents
|
||||
// of the results will differ:
|
||||
// - _OPEN
|
||||
// (packed) uint64 : Global symbol ID.
|
||||
// (packed) uint32 : machine type.
|
||||
// (packed) uint32 : DIA version.
|
||||
//
|
||||
// NOTE: Currently, this IOCTL only makes sense to check
|
||||
// for completeness of operation _OPEN, but this
|
||||
// might change in the future.
|
||||
//
|
||||
// client->server
|
||||
// (packed) uint32 : session handle
|
||||
// server->client
|
||||
// (packed) uint32 : See pdb_op_completion_t
|
||||
// Depending on this first byte, the following will be come:
|
||||
// pdb_op_not_complete:
|
||||
// nothing
|
||||
// pdb_op_complete:
|
||||
// (packed) uint32 : global symbol ID
|
||||
// (packed) uint32 : machine type
|
||||
// (packed) uint32 : DIA version
|
||||
// (unpacked) str : used file name
|
||||
// pdb_op_failure:
|
||||
// (unpacked) str : error message
|
||||
|
||||
#define WIN32_IOCTL_PDB_OPERATION_COMPLETE 7
|
||||
|
||||
// Get lines by VA
|
||||
//
|
||||
// client->server
|
||||
// (packed) uint32 : session handle
|
||||
// (packed) ea_t : VA
|
||||
// (packed) uint64 : length
|
||||
// server->client
|
||||
// (packed) uint32 : the number of line-number objects
|
||||
// (packed) data : the line-number objects (N times)
|
||||
//
|
||||
// Each of the line-number objects is transmitted like so:
|
||||
// (packed) ea_t : VA
|
||||
// (packed) uint32 : length
|
||||
// (packed) uint32 : columnNumber
|
||||
// (packed) uint32 : columnNumberEnd
|
||||
// (packed) uint32 : lineNumber
|
||||
// (packed) uint32 : lineNumberEnd
|
||||
// (packed) uint32 : file_id
|
||||
// (unpacked) byte : statement
|
||||
#define WIN32_IOCTL_PDB_SIP_FETCH_LINES_BY_VA 8
|
||||
|
||||
// Get lines by coordinates
|
||||
//
|
||||
// client->server
|
||||
// (packed) uint32 : session handle
|
||||
// (packed) uint32 : file ID
|
||||
// (packed) uint32 : lnnum
|
||||
// (packed) uint32 : colnum
|
||||
// server->client
|
||||
// same as WIN32_IOCTL_PDB_SIP_FETCH_LINES_BY_VA
|
||||
#define WIN32_IOCTL_PDB_SIP_FETCH_LINES_BY_COORDS 9
|
||||
|
||||
// Get symbols at EA
|
||||
//
|
||||
// client->server
|
||||
// (packed) uint32 : session handle
|
||||
// (packed) ea_t : VA
|
||||
// (packed) uint64 : length
|
||||
// (packed) uint32 : children type (a SymTagEnum)
|
||||
// server->client
|
||||
// (unpacked) uint32: Number of symbols whose data
|
||||
// has been fetched.
|
||||
// (serialized) data: Packed symbol data (N times).
|
||||
#define WIN32_IOCTL_PDB_SIP_FETCH_SYMBOLS_AT_VA 10
|
||||
|
||||
// Get compilands for file
|
||||
//
|
||||
// client->server
|
||||
// (packed) uint32 : session handle
|
||||
// (packed) uint32 : file ID
|
||||
// server->client
|
||||
// (unpacked) uint32: Number of symbols whose data
|
||||
// has been fetched.
|
||||
// (serialized) data: Packed symbol data (N times).
|
||||
#define WIN32_IOCTL_PDB_SIP_FETCH_FILE_COMPILANDS 11
|
||||
|
||||
// Get path for file ID
|
||||
//
|
||||
// client->server
|
||||
// (packed) uint32 : session handle
|
||||
// (packed) uint32 : file ID
|
||||
// server->client
|
||||
// (unpacked) str : the path
|
||||
#define WIN32_IOCTL_PDB_SIP_FETCH_FILE_PATH 12
|
||||
|
||||
// Get files IDs for files corresponding to symbol
|
||||
//
|
||||
// client->server
|
||||
// (packed) uint32 : session handle
|
||||
// (packed) uint64 : symbol ID
|
||||
// server->client
|
||||
// (packed) uint32 : the number of IDs
|
||||
// (packed) uint32 : file ID (N times).
|
||||
#define WIN32_IOCTL_PDB_SIP_FETCH_SYMBOL_FILES 13
|
||||
|
||||
// Get files IDs for files whose name matches
|
||||
//
|
||||
// client->server
|
||||
// (packed) uint32 : session handle
|
||||
// (unpacked) str : the file name
|
||||
// server->client
|
||||
// (packed) uint32 : the number of IDs
|
||||
// (packed) uint32 : file ID (N times).
|
||||
#define WIN32_IOCTL_PDB_SIP_FIND_FILES 14
|
||||
|
||||
enum ioctl_pdb_code_t
|
||||
{
|
||||
pdb_ok = 1,
|
||||
pdb_error = -2,
|
||||
};
|
||||
|
||||
enum pdb_op_completion_t
|
||||
{
|
||||
pdb_op_not_complete = 0,
|
||||
pdb_op_complete = 1,
|
||||
pdb_op_failure = -1,
|
||||
};
|
||||
|
||||
|
||||
// WIN32_IOCTL_WRMSR uses this structure:
|
||||
struct win32_wrmsr_t
|
||||
{
|
||||
uint32 reg;
|
||||
uint64 value;
|
||||
};
|
||||
|
||||
|
||||
#endif // WIN32_RPC_H
|
||||
406
idasdk76/dbg/win32/win32_server.cpp
Normal file
406
idasdk76/dbg/win32/win32_server.cpp
Normal file
@@ -0,0 +1,406 @@
|
||||
//
|
||||
//
|
||||
// This file contains win32 specific implementations of win32_debugger_module class
|
||||
// server-side functionality only
|
||||
//
|
||||
//
|
||||
|
||||
#include <pro.h>
|
||||
#include "win32_rpc.h"
|
||||
#include "win32_debmod.h"
|
||||
#include "dbg_rpc_hlp.h"
|
||||
|
||||
bool ida_export idb_utf8(qstring *, const char *, int, int) { return false; }
|
||||
|
||||
#include "tilfuncs.hpp"
|
||||
|
||||
//---------------------------------------------------------- main thread ---
|
||||
static AS_PRINTF(3, 4) int pdb_ioctl_error(void **poutbuf, ssize_t *poutsize, const char *format, ...)
|
||||
{
|
||||
char buf[MAXSTR];
|
||||
va_list va;
|
||||
va_start(va, format);
|
||||
int len = qvsnprintf(buf, sizeof(buf), format, va);
|
||||
va_end(va);
|
||||
msg("%s", buf);
|
||||
|
||||
*poutsize = len + 1;
|
||||
*poutbuf = qstrdup(buf);
|
||||
return pdb_error;
|
||||
}
|
||||
|
||||
//---------------------------------------------------------- main thread ---
|
||||
void win32_debmod_t::handle_pdb_thread_request(void *_pdb_rsess)
|
||||
{
|
||||
pdb_remote_session_t *pdb_rsess = (pdb_remote_session_t *) _pdb_rsess;
|
||||
pdb_remote_session_t::client_read_request_t &rr = pdb_rsess->client_read_request;
|
||||
if ( rr.kind == READ_INPUT_FILE )
|
||||
{
|
||||
// read input file
|
||||
bytevec_t req;
|
||||
req.pack_dq(rr.off_ea);
|
||||
req.pack_dd(rr.size);
|
||||
void *outbuf = NULL;
|
||||
ssize_t outsize = 0;
|
||||
// send request to IDA
|
||||
int rc = send_ioctl(WIN32_IOCTL_READFILE, req.begin(), req.size(), &outbuf, &outsize);
|
||||
if ( rc == 1 && outbuf != NULL )
|
||||
{
|
||||
// OK
|
||||
size_t copylen = qmin(rr.size, outsize);
|
||||
memcpy(rr.buffer, outbuf, copylen);
|
||||
rr.size = copylen;
|
||||
rr.result = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
rr.result = false;
|
||||
}
|
||||
if ( outbuf != NULL )
|
||||
qfree(outbuf);
|
||||
}
|
||||
else if ( rr.kind == READ_MEMORY )
|
||||
{
|
||||
// read memory
|
||||
ea_t ea = ea_t(rr.off_ea);
|
||||
void *buf = rr.buffer;
|
||||
size_t size = rr.size;
|
||||
ssize_t rc = _read_memory(ea, buf, size);
|
||||
if ( rc >= 0 )
|
||||
rr.size = rc;
|
||||
rr.result = rc >= 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
// unknown request
|
||||
rr.result = false;
|
||||
}
|
||||
|
||||
rr.read_complete();
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
pdb_remote_session_t *win32_debmod_t::get_pdb_session(int id)
|
||||
{
|
||||
for ( size_t i = 0; i < pdb_remote_sessions.size(); ++i )
|
||||
if ( pdb_remote_sessions[i]->get_id() == id )
|
||||
return pdb_remote_sessions[i];
|
||||
return NULL;
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
void win32_debmod_t::delete_pdb_session(int id)
|
||||
{
|
||||
for ( size_t i = 0; i < pdb_remote_sessions.size(); ++i )
|
||||
{
|
||||
if ( pdb_remote_sessions[i]->get_id() == id )
|
||||
{
|
||||
pdb_remote_sessions[i]->stop();
|
||||
delete pdb_remote_sessions[i];
|
||||
pdb_remote_sessions.erase(pdb_remote_sessions.begin() + i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
void close_pdb_remote_session(pdb_remote_session_t *session)
|
||||
{
|
||||
session->stop();
|
||||
delete session;
|
||||
}
|
||||
|
||||
//---------------------------------------------------------- main thread ---
|
||||
int idaapi win32_debmod_t::handle_ioctl(
|
||||
int fn,
|
||||
const void *buf,
|
||||
size_t size,
|
||||
void **poutbuf,
|
||||
ssize_t *poutsize)
|
||||
{
|
||||
qnotused(size);
|
||||
int sid = 0; // pdb_remote_session_t ID
|
||||
pdb_remote_session_t *pdb_rsess = NULL;
|
||||
switch ( fn )
|
||||
{
|
||||
case WIN32_IOCTL_RDMSR:
|
||||
QASSERT(30119, size == sizeof(uval_t));
|
||||
{
|
||||
uint64 value;
|
||||
uval_t reg = *(uval_t *)buf;
|
||||
int code = rdmsr(reg, &value);
|
||||
if ( SUCCEEDED(code) )
|
||||
{
|
||||
*poutbuf = qalloc(sizeof(value));
|
||||
if ( *poutbuf != NULL )
|
||||
{
|
||||
memcpy(*poutbuf, &value, sizeof(value));
|
||||
*poutsize = sizeof(value);
|
||||
}
|
||||
}
|
||||
return code;
|
||||
}
|
||||
|
||||
case WIN32_IOCTL_WRMSR:
|
||||
QASSERT(30120, size == sizeof(win32_wrmsr_t));
|
||||
{
|
||||
win32_wrmsr_t &msr = *(win32_wrmsr_t *)buf;
|
||||
return wrmsr(msr.reg, msr.value);
|
||||
}
|
||||
|
||||
#define ENSURE_PDB_THREAD() \
|
||||
do \
|
||||
{ \
|
||||
if ( !pdb_thread.is_running() ) \
|
||||
return pdb_ioctl_error( \
|
||||
poutbuf, poutsize, "PDB thread not running?!?\n"); \
|
||||
} while ( false )
|
||||
|
||||
#define GET_OPENED_SESSION() \
|
||||
do \
|
||||
{ \
|
||||
if ( mmdsr.empty() ) \
|
||||
return pdb_error; \
|
||||
sid = mmdsr.unpack_dd(); \
|
||||
pdb_rsess = get_pdb_session(sid); \
|
||||
} while ( false )
|
||||
|
||||
#define ENSURE_SESSION_OPENED() \
|
||||
do \
|
||||
{ \
|
||||
GET_OPENED_SESSION(); \
|
||||
if ( pdb_rsess == NULL ) \
|
||||
return pdb_ioctl_error( \
|
||||
poutbuf, poutsize, "Unknown PDB session #%d\n", sid); \
|
||||
} while ( false )
|
||||
|
||||
#define FLUSH_PDB_REQUEST_STORAGE() \
|
||||
do \
|
||||
{ \
|
||||
size_t sz = pdb_rsess->storage.size(); \
|
||||
uint8 *raw = (uint8 *) qalloc(sz); \
|
||||
if ( raw == NULL ) \
|
||||
return pdb_error; \
|
||||
memcpy(raw, pdb_rsess->storage.begin(), sz); \
|
||||
*poutbuf = raw; \
|
||||
*poutsize = sz; \
|
||||
} while ( false )
|
||||
|
||||
case WIN32_IOCTL_PDB_OPEN:
|
||||
{
|
||||
pdb_thread.start_if_needed();
|
||||
memory_deserializer_t mmdsr(buf, size);
|
||||
|
||||
pdb_rsess = new pdb_remote_session_t();
|
||||
pdb_remote_sessions.push_back(pdb_rsess);
|
||||
|
||||
compiler_info_t cci;
|
||||
mmdsr.unpack_obj(&cci, sizeof(cci));
|
||||
pdbargs_t args;
|
||||
args.pdb_path = mmdsr.unpack_str();
|
||||
args.input_path = mmdsr.unpack_str();
|
||||
mmdsr.unpack_obj(&args.pdb_sign, sizeof(args.pdb_sign));
|
||||
args.spath = mmdsr.unpack_str();
|
||||
args.loaded_base = mmdsr.unpack_ea64();
|
||||
args.flags = mmdsr.unpack_dd();
|
||||
pdb_rsess->open(cci, args);
|
||||
bytevec_t storage;
|
||||
storage.pack_dd(pdb_rsess->get_id());
|
||||
*poutsize = storage.size();
|
||||
*poutbuf = storage.extract();
|
||||
pdb_rsess->is_opening = true;
|
||||
}
|
||||
return pdb_ok;
|
||||
|
||||
case WIN32_IOCTL_PDB_OPERATION_COMPLETE:
|
||||
// This is used, in a polling fashion, by the the client,
|
||||
// to check on completeness of the fetch. At the same time,
|
||||
// this is our wake-up call for looking whether the
|
||||
// fetch thread requires more information.
|
||||
{
|
||||
ENSURE_PDB_THREAD();
|
||||
memory_deserializer_t mmdsr(buf, size);
|
||||
ENSURE_SESSION_OPENED();
|
||||
|
||||
// If we've got a read request, handle it now
|
||||
if ( pdb_rsess->client_read_request.pending() )
|
||||
handle_pdb_thread_request(pdb_rsess);
|
||||
|
||||
bytevec_t storage;
|
||||
bool done = pdb_rsess->is_done();
|
||||
if ( done )
|
||||
{
|
||||
pdb_rsess->is_opening = false;
|
||||
const char *fname = pdb_rsess->session_ref.session->get_used_fname();
|
||||
local_pdb_access_t *acc = pdb_rsess->session_ref.session->pdb_access;
|
||||
HRESULT hr = acc != NULL ? S_OK : E_FAIL;
|
||||
if ( SUCCEEDED(hr) )
|
||||
{
|
||||
storage.pack_dd(uint32(pdb_op_complete));
|
||||
storage.pack_dd(acc->get_global_symbol_id());
|
||||
storage.pack_dd(acc->get_machine_type());
|
||||
storage.pack_dd(acc->get_dia_version());
|
||||
storage.pack_str(fname);
|
||||
}
|
||||
else
|
||||
{
|
||||
storage.pack_dd(uint32(pdb_op_failure));
|
||||
qstring errmsg;
|
||||
errmsg.sprnt("%s: %s\n", fname, pdberr(hr));
|
||||
storage.pack_str(errmsg.c_str());
|
||||
delete_pdb_session(sid);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
storage.pack_dd(uint32(pdb_op_not_complete));
|
||||
}
|
||||
|
||||
*poutsize = storage.size();
|
||||
*poutbuf = storage.extract();
|
||||
return pdb_ok;
|
||||
}
|
||||
|
||||
case WIN32_IOCTL_PDB_FETCH_SYMBOL:
|
||||
case WIN32_IOCTL_PDB_FETCH_CHILDREN:
|
||||
{
|
||||
ENSURE_PDB_THREAD();
|
||||
memory_deserializer_t mmdsr(buf, size);
|
||||
ENSURE_SESSION_OPENED();
|
||||
|
||||
DWORD sym_id = mmdsr.unpack_dd();
|
||||
// msg("Fetch%s 0x%x\n",
|
||||
// (fn == WIN32_IOCTL_PDB_FETCH_CHILDREN ? " children for" : ""),
|
||||
// (uint32) sym);
|
||||
bool ok;
|
||||
if ( fn == WIN32_IOCTL_PDB_FETCH_SYMBOL )
|
||||
{
|
||||
// Symbol
|
||||
ok = pdb_rsess->fetch_symbol(sym_id);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Children
|
||||
enum SymTagEnum children_type = (enum SymTagEnum) mmdsr.unpack_dd();
|
||||
ok = pdb_rsess->fetch_children(sym_id, children_type);
|
||||
}
|
||||
|
||||
if ( ok )
|
||||
FLUSH_PDB_REQUEST_STORAGE();
|
||||
|
||||
return ok ? pdb_ok : pdb_error;
|
||||
}
|
||||
|
||||
case WIN32_IOCTL_PDB_CLOSE:
|
||||
{
|
||||
ENSURE_PDB_THREAD();
|
||||
memory_deserializer_t mmdsr(buf, size);
|
||||
GET_OPENED_SESSION();
|
||||
if ( pdb_rsess != NULL )
|
||||
delete_pdb_session(sid);
|
||||
|
||||
return pdb_ok;
|
||||
}
|
||||
|
||||
case WIN32_IOCTL_PDB_SIP_FETCH_LINES_BY_VA:
|
||||
{
|
||||
ENSURE_PDB_THREAD();
|
||||
memory_deserializer_t mmdsr(buf, size);
|
||||
ENSURE_SESSION_OPENED();
|
||||
ea_t va = mmdsr.unpack_ea64();
|
||||
uint64 length = mmdsr.unpack_dq();
|
||||
|
||||
bool ok = pdb_rsess->fetch_lines_by_va(
|
||||
va, length);
|
||||
if ( ok )
|
||||
FLUSH_PDB_REQUEST_STORAGE();
|
||||
return ok ? pdb_ok : pdb_error;
|
||||
}
|
||||
|
||||
case WIN32_IOCTL_PDB_SIP_FETCH_LINES_BY_COORDS:
|
||||
{
|
||||
ENSURE_PDB_THREAD();
|
||||
memory_deserializer_t mmdsr(buf, size);
|
||||
ENSURE_SESSION_OPENED();
|
||||
|
||||
DWORD file_id = mmdsr.unpack_dd();
|
||||
DWORD lnnum = mmdsr.unpack_dd();
|
||||
DWORD colnum = mmdsr.unpack_dd();
|
||||
bool ok = pdb_rsess->fetch_lines_by_coords(file_id, lnnum, colnum);
|
||||
if ( ok )
|
||||
FLUSH_PDB_REQUEST_STORAGE();
|
||||
return ok ? pdb_ok : pdb_error;
|
||||
}
|
||||
|
||||
case WIN32_IOCTL_PDB_SIP_FETCH_SYMBOLS_AT_VA:
|
||||
{
|
||||
ENSURE_PDB_THREAD();
|
||||
memory_deserializer_t mmdsr(buf, size);
|
||||
ENSURE_SESSION_OPENED();
|
||||
|
||||
ea_t va = mmdsr.unpack_ea64();
|
||||
uint64 length = mmdsr.unpack_dq();
|
||||
enum SymTagEnum type = (enum SymTagEnum) mmdsr.unpack_dd();
|
||||
bool ok = pdb_rsess->fetch_symbols_at_va(va, length, type);
|
||||
if ( ok )
|
||||
FLUSH_PDB_REQUEST_STORAGE();
|
||||
return ok ? pdb_ok : pdb_error;
|
||||
}
|
||||
|
||||
case WIN32_IOCTL_PDB_SIP_FETCH_FILE_COMPILANDS:
|
||||
{
|
||||
ENSURE_PDB_THREAD();
|
||||
memory_deserializer_t mmdsr(buf, size);
|
||||
ENSURE_SESSION_OPENED();
|
||||
|
||||
uint32 file_id = mmdsr.unpack_dd();
|
||||
bool ok = pdb_rsess->fetch_file_compilands(file_id);
|
||||
if ( ok )
|
||||
FLUSH_PDB_REQUEST_STORAGE();
|
||||
return ok ? pdb_ok : pdb_error;
|
||||
}
|
||||
|
||||
case WIN32_IOCTL_PDB_SIP_FETCH_FILE_PATH:
|
||||
{
|
||||
ENSURE_PDB_THREAD();
|
||||
memory_deserializer_t mmdsr(buf, size);
|
||||
ENSURE_SESSION_OPENED();
|
||||
|
||||
uint32 file_id = mmdsr.unpack_dd();
|
||||
bool ok = pdb_rsess->fetch_file_path(file_id);
|
||||
if ( ok )
|
||||
FLUSH_PDB_REQUEST_STORAGE();
|
||||
return ok ? pdb_ok : pdb_error;
|
||||
}
|
||||
|
||||
case WIN32_IOCTL_PDB_SIP_FETCH_SYMBOL_FILES:
|
||||
{
|
||||
ENSURE_PDB_THREAD();
|
||||
memory_deserializer_t mmdsr(buf, size);
|
||||
ENSURE_SESSION_OPENED();
|
||||
|
||||
DWORD sym_id = mmdsr.unpack_dd();
|
||||
bool ok = pdb_rsess->fetch_symbol_files(sym_id);
|
||||
if ( ok )
|
||||
FLUSH_PDB_REQUEST_STORAGE();
|
||||
return ok ? pdb_ok : pdb_error;
|
||||
}
|
||||
|
||||
case WIN32_IOCTL_PDB_SIP_FIND_FILES:
|
||||
{
|
||||
ENSURE_PDB_THREAD();
|
||||
memory_deserializer_t mmdsr(buf, size);
|
||||
ENSURE_SESSION_OPENED();
|
||||
|
||||
qstring fname = mmdsr.unpack_str();
|
||||
bool ok = pdb_rsess->fetch_files(fname.c_str());
|
||||
if ( ok )
|
||||
FLUSH_PDB_REQUEST_STORAGE();
|
||||
return ok ? pdb_ok : pdb_error;
|
||||
}
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
15
idasdk76/dbg/win32/win32_server_stub.cpp
Normal file
15
idasdk76/dbg/win32/win32_server_stub.cpp
Normal file
@@ -0,0 +1,15 @@
|
||||
//
|
||||
//
|
||||
// This file contains win32 specific implementations of win32_debugger_module class
|
||||
// IDA-side functionality only (for local debugger)
|
||||
//
|
||||
//
|
||||
|
||||
#include <pro.h>
|
||||
#include "win32_debmod.h"
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
int idaapi win32_debmod_t::handle_ioctl(int /*fn*/, const void * /*buf*/, size_t /*size*/, void ** /*poutbuf*/, ssize_t * /*poutsize*/)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
128
idasdk76/dbg/win32/win32_stub.cpp
Normal file
128
idasdk76/dbg/win32/win32_stub.cpp
Normal file
@@ -0,0 +1,128 @@
|
||||
#define REMOTE_DEBUGGER
|
||||
#define RPC_CLIENT
|
||||
|
||||
static const char wanted_name[] = "Remote Windows debugger";
|
||||
|
||||
#define DEBUGGER_NAME "win32"
|
||||
#define PROCESSOR_NAME "metapc"
|
||||
#define DEFAULT_PLATFORM_NAME "win32"
|
||||
#define TARGET_PROCESSOR PLFM_386
|
||||
#define DEBUGGER_ID DEBUGGER_ID_X86_IA32_WIN32_USER
|
||||
#define DEBUGGER_FLAGS (DBG_FLAG_REMOTE \
|
||||
| DBG_FLAG_EXITSHOTOK \
|
||||
| DBG_FLAG_LOWCNDS \
|
||||
| DBG_FLAG_DEBTHREAD \
|
||||
| DBG_FLAG_ANYSIZE_HWBPT)
|
||||
#define DEBUGGER_RESMOD (DBG_RESMOD_STEP_INTO)
|
||||
#define HAVE_APPCALL
|
||||
#define S_FILETYPE f_PE
|
||||
#define win32_term_plugin term_plugin
|
||||
|
||||
#include <pro.h>
|
||||
#include <idp.hpp>
|
||||
#include <idd.hpp>
|
||||
#include <ua.hpp>
|
||||
#include <range.hpp>
|
||||
#include <loader.hpp>
|
||||
#include <kernwin.hpp>
|
||||
#include <network.hpp>
|
||||
|
||||
#include "w32sehch.h"
|
||||
#include "dbg_rpc_client.h"
|
||||
#include "rpc_debmod.h"
|
||||
|
||||
class win32_rpc_debmod_t : public rpc_debmod_t
|
||||
{
|
||||
typedef rpc_debmod_t inherited;
|
||||
public:
|
||||
win32_rpc_debmod_t(const char *default_platform)
|
||||
: rpc_debmod_t(default_platform) {}
|
||||
|
||||
virtual bool idaapi open_remote(
|
||||
const char *hostname,
|
||||
int port_number,
|
||||
const char *password,
|
||||
qstring *errbuf) override
|
||||
{
|
||||
char path[QMAXPATH];
|
||||
get_input_file_path(path, sizeof(path));
|
||||
pdb_file_path = path;
|
||||
return inherited::open_remote(hostname, port_number, password, errbuf);
|
||||
}
|
||||
|
||||
qstring pdb_file_path;
|
||||
};
|
||||
|
||||
win32_rpc_debmod_t g_dbgmod(DEFAULT_PLATFORM_NAME);
|
||||
#include "common_stub_impl.cpp"
|
||||
|
||||
#include "pc_local_impl.cpp"
|
||||
#include "win32_local_impl.cpp"
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// handler on IDA: Server -> IDA
|
||||
static int ioctl_handler(
|
||||
rpc_engine_t * /*rpc*/,
|
||||
int fn,
|
||||
const void *buf,
|
||||
size_t size,
|
||||
void **poutbuf,
|
||||
ssize_t *poutsize)
|
||||
{
|
||||
qnotused(size);
|
||||
switch ( fn )
|
||||
{
|
||||
case WIN32_IOCTL_READFILE:
|
||||
{
|
||||
user_cancelled();
|
||||
const uchar *ptr = (const uchar *)buf;
|
||||
const uchar *end = ptr + size;
|
||||
uint64 offset = unpack_dq(&ptr, end);
|
||||
uint32 length = unpack_dd(&ptr, end);
|
||||
|
||||
*poutbuf = NULL;
|
||||
*poutsize = 0;
|
||||
if ( length != 0 )
|
||||
{
|
||||
FILE *infile = qfopen(g_dbgmod.pdb_file_path.c_str(), "rb");
|
||||
if ( infile == NULL )
|
||||
return -2;
|
||||
|
||||
void *outbuf = qalloc(length);
|
||||
if ( outbuf == NULL )
|
||||
return -2;
|
||||
|
||||
qfseek(infile, offset, SEEK_SET);
|
||||
int readlen = qfread(infile, outbuf, length);
|
||||
qfclose(infile);
|
||||
|
||||
if ( readlen < 0 || readlen > length )
|
||||
{
|
||||
qfree(outbuf);
|
||||
return -2;
|
||||
}
|
||||
*poutbuf = outbuf;
|
||||
*poutsize = readlen;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
//lint -esym(528,init_plugin) static symbol '' not referenced
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// Initialize Win32 debugger stub
|
||||
static bool init_plugin(void)
|
||||
{
|
||||
// There is no need to call win32_init_plugin() (which checks the PE
|
||||
// file parameters) if the debugger is only being used to fetch PDBs.
|
||||
bool should_init = !netnode(PDB_NODE_NAME).altval(PDB_LOADING_WIN32_DBG);
|
||||
if ( should_init && !win32_init_plugin() )
|
||||
return false;
|
||||
g_dbgmod.set_ioctl_handler(ioctl_handler);
|
||||
return true;
|
||||
}
|
||||
|
||||
#include "common_local_impl.cpp"
|
||||
282
idasdk76/dbg/win32/win32_undoc.h
Normal file
282
idasdk76/dbg/win32/win32_undoc.h
Normal file
@@ -0,0 +1,282 @@
|
||||
|
||||
#ifndef WIN32_UNDOC_H
|
||||
#define WIN32_UNDOC_H
|
||||
|
||||
// Definitions for Windows DDK and other undocumented MS Windows types.
|
||||
// Instead of requiring a DDK and NDK, we just use this file.
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
// WinDDK types
|
||||
|
||||
struct UNICODE_STRING
|
||||
{
|
||||
USHORT Length;
|
||||
USHORT MaximumLength;
|
||||
PWCH Buffer;
|
||||
};
|
||||
typedef UNICODE_STRING *PUNICODE_STRING;
|
||||
|
||||
extern "C" char _RTL_CONSTANT_STRING_type_check(const WCHAR *s);
|
||||
// __typeof would be desirable here instead of sizeof.
|
||||
template <size_t N> class _RTL_CONSTANT_STRING_remove_const_template_class;
|
||||
template <> class _RTL_CONSTANT_STRING_remove_const_template_class<sizeof(char)>
|
||||
{
|
||||
public:
|
||||
typedef char T;
|
||||
};
|
||||
template <> class _RTL_CONSTANT_STRING_remove_const_template_class<sizeof(WCHAR)>
|
||||
{
|
||||
public:
|
||||
typedef WCHAR T;
|
||||
};
|
||||
#define _RTL_CONSTANT_STRING_remove_const_macro(s) \
|
||||
(const_cast<_RTL_CONSTANT_STRING_remove_const_template_class<sizeof((s)[0])>::T*>(s))
|
||||
|
||||
#define RTL_CONSTANT_STRING(s) \
|
||||
{ \
|
||||
sizeof(s) - sizeof((s)[0]), \
|
||||
sizeof(s) / sizeof(_RTL_CONSTANT_STRING_type_check(s)), \
|
||||
_RTL_CONSTANT_STRING_remove_const_macro(s) \
|
||||
}
|
||||
|
||||
typedef struct _OBJECT_ATTRIBUTES64
|
||||
{
|
||||
ULONG Length;
|
||||
ULONG64 RootDirectory;
|
||||
ULONG64 ObjectName;
|
||||
ULONG Attributes;
|
||||
ULONG64 SecurityDescriptor;
|
||||
ULONG64 SecurityQualityOfService;
|
||||
} OBJECT_ATTRIBUTES64;
|
||||
typedef OBJECT_ATTRIBUTES64 *POBJECT_ATTRIBUTES64;
|
||||
typedef CONST OBJECT_ATTRIBUTES64 *PCOBJECT_ATTRIBUTES64;
|
||||
|
||||
typedef struct _OBJECT_ATTRIBUTES32
|
||||
{
|
||||
ULONG Length;
|
||||
ULONG RootDirectory;
|
||||
ULONG ObjectName;
|
||||
ULONG Attributes;
|
||||
ULONG SecurityDescriptor;
|
||||
ULONG SecurityQualityOfService;
|
||||
} OBJECT_ATTRIBUTES32;
|
||||
typedef OBJECT_ATTRIBUTES32 *POBJECT_ATTRIBUTES32;
|
||||
typedef CONST OBJECT_ATTRIBUTES32 *PCOBJECT_ATTRIBUTES32;
|
||||
|
||||
typedef struct _OBJECT_ATTRIBUTES
|
||||
{
|
||||
ULONG Length;
|
||||
HANDLE RootDirectory;
|
||||
PUNICODE_STRING ObjectName;
|
||||
ULONG Attributes;
|
||||
PVOID SecurityDescriptor; // Points to type SECURITY_DESCRIPTOR
|
||||
PVOID SecurityQualityOfService; // Points to type SECURITY_QUALITY_OF_SERVICE
|
||||
} OBJECT_ATTRIBUTES;
|
||||
typedef OBJECT_ATTRIBUTES *POBJECT_ATTRIBUTES;
|
||||
typedef CONST OBJECT_ATTRIBUTES *PCOBJECT_ATTRIBUTES;
|
||||
|
||||
#define InitializeObjectAttributes(p,n,a,r,s) \
|
||||
{ \
|
||||
(p)->Length = sizeof(OBJECT_ATTRIBUTES); \
|
||||
(p)->RootDirectory = (r); \
|
||||
(p)->Attributes = (a); \
|
||||
(p)->ObjectName = (n); \
|
||||
(p)->SecurityDescriptor = (s); \
|
||||
(p)->SecurityQualityOfService = NULL; \
|
||||
}
|
||||
|
||||
//
|
||||
// Definitions for Object Creation
|
||||
//
|
||||
#define OBJ_INHERIT 0x00000002L
|
||||
#define OBJ_PERMANENT 0x00000010L
|
||||
#define OBJ_EXCLUSIVE 0x00000020L
|
||||
#define OBJ_CASE_INSENSITIVE 0x00000040L
|
||||
#define OBJ_OPENIF 0x00000080L
|
||||
#define OBJ_OPENLINK 0x00000100L
|
||||
#define OBJ_KERNEL_HANDLE 0x00000200L
|
||||
#define OBJ_FORCE_ACCESS_CHECK 0x00000400L
|
||||
#define OBJ_VALID_ATTRIBUTES 0x000007F2L
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
// Undocumented types - pulled out of MS Windows NDK by Alex Ionecsu
|
||||
|
||||
//
|
||||
// Privilege constants
|
||||
//
|
||||
#define SE_MIN_WELL_KNOWN_PRIVILEGE (2L)
|
||||
#define SE_CREATE_TOKEN_PRIVILEGE (2L)
|
||||
#define SE_ASSIGNPRIMARYTOKEN_PRIVILEGE (3L)
|
||||
#define SE_LOCK_MEMORY_PRIVILEGE (4L)
|
||||
#define SE_INCREASE_QUOTA_PRIVILEGE (5L)
|
||||
#define SE_UNSOLICITED_INPUT_PRIVILEGE (6L)
|
||||
#define SE_MACHINE_ACCOUNT_PRIVILEGE (6L)
|
||||
#define SE_TCB_PRIVILEGE (7L)
|
||||
#define SE_SECURITY_PRIVILEGE (8L)
|
||||
#define SE_TAKE_OWNERSHIP_PRIVILEGE (9L)
|
||||
#define SE_LOAD_DRIVER_PRIVILEGE (10L)
|
||||
#define SE_SYSTEM_PROFILE_PRIVILEGE (11L)
|
||||
#define SE_SYSTEMTIME_PRIVILEGE (12L)
|
||||
#define SE_PROF_SINGLE_PROCESS_PRIVILEGE (13L)
|
||||
#define SE_INC_BASE_PRIORITY_PRIVILEGE (14L)
|
||||
#define SE_CREATE_PAGEFILE_PRIVILEGE (15L)
|
||||
#define SE_CREATE_PERMANENT_PRIVILEGE (16L)
|
||||
#define SE_BACKUP_PRIVILEGE (17L)
|
||||
#define SE_RESTORE_PRIVILEGE (18L)
|
||||
#define SE_SHUTDOWN_PRIVILEGE (19L)
|
||||
#define SE_DEBUG_PRIVILEGE (20L)
|
||||
#define SE_AUDIT_PRIVILEGE (21L)
|
||||
#define SE_SYSTEM_ENVIRONMENT_PRIVILEGE (22L)
|
||||
#define SE_CHANGE_NOTIFY_PRIVILEGE (23L)
|
||||
#define SE_REMOTE_SHUTDOWN_PRIVILEGE (24L)
|
||||
#define SE_MAX_WELL_KNOWN_PRIVILEGE (SE_REMOTE_SHUTDOWN_PRIVILEGE)
|
||||
|
||||
|
||||
// Undocumented NTSystemDebugControl function (to read/write MSRs)
|
||||
enum SYSDBG_COMMAND
|
||||
{
|
||||
SysDbgQueryModuleInformation = 1,
|
||||
SysDbgQueryTraceInformation = 2,
|
||||
SysDbgSetTracepoint = 3,
|
||||
SysDbgSetSpecialCall = 4,
|
||||
SysDbgClearSpecialCalls = 5,
|
||||
SysDbgQuerySpecialCalls = 6,
|
||||
SysDbgReadMsr = 16,
|
||||
SysDbgWriteMsr = 17,
|
||||
};
|
||||
|
||||
struct SYSDBG_MSR
|
||||
{
|
||||
uint32 reg;
|
||||
uint32 padding;
|
||||
uint64 value;
|
||||
};
|
||||
#define NTSTATUS int
|
||||
#ifndef NTAPI
|
||||
#define NTAPI WINAPI
|
||||
#endif
|
||||
|
||||
#ifndef STATUS_NOT_IMPLEMENTED
|
||||
#define STATUS_NOT_IMPLEMENTED 0xC0000002
|
||||
#endif
|
||||
|
||||
#ifndef STATUS_IMAGE_ALREADY_LOADED
|
||||
#define STATUS_IMAGE_ALREADY_LOADED 0xC000010E
|
||||
#endif
|
||||
|
||||
//
|
||||
// NtCreateFile OpenType Flags
|
||||
//
|
||||
#define FILE_SUPERSEDE 0x00000000
|
||||
#define FILE_OPEN 0x00000001
|
||||
#define FILE_CREATE 0x00000002
|
||||
#define FILE_OPEN_IF 0x00000003
|
||||
#define FILE_OVERWRITE 0x00000004
|
||||
#define FILE_OVERWRITE_IF 0x00000005
|
||||
#define FILE_MAXIMUM_DISPOSITION 0x00000005
|
||||
|
||||
//
|
||||
// NtCreateFile Flags
|
||||
//
|
||||
#define FILE_DIRECTORY_FILE 0x00000001
|
||||
#define FILE_WRITE_THROUGH 0x00000002
|
||||
#define FILE_SEQUENTIAL_ONLY 0x00000004
|
||||
#define FILE_NO_INTERMEDIATE_BUFFERING 0x00000008
|
||||
#define FILE_SYNCHRONOUS_IO_ALERT 0x00000010
|
||||
#define FILE_SYNCHRONOUS_IO_NONALERT 0x00000020
|
||||
#define FILE_NON_DIRECTORY_FILE 0x00000040
|
||||
#define FILE_CREATE_TREE_CONNECTION 0x00000080
|
||||
#define FILE_COMPLETE_IF_OPLOCKED 0x00000100
|
||||
#define FILE_NO_EA_KNOWLEDGE 0x00000200
|
||||
#define FILE_OPEN_FOR_RECOVERY 0x00000400
|
||||
#define FILE_RANDOM_ACCESS 0x00000800
|
||||
#define FILE_DELETE_ON_CLOSE 0x00001000
|
||||
#define FILE_OPEN_BY_FILE_ID 0x00002000
|
||||
#define FILE_OPEN_FOR_BACKUP_INTENT 0x00004000
|
||||
#define FILE_NO_COMPRESSION 0x00008000
|
||||
#define FILE_RESERVE_OPFILTER 0x00100000
|
||||
#define FILE_OPEN_REPARSE_POINT 0x00200000
|
||||
#define FILE_OPEN_NO_RECALL 0x00400000
|
||||
#define FILE_OPEN_FOR_FREE_SPACE_QUERY 0x00800000
|
||||
|
||||
//
|
||||
// Interface for communicating with private WinDBG interface
|
||||
//
|
||||
#define KLDD_CODE_DEBUG_CONTROL \
|
||||
CTL_CODE(FILE_DEVICE_UNKNOWN, 1, METHOD_NEITHER, FILE_READ_ACCESS | FILE_WRITE_ACCESS)
|
||||
|
||||
typedef struct _KLDD_DATA_DEBUG_CONTROL
|
||||
{
|
||||
SYSDBG_COMMAND Command;
|
||||
PVOID InputBuffer;
|
||||
SIZE_T InputBufferLength;
|
||||
} KLDD_DATA_DEBUG_CONTROL, *PKLDD_DATA_DEBUG_CONTROL;
|
||||
|
||||
//
|
||||
// I/O Status Block
|
||||
//
|
||||
typedef struct _IO_STATUS_BLOCK
|
||||
{
|
||||
union
|
||||
{
|
||||
NTSTATUS Status;
|
||||
PVOID Pointer;
|
||||
};
|
||||
ULONG_PTR Information;
|
||||
} IO_STATUS_BLOCK, *PIO_STATUS_BLOCK;
|
||||
|
||||
//
|
||||
// APC Callback for NtDeviceIoControlFile
|
||||
//
|
||||
typedef VOID
|
||||
(NTAPI *PIO_APC_ROUTINE)(
|
||||
IN PVOID ApcContext,
|
||||
IN PIO_STATUS_BLOCK IoStatusBlock,
|
||||
IN ULONG Reserved);
|
||||
|
||||
typedef NTSTATUS NTAPI NtSystemDebugControl_t(
|
||||
IN SYSDBG_COMMAND Command,
|
||||
IN PVOID InputBuffer OPTIONAL,
|
||||
IN ULONG InputBufferLength,
|
||||
OUT PVOID OutputBuffer OPTIONAL,
|
||||
IN ULONG OutputBufferLength,
|
||||
OUT PULONG ReturnLength OPTIONAL);
|
||||
|
||||
typedef NTSTATUS NTAPI NtLoadDriver_t(
|
||||
IN PUNICODE_STRING DriverServiceName);
|
||||
|
||||
typedef NTSTATUS NTAPI NtUnloadDriver_t(
|
||||
IN PUNICODE_STRING DriverServiceName);
|
||||
|
||||
typedef NTSTATUS NTAPI RtlAdjustPrivilege_t(
|
||||
IN ULONG Privilege,
|
||||
IN BOOLEAN NewValue,
|
||||
IN BOOLEAN ForThread,
|
||||
OUT PBOOLEAN OldValue);
|
||||
|
||||
typedef NTSTATUS NTAPI NtCreateFile_t(
|
||||
OUT PHANDLE FileHandle,
|
||||
IN ACCESS_MASK DesiredAccess,
|
||||
IN POBJECT_ATTRIBUTES ObjectAttributes,
|
||||
OUT PIO_STATUS_BLOCK IoStatusBlock,
|
||||
IN PLARGE_INTEGER AllocationSize OPTIONAL,
|
||||
IN ULONG FileAttributes,
|
||||
IN ULONG ShareAccess,
|
||||
IN ULONG CreateDisposition,
|
||||
IN ULONG CreateOptions,
|
||||
IN PVOID EaBuffer OPTIONAL,
|
||||
IN ULONG EaLength);
|
||||
|
||||
typedef NTSTATUS NTAPI NtDeviceIoControlFile_t(
|
||||
IN HANDLE DeviceHandle,
|
||||
IN HANDLE Event OPTIONAL,
|
||||
IN PIO_APC_ROUTINE UserApcRoutine OPTIONAL,
|
||||
IN PVOID UserApcContext OPTIONAL,
|
||||
OUT PIO_STATUS_BLOCK IoStatusBlock,
|
||||
IN ULONG IoControlCode,
|
||||
IN PVOID InputBuffer,
|
||||
IN ULONG InputBufferSize,
|
||||
OUT PVOID OutputBuffer,
|
||||
IN ULONG OutputBufferSize);
|
||||
|
||||
#endif // define WIN32_UNDOC_H
|
||||
46
idasdk76/dbg/win32/win32_user.cpp
Normal file
46
idasdk76/dbg/win32/win32_user.cpp
Normal file
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
This is main source code for the local win32 debugger module
|
||||
*/
|
||||
|
||||
static const char wanted_name[] = "Local Windows debugger";
|
||||
|
||||
#define DEBUGGER_NAME "win32"
|
||||
#define PROCESSOR_NAME "metapc"
|
||||
#define TARGET_PROCESSOR PLFM_386
|
||||
#define DEBUGGER_ID DEBUGGER_ID_X86_IA32_WIN32_USER
|
||||
#define DEBUGGER_FLAGS (DBG_FLAG_EXITSHOTOK \
|
||||
| DBG_FLAG_LOWCNDS \
|
||||
| DBG_FLAG_DEBTHREAD \
|
||||
| DBG_FLAG_ANYSIZE_HWBPT)
|
||||
#define DEBUGGER_RESMOD (DBG_RESMOD_STEP_INTO)
|
||||
|
||||
#define HAVE_APPCALL
|
||||
#define S_FILETYPE f_PE
|
||||
|
||||
// We must rename those method because common files
|
||||
// refer to them as init_plugin/term_plugin
|
||||
// Some other debugger modules compatible with win32
|
||||
// have their own init/term and still call win32_init/term
|
||||
// (since no renaming takes place)
|
||||
#define win32_init_plugin init_plugin
|
||||
#define win32_term_plugin term_plugin
|
||||
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#include <windows.h>
|
||||
#include <objidl.h>
|
||||
|
||||
#include <fpro.h>
|
||||
#include <ua.hpp>
|
||||
#include <idd.hpp>
|
||||
#include <loader.hpp>
|
||||
#include "win32_debmod.h"
|
||||
#include "w32sehch.h"
|
||||
|
||||
win32_debmod_t g_dbgmod;
|
||||
#include "common_stub_impl.cpp"
|
||||
|
||||
#include "pc_local_impl.cpp"
|
||||
#include "win32_local_impl.cpp"
|
||||
#include "common_local_impl.cpp"
|
||||
|
||||
#include "win32_server_stub.cpp"
|
||||
87
idasdk76/dbg/win32/win32_util.cpp
Normal file
87
idasdk76/dbg/win32/win32_util.cpp
Normal file
@@ -0,0 +1,87 @@
|
||||
#include <windows.h>
|
||||
#include <pro.h>
|
||||
#include "winbase_debmod.h"
|
||||
#include "win32_util.hpp"
|
||||
|
||||
#ifndef __X86__
|
||||
typedef BOOL WINAPI IsWow64Process_t(HANDLE, PBOOL);
|
||||
static IsWow64Process_t *_IsWow64Process = NULL;
|
||||
#endif
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
//lint -esym(818,handle) could be pointer to const
|
||||
wow64_state_t check_wow64_handle(HANDLE handle)
|
||||
{
|
||||
#ifdef __X86__
|
||||
qnotused(handle);
|
||||
#else
|
||||
if ( _IsWow64Process == NULL )
|
||||
{
|
||||
HMODULE k32 = GetModuleHandle(kernel32_dll);
|
||||
*(FARPROC*)&_IsWow64Process = GetProcAddress(k32, TEXT("IsWow64Process"));
|
||||
if ( _IsWow64Process == NULL )
|
||||
return WOW64_NONE; // unknown
|
||||
}
|
||||
BOOL bIsWow64 = FALSE;
|
||||
if ( _IsWow64Process(handle, &bIsWow64) && bIsWow64 != 0 )
|
||||
return WOW64_YES;
|
||||
#endif
|
||||
return WOW64_NO;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
wow64_state_t check_wow64_pid(int pid)
|
||||
{
|
||||
wow64_state_t r = WOW64_BAD; // assume pid is bad
|
||||
HANDLE h = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, pid);
|
||||
if ( h != NULL )
|
||||
{
|
||||
r = check_wow64_handle(h);
|
||||
CloseHandle(h);
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
#ifdef _MSC_VER
|
||||
# pragma warning(push)
|
||||
# pragma warning(disable: 4996) // GetVersionEx was declared deprecated
|
||||
#endif
|
||||
win_version_t::win_version_t()
|
||||
{
|
||||
OSVersionInfo.dwOSVersionInfoSize = sizeof(OSVersionInfo);
|
||||
ver_ok = GetVersionEx(&OSVersionInfo) != 0; //lint !e2586 is deprecated
|
||||
#ifdef __X86__
|
||||
is_64bit_os = false;
|
||||
if ( OSVersionInfo.dwMajorVersion > 5
|
||||
|| OSVersionInfo.dwMajorVersion == 5 && OSVersionInfo.dwMinorVersion >= 1 )
|
||||
{
|
||||
is_64bit_os = check_wow64_handle(GetCurrentProcess()) > 0;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
#ifdef _MSC_VER
|
||||
# pragma warning(pop)
|
||||
#endif
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
win_tool_help_t::win_tool_help_t()
|
||||
{
|
||||
use_debug_break = qgetenv("IDA_DEBUGBREAKPROCESS");
|
||||
HMODULE kern_handle = GetModuleHandle(kernel32_dll);
|
||||
*(FARPROC*)&_DebugActiveProcessStop = GetProcAddress(kern_handle, TEXT("DebugActiveProcessStop"));
|
||||
*(FARPROC*)&_DebugBreakProcess = GetProcAddress(kern_handle, TEXT("DebugBreakProcess"));
|
||||
|
||||
// find the needed functions
|
||||
*(FARPROC*)&_CreateToolhelp32Snapshot = GetProcAddress(kern_handle, TEXT("CreateToolhelp32Snapshot"));
|
||||
*(FARPROC*)&_Process32First = GetProcAddress(kern_handle, TEXT("Process32First"));
|
||||
*(FARPROC*)&_Process32Next = GetProcAddress(kern_handle, TEXT("Process32Next"));
|
||||
*(FARPROC*)&_Module32First = GetProcAddress(kern_handle, TEXT("Module32First"));
|
||||
*(FARPROC*)&_Module32Next = GetProcAddress(kern_handle, TEXT("Module32Next"));
|
||||
|
||||
inited = _CreateToolhelp32Snapshot != NULL
|
||||
&& _Process32First != NULL
|
||||
&& _Process32Next != NULL
|
||||
&& _Module32First != NULL
|
||||
&& _Module32Next != NULL;
|
||||
}
|
||||
347
idasdk76/dbg/win32/win32_util.hpp
Normal file
347
idasdk76/dbg/win32/win32_util.hpp
Normal file
@@ -0,0 +1,347 @@
|
||||
//
|
||||
// Wrapper for Windows ToolHelp library: enumerate processes/modules
|
||||
//
|
||||
// PSAPI.DLL: NT, 2K, XP/2K3
|
||||
// KERNEL32.DLL (ToolHelp functions): 9X, ME, 2K, XP/2K3
|
||||
//? add NT support
|
||||
|
||||
#ifndef __TOOLHELP_HPP__
|
||||
#define __TOOLHELP_HPP__
|
||||
|
||||
#ifdef __NT__
|
||||
|
||||
#include <windows.h>
|
||||
#include <Tlhelp32.h>
|
||||
|
||||
#include <dbghelp.h>
|
||||
|
||||
#include <segment.hpp>
|
||||
|
||||
#ifdef UNICODE
|
||||
#define LookupPrivilegeValue_Name "LookupPrivilegeValueW"
|
||||
#else
|
||||
#define LookupPrivilegeValue_Name "LookupPrivilegeValueA"
|
||||
#endif
|
||||
|
||||
#ifndef TH32CS_SNAPNOHEAPS
|
||||
# define TH32CS_SNAPNOHEAPS 0x0
|
||||
#endif
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
enum wow64_state_t
|
||||
{
|
||||
WOW64_NONE = -2, // unknown yet
|
||||
WOW64_BAD = -1,
|
||||
WOW64_NO = 0,
|
||||
WOW64_YES = 1,
|
||||
};
|
||||
|
||||
wow64_state_t check_wow64_handle(HANDLE handle);
|
||||
wow64_state_t check_wow64_pid(int pid);
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
class win_version_t
|
||||
{
|
||||
public:
|
||||
win_version_t();
|
||||
const OSVERSIONINFO &get_info() const { return OSVersionInfo; }
|
||||
bool ok() { return ver_ok; }
|
||||
bool is_NT()
|
||||
{
|
||||
return ok() && OSVersionInfo.dwPlatformId == VER_PLATFORM_WIN32_NT;
|
||||
}
|
||||
bool is_strictly_xp() // Is strictly XP (32bit)?
|
||||
{
|
||||
return ok()
|
||||
&& is_NT()
|
||||
&& OSVersionInfo.dwMajorVersion == 5
|
||||
&& OSVersionInfo.dwMinorVersion == 1;
|
||||
}
|
||||
bool is_DW32()
|
||||
{
|
||||
return ok() && OSVersionInfo.dwPlatformId == 3;
|
||||
}
|
||||
bool is_2K() // Is at least Win2K?
|
||||
{
|
||||
return ok() && OSVersionInfo.dwMajorVersion >= 5;
|
||||
}
|
||||
bool is_64bitOS()
|
||||
{
|
||||
#ifndef __X86__
|
||||
return true;
|
||||
#else
|
||||
return is_64bit_os;
|
||||
#endif
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// GetProcessDEPPolicy() is broken for Win8, Win8.1, Win10:
|
||||
// always set *lpPermanent == CL register, if not permanently policy.
|
||||
// https://social.msdn.microsoft.com/Forums/windowsdesktop/en-US/05683d76-3c8a-49de-91e3-9d7ab8492f39/getprocessdeppolicy-does-not-set-the-correct-value-to-lppermanent?forum=windowscompatibility
|
||||
bool is_GetProcessDEPPolicy_broken()
|
||||
{
|
||||
return ok()
|
||||
&& ((OSVersionInfo.dwMajorVersion == 10 && OSVersionInfo.dwMinorVersion == 0) // Windows 10
|
||||
|| (OSVersionInfo.dwMajorVersion == 6 && OSVersionInfo.dwMinorVersion == 3) // Windows 8.1
|
||||
|| (OSVersionInfo.dwMajorVersion == 6 && OSVersionInfo.dwMinorVersion == 2)); // Windows 8
|
||||
}
|
||||
|
||||
private:
|
||||
OSVERSIONINFO OSVersionInfo;
|
||||
bool ver_ok;
|
||||
#ifdef __X86__
|
||||
bool is_64bit_os;
|
||||
#endif
|
||||
};
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
//-V:win_tool_help_t:730 Not all members of a class are initialized inside the constructor
|
||||
class win_tool_help_t
|
||||
{
|
||||
public:
|
||||
win_tool_help_t();
|
||||
bool ok() { return inited; }
|
||||
bool use_debug_break_process();
|
||||
bool debug_break_process(HANDLE process_handle);
|
||||
bool use_debug_detach_process();
|
||||
bool debug_detach_process(pid_t pid);
|
||||
|
||||
private:
|
||||
// function prototypes
|
||||
typedef HANDLE WINAPI CreateToolhelp32Snapshot_t(DWORD dwFlags, DWORD th32ProcessID);
|
||||
typedef BOOL WINAPI Process32First_t(HANDLE hSnapshot, LPPROCESSENTRY32 lppe);
|
||||
typedef BOOL WINAPI Process32Next_t(HANDLE hSnapshot, LPPROCESSENTRY32 lppe);
|
||||
typedef BOOL WINAPI Module32First_t(HANDLE hSnapshot, LPMODULEENTRY32 lpme);
|
||||
typedef BOOL WINAPI Module32Next_t(HANDLE hSnapshot, LPMODULEENTRY32 lpme);
|
||||
typedef BOOL WINAPI DebugActiveProcessStop_t(DWORD dwProcessID);
|
||||
typedef BOOL WINAPI DebugBreakProcess_t(HANDLE Process);
|
||||
typedef BOOL WINAPI CloseToolhelp32Snapshot_t(HANDLE hSnapshot);
|
||||
|
||||
// functions pointers
|
||||
CreateToolhelp32Snapshot_t *_CreateToolhelp32Snapshot;
|
||||
Process32First_t *_Process32First;
|
||||
Process32Next_t *_Process32Next;
|
||||
Module32First_t *_Module32First;
|
||||
Module32Next_t *_Module32Next;
|
||||
DebugActiveProcessStop_t *_DebugActiveProcessStop;
|
||||
DebugBreakProcess_t *_DebugBreakProcess;
|
||||
|
||||
bool inited;
|
||||
bool use_debug_break;
|
||||
|
||||
friend class toolhelp_snapshot_t;
|
||||
friend class process_snapshot_t;
|
||||
friend class module_snapshot_t;
|
||||
};
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
class toolhelp_snapshot_t
|
||||
{
|
||||
public:
|
||||
inline toolhelp_snapshot_t(win_tool_help_t *tool);
|
||||
inline ~toolhelp_snapshot_t();
|
||||
inline bool ok();
|
||||
inline bool open(uint32 flags, pid_t pid);
|
||||
inline void close();
|
||||
inline uint32 last_err();
|
||||
protected:
|
||||
bool seterr(); // always returns 'false' for convenience
|
||||
win_tool_help_t *t;
|
||||
HANDLE h;
|
||||
uint32 last_error;
|
||||
};
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
class process_snapshot_t: public toolhelp_snapshot_t
|
||||
{
|
||||
public:
|
||||
inline process_snapshot_t(win_tool_help_t *tool);
|
||||
inline bool first(uint32 flags, LPPROCESSENTRY32 lppe);
|
||||
inline bool next(LPPROCESSENTRY32 lppe);
|
||||
};
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
class module_snapshot_t: public toolhelp_snapshot_t
|
||||
{
|
||||
public:
|
||||
inline module_snapshot_t(win_tool_help_t *tool);
|
||||
inline bool first(uint32 flags, pid_t pid, LPMODULEENTRY32 lpme);
|
||||
inline bool next(LPMODULEENTRY32 lpme);
|
||||
};
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
inline bool win_tool_help_t::use_debug_break_process()
|
||||
{
|
||||
return use_debug_break && _DebugBreakProcess != NULL;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
inline bool win_tool_help_t::debug_break_process(HANDLE process_handle)
|
||||
{
|
||||
return process_handle != INVALID_HANDLE_VALUE && _DebugBreakProcess(process_handle);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
inline bool win_tool_help_t::use_debug_detach_process()
|
||||
{
|
||||
return _DebugActiveProcessStop != NULL;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
inline bool win_tool_help_t::debug_detach_process(pid_t pid)
|
||||
{
|
||||
return _DebugActiveProcessStop != NULL && _DebugActiveProcessStop(pid);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
inline process_snapshot_t::process_snapshot_t(win_tool_help_t *tool)
|
||||
: toolhelp_snapshot_t(tool)
|
||||
{
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
inline bool process_snapshot_t::first(uint32 flags, LPPROCESSENTRY32 lppe)
|
||||
{
|
||||
open(TH32CS_SNAPPROCESS | flags, 0);
|
||||
lppe->dwSize = sizeof(PROCESSENTRY32);
|
||||
if ( ok() && t->_Process32First(h, lppe) )
|
||||
{
|
||||
// ignore "System Process" (ID==0)
|
||||
return lppe->th32ProcessID != 0 || next(lppe);
|
||||
}
|
||||
return seterr();
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
inline bool process_snapshot_t::next(LPPROCESSENTRY32 lppe)
|
||||
{
|
||||
while ( ok() )
|
||||
{
|
||||
if ( !t->_Process32Next(h, lppe) )
|
||||
break;
|
||||
// ignore "System Process" (ID==0)
|
||||
if ( lppe->th32ProcessID != 0 )
|
||||
return true;
|
||||
}
|
||||
return seterr();
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
inline module_snapshot_t::module_snapshot_t(win_tool_help_t *tool)
|
||||
: toolhelp_snapshot_t(tool)
|
||||
{
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
inline bool module_snapshot_t::first(uint32 flags, pid_t pid, LPMODULEENTRY32 lpme)
|
||||
{
|
||||
wow64_state_t state = check_wow64_pid(pid);
|
||||
flags |= state == WOW64_YES ? TH32CS_SNAPMODULE32 : 0;
|
||||
if ( !open(TH32CS_SNAPMODULE | flags, pid) )
|
||||
return false;
|
||||
lpme->dwSize = sizeof(MODULEENTRY32);
|
||||
if ( t->_Module32First(h, lpme) )
|
||||
return true;
|
||||
return seterr();
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
inline bool module_snapshot_t::next(LPMODULEENTRY32 lpme)
|
||||
{
|
||||
if ( ok() )
|
||||
return false;
|
||||
if ( t->_Module32Next(h, lpme) )
|
||||
return true;
|
||||
seterr();
|
||||
return false;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
inline toolhelp_snapshot_t::toolhelp_snapshot_t(win_tool_help_t *tool)
|
||||
: t(tool), h(INVALID_HANDLE_VALUE), last_error(0)
|
||||
{
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
inline toolhelp_snapshot_t::~toolhelp_snapshot_t()
|
||||
{
|
||||
close();
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
inline bool toolhelp_snapshot_t::ok()
|
||||
{
|
||||
return h != INVALID_HANDLE_VALUE;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// // always returns 'false' for convenience
|
||||
inline bool toolhelp_snapshot_t::seterr()
|
||||
{
|
||||
last_error = GetLastError();
|
||||
return false;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// // always returns 'false' for convenience
|
||||
inline uint32 toolhelp_snapshot_t::last_err()
|
||||
{
|
||||
return last_error;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
inline bool toolhelp_snapshot_t::open(uint32 flags, pid_t pid)
|
||||
{
|
||||
if ( !t->ok() )
|
||||
return false;
|
||||
close();
|
||||
for ( int cnt=0; cnt < 5; cnt++ )
|
||||
{
|
||||
h = t->_CreateToolhelp32Snapshot(flags, pid);
|
||||
if ( h != INVALID_HANDLE_VALUE )
|
||||
return true;
|
||||
seterr();
|
||||
// MSDN: If the function fails with ERROR_BAD_LENGTH, retry
|
||||
// the function until it succeeds.
|
||||
if ( last_err() != ERROR_BAD_LENGTH
|
||||
|| (flags & (TH32CS_SNAPMODULE | TH32CS_SNAPMODULE32)) == 0 )
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
inline void toolhelp_snapshot_t::close()
|
||||
{
|
||||
if ( t->ok() && h != INVALID_HANDLE_VALUE )
|
||||
CloseHandle(h);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// convert Windows protection modes to IDA protection modes
|
||||
inline uchar win_prot_to_ida_perm(DWORD protection)
|
||||
{
|
||||
uchar perm = 0;
|
||||
|
||||
if ( protection & PAGE_READONLY )
|
||||
perm |= SEGPERM_READ;
|
||||
if ( protection & PAGE_READWRITE )
|
||||
perm |= SEGPERM_READ | SEGPERM_WRITE;
|
||||
if ( protection & PAGE_WRITECOPY )
|
||||
perm |= SEGPERM_READ | SEGPERM_WRITE;
|
||||
if ( protection & PAGE_EXECUTE )
|
||||
perm |= SEGPERM_EXEC;
|
||||
if ( protection & PAGE_EXECUTE_READ )
|
||||
perm |= SEGPERM_READ | SEGPERM_EXEC;
|
||||
if ( protection & PAGE_EXECUTE_READWRITE )
|
||||
perm |= SEGPERM_READ | SEGPERM_WRITE | SEGPERM_EXEC;
|
||||
if ( protection & PAGE_EXECUTE_WRITECOPY )
|
||||
perm |= SEGPERM_READ | SEGPERM_WRITE | SEGPERM_EXEC;
|
||||
|
||||
return perm;
|
||||
}
|
||||
|
||||
#endif // __NT__
|
||||
#endif // __TOOLHELP_HPP__
|
||||
633
idasdk76/dbg/win32/winbase_debmod.cpp
Normal file
633
idasdk76/dbg/win32/winbase_debmod.cpp
Normal file
@@ -0,0 +1,633 @@
|
||||
#include <windows.h>
|
||||
#include <ida.hpp>
|
||||
#include "winbase_debmod.h"
|
||||
|
||||
#ifndef __X86__
|
||||
#define IDA_ADDRESS_SIZE 8
|
||||
#else
|
||||
#define IDA_ADDRESS_SIZE 4
|
||||
#endif
|
||||
|
||||
const TCHAR kernel32_dll[] = TEXT("kernel32.dll");
|
||||
|
||||
typedef BOOL WINAPI GetProcessDEPPolicy_t(HANDLE hProcess, LPDWORD lpFlags, PBOOL lpPermanent);
|
||||
static GetProcessDEPPolicy_t *_GetProcessDEPPolicy = NULL;
|
||||
|
||||
typedef dep_policy_t WINAPI GetSystemDEPPolicy_t(void);
|
||||
static GetSystemDEPPolicy_t *_GetSystemDEPPolicy = NULL;
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
winbase_debmod_t::winbase_debmod_t(void)
|
||||
{
|
||||
HMODULE k32 = GetModuleHandle(kernel32_dll);
|
||||
|
||||
if ( _GetProcessDEPPolicy == NULL )
|
||||
*(FARPROC*)&_GetProcessDEPPolicy = GetProcAddress(k32, TEXT("GetProcessDEPPolicy"));
|
||||
|
||||
if ( _GetSystemDEPPolicy == NULL )
|
||||
*(FARPROC*)&_GetSystemDEPPolicy = GetProcAddress(k32, TEXT("GetSystemDEPPolicy"));
|
||||
|
||||
if ( _GetSystemDEPPolicy != NULL )
|
||||
dep_policy = _GetSystemDEPPolicy();
|
||||
|
||||
win_tool_help = NULL;
|
||||
set_platform("win32");
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// Prepare new page protections for a breakpoint of BPTTYPE.
|
||||
// Use INPUT as starting page protections.
|
||||
// Return false in the case of failure.
|
||||
bool winbase_debmod_t::remove_page_protections(
|
||||
DWORD *p_input,
|
||||
bpttype_t bpttype,
|
||||
dep_policy_t dpolicy,
|
||||
HANDLE proc_handle)
|
||||
{
|
||||
// If PAGE_GUARD is already set, do not change anything, it is already ok
|
||||
DWORD input = *p_input;
|
||||
if ( (input & PAGE_GUARD) != 0 )
|
||||
return false;
|
||||
|
||||
// Convert between Unix permissions and Win32 page protections using this array:
|
||||
static const uchar win32_page_protections[] =
|
||||
{
|
||||
PAGE_NOACCESS, // 000
|
||||
PAGE_READONLY, // 001
|
||||
0xFF, // 010 WRITE_ONLY does not exist on win32
|
||||
PAGE_READWRITE, // 011
|
||||
PAGE_EXECUTE, // 100
|
||||
PAGE_EXECUTE_READ, // 101
|
||||
0xFF, // 110 EXECUTE_WRITE does not exist on win32
|
||||
PAGE_EXECUTE_READWRITE, // 111
|
||||
};
|
||||
uchar unix;
|
||||
// convert ..COPY page protections into their non-copy counterparts
|
||||
// this is the best thing we can do with them because they are automatically
|
||||
// converted by the system upon a write access
|
||||
if ( (input & PAGE_WRITECOPY) != 0 )
|
||||
{
|
||||
unix = 3; // rw
|
||||
}
|
||||
else if ( (input & PAGE_EXECUTE_WRITECOPY) != 0 )
|
||||
{
|
||||
unix = 7; // rwx
|
||||
}
|
||||
else
|
||||
{
|
||||
for ( unix=0; unix < 8; unix++ )
|
||||
{
|
||||
uchar p = win32_page_protections[unix];
|
||||
if ( p != 0xFF && (input & p) != 0 )
|
||||
break;
|
||||
}
|
||||
}
|
||||
QASSERT(622, unix < 8);
|
||||
|
||||
// convert bpttype into unix permissions
|
||||
int del = 0;
|
||||
if ( (bpttype & BPT_READ) != 0 )
|
||||
del |= 1;
|
||||
if ( (bpttype & BPT_WRITE) != 0 )
|
||||
del |= 2;
|
||||
if ( (bpttype & BPT_EXEC) != 0 )
|
||||
{
|
||||
del |= 4;
|
||||
// if DEP is disabled for this process then a program can
|
||||
// happily execute code in a read only area so we need to
|
||||
// remove *all* privileges, unfortunately
|
||||
if ( dpolicy != dp_always_on )
|
||||
{
|
||||
// on XP, GetProcessDEPPolicy returns DEP policy for current process (i.e. the debugger)
|
||||
// so we can't use it
|
||||
// assume that DEP is disabled by default
|
||||
DWORD flags = 0;
|
||||
BOOL permanent = 0;
|
||||
if ( _GetProcessDEPPolicy == NULL
|
||||
|| winver.is_strictly_xp()
|
||||
|| winver.is_GetProcessDEPPolicy_broken()
|
||||
|| _GetProcessDEPPolicy(proc_handle, &flags, &permanent) )
|
||||
{
|
||||
// flags == 0: DEP is disabled for the specified process.
|
||||
//
|
||||
// Remarks: if permanent == 0 and global DEP policy is OptIn
|
||||
// flags may be equal to 1 *but* having DEP disabled because,
|
||||
// in case the process called SetProcessDEPPolicy the
|
||||
// permanent argument would be 1, it seems to be a bug in the
|
||||
// documentation
|
||||
if ( (dpolicy == dp_opt_in && permanent == 0) || flags == 0 )
|
||||
del |= 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Remove the access types to trigger on
|
||||
unix &= ~del;
|
||||
|
||||
// Handle WRITE_ONLY and EXECUTE_WRITE cases because win32 does not have them.
|
||||
// We use stricter page permissions for them. This means that there will
|
||||
// be more useless exceptions but we cannot do much about it.
|
||||
if ( unix == 2 || unix == 6 )
|
||||
unix = 0; // use PAGE_NOACCESS instead of WRITE_ONLY or EXECUTE_WRITE
|
||||
|
||||
uchar perm = win32_page_protections[unix];
|
||||
*p_input = (input & ~0xFF) | perm;
|
||||
return true;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
bool idaapi winbase_debmod_t::dbg_enable_page_bpt(
|
||||
page_bpts_t::iterator p,
|
||||
bool enable)
|
||||
{
|
||||
pagebpt_data_t &bpt = p->second;
|
||||
if ( (bpt.old_prot != 0) == enable )
|
||||
return false; // already the desired state
|
||||
|
||||
debdeb("dbg_enable_page_bpt(%s): page_ea=%a, old_prot=0x%x, new_prot=0x%x\n", enable ? "true" : "false", bpt.page_ea, bpt.old_prot, bpt.new_prot);
|
||||
|
||||
DWORD old;
|
||||
DWORD prot = enable ? bpt.new_prot : bpt.old_prot;
|
||||
if ( !VirtualProtectEx(process_handle, (void*)(size_t)bpt.page_ea,
|
||||
bpt.real_len, prot, &old) )
|
||||
{
|
||||
deberr("VirtualProtectEx");
|
||||
// if the page disappeared while disabling a bpt, do not complain,
|
||||
// silently return success
|
||||
if ( enable )
|
||||
return false;
|
||||
old = 0;
|
||||
}
|
||||
|
||||
debdeb(" success! old=0x%x\n", old);
|
||||
|
||||
bpt.old_prot = enable ? old : 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// Should we generate a BREAKPOINT event because of page bpt?
|
||||
//lint -e{1746} could be made const reference
|
||||
bool should_fire_page_bpt(
|
||||
page_bpts_t::iterator p,
|
||||
ea_t ea,
|
||||
DWORD failed_access_type,
|
||||
ea_t pc,
|
||||
dep_policy_t dep_policy)
|
||||
{
|
||||
const pagebpt_data_t &bpt = p->second;
|
||||
if ( !interval::contains(bpt.ea, bpt.user_len, ea) )
|
||||
return false; // not in the user-defined interval
|
||||
|
||||
int bit;
|
||||
switch ( failed_access_type )
|
||||
{
|
||||
default:
|
||||
INTERR(623); //-V796 no break
|
||||
case EXCEPTION_READ_FAULT: // failed READ access
|
||||
// depending on the DEP policy we mark this access also
|
||||
// to be triggered in case of EXEC breakpoints
|
||||
bit = BPT_READ;
|
||||
if ( dep_policy != dp_always_on && bpt.type == BPT_EXEC && pc == ea )
|
||||
bit |= BPT_EXEC;
|
||||
break;
|
||||
case EXCEPTION_WRITE_FAULT: // failed WRITE access
|
||||
bit = BPT_WRITE;
|
||||
break;
|
||||
case EXCEPTION_EXECUTE_FAULT: // failed EXECUTE access
|
||||
bit = BPT_EXEC;
|
||||
break;
|
||||
}
|
||||
return (bpt.type & bit) != 0;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// returns 0-failure, 2-success
|
||||
int idaapi winbase_debmod_t::dbg_add_page_bpt(
|
||||
bpttype_t type,
|
||||
ea_t ea,
|
||||
int size)
|
||||
{
|
||||
// only one page breakpoint per page is permitted
|
||||
page_bpts_t::iterator p = find_page_bpt(ea, size);
|
||||
if ( p != page_bpts.end() )
|
||||
return 0; // another page bpt exists
|
||||
|
||||
// Find out the current page protections
|
||||
MEMORY_BASIC_INFORMATION meminfo;
|
||||
ea_t page_ea = calc_page_base(ea);
|
||||
if ( !VirtualQueryEx(process_handle, (void *)(size_t)page_ea,
|
||||
&meminfo, sizeof(meminfo)) )
|
||||
{
|
||||
deberr("VirtualQueryEx");
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Make sure the page is loaded
|
||||
if ( (meminfo.State & MEM_FREE) != 0 )
|
||||
{
|
||||
deberr("%a: the page has not been allocated", page_ea);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// According to MSDN documentation for VirtualQueryEx
|
||||
// (...)
|
||||
// AllocationProtect
|
||||
// The memory protection option when the region was initially allocated. This member can be
|
||||
// one of the memory protection constants or 0 if the caller does not have access.
|
||||
//
|
||||
// Unfortunately, there is no more information about why it my happen so, for now, I'm just
|
||||
// returning an error.
|
||||
if ( meminfo.Protect == 0 )
|
||||
{
|
||||
deberr("%a: the page cannot be accessed", page_ea);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Calculate new page protections
|
||||
int aligned_len = align_up((ea-page_ea)+size, MEMORY_PAGE_SIZE);
|
||||
int real_len = 0;
|
||||
DWORD prot = meminfo.Protect;
|
||||
if ( remove_page_protections(&prot, type, dep_policy, process_handle) )
|
||||
{ // We have to set new protections
|
||||
real_len = aligned_len;
|
||||
}
|
||||
|
||||
// Remember the new breakpoint
|
||||
p = page_bpts.insert(std::make_pair(page_ea, pagebpt_data_t())).first;
|
||||
pagebpt_data_t &bpt = p->second;
|
||||
bpt.ea = ea;
|
||||
bpt.user_len = size;
|
||||
bpt.page_ea = page_ea;
|
||||
bpt.aligned_len = aligned_len;
|
||||
bpt.real_len = real_len;
|
||||
bpt.old_prot = 0;
|
||||
bpt.new_prot = prot;
|
||||
bpt.type = type;
|
||||
|
||||
// for PAGE_GUARD pages, no need to change the permissions, everything is fine already
|
||||
if ( real_len == 0 )
|
||||
{
|
||||
bpt.old_prot = meminfo.Protect;
|
||||
return 2;
|
||||
}
|
||||
|
||||
return dbg_enable_page_bpt(p, true) ? 2 : 0;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// returns true if changed *protect (in other words, if we have to mask
|
||||
// the real page protections and return the original one)
|
||||
bool winbase_debmod_t::mask_page_bpts(
|
||||
ea_t startea,
|
||||
ea_t endea,
|
||||
uint32 *protect)
|
||||
{
|
||||
// if we have page breakpoints, what we return must be changed to show the
|
||||
// real segment privileges, instead of the new ones we applied for the bpt
|
||||
int newprot = 0;
|
||||
page_bpts_t::iterator p = page_bpts.begin();
|
||||
while ( p != page_bpts.end() )
|
||||
{
|
||||
pagebpt_data_t &pbd = p->second;
|
||||
if ( pbd.page_ea + pbd.real_len > startea )
|
||||
{
|
||||
if ( pbd.page_ea >= endea )
|
||||
break;
|
||||
if ( pbd.old_prot != 0 )
|
||||
{ // bpt has been written to the process memory
|
||||
if ( *protect == pbd.new_prot )
|
||||
{ // return the old protection, before setting the page bpt
|
||||
newprot = pbd.old_prot;
|
||||
}
|
||||
else
|
||||
{
|
||||
debdeb("mask_page_bpts: app changed our page protection for %a (expected: 0x%x, actual: 0x%x)\n", pbd.page_ea, pbd.new_prot, *protect);
|
||||
// page protection has been changed by the application
|
||||
DWORD prot = *protect;
|
||||
if ( prot == PAGE_WRITECOPY && pbd.new_prot == PAGE_READWRITE
|
||||
|| prot == PAGE_EXECUTE_WRITECOPY && pbd.new_prot == PAGE_EXECUTE_READWRITE )
|
||||
{
|
||||
// in some cases OS may restore WRITECOPY protection; do nothing in such cases since it works the same way for breakpoint purposes
|
||||
debdeb(" ignoring changes to WRITECOPY protection\n");
|
||||
}
|
||||
else if ( remove_page_protections(&prot, pbd.type, dep_policy, process_handle) )
|
||||
{
|
||||
pbd.new_prot = prot;
|
||||
pbd.old_prot = 0; // mark our bpt as non-written
|
||||
debdeb(" will re-set protection to 0x%x\n", pbd.new_prot);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
++p;
|
||||
}
|
||||
if ( newprot != 0 )
|
||||
{
|
||||
*protect = newprot;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// Page breakpoints modify the page protections to induce access violations.
|
||||
// We must hide the modified page protections from IDA and report the original
|
||||
// page protections.
|
||||
// Second, the application may render a page bpt inactive by changing its page protections.
|
||||
// In this case we must report to IDA the new page protections and also reactivate
|
||||
// the page breakpoint.
|
||||
void winbase_debmod_t::verify_page_protections(
|
||||
meminfo_vec_t *areas,
|
||||
const win32_prots_t &prots)
|
||||
{
|
||||
QASSERT(624, areas->size() == prots.size());
|
||||
if ( page_bpts.empty() )
|
||||
return;
|
||||
|
||||
for ( int i = 0; i < areas->size(); i++ )
|
||||
{
|
||||
uint32 prot = prots[i];
|
||||
memory_info_t &a = areas->at(i);
|
||||
if ( mask_page_bpts(a.start_ea, a.end_ea, &prot) )
|
||||
a.perm = win_prot_to_ida_perm(prot);
|
||||
}
|
||||
|
||||
// reactivate all disabled page bpts, if any
|
||||
enable_page_bpts(true);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
#ifndef __X86__
|
||||
wow64_state_t winbase_debmod_t::check_wow64_process()
|
||||
{
|
||||
if ( is_wow64 == WOW64_NONE )
|
||||
{
|
||||
is_wow64 = check_wow64_handle(process_handle);
|
||||
if ( is_wow64 > 0 )
|
||||
dmsg("WOW64 process has been detected (pid=%d)\n", pid);
|
||||
}
|
||||
return is_wow64;
|
||||
}
|
||||
#endif
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
bool highdll_vec_t::has(eanat_t addr) const
|
||||
{
|
||||
for ( int i = 0; i < size(); ++i )
|
||||
if ( (*this)[i].has(addr) )
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
bool highdll_vec_t::add(eanat_t addr, size_t sz, HANDLE h)
|
||||
{
|
||||
if ( has(addr) )
|
||||
return false;
|
||||
|
||||
// check removed: on new win10 we can have above 4GB:
|
||||
// ntdll.dll, wow64.dll, wow64win.dll
|
||||
// QASSERT(1491, size() < 2);
|
||||
highdll_range_t &r = push_back();
|
||||
r.start = addr;
|
||||
r.end = addr + sz;
|
||||
r.handle = h;
|
||||
return true;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
bool highdll_vec_t::add_high_module(
|
||||
eanat_t addr,
|
||||
size_t sz,
|
||||
HANDLE h)
|
||||
{
|
||||
if ( ea_t(addr) == addr )
|
||||
return false;
|
||||
add(addr, sz, h); //-V779 unreachable code
|
||||
return true;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
bool highdll_vec_t::del_high_module(HANDLE *h, eanat_t addr)
|
||||
{
|
||||
for ( int i = 0; i < size(); ++i )
|
||||
{
|
||||
const highdll_range_t &r = (*this)[i];
|
||||
if ( r.start == addr )
|
||||
{
|
||||
if ( h != NULL )
|
||||
*h = r.handle;
|
||||
erase(begin() + i);
|
||||
return ea_t(addr) != addr;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
void idaapi winbase_debmod_t::dbg_term(void)
|
||||
{
|
||||
is_wow64 = WOW64_NONE;
|
||||
delete win_tool_help;
|
||||
win_tool_help = NULL;
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
bool winbase_debmod_t::handle_process_start(pid_t _pid)
|
||||
{
|
||||
debapp_attrs.addrsize = get_process_addrsize(_pid);
|
||||
is64 = debapp_attrs.addrsize == 8;
|
||||
|
||||
term_reg_ctx();
|
||||
init_reg_ctx();
|
||||
return true;
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
void winbase_debmod_t::cleanup(void)
|
||||
{
|
||||
inherited::cleanup();
|
||||
is64 = false;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// Check if we need to install a temporary breakpoint to workaround the
|
||||
// 'freely running after syscall' problem. Exactly, the problem is the
|
||||
// following: after single stepping over a "jmp far ptr" instruction in
|
||||
// wow64cpu.dll for a 32bits process under a 64bits OS (Win7), the trap flag
|
||||
// is lost. Probably, it's a bug in wow64cpu!CpuReturnFromSimulatedCode.
|
||||
//
|
||||
// So, if we find an instruction like "call large dword fs:XX" we add a
|
||||
// temporary breakpoint at the next instruction and re-enable tracing
|
||||
// when the breakpoint is reached.
|
||||
bool winbase_debmod_t::check_for_call_large(
|
||||
const debug_event_t *event,
|
||||
HANDLE handle)
|
||||
{
|
||||
if ( check_wow64_handle(handle) <= 0 )
|
||||
return false;
|
||||
uchar buf[3];
|
||||
if ( dbg_read_memory(event->ea, buf, 3, NULL) == 3 )
|
||||
{
|
||||
// is it the call large instruction?
|
||||
if ( memcmp(buf, "\x64\xFF\x15", 3) == 0 )
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// Get process bitness: 32bit - 4, 64bit - 8, 0 - unknown
|
||||
int idaapi winbase_debmod_t::get_process_bitness(int _pid)
|
||||
{
|
||||
if ( _pid != -1 && _pid != GetCurrentProcessId() )
|
||||
{
|
||||
if ( !winver.is_64bitOS() )
|
||||
return 4;
|
||||
switch ( check_wow64_pid(_pid) )
|
||||
{
|
||||
case WOW64_BAD: return 0; // bad process id
|
||||
case WOW64_YES: return 4; // wow64 process, 32bit
|
||||
case WOW64_NO: return 8; // regular 64bit process
|
||||
default: break;
|
||||
}
|
||||
}
|
||||
return IDA_ADDRESS_SIZE;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
static const char *str_bitness(int addrsize)
|
||||
{
|
||||
switch ( addrsize )
|
||||
{
|
||||
case 8:
|
||||
return "[64]";
|
||||
case 4:
|
||||
return "[32]";
|
||||
default:
|
||||
return "[x]";
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// this function may correct pinfo->addrsize
|
||||
bool winbase_debmod_t::get_process_path(
|
||||
ext_process_info_t *pinfo,
|
||||
char *buf,
|
||||
size_t bufsize)
|
||||
{
|
||||
module_snapshot_t msnap(get_tool_help());
|
||||
MODULEENTRY32 me;
|
||||
if ( !msnap.first(TH32CS_SNAPMODULE, pinfo->pid, &me) )
|
||||
{
|
||||
if ( msnap.last_err() == ERROR_PARTIAL_COPY && pinfo->addrsize == 0 )
|
||||
{
|
||||
// MSDN: If the specified process is a 64-bit process and the caller is a
|
||||
// 32-bit process, error code is ERROR_PARTIAL_COPY
|
||||
pinfo->addrsize = 8;
|
||||
}
|
||||
qstrncpy(buf, pinfo->name.c_str(), bufsize);
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
tchar_utf8(buf, me.szExePath, bufsize);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
win_tool_help_t *winbase_debmod_t::get_tool_help()
|
||||
{
|
||||
if ( win_tool_help == NULL )
|
||||
win_tool_help = new win_tool_help_t;
|
||||
return win_tool_help;
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
int winbase_debmod_t::get_process_addrsize(pid_t _pid)
|
||||
{
|
||||
int addrsize = get_process_bitness(_pid);
|
||||
return addrsize != 0 ? addrsize : IDA_ADDRESS_SIZE;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
//lint -e{1762} could be made const [in fact it cannot be made const in x64 mode]
|
||||
bool winbase_debmod_t::is_ntdll_name(const char *path)
|
||||
{
|
||||
const char *base_name = qbasename(path);
|
||||
const char *ntdll_name = winver.is_NT()
|
||||
? "ntdll.dll" // NT
|
||||
: "kernel32.dll"; // 9X/Me and KERNEL32.DLL
|
||||
if ( strieq(base_name, ntdll_name) )
|
||||
return true;
|
||||
#ifndef __X86__
|
||||
if ( winver.is_NT()
|
||||
&& check_wow64_process() == WOW64_YES
|
||||
&& strieq(base_name, "ntdll32.dll") )
|
||||
{
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
//lint -esym(1762,winbase_debmod_t::build_process_ext_name) could be made const
|
||||
void winbase_debmod_t::build_process_ext_name(ext_process_info_t *pinfo)
|
||||
{
|
||||
char fullname[MAXSTR];
|
||||
if ( get_process_path(pinfo, fullname, sizeof(fullname))
|
||||
&& pinfo->addrsize == 0 )
|
||||
{
|
||||
// the WOW64 is optional on R2 x64 server
|
||||
pinfo->addrsize = IDA_ADDRESS_SIZE;
|
||||
}
|
||||
pinfo->ext_name = str_bitness(pinfo->addrsize);
|
||||
if ( !pinfo->ext_name.empty() )
|
||||
pinfo->ext_name += ' ';
|
||||
pinfo->ext_name += fullname;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
int idaapi winbase_debmod_t::get_process_list(procvec_t *list, qstring *)
|
||||
{
|
||||
int mypid = GetCurrentProcessId();
|
||||
list->clear();
|
||||
|
||||
process_snapshot_t psnap(get_tool_help());
|
||||
PROCESSENTRY32 pe32;
|
||||
for ( bool ok = psnap.first(TH32CS_SNAPNOHEAPS, &pe32); ok; ok = psnap.next(&pe32) )
|
||||
{
|
||||
if ( pe32.th32ProcessID != mypid )
|
||||
{
|
||||
int addrsize = get_process_bitness(pe32.th32ProcessID);
|
||||
#ifndef __EA64__
|
||||
if ( addrsize > 4 )
|
||||
continue; // skip 64bit processes, we cannot debug them because ea_t is 32bit
|
||||
#endif
|
||||
ext_process_info_t pinfo;
|
||||
pinfo.pid = pe32.th32ProcessID;
|
||||
pinfo.addrsize = addrsize;
|
||||
tchar_utf8(&pinfo.name, pe32.szExeFile);
|
||||
build_process_ext_name(&pinfo);
|
||||
list->push_back(pinfo);
|
||||
}
|
||||
}
|
||||
return list->size();
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// Returns the file name assciated with pid
|
||||
bool idaapi winbase_debmod_t::get_exec_fname(int _pid, char *buf, size_t bufsize)
|
||||
{
|
||||
ext_process_info_t pinfo;
|
||||
pinfo.pid = _pid;
|
||||
pinfo.name.qclear();
|
||||
return get_process_path(&pinfo, buf, bufsize);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
win_tool_help_t *winbase_debmod_t::win_tool_help = NULL;
|
||||
win_version_t winbase_debmod_t::winver;
|
||||
185
idasdk76/dbg/win32/winbase_debmod.h
Normal file
185
idasdk76/dbg/win32/winbase_debmod.h
Normal file
@@ -0,0 +1,185 @@
|
||||
#ifndef __WINBASE_HPP__
|
||||
#define __WINBASE_HPP__
|
||||
|
||||
// Base class for win32 and windbg modules
|
||||
|
||||
using std::for_each;
|
||||
using std::pair;
|
||||
using std::make_pair;
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
#define BASE_DEBUGGER_MODULE pc_debmod_t
|
||||
#include "deb_pc.hpp"
|
||||
#include "pc_debmod.h"
|
||||
#define BPT_CODE_SIZE X86_BPT_SIZE
|
||||
#include "win32_util.hpp"
|
||||
|
||||
extern const TCHAR kernel32_dll[];
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// DEP policies
|
||||
enum dep_policy_t
|
||||
{
|
||||
dp_always_off,
|
||||
dp_always_on,
|
||||
dp_opt_in,
|
||||
dp_opt_out
|
||||
};
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
enum attach_status_t
|
||||
{
|
||||
as_none, // no attach to process requested
|
||||
as_attaching, // waiting for CREATE_PROCESS_DEBUG_EVENT, indicating the process is attached
|
||||
as_breakpoint, // waiting for first breakpoint, indicating the process was properly initialized and suspended
|
||||
as_attached, // process was successfully attached
|
||||
as_detaching, // waiting for next get_debug_event() request, to return the process as detached
|
||||
as_attach_kernel, // attaching to kernel
|
||||
};
|
||||
|
||||
// vector of win32 page protections
|
||||
// we need this type because meminfo_t does not contain the original win32 protections
|
||||
// but we need them to verify page bpts
|
||||
typedef qvector<uint32> win32_prots_t;
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// When debugging WOW64 processes with ida32 we have to take into account
|
||||
// ntdll.dll (and wow64*.dll), which are x64 files
|
||||
// that can be loaded into high addresses (above 4GB)
|
||||
// Since ea_t cannot represent such addresses,
|
||||
// we use our own type to remember the DLL boundaries
|
||||
|
||||
typedef size_t eanat_t;
|
||||
|
||||
struct highdll_range_t
|
||||
{
|
||||
eanat_t start;
|
||||
eanat_t end;
|
||||
HANDLE handle;
|
||||
highdll_range_t() : start(0), end(0), handle(INVALID_HANDLE_VALUE) {}
|
||||
bool has(eanat_t addr) const { return addr >= start && addr < end; }
|
||||
};
|
||||
DECLARE_TYPE_AS_MOVABLE(highdll_range_t);
|
||||
|
||||
struct highdll_vec_t : protected qvector<highdll_range_t>
|
||||
{
|
||||
private:
|
||||
size_t num_ntdlls; // count of actual ntdll*.dll modules in the list
|
||||
public:
|
||||
typedef qvector<highdll_range_t> inherited;
|
||||
highdll_vec_t() : num_ntdlls(0) {}
|
||||
void clear() { inherited::clear(); num_ntdlls = 0; }
|
||||
size_t size() const { return inherited::size(); }
|
||||
size_t count_ntdlls() const { return num_ntdlls; }
|
||||
bool empty() const { return inherited::empty(); }
|
||||
// return false if there is already a dll with such an address
|
||||
bool add(eanat_t addr, size_t size, HANDLE h = INVALID_HANDLE_VALUE);
|
||||
bool add_ntdll(eanat_t addr, size_t size, HANDLE h = INVALID_HANDLE_VALUE)
|
||||
{
|
||||
bool ok = add(addr, size, h);
|
||||
if ( ok )
|
||||
num_ntdlls++;
|
||||
return ok;
|
||||
};
|
||||
// it returns true if the dll address doesn't fit in `ea_t`
|
||||
bool add_high_module(
|
||||
eanat_t addr,
|
||||
size_t size,
|
||||
HANDLE h = INVALID_HANDLE_VALUE);
|
||||
// it returns true if the dll address doesn't fit to `ea_t`
|
||||
bool del_high_module(HANDLE *h, eanat_t addr);
|
||||
bool has(eanat_t addr) const;
|
||||
};
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
class winbase_debmod_t: public BASE_DEBUGGER_MODULE
|
||||
{
|
||||
typedef BASE_DEBUGGER_MODULE inherited;
|
||||
wow64_state_t is_wow64 = WOW64_NONE; // use check_wow64_process()
|
||||
|
||||
protected:
|
||||
HANDLE process_handle = INVALID_HANDLE_VALUE;
|
||||
dep_policy_t dep_policy = dp_always_off;
|
||||
highdll_vec_t highdlls;
|
||||
bool is64 = false;
|
||||
|
||||
// local functions
|
||||
bool mask_page_bpts(ea_t startea, ea_t endea, uint32 *protect);
|
||||
void verify_page_protections(meminfo_vec_t *areas, const win32_prots_t &prots);
|
||||
|
||||
winbase_debmod_t(void);
|
||||
|
||||
// overridden virtual functions
|
||||
bool idaapi dbg_enable_page_bpt(page_bpts_t::iterator p, bool enable);
|
||||
int idaapi dbg_add_page_bpt(bpttype_t type, ea_t ea, int size);
|
||||
bool check_for_call_large(const debug_event_t *event, HANDLE process_handle);
|
||||
#ifndef __X86__
|
||||
wow64_state_t check_wow64_process();
|
||||
#else
|
||||
wow64_state_t check_wow64_process() { return WOW64_NO; }
|
||||
#endif
|
||||
|
||||
int get_process_addrsize(pid_t pid);
|
||||
|
||||
bool is_ntdll_name(const char *path);
|
||||
|
||||
// return number of processes, -1 - not implemented
|
||||
virtual int idaapi get_process_list(procvec_t *proclist, qstring *errbuf) override;
|
||||
// return the file name assciated with pid
|
||||
virtual bool idaapi get_exec_fname(int pid, char *buf, size_t bufsize) newapi;
|
||||
// get process bitness: 32bit - 4, 64bit - 8, 0 - unknown
|
||||
virtual int idaapi get_process_bitness(int pid) newapi;
|
||||
|
||||
virtual void init_reg_ctx(void) newapi {}
|
||||
virtual void term_reg_ctx(void) newapi {}
|
||||
|
||||
public:
|
||||
virtual void idaapi dbg_term(void) override;
|
||||
|
||||
static win_tool_help_t *get_tool_help();
|
||||
static win_version_t winver;
|
||||
|
||||
protected:
|
||||
bool handle_process_start(pid_t _pid);
|
||||
void cleanup(void);
|
||||
|
||||
private:
|
||||
void build_process_ext_name(ext_process_info_t *pinfo);
|
||||
static bool get_process_path(
|
||||
ext_process_info_t *pinfo,
|
||||
char *buf,
|
||||
size_t bufsize);
|
||||
static bool remove_page_protections(
|
||||
DWORD *p_input,
|
||||
bpttype_t bpttype,
|
||||
dep_policy_t dpolicy,
|
||||
HANDLE proc_handle);
|
||||
|
||||
static win_tool_help_t *win_tool_help;
|
||||
};
|
||||
|
||||
bool should_fire_page_bpt(page_bpts_t::iterator p, ea_t ea, DWORD failed_access_type, ea_t pc, dep_policy_t dep_policy);
|
||||
|
||||
#ifdef _PE_H_
|
||||
bool read_pe_header(peheader_t *pe);
|
||||
#endif
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
inline void tchar_utf8(qstring *buf, TCHAR *tchar)
|
||||
{
|
||||
#ifdef UNICODE
|
||||
utf16_utf8(buf, tchar);
|
||||
#else
|
||||
acp_utf8(buf, tchar);
|
||||
#endif
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
inline void tchar_utf8(char *buf, TCHAR *tchar, size_t bufsize)
|
||||
{
|
||||
qstring utf8;
|
||||
tchar_utf8(&utf8, tchar);
|
||||
qstrncpy(buf, utf8.c_str(), bufsize);
|
||||
}
|
||||
|
||||
#endif
|
||||
131
idasdk76/defaults.mk
Normal file
131
idasdk76/defaults.mk
Normal file
@@ -0,0 +1,131 @@
|
||||
#############################################################################
|
||||
# versions and paths for various external libraries and utils
|
||||
|
||||
ifdef __NT__
|
||||
# The following variables may have been set by vcvars.bat. You may
|
||||
# also set them manually. The default installation directories are
|
||||
# defined below in case these variables are not set.
|
||||
# Note: the following paths use backslashes (and may also contain a
|
||||
# trailing backslash) in order to conform to the variables
|
||||
# exported by vcvars.bat.
|
||||
|
||||
# Visual C++ 2017 Install Directory
|
||||
VCINSTALLDIR ?= '$(PROGRAMFILES)\Microsoft Visual Studio\2017\Professional\VC\'
|
||||
|
||||
# Visual C++ 2017 Tools Version
|
||||
# Note: if this variable is not set, the default version is obtained
|
||||
# in allmake.mak under "Visual C++ 2017 Tools Version".
|
||||
# VCToolsVersion ?= '14.11.25503'
|
||||
|
||||
# Windows SDK Install Directory
|
||||
WindowsSdkDir ?= '$(PROGRAMFILES)\Windows Kits\10\'
|
||||
|
||||
# Windows SDK version
|
||||
# Note: if this variable is not set, the latest version is detected
|
||||
# in allmake.mak under "Windows SDK Version".
|
||||
# WindowsSDKVersion ?= '10.0.17134.0\'
|
||||
|
||||
# Microsoft SDK v7.1A is only used for the win32 debugger server for
|
||||
# Windows XP compatibility.
|
||||
MSSDK71_PATH = '$(PROGRAMFILES)/Microsoft SDKs/Windows/v7.1A'
|
||||
else ifdef __MAC__
|
||||
# oldest supported version of MacOSX
|
||||
ifdef __ARM__
|
||||
MACOSX_DEPLOYMENT_TARGET = 11.0
|
||||
else
|
||||
MACOSX_DEPLOYMENT_TARGET = 10.9
|
||||
endif
|
||||
endif
|
||||
|
||||
# Python
|
||||
PYTHON_VERSION_MAJOR?=3
|
||||
PYTHON_VERSION_MINOR?=4
|
||||
PYTHON_VERNAME=python$(PYTHON_VERSION_MAJOR).$(PYTHON_VERSION_MINOR)
|
||||
|
||||
# TODO clean this up
|
||||
ifdef __NT__
|
||||
ifneq (,$(wildcard /cygdrive/c/Program\ Files/Python$(PYTHON_VERSION_MAJOR)$(PYTHON_VERSION_MINOR)/python.exe))
|
||||
PYTHON_ROOT ?= C:/Program Files/Python$(PYTHON_VERSION_MAJOR)$(PYTHON_VERSION_MINOR)
|
||||
else
|
||||
ifeq ($(PYTHON_VERSION_MAJOR),2)
|
||||
PYTHON_VERSUF=-x64
|
||||
endif
|
||||
PYTHON_ROOT ?= $(SYSTEMDRIVE)/Python$(PYTHON_VERSION_MAJOR)$(PYTHON_VERSION_MINOR)$(PYTHON_VERSUF)
|
||||
endif
|
||||
PYTHON ?= "$(PYTHON_ROOT)/python.exe"
|
||||
else
|
||||
PYTHON ?= $(PYTHON_VERNAME)
|
||||
endif
|
||||
|
||||
# Qt
|
||||
QTPROC-1=x64
|
||||
QTPROC-$(__ARM__)=arm64
|
||||
QTVER?=5.6.3-$(QTPROC-1)
|
||||
|
||||
QTDIR-$(__LINUX__) = /usr/local/Qt/$(QTVER)/
|
||||
QTDIR-$(__MAC__) = /Users/Shared/Qt/$(QTVER)/
|
||||
QTDIR-$(__NT__) = $(SYSTEMDRIVE)/Qt/$(QTVER)/
|
||||
QTDIR ?= $(QTDIR-1)
|
||||
|
||||
ifdef __NT__
|
||||
ifdef NDEBUG
|
||||
QTSUFF=.dll
|
||||
else
|
||||
QTSUFF=d.dll
|
||||
endif
|
||||
QTLIBDIR=bin
|
||||
else ifdef __LINUX__
|
||||
QTPREF=lib
|
||||
QTSUFF=.so.5
|
||||
QTLIBDIR=lib
|
||||
endif
|
||||
|
||||
# SWiG
|
||||
ifeq ($(PYTHON_VERSION_MAJOR),3)
|
||||
SWIG_VERSION?=4.0.1
|
||||
ifdef __NT__
|
||||
SWIG_DIR_SUFFIX?=-py3-stable-abi-cygwin
|
||||
else
|
||||
SWIG_DIR_SUFFIX?=-py3-stable-abi
|
||||
endif
|
||||
else
|
||||
SWIG_VERSION?=4.0.0
|
||||
endif
|
||||
ifdef __NT__
|
||||
ifeq ($(PYTHON_VERSION_MAJOR),3)
|
||||
SWIG_DISTRIBUTION_HAS_UNIX_LAYOUT:=1
|
||||
endif
|
||||
else
|
||||
SWIG_DISTRIBUTION_HAS_UNIX_LAYOUT:=1
|
||||
endif
|
||||
|
||||
ifeq ($(SWIG_DISTRIBUTION_HAS_UNIX_LAYOUT),1)
|
||||
ifdef USE_CCACHE
|
||||
# we set CCACHE_DIR so as to not interfere with the system's ccache
|
||||
# and we set CCACHE_CPP2 to prevent SWiG from printing a bunch of
|
||||
# warnings due to re-using of the preprocessed source.
|
||||
SWIG?=CCACHE_DIR='$${HOME}/.ccache-swig' CCACHE_CPP2=1 $(SWIG_HOME)/bin/ccache-swig $(SWIG_HOME)/bin/swig
|
||||
else
|
||||
SWIG?=$(SWIG_HOME)/bin/swig
|
||||
endif
|
||||
SWIG_INCLUDES?=-I$(SWIG_HOME)/share/swig/$(SWIG_VERSION)/python -I$(SWIG_HOME)/share/swig/$(SWIG_VERSION)
|
||||
else
|
||||
SWIG?=$(SWIG_HOME)/swig.exe
|
||||
SWIG_INCLUDES?=-I$(SWIG_HOME)/Lib/python -I$(SWIG_HOME)/Lib
|
||||
endif
|
||||
|
||||
#############################################################################
|
||||
# keep all paths in unix format, with forward slashes
|
||||
ifeq ($(OS),Windows_NT)
|
||||
# define: convert dos path to unix path by replacing backslashes by slashes
|
||||
unixpath=$(subst \,/,$(1))
|
||||
|
||||
PYTHON_ROOT :=$(call unixpath,$(PYTHON_ROOT))
|
||||
PYTHON :=$(call unixpath,$(PYTHON))
|
||||
SWIG :=$(call unixpath,$(SWIG))
|
||||
QTDIR :=$(call unixpath,$(QTDIR))
|
||||
endif
|
||||
|
||||
#############################################################################
|
||||
# http://stackoverflow.com/questions/16467718/how-to-print-out-a-variable-in-makefile
|
||||
.print-% : ; @echo $($*)
|
||||
21420
idasdk76/include/allins.hpp
Normal file
21420
idasdk76/include/allins.hpp
Normal file
File diff suppressed because it is too large
Load Diff
12
idasdk76/include/allow_deprecated.hpp
Normal file
12
idasdk76/include/allow_deprecated.hpp
Normal file
@@ -0,0 +1,12 @@
|
||||
// this file should be included before calling deprecated functions
|
||||
// it should be included at the point where the definitions of deprecated
|
||||
// functions begin in the source file. this way a deprecated function may call
|
||||
// another deprecated function without raising a warning.
|
||||
|
||||
// deprecated functions may call each other
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(disable:4996)
|
||||
#endif
|
||||
#ifdef __GNUC__
|
||||
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
|
||||
#endif
|
||||
289
idasdk76/include/auto.hpp
Normal file
289
idasdk76/include/auto.hpp
Normal file
@@ -0,0 +1,289 @@
|
||||
/*
|
||||
* Interactive disassembler (IDA).
|
||||
* Copyright (c) 1990-2021 Hex-Rays
|
||||
* ALL RIGHTS RESERVED.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef _AUTO_HPP
|
||||
#define _AUTO_HPP
|
||||
#include <ida.hpp>
|
||||
|
||||
/*! \file auto.hpp
|
||||
|
||||
\brief Functions that work with the autoanalyzer queue.
|
||||
|
||||
The autoanalyzer works when IDA is not busy processing
|
||||
the user keystrokes. It has several queues, each queue having
|
||||
its own priority. The analyzer stops when all queues are empty.
|
||||
|
||||
A queue contains addresses or address ranges.
|
||||
The addresses are kept sorted by their values.
|
||||
The analyzer will process all addresses from the first queue, then
|
||||
switch to the second queue and so on.
|
||||
There are no limitations on the size of the queues.
|
||||
|
||||
This file also contains functions that deal with the IDA status
|
||||
indicator and the autoanalysis indicator.
|
||||
You may use these functions to change the indicator value.
|
||||
*/
|
||||
|
||||
typedef int atype_t; ///< identifies an autoanalysis queue - see \ref AU_
|
||||
|
||||
/// \defgroup AU_ Autoanalysis queues
|
||||
/// Names and priorities of the analyzer queues
|
||||
//@{
|
||||
const atype_t
|
||||
AU_NONE = 00, ///< placeholder, not used
|
||||
AU_UNK = 10, ///< 0: convert to unexplored
|
||||
AU_CODE = 20, ///< 1: convert to instruction
|
||||
AU_WEAK = 25, ///< 2: convert to instruction (ida decision)
|
||||
AU_PROC = 30, ///< 3: convert to procedure start
|
||||
AU_TAIL = 35, ///< 4: add a procedure tail
|
||||
AU_FCHUNK=38, ///< 5: find func chunks
|
||||
AU_USED = 40, ///< 6: reanalyze
|
||||
AU_TYPE = 50, ///< 7: apply type information
|
||||
AU_LIBF = 60, ///< 8: apply signature to address
|
||||
AU_LBF2 = 70, ///< 9: the same, second pass
|
||||
AU_LBF3 = 80, ///< 10: the same, third pass
|
||||
AU_CHLB = 90, ///< 11: load signature file (file name is kept separately)
|
||||
AU_FINAL=200; ///< 12: final pass
|
||||
//@}
|
||||
|
||||
|
||||
typedef int idastate_t; ///< IDA status indicator - see \ref st_
|
||||
|
||||
/// \defgroup st_ Status indicator states
|
||||
//@{
|
||||
const idastate_t
|
||||
// meaning
|
||||
st_Ready = 0, ///< READY: IDA is doing nothing
|
||||
st_Think = 1, ///< THINKING: Autoanalysis on, the user may press keys
|
||||
st_Waiting = 2, ///< WAITING: Waiting for the user input
|
||||
st_Work = 3; ///< BUSY: IDA is busy
|
||||
//@}
|
||||
|
||||
|
||||
/// Get current state of autoanalyzer.
|
||||
/// If auto_state == ::AU_NONE, IDA is currently not running the analysis
|
||||
/// (it could be temporarily interrupted to perform the user's requests, for example).
|
||||
|
||||
idaman atype_t ida_export get_auto_state(void);
|
||||
|
||||
|
||||
/// Set current state of autoanalyzer.
|
||||
/// \param new_state new state of autoanalyzer
|
||||
/// \return previous state
|
||||
|
||||
idaman atype_t ida_export set_auto_state(atype_t new_state);
|
||||
|
||||
|
||||
/// See ::get_auto_display
|
||||
struct auto_display_t
|
||||
{
|
||||
atype_t type = AU_NONE;
|
||||
ea_t ea = BADADDR;
|
||||
idastate_t state = st_Ready;
|
||||
};
|
||||
|
||||
/// Get structure which holds the autoanalysis indicator contents
|
||||
|
||||
idaman bool ida_export get_auto_display(auto_display_t *auto_display);
|
||||
|
||||
|
||||
/// Change autoanalysis indicator value.
|
||||
/// \param ea linear address being analyzed
|
||||
/// \param type autoanalysis type (see \ref AU_)
|
||||
|
||||
idaman void ida_export show_auto(ea_t ea, atype_t type=AU_NONE);
|
||||
|
||||
|
||||
/// Show an address on the autoanalysis indicator.
|
||||
/// The address is displayed in the form " @:12345678".
|
||||
/// \param ea - linear address to display
|
||||
|
||||
inline void show_addr(ea_t ea) { show_auto(ea); }
|
||||
|
||||
|
||||
/// Change IDA status indicator value
|
||||
/// \param st - new indicator status
|
||||
/// \return old indicator status
|
||||
|
||||
idaman idastate_t ida_export set_ida_state(idastate_t st);
|
||||
|
||||
|
||||
/// Is it allowed to create stack variables automatically?.
|
||||
/// This function should be used by IDP modules before creating stack vars.
|
||||
|
||||
inline bool may_create_stkvars(void)
|
||||
{
|
||||
return inf_should_create_stkvars() && get_auto_state() == AU_USED;
|
||||
}
|
||||
|
||||
|
||||
/// Is it allowed to trace stack pointer automatically?.
|
||||
/// This function should be used by IDP modules before tracing sp.
|
||||
|
||||
inline bool may_trace_sp(void)
|
||||
{
|
||||
if ( inf_should_trace_sp() )
|
||||
{
|
||||
atype_t auto_state = get_auto_state();
|
||||
return auto_state == AU_USED;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
/// Put range of addresses into a queue.
|
||||
/// 'start' may be higher than 'end', the kernel will swap them in this case.
|
||||
/// 'end' doesn't belong to the range.
|
||||
|
||||
idaman void ida_export auto_mark_range(ea_t start,ea_t end,atype_t type);
|
||||
|
||||
|
||||
/// Put single address into a queue. Queues keep addresses sorted.
|
||||
|
||||
inline void auto_mark(ea_t ea, atype_t type)
|
||||
{
|
||||
auto_mark_range(ea, ea+1, type);
|
||||
}
|
||||
|
||||
|
||||
/// Remove range of addresses from a queue.
|
||||
/// 'start' may be higher than 'end', the kernel will swap them in this case.
|
||||
/// 'end' doesn't belong to the range.
|
||||
|
||||
idaman void ida_export auto_unmark(ea_t start, ea_t end, atype_t type);
|
||||
|
||||
// Convenience functions
|
||||
|
||||
/// Plan to perform reanalysis
|
||||
inline void plan_ea(ea_t ea)
|
||||
{
|
||||
auto_mark(ea, AU_USED);
|
||||
}
|
||||
/// Plan to perform reanalysis
|
||||
inline void plan_range(ea_t sEA, ea_t eEA)
|
||||
{
|
||||
auto_mark_range(sEA, eEA, AU_USED);
|
||||
}
|
||||
/// Plan to make code
|
||||
inline void auto_make_code(ea_t ea)
|
||||
{
|
||||
auto_mark(ea, AU_CODE);
|
||||
}
|
||||
/// Plan to make code&function
|
||||
inline void auto_make_proc(ea_t ea)
|
||||
{
|
||||
auto_make_code(ea);
|
||||
auto_mark(ea, AU_PROC);
|
||||
}
|
||||
|
||||
/// Plan to reanalyze callers of the specified address.
|
||||
/// This function will add to ::AU_USED queue all instructions that
|
||||
/// call (not jump to) the specified address.
|
||||
/// \param ea linear address of callee
|
||||
/// \param noret !=0: the callee doesn't return, mark to undefine subsequent
|
||||
/// instructions in the caller. 0: do nothing.
|
||||
|
||||
idaman void ida_export reanalyze_callers(ea_t ea, bool noret);
|
||||
|
||||
|
||||
/// Delete all analysis info that IDA generated for for the given range
|
||||
|
||||
idaman void ida_export revert_ida_decisions(ea_t ea1, ea_t ea2);
|
||||
|
||||
|
||||
/// Plan to apply the callee's type to the calling point
|
||||
|
||||
idaman void ida_export auto_apply_type(ea_t caller, ea_t callee);
|
||||
|
||||
/// Plan to apply the tail_ea chunk to the parent
|
||||
/// \param tail_ea linear address of start of tail
|
||||
/// \param parent_ea linear address within parent. If BADADDR, automatically
|
||||
/// try to find parent via xrefs.
|
||||
|
||||
idaman void ida_export auto_apply_tail(ea_t tail_ea, ea_t parent_ea);
|
||||
|
||||
/// Analyze the specified range.
|
||||
/// Try to create instructions where possible.
|
||||
/// Make the final pass over the specified range if specified.
|
||||
/// This function doesn't return until the range is analyzed.
|
||||
/// \retval 1 ok
|
||||
/// \retval 0 Ctrl-Break was pressed
|
||||
|
||||
idaman int ida_export plan_and_wait(ea_t ea1, ea_t ea2, bool final_pass=true);
|
||||
|
||||
|
||||
/// Process everything in the queues and return true.
|
||||
/// \return false if the user clicked cancel.
|
||||
/// (the wait box must be displayed by the caller if desired)
|
||||
|
||||
idaman bool ida_export auto_wait(void);
|
||||
|
||||
|
||||
/// Process everything in the specified range and return true.
|
||||
/// \return number of autoanalysis steps made. -1 if the user clicked cancel.
|
||||
/// (the wait box must be displayed by the caller if desired)
|
||||
|
||||
idaman ssize_t ida_export auto_wait_range(ea_t ea1, ea_t ea2);
|
||||
|
||||
|
||||
/// Analyze one address in the specified range and return true.
|
||||
/// \return if processed anything. false means that there is nothing to
|
||||
/// process in the specified range.
|
||||
|
||||
idaman bool ida_export auto_make_step(ea_t ea1, ea_t ea2);
|
||||
|
||||
|
||||
/// Remove an address range (ea1..ea2) from queues ::AU_CODE, ::AU_PROC, ::AU_USED.
|
||||
/// To remove an address range from other queues use auto_unmark() function.
|
||||
/// 'ea1' may be higher than 'ea2', the kernel will swap them in this case.
|
||||
/// 'ea2' doesn't belong to the range.
|
||||
|
||||
idaman void ida_export auto_cancel(ea_t ea1, ea_t ea2);
|
||||
|
||||
|
||||
/// Are all queues empty?
|
||||
/// (i.e. has autoanalysis finished?).
|
||||
|
||||
idaman bool ida_export auto_is_ok(void);
|
||||
|
||||
|
||||
/// Peek into a queue 'type' for an address not lower than 'low_ea'.
|
||||
/// Do not remove address from the queue.
|
||||
/// \return the address or #BADADDR
|
||||
|
||||
idaman ea_t ida_export peek_auto_queue(ea_t low_ea, atype_t type);
|
||||
|
||||
|
||||
/// Retrieve an address from queues regarding their priority.
|
||||
/// Returns #BADADDR if no addresses not lower than 'lowEA' and less than
|
||||
/// 'highEA' are found in the queues.
|
||||
/// Otherwise *type will have queue type.
|
||||
|
||||
idaman ea_t ida_export auto_get(atype_t *type, ea_t lowEA, ea_t highEA);
|
||||
|
||||
|
||||
/// Try to create instruction
|
||||
/// \param ea linear address of callee
|
||||
/// \return the length of the instruction or 0
|
||||
|
||||
idaman int ida_export auto_recreate_insn(ea_t ea);
|
||||
|
||||
|
||||
/// Get autoanalyzer state
|
||||
|
||||
idaman bool ida_export is_auto_enabled(void);
|
||||
|
||||
|
||||
/// Temporarily enable/disable autoanalyzer. Not user-facing, but rather because
|
||||
/// IDA sometimes need to turn AA on/off regardless of inf.s_genflags:INFFL_AUTO
|
||||
/// \return old state
|
||||
|
||||
idaman bool ida_export enable_auto(bool enable);
|
||||
|
||||
|
||||
|
||||
#endif // _AUTO_HPP
|
||||
350
idasdk76/include/bitrange.hpp
Normal file
350
idasdk76/include/bitrange.hpp
Normal file
@@ -0,0 +1,350 @@
|
||||
#ifndef _BITMASK_HPP
|
||||
#define _BITMASK_HPP
|
||||
|
||||
/*! \file bitrange.hpp
|
||||
|
||||
\brief Definition of the ::bitrange_t class
|
||||
|
||||
*/
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
/// This class manages the offset and size of a value that occupies
|
||||
/// a number of contiguous bits within some container - generally a byte array.
|
||||
/// A special state - empty range (both offset and size are zeroes) - determines
|
||||
/// the value as all bits of the container.
|
||||
class bitrange_t
|
||||
{
|
||||
public:
|
||||
/// Constructor
|
||||
explicit bitrange_t(uint16 bit_ofs = 0, uint16 size_in_bits = 0);
|
||||
/// Initialize offset and size to given values
|
||||
inline void init(uint16 bit_ofs, uint16 size_in_bits);
|
||||
|
||||
/// Make the bitrange empty
|
||||
inline void reset();
|
||||
/// Is the bitrange empty?
|
||||
inline bool empty() const;
|
||||
|
||||
/// Get offset of 1st bit
|
||||
inline uint bitoff() const;
|
||||
/// Get size of the value in bits
|
||||
inline uint bitsize() const;
|
||||
/// Size of the value in bytes
|
||||
inline uint bytesize() const;
|
||||
/// Convert to mask of 64 bits
|
||||
inline uint64 mask64() const;
|
||||
|
||||
/// Does have common bits with another bitrange?
|
||||
inline bool has_common(const bitrange_t &r) const;
|
||||
|
||||
/// Apply mask to a bitrange
|
||||
/// \param subrange range *inside* the main bitrange to keep
|
||||
/// After this operation the main bitrange will be truncated
|
||||
/// to have only the bits that are specified by subrange.
|
||||
/// Example: [off=8,nbits=4], subrange[off=1,nbits=2] => [off=9,nbits=2]
|
||||
/// \return success
|
||||
inline bool apply_mask(const bitrange_t &subrange);
|
||||
|
||||
/// Intersect two ranges
|
||||
inline void intersect(const bitrange_t &r);
|
||||
|
||||
/// Create union of 2 ranges including the hole between them
|
||||
inline void create_union(const bitrange_t &r);
|
||||
|
||||
/// Subtract a bitrange
|
||||
inline bool sub(const bitrange_t &r);
|
||||
|
||||
/// Shift range down (left)
|
||||
inline void shift_down(uint cnt);
|
||||
/// Shift range up (right)
|
||||
inline void shift_up(uint cnt);
|
||||
|
||||
/// Initialize bitrange_t with offset/size defined by given mask
|
||||
template <class T> inline void assign_max_nonzero(T mask);
|
||||
|
||||
/// \name Extract
|
||||
/// Extract a value from 'src' according to the bitrange
|
||||
/// \param dst vector the extracted value will be stored to
|
||||
/// \param src source buffer
|
||||
/// \param src_size size of source buffer
|
||||
/// \param is_mf is Msb First? (TRUE-big endian, FALSE-little endian)
|
||||
//@{
|
||||
inline bool extract(
|
||||
bytevec_t *dst,
|
||||
const void *src,
|
||||
size_t src_size,
|
||||
bool is_mf) const;
|
||||
inline bool extract(
|
||||
void *dst,
|
||||
size_t dst_size,
|
||||
const void *src,
|
||||
size_t src_size,
|
||||
bool is_mf) const;
|
||||
//@}
|
||||
|
||||
/// \name Inject
|
||||
/// Inject a value into 'dst' according to the bitrange
|
||||
/// \param dst a buffer the value will be injected into
|
||||
/// \param dst_size size of buffer
|
||||
/// \param src source value
|
||||
/// \param is_mf is Msb First? (TRUE-big endian, FALSE-little endian)
|
||||
//@{
|
||||
inline bool inject(
|
||||
void *dst,
|
||||
size_t dst_size,
|
||||
const void *src,
|
||||
size_t src_size,
|
||||
bool is_mf) const;
|
||||
inline bool inject(
|
||||
void *dst,
|
||||
size_t dst_size,
|
||||
const bytevec_t &src,
|
||||
bool is_mf) const;
|
||||
//@}
|
||||
|
||||
DECLARE_COMPARISONS(bitrange_t);
|
||||
|
||||
private:
|
||||
uint16 offset; ///< offset of 1st bit starting with the lowest bit
|
||||
uint16 nbits; ///< size of the value in bits
|
||||
};
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
inline bitrange_t::bitrange_t(uint16 bit_ofs, uint16 size_in_bits)
|
||||
: offset(bit_ofs), nbits(size_in_bits)
|
||||
{
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
inline void bitrange_t::init(uint16 bit_ofs, uint16 size_in_bits)
|
||||
{
|
||||
offset = bit_ofs;
|
||||
nbits = size_in_bits;
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
inline void bitrange_t::reset()
|
||||
{
|
||||
init(0, 0);
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
inline bool bitrange_t::empty() const
|
||||
{
|
||||
return nbits == 0;
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
inline uint bitrange_t::bitoff() const
|
||||
{
|
||||
return offset;
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
inline uint bitrange_t::bitsize() const
|
||||
{
|
||||
return nbits;
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
inline uint bitrange_t::bytesize() const
|
||||
{
|
||||
return (nbits + 8-1) / 8;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
inline uint64 bitrange_t::mask64() const
|
||||
{
|
||||
return empty() ? 0 : (left_shift(uint64(1), nbits)-1) << offset;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
inline bool bitrange_t::apply_mask(const bitrange_t &submask)
|
||||
{
|
||||
if ( submask.bitoff() + submask.bitsize() > bitsize() )
|
||||
return false;
|
||||
init(bitoff() + submask.bitoff(), submask.bitsize());
|
||||
return true;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
inline void bitrange_t::intersect(const bitrange_t &r)
|
||||
{
|
||||
uint16 e1 = offset + nbits;
|
||||
uint16 e2 = r.offset + r.nbits;
|
||||
uint16 e = qmin(e1, e2);
|
||||
uint16 s = qmax(offset, r.offset);
|
||||
if ( s > e )
|
||||
{
|
||||
s = 0;
|
||||
e = 0;
|
||||
}
|
||||
init(s, e-s);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
inline bool bitrange_t::has_common(const bitrange_t &r) const
|
||||
{
|
||||
return interval::overlap(offset, nbits, r.offset, r.nbits);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
inline void bitrange_t::create_union(const bitrange_t &r)
|
||||
{
|
||||
uint16 e1 = offset + nbits;
|
||||
uint16 e2 = r.offset + r.nbits;
|
||||
uint16 e = qmax(e1, e2);
|
||||
uint16 s = qmin(offset, r.offset);
|
||||
init(s, e-s);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
inline bool bitrange_t::sub(const bitrange_t &r)
|
||||
{
|
||||
// r is in the middle of our bitrange, cannot handle this case
|
||||
// because it would require 2 bitranges :/
|
||||
uint16 end = offset + nbits;
|
||||
uint16 rend = r.offset + r.nbits;
|
||||
if ( r.offset > offset && rend < end )
|
||||
return false;
|
||||
if ( r.offset <= offset )
|
||||
{
|
||||
if ( rend > end )
|
||||
{
|
||||
reset();
|
||||
}
|
||||
else if ( rend > offset )
|
||||
{
|
||||
offset = rend;
|
||||
nbits = end - offset;
|
||||
}
|
||||
}
|
||||
else if ( r.offset < end )
|
||||
{
|
||||
nbits = r.offset - offset;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
inline int bitrange_t::compare(const bitrange_t &r) const
|
||||
{
|
||||
if ( offset != r.offset )
|
||||
return offset < r.offset ? -1 : 1;
|
||||
if ( nbits != r.nbits )
|
||||
return nbits < r.nbits ? -1 : 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
inline void bitrange_t::shift_down(uint cnt)
|
||||
{
|
||||
if ( cnt > offset )
|
||||
{
|
||||
cnt -= offset;
|
||||
offset = 0;
|
||||
if ( cnt > nbits )
|
||||
nbits = 0;
|
||||
else
|
||||
nbits -= cnt;
|
||||
}
|
||||
else
|
||||
{
|
||||
offset -= cnt;
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
inline void bitrange_t::shift_up(uint cnt)
|
||||
{
|
||||
offset += cnt;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
template <class T> inline void bitrange_t::assign_max_nonzero(T mask)
|
||||
{
|
||||
if ( mask == T(0) )
|
||||
{
|
||||
reset();
|
||||
return;
|
||||
}
|
||||
int i = 0;
|
||||
T bit = T(1);
|
||||
for ( i=0; i < sizeof(T)*8; ++i, bit <<= 1 )
|
||||
if ( (mask & bit) != 0 )
|
||||
break;
|
||||
offset = i;
|
||||
i = sizeof(T)*8 - 1;
|
||||
bit = left_shift(T(1), i);
|
||||
while ( i >= offset )
|
||||
{
|
||||
if ( (mask & bit) != 0 )
|
||||
break;
|
||||
--i;
|
||||
bit >>= 1;
|
||||
}
|
||||
nbits = i - offset + 1;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
#ifndef SWIG
|
||||
/// \name Helper functions
|
||||
/// Should not be called directly!
|
||||
//@{
|
||||
idaman bool ida_export bitrange_t_extract_using_bitrange(const bitrange_t *bm, void *dst, size_t dst_size, const void *src, size_t src_size, bool is_mf);
|
||||
idaman bool ida_export bitrange_t_inject_using_bitrange(const bitrange_t *bm, void *dst, size_t dst_size, const void *src, size_t src_size, bool is_mf);
|
||||
//@}
|
||||
#else
|
||||
#endif // SWIG
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
inline bool bitrange_t::extract(
|
||||
void *dst,
|
||||
size_t dst_size,
|
||||
const void *src,
|
||||
size_t src_size,
|
||||
bool is_mf) const
|
||||
{
|
||||
return bitrange_t_extract_using_bitrange(this, dst, dst_size, src, src_size, is_mf);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
inline bool bitrange_t::extract(
|
||||
bytevec_t *dst,
|
||||
const void *src,
|
||||
size_t src_size,
|
||||
bool is_mf) const
|
||||
{
|
||||
size_t dst_size = empty() ? src_size : bytesize();
|
||||
dst->resize(dst_size);
|
||||
return bitrange_t_extract_using_bitrange(this,
|
||||
dst->begin(), dst_size,
|
||||
src, src_size,
|
||||
is_mf);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
inline bool bitrange_t::inject(
|
||||
void *dst,
|
||||
size_t dst_size,
|
||||
const void *src,
|
||||
size_t src_size,
|
||||
bool is_mf) const
|
||||
{
|
||||
return bitrange_t_inject_using_bitrange(this, dst, dst_size, src, src_size, is_mf);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
inline bool bitrange_t::inject(
|
||||
void *dst,
|
||||
size_t dst_size,
|
||||
const bytevec_t &src,
|
||||
bool is_mf) const
|
||||
{
|
||||
return bitrange_t_inject_using_bitrange(this,
|
||||
dst, dst_size,
|
||||
src.begin(), src.size(),
|
||||
is_mf);
|
||||
}
|
||||
|
||||
#endif // define _BITMASK_HPP
|
||||
2556
idasdk76/include/bytes.hpp
Normal file
2556
idasdk76/include/bytes.hpp
Normal file
File diff suppressed because it is too large
Load Diff
173
idasdk76/include/compress.hpp
Normal file
173
idasdk76/include/compress.hpp
Normal file
@@ -0,0 +1,173 @@
|
||||
/*
|
||||
* Interactive disassembler (IDA).
|
||||
* Copyright (c) 1990-2021 Hex-Rays
|
||||
* ALL RIGHTS RESERVED.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef COMPRESS_HPP
|
||||
#define COMPRESS_HPP
|
||||
|
||||
#include <diskio.hpp>
|
||||
|
||||
/*! \file compress.hpp
|
||||
|
||||
\brief Data compression functions
|
||||
|
||||
*/
|
||||
|
||||
/// Compress data.
|
||||
/// This function depends on the value of legacy_idb, so it is not completely
|
||||
/// thread safe. However, legacy_idb does not change its value.
|
||||
/// \return \ref PKZ_
|
||||
|
||||
idaman THREAD_SAFE int ida_export zip_deflate(
|
||||
void *ud,
|
||||
ssize_t (idaapi *file_reader)(void *ud, void *buf, size_t size),
|
||||
ssize_t (idaapi *file_writer)(void *ud, const void *buf, size_t size));
|
||||
|
||||
|
||||
/// Uncompress data.
|
||||
/// This function depends on the value of legacy_idb, so it is not completely
|
||||
/// thread safe. However, legacy_idb does not change its value.
|
||||
/// \return \ref PKZ_
|
||||
|
||||
idaman THREAD_SAFE int ida_export zip_inflate(
|
||||
void *ud,
|
||||
ssize_t (idaapi *file_reader)(void *ud, void *buf, size_t size),
|
||||
ssize_t (idaapi *file_writer)(void *ud, const void *buf, size_t size));
|
||||
|
||||
|
||||
/// Process zip file and enumerate all files stored in it
|
||||
/// \param zipfile name of zip file
|
||||
/// \param callback callback for each file. params:
|
||||
/// - ud: user data
|
||||
/// - offset: offset in the zip file
|
||||
/// - method: compression method (\ref compression_methods)
|
||||
/// - csize: compressed size
|
||||
/// - ucsize: uncompressed size
|
||||
/// - attributes: file attributes
|
||||
/// \param ud user data
|
||||
/// \return \ref PKZ_
|
||||
|
||||
idaman THREAD_SAFE int ida_export process_zipfile(
|
||||
const char *zipfile,
|
||||
int (idaapi *callback)(
|
||||
void *ud,
|
||||
qoff64_t offset,
|
||||
int method,
|
||||
uint64 csize,
|
||||
uint64 ucsize,
|
||||
uint32 attributes,
|
||||
const char *filename),
|
||||
void *ud = NULL);
|
||||
|
||||
|
||||
/// Process zip file and enumerate all files stored in it
|
||||
/// \param li input file
|
||||
/// \param callback callback for each file. params:
|
||||
/// - ud: user data
|
||||
/// - offset: offset in the zip file
|
||||
/// - method: compression method (\ref compression_methods)
|
||||
/// - csize: compressed size
|
||||
/// - ucsize: uncompressed size
|
||||
/// - attributes: file attributes
|
||||
/// \param ud user data
|
||||
/// \return \ref PKZ_
|
||||
|
||||
idaman THREAD_SAFE int ida_export process_zip_linput(
|
||||
linput_t *li,
|
||||
int (idaapi *callback)(
|
||||
void *ud,
|
||||
qoff64_t offset,
|
||||
int method,
|
||||
uint64 csize,
|
||||
uint64 ucsize,
|
||||
uint32 attributes,
|
||||
const char *filename),
|
||||
void *ud = NULL);
|
||||
|
||||
|
||||
/// Search for specified entry in zip file, and calls the
|
||||
/// callback with it, if found.
|
||||
/// \param zipfile name of zip file
|
||||
/// \param entry entry in zip file. E.g., "path/to/entry.dat"
|
||||
/// \param callback callback for each file. params:
|
||||
/// - ud: user data
|
||||
/// - offset: offset in the zip file
|
||||
/// - method: compression method (\ref compression_methods)
|
||||
/// - csize: compressed size
|
||||
/// - ucsize: uncompressed size
|
||||
/// - attributes: file attributes
|
||||
/// \param ud user data
|
||||
/// \param case_sensitive should the search be case sensitive?
|
||||
/// \return \ref PKZ_
|
||||
|
||||
idaman THREAD_SAFE int ida_export process_zipfile_entry(
|
||||
const char *zipfile,
|
||||
const char *entry,
|
||||
int (idaapi *callback)(
|
||||
void *ud,
|
||||
qoff64_t offset,
|
||||
int method,
|
||||
uint64 csize,
|
||||
uint64 ucsize,
|
||||
uint32 attributes,
|
||||
const char *filename),
|
||||
void *ud = NULL,
|
||||
bool case_sensitive = true);
|
||||
|
||||
|
||||
/// \defgroup PKZ_ Compression error codes
|
||||
/// Returned by functions in compress.hpp
|
||||
//@{
|
||||
#define PKZ_OK 0
|
||||
#define PKZ_ERRNO 1
|
||||
#define PKZ_STREAM_ERROR 2
|
||||
#define PKZ_DATA_ERROR 3
|
||||
#define PKZ_MEM_ERROR 4
|
||||
#define PKZ_BUF_ERROR 5
|
||||
#define PKZ_VERSION_ERROR 6
|
||||
#define PKZ_RERR 777 // read error
|
||||
#define PKZ_WERR 778 // write error
|
||||
//@}
|
||||
|
||||
/// \defgroup compression_methods Compression methods
|
||||
/// passed as 'method' parameter to callback functions in compress.hpp
|
||||
//@{
|
||||
#define STORED 0
|
||||
#define SHRUNK 1
|
||||
#define REDUCED1 2
|
||||
#define REDUCED2 3
|
||||
#define REDUCED3 4
|
||||
#define REDUCED4 5
|
||||
#define IMPLODED 6
|
||||
#define TOKENIZED 7
|
||||
#define DEFLATED 8
|
||||
#define NUM_METHODS 9 /* index of last method + 1 */
|
||||
//@}
|
||||
|
||||
extern bool legacy_idb; ///< for old idb files
|
||||
|
||||
/// Upon closing outer linput, perform one of these actions
|
||||
enum linput_close_code_t
|
||||
{
|
||||
LOC_CLOSE, ///< close the inner linput
|
||||
LOC_UNMAKE, ///< unmake the inner linput
|
||||
LOC_KEEP, ///< do nothing
|
||||
};
|
||||
|
||||
|
||||
/// Create a linput to read a compressed input stream
|
||||
/// \param in linput with compressed data, seeked to the stream beginning
|
||||
/// \param insize size of compressed data. -1 - unknown
|
||||
/// \param loc what to do upon closing the resulting linput
|
||||
/// \return linput that can be used to read uncompressed data.
|
||||
/// NULL if any error (no more linput descriptors).
|
||||
|
||||
idaman THREAD_SAFE linput_t *ida_export create_zip_linput(
|
||||
linput_t *in,
|
||||
ssize_t insize=-1,
|
||||
linput_close_code_t loc=LOC_CLOSE);
|
||||
|
||||
#endif
|
||||
543
idasdk76/include/config.hpp
Normal file
543
idasdk76/include/config.hpp
Normal file
@@ -0,0 +1,543 @@
|
||||
/*
|
||||
* Interactive disassembler (IDA).
|
||||
* Copyright (c) 1990-2021 Hex-Rays
|
||||
* ALL RIGHTS RESERVED.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef _CONFIG_HPP
|
||||
#define _CONFIG_HPP
|
||||
|
||||
//-----------------------------------------------------------------------
|
||||
/// \defgroup IDPOPT_T Option value types
|
||||
/// Passed as 'value_type' parameter to ::set_options_t callbacks
|
||||
//@{
|
||||
#define IDPOPT_STR 1 ///< string constant (char *)
|
||||
#define IDPOPT_NUM 2 ///< number (uval_t *)
|
||||
#define IDPOPT_BIT 3 ///< bit, yes/no (int *)
|
||||
#define IDPOPT_I64 5 ///< 64bit number (int64 *)
|
||||
#define IDPOPT_CST 6 ///< lexer (lexer_t*)
|
||||
///< Custom type, starting with a '{'
|
||||
///< Values of this type should be handled by
|
||||
///< ::set_options_t callbacks. E.g.,:
|
||||
///< \code
|
||||
///< ERROR_STRINGS =
|
||||
///< {
|
||||
///< {0, "Unknown error"},
|
||||
///< {1, "Missing filename"},
|
||||
///< {5, "Out-of-memory"}
|
||||
///< }
|
||||
///< \endcode
|
||||
///< For values of this type, the data that will
|
||||
///< be passed as the callback's 'value' parameter
|
||||
///< is the lexer instance that is being used
|
||||
///< to parse the configuration file.
|
||||
///< You can use \ref parse_json() (see parsejson.hpp)
|
||||
///< to parse JSON-format data
|
||||
///< NB: the '{' is already consumed by the parser,
|
||||
///< so you need to push it again if it's a part of the JSON object
|
||||
//@}
|
||||
|
||||
/// \defgroup IDPOPT_RET Option result codes
|
||||
/// Predefined return values for ::set_options_t callbacks
|
||||
//@{
|
||||
#define IDPOPT_OK NULL ///< ok
|
||||
#define IDPOPT_BADKEY ((char*)1) ///< illegal keyword
|
||||
#define IDPOPT_BADTYPE ((char*)2) ///< illegal type of value
|
||||
#define IDPOPT_BADVALUE ((char*)3) ///< illegal value (bad range, for example)
|
||||
//@}
|
||||
|
||||
|
||||
/// Callback - called when a config directive is processed in IDA.
|
||||
/// Also see read_config_file() and processor_t::set_idp_options
|
||||
/// \param keyword keyword encountered in IDA.CFG/user config file.
|
||||
/// if NULL, then an interactive dialog form should be displayed
|
||||
/// \param value_type type of value of the keyword - one of \ref IDPOPT_T
|
||||
/// \param value pointer to value
|
||||
/// \param idb_loaded true if the ev_oldfile/ev_newfile events have been generated?
|
||||
/// \return one of \ref IDPOPT_RET, otherwise a pointer to an error message
|
||||
|
||||
typedef const char *(idaapi set_options_t)(
|
||||
const char *keyword,
|
||||
int value_type,
|
||||
const void *value,
|
||||
bool idb_loaded);
|
||||
|
||||
/// \defgroup IDAOPT_PRIO Option priority
|
||||
/// Specifies the priority of a configuration option. Since options may
|
||||
/// be specified in different way, and applied in various orders, we need
|
||||
/// option priorities.
|
||||
/// Normally the default priority option does not overwrite the existing value
|
||||
/// whereas the high priority one does.
|
||||
/// High priority options may be stored in the database to be available
|
||||
/// in the next session.
|
||||
//@{
|
||||
#define IDPOPT_PRI_DEFAULT 1 ///< default priority - taken from config file
|
||||
#define IDPOPT_PRI_HIGH 2 ///< high priority - received from UI or a script function
|
||||
//@}
|
||||
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
/// Parse the value type for the value token 'value'.
|
||||
/// This is mostly used for converting from values that a cfgopt_handler_t
|
||||
/// receives, into data that callbacks
|
||||
/// - processor_t::set_idp_options
|
||||
/// - debugger_t::set_dbg_options
|
||||
/// expect.
|
||||
///
|
||||
/// Plugins that wish to use options shouldn't rely on this,
|
||||
/// and use the cfgopt_t utility instead.
|
||||
///
|
||||
/// \param out parsed data
|
||||
/// \param lx the lexer in use
|
||||
/// \param value the value token
|
||||
/// \return true if guessing didn't lead to an error, false otherwise.
|
||||
/// note that even if 'true' is returned, it doesn't mean the
|
||||
/// type could be guessed: merely that no syntax error occurred.
|
||||
class lexer_t;
|
||||
struct token_t;
|
||||
idaman bool ida_export parse_config_value(
|
||||
idc_value_t *out,
|
||||
lexer_t *lx,
|
||||
const token_t &value);
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
typedef const char *(idaapi cfgopt_handler_t)(
|
||||
lexer_t *lx,
|
||||
const token_t &keyword,
|
||||
const token_t &value);
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
typedef const char *(idaapi cfgopt_handler2_t)(
|
||||
lexer_t *lx,
|
||||
const token_t &keyword,
|
||||
const token_t &value,
|
||||
int64 param1,
|
||||
int64 param2);
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
typedef const char *(idaapi cfgopt_handler3_t)(
|
||||
lexer_t *lx,
|
||||
const token_t &keyword,
|
||||
const token_t &value,
|
||||
int64 param1,
|
||||
int64 param2,
|
||||
void *obj);
|
||||
|
||||
//-----------------------------------------------------------------------
|
||||
/// used by cfgopt_t. You shouldn't have to deal with those directly.
|
||||
#define IDPOPT_NUM_INT (0)
|
||||
#define IDPOPT_NUM_CHAR (1 << 24)
|
||||
#define IDPOPT_NUM_SHORT (2 << 24)
|
||||
#define IDPOPT_NUM_RANGE (1 << 26)
|
||||
#define IDPOPT_NUM_UNS (1 << 27)
|
||||
|
||||
#define IDPOPT_BIT_UINT 0
|
||||
#define IDPOPT_BIT_UCHAR (1 << 24)
|
||||
#define IDPOPT_BIT_USHORT (2 << 24)
|
||||
#define IDPOPT_BIT_BOOL (3 << 24)
|
||||
|
||||
#define IDPOPT_STR_QSTRING (1 << 24)
|
||||
#define IDPOPT_STR_LONG (1 << 25)
|
||||
|
||||
#define IDPOPT_I64_RANGES (1 << 24)
|
||||
#define IDPOPT_I64_UNS (1 << 25)
|
||||
|
||||
#define IDPOPT_CST_PARAMS (1 << 24)
|
||||
|
||||
#define IDPOPT_MBROFF (1 << 18)
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
struct cfgopt_t;
|
||||
idaman const char *ida_export cfgopt_t__apply(
|
||||
const cfgopt_t *_this,
|
||||
int vtype,
|
||||
const void *vdata);
|
||||
idaman const char *ida_export cfgopt_t__apply2(
|
||||
const cfgopt_t *_this,
|
||||
int vtype,
|
||||
const void *vdata,
|
||||
void *obj);
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
// cfgopt_t objects are suitable for being statically initialized, and
|
||||
// passed to 'read_config_file'.
|
||||
//
|
||||
// E.g.,
|
||||
// ---
|
||||
// static const cfgopt_t g_opts[] =
|
||||
// {
|
||||
// cfgopt_t("AUTO_UNDEFINE", &auto_undefine, -1, 1),
|
||||
// cfgopt_t("NOVICE", &novice, true),
|
||||
// cfgopt_t("EDITOR", editor_buf, sizeof(editor_buf)),
|
||||
// cfgopt_t("SCREEN_PALETTE", set_screen_palette), // specific handler for SCREEN_PALETTE
|
||||
// };
|
||||
//
|
||||
// ...
|
||||
//
|
||||
// read_config_file("myfile", g_opts, qnumber(g_opts), other_handler)
|
||||
// ---
|
||||
//
|
||||
// NOTES:
|
||||
// * so-called 'long' strings (the default) can span on multiple lines,
|
||||
// and are terminated by a ';'
|
||||
struct cfgopt_t
|
||||
{
|
||||
const char *name;
|
||||
union
|
||||
{
|
||||
void *ptr;
|
||||
size_t mbroff; // offset of a structure member
|
||||
cfgopt_handler_t *hnd; // to avoid reinterpret_cast and gcc's error:
|
||||
cfgopt_handler2_t *hnd2; // "a reinterpret_cast is not a constant expression"
|
||||
cfgopt_handler3_t *hnd3; //
|
||||
};
|
||||
int flags;
|
||||
struct num_range_t
|
||||
{
|
||||
constexpr num_range_t(int64 _min, int64 _max) : minval(_min), maxval(_max) {}
|
||||
int64 minval;
|
||||
int64 maxval;
|
||||
};
|
||||
struct params_t
|
||||
{
|
||||
constexpr params_t(int64 _p1, int64 _p2) : p1(_p1), p2(_p2) {}
|
||||
int64 p1;
|
||||
int64 p2;
|
||||
};
|
||||
union
|
||||
{
|
||||
size_t buf_size;
|
||||
num_range_t num_range;
|
||||
uint32 bit_flags;
|
||||
params_t params;
|
||||
void *mbroff_obj;
|
||||
};
|
||||
|
||||
// IDPOPT_STR
|
||||
constexpr cfgopt_t(const char *_n, char *_p, size_t _sz, bool _long = true)
|
||||
: name(_n), ptr(_p), flags(IDPOPT_STR | (_long ? IDPOPT_STR_LONG : 0)), buf_size(_sz)
|
||||
{}
|
||||
constexpr cfgopt_t(const char *_n, qstring *_p, bool _long = true)
|
||||
: name(_n), ptr(_p), flags(IDPOPT_STR | IDPOPT_STR_QSTRING | (_long ? IDPOPT_STR_LONG : 0)), buf_size(0)
|
||||
{}
|
||||
|
||||
// IDPOPT_NUM
|
||||
constexpr cfgopt_t(const char *_n, int *_p)
|
||||
: name(_n), ptr(_p), flags(IDPOPT_NUM), buf_size(0) {}
|
||||
constexpr cfgopt_t(const char *_n, uint *_p)
|
||||
: name(_n), ptr(_p), flags(IDPOPT_NUM | IDPOPT_NUM_UNS), buf_size(0) {}
|
||||
constexpr cfgopt_t(const char *_n, char *_p)
|
||||
: name(_n), ptr(_p), flags(IDPOPT_NUM | IDPOPT_NUM_CHAR), buf_size(0) {}
|
||||
constexpr cfgopt_t(const char *_n, uchar *_p)
|
||||
: name(_n), ptr(_p), flags(IDPOPT_NUM | IDPOPT_NUM_UNS | IDPOPT_NUM_CHAR), buf_size(0) {}
|
||||
constexpr cfgopt_t(const char *_n, short *_p)
|
||||
: name(_n), ptr(_p), flags(IDPOPT_NUM | IDPOPT_NUM_SHORT), buf_size(0) {}
|
||||
constexpr cfgopt_t(const char *_n, ushort *_p)
|
||||
: name(_n), ptr(_p), flags(IDPOPT_NUM | IDPOPT_NUM_UNS | IDPOPT_NUM_SHORT), buf_size(0) {}
|
||||
// IDPOPT_NUM + ranges
|
||||
constexpr cfgopt_t(const char *_n, int *_p, int _min, int _max)
|
||||
: name(_n), ptr(_p), flags(IDPOPT_NUM | IDPOPT_NUM_RANGE), num_range(_min, _max) {}
|
||||
constexpr cfgopt_t(const char *_n, uint *_p, uint _min, uint _max)
|
||||
: name(_n), ptr(_p), flags(IDPOPT_NUM | IDPOPT_NUM_UNS | IDPOPT_NUM_RANGE), num_range(_min, _max) {}
|
||||
constexpr cfgopt_t(const char *_n, char *_p, char _min, char _max)
|
||||
: name(_n), ptr(_p), flags(IDPOPT_NUM | IDPOPT_NUM_CHAR | IDPOPT_NUM_RANGE), num_range(_min, _max) {}
|
||||
constexpr cfgopt_t(const char *_n, uchar *_p, uchar _min, uchar _max)
|
||||
: name(_n), ptr(_p), flags(IDPOPT_NUM | IDPOPT_NUM_UNS | IDPOPT_NUM_CHAR | IDPOPT_NUM_RANGE), num_range(_min, _max) {}
|
||||
constexpr cfgopt_t(const char *_n, short *_p, short _min, short _max)
|
||||
: name(_n), ptr(_p), flags(IDPOPT_NUM | IDPOPT_NUM_RANGE | IDPOPT_NUM_SHORT), num_range(_min, _max) {}
|
||||
constexpr cfgopt_t(const char *_n, ushort *_p, ushort _min, ushort _max)
|
||||
: name(_n), ptr(_p), flags(IDPOPT_NUM | IDPOPT_NUM_UNS | IDPOPT_NUM_RANGE | IDPOPT_NUM_SHORT), num_range(_min, _max) {}
|
||||
|
||||
// IDPOPT_BIT
|
||||
constexpr cfgopt_t(const char *_n, bool *_p, bool _flags) : name(_n), ptr(_p), flags(IDPOPT_BIT | IDPOPT_BIT_BOOL), bit_flags(_flags) {}
|
||||
constexpr cfgopt_t(const char *_n, uchar *_p, uchar _flags) : name(_n), ptr(_p), flags(IDPOPT_BIT | IDPOPT_BIT_UCHAR), bit_flags(_flags) {}
|
||||
constexpr cfgopt_t(const char *_n, ushort *_p, ushort _flags) : name(_n), ptr(_p), flags(IDPOPT_BIT | IDPOPT_BIT_USHORT), bit_flags(_flags) {}
|
||||
constexpr cfgopt_t(const char *_n, uint32 *_p, uint32 _flags) : name(_n), ptr(_p), flags(IDPOPT_BIT), bit_flags(_flags) {}
|
||||
|
||||
// IDPOPT_I64
|
||||
constexpr cfgopt_t(const char *_n, int64 *_p) : name(_n), ptr(_p), flags(IDPOPT_I64), buf_size(0) {}
|
||||
constexpr cfgopt_t(const char *_n, uint64 *_p) : name(_n), ptr(_p), flags(IDPOPT_I64 | IDPOPT_NUM_UNS), buf_size(0) {}
|
||||
// IDPOPT_I64 + ranges
|
||||
constexpr cfgopt_t(const char *_n, int64 *_p, int64 _min, int64 _max)
|
||||
: name(_n), ptr(_p), flags(IDPOPT_I64 | IDPOPT_I64_RANGES), num_range(_min, _max) {}
|
||||
constexpr cfgopt_t(const char *_n, uint64 *_p, uint64 _min, uint64 _max)
|
||||
: name(_n), ptr(_p), flags(IDPOPT_I64 | IDPOPT_I64_UNS | IDPOPT_I64_RANGES), num_range(int64(_min), int64(_max)) {}
|
||||
|
||||
// IDPOPT_CST
|
||||
constexpr cfgopt_t(const char *_n, cfgopt_handler_t *_p)
|
||||
: name(_n), hnd(_p), flags(IDPOPT_CST), buf_size(0) {}
|
||||
// IDPOPT_CST + params
|
||||
constexpr cfgopt_t(const char *_n, cfgopt_handler2_t *_p, int64 _p1=0, int64 _p2=0)
|
||||
: name(_n), hnd2(_p), flags(IDPOPT_CST | IDPOPT_CST_PARAMS), params(_p1, _p2) {}
|
||||
|
||||
// configuration option based on the offset of a structure member
|
||||
|
||||
// IDPOPT_STR
|
||||
template<class T>
|
||||
constexpr cfgopt_t(const char *_n, qstring T:: *, size_t _mbroff, bool _long = true)
|
||||
: name(_n),
|
||||
mbroff(_mbroff),
|
||||
flags(IDPOPT_MBROFF | IDPOPT_STR | IDPOPT_STR_QSTRING | (_long ? IDPOPT_STR_LONG : 0)),
|
||||
buf_size(0)
|
||||
{}
|
||||
#define CFGOPT_QS(nm, cfgt, cfgm, _long) \
|
||||
cfgopt_t(nm, &cfgt::cfgm, qoffsetof(cfgt, cfgm), _long)
|
||||
|
||||
#define CFGOPT_INNER_QS(nm, cfgt, cfgm, mt, mf, _long) \
|
||||
cfgopt_t(nm, &mt::mf, qoffsetof(cfgt, cfgm) + qoffsetof(mt, mf), _long)
|
||||
|
||||
|
||||
// IDPOPT_NUM
|
||||
#define CTR_CFGOPT(ctrtype, ctrflags) \
|
||||
template<class T> \
|
||||
constexpr cfgopt_t(const char *_n, ctrtype T:: *, size_t _mbroff) \
|
||||
: name(_n), \
|
||||
mbroff(_mbroff), \
|
||||
flags(IDPOPT_MBROFF|IDPOPT_NUM|ctrflags), \
|
||||
buf_size(0) \
|
||||
{}
|
||||
CTR_CFGOPT(int, 0)
|
||||
CTR_CFGOPT(uint, IDPOPT_NUM_UNS)
|
||||
CTR_CFGOPT(char, IDPOPT_NUM_CHAR)
|
||||
CTR_CFGOPT(uchar, IDPOPT_NUM_UNS|IDPOPT_NUM_CHAR)
|
||||
CTR_CFGOPT(short, IDPOPT_NUM_SHORT)
|
||||
CTR_CFGOPT(ushort, IDPOPT_NUM_SHORT|IDPOPT_NUM_UNS)
|
||||
#undef CTR_CFGOPT
|
||||
|
||||
#define CFGOPT_N(nm, cfgt, cfgm) \
|
||||
cfgopt_t(nm, &cfgt::cfgm, qoffsetof(cfgt, cfgm))
|
||||
|
||||
#define CFGOPT_INNER_N(nm, cfgt, cfgm, mt, mf) \
|
||||
cfgopt_t(nm, &mt::mf, qoffsetof(cfgt, cfgm) + qoffsetof(mt, mf))
|
||||
|
||||
|
||||
// IDPOPT_NUM + ranges
|
||||
#define CTR_CFGOPT(ctrtype, ctrflags) \
|
||||
template<class T> \
|
||||
constexpr cfgopt_t(const char *_n, ctrtype T:: *, size_t _mbroff, int64 _min, int64 _max) \
|
||||
: name(_n), \
|
||||
mbroff(_mbroff), \
|
||||
flags(IDPOPT_MBROFF|IDPOPT_NUM|IDPOPT_NUM_RANGE|ctrflags), \
|
||||
num_range(_min, _max) \
|
||||
{}
|
||||
CTR_CFGOPT(int, 0)
|
||||
CTR_CFGOPT(uint, IDPOPT_NUM_UNS)
|
||||
CTR_CFGOPT(char, IDPOPT_NUM_CHAR)
|
||||
CTR_CFGOPT(uchar, IDPOPT_NUM_UNS|IDPOPT_NUM_CHAR)
|
||||
CTR_CFGOPT(short, IDPOPT_NUM_SHORT)
|
||||
CTR_CFGOPT(ushort, IDPOPT_NUM_SHORT|IDPOPT_NUM_UNS)
|
||||
#undef CTR_CFGOPT
|
||||
|
||||
#define CFGOPT_R(nm, cfgt, cfgm, min, max) \
|
||||
cfgopt_t(nm, &cfgt::cfgm, qoffsetof(cfgt, cfgm), min, max)
|
||||
|
||||
#define CFGOPT_INNER_R(nm, cfgt, cfgm, mt, mf, min, max) \
|
||||
cfgopt_t(nm, &mt::mf, qoffsetof(cfgt, cfgm) + qoffsetof(mt, mf), min, max)
|
||||
|
||||
|
||||
// IDPOPT_BIT
|
||||
#define CTR_CFGOPT(ctrtype, ctrflags) \
|
||||
template<class T> \
|
||||
constexpr cfgopt_t(const char *_n, ctrtype T:: *, size_t _mbroff, ctrtype _flags) \
|
||||
: name(_n), \
|
||||
mbroff(_mbroff), \
|
||||
flags(IDPOPT_MBROFF|IDPOPT_BIT|ctrflags), \
|
||||
bit_flags(_flags) \
|
||||
{}
|
||||
CTR_CFGOPT(bool, IDPOPT_BIT_BOOL);
|
||||
CTR_CFGOPT(uchar, IDPOPT_BIT_UCHAR);
|
||||
CTR_CFGOPT(ushort, IDPOPT_BIT_USHORT);
|
||||
CTR_CFGOPT(uint32, 0);
|
||||
#undef CTR_CFGOPT
|
||||
#define CFGOPT_B(nm, cfgt, cfgm, _flags) \
|
||||
cfgopt_t(nm, &cfgt::cfgm, qoffsetof(cfgt, cfgm), _flags)
|
||||
|
||||
#define CFGOPT_INNER_B(nm, cfgt, cfgm, mt, mf, _flags) \
|
||||
cfgopt_t(nm, &mt::mf, qoffsetof(cfgt, cfgm) + qoffsetof(mt, mf), _flags)
|
||||
|
||||
|
||||
// IDPOPT_I64
|
||||
template<class T>
|
||||
constexpr cfgopt_t(const char *_n, int64 T:: *, size_t _mbroff)
|
||||
: name(_n),
|
||||
mbroff(_mbroff),
|
||||
flags(IDPOPT_MBROFF|IDPOPT_I64),
|
||||
buf_size(0)
|
||||
{}
|
||||
template<class T>
|
||||
constexpr cfgopt_t(const char *_n, uint64 T:: *, size_t _mbroff)
|
||||
: name(_n),
|
||||
mbroff(_mbroff),
|
||||
flags(IDPOPT_MBROFF|IDPOPT_I64|IDPOPT_NUM_UNS),
|
||||
buf_size(0)
|
||||
{}
|
||||
|
||||
// IDPOPT_I64 + ranges
|
||||
template<class T>
|
||||
constexpr cfgopt_t(const char *_n, int64 T:: *, size_t _mbroff, int64 _min, int64 _max)
|
||||
: name(_n),
|
||||
mbroff(_mbroff),
|
||||
flags(IDPOPT_MBROFF|IDPOPT_I64|IDPOPT_I64_RANGES),
|
||||
num_range(_min, _max)
|
||||
{}
|
||||
template<class T>
|
||||
constexpr cfgopt_t(const char *_n, uint64 T:: *, size_t _mbroff, uint64 _min, uint64 _max)
|
||||
: name(_n),
|
||||
mbroff(_mbroff),
|
||||
flags(IDPOPT_MBROFF|IDPOPT_I64|IDPOPT_I64_UNS|IDPOPT_I64_RANGES),
|
||||
num_range(int64(_min), int64(_max))
|
||||
{}
|
||||
|
||||
// IDPOPT_CST + params
|
||||
constexpr cfgopt_t(const char *_n, cfgopt_handler3_t *_p, int64 _p1=0, int64 _p2=0)
|
||||
: name(_n), hnd3(_p), flags(IDPOPT_MBROFF|IDPOPT_CST), params(_p1, _p2) {}
|
||||
|
||||
int type() const { return flags & 0xf; }
|
||||
int qualifier() const { return flags & 0xf000000; }
|
||||
|
||||
const char *apply(int vtype, const void *vdata, void *obj=nullptr) const
|
||||
{
|
||||
return cfgopt_t__apply2(this, vtype, vdata, obj);
|
||||
}
|
||||
};
|
||||
|
||||
/// Parse the input, and apply options.
|
||||
///
|
||||
/// \param input input file name, or string
|
||||
/// \param is_file is input a string, or a file name
|
||||
/// \param opts options destcriptions
|
||||
/// \param nopts the number of entries present in the 'opts' array
|
||||
/// \param defhdlr a handler to be called, if a directive couldn't be found in 'opts'
|
||||
/// \param defines a list of preprocessor identifiers to define (so it is
|
||||
/// possible to use #ifdef checks in the file.)
|
||||
/// NB: the actual identifier defined by the parser will be
|
||||
/// surrounded with double underscores (e.g., passing 'FOO'
|
||||
/// will result in '__FOO__' being defined)
|
||||
/// Additionally, the parser will also define a similar macro
|
||||
/// with the current processor name (e.g., __ARM__)
|
||||
/// \param ndefines the number of defines in the list
|
||||
/// \param obj see cfgopt_t constructor based on the offset of a structure member
|
||||
/// \return true if parsing finished without errors, false if there was a
|
||||
/// syntax error, callback returned an error, or no file was found
|
||||
/// at all.
|
||||
|
||||
idaman bool ida_export read_config(
|
||||
const char *input,
|
||||
bool is_file,
|
||||
const cfgopt_t opts[],
|
||||
size_t nopts,
|
||||
cfgopt_handler_t *defhdlr = NULL,
|
||||
const char *const *defines = NULL,
|
||||
size_t ndefines = 0);
|
||||
|
||||
idaman bool ida_export read_config2(
|
||||
const char *input,
|
||||
bool is_file,
|
||||
const cfgopt_t opts[],
|
||||
size_t nopts,
|
||||
cfgopt_handler_t *defhdlr = nullptr,
|
||||
const char *const *defines = nullptr,
|
||||
size_t ndefines = 0,
|
||||
void *obj = nullptr);
|
||||
|
||||
inline bool read_config_file2(
|
||||
const char *filename,
|
||||
const cfgopt_t opts[],
|
||||
size_t nopts,
|
||||
cfgopt_handler_t *defhdlr = nullptr,
|
||||
const char *const *defines = nullptr,
|
||||
size_t ndefines = 0,
|
||||
void *obj = nullptr)
|
||||
{
|
||||
return read_config2(filename, true, opts, nopts, defhdlr, defines, ndefines, obj);
|
||||
}
|
||||
|
||||
/// Search for all IDA system files with the given name.
|
||||
/// This function will search, in that order, for the following files:
|
||||
/// -# %IDADIR%/cfg/<file>
|
||||
/// -# for each directory 'ONEDIR' in %IDAUSR%: %ONEDIR%/cfg/<file>
|
||||
///
|
||||
/// For each directive in each of those files, the same processing as
|
||||
/// that of read_config will be performed.
|
||||
|
||||
inline bool read_config_file(
|
||||
const char *filename,
|
||||
const cfgopt_t opts[],
|
||||
size_t nopts,
|
||||
cfgopt_handler_t *defhdlr = NULL,
|
||||
const char *const *defines = NULL,
|
||||
size_t ndefines = 0)
|
||||
{
|
||||
return read_config(filename, true, opts, nopts, defhdlr, defines, ndefines);
|
||||
}
|
||||
|
||||
|
||||
/// For each directive in 'string', the same processing as that of
|
||||
/// read_config will be performed.
|
||||
inline bool read_config_string(
|
||||
const char *string,
|
||||
const cfgopt_t opts[],
|
||||
size_t nopts,
|
||||
cfgopt_handler_t *defhdlr = NULL,
|
||||
const char *const *defines = NULL,
|
||||
size_t ndefines = 0)
|
||||
{
|
||||
return read_config(string, false, opts, nopts, defhdlr, defines, ndefines);
|
||||
}
|
||||
|
||||
|
||||
/// Process one or more config directive(s).
|
||||
/// \param directive the directives to process
|
||||
/// \param priority priority \ref IDPOPT_RET
|
||||
/// In the case of errors this function displays a message and exits.
|
||||
|
||||
idaman void ida_export process_config_directive(
|
||||
const char *directive,
|
||||
int priority=IDPOPT_PRI_HIGH);
|
||||
|
||||
|
||||
/// Register array of config options.
|
||||
/// This function can be used by a plugin to register the config options.
|
||||
/// After registering an option, it becomes usable by the
|
||||
/// process_config_directive() function.
|
||||
/// \param opts array of config options
|
||||
/// \param nopts number of options to install. 0 means uninstall
|
||||
/// \param cb callback that will be invoked upon changing a config option
|
||||
/// \param obj see cfgopt_t constructor based on the offset of a structure member
|
||||
/// \return success
|
||||
|
||||
typedef void idaapi config_changed_cb_t(const cfgopt_t &opt, int vtype, const void *vdata);
|
||||
|
||||
idaman bool ida_export register_cfgopts(
|
||||
const cfgopt_t opts[],
|
||||
size_t nopts,
|
||||
config_changed_cb_t cb=nullptr,
|
||||
void *obj=nullptr);
|
||||
|
||||
|
||||
/// Get one of config parameters defined by CC_PARMS in ida.cfg.
|
||||
/// All parameters for all compilers are stored in local map during last read
|
||||
/// of ida.cfg - this function just returns previously stored parameter value for
|
||||
/// given compiler (NULL if no such parameter)
|
||||
idaman const char *ida_export cfg_get_cc_parm(comp_t compid, const char *name);
|
||||
|
||||
|
||||
/// Get header path config parameter from ida.cfg.
|
||||
/// Also see cfg_get_cc_parm()
|
||||
|
||||
inline const char *cfg_get_cc_header_path(comp_t compid)
|
||||
{
|
||||
return cfg_get_cc_parm(compid, "HEADER_PATH");
|
||||
}
|
||||
|
||||
|
||||
/// Get predefined macros config parameter from ida.cfg.
|
||||
/// Also see cfg_get_cc_parm()
|
||||
|
||||
inline const char *cfg_get_cc_predefined_macros(comp_t compid)
|
||||
{
|
||||
return cfg_get_cc_parm(compid, "PREDEFINED_MACROS");
|
||||
}
|
||||
|
||||
#endif // _CONFIG_HPP
|
||||
2625
idasdk76/include/dbg.hpp
Normal file
2625
idasdk76/include/dbg.hpp
Normal file
File diff suppressed because it is too large
Load Diff
269
idasdk76/include/demangle.hpp
Normal file
269
idasdk76/include/demangle.hpp
Normal file
@@ -0,0 +1,269 @@
|
||||
/*
|
||||
* CPP/D/Swift Demangler.
|
||||
* Copyright (c) 2000-2018 by Iouri Kharon.
|
||||
* E-mail: yjh@styx.cabel.net
|
||||
*
|
||||
* ALL RIGHTS RESERVED.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef _DEMANGLE_HPP
|
||||
#define _DEMANGLE_HPP
|
||||
|
||||
// int32 result code
|
||||
#define ME_INTERR -1 // Internal error
|
||||
#define ME_PARAMERR -2 // Input parameters are wrong
|
||||
#define ME_ILLSTR -3 // Incorrectly mangled name
|
||||
#define ME_SMALLANS -4 // Output buffer is too small
|
||||
// This code is possible only with the 'old' calling
|
||||
// form. With the new calling form the output buffer
|
||||
// will have '...' as the last characters and the
|
||||
// result code or'ed with the sign bit
|
||||
#define ME_FRAME -5 // Partial demanging is possible (the input name has
|
||||
// unrecognized suffix)
|
||||
#define ME_NOCOMP -6 // Could not determine the compiler
|
||||
#define ME_ERRAUTO -7 // Specified compiler is impossible for the input name
|
||||
#define ME_NOHASHMEM -8 // Out of internal indexes-most likely bad input name
|
||||
#define ME_NOSTRMEM -9 // Out of internal buffers (can't be!:)
|
||||
|
||||
#define ME_NOERROR_LIMIT -10 // Lowest error number. Lower values
|
||||
// signal about the truncated output name
|
||||
// (the output buffer is too small)
|
||||
|
||||
#define M_PRCMSK 0x0000000F // If = 0, then data
|
||||
#define MT_DEFAULT 0x00000001 // 1 - default (for watcom/gnu only this)
|
||||
#define MT_CDECL 0x00000002 // 2 - __cdecl
|
||||
#define MT_PASCAL 0x00000003 // 3 - __pascal
|
||||
#define MT_STDCALL 0x00000004 // 4 - __stdcall
|
||||
#define MT_FASTCALL 0x00000005 // 5 - __fastcall
|
||||
#define MT_THISCALL 0x00000006 // 6 - __thiscall [ms & bc => pragma only]
|
||||
#define MT_FORTRAN 0x00000007 // 7 - __fortran
|
||||
#define MT_SYSCALL 0x00000008 // 8 - __syscall [without ms]
|
||||
#define MT_INTERRUPT 0x00000009 // 9 - __interrupt (only with __cdecl!)
|
||||
#define MT_MSFASTCALL 0x0000000A // A - __msfastcall (bc)
|
||||
#define MT_CLRCALL 0x0000000B // B - __clrcall (vc7)
|
||||
#define MT_DMDCALL 0x0000000C // C - __dcall (dm D language abi)
|
||||
#define MT_VECTORCALL 0x0000000D // D - __vectorcall (vc13)
|
||||
#define MT_REGCALL 0x0000000E // E - __regcall (icl, clang)
|
||||
|
||||
// reserved
|
||||
#define MT_LOCALNAME 0x0000000F // f - might be function or data. Currently
|
||||
// is used only for bc - as the
|
||||
// identifier for local pascal labels
|
||||
|
||||
#define M_SAVEREGS 0x00000010 // For functions with "__saveregs"
|
||||
|
||||
#define M_CLASS 0x000000E0 // 0 - no keyword (not a member field)
|
||||
#define MT_PUBLIC 0x00000020 // 1 - public
|
||||
#define MT_PRIVATE 0x00000040 // 2 - private
|
||||
#define MT_PROTECT 0x00000060 // 3 - protected
|
||||
#define MT_MEMBER 0x00000080 // 4 - undetermined (bc/wat/gcc)
|
||||
#define MT_VTABLE 0x000000A0 // 5 - vtable (bc/gnu)
|
||||
#define MT_RTTI 0x000000C0 // 6 - typeinfo table (gcc3), witness table (Swift)
|
||||
// reserved
|
||||
|
||||
#define M_PARMSK 0x0000FF00 // Parameter number mask (excluding ellipsis)
|
||||
// 255 - >= 255
|
||||
#define MT_PARSHF 8 // shift to PARMSK
|
||||
#define MT_PARMAX 0xFF // Number limiter
|
||||
// ATT: when CC is __vectorcall and mode is 'C'
|
||||
// real argscount is unknown. This number is
|
||||
// total sizeof of all arguments divided to
|
||||
// sizeof of defptr
|
||||
#define M_ELLIPSIS 0x00010000 // The function _certainly_ has '...'
|
||||
#define MT_VOIDARG 0x0001FF00 // If = 0, the func(void), i.e. no parameters
|
||||
#define M_STATIC 0x00020000 // static
|
||||
// gcc3 - static data in a function
|
||||
// might be encountered in object files and
|
||||
// (possibly) in binaries with debug info
|
||||
#define M_VIRTUAL 0x00040000 // virtual
|
||||
// NOTE: for (D) not virtual -- this (counted!)
|
||||
#define M_AUTOCRT 0x00080000 // Most likely "autogenerated" function (data)
|
||||
// NOTE: +M_STATIC => "__linkproc__" (bc)
|
||||
|
||||
#define M_TYPMASK 0x00700000 // Special functions (0-regular function)
|
||||
#define MT_OPERAT 0x00100000 // 1 - operator
|
||||
#define MT_CONSTR 0x00200000 // 2 - constructor
|
||||
#define MT_DESTR 0x00300000 // 3 - destructor
|
||||
#define MT_CASTING 0x00400000 // 4 - type conversion
|
||||
#define MT_CLRCDTOR 0x00500000 // 5 - delphi2010 CLR ctor/dtor for packages
|
||||
// reserved
|
||||
|
||||
#define M_TRUNCATE 0x00800000 // Name was truncated by the compiler (bc/va)
|
||||
#define M_THUNK 0x01000000 // [thunk]:
|
||||
#define M_ANONNSP 0x02000000 // ms => Anonymous Namespace for field
|
||||
// gc3 => Item placed in Anonymous namespace
|
||||
// wat => anonymous_enum
|
||||
// bc => + TMPLNAM = PascalTemplate (for DCC)
|
||||
// If separate - "automatic" except_t
|
||||
// from CBuilder for "external" variables
|
||||
// or a template for global object
|
||||
// constructor/destructor tables (for CBuilder
|
||||
// as well)
|
||||
#define M_TMPLNAM 0x04000000 // ms => template name (?)
|
||||
// wat =>
|
||||
// bc => template name => its description table
|
||||
// gc3 => any template funciton/data
|
||||
#define M_DBGNAME 0x08000000 // ms => CV:
|
||||
// wat => xxxx: (T?xxxx-form)
|
||||
// bc => old pascal format (capitalized)
|
||||
// gc3 => unicode symbols or 'vendor-extension'
|
||||
// qualifiers are present
|
||||
|
||||
#define M_COMPILER 0x70000000 // Compiler mask (0-unknown)
|
||||
#define MT_MSCOMP 0x10000000 // 1 - microsoft/symantec
|
||||
#define MT_BORLAN 0x20000000 // 2 - borland
|
||||
#define MT_WATCOM 0x30000000 // 3 - watcom
|
||||
#define MT_OTHER 0x40000000 // 4 - digital mars D language (start: _D)
|
||||
// - apple Swift language (start: [_]_T)
|
||||
// !!! The following definitions must be last and in this order!
|
||||
#define MT_GNU 0x50000000 // 5 - GNU - (over VA for autodetection)
|
||||
#define MT_GCC3 0x60000000 // 6 - gcc-v3
|
||||
// In the short form this answer is possible
|
||||
// for GNU/VA as well, but gcc3 can be
|
||||
// explicitly requested only with it.
|
||||
// Autodetection works but not very reliable.
|
||||
#define MT_VISAGE 0x70000000 // 7 - Visual Age - never autodetected
|
||||
// In the short form this answer means VA
|
||||
// or GNU. In the automatic mode GNU will
|
||||
// be used!
|
||||
//---------------------------------------------------------------------------
|
||||
// Flags to inhibit different parts of the demangled name
|
||||
#define MNG_PTRMSK 0x7 // Memory model mask
|
||||
// DO NOT change order in this group (PtrType)
|
||||
#define MNG_DEFNEAR 0x0 // inhibit near, display everything else
|
||||
#define MNG_DEFNEARANY 0x1 // inhibit near/__ptr64, display everything else
|
||||
#define MNG_DEFFAR 0x2 // inhibit far, display everything else
|
||||
#define MNG_NOPTRTYP16 0x3 // inhibit everything (disables vc7-extensions)
|
||||
#define MNG_DEFHUGE 0x4 // inhibit huge, display everything else
|
||||
#define MNG_DEFPTR64 0x5 // inhibit __pt64, display everything else
|
||||
// ATT: in 64bit must be + MNG_NOTYPE|MNG_NOCALLC
|
||||
#define MNG_DEFNONE 0x6 // display everything
|
||||
#define MNG_NOPTRTYP 0x7 // inhibit everything
|
||||
//
|
||||
#define MNG_NODEFINIT 0x00000008 // Inhibit everything except the main name
|
||||
// This flag is not recommended
|
||||
// for __fastcall/__stdcall GCC3 names
|
||||
// because there is a high probablity of
|
||||
// incorrect demangling. Use it only when
|
||||
// you are sure that the input is a
|
||||
// cygwin/mingw function name
|
||||
//
|
||||
#define MNG_NOUNDERSCORE 0x00000010 // Inhibit underscores in __ccall, __pascal... +
|
||||
#define MNG_NOTYPE 0x00000020 // Inhibit callc&based
|
||||
#define MNG_NORETTYPE 0x00000040 // Inhibit return type of functions
|
||||
#define MNG_NOBASEDT 0x00000080 // Inhibit base types
|
||||
// NOTE: also inhibits "__linkproc__"
|
||||
// NOTE: -"- 'implicit self types' (Swift)
|
||||
#define MNG_NOCALLC 0x00000100 // Inhibit __pascal/__ccall/etc
|
||||
// NOTE: also inhibits "extern (cc)" (D)
|
||||
#define MNG_NOPOSTFC 0x00000200 // Inhibit postfix const
|
||||
#define MNG_NOSCTYP 0x00000400 // Inhibit public/private/protected
|
||||
// NOTE: also inhibits in/out/lazy for args (D)
|
||||
// NOTE: -"- dynamic/super/override/... (Swift)
|
||||
#define MNG_NOTHROW 0x00000800 // Inhibit throw description
|
||||
// NOTE: also inhibits all funcattr (D)
|
||||
#define MNG_NOSTVIR 0x00001000 // Inhibit "static" & "virtual"
|
||||
// NOTE: also inhibits (D) top-level procs (<=)
|
||||
#define MNG_NOECSU 0x00002000 // Inhibit class/struct/union/enum[/D:typedef]
|
||||
#define MNG_NOCSVOL 0x00004000 // Inhibit const/volatile/restrict
|
||||
// NOTE: also inhibits __unaligned (vc)
|
||||
// NOTE: also inhibits transaction_safe(gcc)
|
||||
// NOTE: also inhibits shared/immutable (D)
|
||||
// NOTE: also inhibits prefix/postfix/infix/inout (Swift)
|
||||
#define MNG_NOCLOSUR 0x00008000 // Inhibit __closure for borland
|
||||
// 'reabstract thunk' description (Swift)
|
||||
#define MNG_NOUNALG 0x00010000 // Inhibit __unaligned (see NOCSVOL)
|
||||
// NOTE: also inhibit transaction_safe (see NOCSVOL)
|
||||
#define MNG_NOMANAGE 0x00020000 // Inhibit __pin/__box/__gc for ms(.net)
|
||||
// NOTE: also inhibit archetype/witness (Swift)
|
||||
// NOTE: also ingibit [abi:xxxx] (gcc3)
|
||||
#define MNG_NOMODULE 0x00040000 // Inhibit module names (Swift)
|
||||
// 0x00080000
|
||||
//
|
||||
#define MNG_SHORT_S 0x00100000 // signed (int) is displayed as s(int)
|
||||
#define MNG_SHORT_U 0x00200000 // unsigned (int) is displayed as u(int)
|
||||
#define MNG_ZPT_SPACE 0x00400000 // Display space after comma in the arglist
|
||||
// NOTE: also spaces in name:type pair (Swift)
|
||||
// and around Swift return clause ->
|
||||
#define MNG_DROP_IMP 0x00800000 // Inhibit __declspec(dllimport)
|
||||
//
|
||||
// 0x01000000
|
||||
#define MNG_IGN_ANYWAY 0x02000000 // Ingore '_nn' at the end of name
|
||||
#define MNG_IGN_JMP 0x04000000 // Ingore 'j_' at the beginning of name
|
||||
#define MNG_MOVE_JMP 0x08000000 // Move 'j_' prefix to the demangled name
|
||||
// If both MNG_IGN_JMP and MNG_MOVE_JMP
|
||||
// are set then move the prefix only if
|
||||
// the name was not truncated
|
||||
//
|
||||
#define MNG_COMPILER_MSK 0x70000000 // Compiler mask (0-autodetect)
|
||||
|
||||
#define MNG_SHORT_FORM (MNG_NOTYPE|MNG_NORETTYPE|MNG_NOPOSTFC|MNG_NOPTRTYP \
|
||||
| MNG_NOSCTYP|MNG_NOTHROW|MNG_NOSTVIR|MNG_NOECSU|MNG_NOCLOSUR \
|
||||
| MNG_SHORT_U|MNG_DROP_IMP|MNG_NOUNALG|MNG_NOMANAGE \
|
||||
| MNG_IGN_JMP|MNG_MOVE_JMP|MNG_IGN_ANYWAY)
|
||||
#define MNG_LONG_FORM (MNG_ZPT_SPACE | MNG_IGN_JMP | MNG_IGN_ANYWAY | MNG_NOPTRTYP)
|
||||
|
||||
// The description of the following symbol is in the notes
|
||||
#define MNG_CALC_VALID (MNG_COMPILER_MSK|MNG_IGN_JMP|MNG_IGN_ANYWAY)
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
//---------------------------------------------------------------------------
|
||||
#ifndef H2ASH
|
||||
|
||||
#if !defined(NO_OBSOLETE_FUNCS) || defined(__DEFINE_DEMANGLE__)
|
||||
typedef int32 ida_export demangler_t(
|
||||
char *answer,
|
||||
uint answer_length,
|
||||
const char *str,
|
||||
uint32 disable_mask);
|
||||
idaman demangler_t demangle;
|
||||
#endif
|
||||
|
||||
// If answer_length == 0 then no demangling is performed neither. The function
|
||||
// will check if demangling is possible and what compiler is used to mangle
|
||||
// the name. If the name cannot be demangled then the function will return 0.
|
||||
// NOTE: the answer MT_MSCOMP+1 means __msfastcall
|
||||
// (or borland class name with "_4" suffix) and the demangling is possible
|
||||
// either as MS (__fastcall) or as bc (__msfastcall)
|
||||
// NOTE: the answer MT_GCC3+1 means POSSIBLE mingw/cygwin with
|
||||
// __stdcall/__fastcall but it might also mean ms-stdcall.
|
||||
// In essense it means that the demangler cannot determine the compiler
|
||||
// precisely.
|
||||
// It also means that the demangling is possible in the gcc3 mode
|
||||
// ONLY when the compiler is explicitly set to gcc3 and MNG_NODEFINIT
|
||||
// bit is not set.
|
||||
|
||||
// If answer == NULL then the demangler will return check if the demangling
|
||||
// is possible and only return the flags.
|
||||
// In this case answer_length should be enough to hold the demangled name.
|
||||
|
||||
// NOTE: If int32(answer_length) < 0 then the demangler will calcuate the
|
||||
// the number of purged bytes for the given name. In this case
|
||||
// disable_mask may contain only bits included in MNG_CALC_VALID,
|
||||
// and -answer_length must be equal to the register size (e.g.
|
||||
// sizeof(uint16/uint32/uint64)). The value of the register size
|
||||
// is used to check the numeric value in the ms stdcall/fastcall
|
||||
// encoding (it is used in the gcc mode too).
|
||||
// if return value <= 0 - no purged bytes or valid information.
|
||||
// If (value & 1) != 0 - ms stdcall (definite npurged is value-1.
|
||||
// If (value & 1) == 0 - 'encoded' counter (not implemented yet).
|
||||
|
||||
// If answer != NULL (and answer_length != 0) - perform demangling and fill out.
|
||||
// NOTE: if int32(answer_length) < 0 then the buffer size will be -answer_length
|
||||
// but 'answer' is interpreted not as a pointer to the output buffer but
|
||||
// as a pointer to pointer to the output buffer (char**).
|
||||
// In this case if the function succeeds,a pointer to the answer end
|
||||
// will be returned in the pointer (like stpcpy). In this form 'answer'
|
||||
// cannot be NULL and *(char**)answer cannot be NULL.
|
||||
// answer_length must be greater than 9 for the 'truncated' answer.
|
||||
|
||||
typedef int mangled_name_type_t;
|
||||
const mangled_name_type_t MANGLED_CODE = 0;
|
||||
const mangled_name_type_t MANGLED_DATA = 1;
|
||||
const mangled_name_type_t MANGLED_UNKNOWN = 2;
|
||||
|
||||
idaman mangled_name_type_t ida_export get_mangled_name_type(const char *name);
|
||||
|
||||
#endif // H2ASH
|
||||
#endif // _DEMANGLE_HPP
|
||||
468
idasdk76/include/dirtree.hpp
Normal file
468
idasdk76/include/dirtree.hpp
Normal file
@@ -0,0 +1,468 @@
|
||||
/*
|
||||
|
||||
This class is used to organize a directory tree on top of any
|
||||
collection that allows for accessing its elements by an id (inode).
|
||||
No requirements are imposed on the inodes apart from the forbidden
|
||||
value -1 (it is used ot denote a bad inode).
|
||||
|
||||
The dirspec_t class is used to specialize the dirtree.
|
||||
|
||||
It can be used to introduce a directory structure for:
|
||||
- local types
|
||||
- structs
|
||||
- enums
|
||||
- functions
|
||||
- names
|
||||
- etc
|
||||
|
||||
*/
|
||||
|
||||
#ifndef DIRTREE_HPP
|
||||
#define DIRTREE_HPP
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
typedef qvector<inode_t> inodevec_t; // sequence of inodes
|
||||
|
||||
/// Directory indexes are simple numbers like 0,1,2,3...
|
||||
/// They are independent of inode numbers.
|
||||
/// The root directory always exists and has the index 0 (\ref direntry_t::ROOTIDX).
|
||||
typedef uval_t diridx_t;
|
||||
typedef qvector<diridx_t> dirvec_t; // sequence of directory indexes
|
||||
|
||||
// Directory entry: either a file or directory
|
||||
// We use PACKED to save memory, without it we would spend 64 bits instead of
|
||||
// 8 bits to store a 1-bit value on ida64.
|
||||
#pragma pack(push, 1)
|
||||
struct PACKED direntry_t
|
||||
{
|
||||
uval_t idx; // diridx_t or inode_t
|
||||
bool isdir;
|
||||
|
||||
static const uval_t BADIDX = uval_t(-1);
|
||||
static const uval_t ROOTIDX = 0;
|
||||
|
||||
direntry_t(uval_t i=BADIDX, bool d=false) : idx(i), isdir(d) {}
|
||||
bool valid() const { return idx != BADIDX; }
|
||||
|
||||
bool operator==(const direntry_t &r) const
|
||||
{
|
||||
return idx == r.idx && isdir == r.isdir;
|
||||
}
|
||||
bool operator!=(const direntry_t &r) const
|
||||
{
|
||||
return !(*this == r);
|
||||
}
|
||||
bool operator<(const direntry_t &r) const
|
||||
{
|
||||
if ( !isdir && r.isdir )
|
||||
return true;
|
||||
if ( isdir && !r.isdir )
|
||||
return false;
|
||||
return idx < r.idx;
|
||||
}
|
||||
};
|
||||
#pragma pack(pop)
|
||||
DECLARE_TYPE_AS_MOVABLE(direntry_t);
|
||||
typedef qvector<direntry_t> direntry_vec_t;
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
/// Directory tree specialization. This is an abstract base class that
|
||||
/// represents 'file items' of our directory structure.
|
||||
struct dirspec_t
|
||||
{
|
||||
uint32 flags;
|
||||
enum
|
||||
{
|
||||
DSF_INODE_EA = 0x01, // inode is EA, will be handled during segment moving
|
||||
DSF_PRIVRANGE = 0x02, // inode is tid_t, structure or enum id, will be handled during segment moving
|
||||
};
|
||||
// netnode name to load/save directory tree
|
||||
// if not specified the loading/storing operations are not supported
|
||||
qstring nodename;
|
||||
|
||||
dirspec_t(const char *nm=nullptr, uint32 f=0) : flags(f), nodename(nm) {}
|
||||
|
||||
virtual ~dirspec_t() {}
|
||||
|
||||
// get the entry name. for example, the structure name
|
||||
// out may be nullptr; in this case get_name can be used to validate an inode.
|
||||
// return false if the entry does not exist.
|
||||
virtual bool get_name(qstring *out, inode_t inode) = 0;
|
||||
// get the entry inode. for example, the structure id
|
||||
virtual inode_t get_inode(diridx_t diridx, const char *name) = 0;
|
||||
// print additional attributes of the entry. for example, is union? is mapped?
|
||||
virtual qstring get_attrs(inode_t inode) const = 0;
|
||||
// rename the entry
|
||||
virtual bool rename_inode(inode_t inode, const char *newname) = 0;
|
||||
// event: unlinked an inode
|
||||
virtual void unlink_inode(inode_t) {}
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
/// Position in the directory tree
|
||||
struct dirtree_cursor_t
|
||||
{
|
||||
diridx_t parent;
|
||||
size_t rank; // item rank: index into parent.subdirs or parent.inodes
|
||||
// if the index value is greated than parent.subdirs.size()
|
||||
// then it points to parent.inodes (after -= parent.subdirs.size())
|
||||
dirtree_cursor_t(diridx_t _parent=direntry_t::BADIDX, size_t _rank=size_t(-1))
|
||||
: parent(_parent), rank(_rank) {}
|
||||
bool valid() const { return parent != direntry_t::BADIDX || rank == 0; }
|
||||
bool is_root_cursor() const { return parent == direntry_t::BADIDX && rank == 0; }
|
||||
void set_root_cursor(void) { parent = direntry_t::BADIDX; rank = 0; }
|
||||
|
||||
static dirtree_cursor_t root_cursor()
|
||||
{
|
||||
dirtree_cursor_t c;
|
||||
c.set_root_cursor();
|
||||
return c;
|
||||
}
|
||||
|
||||
DECLARE_COMPARISONS(dirtree_cursor_t)
|
||||
{
|
||||
if ( parent < r.parent ) return -1;
|
||||
if ( parent > r.parent ) return 1;
|
||||
if ( rank < r.rank ) return -1;
|
||||
if ( rank > r.rank ) return 1;
|
||||
return 0;
|
||||
}
|
||||
};
|
||||
DECLARE_TYPE_AS_MOVABLE(dirtree_cursor_t);
|
||||
typedef qvector<dirtree_cursor_t> dirtree_cursor_vec_t;
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
struct dirtree_selection_t : public dirtree_cursor_vec_t {};
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
/// Helper class to iterate over files
|
||||
struct dirtree_iterator_t
|
||||
{
|
||||
qstring pattern;
|
||||
dirtree_cursor_t cursor;
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
// Directory tree: error codes
|
||||
enum dterr_t
|
||||
{
|
||||
DTE_OK, // ok
|
||||
DTE_ALREADY_EXISTS, // item already exists
|
||||
DTE_NOT_FOUND, // item not found
|
||||
DTE_NOT_DIRECTORY, // item is not a directory
|
||||
DTE_NOT_EMPTY, // directory is not empty
|
||||
DTE_BAD_PATH, // invalid path
|
||||
DTE_CANT_RENAME, // failed to rename an item
|
||||
DTE_OWN_CHILD, // moving inside subdirectory of itself
|
||||
DTE_MAX_DIR, // maximum directory count achieved
|
||||
DTE_LAST,
|
||||
};
|
||||
|
||||
class dirtree_t;
|
||||
class dirtree_impl_t;
|
||||
struct segm_move_infos_t;
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
// internal functions; use dirtree_t members instead
|
||||
#ifndef SWIG
|
||||
idaman dirtree_impl_t *ida_export create_dirtree(dirtree_t *dt, dirspec_t *ds);
|
||||
idaman void ida_export delete_dirtree(dirtree_impl_t *d);
|
||||
idaman bool ida_export load_dirtree(dirtree_impl_t *d);
|
||||
idaman bool ida_export save_dirtree(dirtree_impl_t *d);
|
||||
void reset_dirtree(dirtree_impl_t *d);
|
||||
idaman const char *ida_export dirtree_errstr(dterr_t err);
|
||||
idaman dterr_t ida_export dirtree_chdir(dirtree_impl_t *d, const char *path);
|
||||
idaman void ida_export dirtree_getcwd(qstring *out, const dirtree_impl_t *d);
|
||||
idaman void ida_export dirtree_resolve_path(direntry_t *de, const dirtree_impl_t *d, const char *path);
|
||||
idaman void ida_export dirtree_resolve_cursor(direntry_t *de, const dirtree_impl_t *d, const dirtree_cursor_t &cursor);
|
||||
idaman bool ida_export dirtree_get_entry_name(qstring *out, const dirtree_impl_t *d, const direntry_t &de);
|
||||
idaman void ida_export dirtree_get_entry_attrs(qstring *out, const dirtree_impl_t *d, const direntry_t &de);
|
||||
idaman ssize_t ida_export dirtree_get_dir_size(dirtree_impl_t *d, diridx_t diridx);
|
||||
idaman bool ida_export dirtree_findfirst(dirtree_impl_t *d, dirtree_iterator_t *ff, const char *pattern);
|
||||
idaman bool ida_export dirtree_findnext(dirtree_impl_t *d, dirtree_iterator_t *ff);
|
||||
idaman bool ida_export dirtree_get_abspath_by_cursor(qstring *out, const dirtree_impl_t *d, const dirtree_cursor_t &cursor);
|
||||
idaman bool ida_export dirtree_get_abspath_by_relpath(qstring *out, const dirtree_impl_t *d, const char *relpath);
|
||||
idaman dterr_t ida_export dirtree_mkdir(dirtree_impl_t *d, const char *path);
|
||||
idaman dterr_t ida_export dirtree_rmdir(dirtree_impl_t *d, const char *path);
|
||||
idaman dterr_t ida_export dirtree_link(dirtree_impl_t *d, const char *path, bool do_link);
|
||||
idaman dterr_t ida_export dirtree_link_inode(dirtree_impl_t *d, inode_t inode, bool do_link);
|
||||
idaman dterr_t ida_export dirtree_rename(dirtree_impl_t *d, const char *from, const char *to);
|
||||
idaman ssize_t ida_export dirtree_get_rank(const dirtree_impl_t *d, diridx_t diridx, const direntry_t &de);
|
||||
idaman dterr_t ida_export dirtree_change_rank(dirtree_impl_t *d, const char *path, ssize_t rank_delta);
|
||||
idaman void ida_export dirtree_get_parent_cursor(dirtree_cursor_t *out, const dirtree_impl_t *d, const dirtree_cursor_t &cursor);
|
||||
idaman void ida_export notify_dirtree(dirtree_impl_t *d, bool added, inode_t inode);
|
||||
idaman const char *ida_export dirtree_get_nodename(const dirtree_impl_t *d);
|
||||
idaman void ida_export dirtree_set_nodename(dirtree_impl_t *d, const char *nm);
|
||||
#endif // SWIG
|
||||
|
||||
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
/// Directory tree.
|
||||
/// This class organizes a virtual directory tree over items that
|
||||
/// are represented by dirspec_t.
|
||||
class dirtree_t
|
||||
{
|
||||
dirtree_impl_t *d;
|
||||
|
||||
|
||||
public:
|
||||
//lint -sem(dirtree_t::dirtree_t, custodial(1))
|
||||
dirtree_t(dirspec_t *ds) { d = create_dirtree(this, ds); }
|
||||
~dirtree_t() { delete_dirtree(d); }
|
||||
|
||||
/// Get textual representation of the error code
|
||||
static const char *errstr(dterr_t err) { return dirtree_errstr(err); }
|
||||
|
||||
/// Change current directory
|
||||
/// \param path new current directory
|
||||
/// \return \ref dterr_t error code
|
||||
dterr_t chdir(const char *path) { return dirtree_chdir(d, path); }
|
||||
|
||||
/// Get current directory
|
||||
/// \return the current working directory
|
||||
qstring getcwd() const
|
||||
{
|
||||
qstring out;
|
||||
dirtree_getcwd(&out, d);
|
||||
return out;
|
||||
}
|
||||
|
||||
/// Get absolute path pointed by the cursor
|
||||
/// \param cursor
|
||||
/// \return path; empty string if error
|
||||
/// \note see also resolve_cursor()
|
||||
qstring get_abspath(const dirtree_cursor_t &cursor) const
|
||||
{
|
||||
qstring out;
|
||||
dirtree_get_abspath_by_cursor(&out, d, cursor);
|
||||
return out;
|
||||
}
|
||||
|
||||
/// Construct an absolute path from the specified relative path.
|
||||
/// This function verifies the directory part of the specified path.
|
||||
/// The last component of the specified path is not verified.
|
||||
/// \param relpath relative path
|
||||
/// \return path. empty path means wrong directory part of RELPATH
|
||||
qstring get_abspath(const char *relpath) const
|
||||
{
|
||||
qstring out;
|
||||
dirtree_get_abspath_by_relpath(&out, d, relpath);
|
||||
return out;
|
||||
}
|
||||
|
||||
/// Resolve cursor
|
||||
/// \param cursor to analyze
|
||||
/// \return directory entry;
|
||||
/// if the cursor is bad, the resolved entry will be invalid.
|
||||
/// \note see also get_abspath()
|
||||
direntry_t resolve_cursor(const dirtree_cursor_t &cursor) const
|
||||
{
|
||||
direntry_t de;
|
||||
dirtree_resolve_cursor(&de, d, cursor);
|
||||
return de;
|
||||
}
|
||||
|
||||
/// Resolve path
|
||||
/// \param path to analyze
|
||||
/// \return directory entry
|
||||
direntry_t resolve_path(const char *path) const
|
||||
{
|
||||
direntry_t de;
|
||||
dirtree_resolve_path(&de, d, path);
|
||||
return de;
|
||||
}
|
||||
|
||||
static bool isdir(const direntry_t &de) { return de.valid() && de.isdir; }
|
||||
static bool isfile(const direntry_t &de) { return de.valid() && !de.isdir; }
|
||||
|
||||
/// Is a directory?
|
||||
/// \param path to analyze
|
||||
/// \return true if the specified path is a directory
|
||||
bool isdir(const char *path) const
|
||||
{
|
||||
direntry_t de = resolve_path(path);
|
||||
return isdir(de);
|
||||
}
|
||||
|
||||
/// Is a file?
|
||||
/// \param path to analyze
|
||||
/// \return true if the specified path is a file
|
||||
bool isfile(const char *path) const
|
||||
{
|
||||
direntry_t de = resolve_path(path);
|
||||
return isfile(de);
|
||||
}
|
||||
|
||||
/// Get entry name
|
||||
/// \param de directory entry
|
||||
/// \return name
|
||||
qstring get_entry_name(const direntry_t &de) const
|
||||
{
|
||||
qstring out;
|
||||
dirtree_get_entry_name(&out, d, de);
|
||||
return out;
|
||||
}
|
||||
|
||||
/// Get dir size
|
||||
/// \param diridx directory index
|
||||
/// \return number of entries under this directory;
|
||||
/// if error, return -1
|
||||
ssize_t get_dir_size(diridx_t diridx) const { return dirtree_get_dir_size(d, diridx); }
|
||||
|
||||
/// Get entry attributes
|
||||
/// \param de directory entry
|
||||
/// \return name
|
||||
qstring get_entry_attrs(const direntry_t &de) const
|
||||
{
|
||||
qstring out;
|
||||
dirtree_get_entry_attrs(&out, d, de);
|
||||
return out;
|
||||
}
|
||||
|
||||
/// Start iterating over files in a directory
|
||||
/// \param ff directory iterator. it will be initialized by the function
|
||||
/// \param pattern pattern to search for
|
||||
/// \return success
|
||||
bool findfirst(dirtree_iterator_t *ff, const char *pattern) const
|
||||
{
|
||||
return dirtree_findfirst(d, ff, pattern);
|
||||
}
|
||||
|
||||
/// Continue iterating over files in a directory
|
||||
/// \param ff directory iterator
|
||||
/// \return success
|
||||
bool findnext(dirtree_iterator_t *ff) const
|
||||
{
|
||||
return dirtree_findnext(d, ff);
|
||||
}
|
||||
|
||||
/// Create a directory.
|
||||
/// \param path directory to create
|
||||
/// \return \ref dterr_t error code
|
||||
dterr_t mkdir(const char *path) { return dirtree_mkdir(d, path); }
|
||||
|
||||
/// Remove a directory.
|
||||
/// \param path directory to delete
|
||||
/// \return \ref dterr_t error code
|
||||
dterr_t rmdir(const char *path) { return dirtree_rmdir(d, path); }
|
||||
|
||||
/// Add a file item into a directory.
|
||||
/// \param path path to item to add to a directory
|
||||
/// \return \ref dterr_t error code
|
||||
dterr_t link(const char *path) { return dirtree_link(d, path, true); }
|
||||
|
||||
/// Remove a file item from a directory.
|
||||
/// \param path path to item remove from a directory
|
||||
/// \return \ref dterr_t error code
|
||||
dterr_t unlink(const char *path) { return dirtree_link(d, path, false); }
|
||||
|
||||
/// Add an inode into the current directory
|
||||
/// \param inode
|
||||
/// \return \ref dterr_t error code
|
||||
dterr_t link(inode_t inode) { return dirtree_link_inode(d, inode, true); }
|
||||
|
||||
/// Remove an inode from the current directory
|
||||
/// \param inode
|
||||
/// \return \ref dterr_t error code
|
||||
dterr_t unlink(inode_t inode) { return dirtree_link_inode(d, inode, false); }
|
||||
|
||||
/// Rename a directory entry.
|
||||
/// \param from source path
|
||||
/// \param to destination path
|
||||
/// \return \ref dterr_t error code
|
||||
/// \note This function can also rename the item
|
||||
dterr_t rename(const char *from, const char *to)
|
||||
{
|
||||
return dirtree_rename(d, from, to);
|
||||
}
|
||||
|
||||
/// Get ordering rank of an item.
|
||||
/// \param diridx index of the parent directory
|
||||
/// \param de directory entry
|
||||
/// \return number in a range of [0..n) where n is the number of entries in
|
||||
/// the parent directory. -1 if error
|
||||
ssize_t get_rank(diridx_t diridx, const direntry_t &de) const
|
||||
{
|
||||
return dirtree_get_rank(d, diridx, de);
|
||||
}
|
||||
|
||||
/// Change ordering rank of an item.
|
||||
/// \param path path to the item
|
||||
/// \param delta the amount of the change. positive numbers mean to move down
|
||||
/// in the list; negative numbers mean to move up.
|
||||
/// \return \ref dterr_t error code
|
||||
/// \note All subdirectories go before all file entries.
|
||||
dterr_t change_rank(const char *path, ssize_t rank_delta)
|
||||
{
|
||||
return dirtree_change_rank(d, path, rank_delta);
|
||||
}
|
||||
|
||||
/// Get parent cursor.
|
||||
/// \param cursor a valid ditree cursor
|
||||
/// \return cursor's parent
|
||||
dirtree_cursor_t get_parent_cursor(const dirtree_cursor_t &cursor) const
|
||||
{
|
||||
dirtree_cursor_t parent;
|
||||
dirtree_get_parent_cursor(&parent, d, cursor);
|
||||
return parent;
|
||||
}
|
||||
|
||||
/// Load the tree structure from the netnode.
|
||||
/// If dirspec_t::nodename is empty, the operation will be considered a success.
|
||||
/// In addition, calling load() more than once will not do anything,
|
||||
/// and will be considered a success.
|
||||
/// \return success
|
||||
/// \see dirspec_t::nodename.
|
||||
bool load()
|
||||
{
|
||||
return load_dirtree(d);
|
||||
}
|
||||
|
||||
/// Save the tree structure to the netnode.
|
||||
/// \return success
|
||||
/// \see dirspec_t::nodename.
|
||||
bool save() const
|
||||
{
|
||||
return save_dirtree(d);
|
||||
}
|
||||
|
||||
/// netnode name
|
||||
const char *get_nodename() const
|
||||
{
|
||||
return dirtree_get_nodename(d);
|
||||
}
|
||||
|
||||
void set_nodename(const char *nm)
|
||||
{
|
||||
return dirtree_set_nodename(d, nm);
|
||||
}
|
||||
|
||||
/// Notify dirtree about a change of an inode.
|
||||
/// \param add are we adding or deleting an inode?
|
||||
/// \param inode inode in question
|
||||
void notify_dirtree(bool added, inode_t inode)
|
||||
{
|
||||
::notify_dirtree(d, added, inode);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
/// Built-in dirtree specializations:
|
||||
enum dirtree_id_t
|
||||
{
|
||||
DIRTREE_LOCAL_TYPES,
|
||||
DIRTREE_STRUCTS,
|
||||
DIRTREE_ENUMS,
|
||||
DIRTREE_FUNCS,
|
||||
DIRTREE_NAMES,
|
||||
DIRTREE_IMPORTS,
|
||||
DIRTREE_IDAPLACE_BOOKMARKS,
|
||||
DIRTREE_STRUCTS_BOOKMARKS,
|
||||
DIRTREE_ENUMS_BOOKMARKS,
|
||||
DIRTREE_END,
|
||||
};
|
||||
idaman dirtree_t *ida_export get_std_dirtree(dirtree_id_t id);
|
||||
|
||||
|
||||
#endif // define DIRTREE_HPP
|
||||
621
idasdk76/include/diskio.hpp
Normal file
621
idasdk76/include/diskio.hpp
Normal file
@@ -0,0 +1,621 @@
|
||||
/*
|
||||
* Interactive disassembler (IDA).
|
||||
* Copyright (c) 1990-2021 Hex-Rays
|
||||
* ALL RIGHTS RESERVED.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef _DISKIO_HPP
|
||||
#define _DISKIO_HPP
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
/*! \file diskio.hpp
|
||||
|
||||
\brief File I/O functions for IDA
|
||||
|
||||
You should not use standard C file I/O functions in modules.
|
||||
Use functions from this header, pro.h and fpro.h instead.
|
||||
|
||||
This file also declares a call_system() function.
|
||||
*/
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
// S E A R C H F O R F I L E S
|
||||
//-------------------------------------------------------------------------
|
||||
|
||||
|
||||
/// Get IDA directory (if subdir==NULL)
|
||||
/// or the specified subdirectory (see \ref SUBDIR)
|
||||
|
||||
idaman THREAD_SAFE const char *ida_export idadir(const char *subdir);
|
||||
|
||||
|
||||
/// Search for IDA system file.
|
||||
/// This function searches for a file in:
|
||||
/// -# each directory specified by %IDAUSR%
|
||||
/// -# ida directory [+ subdir]
|
||||
/// and returns the first match.
|
||||
/// \param[out] buf buffer for file name
|
||||
/// \param bufsize size of output buffer
|
||||
/// \param filename name of file to search
|
||||
/// \param subdir if specified, the file is looked for in the specified subdirectory
|
||||
/// of the ida directory first (see \ref SUBDIR)
|
||||
/// \return NULL if not found, otherwise a pointer to full file name.
|
||||
|
||||
idaman THREAD_SAFE char *ida_export getsysfile(
|
||||
char *buf,
|
||||
size_t bufsize,
|
||||
const char *filename,
|
||||
const char *subdir);
|
||||
|
||||
/// \defgroup SUBDIR IDA subdirectories
|
||||
/// Passed as 'subdir' parameter to idadir(), getsysfile(), and others.
|
||||
//@{
|
||||
#define CFG_SUBDIR "cfg"
|
||||
#define IDC_SUBDIR "idc"
|
||||
#define IDS_SUBDIR "ids"
|
||||
#define IDP_SUBDIR "procs"
|
||||
#define LDR_SUBDIR "loaders"
|
||||
#define SIG_SUBDIR "sig"
|
||||
#define TIL_SUBDIR "til"
|
||||
#define PLG_SUBDIR "plugins"
|
||||
#define THM_SUBDIR "themes"
|
||||
//@}
|
||||
|
||||
/// Get user ida related directory.
|
||||
/// \code
|
||||
/// - if $IDAUSR is defined:
|
||||
/// - the first element in $IDAUSR
|
||||
/// - else
|
||||
/// - default user directory ($HOME/.idapro or %APPDATA%Hex-Rays/IDA Pro)
|
||||
/// \endcode
|
||||
|
||||
idaman THREAD_SAFE const char *ida_export get_user_idadir(void);
|
||||
|
||||
|
||||
/// Get list of directories in which to find a specific IDA resource
|
||||
/// (see \ref SUBDIR). The order of the resulting list is as follows:
|
||||
/// \code
|
||||
/// - [$IDAUSR/subdir (0..N entries)]
|
||||
/// - $IDADIR/subdir
|
||||
/// \endcode
|
||||
/// \param[out] dirs output vector for directory names
|
||||
/// \param subdir name of the resource to list
|
||||
/// \param flags \ref IDA_SUBDIR_ bits
|
||||
/// \return number of directories appended to 'dirs'
|
||||
|
||||
idaman THREAD_SAFE int ida_export get_ida_subdirs(qstrvec_t *dirs, const char *subdir, int flags=0);
|
||||
|
||||
/// \defgroup IDA_SUBDIR_ Subdirectory modification flags
|
||||
/// Passed as 'flags' parameter to get_ida_subdirs()
|
||||
//@{
|
||||
#define IDA_SUBDIR_IDP 0x0001 ///< append the processor name as a subdirectory
|
||||
#define IDA_SUBDIR_IDADIR_FIRST 0x0002 ///< $IDADIR/subdir will be first, not last
|
||||
#define IDA_SUBDIR_ONLY_EXISTING 0x0004 ///< only existing directories will be present
|
||||
//@}
|
||||
|
||||
|
||||
/// Get a folder location by CSIDL (see \ref CSIDL).
|
||||
/// Path should be of at least MAX_PATH size
|
||||
|
||||
idaman THREAD_SAFE bool ida_export get_special_folder(char *buf, size_t bufsize, int csidl);
|
||||
|
||||
/// \defgroup CSIDL Common CSIDLs
|
||||
/// Passed as 'csidl' parameter to get_special_folder()
|
||||
//@{
|
||||
#ifndef CSIDL_APPDATA
|
||||
#define CSIDL_APPDATA 0x001a
|
||||
#endif
|
||||
#ifndef CSIDL_LOCAL_APPDATA
|
||||
#define CSIDL_LOCAL_APPDATA 0x001c
|
||||
#endif
|
||||
#ifndef CSIDL_PROGRAM_FILES
|
||||
#define CSIDL_PROGRAM_FILES 0x0026
|
||||
#endif
|
||||
#ifndef CSIDL_PROGRAM_FILES_COMMON
|
||||
#define CSIDL_PROGRAM_FILES_COMMON 0x002b
|
||||
#endif
|
||||
#ifndef CSIDL_PROGRAM_FILESX86
|
||||
#define CSIDL_PROGRAM_FILESX86 0x002a
|
||||
#endif
|
||||
//@}
|
||||
|
||||
/// Enumerate files in the specified directory.
|
||||
/// \param[out] answer buffer to contain the file name for which
|
||||
/// file_enumerator_t::visit_file returns non-zero value
|
||||
/// (may be NULL)
|
||||
/// \param answer_size size of 'answer'
|
||||
/// \param path directory to enumerate files in
|
||||
/// \param fname mask of file names to enumerate
|
||||
/// \param fv file_enumerator_t::visit_file function called for each file
|
||||
/// - file: full file name (with path)
|
||||
/// - if returns non-zero value, the enumeration
|
||||
/// is stopped and the return code is
|
||||
/// is returned to the caller.
|
||||
/// the callback function
|
||||
/// \return zero or the code returned by 'func'
|
||||
|
||||
struct file_enumerator_t
|
||||
{
|
||||
virtual int visit_file(const char *file) = 0;
|
||||
virtual ~file_enumerator_t() {}
|
||||
};
|
||||
|
||||
idaman THREAD_SAFE int ida_export enumerate_files2(
|
||||
char *answer,
|
||||
size_t answer_size,
|
||||
const char *path,
|
||||
const char *fname,
|
||||
file_enumerator_t &fv);
|
||||
|
||||
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
// O P E N / R E A D / W R I T E / C L O S E F I L E S
|
||||
//-------------------------------------------------------------------------
|
||||
|
||||
/// \name Open/Read/Write/Close Files
|
||||
/// There are two sets of "open file" functions.
|
||||
/// The first set tries to open a file and returns success or failure.
|
||||
/// The second set is "open or die": if the file cannot be opened
|
||||
/// then the function will display an error message and exit.
|
||||
//@{
|
||||
|
||||
/// Open a new file for write in text mode, deny write.
|
||||
/// If a file exists, it will be removed.
|
||||
/// \return NULL if failure
|
||||
|
||||
idaman THREAD_SAFE FILE *ida_export fopenWT(const char *file);
|
||||
|
||||
|
||||
/// Open a new file for write in binary mode, deny read/write.
|
||||
/// If a file exists, it will be removed.
|
||||
/// \return NULL if failure
|
||||
|
||||
idaman THREAD_SAFE FILE *ida_export fopenWB(const char *file);
|
||||
|
||||
|
||||
/// Open a file for read in text mode, deny none.
|
||||
/// \return NULL if failure
|
||||
|
||||
idaman THREAD_SAFE FILE *ida_export fopenRT(const char *file);
|
||||
|
||||
|
||||
/// Open a file for read in binary mode, deny none.
|
||||
/// \return NULL if failure
|
||||
|
||||
idaman THREAD_SAFE FILE *ida_export fopenRB(const char *file);
|
||||
|
||||
|
||||
/// Open a file for read/write in binary mode, deny write.
|
||||
/// \return NULL if failure
|
||||
|
||||
idaman THREAD_SAFE FILE *ida_export fopenM(const char *file);
|
||||
|
||||
|
||||
/// Open a file for append in text mode, deny none.
|
||||
/// \return NULL if failure
|
||||
|
||||
idaman THREAD_SAFE FILE *ida_export fopenA(const char *file);
|
||||
|
||||
|
||||
/// Open a file for read in binary mode or die, deny none.
|
||||
/// If a file cannot be opened, this function displays a message and exits.
|
||||
|
||||
idaman THREAD_SAFE FILE *ida_export openR(const char *file);
|
||||
|
||||
|
||||
/// Open a file for read in text mode or die, deny none.
|
||||
/// If a file cannot be opened, this function displays a message and exits.
|
||||
|
||||
idaman THREAD_SAFE FILE *ida_export openRT(const char *file);
|
||||
|
||||
|
||||
/// Open a file for read/write in binary mode or die, deny write.
|
||||
/// If a file cannot be opened, this function displays a message and exits.
|
||||
|
||||
idaman THREAD_SAFE FILE *ida_export openM(const char *file);
|
||||
|
||||
|
||||
//@}
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
// F I L E S I Z E / D I S K S P A C E
|
||||
//-------------------------------------------------------------------------
|
||||
|
||||
/// Get length of file in bytes.
|
||||
/// \param fp pointer to file
|
||||
|
||||
idaman THREAD_SAFE uint64 ida_export qfsize(FILE *fp);
|
||||
|
||||
|
||||
/// Change size of file or die.
|
||||
/// If an error occurs, this function displays a message and exits.
|
||||
/// \param fp pointer to file
|
||||
/// \param size new size of file
|
||||
|
||||
idaman THREAD_SAFE void ida_export echsize(FILE *fp, uint64 size);
|
||||
|
||||
|
||||
/// Get free disk space in bytes.
|
||||
/// \param path name of any directory on the disk to get information about
|
||||
|
||||
idaman THREAD_SAFE uint64 ida_export get_free_disk_space(const char *path);
|
||||
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
// I / O P O R T D E F I N I T I O N S F I L E
|
||||
//-------------------------------------------------------------------------
|
||||
/// Describes an I/O port bit
|
||||
struct ioport_bit_t
|
||||
{
|
||||
qstring name; ///< name of the bit
|
||||
qstring cmt; ///< comment
|
||||
};
|
||||
DECLARE_TYPE_AS_MOVABLE(ioport_bit_t);
|
||||
typedef qvector<ioport_bit_t> ioport_bits_t;
|
||||
|
||||
/// Describes an I/O port
|
||||
struct ioport_t
|
||||
{
|
||||
ea_t address; ///< address of the port
|
||||
qstring name; ///< name of the port
|
||||
qstring cmt; ///< comment
|
||||
ioport_bits_t bits; ///< bit names
|
||||
void *userdata; ///< arbitrary data. initialized to NULL.
|
||||
|
||||
ioport_t()
|
||||
: address(0), userdata(NULL)
|
||||
{
|
||||
}
|
||||
};
|
||||
DECLARE_TYPE_AS_MOVABLE(ioport_t);
|
||||
typedef qvector<ioport_t> ioports_t;
|
||||
|
||||
/// Read i/o port definitions from a config file.
|
||||
///
|
||||
/// Each device definition in the input file begins with a line like this:
|
||||
///
|
||||
/// \v{.devicename}
|
||||
///
|
||||
/// After it go the port definitions in this format:
|
||||
///
|
||||
/// \v{portname address}
|
||||
///
|
||||
/// The bit definitions (optional) are represented like this:
|
||||
///
|
||||
/// \v{portname.bitname bitnumber}
|
||||
///
|
||||
/// Lines beginning with a space are ignored.
|
||||
/// comment lines should be started with ';' character.
|
||||
///
|
||||
/// The default device is specified at the start of the file:
|
||||
///
|
||||
/// \v{.default device_name}
|
||||
///
|
||||
/// \note It is permissible to have a symbol mapped to several addresses
|
||||
/// but all addresses must be unique.
|
||||
/// \param[out] ports output vector
|
||||
/// \param device contains device name to load. If default_device[0] == 0
|
||||
/// then the default device is determined by .default directive
|
||||
/// in the config file.
|
||||
/// \param file config file name
|
||||
/// \param callback callback to call when the input line can't be parsed normally.
|
||||
/// - line: input line to parse
|
||||
/// - returns error message. if NULL, then the line is parsed ok.
|
||||
/// \return -1 on error or size of vector
|
||||
|
||||
idaman THREAD_SAFE ssize_t ida_export read_ioports(
|
||||
ioports_t *ports,
|
||||
qstring *device,
|
||||
const char *file,
|
||||
const char *(idaapi *callback)(
|
||||
const ioports_t &ports,
|
||||
const char *line)=NULL);
|
||||
|
||||
|
||||
struct ioports_fallback_t
|
||||
{
|
||||
// returns success or fills ERRBUF with an error message
|
||||
virtual bool handle(qstring *errbuf, const ioports_t &ports, const char *line) = 0;
|
||||
};
|
||||
|
||||
idaman THREAD_SAFE ssize_t ida_export read_ioports2(
|
||||
ioports_t *ports,
|
||||
qstring *device,
|
||||
const char *file,
|
||||
ioports_fallback_t *callback=nullptr);
|
||||
|
||||
|
||||
/// Allow the user to choose the ioport device.
|
||||
/// \param[in,out] device in: contains default device name. If default_device[0] == 0
|
||||
/// then the default device is determined by .default directive
|
||||
/// in the config file.
|
||||
/// out: the selected device name
|
||||
/// \param file config file name
|
||||
/// \param parse_params if present (non NULL), then defines a callback which
|
||||
/// will be called for all lines not starting with a dot (.)
|
||||
/// This callback may parse these lines are prepare a simple
|
||||
/// processor parameter string. This string will be displayed
|
||||
/// along with the device name.
|
||||
/// If it returns #IOPORT_SKIP_DEVICE, then the current
|
||||
/// device will not be included in the list.
|
||||
/// \retval true the user selected a device, its name is in 'device'
|
||||
/// \retval false the selection was cancelled. if device=="NONE" upon return,
|
||||
/// then no devices were found in the configuration file
|
||||
|
||||
idaman THREAD_SAFE bool ida_export choose_ioport_device(
|
||||
qstring *_device,
|
||||
const char *file,
|
||||
const char *(idaapi *parse_params)(
|
||||
qstring *buf,
|
||||
const char *line)=NULL);
|
||||
|
||||
struct choose_ioport_parser_t
|
||||
{
|
||||
/// \retval true and fill PARAM with a displayed string
|
||||
/// \retval false and empty PARAM to skip the current device
|
||||
/// \retval false and fill PARAM with an error message
|
||||
virtual bool parse(qstring *param, const char *line) = 0;
|
||||
};
|
||||
|
||||
idaman THREAD_SAFE bool ida_export choose_ioport_device2(
|
||||
qstring *_device,
|
||||
const char *file,
|
||||
choose_ioport_parser_t *parse_params);
|
||||
|
||||
/// See 'parse_params' parameter to choose_ioport_device()
|
||||
#define IOPORT_SKIP_DEVICE ((const char *)(-1))
|
||||
|
||||
|
||||
/// Find ioport in the array of ioports
|
||||
|
||||
idaman THREAD_SAFE const ioport_t *ida_export find_ioport(const ioports_t &ports, ea_t address);
|
||||
|
||||
|
||||
/// Find ioport bit in the array of ioports
|
||||
|
||||
idaman THREAD_SAFE const ioport_bit_t *ida_export find_ioport_bit(const ioports_t &ports, ea_t address, size_t bit);
|
||||
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
// S Y S T E M S P E C I F I C C A L L S
|
||||
//-------------------------------------------------------------------------
|
||||
|
||||
/// Execute a operating system command.
|
||||
/// This function suspends the interface (Tvision), runs the command
|
||||
/// and redraws the screen.
|
||||
/// \param command command to execute. If NULL, an interactive shell is activated
|
||||
/// \return the error code returned by system() call
|
||||
|
||||
idaman THREAD_SAFE int ida_export call_system(const char *command);
|
||||
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
// L O A D E R I N P U T S O U R C E F U N C T I O N S
|
||||
//-------------------------------------------------------------------------
|
||||
|
||||
/// \name Loader Input Source
|
||||
/// Starting with v4.8 IDA can load and run remote files.
|
||||
/// In order to do that, we replace the FILE* in the loader modules
|
||||
/// with an abstract input source (linput_t). The source might be linked to
|
||||
/// a local or remote file.
|
||||
//@{
|
||||
|
||||
class linput_t; ///< loader input source
|
||||
|
||||
|
||||
/// linput types
|
||||
enum linput_type_t
|
||||
{
|
||||
LINPUT_NONE, ///< invalid linput
|
||||
LINPUT_LOCAL, ///< local file
|
||||
LINPUT_RFILE, ///< remote file (\dbg{open_file}, \dbg{read_file})
|
||||
LINPUT_PROCMEM, ///< debugged process memory (read_dbg_memory())
|
||||
LINPUT_GENERIC ///< generic linput
|
||||
};
|
||||
|
||||
|
||||
/// Read the input source.
|
||||
/// If failed, inform the user and ask him if he wants to continue.
|
||||
/// If he does not, this function will not return (loader_failure() will be called).
|
||||
/// This function may be called only from loaders!
|
||||
|
||||
idaman void ida_export lread(linput_t *li, void *buf, size_t size);
|
||||
|
||||
|
||||
/// Read the input source.
|
||||
/// \return number of read bytes or -1
|
||||
|
||||
idaman ssize_t ida_export qlread(linput_t *li, void *buf, size_t size);
|
||||
|
||||
|
||||
/// Read one line from the input source.
|
||||
/// \return NULL if failure, otherwise 's'
|
||||
|
||||
idaman char *ida_export qlgets(char *s, size_t len, linput_t *li);
|
||||
|
||||
|
||||
/// Read one character from the input source.
|
||||
/// \return EOF if failure, otherwise the read character
|
||||
|
||||
idaman int ida_export qlgetc(linput_t *li);
|
||||
|
||||
|
||||
/// Read multiple bytes and swap if necessary.
|
||||
/// \param li input file
|
||||
/// \param buf pointer to output buffer
|
||||
/// \param size number of bytes to read
|
||||
/// \param mf big endian?
|
||||
/// \retval 0 ok
|
||||
/// \retval -1 failure
|
||||
|
||||
idaman int ida_export lreadbytes(linput_t *li, void *buf, size_t size, bool mf);
|
||||
|
||||
/// Helper to define lread2bytes(), lread4bytes(), etc
|
||||
#define DEF_LREADBYTES(read, type, size) \
|
||||
/*! \brief Read a value from linput - also see lreadbytes() */ \
|
||||
inline int idaapi read(linput_t *li, type *res, bool mf) \
|
||||
{ return lreadbytes(li, res, size, mf); }
|
||||
DEF_LREADBYTES(lread2bytes, int16, 2)
|
||||
DEF_LREADBYTES(lread2bytes, uint16, 2)
|
||||
DEF_LREADBYTES(lread4bytes, int32, 4)
|
||||
DEF_LREADBYTES(lread4bytes, uint32, 4)
|
||||
DEF_LREADBYTES(lread8bytes, int64, 8)
|
||||
DEF_LREADBYTES(lread8bytes, uint64, 8)
|
||||
#undef DEF_LREADBYTES
|
||||
|
||||
|
||||
/// Read a zero-terminated string from the input.
|
||||
/// If fpos == -1 then no seek will be performed.
|
||||
|
||||
idaman char *ida_export qlgetz(
|
||||
linput_t *li,
|
||||
int64 fpos,
|
||||
char *buf,
|
||||
size_t bufsize);
|
||||
|
||||
|
||||
/// Get the input source size
|
||||
|
||||
idaman int64 ida_export qlsize(linput_t *li);
|
||||
|
||||
|
||||
/// Set input source position.
|
||||
/// \return the new position (not 0 as fseek!)
|
||||
|
||||
idaman qoff64_t ida_export qlseek(linput_t *li, qoff64_t pos, int whence=SEEK_SET);
|
||||
|
||||
|
||||
/// Get input source position
|
||||
|
||||
inline qoff64_t idaapi qltell(linput_t *li) { return qlseek(li, 0, SEEK_CUR); }
|
||||
|
||||
|
||||
/// Open loader input
|
||||
|
||||
idaman linput_t *ida_export open_linput(const char *file, bool remote);
|
||||
|
||||
|
||||
/// Close loader input
|
||||
|
||||
idaman THREAD_SAFE void ida_export close_linput(linput_t *li);
|
||||
|
||||
|
||||
/// Get FILE* from the input source.
|
||||
/// If the input source is linked to a remote file, then return NULL.
|
||||
/// Otherwise return the underlying FILE*
|
||||
/// Please do not use this function if possible.
|
||||
|
||||
idaman THREAD_SAFE FILE *ida_export qlfile(linput_t *li);
|
||||
|
||||
|
||||
/// Convert FILE * to input source.
|
||||
/// Used for temporary linput_t objects - call unmake_linput() to free
|
||||
/// the slot after the use.
|
||||
|
||||
idaman THREAD_SAFE linput_t *ida_export make_linput(FILE *fp);
|
||||
|
||||
/// Free an linput_t object (also see make_linput())
|
||||
|
||||
idaman THREAD_SAFE void ida_export unmake_linput(linput_t *li);
|
||||
|
||||
|
||||
/// Generic linput class - may be used to create a linput_t instance for
|
||||
/// any data source
|
||||
struct generic_linput_t
|
||||
{
|
||||
/// \name Warning
|
||||
/// The following two fields must be filled before calling create_generic_linput()
|
||||
//@{
|
||||
uint64 filesize; ///< input file size
|
||||
uint32 blocksize; ///< preferred block size to work with
|
||||
///< read/write sizes will be in multiples of this number.
|
||||
///< for example, 4096 is a nice value
|
||||
///< blocksize 0 means that the filesize is unknown.
|
||||
///< the internal cache will be disabled in this case.
|
||||
///< also, seeks from the file end will fail.
|
||||
///< blocksize=-1 means error.
|
||||
//@}
|
||||
virtual ssize_t idaapi read(qoff64_t off, void *buffer, size_t nbytes) = 0;
|
||||
virtual ~generic_linput_t() {}
|
||||
};
|
||||
|
||||
/// Create a generic linput
|
||||
/// \param gl linput description.
|
||||
/// this object will be destroyed by close_linput()
|
||||
/// using "delete gl;"
|
||||
|
||||
idaman THREAD_SAFE linput_t *ida_export create_generic_linput(generic_linput_t *gl);
|
||||
|
||||
/// Trivial memory linput
|
||||
|
||||
idaman THREAD_SAFE linput_t *ida_export create_bytearray_linput(const uchar *start, size_t size);
|
||||
|
||||
|
||||
/// Create a linput for process memory.
|
||||
/// This linput will use read_dbg_memory() to read data.
|
||||
/// \param start starting address of the input
|
||||
/// \param size size of the memory area to represent as linput
|
||||
/// if unknown, may be passed as 0
|
||||
|
||||
idaman linput_t *ida_export create_memory_linput(ea_t start, asize_t size);
|
||||
|
||||
/// Get linput type
|
||||
|
||||
inline THREAD_SAFE linput_type_t idaapi get_linput_type(linput_t *li)
|
||||
{
|
||||
return *(linput_type_t *)li;
|
||||
}
|
||||
|
||||
/// Object that will free an linput_t at destruction-time
|
||||
typedef janitor_t<linput_t*> linput_janitor_t;
|
||||
/// Free the linput_t
|
||||
template <> inline linput_janitor_t::~janitor_t()
|
||||
{
|
||||
close_linput(resource);
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
/// Helper class - adapts linput to be used in extract_... functions
|
||||
/// as a data supplier (see kernwin.hpp)
|
||||
class linput_buffer_t
|
||||
{
|
||||
public:
|
||||
linput_buffer_t(linput_t *linput, int64 size=0): li(linput), lsize(size) {}
|
||||
ssize_t read(void *buf, size_t n)
|
||||
{
|
||||
return qlread(li, buf, n);
|
||||
}
|
||||
bool eof()
|
||||
{
|
||||
if ( lsize == 0 )
|
||||
lsize = qlsize(li);
|
||||
return qltell(li) >= lsize;
|
||||
}
|
||||
protected:
|
||||
linput_t *li;
|
||||
private:
|
||||
int64 lsize;
|
||||
};
|
||||
|
||||
//@}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
#ifndef NO_OBSOLETE_FUNCS
|
||||
idaman DEPRECATED THREAD_SAFE FILE *ida_export ecreate(const char *file);
|
||||
idaman DEPRECATED THREAD_SAFE void ida_export eclose(FILE *fp);
|
||||
idaman DEPRECATED THREAD_SAFE void ida_export eread(FILE *fp, void *buf, size_t size);
|
||||
idaman DEPRECATED THREAD_SAFE void ida_export ewrite(FILE *fp, const void *buf, size_t size);
|
||||
idaman DEPRECATED THREAD_SAFE void ida_export eseek(FILE *fp, qoff64_t pos);
|
||||
idaman DEPRECATED THREAD_SAFE int ida_export enumerate_files(
|
||||
char *answer,
|
||||
size_t answer_size,
|
||||
const char *path,
|
||||
const char *fname,
|
||||
int (idaapi*func)(const char *file,void *ud),
|
||||
void *ud=NULL);
|
||||
#endif
|
||||
#endif // _DISKIO_HPP
|
||||
106
idasdk76/include/entry.hpp
Normal file
106
idasdk76/include/entry.hpp
Normal file
@@ -0,0 +1,106 @@
|
||||
/*
|
||||
* Interactive disassembler (IDA).
|
||||
* Copyright (c) 1990-2021 Hex-Rays
|
||||
* ALL RIGHTS RESERVED.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef _ENTRY_HPP
|
||||
#define _ENTRY_HPP
|
||||
|
||||
/*! \file entry.hpp
|
||||
|
||||
\brief Functions that deal with entry points
|
||||
|
||||
Exported functions are considered as entry points as well.
|
||||
|
||||
IDA maintains list of entry points to the program.
|
||||
Each entry point:
|
||||
- has an address
|
||||
- has a name
|
||||
- may have an ordinal number
|
||||
*/
|
||||
|
||||
/// Get number of entry points
|
||||
|
||||
idaman size_t ida_export get_entry_qty(void);
|
||||
|
||||
/// \defgroup AEF_ entry flags
|
||||
/// Passed as 'flags' parameter to add_entry(ea_t, const char *, int)
|
||||
//@{
|
||||
#define AEF_UTF8 0x0 ///< the name is given in UTF-8 (default)
|
||||
#define AEF_IDBENC 0x1 ///< the name is given in the IDB encoding;
|
||||
///< non-ASCII bytes will be decoded accordingly
|
||||
///< Specifying AEF_IDBENC also implies AEF_NODUMMY
|
||||
#define AEF_NODUMMY 0x2 ///< automatically prepend the name with '_' if
|
||||
///< it begins with a dummy suffix. See also AEF_IDBENC
|
||||
//@}
|
||||
|
||||
|
||||
/// Add an entry point to the list of entry points.
|
||||
/// \param ord ordinal number
|
||||
/// if ordinal number is equal to 'ea' then ordinal is not used
|
||||
/// \param ea linear address
|
||||
/// \param name name of entry point. If the specified location already
|
||||
/// has a name, the old name will be appended to the regular
|
||||
/// comment. If name == NULL, then the old name will be retained.
|
||||
/// \param makecode should the kernel convert bytes at the entry point
|
||||
/// to instruction(s)
|
||||
/// \param flags See AEF_*
|
||||
/// \return success (currently always true)
|
||||
|
||||
idaman bool ida_export add_entry(uval_t ord, ea_t ea, const char *name, bool makecode, int flags=AEF_UTF8);
|
||||
|
||||
|
||||
/// Get ordinal number of an entry point.
|
||||
/// \param idx internal number of entry point. Should be
|
||||
/// in the range 0..get_entry_qty()-1
|
||||
/// \return ordinal number or 0.
|
||||
|
||||
idaman uval_t ida_export get_entry_ordinal(size_t idx);
|
||||
|
||||
|
||||
/// Get entry point address by its ordinal
|
||||
/// \param ord ordinal number of entry point
|
||||
/// \return address or #BADADDR
|
||||
|
||||
idaman ea_t ida_export get_entry(uval_t ord);
|
||||
|
||||
|
||||
/// Get name of the entry point by its ordinal.
|
||||
/// \param buf output buffer, may be NULL
|
||||
/// \param ord ordinal number of entry point
|
||||
/// \return size of entry name or -1
|
||||
|
||||
idaman ssize_t ida_export get_entry_name(qstring *buf, uval_t ord);
|
||||
|
||||
|
||||
/// Rename entry point.
|
||||
/// \param ord ordinal number of the entry point
|
||||
/// \param name name of entry point. If the specified location already
|
||||
/// has a name, the old name will be appended to a repeatable
|
||||
/// comment.
|
||||
/// \param flags See AEF_*
|
||||
/// \return success
|
||||
|
||||
idaman bool ida_export rename_entry(uval_t ord, const char *name, int flags=AEF_UTF8);
|
||||
|
||||
|
||||
/// Set forwarder name for ordinal.
|
||||
/// \param ord ordinal number of the entry point
|
||||
/// \param name forwarder name for entry point.
|
||||
/// \param flags See AEF_*
|
||||
/// \return success
|
||||
|
||||
idaman bool ida_export set_entry_forwarder(uval_t ord, const char *name, int flags=AEF_UTF8);
|
||||
|
||||
|
||||
/// Get forwarder name for the entry point by its ordinal.
|
||||
/// \param buf output buffer, may be NULL
|
||||
/// \param ord ordinal number of entry point
|
||||
/// \return size of entry forwarder name or -1
|
||||
|
||||
idaman ssize_t ida_export get_entry_forwarder(qstring *buf, uval_t ord);
|
||||
|
||||
|
||||
#endif // _ENTRY_HPP
|
||||
371
idasdk76/include/enum.hpp
Normal file
371
idasdk76/include/enum.hpp
Normal file
@@ -0,0 +1,371 @@
|
||||
/*
|
||||
* Interactive disassembler (IDA).
|
||||
* Copyright (c) 1990-2021 Hex-Rays
|
||||
* ALL RIGHTS RESERVED.
|
||||
*
|
||||
* Enums and bitfields
|
||||
* Bitfields will be abbreviated as "bf".
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef _ENUM_HPP
|
||||
#define _ENUM_HPP
|
||||
|
||||
#include <nalt.hpp>
|
||||
|
||||
/*! \file enum.hpp
|
||||
|
||||
\brief Assembly level enum management
|
||||
|
||||
Enums and bitfields are represented as ::enum_t.
|
||||
*/
|
||||
|
||||
typedef tid_t enum_t; ///< Enums and bitfields
|
||||
|
||||
typedef uval_t bmask_t; ///< unsigned value that describes a bitmask
|
||||
///< a bit mask is 32/64 bits.
|
||||
|
||||
#define DEFMASK (bmask_t(-1)) ///< default bitmask
|
||||
|
||||
typedef uval_t const_t; ///< members of enums
|
||||
|
||||
/// Max number of identical constants allowed for one enum type
|
||||
const uchar MAX_ENUM_SERIAL = 255;
|
||||
|
||||
|
||||
/// Get number of declared ::enum_t types
|
||||
|
||||
idaman size_t ida_export get_enum_qty(void);
|
||||
|
||||
|
||||
/// Get enum by its index in the list of enums (0..get_enum_qty()-1).
|
||||
|
||||
idaman enum_t ida_export getn_enum(size_t idx);
|
||||
|
||||
|
||||
/// Get the index in the list of enums
|
||||
|
||||
idaman uval_t ida_export get_enum_idx(enum_t id);
|
||||
|
||||
|
||||
/// Get enum by name
|
||||
|
||||
idaman enum_t ida_export get_enum(const char *name);
|
||||
|
||||
|
||||
/// Is enum a bitfield?
|
||||
/// (otherwise - plain enum, no bitmasks except for #DEFMASK are allowed)
|
||||
|
||||
idaman bool ida_export is_bf(enum_t id);
|
||||
|
||||
|
||||
/// Is enum collapsed?
|
||||
|
||||
idaman bool ida_export is_enum_hidden(enum_t id);
|
||||
|
||||
|
||||
/// Collapse enum
|
||||
|
||||
idaman bool ida_export set_enum_hidden(enum_t id, bool hidden);
|
||||
|
||||
|
||||
/// Does enum come from type library?
|
||||
|
||||
idaman bool ida_export is_enum_fromtil(enum_t id);
|
||||
|
||||
|
||||
/// Specify that enum comes from a type library
|
||||
|
||||
idaman bool ida_export set_enum_fromtil(enum_t id, bool fromtil);
|
||||
|
||||
|
||||
/// Is a ghost copy of a local type?
|
||||
|
||||
idaman bool ida_export is_ghost_enum(enum_t id);
|
||||
|
||||
|
||||
/// Specify that enum is a ghost copy of a local type
|
||||
|
||||
idaman bool ida_export set_enum_ghost(enum_t id, bool ghost);
|
||||
|
||||
|
||||
/// Get name of enum
|
||||
|
||||
idaman ssize_t ida_export get_enum_name(qstring *out, enum_t id);
|
||||
|
||||
/// Get name of enum
|
||||
/// \param[out] out buffer to hold the name
|
||||
/// \param id enum id
|
||||
/// \param flags \ref ENFL_
|
||||
|
||||
idaman ssize_t ida_export get_enum_name2(qstring *out, enum_t id, int flags=0);
|
||||
|
||||
/// \defgroup ENFL_ Enum name flags
|
||||
/// Passed as 'flags' parameter to get_enum_name()
|
||||
//@{
|
||||
#define ENFL_REGEX 0x0001 ///< apply regular expressions to beautify the name
|
||||
//@}
|
||||
|
||||
inline qstring get_enum_name(tid_t id, int flags=0)
|
||||
{
|
||||
qstring name;
|
||||
get_enum_name2(&name, id, flags);
|
||||
return name;
|
||||
}
|
||||
|
||||
|
||||
/// Get the width of a enum element
|
||||
/// allowed values: 0 (unspecified),1,2,4,8,16,32,64
|
||||
|
||||
idaman size_t ida_export get_enum_width(enum_t id);
|
||||
|
||||
|
||||
/// See comment for get_enum_width()
|
||||
|
||||
idaman bool ida_export set_enum_width(enum_t id, int width);
|
||||
|
||||
|
||||
/// Get enum comment
|
||||
|
||||
idaman ssize_t ida_export get_enum_cmt(qstring *buf, enum_t id, bool repeatable);
|
||||
|
||||
|
||||
/// Get the number of the members of the enum
|
||||
|
||||
idaman size_t ida_export get_enum_size(enum_t id);
|
||||
|
||||
|
||||
/// Get flags determining the representation of the enum.
|
||||
/// (currently they define the numeric base: octal, decimal, hex, bin) and signness.
|
||||
|
||||
idaman flags_t ida_export get_enum_flag(enum_t id);
|
||||
|
||||
|
||||
/// Get a reference to an enum member by its name
|
||||
|
||||
idaman const_t ida_export get_enum_member_by_name(const char *name);
|
||||
|
||||
|
||||
/// Get value of an enum member
|
||||
|
||||
idaman uval_t ida_export get_enum_member_value(const_t id);
|
||||
|
||||
|
||||
/// Get the parent enum of an enum member
|
||||
|
||||
idaman enum_t ida_export get_enum_member_enum(const_t id);
|
||||
|
||||
|
||||
/// Get bitmask of an enum member
|
||||
|
||||
idaman bmask_t ida_export get_enum_member_bmask(const_t id);
|
||||
|
||||
|
||||
/// Find an enum member by enum, value and bitmask
|
||||
/// \note if serial -1, return a member with any serial
|
||||
|
||||
idaman const_t ida_export get_enum_member(enum_t id, uval_t value, int serial, bmask_t mask);
|
||||
|
||||
|
||||
/// \name Access to all used bitmasks in an enum
|
||||
//@{
|
||||
|
||||
/// Get first bitmask in the enum (bitfield)
|
||||
///
|
||||
/// \param enum_id id of enum (bitfield)
|
||||
/// \return the smallest bitmask for enum, or DEFMASK
|
||||
///
|
||||
idaman bmask_t ida_export get_first_bmask(enum_t id);
|
||||
|
||||
/// Get last bitmask in the enum (bitfield)
|
||||
///
|
||||
/// \param enum_id id of enum
|
||||
/// \return the biggest bitmask for enum, or DEFMASK
|
||||
idaman bmask_t ida_export get_last_bmask(enum_t id);
|
||||
|
||||
/// Get next bitmask in the enum (bitfield)
|
||||
///
|
||||
/// \param enum_id id of enum
|
||||
/// \param value the current bitmask
|
||||
/// \return value of a bitmask with value higher than the specified value, or DEFMASK
|
||||
idaman bmask_t ida_export get_next_bmask(enum_t id, bmask_t bmask);
|
||||
|
||||
/// Get prev bitmask in the enum (bitfield)
|
||||
///
|
||||
/// \param enum_id id of enum
|
||||
/// \param value the current bitmask
|
||||
/// \return value of a bitmask with value lower than the specified value, or DEFMASK
|
||||
idaman bmask_t ida_export get_prev_bmask(enum_t id, bmask_t bmask);
|
||||
//@}
|
||||
|
||||
|
||||
/// \name Access to all enum members with specified bitmask
|
||||
/// \note these functions return values, not ::const_t!
|
||||
//@{
|
||||
idaman uval_t ida_export get_first_enum_member(enum_t id, bmask_t bmask=DEFMASK);
|
||||
idaman uval_t ida_export get_last_enum_member(enum_t id, bmask_t bmask=DEFMASK);
|
||||
idaman uval_t ida_export get_next_enum_member(enum_t id, uval_t value, bmask_t bmask=DEFMASK);
|
||||
idaman uval_t ida_export get_prev_enum_member(enum_t id, uval_t value, bmask_t bmask=DEFMASK);
|
||||
//@}
|
||||
|
||||
|
||||
/// Get name of an enum member by const_t
|
||||
|
||||
idaman ssize_t ida_export get_enum_member_name(qstring *out, const_t id);
|
||||
|
||||
|
||||
/// Get enum member's comment
|
||||
|
||||
idaman ssize_t ida_export get_enum_member_cmt(qstring *buf, const_t id, bool repeatable);
|
||||
|
||||
|
||||
/// \name Access to all enum members with specified value and mask
|
||||
/// A sample loop looks like this:
|
||||
/// \code
|
||||
/// const_t main_cid;
|
||||
/// uchar serial;
|
||||
/// for ( const_t cid=main_cid=get_first_serial_enum_member(&serial, id, v, mask);
|
||||
/// cid != BADNODE;
|
||||
/// cid = get_next_serial_enum_member(&serial, main_cid) )
|
||||
/// {
|
||||
/// ...
|
||||
/// }
|
||||
/// \endcode
|
||||
/// The 'out_serial' argument of get_first_serial_enum_member/get_last_serial_enum_member can be NULL.
|
||||
/// The 'in_out_serial' is required for the other functions.
|
||||
//@{
|
||||
idaman const_t ida_export get_first_serial_enum_member(uchar *out_serial, enum_t id, uval_t value, bmask_t bmask);
|
||||
idaman const_t ida_export get_last_serial_enum_member(uchar *out_serial, enum_t id, uval_t value, bmask_t bmask);
|
||||
idaman const_t ida_export get_next_serial_enum_member(uchar *in_out_serial, const_t first_cid);
|
||||
idaman const_t ida_export get_prev_serial_enum_member(uchar *in_out_serial, const_t first_cid);
|
||||
//@}
|
||||
|
||||
|
||||
/// Enum member visitor - see for_all_enum_members().
|
||||
/// Derive your visitor from this class.
|
||||
struct enum_member_visitor_t
|
||||
{
|
||||
/// Implements action to take when enum member is visited.
|
||||
/// \return nonzero to stop the iteration
|
||||
virtual int idaapi visit_enum_member(const_t cid, uval_t value) = 0;
|
||||
};
|
||||
|
||||
|
||||
/// Visit all members of a given enum
|
||||
|
||||
idaman int ida_export for_all_enum_members(enum_t id, enum_member_visitor_t &cv);
|
||||
|
||||
|
||||
/// Get serial number of an enum member
|
||||
|
||||
idaman uchar ida_export get_enum_member_serial(const_t cid);
|
||||
|
||||
|
||||
/// Get corresponding type ordinal number
|
||||
|
||||
idaman int32 ida_export get_enum_type_ordinal(enum_t id);
|
||||
|
||||
|
||||
/// Set corresponding type ordinal number
|
||||
|
||||
idaman void ida_export set_enum_type_ordinal(enum_t id, int32 ord);
|
||||
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// MANIPULATION
|
||||
|
||||
/// Add new enum type.
|
||||
/// - if idx==#BADADDR then add as the last idx
|
||||
/// - if name==NULL then generate a unique name "enum_%d"
|
||||
|
||||
idaman enum_t ida_export add_enum(size_t idx, const char *name, flags_t flag);
|
||||
|
||||
|
||||
/// Delete an enum type
|
||||
|
||||
idaman void ida_export del_enum(enum_t id);
|
||||
|
||||
|
||||
/// Set serial number of enum.
|
||||
/// Also see get_enum_idx().
|
||||
|
||||
idaman bool ida_export set_enum_idx(enum_t id, size_t idx);
|
||||
|
||||
|
||||
/// Set 'bitfield' bit of enum (i.e. convert it to a bitfield)
|
||||
|
||||
idaman bool ida_export set_enum_bf(enum_t id, bool bf);
|
||||
|
||||
|
||||
/// Set name of enum type
|
||||
|
||||
idaman bool ida_export set_enum_name(enum_t id,const char *name);
|
||||
|
||||
|
||||
/// Set comment for enum type
|
||||
|
||||
idaman bool ida_export set_enum_cmt(enum_t id,const char *cmt,bool repeatable);
|
||||
|
||||
|
||||
/// Set data representation flags
|
||||
|
||||
idaman bool ida_export set_enum_flag(enum_t id, flags_t flag);
|
||||
|
||||
|
||||
/// Add member to enum type.
|
||||
/// \return 0 if ok, otherwise one of \ref ENUM_MEMBER_
|
||||
|
||||
idaman int ida_export add_enum_member(
|
||||
enum_t id,
|
||||
const char *name,
|
||||
uval_t value,
|
||||
bmask_t bmask=DEFMASK);
|
||||
|
||||
|
||||
/// \defgroup ENUM_MEMBER_ Add enum member result codes
|
||||
/// Return values for add_enum_member()
|
||||
//@{
|
||||
#define ENUM_MEMBER_ERROR_NAME 1 ///< already have member with this name (bad name)
|
||||
#define ENUM_MEMBER_ERROR_VALUE 2 ///< already have 256 members with this value
|
||||
#define ENUM_MEMBER_ERROR_ENUM 3 ///< bad enum id
|
||||
#define ENUM_MEMBER_ERROR_MASK 4 ///< bad bmask
|
||||
#define ENUM_MEMBER_ERROR_ILLV 5 ///< bad bmask and value combination (~bmask & value != 0)
|
||||
//@}
|
||||
|
||||
|
||||
/// Delete member of enum type
|
||||
|
||||
idaman bool ida_export del_enum_member(enum_t id, uval_t value, uchar serial, bmask_t bmask);
|
||||
|
||||
|
||||
/// Set name of enum member
|
||||
|
||||
idaman bool ida_export set_enum_member_name(const_t id, const char *name);
|
||||
|
||||
|
||||
/// Set comment for enum member
|
||||
|
||||
inline bool set_enum_member_cmt(const_t id, const char *cmt, bool repeatable)
|
||||
{
|
||||
return set_enum_cmt(id, cmt, repeatable);
|
||||
}
|
||||
|
||||
|
||||
/// Is bitmask one bit?
|
||||
|
||||
inline THREAD_SAFE bool is_one_bit_mask(bmask_t mask)
|
||||
{
|
||||
return (mask & (mask-1)) == 0;
|
||||
}
|
||||
|
||||
|
||||
/// \name Work with the bitmask name & comment
|
||||
//@{
|
||||
idaman bool ida_export set_bmask_name(enum_t id, bmask_t bmask, const char *name);
|
||||
idaman ssize_t ida_export get_bmask_name(qstring *out, enum_t id, bmask_t bmask);
|
||||
|
||||
idaman bool ida_export set_bmask_cmt(enum_t id, bmask_t bmask, const char *cmt, bool repeatable);
|
||||
idaman ssize_t ida_export get_bmask_cmt(qstring *buf, enum_t id, bmask_t bmask, bool repeatable);
|
||||
//@}
|
||||
|
||||
#endif // _ENUM_HPP
|
||||
96
idasdk76/include/err.h
Normal file
96
idasdk76/include/err.h
Normal file
@@ -0,0 +1,96 @@
|
||||
/*
|
||||
* Interactive disassembler (IDA).
|
||||
* Copyright (c) 1990-2021 Hex-Rays
|
||||
* ALL RIGHTS RESERVED.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef _ERR_H
|
||||
#define _ERR_H
|
||||
|
||||
#include <errno.h>
|
||||
|
||||
/*! \file err.h
|
||||
|
||||
\brief Thread safe functions that deal with error codes
|
||||
|
||||
*/
|
||||
|
||||
/// Print error message to stderr (analog of perror)
|
||||
|
||||
idaman THREAD_SAFE AS_PRINTF(1, 0) void ida_export vqperror(const char *format, va_list va);
|
||||
|
||||
|
||||
/// Get error description string.
|
||||
/// if _qerrno=-1, get_qerrno() will be used
|
||||
|
||||
idaman THREAD_SAFE const char *ida_export qstrerror(error_t _qerrno);
|
||||
|
||||
|
||||
/// A convenience function to generate error messages (returns "header: error message")
|
||||
|
||||
idaman THREAD_SAFE char *ida_export get_errdesc(const char *header, error_t _qerrno=-1);
|
||||
|
||||
|
||||
/// Get error message for MS Windows error codes
|
||||
|
||||
idaman THREAD_SAFE char *ida_export winerr(int code);
|
||||
|
||||
|
||||
/// errno or GetLastError() depending on the system.
|
||||
|
||||
idaman THREAD_SAFE int ida_export qerrcode(int new_code=-1);
|
||||
|
||||
|
||||
/// Get error string corresponding to qerrcode().
|
||||
/// if code == -1, then qerrcode() will be called.
|
||||
|
||||
idaman const char *ida_export qerrstr(int code=-1);
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
||||
/// See vqperror()
|
||||
|
||||
THREAD_SAFE AS_PRINTF(1, 2) inline void qperror(const char *format, ...)
|
||||
{
|
||||
va_list va;
|
||||
va_start(va, format);
|
||||
vqperror(format, va);
|
||||
va_end(va);
|
||||
}
|
||||
|
||||
|
||||
/// See set_qerrno()
|
||||
|
||||
THREAD_SAFE inline void set_errno(int code)
|
||||
{
|
||||
errno = code;
|
||||
set_qerrno(eOS);
|
||||
}
|
||||
#endif
|
||||
|
||||
// Internal functions
|
||||
/// \cond
|
||||
|
||||
// n=0..3
|
||||
idaman THREAD_SAFE void ida_export set_error_data(int n, size_t data);
|
||||
idaman THREAD_SAFE void ida_export set_error_string(int n, const char *str);
|
||||
idaman THREAD_SAFE size_t ida_export get_error_data(int n);
|
||||
idaman THREAD_SAFE const char *ida_export get_error_string(int n);
|
||||
|
||||
#define QPRM_TYPE(t,n,x) set_error_data(n-1, t(x))
|
||||
#define QPRM_CHAR(n,x) QPRM_TYPE(char,n,x)
|
||||
#define QPRM_SHORT(n,x) QPRM_TYPE(short,n,x)
|
||||
#define QPRM_INT(n,x) QPRM_TYPE(int,n,x)
|
||||
#define QPRM_INT32(n,x) QPRM_TYPE(int32,n,x)
|
||||
#define QPRM_UCHAR(n,x) QPRM_TYPE(uchar,n,x)
|
||||
#define QPRM_USHORT(n,x) QPRM_TYPE(ushort,n,x)
|
||||
#define QPRM_UINT(n,x) QPRM_TYPE(uint,n,x)
|
||||
#define QPRM_UINT32(n,x) QPRM_TYPE(uint32,n,x)
|
||||
#define QPRM(n,x) set_error_string(n-1, x)
|
||||
|
||||
/// \endcond
|
||||
|
||||
#endif
|
||||
|
||||
67
idasdk76/include/exehdr.h
Normal file
67
idasdk76/include/exehdr.h
Normal file
@@ -0,0 +1,67 @@
|
||||
/*
|
||||
* Interactive disassembler (IDA).
|
||||
* Copyright (c) 1990-2021 Hex-Rays
|
||||
* ALL RIGHTS RESERVED.
|
||||
*
|
||||
* EXE-file header layout
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef __EXEHDR_H
|
||||
#define __EXEHDR_H
|
||||
#pragma pack(push, 1)
|
||||
|
||||
struct exehdr
|
||||
{
|
||||
uint16 exe_ident;
|
||||
#define EXE_ID 0x5A4D // 'MZ'
|
||||
#define EXE_ID2 0x4D5A // 'ZM' (DOS works with this also)
|
||||
uint16 PartPag;
|
||||
uint16 PageCnt;
|
||||
uint16 ReloCnt;
|
||||
uint16 HdrSize;
|
||||
uint16 MinMem;
|
||||
uint16 MaxMem;
|
||||
uint16 ReloSS;
|
||||
uint16 ExeSP;
|
||||
uint16 ChkSum;
|
||||
uint16 ExeIP;
|
||||
uint16 ReloCS;
|
||||
uint16 TablOff;
|
||||
uint16 Overlay;
|
||||
/*
|
||||
uint16 res[4]; // Reserved words
|
||||
uint16 oemid; // OEM identifier (for e_oeminfo)
|
||||
uint16 oeminfo; // OEM information; e_oemid specific
|
||||
uint16 res2[10]; // Reserved words
|
||||
uint32 lfanew; // File address of new exe header
|
||||
*/
|
||||
int32 CalcEXE_Length(void)
|
||||
{
|
||||
int32 len = PageCnt * 512L - HdrSize * 16;
|
||||
if ( PartPag != 0 )
|
||||
len -= 512 - PartPag;
|
||||
return len;
|
||||
}
|
||||
void CalcEXE_Pages(int32 len)
|
||||
{
|
||||
PartPag = uint16(len % 512);
|
||||
PageCnt = uint16(len / 512);
|
||||
if ( PartPag != 0 )
|
||||
PageCnt++;
|
||||
}
|
||||
};
|
||||
|
||||
struct exehdr_full: exehdr
|
||||
{
|
||||
uint16 res[4]; // Reserved words
|
||||
uint16 oemid; // OEM identifier (for e_oeminfo)
|
||||
uint16 oeminfo; // OEM information; e_oemid specific
|
||||
uint16 res2[10]; // Reserved words
|
||||
uint32 lfanew; // File address of new exe header
|
||||
};
|
||||
|
||||
#define PSPsize 0x100
|
||||
#define PE_PTROFF 0x3C
|
||||
#pragma pack(pop)
|
||||
#endif
|
||||
1192
idasdk76/include/expr.hpp
Normal file
1192
idasdk76/include/expr.hpp
Normal file
File diff suppressed because it is too large
Load Diff
572
idasdk76/include/fixup.hpp
Normal file
572
idasdk76/include/fixup.hpp
Normal file
@@ -0,0 +1,572 @@
|
||||
/*
|
||||
* Interactive disassembler (IDA).
|
||||
* Copyright (c) 1990-2021 Hex-Rays
|
||||
* ALL RIGHTS RESERVED.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef FIXUP_HPP
|
||||
#define FIXUP_HPP
|
||||
|
||||
#include <nalt.hpp>
|
||||
#include <segment.hpp>
|
||||
|
||||
/*! \file fixup.hpp
|
||||
|
||||
\brief Functions that deal with fixup information
|
||||
|
||||
A loader should setup fixup information using set_fixup().
|
||||
*/
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
/// Fixup information structure
|
||||
|
||||
typedef uint16 fixup_type_t; ///< see \ref fixup_type_t
|
||||
/// \defgroup fixup_type_t Types of fixups
|
||||
/// Fixup may be of standard or custom type. Standard types fall into
|
||||
/// range 1..FIXUP_CUSTOM-1 and custom types fall into range
|
||||
/// FIXUP_CUSTOM..MAX_UINT16.
|
||||
/// \note Fixup type 0 is unused.
|
||||
/// \name Fixup standard types
|
||||
//@{
|
||||
#define FIXUP_OFF8 13 ///< 8-bit offset
|
||||
#define FIXUP_OFF16 1 ///< 16-bit offset
|
||||
#define FIXUP_SEG16 2 ///< 16-bit base--logical segment base (selector)
|
||||
#define FIXUP_PTR16 3 ///< 32-bit long pointer (16-bit base:16-bit
|
||||
///< offset)
|
||||
#define FIXUP_OFF32 4 ///< 32-bit offset
|
||||
#define FIXUP_PTR32 5 ///< 48-bit pointer (16-bit base:32-bit offset)
|
||||
#define FIXUP_HI8 6 ///< high 8 bits of 16bit offset
|
||||
#define FIXUP_HI16 7 ///< high 16 bits of 32bit offset
|
||||
#define FIXUP_LOW8 8 ///< low 8 bits of 16bit offset
|
||||
#define FIXUP_LOW16 9 ///< low 16 bits of 32bit offset
|
||||
#define V695_FIXUP_VHIGH 10 ///< obsolete
|
||||
#define V695_FIXUP_VLOW 11 ///< obsolete
|
||||
#define FIXUP_OFF64 12 ///< 64-bit offset
|
||||
#define FIXUP_OFF8S 14 ///< 8-bit signed offset
|
||||
#define FIXUP_OFF16S 15 ///< 16-bit signed offset
|
||||
#define FIXUP_OFF32S 16 ///< 32-bit signed offset
|
||||
//#define FIXUP_ 0xE
|
||||
#define FIXUP_CUSTOM 0x8000 ///< start of the custom types range
|
||||
//@}
|
||||
|
||||
/// Is fixup processed by processor module?
|
||||
inline THREAD_SAFE bool is_fixup_custom(fixup_type_t type)
|
||||
{
|
||||
return (type & FIXUP_CUSTOM) != 0;
|
||||
}
|
||||
|
||||
/// \defgroup FIXUPF_ Fixup flags
|
||||
/// Used by fixup_data_t
|
||||
//@{
|
||||
/// fixup is relative to the linear address `base'. Otherwise fixup is
|
||||
/// relative to the start of the segment with `sel' selector.
|
||||
#define FIXUPF_REL 0x0001
|
||||
/// target is a location (otherwise - segment).
|
||||
/// Use this bit if the target is a symbol rather than an offset from the
|
||||
/// beginning of a segment.
|
||||
#define FIXUPF_EXTDEF 0x0002
|
||||
/// fixup is ignored by IDA
|
||||
/// - disallows the kernel to convert operands
|
||||
/// - this fixup is not used during output
|
||||
#define FIXUPF_UNUSED 0x0004
|
||||
/// fixup was not present in the input file
|
||||
#define FIXUPF_CREATED 0x0008
|
||||
/// additional flags. The bits from this mask are not stored in the database
|
||||
/// and can be used by the loader at its discretion.
|
||||
#define FIXUPF_LOADER_MASK 0xF0000000
|
||||
//@}
|
||||
|
||||
struct fixup_handler_t;
|
||||
struct fixup_data_t
|
||||
{
|
||||
protected:
|
||||
fixup_type_t type; // fixup type
|
||||
uint32 flags; // FIXUPF_... bits
|
||||
uval_t base; // base for relative fixups
|
||||
|
||||
public:
|
||||
sel_t sel; ///< selector of the target segment.
|
||||
///< BADSEL means an absolute (zero based) target.
|
||||
///< \sa FIXUPF_REL
|
||||
|
||||
ea_t off; ///< target offset
|
||||
///< \note The target is calculated as
|
||||
///< `get_base() + off`.
|
||||
|
||||
adiff_t displacement; ///< displacement (offset from the target)
|
||||
|
||||
public:
|
||||
fixup_data_t()
|
||||
: type(0),
|
||||
flags(0),
|
||||
base(0),
|
||||
sel(BADSEL),
|
||||
off(0),
|
||||
displacement(0) {}
|
||||
fixup_data_t(fixup_type_t type_, uint32 flags_ = 0)
|
||||
: type(type_),
|
||||
flags(flags_),
|
||||
base(0),
|
||||
sel(BADSEL),
|
||||
off(0),
|
||||
displacement(0) {}
|
||||
|
||||
/// Fixup type \ref fixup_type_t
|
||||
fixup_type_t get_type(void) const { return type; }
|
||||
void set_type(fixup_type_t type_) { type = type_; }
|
||||
void set_type_and_flags(fixup_type_t type_, uint32 flags_ = 0)
|
||||
{
|
||||
type = type_;
|
||||
flags = flags_;
|
||||
}
|
||||
|
||||
bool is_custom(void) const; ///< \ref is_fixup_custom()
|
||||
|
||||
/// Fixup flags \ref FIXUPF_
|
||||
uint32 get_flags() const { return flags; }
|
||||
|
||||
bool is_extdef(void) const { return (flags & FIXUPF_EXTDEF) != 0; }
|
||||
void set_extdef(void) { flags |= FIXUPF_EXTDEF; }
|
||||
void clr_extdef(void) { flags &= ~FIXUPF_EXTDEF; }
|
||||
|
||||
bool is_unused(void) const { return (flags & FIXUPF_UNUSED) != 0; }
|
||||
void set_unused(void) { flags |= FIXUPF_UNUSED; }
|
||||
void clr_unused(void) { flags &= ~FIXUPF_UNUSED; }
|
||||
|
||||
/// Is fixup relative?
|
||||
bool has_base(void) const { return (flags & FIXUPF_REL) != 0; }
|
||||
|
||||
/// Is fixup artificial?
|
||||
bool was_created(void) const { return (flags & FIXUPF_CREATED) != 0; }
|
||||
|
||||
/// Get base of fixup.
|
||||
/// \note The target is calculated as `get_base() + off`.
|
||||
/// \sa FIXUPF_REL
|
||||
ea_t get_base() const
|
||||
{
|
||||
return has_base() ? base : sel != BADSEL ? sel2ea(sel) : 0;
|
||||
}
|
||||
|
||||
/// Set base of fixup.
|
||||
/// The target should be set before a call of this function.
|
||||
void set_base(ea_t new_base)
|
||||
{
|
||||
ea_t target = get_base() + off;
|
||||
flags |= FIXUPF_REL;
|
||||
base = new_base;
|
||||
off = target - base;
|
||||
}
|
||||
|
||||
void set_sel(const segment_t *seg)
|
||||
{
|
||||
sel = seg == NULL ? BADSEL : seg->sel;
|
||||
}
|
||||
|
||||
/// Set selector of fixup to the target.
|
||||
/// The target should be set before a call of this function.
|
||||
void set_target_sel()
|
||||
{
|
||||
ea_t target = get_base() + off;
|
||||
set_sel(getseg(target));
|
||||
flags &= ~FIXUPF_REL;
|
||||
base = 0; // just in case
|
||||
off = target - get_base();
|
||||
}
|
||||
|
||||
void set(ea_t source) const; ///< \ref set_fixup()
|
||||
bool get(ea_t source); ///< \ref get_fixup()
|
||||
|
||||
/// \ref get_fixup_handler()
|
||||
const fixup_handler_t *get_handler() const;
|
||||
|
||||
/// \ref get_fixup_desc()
|
||||
const char *get_desc(qstring *buf, ea_t source) const;
|
||||
|
||||
// TODO rewrite to the inline implementation which uses
|
||||
// fixup_handler_t::size
|
||||
int calc_size() const; ///< \ref calc_fixup_size()
|
||||
uval_t get_value(ea_t ea) const; ///< \ref get_fixup_value()
|
||||
bool patch_value(ea_t ea) const; ///< \ref patch_fixup_value()
|
||||
|
||||
};
|
||||
|
||||
/// Get fixup information
|
||||
|
||||
idaman bool ida_export get_fixup(fixup_data_t *fd, ea_t source);
|
||||
|
||||
|
||||
/// Check that a fixup exists at the given address
|
||||
|
||||
inline bool exists_fixup(ea_t source)
|
||||
{
|
||||
return get_fixup(NULL, source);
|
||||
}
|
||||
|
||||
|
||||
/// Set fixup information. You should fill ::fixup_data_t and call this
|
||||
/// function and the kernel will remember information in the database.
|
||||
/// \param source the fixup source address, i.e. the address modified by
|
||||
/// the fixup
|
||||
/// \param fd fixup data
|
||||
|
||||
idaman void ida_export set_fixup(ea_t source, const fixup_data_t &fd);
|
||||
|
||||
|
||||
/// Delete fixup information
|
||||
|
||||
idaman void ida_export del_fixup(ea_t source);
|
||||
|
||||
|
||||
/// \name Enumerate addresses with fixup information:
|
||||
//@{
|
||||
/// Get the first address with fixup information
|
||||
///
|
||||
/// \return the first address with fixup information, or BADADDR
|
||||
idaman ea_t ida_export get_first_fixup_ea(void);
|
||||
|
||||
/// Find next address with fixup information
|
||||
///
|
||||
/// \param ea current address
|
||||
/// \return the next address with fixup information, or BADADDR
|
||||
idaman ea_t ida_export get_next_fixup_ea(ea_t ea);
|
||||
|
||||
/// Find previous address with fixup information
|
||||
///
|
||||
/// \param ea current address
|
||||
/// \return the previous address with fixup information, or BADADDR
|
||||
idaman ea_t ida_export get_prev_fixup_ea(ea_t ea);
|
||||
//@}
|
||||
|
||||
|
||||
/// Get handler of standard or custom fixup
|
||||
|
||||
idaman const fixup_handler_t * ida_export get_fixup_handler(fixup_type_t type);
|
||||
|
||||
|
||||
/// Use fixup information for an address.
|
||||
/// This function converts item_ea flags to offsets/segments.
|
||||
/// For undefined bytes, you may set item_ea == fixup_ea. In this case this
|
||||
/// function will create an item (byte, word, dword) there.
|
||||
/// \param item_ea start address of item to modify
|
||||
/// \param fixup_ea address of fixup record
|
||||
/// \param n number of operand. may be 0, 1, 2, or OPND_ALL
|
||||
/// \param is_macro is the instruction at 'item_ea' a macro?
|
||||
/// if yes, then partial fixups (HIGH, LOW) won't be applied
|
||||
/// \retval false no fixup at fixup_ea or it has #FIXUPF_UNUSED flag
|
||||
/// \retval true ok, the fixup information was applied
|
||||
|
||||
idaman bool ida_export apply_fixup(ea_t item_ea, ea_t fixup_ea, int n, bool is_macro);
|
||||
|
||||
|
||||
/// Get the operand value.
|
||||
/// This function get fixup bytes from data or an instruction at `ea' and
|
||||
/// convert them to the operand value (maybe partially).
|
||||
/// It is opposite in meaning to the `patch_fixup_value()`.
|
||||
/// For example, FIXUP_HI8 read a byte at `ea' and shifts it left by 8 bits,
|
||||
/// or AArch64's custom fixup BRANCH26 get low 26 bits of the insn at `ea'
|
||||
/// and shifts it left by 2 bits.
|
||||
/// This function is mainly used to get a relocation addend.
|
||||
/// \param ea address to get fixup bytes from, the size of the fixup
|
||||
/// bytes depends on the fixup type.
|
||||
/// \sa fixup_handler_t::size
|
||||
/// \param type fixup type
|
||||
/// \retval operand value
|
||||
|
||||
idaman uval_t ida_export get_fixup_value(ea_t ea, fixup_type_t type);
|
||||
|
||||
|
||||
/// Patch the fixup bytes.
|
||||
/// This function updates data or an instruction at `ea' to the fixup bytes.
|
||||
/// For example, FIXUP_HI8 updates a byte at `ea' to the high byte of
|
||||
/// `fd->off', or AArch64's custom fixup BRANCH26 updates low 26 bits of the
|
||||
/// insn at `ea' to the value of `fd->off' shifted right by 2.
|
||||
/// \param ea address where data are changed, the size of the changed data
|
||||
/// depends on the fixup type.
|
||||
/// \sa fixup_handler_t::size
|
||||
/// \param fd fixup data
|
||||
/// \retval false the fixup bytes do not fit (e.g. `fd->off' is greater
|
||||
/// than 0xFFFFFFC for BRANCH26). The database is changed
|
||||
/// even in this case.
|
||||
|
||||
idaman bool ida_export patch_fixup_value(ea_t ea, const fixup_data_t &fd);
|
||||
|
||||
|
||||
/// Get FIXUP description comment.
|
||||
|
||||
idaman const char *ida_export get_fixup_desc(
|
||||
qstring *buf,
|
||||
ea_t source,
|
||||
const fixup_data_t &fd);
|
||||
|
||||
|
||||
/// Calculate size of fixup in bytes (the number of bytes the fixup patches)
|
||||
/// \retval -1 means error
|
||||
|
||||
idaman int ida_export calc_fixup_size(fixup_type_t type);
|
||||
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// inline implementation
|
||||
inline bool fixup_data_t::is_custom(void) const
|
||||
{
|
||||
return is_fixup_custom(type);
|
||||
}
|
||||
inline void fixup_data_t::set(ea_t source) const
|
||||
{
|
||||
set_fixup(source, *this);
|
||||
}
|
||||
|
||||
inline bool fixup_data_t::get(ea_t source)
|
||||
{
|
||||
return get_fixup(this, source);
|
||||
}
|
||||
|
||||
inline const char *fixup_data_t::get_desc(qstring *buf, ea_t source) const
|
||||
{
|
||||
return get_fixup_desc(buf, source, *this);
|
||||
}
|
||||
|
||||
inline int fixup_data_t::calc_size() const
|
||||
{
|
||||
return calc_fixup_size(type);
|
||||
}
|
||||
|
||||
inline uval_t fixup_data_t::get_value(ea_t ea) const
|
||||
{
|
||||
return get_fixup_value(ea, type);
|
||||
}
|
||||
|
||||
inline bool fixup_data_t::patch_value(ea_t ea) const
|
||||
{
|
||||
return patch_fixup_value(ea, *this);
|
||||
}
|
||||
|
||||
inline const fixup_handler_t *fixup_data_t::get_handler() const
|
||||
{
|
||||
return get_fixup_handler(type);
|
||||
}
|
||||
|
||||
|
||||
/// \name Custom fixups
|
||||
/// Processor modules and plugins may register custom fixup handlers. File
|
||||
/// loaders should use find_custom_fixup() function to find the handler
|
||||
/// created by the processor module. The custom fixup handlers will be
|
||||
/// unregistered automatically before the database gets closed.
|
||||
//@{
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
/// Implements the core behavior of a custom fixup
|
||||
struct fixup_handler_t
|
||||
{
|
||||
int32 cbsize; ///< size of this structure
|
||||
const char *name; ///< Format name, must be unique
|
||||
uint32 props; ///< \ref FHF_
|
||||
/// \defgroup FHF_ Fixup handler properties
|
||||
/// Used by fixup_handler_t::props
|
||||
//@{
|
||||
#define FHF_VERIFY 0x0001 ///< verify that the value fits into WIDTH
|
||||
///< bits. If this property is not set we
|
||||
///< just truncate the value.
|
||||
#define FHF_CODE 0x0002 ///< verify that ITEM_EA in std_apply() points
|
||||
///< to an instruction.
|
||||
#define FHF_FORCE_CODE 0x0004 ///< if ITEM_EA in std_apply() points to an
|
||||
///< unknown item, then convert it to code.
|
||||
///< this property is valid only with FHF_CODE.
|
||||
#define FHF_ABS_OPVAL 0x0008 ///< create absolute refinfo in std_apply()
|
||||
///< because the operand also has the absolute
|
||||
///< value (usually for o_near operands)
|
||||
#define FHF_SIGNED 0x0010 ///< the operand value is signed.
|
||||
///< create a refinfo with REFINFO_SIGNEDOP in
|
||||
///< std_apply()
|
||||
//@}
|
||||
/// Is the operand value signed?
|
||||
bool is_signed() const { return (props & FHF_SIGNED) != 0; }
|
||||
|
||||
/// \defgroup fh_options Tuning options
|
||||
//@{
|
||||
/// The examples below show how these options work.
|
||||
/// \sa std_patch_value() std_get_value()
|
||||
uint8 size; ///< size in bytes
|
||||
uint8 width; ///< number of significant bits before shifting
|
||||
uint8 shift; ///< number of bits to shift right before patching.
|
||||
///< The following should be true:
|
||||
///< width - shift <= size * 8
|
||||
uint8 rsrv4; // reserved
|
||||
|
||||
uint32 reftype; ///< reference info type and flags,
|
||||
///< std_apply() produces an offset of this type
|
||||
//@}
|
||||
|
||||
/// Apply a fixup: take it into account while analyzing the file.
|
||||
/// Usually it consists of converting the operand into an offset expression.
|
||||
/// \sa apply_fixup()
|
||||
/// If this callback is not specified then std_apply() is used.
|
||||
bool (idaapi *apply)(
|
||||
const fixup_handler_t *fh,
|
||||
ea_t item_ea,
|
||||
ea_t fixup_ea,
|
||||
int opnum,
|
||||
bool is_macro,
|
||||
const fixup_data_t &fd);
|
||||
|
||||
/// Get the operand value.
|
||||
/// This callback is called from get_fixup_value().
|
||||
/// \sa get_fixup_value()
|
||||
/// If this callback is not specified then std_get_value() is used.
|
||||
uval_t (idaapi *get_value)(const fixup_handler_t *fh, ea_t ea);
|
||||
|
||||
/// Patch the fixup bytes.
|
||||
/// This callback is called from patch_fixup_value() or after changing the
|
||||
/// fixup (e.g. after it was moved from one location to another).
|
||||
/// If this callback is not specified then std_patch_value() is used.
|
||||
/// \sa patch_fixup_value()
|
||||
/// \retval false the fixup bytes do not fit. The database is changed
|
||||
/// even in this case.
|
||||
bool (idaapi *patch_value)(
|
||||
const fixup_handler_t *fh,
|
||||
ea_t ea,
|
||||
const fixup_data_t &fd);
|
||||
|
||||
#ifndef SWIG
|
||||
DECLARE_COMPARISONS(fixup_handler_t);
|
||||
#endif
|
||||
};
|
||||
|
||||
|
||||
/// \name std_apply()
|
||||
/// This internal function takes \ref fh_options and \ref FHF_ to convert
|
||||
/// the fixup to an offset.
|
||||
|
||||
/// \name std_patch_value()
|
||||
/// This internal function takes \ref fh_options and \ref FHF_ to determine
|
||||
/// how to patch the bytes.
|
||||
/// 1) it verifies that the fixup value fits to the fixup_handler_t::width
|
||||
/// bits if the FHF_VERIFY property is specified,
|
||||
/// 2) it discards bits that do not fit,
|
||||
/// 3) it shifts the result right by fixup_handler_t::shift bits,
|
||||
/// 4) it puts the result to the rightmost bits of fixup_handler_t::size
|
||||
/// bytes at the given address.
|
||||
/// For example, FIXUP_HI8 uses size = 1, and width = 16, and shift = 8, and
|
||||
/// property FHF_VERIFY, or MIPS's custom fixup BRANCH26 uses size = 4,
|
||||
/// and width = 28, and shift = 2.
|
||||
/// In details:
|
||||
/// a) size = 1, width = 16, shift = 8
|
||||
/// - the value to patch is masked with 0xFFFF (width=16)
|
||||
/// - then it is shifted right by 8 bits (shift=8)
|
||||
/// - then the result is patched to the 8bit data at the fixup address
|
||||
/// e.g.
|
||||
/// 0xXX the original data
|
||||
/// 0x1234 the value
|
||||
/// 0x0012 the shifted value
|
||||
/// 0x12 the patched data
|
||||
/// b) size = 4, width = 28, shift = 2
|
||||
/// - the value to patch is masked with 0xFFFFFFF (width=28)
|
||||
/// - then it is shifted right by 2 bits (shift=2)
|
||||
/// - then the result is patched in the low 26 bits of the 32bit
|
||||
/// e.g.
|
||||
/// 0x10000000 an instruction at the fixup address
|
||||
/// 0x0000005C the value
|
||||
/// 0x00000017 the shifted value
|
||||
/// 0x10000017 the patched insn
|
||||
|
||||
/// \name std_get_value()
|
||||
/// This internal function takes \ref fh_options to determine how to get the
|
||||
/// operand value.
|
||||
/// It is opposite in meaning to the std_patch_value().
|
||||
/// 1) it gets the fixup_handler_t::size bytes at the given address,
|
||||
/// 2) it shifts the result left by fixup_handler_t::shift bits,
|
||||
/// 3) it returns the rightmost fixup_handler_t::width bits as a signed
|
||||
/// value.
|
||||
/// In details:
|
||||
/// b) size = 4, width = 28, shift = 2
|
||||
/// - it gets 4 bytes from the fixup address (the branch insn)
|
||||
/// - then it shifts this dword left by 2 bits (shift=2)
|
||||
/// - then the result is masked with 0xFFFFFFF (width=28)
|
||||
/// e.g.
|
||||
/// 0x10000017 the insn
|
||||
/// 0x4000005C the unshifted value
|
||||
/// 0x0000005C the masked result
|
||||
|
||||
|
||||
/// Register a new custom fixup.
|
||||
/// This function must be called by a processor module or plugin, but not
|
||||
/// by a file loader. File loaders should use find_custom_fixup() function
|
||||
/// to find the handler created by the processor module.
|
||||
/// \return id of the new custom fixup handler with FIXUP_CUSTOM bit set or
|
||||
/// 0 (e.g. when the custom fixup handler with the same name was
|
||||
/// already registered).
|
||||
|
||||
idaman fixup_type_t ida_export register_custom_fixup(
|
||||
const fixup_handler_t *cfh);
|
||||
|
||||
|
||||
/// Unregister a new custom fixup format. Should be called by the processor
|
||||
/// module before the database gets closed.
|
||||
|
||||
idaman bool ida_export unregister_custom_fixup(fixup_type_t type);
|
||||
|
||||
|
||||
/// Get id of a custom fixup handler.
|
||||
/// \param name name of the custom fixup handler
|
||||
/// \return id with FIXUP_CUSTOM bit set or 0
|
||||
|
||||
idaman fixup_type_t ida_export find_custom_fixup(const char *name);
|
||||
|
||||
//@}
|
||||
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
/// Collect fixup records for the specified range.
|
||||
/// Partially overlapping records will be reported too.
|
||||
/// \return success (false means no fixup info have been found)
|
||||
|
||||
struct fixup_info_t
|
||||
{
|
||||
ea_t ea;
|
||||
fixup_data_t fd;
|
||||
};
|
||||
DECLARE_TYPE_AS_MOVABLE(fixup_info_t);
|
||||
typedef qvector<fixup_info_t> fixups_t;
|
||||
|
||||
idaman bool ida_export get_fixups(fixups_t *out, ea_t ea, asize_t size);
|
||||
|
||||
|
||||
/// Does the specified address range contain any fixup information?
|
||||
|
||||
inline bool contains_fixups(ea_t ea, asize_t size)
|
||||
{
|
||||
return get_fixups(NULL, ea, size);
|
||||
}
|
||||
|
||||
|
||||
/// Relocate the bytes with fixup information once more (generic function).
|
||||
/// This function may be called from loader_t::move_segm() if it suits the goal.
|
||||
/// If loader_t::move_segm is not defined then this function will be called
|
||||
/// automatically when moving segments or rebasing the entire program.
|
||||
/// Special parameter values (from = BADADDR, size = 0, to = delta) are used
|
||||
/// when the function is called from rebase_program(delta).
|
||||
|
||||
idaman void ida_export gen_fix_fixups(ea_t from, ea_t to, asize_t size);
|
||||
|
||||
|
||||
/// Handle two fixups in a macro.
|
||||
/// We often combine two instruction that load parts of a value into one
|
||||
/// macro instruction. For example:
|
||||
/// ARM: ADRP X0, #var@PAGE
|
||||
/// ADD X0, X0, #var@PAGEOFF --> ADRL X0, var
|
||||
/// MIPS: lui $v0, %hi(var)
|
||||
/// addiu $v0, $v0, %lo(var) --> la $v0, var
|
||||
/// When applying the fixups that fall inside such a macro, we should convert
|
||||
/// them to one refinfo. This function does exactly that.
|
||||
/// It should be called from the apply() callback of a custom fixup.
|
||||
/// \return success ('false' means that RI was not changed)
|
||||
|
||||
idaman bool ida_export handle_fixups_in_macro(
|
||||
refinfo_t *ri,
|
||||
ea_t ea,
|
||||
fixup_type_t other,
|
||||
uint32 macro_reft_and_flags);
|
||||
|
||||
|
||||
#endif // FIXUP_HPP
|
||||
223
idasdk76/include/fpro.h
Normal file
223
idasdk76/include/fpro.h
Normal file
@@ -0,0 +1,223 @@
|
||||
/*
|
||||
* Interactive disassembler (IDA).
|
||||
* Copyright (c) 1990-2021 Hex-Rays
|
||||
* ALL RIGHTS RESERVED.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef __FPRO_H
|
||||
#define __FPRO_H
|
||||
|
||||
#include <pro.h>
|
||||
#include <stdio.h>
|
||||
|
||||
/*! \file fpro.h
|
||||
|
||||
\brief System independent counterparts of FILE* related functions from Clib
|
||||
|
||||
You should not use C standard I/O functions in your modules.
|
||||
The reason: Each module compiled with Borland
|
||||
(and statically linked to Borland's library) will host a copy of
|
||||
the FILE * information.
|
||||
|
||||
So, if you open a file in the plugin and pass the handle to the
|
||||
kernel, the kernel will not be able to use it.
|
||||
|
||||
If you really need to use the standard functions, define USE_STANDARD_FILE_FUNCTIONS.
|
||||
In this case do not mix them with q... functions.
|
||||
*/
|
||||
|
||||
#if !defined(USE_STANDARD_FILE_FUNCTIONS) && !defined(__CODE_CHECKER__)
|
||||
#undef stdin
|
||||
#undef stdout
|
||||
#undef stderr
|
||||
#undef fgetc
|
||||
#undef fputc
|
||||
#define stdin dont_use_stdin ///< use gets()
|
||||
#define stdout dont_use_stdout ///< use qprintf()
|
||||
#define stderr dont_use_stderr ///< use qeprintf()
|
||||
#define fopen dont_use_fopen ///< use qfopen()
|
||||
#define fread dont_use_fread ///< use qfread()
|
||||
#define fwrite dont_use_fwrite ///< use qfwrite()
|
||||
#define ftell dont_use_ftell ///< use qftell()
|
||||
#define fseek dont_use_fseek ///< use qfseek()
|
||||
#define fclose dont_use_fclose ///< use qfclose()
|
||||
#define fflush dont_use_fflush ///< use qflush()
|
||||
#define fputc dont_use_fputc ///< use qfputc()
|
||||
#define fgetc dont_use_fgetc ///< use qfgetc()
|
||||
#define fgets dont_use_fgets ///< use qfgets()
|
||||
#define fputs dont_use_fputs ///< use qfputs()
|
||||
#define vfprintf dont_use_vfprintf ///< use qvfprintf()
|
||||
#define vfscanf dont_use_vfscanf ///< use qvfscanf()
|
||||
#define fprintf dont_use_fprintf ///< use qfprintf()
|
||||
#define fscanf dont_use_fscanf ///< use qfscanf()
|
||||
#endif
|
||||
|
||||
|
||||
/// \name File I/O
|
||||
/// The following functions work just like their counterparts from Clib,
|
||||
/// only they are safer, system independent, and they set qerrno (see get_qerrno()).
|
||||
//@{
|
||||
idaman THREAD_SAFE FILE *ida_export qfopen(const char *file, const char *mode);
|
||||
idaman THREAD_SAFE ssize_t ida_export qfread(FILE *fp, void *buf, size_t n);
|
||||
idaman THREAD_SAFE ssize_t ida_export qfwrite(FILE *fp, const void *buf, size_t n);
|
||||
idaman THREAD_SAFE qoff64_t ida_export qftell(FILE *fp);
|
||||
idaman THREAD_SAFE int ida_export qfseek(FILE *fp, qoff64_t offset, int whence);
|
||||
idaman THREAD_SAFE int ida_export qfclose(FILE *fp);
|
||||
idaman THREAD_SAFE int ida_export qflush(FILE *fp);
|
||||
idaman THREAD_SAFE int ida_export qfputc(int chr, FILE *fp);
|
||||
idaman THREAD_SAFE int ida_export qfgetc(FILE *fp);
|
||||
idaman THREAD_SAFE char *ida_export qfgets(char *s, size_t len, FILE *fp);
|
||||
idaman THREAD_SAFE int ida_export qfputs(const char *s, FILE *fp);
|
||||
idaman FILE *ida_export qtmpfile(void);
|
||||
idaman THREAD_SAFE int ida_export qunlink(const char *fname);
|
||||
idaman THREAD_SAFE int ida_export qaccess(const char *fname, int mode);
|
||||
idaman THREAD_SAFE char *ida_export qgets(char *line, size_t linesize);
|
||||
|
||||
idaman THREAD_SAFE AS_PRINTF(2, 0) int ida_export qvfprintf(FILE *fp, const char *format, va_list va);
|
||||
idaman THREAD_SAFE AS_PRINTF(1, 0) int ida_export qvprintf(const char *format, va_list va);
|
||||
idaman THREAD_SAFE AS_PRINTF(1, 0) int ida_export qveprintf(const char *format, va_list va);
|
||||
idaman THREAD_SAFE AS_SCANF(2, 0) int ida_export qvfscanf(FILE *fp, const char *format, va_list va);
|
||||
|
||||
#ifdef __cplusplus
|
||||
THREAD_SAFE AS_PRINTF(2, 3) inline int qfprintf(FILE *fp, const char *format, ...)
|
||||
{
|
||||
va_list va;
|
||||
va_start(va, format);
|
||||
int code = qvfprintf(fp, format, va);
|
||||
va_end(va);
|
||||
return code;
|
||||
}
|
||||
|
||||
THREAD_SAFE AS_PRINTF(1, 2) inline int qprintf(const char *format, ...)
|
||||
{
|
||||
va_list va;
|
||||
va_start(va, format);
|
||||
int code = qvprintf(format, va);
|
||||
va_end(va);
|
||||
return code;
|
||||
}
|
||||
|
||||
THREAD_SAFE AS_PRINTF(1, 2) inline int qeprintf(const char *format, ...)
|
||||
{
|
||||
va_list va;
|
||||
va_start(va, format);
|
||||
int code = qveprintf(format, va);
|
||||
va_end(va);
|
||||
return code;
|
||||
}
|
||||
|
||||
THREAD_SAFE AS_SCANF(2, 3) inline int qfscanf(FILE *fp, const char *format, ...)
|
||||
{
|
||||
va_list va;
|
||||
va_start(va, format);
|
||||
int code = qvfscanf(fp, format, va);
|
||||
va_end(va);
|
||||
return code;
|
||||
}
|
||||
#endif
|
||||
//@}
|
||||
|
||||
|
||||
/// Read line from file (the newline is removed from the output buffer)
|
||||
/// \param buf output buffer
|
||||
/// \param fp pointer to file
|
||||
/// \return -1 or length of line
|
||||
|
||||
idaman THREAD_SAFE ssize_t ida_export qgetline(qstring *buf, FILE *fp);
|
||||
|
||||
/// Rename a file: 'newname' may exist, and will be deleted
|
||||
|
||||
idaman THREAD_SAFE int ida_export qrename(const char *oldfname, const char *newfname);
|
||||
|
||||
/// Move a file - more powerful version of qrename
|
||||
/// \retval 0 success
|
||||
/// \retval -1 system error
|
||||
/// \retval else a combination of flags to be given for successful move
|
||||
idaman THREAD_SAFE int ida_export qmove(const char *oldfname, const char *newfname, uint32 flags);
|
||||
enum
|
||||
{
|
||||
QMOVE_CROSS_FS = 0x01, // UNIX: allow moving between different filesystem
|
||||
QMOVE_OVERWRITE = 0x02, // Overwrite existing file
|
||||
QMOVE_OVR_RO = 0x04, // Overwrite file even if it is write-protected
|
||||
};
|
||||
|
||||
/// Copy a file.
|
||||
/// \param from source file name
|
||||
/// \param to destination file name
|
||||
/// \param overwrite overwrite output if it exists?
|
||||
/// \param cb user callback. return false to abort the copy loop
|
||||
/// \param ud user data passed back to cb
|
||||
/// \param flags reserved (should be zero)
|
||||
/// \retval -1 input file not found
|
||||
/// \retval -2 output file not writable
|
||||
/// \retval -3 output file already exists while overwrite is false
|
||||
/// \retval -4 write failure
|
||||
/// \retval -5 interrupted from the callback
|
||||
|
||||
idaman THREAD_SAFE int ida_export qcopyfile(
|
||||
const char *from,
|
||||
const char *to,
|
||||
bool overwrite = true,
|
||||
bool (idaapi *cb)(uint64 pos, uint64 total, void *ud)=NULL,
|
||||
void *ud = NULL,
|
||||
int flags = 0);
|
||||
|
||||
|
||||
/// Get temporary file name.
|
||||
/// Returns absolute path, includes directory, and uses TEMP/TMP vars.
|
||||
|
||||
idaman char *ida_export qtmpnam(char *buf, size_t bufsize);
|
||||
|
||||
|
||||
/// File janitor: will close a file at destruction-time
|
||||
typedef janitor_t<FILE*> file_janitor_t;
|
||||
template <> inline file_janitor_t::~janitor_t()
|
||||
{
|
||||
qfclose(resource);
|
||||
}
|
||||
|
||||
/// \name readbytes/writebytes
|
||||
/// Add-ins for 2..32 byte read/writes.
|
||||
/// \param fp pointer to file
|
||||
/// \param res value read from file
|
||||
/// \param size size of value in bytes (1..32)
|
||||
/// \param mostfirst is MSB first? (0/1)
|
||||
/// \retval 0 All these functions return 0 on success
|
||||
//@{
|
||||
idaman THREAD_SAFE int ida_export freadbytes(FILE *fp,void *res,int size,int mostfirst);
|
||||
idaman THREAD_SAFE int ida_export fwritebytes(FILE *fp,const void *l,int size,int mostfirst);
|
||||
|
||||
#ifdef __cplusplus
|
||||
#define DEF_FREADBYTES(read, write, type, size) \
|
||||
inline THREAD_SAFE int read(FILE *fp, type *res, bool mostfirst) \
|
||||
{ return freadbytes(fp, res, size, mostfirst); } \
|
||||
inline THREAD_SAFE int write(FILE *fp, const type *res, bool mostfirst) \
|
||||
{ return fwritebytes(fp, res, size, mostfirst); }
|
||||
DEF_FREADBYTES(fread2bytes, fwrite2bytes, int16, 2)
|
||||
DEF_FREADBYTES(fread2bytes, fwrite2bytes, uint16, 2)
|
||||
DEF_FREADBYTES(fread4bytes, fwrite4bytes, int32, 4)
|
||||
DEF_FREADBYTES(fread4bytes, fwrite4bytes, uint32, 4)
|
||||
DEF_FREADBYTES(fread8bytes, fwrite8bytes, longlong, 8)
|
||||
DEF_FREADBYTES(fread8bytes, fwrite8bytes, ulonglong, 8)
|
||||
#else
|
||||
#define fread2bytes(fp,v,mf) freadbytes(fp,v,2,mf)
|
||||
#define fwrite2bytes(fp,v,mf) fwritebytes(fp,v,2,mf)
|
||||
#define fread4bytes(fp,v,mf) freadbytes(fp,v,4,mf)
|
||||
#define fwrite4bytes(fp,v,mf) fwritebytes(fp,v,4,mf)
|
||||
#define fread8bytes(fp,v,mf) freadbytes(fp,v,8,mf)
|
||||
#define fwrite8bytes(fp,v,mf) fwritebytes(fp,v,8,mf)
|
||||
#endif
|
||||
//@}
|
||||
|
||||
#if !defined(feof) || !defined(ferror)
|
||||
// If feof() and ferror() are not macros, we cannot use them
|
||||
// Fortunately, for borland and vc they are macros, so there is no problem
|
||||
// GCC defines them as functions: I have no idea whether they will work or not
|
||||
// Anyway we remove the error directive from this file
|
||||
// so the plugins can be compiled with gcc
|
||||
//#error feof or ferror are not macros!
|
||||
#endif
|
||||
|
||||
|
||||
#endif
|
||||
609
idasdk76/include/frame.hpp
Normal file
609
idasdk76/include/frame.hpp
Normal file
@@ -0,0 +1,609 @@
|
||||
/*
|
||||
* Interactive disassembler (IDA).
|
||||
* Copyright (c) 1990-2021 Hex-Rays
|
||||
* ALL RIGHTS RESERVED.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef _FRAME_HPP
|
||||
#define _FRAME_HPP
|
||||
#include <idp.hpp>
|
||||
|
||||
/*! \file frame.hpp
|
||||
|
||||
\brief Routines to manipulate function stack frames, stack
|
||||
variables, register variables and local labels.
|
||||
|
||||
The frame is represented as a structure:
|
||||
<pre>
|
||||
+------------------------------------------------+
|
||||
| function arguments |
|
||||
+------------------------------------------------+
|
||||
| return address (isn't stored in func_t) |
|
||||
+------------------------------------------------+
|
||||
| saved registers (SI, DI, etc - func_t::frregs) |
|
||||
+------------------------------------------------+ <- typical BP
|
||||
| | |
|
||||
| | | func_t::fpd
|
||||
| | |
|
||||
| | <- real BP
|
||||
| local variables (func_t::frsize) |
|
||||
| |
|
||||
| |
|
||||
+------------------------------------------------+ <- SP
|
||||
</pre>
|
||||
|
||||
To access the structure of a function frame, use:
|
||||
- get_struc() (use func_t::frame as structure ID)
|
||||
- get_frame(const func_t *pfn)
|
||||
- get_frame(ea_t ea)
|
||||
*/
|
||||
|
||||
class struc_t;
|
||||
class member_t;
|
||||
class op_t;
|
||||
|
||||
// We need to trace value of SP register. For this we introduce
|
||||
// an array of SP register change points.
|
||||
|
||||
// SP register change point
|
||||
//
|
||||
// NOTE: To manipulate/modify stack points, please use the specialized
|
||||
// functions provided below in this file (stack pointer change points)
|
||||
|
||||
struct stkpnt_t
|
||||
{
|
||||
ea_t ea; // linear address
|
||||
sval_t spd; // here we keep a cumulative difference from [BP-frsize]
|
||||
|
||||
stkpnt_t &operator=(const stkpnt_t &) = default;
|
||||
DECLARE_COMPARISONS(stkpnt_t)
|
||||
{
|
||||
if ( ea < r.ea )
|
||||
return -1;
|
||||
if ( ea > r.ea )
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
};
|
||||
DECLARE_TYPE_AS_MOVABLE(stkpnt_t);
|
||||
// we declare a struct to be able to forward declare it in other files
|
||||
struct stkpnts_t : public qvector<stkpnt_t>
|
||||
{
|
||||
DECLARE_COMPARISONS(stkpnts_t) { return compare_containers(*this, r); }
|
||||
};
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// F R A M E M A N I P U L A T I O N
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
/// Add function frame.
|
||||
/// \param pfn pointer to function structure
|
||||
/// \param frsize size of function local variables
|
||||
/// \param frregs size of saved registers
|
||||
/// \param argsize size of function arguments range which will be purged upon return.
|
||||
/// this parameter is used for __stdcall and __pascal calling conventions.
|
||||
/// for other calling conventions please pass 0.
|
||||
/// \retval 1 ok
|
||||
/// \retval 0 failed (no function, frame already exists)
|
||||
|
||||
idaman bool ida_export add_frame(
|
||||
func_t *pfn,
|
||||
sval_t frsize,
|
||||
ushort frregs,
|
||||
asize_t argsize);
|
||||
|
||||
|
||||
/// Delete a function frame.
|
||||
/// \param pfn pointer to function structure
|
||||
/// \return success
|
||||
|
||||
idaman bool ida_export del_frame(func_t *pfn);
|
||||
|
||||
|
||||
/// Set size of function frame.
|
||||
/// Note: The returned size may not include all stack arguments. It does so
|
||||
/// only for __stdcall and __fastcall calling conventions. To get the entire
|
||||
/// frame size for all cases use get_struc_size(get_frame(pfn)).
|
||||
/// \param pfn pointer to function structure
|
||||
/// \param frsize size of function local variables
|
||||
/// \param frregs size of saved registers
|
||||
/// \param argsize size of function arguments that will be purged
|
||||
/// from the stack upon return
|
||||
/// \return success
|
||||
|
||||
idaman bool ida_export set_frame_size(
|
||||
func_t *pfn,
|
||||
asize_t frsize,
|
||||
ushort frregs,
|
||||
asize_t argsize);
|
||||
|
||||
|
||||
/// Get full size of a function frame.
|
||||
/// This function takes into account size of local variables + size of
|
||||
/// saved registers + size of return address + number of purged bytes.
|
||||
/// The purged bytes correspond to the arguments of the functions with
|
||||
/// __stdcall and __fastcall calling conventions.
|
||||
/// \param pfn pointer to function structure, may be NULL
|
||||
/// \return size of frame in bytes or zero
|
||||
|
||||
idaman asize_t ida_export get_frame_size(const func_t *pfn);
|
||||
|
||||
|
||||
/// Get size of function return address.
|
||||
/// \param pfn pointer to function structure, can't be NULL
|
||||
|
||||
idaman int ida_export get_frame_retsize(const func_t *pfn);
|
||||
|
||||
/// Parts of a frame
|
||||
enum frame_part_t
|
||||
{
|
||||
FPC_ARGS,
|
||||
FPC_RETADDR,
|
||||
FPC_SAVREGS,
|
||||
FPC_LVARS,
|
||||
};
|
||||
|
||||
/// Get offsets of the frame part in the frame.
|
||||
/// \param range pointer to the output buffer with the frame part
|
||||
/// start/end(exclusive) offsets, can't be NULL
|
||||
/// \param pfn pointer to function structure, can't be NULL
|
||||
/// \param part frame part
|
||||
|
||||
idaman void ida_export get_frame_part(range_t *range, const func_t *pfn, frame_part_t part);
|
||||
|
||||
/// Get starting address of arguments section
|
||||
|
||||
inline ea_t frame_off_args(const func_t *pfn)
|
||||
{
|
||||
range_t range;
|
||||
get_frame_part(&range, pfn, FPC_ARGS);
|
||||
return range.start_ea;
|
||||
}
|
||||
|
||||
/// Get starting address of return address section
|
||||
|
||||
inline ea_t frame_off_retaddr(const func_t *pfn)
|
||||
{
|
||||
range_t range;
|
||||
get_frame_part(&range, pfn, FPC_RETADDR);
|
||||
return range.start_ea;
|
||||
}
|
||||
|
||||
/// Get starting address of saved registers section
|
||||
|
||||
inline ea_t frame_off_savregs(const func_t *pfn)
|
||||
{
|
||||
range_t range;
|
||||
get_frame_part(&range, pfn, FPC_SAVREGS);
|
||||
return range.start_ea;
|
||||
}
|
||||
|
||||
/// Get start address of local variables section
|
||||
|
||||
inline ea_t frame_off_lvars(const func_t *pfn)
|
||||
{
|
||||
range_t range;
|
||||
get_frame_part(&range, pfn, FPC_LVARS);
|
||||
return range.start_ea;
|
||||
}
|
||||
|
||||
/// Does the given offset lie within the arguments section?
|
||||
|
||||
inline bool processor_t::is_funcarg_off(const func_t *pfn, uval_t frameoff) const
|
||||
{
|
||||
range_t args;
|
||||
get_frame_part(&args, pfn, FPC_ARGS);
|
||||
return stkup()
|
||||
? frameoff < args.end_ea
|
||||
: frameoff >= args.start_ea;
|
||||
}
|
||||
|
||||
/// Does the given offset lie within the local variables section?
|
||||
|
||||
inline sval_t processor_t::lvar_off(const func_t *pfn, uval_t frameoff) const
|
||||
{
|
||||
range_t lvars;
|
||||
get_frame_part(&lvars, pfn, FPC_LVARS);
|
||||
return stkup()
|
||||
? frameoff - lvars.start_ea
|
||||
: lvars.end_ea - frameoff;
|
||||
}
|
||||
|
||||
/// Get pointer to function frame.
|
||||
/// \param pfn pointer to function structure
|
||||
|
||||
idaman struc_t *ida_export get_frame(const func_t *pfn);
|
||||
|
||||
/// Get pointer to function frame.
|
||||
/// \param ea any address in the function
|
||||
|
||||
inline struc_t *get_frame(ea_t ea) { return get_frame(get_func(ea)); }
|
||||
|
||||
|
||||
/// Convert struct offsets into fp-relative offsets.
|
||||
/// This function converts the offsets inside the struc_t object
|
||||
/// into the frame pointer offsets (for example, EBP-relative).
|
||||
|
||||
inline sval_t soff_to_fpoff(func_t *pfn, uval_t soff)
|
||||
{
|
||||
return soff - pfn->frsize + pfn->fpd;
|
||||
}
|
||||
|
||||
|
||||
/// Update frame pointer delta.
|
||||
/// \param pfn pointer to function structure
|
||||
/// \param fpd new fpd value.
|
||||
/// cannot be bigger than the local variable range size.
|
||||
/// \return success
|
||||
|
||||
idaman bool ida_export update_fpd(func_t *pfn, asize_t fpd);
|
||||
|
||||
|
||||
/// Set the number of purged bytes for a function or data item (funcptr).
|
||||
/// This function will update the database and plan to reanalyze items
|
||||
/// referencing the specified address. It works only for processors
|
||||
/// with #PR_PURGING bit in 16 and 32 bit modes.
|
||||
/// \param ea address of the function of item
|
||||
/// \param nbytes number of purged bytes
|
||||
/// \param override_old_value may overwrite old information about purged bytes
|
||||
/// \return success
|
||||
|
||||
idaman bool ida_export set_purged(ea_t ea, int nbytes, bool override_old_value);
|
||||
|
||||
|
||||
/// Get function by its frame id.
|
||||
/// \warning this function works only with databases created by IDA > 5.6
|
||||
/// \param frame_id id of the function frame
|
||||
/// \return start address of the function or #BADADDR
|
||||
|
||||
idaman ea_t ida_export get_func_by_frame(tid_t frame_id);
|
||||
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// S T A C K V A R I A B L E S
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
/// Get pointer to stack variable.
|
||||
/// \param actval actual value used to fetch stack variable
|
||||
/// this pointer may point to 'v'
|
||||
/// \param insn the instruction
|
||||
/// \param x reference to instruction operand
|
||||
/// \param v immediate value in the operand (usually x.addr)
|
||||
/// \return NULL or ptr to stack variable
|
||||
|
||||
idaman member_t *ida_export get_stkvar(
|
||||
sval_t *actval,
|
||||
const insn_t &insn,
|
||||
const op_t &x,
|
||||
sval_t v);
|
||||
|
||||
/// Automatically add stack variable if doesn't exist.
|
||||
/// Processor modules should use insn_t::create_stkvar().
|
||||
/// \param insn the instruction
|
||||
/// \param x reference to instruction operand
|
||||
/// \param v immediate value in the operand (usually x.addr)
|
||||
/// \param flags \ref STKVAR_1
|
||||
/// \return success
|
||||
|
||||
idaman bool ida_export add_stkvar(const insn_t &insn, const op_t &x, sval_t v, int flags);
|
||||
|
||||
/// \defgroup STKVAR_1 Add stkvar flags
|
||||
/// Passed as 'flags' parameter to add_stkvar()
|
||||
//@{
|
||||
#define STKVAR_VALID_SIZE 0x0001 ///< x.dtyp contains correct variable type
|
||||
///< (for insns like 'lea' this bit must be off)
|
||||
///< in general, dr_O references do not allow
|
||||
///< to determine the variable size
|
||||
//@}
|
||||
|
||||
/// Define/redefine a stack variable.
|
||||
/// \param pfn pointer to function
|
||||
/// \param name variable name, NULL means autogenerate a name
|
||||
/// \param off offset of the stack variable in the frame.
|
||||
/// negative values denote local variables, positive - function arguments.
|
||||
/// \param flags variable type flags (byte_flag() for a byte variable, for example)
|
||||
/// \param ti additional type information (like offsets, structs, etc)
|
||||
/// \param nbytes number of bytes occupied by the variable
|
||||
/// \return success
|
||||
|
||||
idaman bool ida_export define_stkvar(
|
||||
func_t *pfn,
|
||||
const char *name,
|
||||
sval_t off,
|
||||
flags_t flags,
|
||||
const opinfo_t *ti,
|
||||
asize_t nbytes);
|
||||
|
||||
|
||||
/// Build automatic stack variable name.
|
||||
/// \param buf pointer to buffer
|
||||
/// \param pfn pointer to function (can't be NULL!)
|
||||
/// \param v value of variable offset
|
||||
/// \return length of stack variable name or -1
|
||||
|
||||
idaman ssize_t ida_export build_stkvar_name(
|
||||
qstring *buf,
|
||||
const func_t *pfn,
|
||||
sval_t v);
|
||||
|
||||
|
||||
/// Calculate offset of stack variable in the frame structure.
|
||||
/// \param pfn pointer to function (can't be NULL!)
|
||||
/// \param insn the instruction
|
||||
/// \param n number of operand: (0..#UA_MAXOP-1)
|
||||
/// -1 if error, return #BADADDR
|
||||
/// \return #BADADDR if some error (issue a warning if stack frame is bad)
|
||||
|
||||
idaman ea_t ida_export calc_stkvar_struc_offset(
|
||||
func_t *pfn,
|
||||
const insn_t &insn,
|
||||
int n);
|
||||
|
||||
|
||||
/// Find and delete wrong frame info.
|
||||
/// Namely, we delete:
|
||||
/// - unreferenced stack variable definitions
|
||||
/// - references to dead stack variables (i.e. operands displayed in red)
|
||||
/// these operands will be untyped and most likely displayed in hex.
|
||||
/// We also plan to reanalyze instruction with the stack frame references
|
||||
/// \param pfn pointer to the function
|
||||
/// \param should_reanalyze callback to determine which instructions to reanalyze
|
||||
/// \return number of deleted definitions
|
||||
|
||||
idaman int ida_export delete_wrong_frame_info(
|
||||
func_t *pfn,
|
||||
bool idaapi should_reanalyze(const insn_t &insn));
|
||||
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// R E G I S T E R V A R I A B L E S
|
||||
//--------------------------------------------------------------------------
|
||||
/// \defgroup regvar Register variables
|
||||
/// Definition of ::regvar_t and related functions
|
||||
//@{
|
||||
|
||||
idaman void ida_export free_regvar(struct regvar_t *v);
|
||||
|
||||
/// A register variable allows the user to rename a general processor register
|
||||
/// to a meaningful name.
|
||||
/// IDA doesn't check whether the target assembler supports the register renaming.
|
||||
/// All register definitions will appear at the beginning of the function.
|
||||
struct regvar_t : public range_t
|
||||
{
|
||||
char *canon = nullptr; ///< canonical register name (case-insensitive)
|
||||
char *user = nullptr; ///< user-defined register name
|
||||
char *cmt = nullptr; ///< comment to appear near definition
|
||||
|
||||
regvar_t() {}
|
||||
regvar_t(const regvar_t &r) : range_t(r)
|
||||
{
|
||||
canon = ::qstrdup(r.canon);
|
||||
user = ::qstrdup(r.user);
|
||||
cmt = ::qstrdup(r.cmt);
|
||||
}
|
||||
~regvar_t() { free_regvar(this); }
|
||||
regvar_t &operator=(const regvar_t &r)
|
||||
{
|
||||
if ( this != &r )
|
||||
{
|
||||
free_regvar(this);
|
||||
new (this) regvar_t(r);
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
void swap(regvar_t &r)
|
||||
{
|
||||
uchar buf[sizeof(*this)];
|
||||
memcpy(buf, &r, sizeof(buf));
|
||||
memcpy(&r, this, sizeof(buf));
|
||||
memcpy(this, buf, sizeof(buf));
|
||||
}
|
||||
#ifndef SWIG
|
||||
DECLARE_COMPARISONS(regvar_t);
|
||||
#endif
|
||||
};
|
||||
DECLARE_TYPE_AS_MOVABLE(regvar_t);
|
||||
|
||||
/// Define a register variable.
|
||||
/// \param pfn function in which the definition will be created
|
||||
/// \param ea1,ea2 range of addresses within the function where the definition
|
||||
/// will be used
|
||||
/// \param canon name of a general register
|
||||
/// \param user user-defined name for the register
|
||||
/// \param cmt comment for the definition
|
||||
/// \return \ref REGVAR_ERROR_
|
||||
|
||||
idaman int ida_export add_regvar(
|
||||
func_t *pfn,
|
||||
ea_t ea1,
|
||||
ea_t ea2,
|
||||
const char *canon,
|
||||
const char *user,
|
||||
const char *cmt);
|
||||
/// \defgroup REGVAR_ERROR_ Register variable error codes
|
||||
/// Return values for functions in described in \ref regvar
|
||||
//@{
|
||||
#define REGVAR_ERROR_OK 0 ///< all ok
|
||||
#define REGVAR_ERROR_ARG (-1) ///< function arguments are bad
|
||||
#define REGVAR_ERROR_RANGE (-2) ///< the definition range is bad
|
||||
#define REGVAR_ERROR_NAME (-3) ///< the provided name(s) can't be accepted
|
||||
//@}
|
||||
|
||||
/// Find a register variable definition (powerful version).
|
||||
/// One of 'canon' and 'user' should be NULL.
|
||||
/// If both 'canon' and 'user' are NULL it returns the first regvar
|
||||
/// definition in the range.
|
||||
/// \param pfn function in question
|
||||
/// \param ea1,ea2 range of addresses to search.
|
||||
/// ea1==BADADDR means the entire function
|
||||
/// \param canon name of a general register
|
||||
/// \param user user-defined name for the register
|
||||
/// \return NULL-not found, otherwise ptr to regvar_t
|
||||
|
||||
idaman regvar_t *ida_export find_regvar(func_t *pfn, ea_t ea1, ea_t ea2, const char *canon, const char *user);
|
||||
|
||||
|
||||
/// Find a register variable definition.
|
||||
/// \param pfn function in question
|
||||
/// \param ea current address
|
||||
/// \param canon name of a general register
|
||||
/// \return NULL-not found, otherwise ptr to regvar_t
|
||||
|
||||
inline regvar_t *find_regvar(func_t *pfn, ea_t ea, const char *canon)
|
||||
{
|
||||
return find_regvar(pfn, ea, ea+1, canon, NULL);
|
||||
}
|
||||
|
||||
|
||||
/// Is there a register variable definition?
|
||||
/// \param pfn function in question
|
||||
/// \param ea current address
|
||||
|
||||
inline bool has_regvar(func_t *pfn, ea_t ea)
|
||||
{
|
||||
return find_regvar(pfn, ea, ea+1, NULL, NULL) != NULL;
|
||||
}
|
||||
|
||||
|
||||
/// Rename a register variable.
|
||||
/// \param pfn function in question
|
||||
/// \param v variable to rename
|
||||
/// \param user new user-defined name for the register
|
||||
/// \return \ref REGVAR_ERROR_
|
||||
|
||||
idaman int ida_export rename_regvar(func_t *pfn, regvar_t *v, const char *user);
|
||||
|
||||
|
||||
/// Set comment for a register variable.
|
||||
/// \param pfn function in question
|
||||
/// \param v variable to rename
|
||||
/// \param cmt new comment
|
||||
/// \return \ref REGVAR_ERROR_
|
||||
|
||||
idaman int ida_export set_regvar_cmt(func_t *pfn, regvar_t *v, const char *cmt);
|
||||
|
||||
|
||||
/// Delete a register variable definition.
|
||||
/// \param pfn function in question
|
||||
/// \param ea1,ea2 range of addresses within the function where the definition holds
|
||||
/// \param canon name of a general register
|
||||
/// \return \ref REGVAR_ERROR_
|
||||
|
||||
idaman int ida_export del_regvar(func_t *pfn, ea_t ea1, ea_t ea2, const char *canon);
|
||||
|
||||
//@} regvar
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// S P R E G I S T E R C H A N G E P O I N T S
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
/// Add automatic SP register change point.
|
||||
/// \param pfn pointer to function. may be NULL.
|
||||
/// \param ea linear address where SP changes.
|
||||
/// usually this is the end of the instruction which
|
||||
/// modifies the stack pointer (\cmd{ea}+\cmd{size})
|
||||
/// \param delta difference between old and new values of SP
|
||||
/// \return success
|
||||
|
||||
idaman bool ida_export add_auto_stkpnt(func_t *pfn, ea_t ea, sval_t delta);
|
||||
|
||||
|
||||
/// Add user-defined SP register change point.
|
||||
/// \param ea linear address where SP changes
|
||||
/// \param delta difference between old and new values of SP
|
||||
/// \return success
|
||||
|
||||
idaman bool ida_export add_user_stkpnt(ea_t ea, sval_t delta);
|
||||
|
||||
|
||||
/// Delete SP register change point.
|
||||
/// \param pfn pointer to function. may be NULL.
|
||||
/// \param ea linear address
|
||||
/// \return success
|
||||
|
||||
idaman bool ida_export del_stkpnt(func_t *pfn, ea_t ea);
|
||||
|
||||
|
||||
/// Get difference between the initial and current values of ESP.
|
||||
/// \param pfn pointer to function. may be NULL.
|
||||
/// \param ea linear address of an instruction
|
||||
/// \return 0 or the difference, usually a negative number.
|
||||
/// returns the sp-diff before executing the instruction.
|
||||
|
||||
idaman sval_t ida_export get_spd(func_t *pfn, ea_t ea);
|
||||
|
||||
|
||||
/// Get effective difference between the initial and current values of ESP.
|
||||
/// This function returns the sp-diff used by the instruction.
|
||||
/// The difference between get_spd() and get_effective_spd() is present only
|
||||
/// for instructions like "pop [esp+N]": they modify sp and use the modified value.
|
||||
/// \param pfn pointer to function. may be NULL.
|
||||
/// \param ea linear address
|
||||
/// \return 0 or the difference, usually a negative number
|
||||
|
||||
idaman sval_t ida_export get_effective_spd(func_t *pfn, ea_t ea);
|
||||
|
||||
|
||||
/// Get modification of SP made at the specified location
|
||||
/// \param pfn pointer to function. may be NULL.
|
||||
/// \param ea linear address
|
||||
/// \return 0 if the specified location doesn't contain a SP change point.
|
||||
/// otherwise return delta of SP modification.
|
||||
|
||||
idaman sval_t ida_export get_sp_delta(func_t *pfn, ea_t ea);
|
||||
|
||||
|
||||
/// Recalculate SP delta for an instruction that stops execution.
|
||||
/// The next instruction is not reached from the current instruction.
|
||||
/// We need to recalculate SP for the next instruction.
|
||||
///
|
||||
/// This function will create a new automatic SP register change
|
||||
/// point if necessary. It should be called from the emulator (emu.cpp)
|
||||
/// when auto_state == ::AU_USED if the current instruction doesn't pass
|
||||
/// the execution flow to the next instruction.
|
||||
/// \param cur_ea linear address of the current instruction
|
||||
/// \retval 1 new stkpnt is added
|
||||
/// \retval 0 nothing is changed
|
||||
|
||||
idaman bool ida_export recalc_spd(ea_t cur_ea);
|
||||
|
||||
|
||||
/// An xref to an argument or variable located in a function's stack frame
|
||||
struct xreflist_entry_t
|
||||
{
|
||||
ea_t ea; ///< Location of the insn referencing the stack frame member
|
||||
uchar opnum; ///< Number of the operand of that instruction
|
||||
uchar type; ///< The type of xref (::cref_t & ::dref_t)
|
||||
|
||||
DECLARE_COMPARISONS(xreflist_entry_t)
|
||||
{
|
||||
int code = ::compare(ea, r.ea);
|
||||
if ( code == 0 )
|
||||
{
|
||||
code = ::compare(type, r.type);
|
||||
if ( code == 0 )
|
||||
code = ::compare(opnum, r.opnum);
|
||||
}
|
||||
return code;
|
||||
}
|
||||
};
|
||||
DECLARE_TYPE_AS_MOVABLE(xreflist_entry_t);
|
||||
typedef qvector<xreflist_entry_t> xreflist_t; ///< vector of xrefs to variables in a function's stack frame
|
||||
|
||||
/// Fill 'out' with a list of all the xrefs made from function 'pfn', to
|
||||
/// the argument or variable 'mptr' in 'pfn's stack frame.
|
||||
/// \param out the list of xrefs to fill.
|
||||
/// \param pfn the function to scan.
|
||||
/// \param mptr the argument/variable in pfn's stack frame.
|
||||
|
||||
idaman void ida_export build_stkvar_xrefs(xreflist_t *out, func_t *pfn, const member_t *mptr);
|
||||
|
||||
|
||||
#ifndef NO_OBSOLETE_FUNCS
|
||||
idaman DEPRECATED ea_t ida_export get_min_spd_ea(func_t *pfn);
|
||||
idaman DEPRECATED int ida_export delete_unreferenced_stkvars(func_t *pfn);
|
||||
idaman DEPRECATED int ida_export delete_wrong_stkvar_ops(func_t *pfn);
|
||||
#endif
|
||||
|
||||
#endif // _FRAME_HPP
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user